5c7d8ab1ad
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).
148 lines
3.5 KiB
C
148 lines
3.5 KiB
C
/* See LICENSE for license details. */
|
|
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
#include "patches.h"
|
|
|
|
/* macros */
|
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
#define MAX(a, b) ((a) < (b) ? (b) : (a))
|
|
#define LEN(a) (sizeof(a) / sizeof(a)[0])
|
|
#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
|
|
#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
|
|
#define DEFAULT(a, b) (a) = (a) ? (a) : (b)
|
|
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
|
|
#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \
|
|
(a).bg != (b).bg)
|
|
#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
|
|
(t1.tv_nsec-t2.tv_nsec)/1E6)
|
|
#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
|
|
|
|
#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
|
|
#define IS_TRUECOL(x) (1 << 24 & (x))
|
|
|
|
enum glyph_attribute {
|
|
ATTR_NULL = 0,
|
|
ATTR_BOLD = 1 << 0,
|
|
ATTR_FAINT = 1 << 1,
|
|
ATTR_ITALIC = 1 << 2,
|
|
ATTR_UNDERLINE = 1 << 3,
|
|
ATTR_BLINK = 1 << 4,
|
|
ATTR_REVERSE = 1 << 5,
|
|
ATTR_INVISIBLE = 1 << 6,
|
|
ATTR_STRUCK = 1 << 7,
|
|
ATTR_WRAP = 1 << 8,
|
|
ATTR_WIDE = 1 << 9,
|
|
ATTR_WDUMMY = 1 << 10,
|
|
#if BOXDRAW_PATCH
|
|
ATTR_BOXDRAW = 1 << 11,
|
|
#endif // BOXDRAW_PATCH
|
|
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
|
|
};
|
|
|
|
enum selection_mode {
|
|
SEL_IDLE = 0,
|
|
SEL_EMPTY = 1,
|
|
SEL_READY = 2
|
|
};
|
|
|
|
enum selection_type {
|
|
SEL_REGULAR = 1,
|
|
SEL_RECTANGULAR = 2
|
|
};
|
|
|
|
enum selection_snap {
|
|
SNAP_WORD = 1,
|
|
SNAP_LINE = 2
|
|
};
|
|
|
|
typedef unsigned char uchar;
|
|
typedef unsigned int uint;
|
|
typedef unsigned long ulong;
|
|
typedef unsigned short ushort;
|
|
|
|
typedef uint_least32_t Rune;
|
|
|
|
#define Glyph Glyph_
|
|
typedef struct {
|
|
Rune u; /* character code */
|
|
ushort mode; /* attribute flags */
|
|
uint32_t fg; /* foreground */
|
|
uint32_t bg; /* background */
|
|
} Glyph;
|
|
|
|
typedef Glyph *Line;
|
|
|
|
typedef union {
|
|
int i;
|
|
uint ui;
|
|
float f;
|
|
const void *v;
|
|
const char *s;
|
|
} Arg;
|
|
|
|
void die(const char *, ...);
|
|
void redraw(void);
|
|
void draw(void);
|
|
|
|
void printscreen(const Arg *);
|
|
void printsel(const Arg *);
|
|
void sendbreak(const Arg *);
|
|
void toggleprinter(const Arg *);
|
|
|
|
int tattrset(int);
|
|
void tnew(int, int);
|
|
void tresize(int, int);
|
|
void tsetdirtattr(int);
|
|
void ttyhangup(void);
|
|
int ttynew(char *, char *, char *, char **);
|
|
size_t ttyread(void);
|
|
void ttyresize(int, int);
|
|
void ttywrite(const char *, size_t, int);
|
|
|
|
void resettitle(void);
|
|
|
|
void selclear(void);
|
|
void selinit(void);
|
|
void selstart(int, int, int);
|
|
void selextend(int, int, int, int);
|
|
int selected(int, int);
|
|
char *getsel(void);
|
|
|
|
size_t utf8encode(Rune, char *);
|
|
|
|
void *xmalloc(size_t);
|
|
void *xrealloc(void *, size_t);
|
|
char *xstrdup(char *);
|
|
#if BOXDRAW_PATCH
|
|
int isboxdraw(Rune);
|
|
ushort boxdrawindex(const Glyph *);
|
|
#ifdef XFT_VERSION
|
|
/* only exposed to x.c, otherwise we'll need Xft.h for the types */
|
|
void boxdraw_xinit(Display *, Colormap, XftDraw *, Visual *);
|
|
void drawboxes(int, int, int, int, XftColor *, XftColor *, const XftGlyphFontSpec *, int);
|
|
#endif // XFT_VERSION
|
|
#endif // BOXDRAW_PATCH
|
|
#if RELATIVEBORDER_PATCH
|
|
int borderpx;
|
|
#endif // RELATIVEBORDER_PATCH
|
|
|
|
/* config.h globals */
|
|
extern char *utmp;
|
|
extern char *scroll;
|
|
extern char *stty_args;
|
|
extern char *vtiden;
|
|
extern wchar_t *worddelimiters;
|
|
extern int allowaltscreen;
|
|
extern char *termname;
|
|
extern unsigned int tabspaces;
|
|
extern unsigned int defaultfg;
|
|
extern unsigned int defaultbg;
|
|
extern unsigned int defaultcs;
|
|
#if BOXDRAW_PATCH
|
|
extern const int boxdraw, boxdraw_bold, boxdraw_braille;
|
|
#endif // BOXDRAW_PATCH
|
|
#if ALPHA_PATCH
|
|
extern float alpha;
|
|
#endif // ALPHA_PATCH
|