st-appsync-0.8.3.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       st-appsync-0.8.3.diff (7593B)
       ---
            1 From 97bdda00d211f989ee42c02a08e96b41800544f4 Mon Sep 17 00:00:00 2001
            2 From: "Avi Halachmi (:avih)" <avihpit@yahoo.com>
            3 Date: Sat, 18 Apr 2020 13:56:11 +0300
            4 Subject: [PATCH] application-sync: support Synchronized-Updates
            5 
            6 See https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec
            7 
            8 In a nutshell: allow an application to suspend drawing until it has
            9 completed some output - so that the terminal will not flicker/tear by
           10 rendering partial content. If the end-of-suspension sequence doesn't
           11 arrive, the terminal bails out after a timeout (default: 200 ms).
           12 
           13 The feature is supported and pioneered by iTerm2. There are probably
           14 very few other terminals or applications which support this feature
           15 currently.
           16 
           17 One notable application which does support it is tmux (master as of
           18 2020-04-18) - where cursor flicker is completely avoided when a pane
           19 has new content. E.g. run in one pane: `while :; do cat x.c; done'
           20 while the cursor is at another pane.
           21 
           22 The terminfo string `Sync' added to `st.info' is also a tmux extension
           23 which tmux detects automatically when `st.info` is installed.
           24 
           25 Notes:
           26 
           27 - Draw-suspension begins on BSU sequence (Begin-Synchronized-Update),
           28   and ends on ESU sequence (End-Synchronized-Update).
           29 
           30 - BSU, ESU are "\033P=1s\033\\", "\033P=2s\033\\" respectively (DCS).
           31 
           32 - SU doesn't support nesting - BSU begins or extends, ESU always ends.
           33 
           34 - ESU without BSU is ignored.
           35 
           36 - BSU after BSU extends (resets the timeout), so an application could
           37   send BSU in a loop and keep drawing suspended - exactly like it can
           38   not-draw anything in a loop. But as soon as it exits/aborted then
           39   drawing is resumed according to the timeout even without ESU.
           40 
           41 - This implementation focuses on ESU and doesn't really care about BSU
           42   in the sense that it tries hard to draw exactly once ESU arrives (if
           43   it's not too soon after the last draw - according to minlatency),
           44   and doesn't try to draw the content just up-to BSU. These two sides
           45   complement eachother - not-drawing on BSU increases the chance that
           46   ESU is not too soon after the last draw. This approach was chosen
           47   because the application's main focus is that ESU indicates to the
           48   terminal that the content is now ready - and that's when we try to
           49   draw.
           50 ---
           51  config.def.h |  6 ++++++
           52  st.c         | 48 ++++++++++++++++++++++++++++++++++++++++++++++--
           53  st.info      |  1 +
           54  x.c          | 22 +++++++++++++++++++---
           55  4 files changed, 72 insertions(+), 5 deletions(-)
           56 
           57 diff --git a/config.def.h b/config.def.h
           58 index fdbacfd..d44c28e 100644
           59 --- a/config.def.h
           60 +++ b/config.def.h
           61 @@ -52,6 +52,12 @@ int allowaltscreen = 1;
           62  static double minlatency = 8;
           63  static double maxlatency = 33;
           64  
           65 +/*
           66 + * Synchronized-Update timeout in ms
           67 + * https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec
           68 + */
           69 +static uint su_timeout = 200;
           70 +
           71  /*
           72   * blinking timeout (set to 0 to disable blinking) for the terminal blinking
           73   * attribute.
           74 diff --git a/st.c b/st.c
           75 index 0ce6ac2..d53b882 100644
           76 --- a/st.c
           77 +++ b/st.c
           78 @@ -232,6 +232,33 @@ static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
           79  static Rune utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,  0x800,  0x10000};
           80  static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
           81  
           82 +#include <time.h>
           83 +static int su = 0;
           84 +struct timespec sutv;
           85 +
           86 +static void
           87 +tsync_begin()
           88 +{
           89 +        clock_gettime(CLOCK_MONOTONIC, &sutv);
           90 +        su = 1;
           91 +}
           92 +
           93 +static void
           94 +tsync_end()
           95 +{
           96 +        su = 0;
           97 +}
           98 +
           99 +int
          100 +tinsync(uint timeout)
          101 +{
          102 +        struct timespec now;
          103 +        if (su && !clock_gettime(CLOCK_MONOTONIC, &now)
          104 +               && TIMEDIFF(now, sutv) >= timeout)
          105 +                su = 0;
          106 +        return su;
          107 +}
          108 +
          109  ssize_t
          110  xwrite(int fd, const char *s, size_t len)
          111  {
          112 @@ -818,6 +845,9 @@ ttynew(char *line, char *cmd, char *out, char **args)
          113          return cmdfd;
          114  }
          115  
          116 +static int twrite_aborted = 0;
          117 +int ttyread_pending() { return twrite_aborted; }
          118 +
          119  size_t
          120  ttyread(void)
          121  {
          122 @@ -826,7 +856,7 @@ ttyread(void)
          123          int ret, written;
          124  
          125          /* append read bytes to unprocessed bytes */
          126 -        ret = read(cmdfd, buf+buflen, LEN(buf)-buflen);
          127 +        ret = twrite_aborted ? 1 : read(cmdfd, buf+buflen, LEN(buf)-buflen);
          128  
          129          switch (ret) {
          130          case 0:
          131 @@ -834,7 +864,7 @@ ttyread(void)
          132          case -1:
          133                  die("couldn't read from shell: %s\n", strerror(errno));
          134          default:
          135 -                buflen += ret;
          136 +                buflen += twrite_aborted ? 0 : ret;
          137                  written = twrite(buf, buflen, 0);
          138                  buflen -= written;
          139                  /* keep any incomplete UTF-8 byte sequence for the next call */
          140 @@ -995,6 +1025,7 @@ tsetdirtattr(int attr)
          141  void
          142  tfulldirt(void)
          143  {
          144 +        tsync_end();
          145          tsetdirt(0, term.row-1);
          146  }
          147  
          148 @@ -1901,6 +1932,12 @@ strhandle(void)
          149                  return;
          150          case 'P': /* DCS -- Device Control String */
          151                  term.mode |= ESC_DCS;
          152 +                /* https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec */
          153 +                if (strstr(strescseq.buf, "=1s") == strescseq.buf)
          154 +                        tsync_begin(), term.mode &= ~ESC_DCS;  /* BSU */
          155 +                else if (strstr(strescseq.buf, "=2s") == strescseq.buf)
          156 +                        tsync_end(), term.mode &= ~ESC_DCS;  /* ESU */
          157 +                return;
          158          case '_': /* APC -- Application Program Command */
          159          case '^': /* PM -- Privacy Message */
          160                  return;
          161 @@ -2454,6 +2491,9 @@ twrite(const char *buf, int buflen, int show_ctrl)
          162          Rune u;
          163          int n;
          164  
          165 +        int su0 = su;
          166 +        twrite_aborted = 0;
          167 +
          168          for (n = 0; n < buflen; n += charsize) {
          169                  if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
          170                          /* process a complete utf8 char */
          171 @@ -2464,6 +2504,10 @@ twrite(const char *buf, int buflen, int show_ctrl)
          172                          u = buf[n] & 0xFF;
          173                          charsize = 1;
          174                  }
          175 +                if (su0 && !su) {
          176 +                        twrite_aborted = 1;
          177 +                        break;  // ESU - allow rendering before a new BSU
          178 +                }
          179                  if (show_ctrl && ISCONTROL(u)) {
          180                          if (u & 0x80) {
          181                                  u &= 0x7f;
          182 diff --git a/st.info b/st.info
          183 index e2abc98..0a781db 100644
          184 --- a/st.info
          185 +++ b/st.info
          186 @@ -188,6 +188,7 @@ st-mono| simpleterm monocolor,
          187          Ms=\E]52;%p1%s;%p2%s\007,
          188          Se=\E[2 q,
          189          Ss=\E[%p1%d q,
          190 +        Sync=\EP=%p1%ds\E\\,
          191  
          192  st| simpleterm,
          193          use=st-mono,
          194 diff --git a/x.c b/x.c
          195 index cbbd11f..38b08a8 100644
          196 --- a/x.c
          197 +++ b/x.c
          198 @@ -1861,6 +1861,9 @@ resize(XEvent *e)
          199          cresize(e->xconfigure.width, e->xconfigure.height);
          200  }
          201  
          202 +int tinsync(uint);
          203 +int ttyread_pending();
          204 +
          205  void
          206  run(void)
          207  {
          208 @@ -1895,7 +1898,7 @@ run(void)
          209                  FD_SET(ttyfd, &rfd);
          210                  FD_SET(xfd, &rfd);
          211  
          212 -                if (XPending(xw.dpy))
          213 +                if (XPending(xw.dpy) || ttyread_pending())
          214                          timeout = 0;  /* existing events might not set xfd */
          215  
          216                  seltv.tv_sec = timeout / 1E3;
          217 @@ -1909,7 +1912,8 @@ run(void)
          218                  }
          219                  clock_gettime(CLOCK_MONOTONIC, &now);
          220  
          221 -                if (FD_ISSET(ttyfd, &rfd))
          222 +                int ttyin = FD_ISSET(ttyfd, &rfd) || ttyread_pending();
          223 +                if (ttyin)
          224                          ttyread();
          225  
          226                  xev = 0;
          227 @@ -1933,7 +1937,7 @@ run(void)
          228                   * maximum latency intervals during `cat huge.txt`, and perfect
          229                   * sync with periodic updates from animations/key-repeats/etc.
          230                   */
          231 -                if (FD_ISSET(ttyfd, &rfd) || xev) {
          232 +                if (ttyin || xev) {
          233                          if (!drawing) {
          234                                  trigger = now;
          235                                  drawing = 1;
          236 @@ -1944,6 +1948,18 @@ run(void)
          237                                  continue;  /* we have time, try to find idle */
          238                  }
          239  
          240 +                if (tinsync(su_timeout)) {
          241 +                        /*
          242 +                         * on synchronized-update draw-suspension: don't reset
          243 +                         * drawing so that we draw ASAP once we can (just after
          244 +                         * ESU). it won't be too soon because we already can
          245 +                         * draw now but we skip. we set timeout > 0 to draw on
          246 +                         * SU-timeout even without new content.
          247 +                         */
          248 +                        timeout = minlatency;
          249 +                        continue;
          250 +                }
          251 +
          252                  /* idle detected or maxlatency exhausted -> draw */
          253                  timeout = -1;
          254                  if (blinktimeout && tattrset(ATTR_BLINK)) {
          255 
          256 base-commit: 43a395ae91f7d67ce694e65edeaa7bbc720dd027
          257 prerequisite-patch-id: d7d5e516bc74afe094ffbfc3edb19c11d49df4e7
          258 -- 
          259 2.17.1
          260