auto-sync patch

This commit is contained in:
Luke Smith 2020-05-06 13:58:09 -04:00
parent b35e4f5727
commit 4f44d2d13f
No known key found for this signature in database
GPG key ID: 4C50B54A911F6252
2 changed files with 65 additions and 68 deletions

View file

@ -42,9 +42,14 @@ static unsigned int tripleclicktimeout = 600;
/* alt screens */ /* alt screens */
int allowaltscreen = 1; int allowaltscreen = 1;
/* frames per second st should at maximum draw to the screen */ /*
static unsigned int xfps = 120; * draw latency range in ms - from new content/keypress/etc until drawing.
static unsigned int actionfps = 30; * within this range, st draws when content stops arriving (idle). mostly it's
* near minlatency, but it waits longer for slow updates to avoid partial draw.
* low minlatency will tear/flicker more, as it can "detect" idle too early.
*/
static double minlatency = 8;
static double maxlatency = 33;
/* /*
* blinking timeout (set to 0 to disable blinking) for the terminal blinking * blinking timeout (set to 0 to disable blinking) for the terminal blinking
@ -197,8 +202,6 @@ ResourcePref resources[] = {
{ "cursorColor", STRING, &colorname[256] }, { "cursorColor", STRING, &colorname[256] },
{ "termname", STRING, &termname }, { "termname", STRING, &termname },
{ "shell", STRING, &shell }, { "shell", STRING, &shell },
{ "xfps", INTEGER, &xfps },
{ "actionfps", INTEGER, &actionfps },
{ "blinktimeout", INTEGER, &blinktimeout }, { "blinktimeout", INTEGER, &blinktimeout },
{ "bellvolume", INTEGER, &bellvolume }, { "bellvolume", INTEGER, &bellvolume },
{ "tabspaces", INTEGER, &tabspaces }, { "tabspaces", INTEGER, &tabspaces },

120
x.c
View file

@ -1956,10 +1956,9 @@ run(void)
XEvent ev; XEvent ev;
int w = win.w, h = win.h; int w = win.w, h = win.h;
fd_set rfd; fd_set rfd;
int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0; int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing;
int ttyfd; struct timespec seltv, *tv, now, lastblink, trigger;
struct timespec drawtimeout, *tv = NULL, now, last, lastblink; double timeout;
long deltatime;
/* Waiting for window mapping */ /* Waiting for window mapping */
do { do {
@ -1980,82 +1979,77 @@ run(void)
ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd);
cresize(w, h); cresize(w, h);
clock_gettime(CLOCK_MONOTONIC, &last); for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) {
lastblink = last;
for (xev = actionfps;;) {
FD_ZERO(&rfd); FD_ZERO(&rfd);
FD_SET(ttyfd, &rfd); FD_SET(ttyfd, &rfd);
FD_SET(xfd, &rfd); FD_SET(xfd, &rfd);
if (XPending(xw.dpy))
timeout = 0; /* existing events might not set xfd */
seltv.tv_sec = timeout / 1E3;
seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec);
tv = timeout >= 0 ? &seltv : NULL;
if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) {
if (errno == EINTR) if (errno == EINTR)
continue; continue;
die("select failed: %s\n", strerror(errno)); die("select failed: %s\n", strerror(errno));
} }
if (FD_ISSET(ttyfd, &rfd)) {
ttyread();
if (blinktimeout) {
blinkset = tattrset(ATTR_BLINK);
if (!blinkset)
MODBIT(win.mode, 0, MODE_BLINK);
}
}
if (FD_ISSET(xfd, &rfd))
xev = actionfps;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
drawtimeout.tv_sec = 0;
drawtimeout.tv_nsec = (1000 * 1E6)/ xfps;
tv = &drawtimeout;
dodraw = 0; if (FD_ISSET(ttyfd, &rfd))
if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) { ttyread();
tsetdirtattr(ATTR_BLINK);
win.mode ^= MODE_BLINK; xev = 0;
lastblink = now; while (XPending(xw.dpy)) {
dodraw = 1; xev = 1;
} XNextEvent(xw.dpy, &ev);
deltatime = TIMEDIFF(now, last); if (XFilterEvent(&ev, None))
if (deltatime > 1000 / (xev ? xfps : actionfps)) { continue;
dodraw = 1; if (handler[ev.type])
last = now; (handler[ev.type])(&ev);
} }
if (dodraw) { /*
while (XPending(xw.dpy)) { * To reduce flicker and tearing, when new content or event
XNextEvent(xw.dpy, &ev); * triggers drawing, we first wait a bit to ensure we got
if (XFilterEvent(&ev, None)) * everything, and if nothing new arrives - we draw.
continue; * We start with trying to wait minlatency ms. If more content
if (handler[ev.type]) * arrives sooner, we retry with shorter and shorter preiods,
(handler[ev.type])(&ev); * and eventually draw even without idle after maxlatency ms.
* Typically this results in low latency while interacting,
* 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 (!drawing) {
trigger = now;
drawing = 1;
} }
timeout = (maxlatency - TIMEDIFF(now, trigger)) \
/ maxlatency * minlatency;
if (timeout > 0)
continue; /* we have time, try to find idle */
}
draw(); /* idle detected or maxlatency exhausted -> draw */
XFlush(xw.dpy); timeout = -1;
if (blinktimeout && tattrset(ATTR_BLINK)) {
if (xev && !FD_ISSET(xfd, &rfd)) timeout = blinktimeout - TIMEDIFF(now, lastblink);
xev--; if (timeout <= 0) {
if (!FD_ISSET(ttyfd, &rfd) && !FD_ISSET(xfd, &rfd)) { if (-timeout > blinktimeout) /* start visible */
if (blinkset) { win.mode |= MODE_BLINK;
if (TIMEDIFF(now, lastblink) \ win.mode ^= MODE_BLINK;
> blinktimeout) { tsetdirtattr(ATTR_BLINK);
drawtimeout.tv_nsec = 1000; lastblink = now;
} else { timeout = blinktimeout;
drawtimeout.tv_nsec = (1E6 * \
(blinktimeout - \
TIMEDIFF(now,
lastblink)));
}
drawtimeout.tv_sec = \
drawtimeout.tv_nsec / 1E9;
drawtimeout.tv_nsec %= (long)1E9;
} else {
tv = NULL;
}
} }
} }
draw();
XFlush(xw.dpy);
drawing = 0;
} }
} }