summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--config.def.h11
-rw-r--r--x.c120
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 */
44int allowaltscreen = 1; 44int allowaltscreen = 1;
45 45
46/* frames per second st should at maximum draw to the screen */ 46/*
47static unsigned int xfps = 120; 47 * draw latency range in ms - from new content/keypress/etc until drawing.
48static 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 */
52static double minlatency = 8;
53static 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
diff --git a/x.c b/x.c
index e5f1737..cbbd11f 100644
--- a/x.c
+++ b/x.c
@@ -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