st-autosync-0.8.3.diff - sites - public wiki contents of suckless.org | |
git clone git://git.suckless.org/sites | |
Log | |
Files | |
Refs | |
--- | |
st-autosync-0.8.3.diff (7328B) | |
--- | |
1 From 1892290c3b0ef064083c8af4e4bec443a36ca5c8 Mon Sep 17 00:00:00 2001 | |
2 From: "Avi Halachmi (:avih)" <[email protected]> | |
3 Date: Tue, 26 Feb 2019 22:37:49 +0200 | |
4 Subject: [PATCH] auto-sync: draw on idle to avoid flicker/tearing | |
5 | |
6 st could easily tear/flicker with animation or other unattended | |
7 output. This commit eliminates most of the tear/flicker. | |
8 | |
9 Before this commit, the display timing had two "modes": | |
10 | |
11 - Interactively, st was waiting fixed `1000/xfps` ms after forwarding | |
12 the kb/mouse event to the application and before drawing. | |
13 | |
14 - Unattended, and specifically with animations, the draw frequency was | |
15 throttled to `actionfps`. Animation at a higher rate would throttle | |
16 and likely tear, and at lower rates it was tearing big frames | |
17 (specifically, when one `read` didn't get a full "frame"). | |
18 | |
19 The interactive behavior was decent, but it was impossible to get good | |
20 unattended-draw behavior even with carefully chosen configuration. | |
21 | |
22 This commit changes the behavior such that it draws on idle instead of | |
23 using fixed latency/frequency. This means that it tries to draw only | |
24 when it's very likely that the application has completed its output | |
25 (or after some duration without idle), so it mostly succeeds to avoid | |
26 tear, flicker, and partial drawing. | |
27 | |
28 The config values minlatency/maxlatency replace xfps/actionfps and | |
29 define the range which the algorithm is allowed to wait from the | |
30 initial draw-trigger until the actual draw. The range enables the | |
31 flexibility to choose when to draw - when least likely to flicker. | |
32 | |
33 It also unifies the interactive and unattended behavior and config | |
34 values, which makes the code simpler as well - without sacrificing | |
35 latency during interactive use, because typically interactively idle | |
36 arrives very quickly, so the wait is typically minlatency. | |
37 | |
38 While it only slighly improves interactive behavior, for animations | |
39 and other unattended-drawing it improves greatly, as it effectively | |
40 adapts to any [animation] output rate without tearing, throttling, | |
41 redundant drawing, or unnecessary delays (sounds impossible, but it | |
42 works). | |
43 --- | |
44 config.def.h | 11 +++-- | |
45 x.c | 120 ++++++++++++++++++++++++--------------------------- | |
46 2 files changed, 65 insertions(+), 66 deletions(-) | |
47 | |
48 diff --git a/config.def.h b/config.def.h | |
49 index 0895a1f..fdbacfd 100644 | |
50 --- a/config.def.h | |
51 +++ b/config.def.h | |
52 @@ -43,9 +43,14 @@ static unsigned int tripleclicktimeout = 600; | |
53 /* alt screens */ | |
54 int allowaltscreen = 1; | |
55 | |
56 -/* frames per second st should at maximum draw to the screen */ | |
57 -static unsigned int xfps = 120; | |
58 -static unsigned int actionfps = 30; | |
59 +/* | |
60 + * draw latency range in ms - from new content/keypress/etc until drawi… | |
61 + * within this range, st draws when content stops arriving (idle). most… | |
62 + * near minlatency, but it waits longer for slow updates to avoid parti… | |
63 + * low minlatency will tear/flicker more, as it can "detect" idle too e… | |
64 + */ | |
65 +static double minlatency = 8; | |
66 +static double maxlatency = 33; | |
67 | |
68 /* | |
69 * blinking timeout (set to 0 to disable blinking) for the terminal bli… | |
70 diff --git a/x.c b/x.c | |
71 index e5f1737..cbbd11f 100644 | |
72 --- a/x.c | |
73 +++ b/x.c | |
74 @@ -1867,10 +1867,9 @@ run(void) | |
75 XEvent ev; | |
76 int w = win.w, h = win.h; | |
77 fd_set rfd; | |
78 - int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw … | |
79 - int ttyfd; | |
80 - struct timespec drawtimeout, *tv = NULL, now, last, lastblink; | |
81 - long deltatime; | |
82 + int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; | |
83 + struct timespec seltv, *tv, now, lastblink, trigger; | |
84 + double timeout; | |
85 | |
86 /* Waiting for window mapping */ | |
87 do { | |
88 @@ -1891,82 +1890,77 @@ run(void) | |
89 ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); | |
90 cresize(w, h); | |
91 | |
92 - clock_gettime(CLOCK_MONOTONIC, &last); | |
93 - lastblink = last; | |
94 - | |
95 - for (xev = actionfps;;) { | |
96 + for (timeout = -1, drawing = 0, lastblink = (struct timespec){0… | |
97 FD_ZERO(&rfd); | |
98 FD_SET(ttyfd, &rfd); | |
99 FD_SET(xfd, &rfd); | |
100 | |
101 + if (XPending(xw.dpy)) | |
102 + timeout = 0; /* existing events might not set … | |
103 + | |
104 + seltv.tv_sec = timeout / 1E3; | |
105 + seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); | |
106 + tv = timeout >= 0 ? &seltv : NULL; | |
107 + | |
108 if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NU… | |
109 if (errno == EINTR) | |
110 continue; | |
111 die("select failed: %s\n", strerror(errno)); | |
112 } | |
113 - if (FD_ISSET(ttyfd, &rfd)) { | |
114 - ttyread(); | |
115 - if (blinktimeout) { | |
116 - blinkset = tattrset(ATTR_BLINK); | |
117 - if (!blinkset) | |
118 - MODBIT(win.mode, 0, MODE_BLINK); | |
119 - } | |
120 - } | |
121 + clock_gettime(CLOCK_MONOTONIC, &now); | |
122 | |
123 - if (FD_ISSET(xfd, &rfd)) | |
124 - xev = actionfps; | |
125 + if (FD_ISSET(ttyfd, &rfd)) | |
126 + ttyread(); | |
127 | |
128 - clock_gettime(CLOCK_MONOTONIC, &now); | |
129 - drawtimeout.tv_sec = 0; | |
130 - drawtimeout.tv_nsec = (1000 * 1E6)/ xfps; | |
131 - tv = &drawtimeout; | |
132 - | |
133 - dodraw = 0; | |
134 - if (blinktimeout && TIMEDIFF(now, lastblink) > blinktim… | |
135 - tsetdirtattr(ATTR_BLINK); | |
136 - win.mode ^= MODE_BLINK; | |
137 - lastblink = now; | |
138 - dodraw = 1; | |
139 - } | |
140 - deltatime = TIMEDIFF(now, last); | |
141 - if (deltatime > 1000 / (xev ? xfps : actionfps)) { | |
142 - dodraw = 1; | |
143 - last = now; | |
144 + xev = 0; | |
145 + while (XPending(xw.dpy)) { | |
146 + xev = 1; | |
147 + XNextEvent(xw.dpy, &ev); | |
148 + if (XFilterEvent(&ev, None)) | |
149 + continue; | |
150 + if (handler[ev.type]) | |
151 + (handler[ev.type])(&ev); | |
152 } | |
153 | |
154 - if (dodraw) { | |
155 - while (XPending(xw.dpy)) { | |
156 - XNextEvent(xw.dpy, &ev); | |
157 - if (XFilterEvent(&ev, None)) | |
158 - continue; | |
159 - if (handler[ev.type]) | |
160 - (handler[ev.type])(&ev); | |
161 + /* | |
162 + * To reduce flicker and tearing, when new content or e… | |
163 + * triggers drawing, we first wait a bit to ensure we g… | |
164 + * everything, and if nothing new arrives - we draw. | |
165 + * We start with trying to wait minlatency ms. If more … | |
166 + * arrives sooner, we retry with shorter and shorter pr… | |
167 + * and eventually draw even without idle after maxlaten… | |
168 + * Typically this results in low latency while interact… | |
169 + * maximum latency intervals during `cat huge.txt`, and… | |
170 + * sync with periodic updates from animations/key-repea… | |
171 + */ | |
172 + if (FD_ISSET(ttyfd, &rfd) || xev) { | |
173 + if (!drawing) { | |
174 + trigger = now; | |
175 + drawing = 1; | |
176 } | |
177 + timeout = (maxlatency - TIMEDIFF(now, trigger))… | |
178 + / maxlatency * minlatency; | |
179 + if (timeout > 0) | |
180 + continue; /* we have time, try to find… | |
181 + } | |
182 | |
183 - draw(); | |
184 - XFlush(xw.dpy); | |
185 - | |
186 - if (xev && !FD_ISSET(xfd, &rfd)) | |
187 - xev--; | |
188 - if (!FD_ISSET(ttyfd, &rfd) && !FD_ISSET(xfd, &r… | |
189 - if (blinkset) { | |
190 - if (TIMEDIFF(now, lastblink) \ | |
191 - > blinktimeout)… | |
192 - drawtimeout.tv_nsec = 1… | |
193 - } else { | |
194 - drawtimeout.tv_nsec = (… | |
195 - (blinktimeout -… | |
196 - TIMEDIFF(now, | |
197 - lastbli… | |
198 - } | |
199 - drawtimeout.tv_sec = \ | |
200 - drawtimeout.tv_nsec / 1E9; | |
201 - drawtimeout.tv_nsec %= (long)1E… | |
202 - } else { | |
203 - tv = NULL; | |
204 - } | |
205 + /* idle detected or maxlatency exhausted -> draw */ | |
206 + timeout = -1; | |
207 + if (blinktimeout && tattrset(ATTR_BLINK)) { | |
208 + timeout = blinktimeout - TIMEDIFF(now, lastblin… | |
209 + if (timeout <= 0) { | |
210 + if (-timeout > blinktimeout) /* start v… | |
211 + win.mode |= MODE_BLINK; | |
212 + win.mode ^= MODE_BLINK; | |
213 + tsetdirtattr(ATTR_BLINK); | |
214 + lastblink = now; | |
215 + timeout = blinktimeout; | |
216 } | |
217 } | |
218 + | |
219 + draw(); | |
220 + XFlush(xw.dpy); | |
221 + drawing = 0; | |
222 } | |
223 } | |
224 | |
225 | |
226 base-commit: 43a395ae91f7d67ce694e65edeaa7bbc720dd027 | |
227 -- | |
228 2.17.1 | |
229 |