summaryrefslogtreecommitdiff
path: root/based-simple-term/patches
diff options
context:
space:
mode:
Diffstat (limited to 'based-simple-term/patches')
-rwxr-xr-xbased-simple-term/patches/st-alpha-20220206-0.8.5.diff146
-rwxr-xr-xbased-simple-term/patches/st-appsync-20200618-b27a383.diff259
-rwxr-xr-xbased-simple-term/patches/st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff601
-rwxr-xr-xbased-simple-term/patches/st-background-image-0.8.4.diff262
-rwxr-xr-xbased-simple-term/patches/st-delkey-20201112-4ef0cbd.diff20
-rwxr-xr-xbased-simple-term/patches/st-dynamic-cursor-color-0.8.4.diff52
-rwxr-xr-xbased-simple-term/patches/st-scrollback-20210507-4536f46.diff351
-rwxr-xr-xbased-simple-term/patches/st-scrollback-mouse-20191024-a2c479c.diff13
-rwxr-xr-xbased-simple-term/patches/st-scrollback-mouse-altscreen-20220127-2c5edf2.diff78
-rwxr-xr-xbased-simple-term/patches/st-undercurl-0.8.4-20210822.diff605
-rwxr-xr-xbased-simple-term/patches/st-undercurl-0.8.4-20210822.diff_605
-rwxr-xr-xbased-simple-term/patches/st-w3m-0.8.3.diff42
-rwxr-xr-xbased-simple-term/patches/st-w3m-0.8.3.diff_42
13 files changed, 3076 insertions, 0 deletions
diff --git a/based-simple-term/patches/st-alpha-20220206-0.8.5.diff b/based-simple-term/patches/st-alpha-20220206-0.8.5.diff
new file mode 100755
index 0000000..ab029f6
--- /dev/null
+++ b/based-simple-term/patches/st-alpha-20220206-0.8.5.diff
@@ -0,0 +1,146 @@
+diff --git a/config.def.h b/config.def.h
+index 91ab8ca..6af616e 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -93,6 +93,9 @@ char *termname = "st-256color";
+ */
+ unsigned int tabspaces = 8;
+
++/* bg opacity */
++float alpha = 0.8;
++
+ /* Terminal colors (16 first used in escape sequence) */
+ static const char *colorname[] = {
+ /* 8 normal colors */
+diff --git a/config.mk b/config.mk
+index 4c4c5d5..0114bad 100644
+--- a/config.mk
++++ b/config.mk
+@@ -16,7 +16,7 @@ PKG_CONFIG = pkg-config
+ INCS = -I$(X11INC) \
+ `$(PKG_CONFIG) --cflags fontconfig` \
+ `$(PKG_CONFIG) --cflags freetype2`
+-LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
++LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
+ `$(PKG_CONFIG) --libs fontconfig` \
+ `$(PKG_CONFIG) --libs freetype2`
+
+diff --git a/st.h b/st.h
+index 519b9bd..8bb533d 100644
+--- a/st.h
++++ b/st.h
+@@ -126,3 +126,4 @@ extern unsigned int tabspaces;
+ extern unsigned int defaultfg;
+ extern unsigned int defaultbg;
+ extern unsigned int defaultcs;
++extern float alpha;
+diff --git a/x.c b/x.c
+index 8a16faa..ddf4178 100644
+--- a/x.c
++++ b/x.c
+@@ -105,6 +105,7 @@ typedef struct {
+ 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;
+@@ -243,6 +244,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;
+@@ -736,7 +738,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);
+
+@@ -796,6 +798,13 @@ xloadcols(void)
+ else
+ die("could not allocate color %d\n", i);
+ }
++
++ /* 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].pixel &= 0x00FFFFFF;
++ dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24;
+ loaded = 1;
+ }
+
+@@ -1118,11 +1127,23 @@ 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())
+@@ -1132,7 +1153,7 @@ xinit(int cols, int rows)
+ xloadfonts(usedfont, 0);
+
+ /* colors */
+- xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
++ xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None);
+ xloadcols();
+
+ /* adjust fixed window geometry */
+@@ -1152,19 +1173,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);
+
+@@ -2019,6 +2036,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;
diff --git a/based-simple-term/patches/st-appsync-20200618-b27a383.diff b/based-simple-term/patches/st-appsync-20200618-b27a383.diff
new file mode 100755
index 0000000..4736325
--- /dev/null
+++ b/based-simple-term/patches/st-appsync-20200618-b27a383.diff
@@ -0,0 +1,259 @@
+From 8c9c920325fa10440a96736ba58ec647a0365e22 Mon Sep 17 00:00:00 2001
+From: "Avi Halachmi (:avih)" <avihpit@yahoo.com>
+Date: Sat, 18 Apr 2020 13:56:11 +0300
+Subject: [PATCH] application-sync: support Synchronized-Updates
+
+See https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec
+
+In a nutshell: allow an application to suspend drawing until it has
+completed some output - so that the terminal will not flicker/tear by
+rendering partial content. If the end-of-suspension sequence doesn't
+arrive, the terminal bails out after a timeout (default: 200 ms).
+
+The feature is supported and pioneered by iTerm2. There are probably
+very few other terminals or applications which support this feature
+currently.
+
+One notable application which does support it is tmux (master as of
+2020-04-18) - where cursor flicker is completely avoided when a pane
+has new content. E.g. run in one pane: `while :; do cat x.c; done'
+while the cursor is at another pane.
+
+The terminfo string `Sync' added to `st.info' is also a tmux extension
+which tmux detects automatically when `st.info` is installed.
+
+Notes:
+
+- Draw-suspension begins on BSU sequence (Begin-Synchronized-Update),
+ and ends on ESU sequence (End-Synchronized-Update).
+
+- BSU, ESU are "\033P=1s\033\\", "\033P=2s\033\\" respectively (DCS).
+
+- SU doesn't support nesting - BSU begins or extends, ESU always ends.
+
+- ESU without BSU is ignored.
+
+- BSU after BSU extends (resets the timeout), so an application could
+ send BSU in a loop and keep drawing suspended - exactly like it can
+ not-draw anything in a loop. But as soon as it exits/aborted then
+ drawing is resumed according to the timeout even without ESU.
+
+- This implementation focuses on ESU and doesn't really care about BSU
+ in the sense that it tries hard to draw exactly once ESU arrives (if
+ it's not too soon after the last draw - according to minlatency),
+ and doesn't try to draw the content just up-to BSU. These two sides
+ complement eachother - not-drawing on BSU increases the chance that
+ ESU is not too soon after the last draw. This approach was chosen
+ because the application's main focus is that ESU indicates to the
+ terminal that the content is now ready - and that's when we try to
+ draw.
+---
+ config.def.h | 6 ++++++
+ st.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++--
+ st.info | 1 +
+ x.c | 22 +++++++++++++++++++---
+ 4 files changed, 72 insertions(+), 5 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 6f05dce..80d768e 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -56,6 +56,12 @@ int allowwindowops = 0;
+ 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.
+diff --git a/st.c b/st.c
+index 76b7e0d..0582e77 100644
+--- a/st.c
++++ b/st.c
+@@ -231,6 +231,33 @@ static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
+ static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
+ static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+
++#include <time.h>
++static int su = 0;
++struct timespec sutv;
++
++static void
++tsync_begin()
++{
++ clock_gettime(CLOCK_MONOTONIC, &sutv);
++ su = 1;
++}
++
++static void
++tsync_end()
++{
++ su = 0;
++}
++
++int
++tinsync(uint timeout)
++{
++ struct timespec now;
++ if (su && !clock_gettime(CLOCK_MONOTONIC, &now)
++ && TIMEDIFF(now, sutv) >= timeout)
++ su = 0;
++ return su;
++}
++
+ ssize_t
+ xwrite(int fd, const char *s, size_t len)
+ {
+@@ -818,6 +845,9 @@ ttynew(char *line, char *cmd, char *out, char **args)
+ return cmdfd;
+ }
+
++static int twrite_aborted = 0;
++int ttyread_pending() { return twrite_aborted; }
++
+ size_t
+ ttyread(void)
+ {
+@@ -826,7 +856,7 @@ ttyread(void)
+ int ret, written;
+
+ /* append read bytes to unprocessed bytes */
+- ret = read(cmdfd, buf+buflen, LEN(buf)-buflen);
++ ret = twrite_aborted ? 1 : read(cmdfd, buf+buflen, LEN(buf)-buflen);
+
+ switch (ret) {
+ case 0:
+@@ -834,7 +864,7 @@ ttyread(void)
+ case -1:
+ die("couldn't read from shell: %s\n", strerror(errno));
+ default:
+- buflen += ret;
++ buflen += twrite_aborted ? 0 : ret;
+ written = twrite(buf, buflen, 0);
+ buflen -= written;
+ /* keep any incomplete UTF-8 byte sequence for the next call */
+@@ -994,6 +1024,7 @@ tsetdirtattr(int attr)
+ void
+ tfulldirt(void)
+ {
++ tsync_end();
+ tsetdirt(0, term.row-1);
+ }
+
+@@ -1895,6 +1926,12 @@ strhandle(void)
+ xsettitle(strescseq.args[0]);
+ return;
+ case 'P': /* DCS -- Device Control String */
++ /* https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec */
++ if (strstr(strescseq.buf, "=1s") == strescseq.buf)
++ tsync_begin(); /* BSU */
++ else if (strstr(strescseq.buf, "=2s") == strescseq.buf)
++ tsync_end(); /* ESU */
++ return;
+ case '_': /* APC -- Application Program Command */
+ case '^': /* PM -- Privacy Message */
+ return;
+@@ -2436,6 +2473,9 @@ twrite(const char *buf, int buflen, int show_ctrl)
+ Rune u;
+ int n;
+
++ int su0 = su;
++ twrite_aborted = 0;
++
+ for (n = 0; n < buflen; n += charsize) {
+ if (IS_SET(MODE_UTF8)) {
+ /* process a complete utf8 char */
+@@ -2446,6 +2486,10 @@ twrite(const char *buf, int buflen, int show_ctrl)
+ u = buf[n] & 0xFF;
+ charsize = 1;
+ }
++ if (su0 && !su) {
++ twrite_aborted = 1;
++ break; // ESU - allow rendering before a new BSU
++ }
+ if (show_ctrl && ISCONTROL(u)) {
+ if (u & 0x80) {
+ u &= 0x7f;
+diff --git a/st.info b/st.info
+index 8201ad6..b32b446 100644
+--- a/st.info
++++ b/st.info
+@@ -191,6 +191,7 @@ st-mono| simpleterm monocolor,
+ Ms=\E]52;%p1%s;%p2%s\007,
+ Se=\E[2 q,
+ Ss=\E[%p1%d q,
++ Sync=\EP=%p1%ds\E\\,
+
+ st| simpleterm,
+ use=st-mono,
+diff --git a/x.c b/x.c
+index 210f184..27ff4e2 100644
+--- a/x.c
++++ b/x.c
+@@ -1861,6 +1861,9 @@ resize(XEvent *e)
+ cresize(e->xconfigure.width, e->xconfigure.height);
+ }
+
++int tinsync(uint);
++int ttyread_pending();
++
+ void
+ run(void)
+ {
+@@ -1895,7 +1898,7 @@ run(void)
+ FD_SET(ttyfd, &rfd);
+ FD_SET(xfd, &rfd);
+
+- if (XPending(xw.dpy))
++ if (XPending(xw.dpy) || ttyread_pending())
+ timeout = 0; /* existing events might not set xfd */
+
+ seltv.tv_sec = timeout / 1E3;
+@@ -1909,7 +1912,8 @@ run(void)
+ }
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+- if (FD_ISSET(ttyfd, &rfd))
++ int ttyin = FD_ISSET(ttyfd, &rfd) || ttyread_pending();
++ if (ttyin)
+ ttyread();
+
+ xev = 0;
+@@ -1933,7 +1937,7 @@ run(void)
+ * maximum latency intervals during `cat huge.txt`, and perfect
+ * sync with periodic updates from animations/key-repeats/etc.
+ */
+- if (FD_ISSET(ttyfd, &rfd) || xev) {
++ if (ttyin || xev) {
+ if (!drawing) {
+ trigger = now;
+ drawing = 1;
+@@ -1944,6 +1948,18 @@ run(void)
+ continue; /* we have time, try to find idle */
+ }
+
++ if (tinsync(su_timeout)) {
++ /*
++ * on synchronized-update draw-suspension: don't reset
++ * drawing so that we draw ASAP once we can (just after
++ * ESU). it won't be too soon because we already can
++ * draw now but we skip. we set timeout > 0 to draw on
++ * SU-timeout even without new content.
++ */
++ timeout = minlatency;
++ continue;
++ }
++
+ /* idle detected or maxlatency exhausted -> draw */
+ timeout = -1;
+ if (blinktimeout && tattrset(ATTR_BLINK)) {
+
+base-commit: b27a383a3acc7decf00e6e889fca265430b5d329
+--
+2.17.1
+
diff --git a/based-simple-term/patches/st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff b/based-simple-term/patches/st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff
new file mode 100755
index 0000000..c41506f
--- /dev/null
+++ b/based-simple-term/patches/st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff
@@ -0,0 +1,601 @@
+diff -uraN st-0.8.4/autocomplete.h st-autocomplete/autocomplete.h
+--- st-0.8.4/autocomplete.h 1970-01-01 04:00:00.000000000 +0400
++++ st-autocomplete/autocomplete.h 2021-12-14 20:20:03.322050025 +0400
+@@ -0,0 +1,16 @@
++# ifndef __ST_AUTOCOMPLETE_H
++# define __ST_AUTOCOMPLETE_H
++
++enum {
++ ACMPL_DEACTIVATE,
++ ACMPL_WORD,
++ ACMPL_WWORD,
++ ACMPL_FUZZY_WORD,
++ ACMPL_FUZZY_WWORD,
++ ACMPL_FUZZY,
++ ACMPL_SUFFIX,
++ ACMPL_SURROUND,
++ ACMPL_UNDO,
++};
++
++# endif // __ST_AUTOCOMPLETE_H
+diff -uraN st-0.8.4/config.def.h st-autocomplete/config.def.h
+--- st-0.8.4/config.def.h 2021-12-14 20:03:03.981322164 +0400
++++ st-autocomplete/config.def.h 2021-12-14 20:22:30.088821478 +0400
+@@ -168,6 +168,8 @@
+ */
+ static uint forcemousemod = ShiftMask;
+
++# include "autocomplete.h"
++
+ /*
+ * Internal mouse shortcuts.
+ * Beware that overloading Button1 will disable the selection.
+@@ -199,6 +201,14 @@
+ { TERMMOD, XK_Y, selpaste, {.i = 0} },
+ { ShiftMask, XK_Insert, selpaste, {.i = 0} },
+ { TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
++ { ControlMask|Mod1Mask, XK_slash, autocomplete, { .i = ACMPL_WORD } },
++ { ControlMask|Mod1Mask, XK_period, autocomplete, { .i = ACMPL_FUZZY_WORD } },
++ { ControlMask|Mod1Mask, XK_comma, autocomplete, { .i = ACMPL_FUZZY } },
++ { ControlMask|Mod1Mask, XK_apostrophe, autocomplete, { .i = ACMPL_SUFFIX } },
++ { ControlMask|Mod1Mask, XK_semicolon, autocomplete, { .i = ACMPL_SURROUND } },
++ { ControlMask|Mod1Mask, XK_bracketright,autocomplete, { .i = ACMPL_WWORD } },
++ { ControlMask|Mod1Mask, XK_bracketleft, autocomplete, { .i = ACMPL_FUZZY_WWORD } },
++ { ControlMask|Mod1Mask, XK_equal, autocomplete, { .i = ACMPL_UNDO } },
+ };
+
+ /*
+diff -uraN st-0.8.4/Makefile st-autocomplete/Makefile
+--- st-0.8.4/Makefile 2021-12-14 20:03:03.981322164 +0400
++++ st-autocomplete/Makefile 2021-12-15 07:12:40.291573671 +0400
+@@ -44,6 +44,8 @@
+ mkdir -p $(DESTDIR)$(PREFIX)/bin
+ cp -f st $(DESTDIR)$(PREFIX)/bin
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/st
++ cp -f st-autocomplete $(DESTDIR)$(PREFIX)/bin
++ chmod 755 $(DESTDIR)$(PREFIX)/bin/st-autocomplete
+ mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+ sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1
+ chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1
+@@ -52,6 +54,7 @@
+
+ uninstall:
+ rm -f $(DESTDIR)$(PREFIX)/bin/st
++ rm -f $(DESTDIR)$(PREFIX)/bin/st-autocomplete
+ rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1
+
+ .PHONY: all options clean dist install uninstall
+diff -uraN st-0.8.4/st-autocomplete st-autocomplete/st-autocomplete
+--- st-0.8.4/st-autocomplete 1970-01-01 04:00:00.000000000 +0400
++++ st-autocomplete/st-autocomplete 2021-12-14 20:18:13.171971360 +0400
+@@ -0,0 +1,266 @@
++#!/usr/bin/perl
++#########################################################################
++# Copyright (C) 2012-2021 Wojciech Siewierski, Gaspar Vardanyan #
++# #
++# This program is free software: you can redistribute it and/or modify #
++# it under the terms of the GNU General Public License as published by #
++# the Free Software Foundation, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# This program is distributed in the hope that it will be useful, #
++# but WITHOUT ANY WARRANTY; without even the implied warranty of #
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
++# GNU General Public License for more details. #
++# #
++# You should have received a copy of the GNU General Public License #
++# along with this program. If not, see <http://www.gnu.org/licenses/>. #
++#########################################################################
++
++my ($cmd, $cursor_row, $cursor_column) = @ARGV;
++
++# A reference to a function that transforms the completed word
++# into a regex matching the completions. Usually generated by
++# generate_matcher().
++#
++# For example
++# $fun = generate_matcher(".*");
++# $fun->("foo");
++# would return "f.*o.*o"
++#
++# In other words, indirectly decides which characters can
++# appear in the completion.
++my $matcher;
++
++# A regular expression matching a character before each match.
++# For example, it you want to match the text after a
++# whitespace, set it to "\s".
++my $char_class_before;
++
++# A regular expression matching every character in the entered
++# text that will be used to find matching completions. Usually
++# "\w" or similar.
++my $char_class_to_complete;
++
++# A regular expression matching every allowed last character
++# of the completion (uses greedy matching).
++my $char_class_at_end;
++
++if ($cmd eq 'word-complete') {
++ # Basic word completion. Completes the current word
++ # without any special matching.
++ $char_class_before = '[^-\w]';
++ $matcher = sub { quotemeta shift }; # identity
++ $char_class_at_end = '[-\w]';
++ $char_class_to_complete = '[-\w]';
++} elsif ($cmd eq 'WORD-complete') {
++ # The same as above but in the Vim meaning of a "WORD" --
++ # whitespace delimited.
++ $char_class_before = '\s';
++ $matcher = sub { quotemeta shift };
++ $char_class_at_end = '\S';
++ $char_class_to_complete = '\S';
++} elsif ($cmd eq 'fuzzy-word-complete' ||
++ $cmd eq 'skeleton-word-complete') {
++ # Fuzzy completion of the current word.
++ $char_class_before = '[^-\w]';
++ $matcher = generate_matcher('[-\w]*');
++ $char_class_at_end = '[-\w]';
++ $char_class_to_complete = '[-\w]';
++} elsif ($cmd eq 'fuzzy-WORD-complete') {
++ # Fuzzy completion of the current WORD.
++ $char_class_before = '\s';
++ $matcher = generate_matcher('\S*');
++ $char_class_at_end = '\S';
++ $char_class_to_complete = '\S';
++} elsif ($cmd eq 'fuzzy-complete' ||
++ $cmd eq 'skeleton-complete') {
++ # Fuzzy completion of an arbitrary text.
++ $char_class_before = '\W';
++ $matcher = generate_matcher('.*?');
++ $char_class_at_end = '\w';
++ $char_class_to_complete = '\S';
++} elsif ($cmd eq 'suffix-complete') {
++ # Fuzzy completion of an completing suffixes, like
++ # completing test=hello from /blah/hello.
++ $char_class_before = '\S';
++ $matcher = generate_matcher('\S*');
++ $char_class_at_end = '\S';
++ $char_class_to_complete = '\S';
++} elsif ($cmd eq 'surround-complete') {
++ # Completing contents of quotes and braces.
++
++ # Here we are using three named groups: s, b, p for quotes, braces
++ # and parenthesis.
++ $char_class_before = '((?<q>["\'`])|(?<b>\[)|(?<p>\())';
++
++ $matcher = generate_matcher('.*?');
++
++ # Here we match text till enclosing pair, using perl conditionals in
++ # regexps (?(condition)yes-expression|no-expression).
++ # \0 is used to hack concatenation with '*' later in the code.
++ $char_class_at_end = '.*?(.(?=(?(<b>)\]|((?(<p>)\)|\g{q})))))\0';
++ $char_class_to_complete = '\S';
++}
++
++my $lines = [];
++
++while (<STDIN>)
++{
++ push @{$lines}, $_;
++}
++
++# read the word behind the cursor
++$_ = substr(@{$lines} [$cursor_row], 0, $cursor_column); # get the current line up to the cursor...
++s/.*?($char_class_to_complete*)$/$1/; # ...and read the last word from it
++my $word_to_complete = $_;
++
++# ignore the completed word itself
++$self->{already_completed}{$word_to_complete} = 1;
++
++print stdout "$word_to_complete\n";
++
++# search for matches
++while (my $completion = find_match($self,
++ $word_to_complete,
++ $self->{next_row} // $cursor_row,
++ $matcher->($word_to_complete),
++ $char_class_before,
++ $char_class_at_end)
++) {
++ calc_match_coords($self,
++ $self->{next_row}+1,
++ $completion);
++ print stdout "$completion @{$self->{highlight}}\n";
++}
++
++leave($self);
++
++
++
++######################################################################
++
++# Finds the next matching completion in the row current row or above
++# while skipping duplicates using skip_duplicates().
++sub find_match {
++ my ($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end) = @_;
++ $self->{matches_in_row} //= [];
++
++ # cycle through all the matches in the current row if not starting a new search
++ if (@{$self->{matches_in_row}}) {
++ return skip_duplicates($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end);
++ }
++
++
++ my $i;
++ # search through all the rows starting with current one or one above the last checked
++ for ($i = $current_row; $i >= 0; --$i) {
++ my $line = @{$lines} [$i]; # get the line of text from the row
++
++ if ($i == $cursor_row) {
++ $line = substr $line, 0, $cursor_column;
++ }
++
++ $_ = $line;
++
++ # find all the matches in the current line
++ my $match;
++ push @{$self->{matches_in_row}}, $+{match} while ($_, $match) = /
++ (.*${char_class_before})
++ (?<match>
++ ${regexp}
++ ${char_class_at_end}*
++ )
++ /ix;
++ # corner case: match at the very beginning of line
++ push @{$self->{matches_in_row}}, $+{match} if $line =~ /^(${char_class_before}){0}(?<match>$regexp$char_class_at_end*)/i;
++
++ if (@{$self->{matches_in_row}}) {
++ # remember which row should be searched next
++ $self->{next_row} = --$i;
++
++ # arguments needed for find_match() mutual recursion
++ return skip_duplicates($self, $word_to_match, $i, $regexp, $char_class_before, $char_class_at_end);
++ }
++ }
++
++ # no more possible completions, revert to the original word
++ $self->{next_row} = -1 if $i < 0;
++
++ return undef;
++}
++
++######################################################################
++
++# Checks whether the completion found by find_match() was already
++# found and if it was, calls find_match() again to find the next
++# completion.
++#
++# Takes all the arguments that find_match() would take, to make a
++# mutually recursive call.
++sub skip_duplicates {
++ my $self = $_[0];
++ my $completion = shift @{$self->{matches_in_row}}; # get the rightmost one
++
++ # check for duplicates
++ if (exists $self->{already_completed}{$completion}) {
++ # skip this completion
++ return find_match(@_);
++ } else {
++ $self->{already_completed}{$completion} = 1;
++ return $completion;
++ }
++}
++
++######################################################################
++
++# Returns a function that takes a string and returns that string with
++# this function's argument inserted between its every two characters.
++# The resulting string is used as a regular expression matching the
++# completion candidates.
++sub generate_matcher {
++ my $regex_between = shift;
++
++ sub {
++ $_ = shift;
++
++ # sorry for this lispy code, I couldn't resist ;)
++ (join "$regex_between",
++ (map quotemeta,
++ (split //)))
++ }
++}
++
++######################################################################
++
++sub calc_match_coords {
++ my ($self, $linenum, $completion) = @_;
++
++ my $line = @{$lines} [$linenum];
++ my $re = quotemeta $completion;
++
++ $line =~ /$re/;
++
++ #my ($beg_row, $beg_col) = $line->coord_of($-[0]);
++ #my ($end_row, $end_col) = $line->coord_of($+[0]);
++ my $beg = $-[0];
++ my $end = $+[0];
++
++ if (exists $self->{highlight}) {
++ delete $self->{highlight};
++ }
++ # () # TODO: what does () do in perl ????
++
++ # $self->{highlight} = [$beg_row, $beg_col, $end_row, $end_col];
++ $self->{highlight} = [$linenum, $beg, $end];
++}
++
++######################################################################
++
++sub leave {
++ my ($self) = @_;
++
++ delete $self->{next_row};
++ delete $self->{matches_in_row};
++ delete $self->{already_completed};
++ delete $self->{highlight};
++}
+diff -uraN st-0.8.4/st.c st-autocomplete/st.c
+--- st-0.8.4/st.c 2021-12-14 20:03:03.981322164 +0400
++++ st-autocomplete/st.c 2021-12-15 07:44:05.609586643 +0400
+@@ -17,6 +17,7 @@
+ #include <unistd.h>
+ #include <wchar.h>
+
++#include "autocomplete.h"
+ #include "st.h"
+ #include "win.h"
+
+@@ -2476,6 +2477,9 @@
+ return;
+ }
+
++ if ( row < term.row || col < term.col )
++ autocomplete ((const Arg []) { ACMPL_DEACTIVATE });
++
+ /*
+ * slide screen to keep cursor where we expect it -
+ * tscrollup would work here, but we can optimize to
+@@ -2595,3 +2599,211 @@
+ tfulldirt();
+ draw();
+ }
++
++void autocomplete (const Arg * arg)
++{
++ static _Bool active = 0;
++
++ int acmpl_cmdindex = arg -> i;
++
++ static int acmpl_cmdindex_prev;
++
++ if (active == 0)
++ acmpl_cmdindex_prev = acmpl_cmdindex;
++
++ static const char * const (acmpl_cmd []) = {
++ [ACMPL_DEACTIVATE] = "__DEACTIVATE__",
++ [ACMPL_WORD] = "word-complete",
++ [ACMPL_WWORD] = "WORD-complete",
++ [ACMPL_FUZZY_WORD] = "fuzzy-word-complete",
++ [ACMPL_FUZZY_WWORD] = "fuzzy-WORD-complete",
++ [ACMPL_FUZZY] = "fuzzy-complete",
++ [ACMPL_SUFFIX] = "suffix-complete",
++ [ACMPL_SURROUND] = "surround-complete",
++ [ACMPL_UNDO] = "__UNDO__",
++ };
++
++ static char acmpl [1000]; // ACMPL_ISSUE: why 1000?
++
++ static FILE * acmpl_exec = NULL;
++ static int acmpl_status;
++
++ static const char * stbuffile;
++ static char target [1000]; // ACMPL_ISSUE: why 1000? dynamically allocate char array of size term.col
++ static size_t targetlen;
++
++ static char completion [1000] = {0}; // ACMPL_ISSUE: why 1000? dynamically allocate char array of size term.col
++ static size_t complen_prev = 0; // NOTE: always clear this variable after clearing completion
++
++// Check for deactivation
++
++ if (acmpl_cmdindex == ACMPL_DEACTIVATE)
++ {
++
++// Deactivate autocomplete mode keeping current completion
++
++ if (active)
++ {
++ active = 0;
++ pclose (acmpl_exec);
++ remove (stbuffile);
++
++ if (complen_prev)
++ {
++ selclear ();
++ complen_prev = 0;
++ }
++ }
++
++ return;
++ }
++
++// Check for undo
++
++ if (acmpl_cmdindex == ACMPL_UNDO)
++ {
++
++// Deactivate autocomplete mode recovering target
++
++ if (active)
++ {
++ active = 0;
++ pclose (acmpl_exec);
++ remove (stbuffile);
++
++ if (complen_prev)
++ {
++ selclear ();
++ for (size_t i = 0; i < complen_prev; i++)
++ ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is the right way
++ complen_prev = 0;
++ ttywrite (target, targetlen, 0); // ACMPL_ISSUE: I'm not sure that this is a right solution
++ }
++ }
++
++ return;
++ }
++
++// Check for command change
++
++ if (acmpl_cmdindex != acmpl_cmdindex_prev)
++ {
++
++// If command is changed, goto acmpl_begin avoiding rewriting st buffer
++
++ if (active)
++ {
++ acmpl_cmdindex_prev = acmpl_cmdindex;
++
++ goto acmpl_begin;
++ }
++ }
++
++// If not active
++
++ if (active == 0)
++ {
++ acmpl_cmdindex_prev = acmpl_cmdindex;
++
++// Write st buffer to a temp file
++
++ stbuffile = tmpnam (NULL); // check for return value ...
++ // ACMPL_ISSUE: use coprocesses instead of temp files
++ sprintf (
++ acmpl,
++ "cat %100s | st-autocomplete %500s %d %d", // ACMPL_ISSUE: why 100 and 500?
++ stbuffile,
++ acmpl_cmd [acmpl_cmdindex],
++ term.c.y,
++ term.c.x
++ );
++
++ FILE * stbuf = fopen (stbuffile, "w"); // check for opening error ...
++ char * stbufline = malloc (term.col + 2); // check for allocating error ...
++
++ for (size_t y = 0; y < term.row; y++)
++ {
++ size_t x = 0;
++ for (; x < term.col; x++)
++ utf8encode (term.line [y] [x].u, stbufline + x);
++ stbufline [x] = '\n';
++ stbufline [x + 1] = 0;
++ fputs (stbufline, stbuf);
++ }
++
++ free (stbufline);
++ fclose (stbuf);
++
++acmpl_begin:
++
++// Run st-autocomplete
++
++ acmpl_exec = popen (acmpl, "r"); // ACMPL_ISSUE: popen isn't defined by The Standard. Does it work in BSDs for example?
++ // check for popen error ...
++
++// Read the target, targetlen
++
++ fscanf (acmpl_exec, "%500s\n", target); // check for scanning error ...
++ targetlen = strlen (target);
++ }
++
++// Read a completion if exists (acmpl_status)
++
++ unsigned line, beg, end;
++
++ acmpl_status = fscanf (acmpl_exec, "%500s %u %u %u\n", completion, & line, & beg, & end);
++ // ACMPL_ISSUE: why 500? use term.col instead
++
++// Exit if no completions found
++
++ if (active == 0 && acmpl_status == EOF)
++ {
++
++// Close st-autocomplete and exit without activating the autocomplete mode
++
++ pclose (acmpl_exec);
++ remove (stbuffile);
++ return;
++ }
++
++// If completions found, enable autocomplete mode and autocomplete the target
++
++ active = 1;
++
++// Clear target before first completion
++
++ if (complen_prev == 0)
++ {
++ for (size_t i = 0; i < targetlen; i++)
++ ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is a right solution
++ }
++
++// Clear previuos completion if this is not the first
++
++ else
++ {
++ selclear ();
++ for (size_t i = 0; i < complen_prev; i++)
++ ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is a right solution
++ complen_prev = 0;
++ }
++
++// If no more completions found, reset and restart
++
++ if (acmpl_status == EOF)
++ {
++ active = 0;
++ pclose (acmpl_exec);
++ ttywrite (target, targetlen, 0);
++ goto acmpl_begin;
++ }
++
++// Read the new completion and autcomplete
++
++ selstart (beg, line, 0);
++ selextend (end - 1, line, 1, 0);
++ xsetsel (getsel ());
++
++ complen_prev = strlen (completion);
++ ttywrite (completion, complen_prev, 0);
++}
+diff -uraN st-0.8.4/st.h st-autocomplete/st.h
+--- st-0.8.4/st.h 2021-12-14 20:03:03.981322164 +0400
++++ st-autocomplete/st.h 2021-12-14 20:28:59.272432749 +0400
+@@ -77,6 +77,8 @@
+ const char *s;
+ } Arg;
+
++void autocomplete (const Arg *);
++
+ void die(const char *, ...);
+ void redraw(void);
+ void draw(void);
+diff -uraN st-0.8.4/x.c st-autocomplete/x.c
+--- st-0.8.4/x.c 2021-12-14 20:03:03.981322164 +0400
++++ st-autocomplete/x.c 2021-12-14 20:30:30.045830893 +0400
+@@ -1803,11 +1803,15 @@
+ /* 1. shortcuts */
+ for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
+ if (ksym == bp->keysym && match(bp->mod, e->state)) {
++ if (bp -> func != autocomplete)
++ autocomplete ((const Arg []) { ACMPL_DEACTIVATE });
+ bp->func(&(bp->arg));
+ return;
+ }
+ }
+
++ autocomplete ((const Arg []) { ACMPL_DEACTIVATE });
++
+ /* 2. custom keys from config.h */
+ if ((customkey = kmap(ksym, e->state))) {
+ ttywrite(customkey, strlen(customkey), 1);
diff --git a/based-simple-term/patches/st-background-image-0.8.4.diff b/based-simple-term/patches/st-background-image-0.8.4.diff
new file mode 100755
index 0000000..f32c947
--- /dev/null
+++ b/based-simple-term/patches/st-background-image-0.8.4.diff
@@ -0,0 +1,262 @@
+From 2c984d74ca15806dcfa174b7e75f48c0d01a49bf Mon Sep 17 00:00:00 2001
+From: Matthias Schoth <mschoth@gmail.com>
+Date: Thu, 17 Feb 2022 00:23:23 +0100
+Subject: [PATCH] Implements background image and pseudo transparancy support.
+
+---
+ config.def.h | 8 +++
+ x.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++----
+ 2 files changed, 139 insertions(+), 10 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 6f05dce..3d352db 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -8,6 +8,14 @@
+ static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
+ static int borderpx = 2;
+
++/*
++ * background image
++ * expects farbfeld format
++ * pseudo transparency fixes coordinates to the screen origin
++ */
++static const char *bgfile = "/path/to/image.ff";
++static const int pseudotransparency = 0;
++
+ /*
+ * What program is execed by st depends of these precedence rules:
+ * 1: program passed with -e
+diff --git a/x.c b/x.c
+index 210f184..5ecb8e5 100644
+--- a/x.c
++++ b/x.c
+@@ -14,6 +14,7 @@
+ #include <X11/keysym.h>
+ #include <X11/Xft/Xft.h>
+ #include <X11/XKBlib.h>
++#include <arpa/inet.h>
+
+ char *argv0;
+ #include "arg.h"
+@@ -81,6 +82,7 @@ typedef XftGlyphFontSpec GlyphFontSpec;
+ typedef struct {
+ int tw, th; /* tty width and height */
+ int w, h; /* window width and height */
++ int x, y; /* window location */
+ int ch; /* char height */
+ int cw; /* char width */
+ int mode; /* window state/mode flags */
+@@ -101,6 +103,7 @@ typedef struct {
+ XVaNestedList spotlist;
+ } ime;
+ Draw draw;
++ GC bggc; /* Graphics Context for background */
+ Visual *vis;
+ XSetWindowAttributes attrs;
+ int scr;
+@@ -151,6 +154,9 @@ 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 updatexy(void);
++static XImage *loadff(const char *);
++static void bginit();
+ static void cresize(int, int);
+ static void xresize(int, int);
+ static void xhints(void);
+@@ -502,6 +508,12 @@ propnotify(XEvent *e)
+ xpev->atom == clipboard)) {
+ selnotify(e);
+ }
++
++ if (pseudotransparency &&
++ !strncmp(XGetAtomName(xw.dpy, e->xproperty.atom), "_NET_WM_STATE", 13)) {
++ updatexy();
++ redraw();
++ }
+ }
+
+ void
+@@ -532,7 +544,8 @@ selnotify(XEvent *e)
+ return;
+ }
+
+- if (e->type == PropertyNotify && nitems == 0 && rem == 0) {
++ if (e->type == PropertyNotify && nitems == 0 && rem == 0 &&
++ !pseudotransparency) {
+ /*
+ * If there is some PropertyNotify with no data, then
+ * this is the signal of the selection owner that all
+@@ -550,9 +563,11 @@ selnotify(XEvent *e)
+ * when the selection owner does send us the next
+ * chunk of data.
+ */
+- MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
+- XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
++ if (!pseudotransparency) {
++ MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
++ XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
+ &xw.attrs);
++ }
+
+ /*
+ * Deleting the property is the transfer start signal.
+@@ -820,9 +835,9 @@ xsetcolorname(int x, const char *name)
+ void
+ xclear(int x1, int y1, int x2, int y2)
+ {
+- XftDrawRect(xw.draw,
+- &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg],
+- x1, y1, x2-x1, y2-y1);
++ if (pseudotransparency)
++ XSetTSOrigin(xw.dpy, xw.bggc, -win.x, -win.y);
++ XFillRectangle(xw.dpy, xw.buf, xw.bggc, x1, y1, x2-x1, y2-y1);
+ }
+
+ void
+@@ -1207,6 +1222,100 @@ xinit(int cols, int rows)
+ xsel.xtarget = XA_STRING;
+ }
+
++void
++updatexy()
++{
++ Window child;
++ XTranslateCoordinates(xw.dpy, xw.win, DefaultRootWindow(xw.dpy), 0, 0,
++ &win.x, &win.y, &child);
++}
++
++/*
++ * load farbfeld file to XImage
++ */
++XImage*
++loadff(const char *filename)
++{
++ uint32_t i, hdr[4], w, h, size;
++ uint64_t *data;
++ FILE *f = fopen(filename, "rb");
++
++ if (f == NULL) {
++ fprintf(stderr, "could not load background image.\n");
++ return NULL;
++ }
++
++ if (fread(hdr, sizeof(*hdr), LEN(hdr), f) != LEN(hdr))
++ if (ferror(f)) {
++ fprintf(stderr, "fread:");
++ return NULL;
++ }
++ else {
++ fprintf(stderr, "fread: Unexpected end of file\n");
++ return NULL;
++ }
++
++ if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) {
++ fprintf(stderr, "Invalid magic value");
++ return NULL;
++ }
++
++ w = ntohl(hdr[2]);
++ h = ntohl(hdr[3]);
++ size = w * h;
++ data = malloc(size * sizeof(uint64_t));
++
++ if (fread(data, sizeof(uint64_t), size, f) != size)
++ if (ferror(f)) {
++ fprintf(stderr, "fread:");
++ return NULL;
++ }
++ else {
++ fprintf(stderr, "fread: Unexpected end of file");
++ return NULL;
++ }
++
++ fclose(f);
++
++ for (i = 0; i < size; i++)
++ data[i] = (data[i] & 0x00000000000000FF) << 16 |
++ (data[i] & 0x0000000000FF0000) >> 8 |
++ (data[i] & 0x000000FF00000000) >> 32;
++
++ XImage *xi = XCreateImage(xw.dpy, DefaultVisual(xw.dpy, xw.scr),
++ DefaultDepth(xw.dpy, xw.scr), ZPixmap, 0,
++ (char *)data, w, h, 32, w * 8);
++ xi->bits_per_pixel = 64;
++ return xi;
++}
++
++/*
++ * initialize background image
++ */
++void
++bginit()
++{
++ XGCValues gcvalues;
++ Drawable bgimg;
++ XImage *bgxi = loadff(bgfile);
++
++ memset(&gcvalues, 0, sizeof(gcvalues));
++ xw.bggc = XCreateGC(xw.dpy, xw.win, 0, &gcvalues);
++ if (!bgxi) return;
++ bgimg = XCreatePixmap(xw.dpy, xw.win, bgxi->width, bgxi->height,
++ DefaultDepth(xw.dpy, xw.scr));
++ XPutImage(xw.dpy, bgimg, dc.gc, bgxi, 0, 0, 0, 0, bgxi->width,
++ bgxi->height);
++ XDestroyImage(bgxi);
++ XSetTile(xw.dpy, xw.bggc, bgimg);
++ XSetFillStyle(xw.dpy, xw.bggc, FillTiled);
++ if (pseudotransparency) {
++ updatexy();
++ MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
++ XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
++ }
++}
++
+ int
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
+ {
+@@ -1447,7 +1556,10 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+ 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);
++ if (bg == &dc.col[defaultbg])
++ xclear(winx, winy, winx + width, winy + win.ch);
++ else
++ XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
+
+ /* Set the clip region because Xft is sometimes dirty. */
+ r.x = 0;
+@@ -1855,9 +1967,17 @@ cmessage(XEvent *e)
+ void
+ resize(XEvent *e)
+ {
+- if (e->xconfigure.width == win.w && e->xconfigure.height == win.h)
+- return;
+-
++ if (pseudotransparency) {
++ if (e->xconfigure.width == win.w &&
++ e->xconfigure.height == win.h &&
++ e->xconfigure.x == win.x && e->xconfigure.y == win.y)
++ return;
++ updatexy();
++ } else {
++ if (e->xconfigure.width == win.w &&
++ e->xconfigure.height == win.h)
++ return;
++ }
+ cresize(e->xconfigure.width, e->xconfigure.height);
+ }
+
+@@ -2041,6 +2161,7 @@ run:
+ rows = MAX(rows, 1);
+ tnew(cols, rows);
+ xinit(cols, rows);
++ bginit();
+ xsetenv();
+ selinit();
+ run();
+--
+2.35.1
+
diff --git a/based-simple-term/patches/st-delkey-20201112-4ef0cbd.diff b/based-simple-term/patches/st-delkey-20201112-4ef0cbd.diff
new file mode 100755
index 0000000..c334b0d
--- /dev/null
+++ b/based-simple-term/patches/st-delkey-20201112-4ef0cbd.diff
@@ -0,0 +1,20 @@
+--- config.def.h.orig 2020-11-12 20:23:48.867954750 +0100
++++ config.def.h 2020-11-12 20:21:15.055922720 +0100
+@@ -276,7 +276,7 @@
+ { 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_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},
+@@ -344,7 +344,7 @@
+ { 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_Delete, XK_ANY_MOD, "\033[3~", +1, 0},
+ { XK_BackSpace, XK_NO_MOD, "\177", 0, 0},
+ { XK_BackSpace, Mod1Mask, "\033\177", 0, 0},
diff --git a/based-simple-term/patches/st-dynamic-cursor-color-0.8.4.diff b/based-simple-term/patches/st-dynamic-cursor-color-0.8.4.diff
new file mode 100755
index 0000000..a763d6d
--- /dev/null
+++ b/based-simple-term/patches/st-dynamic-cursor-color-0.8.4.diff
@@ -0,0 +1,52 @@
+From 01e706efbc13194a4a4404e91b93a9638a3c1bea Mon Sep 17 00:00:00 2001
+From: Kipras Melnikovas <kipras@kipras.org>
+Date: Thu, 25 Feb 2021 14:31:26 +0200
+Subject: [PATCH] refactor dynamic-cursor-color patch
+
+Signed-off-by: Kipras Melnikovas <kipras@kipras.org>
+---
+ x.c | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/x.c b/x.c
+index 120e495..ab66364 100644
+--- a/x.c
++++ b/x.c
+@@ -1489,6 +1489,7 @@ void
+ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
+ {
+ Color drawcol;
++ XRenderColor colbg;
+
+ /* remove the old cursor */
+ if (selected(ox, oy))
+@@ -1518,10 +1519,24 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
+ g.fg = defaultfg;
+ g.bg = defaultrcs;
+ } else {
++ /** this is the main part of the dynamic cursor color patch */
++ g.bg = g.fg;
+ g.fg = defaultbg;
+- g.bg = defaultcs;
+ }
+- drawcol = dc.col[g.bg];
++
++ /**
++ * and this is the second part of the dynamic cursor color patch.
++ * it handles the `drawcol` variable
++ */
++ if (IS_TRUECOL(g.bg)) {
++ colbg.alpha = 0xffff;
++ colbg.red = TRUERED(g.bg);
++ colbg.green = TRUEGREEN(g.bg);
++ colbg.blue = TRUEBLUE(g.bg);
++ XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &drawcol);
++ } else {
++ drawcol = dc.col[g.bg];
++ }
+ }
+
+ /* draw the new one */
+--
+2.30.1
+
diff --git a/based-simple-term/patches/st-scrollback-20210507-4536f46.diff b/based-simple-term/patches/st-scrollback-20210507-4536f46.diff
new file mode 100755
index 0000000..f960f6b
--- /dev/null
+++ b/based-simple-term/patches/st-scrollback-20210507-4536f46.diff
@@ -0,0 +1,351 @@
+diff --git a/config.def.h b/config.def.h
+index 6f05dce..93cbcc0 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -199,6 +199,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 ebdf360..817cc47 100644
+--- a/st.c
++++ b/st.c
+@@ -35,6 +35,7 @@
+ #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)
+@@ -42,6 +43,9 @@
+ #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])
+
+ enum term_mode {
+ MODE_WRAP = 1 << 0,
+@@ -115,6 +119,9 @@ typedef struct {
+ int col; /* nb col */
+ 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 */
+@@ -184,8 +191,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);
+@@ -416,10 +423,10 @@ tlinelen(int y)
+ {
+ int i = term.col;
+
+- if (term.line[y][i - 1].mode & ATTR_WRAP)
++ if (TLINE(y)[i - 1].mode & ATTR_WRAP)
+ return i;
+
+- while (i > 0 && term.line[y][i - 1].u == ' ')
++ while (i > 0 && TLINE(y)[i - 1].u == ' ')
+ --i;
+
+ return i;
+@@ -528,7 +535,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 +550,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 +578,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 +616,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;
+
+@@ -850,6 +857,9 @@ 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);
+@@ -1061,13 +1071,53 @@ tswapscreen(void)
+ }
+
+ void
+-tscrolldown(int orig, int n)
++kscrolldown(const Arg* a)
++{
++ int n = a->i;
++
++ if (n < 0)
++ n = term.row + n;
++
++ if (n > term.scr)
++ n = term.scr;
++
++ if (term.scr > 0) {
++ term.scr -= n;
++ selscroll(0, -n);
++ tfulldirt();
++ }
++}
++
++void
++kscrollup(const Arg* a)
++{
++ int n = a->i;
++
++ if (n < 0)
++ n = term.row + n;
++
++ if (term.scr <= HISTSIZE-n) {
++ term.scr += n;
++ selscroll(0, n);
++ tfulldirt();
++ }
++}
++
++void
++tscrolldown(int orig, int n, int copyhist)
+ {
+ int i;
+ Line temp;
+
+ LIMIT(n, 0, term.bot-orig+1);
+
++ 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;
++ }
++
+ tsetdirt(orig, term.bot-n);
+ tclearregion(0, term.bot-n+1, term.col-1, term.bot);
+
+@@ -1077,17 +1127,28 @@ tscrolldown(int orig, int n)
+ term.line[i-n] = temp;
+ }
+
+- selscroll(orig, n);
++ if (term.scr == 0)
++ 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);
+
++ if (copyhist) {
++ term.histi = (term.histi + 1) % HISTSIZE;
++ temp = term.hist[term.histi];
++ term.hist[term.histi] = term.line[orig];
++ term.line[orig] = 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);
+
+@@ -1097,7 +1158,8 @@ tscrollup(int orig, int n)
+ term.line[i+n] = temp;
+ }
+
+- selscroll(orig, -n);
++ if (term.scr == 0)
++ selscroll(orig, -n);
+ }
+
+ void
+@@ -1126,7 +1188,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++;
+ }
+@@ -1291,14 +1353,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
+@@ -1735,11 +1797,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);
+@@ -2251,7 +2313,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);
+ }
+@@ -2264,7 +2326,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);
+ }
+@@ -2474,7 +2536,7 @@ twrite(const char *buf, int buflen, int show_ctrl)
+ void
+ tresize(int col, int row)
+ {
+- int i;
++ int i, j;
+ int minrow = MIN(row, term.row);
+ int mincol = MIN(col, term.col);
+ int *bp;
+@@ -2511,6 +2573,14 @@ tresize(int col, int row)
+ term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
+ term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
+
++ 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));
+@@ -2569,7 +2639,7 @@ drawregion(int x1, int y1, int x2, int y2)
+ continue;
+
+ term.dirty[y] = 0;
+- xdrawline(term.line[y], x1, y, x2);
++ xdrawline(TLINE(y), x1, y, x2);
+ }
+ }
+
+@@ -2590,8 +2660,9 @@ draw(void)
+ 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 (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.ocx = cx;
+ term.ocy = term.c.y;
+ xfinishdraw();
+diff --git a/st.h b/st.h
+index fa2eddf..adda2db 100644
+--- a/st.h
++++ b/st.h
+@@ -81,6 +81,8 @@ void die(const char *, ...);
+ void redraw(void);
+ void draw(void);
+
++void kscrolldown(const Arg *);
++void kscrollup(const Arg *);
+ void printscreen(const Arg *);
+ void printsel(const Arg *);
+ void sendbreak(const Arg *);
diff --git a/based-simple-term/patches/st-scrollback-mouse-20191024-a2c479c.diff b/based-simple-term/patches/st-scrollback-mouse-20191024-a2c479c.diff
new file mode 100755
index 0000000..49eba8e
--- /dev/null
+++ b/based-simple-term/patches/st-scrollback-mouse-20191024-a2c479c.diff
@@ -0,0 +1,13 @@
+diff --git a/config.def.h b/config.def.h
+index ec1b576..4b3bf15 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -163,6 +163,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 },
+ { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} },
+ { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} },
diff --git a/based-simple-term/patches/st-scrollback-mouse-altscreen-20220127-2c5edf2.diff b/based-simple-term/patches/st-scrollback-mouse-altscreen-20220127-2c5edf2.diff
new file mode 100755
index 0000000..6a8722b
--- /dev/null
+++ b/based-simple-term/patches/st-scrollback-mouse-altscreen-20220127-2c5edf2.diff
@@ -0,0 +1,78 @@
+From 580e3f386e9215707100e9ba44797701943fd927 Mon Sep 17 00:00:00 2001
+From: asparagii <michele.lambertucci1@gmail.com>
+Date: Thu, 27 Jan 2022 15:49:27 +0100
+Subject: [PATCH] st-scrollback-mouse-altscreen
+
+---
+ config.def.h | 4 ++--
+ st.c | 5 +++++
+ st.h | 1 +
+ x.c | 2 ++
+ 4 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index c217315..c223706 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -176,8 +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, Button4, kscrollup, {.i = 1}, 0, /* !alt */ -1 },
++ { XK_ANY_MOD, Button5, kscrolldown, {.i = 1}, 0, /* !alt */ -1 },
+ { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 },
+ { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} },
+ { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} },
+diff --git a/st.c b/st.c
+index f3af82b..876a6bf 100644
+--- a/st.c
++++ b/st.c
+@@ -1060,6 +1060,11 @@ tnew(int col, int row)
+ treset();
+ }
+
++int tisaltscr(void)
++{
++ return IS_SET(MODE_ALTSCREEN);
++}
++
+ void
+ tswapscreen(void)
+ {
+diff --git a/st.h b/st.h
+index da36b34..e95c6f8 100644
+--- a/st.h
++++ b/st.h
+@@ -89,6 +89,7 @@ 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);
+diff --git a/x.c b/x.c
+index cd96575..9274556 100644
+--- a/x.c
++++ b/x.c
+@@ -34,6 +34,7 @@ typedef struct {
+ void (*func)(const Arg *);
+ const Arg arg;
+ uint release;
++ int altscrn; /* 0: don't care, -1: not alt screen, 1: alt screen */
+ } MouseShortcut;
+
+ typedef struct {
+@@ -455,6 +456,7 @@ mouseaction(XEvent *e, uint release)
+ for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
+ if (ms->release == release &&
+ ms->button == e->xbutton.button &&
++ (!ms->altscrn || (ms->altscrn == (tisaltscr() ? 1 : -1))) &&
+ (match(ms->mod, state) || /* exact or forced */
+ match(ms->mod, state & ~forcemousemod))) {
+ ms->func(&(ms->arg));
+--
+2.34.1
+
diff --git a/based-simple-term/patches/st-undercurl-0.8.4-20210822.diff b/based-simple-term/patches/st-undercurl-0.8.4-20210822.diff
new file mode 100755
index 0000000..fab2ddc
--- /dev/null
+++ b/based-simple-term/patches/st-undercurl-0.8.4-20210822.diff
@@ -0,0 +1,605 @@
+diff --git a/config.def.h b/config.def.h
+index 6f05dce..7ae1b92 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -470,3 +470,27 @@ static char ascii_printable[] =
+ " !\"#$%&'()*+,-./0123456789:;<=>?"
+ "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+ "`abcdefghijklmnopqrstuvwxyz{|}~";
++
++/**
++ * Undercurl style. Set UNDERCURL_STYLE to one of the available styles.
++ *
++ * Curly: Dunno how to draw it *shrug*
++ * _ _ _ _
++ * ( ) ( ) ( ) ( )
++ * (_) (_) (_) (_)
++ *
++ * Spiky:
++ * /\ /\ /\ /\
++ * \/ \/ \/
++ *
++ * Capped:
++ * _ _ _
++ * / \ / \ / \
++ * \_/ \_/
++ */
++// Available styles
++#define UNDERCURL_CURLY 0
++#define UNDERCURL_SPIKY 1
++#define UNDERCURL_CAPPED 2
++// Active style
++#define UNDERCURL_STYLE UNDERCURL_SPIKY
+diff --git a/st.c b/st.c
+index 76b7e0d..542ab3a 100644
+--- a/st.c
++++ b/st.c
+@@ -33,6 +33,7 @@
+ #define UTF_SIZ 4
+ #define ESC_BUF_SIZ (128*UTF_SIZ)
+ #define ESC_ARG_SIZ 16
++#define CAR_PER_ARG 4
+ #define STR_BUF_SIZ ESC_BUF_SIZ
+ #define STR_ARG_SIZ ESC_ARG_SIZ
+
+@@ -139,6 +140,7 @@ typedef struct {
+ int arg[ESC_ARG_SIZ];
+ int narg; /* nb of args */
+ char mode[2];
++ int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */
+ } CSIEscape;
+
+ /* STR Escape sequence structs */
+@@ -159,6 +161,7 @@ static void ttywriteraw(const char *, size_t);
+
+ static void csidump(void);
+ static void csihandle(void);
++static void readcolonargs(char **, int, int[][CAR_PER_ARG]);
+ static void csiparse(void);
+ static void csireset(void);
+ static int eschandle(uchar);
+@@ -1131,6 +1134,28 @@ tnewline(int first_col)
+ tmoveto(first_col ? 0 : term.c.x, y);
+ }
+
++void
++readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG])
++{
++ int i = 0;
++ for (; i < CAR_PER_ARG; i++)
++ params[cursor][i] = -1;
++
++ if (**p != ':')
++ return;
++
++ char *np = NULL;
++ i = 0;
++
++ while (**p == ':' && i < CAR_PER_ARG) {
++ while (**p == ':')
++ (*p)++;
++ params[cursor][i] = strtol(*p, &np, 10);
++ *p = np;
++ i++;
++ }
++}
++
+ void
+ csiparse(void)
+ {
+@@ -1153,6 +1178,7 @@ csiparse(void)
+ v = -1;
+ csiescseq.arg[csiescseq.narg++] = v;
+ p = np;
++ readcolonargs(&p, csiescseq.narg-1, csiescseq.carg);
+ if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
+ break;
+ p++;
+@@ -1369,6 +1395,10 @@ tsetattr(int *attr, int l)
+ ATTR_STRUCK );
+ term.c.attr.fg = defaultfg;
+ term.c.attr.bg = defaultbg;
++ term.c.attr.ustyle = -1;
++ term.c.attr.ucolor[0] = -1;
++ term.c.attr.ucolor[1] = -1;
++ term.c.attr.ucolor[2] = -1;
+ break;
+ case 1:
+ term.c.attr.mode |= ATTR_BOLD;
+@@ -1380,7 +1410,14 @@ tsetattr(int *attr, int l)
+ term.c.attr.mode |= ATTR_ITALIC;
+ break;
+ case 4:
+- term.c.attr.mode |= ATTR_UNDERLINE;
++ term.c.attr.ustyle = csiescseq.carg[i][0];
++
++ if (term.c.attr.ustyle != 0)
++ term.c.attr.mode |= ATTR_UNDERLINE;
++ else
++ term.c.attr.mode &= ~ATTR_UNDERLINE;
++
++ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
+ break;
+ case 5: /* slow blink */
+ /* FALLTHROUGH */
+@@ -1431,6 +1468,18 @@ tsetattr(int *attr, int l)
+ case 49:
+ term.c.attr.bg = defaultbg;
+ break;
++ case 58:
++ term.c.attr.ucolor[0] = csiescseq.carg[i][1];
++ term.c.attr.ucolor[1] = csiescseq.carg[i][2];
++ term.c.attr.ucolor[2] = csiescseq.carg[i][3];
++ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
++ break;
++ case 59:
++ term.c.attr.ucolor[0] = -1;
++ term.c.attr.ucolor[1] = -1;
++ term.c.attr.ucolor[2] = -1;
++ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
++ break;
+ default:
+ if (BETWEEN(attr[i], 30, 37)) {
+ term.c.attr.fg = attr[i] - 30;
+diff --git a/st.h b/st.h
+index 3d351b6..95bdcbd 100644
+--- a/st.h
++++ b/st.h
+@@ -34,6 +34,7 @@ enum glyph_attribute {
+ ATTR_WIDE = 1 << 9,
+ ATTR_WDUMMY = 1 << 10,
+ ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
++ ATTR_DIRTYUNDERLINE = 1 << 15,
+ };
+
+ enum selection_mode {
+@@ -65,6 +66,8 @@ typedef struct {
+ ushort mode; /* attribute flags */
+ uint32_t fg; /* foreground */
+ uint32_t bg; /* background */
++ int ustyle; /* underline style */
++ int ucolor[3]; /* underline color */
+ } Glyph;
+
+ typedef Glyph *Line;
+diff --git a/st.info b/st.info
+index 8201ad6..659878c 100644
+--- a/st.info
++++ b/st.info
+@@ -1,4 +1,5 @@
+ st-mono| simpleterm monocolor,
++ Su,
+ acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
+ am,
+ bce,
+diff --git a/x.c b/x.c
+index 210f184..3a0e79e 100644
+--- a/x.c
++++ b/x.c
+@@ -45,6 +45,14 @@ typedef struct {
+ signed char appcursor; /* application cursor */
+ } Key;
+
++/* Undercurl slope types */
++enum undercurl_slope_type {
++ UNDERCURL_SLOPE_ASCENDING = 0,
++ UNDERCURL_SLOPE_TOP_CAP = 1,
++ UNDERCURL_SLOPE_DESCENDING = 2,
++ UNDERCURL_SLOPE_BOTTOM_CAP = 3
++};
++
+ /* X modifiers */
+ #define XK_ANY_MOD UINT_MAX
+ #define XK_NO_MOD 0
+@@ -1339,6 +1347,51 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
+ return numspecs;
+ }
+
++static int isSlopeRising (int x, int iPoint, int waveWidth)
++{
++ // . . . .
++ // / \ / \ / \ / \
++ // / \ / \ / \ / \
++ // . . . . .
++
++ // Find absolute `x` of point
++ x += iPoint * (waveWidth/2);
++
++ // Find index of absolute wave
++ int absSlope = x / ((float)waveWidth/2);
++
++ return (absSlope % 2);
++}
++
++static int getSlope (int x, int iPoint, int waveWidth)
++{
++ // Sizes: Caps are half width of slopes
++ // 1_2 1_2 1_2 1_2
++ // / \ / \ / \ / \
++ // / \ / \ / \ / \
++ // 0 3_0 3_0 3_0 3_
++ // <2-> <1> <---6---->
++
++ // Find type of first point
++ int firstType;
++ x -= (x / waveWidth) * waveWidth;
++ if (x < (waveWidth * (2.f/6.f)))
++ firstType = UNDERCURL_SLOPE_ASCENDING;
++ else if (x < (waveWidth * (3.f/6.f)))
++ firstType = UNDERCURL_SLOPE_TOP_CAP;
++ else if (x < (waveWidth * (5.f/6.f)))
++ firstType = UNDERCURL_SLOPE_DESCENDING;
++ else
++ firstType = UNDERCURL_SLOPE_BOTTOM_CAP;
++
++ // Find type of given point
++ int pointType = (iPoint % 4);
++ pointType += firstType;
++ pointType %= 4;
++
++ return pointType;
++}
++
+ void
+ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
+ {
+@@ -1461,8 +1514,357 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+
+ /* Render underline and strikethrough. */
+ if (base.mode & ATTR_UNDERLINE) {
+- XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
+- width, 1);
++ // Underline Color
++ const int widthThreshold = 28; // +1 width every widthThreshold px of font
++ int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width
++ int linecolor;
++ if ((base.ucolor[0] >= 0) &&
++ !(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) &&
++ !(base.mode & ATTR_INVISIBLE)
++ ) {
++ // Special color for underline
++ // Index
++ if (base.ucolor[1] < 0) {
++ linecolor = dc.col[base.ucolor[0]].pixel;
++ }
++ // RGB
++ else {
++ XColor lcolor;
++ lcolor.red = base.ucolor[0] * 257;
++ lcolor.green = base.ucolor[1] * 257;
++ lcolor.blue = base.ucolor[2] * 257;
++ lcolor.flags = DoRed | DoGreen | DoBlue;
++ XAllocColor(xw.dpy, xw.cmap, &lcolor);
++ linecolor = lcolor.pixel;
++ }
++ } else {
++ // Foreground color for underline
++ linecolor = fg->pixel;
++ }
++
++ XGCValues ugcv = {
++ .foreground = linecolor,
++ .line_width = wlw,
++ .line_style = LineSolid,
++ .cap_style = CapNotLast
++ };
++
++ GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw),
++ GCForeground | GCLineWidth | GCLineStyle | GCCapStyle,
++ &ugcv);
++
++ // Underline Style
++ if (base.ustyle != 3) {
++ //XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1);
++ XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx,
++ winy + dc.font.ascent + 1, width, wlw);
++ } else if (base.ustyle == 3) {
++ int ww = win.cw;//width;
++ int wh = dc.font.descent - wlw/2 - 1;//r.height/7;
++ int wx = winx;
++ int wy = winy + win.ch - dc.font.descent;
++
++#if UNDERCURL_STYLE == UNDERCURL_CURLY
++ // Draw waves
++ int narcs = charlen * 2 + 1;
++ XArc *arcs = xmalloc(sizeof(XArc) * narcs);
++
++ int i = 0;
++ for (i = 0; i < charlen-1; i++) {
++ arcs[i*2] = (XArc) {
++ .x = wx + win.cw * i + ww / 4,
++ .y = wy,
++ .width = win.cw / 2,
++ .height = wh,
++ .angle1 = 0,
++ .angle2 = 180 * 64
++ };
++ arcs[i*2+1] = (XArc) {
++ .x = wx + win.cw * i + ww * 0.75,
++ .y = wy,
++ .width = win.cw/2,
++ .height = wh,
++ .angle1 = 180 * 64,
++ .angle2 = 180 * 64
++ };
++ }
++ // Last wave
++ arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh,
++ 0, 180 * 64 };
++ // Last wave tail
++ arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.),
++ wh, 180 * 64, 90 * 64};
++ // First wave tail
++ i++;
++ arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64,
++ 90 * 64 };
++
++ XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs);
++
++ free(arcs);
++#elif UNDERCURL_STYLE == UNDERCURL_SPIKY
++ // Make the underline corridor larger
++ /*
++ wy -= wh;
++ */
++ wh *= 2;
++
++ // Set the angle of the slope to 45°
++ ww = wh;
++
++ // Position of wave is independent of word, it's absolute
++ wx = (wx / (ww/2)) * (ww/2);
++
++ int marginStart = winx - wx;
++
++ // Calculate number of points with floating precision
++ float n = width; // Width of word in pixels
++ n = (n / ww) * 2; // Number of slopes (/ or \)
++ n += 2; // Add two last points
++ int npoints = n; // Convert to int
++
++ // Total length of underline
++ float waveLength = 0;
++
++ if (npoints >= 3) {
++ // We add an aditional slot in case we use a bonus point
++ XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
++
++ // First point (Starts with the word bounds)
++ points[0] = (XPoint) {
++ .x = wx + marginStart,
++ .y = (isSlopeRising(wx, 0, ww))
++ ? (wy - marginStart + ww/2.f)
++ : (wy + marginStart)
++ };
++
++ // Second point (Goes back to the absolute point coordinates)
++ points[1] = (XPoint) {
++ .x = (ww/2.f) - marginStart,
++ .y = (isSlopeRising(wx, 1, ww))
++ ? (ww/2.f - marginStart)
++ : (-ww/2.f + marginStart)
++ };
++ waveLength += (ww/2.f) - marginStart;
++
++ // The rest of the points
++ for (int i = 2; i < npoints-1; i++) {
++ points[i] = (XPoint) {
++ .x = ww/2,
++ .y = (isSlopeRising(wx, i, ww))
++ ? wh/2
++ : -wh/2
++ };
++ waveLength += ww/2;
++ }
++
++ // Last point
++ points[npoints-1] = (XPoint) {
++ .x = ww/2,
++ .y = (isSlopeRising(wx, npoints-1, ww))
++ ? wh/2
++ : -wh/2
++ };
++ waveLength += ww/2;
++
++ // End
++ if (waveLength < width) { // Add a bonus point?
++ int marginEnd = width - waveLength;
++ points[npoints] = (XPoint) {
++ .x = marginEnd,
++ .y = (isSlopeRising(wx, npoints, ww))
++ ? (marginEnd)
++ : (-marginEnd)
++ };
++
++ npoints++;
++ } else if (waveLength > width) { // Is last point too far?
++ int marginEnd = waveLength - width;
++ points[npoints-1].x -= marginEnd;
++ if (isSlopeRising(wx, npoints-1, ww))
++ points[npoints-1].y -= (marginEnd);
++ else
++ points[npoints-1].y += (marginEnd);
++ }
++
++ // Draw the lines
++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
++ CoordModePrevious);
++
++ // Draw a second underline with an offset of 1 pixel
++ if ( ((win.ch / (widthThreshold/2)) % 2)) {
++ points[0].x++;
++
++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
++ npoints, CoordModePrevious);
++ }
++
++ // Free resources
++ free(points);
++ }
++#else // UNDERCURL_CAPPED
++ // Cap is half of wave width
++ float capRatio = 0.5f;
++
++ // Make the underline corridor larger
++ wh *= 2;
++
++ // Set the angle of the slope to 45°
++ ww = wh;
++ ww *= 1 + capRatio; // Add a bit of width for the cap
++
++ // Position of wave is independent of word, it's absolute
++ wx = (wx / ww) * ww;
++
++ float marginStart;
++ switch(getSlope(winx, 0, ww)) {
++ case UNDERCURL_SLOPE_ASCENDING:
++ marginStart = winx - wx;
++ break;
++ case UNDERCURL_SLOPE_TOP_CAP:
++ marginStart = winx - (wx + (ww * (2.f/6.f)));
++ break;
++ case UNDERCURL_SLOPE_DESCENDING:
++ marginStart = winx - (wx + (ww * (3.f/6.f)));
++ break;
++ case UNDERCURL_SLOPE_BOTTOM_CAP:
++ marginStart = winx - (wx + (ww * (5.f/6.f)));
++ break;
++ }
++
++ // Calculate number of points with floating precision
++ float n = width; // Width of word in pixels
++ // ._.
++ n = (n / ww) * 4; // Number of points (./ \.)
++ n += 2; // Add two last points
++ int npoints = n; // Convert to int
++
++ // Position of the pen to draw the lines
++ float penX = 0;
++ float penY = 0;
++
++ if (npoints >= 3) {
++ XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
++
++ // First point (Starts with the word bounds)
++ penX = winx;
++ switch (getSlope(winx, 0, ww)) {
++ case UNDERCURL_SLOPE_ASCENDING:
++ penY = wy + wh/2.f - marginStart;
++ break;
++ case UNDERCURL_SLOPE_TOP_CAP:
++ penY = wy;
++ break;
++ case UNDERCURL_SLOPE_DESCENDING:
++ penY = wy + marginStart;
++ break;
++ case UNDERCURL_SLOPE_BOTTOM_CAP:
++ penY = wy + wh/2.f;
++ break;
++ }
++ points[0].x = penX;
++ points[0].y = penY;
++
++ // Second point (Goes back to the absolute point coordinates)
++ switch (getSlope(winx, 1, ww)) {
++ case UNDERCURL_SLOPE_ASCENDING:
++ penX += ww * (1.f/6.f) - marginStart;
++ penY += 0;
++ break;
++ case UNDERCURL_SLOPE_TOP_CAP:
++ penX += ww * (2.f/6.f) - marginStart;
++ penY += -wh/2.f + marginStart;
++ break;
++ case UNDERCURL_SLOPE_DESCENDING:
++ penX += ww * (1.f/6.f) - marginStart;
++ penY += 0;
++ break;
++ case UNDERCURL_SLOPE_BOTTOM_CAP:
++ penX += ww * (2.f/6.f) - marginStart;
++ penY += -marginStart + wh/2.f;
++ break;
++ }
++ points[1].x = penX;
++ points[1].y = penY;
++
++ // The rest of the points
++ for (int i = 2; i < npoints; i++) {
++ switch (getSlope(winx, i, ww)) {
++ case UNDERCURL_SLOPE_ASCENDING:
++ case UNDERCURL_SLOPE_DESCENDING:
++ penX += ww * (1.f/6.f);
++ penY += 0;
++ break;
++ case UNDERCURL_SLOPE_TOP_CAP:
++ penX += ww * (2.f/6.f);
++ penY += -wh / 2.f;
++ break;
++ case UNDERCURL_SLOPE_BOTTOM_CAP:
++ penX += ww * (2.f/6.f);
++ penY += wh / 2.f;
++ break;
++ }
++ points[i].x = penX;
++ points[i].y = penY;
++ }
++
++ // End
++ float waveLength = penX - winx;
++ if (waveLength < width) { // Add a bonus point?
++ int marginEnd = width - waveLength;
++ penX += marginEnd;
++ switch(getSlope(winx, npoints, ww)) {
++ case UNDERCURL_SLOPE_ASCENDING:
++ case UNDERCURL_SLOPE_DESCENDING:
++ //penY += 0;
++ break;
++ case UNDERCURL_SLOPE_TOP_CAP:
++ penY += -marginEnd;
++ break;
++ case UNDERCURL_SLOPE_BOTTOM_CAP:
++ penY += marginEnd;
++ break;
++ }
++
++ points[npoints].x = penX;
++ points[npoints].y = penY;
++
++ npoints++;
++ } else if (waveLength > width) { // Is last point too far?
++ int marginEnd = waveLength - width;
++ points[npoints-1].x -= marginEnd;
++ switch(getSlope(winx, npoints-1, ww)) {
++ case UNDERCURL_SLOPE_TOP_CAP:
++ points[npoints-1].y += marginEnd;
++ break;
++ case UNDERCURL_SLOPE_BOTTOM_CAP:
++ points[npoints-1].y -= marginEnd;
++ break;
++ default:
++ break;
++ }
++ }
++
++ // Draw the lines
++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
++ CoordModeOrigin);
++
++ // Draw a second underline with an offset of 1 pixel
++ if ( ((win.ch / (widthThreshold/2)) % 2)) {
++ for (int i = 0; i < npoints; i++)
++ points[i].x++;
++
++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
++ npoints, CoordModeOrigin);
++ }
++
++ // Free resources
++ free(points);
++ }
++#endif
++ }
++
++ XFreeGC(xw.dpy, ugc);
+ }
+
+ if (base.mode & ATTR_STRUCK) {
diff --git a/based-simple-term/patches/st-undercurl-0.8.4-20210822.diff_ b/based-simple-term/patches/st-undercurl-0.8.4-20210822.diff_
new file mode 100755
index 0000000..fab2ddc
--- /dev/null
+++ b/based-simple-term/patches/st-undercurl-0.8.4-20210822.diff_
@@ -0,0 +1,605 @@
+diff --git a/config.def.h b/config.def.h
+index 6f05dce..7ae1b92 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -470,3 +470,27 @@ static char ascii_printable[] =
+ " !\"#$%&'()*+,-./0123456789:;<=>?"
+ "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+ "`abcdefghijklmnopqrstuvwxyz{|}~";
++
++/**
++ * Undercurl style. Set UNDERCURL_STYLE to one of the available styles.
++ *
++ * Curly: Dunno how to draw it *shrug*
++ * _ _ _ _
++ * ( ) ( ) ( ) ( )
++ * (_) (_) (_) (_)
++ *
++ * Spiky:
++ * /\ /\ /\ /\
++ * \/ \/ \/
++ *
++ * Capped:
++ * _ _ _
++ * / \ / \ / \
++ * \_/ \_/
++ */
++// Available styles
++#define UNDERCURL_CURLY 0
++#define UNDERCURL_SPIKY 1
++#define UNDERCURL_CAPPED 2
++// Active style
++#define UNDERCURL_STYLE UNDERCURL_SPIKY
+diff --git a/st.c b/st.c
+index 76b7e0d..542ab3a 100644
+--- a/st.c
++++ b/st.c
+@@ -33,6 +33,7 @@
+ #define UTF_SIZ 4
+ #define ESC_BUF_SIZ (128*UTF_SIZ)
+ #define ESC_ARG_SIZ 16
++#define CAR_PER_ARG 4
+ #define STR_BUF_SIZ ESC_BUF_SIZ
+ #define STR_ARG_SIZ ESC_ARG_SIZ
+
+@@ -139,6 +140,7 @@ typedef struct {
+ int arg[ESC_ARG_SIZ];
+ int narg; /* nb of args */
+ char mode[2];
++ int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */
+ } CSIEscape;
+
+ /* STR Escape sequence structs */
+@@ -159,6 +161,7 @@ static void ttywriteraw(const char *, size_t);
+
+ static void csidump(void);
+ static void csihandle(void);
++static void readcolonargs(char **, int, int[][CAR_PER_ARG]);
+ static void csiparse(void);
+ static void csireset(void);
+ static int eschandle(uchar);
+@@ -1131,6 +1134,28 @@ tnewline(int first_col)
+ tmoveto(first_col ? 0 : term.c.x, y);
+ }
+
++void
++readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG])
++{
++ int i = 0;
++ for (; i < CAR_PER_ARG; i++)
++ params[cursor][i] = -1;
++
++ if (**p != ':')
++ return;
++
++ char *np = NULL;
++ i = 0;
++
++ while (**p == ':' && i < CAR_PER_ARG) {
++ while (**p == ':')
++ (*p)++;
++ params[cursor][i] = strtol(*p, &np, 10);
++ *p = np;
++ i++;
++ }
++}
++
+ void
+ csiparse(void)
+ {
+@@ -1153,6 +1178,7 @@ csiparse(void)
+ v = -1;
+ csiescseq.arg[csiescseq.narg++] = v;
+ p = np;
++ readcolonargs(&p, csiescseq.narg-1, csiescseq.carg);
+ if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
+ break;
+ p++;
+@@ -1369,6 +1395,10 @@ tsetattr(int *attr, int l)
+ ATTR_STRUCK );
+ term.c.attr.fg = defaultfg;
+ term.c.attr.bg = defaultbg;
++ term.c.attr.ustyle = -1;
++ term.c.attr.ucolor[0] = -1;
++ term.c.attr.ucolor[1] = -1;
++ term.c.attr.ucolor[2] = -1;
+ break;
+ case 1:
+ term.c.attr.mode |= ATTR_BOLD;
+@@ -1380,7 +1410,14 @@ tsetattr(int *attr, int l)
+ term.c.attr.mode |= ATTR_ITALIC;
+ break;
+ case 4:
+- term.c.attr.mode |= ATTR_UNDERLINE;
++ term.c.attr.ustyle = csiescseq.carg[i][0];
++
++ if (term.c.attr.ustyle != 0)
++ term.c.attr.mode |= ATTR_UNDERLINE;
++ else
++ term.c.attr.mode &= ~ATTR_UNDERLINE;
++
++ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
+ break;
+ case 5: /* slow blink */
+ /* FALLTHROUGH */
+@@ -1431,6 +1468,18 @@ tsetattr(int *attr, int l)
+ case 49:
+ term.c.attr.bg = defaultbg;
+ break;
++ case 58:
++ term.c.attr.ucolor[0] = csiescseq.carg[i][1];
++ term.c.attr.ucolor[1] = csiescseq.carg[i][2];
++ term.c.attr.ucolor[2] = csiescseq.carg[i][3];
++ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
++ break;
++ case 59:
++ term.c.attr.ucolor[0] = -1;
++ term.c.attr.ucolor[1] = -1;
++ term.c.attr.ucolor[2] = -1;
++ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
++ break;
+ default:
+ if (BETWEEN(attr[i], 30, 37)) {
+ term.c.attr.fg = attr[i] - 30;
+diff --git a/st.h b/st.h
+index 3d351b6..95bdcbd 100644
+--- a/st.h
++++ b/st.h
+@@ -34,6 +34,7 @@ enum glyph_attribute {
+ ATTR_WIDE = 1 << 9,
+ ATTR_WDUMMY = 1 << 10,
+ ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
++ ATTR_DIRTYUNDERLINE = 1 << 15,
+ };
+
+ enum selection_mode {
+@@ -65,6 +66,8 @@ typedef struct {
+ ushort mode; /* attribute flags */
+ uint32_t fg; /* foreground */
+ uint32_t bg; /* background */
++ int ustyle; /* underline style */
++ int ucolor[3]; /* underline color */
+ } Glyph;
+
+ typedef Glyph *Line;
+diff --git a/st.info b/st.info
+index 8201ad6..659878c 100644
+--- a/st.info
++++ b/st.info
+@@ -1,4 +1,5 @@
+ st-mono| simpleterm monocolor,
++ Su,
+ acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
+ am,
+ bce,
+diff --git a/x.c b/x.c
+index 210f184..3a0e79e 100644
+--- a/x.c
++++ b/x.c
+@@ -45,6 +45,14 @@ typedef struct {
+ signed char appcursor; /* application cursor */
+ } Key;
+
++/* Undercurl slope types */
++enum undercurl_slope_type {
++ UNDERCURL_SLOPE_ASCENDING = 0,
++ UNDERCURL_SLOPE_TOP_CAP = 1,
++ UNDERCURL_SLOPE_DESCENDING = 2,
++ UNDERCURL_SLOPE_BOTTOM_CAP = 3
++};
++
+ /* X modifiers */
+ #define XK_ANY_MOD UINT_MAX
+ #define XK_NO_MOD 0
+@@ -1339,6 +1347,51 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
+ return numspecs;
+ }
+
++static int isSlopeRising (int x, int iPoint, int waveWidth)
++{
++ // . . . .
++ // / \ / \ / \ / \
++ // / \ / \ / \ / \
++ // . . . . .
++
++ // Find absolute `x` of point
++ x += iPoint * (waveWidth/2);
++
++ // Find index of absolute wave
++ int absSlope = x / ((float)waveWidth/2);
++
++ return (absSlope % 2);
++}
++
++static int getSlope (int x, int iPoint, int waveWidth)
++{
++ // Sizes: Caps are half width of slopes
++ // 1_2 1_2 1_2 1_2
++ // / \ / \ / \ / \
++ // / \ / \ / \ / \
++ // 0 3_0 3_0 3_0 3_
++ // <2-> <1> <---6---->
++
++ // Find type of first point
++ int firstType;
++ x -= (x / waveWidth) * waveWidth;
++ if (x < (waveWidth * (2.f/6.f)))
++ firstType = UNDERCURL_SLOPE_ASCENDING;
++ else if (x < (waveWidth * (3.f/6.f)))
++ firstType = UNDERCURL_SLOPE_TOP_CAP;
++ else if (x < (waveWidth * (5.f/6.f)))
++ firstType = UNDERCURL_SLOPE_DESCENDING;
++ else
++ firstType = UNDERCURL_SLOPE_BOTTOM_CAP;
++
++ // Find type of given point
++ int pointType = (iPoint % 4);
++ pointType += firstType;
++ pointType %= 4;
++
++ return pointType;
++}
++
+ void
+ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
+ {
+@@ -1461,8 +1514,357 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+
+ /* Render underline and strikethrough. */
+ if (base.mode & ATTR_UNDERLINE) {
+- XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
+- width, 1);
++ // Underline Color
++ const int widthThreshold = 28; // +1 width every widthThreshold px of font
++ int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width
++ int linecolor;
++ if ((base.ucolor[0] >= 0) &&
++ !(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) &&
++ !(base.mode & ATTR_INVISIBLE)
++ ) {
++ // Special color for underline
++ // Index
++ if (base.ucolor[1] < 0) {
++ linecolor = dc.col[base.ucolor[0]].pixel;
++ }
++ // RGB
++ else {
++ XColor lcolor;
++ lcolor.red = base.ucolor[0] * 257;
++ lcolor.green = base.ucolor[1] * 257;
++ lcolor.blue = base.ucolor[2] * 257;
++ lcolor.flags = DoRed | DoGreen | DoBlue;
++ XAllocColor(xw.dpy, xw.cmap, &lcolor);
++ linecolor = lcolor.pixel;
++ }
++ } else {
++ // Foreground color for underline
++ linecolor = fg->pixel;
++ }
++
++ XGCValues ugcv = {
++ .foreground = linecolor,
++ .line_width = wlw,
++ .line_style = LineSolid,
++ .cap_style = CapNotLast
++ };
++
++ GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw),
++ GCForeground | GCLineWidth | GCLineStyle | GCCapStyle,
++ &ugcv);
++
++ // Underline Style
++ if (base.ustyle != 3) {
++ //XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1);
++ XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx,
++ winy + dc.font.ascent + 1, width, wlw);
++ } else if (base.ustyle == 3) {
++ int ww = win.cw;//width;
++ int wh = dc.font.descent - wlw/2 - 1;//r.height/7;
++ int wx = winx;
++ int wy = winy + win.ch - dc.font.descent;
++
++#if UNDERCURL_STYLE == UNDERCURL_CURLY
++ // Draw waves
++ int narcs = charlen * 2 + 1;
++ XArc *arcs = xmalloc(sizeof(XArc) * narcs);
++
++ int i = 0;
++ for (i = 0; i < charlen-1; i++) {
++ arcs[i*2] = (XArc) {
++ .x = wx + win.cw * i + ww / 4,
++ .y = wy,
++ .width = win.cw / 2,
++ .height = wh,
++ .angle1 = 0,
++ .angle2 = 180 * 64
++ };
++ arcs[i*2+1] = (XArc) {
++ .x = wx + win.cw * i + ww * 0.75,
++ .y = wy,
++ .width = win.cw/2,
++ .height = wh,
++ .angle1 = 180 * 64,
++ .angle2 = 180 * 64
++ };
++ }
++ // Last wave
++ arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh,
++ 0, 180 * 64 };
++ // Last wave tail
++ arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.),
++ wh, 180 * 64, 90 * 64};
++ // First wave tail
++ i++;
++ arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64,
++ 90 * 64 };
++
++ XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs);
++
++ free(arcs);
++#elif UNDERCURL_STYLE == UNDERCURL_SPIKY
++ // Make the underline corridor larger
++ /*
++ wy -= wh;
++ */
++ wh *= 2;
++
++ // Set the angle of the slope to 45°
++ ww = wh;
++
++ // Position of wave is independent of word, it's absolute
++ wx = (wx / (ww/2)) * (ww/2);
++
++ int marginStart = winx - wx;
++
++ // Calculate number of points with floating precision
++ float n = width; // Width of word in pixels
++ n = (n / ww) * 2; // Number of slopes (/ or \)
++ n += 2; // Add two last points
++ int npoints = n; // Convert to int
++
++ // Total length of underline
++ float waveLength = 0;
++
++ if (npoints >= 3) {
++ // We add an aditional slot in case we use a bonus point
++ XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
++
++ // First point (Starts with the word bounds)
++ points[0] = (XPoint) {
++ .x = wx + marginStart,
++ .y = (isSlopeRising(wx, 0, ww))
++ ? (wy - marginStart + ww/2.f)
++ : (wy + marginStart)
++ };
++
++ // Second point (Goes back to the absolute point coordinates)
++ points[1] = (XPoint) {
++ .x = (ww/2.f) - marginStart,
++ .y = (isSlopeRising(wx, 1, ww))
++ ? (ww/2.f - marginStart)
++ : (-ww/2.f + marginStart)
++ };
++ waveLength += (ww/2.f) - marginStart;
++
++ // The rest of the points
++ for (int i = 2; i < npoints-1; i++) {
++ points[i] = (XPoint) {
++ .x = ww/2,
++ .y = (isSlopeRising(wx, i, ww))
++ ? wh/2
++ : -wh/2
++ };
++ waveLength += ww/2;
++ }
++
++ // Last point
++ points[npoints-1] = (XPoint) {
++ .x = ww/2,
++ .y = (isSlopeRising(wx, npoints-1, ww))
++ ? wh/2
++ : -wh/2
++ };
++ waveLength += ww/2;
++
++ // End
++ if (waveLength < width) { // Add a bonus point?
++ int marginEnd = width - waveLength;
++ points[npoints] = (XPoint) {
++ .x = marginEnd,
++ .y = (isSlopeRising(wx, npoints, ww))
++ ? (marginEnd)
++ : (-marginEnd)
++ };
++
++ npoints++;
++ } else if (waveLength > width) { // Is last point too far?
++ int marginEnd = waveLength - width;
++ points[npoints-1].x -= marginEnd;
++ if (isSlopeRising(wx, npoints-1, ww))
++ points[npoints-1].y -= (marginEnd);
++ else
++ points[npoints-1].y += (marginEnd);
++ }
++
++ // Draw the lines
++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
++ CoordModePrevious);
++
++ // Draw a second underline with an offset of 1 pixel
++ if ( ((win.ch / (widthThreshold/2)) % 2)) {
++ points[0].x++;
++
++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
++ npoints, CoordModePrevious);
++ }
++
++ // Free resources
++ free(points);
++ }
++#else // UNDERCURL_CAPPED
++ // Cap is half of wave width
++ float capRatio = 0.5f;
++
++ // Make the underline corridor larger
++ wh *= 2;
++
++ // Set the angle of the slope to 45°
++ ww = wh;
++ ww *= 1 + capRatio; // Add a bit of width for the cap
++
++ // Position of wave is independent of word, it's absolute
++ wx = (wx / ww) * ww;
++
++ float marginStart;
++ switch(getSlope(winx, 0, ww)) {
++ case UNDERCURL_SLOPE_ASCENDING:
++ marginStart = winx - wx;
++ break;
++ case UNDERCURL_SLOPE_TOP_CAP:
++ marginStart = winx - (wx + (ww * (2.f/6.f)));
++ break;
++ case UNDERCURL_SLOPE_DESCENDING:
++ marginStart = winx - (wx + (ww * (3.f/6.f)));
++ break;
++ case UNDERCURL_SLOPE_BOTTOM_CAP:
++ marginStart = winx - (wx + (ww * (5.f/6.f)));
++ break;
++ }
++
++ // Calculate number of points with floating precision
++ float n = width; // Width of word in pixels
++ // ._.
++ n = (n / ww) * 4; // Number of points (./ \.)
++ n += 2; // Add two last points
++ int npoints = n; // Convert to int
++
++ // Position of the pen to draw the lines
++ float penX = 0;
++ float penY = 0;
++
++ if (npoints >= 3) {
++ XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
++
++ // First point (Starts with the word bounds)
++ penX = winx;
++ switch (getSlope(winx, 0, ww)) {
++ case UNDERCURL_SLOPE_ASCENDING:
++ penY = wy + wh/2.f - marginStart;
++ break;
++ case UNDERCURL_SLOPE_TOP_CAP:
++ penY = wy;
++ break;
++ case UNDERCURL_SLOPE_DESCENDING:
++ penY = wy + marginStart;
++ break;
++ case UNDERCURL_SLOPE_BOTTOM_CAP:
++ penY = wy + wh/2.f;
++ break;
++ }
++ points[0].x = penX;
++ points[0].y = penY;
++
++ // Second point (Goes back to the absolute point coordinates)
++ switch (getSlope(winx, 1, ww)) {
++ case UNDERCURL_SLOPE_ASCENDING:
++ penX += ww * (1.f/6.f) - marginStart;
++ penY += 0;
++ break;
++ case UNDERCURL_SLOPE_TOP_CAP:
++ penX += ww * (2.f/6.f) - marginStart;
++ penY += -wh/2.f + marginStart;
++ break;
++ case UNDERCURL_SLOPE_DESCENDING:
++ penX += ww * (1.f/6.f) - marginStart;
++ penY += 0;
++ break;
++ case UNDERCURL_SLOPE_BOTTOM_CAP:
++ penX += ww * (2.f/6.f) - marginStart;
++ penY += -marginStart + wh/2.f;
++ break;
++ }
++ points[1].x = penX;
++ points[1].y = penY;
++
++ // The rest of the points
++ for (int i = 2; i < npoints; i++) {
++ switch (getSlope(winx, i, ww)) {
++ case UNDERCURL_SLOPE_ASCENDING:
++ case UNDERCURL_SLOPE_DESCENDING:
++ penX += ww * (1.f/6.f);
++ penY += 0;
++ break;
++ case UNDERCURL_SLOPE_TOP_CAP:
++ penX += ww * (2.f/6.f);
++ penY += -wh / 2.f;
++ break;
++ case UNDERCURL_SLOPE_BOTTOM_CAP:
++ penX += ww * (2.f/6.f);
++ penY += wh / 2.f;
++ break;
++ }
++ points[i].x = penX;
++ points[i].y = penY;
++ }
++
++ // End
++ float waveLength = penX - winx;
++ if (waveLength < width) { // Add a bonus point?
++ int marginEnd = width - waveLength;
++ penX += marginEnd;
++ switch(getSlope(winx, npoints, ww)) {
++ case UNDERCURL_SLOPE_ASCENDING:
++ case UNDERCURL_SLOPE_DESCENDING:
++ //penY += 0;
++ break;
++ case UNDERCURL_SLOPE_TOP_CAP:
++ penY += -marginEnd;
++ break;
++ case UNDERCURL_SLOPE_BOTTOM_CAP:
++ penY += marginEnd;
++ break;
++ }
++
++ points[npoints].x = penX;
++ points[npoints].y = penY;
++
++ npoints++;
++ } else if (waveLength > width) { // Is last point too far?
++ int marginEnd = waveLength - width;
++ points[npoints-1].x -= marginEnd;
++ switch(getSlope(winx, npoints-1, ww)) {
++ case UNDERCURL_SLOPE_TOP_CAP:
++ points[npoints-1].y += marginEnd;
++ break;
++ case UNDERCURL_SLOPE_BOTTOM_CAP:
++ points[npoints-1].y -= marginEnd;
++ break;
++ default:
++ break;
++ }
++ }
++
++ // Draw the lines
++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
++ CoordModeOrigin);
++
++ // Draw a second underline with an offset of 1 pixel
++ if ( ((win.ch / (widthThreshold/2)) % 2)) {
++ for (int i = 0; i < npoints; i++)
++ points[i].x++;
++
++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
++ npoints, CoordModeOrigin);
++ }
++
++ // Free resources
++ free(points);
++ }
++#endif
++ }
++
++ XFreeGC(xw.dpy, ugc);
+ }
+
+ if (base.mode & ATTR_STRUCK) {
diff --git a/based-simple-term/patches/st-w3m-0.8.3.diff b/based-simple-term/patches/st-w3m-0.8.3.diff
new file mode 100755
index 0000000..a4e104b
--- /dev/null
+++ b/based-simple-term/patches/st-w3m-0.8.3.diff
@@ -0,0 +1,42 @@
+From 69cffc587b54b0a9cd81adb87abad8e526d5b25b Mon Sep 17 00:00:00 2001
+From: "Avi Halachmi (:avih)" <avihpit@yahoo.com>
+Date: Thu, 4 Jun 2020 17:35:08 +0300
+Subject: [PATCH] support w3m images
+
+w3m images are a hack which renders on top of the terminal's drawable,
+which didn't work in st because when using double buffering, the front
+buffer (on which w3m draws its images) is ignored, and st draws only
+on the back buffer, which is then copied to the front buffer.
+
+There's a patch to make it work at the FAQ already, but that patch
+canceles double-buffering, which can have negative side effects on
+some cases such as flickering.
+
+This patch achieves the same goal but instead of canceling the double
+buffer it first copies the front buffer to the back buffer.
+
+This has the same issues as the FAQ patch in that the cursor line is
+deleted at the image (because st renders always full lines), but
+otherwise it's simpler and does keeps double buffering.
+---
+ x.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/x.c b/x.c
+index e5f1737..b6ae162 100644
+--- a/x.c
++++ b/x.c
+@@ -1594,6 +1594,8 @@ xsettitle(char *p)
+ int
+ xstartdraw(void)
+ {
++ if (IS_SET(MODE_VISIBLE))
++ XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0);
+ return IS_SET(MODE_VISIBLE);
+ }
+
+
+base-commit: 43a395ae91f7d67ce694e65edeaa7bbc720dd027
+--
+2.17.1
+
diff --git a/based-simple-term/patches/st-w3m-0.8.3.diff_ b/based-simple-term/patches/st-w3m-0.8.3.diff_
new file mode 100755
index 0000000..a4e104b
--- /dev/null
+++ b/based-simple-term/patches/st-w3m-0.8.3.diff_
@@ -0,0 +1,42 @@
+From 69cffc587b54b0a9cd81adb87abad8e526d5b25b Mon Sep 17 00:00:00 2001
+From: "Avi Halachmi (:avih)" <avihpit@yahoo.com>
+Date: Thu, 4 Jun 2020 17:35:08 +0300
+Subject: [PATCH] support w3m images
+
+w3m images are a hack which renders on top of the terminal's drawable,
+which didn't work in st because when using double buffering, the front
+buffer (on which w3m draws its images) is ignored, and st draws only
+on the back buffer, which is then copied to the front buffer.
+
+There's a patch to make it work at the FAQ already, but that patch
+canceles double-buffering, which can have negative side effects on
+some cases such as flickering.
+
+This patch achieves the same goal but instead of canceling the double
+buffer it first copies the front buffer to the back buffer.
+
+This has the same issues as the FAQ patch in that the cursor line is
+deleted at the image (because st renders always full lines), but
+otherwise it's simpler and does keeps double buffering.
+---
+ x.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/x.c b/x.c
+index e5f1737..b6ae162 100644
+--- a/x.c
++++ b/x.c
+@@ -1594,6 +1594,8 @@ xsettitle(char *p)
+ int
+ xstartdraw(void)
+ {
++ if (IS_SET(MODE_VISIBLE))
++ XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0);
+ return IS_SET(MODE_VISIBLE);
+ }
+
+
+base-commit: 43a395ae91f7d67ce694e65edeaa7bbc720dd027
+--
+2.17.1
+