diff options
| author | ProsperousPotato <ProsperousPotato@users.noreply.github.com> | 2025-06-14 16:39:03 +0100 |
|---|---|---|
| committer | ProsperousPotato <ProsperousPotato@users.noreply.github.com> | 2025-06-14 16:39:03 +0100 |
| commit | c827a90d71ca48a99f5e94a4698574f8146c394f (patch) | |
| tree | db2394307cdbb47d2023b0f90ccd842e703e0882 | |
| parent | b6e3cea71c32e40523375a8949a65568604821c8 (diff) | |
rewrite from original
| -rw-r--r-- | Makefile | 27 | ||||
| -rw-r--r-- | PKGBUILD | 45 | ||||
| -rw-r--r-- | boxdraw.c | 194 | ||||
| -rw-r--r-- | boxdraw_data.h | 214 | ||||
| -rw-r--r-- | config.h | 207 | ||||
| -rw-r--r-- | config.mk | 14 | ||||
| -rw-r--r-- | disablebold.diff | 69 | ||||
| -rw-r--r-- | hb.c | 154 | ||||
| -rw-r--r-- | hb.h | 7 | ||||
| -rw-r--r-- | st-copyout | 13 | ||||
| -rw-r--r-- | st-urlhandler | 19 | ||||
| -rw-r--r-- | st.1 | 54 | ||||
| -rw-r--r-- | st.c | 436 | ||||
| -rw-r--r-- | st.h | 105 | ||||
| -rw-r--r-- | st.info | 10 | ||||
| -rw-r--r-- | win.h | 4 | ||||
| -rw-r--r-- | x.c | 879 |
17 files changed, 1046 insertions, 1405 deletions
@@ -4,24 +4,19 @@ include config.mk -SRC = st.c x.c boxdraw.c hb.c +SRC = st.c x.c rowcolumn_diacritics_helpers.c graphics.c OBJ = $(SRC:.c=.o) -all: options st +all: st -options: - @echo st build options: - @echo "CFLAGS = $(STCFLAGS)" - @echo "LDFLAGS = $(STLDFLAGS)" - @echo "CC = $(CC)" +config.h: + cp config.def.h config.h .c.o: $(CC) $(STCFLAGS) -c $< st.o: config.h st.h win.h -x.o: arg.h config.h st.h win.h hb.h -hb.o: st.h -boxdraw.o: config.h st.h boxdraw_data.h +x.o: arg.h config.h st.h win.h graphics.h $(OBJ): config.h config.mk @@ -29,12 +24,12 @@ st: $(OBJ) $(CC) -o $@ $(OBJ) $(STLDFLAGS) clean: - rm -f st $(OBJ) st-$(VERSION).tar.gz *.rej *.orig *.o + rm -f st $(OBJ) st-$(VERSION).tar.gz dist: clean mkdir -p st-$(VERSION) cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ - config.h st.info st.1 arg.h st.h win.h $(SRC)\ + config.def.h st.info st.1 arg.h st.h win.h $(SRC)\ st-$(VERSION) tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz rm -rf st-$(VERSION) @@ -42,11 +37,7 @@ dist: clean install: st mkdir -p $(DESTDIR)$(PREFIX)/bin cp -f st $(DESTDIR)$(PREFIX)/bin - cp -f st-copyout $(DESTDIR)$(PREFIX)/bin - cp -f st-urlhandler $(DESTDIR)$(PREFIX)/bin chmod 755 $(DESTDIR)$(PREFIX)/bin/st - chmod 755 $(DESTDIR)$(PREFIX)/bin/st-copyout - chmod 755 $(DESTDIR)$(PREFIX)/bin/st-urlhandler mkdir -p $(DESTDIR)$(MANPREFIX)/man1 sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 @@ -55,8 +46,6 @@ install: st uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/st - rm -f $(DESTDIR)$(PREFIX)/bin/st-copyout - rm -f $(DESTDIR)$(PREFIX)/bin/st-urlhandler rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 -.PHONY: all options clean dist install uninstall +.PHONY: all clean dist install uninstall diff --git a/PKGBUILD b/PKGBUILD deleted file mode 100644 index 66dd05e..0000000 --- a/PKGBUILD +++ /dev/null @@ -1,45 +0,0 @@ -# Maintainer: - -pkgname=st-luke-git -_pkgname=st -pkgver=0.8.2.r1062.2087ab9 -pkgrel=1 -epoch=1 -pkgdesc="Luke's simple (suckless) terminal with vim-bindings, transparency, xresources, etc. " -url='https://github.com/LukeSmithxyz/st' -arch=('i686' 'x86_64') -license=('MIT') -options=('zipman') -depends=('libxft') -makedepends=('ncurses' 'libxext' 'git') -optdepends=('dmenu: feed urls to dmenu') -source=(git+https://github.com/LukeSmithxyz/st) -sha1sums=('SKIP') - -provides=("${_pkgname}") -conflicts=("${_pkgname}") - -pkgver() { - cd "${_pkgname}" - printf "%s.r%s.%s" "$(awk '/^VERSION =/ {print $3}' config.mk)" \ - "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" -} - -prepare() { - cd $srcdir/${_pkgname} - # skip terminfo which conflicts with ncurses - sed -i '/tic /d' Makefile -} - -build() { - cd "${_pkgname}" - make X11INC=/usr/include/X11 X11LIB=/usr/lib/X11 -} - -package() { - cd "${_pkgname}" - make PREFIX=/usr DESTDIR="${pkgdir}" install - install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" - install -Dm644 README.md "${pkgdir}/usr/share/doc/${pkgname}/README.md" - install -Dm644 Xdefaults "${pkgdir}/usr/share/doc/${pkgname}/Xdefaults.example" -} diff --git a/boxdraw.c b/boxdraw.c deleted file mode 100644 index 28a92d0..0000000 --- a/boxdraw.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih - * MIT/X Consortium License - */ - -#include <X11/Xft/Xft.h> -#include "st.h" -#include "boxdraw_data.h" - -/* Rounded non-negative integers division of n / d */ -#define DIV(n, d) (((n) + (d) / 2) / (d)) - -static Display *xdpy; -static Colormap xcmap; -static XftDraw *xd; -static Visual *xvis; - -static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort); -static void drawboxlines(int, int, int, int, XftColor *, ushort); - -/* public API */ - -void -boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis) -{ - xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis; -} - -int -isboxdraw(Rune u) -{ - Rune block = u & ~0xff; - return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) || - (boxdraw_braille && block == 0x2800); -} - -/* the "index" is actually the entire shape data encoded as ushort */ -ushort -boxdrawindex(const Glyph *g) -{ - if (boxdraw_braille && (g->u & ~0xff) == 0x2800) - return BRL | (uint8_t)g->u; - if (boxdraw_bold && (g->mode & ATTR_BOLD)) - return BDB | boxdata[(uint8_t)g->u]; - return boxdata[(uint8_t)g->u]; -} - -void -drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg, - const XftGlyphFontSpec *specs, int len) -{ - for ( ; len-- > 0; x += cw, specs++) - drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph); -} - -/* implementation */ - -void -drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd) -{ - ushort cat = bd & ~(BDB | 0xff); /* mask out bold and data */ - if (bd & (BDL | BDA)) { - /* lines (light/double/heavy/arcs) */ - drawboxlines(x, y, w, h, fg, bd); - - } else if (cat == BBD) { - /* lower (8-X)/8 block */ - int d = DIV((uint8_t)bd * h, 8); - XftDrawRect(xd, fg, x, y + d, w, h - d); - - } else if (cat == BBU) { - /* upper X/8 block */ - XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8)); - - } else if (cat == BBL) { - /* left X/8 block */ - XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h); - - } else if (cat == BBR) { - /* right (8-X)/8 block */ - int d = DIV((uint8_t)bd * w, 8); - XftDrawRect(xd, fg, x + d, y, w - d, h); - - } else if (cat == BBQ) { - /* Quadrants */ - int w2 = DIV(w, 2), h2 = DIV(h, 2); - if (bd & TL) - XftDrawRect(xd, fg, x, y, w2, h2); - if (bd & TR) - XftDrawRect(xd, fg, x + w2, y, w - w2, h2); - if (bd & BL) - XftDrawRect(xd, fg, x, y + h2, w2, h - h2); - if (bd & BR) - XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2); - - } else if (bd & BBS) { - /* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */ - int d = (uint8_t)bd; - XftColor xfc; - XRenderColor xrc = { .alpha = 0xffff }; - - xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4); - xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4); - xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4); - - XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc); - XftDrawRect(xd, &xfc, x, y, w, h); - XftColorFree(xdpy, xvis, xcmap, &xfc); - - } else if (cat == BRL) { - /* braille, each data bit corresponds to one dot at 2x4 grid */ - int w1 = DIV(w, 2); - int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4); - - if (bd & 1) XftDrawRect(xd, fg, x, y, w1, h1); - if (bd & 2) XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1); - if (bd & 4) XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2); - if (bd & 8) XftDrawRect(xd, fg, x + w1, y, w - w1, h1); - if (bd & 16) XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1); - if (bd & 32) XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2); - if (bd & 64) XftDrawRect(xd, fg, x, y + h3, w1, h - h3); - if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3); - - } -} - -void -drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd) -{ - /* s: stem thickness. width/8 roughly matches underscore thickness. */ - /* We draw bold as 1.5 * normal-stem and at least 1px thicker. */ - /* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */ - int mwh = MIN(w, h); - int base_s = MAX(1, DIV(mwh, 8)); - int bold = (bd & BDB) && mwh >= 6; /* possibly ignore boldness */ - int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s; - int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2); - /* the s-by-s square (x + w2, y + h2, s, s) is the center texel. */ - /* The base length (per direction till edge) includes this square. */ - - int light = bd & (LL | LU | LR | LD); - int double_ = bd & (DL | DU | DR | DD); - - if (light) { - /* d: additional (negative) length to not-draw the center */ - /* texel - at arcs and avoid drawing inside (some) doubles */ - int arc = bd & BDA; - int multi_light = light & (light - 1); - int multi_double = double_ & (double_ - 1); - /* light crosses double only at DH+LV, DV+LH (ref. shapes) */ - int d = arc || (multi_double && !multi_light) ? -s : 0; - - if (bd & LL) - XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s); - if (bd & LU) - XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d); - if (bd & LR) - XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s); - if (bd & LD) - XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d); - } - - /* double lines - also align with light to form heavy when combined */ - if (double_) { - /* - * going clockwise, for each double-ray: p is additional length - * to the single-ray nearer to the previous direction, and n to - * the next. p and n adjust from the base length to lengths - * which consider other doubles - shorter to avoid intersections - * (p, n), or longer to draw the far-corner texel (n). - */ - int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD; - if (dl) { - int p = dd ? -s : 0, n = du ? -s : dd ? s : 0; - XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s); - XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s); - } - if (du) { - int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0; - XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p); - XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n); - } - if (dr) { - int p = du ? -s : 0, n = dd ? -s : du ? s : 0; - XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s); - XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s); - } - if (dd) { - int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0; - XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p); - XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n); - } - } -} diff --git a/boxdraw_data.h b/boxdraw_data.h deleted file mode 100644 index 7890500..0000000 --- a/boxdraw_data.h +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih - * MIT/X Consortium License - */ - -/* - * U+25XX codepoints data - * - * References: - * http://www.unicode.org/charts/PDF/U2500.pdf - * http://www.unicode.org/charts/PDF/U2580.pdf - * - * Test page: - * https://github.com/GNOME/vte/blob/master/doc/boxes.txt - */ - -/* Each shape is encoded as 16-bits. Higher bits are category, lower are data */ -/* Categories (mutually exclusive except BDB): */ -/* For convenience, BDL/BDA/BBS/BDB are 1 bit each, the rest are enums */ -#define BDL (1<<8) /* Box Draw Lines (light/double/heavy) */ -#define BDA (1<<9) /* Box Draw Arc (light) */ - -#define BBD (1<<10) /* Box Block Down (lower) X/8 */ -#define BBL (2<<10) /* Box Block Left X/8 */ -#define BBU (3<<10) /* Box Block Upper X/8 */ -#define BBR (4<<10) /* Box Block Right X/8 */ -#define BBQ (5<<10) /* Box Block Quadrants */ -#define BRL (6<<10) /* Box Braille (data is lower byte of U28XX) */ - -#define BBS (1<<14) /* Box Block Shades */ -#define BDB (1<<15) /* Box Draw is Bold */ - -/* (BDL/BDA) Light/Double/Heavy x Left/Up/Right/Down/Horizontal/Vertical */ -/* Heavy is light+double (literally drawing light+double align to form heavy) */ -#define LL (1<<0) -#define LU (1<<1) -#define LR (1<<2) -#define LD (1<<3) -#define LH (LL+LR) -#define LV (LU+LD) - -#define DL (1<<4) -#define DU (1<<5) -#define DR (1<<6) -#define DD (1<<7) -#define DH (DL+DR) -#define DV (DU+DD) - -#define HL (LL+DL) -#define HU (LU+DU) -#define HR (LR+DR) -#define HD (LD+DD) -#define HH (HL+HR) -#define HV (HU+HD) - -/* (BBQ) Quadrants Top/Bottom x Left/Right */ -#define TL (1<<0) -#define TR (1<<1) -#define BL (1<<2) -#define BR (1<<3) - -/* Data for U+2500 - U+259F except dashes/diagonals */ -static const unsigned short boxdata[256] = { - /* light lines */ - [0x00] = BDL + LH, /* light horizontal */ - [0x02] = BDL + LV, /* light vertical */ - [0x0c] = BDL + LD + LR, /* light down and right */ - [0x10] = BDL + LD + LL, /* light down and left */ - [0x14] = BDL + LU + LR, /* light up and right */ - [0x18] = BDL + LU + LL, /* light up and left */ - [0x1c] = BDL + LV + LR, /* light vertical and right */ - [0x24] = BDL + LV + LL, /* light vertical and left */ - [0x2c] = BDL + LH + LD, /* light horizontal and down */ - [0x34] = BDL + LH + LU, /* light horizontal and up */ - [0x3c] = BDL + LV + LH, /* light vertical and horizontal */ - [0x74] = BDL + LL, /* light left */ - [0x75] = BDL + LU, /* light up */ - [0x76] = BDL + LR, /* light right */ - [0x77] = BDL + LD, /* light down */ - - /* heavy [+light] lines */ - [0x01] = BDL + HH, - [0x03] = BDL + HV, - [0x0d] = BDL + HR + LD, - [0x0e] = BDL + HD + LR, - [0x0f] = BDL + HD + HR, - [0x11] = BDL + HL + LD, - [0x12] = BDL + HD + LL, - [0x13] = BDL + HD + HL, - [0x15] = BDL + HR + LU, - [0x16] = BDL + HU + LR, - [0x17] = BDL + HU + HR, - [0x19] = BDL + HL + LU, - [0x1a] = BDL + HU + LL, - [0x1b] = BDL + HU + HL, - [0x1d] = BDL + HR + LV, - [0x1e] = BDL + HU + LD + LR, - [0x1f] = BDL + HD + LR + LU, - [0x20] = BDL + HV + LR, - [0x21] = BDL + HU + HR + LD, - [0x22] = BDL + HD + HR + LU, - [0x23] = BDL + HV + HR, - [0x25] = BDL + HL + LV, - [0x26] = BDL + HU + LD + LL, - [0x27] = BDL + HD + LU + LL, - [0x28] = BDL + HV + LL, - [0x29] = BDL + HU + HL + LD, - [0x2a] = BDL + HD + HL + LU, - [0x2b] = BDL + HV + HL, - [0x2d] = BDL + HL + LD + LR, - [0x2e] = BDL + HR + LL + LD, - [0x2f] = BDL + HH + LD, - [0x30] = BDL + HD + LH, - [0x31] = BDL + HD + HL + LR, - [0x32] = BDL + HR + HD + LL, - [0x33] = BDL + HH + HD, - [0x35] = BDL + HL + LU + LR, - [0x36] = BDL + HR + LU + LL, - [0x37] = BDL + HH + LU, - [0x38] = BDL + HU + LH, - [0x39] = BDL + HU + HL + LR, - [0x3a] = BDL + HU + HR + LL, - [0x3b] = BDL + HH + HU, - [0x3d] = BDL + HL + LV + LR, - [0x3e] = BDL + HR + LV + LL, - [0x3f] = BDL + HH + LV, - [0x40] = BDL + HU + LH + LD, - [0x41] = BDL + HD + LH + LU, - [0x42] = BDL + HV + LH, - [0x43] = BDL + HU + HL + LD + LR, - [0x44] = BDL + HU + HR + LD + LL, - [0x45] = BDL + HD + HL + LU + LR, - [0x46] = BDL + HD + HR + LU + LL, - [0x47] = BDL + HH + HU + LD, - [0x48] = BDL + HH + HD + LU, - [0x49] = BDL + HV + HL + LR, - [0x4a] = BDL + HV + HR + LL, - [0x4b] = BDL + HV + HH, - [0x78] = BDL + HL, - [0x79] = BDL + HU, - [0x7a] = BDL + HR, - [0x7b] = BDL + HD, - [0x7c] = BDL + HR + LL, - [0x7d] = BDL + HD + LU, - [0x7e] = BDL + HL + LR, - [0x7f] = BDL + HU + LD, - - /* double [+light] lines */ - [0x50] = BDL + DH, - [0x51] = BDL + DV, - [0x52] = BDL + DR + LD, - [0x53] = BDL + DD + LR, - [0x54] = BDL + DR + DD, - [0x55] = BDL + DL + LD, - [0x56] = BDL + DD + LL, - [0x57] = BDL + DL + DD, - [0x58] = BDL + DR + LU, - [0x59] = BDL + DU + LR, - [0x5a] = BDL + DU + DR, - [0x5b] = BDL + DL + LU, - [0x5c] = BDL + DU + LL, - [0x5d] = BDL + DL + DU, - [0x5e] = BDL + DR + LV, - [0x5f] = BDL + DV + LR, - [0x60] = BDL + DV + DR, - [0x61] = BDL + DL + LV, - [0x62] = BDL + DV + LL, - [0x63] = BDL + DV + DL, - [0x64] = BDL + DH + LD, - [0x65] = BDL + DD + LH, - [0x66] = BDL + DD + DH, - [0x67] = BDL + DH + LU, - [0x68] = BDL + DU + LH, - [0x69] = BDL + DH + DU, - [0x6a] = BDL + DH + LV, - [0x6b] = BDL + DV + LH, - [0x6c] = BDL + DH + DV, - - /* (light) arcs */ - [0x6d] = BDA + LD + LR, - [0x6e] = BDA + LD + LL, - [0x6f] = BDA + LU + LL, - [0x70] = BDA + LU + LR, - - /* Lower (Down) X/8 block (data is 8 - X) */ - [0x81] = BBD + 7, [0x82] = BBD + 6, [0x83] = BBD + 5, [0x84] = BBD + 4, - [0x85] = BBD + 3, [0x86] = BBD + 2, [0x87] = BBD + 1, [0x88] = BBD + 0, - - /* Left X/8 block (data is X) */ - [0x89] = BBL + 7, [0x8a] = BBL + 6, [0x8b] = BBL + 5, [0x8c] = BBL + 4, - [0x8d] = BBL + 3, [0x8e] = BBL + 2, [0x8f] = BBL + 1, - - /* upper 1/2 (4/8), 1/8 block (X), right 1/2, 1/8 block (8-X) */ - [0x80] = BBU + 4, [0x94] = BBU + 1, - [0x90] = BBR + 4, [0x95] = BBR + 7, - - /* Quadrants */ - [0x96] = BBQ + BL, - [0x97] = BBQ + BR, - [0x98] = BBQ + TL, - [0x99] = BBQ + TL + BL + BR, - [0x9a] = BBQ + TL + BR, - [0x9b] = BBQ + TL + TR + BL, - [0x9c] = BBQ + TL + TR + BR, - [0x9d] = BBQ + TR, - [0x9e] = BBQ + BL + TR, - [0x9f] = BBQ + BL + TR + BR, - - /* Shades, data is an alpha value in 25% units (1/4, 1/2, 3/4) */ - [0x91] = BBS + 1, [0x92] = BBS + 2, [0x93] = BBS + 3, - - /* U+2504 - U+250B, U+254C - U+254F: unsupported (dashes) */ - /* U+2571 - U+2573: unsupported (diagonals) */ -}; @@ -2,19 +2,25 @@ /* * appearance -static char *font = "BigBlueTerm437NerdFontMono-Regular:pixelsize=12:autohint=true"; -static char *font2[] = { "BigBlueTerm437NerdFontMono-Regular:pixelsize=12:autohint=true" }; * * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html */ +static char *font = "Terminess Nerd Font Mono:pixelsize=14:antialias=true:autohint=true"; -static char *font = "Bm437 IBM VGA 8x16:pixelsize=16"; -static char *font2[] = { "Bm437 IBM VGA 8x16:pixelsize=16" }; -static int borderpx = 0; - +/* disable bold, italic and roman fonts globally */ int disablebold = 1; int disableitalic = 1; int disableroman = 1; + +static int borderpx = 0; + +/* How to align the content in the window when the size of the terminal + * doesn't perfectly match the size of the window. The values are percentages. + * 50 means center, 0 means flush left/top, 100 means flush right/bottom. + */ +static int anysize_halign = 50; +static int anysize_valign = 50; + /* * What program is execed by st depends of these precedence rules: * 1: program passed with -e @@ -23,14 +29,15 @@ int disableroman = 1; * 4: value of shell in /etc/passwd * 5: value of shell in config.h */ -static char *shell = "/bin/zsh"; +static char *shell = "/bin/sh"; char *utmp = NULL; /* scroll program: to enable use a string like "scroll" */ char *scroll = NULL; char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; /* identification sequence returned in DA and DECID */ -char *vtiden = "\033[?6c"; +/* By default, use the same one as kitty. */ +char *vtiden = "\033[?62c"; /* Kerning / character bounding-box multipliers */ static float cwscale = 1.0; @@ -60,14 +67,14 @@ int allowwindowops = 0; * near minlatency, but it waits longer for slow updates to avoid partial draw. * low minlatency will tear/flicker more, as it can "detect" idle too early. */ -static double minlatency = 8; +static double minlatency = 2; static double maxlatency = 33; /* * blinking timeout (set to 0 to disable blinking) for the terminal blinking * attribute. */ -static unsigned int blinktimeout = 1; +static unsigned int blinktimeout = 800; /* * thickness of underline and bar cursors @@ -75,18 +82,6 @@ static unsigned int blinktimeout = 1; static unsigned int cursorthickness = 2; /* - * 1: render most of the lines/blocks characters without using the font for - * perfect alignment between cells (U2500 - U259F except dashes/diagonals). - * Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored. - * 0: disable (render all U25XX glyphs normally from the font). - */ -const int boxdraw = 1; -const int boxdraw_bold = 1; - -/* braille (U28XX): 1: render as adjacent "pixels", 0: use font */ -const int boxdraw_braille = 0; - -/* * bell volume. It must be a value between -100 and 100. Use 0 for disabling * it */ @@ -114,33 +109,39 @@ unsigned int tabspaces = 8; /* bg opacity */ float alpha = 0.8; -float alphaOffset = 0; -float alphaUnfocus = 0; + +/* Background opacity */ +float alpha_def; /* Terminal colors (16 first used in escape sequence) */ static const char *colorname[] = { - "#282a36", /* hard contrast: #1d2021 / soft contrast: #32302f */ - "#44475a", - "#f44747", - "#ff5555", - "#50fa7b", - "#69ff94", - "#f1fa8c", - "#f4f99d", - "#6272a4", - "#8be9fd", - "#ff79c6", - "#ff92d0", - "#8be9fd", - "#a4ffff", - "#f8f8f2", - "#ffffff", + /* 8 normal colors */ + "black", + "red3", + "green3", + "yellow3", + "blue2", + "magenta3", + "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", + "red", + "green", + "yellow", + "#5c5cff", + "magenta", + "cyan", + "white", + [255] = 0, + /* more colors can be added after 255 to use with DefaultXX */ - "#ffffff", /* 256 -> cursor */ - "#555555", /* 257 -> rev cursor*/ - "#000000", /* 258 -> bg */ - "#ffffff", /* 259 -> fg */ + "#cccccc", + "#555555", + "gray90", /* default foreground colour */ + "black", /* default background colour */ }; @@ -148,11 +149,10 @@ static const char *colorname[] = { * Default colors (colorname index) * foreground, background, cursor, reverse cursor */ -unsigned int defaultfg = 259; -unsigned int defaultbg = 258; +unsigned int defaultfg = 258; +unsigned int defaultbg = 259; unsigned int defaultcs = 256; -unsigned int defaultrcs = 257; -unsigned int background = 258; +static unsigned int defaultrcs = 257; /* * Default shape of cursor @@ -184,50 +184,37 @@ static unsigned int mousebg = 0; static unsigned int defaultattr = 11; /* + * Graphics configuration + */ + +/// The template for the cache directory. +const char graphics_cache_dir_template[] = "/tmp/st-images-XXXXXX"; +/// The max size of a single image file, in bytes. +unsigned graphics_max_single_image_file_size = 20 * 1024 * 1024; +/// The max size of the cache, in bytes. +unsigned graphics_total_file_cache_size = 300 * 1024 * 1024; +/// The max ram size of an image or placement, in bytes. +unsigned graphics_max_single_image_ram_size = 100 * 1024 * 1024; +/// The max total size of all images loaded into RAM. +unsigned graphics_max_total_ram_size = 300 * 1024 * 1024; +/// The max total number of image placements and images. +unsigned graphics_max_total_placements = 4096; +/// The ratio by which limits can be exceeded. This is to reduce the frequency +/// of image removal. +double graphics_excess_tolerance_ratio = 0.05; +/// The minimum delay between redraws caused by animations, in milliseconds. +unsigned graphics_animation_min_delay = 20; + +/* * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). * Note that if you want to use ShiftMask with selmasks, set this to an other * modifier, set to 0 to not use it. */ static uint forcemousemod = ShiftMask; -/* - * Xresources preferences to load at startup - */ -ResourcePref resources[] = { - { "font", STRING, &font }, - { "fontalt0", STRING, &font2[0] }, - { "color0", STRING, &colorname[0] }, - { "color1", STRING, &colorname[1] }, - { "color2", STRING, &colorname[2] }, - { "color3", STRING, &colorname[3] }, - { "color4", STRING, &colorname[4] }, - { "color5", STRING, &colorname[5] }, - { "color6", STRING, &colorname[6] }, - { "color7", STRING, &colorname[7] }, - { "color8", STRING, &colorname[8] }, - { "color9", STRING, &colorname[9] }, - { "color10", STRING, &colorname[10] }, - { "color11", STRING, &colorname[11] }, - { "color12", STRING, &colorname[12] }, - { "color13", STRING, &colorname[13] }, - { "color14", STRING, &colorname[14] }, - { "color15", STRING, &colorname[15] }, - { "background", STRING, &colorname[258] }, - { "foreground", STRING, &colorname[259] }, - { "cursorColor", STRING, &colorname[256] }, - { "termname", STRING, &termname }, - { "shell", STRING, &shell }, - { "minlatency", INTEGER, &minlatency }, - { "maxlatency", INTEGER, &maxlatency }, - { "blinktimeout", INTEGER, &blinktimeout }, - { "bellvolume", INTEGER, &bellvolume }, - { "tabspaces", INTEGER, &tabspaces }, - { "borderpx", INTEGER, &borderpx }, - { "cwscale", FLOAT, &cwscale }, - { "chscale", FLOAT, &chscale }, - { "alpha", FLOAT, &alpha }, - { "alphaOffset", FLOAT, &alphaOffset }, -}; +/* Internal keyboard shortcuts. */ +#define MODKEY Mod1Mask +#define TERMMOD (Mod1Mask|ShiftMask) /* * Internal mouse shortcuts. @@ -235,8 +222,8 @@ ResourcePref resources[] = { */ static MouseShortcut mshortcuts[] = { /* mask button function argument release */ - { XK_NO_MOD, Button4, kscrollup, {.i = 1} }, - { XK_NO_MOD, Button5, kscrolldown, {.i = 1} }, + { TERMMOD, Button3, previewimage, {.s = "feh"} }, + { TERMMOD, Button2, showimageinfo, {}, 1 }, { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, @@ -244,14 +231,6 @@ static MouseShortcut mshortcuts[] = { { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, }; -/* Internal keyboard shortcuts. */ -#define MODKEY Mod1Mask -#define TERMMOD (Mod1Mask|ShiftMask) - -static char *openurlcmd[] = { "/bin/sh", "-c", "st-urlhandler -o", "externalpipe", NULL }; -static char *copyurlcmd[] = { "/bin/sh", "-c", "st-urlhandler -c", "externalpipe", NULL }; -static char *copyoutput[] = { "/bin/sh", "-c", "st-copyout", "externalpipe", NULL }; - static Shortcut shortcuts[] = { /* mask keysym function argument */ { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, @@ -261,34 +240,25 @@ static Shortcut shortcuts[] = { { TERMMOD, XK_Prior, zoom, {.f = +1} }, { TERMMOD, XK_Next, zoom, {.f = -1} }, { TERMMOD, XK_Home, zoomreset, {.f = 0} }, - { TERMMOD, XK_C, clipcopy, {.i = 0} }, - { TERMMOD, XK_V, clippaste, {.i = 0} }, { MODKEY, XK_c, clipcopy, {.i = 0} }, - { ShiftMask, XK_Insert, clippaste, {.i = 0} }, { MODKEY, XK_v, clippaste, {.i = 0} }, + { ShiftMask, XK_Insert, clippaste, {.i = 0} }, { ShiftMask, XK_Insert, selpaste, {.i = 0} }, { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, - { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, - { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, - { MODKEY, XK_Page_Up, kscrollup, {.i = -1} }, - { MODKEY, XK_Page_Down, kscrolldown, {.i = -1} }, + { MODKEY, XK_k, kscrollup, {.i = 1} }, - { MODKEY, XK_j, kscrolldown, {.i = 1} }, - { MODKEY, XK_Up, kscrollup, {.i = 1} }, - { MODKEY, XK_Down, kscrolldown, {.i = 1} }, + { MODKEY, XK_j, kscrolldown, {.i = 1} }, { MODKEY, XK_u, kscrollup, {.i = -1} }, - { MODKEY, XK_d, kscrolldown, {.i = -1} }, - { MODKEY, XK_a, changealpha, {.f = -0.05} }, - { MODKEY, XK_s, changealpha, {.f = +0.05} }, - { TERMMOD, XK_Up, zoom, {.f = +1} }, - { TERMMOD, XK_Down, zoom, {.f = -1} }, - { TERMMOD, XK_K, zoom, {.f = +1} }, - { TERMMOD, XK_J, zoom, {.f = -1} }, - { TERMMOD, XK_U, zoom, {.f = +2} }, - { TERMMOD, XK_D, zoom, {.f = -2} }, - { MODKEY, XK_l, externalpipe, {.v = openurlcmd } }, - { MODKEY, XK_y, externalpipe, {.v = copyurlcmd } }, - { MODKEY, XK_o, externalpipe, {.v = copyoutput } }, + { MODKEY, XK_d, kscrolldown, {.i = -1} }, + + { TERMMOD, XK_F1, togglegrdebug, {.i = 0} }, + { TERMMOD, XK_F6, dumpgrstate, {.i = 0} }, + { TERMMOD, XK_F7, unloadimages, {.i = 0} }, + { TERMMOD, XK_F8, toggleimages, {.i = 0} }, + + { MODKEY, XK_s, chgalpha, {.f = -1} }, /* Decrease opacity */ + { MODKEY, XK_a, chgalpha, {.f = +1} }, /* Increase opacity */ + { MODKEY|ShiftMask, XK_s, chgalpha, {.f = 0} }, /* Reset opacity */ }; /* @@ -560,4 +530,3 @@ static char ascii_printable[] = " !\"#$%&'()*+,-./0123456789:;<=>?" "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" "`abcdefghijklmnopqrstuvwxyz{|}~"; - @@ -1,5 +1,5 @@ # st version -VERSION = 0.8.5 +VERSION = 0.9.2 # Customize below to fit your system @@ -14,13 +14,14 @@ PKG_CONFIG = pkg-config # includes and libs INCS = -I$(X11INC) \ + `$(PKG_CONFIG) --cflags imlib2` \ `$(PKG_CONFIG) --cflags fontconfig` \ - `$(PKG_CONFIG) --cflags freetype2` \ - `$(PKG_CONFIG) --cflags harfbuzz` -LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ + `$(PKG_CONFIG) --cflags freetype2` +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender \ + `$(PKG_CONFIG) --libs imlib2` \ + `$(PKG_CONFIG) --libs zlib` \ `$(PKG_CONFIG) --libs fontconfig` \ - `$(PKG_CONFIG) --libs freetype2` \ - `$(PKG_CONFIG) --libs harfbuzz` + `$(PKG_CONFIG) --libs freetype2` # flags STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 @@ -32,6 +33,7 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ # `$(PKG_CONFIG) --libs fontconfig` \ # `$(PKG_CONFIG) --libs freetype2` +#MANPREFIX = ${PREFIX}/man # compiler and linker # CC = c99 diff --git a/disablebold.diff b/disablebold.diff deleted file mode 100644 index 6083276..0000000 --- a/disablebold.diff +++ /dev/null @@ -1,69 +0,0 @@ -From 1e932656e6ca3a50ec67cafabdb08d711635c504 Mon Sep 17 00:00:00 2001 -From: Alex Kozadaev <snobb@gmx.com> -Date: Fri, 24 Mar 2017 12:11:47 +0000 -Subject: [PATCH] disable bold, italic and roman fonts globally - ---- - config.def.h | 6 ++++++ - x.c | 14 +++++++++++--- - 2 files changed, 17 insertions(+), 3 deletions(-) - -diff --git a/config.def.h b/config.def.h -index 877afab..87c4534 100644 ---- a/config.def.h -+++ b/config.def.h -@@ -6,6 +6,12 @@ - * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html - */ - char font[] = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; -+ -+/* disable bold, italic and roman fonts globally */ -+int disablebold = 0; -+int disableitalic = 0; -+int disableroman = 0; -+ - int borderpx = 2; - - /* -diff --git a/x.c b/x.c -index 743b084..23e4f0a 100644 ---- a/x.c -+++ b/x.c -@@ -158,6 +158,11 @@ typedef struct { - static Fontcache frc[16]; - static int frclen = 0; - -+/* declared in config.h */ -+extern int disablebold; -+extern int disableitalic; -+extern int disableroman; -+ - void - getbuttoninfo(XEvent *e) - { -@@ -828,17 +833,20 @@ xloadfonts(char *fontstr, double fontsize) - win.ch = ceilf(dc.font.height * chscale); - - FcPatternDel(pattern, FC_SLANT); -- FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); -+ if (!disableitalic) -+ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); - if (xloadfont(&dc.ifont, pattern)) - die("st: can't open font %s\n", fontstr); - - FcPatternDel(pattern, FC_WEIGHT); -- FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); -+ if (!disablebold) -+ FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); - if (xloadfont(&dc.ibfont, pattern)) - die("st: can't open font %s\n", fontstr); - - FcPatternDel(pattern, FC_SLANT); -- FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); -+ if (!disableroman) -+ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); - if (xloadfont(&dc.bfont, pattern)) - die("st: can't open font %s\n", fontstr); - --- -2.1.4 @@ -1,154 +0,0 @@ -#include <stdlib.h> -#include <stdio.h> -#include <math.h> -#include <X11/Xft/Xft.h> -#include <X11/cursorfont.h> -#include <hb.h> -#include <hb-ft.h> - -#include "st.h" - -#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END } - -/* - * Replace 0 with a list of font features, wrapped in FEATURE macro, e.g. - * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') - * - * Uncomment either one of the 2 lines below. Uncomment the prior to disable (any) font features. Uncomment the - * latter to enable the (selected) font features. - */ - -hb_feature_t features[] = { 0 }; -//hb_feature_t features[] = { FEATURE('s','s','0','1'), FEATURE('s','s','0','2'), FEATURE('s','s','0','3'), FEATURE('s','s','0','5'), FEATURE('s','s','0','6'), FEATURE('s','s','0','7'), FEATURE('s','s','0','8'), FEATURE('z','e','r','o') }; - -void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length); -hb_font_t *hbfindfont(XftFont *match); - -typedef struct { - XftFont *match; - hb_font_t *font; -} HbFontMatch; - -static int hbfontslen = 0; -static HbFontMatch *hbfontcache = NULL; - -void -hbunloadfonts() -{ - for (int i = 0; i < hbfontslen; i++) { - hb_font_destroy(hbfontcache[i].font); - XftUnlockFace(hbfontcache[i].match); - } - - if (hbfontcache != NULL) { - free(hbfontcache); - hbfontcache = NULL; - } - hbfontslen = 0; -} - -hb_font_t * -hbfindfont(XftFont *match) -{ - for (int i = 0; i < hbfontslen; i++) { - if (hbfontcache[i].match == match) - return hbfontcache[i].font; - } - - /* Font not found in cache, caching it now. */ - hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 1)); - FT_Face face = XftLockFace(match); - hb_font_t *font = hb_ft_font_create(face, NULL); - if (font == NULL) - die("Failed to load Harfbuzz font."); - - hbfontcache[hbfontslen].match = match; - hbfontcache[hbfontslen].font = font; - hbfontslen += 1; - - return font; -} - -void -hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, int x, int y) -{ - int start = 0, length = 1, gstart = 0; - hb_codepoint_t *codepoints = calloc((unsigned int)len, sizeof(hb_codepoint_t)); - - for (int idx = 1, specidx = 1; idx < len; idx++) { - if (glyphs[idx].mode & ATTR_WDUMMY) { - length += 1; - continue; - } - - if (specs[specidx].font != specs[start].font || ATTRCMP(glyphs[gstart], glyphs[idx]) || selected(x + idx, y) != selected(x + gstart, y)) { - hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length); - - /* Reset the sequence. */ - length = 1; - start = specidx; - gstart = idx; - } else { - length += 1; - } - - specidx++; - } - - /* EOL. */ - hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length); - - /* Apply the transformation to glyph specs. */ - for (int i = 0, specidx = 0; i < len; i++) { - if (glyphs[i].mode & ATTR_WDUMMY) - continue; - if (glyphs[i].mode & ATTR_BOXDRAW) { - specidx++; - continue; - } - - if (codepoints[i] != specs[specidx].glyph) - ((Glyph *)glyphs)[i].mode |= ATTR_LIGA; - - specs[specidx++].glyph = codepoints[i]; - } - - free(codepoints); -} - -void -hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length) -{ - hb_font_t *font = hbfindfont(xfont); - if (font == NULL) - return; - - Rune rune; - ushort mode = USHRT_MAX; - hb_buffer_t *buffer = hb_buffer_create(); - hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); - - /* Fill buffer with codepoints. */ - for (int i = start; i < (start+length); i++) { - rune = string[i].u; - mode = string[i].mode; - if (mode & ATTR_WDUMMY) - rune = 0x0020; - hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1); - } - - /* Shape the segment. */ - hb_shape(font, buffer, features, sizeof(features)); - - /* Get new glyph info. */ - hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL); - - /* Write new codepoints. */ - for (int i = 0; i < length; i++) { - hb_codepoint_t gid = info[i].codepoint; - codepoints[start+i] = gid; - } - - /* Cleanup. */ - hb_buffer_destroy(buffer); -} @@ -1,7 +0,0 @@ -#include <X11/Xft/Xft.h> -#include <hb.h> -#include <hb-ft.h> - -void hbunloadfonts(); -void hbtransform(XftGlyphFontSpec *, const Glyph *, size_t, int, int); - diff --git a/st-copyout b/st-copyout deleted file mode 100644 index 0d19e5a..0000000 --- a/st-copyout +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -# Using external pipe with st, give a dmenu prompt of recent commands, -# allowing the user to copy the output of one. -# xclip required for this script. -# By Jaywalker and Luke -tmpfile=$(mktemp /tmp/st-cmd-output.XXXXXX) -trap 'rm "$tmpfile"' 0 1 15 -sed -n "w $tmpfile" -sed -i 's/\x0//g' "$tmpfile" -ps1="$(grep "\S" "$tmpfile" | tail -n 1 | sed 's/^\s*//' | cut -d' ' -f1)" -chosen="$(grep -F "$ps1" "$tmpfile" | sed '$ d' | tac | dmenu -p "Copy which command's output?" -i -l 10 | sed 's/[^^]/[&]/g; s/\^/\\^/g')" -eps1="$(echo "$ps1" | sed 's/[^^]/[&]/g; s/\^/\\^/g')" -awk "/^$chosen$/{p=1;print;next} p&&/$eps1/{p=0};p" "$tmpfile" | xclip -selection clipboard diff --git a/st-urlhandler b/st-urlhandler deleted file mode 100644 index 0eb4586..0000000 --- a/st-urlhandler +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -urlregex="(((http|https|gopher|gemini|ftp|ftps|git)://|www\\.)[a-zA-Z0-9.]*[:;a-zA-Z0-9./+@$&%?$\#=_~-]*)|((magnet:\\?xt=urn:btih:)[a-zA-Z0-9]*)" - -urls="$(sed 's/.*│//g' | tr -d '\n' | # First remove linebreaks and mutt sidebars: - grep -aEo "$urlregex" | # grep only urls as defined above. - uniq | # Ignore neighboring duplicates. - sed "s/\(\.\|,\|;\|\!\\|\?\)$//; - s/^www./http:\/\/www\./")" # xdg-open will not detect url without http - -[ -z "$urls" ] && exit 1 - -while getopts "hoc" o; do case "${o}" in - h) printf "Optional arguments for custom use:\\n -c: copy\\n -o: xdg-open\\n -h: Show this message\\n" && exit 1 ;; - o) chosen="$(echo "$urls" | dmenu -i -p 'Follow which url?' -l 10)" - setsid xdg-open "$chosen" >/dev/null 2>&1 & ;; - c) echo "$urls" | dmenu -i -p 'Copy which url?' -l 10 | tr -d '\n' | xclip -selection clipboard ;; - *) printf "Invalid option: -%s\\n" "$OPTARG" && exit 1 ;; -esac done @@ -125,41 +125,6 @@ and all the remaining arguments are used as a command even without it. .SH SHORTCUTS .TP -.B Alt-j/k or Alt-Up/Down or Alt-Mouse Wheel -Scroll up/down one line at a time. -.TP -.B Alt-u/d or Alt-Page Up/Page Down -Scroll up/down one screen at a time. -.TP -.B Alt-Shift-k/j or Alt-Shift-Page Up/Page Down or Alt-Shift-Mouse Wheel -Increase or decrease font size. -.TP -.B Alt-Home -Reset to default font size. -.TP -.B Shift-Insert or Alt-v -Paste from clipboard. -.TP -.B Alt-c -Copy to clipboard. -.TP -.B Alt-p -Paste/input primary selection. -.TP -.B Alt-l -Show dmenu menu of all URLs on screen and choose one to open. -.TP -.B Alt-y -Show dmenu menu of all URLs on screen and choose one to copy. -.TP -.B Alt-o -Show dmenu menu of all recently run commands and copy the output of the chosen command to the clipboard. -.I xclip -required. -.TP -.B Alt-a/s -Increase or decrease opacity/alpha value (make window more or less transparent). -.TP .B Break Send a break in the serial line. Break key is obtained in PC keyboards @@ -176,6 +141,24 @@ Print the full screen to the .B Print Screen Print the selection to the .I iofile. +.TP +.B Ctrl-Shift-Page Up +Increase font size. +.TP +.B Ctrl-Shift-Page Down +Decrease font size. +.TP +.B Ctrl-Shift-Home +Reset to default font size. +.TP +.B Ctrl-Shift-y +Paste from primary selection (middle mouse button). +.TP +.B Ctrl-Shift-c +Copy the selected text to the clipboard selection. +.TP +.B Ctrl-Shift-v +Paste from the clipboard selection. .SH CUSTOMIZATION .B st can be customized by creating a custom config.h and (re)compiling the source @@ -191,3 +174,4 @@ See the LICENSE file for the terms of redistribution. .BR scroll (1) .SH BUGS See the TODO file in the distribution. + @@ -19,6 +19,7 @@ #include "st.h" #include "win.h" +#include "graphics.h" #if defined(__linux) #include <pty.h> @@ -37,6 +38,10 @@ #define STR_ARG_SIZ ESC_ARG_SIZ #define HISTSIZE 2000 +/* PUA character used as an image placeholder */ +#define IMAGE_PLACEHOLDER_CHAR 0x10EEEE +#define IMAGE_PLACEHOLDER_CHAR_OLD 0xEEEE + /* macros */ #define IS_SET(flag) ((term.mode & (flag)) != 0) #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) @@ -44,9 +49,8 @@ #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) #define ISDELIM(u) (u && wcschr(worddelimiters, u)) #define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ - term.scr + HISTSIZE + 1) % HISTSIZE] : \ - term.line[(y) - term.scr]) -#define TLINE_HIST(y) ((y) <= HISTSIZE-term.row+2 ? term.hist[(y)] : term.line[(y-HISTSIZE+term.row-3)]) + term.scr + HISTSIZE + 1) % HISTSIZE] : \ + term.line[(y) - term.scr]) enum term_mode { MODE_WRAP = 1 << 0, @@ -118,7 +122,8 @@ typedef struct { typedef struct { int row; /* nb row */ int col; /* nb col */ - int maxcol; + int pixw; /* width of the text area in pixels */ + int pixh; /* height of the text area in pixels */ Line *line; /* screen */ Line *alt; /* alternate screen */ Line hist[HISTSIZE]; /* history buffer */ @@ -203,6 +208,7 @@ static void tsetscroll(int, int); static void tswapscreen(void); static void tsetmode(int, int, const int *, int); static int twrite(const char *, int, int); +static void tfulldirt(void); static void tcontrolcode(uchar ); static void tdectest(char ); static void tdefutf8(char); @@ -221,7 +227,6 @@ static Rune utf8decodebyte(char, size_t *); static char utf8encodebyte(Rune, size_t); static size_t utf8validate(Rune *, size_t); -static char *base64dec(const char *); static char base64dec_getc(const char **); static ssize_t xwrite(int, const char *, size_t); @@ -240,6 +245,10 @@ static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; +/* Converts a diacritic to a row/column/etc number. The result is 1-base, 0 + * means "couldn't convert". Defined in rowcolumn_diacritics_helpers.c */ +uint16_t diacritic_to_num(uint32_t code); + ssize_t xwrite(int fd, const char *s, size_t len) { @@ -280,8 +289,6 @@ xrealloc(void *p, size_t len) char * xstrdup(const char *s) { - if ((s = strdup(s)) == NULL) - die("strdup: %s\n", strerror(errno)); char *p; if ((p = strdup(s)) == NULL) @@ -428,20 +435,6 @@ tlinelen(int y) return i; } -int -tlinehistlen(int y) -{ - int i = term.col; - - if (TLINE_HIST(y)[i - 1].mode & ATTR_WRAP) - return i; - - while (i > 0 && TLINE_HIST(y)[i - 1].u == ' ') - --i; - - return i; -} - void selstart(int col, int row, int snap) { @@ -488,7 +481,6 @@ selextend(int col, int row, int type, int done) sel.mode = done ? SEL_IDLE : SEL_READY; } - void selnormalize(void) { @@ -641,6 +633,12 @@ getsel(void) if (gp->mode & ATTR_WDUMMY) continue; + if (gp->mode & ATTR_IMAGE) { + // TODO: Copy diacritics as well + ptr += utf8encode(IMAGE_PLACEHOLDER_CHAR, ptr); + continue; + } + ptr += utf8encode(gp->u, ptr); } @@ -844,7 +842,11 @@ ttyread(void) { static char buf[BUFSIZ]; static int buflen = 0; - int ret, written; + static int already_processing = 0; + int ret, written = 0; + + if (buflen >= LEN(buf)) + return 0; /* append read bytes to unprocessed bytes */ ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); @@ -856,7 +858,24 @@ ttyread(void) die("couldn't read from shell: %s\n", strerror(errno)); default: buflen += ret; - written = twrite(buf, buflen, 0); + if (already_processing) { + /* Avoid recursive call to twrite() */ + return ret; + } + already_processing = 1; + while (1) { + int buflen_before_processing = buflen; + written += twrite(buf + written, buflen - written, 0); + // If buflen changed during the call to twrite, there is + // new data, and we need to keep processing, otherwise + // we can exit. This will not loop forever because the + // buffer is limited, and we don't clean it in this + // loop, so at some point ttywrite will have to drop + // some data. + if (buflen_before_processing == buflen) + break; + } + already_processing = 0; buflen -= written; /* keep any incomplete UTF-8 byte sequence for the next call */ if (buflen > 0) @@ -902,6 +921,7 @@ ttywriteraw(const char *s, size_t n) fd_set wfd, rfd; ssize_t r; size_t lim = 256; + int retries_left = 100; /* * Remember that we are using a pty, which might be a modem line. @@ -910,6 +930,9 @@ ttywriteraw(const char *s, size_t n) * FIXME: Migrate the world to Plan 9. */ while (n > 0) { + if (retries_left-- <= 0) + goto too_many_retries; + FD_ZERO(&wfd); FD_ZERO(&rfd); FD_SET(cmdfd, &wfd); @@ -951,11 +974,16 @@ ttywriteraw(const char *s, size_t n) write_error: die("write error on tty: %s\n", strerror(errno)); +too_many_retries: + fprintf(stderr, "Could not write %zu bytes to tty\n", n); } void ttyresize(int tw, int th) { + term.pixw = tw; + term.pixh = th; + struct winsize w; w.ws_row = term.row; @@ -1043,7 +1071,8 @@ treset(void) term.c = (TCursor){{ .mode = ATTR_NULL, .fg = defaultfg, - .bg = defaultbg + .bg = defaultbg, + .decor = DECOR_DEFAULT_COLOR }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; memset(term.tabs, 0, term.col * sizeof(*term.tabs)); @@ -1066,7 +1095,9 @@ treset(void) void tnew(int col, int row) { - term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; + term = (Term){.c = {.attr = {.fg = defaultfg, + .bg = defaultbg, + .decor = DECOR_DEFAULT_COLOR}}}; tresize(col, row); treset(); } @@ -1177,7 +1208,7 @@ tscrollup(int orig, int n, int copyhist) void selscroll(int orig, int n) { - if (sel.ob.x == -1) + if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN)) return; if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { @@ -1212,6 +1243,7 @@ csiparse(void) { char *p = csiescseq.buf, *np; long int v; + int sep = ';'; /* colon or semi-colon, but not both */ csiescseq.narg = 0; if (*p == '?') { @@ -1229,7 +1261,9 @@ csiparse(void) v = -1; csiescseq.arg[csiescseq.narg++] = v; p = np; - if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + if (sep == ';' && *p == ':') + sep = ':'; /* allow override to colon once */ + if (*p != sep || csiescseq.narg == ESC_ARG_SIZ) break; p++; } @@ -1292,12 +1326,24 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) term.line[y][x-1].mode &= ~ATTR_WIDE; } + if (u == ' ' && term.line[y][x].mode & ATTR_IMAGE && + tgetisclassicplaceholder(&term.line[y][x])) { + // This is a workaround: don't overwrite classic placement + // placeholders with space symbols (unlike Unicode placeholders + // which must be overwritten by anything). + term.line[y][x].bg = attr->bg; + term.dirty[y] = 1; + return; + } + term.dirty[y] = 1; term.line[y][x] = *attr; term.line[y][x].u = u; - if (isboxdraw(u)) - term.line[y][x].mode |= ATTR_BOXDRAW; + if (u == IMAGE_PLACEHOLDER_CHAR || u == IMAGE_PLACEHOLDER_CHAR_OLD) { + term.line[y][x].u = 0; + term.line[y][x].mode |= ATTR_IMAGE; + } } void @@ -1311,8 +1357,8 @@ tclearregion(int x1, int y1, int x2, int y2) if (y1 > y2) temp = y1, y1 = y2, y2 = temp; - LIMIT(x1, 0, term.maxcol-1); - LIMIT(x2, 0, term.maxcol-1); + LIMIT(x1, 0, term.col-1); + LIMIT(x2, 0, term.col-1); LIMIT(y1, 0, term.row-1); LIMIT(y2, 0, term.row-1); @@ -1324,12 +1370,104 @@ tclearregion(int x1, int y1, int x2, int y2) selclear(); gp->fg = term.c.attr.fg; gp->bg = term.c.attr.bg; + gp->decor = term.c.attr.decor; gp->mode = 0; gp->u = ' '; } } } +/// Fills a rectangle area with an image placeholder. The starting point is the +/// cursor. Adds empty lines if needed. The placeholder will be marked as +/// classic. +void +tcreateimgplaceholder(uint32_t image_id, uint32_t placement_id, + int cols, int rows, char do_not_move_cursor) +{ + for (int row = 0; row < rows; ++row) { + int y = term.c.y; + term.dirty[y] = 1; + for (int col = 0; col < cols; ++col) { + int x = term.c.x + col; + if (x >= term.col) + break; + Glyph *gp = &term.line[y][x]; + if (selected(x, y)) + selclear(); + gp->mode = ATTR_IMAGE; + gp->u = 0; + tsetimgrow(gp, row + 1); + tsetimgcol(gp, col + 1); + tsetimgid(gp, image_id); + tsetimgplacementid(gp, placement_id); + tsetimgdiacriticcount(gp, 3); + tsetisclassicplaceholder(gp, 1); + } + // If moving the cursor is not allowed and this is the last line + // of the terminal, we are done. + if (do_not_move_cursor && y == term.row - 1) + break; + // Move the cursor down, maybe creating a new line. The x is + // preserved (we never change term.c.x in the loop above). + if (row != rows - 1) + tnewline(/*first_col=*/0); + } + if (do_not_move_cursor) { + // Return the cursor to the original position. + tmoveto(term.c.x, term.c.y - rows + 1); + } else { + // Move the cursor beyond the last column, as required by the + // protocol. If the cursor goes beyond the screen edge, insert a + // newline to match the behavior of kitty. + if (term.c.x + cols >= term.col) + tnewline(/*first_col=*/1); + else + tmoveto(term.c.x + cols, term.c.y); + } +} + +void gr_for_each_image_cell(int (*callback)(void *data, uint32_t image_id, + uint32_t placement_id, int col, + int row, char is_classic), + void *data) { + for (int row = 0; row < term.row; ++row) { + for (int col = 0; col < term.col; ++col) { + Glyph *gp = &term.line[row][col]; + if (gp->mode & ATTR_IMAGE) { + uint32_t image_id = tgetimgid(gp); + uint32_t placement_id = tgetimgplacementid(gp); + int ret = + callback(data, tgetimgid(gp), + tgetimgplacementid(gp), + tgetimgcol(gp), tgetimgrow(gp), + tgetisclassicplaceholder(gp)); + if (ret == 1) { + term.dirty[row] = 1; + gp->mode = 0; + gp->u = ' '; + } + } + } + } +} + +void gr_schedule_image_redraw_by_id(uint32_t image_id) { + for (int row = 0; row < term.row; ++row) { + if (term.dirty[row]) + continue; + for (int col = 0; col < term.col; ++col) { + Glyph *gp = &term.line[row][col]; + if (gp->mode & ATTR_IMAGE) { + uint32_t cell_image_id = tgetimgid(gp); + if (cell_image_id == image_id) { + term.dirty[row] = 1; + break; + } + } + } + } +} + void tdeletechar(int n) { @@ -1448,6 +1586,7 @@ tsetattr(const int *attr, int l) ATTR_STRUCK ); term.c.attr.fg = defaultfg; term.c.attr.bg = defaultbg; + term.c.attr.decor = DECOR_DEFAULT_COLOR; break; case 1: term.c.attr.mode |= ATTR_BOLD; @@ -1460,6 +1599,20 @@ tsetattr(const int *attr, int l) break; case 4: term.c.attr.mode |= ATTR_UNDERLINE; + if (i + 1 < l) { + idx = attr[++i]; + if (BETWEEN(idx, 1, 5)) { + tsetdecorstyle(&term.c.attr, idx); + } else if (idx == 0) { + term.c.attr.mode &= ~ATTR_UNDERLINE; + tsetdecorstyle(&term.c.attr, 0); + } else { + fprintf(stderr, + "erresc: unknown underline " + "style %d\n", + idx); + } + } break; case 5: /* slow blink */ /* FALLTHROUGH */ @@ -1483,6 +1636,7 @@ tsetattr(const int *attr, int l) break; case 24: term.c.attr.mode &= ~ATTR_UNDERLINE; + tsetdecorstyle(&term.c.attr, 0); break; case 25: term.c.attr.mode &= ~ATTR_BLINK; @@ -1510,6 +1664,13 @@ tsetattr(const int *attr, int l) case 49: term.c.attr.bg = defaultbg; break; + case 58: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + tsetdecorcolor(&term.c.attr, idx); + break; + case 59: + tsetdecorcolor(&term.c.attr, DECOR_DEFAULT_COLOR); + break; default: if (BETWEEN(attr[i], 30, 37)) { term.c.attr.fg = attr[i] - 30; @@ -1726,7 +1887,7 @@ csihandle(void) ttywrite(vtiden, strlen(vtiden), 0); break; case 'b': /* REP -- if last char is printable print it <n> more times */ - DEFAULT(csiescseq.arg[0], 1); + LIMIT(csiescseq.arg[0], 1, 65535); if (term.lastc) while (csiescseq.arg[0]-- > 0) tputc(term.lastc); @@ -1785,7 +1946,7 @@ csihandle(void) } break; case 1: /* above */ - if (term.c.y > 1) + if (term.c.y > 0) tclearregion(0, 0, term.col-1, term.c.y-1); tclearregion(0, term.c.y, term.c.x, term.c.y); break; @@ -1811,6 +1972,7 @@ csihandle(void) } break; case 'S': /* SU -- Scroll <n> line up */ + if (csiescseq.priv) break; DEFAULT(csiescseq.arg[0], 1); tscrollup(term.top, csiescseq.arg[0], 0); break; @@ -1852,11 +2014,18 @@ csihandle(void) case 'm': /* SGR -- Terminal attribute (color) */ tsetattr(csiescseq.arg, csiescseq.narg); break; - case 'n': /* DSR – Device Status Report (cursor position) */ - if (csiescseq.arg[0] == 6) { + case 'n': /* DSR -- Device Status Report */ + switch (csiescseq.arg[0]) { + case 5: /* Status Report "OK" `0n` */ + ttywrite("\033[0n", sizeof("\033[0n") - 1, 0); + break; + case 6: /* Report Cursor Position (CPR) "<row>;<column>R" */ len = snprintf(buf, sizeof(buf), "\033[%i;%iR", - term.c.y+1, term.c.x+1); + term.c.y+1, term.c.x+1); ttywrite(buf, len, 0); + break; + default: + goto unknown; } break; case 'r': /* DECSTBM -- Set Scrolling Region */ @@ -1873,7 +2042,11 @@ csihandle(void) tcursor(CURSOR_SAVE); break; case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ - tcursor(CURSOR_LOAD); + if (csiescseq.priv) { + goto unknown; + } else { + tcursor(CURSOR_LOAD); + } break; case ' ': switch (csiescseq.mode[1]) { @@ -1885,6 +2058,39 @@ csihandle(void) goto unknown; } break; + case '>': + switch (csiescseq.mode[1]) { + case 'q': /* XTVERSION -- Print terminal name and version */ + len = snprintf(buf, sizeof(buf), + "\033P>|st-graphics(%s)\033\\", VERSION); + ttywrite(buf, len, 0); + break; + default: + goto unknown; + } + break; + case 't': /* XTWINOPS -- Window manipulation */ + switch (csiescseq.arg[0]) { + case 14: /* Report text area size in pixels. */ + len = snprintf(buf, sizeof(buf), "\033[4;%i;%it", + term.pixh, term.pixw); + ttywrite(buf, len, 0); + break; + case 16: /* Report character cell size in pixels. */ + len = snprintf(buf, sizeof(buf), "\033[6;%i;%it", + term.pixh / term.row, + term.pixw / term.col); + ttywrite(buf, len, 0); + break; + case 18: /* Report the size of the text area in characters. */ + len = snprintf(buf, sizeof(buf), "\033[8;%i;%it", + term.row, term.col); + ttywrite(buf, len, 0); + break; + default: + goto unknown; + } + break; } } @@ -2015,8 +2221,10 @@ strhandle(void) if (p && !strcmp(p, "?")) { osc_color_response(j, 0, 1); } else if (xsetcolorname(j, p)) { - if (par == 104 && narg <= 1) + if (par == 104 && narg <= 1) { + xloadcols(); return; /* color reset without parameter */ + } fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", j, p ? p : "(null)"); } else { @@ -2032,8 +2240,26 @@ strhandle(void) case 'k': /* old title set compatibility */ xsettitle(strescseq.args[0]); return; - case 'P': /* DCS -- Device Control String */ case '_': /* APC -- Application Program Command */ + if (gr_parse_command(strescseq.buf, strescseq.len)) { + GraphicsCommandResult *res = &graphics_command_result; + if (res->create_placeholder) { + tcreateimgplaceholder( + res->placeholder.image_id, + res->placeholder.placement_id, + res->placeholder.columns, + res->placeholder.rows, + res->placeholder.do_not_move_cursor); + } + if (res->response[0]) + ttywrite(res->response, strlen(res->response), + 0); + if (res->redraw) + tfulldirt(); + return; + } + return; + case 'P': /* DCS -- Device Control String */ case '^': /* PM -- Privacy Message */ return; } @@ -2065,61 +2291,6 @@ strparse(void) } void -externalpipe(const Arg *arg) -{ - int to[2]; - char buf[UTF_SIZ]; - void (*oldsigpipe)(int); - Glyph *bp, *end; - int lastpos, n, newline; - - if (pipe(to) == -1) - return; - - switch (fork()) { - case -1: - close(to[0]); - close(to[1]); - return; - case 0: - dup2(to[0], STDIN_FILENO); - close(to[0]); - close(to[1]); - execvp(((char **)arg->v)[0], (char **)arg->v); - fprintf(stderr, "st: execvp %s\n", ((char **)arg->v)[0]); - perror("failed"); - exit(0); - } - - close(to[0]); - /* ignore sigpipe for now, in case child exists early */ - oldsigpipe = signal(SIGPIPE, SIG_IGN); - newline = 0; - for (n = 0; n <= HISTSIZE + 2; n++) { - bp = TLINE_HIST(n); - lastpos = MIN(tlinehistlen(n) + 1, term.col) - 1; - if (lastpos < 0) - break; - if (lastpos == 0) - continue; - end = &bp[lastpos + 1]; - for (; bp < end; ++bp) - if (xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0) - break; - if ((newline = TLINE_HIST(n)[lastpos].mode & ATTR_WRAP)) - continue; - if (xwrite(to[1], "\n", 1) < 0) - break; - newline = 0; - } - if (newline) - (void)xwrite(to[1], "\n", 1); - close(to[1]); - /* restore */ - signal(SIGPIPE, oldsigpipe); -} - -void strdump(void) { size_t i; @@ -2459,6 +2630,7 @@ eschandle(uchar ascii) treset(); resettitle(); xloadcols(); + xsetmode(0, MODE_HIDE); break; case '=': /* DECPAM -- Application keypad */ xsetmode(1, MODE_APPKEYPAD); @@ -2551,6 +2723,9 @@ check_control_code: * they must not cause conflicts with sequences. */ if (control) { + /* in UTF-8 mode ignore handling C1 control characters */ + if (IS_SET(MODE_UTF8) && ISCONTROLC1(u)) + return; tcontrolcode(u); /* * control codes are not shown ever @@ -2590,6 +2765,33 @@ check_control_code: if (selected(term.c.x, term.c.y)) selclear(); + if (width == 0) { + // It's probably a combining char. Combining characters are not + // supported, so we just ignore them, unless it denotes the row and + // column of an image character. + if (term.c.y <= 0 && term.c.x <= 0) + return; + else if (term.c.x == 0) + gp = &term.line[term.c.y-1][term.col-1]; + else if (term.c.state & CURSOR_WRAPNEXT) + gp = &term.line[term.c.y][term.c.x]; + else + gp = &term.line[term.c.y][term.c.x-1]; + uint16_t num = diacritic_to_num(u); + if (num && (gp->mode & ATTR_IMAGE)) { + unsigned diaccount = tgetimgdiacriticcount(gp); + if (diaccount == 0) + tsetimgrow(gp, num); + else if (diaccount == 1) + tsetimgcol(gp, num); + else if (diaccount == 2) + tsetimg4thbyteplus1(gp, num); + tsetimgdiacriticcount(gp, diaccount + 1); + } + term.lastc = u; + return; + } + gp = &term.line[term.c.y][term.c.x]; if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { gp->mode |= ATTR_WRAP; @@ -2597,11 +2799,16 @@ check_control_code: gp = &term.line[term.c.y][term.c.x]; } - if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) { memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); + gp->mode &= ~ATTR_WIDE; + } if (term.c.x+width > term.col) { - tnewline(1); + if (IS_SET(MODE_WRAP)) + tnewline(1); + else + tmoveto(term.col - width, term.c.y); gp = &term.line[term.c.y][term.c.x]; } @@ -2662,18 +2869,11 @@ void tresize(int col, int row) { int i, j; - int tmp; - int minrow, mincol; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); int *bp; TCursor c; - tmp = col; - if (!term.maxcol) - term.maxcol = term.col; - col = MAX(col, term.maxcol); - minrow = MIN(row, term.row); - mincol = MIN(col, term.maxcol); - if (col < 1 || row < 1) { fprintf(stderr, "tresize: error resizing to %dx%d\n", col, row); @@ -2724,18 +2924,17 @@ tresize(int col, int row) term.line[i] = xmalloc(col * sizeof(Glyph)); term.alt[i] = xmalloc(col * sizeof(Glyph)); } - if (col > term.maxcol) { - bp = term.tabs + term.maxcol; + if (col > term.col) { + bp = term.tabs + term.col; - memset(bp, 0, sizeof(*term.tabs) * (col - term.maxcol)); + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); while (--bp > term.tabs && !*bp) /* nothing */ ; for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) *bp = 1; } /* update terminal size */ - term.col = tmp; - term.maxcol = col; + term.col = col; term.row = row; /* reset scrolling region */ tsetscroll(0, row-1); @@ -2767,6 +2966,8 @@ drawregion(int x1, int y1, int x2, int y2) { int y; + xstartimagedraw(term.dirty, term.row); + for (y = y1; y < y2; y++) { if (!term.dirty[y]) continue; @@ -2774,6 +2975,8 @@ drawregion(int x1, int y1, int x2, int y2) term.dirty[y] = 0; xdrawline(TLINE(y), x1, y, x2); } + + xfinishimagedraw(); } void @@ -2795,11 +2998,7 @@ draw(void) drawregion(0, 0, term.col, term.row); if (term.scr == 0) xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], - term.ocx, term.ocy, term.line[term.ocy][term.ocx], - term.line[term.ocy], term.col); - /* xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], */ - /* term.ocx, term.ocy, term.line[term.ocy][term.ocx], */ - /* term.line[term.ocy], term.col); */ + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); term.ocx = cx; term.ocy = term.c.y; xfinishdraw(); @@ -2814,3 +3013,8 @@ redraw(void) draw(); } +Glyph +getglyphat(int col, int row) +{ + return term.line[row][col]; +} @@ -11,9 +11,8 @@ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) #define DEFAULT(a, b) (a) = (a) ? (a) : (b) #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) -#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP) & (~ATTR_LIGA)) != ((b).mode & (~ATTR_WRAP) & (~ATTR_LIGA)) || \ - (a).fg != (b).fg || \ - (a).bg != (b).bg) +#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ + (a).bg != (b).bg || (a).decor != (b).decor) #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ (t1.tv_nsec-t2.tv_nsec)/1E6) #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) @@ -21,6 +20,10 @@ #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) #define IS_TRUECOL(x) (1 << 24 & (x)) +// This decor color indicates that the fg color should be used. Note that it's +// not a 24-bit color because the 25-th bit is not set. +#define DECOR_DEFAULT_COLOR 0x0ffffff + enum glyph_attribute { ATTR_NULL = 0, ATTR_BOLD = 1 << 0, @@ -34,9 +37,8 @@ enum glyph_attribute { ATTR_WRAP = 1 << 8, ATTR_WIDE = 1 << 9, ATTR_WDUMMY = 1 << 10, - ATTR_BOXDRAW = 1 << 11, - ATTR_LIGA = 1 << 12, ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, + ATTR_IMAGE = 1 << 14, }; enum selection_mode { @@ -55,6 +57,14 @@ enum selection_snap { SNAP_LINE = 2 }; +enum underline_style { + UNDERLINE_STRAIGHT = 1, + UNDERLINE_DOUBLE = 2, + UNDERLINE_CURLY = 3, + UNDERLINE_DOTTED = 4, + UNDERLINE_DASHED = 5, +}; + typedef unsigned char uchar; typedef unsigned int uint; typedef unsigned long ulong; @@ -68,6 +78,7 @@ typedef struct { ushort mode; /* attribute flags */ uint32_t fg; /* foreground */ uint32_t bg; /* background */ + uint32_t decor; /* decoration (like underline) */ } Glyph; typedef Glyph *Line; @@ -82,13 +93,10 @@ typedef union { void die(const char *, ...); void redraw(void); -void tfulldirt(void); void draw(void); -void externalpipe(const Arg *); void kscrolldown(const Arg *); void kscrollup(const Arg *); - void printscreen(const Arg *); void printsel(const Arg *); void sendbreak(const Arg *); @@ -113,20 +121,14 @@ void selextend(int, int, int, int); int selected(int, int); char *getsel(void); +Glyph getglyphat(int, int); + size_t utf8encode(Rune, char *); void *xmalloc(size_t); void *xrealloc(void *, size_t); char *xstrdup(const char *); -int isboxdraw(Rune); -ushort boxdrawindex(const Glyph *); -#ifdef XFT_VERSION -/* only exposed to x.c, otherwise we'll need Xft.h for the types */ -void boxdraw_xinit(Display *, Colormap, XftDraw *, Visual *); -void drawboxes(int, int, int, int, XftColor *, XftColor *, const XftGlyphFontSpec *, int); -#endif - /* config.h globals */ extern char *utmp; extern char *scroll; @@ -139,8 +141,71 @@ extern char *termname; extern unsigned int tabspaces; extern unsigned int defaultfg; extern unsigned int defaultbg; -extern float alpha; -extern float alphaUnfocus; -extern const int boxdraw, boxdraw_bold, boxdraw_braille; extern unsigned int defaultcs; - +extern float alpha_def; + +// Accessors to decoration properties stored in `decor`. +// The 25-th bit is used to indicate if it's a 24-bit color. +static inline uint32_t tgetdecorcolor(Glyph *g) { return g->decor & 0x1ffffff; } +static inline uint32_t tgetdecorstyle(Glyph *g) { return (g->decor >> 25) & 0x7; } +static inline void tsetdecorcolor(Glyph *g, uint32_t color) { + g->decor = (g->decor & ~0x1ffffff) | (color & 0x1ffffff); +} +static inline void tsetdecorstyle(Glyph *g, uint32_t style) { + g->decor = (g->decor & ~(0x7 << 25)) | ((style & 0x7) << 25); +} + + +// Some accessors to image placeholder properties stored in `u`: +// - row (1-base) - 9 bits +// - column (1-base) - 9 bits +// - most significant byte of the image id plus 1 - 9 bits (0 means unspecified, +// don't forget to subtract 1). +// - the original number of diacritics (0, 1, 2, or 3) - 2 bits +// - whether this is a classic (1) or Unicode (0) placeholder - 1 bit +static inline uint32_t tgetimgrow(Glyph *g) { return g->u & 0x1ff; } +static inline uint32_t tgetimgcol(Glyph *g) { return (g->u >> 9) & 0x1ff; } +static inline uint32_t tgetimgid4thbyteplus1(Glyph *g) { return (g->u >> 18) & 0x1ff; } +static inline uint32_t tgetimgdiacriticcount(Glyph *g) { return (g->u >> 27) & 0x3; } +static inline uint32_t tgetisclassicplaceholder(Glyph *g) { return (g->u >> 29) & 0x1; } +static inline void tsetimgrow(Glyph *g, uint32_t row) { + g->u = (g->u & ~0x1ff) | (row & 0x1ff); +} +static inline void tsetimgcol(Glyph *g, uint32_t col) { + g->u = (g->u & ~(0x1ff << 9)) | ((col & 0x1ff) << 9); +} +static inline void tsetimg4thbyteplus1(Glyph *g, uint32_t byteplus1) { + g->u = (g->u & ~(0x1ff << 18)) | ((byteplus1 & 0x1ff) << 18); +} +static inline void tsetimgdiacriticcount(Glyph *g, uint32_t count) { + g->u = (g->u & ~(0x3 << 27)) | ((count & 0x3) << 27); +} +static inline void tsetisclassicplaceholder(Glyph *g, uint32_t isclassic) { + g->u = (g->u & ~(0x1 << 29)) | ((isclassic & 0x1) << 29); +} + +/// Returns the full image id. This is a naive implementation, if the most +/// significant byte is not specified, it's assumed to be 0 instead of inferring +/// it from the cells to the left. +static inline uint32_t tgetimgid(Glyph *g) { + uint32_t msb = tgetimgid4thbyteplus1(g); + if (msb != 0) + --msb; + return (msb << 24) | (g->fg & 0xFFFFFF); +} + +/// Sets the full image id. +static inline void tsetimgid(Glyph *g, uint32_t id) { + g->fg = (id & 0xFFFFFF) | (1 << 24); + tsetimg4thbyteplus1(g, ((id >> 24) & 0xFF) + 1); +} + +static inline uint32_t tgetimgplacementid(Glyph *g) { + if (tgetdecorcolor(g) == DECOR_DEFAULT_COLOR) + return 0; + return g->decor & 0xFFFFFF; +} + +static inline void tsetimgplacementid(Glyph *g, uint32_t id) { + g->decor = (id & 0xFFFFFF) | (1 << 24); +} @@ -184,6 +184,10 @@ st-mono| simpleterm monocolor, # XTerm extensions rmxx=\E[29m, smxx=\E[9m, + BE=\E[?2004h, + BD=\E[?2004l, + PS=\E[200~, + PE=\E[201~, # disabled rep for now: causes some issues with older ncurses versions. # rep=%p1%c\E[%p2%{1}%-%db, # tmux extensions, see TERMINFO EXTENSIONS in tmux(1) @@ -191,6 +195,7 @@ st-mono| simpleterm monocolor, Ms=\E]52;%p1%s;%p2%s\007, Se=\E[2 q, Ss=\E[%p1%d q, + Smulx=\E[4:%p1%dm, st| simpleterm, use=st-mono, @@ -211,6 +216,11 @@ st-256color| simpleterm with 256 colors, initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\, setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, +# Underline colors + Su, + Setulc=\E[58:2:%p1%{65536}%/%d:%p1%{256}%/%{255}%&%d:%p1%{255}%&%d%;m, + Setulc1=\E[58:5:%p1%dm, + ol=\E[59m, st-meta| simpleterm with meta key, use=st, @@ -25,7 +25,7 @@ enum win_mode { void xbell(void); void xclipcopy(void); -void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); +void xdrawcursor(int, int, Glyph, int, int, Glyph); void xdrawline(Line, int, int, int); void xfinishdraw(void); void xloadcols(void); @@ -40,3 +40,5 @@ void xsetsel(char *); int xstartdraw(void); void xximspot(int, int); +void xstartimagedraw(int *dirty, int rows); +void xfinishimagedraw(); @@ -4,6 +4,8 @@ #include <limits.h> #include <locale.h> #include <signal.h> +#include <stdio.h> +#include <stdlib.h> #include <sys/select.h> #include <time.h> #include <unistd.h> @@ -14,13 +16,12 @@ #include <X11/keysym.h> #include <X11/Xft/Xft.h> #include <X11/XKBlib.h> -#include <X11/Xresource.h> char *argv0; #include "arg.h" #include "st.h" #include "win.h" -#include "hb.h" +#include "graphics.h" /* types used in config.h */ typedef struct { @@ -47,19 +48,6 @@ typedef struct { signed char appcursor; /* application cursor */ } Key; -/* Xresources preferences */ -enum resource_type { - STRING = 0, - INTEGER = 1, - FLOAT = 2 -}; - -typedef struct { - char *name; - enum resource_type type; - void *dst; -} ResourcePref; - /* X modifiers */ #define XK_ANY_MOD UINT_MAX #define XK_NO_MOD 0 @@ -70,12 +58,20 @@ static void clipcopy(const Arg *); static void clippaste(const Arg *); static void numlock(const Arg *); static void selpaste(const Arg *); -static void changealpha(const Arg *); static void zoom(const Arg *); static void zoomabs(const Arg *); static void zoomreset(const Arg *); static void ttysend(const Arg *); +static void chgalpha(const Arg *); + +static void previewimage(const Arg *); +static void showimageinfo(const Arg *); +static void togglegrdebug(const Arg *); +static void dumpgrstate(const Arg *); +static void unloadimages(const Arg *); +static void toggleimages(const Arg *); + /* config.h for applying patches and the configuration. */ #include "config.h" @@ -97,6 +93,7 @@ typedef XftGlyphFontSpec GlyphFontSpec; typedef struct { int tw, th; /* tty width and height */ int w, h; /* window width and height */ + int hborderpx, vborderpx; int ch; /* char height */ int cw; /* char width */ int mode; /* window state/mode flags */ @@ -161,6 +158,8 @@ static inline ushort sixd_to_16bit(int); static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); static void xdrawglyph(Glyph, int, int); +static void xdrawimages(Glyph, Line, int x1, int y1, int x2); +static void xdrawoneimagecell(Glyph, int x, int y); static void xclear(int, int, int, int); static int xgeommasktogravity(int); static int ximopen(Display *); @@ -173,8 +172,6 @@ static void xresize(int, int); static void xhints(void); static int xloadcolor(int, const char *, Color *); static int xloadfont(Font *, FcPattern *); -static int xloadsparefont(FcPattern *, int); -static void xloadsparefonts(void); static void xloadfonts(const char *, double); static void xunloadfont(Font *); static void xunloadfonts(void); @@ -182,7 +179,6 @@ static void xsetenv(void); static void xseturgency(int); static int evcol(XEvent *); static int evrow(XEvent *); -static float clamp(float, float, float); static void expose(XEvent *); static void visibility(XEvent *); @@ -240,6 +236,7 @@ static DC dc; static XWindow xw; static XSelection xsel; static TermWindow win; +static unsigned int mouse_col = 0, mouse_row = 0; /* Font Ring Cache */ enum { @@ -263,7 +260,11 @@ static char *usedfont = NULL; static double usedfontsize = 0; static double defaultfontsize = 0; -static char *opt_alpha = NULL; +/* declared in config.h */ +extern int disablebold; +extern int disableitalic; +extern int disableroman; + static char *opt_class = NULL; static char **opt_cmd = NULL; static char *opt_embed = NULL; @@ -273,16 +274,8 @@ static char *opt_line = NULL; static char *opt_name = NULL; static char *opt_title = NULL; -static int focused = 0; - -static int oldbutton = 3; /* button event on startup: 3 = release */ static uint buttons; /* bit field of pressed buttons */ -/* declared in config.h */ -extern int disablebold; -extern int disableitalic; -extern int disableroman; - void clipcopy(const Arg *dummy) { @@ -322,18 +315,6 @@ numlock(const Arg *dummy) } void -changealpha(const Arg *arg) -{ - if((alpha > 0 && arg->f < 0) || (alpha < 1 && arg->f > 0)) - alpha += arg->f; - alpha = clamp(alpha, 0.0, 1.0); - alphaUnfocus = clamp(alpha-alphaOffset, 0.0, 1.0); - - xloadcols(); - redraw(); -} - -void zoom(const Arg *arg) { Arg larg; @@ -347,7 +328,6 @@ zoomabs(const Arg *arg) { xunloadfonts(); xloadfonts(usedfont, arg->f); - xloadsparefonts(); cresize(0, 0); redraw(); xhints(); @@ -370,10 +350,72 @@ ttysend(const Arg *arg) ttywrite(arg->s, strlen(arg->s), 1); } +void +previewimage(const Arg *arg) +{ + Glyph g = getglyphat(mouse_col, mouse_row); + if (g.mode & ATTR_IMAGE) { + uint32_t image_id = tgetimgid(&g); + fprintf(stderr, "Clicked on placeholder %u/%u, x=%d, y=%d\n", + image_id, tgetimgplacementid(&g), tgetimgcol(&g), + tgetimgrow(&g)); + gr_preview_image(image_id, arg->s); + } +} + +void +showimageinfo(const Arg *arg) +{ + Glyph g = getglyphat(mouse_col, mouse_row); + if (g.mode & ATTR_IMAGE) { + uint32_t image_id = tgetimgid(&g); + fprintf(stderr, "Clicked on placeholder %u/%u, x=%d, y=%d\n", + image_id, tgetimgplacementid(&g), tgetimgcol(&g), + tgetimgrow(&g)); + char stcommand[256] = {0}; + size_t len = snprintf(stcommand, sizeof(stcommand), "%s -e less", argv0); + if (len > sizeof(stcommand) - 1) { + fprintf(stderr, "Executable name too long: %s\n", + argv0); + return; + } + gr_show_image_info(image_id, tgetimgplacementid(&g), + tgetimgcol(&g), tgetimgrow(&g), + tgetisclassicplaceholder(&g), + tgetimgdiacriticcount(&g), argv0); + } +} + +void +togglegrdebug(const Arg *arg) +{ + graphics_debug_mode = (graphics_debug_mode + 1) % 3; + redraw(); +} + +void +dumpgrstate(const Arg *arg) +{ + gr_dump_state(); +} + +void +unloadimages(const Arg *arg) +{ + gr_unload_images_to_reduce_ram(); +} + +void +toggleimages(const Arg *arg) +{ + graphics_display_images = !graphics_display_images; + redraw(); +} + int evcol(XEvent *e) { - int x = e->xbutton.x - borderpx; + int x = e->xbutton.x - win.hborderpx; LIMIT(x, 0, win.tw - 1); return x / win.cw; } @@ -381,20 +423,11 @@ evcol(XEvent *e) int evrow(XEvent *e) { - int y = e->xbutton.y - borderpx; + int y = e->xbutton.y - win.vborderpx; LIMIT(y, 0, win.th - 1); return y / win.ch; } -float -clamp(float value, float lower, float upper) { - if(value < lower) - return lower; - if(value > upper) - return upper; - return value; -} - void mousesel(XEvent *e, int done) { @@ -415,59 +448,68 @@ mousesel(XEvent *e, int done) void mousereport(XEvent *e) { - int len, x = evcol(e), y = evrow(e), - button = e->xbutton.button, state = e->xbutton.state; + int len, btn, code; + int x = evcol(e), y = evrow(e); + int state = e->xbutton.state; char buf[40]; static int ox, oy; - /* from urxvt */ - if (e->xbutton.type == MotionNotify) { + if (e->type == MotionNotify) { if (x == ox && y == oy) return; if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) return; - /* MOUSE_MOTION: no reporting if no button is pressed */ - if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) + /* MODE_MOUSEMOTION: no reporting if no button is pressed */ + if (IS_SET(MODE_MOUSEMOTION) && buttons == 0) return; - - button = oldbutton + 32; - ox = x; - oy = y; + /* Set btn to lowest-numbered pressed button, or 12 if no + * buttons are pressed. */ + for (btn = 1; btn <= 11 && !(buttons & (1<<(btn-1))); btn++) + ; + code = 32; } else { - if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) { - button = 3; - } else { - button -= Button1; - if (button >= 3) - button += 64 - 3; - } - if (e->xbutton.type == ButtonPress) { - oldbutton = button; - ox = x; - oy = y; - } else if (e->xbutton.type == ButtonRelease) { - oldbutton = 3; + btn = e->xbutton.button; + /* Only buttons 1 through 11 can be encoded */ + if (btn < 1 || btn > 11) + return; + if (e->type == ButtonRelease) { /* MODE_MOUSEX10: no button release reporting */ if (IS_SET(MODE_MOUSEX10)) return; - if (button == 64 || button == 65) + /* Don't send release events for the scroll wheel */ + if (btn == 4 || btn == 5) return; } + code = 0; } + ox = x; + oy = y; + + /* Encode btn into code. If no button is pressed for a motion event in + * MODE_MOUSEMANY, then encode it as a release. */ + if ((!IS_SET(MODE_MOUSESGR) && e->type == ButtonRelease) || btn == 12) + code += 3; + else if (btn >= 8) + code += 128 + btn - 8; + else if (btn >= 4) + code += 64 + btn - 4; + else + code += btn - 1; + if (!IS_SET(MODE_MOUSEX10)) { - button += ((state & ShiftMask ) ? 4 : 0) - + ((state & Mod4Mask ) ? 8 : 0) - + ((state & ControlMask) ? 16 : 0); + code += ((state & ShiftMask ) ? 4 : 0) + + ((state & Mod1Mask ) ? 8 : 0) /* meta key: alt */ + + ((state & ControlMask) ? 16 : 0); } if (IS_SET(MODE_MOUSESGR)) { len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", - button, x+1, y+1, + code, x+1, y+1, e->type == ButtonRelease ? 'm' : 'M'); } else if (x < 223 && y < 223) { len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", - 32+button, 32+x+1, 32+y+1); + 32+code, 32+x+1, 32+y+1); } else { return; } @@ -494,6 +536,9 @@ mouseaction(XEvent *e, uint release) /* ignore Button<N>mask for Button<N> - it's set on release */ uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); + mouse_col = evcol(e); + mouse_row = evrow(e); + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { if (ms->release == release && ms->button == e->xbutton.button && @@ -781,6 +826,9 @@ cresize(int width, int height) col = MAX(1, col); row = MAX(1, row); + win.hborderpx = (win.w - col * win.cw) * anysize_halign / 100; + win.vborderpx = (win.h - row * win.ch) * anysize_valign / 100; + tresize(col, row); xresize(col, row); ttyresize(win.tw, win.th); @@ -833,28 +881,21 @@ xloadcolor(int i, const char *name, Color *ncolor) } void -xloadalpha(void) -{ - float const usedAlpha = focused ? alpha : alphaUnfocus; - if (opt_alpha) alpha = strtof(opt_alpha, NULL); - dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * usedAlpha); - dc.col[defaultbg].pixel &= 0x00FFFFFF; - dc.col[defaultbg].pixel |= (unsigned char)(0xff * usedAlpha) << 24; -} - -void xloadcols(void) { int i; static int loaded; Color *cp; - if (!loaded) { - dc.collen = 1 + (defaultbg = MAX(LEN(colorname), 256)); + if (loaded) { + for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) + XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); + } else { + dc.collen = MAX(LEN(colorname), 256); dc.col = xmalloc(dc.collen * sizeof(Color)); } - for (i = 0; i+1 < dc.collen; i++) + for (i = 0; i < dc.collen; i++) if (!xloadcolor(i, NULL, &dc.col[i])) { if (colorname[i]) die("could not allocate color '%s'\n", colorname[i]); @@ -862,17 +903,16 @@ xloadcols(void) die("could not allocate color %d\n", i); } - if (dc.collen) // cannot die, as the color is already loaded. - xloadcolor(background, NULL, &dc.col[defaultbg]); - - xloadalpha(); + dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); + dc.col[defaultbg].pixel &= 0x00FFFFFF; + dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; loaded = 1; } int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) { - if (!BETWEEN(x, 0, dc.collen)) + if (!BETWEEN(x, 0, dc.collen - 1)) return 1; *r = dc.col[x].color.red >> 8; @@ -887,7 +927,7 @@ xsetcolorname(int x, const char *name) { Color ncolor; - if (!BETWEEN(x, 0, dc.collen)) + if (!BETWEEN(x, 0, dc.collen - 1)) return 1; if (!xloadcolor(x, name, &ncolor)) @@ -896,6 +936,12 @@ xsetcolorname(int x, const char *name) XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); dc.col[x] = ncolor; + if (x == defaultbg) { + dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); + dc.col[defaultbg].pixel &= 0x00FFFFFF; + dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; + } + return 0; } @@ -913,8 +959,8 @@ xclear(int x1, int y1, int x2, int y2) void xhints(void) { - XClassHint class = {opt_name ? opt_name : "st", - opt_class ? opt_class : "St"}; + XClassHint class = {opt_name ? opt_name : termname, + opt_class ? opt_class : termname}; XWMHints wm = {.flags = InputHint, .input = 1}; XSizeHints *sizeh; @@ -923,8 +969,8 @@ xhints(void) sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; sizeh->height = win.h; sizeh->width = win.w; - sizeh->height_inc = win.ch; - sizeh->width_inc = win.cw; + sizeh->height_inc = 1; + sizeh->width_inc = 1; sizeh->base_height = 2 * borderpx; sizeh->base_width = 2 * borderpx; sizeh->min_height = win.ch + 2 * borderpx; @@ -1068,7 +1114,8 @@ xloadfonts(const char *fontstr, double fontsize) FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); usedfontsize = 12; } - defaultfontsize = usedfontsize; + if (defaultfontsize <= 0) + defaultfontsize = usedfontsize; } if (xloadfont(&dc.font, pattern)) @@ -1078,7 +1125,7 @@ xloadfonts(const char *fontstr, double fontsize) FcPatternGetDouble(dc.font.match->pattern, FC_PIXEL_SIZE, 0, &fontval); usedfontsize = fontval; - if (fontsize == 0) + if (defaultfontsize <= 0 && fontsize == 0) defaultfontsize = fontval; } @@ -1087,121 +1134,26 @@ xloadfonts(const char *fontstr, double fontsize) win.ch = ceilf(dc.font.height * chscale); FcPatternDel(pattern, FC_SLANT); - if (!disableitalic) - FcPatternAddInteger(pattern, FC_SLANT, FRC_ITALIC); + if (!disableitalic) + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); if (xloadfont(&dc.ifont, pattern)) die("can't open font %s\n", fontstr); FcPatternDel(pattern, FC_WEIGHT); - if (!disablebold) - FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + if (!disablebold) + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); if (xloadfont(&dc.ibfont, pattern)) die("can't open font %s\n", fontstr); FcPatternDel(pattern, FC_SLANT); - if (!disableroman) - FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + if (!disableroman) + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); if (xloadfont(&dc.bfont, pattern)) die("can't open font %s\n", fontstr); FcPatternDestroy(pattern); } -int -xloadsparefont(FcPattern *pattern, int flags) -{ - FcPattern *match; - FcResult result; - - match = FcFontMatch(NULL, pattern, &result); - if (!match) { - return 1; - } - - if (!(frc[frclen].font = XftFontOpenPattern(xw.dpy, match))) { - FcPatternDestroy(match); - return 1; - } - - frc[frclen].flags = flags; - /* Believe U+0000 glyph will present in each default font */ - frc[frclen].unicodep = 0; - frclen++; - - return 0; -} - -void -xloadsparefonts(void) -{ - FcPattern *pattern; - double sizeshift, fontval; - int fc; - char **fp; - - if (frclen != 0) - die("can't embed spare fonts. cache isn't empty"); - - /* Calculate count of spare fonts */ - fc = sizeof(font2) / sizeof(*font2); - if (fc == 0) - return; - - /* Allocate memory for cache entries. */ - if (frccap < 4 * fc) { - frccap += 4 * fc - frccap; - frc = xrealloc(frc, frccap * sizeof(Fontcache)); - } - - for (fp = font2; fp - font2 < fc; ++fp) { - - if (**fp == '-') - pattern = XftXlfdParse(*fp, False, False); - else - pattern = FcNameParse((FcChar8 *)*fp); - - if (!pattern) - die("can't open spare font %s\n", *fp); - - if (defaultfontsize > 0) { - sizeshift = usedfontsize - defaultfontsize; - if (sizeshift != 0 && - FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == - FcResultMatch) { - fontval += sizeshift; - FcPatternDel(pattern, FC_PIXEL_SIZE); - FcPatternDel(pattern, FC_SIZE); - FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontval); - } - } - - FcPatternAddBool(pattern, FC_SCALABLE, 1); - - FcConfigSubstitute(NULL, pattern, FcMatchPattern); - XftDefaultSubstitute(xw.dpy, xw.scr, pattern); - - if (xloadsparefont(pattern, FRC_NORMAL)) - die("can't open spare font %s\n", *fp); - - FcPatternDel(pattern, FC_SLANT); - FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); - if (xloadsparefont(pattern, FRC_ITALIC)) - die("can't open spare font %s\n", *fp); - - FcPatternDel(pattern, FC_WEIGHT); - FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); - if (xloadsparefont(pattern, FRC_ITALICBOLD)) - die("can't open spare font %s\n", *fp); - - FcPatternDel(pattern, FC_SLANT); - FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); - if (xloadsparefont(pattern, FRC_BOLD)) - die("can't open spare font %s\n", *fp); - - FcPatternDestroy(pattern); - } -} - void xunloadfont(Font *f) { @@ -1214,9 +1166,6 @@ xunloadfont(Font *f) void xunloadfonts(void) { - /* Clear Harfbuzz font cache. */ - hbunloadfonts(); - /* Free the loaded fonts in the font cache. */ while (frclen > 0) XftFontClose(xw.dpy, frc[--frclen].font); @@ -1286,25 +1235,29 @@ xinit(int cols, int rows) { XGCValues gcvalues; Cursor cursor; - Window parent; + Window parent, root; pid_t thispid = getpid(); XColor xmousefg, xmousebg; XWindowAttributes attr; XVisualInfo vis; + if (!(xw.dpy = XOpenDisplay(NULL))) + die("can't open display\n"); xw.scr = XDefaultScreen(xw.dpy); - if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { - parent = XRootWindow(xw.dpy, xw.scr); - xw.depth = 32; + root = XRootWindow(xw.dpy, xw.scr); + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) + parent = root; + + if (XMatchVisualInfo(xw.dpy, xw.scr, 32, TrueColor, &vis) != 0) { + xw.vis = vis.visual; + xw.depth = vis.depth; } else { XGetWindowAttributes(xw.dpy, parent, &attr); + xw.vis = attr.visual; xw.depth = attr.depth; } - XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); - xw.vis = vis.visual; - /* font */ if (!FcInit()) die("could not init fontconfig.\n"); @@ -1312,16 +1265,16 @@ xinit(int cols, int rows) usedfont = (opt_font == NULL)? font : opt_font; xloadfonts(usedfont, 0); - /* spare fonts */ - xloadsparefonts(); + /* Backup default alpha value */ + alpha_def = alpha; /* colors */ xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); xloadcols(); /* adjust fixed window geometry */ - win.w = 2 * borderpx + cols * win.cw; - win.h = 2 * borderpx + rows * win.ch; + win.w = 2 * win.hborderpx + 2 * borderpx + cols * win.cw; + win.h = 2 * win.vborderpx + 2 * borderpx + rows * win.ch; if (xw.gm & XNegative) xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; if (xw.gm & YNegative) @@ -1340,11 +1293,15 @@ xinit(int cols, int rows) win.w, win.h, 0, xw.depth, InputOutput, xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity | CWEventMask | CWColormap, &xw.attrs); + if (parent != root) + XReparentWindow(xw.dpy, xw.win, parent, xw.l, xw.t); memset(&gcvalues, 0, sizeof(gcvalues)); gcvalues.graphics_exposures = False; - xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); - dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); + dc.gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, + &gcvalues); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, + xw.depth); XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); @@ -1402,13 +1359,14 @@ xinit(int cols, int rows) if (xsel.xtarget == None) xsel.xtarget = XA_STRING; - boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); + // Initialize the graphics (image display) module. + gr_init(xw.dpy, xw.vis, xw.cmap); } int xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) { - float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; + float winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch, xp, yp; ushort mode, prevmode = USHRT_MAX; Font *font = &dc.font; int frcflags = FRC_NORMAL; @@ -1427,9 +1385,14 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x mode = glyphs[i].mode; /* Skip dummy wide-character spacing. */ - if (mode & ATTR_WDUMMY) + if (mode == ATTR_WDUMMY) continue; + /* Draw spaces for image placeholders (images will be drawn + * separately). */ + if (mode & ATTR_IMAGE) + rune = ' '; + /* Determine font for glyph if different from previous glyph. */ if (prevmode != mode) { prevmode = mode; @@ -1449,13 +1412,8 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x yp = winy + font->ascent; } - if (mode & ATTR_BOXDRAW) { - /* minor shoehorning: boxdraw uses only this ushort */ - glyphidx = boxdrawindex(&glyphs[i]); - } else { - /* Lookup character index with default font. */ - glyphidx = XftCharIndex(xw.dpy, font->match, rune); - } + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); if (glyphidx) { specs[numspecs].font = font->match; specs[numspecs].glyph = glyphidx; @@ -1539,17 +1497,82 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x numspecs++; } - /* Harfbuzz transformation for ligatures. */ - hbtransform(specs, glyphs, len, x, y); - return numspecs; } +/* Draws a horizontal dashed line of length `w` starting at `(x, y)`. `wavelen` + * is the length of the dash plus the length of the gap. `fraction` is the + * fraction of the dash length compared to `wavelen`. */ +static void +xdrawunderdashed(Draw draw, Color *color, int x, int y, int w, + int wavelen, float fraction, int thick) +{ + int dashw = MAX(1, fraction * wavelen); + for (int i = x - x % wavelen; i < x + w; i += wavelen) { + int startx = MAX(i, x); + int endx = MIN(i + dashw, x + w); + if (startx < endx) + XftDrawRect(xw.draw, color, startx, y, endx - startx, + thick); + } +} + +/* Draws an undercurl. `h` is the total height, including line thickness. */ +static void +xdrawundercurl(Draw draw, Color *color, int x, int y, int w, int h, int thick) +{ + XGCValues gcvals = {.foreground = color->pixel, + .line_width = thick, + .line_style = LineSolid, + .cap_style = CapRound}; + GC gc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw), + GCForeground | GCLineWidth | GCLineStyle | GCCapStyle, + &gcvals); + + XRectangle clip = {.x = x, .y = y, .width = w, .height = h}; + XSetClipRectangles(xw.dpy, gc, 0, 0, &clip, 1, Unsorted); + + int yoffset = thick / 2; + int segh = MAX(1, h - thick); + /* Make sure every segment is at a 45 degree angle, otherwise it doesn't + * look good without antialiasing. */ + int segw = segh; + int wavelen = MAX(1, segw * 2); + + for (int i = x - (x % wavelen); i < x + w; i += wavelen) { + XPoint points[3] = {{.x = i, .y = y + yoffset}, + {.x = i + segw, .y = y + yoffset + segh}, + {.x = i + wavelen, .y = y + yoffset}}; + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), gc, points, 3, + CoordModeOrigin); + } + + XFreeGC(xw.dpy, gc); +} + +void +chgalpha(const Arg *arg) +{ + if (arg->f == -1.0f && alpha >= 0.1f) + alpha -= 0.1f; + else if (arg->f == 1.0f && alpha < 1.0f) + alpha += 0.1f; + else if (arg->f == 0.0f) + alpha = alpha_def; + else + return; + + dc.col[defaultbg].color.alpha = (unsigned short)(0xFFFF * alpha); + /* Required to remove artifacting from borderpx */ + cresize(0, 0); + redraw(); +} + void xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) { int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); - int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, + int winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch, width = charlen * win.cw; Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; XRenderColor colfg, colbg; @@ -1639,17 +1662,17 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i /* Intelligent cleaning up of the borders. */ if (x == 0) { - xclear(0, (y == 0)? 0 : winy, borderpx, + xclear(0, (y == 0)? 0 : winy, win.hborderpx, winy + win.ch + - ((winy + win.ch >= borderpx + win.th)? win.h : 0)); + ((winy + win.ch >= win.vborderpx + win.th)? win.h : 0)); } - if (winx + width >= borderpx + win.tw) { + if (winx + width >= win.hborderpx + win.tw) { xclear(winx + width, (y == 0)? 0 : winy, win.w, - ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); + ((winy + win.ch >= win.vborderpx + win.th)? win.h : (winy + win.ch))); } if (y == 0) - xclear(winx, 0, winx + width, borderpx); - if (winy + win.ch >= borderpx + win.th) + xclear(winx, 0, winx + width, win.vborderpx); + if (winy + win.ch >= win.vborderpx + win.th) xclear(winx, winy + win.ch, winx + width, win.h); /* Clean up the region we want to draw to. */ @@ -1662,22 +1685,68 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i r.width = width; XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); - if (base.mode & ATTR_BOXDRAW) { - drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); + /* Decoration color. */ + Color decor; + uint32_t decorcolor = tgetdecorcolor(&base); + if (decorcolor == DECOR_DEFAULT_COLOR) { + decor = *fg; + } else if (IS_TRUECOL(decorcolor)) { + colfg.alpha = 0xffff; + colfg.red = TRUERED(decorcolor); + colfg.green = TRUEGREEN(decorcolor); + colfg.blue = TRUEBLUE(decorcolor); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &decor); } else { - /* Render the glyphs. */ - XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + decor = dc.col[decorcolor]; } - - /* Render underline and strikethrough. */ + decor.color.alpha = 0xffff; + decor.pixel |= 0xff << 24; + + /* Float thickness, used as a base to compute other values. */ + float fthick = dc.font.height / 18.0; + /* Integer thickness in pixels. Must not be 0. */ + int thick = MAX(1, roundf(fthick)); + /* The default gap between the baseline and a single underline. */ + int gap = roundf(fthick * 2); + /* The total thickness of a double underline. */ + int doubleh = thick * 2 + ceilf(fthick * 0.5); + /* The total thickness of an undercurl. */ + int curlh = thick * 2 + roundf(fthick * 0.75); + + /* Render the underline before the glyphs. */ if (base.mode & ATTR_UNDERLINE) { - XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, - width, 1); + uint32_t style = tgetdecorstyle(&base); + int liney = winy + dc.font.ascent + gap; + /* Adjust liney to guarantee that a single underline fits. */ + liney -= MAX(0, liney + thick - (winy + win.ch)); + if (style == UNDERLINE_DOUBLE) { + liney -= MAX(0, liney + doubleh - (winy + win.ch)); + XftDrawRect(xw.draw, &decor, winx, liney, width, thick); + XftDrawRect(xw.draw, &decor, winx, + liney + doubleh - thick, width, thick); + } else if (style == UNDERLINE_DOTTED) { + xdrawunderdashed(xw.draw, &decor, winx, liney, width, + thick * 2, 0.5, thick); + } else if (style == UNDERLINE_DASHED) { + int wavelen = MAX(2, win.cw * 0.9); + xdrawunderdashed(xw.draw, &decor, winx, liney, width, + wavelen, 0.65, thick); + } else if (style == UNDERLINE_CURLY) { + liney -= MAX(0, liney + curlh - (winy + win.ch)); + xdrawundercurl(xw.draw, &decor, winx, liney, width, + curlh, thick); + } else { + XftDrawRect(xw.draw, &decor, winx, liney, width, thick); + } } + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + + /* Render strikethrough. Alway use the fg color. */ if (base.mode & ATTR_STRUCK) { - XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3, - width, 1); + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, + width, thick); } /* Reset clip to none. */ @@ -1692,28 +1761,34 @@ xdrawglyph(Glyph g, int x, int y) numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); xdrawglyphfontspecs(&spec, g, numspecs, x, y); + if (g.mode & ATTR_IMAGE) { + gr_start_drawing(xw.buf, win.cw, win.ch); + xdrawoneimagecell(g, x, y); + gr_finish_drawing(xw.buf); + } } void -xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) { Color drawcol; /* remove the old cursor */ if (selected(ox, oy)) og.mode ^= ATTR_REVERSE; - - /* Redraw the line where cursor was previously. - * It will restore the ligatures broken by the cursor. */ - xdrawline(line, 0, oy, len); + xdrawglyph(og, ox, oy); if (IS_SET(MODE_HIDE)) return; + // If it's an image, just draw a ballot box for simplicity. + if (g.mode & ATTR_IMAGE) + g.u = 0x2610; + /* * Select the right color for the right mode. */ - g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW; + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; if (IS_SET(MODE_REVERSE)) { g.mode |= ATTR_REVERSE; @@ -1750,39 +1825,167 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int le case 3: /* Blinking Underline */ case 4: /* Steady Underline */ XftDrawRect(xw.draw, &drawcol, - borderpx + cx * win.cw, - borderpx + (cy + 1) * win.ch - \ + win.hborderpx + cx * win.cw, + win.vborderpx + (cy + 1) * win.ch - \ cursorthickness, win.cw, cursorthickness); break; case 5: /* Blinking bar */ case 6: /* Steady bar */ XftDrawRect(xw.draw, &drawcol, - borderpx + cx * win.cw, - borderpx + cy * win.ch, + win.hborderpx + cx * win.cw, + win.vborderpx + cy * win.ch, cursorthickness, win.ch); break; } } else { XftDrawRect(xw.draw, &drawcol, - borderpx + cx * win.cw, - borderpx + cy * win.ch, + win.hborderpx + cx * win.cw, + win.vborderpx + cy * win.ch, win.cw - 1, 1); XftDrawRect(xw.draw, &drawcol, - borderpx + cx * win.cw, - borderpx + cy * win.ch, + win.hborderpx + cx * win.cw, + win.vborderpx + cy * win.ch, 1, win.ch - 1); XftDrawRect(xw.draw, &drawcol, - borderpx + (cx + 1) * win.cw - 1, - borderpx + cy * win.ch, + win.hborderpx + (cx + 1) * win.cw - 1, + win.vborderpx + cy * win.ch, 1, win.ch - 1); XftDrawRect(xw.draw, &drawcol, - borderpx + cx * win.cw, - borderpx + (cy + 1) * win.ch - 1, + win.hborderpx + cx * win.cw, + win.vborderpx + (cy + 1) * win.ch - 1, win.cw, 1); } } +/* Draw (or queue for drawing) image cells between columns x1 and x2 assuming + * that they have the same attributes (and thus the same lower 24 bits of the + * image ID and the same placement ID). */ +void +xdrawimages(Glyph base, Line line, int x1, int y1, int x2) { + int y_pix = win.vborderpx + y1 * win.ch; + uint32_t image_id_24bits = base.fg & 0xFFFFFF; + uint32_t placement_id = tgetimgplacementid(&base); + // Columns and rows are 1-based, 0 means unspecified. + int last_col = 0; + int last_row = 0; + int last_start_col = 0; + int last_start_x = x1; + // The most significant byte is also 1-base, subtract 1 before use. + uint32_t last_id_4thbyteplus1 = 0; + // We may need to inherit row/column/4th byte from the previous cell. + Glyph *prev = &line[x1 - 1]; + if (x1 > 0 && (prev->mode & ATTR_IMAGE) && + (prev->fg & 0xFFFFFF) == image_id_24bits && + prev->decor == base.decor) { + last_row = tgetimgrow(prev); + last_col = tgetimgcol(prev); + last_id_4thbyteplus1 = tgetimgid4thbyteplus1(prev); + last_start_col = last_col + 1; + } + for (int x = x1; x < x2; ++x) { + Glyph *g = &line[x]; + uint32_t cur_row = tgetimgrow(g); + uint32_t cur_col = tgetimgcol(g); + uint32_t cur_id_4thbyteplus1 = tgetimgid4thbyteplus1(g); + uint32_t num_diacritics = tgetimgdiacriticcount(g); + // If the row is not specified, assume it's the same as the row + // of the previous cell. Note that `cur_row` may contain a + // value imputed earlier, which will be preserved if `last_row` + // is zero (i.e. we don't know the row of the previous cell). + if (last_row && (num_diacritics == 0 || !cur_row)) + cur_row = last_row; + // If the column is not specified and the row is the same as the + // row of the previous cell, then assume that the column is the + // next one. + if (last_col && (num_diacritics <= 1 || !cur_col) && + cur_row == last_row) + cur_col = last_col + 1; + // If the additional id byte is not specified and the + // coordinates are consecutive, assume the byte is also the + // same. + if (last_id_4thbyteplus1 && + (num_diacritics <= 2 || !cur_id_4thbyteplus1) && + cur_row == last_row && cur_col == last_col + 1) + cur_id_4thbyteplus1 = last_id_4thbyteplus1; + // If we couldn't infer row and column, start from the top left + // corner. + if (cur_row == 0) + cur_row = 1; + if (cur_col == 0) + cur_col = 1; + // If this cell breaks a contiguous stripe of image cells, draw + // that line and start a new one. + if (cur_col != last_col + 1 || cur_row != last_row || + cur_id_4thbyteplus1 != last_id_4thbyteplus1) { + uint32_t image_id = image_id_24bits; + if (last_id_4thbyteplus1) + image_id |= (last_id_4thbyteplus1 - 1) << 24; + if (last_row != 0) { + int x_pix = + win.hborderpx + last_start_x * win.cw; + gr_append_imagerect( + xw.buf, image_id, placement_id, + last_start_col - 1, last_col, + last_row - 1, last_row, last_start_x, + y1, x_pix, y_pix, win.cw, win.ch, + base.mode & ATTR_REVERSE); + } + last_start_col = cur_col; + last_start_x = x; + } + last_row = cur_row; + last_col = cur_col; + last_id_4thbyteplus1 = cur_id_4thbyteplus1; + // Populate the missing glyph data to enable inheritance between + // runs and support the naive implementation of tgetimgid. + if (!tgetimgrow(g)) + tsetimgrow(g, cur_row); + // We cannot save this information if there are > 511 cols. + if (!tgetimgcol(g) && (cur_col & ~0x1ff) == 0) + tsetimgcol(g, cur_col); + if (!tgetimgid4thbyteplus1(g)) + tsetimg4thbyteplus1(g, cur_id_4thbyteplus1); + } + uint32_t image_id = image_id_24bits; + if (last_id_4thbyteplus1) + image_id |= (last_id_4thbyteplus1 - 1) << 24; + // Draw the last contiguous stripe. + if (last_row != 0) { + int x_pix = win.hborderpx + last_start_x * win.cw; + gr_append_imagerect(xw.buf, image_id, placement_id, + last_start_col - 1, last_col, last_row - 1, + last_row, last_start_x, y1, x_pix, y_pix, + win.cw, win.ch, base.mode & ATTR_REVERSE); + } +} + +/* Draw just one image cell without inheriting attributes from the left. */ +void xdrawoneimagecell(Glyph g, int x, int y) { + if (!(g.mode & ATTR_IMAGE)) + return; + int x_pix = win.hborderpx + x * win.cw; + int y_pix = win.vborderpx + y * win.ch; + uint32_t row = tgetimgrow(&g) - 1; + uint32_t col = tgetimgcol(&g) - 1; + uint32_t placement_id = tgetimgplacementid(&g); + uint32_t image_id = tgetimgid(&g); + gr_append_imagerect(xw.buf, image_id, placement_id, col, col + 1, row, + row + 1, x, y, x_pix, y_pix, win.cw, win.ch, + g.mode & ATTR_REVERSE); +} + +/* Prepare for image drawing. */ +void xstartimagedraw(int *dirty, int rows) { + gr_start_drawing(xw.buf, win.cw, win.ch); + gr_mark_dirty_animations(dirty, rows); +} + +/* Draw all queued image cells. */ +void xfinishimagedraw() { + gr_finish_drawing(xw.buf); +} + void xsetenv(void) { @@ -1798,6 +2001,9 @@ xseticontitle(char *p) XTextProperty prop; DEFAULT(p, opt_title); + if (p[0] == '\0') + p = opt_title; + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop) != Success) return; @@ -1812,6 +2018,9 @@ xsettitle(char *p) XTextProperty prop; DEFAULT(p, opt_title); + if (p[0] == '\0') + p = opt_title; + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop) != Success) return; @@ -1843,6 +2052,8 @@ xdrawline(Line line, int x1, int y1, int x2) new.mode ^= ATTR_REVERSE; if (i > 0 && ATTRCMP(base, new)) { xdrawglyphfontspecs(specs, base, i, ox, y1); + if (base.mode & ATTR_IMAGE) + xdrawimages(base, line, ox, y1, x); specs += i; numspecs -= i; i = 0; @@ -1855,6 +2066,8 @@ xdrawline(Line line, int x1, int y1, int x2) } if (i > 0) xdrawglyphfontspecs(specs, base, i, ox, y1); + if (i > 0 && base.mode & ATTR_IMAGE) + xdrawimages(base, line, ox, y1, x); } void @@ -1958,22 +2171,12 @@ focus(XEvent *ev) xseturgency(0); if (IS_SET(MODE_FOCUS)) ttywrite("\033[I", 3, 0); - if (!focused) { - focused = 1; - xloadcols(); - tfulldirt(); - } } else { if (xw.ime.xic) XUnsetICFocus(xw.ime.xic); win.mode &= ~MODE_FOCUSED; if (IS_SET(MODE_FOCUS)) ttywrite("\033[O", 3, 0); - if (focused) { - focused = 0; - xloadcols(); - tfulldirt(); - } } } @@ -2024,11 +2227,9 @@ void kpress(XEvent *ev) { XKeyEvent *e = &ev->xkey; - KeySym ksym; - char *buf = NULL, *customkey; - int len = 0; - int buf_size = 64; - int critical = - 1; + KeySym ksym = NoSymbol; + char buf[64], *customkey; + int len; Rune c; Status status; Shortcut *bp; @@ -2036,44 +2237,30 @@ kpress(XEvent *ev) if (IS_SET(MODE_KBDLOCK)) return; -reallocbuf: - if (critical > 0) - goto cleanup; - if (buf) - free(buf); - - buf = xmalloc((buf_size) * sizeof(char)); - critical += 1; - if (xw.ime.xic) { - len = XmbLookupString(xw.ime.xic, e, buf, buf_size, &ksym, &status); - if (status == XBufferOverflow) { - buf_size = len; - goto reallocbuf; - } + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); + if (status == XBufferOverflow) + return; } else { - // Not sure how to fix this and if it is fixable - // but at least it does write something into the buffer - // so it is not as critical - len = XLookupString(e, buf, buf_size, &ksym, NULL); + len = XLookupString(e, buf, sizeof buf, &ksym, NULL); } /* 1. shortcuts */ for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { if (ksym == bp->keysym && match(bp->mod, e->state)) { bp->func(&(bp->arg)); - goto cleanup; + return; } } /* 2. custom keys from config.h */ if ((customkey = kmap(ksym, e->state))) { ttywrite(customkey, strlen(customkey), 1); - goto cleanup; + return; } /* 3. composed string from input method */ if (len == 0) - goto cleanup; + return; if (len == 1 && e->state & Mod1Mask) { if (IS_SET(MODE_8BIT)) { if (*buf < 0177) { @@ -2086,11 +2273,7 @@ reallocbuf: len = 2; } } - if (len <= buf_size) - ttywrite(buf, len, 1); -cleanup: - if (buf) - free(buf); + ttywrite(buf, len, 1); } void @@ -2109,6 +2292,7 @@ cmessage(XEvent *e) } } else if (e->xclient.data.l[0] == xw.wmdeletewin) { ttyhangup(); + gr_deinit(); exit(0); } } @@ -2159,6 +2343,13 @@ run(void) if (XPending(xw.dpy)) timeout = 0; /* existing events might not set xfd */ + /* Decrease the timeout if there are active animations. */ + if (graphics_next_redraw_delay != INT_MAX && + IS_SET(MODE_VISIBLE)) + timeout = timeout < 0 ? graphics_next_redraw_delay + : MIN(timeout, + graphics_next_redraw_delay); + seltv.tv_sec = timeout / 1E3; seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); tv = timeout >= 0 ? &seltv : NULL; @@ -2225,59 +2416,6 @@ run(void) } } -int -resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst) -{ - char **sdst = dst; - int *idst = dst; - float *fdst = dst; - - char fullname[256]; - char fullclass[256]; - char *type; - XrmValue ret; - - snprintf(fullname, sizeof(fullname), "%s.%s", - opt_name ? opt_name : "st", name); - snprintf(fullclass, sizeof(fullclass), "%s.%s", - opt_class ? opt_class : "St", name); - fullname[sizeof(fullname) - 1] = fullclass[sizeof(fullclass) - 1] = '\0'; - - XrmGetResource(db, fullname, fullclass, &type, &ret); - if (ret.addr == NULL || strncmp("String", type, 64)) - return 1; - - switch (rtype) { - case STRING: - *sdst = ret.addr; - break; - case INTEGER: - *idst = strtoul(ret.addr, NULL, 10); - break; - case FLOAT: - *fdst = strtof(ret.addr, NULL); - break; - } - return 0; -} - -void -config_init(void) -{ - char *resm; - XrmDatabase db; - ResourcePref *p; - - XrmInitialize(); - resm = XResourceManagerString(xw.dpy); - if (!resm) - return; - - db = XrmGetStringDatabase(resm); - for (p = resources; p < resources + LEN(resources); p++) - resource_load(db, p->name, p->type, p->dst); -} - void usage(void) { @@ -2303,7 +2441,8 @@ main(int argc, char *argv[]) allowaltscreen = 0; break; case 'A': - opt_alpha = EARGF(usage()); + alpha = strtof(EARGF(usage()), NULL); + LIMIT(alpha, 0.0, 1.0); break; case 'c': opt_class = EARGF(usage()); @@ -2354,15 +2493,8 @@ run: setlocale(LC_CTYPE, ""); XSetLocaleModifiers(""); - - if(!(xw.dpy = XOpenDisplay(NULL))) - die("Can't open display\n"); - - config_init(); cols = MAX(cols, 1); rows = MAX(rows, 1); - defaultbg = MAX(LEN(colorname), 256); - alphaUnfocus = alpha-alphaOffset; tnew(cols, rows); xinit(cols, rows); xsetenv(); @@ -2371,4 +2503,3 @@ run: return 0; } - |
