diff options
| -rw-r--r-- | config.def.h | 11 | ||||
| -rw-r--r-- | x.c | 120 |
2 files changed, 65 insertions, 66 deletions
diff --git a/config.def.h b/config.def.h index 0895a1f..fdbacfd 100644 --- a/config.def.h +++ b/config.def.h | |||
| @@ -43,9 +43,14 @@ static unsigned int tripleclicktimeout = 600; | |||
| 43 | /* alt screens */ | 43 | /* alt screens */ |
| 44 | int allowaltscreen = 1; | 44 | int allowaltscreen = 1; |
| 45 | 45 | ||
| 46 | /* frames per second st should at maximum draw to the screen */ | 46 | /* |
| 47 | static unsigned int xfps = 120; | 47 | * draw latency range in ms - from new content/keypress/etc until drawing. |
| 48 | static unsigned int actionfps = 30; | 48 | * within this range, st draws when content stops arriving (idle). mostly it's |
| 49 | * near minlatency, but it waits longer for slow updates to avoid partial draw. | ||
| 50 | * low minlatency will tear/flicker more, as it can "detect" idle too early. | ||
| 51 | */ | ||
| 52 | static double minlatency = 8; | ||
| 53 | static double maxlatency = 33; | ||
| 49 | 54 | ||
| 50 | /* | 55 | /* |
| 51 | * blinking timeout (set to 0 to disable blinking) for the terminal blinking | 56 | * blinking timeout (set to 0 to disable blinking) for the terminal blinking |
| @@ -1867,10 +1867,9 @@ run(void) | |||
| 1867 | XEvent ev; | 1867 | XEvent ev; |
| 1868 | int w = win.w, h = win.h; | 1868 | int w = win.w, h = win.h; |
| 1869 | fd_set rfd; | 1869 | fd_set rfd; |
| 1870 | int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0; | 1870 | int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; |
| 1871 | int ttyfd; | 1871 | struct timespec seltv, *tv, now, lastblink, trigger; |
| 1872 | struct timespec drawtimeout, *tv = NULL, now, last, lastblink; | 1872 | double timeout; |
| 1873 | long deltatime; | ||
| 1874 | 1873 | ||
| 1875 | /* Waiting for window mapping */ | 1874 | /* Waiting for window mapping */ |
| 1876 | do { | 1875 | do { |
| @@ -1891,82 +1890,77 @@ run(void) | |||
| 1891 | ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); | 1890 | ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); |
| 1892 | cresize(w, h); | 1891 | cresize(w, h); |
| 1893 | 1892 | ||
| 1894 | clock_gettime(CLOCK_MONOTONIC, &last); | 1893 | for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { |
| 1895 | lastblink = last; | ||
| 1896 | |||
| 1897 | for (xev = actionfps;;) { | ||
| 1898 | FD_ZERO(&rfd); | 1894 | FD_ZERO(&rfd); |
| 1899 | FD_SET(ttyfd, &rfd); | 1895 | FD_SET(ttyfd, &rfd); |
| 1900 | FD_SET(xfd, &rfd); | 1896 | FD_SET(xfd, &rfd); |
| 1901 | 1897 | ||
| 1898 | if (XPending(xw.dpy)) | ||
| 1899 | timeout = 0; /* existing events might not set xfd */ | ||
| 1900 | |||
| 1901 | seltv.tv_sec = timeout / 1E3; | ||
| 1902 | seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); | ||
| 1903 | tv = timeout >= 0 ? &seltv : NULL; | ||
| 1904 | |||
| 1902 | if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { | 1905 | if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { |
| 1903 | if (errno == EINTR) | 1906 | if (errno == EINTR) |
| 1904 | continue; | 1907 | continue; |
| 1905 | die("select failed: %s\n", strerror(errno)); | 1908 | die("select failed: %s\n", strerror(errno)); |
| 1906 | } | 1909 | } |
| 1907 | if (FD_ISSET(ttyfd, &rfd)) { | 1910 | clock_gettime(CLOCK_MONOTONIC, &now); |
| 1908 | ttyread(); | ||
| 1909 | if (blinktimeout) { | ||
| 1910 | blinkset = tattrset(ATTR_BLINK); | ||
| 1911 | if (!blinkset) | ||
| 1912 | MODBIT(win.mode, 0, MODE_BLINK); | ||
| 1913 | } | ||
| 1914 | } | ||
| 1915 | 1911 | ||
| 1916 | if (FD_ISSET(xfd, &rfd)) | 1912 | if (FD_ISSET(ttyfd, &rfd)) |
| 1917 | xev = actionfps; | 1913 | ttyread(); |
| 1918 | 1914 | ||
| 1919 | clock_gettime(CLOCK_MONOTONIC, &now); | 1915 | xev = 0; |
| 1920 | drawtimeout.tv_sec = 0; | 1916 | while (XPending(xw.dpy)) { |
| 1921 | drawtimeout.tv_nsec = (1000 * 1E6)/ xfps; | 1917 | xev = 1; |
| 1922 | tv = &drawtimeout; | 1918 | XNextEvent(xw.dpy, &ev); |
| 1923 | 1919 | if (XFilterEvent(&ev, None)) | |
| 1924 | dodraw = 0; | 1920 | continue; |
| 1925 | if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) { | 1921 | if (handler[ev.type]) |
| 1926 | tsetdirtattr(ATTR_BLINK); | 1922 | (handler[ev.type])(&ev); |
| 1927 | win.mode ^= MODE_BLINK; | ||
| 1928 | lastblink = now; | ||
| 1929 | dodraw = 1; | ||
| 1930 | } | ||
| 1931 | deltatime = TIMEDIFF(now, last); | ||
| 1932 | if (deltatime > 1000 / (xev ? xfps : actionfps)) { | ||
| 1933 | dodraw = 1; | ||
| 1934 | last = now; | ||
| 1935 | } | 1923 | } |
| 1936 | 1924 | ||
| 1937 | if (dodraw) { | 1925 | /* |
| 1938 | while (XPending(xw.dpy)) { | 1926 | * To reduce flicker and tearing, when new content or event |
| 1939 | XNextEvent(xw.dpy, &ev); | 1927 | * triggers drawing, we first wait a bit to ensure we got |
| 1940 | if (XFilterEvent(&ev, None)) | 1928 | * everything, and if nothing new arrives - we draw. |
| 1941 | continue; | 1929 | * We start with trying to wait minlatency ms. If more content |
| 1942 | if (handler[ev.type]) | 1930 | * arrives sooner, we retry with shorter and shorter preiods, |
| 1943 | (handler[ev.type])(&ev); | 1931 | * and eventually draw even without idle after maxlatency ms. |
| 1932 | * Typically this results in low latency while interacting, | ||
| 1933 | * maximum latency intervals during `cat huge.txt`, and perfect | ||
| 1934 | * sync with periodic updates from animations/key-repeats/etc. | ||
| 1935 | */ | ||
| 1936 | if (FD_ISSET(ttyfd, &rfd) || xev) { | ||
| 1937 | if (!drawing) { | ||
| 1938 | trigger = now; | ||
| 1939 | drawing = 1; | ||
| 1944 | } | 1940 | } |
| 1941 | timeout = (maxlatency - TIMEDIFF(now, trigger)) \ | ||
| 1942 | / maxlatency * minlatency; | ||
| 1943 | if (timeout > 0) | ||
| 1944 | continue; /* we have time, try to find idle */ | ||
| 1945 | } | ||
| 1945 | 1946 | ||
| 1946 | draw(); | 1947 | /* idle detected or maxlatency exhausted -> draw */ |
| 1947 | XFlush(xw.dpy); | 1948 | timeout = -1; |
| 1948 | 1949 | if (blinktimeout && tattrset(ATTR_BLINK)) { | |
| 1949 | if (xev && !FD_ISSET(xfd, &rfd)) | 1950 | timeout = blinktimeout - TIMEDIFF(now, lastblink); |
| 1950 | xev--; | 1951 | if (timeout <= 0) { |
| 1951 | if (!FD_ISSET(ttyfd, &rfd) && !FD_ISSET(xfd, &rfd)) { | 1952 | if (-timeout > blinktimeout) /* start visible */ |
| 1952 | if (blinkset) { | 1953 | win.mode |= MODE_BLINK; |
| 1953 | if (TIMEDIFF(now, lastblink) \ | 1954 | win.mode ^= MODE_BLINK; |
| 1954 | > blinktimeout) { | 1955 | tsetdirtattr(ATTR_BLINK); |
| 1955 | drawtimeout.tv_nsec = 1000; | 1956 | lastblink = now; |
| 1956 | } else { | 1957 | timeout = blinktimeout; |
| 1957 | drawtimeout.tv_nsec = (1E6 * \ | ||
| 1958 | (blinktimeout - \ | ||
| 1959 | TIMEDIFF(now, | ||
| 1960 | lastblink))); | ||
| 1961 | } | ||
| 1962 | drawtimeout.tv_sec = \ | ||
| 1963 | drawtimeout.tv_nsec / 1E9; | ||
| 1964 | drawtimeout.tv_nsec %= (long)1E9; | ||
| 1965 | } else { | ||
| 1966 | tv = NULL; | ||
| 1967 | } | ||
| 1968 | } | 1958 | } |
| 1969 | } | 1959 | } |
| 1960 | |||
| 1961 | draw(); | ||
| 1962 | XFlush(xw.dpy); | ||
| 1963 | drawing = 0; | ||
| 1970 | } | 1964 | } |
| 1971 | } | 1965 | } |
| 1972 | 1966 | ||
