diff options
Diffstat (limited to 'based-simple-term/patches')
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 + |
