aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjack <[email protected]>2023-11-27 12:34:55 -0700
committerjack <[email protected]>2023-11-27 12:34:55 -0700
commit6809f2a1a81b70385d653a942ec673192daa41b7 (patch)
tree76ceaf65f1e0b4aa37bc852aad09a0ecb193d694
parentcf34fd0adf148540a79ed6fe6444e1fc3260d2ea (diff)
downloadst-6809f2a1a81b70385d653a942ec673192daa41b7.tar.gz
st-6809f2a1a81b70385d653a942ec673192daa41b7.zip
initial
-rw-r--r--Makefile20
-rw-r--r--boxdraw.c194
-rw-r--r--boxdraw_data.h214
-rw-r--r--config.h510
-rw-r--r--config.mk15
-rw-r--r--hb.c140
-rw-r--r--hb.h7
-rw-r--r--normalMode.c284
-rw-r--r--normalMode.h8
-rw-r--r--patches/st-clipboard-20180309-c5ba9c0.diff13
-rw-r--r--patches/st-glyph-wide-support-20220411-ef05519.diff206
-rw-r--r--patches/st-scrollback-mouse-20220127-2c5edf2.diff25
-rw-r--r--patches/st-scrollback-ringbuffer-0.8.5.diff730
-rwxr-xr-xst-copyout13
-rwxr-xr-xst-urlhandler15
-rw-r--r--st.160
-rw-r--r--st.c846
-rw-r--r--st.desktop8
-rw-r--r--st.h45
-rw-r--r--st.info33
-rw-r--r--st.pngbin0 -> 836 bytes
-rw-r--r--utils.h23
-rw-r--r--win.h4
-rw-r--r--x.c844
-rw-r--r--xresources49
25 files changed, 2542 insertions, 1764 deletions
diff --git a/Makefile b/Makefile
index 470ac86..d79bd9d 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
include config.mk
-SRC = st.c x.c
+SRC = st.c x.c boxdraw.c hb.c
OBJ = $(SRC:.c=.o)
all: options st
@@ -22,7 +22,9 @@ config.h:
$(CC) $(STCFLAGS) -c $<
st.o: config.h st.h win.h
-x.o: arg.h 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
$(OBJ): config.h config.mk
@@ -30,7 +32,7 @@ st: $(OBJ)
$(CC) -o $@ $(OBJ) $(STLDFLAGS)
clean:
- rm -f st $(OBJ) st-$(VERSION).tar.gz
+ rm -f st $(OBJ) st-$(VERSION).tar.gz *.o *.orig *.rej
dist: clean
mkdir -p st-$(VERSION)
@@ -43,15 +45,27 @@ 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
tic -sx st.info
@echo Please see the README file regarding the terminfo entry of st.
+ mkdir -p $(DESTDIR)$(ICONPREFIX)
+ [ -f $(ICONNAME) ] && cp -f $(ICONNAME) $(DESTDIR)$(ICONPREFIX) || :
+ mkdir -p $(DESTDIR)$(PREFIX)/share/applications
+ cp -f st.desktop $(DESTDIR)$(PREFIX)/share/applications
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
+ rm -f $(DESTDIR)$(ICONPREFIX)/$(ICONNAME)
+ rm -f $(DESTDIR)$(PREFIX)/share/applications/st.desktop
.PHONY: all options clean dist install uninstall
diff --git a/boxdraw.c b/boxdraw.c
new file mode 100644
index 0000000..28a92d0
--- /dev/null
+++ b/boxdraw.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2018 Avi Halachmi (:avih) [email protected] 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
new file mode 100644
index 0000000..7890500
--- /dev/null
+++ b/boxdraw_data.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2018 Avi Halachmi (:avih) [email protected] 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 8e4d0a4..39f7037 100644
--- a/config.h
+++ b/config.h
@@ -1,11 +1,24 @@
-#pragma once
+/* See LICENSE file for copyright and license details. */
-static char *font = "JetBrainsMono Nerd Font:pixelsize=15:antialias=true:autohint=true";
-static int borderpx = 8;
+/*
+ * appearance
+ *
+ * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
+ */
+static char *font = "JetBrainsMono Nerd Font :pixelsize=15:antialias=true:autohint=true";
+static char *font2[] = { "JetBrainsMono Nerd Font :pixelsize=15:antialias=true:autohint=true" };
+static int borderpx = 0;
-static char *shell = "/bin/zsh";
+/*
+ * What program is execed by st depends of these precedence rules:
+ * 1: program passed with -e
+ * 2: utmp option
+ * 3: SHELL environment variable
+ * 4: value of shell in /etc/passwd
+ * 5: value of shell in config.h
+ */
+static char *shell = "/bin/sh";
char *utmp = NULL;
-char *scroll = NULL;
char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400";
/* identification sequence returned in DA and DECID */
@@ -29,10 +42,6 @@ static unsigned int tripleclicktimeout = 600;
/* alt screens */
int allowaltscreen = 1;
-/* allow certain non-interactive (insecure) window operations such as:
- setting the clipboard text */
-int allowwindowops = 0;
-
/*
* draw latency range in ms - from new content/keypress/etc until drawing.
* within this range, st draws when content stops arriving (idle). mostly it's
@@ -43,59 +52,120 @@ static double minlatency = 8;
static double maxlatency = 33;
/*
+ * Synchronized-Update timeout in ms
+ * https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec
+ */
+static uint su_timeout = 200;
+
+/*
* blinking timeout (set to 0 to disable blinking) for the terminal blinking
* attribute.
*/
static unsigned int blinktimeout = 800;
/*
+ * interval (in milliseconds) between each successive call to ximspot. This
+ * improves terminal performance while not reducing functionality to those
+ * whom need XIM support.
+ */
+int ximspot_update_interval = 1000;
+
+/*
* thickness of underline and bar cursors
*/
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 = 1;
+
+/*
* bell volume. It must be a value between -100 and 100. Use 0 for disabling
* it
*/
static int bellvolume = 0;
/* default TERM value */
-char *termname = "st";
+char *termname = "st-256color";
/*
* spaces per tab
+ *
+ * When you are changing this value, don't forget to adapt the »it« value in
+ * the st.info and appropriately install the st.info in the environment where
+ * you use this st version.
+ *
+ * it#$tabspaces,
+ *
+ * Secondly make sure your kernel is not expanding tabs. When running `stty
+ * -a` »tab0« should appear. You can tell the terminal to not expand tabs by
+ * running following command:
+ *
+ * stty tabs
*/
-unsigned int tabspaces = 4;
+unsigned int tabspaces = 8;
+
+/* bg opacity */
+float alpha = 1.0;
/* Terminal colors (16 first used in escape sequence) */
static const char *colorname[] = {
- "#1d2021", // or #1C1C1C
- "#cc241d",
- "#98971a",
- "#d79921",
- "#458588",
- "#b16286",
- "#689d6a",
- "#a89984",
- "#928374",
- "#fb4934",
- "#b8bb26",
- "#fabd2f",
- "#83a598",
- "#d3869b",
- "#8ec07c",
- "#ebdbb2"
+ "#282828", /* hard contrast: #1d2021 / soft contrast: #32302f */
+ "#cc241d",
+ "#98971a",
+ "#d79921",
+ "#458588",
+ "#b16286",
+ "#689d6a",
+ "#a89984",
+ "#928374",
+ "#fb4934",
+ "#b8bb26",
+ "#fabd2f",
+ "#83a598",
+ "#d3869b",
+ "#8ec07c",
+ "#ebdbb2",
+ [255] = 0,
+ /* more colors can be added after 255 to use with DefaultXX */
+ "#add8e6", /* 256 -> cursor */
+ "#555555", /* 257 -> rev cursor*/
+ "#282828", /* 258 -> bg */
+ "#ffffff", /* 259 -> fg */
};
+
/*
* Default colors (colorname index)
- * foreground, background, cursor
+ * foreground, background, cursor, reverse cursor
+ */
+unsigned int defaultfg = 259;
+unsigned int defaultbg = 258;
+unsigned int defaultcs = 256;
+unsigned int defaultrcs = 257;
+
+/*
+ * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81
+ * Default style of cursor
+ * 0: Blinking block
+ * 1: Blinking block (default)
+ * 2: Steady block ("█")
+ * 3: Blinking underline
+ * 4: Steady underline ("_")
+ * 5: Blinking bar
+ * 6: Steady bar ("|")
+ * 7: Blinking st cursor
+ * 8: Steady st cursor
*/
-unsigned int defaultfg = 15;
-unsigned int defaultbg = 0;
-unsigned int defaultcs = 15;
-static unsigned int defaultrcs = 0;
-static unsigned int cursorshape = 2;
+static unsigned int cursorshape = 1;
/*
* Default columns and rows numbers
@@ -118,47 +188,373 @@ static unsigned int mousebg = 0;
static unsigned int defaultattr = 11;
/*
- * 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.
+ * Xresources preferences to load at startup
*/
-static uint forcemousemod = ShiftMask;
+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 },
+ { "blinktimeout", INTEGER, &blinktimeout },
+ { "bellvolume", INTEGER, &bellvolume },
+ { "tabspaces", INTEGER, &tabspaces },
+ { "borderpx", INTEGER, &borderpx },
+ { "cwscale", FLOAT, &cwscale },
+ { "chscale", FLOAT, &chscale },
+ { "alpha", FLOAT, &alpha },
+ { "ximspot_update_interval", INTEGER, &ximspot_update_interval },
+};
/*
* Internal mouse shortcuts.
* Beware that overloading Button1 will disable the selection.
*/
+const unsigned int mousescrollincrement = 3;
static MouseShortcut mshortcuts[] = {
- /* mask button function argument release */
- { ShiftMask, Button4, kscrollup, {.i = 1} },
- { ShiftMask, Button5, kscrolldown, {.i = 1} }
+ /* button mask string */
+ { Button4, XK_NO_MOD, "\031" },
+ { Button5, XK_NO_MOD, "\005" },
};
/* Internal keyboard shortcuts. */
-#define TERMMOD (ControlMask|ShiftMask)
+#define MODKEY Mod1Mask
+#define TERMMOD (Mod4Mask|ShiftMask)
+
+MouseKey mkeys[] = {
+ /* button mask function argument */
+ { Button4, XK_NO_MOD, kscrollup, {.i = mousescrollincrement} },
+ { Button5, XK_NO_MOD, kscrolldown, {.i = mousescrollincrement} },
+ { Button4, Mod4Mask, zoom, {.f = +1} },
+ { Button5, Mod4Mask, zoom, {.f = -1} },
+};
+
+static char *openurlcmd[] = { "/bin/sh", "-c", "st-urlhandler", "externalpipe", NULL };
+
+static char *copyurlcmd[] = { "/bin/sh", "-c",
+ "tmp=$(sed 's/.*│//g' | tr -d '\n' | grep -aEo '(((http|https|gopher|gemini|ftp|ftps|git)://|www\\.)[a-zA-Z0-9.]*[:]?[a-zA-Z0-9./@$&%?$#=_-~]*)|((magnet:\\?xt=urn:btih:)[a-zA-Z0-9]*)' | uniq | sed 's/^www./http:\\/\\/www\\./g' ); IFS=; [ ! -z $tmp ] && echo $tmp | dmenu -i -p 'Copy which url?' -l 10 | tr -d '\n' | xclip -selection clipboard",
+ "externalpipe", NULL };
+
+static char *copyoutput[] = { "/bin/sh", "-c", "st-copyout", "externalpipe", NULL };
static Shortcut shortcuts[] = {
- /* mask keysym function argument */
- { TERMMOD, XK_C, clipcopy, {.i = 0} },
- { TERMMOD, XK_V, clippaste, {.i = 0} }
+ /* mask keysym function argument */
+ { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} },
+ { ControlMask, XK_Print, toggleprinter, {.i = 0} },
+ { ShiftMask, XK_Print, printscreen, {.i = 0} },
+ { XK_ANY_MOD, XK_Print, printsel, {.i = 0} },
+ { MODKEY, XK_comma, zoom, {.f = +1} },
+ { MODKEY, XK_period, zoom, {.f = -1} },
+ { MODKEY, XK_g, zoomreset, {.f = 0} },
+ { ControlMask | ShiftMask, XK_C, clipcopy, {.i = 0} },
+ { ShiftMask, XK_Insert, clippaste, {.i = 0} },
+ { ControlMask | ShiftMask, XK_V, clippaste, {.i = 0} },
+ { XK_ANY_MOD, Button2, selpaste, {.i = 0} },
+ { MODKEY, XK_Num_Lock, numlock, {.i = 0} },
+ { ControlMask | ShiftMask, XK_U, iso14755, {.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_u, kscrollup, {.i = -1} },
+ { MODKEY, XK_d, kscrolldown, {.i = -1} },
+ { MODKEY, XK_s, changealpha, {.f = -0.05} },
+ { MODKEY, XK_a, changealpha, {.f = +0.05} },
+ { MODKEY, XK_m, changealpha, {.f = +2.00} },
+ { 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 } },
+ { TERMMOD, XK_Return, newterm, {.i = 0} },
+
};
/*
+ * Special keys (change & recompile st.info accordingly)
+ *
+ * Mask value:
+ * * Use XK_ANY_MOD to match the key no matter modifiers state
+ * * Use XK_NO_MOD to match the key alone (no modifiers)
+ * appkey value:
+ * * 0: no value
+ * * > 0: keypad application mode enabled
+ * * = 2: term.numlock = 1
+ * * < 0: keypad application mode disabled
+ * appcursor value:
+ * * 0: no value
+ * * > 0: cursor application mode enabled
+ * * < 0: cursor application mode disabled
+ * crlf value
+ * * 0: no value
+ * * > 0: crlf mode is enabled
+ * * < 0: crlf mode is disabled
+ *
+ * Be careful with the order of the definitions because st searches in
+ * this table sequentially, so any XK_ANY_MOD must be in the last
+ * position for a key.
+ */
+
+/*
* If you want keys other than the X11 function keys (0xFD00 - 0xFFFF)
* to be mapped below, add them to this array.
*/
static KeySym mappedkeys[] = { -1 };
+/*
+ * State bits to ignore when matching key or button events. By default,
+ * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored.
+ */
+static uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
+
+/*
+ * Override mouse-select 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 forceselmod = ShiftMask;
+
+/*
+ * This is the huge key array which defines all compatibility to the Linux
+ * world. Please decide about changes wisely.
+ */
static Key key[] = {
- /* keysym mask string appkey appcursor */
- { XK_Up, XK_ANY_MOD, "\033[A", 0, -1},
- { XK_Up, XK_ANY_MOD, "\033OA", 0, +1},
- { XK_Down, XK_ANY_MOD, "\033[B", 0, -1},
- { XK_Down, XK_ANY_MOD, "\033OB", 0, +1},
- { XK_Left, XK_ANY_MOD, "\033[D", 0, -1},
- { XK_Left, XK_ANY_MOD, "\033OD", 0, +1},
- { XK_Right, XK_ANY_MOD, "\033[C", 0, -1},
- { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}
+ /* keysym mask string appkey appcursor */
+ { XK_KP_Home, ShiftMask, "\033[2J", 0, -1},
+ { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1},
+ { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1},
+ { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1},
+ { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0},
+ { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1},
+ { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1},
+ { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0},
+ { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1},
+ { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1},
+ { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0},
+ { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1},
+ { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1},
+ { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0},
+ { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1},
+ { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1},
+ { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0},
+ { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0},
+ { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0},
+ { XK_KP_End, ControlMask, "\033[J", -1, 0},
+ { XK_KP_End, ControlMask, "\033[1;5F", +1, 0},
+ { XK_KP_End, ShiftMask, "\033[K", -1, 0},
+ { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0},
+ { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0},
+ { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0},
+ { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0},
+ { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0},
+ { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0},
+ { XK_KP_Insert, ControlMask, "\033[L", -1, 0},
+ { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0},
+ { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0},
+ { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0},
+ { XK_KP_Delete, ControlMask, "\033[M", -1, 0},
+ { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0},
+ { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0},
+ { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0},
+ { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0},
+ { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0},
+ { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0},
+ { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0},
+ { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0},
+ { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0},
+ { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0},
+ { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0},
+ { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0},
+ { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0},
+ { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0},
+ { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0},
+ { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0},
+ { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0},
+ { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0},
+ { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0},
+ { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0},
+ { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0},
+ { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0},
+ { XK_Up, ShiftMask, "\033[1;2A", 0, 0},
+ { XK_Up, Mod1Mask, "\033[1;3A", 0, 0},
+ { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0},
+ { XK_Up, ControlMask, "\033[1;5A", 0, 0},
+ { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0},
+ { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0},
+ { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0},
+ { XK_Up, XK_ANY_MOD, "\033[A", 0, -1},
+ { XK_Up, XK_ANY_MOD, "\033OA", 0, +1},
+ { XK_Down, ShiftMask, "\033[1;2B", 0, 0},
+ { XK_Down, Mod1Mask, "\033[1;3B", 0, 0},
+ { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0},
+ { XK_Down, ControlMask, "\033[1;5B", 0, 0},
+ { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0},
+ { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0},
+ { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0},
+ { XK_Down, XK_ANY_MOD, "\033[B", 0, -1},
+ { XK_Down, XK_ANY_MOD, "\033OB", 0, +1},
+ { XK_Left, ShiftMask, "\033[1;2D", 0, 0},
+ { XK_Left, Mod1Mask, "\033[1;3D", 0, 0},
+ { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0},
+ { XK_Left, ControlMask, "\033[1;5D", 0, 0},
+ { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0},
+ { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0},
+ { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0},
+ { XK_Left, XK_ANY_MOD, "\033[D", 0, -1},
+ { XK_Left, XK_ANY_MOD, "\033OD", 0, +1},
+ { XK_Right, ShiftMask, "\033[1;2C", 0, 0},
+ { XK_Right, Mod1Mask, "\033[1;3C", 0, 0},
+ { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0},
+ { XK_Right, ControlMask, "\033[1;5C", 0, 0},
+ { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0},
+ { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0},
+ { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0},
+ { XK_Right, XK_ANY_MOD, "\033[C", 0, -1},
+ { XK_Right, XK_ANY_MOD, "\033OC", 0, +1},
+ { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0},
+ { XK_Return, Mod1Mask, "\033\r", 0, 0},
+ { XK_Return, XK_ANY_MOD, "\r", 0, 0},
+ { XK_Insert, ShiftMask, "\033[4l", -1, 0},
+ { XK_Insert, ShiftMask, "\033[2;2~", +1, 0},
+ { XK_Insert, ControlMask, "\033[L", -1, 0},
+ { XK_Insert, ControlMask, "\033[2;5~", +1, 0},
+ { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0},
+ { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0},
+ { XK_Delete, ControlMask, "\033[M", -1, 0},
+ { XK_Delete, ControlMask, "\033[3;5~", +1, 0},
+ { XK_Delete, ShiftMask, "\033[2K", -1, 0},
+ { XK_Delete, ShiftMask, "\033[3;2~", +1, 0},
+ { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0},
+ { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0},
+ { XK_BackSpace, XK_NO_MOD, "\177", 0, 0},
+ { XK_BackSpace, Mod1Mask, "\033\177", 0, 0},
+ { XK_Home, ShiftMask, "\033[2J", 0, -1},
+ { XK_Home, ShiftMask, "\033[1;2H", 0, +1},
+ { XK_Home, XK_ANY_MOD, "\033[H", 0, -1},
+ { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1},
+ { XK_End, ControlMask, "\033[J", -1, 0},
+ { XK_End, ControlMask, "\033[1;5F", +1, 0},
+ { XK_End, ShiftMask, "\033[K", -1, 0},
+ { XK_End, ShiftMask, "\033[1;2F", +1, 0},
+ { XK_End, XK_ANY_MOD, "\033[4~", 0, 0},
+ { XK_Prior, ControlMask, "\033[5;5~", 0, 0},
+ { XK_Prior, ShiftMask, "\033[5;2~", 0, 0},
+ { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0},
+ { XK_Next, ControlMask, "\033[6;5~", 0, 0},
+ { XK_Next, ShiftMask, "\033[6;2~", 0, 0},
+ { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0},
+ { XK_F1, XK_NO_MOD, "\033OP" , 0, 0},
+ { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0},
+ { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0},
+ { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0},
+ { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0},
+ { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0},
+ { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0},
+ { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0},
+ { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0},
+ { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0},
+ { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0},
+ { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0},
+ { XK_F3, XK_NO_MOD, "\033OR" , 0, 0},
+ { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0},
+ { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0},
+ { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0},
+ { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0},
+ { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0},
+ { XK_F4, XK_NO_MOD, "\033OS" , 0, 0},
+ { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0},
+ { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0},
+ { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0},
+ { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0},
+ { XK_F5, XK_NO_MOD, "\033[15~", 0, 0},
+ { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0},
+ { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0},
+ { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0},
+ { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0},
+ { XK_F6, XK_NO_MOD, "\033[17~", 0, 0},
+ { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0},
+ { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0},
+ { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0},
+ { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0},
+ { XK_F7, XK_NO_MOD, "\033[18~", 0, 0},
+ { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0},
+ { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0},
+ { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0},
+ { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0},
+ { XK_F8, XK_NO_MOD, "\033[19~", 0, 0},
+ { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0},
+ { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0},
+ { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0},
+ { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0},
+ { XK_F9, XK_NO_MOD, "\033[20~", 0, 0},
+ { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0},
+ { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0},
+ { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0},
+ { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0},
+ { XK_F10, XK_NO_MOD, "\033[21~", 0, 0},
+ { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0},
+ { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0},
+ { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0},
+ { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0},
+ { XK_F11, XK_NO_MOD, "\033[23~", 0, 0},
+ { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0},
+ { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0},
+ { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0},
+ { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0},
+ { XK_F12, XK_NO_MOD, "\033[24~", 0, 0},
+ { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0},
+ { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0},
+ { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0},
+ { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0},
+ { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0},
+ { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0},
+ { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0},
+ { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0},
+ { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0},
+ { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0},
+ { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0},
+ { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0},
+ { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0},
+ { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0},
+ { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0},
+ { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0},
+ { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0},
+ { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0},
+ { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0},
+ { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0},
+ { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0},
+ { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0},
+ { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0},
+ { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0},
+ { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0},
+ { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0},
+ { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0},
};
/*
@@ -169,7 +565,7 @@ static Key key[] = {
* If no match is found, regular selection is used.
*/
static uint selmasks[] = {
- [SEL_RECTANGULAR] = Mod1Mask,
+ [SEL_RECTANGULAR] = Mod1Mask,
};
/*
@@ -177,6 +573,6 @@ static uint selmasks[] = {
* of single wide characters.
*/
static char ascii_printable[] =
- " !\"#$%&'()*+,-./0123456789:;<=>?"
- "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
- "`abcdefghijklmnopqrstuvwxyz{|}~";
+" !\"#$%&'()*+,-./0123456789:;<=>?"
+"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+"`abcdefghijklmnopqrstuvwxyz{|}~";
diff --git a/config.mk b/config.mk
index 5d43331..154d609 100644
--- a/config.mk
+++ b/config.mk
@@ -1,11 +1,13 @@
# st version
-VERSION = 0.9
+VERSION = 0.8.5
# Customize below to fit your system
# paths
PREFIX = /usr/local
MANPREFIX = $(PREFIX)/share/man
+ICONPREFIX = $(PREFIX)/share/pixmaps
+ICONNAME = st.png
X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib
@@ -14,14 +16,18 @@ PKG_CONFIG = pkg-config
# includes and libs
INCS = -I$(X11INC) \
+ `$(PKG_CONFIG) --cflags glib-2.0` \
`$(PKG_CONFIG) --cflags fontconfig` \
- `$(PKG_CONFIG) --cflags freetype2`
+ `$(PKG_CONFIG) --cflags freetype2` \
+ `$(PKG_CONFIG) --cflags harfbuzz`
LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
+ `$(PKG_CONFIG) --libs glib-2.0` \
`$(PKG_CONFIG) --libs fontconfig` \
- `$(PKG_CONFIG) --libs freetype2`
+ `$(PKG_CONFIG) --libs freetype2` \
+ `$(PKG_CONFIG) --libs harfbuzz`
# flags
-STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+STCPPFLAGS = -DVERSION=\"$(VERSION)\" -DICON=\"$(ICONPREFIX)/$(ICONNAME)\" -D_XOPEN_SOURCE=600
STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
STLDFLAGS = $(LIBS) $(LDFLAGS)
@@ -30,7 +36,6 @@ 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 = gcc
diff --git a/hb.c b/hb.c
new file mode 100644
index 0000000..467bcac
--- /dev/null
+++ b/hb.c
@@ -0,0 +1,140 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <X11/Xft/Xft.h>
+#include <hb.h>
+#include <hb-ft.h>
+
+#include "st.h"
+
+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(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, NULL, 0);
+
+ /* 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
new file mode 100644
index 0000000..b3e02d0
--- /dev/null
+++ b/hb.h
@@ -0,0 +1,7 @@
+#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/normalMode.c b/normalMode.c
new file mode 100644
index 0000000..f0c7e30
--- /dev/null
+++ b/normalMode.c
@@ -0,0 +1,284 @@
+#include <X11/keysym.h>
+#include <X11/XKBlib.h>
+
+#include "normalMode.h"
+#include "utils.h"
+
+extern Glyph const styleSearch, style[];
+extern char const wDelS[], wDelL[], *nmKeys[];
+extern unsigned int bg[], fg, currentBg, highlightBg, highlightFg, amountNmKeys;
+
+typedef struct { int p[3]; } Pos;
+
+typedef enum {visual='v', visualLine='V', yank = 'y'} Op;
+typedef enum {infix_none=0, infix_i='i', infix_a='a'} Infix;
+typedef enum {fw='/', bw='?'} Search;
+struct NormalModeState {
+ struct OperationState { Op op; Infix infix; } cmd;
+ struct MotionState { uint32_t c; int active; Pos searchPos; Search search; } m;
+} defaultNormalMode, state;
+
+DynamicArray searchStr=UTF8_ARRAY, cCmd=UTF8_ARRAY, lCmd=UTF8_ARRAY;
+Glyph styleCmd;
+char posBuffer[10], braces[6][3] = { {"()"}, {"<>"}, {"{}"}, {"[]"}, {"\"\""}, {"''"}};
+int exited=1, overlay=1;
+static inline Rune cChar() { return term.line[term.c.y][term.c.x].u; }
+static inline int pos(int p, int h) {return IS_SET(MODE_ALTSCREEN)?p:rangeY(p+h*histOff-insertOff);}
+static inline int contains(Rune l, char const * values, size_t const memSize) {
+ for (uint32_t i = 0; i < memSize; ++i) if (l == values[i]) return 1;
+ return 0;
+}
+static inline void decodeTo(char const *cs, size_t len, DynamicArray *arr) {
+ char *var = expand(arr);
+ if (!var) empty(arr); else utf8decode(cs, (Rune*)(var), len);
+}
+static inline void applyPos(Pos p) {
+ term.c.x = p.p[0], term.c.y = p.p[1];
+ if (!IS_SET(MODE_ALTSCREEN) && histOp) term.line = &buf[histOff = p.p[2]];
+}
+/// Find string in history buffer, and provide string-match-lookup for highlighting matches
+static int highlighted(int x, int y) {
+ int const s=term.row*term.col, i=y*term.col+x, sz=size(&searchStr);
+ return sz && i<s && mark[i]!=sz && i+mark[i]<s && !mark[i+mark[i]];
+}
+static void markSearchMatches(int all) {
+ int sz = size(&searchStr), ox = 0, oy = 0, oi=0;
+ for (int y=0; sz && all && y<term.row; ++y)
+ for (int x=0; x<term.col; ++x) term.dirty[y] |= highlighted(x, y);
+ for (int y = 0, wi=0, owi=0, i=0; sz && y < term.row; ++y)
+ for (int x=0; x<term.col; ++x, wi%=sz, ++i, owi=wi)
+ if (all || term.dirty[y]) {
+ mark[i]=sz-(wi=(getU32(&searchStr,wi,1)==term.line[y][x].u?wi+1:0));
+ if (wi==1) ox=x, oy=y, oi=i; else if (!wi && owi) x=ox, y=oy, i=oi;
+ }
+ for (int y=0; sz &&all &&y<term.row; ++y)
+ for (int x=0; x<term.col; ++x) term.dirty[y] |= highlighted(x, y);
+}
+static int findString(int s, int all) {
+ Pos p = (Pos) {.p={term.c.x, term.c.y, IS_SET(MODE_ALTSCREEN) ? 0 : histOff}};
+ historyMove(s, 0, 0);
+ uint32_t strSz=size(&searchStr), maxIter=rows()*term.col+strSz, wIdx=0;
+ for (uint32_t i=0, wi = 0; wIdx<strSz && ++i<=maxIter; historyMove(s, 0, 0), wi=wIdx) {
+ wIdx = (getU32(&searchStr, wIdx, s>0)==cChar())?wIdx+1:0;
+ if (wi && !wIdx) historyMove(-(int)(s*wi), 0, 0);
+ }
+ if (wIdx == strSz && wIdx) historyMove(-(int)(s*strSz), 0, 0);
+ else applyPos(p);
+ markSearchMatches(all);
+ return wIdx == strSz;
+}
+/// Execute series of normal-mode commands from char array / decoded from dynamic array
+ExitState pressKeys(char const* s, size_t e) {
+ ExitState x=success;
+ for (size_t i=0; i<e && (x=(!s[i] ? x : kPressHist(&s[i], 1, 0, NULL))); ++i);
+ return x;
+}
+static ExitState executeCommand(uint32_t *cs, size_t z) {
+ ExitState x=success;
+ char dc [32];
+ for (size_t i=0; i<z && (x=kPressHist(dc, utf8encode(cs[i],dc),0,NULL));++i);
+ return x;
+}
+/// Get character for overlay, if the overlay (st) has something to show, else normal char.
+static void getChar(DynamicArray *st, Glyph *glyphChange, int y, int xEnd, int width, int x) {
+ if (x < xEnd - min(min(width,xEnd), size(st))) *glyphChange = term.line[y][x];
+ else if (x<xEnd) glyphChange->u = *((Rune*)(st->content + (size(st)+x-xEnd)*st->elSize));
+}
+/// Expand "infix" expression: for instance (w =>) l b | | v e | | y
+static ExitState expandExpression(char l) { // ({ =>) l ? { \n | l | v / } \n | h | y
+ int a=state.cmd.infix==infix_a, yank=state.cmd.op=='y', lc=tolower(l), found=1;
+ state.cmd.infix = infix_none;
+ if(!yank && state.cmd.op!=visual && state.cmd.op!=visualLine) return failed;
+ char mot[11] = {'l', 0, 'b', 0, 0, 'v', 0, 'e', 0, 0, (char)(yank ? 'y' : 0)};
+ if (lc == 'w') mot[2] = (char) ('b' - lc + l), mot[7] = (char) ((a ? 'w' : 'e') - lc + l), mot[9]=(char)(a?'h':0);
+ else {
+ mot[1]='?', mot[3]=mot[8]='\n', mot[6]='/', mot[4]=(char)(a?0:'l'), mot[9]=(char)(a?0:'h');
+ for (int i=found=0; !found && i < 6; ++i)
+ if ((found=contains(l,braces[i],2))) mot[2]=braces[i][0], mot[7]=braces[i][1];
+ }
+ if (!found) return failed;
+ assign(&lCmd, &cCmd);
+ empty(&cCmd);
+ state.cmd = defaultNormalMode.cmd;
+ return pressKeys(mot, 11);
+}
+
+ExitState executeMotion(char const cs, KeySym const *const ks) {
+ state.m.c = state.m.c < 1u ? 1u : state.m.c;
+ if (ks && *ks == XK_d) historyMove(0, 0, term.row / 2);
+ else if (ks && *ks == XK_u) historyMove(0, 0, -term.row / 2);
+ else if (ks && *ks == XK_f) historyMove(0, 0, term.row-1+(term.c.y=0));
+ else if (ks && *ks == XK_b) historyMove(0, 0, -(term.c.y=term.row-1));
+ else if (ks && *ks == XK_h) overlay = !overlay;
+ else if (cs == 'K') historyMove(0, 0, -(int)state.m.c);
+ else if (cs == 'J') historyMove(0, 0, (int)state.m.c);
+ else if (cs == 'k') historyMove(0, -(int)state.m.c, 0);
+ else if (cs == 'j') historyMove(0, (int)state.m.c, 0);
+ else if (cs == 'h') historyMove(-(int)state.m.c, 0, 0);
+ else if (cs == 'l') historyMove( (int)state.m.c, 0, 0);
+ else if (cs == 'H') term.c.y = 0;
+ else if (cs == 'M') term.c.y = term.bot / 2;
+ else if (cs == 'L') term.c.y = term.bot;
+ else if (cs == 's' || cs == 'S') altToggle = cs == 's' ? !altToggle : 1;
+ else if (cs == 'G' || cs == 'g') {
+ if (cs == 'G') term.c = c[0] = c[IS_SET(MODE_ALTSCREEN)+1];
+ if (!IS_SET(MODE_ALTSCREEN)) term.line = &buf[histOff=insertOff];
+ } else if (cs == '0') term.c.x = 0;
+ else if (cs == '$') term.c.x = term.col-1;
+ else if (cs == 't') sel.type = sel.type==SEL_REGULAR ? SEL_RECTANGULAR : SEL_REGULAR;
+ else if (cs == 'n' || cs == 'N') {
+ int const d = ((cs=='N')!=(state.m.search==bw))?-1:1;
+ for (uint32_t i = state.m.c; i && findString(d, 0); --i);
+ } else if (contains(cs, "wWeEbB", 6)) {
+ int const low=cs<=90, off=tolower(cs)!='w', sgn=(tolower(cs)=='b')?-1:1;
+ size_t const l=strlen(wDelL), s=strlen(wDelS), maxIt=rows()*term.col;
+ for (int it=0, on=0; state.m.c > 0 && it < maxIt; ++it) {
+ // If an offset is to be performed in beginning or not in beginning, move in history.
+ if ((off || it) && historyMove(sgn, 0, 0)) break;
+ // Determine if the category of the current letter changed since last iteration.
+ int n = 1<<(contains(cChar(),wDelS,s) ?(2-low) :!contains(cChar(),wDelL,l)),
+ found = (on|=n)^n && ((off ?on^n :n)!=1);
+ // If a reverse offset is to be performed and this is the last letter:
+ if (found && off) historyMove(-sgn, 0, 0);
+ // Terminate iteration: reset #it and old n value #on and decrease operation count:
+ if (found) it=-1, on=0, --state.m.c;
+ }
+ } else return failed;
+ state.m.c = 0;
+ return state.cmd.op == yank ? exitMotion : success;
+}
+
+ExitState kPressHist(char const *cs, size_t len, int ctrl, KeySym const *kSym) {
+ historyOpToggle(1, 1);
+ int const prevYOff=IS_SET(MODE_ALTSCREEN)?0:histOff, search=state.m.search&&state.m.active,
+ prevAltToggle=altToggle, prevOverlay=overlay;
+ int const noOp=!state.cmd.op&&!state.cmd.infix, num=len==1&&BETWEEN(cs[0],48,57),
+ esc=kSym&&*kSym==XK_Escape, ret=(kSym&&*kSym==XK_Return)||(len==1&&cs[0]=='\n'),
+ quantifier=num&&(cs[0]!='0'||state.m.c), ins=!search &&noOp &&len &&cs[0]=='i';
+ exited = 0;
+ ExitState result = success;
+ if (esc || ret || ins) { result = exitMotion, len = 0;
+ } else if (kSym && *kSym == XK_BackSpace) {
+ if ((search || state.m.c) && size(&cCmd)) pop(&cCmd);
+ if (search) {
+ if (size(&searchStr)) pop(&searchStr);
+ else result = exitMotion;
+ if (!size(&searchStr)) tfulldirt();
+ applyPos(state.m.searchPos);
+ findString(state.m.search==fw ? 1 : -1, 1);
+ } else if (state.m.c) state.m.c /= 10;
+ len = 0;
+ } else if (search) {
+ if (len >= 1) decodeTo(cs, len, &searchStr);
+ applyPos(state.m.searchPos);
+ findString(state.m.search==fw ? 1 : -1, 1);
+ } else if (len == 0) { result = failed;
+ } else if (quantifier) { state.m.c = min(SHRT_MAX, (int)state.m.c*10+cs[0]-48);
+ } else if (state.cmd.infix && state.cmd.op && (result = expandExpression(cs[0]), len=0)) {
+ } else if (cs[0] == 'd') { state = defaultNormalMode; result = exitMotion; state.m.active = 1;
+ } else if (cs[0] == '.') {
+ if (size(&cCmd)) assign(&lCmd, &cCmd);
+ empty(&cCmd);
+ executeCommand((uint32_t*) lCmd.content, size(&lCmd));
+ empty(&cCmd);
+ len = 0;
+ } else if (cs[0] == 'r') { tfulldirt();
+ } else if (cs[0] == 'c') {
+ empty(&lCmd);
+ empty(&cCmd);
+ empty(&searchStr);
+ tfulldirt();
+ len = 0;
+ } else if (cs[0] == fw || cs[0] == bw) {
+ empty(&searchStr);
+ state.m.search = (Search) cs[0];
+ state.m.searchPos = (Pos){.p={term.c.x, term.c.y, prevYOff}};
+ state.m.active = 1;
+ } else if (cs[0]==infix_i || cs[0]==infix_a) { state.cmd.infix=(Infix) cs[0];
+ } else if (cs[0] == 'y') {
+ if (state.cmd.op) {
+ result = (state.cmd.op == yank || state.cmd.op == visualLine) ? exitOp : exitMotion;
+ if (state.cmd.op == yank) selstart(0, term.c.y, 0);
+ } else selstart(term.c.x, term.c.y, 0);
+ state.cmd.op = yank;
+ } else if (cs[0] == visual || cs[0] == visualLine) {
+ if (state.cmd.op != (Op) cs[0]) {
+ state.cmd = defaultNormalMode.cmd;
+ state.cmd.op = (Op) cs[0];
+ selstart(cs[0] == visualLine ?0 :term.c.x, term.c.y, 0);
+ } else result = exitOp;
+ } else if (!(result =executeMotion((char) (len?cs[0]:0), ctrl?kSym:NULL))) {
+ result=failed;
+ for (size_t i = 0; !ctrl && i < amountNmKeys; ++i)
+ if (cs[0]==nmKeys[i][0] &&
+ failed!=(result=pressKeys(&nmKeys[i][1], strlen(nmKeys[i])-1))) goto end;
+ } // Operation/Motion finished if valid: update cmd string, extend selection, update search
+ if (result != failed) {
+ if (len == 1 && !ctrl) decodeTo(cs, len, &cCmd);
+ if ((state.cmd.op == visualLine) || ((state.cmd.op == yank) && (result == exitOp))) {
+ int const off = term.c.y + (IS_SET(MODE_ALTSCREEN) ? 0 : histOff) < sel.ob.y; //< Selection start below end.
+ sel.ob.x = off ? term.col - 1 : 0;
+ selextend(off ? 0 : term.col-1, term.c.y, sel.type, 0);
+ } else if (sel.oe.x != -1) {
+ selextend(term.c.x, term.c.y, sel.type, 0);
+ }
+ } // Set repaint for motion or status bar
+ if (!IS_SET(MODE_ALTSCREEN) && prevYOff != histOff) tfulldirt();
+ // Terminate Motion / operation if thus indicated
+ if (result == exitMotion) {
+ if (!state.m.active) result = (exited=noOp) ? finish : exitOp;
+ state.m.active = (int) (state.m.c = 0u);
+ }
+ if (result == exitOp || result == finish) {
+ if (state.cmd.op == yank) {
+ xsetsel(getsel());
+ xclipcopy();
+ }
+ state = defaultNormalMode;
+ selclear();
+ if (!esc) assign(&lCmd, &cCmd);
+ empty(&cCmd);
+ } // Update the content displayed in the history overlay
+ styleCmd = style[state.cmd.op==yank ? 1 : (state.cmd.op==visual ? 2 :
+ (state.cmd.op==visualLine ? 3 :0))];
+ int const posLin = !IS_SET(MODE_ALTSCREEN) ? rangeY(insertOff-histOff):0, h=rows()-term.row;
+ if (!posLin || posLin==h || !h) strcpy(posBuffer, posLin ? " [BOT] " : " [TOP] ");
+ else sprintf(posBuffer, " % 3d%c ", min(100, max(0, (int)(.5 + posLin * 100. / h))),'%');
+ if ((overlay || overlay!=prevOverlay) && term.col>9 && term.row>4) {
+ if (!term.dirty[term.row-1]) xdrawline(term.line[term.row-1], term.col*2/3, term.row-1, term.col-1);
+ if (!term.dirty[term.row-2]) xdrawline(term.line[term.row-2], term.col*2/3, term.row-2, term.col-1);
+ }
+ if (result==finish) altToggle = 0;
+ if (altToggle != prevAltToggle) tswapscreen();
+end:
+ historyOpToggle(-1, 1);
+ return result;
+}
+
+void historyOverlay(int x, int y, Glyph* g) {
+ if (!histMode) return;
+ TCursor const *cHist = histOp ? &term.c : &c[0];
+ if(overlay && term.col > 9 && term.row > 4 && (x > (2*term.col/3)) && (y >= (term.row-2))) {
+ *g = (y == term.row - 2) ? styleSearch : styleCmd;
+ if (y == term.row-2) getChar(&searchStr, g, term.row-2, term.col-2, term.col/3, x);
+ else if (x > term.col - 7) g->u = (Rune)(posBuffer[x - term.col + 7]);
+ else getChar(size(&cCmd) ?&cCmd :&lCmd, g, term.row-1, term.col-7, term.col/3-6, x);
+ } else if (highlighted(x, y)) g->bg = highlightBg, g->fg = highlightFg;
+ else if ((x==cHist->x) ^ (y==cHist->y)) g->bg = currentBg;
+ else if (x==cHist->x) g->mode^=ATTR_REVERSE;
+}
+void historyPreDraw() {
+ static Pos op = {.p={0, 0, 0}};
+ historyOpToggle(1, 0);
+ // Draw the cursor cross if changed
+ if (term.c.y >= term.row || op.p[1] >= term.row) tfulldirt();
+ else if (exited || (op.p[1] != term.c.y)) term.dirty[term.c.y] = term.dirty[op.p[1]] = 1;
+ for (int i=0; (exited || term.c.x != op.p[0]) && i<term.row; ++i) if (!term.dirty[i]) {
+ xdrawline(term.line[i], term.c.x, i, term.c.x + 1);
+ xdrawline(term.line[i], op.p[0], i, op.p[0] + 1);
+ }
+ // Update search results either only for lines with new content or all results if exiting
+ markSearchMatches(exited);
+ op = (Pos){.p = {term.c.x, term.c.y, 0}};
+ historyOpToggle(-1, 0);
+}
diff --git a/normalMode.h b/normalMode.h
new file mode 100644
index 0000000..eb77484
--- /dev/null
+++ b/normalMode.h
@@ -0,0 +1,8 @@
+void normalMode();
+void historyPreDraw();
+void historyOverlay(int x, int y, Glyph* g);
+void historyModeToggle(int start);
+void historyOpToggle(int, int);
+typedef enum {failed=0, success=1, exitMotion=2, exitOp=3, finish=4} ExitState;
+ExitState kPressHist(char const *txt, size_t len, int ctrl, KeySym const *kSym);
+ExitState pressKeys(char const* s, size_t e);
diff --git a/patches/st-clipboard-20180309-c5ba9c0.diff b/patches/st-clipboard-20180309-c5ba9c0.diff
deleted file mode 100644
index 4a14e2b..0000000
--- a/patches/st-clipboard-20180309-c5ba9c0.diff
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/x.c b/x.c
-index 13fa9f4..88fd341 100644
---- a/x.c
-+++ b/x.c
-@@ -624,6 +624,8 @@ setsel(char *str, Time t)
- XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
- if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
- selclear();
-+
-+ xclipcopy();
- }
-
- void
diff --git a/patches/st-glyph-wide-support-20220411-ef05519.diff b/patches/st-glyph-wide-support-20220411-ef05519.diff
deleted file mode 100644
index f88c5ee..0000000
--- a/patches/st-glyph-wide-support-20220411-ef05519.diff
+++ /dev/null
@@ -1,206 +0,0 @@
-From 68de38fadd04f2f454bceccea0fccc8276b635cb Mon Sep 17 00:00:00 2001
-From: wael <[email protected]>
-Date: Mon, 11 Apr 2022 16:45:49 +0300
-Subject: [PATCH] add glyph wide support patch
-
----
- st.h | 6 +++
- x.c | 134 +++++++++++++++++++++++++++++------------------------------
- 2 files changed, 73 insertions(+), 67 deletions(-)
-
-diff --git a/st.h b/st.h
-index 519b9bd..4f74621 100644
---- a/st.h
-+++ b/st.h
-@@ -36,6 +36,12 @@ enum glyph_attribute {
- ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
- };
-
-+enum drawing_mode {
-+ DRAW_NONE = 0,
-+ DRAW_BG = 1 << 0,
-+ DRAW_FG = 1 << 1,
-+};
-+
- enum selection_mode {
- SEL_IDLE = 0,
- SEL_EMPTY = 1,
-diff --git a/x.c b/x.c
-index 2a3bd38..d60df52 100644
---- a/x.c
-+++ b/x.c
-@@ -142,7 +142,7 @@ typedef struct {
-
- 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 xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int);
- static void xdrawglyph(Glyph, int, int);
- static void xclear(int, int, int, int);
- static int xgeommasktogravity(int);
-@@ -1372,7 +1372,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
- }
-
- void
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int dmode)
- {
- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
-@@ -1463,47 +1463,40 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
- if (base.mode & ATTR_INVISIBLE)
- fg = bg;
-
-- /* Intelligent cleaning up of the borders. */
-- if (x == 0) {
-- xclear(0, (y == 0)? 0 : winy, borderpx,
-- winy + win.ch +
-- ((winy + win.ch >= borderpx + win.th)? win.h : 0));
-- }
-- if (winx + width >= borderpx + win.tw) {
-- xclear(winx + width, (y == 0)? 0 : winy, win.w,
-- ((winy + win.ch >= borderpx + 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, winy + win.ch, winx + width, win.h);
--
-- /* Clean up the region we want to draw to. */
-- XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
--
-- /* Set the clip region because Xft is sometimes dirty. */
-- r.x = 0;
-- r.y = 0;
-- r.height = win.ch;
-- r.width = width;
-- XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
--
-- /* Render the glyphs. */
-- XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
--
-- /* Render underline and strikethrough. */
-- if (base.mode & ATTR_UNDERLINE) {
-- XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1,
-- width, 1);
-- }
--
-- if (base.mode & ATTR_STRUCK) {
-- XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3,
-- width, 1);
-- }
--
-- /* Reset clip to none. */
-- XftDrawSetClip(xw.draw, 0);
-+ if (dmode & DRAW_BG) {
-+ /* Intelligent cleaning up of the borders. */
-+ if (x == 0) {
-+ xclear(0, (y == 0)? 0 : winy, borderpx,
-+ winy + win.ch +
-+ ((winy + win.ch >= borderpx + win.th)? win.h : 0));
-+ }
-+ if (winx + width >= borderpx + win.tw) {
-+ xclear(winx + width, (y == 0)? 0 : winy, win.w,
-+ ((winy + win.ch >= borderpx + 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, winy + win.ch, winx + width, win.h);
-+ /* Fill the background */
-+ XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
-+ }
-+
-+ if (dmode & DRAW_FG) {
-+ /* Render the glyphs. */
-+ XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
-+
-+ /* Render underline and strikethrough. */
-+ if (base.mode & ATTR_UNDERLINE) {
-+ XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
-+ width, 1);
-+ }
-+
-+ if (base.mode & ATTR_STRUCK) {
-+ XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3,
-+ width, 1);
-+ }
-+ }
- }
-
- void
-@@ -1513,7 +1506,7 @@ xdrawglyph(Glyph g, int x, int y)
- XftGlyphFontSpec spec;
-
- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
-+ xdrawglyphfontspecs(&spec, g, numspecs, x, y, DRAW_BG | DRAW_FG);
- }
-
- void
-@@ -1648,32 +1641,39 @@ xstartdraw(void)
- void
- xdrawline(Line line, int x1, int y1, int x2)
- {
-- int i, x, ox, numspecs;
-+ int i, x, ox, numspecs, numspecs_cached;
- Glyph base, new;
-- XftGlyphFontSpec *specs = xw.specbuf;
--
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
-- i = ox = 0;
-- for (x = x1; x < x2 && i < numspecs; x++) {
-- new = line[x];
-- if (new.mode == ATTR_WDUMMY)
-- continue;
-- if (selected(x, y1))
-- new.mode ^= ATTR_REVERSE;
-- if (i > 0 && ATTRCMP(base, new)) {
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-- specs += i;
-- numspecs -= i;
-- i = 0;
-- }
-- if (i == 0) {
-- ox = x;
-- base = new;
-+ XftGlyphFontSpec *specs;
-+
-+ numspecs_cached = xmakeglyphfontspecs(xw.specbuf, &line[x1], x2 - x1, x1, y1);
-+
-+ /* Draw line in 2 passes: background and foreground. This way wide glyphs
-+ won't get truncated (#223) */
-+ for (int dmode = DRAW_BG; dmode <= DRAW_FG; dmode <<= 1) {
-+ specs = xw.specbuf;
-+ numspecs = numspecs_cached;
-+ i = ox = 0;
-+ for (x = x1; x < x2 && i < numspecs; x++) {
-+ new = line[x];
-+ if (new.mode == ATTR_WDUMMY)
-+ continue;
-+ if (selected(x, y1))
-+ new.mode ^= ATTR_REVERSE;
-+ if (i > 0 && ATTRCMP(base, new)) {
-+ xdrawglyphfontspecs(specs, base, i, ox, y1, dmode);
-+ specs += i;
-+ numspecs -= i;
-+ i = 0;
-+ }
-+ if (i == 0) {
-+ ox = x;
-+ base = new;
-+ }
-+ i++;
- }
-- i++;
-+ if (i > 0)
-+ xdrawglyphfontspecs(specs, base, i, ox, y1, dmode);
- }
-- if (i > 0)
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
- }
-
- void
---
-2.35.1
-
diff --git a/patches/st-scrollback-mouse-20220127-2c5edf2.diff b/patches/st-scrollback-mouse-20220127-2c5edf2.diff
deleted file mode 100644
index 5c47abc..0000000
--- a/patches/st-scrollback-mouse-20220127-2c5edf2.diff
+++ /dev/null
@@ -1,25 +0,0 @@
-From b5d3351a21442a842e01e8c0317603b6890b379c Mon Sep 17 00:00:00 2001
-From: asparagii <[email protected]>
-Date: Thu, 27 Jan 2022 15:44:02 +0100
-Subject: [PATCH] st-scrollback-mouse
-
----
- config.def.h | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/config.def.h b/config.def.h
-index e3b469b..c217315 100644
---- a/config.def.h
-+++ b/config.def.h
-@@ -176,6 +176,8 @@ static uint forcemousemod = ShiftMask;
- */
- static MouseShortcut mshortcuts[] = {
- /* mask button function argument release */
-+ { ShiftMask, Button4, kscrollup, {.i = 1} },
-+ { ShiftMask, Button5, kscrolldown, {.i = 1} },
- { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 },
- { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} },
- { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} },
---
-2.34.1
-
diff --git a/patches/st-scrollback-ringbuffer-0.8.5.diff b/patches/st-scrollback-ringbuffer-0.8.5.diff
deleted file mode 100644
index 7c060c5..0000000
--- a/patches/st-scrollback-ringbuffer-0.8.5.diff
+++ /dev/null
@@ -1,730 +0,0 @@
-commit 0663bdf11a409961da5b1120741a69814da8ce65
-Author: Timo Röhling <[email protected]>
-Date: Tue Nov 23 19:45:33 2021 +0100
-
- Terminal scrollback with ring buffer
-
- This patch adds a ring buffer for scrollback to the terminal. The
- advantage of using a ring buffer is that the common case, scrolling with
- no static screen content, can be achieved very efficiently by
- incrementing and decrementing the starting line (modulo buffer size).
-
- The scrollback buffer is limited to HISTSIZE lines in order to bound
- memory usage. As the lines are allocated on demand, it is possible to
- implement unlimited scrollback with few changes. If the terminal is
- reset, the scroll back buffer is reset, too.
-
-diff --git a/config.def.h b/config.def.h
-index 91ab8ca..e3b469b 100644
---- a/config.def.h
-+++ b/config.def.h
-@@ -201,6 +201,8 @@ static Shortcut shortcuts[] = {
- { TERMMOD, XK_Y, selpaste, {.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} },
- };
-
- /*
-diff --git a/st.c b/st.c
-index 51049ba..f9e24ba 100644
---- a/st.c
-+++ b/st.c
-@@ -43,6 +43,10 @@
- #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
- #define ISDELIM(u) (u && wcschr(worddelimiters, u))
-
-+#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)]
-+#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size)
-+#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)])
-+
- enum term_mode {
- MODE_WRAP = 1 << 0,
- MODE_INSERT = 1 << 1,
-@@ -109,12 +113,21 @@ typedef struct {
- int alt;
- } Selection;
-
-+/* Screen lines */
-+typedef struct {
-+ Line* buffer; /* ring buffer */
-+ int size; /* size of buffer */
-+ int cur; /* start of active screen */
-+ int off; /* scrollback line offset */
-+ TCursor sc; /* saved cursor */
-+} LineBuffer;
-+
- /* Internal representation of the screen */
- typedef struct {
- int row; /* nb row */
- int col; /* nb col */
-- Line *line; /* screen */
-- Line *alt; /* alternate screen */
-+ LineBuffer screen[2]; /* screen and alternate screen */
-+ int linelen; /* allocated line length */
- int *dirty; /* dirtyness of lines */
- TCursor c; /* cursor */
- int ocx; /* old cursor col */
-@@ -202,6 +215,8 @@ static void tdeftran(char);
- static void tstrsequence(uchar);
-
- static void drawregion(int, int, int, int);
-+static void clearline(Line, Glyph, int, int);
-+static Line ensureline(Line);
-
- static void selnormalize(void);
- static void selscroll(int, int);
-@@ -415,11 +430,12 @@ int
- tlinelen(int y)
- {
- int i = term.col;
-+ Line line = TLINE(y);
-
-- if (term.line[y][i - 1].mode & ATTR_WRAP)
-+ if (line[i - 1].mode & ATTR_WRAP)
- return i;
-
-- while (i > 0 && term.line[y][i - 1].u == ' ')
-+ while (i > 0 && line[i - 1].u == ' ')
- --i;
-
- return i;
-@@ -528,7 +544,7 @@ selsnap(int *x, int *y, int direction)
- * Snap around if the word wraps around at the end or
- * beginning of a line.
- */
-- prevgp = &term.line[*y][*x];
-+ prevgp = &TLINE(*y)[*x];
- prevdelim = ISDELIM(prevgp->u);
- for (;;) {
- newx = *x + direction;
-@@ -543,14 +559,14 @@ selsnap(int *x, int *y, int direction)
- yt = *y, xt = *x;
- else
- yt = newy, xt = newx;
-- if (!(term.line[yt][xt].mode & ATTR_WRAP))
-+ if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
- break;
- }
-
- if (newx >= tlinelen(newy))
- break;
-
-- gp = &term.line[newy][newx];
-+ gp = &TLINE(newy)[newx];
- delim = ISDELIM(gp->u);
- if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
- || (delim && gp->u != prevgp->u)))
-@@ -571,14 +587,14 @@ selsnap(int *x, int *y, int direction)
- *x = (direction < 0) ? 0 : term.col - 1;
- if (direction < 0) {
- for (; *y > 0; *y += direction) {
-- if (!(term.line[*y-1][term.col-1].mode
-+ if (!(TLINE(*y-1)[term.col-1].mode
- & ATTR_WRAP)) {
- break;
- }
- }
- } else if (direction > 0) {
- for (; *y < term.row-1; *y += direction) {
-- if (!(term.line[*y][term.col-1].mode
-+ if (!(TLINE(*y)[term.col-1].mode
- & ATTR_WRAP)) {
- break;
- }
-@@ -609,13 +625,13 @@ getsel(void)
- }
-
- if (sel.type == SEL_RECTANGULAR) {
-- gp = &term.line[y][sel.nb.x];
-+ gp = &TLINE(y)[sel.nb.x];
- lastx = sel.ne.x;
- } else {
-- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
-+ gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
- lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
- }
-- last = &term.line[y][MIN(lastx, linelen-1)];
-+ last = &TLINE(y)[MIN(lastx, linelen-1)];
- while (last >= gp && last->u == ' ')
- --last;
-
-@@ -956,12 +972,15 @@ int
- tattrset(int attr)
- {
- int i, j;
-+ int y = TLINEOFFSET(0);
-
- for (i = 0; i < term.row-1; i++) {
-+ Line line = TSCREEN.buffer[y];
- for (j = 0; j < term.col-1; j++) {
-- if (term.line[i][j].mode & attr)
-+ if (line[j].mode & attr)
- return 1;
- }
-+ y = (y+1) % TSCREEN.size;
- }
-
- return 0;
-@@ -983,14 +1002,17 @@ void
- tsetdirtattr(int attr)
- {
- int i, j;
-+ int y = TLINEOFFSET(0);
-
- for (i = 0; i < term.row-1; i++) {
-+ Line line = TSCREEN.buffer[y];
- for (j = 0; j < term.col-1; j++) {
-- if (term.line[i][j].mode & attr) {
-+ if (line[j].mode & attr) {
- tsetdirt(i, i);
- break;
- }
- }
-+ y = (y+1) % TSCREEN.size;
- }
- }
-
-@@ -1003,27 +1025,19 @@ tfulldirt(void)
- void
- tcursor(int mode)
- {
-- static TCursor c[2];
-- int alt = IS_SET(MODE_ALTSCREEN);
--
- if (mode == CURSOR_SAVE) {
-- c[alt] = term.c;
-+ TSCREEN.sc = term.c;
- } else if (mode == CURSOR_LOAD) {
-- term.c = c[alt];
-- tmoveto(c[alt].x, c[alt].y);
-+ term.c = TSCREEN.sc;
-+ tmoveto(term.c.x, term.c.y);
- }
- }
-
- void
- treset(void)
- {
-- uint i;
--
-- term.c = (TCursor){{
-- .mode = ATTR_NULL,
-- .fg = defaultfg,
-- .bg = defaultbg
-- }, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
-+ int i, j;
-+ Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg};
-
- memset(term.tabs, 0, term.col * sizeof(*term.tabs));
- for (i = tabspaces; i < term.col; i += tabspaces)
-@@ -1035,17 +1049,37 @@ treset(void)
- term.charset = 0;
-
- for (i = 0; i < 2; i++) {
-- tmoveto(0, 0);
-- tcursor(CURSOR_SAVE);
-- tclearregion(0, 0, term.col-1, term.row-1);
-- tswapscreen();
-+ term.screen[i].sc = (TCursor){{
-+ .fg = defaultfg,
-+ .bg = defaultbg
-+ }};
-+ term.screen[i].cur = 0;
-+ term.screen[i].off = 0;
-+ for (j = 0; j < term.row; ++j) {
-+ if (term.col != term.linelen)
-+ term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph));
-+ clearline(term.screen[i].buffer[j], g, 0, term.col);
-+ }
-+ for (j = term.row; j < term.screen[i].size; ++j) {
-+ free(term.screen[i].buffer[j]);
-+ term.screen[i].buffer[j] = NULL;
-+ }
- }
-+ tcursor(CURSOR_LOAD);
-+ term.linelen = term.col;
-+ tfulldirt();
- }
-
- void
- tnew(int col, int row)
- {
-- term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
-+ int i;
-+ term = (Term){};
-+ term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line));
-+ term.screen[0].size = HISTSIZE;
-+ term.screen[1].buffer = NULL;
-+ for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL;
-+
- tresize(col, row);
- treset();
- }
-@@ -1053,14 +1087,42 @@ tnew(int col, int row)
- void
- tswapscreen(void)
- {
-- Line *tmp = term.line;
--
-- term.line = term.alt;
-- term.alt = tmp;
- term.mode ^= MODE_ALTSCREEN;
- tfulldirt();
- }
-
-+void
-+kscrollup(const Arg *a)
-+{
-+ int n = a->i;
-+
-+ if (IS_SET(MODE_ALTSCREEN))
-+ return;
-+
-+ if (n < 0) n = (-n) * term.row;
-+ if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off;
-+ while (!TLINE(-n)) --n;
-+ TSCREEN.off += n;
-+ selscroll(0, n);
-+ tfulldirt();
-+}
-+
-+void
-+kscrolldown(const Arg *a)
-+{
-+
-+ int n = a->i;
-+
-+ if (IS_SET(MODE_ALTSCREEN))
-+ return;
-+
-+ if (n < 0) n = (-n) * term.row;
-+ if (n > TSCREEN.off) n = TSCREEN.off;
-+ TSCREEN.off -= n;
-+ selscroll(0, -n);
-+ tfulldirt();
-+}
-+
- void
- tscrolldown(int orig, int n)
- {
-@@ -1069,15 +1131,29 @@ tscrolldown(int orig, int n)
-
- LIMIT(n, 0, term.bot-orig+1);
-
-- tsetdirt(orig, term.bot-n);
-- tclearregion(0, term.bot-n+1, term.col-1, term.bot);
-+ /* Ensure that lines are allocated */
-+ for (i = -n; i < 0; i++) {
-+ TLINE(i) = ensureline(TLINE(i));
-+ }
-
-- for (i = term.bot; i >= orig+n; i--) {
-- temp = term.line[i];
-- term.line[i] = term.line[i-n];
-- term.line[i-n] = temp;
-+ /* Shift non-scrolling areas in ring buffer */
-+ for (i = term.bot+1; i < term.row; i++) {
-+ temp = TLINE(i);
-+ TLINE(i) = TLINE(i-n);
-+ TLINE(i-n) = temp;
-+ }
-+ for (i = 0; i < orig; i++) {
-+ temp = TLINE(i);
-+ TLINE(i) = TLINE(i-n);
-+ TLINE(i-n) = temp;
- }
-
-+ /* Scroll buffer */
-+ TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size;
-+ /* Clear lines that have entered the view */
-+ tclearregion(0, orig, term.linelen-1, orig+n-1);
-+ /* Redraw portion of the screen that has scrolled */
-+ tsetdirt(orig+n-1, term.bot);
- selscroll(orig, n);
- }
-
-@@ -1089,15 +1165,29 @@ tscrollup(int orig, int n)
-
- LIMIT(n, 0, term.bot-orig+1);
-
-- tclearregion(0, orig, term.col-1, orig+n-1);
-- tsetdirt(orig+n, term.bot);
-+ /* Ensure that lines are allocated */
-+ for (i = term.row; i < term.row + n; i++) {
-+ TLINE(i) = ensureline(TLINE(i));
-+ }
-
-- for (i = orig; i <= term.bot-n; i++) {
-- temp = term.line[i];
-- term.line[i] = term.line[i+n];
-- term.line[i+n] = temp;
-+ /* Shift non-scrolling areas in ring buffer */
-+ for (i = orig-1; i >= 0; i--) {
-+ temp = TLINE(i);
-+ TLINE(i) = TLINE(i+n);
-+ TLINE(i+n) = temp;
-+ }
-+ for (i = term.row-1; i >term.bot; i--) {
-+ temp = TLINE(i);
-+ TLINE(i) = TLINE(i+n);
-+ TLINE(i+n) = temp;
- }
-
-+ /* Scroll buffer */
-+ TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size;
-+ /* Clear lines that have entered the view */
-+ tclearregion(0, term.bot-n+1, term.linelen-1, term.bot);
-+ /* Redraw portion of the screen that has scrolled */
-+ tsetdirt(orig, term.bot-n+1);
- selscroll(orig, -n);
- }
-
-@@ -1201,6 +1291,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
- "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
- "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
- };
-+ Line line = TLINE(y);
-
- /*
- * The table is proudly stolen from rxvt.
-@@ -1209,25 +1300,25 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
- BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
- utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
-
-- if (term.line[y][x].mode & ATTR_WIDE) {
-+ if (line[x].mode & ATTR_WIDE) {
- if (x+1 < term.col) {
-- term.line[y][x+1].u = ' ';
-- term.line[y][x+1].mode &= ~ATTR_WDUMMY;
-+ line[x+1].u = ' ';
-+ line[x+1].mode &= ~ATTR_WDUMMY;
- }
-- } else if (term.line[y][x].mode & ATTR_WDUMMY) {
-- term.line[y][x-1].u = ' ';
-- term.line[y][x-1].mode &= ~ATTR_WIDE;
-+ } else if (line[x].mode & ATTR_WDUMMY) {
-+ line[x-1].u = ' ';
-+ line[x-1].mode &= ~ATTR_WIDE;
- }
-
- term.dirty[y] = 1;
-- term.line[y][x] = *attr;
-- term.line[y][x].u = u;
-+ line[x] = *attr;
-+ line[x].u = u;
- }
-
- void
- tclearregion(int x1, int y1, int x2, int y2)
- {
-- int x, y, temp;
-+ int x, y, L, S, temp;
- Glyph *gp;
-
- if (x1 > x2)
-@@ -1235,15 +1326,16 @@ tclearregion(int x1, int y1, int x2, int y2)
- if (y1 > y2)
- temp = y1, y1 = y2, y2 = temp;
-
-- LIMIT(x1, 0, term.col-1);
-- LIMIT(x2, 0, term.col-1);
-+ LIMIT(x1, 0, term.linelen-1);
-+ LIMIT(x2, 0, term.linelen-1);
- LIMIT(y1, 0, term.row-1);
- LIMIT(y2, 0, term.row-1);
-
-+ L = TLINEOFFSET(y1);
- for (y = y1; y <= y2; y++) {
- term.dirty[y] = 1;
- for (x = x1; x <= x2; x++) {
-- gp = &term.line[y][x];
-+ gp = &TSCREEN.buffer[L][x];
- if (selected(x, y))
- selclear();
- gp->fg = term.c.attr.fg;
-@@ -1251,6 +1343,7 @@ tclearregion(int x1, int y1, int x2, int y2)
- gp->mode = 0;
- gp->u = ' ';
- }
-+ L = (L + 1) % TSCREEN.size;
- }
- }
-
-@@ -1265,7 +1358,7 @@ tdeletechar(int n)
- dst = term.c.x;
- src = term.c.x + n;
- size = term.col - src;
-- line = term.line[term.c.y];
-+ line = TLINE(term.c.y);
-
- memmove(&line[dst], &line[src], size * sizeof(Glyph));
- tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
-@@ -1282,7 +1375,7 @@ tinsertblank(int n)
- dst = term.c.x + n;
- src = term.c.x;
- size = term.col - dst;
-- line = term.line[term.c.y];
-+ line = TLINE(term.c.y);
-
- memmove(&line[dst], &line[src], size * sizeof(Glyph));
- tclearregion(src, term.c.y, dst - 1, term.c.y);
-@@ -2103,7 +2196,7 @@ tdumpline(int n)
- char buf[UTF_SIZ];
- const Glyph *bp, *end;
-
-- bp = &term.line[n][0];
-+ bp = &TLINE(n)[0];
- end = &bp[MIN(tlinelen(n), term.col) - 1];
- if (bp != end || bp->u != ' ') {
- for ( ; bp <= end; ++bp)
-@@ -2486,11 +2579,11 @@ check_control_code:
- if (selected(term.c.x, term.c.y))
- selclear();
-
-- gp = &term.line[term.c.y][term.c.x];
-+ gp = &TLINE(term.c.y)[term.c.x];
- if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
- gp->mode |= ATTR_WRAP;
- tnewline(1);
-- gp = &term.line[term.c.y][term.c.x];
-+ gp = &TLINE(term.c.y)[term.c.x];
- }
-
- if (IS_SET(MODE_INSERT) && term.c.x+width < term.col)
-@@ -2498,7 +2591,7 @@ check_control_code:
-
- if (term.c.x+width > term.col) {
- tnewline(1);
-- gp = &term.line[term.c.y][term.c.x];
-+ gp = &TLINE(term.c.y)[term.c.x];
- }
-
- tsetchar(u, &term.c.attr, term.c.x, term.c.y);
-@@ -2529,6 +2622,11 @@ twrite(const char *buf, int buflen, int show_ctrl)
- Rune u;
- int n;
-
-+ if (TSCREEN.off) {
-+ TSCREEN.off = 0;
-+ tfulldirt();
-+ }
-+
- for (n = 0; n < buflen; n += charsize) {
- if (IS_SET(MODE_UTF8)) {
- /* process a complete utf8 char */
-@@ -2555,56 +2653,85 @@ twrite(const char *buf, int buflen, int show_ctrl)
- }
-
- void
--tresize(int col, int row)
-+clearline(Line line, Glyph g, int x, int xend)
- {
- int i;
-+ g.mode = 0;
-+ g.u = ' ';
-+ for (i = x; i < xend; ++i) {
-+ line[i] = g;
-+ }
-+}
-+
-+Line
-+ensureline(Line line)
-+{
-+ if (!line) {
-+ line = xmalloc(term.linelen * sizeof(Glyph));
-+ }
-+ return line;
-+}
-+
-+void
-+tresize(int col, int row)
-+{
-+ int i, j;
- int minrow = MIN(row, term.row);
- int mincol = MIN(col, term.col);
-+ int linelen = MAX(col, term.linelen);
- int *bp;
-- TCursor c;
-
-- if (col < 1 || row < 1) {
-+ if (col < 1 || row < 1 || row > HISTSIZE) {
- fprintf(stderr,
- "tresize: error resizing to %dx%d\n", col, row);
- return;
- }
-
-- /*
-- * slide screen to keep cursor where we expect it -
-- * tscrollup would work here, but we can optimize to
-- * memmove because we're freeing the earlier lines
-- */
-- for (i = 0; i <= term.c.y - row; i++) {
-- free(term.line[i]);
-- free(term.alt[i]);
-+ /* Shift buffer to keep the cursor where we expect it */
-+ if (row <= term.c.y) {
-+ term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size;
-+ }
-+
-+ /* Resize and clear line buffers as needed */
-+ if (linelen > term.linelen) {
-+ for (i = 0; i < term.screen[0].size; ++i) {
-+ if (term.screen[0].buffer[i]) {
-+ term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph));
-+ clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen);
-+ }
-+ }
-+ for (i = 0; i < minrow; ++i) {
-+ term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph));
-+ clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen);
-+ }
- }
-- /* ensure that both src and dst are not NULL */
-- if (i > 0) {
-- memmove(term.line, term.line + i, row * sizeof(Line));
-- memmove(term.alt, term.alt + i, row * sizeof(Line));
-+ /* Allocate all visible lines for regular line buffer */
-+ for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size)
-+ {
-+ if (!term.screen[0].buffer[j]) {
-+ term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph));
-+ }
-+ if (i >= term.row) {
-+ clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen);
-+ }
- }
-- for (i += row; i < term.row; i++) {
-- free(term.line[i]);
-- free(term.alt[i]);
-+ /* Resize alt screen */
-+ term.screen[1].cur = 0;
-+ term.screen[1].size = row;
-+ for (i = row; i < term.row; ++i) {
-+ free(term.screen[1].buffer[i]);
-+ }
-+ term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line));
-+ for (i = term.row; i < row; ++i) {
-+ term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph));
-+ clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen);
- }
-
- /* resize to new height */
-- term.line = xrealloc(term.line, row * sizeof(Line));
-- term.alt = xrealloc(term.alt, row * sizeof(Line));
- term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
- term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
-
-- /* resize each row to new width, zero-pad if needed */
-- for (i = 0; i < minrow; i++) {
-- term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
-- term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph));
-- }
--
-- /* allocate any new rows */
-- for (/* i = minrow */; i < row; i++) {
-- term.line[i] = xmalloc(col * sizeof(Glyph));
-- term.alt[i] = xmalloc(col * sizeof(Glyph));
-- }
-+ /* fix tabstops */
- if (col > term.col) {
- bp = term.tabs + term.col;
-
-@@ -2614,26 +2741,16 @@ tresize(int col, int row)
- for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
- *bp = 1;
- }
-+
- /* update terminal size */
- term.col = col;
- term.row = row;
-+ term.linelen = linelen;
- /* reset scrolling region */
- tsetscroll(0, row-1);
- /* make use of the LIMIT in tmoveto */
- tmoveto(term.c.x, term.c.y);
-- /* Clearing both screens (it makes dirty all lines) */
-- c = term.c;
-- for (i = 0; i < 2; i++) {
-- if (mincol < col && 0 < minrow) {
-- tclearregion(mincol, 0, col - 1, minrow - 1);
-- }
-- if (0 < col && minrow < row) {
-- tclearregion(0, minrow, col - 1, row - 1);
-- }
-- tswapscreen();
-- tcursor(CURSOR_LOAD);
-- }
-- term.c = c;
-+ tfulldirt();
- }
-
- void
-@@ -2645,14 +2762,15 @@ resettitle(void)
- void
- drawregion(int x1, int y1, int x2, int y2)
- {
-- int y;
-+ int y, L;
-
-+ L = TLINEOFFSET(y1);
- for (y = y1; y < y2; y++) {
-- if (!term.dirty[y])
-- continue;
--
-- term.dirty[y] = 0;
-- xdrawline(term.line[y], x1, y, x2);
-+ if (term.dirty[y]) {
-+ term.dirty[y] = 0;
-+ xdrawline(TSCREEN.buffer[L], x1, y, x2);
-+ }
-+ L = (L + 1) % TSCREEN.size;
- }
- }
-
-@@ -2667,14 +2785,15 @@ draw(void)
- /* adjust cursor position */
- LIMIT(term.ocx, 0, term.col-1);
- LIMIT(term.ocy, 0, term.row-1);
-- if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY)
-+ if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY)
- term.ocx--;
-- if (term.line[term.c.y][cx].mode & ATTR_WDUMMY)
-+ if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY)
- cx--;
-
- drawregion(0, 0, term.col, term.row);
-- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
-- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
-+ if (TSCREEN.off == 0)
-+ xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
-+ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
- term.ocx = cx;
- term.ocy = term.c.y;
- xfinishdraw();
-diff --git a/st.h b/st.h
-index 519b9bd..b48e810 100644
---- a/st.h
-+++ b/st.h
-@@ -19,6 +19,7 @@
-
- #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
- #define IS_TRUECOL(x) (1 << 24 & (x))
-+#define HISTSIZE 2000
-
- enum glyph_attribute {
- ATTR_NULL = 0,
-diff --git a/x.c b/x.c
-index 8a16faa..1bb5853 100644
---- a/x.c
-+++ b/x.c
-@@ -59,6 +59,8 @@ static void zoom(const Arg *);
- static void zoomabs(const Arg *);
- static void zoomreset(const Arg *);
- static void ttysend(const Arg *);
-+void kscrollup(const Arg *);
-+void kscrolldown(const Arg *);
-
- /* config.h for applying patches and the configuration. */
- #include "config.h"
diff --git a/st-copyout b/st-copyout
new file mode 100755
index 0000000..0d19e5a
--- /dev/null
+++ b/st-copyout
@@ -0,0 +1,13 @@
+#!/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
new file mode 100755
index 0000000..0d39dd5
--- /dev/null
+++ b/st-urlhandler
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+urlregex="(((http|https)://|www\\.)[a-zA-Z0-9.]*[:]?[a-zA-Z0-9./@$&%?$\#=_~-]*)|((magnet:\\?xt=urn:btih:)[a-zA-Z0-9]*)"
+
+# First remove linebreaks and mutt sidebars:
+urls="$(sed 's/.*│//g' | tr -d '\n' |
+ grep -aEo "$urlregex" | # grep only urls as defined above.
+ uniq | # Ignore neighboring duplicates.
+ sed 's/^www./http:\/\/www\./g')"
+
+[ -z "$urls" ] && exit
+
+chosen="$(echo "$urls" | dmenu -i -p 'Follow which url?' -l 10)"
+
+setsid xdg-open "$chosen" >/dev/null 2>&1 &
diff --git a/st.1 b/st.1
index 39120b4..8701592 100644
--- a/st.1
+++ b/st.1
@@ -1,6 +1,6 @@
.TH ST 1 st\-VERSION
.SH NAME
-st \- simple terminal
+st \- simple terminal (Luke Smith (https://lukesmith.xyz)'s build)
.SH SYNOPSIS
.B st
.RB [ \-aiv ]
@@ -125,6 +125,41 @@ 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
@@ -142,23 +177,9 @@ Print the full screen to the
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.
+.B Alt-Ctrl
+Launch dmenu to enter a unicode codepoint and send the corresponding glyph
+to st.
.SH CUSTOMIZATION
.B st
can be customized by creating a custom config.h and (re)compiling the source
@@ -170,8 +191,7 @@ See the LICENSE file for the terms of redistribution.
.SH SEE ALSO
.BR tabbed (1),
.BR utmp (1),
-.BR stty (1),
-.BR scroll (1)
+.BR stty (1)
.SH BUGS
See the TODO file in the distribution.
diff --git a/st.c b/st.c
index c44797b..c180375 100644
--- a/st.c
+++ b/st.c
@@ -14,6 +14,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
+#include <time.h>
#include <unistd.h>
#include <wchar.h>
@@ -35,17 +36,23 @@
#define ESC_ARG_SIZ 16
#define STR_BUF_SIZ ESC_BUF_SIZ
#define STR_ARG_SIZ ESC_ARG_SIZ
+#define HISTSIZE 2000
/* macros */
#define IS_SET(flag) ((term.mode & (flag)) != 0)
-#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f)
+#define NUMMAXLEN(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1)
+#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177')
#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
#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 TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)]
-#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size)
-#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)])
+#define TLINE_HIST(y) ((y) <= HISTSIZE-term.row+2 ? term.hist[(y)] : term.line[(y-HISTSIZE+term.row-3)])
+
+/* constants */
+#define ISO14755CMD "dmenu -w \"$WINDOWID\" -p codepoint: </dev/null"
enum term_mode {
MODE_WRAP = 1 << 0,
@@ -55,6 +62,7 @@ enum term_mode {
MODE_ECHO = 1 << 4,
MODE_PRINT = 1 << 5,
MODE_UTF8 = 1 << 6,
+ MODE_SIXEL = 1 << 7,
};
enum cursor_movement {
@@ -81,11 +89,12 @@ enum charset {
enum escape_state {
ESC_START = 1,
ESC_CSI = 2,
- ESC_STR = 4, /* DCS, OSC, PM, APC */
+ ESC_STR = 4, /* OSC, PM, APC */
ESC_ALTCHARSET = 8,
ESC_STR_END = 16, /* a final string was encountered */
ESC_TEST = 32, /* Enter in test mode */
ESC_UTF8 = 64,
+ ESC_DCS =128,
};
typedef struct {
@@ -113,21 +122,16 @@ typedef struct {
int alt;
} Selection;
-/* Screen lines */
-typedef struct {
- Line* buffer; /* ring buffer */
- int size; /* size of buffer */
- int cur; /* start of active screen */
- int off; /* scrollback line offset */
- TCursor sc; /* saved cursor */
-} LineBuffer;
-
/* Internal representation of the screen */
typedef struct {
int row; /* nb row */
int col; /* nb col */
- LineBuffer screen[2]; /* screen and alternate screen */
- int linelen; /* allocated line length */
+ int maxcol;
+ Line *line; /* screen */
+ Line *alt; /* alternate screen */
+ Line hist[HISTSIZE]; /* history buffer */
+ int histi; /* history index */
+ int scr; /* scroll back */
int *dirty; /* dirtyness of lines */
TCursor c; /* cursor */
int ocx; /* old cursor col */
@@ -140,14 +144,14 @@ typedef struct {
int charset; /* current charset */
int icharset; /* selected charset for sequence */
int *tabs;
- Rune lastc; /* last printed char outside of sequence, 0 if control */
+ struct timespec last_ximspot_update;
} Term;
/* CSI Escape sequence structs */
/* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
typedef struct {
char buf[ESC_BUF_SIZ]; /* raw string */
- size_t len; /* raw string length */
+ int len; /* raw string length */
char priv;
int arg[ESC_ARG_SIZ];
int narg; /* nb of args */
@@ -158,14 +162,14 @@ typedef struct {
/* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */
typedef struct {
char type; /* ESC type ... */
- char *buf; /* allocated raw string */
- size_t siz; /* allocation size */
- size_t len; /* raw string length */
+ char buf[STR_BUF_SIZ]; /* raw string */
+ int len; /* raw string length */
char *args[STR_ARG_SIZ];
int narg; /* nb of args */
} STREscape;
static void execsh(char *, char **);
+static char *getcwd_by_pid(pid_t pid);
static void stty(char **);
static void sigchld(int);
static void ttywriteraw(const char *, size_t);
@@ -174,7 +178,6 @@ static void csidump(void);
static void csihandle(void);
static void csiparse(void);
static void csireset(void);
-static void osc_color_response(int, int, int);
static int eschandle(uchar);
static void strdump(void);
static void strhandle(void);
@@ -198,8 +201,8 @@ static void tnewline(int);
static void tputtab(int);
static void tputc(Rune);
static void treset(void);
-static void tscrollup(int, int);
-static void tscrolldown(int, int);
+static void tscrollup(int, int, int);
+static void tscrolldown(int, int, int);
static void tsetattr(const int *, int);
static void tsetchar(Rune, const Glyph *, int, int);
static void tsetdirt(int, int);
@@ -216,8 +219,6 @@ static void tdeftran(char);
static void tstrsequence(uchar);
static void drawregion(int, int, int, int);
-static void clearline(Line, Glyph, int, int);
-static Line ensureline(Line);
static void selnormalize(void);
static void selscroll(int, int);
@@ -287,7 +288,7 @@ xrealloc(void *p, size_t len)
char *
xstrdup(const char *s)
{
- char *p;
+ char *p;
if ((p = strdup(s)) == NULL)
die("strdup: %s\n", strerror(errno));
@@ -365,12 +366,26 @@ utf8validate(Rune *u, size_t i)
return i;
}
+static const char base64_digits[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0,
+ 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1,
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
char
base64dec_getc(const char **src)
{
- while (**src && !isprint((unsigned char)**src))
- (*src)++;
- return **src ? *((*src)++) : '='; /* emulate padding if string ends */
+ while (**src && !isprint(**src)) (*src)++;
+ return *((*src)++);
}
char *
@@ -378,13 +393,6 @@ base64dec(const char *src)
{
size_t in_len = strlen(src);
char *result, *dst;
- static const char base64_digits[256] = {
- [43] = 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
- 0, 0, 0, -1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
- 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0,
- 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
- 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
- };
if (in_len % 4)
in_len += 4 - (in_len % 4);
@@ -395,10 +403,6 @@ base64dec(const char *src)
int c = base64_digits[(unsigned char) base64dec_getc(&src)];
int d = base64_digits[(unsigned char) base64dec_getc(&src)];
- /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */
- if (a == -1 || b == -1)
- break;
-
*dst++ = (a << 2) | ((b & 0x30) >> 4);
if (c == -1)
break;
@@ -423,12 +427,25 @@ int
tlinelen(int y)
{
int i = term.col;
- Line line = TLINE(y);
- if (line[i - 1].mode & ATTR_WRAP)
+ if (TLINE(y)[i - 1].mode & ATTR_WRAP)
+ return i;
+
+ while (i > 0 && TLINE(y)[i - 1].u == ' ')
+ --i;
+
+ return i;
+}
+
+int
+tlinehistlen(int y)
+{
+ int i = term.col;
+
+ if (TLINE_HIST(y)[i - 1].mode & ATTR_WRAP)
return i;
- while (i > 0 && line[i - 1].u == ' ')
+ while (i > 0 && TLINE_HIST(y)[i - 1].u == ' ')
--i;
return i;
@@ -644,8 +661,7 @@ getsel(void)
* st.
* FIXME: Fix the computer world.
*/
- if ((y < sel.ne.y || lastx >= linelen) &&
- (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
+ if ((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP))
*ptr++ = '\n';
}
*ptr = 0;
@@ -676,7 +692,7 @@ die(const char *errstr, ...)
void
execsh(char *cmd, char **args)
{
- char *sh, *prog, *arg;
+ char *sh, *prog;
const struct passwd *pw;
errno = 0;
@@ -690,20 +706,13 @@ execsh(char *cmd, char **args)
if ((sh = getenv("SHELL")) == NULL)
sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd;
- if (args) {
+ if (args)
prog = args[0];
- arg = NULL;
- } else if (scroll) {
- prog = scroll;
- arg = utmp ? utmp : sh;
- } else if (utmp) {
+ else if (utmp)
prog = utmp;
- arg = NULL;
- } else {
+ else
prog = sh;
- arg = NULL;
- }
- DEFAULT(args, ((char *[]) {prog, arg, NULL}));
+ DEFAULT(args, ((char *[]) {prog, NULL}));
unsetenv("COLUMNS");
unsetenv("LINES");
@@ -741,7 +750,7 @@ sigchld(int a)
die("child exited with status %d\n", WEXITSTATUS(stat));
else if (WIFSIGNALED(stat))
die("child terminated due to signal %d\n", WTERMSIG(stat));
- _exit(0);
+ exit(0);
}
void
@@ -802,15 +811,15 @@ ttynew(const char *line, char *cmd, const char *out, char **args)
break;
case 0:
close(iofd);
- close(m);
+ close(m);
setsid(); /* create a new process group */
dup2(s, 0);
dup2(s, 1);
dup2(s, 2);
if (ioctl(s, TIOCSCTTY, NULL) < 0)
die("ioctl TIOCSCTTY failed: %s\n", strerror(errno));
- if (s > 2)
- close(s);
+ if (s > 2)
+ close(s);
#ifdef __OpenBSD__
if (pledge("stdio getpw proc exec", NULL) == -1)
die("pledge\n");
@@ -835,31 +844,30 @@ ttyread(void)
{
static char buf[BUFSIZ];
static int buflen = 0;
- int ret, written;
+ int written;
+ int ret;
/* append read bytes to unprocessed bytes */
- ret = read(cmdfd, buf+buflen, LEN(buf)-buflen);
-
- switch (ret) {
- case 0:
- exit(0);
- case -1:
+ if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0)
die("couldn't read from shell: %s\n", strerror(errno));
- default:
- buflen += ret;
- written = twrite(buf, buflen, 0);
- buflen -= written;
- /* keep any incomplete UTF-8 byte sequence for the next call */
- if (buflen > 0)
- memmove(buf, buf + written, buflen);
- return ret;
- }
+ buflen += ret;
+
+ written = twrite(buf, buflen, 0);
+ buflen -= written;
+ /* keep any uncomplete utf8 char for the next call */
+ if (buflen > 0)
+ memmove(buf, buf + written, buflen);
+
+ return ret;
}
void
ttywrite(const char *s, size_t n, int may_echo)
{
const char *next;
+ Arg arg = (Arg) { .i = term.scr };
+
+ kscrolldown(&arg);
if (may_echo && IS_SET(MODE_ECHO))
twrite(s, n, 1);
@@ -955,7 +963,7 @@ ttyresize(int tw, int th)
}
void
-ttyhangup(void)
+ttyhangup()
{
/* Send SIGHUP to shell */
kill(pid, SIGHUP);
@@ -965,15 +973,12 @@ int
tattrset(int attr)
{
int i, j;
- int y = TLINEOFFSET(0);
for (i = 0; i < term.row-1; i++) {
- Line line = TSCREEN.buffer[y];
for (j = 0; j < term.col-1; j++) {
- if (line[j].mode & attr)
+ if (term.line[i][j].mode & attr)
return 1;
}
- y = (y+1) % TSCREEN.size;
}
return 0;
@@ -995,17 +1000,14 @@ void
tsetdirtattr(int attr)
{
int i, j;
- int y = TLINEOFFSET(0);
for (i = 0; i < term.row-1; i++) {
- Line line = TSCREEN.buffer[y];
for (j = 0; j < term.col-1; j++) {
- if (line[j].mode & attr) {
+ if (term.line[i][j].mode & attr) {
tsetdirt(i, i);
break;
}
}
- y = (y+1) % TSCREEN.size;
}
}
@@ -1018,19 +1020,27 @@ tfulldirt(void)
void
tcursor(int mode)
{
+ static TCursor c[2];
+ int alt = IS_SET(MODE_ALTSCREEN);
+
if (mode == CURSOR_SAVE) {
- TSCREEN.sc = term.c;
+ c[alt] = term.c;
} else if (mode == CURSOR_LOAD) {
- term.c = TSCREEN.sc;
- tmoveto(term.c.x, term.c.y);
+ term.c = c[alt];
+ tmoveto(c[alt].x, c[alt].y);
}
}
void
treset(void)
{
- int i, j;
- Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg};
+ uint i;
+
+ term.c = (TCursor){{
+ .mode = ATTR_NULL,
+ .fg = defaultfg,
+ .bg = defaultbg
+ }, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
memset(term.tabs, 0, term.col * sizeof(*term.tabs));
for (i = tabspaces; i < term.col; i += tabspaces)
@@ -1042,145 +1052,145 @@ treset(void)
term.charset = 0;
for (i = 0; i < 2; i++) {
- term.screen[i].sc = (TCursor){{
- .fg = defaultfg,
- .bg = defaultbg
- }};
- term.screen[i].cur = 0;
- term.screen[i].off = 0;
- for (j = 0; j < term.row; ++j) {
- if (term.col != term.linelen)
- term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph));
- clearline(term.screen[i].buffer[j], g, 0, term.col);
- }
- for (j = term.row; j < term.screen[i].size; ++j) {
- free(term.screen[i].buffer[j]);
- term.screen[i].buffer[j] = NULL;
- }
+ tmoveto(0, 0);
+ tcursor(CURSOR_SAVE);
+ tclearregion(0, 0, term.col-1, term.row-1);
+ tswapscreen();
}
- tcursor(CURSOR_LOAD);
- term.linelen = term.col;
- tfulldirt();
}
void
tnew(int col, int row)
{
- int i;
- term = (Term){};
- term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line));
- term.screen[0].size = HISTSIZE;
- term.screen[1].buffer = NULL;
- for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL;
-
+ term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
+ clock_gettime(CLOCK_MONOTONIC, &term.last_ximspot_update);
tresize(col, row);
treset();
}
+int tisaltscr(void)
+{
+ return IS_SET(MODE_ALTSCREEN);
+}
+
void
tswapscreen(void)
{
+ Line *tmp = term.line;
+
+ term.line = term.alt;
+ term.alt = tmp;
term.mode ^= MODE_ALTSCREEN;
tfulldirt();
}
void
-kscrollup(const Arg *a)
+newterm(const Arg* a)
+{
+ switch (fork()) {
+ case -1:
+ die("fork failed: %s\n", strerror(errno));
+ break;
+ case 0:
+ chdir(getcwd_by_pid(pid));
+ execlp("st", "./st", NULL);
+ break;
+ }
+}
+
+static char *getcwd_by_pid(pid_t pid) {
+ char buf[32];
+ snprintf(buf, sizeof buf, "/proc/%d/cwd", pid);
+ return realpath(buf, NULL);
+}
+
+void
+kscrolldown(const Arg* a)
{
int n = a->i;
- if (IS_SET(MODE_ALTSCREEN))
- return;
+ if (n < 0)
+ n = term.row + n;
- if (n < 0) n = (-n) * term.row;
- if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off;
- while (!TLINE(-n)) --n;
- TSCREEN.off += n;
- selscroll(0, n);
- tfulldirt();
+ if (n > term.scr)
+ n = term.scr;
+
+ if (term.scr > 0) {
+ term.scr -= n;
+ selscroll(0, -n);
+ tfulldirt();
+ }
}
void
-kscrolldown(const Arg *a)
+kscrollup(const Arg* a)
{
-
int n = a->i;
- if (IS_SET(MODE_ALTSCREEN))
- return;
+ if (n < 0)
+ n = term.row + n;
- if (n < 0) n = (-n) * term.row;
- if (n > TSCREEN.off) n = TSCREEN.off;
- TSCREEN.off -= n;
- selscroll(0, -n);
- tfulldirt();
+ if (term.scr <= HISTSIZE-n) {
+ term.scr += n;
+ selscroll(0, n);
+ tfulldirt();
+ }
}
void
-tscrolldown(int orig, int n)
+tscrolldown(int orig, int n, int copyhist)
{
int i;
Line temp;
LIMIT(n, 0, term.bot-orig+1);
- /* Ensure that lines are allocated */
- for (i = -n; i < 0; i++) {
- TLINE(i) = ensureline(TLINE(i));
+ if (copyhist) {
+ term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
+ temp = term.hist[term.histi];
+ term.hist[term.histi] = term.line[term.bot];
+ term.line[term.bot] = temp;
}
- /* Shift non-scrolling areas in ring buffer */
- for (i = term.bot+1; i < term.row; i++) {
- temp = TLINE(i);
- TLINE(i) = TLINE(i-n);
- TLINE(i-n) = temp;
- }
- for (i = 0; i < orig; i++) {
- temp = TLINE(i);
- TLINE(i) = TLINE(i-n);
- TLINE(i-n) = temp;
+ tsetdirt(orig, term.bot-n);
+ tclearregion(0, term.bot-n+1, term.col-1, term.bot);
+
+ for (i = term.bot; i >= orig+n; i--) {
+ temp = term.line[i];
+ term.line[i] = term.line[i-n];
+ term.line[i-n] = temp;
}
- /* Scroll buffer */
- TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size;
- /* Clear lines that have entered the view */
- tclearregion(0, orig, term.linelen-1, orig+n-1);
- /* Redraw portion of the screen that has scrolled */
- tsetdirt(orig+n-1, term.bot);
selscroll(orig, n);
}
void
-tscrollup(int orig, int n)
+tscrollup(int orig, int n, int copyhist)
{
int i;
Line temp;
LIMIT(n, 0, term.bot-orig+1);
- /* Ensure that lines are allocated */
- for (i = term.row; i < term.row + n; i++) {
- TLINE(i) = ensureline(TLINE(i));
+ if (copyhist) {
+ term.histi = (term.histi + 1) % HISTSIZE;
+ temp = term.hist[term.histi];
+ term.hist[term.histi] = term.line[orig];
+ term.line[orig] = temp;
}
- /* Shift non-scrolling areas in ring buffer */
- for (i = orig-1; i >= 0; i--) {
- temp = TLINE(i);
- TLINE(i) = TLINE(i+n);
- TLINE(i+n) = temp;
- }
- for (i = term.row-1; i >term.bot; i--) {
- temp = TLINE(i);
- TLINE(i) = TLINE(i+n);
- TLINE(i+n) = temp;
+ if (term.scr > 0 && term.scr < HISTSIZE)
+ term.scr = MIN(term.scr + n, HISTSIZE-1);
+
+ tclearregion(0, orig, term.col-1, orig+n-1);
+ tsetdirt(orig+n, term.bot);
+
+ for (i = orig; i <= term.bot-n; i++) {
+ temp = term.line[i];
+ term.line[i] = term.line[i+n];
+ term.line[i+n] = temp;
}
- /* Scroll buffer */
- TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size;
- /* Clear lines that have entered the view */
- tclearregion(0, term.bot-n+1, term.linelen-1, term.bot);
- /* Redraw portion of the screen that has scrolled */
- tsetdirt(orig, term.bot-n+1);
selscroll(orig, -n);
}
@@ -1190,17 +1200,27 @@ selscroll(int orig, int n)
if (sel.ob.x == -1)
return;
- if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) {
- selclear();
- } else if (BETWEEN(sel.nb.y, orig, term.bot)) {
- sel.ob.y += n;
- sel.oe.y += n;
- if (sel.ob.y < term.top || sel.ob.y > term.bot ||
- sel.oe.y < term.top || sel.oe.y > term.bot) {
+ if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) {
+ if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) {
selclear();
+ return;
+ }
+ if (sel.type == SEL_RECTANGULAR) {
+ if (sel.ob.y < term.top)
+ sel.ob.y = term.top;
+ if (sel.oe.y > term.bot)
+ sel.oe.y = term.bot;
} else {
- selnormalize();
+ if (sel.ob.y < term.top) {
+ sel.ob.y = term.top;
+ sel.ob.x = 0;
+ }
+ if (sel.oe.y > term.bot) {
+ sel.oe.y = term.bot;
+ sel.oe.x = term.col;
+ }
}
+ selnormalize();
}
}
@@ -1210,7 +1230,7 @@ tnewline(int first_col)
int y = term.c.y;
if (y == term.bot) {
- tscrollup(term.top, 1);
+ tscrollup(term.top, 1, 1);
} else {
y++;
}
@@ -1272,7 +1292,7 @@ tmoveto(int x, int y)
}
void
-tsetchar(Rune u, const Glyph *attr, int x, int y)
+tsetchar(Rune u,const Glyph *attr, int x, int y)
{
static const char *vt100_0[62] = { /* 0x41 - 0x7e */
"↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */
@@ -1284,7 +1304,6 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
"⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
"│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
};
- Line line = TLINE(y);
/*
* The table is proudly stolen from rxvt.
@@ -1293,25 +1312,28 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
- if (line[x].mode & ATTR_WIDE) {
+ if (term.line[y][x].mode & ATTR_WIDE) {
if (x+1 < term.col) {
- line[x+1].u = ' ';
- line[x+1].mode &= ~ATTR_WDUMMY;
+ term.line[y][x+1].u = ' ';
+ term.line[y][x+1].mode &= ~ATTR_WDUMMY;
}
- } else if (line[x].mode & ATTR_WDUMMY) {
- line[x-1].u = ' ';
- line[x-1].mode &= ~ATTR_WIDE;
+ } else if (term.line[y][x].mode & ATTR_WDUMMY) {
+ term.line[y][x-1].u = ' ';
+ term.line[y][x-1].mode &= ~ATTR_WIDE;
}
term.dirty[y] = 1;
- line[x] = *attr;
- line[x].u = u;
+ term.line[y][x] = *attr;
+ term.line[y][x].u = u;
+
+ if (isboxdraw(u))
+ term.line[y][x].mode |= ATTR_BOXDRAW;
}
void
tclearregion(int x1, int y1, int x2, int y2)
{
- int x, y, L, S, temp;
+ int x, y, temp;
Glyph *gp;
if (x1 > x2)
@@ -1319,16 +1341,15 @@ tclearregion(int x1, int y1, int x2, int y2)
if (y1 > y2)
temp = y1, y1 = y2, y2 = temp;
- LIMIT(x1, 0, term.linelen-1);
- LIMIT(x2, 0, term.linelen-1);
+ LIMIT(x1, 0, term.maxcol-1);
+ LIMIT(x2, 0, term.maxcol-1);
LIMIT(y1, 0, term.row-1);
LIMIT(y2, 0, term.row-1);
- L = TLINEOFFSET(y1);
for (y = y1; y <= y2; y++) {
term.dirty[y] = 1;
for (x = x1; x <= x2; x++) {
- gp = &TSCREEN.buffer[L][x];
+ gp = &term.line[y][x];
if (selected(x, y))
selclear();
gp->fg = term.c.attr.fg;
@@ -1336,7 +1357,6 @@ tclearregion(int x1, int y1, int x2, int y2)
gp->mode = 0;
gp->u = ' ';
}
- L = (L + 1) % TSCREEN.size;
}
}
@@ -1351,7 +1371,7 @@ tdeletechar(int n)
dst = term.c.x;
src = term.c.x + n;
size = term.col - src;
- line = TLINE(term.c.y);
+ line = term.line[term.c.y];
memmove(&line[dst], &line[src], size * sizeof(Glyph));
tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
@@ -1368,7 +1388,7 @@ tinsertblank(int n)
dst = term.c.x + n;
src = term.c.x;
size = term.col - dst;
- line = TLINE(term.c.y);
+ line = term.line[term.c.y];
memmove(&line[dst], &line[src], size * sizeof(Glyph));
tclearregion(src, term.c.y, dst - 1, term.c.y);
@@ -1378,14 +1398,14 @@ void
tinsertblankline(int n)
{
if (BETWEEN(term.c.y, term.top, term.bot))
- tscrolldown(term.c.y, n);
+ tscrolldown(term.c.y, n, 0);
}
void
tdeleteline(int n)
{
if (BETWEEN(term.c.y, term.top, term.bot))
- tscrollup(term.c.y, n);
+ tscrollup(term.c.y, n, 0);
}
int32_t
@@ -1557,9 +1577,9 @@ tsetscroll(int t, int b)
}
void
-tsetmode(int priv, int set, const int *args, int narg)
+tsetmode(int priv, int set,const int *args, int narg)
{
- int alt; const int *lim;
+ int alt; const int *lim;
for (lim = args + narg; args < lim; ++args) {
if (priv) {
@@ -1735,12 +1755,6 @@ csihandle(void)
if (csiescseq.arg[0] == 0)
ttywrite(vtiden, strlen(vtiden), 0);
break;
- case 'b': /* REP -- if last char is printable print it <n> more times */
- DEFAULT(csiescseq.arg[0], 1);
- if (term.lastc)
- while (csiescseq.arg[0]-- > 0)
- tputc(term.lastc);
- break;
case 'C': /* CUF -- Cursor <n> Forward */
case 'a': /* HPR -- Cursor <n> Forward */
DEFAULT(csiescseq.arg[0], 1);
@@ -1822,11 +1836,11 @@ csihandle(void)
break;
case 'S': /* SU -- Scroll <n> line up */
DEFAULT(csiescseq.arg[0], 1);
- tscrollup(term.top, csiescseq.arg[0]);
+ tscrollup(term.top, csiescseq.arg[0], 0);
break;
case 'T': /* SD -- Scroll <n> line down */
DEFAULT(csiescseq.arg[0], 1);
- tscrolldown(term.top, csiescseq.arg[0]);
+ tscrolldown(term.top, csiescseq.arg[0], 0);
break;
case 'L': /* IL -- Insert <n> blank lines */
DEFAULT(csiescseq.arg[0], 1);
@@ -1864,7 +1878,7 @@ csihandle(void)
break;
case 'n': /* DSR – Device Status Report (cursor position) */
if (csiescseq.arg[0] == 6) {
- len = snprintf(buf, sizeof(buf), "\033[%i;%iR",
+ len = snprintf(buf, sizeof(buf),"\033[%i;%iR",
term.c.y+1, term.c.x+1);
ttywrite(buf, len, 0);
}
@@ -1901,7 +1915,7 @@ csihandle(void)
void
csidump(void)
{
- size_t i;
+ int i;
uint c;
fprintf(stderr, "ESC[");
@@ -1929,40 +1943,10 @@ csireset(void)
}
void
-osc_color_response(int num, int index, int is_osc4)
-{
- int n;
- char buf[32];
- unsigned char r, g, b;
-
- if (xgetcolor(is_osc4 ? num : index, &r, &g, &b)) {
- fprintf(stderr, "erresc: failed to fetch %s color %d\n",
- is_osc4 ? "osc4" : "osc",
- is_osc4 ? num : index);
- return;
- }
-
- n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x\007",
- is_osc4 ? "4;" : "", num, r, r, g, g, b, b);
- if (n < 0 || n >= sizeof(buf)) {
- fprintf(stderr, "error: %s while printing %s response\n",
- n < 0 ? "snprintf failed" : "truncation occurred",
- is_osc4 ? "osc4" : "osc");
- } else {
- ttywrite(buf, n, 1);
- }
-}
-
-void
strhandle(void)
{
char *p = NULL, *dec;
int j, narg, par;
- const struct { int idx; char *str; } osc_table[] = {
- { defaultfg, "foreground" },
- { defaultbg, "background" },
- { defaultcs, "cursor" }
- };
term.esc &= ~(ESC_STR_END|ESC_STR);
strparse();
@@ -1972,13 +1956,13 @@ strhandle(void)
case ']': /* OSC -- Operating System Command */
switch (par) {
case 0:
- if (narg > 1) {
+ if (narg > 1) {
xsettitle(strescseq.args[1]);
xseticontitle(strescseq.args[1]);
}
return;
case 1:
- if (narg > 1)
+ if (narg > 1)
xseticontitle(strescseq.args[1]);
return;
case 2:
@@ -1986,7 +1970,7 @@ strhandle(void)
xsettitle(strescseq.args[1]);
return;
case 52:
- if (narg > 2 && allowwindowops) {
+ if (narg > 2) {
dec = base64dec(strescseq.args[2]);
if (dec) {
xsetsel(dec);
@@ -1996,45 +1980,37 @@ strhandle(void)
}
}
return;
- case 10:
- case 11:
- case 12:
- if (narg < 2)
- break;
- p = strescseq.args[1];
- if ((j = par - 10) < 0 || j >= LEN(osc_table))
- break; /* shouldn't be possible */
-
- if (!strcmp(p, "?")) {
- osc_color_response(par, osc_table[j].idx, 0);
- } else if (xsetcolorname(osc_table[j].idx, p)) {
- fprintf(stderr, "erresc: invalid %s color: %s\n",
- osc_table[j].str, p);
- } else {
- tfulldirt();
- }
- return;
case 4: /* color set */
- if (narg < 3)
+ case 10: /* foreground set */
+ case 11: /* background set */
+ case 12: /* cursor color */
+ if ((par == 4 && narg < 3) || narg < 2)
break;
- p = strescseq.args[2];
+ p = strescseq.args[((par == 4) ? 2 : 1)];
/* FALLTHROUGH */
- case 104: /* color reset */
- j = (narg > 1) ? atoi(strescseq.args[1]) : -1;
-
- if (p && !strcmp(p, "?")) {
- osc_color_response(j, 0, 1);
- } else if (xsetcolorname(j, p)) {
+ case 104: /* color reset, here p = NULL */
+ if (par == 10)
+ j = defaultfg;
+ else if (par == 11)
+ j = defaultbg;
+ else if (par == 12)
+ j = defaultcs;
+ else
+ j = (narg > 1) ? atoi(strescseq.args[1]) : -1;
+
+ if (xsetcolorname(j, p)) {
if (par == 104 && narg <= 1)
return; /* color reset without parameter */
fprintf(stderr, "erresc: invalid color j=%d, p=%s\n",
- j, p ? p : "(null)");
+ j, p ? p : "(null)");
} else {
/*
* TODO if defaultbg color is changed, borders
* are dirty
*/
- tfulldirt();
+ if (j == defaultbg)
+ xclearwin();
+ redraw();
}
return;
}
@@ -2043,6 +2019,7 @@ strhandle(void)
xsettitle(strescseq.args[0]);
return;
case 'P': /* DCS -- Device Control String */
+ term.mode |= ESC_DCS;
case '_': /* APC -- Application Program Command */
case '^': /* PM -- Privacy Message */
return;
@@ -2077,7 +2054,7 @@ strparse(void)
void
strdump(void)
{
- size_t i;
+ int i;
uint c;
fprintf(stderr, "ESC%c", strescseq.type);
@@ -2104,10 +2081,7 @@ strdump(void)
void
strreset(void)
{
- strescseq = (STREscape){
- .buf = xrealloc(strescseq.buf, STR_BUF_SIZ),
- .siz = STR_BUF_SIZ,
- };
+ memset(&strescseq, 0, sizeof(strescseq));
}
void
@@ -2128,6 +2102,84 @@ tprinter(char *s, size_t len)
}
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;
+ /* modify externalpipe patch to pipe history too */
+ 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
+iso14755(const Arg *arg)
+{
+ FILE *p;
+ char *us, *e, codepoint[9], uc[UTF_SIZ];
+ unsigned long utf32;
+
+ if (!(p = popen(ISO14755CMD, "r")))
+ return;
+
+ us = fgets(codepoint, sizeof(codepoint), p);
+ pclose(p);
+
+ if (!us || *us == '\0' || *us == '-' || strlen(us) > 7)
+ return;
+ if ((utf32 = strtoul(us, &e, 16)) == ULONG_MAX ||
+ (*e != '\n' && *e != '\0'))
+ return;
+
+ ttywrite(uc, utf8encode(utf32, uc), 1);
+}
+
+void
toggleprinter(const Arg *arg)
{
term.mode ^= MODE_PRINT;
@@ -2162,10 +2214,10 @@ tdumpline(int n)
char buf[UTF_SIZ];
const Glyph *bp, *end;
- bp = &TLINE(n)[0];
+ bp = &term.line[n][0];
end = &bp[MIN(tlinelen(n), term.col) - 1];
if (bp != end || bp->u != ' ') {
- for ( ; bp <= end; ++bp)
+ for ( ;bp <= end; ++bp)
tprinter(buf, utf8encode(bp->u, buf));
}
tprinter("\n", 1);
@@ -2236,9 +2288,12 @@ tdectest(char c)
void
tstrsequence(uchar c)
{
+ strreset();
+
switch (c) {
case 0x90: /* DCS -- Device Control String */
c = 'P';
+ term.esc |= ESC_DCS;
break;
case 0x9f: /* APC -- Application Program Command */
c = '_';
@@ -2250,7 +2305,6 @@ tstrsequence(uchar c)
c = ']';
break;
}
- strreset();
strescseq.type = c;
term.esc |= ESC_STR;
}
@@ -2293,7 +2347,6 @@ tcontrolcode(uchar ascii)
return;
case '\032': /* SUB */
tsetchar('?', &term.c.attr, term.c.x, term.c.y);
- /* FALLTHROUGH */
case '\030': /* CAN */
csireset();
break;
@@ -2389,7 +2442,7 @@ eschandle(uchar ascii)
return 0;
case 'D': /* IND -- Linefeed */
if (term.c.y == term.bot) {
- tscrollup(term.top, 1);
+ tscrollup(term.top, 1, 1);
} else {
tmoveto(term.c.x, term.c.y+1);
}
@@ -2402,7 +2455,7 @@ eschandle(uchar ascii)
break;
case 'M': /* RI -- Reverse index */
if (term.c.y == term.top) {
- tscrolldown(term.top, 1);
+ tscrolldown(term.top, 1, 1);
} else {
tmoveto(term.c.x, term.c.y-1);
}
@@ -2448,13 +2501,15 @@ tputc(Rune u)
Glyph *gp;
control = ISCONTROL(u);
- if (u < 127 || !IS_SET(MODE_UTF8)) {
+ if (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
c[0] = u;
width = len = 1;
} else {
len = utf8encode(u, c);
- if (!control && (width = wcwidth(u)) == -1)
+ if (!control && (width = wcwidth(u)) == -1) {
+ memcpy(c, "\357\277\275", 4); /* UTF_INVALID */
width = 1;
+ }
}
if (IS_SET(MODE_PRINT))
@@ -2469,12 +2524,24 @@ tputc(Rune u)
if (term.esc & ESC_STR) {
if (u == '\a' || u == 030 || u == 032 || u == 033 ||
ISCONTROLC1(u)) {
- term.esc &= ~(ESC_START|ESC_STR);
+ term.esc &= ~(ESC_START|ESC_STR|ESC_DCS);
+ if (IS_SET(MODE_SIXEL)) {
+ /* TODO: render sixel */;
+ term.mode &= ~MODE_SIXEL;
+ return;
+ }
term.esc |= ESC_STR_END;
goto check_control_code;
}
- if (strescseq.len+len >= strescseq.siz) {
+ if (IS_SET(MODE_SIXEL)) {
+ /* TODO: implement sixel mode */
+ return;
+ }
+ if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q')
+ term.mode |= MODE_SIXEL;
+
+ if (strescseq.len+len >= sizeof(strescseq.buf)-1) {
/*
* Here is a bug in terminals. If the user never sends
* some code to stop the str or esc command, then st
@@ -2488,10 +2555,7 @@ tputc(Rune u)
* term.esc = 0;
* strhandle();
*/
- if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2)
- return;
- strescseq.siz *= 2;
- strescseq.buf = xrealloc(strescseq.buf, strescseq.siz);
+ return;
}
memmove(&strescseq.buf[strescseq.len], c, len);
@@ -2510,8 +2574,6 @@ check_control_code:
/*
* control codes are not shown ever
*/
- if (!term.esc)
- term.lastc = 0;
return;
} else if (term.esc & ESC_START) {
if (term.esc & ESC_CSI) {
@@ -2542,14 +2604,14 @@ check_control_code:
*/
return;
}
- if (selected(term.c.x, term.c.y))
+ if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y))
selclear();
- gp = &TLINE(term.c.y)[term.c.x];
+ gp = &term.line[term.c.y][term.c.x];
if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
gp->mode |= ATTR_WRAP;
tnewline(1);
- gp = &TLINE(term.c.y)[term.c.x];
+ gp = &term.line[term.c.y][term.c.x];
}
if (IS_SET(MODE_INSERT) && term.c.x+width < term.col)
@@ -2557,16 +2619,15 @@ check_control_code:
if (term.c.x+width > term.col) {
tnewline(1);
- gp = &TLINE(term.c.y)[term.c.x];
+ gp = &term.line[term.c.y][term.c.x];
}
tsetchar(u, &term.c.attr, term.c.x, term.c.y);
- term.lastc = u;
if (width == 2) {
gp->mode |= ATTR_WIDE;
if (term.c.x+1 < term.col) {
- if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) {
+ if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) {
gp[2].u = ' ';
gp[2].mode &= ~ATTR_WDUMMY;
}
@@ -2588,13 +2649,8 @@ twrite(const char *buf, int buflen, int show_ctrl)
Rune u;
int n;
- if (TSCREEN.off) {
- TSCREEN.off = 0;
- tfulldirt();
- }
-
for (n = 0; n < buflen; n += charsize) {
- if (IS_SET(MODE_UTF8)) {
+ if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
/* process a complete utf8 char */
charsize = utf8decode(buf + n, &u, buflen - n);
if (charsize == 0)
@@ -2619,104 +2675,101 @@ twrite(const char *buf, int buflen, int show_ctrl)
}
void
-clearline(Line line, Glyph g, int x, int xend)
-{
- int i;
- g.mode = 0;
- g.u = ' ';
- for (i = x; i < xend; ++i) {
- line[i] = g;
- }
-}
-
-Line
-ensureline(Line line)
-{
- if (!line) {
- line = xmalloc(term.linelen * sizeof(Glyph));
- }
- return line;
-}
-
-void
tresize(int col, int row)
{
int i, j;
- int minrow = MIN(row, term.row);
- int mincol = MIN(col, term.col);
- int linelen = MAX(col, term.linelen);
+ int tmp;
+ int minrow, mincol;
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 || row > HISTSIZE) {
+ if (col < 1 || row < 1) {
fprintf(stderr,
"tresize: error resizing to %dx%d\n", col, row);
return;
}
- /* Shift buffer to keep the cursor where we expect it */
- if (row <= term.c.y) {
- term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size;
- }
-
- /* Resize and clear line buffers as needed */
- if (linelen > term.linelen) {
- for (i = 0; i < term.screen[0].size; ++i) {
- if (term.screen[0].buffer[i]) {
- term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph));
- clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen);
- }
- }
- for (i = 0; i < minrow; ++i) {
- term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph));
- clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen);
- }
- }
- /* Allocate all visible lines for regular line buffer */
- for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size)
- {
- if (!term.screen[0].buffer[j]) {
- term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph));
- }
- if (i >= term.row) {
- clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen);
- }
+ /*
+ * slide screen to keep cursor where we expect it -
+ * tscrollup would work here, but we can optimize to
+ * memmove because we're freeing the earlier lines
+ */
+ for (i = 0; i <= term.c.y - row; i++) {
+ free(term.line[i]);
+ free(term.alt[i]);
}
- /* Resize alt screen */
- term.screen[1].cur = 0;
- term.screen[1].size = row;
- for (i = row; i < term.row; ++i) {
- free(term.screen[1].buffer[i]);
+ /* ensure that both src and dst are not NULL */
+ if (i > 0) {
+ memmove(term.line, term.line + i, row * sizeof(Line));
+ memmove(term.alt, term.alt + i, row * sizeof(Line));
}
- term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line));
- for (i = term.row; i < row; ++i) {
- term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph));
- clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen);
+ for (i += row; i < term.row; i++) {
+ free(term.line[i]);
+ free(term.alt[i]);
}
/* resize to new height */
+ term.line = xrealloc(term.line, row * sizeof(Line));
+ term.alt = xrealloc(term.alt, row * sizeof(Line));
term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
- /* fix tabstops */
- if (col > term.col) {
- bp = term.tabs + term.col;
+ for (i = 0; i < HISTSIZE; i++) {
+ term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
+ for (j = mincol; j < col; j++) {
+ term.hist[i][j] = term.c.attr;
+ term.hist[i][j].u = ' ';
+ }
+ }
+
+ /* resize each row to new width, zero-pad if needed */
+ for (i = 0; i < minrow; i++) {
+ term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
+ term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph));
+ }
+
+ /* allocate any new rows */
+ for (/* i = minrow */; i < row; i++) {
+ term.line[i] = xmalloc(col * sizeof(Glyph));
+ term.alt[i] = xmalloc(col * sizeof(Glyph));
+ }
- memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
+ if (col > term.maxcol) {
+ bp = term.tabs + term.maxcol;
+ memset(bp, 0, sizeof(*term.tabs) * (col - term.maxcol));
while (--bp > term.tabs && !*bp)
/* nothing */ ;
for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
*bp = 1;
}
-
/* update terminal size */
- term.col = col;
+ term.col = tmp;
+ term.maxcol = col;
term.row = row;
- term.linelen = linelen;
/* reset scrolling region */
tsetscroll(0, row-1);
/* make use of the LIMIT in tmoveto */
tmoveto(term.c.x, term.c.y);
- tfulldirt();
+ /* Clearing both screens (it makes dirty all lines) */
+ c = term.c;
+ for (i = 0; i < 2; i++) {
+ if (mincol < col && 0 < minrow) {
+ tclearregion(mincol, 0, col - 1, minrow - 1);
+ }
+ if (0 < col && minrow < row) {
+ tclearregion(0, minrow, col - 1, row - 1);
+ }
+ tswapscreen();
+ tcursor(CURSOR_LOAD);
+ }
+ term.c = c;
}
void
@@ -2728,22 +2781,20 @@ resettitle(void)
void
drawregion(int x1, int y1, int x2, int y2)
{
- int y, L;
-
- L = TLINEOFFSET(y1);
+ int y;
for (y = y1; y < y2; y++) {
- if (term.dirty[y]) {
- term.dirty[y] = 0;
- xdrawline(TSCREEN.buffer[L], x1, y, x2);
- }
- L = (L + 1) % TSCREEN.size;
+ if (!term.dirty[y])
+ continue;
+
+ term.dirty[y] = 0;
+ xdrawline(TLINE(y), x1, y, x2);
}
}
void
draw(void)
{
- int cx = term.c.x, ocx = term.ocx, ocy = term.ocy;
+ int cx = term.c.x;
if (!xstartdraw())
return;
@@ -2751,20 +2802,25 @@ draw(void)
/* adjust cursor position */
LIMIT(term.ocx, 0, term.col-1);
LIMIT(term.ocy, 0, term.row-1);
- if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY)
+ if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY)
term.ocx--;
- if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY)
+ if (term.line[term.c.y][cx].mode & ATTR_WDUMMY)
cx--;
drawregion(0, 0, term.col, term.row);
- if (TSCREEN.off == 0)
- xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
- term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
- term.ocx = cx;
- term.ocy = term.c.y;
+ 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);
+ term.ocx = cx, term.ocy = term.c.y;
xfinishdraw();
- if (ocx != term.ocx || ocy != term.ocy)
+
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ if (ximspot_update_interval && TIMEDIFF(now, term.last_ximspot_update) > ximspot_update_interval) {
xximspot(term.ocx, term.ocy);
+ term.last_ximspot_update = now;
+ }
}
void
diff --git a/st.desktop b/st.desktop
new file mode 100644
index 0000000..b9f767e
--- /dev/null
+++ b/st.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Type=Application
+Name=st
+Comment=simple-terminal emulator for X
+Icon=st
+Exec=st
+Categories=System;TerminalEmulator
+StartupWMClass=st-256color
diff --git a/st.h b/st.h
index 35e23c6..f64d44c 100644
--- a/st.h
+++ b/st.h
@@ -3,15 +3,17 @@
#include <stdint.h>
#include <sys/types.h>
+//#include <gd.h>
+#include <glib.h>
+
/* macros */
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#define MAX(a, b) ((a) < (b) ? (b) : (a))
#define LEN(a) (sizeof(a) / sizeof(a)[0])
#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
#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 != (b).mode || (a).fg != (b).fg || \
+#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 TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
(t1.tv_nsec-t2.tv_nsec)/1E6)
@@ -19,7 +21,6 @@
#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
#define IS_TRUECOL(x) (1 << 24 & (x))
-#define HISTSIZE 2000
enum glyph_attribute {
ATTR_NULL = 0,
@@ -34,13 +35,15 @@ 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,
};
enum drawing_mode {
- DRAW_NONE = 0,
- DRAW_BG = 1 << 0,
- DRAW_FG = 1 << 1,
+ DRAW_NONE = 0,
+ DRAW_BG = 1 << 0,
+ DRAW_FG = 1 << 1,
};
enum selection_mode {
@@ -81,19 +84,31 @@ typedef union {
uint ui;
float f;
const void *v;
- const char *s;
} Arg;
+typedef struct {
+ uint b;
+ uint mask;
+ void (*func)(const Arg *);
+ const Arg arg;
+} MouseKey;
+
void die(const char *, ...);
void redraw(void);
void draw(void);
+void externalpipe(const Arg *);
+void iso14755(const Arg *);
+void kscrolldown(const Arg *);
+void kscrollup(const Arg *);
+void newterm(const Arg *);
void printscreen(const Arg *);
void printsel(const Arg *);
void sendbreak(const Arg *);
void toggleprinter(const Arg *);
int tattrset(int);
+int tisaltscr(void);
void tnew(int, int);
void tresize(int, int);
void tsetdirtattr(int);
@@ -118,16 +133,26 @@ 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;
extern char *stty_args;
extern char *vtiden;
extern wchar_t *worddelimiters;
extern int allowaltscreen;
-extern int allowwindowops;
extern char *termname;
extern unsigned int tabspaces;
extern unsigned int defaultfg;
extern unsigned int defaultbg;
extern unsigned int defaultcs;
+extern const int boxdraw, boxdraw_bold, boxdraw_braille;
+extern float alpha;
+extern MouseKey mkeys[];
+extern int ximspot_update_interval;
diff --git a/st.info b/st.info
index 8201ad6..78ffd30 100644
--- a/st.info
+++ b/st.info
@@ -1,4 +1,4 @@
-st-mono| simpleterm monocolor,
+st| simpleterm,
acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
am,
bce,
@@ -10,7 +10,7 @@ st-mono| simpleterm monocolor,
civis=\E[?25l,
clear=\E[H\E[2J,
cnorm=\E[?12l\E[?25h,
- colors#2,
+ colors#8,
cols#80,
cr=^M,
csr=\E[%i%p1%d;%p2%dr,
@@ -158,7 +158,6 @@ st-mono| simpleterm monocolor,
rc=\E8,
rev=\E[7m,
ri=\EM,
- rin=\E[%p1%dT,
ritm=\E[23m,
rmacs=\E(B,
rmcup=\E[?1049l,
@@ -169,8 +168,13 @@ st-mono| simpleterm monocolor,
rs1=\Ec,
rs2=\E[4l\E>\E[?1034l,
sc=\E7,
- sitm=\E[3m,
+ setab=\E[4%p1%dm,
+ setaf=\E[3%p1%dm,
+ setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
+ setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
sgr0=\E[0m,
+ sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
+ sitm=\E[3m,
smacs=\E(0,
smcup=\E[?1049h,
smir=\E[4h,
@@ -184,23 +188,12 @@ st-mono| simpleterm monocolor,
# XTerm extensions
rmxx=\E[29m,
smxx=\E[9m,
-# 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)
Tc,
Ms=\E]52;%p1%s;%p2%s\007,
Se=\E[2 q,
Ss=\E[%p1%d q,
-st| simpleterm,
- use=st-mono,
- colors#8,
- setab=\E[4%p1%dm,
- setaf=\E[3%p1%dm,
- setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
- setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
- sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
-
st-256color| simpleterm with 256 colors,
use=st,
ccc,
@@ -227,13 +220,3 @@ st-meta-256color| simpleterm with meta key and 256 colors,
smm=\E[?1034h,
rs2=\E[4l\E>\E[?1034h,
is2=\E[4l\E>\E[?1034h,
-
-st-bs| simpleterm with backspace as backspace,
- use=st,
- kbs=\010,
- kdch1=\177,
-
-st-bs-256color| simpleterm with backspace as backspace and 256colors,
- use=st-256color,
- kbs=\010,
- kdch1=\177,
diff --git a/st.png b/st.png
new file mode 100644
index 0000000..806c142
--- /dev/null
+++ b/st.png
Binary files differ
diff --git a/utils.h b/utils.h
new file mode 100644
index 0000000..ca435e6
--- /dev/null
+++ b/utils.h
@@ -0,0 +1,23 @@
+/// Dynamic memory-chunk, with (1) datatype size, (2/3) initialized / allocated chunk, (4) content
+typedef struct { uint8_t const elSize; uint32_t init, alloc; char* content; } DynamicArray;
+#define UTF8_ARRAY {4, 0, 0, NULL}
+
+static inline int p_alloc(DynamicArray *s, uint32_t amount) {
+ uint32_t const diff=s->init+s->elSize*amount-s->alloc, nas=s->alloc+max(diff,15)*s->elSize;
+ if (s->alloc < s->init + s->elSize * amount) {
+ char* tmp = realloc(s->content, nas);
+ if (!tmp) return 0;
+ s->alloc = nas, s->content = tmp;
+ }
+ return 1;
+}
+static inline char *view(DynamicArray * s, uint32_t i) { return s->content + i*s->elSize; }
+static inline char *end(DynamicArray *s, uint32_t i) { return s->content +s->init-(i+1)*s->elSize; }
+static inline uint32_t getU32(DynamicArray* s, uint32_t i, int b) { return *((uint32_t*) (b ?view(s,i) :end(s,i))); }
+static char *expand(DynamicArray *s) { if (!p_alloc(s, 1)) return NULL; s->init += s->elSize; return end(s, 0); }
+static inline void pop(DynamicArray* s) { s->init -= s->elSize; }
+static inline void empty(DynamicArray* s) { s->init = 0; }
+static inline int size(DynamicArray const * s) { return s->init / s->elSize; }
+static inline void assign(DynamicArray* s, DynamicArray const *o) {
+ if (p_alloc(s, size(o))) memcpy(s->content, o->content, (s->init=o->init));
+}
diff --git a/win.h b/win.h
index 6de960d..b9532a9 100644
--- a/win.h
+++ b/win.h
@@ -25,12 +25,11 @@ enum win_mode {
void xbell(void);
void xclipcopy(void);
-void xdrawcursor(int, int, Glyph, int, int, Glyph);
+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
void xdrawline(Line, int, int, int);
void xfinishdraw(void);
void xloadcols(void);
int xsetcolorname(int, const char *);
-int xgetcolor(int, unsigned char *, unsigned char *, unsigned char *);
void xseticontitle(char *);
void xsettitle(char *);
int xsetcursor(int);
@@ -39,3 +38,4 @@ void xsetpointermotion(int);
void xsetsel(char *);
int xstartdraw(void);
void xximspot(int, int);
+void xclearwin(void);
diff --git a/x.c b/x.c
index e027f67..87b1001 100644
--- a/x.c
+++ b/x.c
@@ -14,11 +14,13 @@
#include <X11/keysym.h>
#include <X11/Xft/Xft.h>
#include <X11/XKBlib.h>
+#include <X11/Xresource.h>
-char *argv0;
+static char *argv0;
#include "arg.h"
#include "st.h"
#include "win.h"
+#include "hb.h"
/* types used in config.h */
typedef struct {
@@ -29,11 +31,9 @@ typedef struct {
} Shortcut;
typedef struct {
- uint mod;
- uint button;
- void (*func)(const Arg *);
- const Arg arg;
- uint release;
+ uint b;
+ uint mask;
+ char *s;
} MouseShortcut;
typedef struct {
@@ -45,6 +45,19 @@ 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
@@ -55,12 +68,10 @@ 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 *);
-void kscrollup(const Arg *);
-void kscrolldown(const Arg *);
/* config.h for applying patches and the configuration. */
#include "config.h"
@@ -79,12 +90,16 @@ typedef XftDraw *Draw;
typedef XftColor Color;
typedef XftGlyphFontSpec GlyphFontSpec;
+typedef unsigned long int CARD32;
+
/* Purely graphic info */
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 cyo; /* char y offset */
int mode; /* window state/mode flags */
int cursor; /* cursor style */
} TermWindow;
@@ -95,18 +110,15 @@ typedef struct {
Window win;
Drawable buf;
GlyphFontSpec *specbuf; /* font spec buffer used for rendering */
- Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid;
- struct {
- XIM xim;
- XIC xic;
- XPoint spot;
- XVaNestedList spotlist;
- } ime;
+ Atom xembed, wmdeletewin, netwmname, netwmicon, netwmiconname, netwmpid;
+ XIM xim;
+ XIC xic;
Draw draw;
Visual *vis;
XSetWindowAttributes attrs;
int scr;
int isfixed; /* is fixed geometry? */
+ int depth; /* bit depth */
int l, t; /* left and top offset */
int gm; /* geometry mask */
} XWindow;
@@ -148,10 +160,9 @@ static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int,
static void xdrawglyph(Glyph, int, int);
static void xclear(int, int, int, int);
static int xgeommasktogravity(int);
-static int ximopen(Display *);
+static void ximopen(Display *);
static void ximinstantiate(Display *, XPointer, XPointer);
static void ximdestroy(XIM, XPointer, XPointer);
-static int xicdestroy(XIC, XPointer, XPointer);
static void xinit(int, int);
static void cresize(int, int);
static void xresize(int, int);
@@ -159,6 +170,8 @@ static void xhints(void);
static int xloadcolor(int, const char *, Color *);
static int xloadfont(Font *, FcPattern *);
static void xloadfonts(const char *, double);
+static int xloadsparefont(FcPattern *, int);
+static void xloadsparefonts(void);
static void xunloadfont(Font *);
static void xunloadfonts(void);
static void xsetenv(void);
@@ -173,8 +186,6 @@ static void kpress(XEvent *);
static void cmessage(XEvent *);
static void resize(XEvent *);
static void focus(XEvent *);
-static uint buttonmask(uint);
-static int mouseaction(XEvent *, uint);
static void brelease(XEvent *);
static void bpress(XEvent *);
static void bmotion(XEvent *);
@@ -245,6 +256,7 @@ static char *usedfont = NULL;
static double usedfontsize = 0;
static double defaultfontsize = 0;
+static char *opt_alpha = NULL;
static char *opt_class = NULL;
static char **opt_cmd = NULL;
static char *opt_embed = NULL;
@@ -254,7 +266,8 @@ static char *opt_line = NULL;
static char *opt_name = NULL;
static char *opt_title = NULL;
-static uint buttons; /* bit field of pressed buttons */
+static int oldbutton = 3; /* button event on startup: 3 = release */
+static int cursorblinks = 0;
void
clipcopy(const Arg *dummy)
@@ -295,6 +308,22 @@ numlock(const Arg *dummy)
}
void
+changealpha(const Arg *arg)
+{
+ if(alpha > 1 && arg->f == 2 )
+ alpha = 1;
+ if((alpha > 0 && arg->f < 0) || (alpha < 1 && arg->f > 0))
+ alpha += arg->f;
+ if(alpha < 0)
+ alpha = 0;
+ if(alpha > 1)
+ alpha = 1;
+
+ xloadcols();
+ redraw();
+}
+
+void
zoom(const Arg *arg)
{
Arg larg;
@@ -308,6 +337,7 @@ zoomabs(const Arg *arg)
{
xunloadfonts();
xloadfonts(usedfont, arg->f);
+ xloadsparefonts();
cresize(0, 0);
redraw();
xhints();
@@ -324,12 +354,6 @@ zoomreset(const Arg *arg)
}
}
-void
-ttysend(const Arg *arg)
-{
- ttywrite(arg->s, strlen(arg->s), 1);
-}
-
int
evcol(XEvent *e)
{
@@ -350,7 +374,7 @@ void
mousesel(XEvent *e, int done)
{
int type, seltype = SEL_REGULAR;
- uint state = e->xbutton.state & ~(Button1Mask | forcemousemod);
+ uint state = e->xbutton.state & ~(Button1Mask | forceselmod);
for (type = 1; type < LEN(selmasks); ++type) {
if (match(selmasks[type], state)) {
@@ -366,68 +390,62 @@ mousesel(XEvent *e, int done)
void
mousereport(XEvent *e)
{
- int len, btn, code;
- int x = evcol(e), y = evrow(e);
- int state = e->xbutton.state;
+ int len, x = evcol(e), y = evrow(e),
+ button = e->xbutton.button, state = e->xbutton.state;
char buf[40];
static int ox, oy;
- if (e->type == MotionNotify) {
+ /* from urxvt */
+ if (e->xbutton.type == MotionNotify) {
if (x == ox && y == oy)
return;
if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY))
return;
- /* MODE_MOUSEMOTION: no reporting if no button is pressed */
- if (IS_SET(MODE_MOUSEMOTION) && buttons == 0)
+ /* MOUSE_MOTION: no reporting if no button is pressed */
+ if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3)
return;
- /* 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;
+
+ button = oldbutton + 32;
+ ox = x;
+ oy = y;
} else {
- btn = e->xbutton.button;
- /* Only buttons 1 through 11 can be encoded */
- if (btn < 1 || btn > 11)
- return;
- if (e->type == ButtonRelease) {
+ if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) {
+ button = 3;
+ } else {
+ button -= Button1;
+ if (button >= 3)
+ if (button >= 7)
+ button += 128 - 7;
+ else 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;
/* MODE_MOUSEX10: no button release reporting */
if (IS_SET(MODE_MOUSEX10))
return;
- /* Don't send release events for the scroll wheel */
- if (btn == 4 || btn == 5)
+ if (button == 64 || button == 65)
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)) {
- code += ((state & ShiftMask ) ? 4 : 0)
- + ((state & Mod1Mask ) ? 8 : 0) /* meta key: alt */
- + ((state & ControlMask) ? 16 : 0);
+ button += ((state & ShiftMask ) ? 4 : 0)
+ + ((state & Mod4Mask ) ? 8 : 0)
+ + ((state & ControlMask) ? 16 : 0);
}
if (IS_SET(MODE_MOUSESGR)) {
len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c",
- code, x+1, y+1,
- e->type == ButtonRelease ? 'm' : 'M');
+ button, x+1, y+1,
+ e->xbutton.type == ButtonRelease ? 'm' : 'M');
} else if (x < 223 && y < 223) {
len = snprintf(buf, sizeof(buf), "\033[M%c%c%c",
- 32+code, 32+x+1, 32+y+1);
+ 32+button, 32+x+1, 32+y+1);
} else {
return;
}
@@ -435,57 +453,38 @@ mousereport(XEvent *e)
ttywrite(buf, len, 0);
}
-uint
-buttonmask(uint button)
-{
- return button == Button1 ? Button1Mask
- : button == Button2 ? Button2Mask
- : button == Button3 ? Button3Mask
- : button == Button4 ? Button4Mask
- : button == Button5 ? Button5Mask
- : 0;
-}
-
-int
-mouseaction(XEvent *e, uint release)
-{
- MouseShortcut *ms;
-
- /* ignore Button<N>mask for Button<N> - it's set on release */
- uint state = e->xbutton.state & ~buttonmask(e->xbutton.button);
-
- for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
- if (ms->release == release &&
- ms->button == e->xbutton.button &&
- (match(ms->mod, state) || /* exact or forced */
- match(ms->mod, state & ~forcemousemod))) {
- ms->func(&(ms->arg));
- return 1;
- }
- }
-
- return 0;
-}
-
void
bpress(XEvent *e)
{
- int btn = e->xbutton.button;
struct timespec now;
+ MouseShortcut *ms;
+ MouseKey *mk;
int snap;
- if (1 <= btn && btn <= 11)
- buttons |= 1 << (btn-1);
-
- if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
+ if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
mousereport(e);
return;
}
- if (mouseaction(e, 0))
- return;
+ if (tisaltscr()) {
+ for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
+ if (e->xbutton.button == ms->b
+ && match(ms->mask, e->xbutton.state)) {
+ ttywrite(ms->s, strlen(ms->s), 1);
+ return;
+ }
+ }
+ }
+
+ for (mk = mkeys; mk < mkeys + LEN(mkeys); mk++) {
+ if (e->xbutton.button == mk->b
+ && match(mk->mask, e->xbutton.state)) {
+ mk->func(&mk->arg);
+ return;
+ }
+ }
- if (btn == Button1) {
+ if (e->xbutton.button == Button1) {
/*
* If the user clicks below predefined timeouts specific
* snapping behaviour is exposed.
@@ -688,8 +687,6 @@ setsel(char *str, Time t)
XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
selclear();
-
- xclipcopy();
}
void
@@ -701,26 +698,20 @@ xsetsel(char *str)
void
brelease(XEvent *e)
{
- int btn = e->xbutton.button;
-
- if (1 <= btn && btn <= 11)
- buttons &= ~(1 << (btn-1));
-
- if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
+ if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
mousereport(e);
return;
}
-
- if (mouseaction(e, 1))
- return;
- if (btn == Button1)
+ if (e->xbutton.button == Button3)
+ selpaste(NULL);
+ else if (e->xbutton.button == Button1)
mousesel(e, 1);
}
void
bmotion(XEvent *e)
{
- if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
+ if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
mousereport(e);
return;
}
@@ -756,7 +747,7 @@ xresize(int col, int row)
XFreePixmap(xw.dpy, xw.buf);
xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
- DefaultDepth(xw.dpy, xw.scr));
+ xw.depth);
XftDrawChange(xw.draw, xw.buf);
xclear(0, 0, win.w, win.h);
@@ -816,20 +807,20 @@ xloadcols(void)
else
die("could not allocate color %d\n", i);
}
- loaded = 1;
-}
-int
-xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b)
-{
- if (!BETWEEN(x, 0, dc.collen))
- return 1;
-
- *r = dc.col[x].color.red >> 8;
- *g = dc.col[x].color.green >> 8;
- *b = dc.col[x].color.blue >> 8;
-
- return 0;
+ /* set alpha value of bg color */
+ if (opt_alpha)
+ alpha = strtof(opt_alpha, NULL);
+ dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha);
+ dc.col[defaultbg].color.red =
+ ((unsigned short)(dc.col[defaultbg].color.red * alpha)) & 0xff00;
+ dc.col[defaultbg].color.green =
+ ((unsigned short)(dc.col[defaultbg].color.green * alpha)) & 0xff00;
+ dc.col[defaultbg].color.blue =
+ ((unsigned short)(dc.col[defaultbg].color.blue * alpha)) & 0xff00;
+ dc.col[defaultbg].pixel &= 0x00FFFFFF;
+ dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24;
+ loaded = 1;
}
int
@@ -845,6 +836,8 @@ 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);
return 0;
}
@@ -861,10 +854,17 @@ xclear(int x1, int y1, int x2, int y2)
}
void
+xclearwin(void)
+{
+ xclear(0, 0, win.w, win.h);
+}
+
+
+void
xhints(void)
{
- XClassHint class = {opt_name ? opt_name : termname,
- opt_class ? opt_class : termname};
+ XClassHint class = {opt_name ? opt_name : "st",
+ opt_class ? opt_class : "St"};
XWMHints wm = {.flags = InputHint, .input = 1};
XSizeHints *sizeh;
@@ -873,8 +873,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;
@@ -993,7 +993,7 @@ xloadfonts(const char *fontstr, double fontsize)
if (fontstr[0] == '-')
pattern = XftXlfdParse(fontstr, False, False);
else
- pattern = FcNameParse((const FcChar8 *)fontstr);
+ pattern = FcNameParse((const FcChar8 *)fontstr);
if (!pattern)
die("can't open font %s\n", fontstr);
@@ -1035,6 +1035,7 @@ xloadfonts(const char *fontstr, double fontsize)
/* Setting character width and height. */
win.cw = ceilf(dc.font.width * cwscale);
win.ch = ceilf(dc.font.height * chscale);
+ win.cyo = ceilf(dc.font.height * (chscale - 1) / 2);
FcPatternDel(pattern, FC_SLANT);
FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
@@ -1054,6 +1055,101 @@ xloadfonts(const char *fontstr, double fontsize)
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)
{
@@ -1066,6 +1162,9 @@ 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);
@@ -1076,58 +1175,41 @@ xunloadfonts(void)
xunloadfont(&dc.ibfont);
}
-int
+void
ximopen(Display *dpy)
{
- XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy };
- XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy };
-
- xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL);
- if (xw.ime.xim == NULL)
- return 0;
-
- if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL))
- fprintf(stderr, "XSetIMValues: "
- "Could not set XNDestroyCallback.\n");
+ XIMCallback destroy = { .client_data = NULL, .callback = ximdestroy };
- xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot,
- NULL);
-
- if (xw.ime.xic == NULL) {
- xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle,
- XIMPreeditNothing | XIMStatusNothing,
- XNClientWindow, xw.win,
- XNDestroyCallback, &icdestroy,
- NULL);
+ if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
+ XSetLocaleModifiers("@im=local");
+ if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
+ XSetLocaleModifiers("@im=");
+ if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL)
+ die("XOpenIM failed. Could not open input device.\n");
+ }
}
- if (xw.ime.xic == NULL)
- fprintf(stderr, "XCreateIC: Could not create input context.\n");
-
- return 1;
+ if (XSetIMValues(xw.xim, XNDestroyCallback, &destroy, NULL) != NULL)
+ die("XSetIMValues failed. Could not set input method value.\n");
+ xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
+ XNClientWindow, xw.win, XNFocusWindow, xw.win, NULL);
+ if (xw.xic == NULL)
+ die("XCreateIC failed. Could not obtain input method.\n");
}
void
ximinstantiate(Display *dpy, XPointer client, XPointer call)
{
- if (ximopen(dpy))
- XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
- ximinstantiate, NULL);
+ ximopen(dpy);
+ XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
+ ximinstantiate, NULL);
}
void
ximdestroy(XIM xim, XPointer client, XPointer call)
{
- xw.ime.xim = NULL;
+ xw.xim = NULL;
XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
- ximinstantiate, NULL);
- XFree(xw.ime.spotlist);
-}
-
-int
-xicdestroy(XIC xim, XPointer client, XPointer call)
-{
- xw.ime.xic = NULL;
- return 1;
+ ximinstantiate, NULL);
}
void
@@ -1138,11 +1220,21 @@ xinit(int cols, int rows)
Window parent;
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);
- xw.vis = XDefaultVisual(xw.dpy, xw.scr);
+
+ if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) {
+ parent = XRootWindow(xw.dpy, xw.scr);
+ xw.depth = 32;
+ } else {
+ XGetWindowAttributes(xw.dpy, parent, &attr);
+ xw.depth = attr.depth;
+ }
+
+ XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis);
+ xw.vis = vis.visual;
/* font */
if (!FcInit())
@@ -1151,13 +1243,16 @@ xinit(int cols, int rows)
usedfont = (opt_font == NULL)? font : opt_font;
xloadfonts(usedfont, 0);
+ /* spare fonts */
+ xloadsparefonts();
+
/* colors */
- xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
+ 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 + cols * win.cw;
+ win.h = 2 * win.vborderpx + rows * win.ch;
if (xw.gm & XNegative)
xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
if (xw.gm & YNegative)
@@ -1172,19 +1267,15 @@ xinit(int cols, int rows)
| ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
xw.attrs.colormap = xw.cmap;
- if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
- parent = XRootWindow(xw.dpy, xw.scr);
xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t,
- win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
+ win.w, win.h, 0, xw.depth, InputOutput,
xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
| CWEventMask | CWColormap, &xw.attrs);
memset(&gcvalues, 0, sizeof(gcvalues));
gcvalues.graphics_exposures = False;
- dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
- &gcvalues);
- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
- DefaultDepth(xw.dpy, xw.scr));
+ xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth);
+ dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues);
XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
@@ -1195,10 +1286,7 @@ xinit(int cols, int rows)
xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
/* input methods */
- if (!ximopen(xw.dpy)) {
- XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
- ximinstantiate, NULL);
- }
+ ximopen(xw.dpy);
/* white cursor, black outline */
cursor = XCreateFontCursor(xw.dpy, mouseshape);
@@ -1221,7 +1309,7 @@ xinit(int cols, int rows)
xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
- xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False);
+ xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False);
XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False);
@@ -1230,8 +1318,8 @@ xinit(int cols, int rows)
win.mode = MODE_NUMLOCK;
resettitle();
- xhints();
XMapWindow(xw.dpy, xw.win);
+ xhints();
XSync(xw.dpy, False);
clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1);
@@ -1241,6 +1329,8 @@ xinit(int cols, int rows)
xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
if (xsel.xtarget == None)
xsel.xtarget = XA_STRING;
+
+ boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis);
}
int
@@ -1259,13 +1349,13 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
FcCharSet *fccharset;
int i, f, numspecs = 0;
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
+ for (i = 0, xp = winx, yp = winy + font->ascent + win.cyo; i < len; ++i) {
/* Fetch rune and mode for current glyph. */
rune = glyphs[i].u;
mode = glyphs[i].mode;
/* Skip dummy wide-character spacing. */
- if (mode == ATTR_WDUMMY)
+ if (mode & ATTR_WDUMMY)
continue;
/* Determine font for glyph if different from previous glyph. */
@@ -1284,11 +1374,16 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
font = &dc.bfont;
frcflags = FRC_BOLD;
}
- yp = winy + font->ascent;
+ yp = winy + font->ascent + win.cyo;
}
- /* Lookup character index with default font. */
- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
+ 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);
+ }
if (glyphidx) {
specs[numspecs].font = font->match;
specs[numspecs].glyph = glyphidx;
@@ -1372,6 +1467,9 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
numspecs++;
}
+ /* Harfbuzz transformation for ligatures. */
+ hbtransform(specs, glyphs, len, x, y);
+
return numspecs;
}
@@ -1383,7 +1481,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
width = charlen * win.cw;
Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
XRenderColor colfg, colbg;
- XRectangle r;
/* Fallback on color display for attributes not supported by the font */
if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) {
@@ -1416,10 +1513,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
bg = &dc.col[base.bg];
}
- /* Change basic system colors [0-7] to bright system colors [8-15] */
- if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7))
- fg = &dc.col[base.fg + 8];
-
if (IS_SET(MODE_REVERSE)) {
if (fg == &dc.col[defaultfg]) {
fg = &dc.col[defaultbg];
@@ -1467,40 +1560,45 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
if (base.mode & ATTR_INVISIBLE)
fg = bg;
- if (dmode & DRAW_BG) {
- /* Intelligent cleaning up of the borders. */
- if (x == 0) {
- xclear(0, (y == 0)? 0 : winy, borderpx,
- winy + win.ch +
- ((winy + win.ch >= borderpx + win.th)? win.h : 0));
- }
- if (winx + width >= borderpx + win.tw) {
- xclear(winx + width, (y == 0)? 0 : winy, win.w,
- ((winy + win.ch >= borderpx + 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, winy + win.ch, winx + width, win.h);
- /* Fill the background */
- XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
- }
-
- if (dmode & DRAW_FG) {
- /* Render the glyphs. */
- XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
-
- /* Render underline and strikethrough. */
- if (base.mode & ATTR_UNDERLINE) {
- XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
- width, 1);
- }
+ if (dmode & DRAW_BG) {
+ /* Intelligent cleaning up of the borders. */
+ if (x == 0) {
+ xclear(0, (y == 0)? 0 : winy, borderpx,
+ winy + win.ch +
+ ((winy + win.ch >= borderpx + win.th)? win.h : 0));
+ }
+ if (winx + width >= borderpx + win.tw) {
+ xclear(winx + width, (y == 0)? 0 : winy, win.w,
+ ((winy + win.ch >= borderpx + 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, winy + win.ch, winx + width, win.h);
- if (base.mode & ATTR_STRUCK) {
- XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3,
- width, 1);
- }
- }
+ /* Fill the background */
+ XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
+ }
+
+ if (dmode & DRAW_FG) {
+ if (base.mode & ATTR_BOXDRAW) {
+ drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len);
+ } else {
+ /* Render the glyphs. */
+ XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
+ }
+
+ /* Render underline and strikethrough. */
+ if (base.mode & ATTR_UNDERLINE) {
+ XftDrawRect(xw.draw, fg, winx, winy + win.cyo + dc.font.ascent + 1,
+ width, 1);
+ }
+
+ if (base.mode & ATTR_STRUCK) {
+ XftDrawRect(xw.draw, fg, winx, winy + win.cyo + 2 * dc.font.ascent / 3,
+ width, 1);
+ }
+ }
}
void
@@ -1514,22 +1612,24 @@ xdrawglyph(Glyph g, int x, int y)
}
void
-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len)
{
Color drawcol;
/* remove the old cursor */
if (selected(ox, oy))
og.mode ^= ATTR_REVERSE;
- xdrawglyph(og, ox, oy);
- if (IS_SET(MODE_HIDE))
- return;
+ /* Redraw the line where cursor was previously.
+ * It will restore the ligatures broken by the cursor. */
+ xdrawline(line, 0, oy, len);
+
+ if (IS_SET(MODE_HIDE) || !IS_SET(MODE_FOCUSED)) return;
/*
* Select the right color for the right mode.
*/
- g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE;
+ g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW;
if (IS_SET(MODE_REVERSE)) {
g.mode |= ATTR_REVERSE;
@@ -1555,16 +1655,19 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
/* draw the new one */
if (IS_SET(MODE_FOCUSED)) {
switch (win.cursor) {
- case 7: /* st extension */
- g.u = 0x2603; /* snowman (U+2603) */
+ case 0: /* Blinking block */
+ case 1: /* Blinking block (default) */
+ if (IS_SET(MODE_BLINK))
+ break;
/* FALLTHROUGH */
- case 0: /* Blinking Block */
- case 1: /* Blinking Block (Default) */
- case 2: /* Steady Block */
+ case 2: /* Steady block */
xdrawglyph(g, cx, cy);
break;
- case 3: /* Blinking Underline */
- case 4: /* Steady Underline */
+ case 3: /* Blinking underline */
+ if (IS_SET(MODE_BLINK))
+ break;
+ /* FALLTHROUGH */
+ case 4: /* Steady underline */
XftDrawRect(xw.draw, &drawcol,
borderpx + cx * win.cw,
borderpx + (cy + 1) * win.ch - \
@@ -1572,13 +1675,19 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
win.cw, cursorthickness);
break;
case 5: /* Blinking bar */
+ if (IS_SET(MODE_BLINK))
+ break;
+ /* FALLTHROUGH */
case 6: /* Steady bar */
XftDrawRect(xw.draw, &drawcol,
borderpx + cx * win.cw,
borderpx + cy * win.ch,
cursorthickness, win.ch);
break;
- }
+ case 7: /* Blinking st cursor */
+ if (IS_SET(MODE_BLINK))
+ break;
+ }
} else {
XftDrawRect(xw.draw, &drawcol,
borderpx + cx * win.cw,
@@ -1614,7 +1723,7 @@ xseticontitle(char *p)
XTextProperty prop;
DEFAULT(p, opt_title);
- if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
+ if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
&prop) != Success)
return;
XSetWMIconName(xw.dpy, xw.win, &prop);
@@ -1628,7 +1737,7 @@ xsettitle(char *p)
XTextProperty prop;
DEFAULT(p, opt_title);
- if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
+ if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
&prop) != Success)
return;
XSetWMName(xw.dpy, xw.win, &prop);
@@ -1652,7 +1761,7 @@ xdrawline(Line line, int x1, int y1, int x2)
numspecs_cached = xmakeglyphfontspecs(xw.specbuf, &line[x1], x2 - x1, x1, y1);
/* Draw line in 2 passes: background and foreground. This way wide glyphs
- won't get truncated (#223) */
+ won't get truncated (#223) */
for (int dmode = DRAW_BG; dmode <= DRAW_FG; dmode <<= 1) {
specs = xw.specbuf;
numspecs = numspecs_cached;
@@ -1693,13 +1802,11 @@ xfinishdraw(void)
void
xximspot(int x, int y)
{
- if (xw.ime.xic == NULL)
- return;
-
- xw.ime.spot.x = borderpx + x * win.cw;
- xw.ime.spot.y = borderpx + (y + 1) * win.ch;
+ XPoint spot = { borderpx + x * win.cw, borderpx + (y + 1) * win.ch };
+ XVaNestedList attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
- XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL);
+ XSetICValues(xw.xic, XNPreeditAttributes, attr, NULL);
+ XFree(attr);
}
void
@@ -1741,9 +1848,13 @@ xsetmode(int set, unsigned int flags)
int
xsetcursor(int cursor)
{
- if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */
+ DEFAULT(cursor, 1);
+ if (!BETWEEN(cursor, 0, 8)) /* 7-8: st extensions */
return 1;
win.cursor = cursor;
+ cursorblinks = win.cursor == 0 || win.cursor == 1 ||
+ win.cursor == 3 || win.cursor == 5 ||
+ win.cursor == 7;
return 0;
}
@@ -1775,15 +1886,13 @@ focus(XEvent *ev)
return;
if (ev->type == FocusIn) {
- if (xw.ime.xic)
- XSetICFocus(xw.ime.xic);
+ XSetICFocus(xw.xic);
win.mode |= MODE_FOCUSED;
xseturgency(0);
if (IS_SET(MODE_FOCUS))
ttywrite("\033[I", 3, 0);
} else {
- if (xw.ime.xic)
- XUnsetICFocus(xw.ime.xic);
+ XUnsetICFocus(xw.xic);
win.mode &= ~MODE_FOCUSED;
if (IS_SET(MODE_FOCUS))
ttywrite("\033[O", 3, 0);
@@ -1793,7 +1902,7 @@ focus(XEvent *ev)
int
match(uint mask, uint state)
{
- return mask == XK_ANY_MOD || mask == (state & ~(Mod2Mask | XK_SWITCH_MOD));
+ return mask == XK_ANY_MOD || mask == (state & ~ignoremod);
}
char*
@@ -1838,7 +1947,7 @@ kpress(XEvent *ev)
{
XKeyEvent *e = &ev->xkey;
KeySym ksym;
- char buf[64], *customkey;
+ char buf[32], *customkey;
int len;
Rune c;
Status status;
@@ -1847,10 +1956,7 @@ kpress(XEvent *ev)
if (IS_SET(MODE_KBDLOCK))
return;
- if (xw.ime.xic)
- len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
- else
- len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
+ len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status);
/* 1. shortcuts */
for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
if (ksym == bp->keysym && match(bp->mod, e->state)) {
@@ -1978,7 +2084,7 @@ run(void)
* triggers drawing, we first wait a bit to ensure we got
* everything, and if nothing new arrives - we draw.
* We start with trying to wait minlatency ms. If more content
- * arrives sooner, we retry with shorter and shorter periods,
+ * arrives sooner, we retry with shorter and shorter preiods,
* and eventually draw even without idle after maxlatency ms.
* Typically this results in low latency while interacting,
* maximum latency intervals during `cat huge.txt`, and perfect
@@ -1987,6 +2093,10 @@ run(void)
if (FD_ISSET(ttyfd, &rfd) || xev) {
if (!drawing) {
trigger = now;
+ if (IS_SET(MODE_BLINK)) {
+ win.mode ^= MODE_BLINK;
+ }
+ lastblink = now;
drawing = 1;
}
timeout = (maxlatency - TIMEDIFF(now, trigger)) \
@@ -1997,7 +2107,7 @@ run(void)
/* idle detected or maxlatency exhausted -> draw */
timeout = -1;
- if (blinktimeout && tattrset(ATTR_BLINK)) {
+ if (blinktimeout && (cursorblinks || tattrset(ATTR_BLINK))) {
timeout = blinktimeout - TIMEDIFF(now, lastblink);
if (timeout <= 0) {
if (-timeout > blinktimeout) /* start visible */
@@ -2015,6 +2125,183 @@ run(void)
}
}
+#define XRESOURCE_LOAD_META(NAME) \
+ if(!XrmGetResource(xrdb, "st." NAME, "st." NAME, &type, &ret)) \
+ XrmGetResource(xrdb, "*." NAME, "*." NAME, &type, &ret); \
+ if (ret.addr != NULL && !strncmp("String", type, 64))
+
+#define XRESOURCE_LOAD_STRING(NAME, DST) \
+ XRESOURCE_LOAD_META(NAME) \
+ DST = ret.addr;
+
+#define XRESOURCE_LOAD_CHAR(NAME, DST) \
+ XRESOURCE_LOAD_META(NAME) \
+ DST = ret.addr[0];
+
+#define XRESOURCE_LOAD_INTEGER(NAME, DST) \
+ XRESOURCE_LOAD_META(NAME) \
+ DST = strtoul(ret.addr, NULL, 10);
+
+#define XRESOURCE_LOAD_FLOAT(NAME, DST) \
+ XRESOURCE_LOAD_META(NAME) \
+ DST = strtof(ret.addr, NULL);
+
+void
+xrdb_load(void)
+{
+ /* XXX */
+ char *xrm;
+ char *type;
+ XrmDatabase xrdb;
+ XrmValue ret;
+ Display *dpy;
+
+ if(!(dpy = XOpenDisplay(NULL)))
+ die("Can't open display\n");
+
+ XrmInitialize();
+ xrm = XResourceManagerString(dpy);
+
+ if (xrm != NULL) {
+ xrdb = XrmGetStringDatabase(xrm);
+
+ /* handling colors here without macros to do via loop. */
+ int i = 0;
+ char loadValue[12] = "";
+ for (i = 0; i < 256; i++)
+ {
+ sprintf(loadValue, "%s%d", "st.color", i);
+
+ if(!XrmGetResource(xrdb, loadValue, loadValue, &type, &ret))
+ {
+ sprintf(loadValue, "%s%d", "*.color", i);
+ if (!XrmGetResource(xrdb, loadValue, loadValue, &type, &ret))
+ /* reset if not found (unless in range for defaults). */
+ if (i > 15)
+ colorname[i] = NULL;
+ }
+
+ if (ret.addr != NULL && !strncmp("String", type, 64))
+ colorname[i] = ret.addr;
+ }
+
+ XRESOURCE_LOAD_STRING("foreground", colorname[defaultfg]);
+ XRESOURCE_LOAD_STRING("background", colorname[defaultbg]);
+ XRESOURCE_LOAD_STRING("cursorfg", colorname[defaultcs])
+ else {
+ // this looks confusing because we are chaining off of the if
+ // in the macro. probably we should be wrapping everything blocks
+ // so this isn't possible...
+ defaultcs = defaultfg;
+ }
+ XRESOURCE_LOAD_STRING("reverse-cursor", colorname[defaultrcs])
+ else {
+ // see above.
+ defaultrcs = defaultbg;
+ }
+
+ XRESOURCE_LOAD_STRING("font", font);
+ XRESOURCE_LOAD_STRING("termname", termname);
+
+ /* XRESOURCE_LOAD_INTEGER("xfps", xfps); */
+ /* XRESOURCE_LOAD_INTEGER("actionfps", actionfps); */
+ XRESOURCE_LOAD_INTEGER("blinktimeout", blinktimeout);
+ XRESOURCE_LOAD_INTEGER("bellvolume", bellvolume);
+ XRESOURCE_LOAD_INTEGER("borderpx", borderpx);
+ /* XRESOURCE_LOAD_INTEGER("borderless", borderless); */
+ XRESOURCE_LOAD_INTEGER("cursorshape", cursorshape);
+
+ /* cursorblinkstate = 1; // in case if cursor shape was changed from a blinking one to a non-blinking */
+ /* XRESOURCE_LOAD_INTEGER("cursorthickness", cursorthickness); */
+ /* XRESOURCE_LOAD_INTEGER("cursorblinkstyle", cursorblinkstyle); */
+ /* XRESOURCE_LOAD_INTEGER("cursorblinkontype", cursorblinkontype); */
+
+ /* todo: https://github.com/gnotclub/xst/commit/1e82647b0e04077e975679a4b4cf1eb02b04e6bc */
+ /* XRESOURCE_LOAD_INTEGER("mouseScrollLines", mousescrolllines); */
+
+ XRESOURCE_LOAD_FLOAT("cwscale", cwscale);
+ XRESOURCE_LOAD_FLOAT("chscale", chscale);
+
+ /* XRESOURCE_LOAD_CHAR("prompt_char", prompt_char); */
+
+ }
+ XFlush(dpy);
+}
+
+void
+reload(int sig)
+{
+ xrdb_load();
+
+ /* colors, fonts */
+ xloadcols();
+ xunloadfonts();
+ xloadfonts(font, 0);
+
+ /* pretend the window just got resized */
+ cresize(win.w, win.h);
+
+ redraw();
+
+ /* triggers re-render if we're visible. */
+ ttywrite("\033[O", 3, 1);
+
+ signal(SIGUSR1, reload);
+}
+
+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)
{
@@ -2039,6 +2326,9 @@ main(int argc, char *argv[])
case 'a':
allowaltscreen = 0;
break;
+ case 'A':
+ opt_alpha = EARGF(usage());
+ break;
case 'c':
opt_class = EARGF(usage());
break;
@@ -2088,8 +2378,16 @@ run:
setlocale(LC_CTYPE, "");
XSetLocaleModifiers("");
+ xrdb_load();
+ signal(SIGUSR1, reload);
+
+ if(!(xw.dpy = XOpenDisplay(NULL)))
+ die("Can't open display\n");
+
+ config_init();
cols = MAX(cols, 1);
rows = MAX(rows, 1);
+ signal(SIGUSR1, reload);
tnew(cols, rows);
xinit(cols, rows);
xsetenv();
diff --git a/xresources b/xresources
new file mode 100644
index 0000000..534451c
--- /dev/null
+++ b/xresources
@@ -0,0 +1,49 @@
+Xft.antialias: 1
+Xft.hinting: 1
+Xft.autohint: 0
+Xft.hintstyle: hintslight
+Xft.rgba: rgb
+Xft.lcdfilter: lcddefault
+
+st.font: JetBrainsMono Nerd Font:style:medium:pixelsize=13
+
+! window padding
+st.borderpx: 20
+
+!- 0.1 to 0.9 = transparency
+st.alpha: 1.0
+
+*background: #181f21
+*foreground: #dadada
+
+! Black + DarkGrey
+*color0: #22292b
+*color8: #575e60
+
+! DarkRed + Red
+*color1: #e06e6e
+*color9: #ef7d7d
+
+! DarkGreen + Green
+*color2: #8ccf7e
+*color10: #9bdead
+
+! DarkYellow + Yellow
+*color3: #e5c76b
+*color11: #f4d67a
+
+! DarkBlue + Blue
+*color4: #67b0e8
+*color12: #6cb5ed
+
+! DarkMagenta + Magenta
+*color5: #c47fd5
+*color13: #ce89df
+
+! DarkCyan + Cyan
+*color6: #6da4cd
+*color14: #67cbe7
+
+! LightGrey + White
+*color7: #b3b9b8
+*color15: #bdc3c2