Compare commits

..

No commits in common. 'master' and '0.8.3' have entirely different histories.

59
FAQ

@ -2,14 +2,12 @@
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
@ -17,7 +15,6 @@ 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.
@ -26,13 +23,11 @@ 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:
@ -88,14 +83,12 @@ 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
@ -104,7 +97,6 @@ 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
@ -166,60 +158,19 @@ 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)?
## 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
@@ -732,10 +732,6 @@ xresize(int col, int row)
win.tw = col * win.cw;
win.th = 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);
/* resize to new width */
@@ -1148,8 +1144,7 @@ xinit(int cols, int rows)
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);
@@ -1632,8 +1627,6 @@ xdrawline(Line line, int x1, int y1, int x2)
void
xfinishdraw(void)
{
- 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);
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.
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

@ -1,6 +1,6 @@
MIT/X Consortium License
© 2014-2020 Hiltjo Posthuma <hiltjo at codemadness dot org>
© 2014-2018 Hiltjo Posthuma <hiltjo at codemadness dot org>
© 2018 Devin J. Pohly <djpohly at gmail dot com>
© 2014-2017 Quentin Rameau <quinq at fifth dot space>
© 2009-2012 Aurélien APTEL <aurelien dot aptel at gmail dot com>

@ -5,8 +5,8 @@
*
* font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
*/
static char *font = "Liberation Mono:pixelsize=14:antialias=true:autohint=true";
static int borderpx = 3;
static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
static int borderpx = 2;
/*
* What program is execed by st depends of these precedence rules:
@ -43,18 +43,9 @@ static unsigned int tripleclicktimeout = 600;
/* alt screens */
int allowaltscreen = 1;
/* 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;
/* frames per second st should at maximum draw to the screen */
static unsigned int xfps = 120;
static unsigned int actionfps = 30;
/*
* blinking timeout (set to 0 to disable blinking) for the terminal blinking
@ -96,24 +87,24 @@ unsigned int tabspaces = 8;
/* Terminal colors (16 first used in escape sequence) */
static const char *colorname[] = {
/* 8 normal colors */
"#282828",
"#cc241d",
"#98971a",
"#d79921",
"#458588",
"#b16286",
"#689d61",
"#a89984",
"black",
"red3",
"green3",
"yellow3",
"blue2",
"magenta3",
"cyan3",
"gray90",
/* 8 bright colors */
"#928374",
"#fb4934",
"#b8bb26",
"#fabd2f",
"#83a598",
"#d3869b",
"#8ec07c",
"#ebdbb2",
"gray50",
"red",
"green",
"yellow",
"#5c5cff",
"magenta",
"cyan",
"white",
[255] = 0,
@ -175,9 +166,7 @@ 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"} },
};
@ -191,9 +180,9 @@ static Shortcut shortcuts[] = {
{ ControlMask, XK_Print, toggleprinter, {.i = 0} },
{ ShiftMask, XK_Print, printscreen, {.i = 0} },
{ XK_ANY_MOD, XK_Print, printsel, {.i = 0} },
{ TERMMOD, XK_K, zoom, {.f = +1} },
{ TERMMOD, XK_J, zoom, {.f = -1} },
{ TERMMOD, XK_L, zoomreset, {.f = 0} },
{ TERMMOD, XK_Prior, zoom, {.f = +1} },
{ TERMMOD, XK_Next, zoom, {.f = -1} },
{ TERMMOD, XK_Home, zoomreset, {.f = 0} },
{ TERMMOD, XK_C, clipcopy, {.i = 0} },
{ TERMMOD, XK_V, clippaste, {.i = 0} },
{ TERMMOD, XK_Y, selpaste, {.i = 0} },
@ -221,6 +210,7 @@ static Shortcut shortcuts[] = {
* this table sequentially, so any XK_ANY_MOD must be in the last
* position for a key.
*/
/*
* If you want keys other than the X11 function keys (0xFD00 - 0xFFFF)
* to be mapped below, add them to this array.

@ -1,5 +1,5 @@
# st version
VERSION = 0.8.4
VERSION = 0.8.3
# Customize below to fit your system
@ -28,8 +28,8 @@ STLDFLAGS = $(LIBS) $(LDFLAGS)
# OpenBSD:
#CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE
#LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \
# `$(PKG_CONFIG) --libs fontconfig` \
# `$(PKG_CONFIG) --libs freetype2`
# `pkg-config --libs fontconfig` \
# `pkg-config --libs freetype2`
# compiler and linker
# CC = c99

141
st.c

@ -38,7 +38,7 @@
/* macros */
#define IS_SET(flag) ((term.mode & (flag)) != 0)
#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f)
#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177')
#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
#define ISDELIM(u) (u && wcschr(worddelimiters, u))
@ -51,6 +51,7 @@ enum term_mode {
MODE_ECHO = 1 << 4,
MODE_PRINT = 1 << 5,
MODE_UTF8 = 1 << 6,
MODE_SIXEL = 1 << 7,
};
enum cursor_movement {
@ -77,11 +78,12 @@ enum charset {
enum escape_state {
ESC_START = 1,
ESC_CSI = 2,
ESC_STR = 4, /* DCS, OSC, PM, APC */
ESC_STR = 4, /* OSC, PM, APC */
ESC_ALTCHARSET = 8,
ESC_STR_END = 16, /* a final string was encountered */
ESC_TEST = 32, /* Enter in test mode */
ESC_UTF8 = 64,
ESC_DCS =128,
};
typedef struct {
@ -127,7 +129,6 @@ 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 */
@ -186,18 +187,18 @@ static void tputc(Rune);
static void treset(void);
static void tscrollup(int, int);
static void tscrolldown(int, int);
static void tsetattr(const int *, int);
static void tsetchar(Rune, const Glyph *, int, int);
static void tsetattr(int *, int);
static void tsetchar(Rune, Glyph *, int, int);
static void tsetdirt(int, int);
static void tsetscroll(int, int);
static void tswapscreen(void);
static void tsetmode(int, int, const int *, int);
static void tsetmode(int, int, int *, int);
static int twrite(const char *, int, int);
static void tfulldirt(void);
static void tcontrolcode(uchar );
static void tdectest(char );
static void tdefutf8(char);
static int32_t tdefcolor(const int *, int *, int);
static int32_t tdefcolor(int *, int *, int);
static void tdeftran(char);
static void tstrsequence(uchar);
@ -226,10 +227,10 @@ static int iofd = 1;
static int cmdfd;
static pid_t pid;
static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
ssize_t
xwrite(int fd, const char *s, size_t len)
@ -269,14 +270,12 @@ xrealloc(void *p, size_t len)
}
char *
xstrdup(const char *s)
xstrdup(char *s)
{
char *p;
if ((p = strdup(s)) == NULL)
if ((s = strdup(s)) == NULL)
die("strdup: %s\n", strerror(errno));
return p;
return s;
}
size_t
@ -520,7 +519,7 @@ selsnap(int *x, int *y, int direction)
{
int newx, newy, xt, yt;
int delim, prevdelim;
const Glyph *gp, *prevgp;
Glyph *gp, *prevgp;
switch (sel.snap) {
case SNAP_WORD:
@ -593,7 +592,7 @@ getsel(void)
{
char *str, *ptr;
int y, bufsize, lastx, linelen;
const Glyph *gp, *last;
Glyph *gp, *last;
if (sel.ob.x == -1)
return NULL;
@ -635,8 +634,7 @@ getsel(void)
* st.
* FIXME: Fix the computer world.
*/
if ((y < sel.ne.y || lastx >= linelen) &&
(!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
if ((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP))
*ptr++ = '\n';
}
*ptr = 0;
@ -732,7 +730,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
@ -760,7 +758,7 @@ stty(char **args)
}
int
ttynew(const char *line, char *cmd, const char *out, char **args)
ttynew(char *line, char *cmd, char *out, char **args)
{
int m, s;
@ -793,15 +791,14 @@ ttynew(const char *line, char *cmd, const char *out, char **args)
break;
case 0:
close(iofd);
close(m);
setsid(); /* create a new process group */
dup2(s, 0);
dup2(s, 1);
dup2(s, 2);
if (ioctl(s, TIOCSCTTY, NULL) < 0)
die("ioctl TIOCSCTTY failed: %s\n", strerror(errno));
if (s > 2)
close(s);
close(s);
close(m);
#ifdef __OpenBSD__
if (pledge("stdio getpw proc exec", NULL) == -1)
die("pledge\n");
@ -844,6 +841,7 @@ ttyread(void)
if (buflen > 0)
memmove(buf, buf + written, buflen);
return ret;
}
}
@ -1107,17 +1105,27 @@ selscroll(int orig, int n)
if (sel.ob.x == -1)
return;
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) {
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) {
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 {
selnormalize();
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();
}
}
@ -1189,9 +1197,9 @@ tmoveto(int x, int y)
}
void
tsetchar(Rune u, const Glyph *attr, int x, int y)
tsetchar(Rune u, Glyph *attr, int x, int y)
{
static const char *vt100_0[62] = { /* 0x41 - 0x7e */
static char *vt100_0[62] = { /* 0x41 - 0x7e */
"", "", "", "", "", "", "", /* A - G */
0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
@ -1303,7 +1311,7 @@ tdeleteline(int n)
}
int32_t
tdefcolor(const int *attr, int *npar, int l)
tdefcolor(int *attr, int *npar, int l)
{
int32_t idx = -1;
uint r, g, b;
@ -1353,7 +1361,7 @@ tdefcolor(const int *attr, int *npar, int l)
}
void
tsetattr(const int *attr, int l)
tsetattr(int *attr, int l)
{
int i;
int32_t idx;
@ -1471,9 +1479,9 @@ tsetscroll(int t, int b)
}
void
tsetmode(int priv, int set, const int *args, int narg)
tsetmode(int priv, int set, int *args, int narg)
{
int alt; const int *lim;
int alt, *lim;
for (lim = args + narg; args < lim; ++args) {
if (priv) {
@ -1649,12 +1657,6 @@ 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);
@ -1778,7 +1780,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);
}
@ -1856,21 +1858,13 @@ strhandle(void)
case ']': /* OSC -- Operating System Command */
switch (par) {
case 0:
if (narg > 1) {
xsettitle(strescseq.args[1]);
xseticontitle(strescseq.args[1]);
}
return;
case 1:
if (narg > 1)
xseticontitle(strescseq.args[1]);
return;
case 2:
if (narg > 1)
xsettitle(strescseq.args[1]);
return;
case 52:
if (narg > 2 && allowwindowops) {
if (narg > 2) {
dec = base64dec(strescseq.args[2]);
if (dec) {
xsetsel(dec);
@ -1906,6 +1900,7 @@ strhandle(void)
xsettitle(strescseq.args[0]);
return;
case 'P': /* DCS -- Device Control String */
term.mode |= ESC_DCS;
case '_': /* APC -- Application Program Command */
case '^': /* PM -- Privacy Message */
return;
@ -2023,12 +2018,12 @@ void
tdumpline(int n)
{
char buf[UTF_SIZ];
const Glyph *bp, *end;
Glyph *bp, *end;
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);
@ -2099,9 +2094,12 @@ tdectest(char c)
void
tstrsequence(uchar c)
{
strreset();
switch (c) {
case 0x90: /* DCS -- Device Control String */
c = 'P';
term.esc |= ESC_DCS;
break;
case 0x9f: /* APC -- Application Program Command */
c = '_';
@ -2113,7 +2111,6 @@ tstrsequence(uchar c)
c = ']';
break;
}
strreset();
strescseq.type = c;
term.esc |= ESC_STR;
}
@ -2156,7 +2153,6 @@ tcontrolcode(uchar ascii)
return;
case '\032': /* SUB */
tsetchar('?', &term.c.attr, term.c.x, term.c.y);
/* FALLTHROUGH */
case '\030': /* CAN */
csireset();
break;
@ -2311,13 +2307,15 @@ tputc(Rune u)
Glyph *gp;
control = ISCONTROL(u);
if (u < 127 || !IS_SET(MODE_UTF8)) {
if (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
c[0] = u;
width = len = 1;
} else {
len = utf8encode(u, c);
if (!control && (width = wcwidth(u)) == -1)
if (!control && (width = wcwidth(u)) == -1) {
memcpy(c, "\357\277\275", 4); /* UTF_INVALID */
width = 1;
}
}
if (IS_SET(MODE_PRINT))
@ -2332,11 +2330,23 @@ tputc(Rune u)
if (term.esc & ESC_STR) {
if (u == '\a' || u == 030 || u == 032 || u == 033 ||
ISCONTROLC1(u)) {
term.esc &= ~(ESC_START|ESC_STR);
term.esc &= ~(ESC_START|ESC_STR|ESC_DCS);
if (IS_SET(MODE_SIXEL)) {
/* TODO: render sixel */;
term.mode &= ~MODE_SIXEL;
return;
}
term.esc |= ESC_STR_END;
goto check_control_code;
}
if (IS_SET(MODE_SIXEL)) {
/* TODO: implement sixel mode */
return;
}
if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q')
term.mode |= MODE_SIXEL;
if (strescseq.len+len >= strescseq.siz) {
/*
* Here is a bug in terminals. If the user never sends
@ -2373,8 +2383,6 @@ 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) {
@ -2405,7 +2413,7 @@ check_control_code:
*/
return;
}
if (selected(term.c.x, term.c.y))
if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y))
selclear();
gp = &term.line[term.c.y][term.c.x];
@ -2424,7 +2432,6 @@ check_control_code:
}
tsetchar(u, &term.c.attr, term.c.x, term.c.y);
term.lastc = u;
if (width == 2) {
gp->mode |= ATTR_WIDE;
@ -2448,7 +2455,7 @@ twrite(const char *buf, int buflen, int show_ctrl)
int n;
for (n = 0; n < buflen; n += charsize) {
if (IS_SET(MODE_UTF8)) {
if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
/* process a complete utf8 char */
charsize = utf8decode(buf + n, &u, buflen - n);
if (charsize == 0)

@ -91,7 +91,7 @@ void tnew(int, int);
void tresize(int, int);
void tsetdirtattr(int);
void ttyhangup(void);
int ttynew(const char *, char *, const char *, char **);
int ttynew(char *, char *, char *, char **);
size_t ttyread(void);
void ttyresize(int, int);
void ttywrite(const char *, size_t, int);
@ -109,7 +109,7 @@ size_t utf8encode(Rune, char *);
void *xmalloc(size_t);
void *xrealloc(void *, size_t);
char *xstrdup(const char *);
char *xstrdup(char *);
/* config.h globals */
extern char *utmp;
@ -118,7 +118,6 @@ 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;

@ -158,7 +158,6 @@ st-mono| simpleterm monocolor,
rc=\E8,
rev=\E[7m,
ri=\EM,
rin=\E[%p1%dT,
ritm=\E[23m,
rmacs=\E(B,
rmcup=\E[?1049l,
@ -184,8 +183,6 @@ 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,

@ -30,7 +30,6 @@ void xdrawline(Line, int, int, int);
void xfinishdraw(void);
void xloadcols(void);
int xsetcolorname(int, const char *);
void xseticontitle(char *);
void xsettitle(char *);
int xsetcursor(int);
void xsetmode(int, unsigned int);

252
x.c

@ -48,7 +48,7 @@ typedef struct {
/* X modifiers */
#define XK_ANY_MOD UINT_MAX
#define XK_NO_MOD 0
#define XK_SWITCH_MOD (1<<13|1<<14)
#define XK_SWITCH_MOD (1<<13)
/* function definitions used in config.h */
static void clipcopy(const Arg *);
@ -81,7 +81,6 @@ typedef XftGlyphFontSpec GlyphFontSpec;
typedef struct {
int tw, th; /* tty width and height */
int w, h; /* window width and height */
int hborderpx, vborderpx;
int ch; /* char height */
int cw; /* char width */
int mode; /* window state/mode flags */
@ -94,7 +93,7 @@ typedef struct {
Window win;
Drawable buf;
GlyphFontSpec *specbuf; /* font spec buffer used for rendering */
Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid;
Atom xembed, wmdeletewin, netwmname, netwmpid;
struct {
XIM xim;
XIC xic;
@ -104,11 +103,6 @@ typedef struct {
Draw draw;
Visual *vis;
XSetWindowAttributes attrs;
/* Here, we use the term *pointer* to differentiate the cursor
* one sees when hovering the mouse over the terminal from, e.g.,
* a green rectangle where text would be entered. */
Cursor vpointer, bpointer; /* visible and hidden pointers */
int pointerisvisible;
int scr;
int isfixed; /* is fixed geometry? */
int l, t; /* left and top offset */
@ -162,7 +156,7 @@ static void xresize(int, int);
static void xhints(void);
static int xloadcolor(int, const char *, Color *);
static int xloadfont(Font *, FcPattern *);
static void xloadfonts(const char *, double);
static void xloadfonts(char *, double);
static void xunloadfont(Font *);
static void xunloadfonts(void);
static void xsetenv(void);
@ -337,7 +331,7 @@ ttysend(const Arg *arg)
int
evcol(XEvent *e)
{
int x = e->xbutton.x - win.hborderpx;
int x = e->xbutton.x - borderpx;
LIMIT(x, 0, win.tw - 1);
return x / win.cw;
}
@ -345,7 +339,7 @@ evcol(XEvent *e)
int
evrow(XEvent *e)
{
int y = e->xbutton.y - win.vborderpx;
int y = e->xbutton.y - borderpx;
LIMIT(y, 0, win.th - 1);
return y / win.ch;
}
@ -393,9 +387,7 @@ mousereport(XEvent *e)
button = 3;
} else {
button -= Button1;
if (button >= 7)
button += 128 - 7;
else if (button >= 3)
if (button >= 3)
button += 64 - 3;
}
if (e->xbutton.type == ButtonPress) {
@ -706,13 +698,6 @@ brelease(XEvent *e)
void
bmotion(XEvent *e)
{
if (!xw.pointerisvisible) {
XDefineCursor(xw.dpy, xw.win, xw.vpointer);
xw.pointerisvisible = 1;
if (!IS_SET(MODE_MOUSEMANY))
xsetpointermotion(0);
}
if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
mousereport(e);
return;
@ -736,9 +721,6 @@ cresize(int width, int height)
col = MAX(1, col);
row = MAX(1, row);
win.hborderpx = (win.w - col * win.cw) / 2;
win.vborderpx = (win.h - row * win.ch) / 2;
tresize(col, row);
xresize(col, row);
ttyresize(win.tw, win.th);
@ -856,8 +838,8 @@ xhints(void)
sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize;
sizeh->height = win.h;
sizeh->width = win.w;
sizeh->height_inc = 1;
sizeh->width_inc = 1;
sizeh->height_inc = win.ch;
sizeh->width_inc = win.cw;
sizeh->base_height = 2 * borderpx;
sizeh->base_width = 2 * borderpx;
sizeh->min_height = win.ch + 2 * borderpx;
@ -968,7 +950,7 @@ xloadfont(Font *f, FcPattern *pattern)
}
void
xloadfonts(const char *fontstr, double fontsize)
xloadfonts(char *fontstr, double fontsize)
{
FcPattern *pattern;
double fontval;
@ -976,7 +958,7 @@ xloadfonts(const char *fontstr, double fontsize)
if (fontstr[0] == '-')
pattern = XftXlfdParse(fontstr, False, False);
else
pattern = FcNameParse((const FcChar8 *)fontstr);
pattern = FcNameParse((FcChar8 *)fontstr);
if (!pattern)
die("can't open font %s\n", fontstr);
@ -1117,10 +1099,10 @@ void
xinit(int cols, int rows)
{
XGCValues gcvalues;
Cursor cursor;
Window parent;
pid_t thispid = getpid();
XColor xmousefg, xmousebg;
Pixmap blankpm;
if (!(xw.dpy = XOpenDisplay(NULL)))
die("can't open display\n");
@ -1139,8 +1121,8 @@ xinit(int cols, int rows)
xloadcols();
/* adjust fixed window geometry */
win.w = 2 * win.hborderpx + cols * win.cw;
win.h = 2 * win.vborderpx + rows * win.ch;
win.w = 2 * borderpx + cols * win.cw;
win.h = 2 * borderpx + rows * win.ch;
if (xw.gm & XNegative)
xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
if (xw.gm & YNegative)
@ -1184,9 +1166,8 @@ xinit(int cols, int rows)
}
/* white cursor, black outline */
xw.pointerisvisible = 1;
xw.vpointer = XCreateFontCursor(xw.dpy, mouseshape);
XDefineCursor(xw.dpy, xw.win, xw.vpointer);
cursor = XCreateFontCursor(xw.dpy, mouseshape);
XDefineCursor(xw.dpy, xw.win, cursor);
if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) {
xmousefg.red = 0xffff;
@ -1200,15 +1181,11 @@ xinit(int cols, int rows)
xmousebg.blue = 0x0000;
}
XRecolorCursor(xw.dpy, xw.vpointer, &xmousefg, &xmousebg);
blankpm = XCreateBitmapFromData(xw.dpy, xw.win, &(char){0}, 1, 1);
xw.bpointer = XCreatePixmapCursor(xw.dpy, blankpm, blankpm,
&xmousefg, &xmousebg, 0, 0);
XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg);
xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False);
XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False);
@ -1233,7 +1210,7 @@ xinit(int cols, int rows)
int
xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
{
float winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch, xp, yp;
float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
ushort mode, prevmode = USHRT_MAX;
Font *font = &dc.font;
int frcflags = FRC_NORMAL;
@ -1366,7 +1343,7 @@ void
xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
{
int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
int winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch,
int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
width = charlen * win.cw;
Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
XRenderColor colfg, colbg;
@ -1456,17 +1433,17 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
/* Intelligent cleaning up of the borders. */
if (x == 0) {
xclear(0, (y == 0)? 0 : winy, win.vborderpx,
xclear(0, (y == 0)? 0 : winy, borderpx,
winy + win.ch +
((winy + win.ch >= win.vborderpx + win.th)? win.h : 0));
((winy + win.ch >= borderpx + win.th)? win.h : 0));
}
if (winx + width >= win.hborderpx + win.tw) {
if (winx + width >= borderpx + win.tw) {
xclear(winx + width, (y == 0)? 0 : winy, win.w,
((winy + win.ch >= win.vborderpx + win.th)? win.h : (winy + win.ch)));
((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch)));
}
if (y == 0)
xclear(winx, 0, winx + width, win.hborderpx);
if (winy + win.ch >= win.vborderpx + win.th)
xclear(winx, 0, winx + width, borderpx);
if (winy + win.ch >= borderpx + win.th)
xclear(winx, winy + win.ch, winx + width, win.h);
/* Clean up the region we want to draw to. */
@ -1549,9 +1526,8 @@ 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 */
g.u = 0x2603; /* snowman (U+2603) */
/* FALLTHROUGH */
case 7: /* st extension: snowman (U+2603) */
g.u = 0x2603;
case 0: /* Blinking Block */
case 1: /* Blinking Block (Default) */
case 2: /* Steady Block */
@ -1560,35 +1536,35 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
case 3: /* Blinking Underline */
case 4: /* Steady Underline */
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + cx * win.cw,
win.vborderpx + (cy + 1) * win.ch - \
borderpx + cx * win.cw,
borderpx + (cy + 1) * win.ch - \
cursorthickness,
win.cw, cursorthickness);
break;
case 5: /* Blinking bar */
case 6: /* Steady bar */
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + cx * win.cw,
win.vborderpx + cy * win.ch,
borderpx + cx * win.cw,
borderpx + cy * win.ch,
cursorthickness, win.ch);
break;
}
} else {
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + cx * win.cw,
win.vborderpx + cy * win.ch,
borderpx + cx * win.cw,
borderpx + cy * win.ch,
win.cw - 1, 1);
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + cx * win.cw,
win.vborderpx + cy * win.ch,
borderpx + cx * win.cw,
borderpx + cy * win.ch,
1, win.ch - 1);
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + (cx + 1) * win.cw - 1,
win.vborderpx + cy * win.ch,
borderpx + (cx + 1) * win.cw - 1,
borderpx + cy * win.ch,
1, win.ch - 1);
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + cx * win.cw,
win.vborderpx + (cy + 1) * win.ch - 1,
borderpx + cx * win.cw,
borderpx + (cy + 1) * win.ch - 1,
win.cw, 1);
}
}
@ -1602,29 +1578,14 @@ xsetenv(void)
setenv("WINDOWID", buf, 1);
}
void
xseticontitle(char *p)
{
XTextProperty prop;
DEFAULT(p, opt_title);
if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
&prop) != Success)
return;
XSetWMIconName(xw.dpy, xw.win, &prop);
XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname);
XFree(prop.value);
}
void
xsettitle(char *p)
{
XTextProperty prop;
DEFAULT(p, opt_title);
if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
&prop) != Success)
return;
Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
&prop);
XSetWMName(xw.dpy, xw.win, &prop);
XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname);
XFree(prop.value);
@ -1712,8 +1673,6 @@ unmap(XEvent *ev)
void
xsetpointermotion(int set)
{
if (!set && !xw.pointerisvisible)
return;
MODBIT(xw.attrs.event_mask, set, PointerMotionMask);
XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
}
@ -1730,7 +1689,8 @@ xsetmode(int set, unsigned int flags)
int
xsetcursor(int cursor)
{
if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */
DEFAULT(cursor, 1);
if (!BETWEEN(cursor, 0, 6))
return 1;
win.cursor = cursor;
return 0;
@ -1833,12 +1793,6 @@ kpress(XEvent *ev)
Status status;
Shortcut *bp;
if (xw.pointerisvisible) {
XDefineCursor(xw.dpy, xw.win, xw.bpointer);
xsetpointermotion(1);
xw.pointerisvisible = 0;
}
if (IS_SET(MODE_KBDLOCK))
return;
@ -1913,9 +1867,10 @@ run(void)
XEvent ev;
int w = win.w, h = win.h;
fd_set rfd;
int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing;
struct timespec seltv, *tv, now, lastblink, trigger;
double timeout;
int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0;
int ttyfd;
struct timespec drawtimeout, *tv = NULL, now, last, lastblink;
long deltatime;
/* Waiting for window mapping */
do {
@ -1936,77 +1891,82 @@ run(void)
ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd);
cresize(w, h);
for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) {
clock_gettime(CLOCK_MONOTONIC, &last);
lastblink = last;
for (xev = actionfps;;) {
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));
}
clock_gettime(CLOCK_MONOTONIC, &now);
if (FD_ISSET(ttyfd, &rfd))
if (FD_ISSET(ttyfd, &rfd)) {
ttyread();
if (blinktimeout) {
blinkset = tattrset(ATTR_BLINK);
if (!blinkset)
MODBIT(win.mode, 0, MODE_BLINK);
}
}
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 (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;
}
/*
* 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;
if (dodraw) {
while (XPending(xw.dpy)) {
XNextEvent(xw.dpy, &ev);
if (XFilterEvent(&ev, None))
continue;
if (handler[ev.type])
(handler[ev.type])(&ev);
}
timeout = (maxlatency - TIMEDIFF(now, trigger)) \
/ maxlatency * minlatency;
if (timeout > 0)
continue; /* we have time, try to find idle */
}
/* 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);
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;
}
}
}
draw();
XFlush(xw.dpy);
drawing = 0;
}
}
@ -2028,7 +1988,7 @@ main(int argc, char *argv[])
{
xw.l = xw.t = 0;
xw.isfixed = False;
xsetcursor(cursorshape);
win.cursor = cursorshape;
ARGBEGIN {
case 'a':

Loading…
Cancel
Save