diff --git a/README.md b/README.md index 75e5115..7f5d266 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the ### Changelog: +2021-05-09 - Added the sync patch + 2021-05-08 - Added blinking cursor, delkey, undercurl,universcroll, desktopentry, netwmicon and osc_10_11_12_2 patches 2021-05-07 - Added xresources reload patch @@ -184,6 +186,11 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the - [spoiler](https://st.suckless.org/patches/spoiler/) - use inverted defaultbg/fg for selection when bg/fg are the same + - [sync](https://st.suckless.org/patches/sync/) + - adds synchronized-updates/application-sync support in st + - this has no effect except when an application uses the synchronized-update escape sequences + - with this patch nearly all cursor flicker is eliminated in tmux, and tmux detects it automatically via terminfo + - [themed-cursor](https://st.suckless.org/patches/themed_cursor/) - instead of a default X cursor, use the xterm cursor from your cursor theme diff --git a/config.def.h b/config.def.h index 84a639d..c6d37bd 100644 --- a/config.def.h +++ b/config.def.h @@ -74,6 +74,14 @@ int allowwindowops = 0; static double minlatency = 8; static double maxlatency = 33; +#if SYNC_PATCH +/* + * Synchronized-Update timeout in ms + * https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec + */ +static uint su_timeout = 200; +#endif // SYNC_PATCH + /* * blinking timeout (set to 0 to disable blinking) for the terminal blinking * attribute. diff --git a/patch/st_include.c b/patch/st_include.c index b33e975..4ef29f9 100644 --- a/patch/st_include.c +++ b/patch/st_include.c @@ -25,4 +25,7 @@ #endif #if SIXEL_PATCH #include "sixel_st.c" +#endif +#if SYNC_PATCH +#include "sync.c" #endif \ No newline at end of file diff --git a/patch/st_include.h b/patch/st_include.h index 3dc496d..93f46c0 100644 --- a/patch/st_include.h +++ b/patch/st_include.h @@ -25,4 +25,7 @@ #endif #if SIXEL_PATCH #include "sixel_st.h" +#endif +#if SYNC_PATCH +#include "sync.h" #endif \ No newline at end of file diff --git a/patch/sync.c b/patch/sync.c new file mode 100644 index 0000000..a0a815c --- /dev/null +++ b/patch/sync.c @@ -0,0 +1,31 @@ +#include +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; +} + +int +ttyread_pending() +{ + return twrite_aborted; +} \ No newline at end of file diff --git a/patch/sync.h b/patch/sync.h new file mode 100644 index 0000000..6c2ebe7 --- /dev/null +++ b/patch/sync.h @@ -0,0 +1,7 @@ +static int su = 0; +static int twrite_aborted = 0; + +static void tsync_begin(); +static void tsync_end(); +int tinsync(uint timeout); +int ttyread_pending(); \ No newline at end of file diff --git a/patches.def.h b/patches.def.h index 7336e96..e2b2a05 100644 --- a/patches.def.h +++ b/patches.def.h @@ -271,6 +271,19 @@ */ #define SPOILER_PATCH 0 +/* This patch adds synchronized-updates/application-sync support in st. + * This will have no effect except when an application uses the synchronized-update escape + * sequences. With this patch nearly all cursor flicker is eliminated in tmux, and tmux detects + * it automatically via terminfo. + * + * Note: this patch alters st.info to promote support for extra escape sequences, which can + * potentially cause application misbehaviour if you do not use this patch. Try removing or + * commenting out the corresponding line in st.info if this is causing issues. + * + * https://st.suckless.org/patches/sync/ + */ +#define SYNC_PATCH 0 + /* Instead of a default X cursor, use the xterm cursor from your cursor theme. * You need to uncomment the corresponding line in config.mk to use the -lXcursor library * when including this patch. @@ -288,6 +301,10 @@ * | \- sets underline style to curvy * \- set underline * + * Note: this patch alters st.info to promote support for extra escape sequences, which can + * potentially cause application misbehaviour if you do not use this patch. Try removing or + * commenting out the corresponding line in st.info if this is causing issues. + * * https://st.suckless.org/patches/undercurl/ */ #define UNDERCURL_PATCH 0 diff --git a/st.c b/st.c index 1caa949..0f5599e 100644 --- a/st.c +++ b/st.c @@ -904,7 +904,11 @@ ttyread(void) int ret, written; /* append read bytes to unprocessed bytes */ + #if SYNC_PATCH + ret = twrite_aborted ? 1 : read(cmdfd, buf+buflen, LEN(buf)-buflen); + #else ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); + #endif // SYNC_PATCH switch (ret) { case 0: @@ -912,7 +916,11 @@ ttyread(void) case -1: die("couldn't read from shell: %s\n", strerror(errno)); default: + #if SYNC_PATCH + buflen += twrite_aborted ? 0 : ret; + #else buflen += ret; + #endif // SYNC_PATCH written = twrite(buf, buflen, 0); buflen -= written; /* keep any incomplete UTF-8 byte sequence for the next call */ @@ -1077,6 +1085,9 @@ tsetdirtattr(int attr) void tfulldirt(void) { + #if SYNC_PATCH + tsync_end(); + #endif // SYNC_PATCH tsetdirt(0, term.row-1); } @@ -2241,8 +2252,17 @@ strhandle(void) tnewline(1); } } - return; #endif // SIXEL_PATCH + #if SYNC_PATCH + /* 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 */ + #endif // SYNC_PATCH + #if SIXEL_PATCH || SYNC_PATCH + return; + #endif // SIXEL_PATCH | SYNC_PATCH case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ return; @@ -2834,6 +2854,11 @@ twrite(const char *buf, int buflen, int show_ctrl) Rune u; int n; + #if SYNC_PATCH + int su0 = su; + twrite_aborted = 0; + #endif // SYNC_PATCH + for (n = 0; n < buflen; n += charsize) { #if SIXEL_PATCH if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) @@ -2849,6 +2874,12 @@ twrite(const char *buf, int buflen, int show_ctrl) u = buf[n] & 0xFF; charsize = 1; } + #if SYNC_PATCH + if (su0 && !su) { + twrite_aborted = 1; + break; // ESU - allow rendering before a new BSU + } + #endif // SYNC_PATCH if (show_ctrl && ISCONTROL(u)) { if (u & 0x80) { u &= 0x7f; diff --git a/st.info b/st.info index 659878c..a45d8de 100644 --- a/st.info +++ b/st.info @@ -1,4 +1,5 @@ st-mono| simpleterm monocolor, +# undercurl patch / UNDERCURL_PATCH Su, acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, am, @@ -192,6 +193,8 @@ st-mono| simpleterm monocolor, Ms=\E]52;%p1%s;%p2%s\007, Se=\E[2 q, Ss=\E[%p1%d q, +# sync patch / SYNC_PATCH + Sync=\EP=%p1%ds\E\\, st| simpleterm, use=st-mono, diff --git a/x.c b/x.c index c8fffe0..b0b7709 100644 --- a/x.c +++ b/x.c @@ -2539,7 +2539,11 @@ run(void) FD_SET(ttyfd, &rfd); FD_SET(xfd, &rfd); + #if SYNC_PATCH + if (XPending(xw.dpy) || ttyread_pending()) + #else if (XPending(xw.dpy)) + #endif // SYNC_PATCH timeout = 0; /* existing events might not set xfd */ seltv.tv_sec = timeout / 1E3; @@ -2553,8 +2557,14 @@ run(void) } clock_gettime(CLOCK_MONOTONIC, &now); + #if SYNC_PATCH + int ttyin = FD_ISSET(ttyfd, &rfd) || ttyread_pending(); + if (ttyin) + ttyread(); + #else if (FD_ISSET(ttyfd, &rfd)) ttyread(); + #endif // SYNC_PATCH xev = 0; while (XPending(xw.dpy)) { @@ -2577,7 +2587,12 @@ 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 SYNC_PATCH + if (ttyin || xev) + #else + if (FD_ISSET(ttyfd, &rfd) || xev) + #endif // SYNC_PATCH + { if (!drawing) { trigger = now; #if BLINKING_CURSOR_PATCH @@ -2594,6 +2609,20 @@ run(void) continue; /* we have time, try to find idle */ } + #if SYNC_PATCH + 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; + } + #endif // SYNC_PATCH + /* idle detected or maxlatency exhausted -> draw */ timeout = -1; #if BLINKING_CURSOR_PATCH