Compare commits

...

18 Commits

Author SHA1 Message Date
Hiltjo Posthuma
a2a704492b config.def.h: add an option allowwindowops, by default off (secure)
Similar to the xterm AllowWindowOps option, this is an option to allow or
disallow certain (non-interactive) operations that can be insecure or
exploited.

NOTE: xsettitle() is not guarded by this because st does not support printing
the window title. Else this could be exploitable (arbitrary code execution).
Similar problems have been found in the past in other terminal emulators.

The sequence for base64-encoded clipboard copy is now guarded because it allows
a sequence written to the terminal to manipulate the clipboard of the running
user non-interactively, for example:

printf '\x1b]52;0;ZWNobyBoaQ0=\a'
2020-05-30 22:06:15 +02:00
Hiltjo Posthuma
0f8b40652b FAQ: add some details about the w3m img hack
... and an example patch to switch from double-buffering to a single buffer.
2020-05-30 22:05:17 +02:00
Hiltjo Posthuma
e6e2c6199f tiny style fix 2020-05-30 22:05:17 +02:00
Hiltjo Posthuma
94b8ec0021 Partially add back in "support REP (repeat) escape sequence"
Add the functionality back in for xterm compatibility, but do not expose the
capability in st.info (yet).

Some notes:

It was reverted because it caused some issues with ncurses in some
configurations, namely when using BSD padding (--enable-bsdpad, BSD_TPUTS) in
ncurses it caused issues with repeating digits.

A fix has been upstreamed in ncurses since snapshot 20200523. The fix is also
backported to OpenBSD -current.
2020-05-30 22:04:28 +02:00
Steve Ward
dec6b530a4 Call xsetcursor to set win.cursor in main
In xsetcursor, remove "DEFAULT(cursor, 1)" because 0 is a valid value.
Increase max allowed value of cursor from 6 to 7 (st extension).
2020-05-24 13:45:42 +02:00
Hiltjo Posthuma
475a0a36cb Revert "support REP (repeat) escape sequence"
This reverts commit e8392b282c.

There is currently a bug in older ncurses versions (like on OpenBSD) where a
fix for a bug with REP is not backported yet. Most likely in tty/tty_update.c:

Noticed while using lynx (which uses ncurses/curses).
To reproduce using lynx: echo "Z0000000" | lynx -stdin

or using the program:

int
main(void)
{
	WINDOW *win;
	win = initscr();

	printw("Z0000000");

	refresh();

	sleep(5);

	return 0;
}

This prints "ZZZZZZZ" (incorrectly).
2020-05-16 21:06:13 +02:00
Avi Halachmi (:avih)
e8392b282c support REP (repeat) escape sequence
The sequence \e[Nb prints the last printed char N (more) times if it's
printable, and it's ignored after newline or other control chars.

This is Ecma-048/ANSI-X3.6 sequence and not DEC VT. It's supported by
xterm, and ncurses uses it when possible, e.g. when TERM is xterm* (and
with this commit also st*).

xterm supports only codepoints<=255, possibly due to internal limits.
We support any value/codepoint which was placed in a cell.

To test:
- tput rep 65 4 -> prints 'AAAA'
- printf "\342\225\246\033[4b" -> prints U+2566 1+4 times.
2020-05-16 14:08:10 +02:00
Roberto E. Vargas
f8afebdfa0 Add rin terminfo capability
Tianlin Qu discovered that st is missing rin (scroll back #1 lines).
2020-05-16 14:07:31 +02:00
k0ga
bda9c9ffa6 Make shift+wheel behaves as shift+Prev/Next
St uses a very good hack where mouse wheel genereates ^Y and ^E,
that are the same keys that less and vi uses for backward and
fordward scrolling. Scroll, as many terminal emulators, use
shift+Prev/Next for scrolling, but it is also using ^E and ^Y
for scroling, characters that are reserved in the POSIX shell
in emacs mode for end of line and yanking, making scroll unsable
in st.

This patch adds a new hack, making shift+wheel returning the
same sequences than shift+Prev/Next, meaning that scroll or
any other similar program will not be able to differentiate
between them.
2020-05-16 12:37:14 +02:00
Jakub Leszczak
045a0fab4f Fix selection: selscroll 2020-05-12 15:38:17 +02:00
Jakub Leszczak
9c30066e73 Fix selection: ignore ATTR_WRAP when rectangular selection in getsel 2020-05-12 15:38:02 +02:00
Jakub Leszczak
8304d4f059 Fix selection: selclear in tputc 2020-05-12 15:37:59 +02:00
Hiltjo Posthuma
914fb825df code-style: add fallthrough comment
Patch by Steve Ward, thanks.
2020-05-09 14:43:31 +02:00
Hiltjo Posthuma
cde480c693 optimize column width calculation and utf-8 encode for ASCII
In particular on OpenBSD and on glibc wcwidth() is quite expensive.
On musl there is little difference.
2020-05-09 14:11:25 +02:00
Hiltjo Posthuma
8211e36d28 fix for incorrect (partial) written sequences when libc wcwidth() == -1
Fix an issue with incorrect (partial) written sequences when libc wcwidth() ==
-1. The sequence is updated to on wcwidth(u) == -1:

	c = "\357\277\275"

but len isn't.

A way to reproduce in practise:

* st -o dump.txt
* In the terminal: printf '\xcd\xb8'
- This is codepoint 888, on OpenBSD it reports wcwidth() == -1.
- Quit the terminal.
- Look in dump.txt (partial written sequence of "UTF_INVALID").

This was introduced in:

"	commit 11625c7166
	Author: czarkoff@gmail.com <czarkoff@gmail.com>
	Date:   Tue Oct 28 12:55:28 2014 +0100

	    Replace character with U+FFFD if wcwidth() is -1

	    Helpful when new Unicode codepoints are not recognized by libc."

Change:

Remove setting the sequence. If this happens to break something, another
solution could be setting len = 3 for the sequence.
2020-05-09 14:07:52 +02:00
Hiltjo Posthuma
87545c612e tiny code-style and typo-fix in comment 2020-05-09 14:05:04 +02:00
Avi Halachmi (:avih)
1d59091065 auto-sync: draw on idle to avoid flicker/tearing
st could easily tear/flicker with animation or other unattended
output. This commit eliminates most of the tear/flicker.

Before this commit, the display timing had two "modes":

- Interactively, st was waiting fixed `1000/xfps` ms after forwarding
  the kb/mouse event to the application and before drawing.

- Unattended, and specifically with animations, the draw frequency was
  throttled to `actionfps`. Animation at a higher rate would throttle
  and likely tear, and at lower rates it was tearing big frames
  (specifically, when one `read` didn't get a full "frame").

The interactive behavior was decent, but it was impossible to get good
unattended-draw behavior even with carefully chosen configuration.

This commit changes the behavior such that it draws on idle instead of
using fixed latency/frequency. This means that it tries to draw only
when it's very likely that the application has completed its output
(or after some duration without idle), so it mostly succeeds to avoid
tear, flicker, and partial drawing.

The config values minlatency/maxlatency replace xfps/actionfps and
define the range which the algorithm is allowed to wait from the
initial draw-trigger until the actual draw. The range enables the
flexibility to choose when to draw - when least likely to flicker.

It also unifies the interactive and unattended behavior and config
values, which makes the code simpler as well - without sacrificing
latency during interactive use, because typically interactively idle
arrives very quickly, so the wait is typically minlatency.

While it only slighly improves interactive behavior, for animations
and other unattended-drawing it improves greatly, as it effectively
adapts to any [animation] output rate without tearing, throttling,
redundant drawing, or unnecessary delays (sounds impossible, but it
works).
2020-05-09 13:53:50 +02:00
Jan Klemkow
d6ea0a1a61 replace exit(3) by _exit(2) in signal handler sigchld()
exit(3) is not async-signal-safe but, _exit(2) is.
This change prevents st to crash and dump core.
2020-04-30 01:21:21 +02:00
6 changed files with 163 additions and 106 deletions

59
FAQ
View File

@ -2,12 +2,14 @@
Use the excellent tool of [utmp](https://git.suckless.org/utmp/) for this task.
## Some _random program_ complains that st is unknown/not recognised/unsupported/whatever!
It means that st doesnt have any terminfo entry on your system. Chances are
you did not `make install`. If you just want to test it without installing it,
you can manually run `tic -sx st.info`.
## Nothing works, and nothing is said about an unknown terminal!
* Some programs just assume theyre running in xterm i.e. they dont rely on
@ -15,6 +17,7 @@ you can manually run `tic -sx st.info`.
* Some programs dont complain about the lacking st description and default to
another terminal. In that case see the question about terminfo.
## How do I scroll back up?
* Using a terminal multiplexer.
@ -23,11 +26,13 @@ you can manually run `tic -sx st.info`.
* Using the excellent tool of [scroll](https://git.suckless.org/scroll/).
* Using the scrollback [patch](https://st.suckless.org/patches/scrollback/).
## I would like to have utmp and/or scroll functionality by default
You can add the absolute patch of both programs in your config.h
file. You only have to modify the value of utmp and scroll variables.
## Why doesn't the Del key work in some programs?
Taken from the terminfo manpage:
@ -83,12 +88,14 @@ If you are using zsh, then read the zsh FAQ
Putting these lines into your .zshrc will fix the problems.
## How can I use meta in 8bit mode?
St supports meta in 8bit mode, but the default terminfo entry doesn't
use this capability. If you want it, you have to use the 'st-meta' value
in TERM.
## I cannot compile st in OpenBSD
OpenBSD lacks librt, despite it being mandatory in POSIX
@ -97,6 +104,7 @@ If you want to compile st for OpenBSD you have to remove -lrt from config.mk, an
st will compile without any loss of functionality, because all the functions are
included in libc on this platform.
## The Backspace Case
St is emulating the Linux way of handling backspace being delete and delete being
@ -158,19 +166,60 @@ terminal users wants its backspace to be how he feels it:
[1] http://www.ibb.net/~anne/keyboard.html
[2] http://www.tldp.org/HOWTO/Keyboard-and-Console-HOWTO-5.html
## But I really want the old grumpy behaviour of my terminal
Apply [1].
[1] https://st.suckless.org/patches/delkey
## Why do images not work in st (in programs such as w3m)?
This is a terrible hack that overdraws an image on top of the terminal emulator
window. It also relies on a very specific way the terminal draws it's contents.
## Why do images not work in st using the w3m image hack?
w3mimg uses a hack that draws an image on top of the terminal emulator Drawable
window. The hack relies on the terminal to use a single buffer to draw its
contents directly.
st uses double-buffered drawing so the image is quickly replaced and may show a
short flicker effect.
Below is a patch example to change st double-buffering to a single Drawable
buffer.
diff --git a/x.c b/x.c
--- a/x.c
+++ b/x.c
@@ -561,10 +561,6 @@ xresize(int col, int row)
win.tw = MAX(1, col * win.cw);
win.th = MAX(1, row * win.ch);
- XFreePixmap(xw.dpy, xw.buf);
- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
- DefaultDepth(xw.dpy, xw.scr));
- XftDrawChange(xw.draw, xw.buf);
xclear(0, 0, win.w, win.h);
}
@@ -921,8 +917,7 @@ xinit(void)
gcvalues.graphics_exposures = False;
dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
&gcvalues);
- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
- DefaultDepth(xw.dpy, xw.scr));
+ xw.buf = xw.win;
XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
@@ -1386,8 +1381,6 @@ void
draw(void)
{
drawregion(0, 0, term.col, term.row);
- XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w,
- win.h, 0, 0);
XSetForeground(xw.dpy, dc.gc,
dc.col[IS_SET(MODE_REVERSE)?
defaultfg : defaultbg].pixel);
A more proper (but limited way) would be using sixels. Which st doesn't
support.
## BadLength X error in Xft when trying to render emoji

View File

@ -43,9 +43,18 @@ static unsigned int tripleclicktimeout = 600;
/* alt screens */
int allowaltscreen = 1;
/* frames per second st should at maximum draw to the screen */
static unsigned int xfps = 120;
static unsigned int actionfps = 30;
/* allow certain non-interactive (insecure) window operations such as:
setting the clipboard text */
int allowwindowops = 0;
/*
* draw latency range in ms - from new content/keypress/etc until drawing.
* 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
@ -166,7 +175,9 @@ static uint forcemousemod = ShiftMask;
static MouseShortcut mshortcuts[] = {
/* mask button function argument release */
{ XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 },
{ ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} },
{ XK_ANY_MOD, Button4, ttysend, {.s = "\031"} },
{ ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} },
{ XK_ANY_MOD, Button5, ttysend, {.s = "\005"} },
};

59
st.c
View File

@ -38,7 +38,7 @@
/* macros */
#define IS_SET(flag) ((term.mode & (flag)) != 0)
#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177')
#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f)
#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
#define ISDELIM(u) (u && wcschr(worddelimiters, u))
@ -129,6 +129,7 @@ typedef struct {
int charset; /* current charset */
int icharset; /* selected charset for sequence */
int *tabs;
Rune lastc; /* last printed char outside of sequence, 0 if control */
} Term;
/* CSI Escape sequence structs */
@ -634,7 +635,8 @@ getsel(void)
* st.
* FIXME: Fix the computer world.
*/
if ((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP))
if ((y < sel.ne.y || lastx >= linelen) &&
(!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
*ptr++ = '\n';
}
*ptr = 0;
@ -730,7 +732,7 @@ sigchld(int a)
die("child exited with status %d\n", WEXITSTATUS(stat));
else if (WIFSIGNALED(stat))
die("child terminated due to signal %d\n", WTERMSIG(stat));
exit(0);
_exit(0);
}
void
@ -841,7 +843,6 @@ ttyread(void)
if (buflen > 0)
memmove(buf, buf + written, buflen);
return ret;
}
}
@ -1105,27 +1106,17 @@ selscroll(int orig, int n)
if (sel.ob.x == -1)
return;
if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) {
if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) {
if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) {
selclear();
} else if (BETWEEN(sel.nb.y, orig, term.bot)) {
sel.ob.y += n;
sel.oe.y += n;
if (sel.ob.y < term.top || sel.ob.y > term.bot ||
sel.oe.y < term.top || sel.oe.y > term.bot) {
selclear();
return;
}
if (sel.type == SEL_RECTANGULAR) {
if (sel.ob.y < term.top)
sel.ob.y = term.top;
if (sel.oe.y > term.bot)
sel.oe.y = term.bot;
} else {
if (sel.ob.y < term.top) {
sel.ob.y = term.top;
sel.ob.x = 0;
}
if (sel.oe.y > term.bot) {
sel.oe.y = term.bot;
sel.oe.x = term.col;
}
selnormalize();
}
selnormalize();
}
}
@ -1657,6 +1648,12 @@ csihandle(void)
if (csiescseq.arg[0] == 0)
ttywrite(vtiden, strlen(vtiden), 0);
break;
case 'b': /* REP -- if last char is printable print it <n> more times */
DEFAULT(csiescseq.arg[0], 1);
if (term.lastc)
while (csiescseq.arg[0]-- > 0)
tputc(term.lastc);
break;
case 'C': /* CUF -- Cursor <n> Forward */
case 'a': /* HPR -- Cursor <n> Forward */
DEFAULT(csiescseq.arg[0], 1);
@ -1780,7 +1777,7 @@ csihandle(void)
break;
case 'n': /* DSR Device Status Report (cursor position) */
if (csiescseq.arg[0] == 6) {
len = snprintf(buf, sizeof(buf),"\033[%i;%iR",
len = snprintf(buf, sizeof(buf), "\033[%i;%iR",
term.c.y+1, term.c.x+1);
ttywrite(buf, len, 0);
}
@ -1864,7 +1861,7 @@ strhandle(void)
xsettitle(strescseq.args[1]);
return;
case 52:
if (narg > 2) {
if (narg > 2 && allowwindowops) {
dec = base64dec(strescseq.args[2]);
if (dec) {
xsetsel(dec);
@ -2023,7 +2020,7 @@ tdumpline(int n)
bp = &term.line[n][0];
end = &bp[MIN(tlinelen(n), term.col) - 1];
if (bp != end || bp->u != ' ') {
for ( ;bp <= end; ++bp)
for ( ; bp <= end; ++bp)
tprinter(buf, utf8encode(bp->u, buf));
}
tprinter("\n", 1);
@ -2153,6 +2150,7 @@ tcontrolcode(uchar ascii)
return;
case '\032': /* SUB */
tsetchar('?', &term.c.attr, term.c.x, term.c.y);
/* FALLTHROUGH */
case '\030': /* CAN */
csireset();
break;
@ -2307,15 +2305,13 @@ tputc(Rune u)
Glyph *gp;
control = ISCONTROL(u);
if (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
if (u < 127 || !IS_SET(MODE_UTF8 | MODE_SIXEL)) {
c[0] = u;
width = len = 1;
} else {
len = utf8encode(u, c);
if (!control && (width = wcwidth(u)) == -1) {
memcpy(c, "\357\277\275", 4); /* UTF_INVALID */
if (!control && (width = wcwidth(u)) == -1)
width = 1;
}
}
if (IS_SET(MODE_PRINT))
@ -2383,6 +2379,8 @@ check_control_code:
/*
* control codes are not shown ever
*/
if (!term.esc)
term.lastc = 0;
return;
} else if (term.esc & ESC_START) {
if (term.esc & ESC_CSI) {
@ -2413,7 +2411,7 @@ check_control_code:
*/
return;
}
if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y))
if (selected(term.c.x, term.c.y))
selclear();
gp = &term.line[term.c.y][term.c.x];
@ -2432,6 +2430,7 @@ check_control_code:
}
tsetchar(u, &term.c.attr, term.c.x, term.c.y);
term.lastc = u;
if (width == 2) {
gp->mode |= ATTR_WIDE;

1
st.h
View File

@ -118,6 +118,7 @@ extern char *stty_args;
extern char *vtiden;
extern wchar_t *worddelimiters;
extern int allowaltscreen;
extern int allowwindowops;
extern char *termname;
extern unsigned int tabspaces;
extern unsigned int defaultfg;

View File

@ -158,6 +158,7 @@ st-mono| simpleterm monocolor,
rc=\E8,
rev=\E[7m,
ri=\EM,
rin=\E[%p1%dT,
ritm=\E[23m,
rmacs=\E(B,
rmcup=\E[?1049l,
@ -183,6 +184,8 @@ st-mono| simpleterm monocolor,
# XTerm extensions
rmxx=\E[29m,
smxx=\E[9m,
# disabled rep for now: causes some issues with older ncurses versions.
# rep=%p1%c\E[%p2%{1}%-%db,
# tmux extensions, see TERMINFO EXTENSIONS in tmux(1)
Tc,
Ms=\E]52;%p1%s;%p2%s\007,

130
x.c
View File

@ -1526,8 +1526,9 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
/* draw the new one */
if (IS_SET(MODE_FOCUSED)) {
switch (win.cursor) {
case 7: /* st extension: snowman (U+2603) */
g.u = 0x2603;
case 7: /* st extension */
g.u = 0x2603; /* snowman (U+2603) */
/* FALLTHROUGH */
case 0: /* Blinking Block */
case 1: /* Blinking Block (Default) */
case 2: /* Steady Block */
@ -1689,8 +1690,7 @@ xsetmode(int set, unsigned int flags)
int
xsetcursor(int cursor)
{
DEFAULT(cursor, 1);
if (!BETWEEN(cursor, 0, 6))
if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */
return 1;
win.cursor = cursor;
return 0;
@ -1867,10 +1867,9 @@ run(void)
XEvent ev;
int w = win.w, h = win.h;
fd_set rfd;
int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0;
int ttyfd;
struct timespec drawtimeout, *tv = NULL, now, last, lastblink;
long deltatime;
int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing;
struct timespec seltv, *tv, now, lastblink, trigger;
double timeout;
/* Waiting for window mapping */
do {
@ -1891,82 +1890,77 @@ run(void)
ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd);
cresize(w, h);
clock_gettime(CLOCK_MONOTONIC, &last);
lastblink = last;
for (xev = actionfps;;) {
for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) {
FD_ZERO(&rfd);
FD_SET(ttyfd, &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 (errno == EINTR)
continue;
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);
drawtimeout.tv_sec = 0;
drawtimeout.tv_nsec = (1000 * 1E6)/ xfps;
tv = &drawtimeout;
dodraw = 0;
if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) {
tsetdirtattr(ATTR_BLINK);
win.mode ^= MODE_BLINK;
lastblink = now;
dodraw = 1;
}
deltatime = TIMEDIFF(now, last);
if (deltatime > 1000 / (xev ? xfps : actionfps)) {
dodraw = 1;
last = now;
if (FD_ISSET(ttyfd, &rfd))
ttyread();
xev = 0;
while (XPending(xw.dpy)) {
xev = 1;
XNextEvent(xw.dpy, &ev);
if (XFilterEvent(&ev, None))
continue;
if (handler[ev.type])
(handler[ev.type])(&ev);
}
if (dodraw) {
while (XPending(xw.dpy)) {
XNextEvent(xw.dpy, &ev);
if (XFilterEvent(&ev, None))
continue;
if (handler[ev.type])
(handler[ev.type])(&ev);
/*
* To reduce flicker and tearing, when new content or event
* triggers drawing, we first wait a bit to ensure we got
* everything, and if nothing new arrives - we draw.
* We start with trying to wait minlatency ms. If more content
* arrives sooner, we retry with shorter and shorter periods,
* 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();
XFlush(xw.dpy);
if (xev && !FD_ISSET(xfd, &rfd))
xev--;
if (!FD_ISSET(ttyfd, &rfd) && !FD_ISSET(xfd, &rfd)) {
if (blinkset) {
if (TIMEDIFF(now, lastblink) \
> blinktimeout) {
drawtimeout.tv_nsec = 1000;
} else {
drawtimeout.tv_nsec = (1E6 * \
(blinktimeout - \
TIMEDIFF(now,
lastblink)));
}
drawtimeout.tv_sec = \
drawtimeout.tv_nsec / 1E9;
drawtimeout.tv_nsec %= (long)1E9;
} else {
tv = NULL;
}
/* idle detected or maxlatency exhausted -> draw */
timeout = -1;
if (blinktimeout && tattrset(ATTR_BLINK)) {
timeout = blinktimeout - TIMEDIFF(now, lastblink);
if (timeout <= 0) {
if (-timeout > blinktimeout) /* start visible */
win.mode |= MODE_BLINK;
win.mode ^= MODE_BLINK;
tsetdirtattr(ATTR_BLINK);
lastblink = now;
timeout = blinktimeout;
}
}
draw();
XFlush(xw.dpy);
drawing = 0;
}
}
@ -1988,7 +1982,7 @@ main(int argc, char *argv[])
{
xw.l = xw.t = 0;
xw.isfixed = False;
win.cursor = cursorshape;
xsetcursor(cursorshape);
ARGBEGIN {
case 'a':