aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorProsperousPotato <ProsperousPotato@users.noreply.github.com>2025-06-14 16:39:03 +0100
committerProsperousPotato <ProsperousPotato@users.noreply.github.com>2025-06-14 16:39:03 +0100
commitc827a90d71ca48a99f5e94a4698574f8146c394f (patch)
treedb2394307cdbb47d2023b0f90ccd842e703e0882
parentb6e3cea71c32e40523375a8949a65568604821c8 (diff)
rewrite from original
-rw-r--r--Makefile27
-rw-r--r--PKGBUILD45
-rw-r--r--boxdraw.c194
-rw-r--r--boxdraw_data.h214
-rw-r--r--config.h207
-rw-r--r--config.mk14
-rw-r--r--disablebold.diff69
-rw-r--r--hb.c154
-rw-r--r--hb.h7
-rw-r--r--st-copyout13
-rw-r--r--st-urlhandler19
-rw-r--r--st.154
-rw-r--r--st.c436
-rw-r--r--st.h105
-rw-r--r--st.info10
-rw-r--r--win.h4
-rw-r--r--x.c879
17 files changed, 1046 insertions, 1405 deletions
diff --git a/Makefile b/Makefile
index 02045f0..e79b89e 100644
--- a/Makefile
+++ b/Makefile
@@ -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) */
-};
diff --git a/config.h b/config.h
index a8d8d13..d28078f 100644
--- a/config.h
+++ b/config.h
@@ -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{|}~";
-
diff --git a/config.mk b/config.mk
index ef6de39..cb2875c 100644
--- a/config.mk
+++ b/config.mk
@@ -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
diff --git a/hb.c b/hb.c
deleted file mode 100644
index 8000afa..0000000
--- a/hb.c
+++ /dev/null
@@ -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);
-}
diff --git a/hb.h b/hb.h
deleted file mode 100644
index b3e02d0..0000000
--- a/hb.h
+++ /dev/null
@@ -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
diff --git a/st.1 b/st.1
index 37f7e84..39120b4 100644
--- a/st.1
+++ b/st.1
@@ -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.
+
diff --git a/st.c b/st.c
index 0f4593e..9f63ac1 100644
--- a/st.c
+++ b/st.c
@@ -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];
+}
diff --git a/st.h b/st.h
index 60d973a..4387e0a 100644
--- a/st.h
+++ b/st.h
@@ -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);
+}
diff --git a/st.info b/st.info
index 8201ad6..ded76c1 100644
--- a/st.info
+++ b/st.info
@@ -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,
diff --git a/win.h b/win.h
index 8839e31..31b3fff 100644
--- a/win.h
+++ b/win.h
@@ -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();
diff --git a/x.c b/x.c
index b1ef2ba..0f775ce 100644
--- a/x.c
+++ b/x.c
@@ -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;
}
-