From c0186e78c1c0a475acbdb95b450c1ca60bc77ed6 Mon Sep 17 00:00:00 2001 From: Shi Tian Date: Sun, 25 Jun 2023 05:38:33 +0000 Subject: [PATCH 01/16] Fix for wide character being incorrectly cleared on MODE_INSERT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under insert mode, when inserting a normal character in front of a wide character, the affected region is shifted to the right by one cell. However, the empty cell is reset as if being a part of a wide character, causing the following cell being mishandled as a dummy cell. To reproduce the bug: printf '\033[4h' # set MODE_INSERT printf 妳好 printf '\033[4D' printf 'x' printf '\033[4l\n' --- st.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/st.c b/st.c index 96d0d48..0860488 100644 --- a/st.c +++ b/st.c @@ -2654,8 +2654,10 @@ tputc(Rune u) gp = &term.line[term.c.y][term.c.x]; } - if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) { memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); + gp->mode &= ~ATTR_WIDE; + } if (term.c.x+width > term.col) { tnewline(1); From f6b97e6f4311e41898bd8382826563d40d3a1124 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 22 Sep 2023 15:16:52 +0200 Subject: [PATCH 02/16] Makefile: remove the options target The Makefile used to suppress output (by using @), so this target made sense at the time. But the Makefile should be simple and make debugging with less abstractions or fancy printing. The Makefile was made verbose and doesn't hide the build output, so remove this target. Prompted by a question on the mailing list about the options target. --- Makefile | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 75566f0..b557c7c 100644 --- a/Makefile +++ b/Makefile @@ -7,13 +7,7 @@ include config.mk SRC = st.c x.c boxdraw.c OBJ = $(SRC:.c=.o) -all: options st - -options: - @echo st build options: - @echo "CFLAGS = $(STCFLAGS)" - @echo "LDFLAGS = $(STLDFLAGS)" - @echo "CC = $(CC)" +all: st config.h: cp config.def.h config.h @@ -62,4 +56,4 @@ uninstall: rm -f $(DESTDIR)$(PREFIX)/share/terminfo/x/xst-meta-256color rm -f $(DESTDIR)$(PREFIX)/share/terminfo/x/xst-mono -.PHONY: all options clean dist install uninstall +.PHONY: all clean dist install uninstall From 371b9fdc406d15ff113e0a7692bd0fd6afdcc183 Mon Sep 17 00:00:00 2001 From: actionless Date: Mon, 18 Nov 2024 13:57:25 +0100 Subject: [PATCH 03/16] chore(makefile): fix some leftovers of unrenamed st->xst --- Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index b557c7c..40dc8d5 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# st - simple terminal +# xst - xresources simple terminal # See LICENSE file for copyright and license details. .POSIX: @@ -7,7 +7,7 @@ include config.mk SRC = st.c x.c boxdraw.c OBJ = $(SRC:.c=.o) -all: st +all: xst config.h: cp config.def.h config.h @@ -21,21 +21,21 @@ boxdraw.o: config.h st.h boxdraw_data.h $(OBJ): config.h config.mk -st: $(OBJ) +xst: $(OBJ) $(CC) -o xst $(OBJ) $(STLDFLAGS) clean: - rm -f xst config.h $(OBJ) st-$(VERSION).tar.gz + rm -f xst config.h $(OBJ) xst-$(VERSION).tar.gz dist: clean - mkdir -p st-$(VERSION) + mkdir -p xst-$(VERSION) cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ config.def.h st.info st.1 arg.h st.h win.h $(SRC)\ - st-$(VERSION) - tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz - rm -rf st-$(VERSION) + xst-$(VERSION) + tar -cf - xst-$(VERSION) | gzip > xst-$(VERSION).tar.gz + rm -rf xst-$(VERSION) -install: st +install: xst mkdir -p $(DESTDIR)$(PREFIX)/bin cp -f xst $(DESTDIR)$(PREFIX)/bin chmod 755 $(DESTDIR)$(PREFIX)/bin/xst From b466692f22a12258e4ab1a10bd2c560c72f9c529 Mon Sep 17 00:00:00 2001 From: Peter Hofmann Date: Sat, 7 Oct 2023 07:39:00 +0200 Subject: [PATCH 04/16] Fix bounds checks of dc.col dc.collen is the length of dc.col, not the maximum index, hence if x is equal to dc.collen, then it's an error. With config.def.h, the last valid index is 259, so this correctly reports "black": $ printf '\033]4;259;?\e\\' 260 is an invalid index and this reports garbage instead of printing an error: $ printf '\033]4;260;?\e\\' --- x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x.c b/x.c index 29dafe9..e50e719 100644 --- a/x.c +++ b/x.c @@ -888,7 +888,7 @@ xloadcols(void) int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) { - if (!BETWEEN(x, 0, dc.collen)) + if (!BETWEEN(x, 0, dc.collen - 1)) return 1; *r = dc.col[x].color.red >> 8; @@ -903,7 +903,7 @@ xsetcolorname(int x, const char *name) { Color ncolor; - if (!BETWEEN(x, 0, dc.collen)) + if (!BETWEEN(x, 0, dc.collen - 1)) return 1; if (!xloadcolor(x, name, &ncolor)) From 4fcb5c7cc5b22fdf16109f98059b33fdeefa6daa Mon Sep 17 00:00:00 2001 From: Peter Hofmann Date: Sat, 7 Oct 2023 07:40:07 +0200 Subject: [PATCH 05/16] Don't scroll selection on the other screen Fixes garbage selections when switching to/from the alternate screen. How to reproduce: - Be in primary screen. - Select something. - Run this (switches to alternate screen, positions the cursor at the bottom, triggers selscroll(), and then goes back to primary screen): tput smcup; tput cup $(tput lines) 0; echo foo; tput rmcup - Notice how the (visual) selection now covers a different line. The reason is that selscroll() calls selnormalize() and that cannot find the original range anymore. It's all empty lines now, so it snaps to "select the whole line". --- st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.c b/st.c index 0860488..ff58212 100644 --- a/st.c +++ b/st.c @@ -1178,7 +1178,7 @@ tscrollup(int orig, int n, int copyhist) void selscroll(int orig, int n) { - if (sel.ob.x == -1) + if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN)) return; if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { From dbb0c881a9ff4513621a0c643d0179ccb316abdd Mon Sep 17 00:00:00 2001 From: Peter Hofmann Date: Sat, 7 Oct 2023 07:40:39 +0200 Subject: [PATCH 06/16] Fix wide glyphs breaking "nowrap" mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consider the following example: printf '\e[?7l';\ for i in $(seq $(($(tput cols) - 1))); do printf a; done;\ printf '🙈\n';\ printf '\e[?7h' Even though MODE_WRAP has been disabled, the emoji appeared on the next line. This patch keeps wide glyphs on the same line and moves them to the right-most possible position. --- st.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/st.c b/st.c index ff58212..f177665 100644 --- a/st.c +++ b/st.c @@ -2660,7 +2660,10 @@ tputc(Rune u) } if (term.c.x+width > term.col) { - tnewline(1); + if (IS_SET(MODE_WRAP)) + tnewline(1); + else + tmoveto(term.col - width, term.c.y); gp = &term.line[term.c.y][term.c.x]; } From fae3009862a410634ff8061edadf5c9a2902cdf5 Mon Sep 17 00:00:00 2001 From: Peter Hofmann Date: Sat, 7 Oct 2023 07:40:45 +0200 Subject: [PATCH 07/16] Unhide cursor on RIS (\033c) It is unclear if it's "required" to do this on RIS, but it's useful when calling reset(1) after interactive programs have crashed and garbled up the screen. FWIW, other terminals do it as well (tested with XTerm, VTE, Kitty, Alacritty, Linux VT). --- st.c | 1 + 1 file changed, 1 insertion(+) diff --git a/st.c b/st.c index f177665..00c5f84 100644 --- a/st.c +++ b/st.c @@ -2513,6 +2513,7 @@ eschandle(uchar ascii) treset(); resettitle(); xloadcols(); + xsetmode(0, MODE_HIDE); break; case '=': /* DECPAM -- Application keypad */ xsetmode(1, MODE_APPKEYPAD); From 88b535df68a42b4a1666480e271ab7535c198338 Mon Sep 17 00:00:00 2001 From: Peter Hofmann Date: Sat, 7 Oct 2023 07:41:02 +0200 Subject: [PATCH 08/16] Add terminfo entries for bracketed paste mode Helps Vim (and hopefully others) to discover that this feature exists without further user configuration. --- st.info | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/st.info b/st.info index fe0d4f5..eeb9bbb 100644 --- a/st.info +++ b/st.info @@ -185,6 +185,10 @@ xst-mono| simpleterm monocolor, # XTerm extensions rmxx=\E[29m, smxx=\E[9m, + BE=\E[?2004h, + BD=\E[?2004l, + PS=\E[200~, + PE=\E[201~, # disabled rep for now: causes some issues with older ncurses versions. # rep=%p1%c\E[%p2%{1}%-%db, # tmux extensions, see TERMINFO EXTENSIONS in tmux(1) From 4ea3a6ed8cb9ecf0febbc15c98dfde38ba14fe19 Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Sun, 18 Feb 2024 06:56:49 -0600 Subject: [PATCH 09/16] csi: check for private marker in 'S' case The handler for 'S' final character does not check for a private marker. This can cause a conflict with a sequence called 'XTSMGRAPHICS' which also has an 'S' final character, but uses the private marker '?'. Without checking for a private marker, st will perform a scroll up operation when XTSMGRAPHICS is seen, which can cause unexpected display artifacts. --- st.c | 1 + 1 file changed, 1 insertion(+) diff --git a/st.c b/st.c index 00c5f84..08b7c22 100644 --- a/st.c +++ b/st.c @@ -1858,6 +1858,7 @@ csihandle(void) } break; case 'S': /* SU -- Scroll line up */ + if (csiescseq.priv) break; DEFAULT(csiescseq.arg[0], 1); tscrollup(term.top, csiescseq.arg[0], 0); break; From 3a61f5495651613e4fb1f1251613e35dc1e211e4 Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Sun, 25 Feb 2024 01:31:31 +0100 Subject: [PATCH 10/16] Fix cursor move with wide glyphs st would always move back 1 column, even with wide glyhps (using more than a single column). The glyph rune is set on its first column, and the other ones are to 0, so loop until we detect the start of the previous glyph. --- st.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/st.c b/st.c index 08b7c22..a897183 100644 --- a/st.c +++ b/st.c @@ -91,8 +91,8 @@ enum escape_state { typedef struct { Glyph attr; /* current char attributes */ - int x; - int y; + int x; /* terminal column */ + int y; /* terminal row */ char state; } TCursor; @@ -2358,12 +2358,16 @@ tstrsequence(uchar c) void tcontrolcode(uchar ascii) { + size_t i; + switch (ascii) { case '\t': /* HT */ tputtab(1); return; case '\b': /* BS */ - tmoveto(term.c.x-1, term.c.y); + for (i = 1; term.c.x && term.line[term.c.y][term.c.x - i].u == 0; ++i) + ; + tmoveto(term.c.x - i, term.c.y); return; case '\r': /* CR */ tmoveto(0, term.c.y); From 1bbb7e7f8c8cb8690205e7d663c31fc58c9c4135 Mon Sep 17 00:00:00 2001 From: Tommi Hirvola Date: Mon, 4 Mar 2024 12:56:30 +0200 Subject: [PATCH 11/16] set upper limit for REP escape sequence argument Previously, printf 'L\033[2147483647b' would call tputc('L') 2^31 times, making st unresponsive. This commit allows repeating the last character at most 65535 times in order to prevent freezing and DoS attacks. --- st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.c b/st.c index a897183..0523a14 100644 --- a/st.c +++ b/st.c @@ -1773,7 +1773,7 @@ csihandle(void) ttywrite(vtiden, strlen(vtiden), 0); break; case 'b': /* REP -- if last char is printable print it more times */ - DEFAULT(csiescseq.arg[0], 1); + LIMIT(csiescseq.arg[0], 1, 65535); if (term.lastc) while (csiescseq.arg[0]-- > 0) tputc(term.lastc); From 1a8cdf13a8a56c5074ce4003b501cc7ca430a85b Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sun, 17 Mar 2024 14:42:44 +0100 Subject: [PATCH 12/16] config.def.h: improve latency for the default configuration --- config.def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 76c38a1..5564498 100644 --- a/config.def.h +++ b/config.def.h @@ -83,7 +83,7 @@ int allowwindowops = 0; * near minlatency, but it waits longer for slow updates to avoid partial draw. * low minlatency will tear/flicker more, as it can "detect" idle too early. */ -static double minlatency = 8; +static double minlatency = 2; static double maxlatency = 33; /* From 5e548fa305fddc2b7cd56b9d0d377f8306c4328a Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 30 Mar 2024 12:30:49 +0100 Subject: [PATCH 13/16] Revert "Fix cursor move with wide glyphs" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 7473a8d1a57e5f9aba41b953f4e498c35e1c9dc5. This patch needs some more work. It caused regressions with programs that use GNU readline, etc. Original test-case example from Tim Culverhouse : printf " 😀" && sleep 2 && printf "\e[D" && sleep 2 && printf "\e[D" && sleep 2 After the patch it caused regressions, example test-case: printf "A字\bB\n" --- st.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/st.c b/st.c index 0523a14..1e1192b 100644 --- a/st.c +++ b/st.c @@ -91,8 +91,8 @@ enum escape_state { typedef struct { Glyph attr; /* current char attributes */ - int x; /* terminal column */ - int y; /* terminal row */ + int x; + int y; char state; } TCursor; @@ -2358,16 +2358,12 @@ tstrsequence(uchar c) void tcontrolcode(uchar ascii) { - size_t i; - switch (ascii) { case '\t': /* HT */ tputtab(1); return; case '\b': /* BS */ - for (i = 1; term.c.x && term.line[term.c.y][term.c.x - i].u == 0; ++i) - ; - tmoveto(term.c.x - i, term.c.y); + tmoveto(term.c.x-1, term.c.y); return; case '\r': /* CR */ tmoveto(0, term.c.y); From 9f0ebe95b8640721245774ccc5271bda5ef7f4db Mon Sep 17 00:00:00 2001 From: DOGMAN Date: Wed, 3 Apr 2024 19:48:11 +0200 Subject: [PATCH 14/16] Reset title when an empty title string is given With this patch, st will reset its window title when an empty string is given as the terminal title. For example: printf "\033]0;\007" Some applications, like termdown, expect this functionality. xterm implements it, but it seems that most other terminal emulators don't. In any case, I don't see why there should ever be a case where the st window doesn't have a title property. --- x.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/x.c b/x.c index e50e719..231e0bc 100644 --- a/x.c +++ b/x.c @@ -2298,6 +2298,9 @@ xseticontitle(char *p) XTextProperty prop; DEFAULT(p, opt_title); + if (p[0] == '\0') + p = opt_title; + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop) != Success) return; @@ -2312,6 +2315,9 @@ xsettitle(char *p) XTextProperty prop; DEFAULT(p, opt_title); + if (p[0] == '\0') + p = opt_title; + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop) != Success) return; From fd406dc7f46ce4fd54bd46bf682989ca802ffc92 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Wed, 1 May 2024 20:45:39 +0200 Subject: [PATCH 15/16] support colons in SGR character attributes Patch by Mikhail Kot With some modifications to behave more like xterm (see note below). Example: printf '\033[48;2;255:0:0mtest\n' https://invisible-island.net/xterm/ctlseqs/ctlseqs.html Some notes: "CSI Pm m Character Attributes (SGR). [...] o xterm allows either colons (standard) or semicolons (legacy) to separate the subparameters (but after the first colon, colons must be used). ____ Adapted for `xst` by Actionless/Loveless --- st.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/st.c b/st.c index 1e1192b..a2d02ed 100644 --- a/st.c +++ b/st.c @@ -1235,6 +1235,7 @@ csiparse(void) { char *p = csiescseq.buf, *np; long int v; + int sep = ';'; /* colon or semi-colon, but not both */ csiescseq.narg = 0; if (*p == '?') { @@ -1253,7 +1254,9 @@ csiparse(void) csiescseq.arg[csiescseq.narg++] = v; p = np; readcolonargs(&p, csiescseq.narg-1, csiescseq.carg); - if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + if (sep == ';' && *p == ':') + sep = ':'; /* allow override to colon once */ + if (*p != sep || csiescseq.narg == ESC_ARG_SIZ) break; p++; } From 6ecf7e82343c10a26f2fc33811f2fad4d15fdffc Mon Sep 17 00:00:00 2001 From: Lucas de Sena Date: Fri, 9 Aug 2024 13:33:47 +0200 Subject: [PATCH 16/16] fix BadMatch error when embedding on some windows When embedded, st fails with BadMatch error if the embedder's window has non-default colormap/depth/visual. This commit fixes that by creating st's window inside root and then reparent it into embedder. The reference window for dc.gc is also changed to match root's visuals. A similar commit had been made for dmenu[1]. See this issue[2] on github for context. [1]: https://git.suckless.org/dmenu/commit/0fe460dbd469a1d5b6a7140d0e1801935e4a923b.html [2]: https://github.com/phillbush/xfiles/issues/47 ___ Adapted for `xst` by Actionless/Loveless --- x.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/x.c b/x.c index 231e0bc..46eef22 100644 --- a/x.c +++ b/x.c @@ -1322,7 +1322,7 @@ xinit(int cols, int rows) { XGCValues gcvalues; Cursor cursor; - Window parent; + Window parent, root; pid_t thispid = getpid(); XColor xmousefg, xmousebg; XWindowAttributes attr; @@ -1378,15 +1378,21 @@ xinit(int cols, int rows) | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; xw.attrs.colormap = xw.cmap; - xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, + root = XRootWindow(xw.dpy, xw.scr); + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) + parent = root; + xw.win = XCreateWindow(xw.dpy, root, xw.l, xw.t, win.w, win.h, 0, xw.depth, InputOutput, xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity | CWEventMask | CWColormap, &xw.attrs); + if (parent != root) + XReparentWindow(xw.dpy, xw.win, parent, xw.l, xw.t); memset(&gcvalues, 0, sizeof(gcvalues)); gcvalues.graphics_exposures = False; + dc.gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, + &gcvalues); 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);