From 64ceaed00614a54ff3420c1b468287c5a78f46b5 Mon Sep 17 00:00:00 2001 From: Noah Swerhun Date: Sat, 6 Nov 2021 14:59:54 -0500 Subject: [PATCH] added boxdraw patch --- Makefile | 3 +- boxdraw.c | 194 +++ boxdraw.o | Bin 0 -> 7728 bytes boxdraw_data.h | 214 +++ config.def.h | 12 + config.def.h.orig | 478 +++++++ config.h | 12 + st | Bin 102160 -> 110856 bytes st-boxdraw_v2-0.8.3.diff | 600 +++++++++ st.c | 3 + st.c.orig | 2668 ++++++++++++++++++++++++++++++++++++++ st.h | 10 + st.h.orig | 128 ++ st.h.rej | 7 + st.o | Bin 76592 -> 77264 bytes x.c | 21 +- x.c.orig | 2070 +++++++++++++++++++++++++++++ x.o | Bin 75280 -> 75520 bytes 18 files changed, 6414 insertions(+), 6 deletions(-) create mode 100644 boxdraw.c create mode 100644 boxdraw.o create mode 100644 boxdraw_data.h create mode 100644 config.def.h.orig create mode 100644 st-boxdraw_v2-0.8.3.diff create mode 100644 st.c.orig create mode 100644 st.h.orig create mode 100644 st.h.rej create mode 100644 x.c.orig diff --git a/Makefile b/Makefile index 470ac86..6dfa212 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ include config.mk -SRC = st.c x.c +SRC = st.c x.c boxdraw.c OBJ = $(SRC:.c=.o) all: options st @@ -23,6 +23,7 @@ config.h: st.o: config.h st.h win.h x.o: arg.h config.h st.h win.h +boxdraw.o: config.h st.h boxdraw_data.h $(OBJ): config.h config.mk diff --git a/boxdraw.c b/boxdraw.c new file mode 100644 index 0000000..28a92d0 --- /dev/null +++ b/boxdraw.c @@ -0,0 +1,194 @@ +/* + * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih + * MIT/X Consortium License + */ + +#include +#include "st.h" +#include "boxdraw_data.h" + +/* Rounded non-negative integers division of n / d */ +#define DIV(n, d) (((n) + (d) / 2) / (d)) + +static Display *xdpy; +static Colormap xcmap; +static XftDraw *xd; +static Visual *xvis; + +static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort); +static void drawboxlines(int, int, int, int, XftColor *, ushort); + +/* public API */ + +void +boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis) +{ + xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis; +} + +int +isboxdraw(Rune u) +{ + Rune block = u & ~0xff; + return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) || + (boxdraw_braille && block == 0x2800); +} + +/* the "index" is actually the entire shape data encoded as ushort */ +ushort +boxdrawindex(const Glyph *g) +{ + if (boxdraw_braille && (g->u & ~0xff) == 0x2800) + return BRL | (uint8_t)g->u; + if (boxdraw_bold && (g->mode & ATTR_BOLD)) + return BDB | boxdata[(uint8_t)g->u]; + return boxdata[(uint8_t)g->u]; +} + +void +drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg, + const XftGlyphFontSpec *specs, int len) +{ + for ( ; len-- > 0; x += cw, specs++) + drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph); +} + +/* implementation */ + +void +drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd) +{ + ushort cat = bd & ~(BDB | 0xff); /* mask out bold and data */ + if (bd & (BDL | BDA)) { + /* lines (light/double/heavy/arcs) */ + drawboxlines(x, y, w, h, fg, bd); + + } else if (cat == BBD) { + /* lower (8-X)/8 block */ + int d = DIV((uint8_t)bd * h, 8); + XftDrawRect(xd, fg, x, y + d, w, h - d); + + } else if (cat == BBU) { + /* upper X/8 block */ + XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8)); + + } else if (cat == BBL) { + /* left X/8 block */ + XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h); + + } else if (cat == BBR) { + /* right (8-X)/8 block */ + int d = DIV((uint8_t)bd * w, 8); + XftDrawRect(xd, fg, x + d, y, w - d, h); + + } else if (cat == BBQ) { + /* Quadrants */ + int w2 = DIV(w, 2), h2 = DIV(h, 2); + if (bd & TL) + XftDrawRect(xd, fg, x, y, w2, h2); + if (bd & TR) + XftDrawRect(xd, fg, x + w2, y, w - w2, h2); + if (bd & BL) + XftDrawRect(xd, fg, x, y + h2, w2, h - h2); + if (bd & BR) + XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2); + + } else if (bd & BBS) { + /* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */ + int d = (uint8_t)bd; + XftColor xfc; + XRenderColor xrc = { .alpha = 0xffff }; + + xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4); + xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4); + xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4); + + XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc); + XftDrawRect(xd, &xfc, x, y, w, h); + XftColorFree(xdpy, xvis, xcmap, &xfc); + + } else if (cat == BRL) { + /* braille, each data bit corresponds to one dot at 2x4 grid */ + int w1 = DIV(w, 2); + int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4); + + if (bd & 1) XftDrawRect(xd, fg, x, y, w1, h1); + if (bd & 2) XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1); + if (bd & 4) XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2); + if (bd & 8) XftDrawRect(xd, fg, x + w1, y, w - w1, h1); + if (bd & 16) XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1); + if (bd & 32) XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2); + if (bd & 64) XftDrawRect(xd, fg, x, y + h3, w1, h - h3); + if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3); + + } +} + +void +drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd) +{ + /* s: stem thickness. width/8 roughly matches underscore thickness. */ + /* We draw bold as 1.5 * normal-stem and at least 1px thicker. */ + /* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */ + int mwh = MIN(w, h); + int base_s = MAX(1, DIV(mwh, 8)); + int bold = (bd & BDB) && mwh >= 6; /* possibly ignore boldness */ + int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s; + int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2); + /* the s-by-s square (x + w2, y + h2, s, s) is the center texel. */ + /* The base length (per direction till edge) includes this square. */ + + int light = bd & (LL | LU | LR | LD); + int double_ = bd & (DL | DU | DR | DD); + + if (light) { + /* d: additional (negative) length to not-draw the center */ + /* texel - at arcs and avoid drawing inside (some) doubles */ + int arc = bd & BDA; + int multi_light = light & (light - 1); + int multi_double = double_ & (double_ - 1); + /* light crosses double only at DH+LV, DV+LH (ref. shapes) */ + int d = arc || (multi_double && !multi_light) ? -s : 0; + + if (bd & LL) + XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s); + if (bd & LU) + XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d); + if (bd & LR) + XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s); + if (bd & LD) + XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d); + } + + /* double lines - also align with light to form heavy when combined */ + if (double_) { + /* + * going clockwise, for each double-ray: p is additional length + * to the single-ray nearer to the previous direction, and n to + * the next. p and n adjust from the base length to lengths + * which consider other doubles - shorter to avoid intersections + * (p, n), or longer to draw the far-corner texel (n). + */ + int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD; + if (dl) { + int p = dd ? -s : 0, n = du ? -s : dd ? s : 0; + XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s); + XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s); + } + if (du) { + int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0; + XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p); + XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n); + } + if (dr) { + int p = du ? -s : 0, n = dd ? -s : du ? s : 0; + XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s); + XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s); + } + if (dd) { + int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0; + XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p); + XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n); + } + } +} diff --git a/boxdraw.o b/boxdraw.o new file mode 100644 index 0000000000000000000000000000000000000000..3080fdc92aa26916aecfad85938e0fc9df48d565 GIT binary patch literal 7728 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 0: keypad application mode enabled + * * = 2: term.numlock = 1 + * * < 0: keypad application mode disabled + * appcursor value: + * * 0: no value + * * > 0: cursor application mode enabled + * * < 0: cursor application mode disabled + * + * Be careful with the order of the definitions because st searches in + * 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. + */ +static KeySym mappedkeys[] = { -1 }; + +/* + * State bits to ignore when matching key or button events. By default, + * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. + */ +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; + +/* + * This is the huge key array which defines all compatibility to the Linux + * world. Please decide about changes wisely. + */ +static Key key[] = { + /* keysym mask string appkey appcursor */ + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + { XK_KP_End, ControlMask, "\033[J", -1, 0}, + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + { XK_Return, Mod1Mask, "\033\r", 0, 0}, + { XK_Return, XK_ANY_MOD, "\r", 0, 0}, + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_Insert, ControlMask, "\033[L", -1, 0}, + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_Delete, ControlMask, "\033[M", -1, 0}, + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, +}; + +/* + * Selection types' masks. + * Use the same masks as usual. + * Button1Mask is always unset, to make masks match between ButtonPress. + * ButtonRelease and MotionNotify. + * If no match is found, regular selection is used. + */ +static uint selmasks[] = { + [SEL_RECTANGULAR] = Mod1Mask, +}; + +/* + * Printable characters in ASCII, used to estimate the advance width + * of single wide characters. + */ +static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; diff --git a/config.h b/config.h index 3bdef97..743d063 100644 --- a/config.h +++ b/config.h @@ -68,6 +68,18 @@ static unsigned int blinktimeout = 800; */ static unsigned int cursorthickness = 2; +/* + * 1: render most of the lines/blocks characters without using the font for + * perfect alignment between cells (U2500 - U259F except dashes/diagonals). + * Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored. + * 0: disable (render all U25XX glyphs normally from the font). + */ +const int boxdraw = 1; +const int boxdraw_bold = 1; + +/* braille (U28XX): 1: render as adjacent "pixels", 0: use font */ +const int boxdraw_braille = 1; + /* * bell volume. It must be a value between -100 and 100. Use 0 for disabling * it diff --git a/st b/st index 420bbab2a3c54d0105a6661bbee6723133372d16..f86821937b4825a99de4d4eb63098e1bca1419fe 100755 GIT binary patch literal 110856 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Ⓢ}aS zLygg_89PP)N$w#X47Fk|qDc$$s42qx=1oV&&1Dy!+xUrp=zQT(9vNZ7S_R5z1Yu7K7eoXg@QrbU2C_as+>(~C`!VSjZ zDZEDJo%onp8fAhbRZ9O)jEho)+MnD)Ee*MeZ}7vK%VfXy{}yWflI)ir;al*zx48V; zUtD;F^Az19*>9mCyoZB%D!%qN7ixcV=@(zu&-oPOxACKBzjUGYS0|sGlK%-|%vYr! zy6{jOS-x{A??2~x=lsff!rxNpiQscZ6yaz1a=zw!3NJo7!9hHQ^1DOM*L*^u7av8B zd6hhHe09F^-%?`?rW!D>IYK&A$z_gj0LBzw^Pq*#iVQ2f;d{ZqdT4ru{X3>d$09q! z51imP7O!?`z3V~S(kbK1FXS{rd87KFkZ{sK_? BD;NL( literal 102160 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 diff --git a/st-boxdraw_v2-0.8.3.diff b/st-boxdraw_v2-0.8.3.diff new file mode 100644 index 0000000..04a868c --- /dev/null +++ b/st-boxdraw_v2-0.8.3.diff @@ -0,0 +1,600 @@ +From 3f3b80b9966c60086f4ed80ce4de0cbf03468d36 Mon Sep 17 00:00:00 2001 +From: "Avi Halachmi (:avih)" +Date: Wed, 26 Dec 2018 14:51:45 +0200 +Subject: [PATCH] boxdraw_v2: custom render lines/blocks/braille for perfect + alignment + +It seems impossible to ensure that blocks and line drawing glyphs +align without visible gaps for all combinations of arbitrary font, +size and width/height scale factor. + +This commit adds options to render most of the lines/blocks and +braille codepoints without using the font such that they align +perfectly regardless of font, size or other configuration values. + +Supported codepoints are U+2500 - U+259F except dashes/diagonals, +and U28XX. + +The lines/blocks data is stored as 16-bit values at boxdraw_data.h + +boxdraw/braille are independent, disabled by default at config[.def].h +--- + Makefile | 3 +- + boxdraw.c | 194 ++++++++++++++++++++++++++++++++++++++++++++ + boxdraw_data.h | 214 +++++++++++++++++++++++++++++++++++++++++++++++++ + config.def.h | 12 +++ + st.c | 3 + + st.h | 10 +++ + x.c | 21 +++-- + 7 files changed, 451 insertions(+), 6 deletions(-) + create mode 100644 boxdraw.c + create mode 100644 boxdraw_data.h + +diff --git a/Makefile b/Makefile +index 470ac86..6dfa212 100644 +--- a/Makefile ++++ b/Makefile +@@ -4,7 +4,7 @@ + + include config.mk + +-SRC = st.c x.c ++SRC = st.c x.c boxdraw.c + OBJ = $(SRC:.c=.o) + + all: options st +@@ -23,6 +23,7 @@ config.h: + + st.o: config.h st.h win.h + x.o: arg.h config.h st.h win.h ++boxdraw.o: config.h st.h boxdraw_data.h + + $(OBJ): config.h config.mk + +diff --git a/boxdraw.c b/boxdraw.c +new file mode 100644 +index 0000000..28a92d0 +--- /dev/null ++++ b/boxdraw.c +@@ -0,0 +1,194 @@ ++/* ++ * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih ++ * MIT/X Consortium License ++ */ ++ ++#include ++#include "st.h" ++#include "boxdraw_data.h" ++ ++/* Rounded non-negative integers division of n / d */ ++#define DIV(n, d) (((n) + (d) / 2) / (d)) ++ ++static Display *xdpy; ++static Colormap xcmap; ++static XftDraw *xd; ++static Visual *xvis; ++ ++static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort); ++static void drawboxlines(int, int, int, int, XftColor *, ushort); ++ ++/* public API */ ++ ++void ++boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis) ++{ ++ xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis; ++} ++ ++int ++isboxdraw(Rune u) ++{ ++ Rune block = u & ~0xff; ++ return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) || ++ (boxdraw_braille && block == 0x2800); ++} ++ ++/* the "index" is actually the entire shape data encoded as ushort */ ++ushort ++boxdrawindex(const Glyph *g) ++{ ++ if (boxdraw_braille && (g->u & ~0xff) == 0x2800) ++ return BRL | (uint8_t)g->u; ++ if (boxdraw_bold && (g->mode & ATTR_BOLD)) ++ return BDB | boxdata[(uint8_t)g->u]; ++ return boxdata[(uint8_t)g->u]; ++} ++ ++void ++drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg, ++ const XftGlyphFontSpec *specs, int len) ++{ ++ for ( ; len-- > 0; x += cw, specs++) ++ drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph); ++} ++ ++/* implementation */ ++ ++void ++drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd) ++{ ++ ushort cat = bd & ~(BDB | 0xff); /* mask out bold and data */ ++ if (bd & (BDL | BDA)) { ++ /* lines (light/double/heavy/arcs) */ ++ drawboxlines(x, y, w, h, fg, bd); ++ ++ } else if (cat == BBD) { ++ /* lower (8-X)/8 block */ ++ int d = DIV((uint8_t)bd * h, 8); ++ XftDrawRect(xd, fg, x, y + d, w, h - d); ++ ++ } else if (cat == BBU) { ++ /* upper X/8 block */ ++ XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8)); ++ ++ } else if (cat == BBL) { ++ /* left X/8 block */ ++ XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h); ++ ++ } else if (cat == BBR) { ++ /* right (8-X)/8 block */ ++ int d = DIV((uint8_t)bd * w, 8); ++ XftDrawRect(xd, fg, x + d, y, w - d, h); ++ ++ } else if (cat == BBQ) { ++ /* Quadrants */ ++ int w2 = DIV(w, 2), h2 = DIV(h, 2); ++ if (bd & TL) ++ XftDrawRect(xd, fg, x, y, w2, h2); ++ if (bd & TR) ++ XftDrawRect(xd, fg, x + w2, y, w - w2, h2); ++ if (bd & BL) ++ XftDrawRect(xd, fg, x, y + h2, w2, h - h2); ++ if (bd & BR) ++ XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2); ++ ++ } else if (bd & BBS) { ++ /* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */ ++ int d = (uint8_t)bd; ++ XftColor xfc; ++ XRenderColor xrc = { .alpha = 0xffff }; ++ ++ xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4); ++ xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4); ++ xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4); ++ ++ XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc); ++ XftDrawRect(xd, &xfc, x, y, w, h); ++ XftColorFree(xdpy, xvis, xcmap, &xfc); ++ ++ } else if (cat == BRL) { ++ /* braille, each data bit corresponds to one dot at 2x4 grid */ ++ int w1 = DIV(w, 2); ++ int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4); ++ ++ if (bd & 1) XftDrawRect(xd, fg, x, y, w1, h1); ++ if (bd & 2) XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1); ++ if (bd & 4) XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2); ++ if (bd & 8) XftDrawRect(xd, fg, x + w1, y, w - w1, h1); ++ if (bd & 16) XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1); ++ if (bd & 32) XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2); ++ if (bd & 64) XftDrawRect(xd, fg, x, y + h3, w1, h - h3); ++ if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3); ++ ++ } ++} ++ ++void ++drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd) ++{ ++ /* s: stem thickness. width/8 roughly matches underscore thickness. */ ++ /* We draw bold as 1.5 * normal-stem and at least 1px thicker. */ ++ /* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */ ++ int mwh = MIN(w, h); ++ int base_s = MAX(1, DIV(mwh, 8)); ++ int bold = (bd & BDB) && mwh >= 6; /* possibly ignore boldness */ ++ int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s; ++ int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2); ++ /* the s-by-s square (x + w2, y + h2, s, s) is the center texel. */ ++ /* The base length (per direction till edge) includes this square. */ ++ ++ int light = bd & (LL | LU | LR | LD); ++ int double_ = bd & (DL | DU | DR | DD); ++ ++ if (light) { ++ /* d: additional (negative) length to not-draw the center */ ++ /* texel - at arcs and avoid drawing inside (some) doubles */ ++ int arc = bd & BDA; ++ int multi_light = light & (light - 1); ++ int multi_double = double_ & (double_ - 1); ++ /* light crosses double only at DH+LV, DV+LH (ref. shapes) */ ++ int d = arc || (multi_double && !multi_light) ? -s : 0; ++ ++ if (bd & LL) ++ XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s); ++ if (bd & LU) ++ XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d); ++ if (bd & LR) ++ XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s); ++ if (bd & LD) ++ XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d); ++ } ++ ++ /* double lines - also align with light to form heavy when combined */ ++ if (double_) { ++ /* ++ * going clockwise, for each double-ray: p is additional length ++ * to the single-ray nearer to the previous direction, and n to ++ * the next. p and n adjust from the base length to lengths ++ * which consider other doubles - shorter to avoid intersections ++ * (p, n), or longer to draw the far-corner texel (n). ++ */ ++ int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD; ++ if (dl) { ++ int p = dd ? -s : 0, n = du ? -s : dd ? s : 0; ++ XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s); ++ XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s); ++ } ++ if (du) { ++ int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0; ++ XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p); ++ XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n); ++ } ++ if (dr) { ++ int p = du ? -s : 0, n = dd ? -s : du ? s : 0; ++ XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s); ++ XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s); ++ } ++ if (dd) { ++ int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0; ++ XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p); ++ XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n); ++ } ++ } ++} +diff --git a/boxdraw_data.h b/boxdraw_data.h +new file mode 100644 +index 0000000..7890500 +--- /dev/null ++++ b/boxdraw_data.h +@@ -0,0 +1,214 @@ ++/* ++ * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih ++ * MIT/X Consortium License ++ */ ++ ++/* ++ * U+25XX codepoints data ++ * ++ * References: ++ * http://www.unicode.org/charts/PDF/U2500.pdf ++ * http://www.unicode.org/charts/PDF/U2580.pdf ++ * ++ * Test page: ++ * https://github.com/GNOME/vte/blob/master/doc/boxes.txt ++ */ ++ ++/* Each shape is encoded as 16-bits. Higher bits are category, lower are data */ ++/* Categories (mutually exclusive except BDB): */ ++/* For convenience, BDL/BDA/BBS/BDB are 1 bit each, the rest are enums */ ++#define BDL (1<<8) /* Box Draw Lines (light/double/heavy) */ ++#define BDA (1<<9) /* Box Draw Arc (light) */ ++ ++#define BBD (1<<10) /* Box Block Down (lower) X/8 */ ++#define BBL (2<<10) /* Box Block Left X/8 */ ++#define BBU (3<<10) /* Box Block Upper X/8 */ ++#define BBR (4<<10) /* Box Block Right X/8 */ ++#define BBQ (5<<10) /* Box Block Quadrants */ ++#define BRL (6<<10) /* Box Braille (data is lower byte of U28XX) */ ++ ++#define BBS (1<<14) /* Box Block Shades */ ++#define BDB (1<<15) /* Box Draw is Bold */ ++ ++/* (BDL/BDA) Light/Double/Heavy x Left/Up/Right/Down/Horizontal/Vertical */ ++/* Heavy is light+double (literally drawing light+double align to form heavy) */ ++#define LL (1<<0) ++#define LU (1<<1) ++#define LR (1<<2) ++#define LD (1<<3) ++#define LH (LL+LR) ++#define LV (LU+LD) ++ ++#define DL (1<<4) ++#define DU (1<<5) ++#define DR (1<<6) ++#define DD (1<<7) ++#define DH (DL+DR) ++#define DV (DU+DD) ++ ++#define HL (LL+DL) ++#define HU (LU+DU) ++#define HR (LR+DR) ++#define HD (LD+DD) ++#define HH (HL+HR) ++#define HV (HU+HD) ++ ++/* (BBQ) Quadrants Top/Bottom x Left/Right */ ++#define TL (1<<0) ++#define TR (1<<1) ++#define BL (1<<2) ++#define BR (1<<3) ++ ++/* Data for U+2500 - U+259F except dashes/diagonals */ ++static const unsigned short boxdata[256] = { ++ /* light lines */ ++ [0x00] = BDL + LH, /* light horizontal */ ++ [0x02] = BDL + LV, /* light vertical */ ++ [0x0c] = BDL + LD + LR, /* light down and right */ ++ [0x10] = BDL + LD + LL, /* light down and left */ ++ [0x14] = BDL + LU + LR, /* light up and right */ ++ [0x18] = BDL + LU + LL, /* light up and left */ ++ [0x1c] = BDL + LV + LR, /* light vertical and right */ ++ [0x24] = BDL + LV + LL, /* light vertical and left */ ++ [0x2c] = BDL + LH + LD, /* light horizontal and down */ ++ [0x34] = BDL + LH + LU, /* light horizontal and up */ ++ [0x3c] = BDL + LV + LH, /* light vertical and horizontal */ ++ [0x74] = BDL + LL, /* light left */ ++ [0x75] = BDL + LU, /* light up */ ++ [0x76] = BDL + LR, /* light right */ ++ [0x77] = BDL + LD, /* light down */ ++ ++ /* heavy [+light] lines */ ++ [0x01] = BDL + HH, ++ [0x03] = BDL + HV, ++ [0x0d] = BDL + HR + LD, ++ [0x0e] = BDL + HD + LR, ++ [0x0f] = BDL + HD + HR, ++ [0x11] = BDL + HL + LD, ++ [0x12] = BDL + HD + LL, ++ [0x13] = BDL + HD + HL, ++ [0x15] = BDL + HR + LU, ++ [0x16] = BDL + HU + LR, ++ [0x17] = BDL + HU + HR, ++ [0x19] = BDL + HL + LU, ++ [0x1a] = BDL + HU + LL, ++ [0x1b] = BDL + HU + HL, ++ [0x1d] = BDL + HR + LV, ++ [0x1e] = BDL + HU + LD + LR, ++ [0x1f] = BDL + HD + LR + LU, ++ [0x20] = BDL + HV + LR, ++ [0x21] = BDL + HU + HR + LD, ++ [0x22] = BDL + HD + HR + LU, ++ [0x23] = BDL + HV + HR, ++ [0x25] = BDL + HL + LV, ++ [0x26] = BDL + HU + LD + LL, ++ [0x27] = BDL + HD + LU + LL, ++ [0x28] = BDL + HV + LL, ++ [0x29] = BDL + HU + HL + LD, ++ [0x2a] = BDL + HD + HL + LU, ++ [0x2b] = BDL + HV + HL, ++ [0x2d] = BDL + HL + LD + LR, ++ [0x2e] = BDL + HR + LL + LD, ++ [0x2f] = BDL + HH + LD, ++ [0x30] = BDL + HD + LH, ++ [0x31] = BDL + HD + HL + LR, ++ [0x32] = BDL + HR + HD + LL, ++ [0x33] = BDL + HH + HD, ++ [0x35] = BDL + HL + LU + LR, ++ [0x36] = BDL + HR + LU + LL, ++ [0x37] = BDL + HH + LU, ++ [0x38] = BDL + HU + LH, ++ [0x39] = BDL + HU + HL + LR, ++ [0x3a] = BDL + HU + HR + LL, ++ [0x3b] = BDL + HH + HU, ++ [0x3d] = BDL + HL + LV + LR, ++ [0x3e] = BDL + HR + LV + LL, ++ [0x3f] = BDL + HH + LV, ++ [0x40] = BDL + HU + LH + LD, ++ [0x41] = BDL + HD + LH + LU, ++ [0x42] = BDL + HV + LH, ++ [0x43] = BDL + HU + HL + LD + LR, ++ [0x44] = BDL + HU + HR + LD + LL, ++ [0x45] = BDL + HD + HL + LU + LR, ++ [0x46] = BDL + HD + HR + LU + LL, ++ [0x47] = BDL + HH + HU + LD, ++ [0x48] = BDL + HH + HD + LU, ++ [0x49] = BDL + HV + HL + LR, ++ [0x4a] = BDL + HV + HR + LL, ++ [0x4b] = BDL + HV + HH, ++ [0x78] = BDL + HL, ++ [0x79] = BDL + HU, ++ [0x7a] = BDL + HR, ++ [0x7b] = BDL + HD, ++ [0x7c] = BDL + HR + LL, ++ [0x7d] = BDL + HD + LU, ++ [0x7e] = BDL + HL + LR, ++ [0x7f] = BDL + HU + LD, ++ ++ /* double [+light] lines */ ++ [0x50] = BDL + DH, ++ [0x51] = BDL + DV, ++ [0x52] = BDL + DR + LD, ++ [0x53] = BDL + DD + LR, ++ [0x54] = BDL + DR + DD, ++ [0x55] = BDL + DL + LD, ++ [0x56] = BDL + DD + LL, ++ [0x57] = BDL + DL + DD, ++ [0x58] = BDL + DR + LU, ++ [0x59] = BDL + DU + LR, ++ [0x5a] = BDL + DU + DR, ++ [0x5b] = BDL + DL + LU, ++ [0x5c] = BDL + DU + LL, ++ [0x5d] = BDL + DL + DU, ++ [0x5e] = BDL + DR + LV, ++ [0x5f] = BDL + DV + LR, ++ [0x60] = BDL + DV + DR, ++ [0x61] = BDL + DL + LV, ++ [0x62] = BDL + DV + LL, ++ [0x63] = BDL + DV + DL, ++ [0x64] = BDL + DH + LD, ++ [0x65] = BDL + DD + LH, ++ [0x66] = BDL + DD + DH, ++ [0x67] = BDL + DH + LU, ++ [0x68] = BDL + DU + LH, ++ [0x69] = BDL + DH + DU, ++ [0x6a] = BDL + DH + LV, ++ [0x6b] = BDL + DV + LH, ++ [0x6c] = BDL + DH + DV, ++ ++ /* (light) arcs */ ++ [0x6d] = BDA + LD + LR, ++ [0x6e] = BDA + LD + LL, ++ [0x6f] = BDA + LU + LL, ++ [0x70] = BDA + LU + LR, ++ ++ /* Lower (Down) X/8 block (data is 8 - X) */ ++ [0x81] = BBD + 7, [0x82] = BBD + 6, [0x83] = BBD + 5, [0x84] = BBD + 4, ++ [0x85] = BBD + 3, [0x86] = BBD + 2, [0x87] = BBD + 1, [0x88] = BBD + 0, ++ ++ /* Left X/8 block (data is X) */ ++ [0x89] = BBL + 7, [0x8a] = BBL + 6, [0x8b] = BBL + 5, [0x8c] = BBL + 4, ++ [0x8d] = BBL + 3, [0x8e] = BBL + 2, [0x8f] = BBL + 1, ++ ++ /* upper 1/2 (4/8), 1/8 block (X), right 1/2, 1/8 block (8-X) */ ++ [0x80] = BBU + 4, [0x94] = BBU + 1, ++ [0x90] = BBR + 4, [0x95] = BBR + 7, ++ ++ /* Quadrants */ ++ [0x96] = BBQ + BL, ++ [0x97] = BBQ + BR, ++ [0x98] = BBQ + TL, ++ [0x99] = BBQ + TL + BL + BR, ++ [0x9a] = BBQ + TL + BR, ++ [0x9b] = BBQ + TL + TR + BL, ++ [0x9c] = BBQ + TL + TR + BR, ++ [0x9d] = BBQ + TR, ++ [0x9e] = BBQ + BL + TR, ++ [0x9f] = BBQ + BL + TR + BR, ++ ++ /* Shades, data is an alpha value in 25% units (1/4, 1/2, 3/4) */ ++ [0x91] = BBS + 1, [0x92] = BBS + 2, [0x93] = BBS + 3, ++ ++ /* U+2504 - U+250B, U+254C - U+254F: unsupported (dashes) */ ++ /* U+2571 - U+2573: unsupported (diagonals) */ ++}; +diff --git a/config.def.h b/config.def.h +index 0895a1f..bf6718b 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -58,6 +58,18 @@ static unsigned int blinktimeout = 800; + */ + static unsigned int cursorthickness = 2; + ++/* ++ * 1: render most of the lines/blocks characters without using the font for ++ * perfect alignment between cells (U2500 - U259F except dashes/diagonals). ++ * Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored. ++ * 0: disable (render all U25XX glyphs normally from the font). ++ */ ++const int boxdraw = 0; ++const int boxdraw_bold = 0; ++ ++/* braille (U28XX): 1: render as adjacent "pixels", 0: use font */ ++const int boxdraw_braille = 0; ++ + /* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it +diff --git a/st.c b/st.c +index 0ce6ac2..c035e19 100644 +--- a/st.c ++++ b/st.c +@@ -1230,6 +1230,9 @@ tsetchar(Rune u, Glyph *attr, int x, int y) + term.dirty[y] = 1; + term.line[y][x] = *attr; + term.line[y][x].u = u; ++ ++ if (isboxdraw(u)) ++ term.line[y][x].mode |= ATTR_BOXDRAW; + } + + void +diff --git a/st.h b/st.h +index d978458..a6c382a 100644 +--- a/st.h ++++ b/st.h +@@ -33,6 +33,7 @@ enum glyph_attribute { + ATTR_WRAP = 1 << 8, + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, ++ ATTR_BOXDRAW = 1 << 11, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, + }; + +@@ -111,6 +112,14 @@ void *xmalloc(size_t); + void *xrealloc(void *, size_t); + char *xstrdup(char *); + ++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 ++ + /* config.h globals */ + extern char *utmp; + extern char *scroll; +@@ -122,3 +131,4 @@ extern char *termname; + extern unsigned int tabspaces; + extern unsigned int defaultfg; + extern unsigned int defaultbg; ++extern const int boxdraw, boxdraw_bold, boxdraw_braille; +diff --git a/x.c b/x.c +index e5f1737..6f7ea2c 100644 +--- a/x.c ++++ b/x.c +@@ -1205,6 +1205,8 @@ xinit(int cols, int rows) + xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); + if (xsel.xtarget == None) + xsel.xtarget = XA_STRING; ++ ++ boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); + } + + int +@@ -1251,8 +1253,13 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x + yp = winy + font->ascent; + } + +- /* Lookup character index with default font. */ +- glyphidx = XftCharIndex(xw.dpy, font->match, rune); ++ if (mode & ATTR_BOXDRAW) { ++ /* minor shoehorning: boxdraw uses only this ushort */ ++ glyphidx = boxdrawindex(&glyphs[i]); ++ } else { ++ /* Lookup character index with default font. */ ++ glyphidx = XftCharIndex(xw.dpy, font->match, rune); ++ } + if (glyphidx) { + specs[numspecs].font = font->match; + specs[numspecs].glyph = glyphidx; +@@ -1456,8 +1463,12 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i + r.width = width; + XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + +- /* Render the glyphs. */ +- XftDrawGlyphFontSpec(xw.draw, fg, specs, len); ++ if (base.mode & ATTR_BOXDRAW) { ++ drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); ++ } else { ++ /* Render the glyphs. */ ++ XftDrawGlyphFontSpec(xw.draw, fg, specs, len); ++ } + + /* Render underline and strikethrough. */ + if (base.mode & ATTR_UNDERLINE) { +@@ -1500,7 +1511,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) + /* + * Select the right color for the right mode. + */ +- g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; ++ g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW; + + if (IS_SET(MODE_REVERSE)) { + g.mode |= ATTR_REVERSE; + +base-commit: 43a395ae91f7d67ce694e65edeaa7bbc720dd027 +-- +2.20.1 + diff --git a/st.c b/st.c index edec064..6b2fdd8 100644 --- a/st.c +++ b/st.c @@ -1281,6 +1281,9 @@ tsetchar(Rune u, Glyph *attr, int x, int y) term.dirty[y] = 1; term.line[y][x] = *attr; term.line[y][x].u = u; + + if (isboxdraw(u)) + term.line[y][x].mode |= ATTR_BOXDRAW; } void diff --git a/st.c.orig b/st.c.orig new file mode 100644 index 0000000..edec064 --- /dev/null +++ b/st.c.orig @@ -0,0 +1,2668 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st.h" +#include "win.h" + +#if defined(__linux) + #include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + #include +#elif defined(__FreeBSD__) || defined(__DragonFly__) + #include +#endif + +/* Arbitrary sizes */ +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 +#define ESC_BUF_SIZ (128*UTF_SIZ) +#define ESC_ARG_SIZ 16 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ +#define HISTSIZE 2000 + +/* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#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)) +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ + term.scr + HISTSIZE + 1) % HISTSIZE] : \ + term.line[(y) - term.scr]) + +enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_ALTSCREEN = 1 << 2, + MODE_CRLF = 1 << 3, + MODE_ECHO = 1 << 4, + MODE_PRINT = 1 << 5, + MODE_UTF8 = 1 << 6, +}; + +enum cursor_movement { + CURSOR_SAVE, + CURSOR_LOAD +}; + +enum cursor_state { + CURSOR_DEFAULT = 0, + CURSOR_WRAPNEXT = 1, + CURSOR_ORIGIN = 2 +}; + +enum charset { + CS_GRAPHIC0, + CS_GRAPHIC1, + CS_UK, + CS_USA, + CS_MULTI, + CS_GER, + CS_FIN +}; + +enum escape_state { + ESC_START = 1, + ESC_CSI = 2, + ESC_STR = 4, /* DCS, 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, +}; + +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +typedef struct { + int mode; + int type; + int snap; + /* + * Selection variables: + * nb – normalized coordinates of the beginning of the selection + * ne – normalized coordinates of the end of the selection + * ob – original coordinates of the beginning of the selection + * oe – original coordinates of the end of the selection + */ + struct { + int x, y; + } nb, ne, ob, oe; + + int alt; +} Selection; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ + Line hist[HISTSIZE]; /* history buffer */ + int histi; /* history index */ + int scr; /* scroll back */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + 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 */ +/* ESC '[' [[ [] [;]] []] */ +typedef struct { + char buf[ESC_BUF_SIZ]; /* raw string */ + size_t len; /* raw string length */ + char priv; + int arg[ESC_ARG_SIZ]; + int narg; /* nb of args */ + char mode[2]; +} CSIEscape; + +/* STR Escape sequence structs */ +/* ESC type [[ [] [;]] ] ESC '\' */ +typedef struct { + char type; /* ESC type ... */ + char *buf; /* allocated raw string */ + size_t siz; /* allocation size */ + size_t len; /* raw string length */ + char *args[STR_ARG_SIZ]; + int narg; /* nb of args */ +} STREscape; + +static void execsh(char *, char **); +static void stty(char **); +static void sigchld(int); +static void ttywriteraw(const char *, size_t); + +static void csidump(void); +static void csihandle(void); +static void csiparse(void); +static void csireset(void); +static int eschandle(uchar); +static void strdump(void); +static void strhandle(void); +static void strparse(void); +static void strreset(void); + +static void tprinter(char *, size_t); +static void tdumpsel(void); +static void tdumpline(int); +static void tdump(void); +static void tclearregion(int, int, int, int); +static void tcursor(int); +static void tdeletechar(int); +static void tdeleteline(int); +static void tinsertblank(int); +static void tinsertblankline(int); +static int tlinelen(int); +static void tmoveto(int, int); +static void tmoveato(int, int); +static void tnewline(int); +static void tputtab(int); +static void tputc(Rune); +static void treset(void); +static void tscrollup(int, int, int); +static void tscrolldown(int, 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, 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(int *, int *, int); +static void tdeftran(char); +static void tstrsequence(uchar); + +static void drawregion(int, int, int, int); + +static void selnormalize(void); +static void selscroll(int, int); +static void selsnap(int *, int *, int); + +static size_t utf8decode(const char *, Rune *, size_t); +static Rune utf8decodebyte(char, size_t *); +static char utf8encodebyte(Rune, size_t); +static size_t utf8validate(Rune *, size_t); + +static char *base64dec(const char *); +static char base64dec_getc(const char **); + +static ssize_t xwrite(int, const char *, size_t); + +/* Globals */ +static Term term; +static Selection sel; +static CSIEscape csiescseq; +static STREscape strescseq; +static int iofd = 1; +static int cmdfd; +static pid_t pid; + +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) +{ + size_t aux = len; + ssize_t r; + + while (len > 0) { + r = write(fd, s, len); + if (r < 0) + return r; + len -= r; + s += r; + } + + return aux; +} + +void * +xmalloc(size_t len) +{ + void *p; + + if (!(p = malloc(len))) + die("malloc: %s\n", strerror(errno)); + + return p; +} + +void * +xrealloc(void *p, size_t len) +{ + if ((p = realloc(p, len)) == NULL) + die("realloc: %s\n", strerror(errno)); + + return p; +} + +char * +xstrdup(char *s) +{ + if ((s = strdup(s)) == NULL) + die("strdup: %s\n", strerror(errno)); + + return s; +} + +size_t +utf8decode(const char *c, Rune *u, size_t clen) +{ + size_t i, j, len, type; + Rune udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type != 0) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Rune +utf8decodebyte(char c, size_t *i) +{ + for (*i = 0; *i < LEN(utfmask); ++(*i)) + if (((uchar)c & utfmask[*i]) == utfbyte[*i]) + return (uchar)c & ~utfmask[*i]; + + return 0; +} + +size_t +utf8encode(Rune u, char *c) +{ + size_t len, i; + + len = utf8validate(&u, 0); + if (len > UTF_SIZ) + return 0; + + for (i = len - 1; i != 0; --i) { + c[i] = utf8encodebyte(u, 0); + u >>= 6; + } + c[0] = utf8encodebyte(u, len); + + return len; +} + +char +utf8encodebyte(Rune u, size_t i) +{ + return utfbyte[i] | (u & ~utfmask[i]); +} + +size_t +utf8validate(Rune *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + + return i; +} + +static const char base64_digits[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +char +base64dec_getc(const char **src) +{ + while (**src && !isprint(**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char * +base64dec(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + + if (in_len % 4) + in_len += 4 - (in_len % 4); + result = dst = xmalloc(in_len / 4 * 3 + 1); + while (*src) { + int a = base64_digits[(unsigned char) base64dec_getc(&src)]; + int b = base64_digits[(unsigned char) base64dec_getc(&src)]; + int c = base64_digits[(unsigned char) base64dec_getc(&src)]; + int d = base64_digits[(unsigned char) base64dec_getc(&src)]; + + /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ + if (a == -1 || b == -1) + break; + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (c == -1) + break; + *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == -1) + break; + *dst++ = ((c & 0x03) << 6) | d; + } + *dst = '\0'; + return result; +} + +void +selinit(void) +{ + sel.mode = SEL_IDLE; + sel.snap = 0; + sel.ob.x = -1; +} + +int +tlinelen(int y) +{ + int i = term.col; + + if (TLINE(y)[i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && TLINE(y)[i - 1].u == ' ') + --i; + + return i; +} + +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type, int done) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + + if (sel.mode == SEL_IDLE) + return; + if (done && sel.mode == SEL_EMPTY) { + selclear(); + return; + } + + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + + sel.mode = done ? SEL_IDLE : SEL_READY; +} + +void +selnormalize(void) +{ + int i; + + if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { + sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; + sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; + } else { + sel.nb.x = MIN(sel.ob.x, sel.oe.x); + sel.ne.x = MAX(sel.ob.x, sel.oe.x); + } + sel.nb.y = MIN(sel.ob.y, sel.oe.y); + sel.ne.y = MAX(sel.ob.y, sel.oe.y); + + selsnap(&sel.nb.x, &sel.nb.y, -1); + selsnap(&sel.ne.x, &sel.ne.y, +1); + + /* expand selection over line breaks */ + if (sel.type == SEL_RECTANGULAR) + return; + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) + sel.nb.x = i; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; +} + +int +selected(int x, int y) +{ + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) + return 0; + + if (sel.type == SEL_RECTANGULAR) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); + + return BETWEEN(y, sel.nb.y, sel.ne.y) + && (y != sel.nb.y || x >= sel.nb.x) + && (y != sel.ne.y || x <= sel.ne.x); +} + +void +selsnap(int *x, int *y, int direction) +{ + int newx, newy, xt, yt; + int delim, prevdelim; + Glyph *gp, *prevgp; + + switch (sel.snap) { + case SNAP_WORD: + /* + * Snap around if the word wraps around at the end or + * beginning of a line. + */ + prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; + newy = *y; + if (!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) + break; + + if (direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + + gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) + break; + + *x = newx; + *y = newy; + prevgp = gp; + prevdelim = delim; + } + break; + case SNAP_LINE: + /* + * Snap around if the the previous line or the current one + * has set ATTR_WRAP at its end. Then the whole next or + * previous line will be selected. + */ + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { + if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { + if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } + break; + } +} + +char * +getsel(void) +{ + char *str, *ptr; + int y, bufsize, lastx, linelen; + Glyph *gp, *last; + + if (sel.ob.x == -1) + return NULL; + + bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; + ptr = str = xmalloc(bufsize); + + /* append every set & selected glyph to the selection */ + for (y = sel.nb.y; y <= sel.ne.y; y++) { + if ((linelen = tlinelen(y)) == 0) { + *ptr++ = '\n'; + continue; + } + + if (sel.type == SEL_RECTANGULAR) { + gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + + for ( ; gp <= last; ++gp) { + if (gp->mode & ATTR_WDUMMY) + continue; + + ptr += utf8encode(gp->u, ptr); + } + + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if ((y < sel.ne.y || lastx >= linelen) && + (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) + *ptr++ = '\n'; + } + *ptr = 0; + return str; +} + +void +selclear(void) +{ + if (sel.ob.x == -1) + return; + sel.mode = SEL_IDLE; + sel.ob.x = -1; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void +execsh(char *cmd, char **args) +{ + char *sh, *prog, *arg; + const struct passwd *pw; + + errno = 0; + if ((pw = getpwuid(getuid())) == NULL) { + if (errno) + die("getpwuid: %s\n", strerror(errno)); + else + die("who are you?\n"); + } + + if ((sh = getenv("SHELL")) == NULL) + sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; + + if (args) { + prog = args[0]; + arg = NULL; + } else if (scroll) { + prog = scroll; + arg = utmp ? utmp : sh; + } else if (utmp) { + prog = utmp; + arg = NULL; + } else { + prog = sh; + arg = NULL; + } + DEFAULT(args, ((char *[]) {prog, arg, NULL})); + + unsetenv("COLUMNS"); + unsetenv("LINES"); + unsetenv("TERMCAP"); + setenv("LOGNAME", pw->pw_name, 1); + setenv("USER", pw->pw_name, 1); + setenv("SHELL", sh, 1); + setenv("HOME", pw->pw_dir, 1); + setenv("TERM", termname, 1); + + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + execvp(prog, args); + _exit(1); +} + +void +sigchld(int a) +{ + int stat; + pid_t p; + + if ((p = waitpid(pid, &stat, WNOHANG)) < 0) + die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); + + if (pid != p) + return; + + if (WIFEXITED(stat) && WEXITSTATUS(stat)) + 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); +} + +void +stty(char **args) +{ + char cmd[_POSIX_ARG_MAX], **p, *q, *s; + size_t n, siz; + + if ((n = strlen(stty_args)) > sizeof(cmd)-1) + die("incorrect stty parameters\n"); + memcpy(cmd, stty_args, n); + q = cmd + n; + siz = sizeof(cmd) - n; + for (p = args; p && (s = *p); ++p) { + if ((n = strlen(s)) > siz-1) + die("stty parameter length too long\n"); + *q++ = ' '; + memcpy(q, s, n); + q += n; + siz -= n + 1; + } + *q = '\0'; + if (system(cmd) != 0) + perror("Couldn't call stty"); +} + +int +ttynew(char *line, char *cmd, char *out, char **args) +{ + int m, s; + + if (out) { + term.mode |= MODE_PRINT; + iofd = (!strcmp(out, "-")) ? + 1 : open(out, O_WRONLY | O_CREAT, 0666); + if (iofd < 0) { + fprintf(stderr, "Error opening %s:%s\n", + out, strerror(errno)); + } + } + + if (line) { + if ((cmdfd = open(line, O_RDWR)) < 0) + die("open line '%s' failed: %s\n", + line, strerror(errno)); + dup2(cmdfd, 0); + stty(args); + return cmdfd; + } + + /* seems to work fine on linux, openbsd and freebsd */ + if (openpty(&m, &s, NULL, NULL, NULL) < 0) + die("openpty failed: %s\n", strerror(errno)); + + switch (pid = fork()) { + case -1: + die("fork failed: %s\n", strerror(errno)); + break; + case 0: + close(iofd); + 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)); + close(s); + close(m); +#ifdef __OpenBSD__ + if (pledge("stdio getpw proc exec", NULL) == -1) + die("pledge\n"); +#endif + execsh(cmd, args); + break; + default: +#ifdef __OpenBSD__ + if (pledge("stdio rpath tty proc", NULL) == -1) + die("pledge\n"); +#endif + close(s); + cmdfd = m; + signal(SIGCHLD, sigchld); + break; + } + return cmdfd; +} + +size_t +ttyread(void) +{ + static char buf[BUFSIZ]; + static int buflen = 0; + int ret, written; + + /* append read bytes to unprocessed bytes */ + ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); + + switch (ret) { + case 0: + exit(0); + case -1: + die("couldn't read from shell: %s\n", strerror(errno)); + default: + buflen += ret; + written = twrite(buf, buflen, 0); + buflen -= written; + /* keep any incomplete UTF-8 byte sequence for the next call */ + if (buflen > 0) + memmove(buf, buf + written, buflen); + return ret; + } +} + +void +ttywrite(const char *s, size_t n, int may_echo) +{ + const char *next; + Arg arg = (Arg) { .i = term.scr }; + + kscrolldown(&arg); + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); + + if (!IS_SET(MODE_CRLF)) { + ttywriteraw(s, n); + return; + } + + /* This is similar to how the kernel handles ONLCR for ttys */ + while (n > 0) { + if (*s == '\r') { + next = s + 1; + ttywriteraw("\r\n", 2); + } else { + next = memchr(s, '\r', n); + DEFAULT(next, s + n); + ttywriteraw(s, next - s); + } + n -= next - s; + s = next; + } +} + +void +ttywriteraw(const char *s, size_t n) +{ + fd_set wfd, rfd; + ssize_t r; + size_t lim = 256; + + /* + * Remember that we are using a pty, which might be a modem line. + * Writing too much will clog the line. That's why we are doing this + * dance. + * FIXME: Migrate the world to Plan 9. + */ + while (n > 0) { + FD_ZERO(&wfd); + FD_ZERO(&rfd); + FD_SET(cmdfd, &wfd); + FD_SET(cmdfd, &rfd); + + /* Check if we can write. */ + if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(cmdfd, &wfd)) { + /* + * Only write the bytes written by ttywrite() or the + * default of 256. This seems to be a reasonable value + * for a serial line. Bigger values might clog the I/O. + */ + if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) + goto write_error; + if (r < n) { + /* + * We weren't able to write out everything. + * This means the buffer is getting full + * again. Empty it. + */ + if (n < lim) + lim = ttyread(); + n -= r; + s += r; + } else { + /* All bytes have been written. */ + break; + } + } + if (FD_ISSET(cmdfd, &rfd)) + lim = ttyread(); + } + return; + +write_error: + die("write error on tty: %s\n", strerror(errno)); +} + +void +ttyresize(int tw, int th) +{ + struct winsize w; + + w.ws_row = term.row; + w.ws_col = term.col; + w.ws_xpixel = tw; + w.ws_ypixel = th; + if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) + fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); +} + +void +ttyhangup() +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + +int +tattrset(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) + return 1; + } + } + + return 0; +} + +void +tsetdirt(int top, int bot) +{ + int i; + + LIMIT(top, 0, term.row-1); + LIMIT(bot, 0, term.row-1); + + for (i = top; i <= bot; i++) + term.dirty[i] = 1; +} + +void +tsetdirtattr(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) { + tsetdirt(i, i); + break; + } + } + } +} + +void +tfulldirt(void) +{ + tsetdirt(0, term.row-1); +} + +void +tcursor(int mode) +{ + static TCursor c[2]; + int alt = IS_SET(MODE_ALTSCREEN); + + if (mode == CURSOR_SAVE) { + c[alt] = term.c; + } else if (mode == CURSOR_LOAD) { + term.c = c[alt]; + tmoveto(c[alt].x, c[alt].y); + } +} + +void +treset(void) +{ + uint i; + + term.c = (TCursor){{ + .mode = ATTR_NULL, + .fg = defaultfg, + .bg = defaultbg + }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) + term.tabs[i] = 1; + term.top = 0; + term.bot = term.row - 1; + term.mode = MODE_WRAP|MODE_UTF8; + memset(term.trantbl, CS_USA, sizeof(term.trantbl)); + term.charset = 0; + + for (i = 0; i < 2; i++) { + tmoveto(0, 0); + tcursor(CURSOR_SAVE); + tclearregion(0, 0, term.col-1, term.row-1); + tswapscreen(); + } +} + +void +tnew(int col, int row) +{ + term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; + tresize(col, row); + treset(); +} + +void +tswapscreen(void) +{ + Line *tmp = term.line; + + term.line = term.alt; + term.alt = tmp; + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); +} + +void +kscrolldown(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (n > term.scr) + n = term.scr; + + if (term.scr > 0) { + term.scr -= n; + selscroll(0, -n); + tfulldirt(); + } +} + +void +kscrollup(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (term.scr <= HISTSIZE-n) { + term.scr += n; + selscroll(0, n); + tfulldirt(); + } +} + +void +tscrolldown(int orig, int n, int copyhist) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + if (copyhist) { + term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[term.bot]; + term.line[term.bot] = temp; + } + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + + for (i = term.bot; i >= orig+n; i--) { + temp = term.line[i]; + term.line[i] = term.line[i-n]; + term.line[i-n] = temp; + } + + if (term.scr == 0) + selscroll(orig, n); +} + +void +tscrollup(int orig, int n, int copyhist) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + if (copyhist) { + term.histi = (term.histi + 1) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[orig]; + term.line[orig] = temp; + } + + if (term.scr > 0 && term.scr < HISTSIZE) + term.scr = MIN(term.scr + n, HISTSIZE-1); + + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + + for (i = orig; i <= term.bot-n; i++) { + temp = term.line[i]; + term.line[i] = term.line[i+n]; + term.line[i+n] = temp; + } + + if (term.scr == 0) + selscroll(orig, -n); +} + +void +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) { + selclear(); + } else { + selnormalize(); + } + } +} + +void +tnewline(int first_col) +{ + int y = term.c.y; + + if (y == term.bot) { + tscrollup(term.top, 1, 1); + } else { + y++; + } + tmoveto(first_col ? 0 : term.c.x, y); +} + +void +csiparse(void) +{ + char *p = csiescseq.buf, *np; + long int v; + + csiescseq.narg = 0; + if (*p == '?') { + csiescseq.priv = 1; + p++; + } + + csiescseq.buf[csiescseq.len] = '\0'; + while (p < csiescseq.buf+csiescseq.len) { + np = NULL; + v = strtol(p, &np, 10); + if (np == p) + v = 0; + if (v == LONG_MAX || v == LONG_MIN) + v = -1; + csiescseq.arg[csiescseq.narg++] = v; + p = np; + if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + break; + p++; + } + csiescseq.mode[0] = *p++; + csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; +} + +/* for absolute user moves, when decom is set */ +void +tmoveato(int x, int y) +{ + tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0)); +} + +void +tmoveto(int x, int y) +{ + int miny, maxy; + + if (term.c.state & CURSOR_ORIGIN) { + miny = term.top; + maxy = term.bot; + } else { + miny = 0; + maxy = term.row - 1; + } + term.c.state &= ~CURSOR_WRAPNEXT; + term.c.x = LIMIT(x, 0, term.col-1); + term.c.y = LIMIT(y, miny, maxy); +} + +void +tsetchar(Rune u, Glyph *attr, int x, int y) +{ + 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 */ + 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ + "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ + "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ + }; + + /* + * The table is proudly stolen from rxvt. + */ + if (term.trantbl[term.charset] == CS_GRAPHIC0 && + BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) + utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); + + if (term.line[y][x].mode & ATTR_WIDE) { + if (x+1 < term.col) { + term.line[y][x+1].u = ' '; + term.line[y][x+1].mode &= ~ATTR_WDUMMY; + } + } else if (term.line[y][x].mode & ATTR_WDUMMY) { + term.line[y][x-1].u = ' '; + term.line[y][x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; + term.line[y][x] = *attr; + term.line[y][x].u = u; +} + +void +tclearregion(int x1, int y1, int x2, int y2) +{ + int x, y, temp; + Glyph *gp; + + if (x1 > x2) + temp = x1, x1 = x2, x2 = temp; + if (y1 > y2) + temp = y1, y1 = y2, y2 = temp; + + LIMIT(x1, 0, term.col-1); + LIMIT(x2, 0, term.col-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { + gp = &term.line[y][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + gp->u = ' '; + } + } +} + +void +tdeletechar(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +} + +void +tinsertblank(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +} + +void +tinsertblankline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrolldown(term.c.y, n, 0); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n, 0); +} + +int32_t +tdefcolor(int *attr, int *npar, int l) +{ + int32_t idx = -1; + uint r, g, b; + + switch (attr[*npar + 1]) { + case 2: /* direct color in RGB space */ + if (*npar + 4 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + r = attr[*npar + 2]; + g = attr[*npar + 3]; + b = attr[*npar + 4]; + *npar += 4; + if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) + fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n", + r, g, b); + else + idx = TRUECOLOR(r, g, b); + break; + case 5: /* indexed color */ + if (*npar + 2 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + *npar += 2; + if (!BETWEEN(attr[*npar], 0, 255)) + fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]); + else + idx = attr[*npar]; + break; + case 0: /* implemented defined (only foreground) */ + case 1: /* transparent */ + case 3: /* direct color in CMY space */ + case 4: /* direct color in CMYK space */ + default: + fprintf(stderr, + "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; + } + + return idx; +} + +void +tsetattr(int *attr, int l) +{ + int i; + int32_t idx; + + for (i = 0; i < l; i++) { + switch (attr[i]) { + case 0: + term.c.attr.mode &= ~( + ATTR_BOLD | + ATTR_FAINT | + ATTR_ITALIC | + ATTR_UNDERLINE | + ATTR_BLINK | + ATTR_REVERSE | + ATTR_INVISIBLE | + ATTR_STRUCK ); + term.c.attr.fg = defaultfg; + term.c.attr.bg = defaultbg; + break; + case 1: + term.c.attr.mode |= ATTR_BOLD; + break; + case 2: + term.c.attr.mode |= ATTR_FAINT; + break; + case 3: + term.c.attr.mode |= ATTR_ITALIC; + break; + case 4: + term.c.attr.mode |= ATTR_UNDERLINE; + break; + case 5: /* slow blink */ + /* FALLTHROUGH */ + case 6: /* rapid blink */ + term.c.attr.mode |= ATTR_BLINK; + break; + case 7: + term.c.attr.mode |= ATTR_REVERSE; + break; + case 8: + term.c.attr.mode |= ATTR_INVISIBLE; + break; + case 9: + term.c.attr.mode |= ATTR_STRUCK; + break; + case 22: + term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); + break; + case 23: + term.c.attr.mode &= ~ATTR_ITALIC; + break; + case 24: + term.c.attr.mode &= ~ATTR_UNDERLINE; + break; + case 25: + term.c.attr.mode &= ~ATTR_BLINK; + break; + case 27: + term.c.attr.mode &= ~ATTR_REVERSE; + break; + case 28: + term.c.attr.mode &= ~ATTR_INVISIBLE; + break; + case 29: + term.c.attr.mode &= ~ATTR_STRUCK; + break; + case 38: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.fg = idx; + break; + case 39: + term.c.attr.fg = defaultfg; + break; + case 48: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.bg = idx; + break; + case 49: + term.c.attr.bg = defaultbg; + break; + default: + if (BETWEEN(attr[i], 30, 37)) { + term.c.attr.fg = attr[i] - 30; + } else if (BETWEEN(attr[i], 40, 47)) { + term.c.attr.bg = attr[i] - 40; + } else if (BETWEEN(attr[i], 90, 97)) { + term.c.attr.fg = attr[i] - 90 + 8; + } else if (BETWEEN(attr[i], 100, 107)) { + term.c.attr.bg = attr[i] - 100 + 8; + } else { + fprintf(stderr, + "erresc(default): gfx attr %d unknown\n", + attr[i]); + csidump(); + } + break; + } + } +} + +void +tsetscroll(int t, int b) +{ + int temp; + + LIMIT(t, 0, term.row-1); + LIMIT(b, 0, term.row-1); + if (t > b) { + temp = t; + t = b; + b = temp; + } + term.top = t; + term.bot = b; +} + +void +tsetmode(int priv, int set, int *args, int narg) +{ + int alt, *lim; + + for (lim = args + narg; args < lim; ++args) { + if (priv) { + switch (*args) { + case 1: /* DECCKM -- Cursor key */ + xsetmode(set, MODE_APPCURSOR); + break; + case 5: /* DECSCNM -- Reverse video */ + xsetmode(set, MODE_REVERSE); + break; + case 6: /* DECOM -- Origin */ + MODBIT(term.c.state, set, CURSOR_ORIGIN); + tmoveato(0, 0); + break; + case 7: /* DECAWM -- Auto wrap */ + MODBIT(term.mode, set, MODE_WRAP); + break; + case 0: /* Error (IGNORED) */ + case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ + case 3: /* DECCOLM -- Column (IGNORED) */ + case 4: /* DECSCLM -- Scroll (IGNORED) */ + case 8: /* DECARM -- Auto repeat (IGNORED) */ + case 18: /* DECPFF -- Printer feed (IGNORED) */ + case 19: /* DECPEX -- Printer extent (IGNORED) */ + case 42: /* DECNRCM -- National characters (IGNORED) */ + case 12: /* att610 -- Start blinking cursor (IGNORED) */ + break; + case 25: /* DECTCEM -- Text Cursor Enable Mode */ + xsetmode(!set, MODE_HIDE); + break; + case 9: /* X10 mouse compatibility mode */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEX10); + break; + case 1000: /* 1000: report button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEBTN); + break; + case 1002: /* 1002: report motion on button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMOTION); + break; + case 1003: /* 1003: enable all mouse motions */ + xsetpointermotion(set); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMANY); + break; + case 1004: /* 1004: send focus events to tty */ + xsetmode(set, MODE_FOCUS); + break; + case 1006: /* 1006: extended reporting mode */ + xsetmode(set, MODE_MOUSESGR); + break; + case 1034: + xsetmode(set, MODE_8BIT); + break; + case 1049: /* swap screen & set/restore cursor as xterm */ + if (!allowaltscreen) + break; + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + /* FALLTHROUGH */ + case 47: /* swap screen */ + case 1047: + if (!allowaltscreen) + break; + alt = IS_SET(MODE_ALTSCREEN); + if (alt) { + tclearregion(0, 0, term.col-1, + term.row-1); + } + if (set ^ alt) /* set is always 1 or 0 */ + tswapscreen(); + if (*args != 1049) + break; + /* FALLTHROUGH */ + case 1048: + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + break; + case 2004: /* 2004: bracketed paste mode */ + xsetmode(set, MODE_BRCKTPASTE); + break; + /* Not implemented mouse modes. See comments there. */ + case 1001: /* mouse highlight mode; can hang the + terminal by design when implemented. */ + case 1005: /* UTF-8 mouse mode; will confuse + applications not supporting UTF-8 + and luit. */ + case 1015: /* urxvt mangled mouse mode; incompatible + and can be mistaken for other control + codes. */ + break; + default: + fprintf(stderr, + "erresc: unknown private set/reset mode %d\n", + *args); + break; + } + } else { + switch (*args) { + case 0: /* Error (IGNORED) */ + break; + case 2: + xsetmode(set, MODE_KBDLOCK); + break; + case 4: /* IRM -- Insertion-replacement */ + MODBIT(term.mode, set, MODE_INSERT); + break; + case 12: /* SRM -- Send/Receive */ + MODBIT(term.mode, !set, MODE_ECHO); + break; + case 20: /* LNM -- Linefeed/new line */ + MODBIT(term.mode, set, MODE_CRLF); + break; + default: + fprintf(stderr, + "erresc: unknown set/reset mode %d\n", + *args); + break; + } + } + } +} + +void +csihandle(void) +{ + char buf[40]; + int len; + + switch (csiescseq.mode[0]) { + default: + unknown: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* die(""); */ + break; + case '@': /* ICH -- Insert blank char */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblank(csiescseq.arg[0]); + break; + case 'A': /* CUU -- Cursor Up */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); + break; + case 'B': /* CUD -- Cursor Down */ + case 'e': /* VPR --Cursor Down */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); + break; + case 'i': /* MC -- Media Copy */ + switch (csiescseq.arg[0]) { + case 0: + tdump(); + break; + case 1: + tdumpline(term.c.y); + break; + case 2: + tdumpsel(); + break; + case 4: + term.mode &= ~MODE_PRINT; + break; + case 5: + term.mode |= MODE_PRINT; + break; + } + break; + case 'c': /* DA -- Device Attributes */ + if (csiescseq.arg[0] == 0) + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'b': /* REP -- if last char is printable print it more times */ + DEFAULT(csiescseq.arg[0], 1); + if (term.lastc) + while (csiescseq.arg[0]-- > 0) + tputc(term.lastc); + break; + case 'C': /* CUF -- Cursor Forward */ + case 'a': /* HPR -- Cursor Forward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x+csiescseq.arg[0], term.c.y); + break; + case 'D': /* CUB -- Cursor Backward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x-csiescseq.arg[0], term.c.y); + break; + case 'E': /* CNL -- Cursor Down and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y+csiescseq.arg[0]); + break; + case 'F': /* CPL -- Cursor Up and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y-csiescseq.arg[0]); + break; + case 'g': /* TBC -- Tabulation clear */ + switch (csiescseq.arg[0]) { + case 0: /* clear current tab stop */ + term.tabs[term.c.x] = 0; + break; + case 3: /* clear all the tabs */ + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + break; + default: + goto unknown; + } + break; + case 'G': /* CHA -- Move to */ + case '`': /* HPA */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(csiescseq.arg[0]-1, term.c.y); + break; + case 'H': /* CUP -- Move to */ + case 'f': /* HVP */ + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], 1); + tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); + break; + case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(csiescseq.arg[0]); + break; + case 'J': /* ED -- Clear screen */ + switch (csiescseq.arg[0]) { + case 0: /* below */ + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); + if (term.c.y < term.row-1) { + tclearregion(0, term.c.y+1, term.col-1, + term.row-1); + } + break; + case 1: /* above */ + if (term.c.y > 1) + tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, 0, term.col-1, term.row-1); + break; + default: + goto unknown; + } + break; + case 'K': /* EL -- Clear line */ + switch (csiescseq.arg[0]) { + case 0: /* right */ + tclearregion(term.c.x, term.c.y, term.col-1, + term.c.y); + break; + case 1: /* left */ + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, term.c.y, term.col-1, term.c.y); + break; + } + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); + tscrollup(term.top, csiescseq.arg[0], 0); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[0], 0); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblankline(csiescseq.arg[0]); + break; + case 'l': /* RM -- Reset Mode */ + tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + break; + case 'M': /* DL -- Delete lines */ + DEFAULT(csiescseq.arg[0], 1); + tdeleteline(csiescseq.arg[0]); + break; + case 'X': /* ECH -- Erase char */ + DEFAULT(csiescseq.arg[0], 1); + tclearregion(term.c.x, term.c.y, + term.c.x + csiescseq.arg[0] - 1, term.c.y); + break; + case 'P': /* DCH -- Delete char */ + DEFAULT(csiescseq.arg[0], 1); + tdeletechar(csiescseq.arg[0]); + break; + case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(-csiescseq.arg[0]); + break; + case 'd': /* VPA -- Move to */ + DEFAULT(csiescseq.arg[0], 1); + tmoveato(term.c.x, csiescseq.arg[0]-1); + break; + case 'h': /* SM -- Set terminal mode */ + tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); + break; + case 'm': /* SGR -- Terminal attribute (color) */ + tsetattr(csiescseq.arg, csiescseq.narg); + break; + case 'n': /* DSR – Device Status Report (cursor position) */ + if (csiescseq.arg[0] == 6) { + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + } + break; + case 'r': /* DECSTBM -- Set Scrolling Region */ + if (csiescseq.priv) { + goto unknown; + } else { + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], term.row); + tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); + tmoveato(0, 0); + } + break; + case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ + tcursor(CURSOR_SAVE); + break; + case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ + tcursor(CURSOR_LOAD); + break; + case ' ': + switch (csiescseq.mode[1]) { + case 'q': /* DECSCUSR -- Set Cursor Style */ + if (xsetcursor(csiescseq.arg[0])) + goto unknown; + break; + default: + goto unknown; + } + break; + } +} + +void +csidump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC["); + for (i = 0; i < csiescseq.len; i++) { + c = csiescseq.buf[i] & 0xff; + if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + putc('\n', stderr); +} + +void +csireset(void) +{ + memset(&csiescseq, 0, sizeof(csiescseq)); +} + +void +strhandle(void) +{ + char *p = NULL, *dec; + int j, narg, par; + + term.esc &= ~(ESC_STR_END|ESC_STR); + strparse(); + par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; + + switch (strescseq.type) { + case ']': /* OSC -- Operating System Command */ + switch (par) { + case 0: + case 1: + case 2: + if (narg > 1) + xsettitle(strescseq.args[1]); + return; + case 52: + if (narg > 2 && allowwindowops) { + dec = base64dec(strescseq.args[2]); + if (dec) { + xsetsel(dec); + xclipcopy(); + } else { + fprintf(stderr, "erresc: invalid base64\n"); + } + } + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset, here p = NULL */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + if (xsetcolorname(j, p)) { + if (par == 104 && narg <= 1) + return; /* color reset without parameter */ + fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", + j, p ? p : "(null)"); + } else { + /* + * TODO if defaultbg color is changed, borders + * are dirty + */ + redraw(); + } + return; + } + break; + case 'k': /* old title set compatibility */ + xsettitle(strescseq.args[0]); + return; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + return; + } + + fprintf(stderr, "erresc: unknown str "); + strdump(); +} + +void +strparse(void) +{ + int c; + char *p = strescseq.buf; + + strescseq.narg = 0; + strescseq.buf[strescseq.len] = '\0'; + + if (*p == '\0') + return; + + while (strescseq.narg < STR_ARG_SIZ) { + strescseq.args[strescseq.narg++] = p; + while ((c = *p) != ';' && c != '\0') + ++p; + if (c == '\0') + return; + *p++ = '\0'; + } +} + +void +strdump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC%c", strescseq.type); + for (i = 0; i < strescseq.len; i++) { + c = strescseq.buf[i] & 0xff; + if (c == '\0') { + putc('\n', stderr); + return; + } else if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + fprintf(stderr, "ESC\\\n"); +} + +void +strreset(void) +{ + strescseq = (STREscape){ + .buf = xrealloc(strescseq.buf, STR_BUF_SIZ), + .siz = STR_BUF_SIZ, + }; +} + +void +sendbreak(const Arg *arg) +{ + if (tcsendbreak(cmdfd, 0)) + perror("Error sending break"); +} + +void +tprinter(char *s, size_t len) +{ + if (iofd != -1 && xwrite(iofd, s, len) < 0) { + perror("Error writing to output file"); + close(iofd); + iofd = -1; + } +} + +void +toggleprinter(const Arg *arg) +{ + term.mode ^= MODE_PRINT; +} + +void +printscreen(const Arg *arg) +{ + tdump(); +} + +void +printsel(const Arg *arg) +{ + tdumpsel(); +} + +void +tdumpsel(void) +{ + char *ptr; + + if ((ptr = getsel())) { + tprinter(ptr, strlen(ptr)); + free(ptr); + } +} + +void +tdumpline(int n) +{ + char buf[UTF_SIZ]; + 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) + tprinter(buf, utf8encode(bp->u, buf)); + } + tprinter("\n", 1); +} + +void +tdump(void) +{ + int i; + + for (i = 0; i < term.row; ++i) + tdumpline(i); +} + +void +tputtab(int n) +{ + uint x = term.c.x; + + if (n > 0) { + while (x < term.col && n--) + for (++x; x < term.col && !term.tabs[x]; ++x) + /* nothing */ ; + } else if (n < 0) { + while (x > 0 && n++) + for (--x; x > 0 && !term.tabs[x]; --x) + /* nothing */ ; + } + term.c.x = LIMIT(x, 0, term.col-1); +} + +void +tdefutf8(char ascii) +{ + if (ascii == 'G') + term.mode |= MODE_UTF8; + else if (ascii == '@') + term.mode &= ~MODE_UTF8; +} + +void +tdeftran(char ascii) +{ + static char cs[] = "0B"; + static int vcs[] = {CS_GRAPHIC0, CS_USA}; + char *p; + + if ((p = strchr(cs, ascii)) == NULL) { + fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); + } else { + term.trantbl[term.icharset] = vcs[p - cs]; + } +} + +void +tdectest(char c) +{ + int x, y; + + if (c == '8') { /* DEC screen alignment test. */ + for (x = 0; x < term.col; ++x) { + for (y = 0; y < term.row; ++y) + tsetchar('E', &term.c.attr, x, y); + } + } +} + +void +tstrsequence(uchar c) +{ + switch (c) { + case 0x90: /* DCS -- Device Control String */ + c = 'P'; + break; + case 0x9f: /* APC -- Application Program Command */ + c = '_'; + break; + case 0x9e: /* PM -- Privacy Message */ + c = '^'; + break; + case 0x9d: /* OSC -- Operating System Command */ + c = ']'; + break; + } + strreset(); + strescseq.type = c; + term.esc |= ESC_STR; +} + +void +tcontrolcode(uchar ascii) +{ + switch (ascii) { + case '\t': /* HT */ + tputtab(1); + return; + case '\b': /* BS */ + tmoveto(term.c.x-1, term.c.y); + return; + case '\r': /* CR */ + tmoveto(0, term.c.y); + return; + case '\f': /* LF */ + case '\v': /* VT */ + case '\n': /* LF */ + /* go to first col if the mode is set */ + tnewline(IS_SET(MODE_CRLF)); + return; + case '\a': /* BEL */ + if (term.esc & ESC_STR_END) { + /* backwards compatibility to xterm */ + strhandle(); + } else { + xbell(); + } + break; + case '\033': /* ESC */ + csireset(); + term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); + term.esc |= ESC_START; + return; + case '\016': /* SO (LS1 -- Locking shift 1) */ + case '\017': /* SI (LS0 -- Locking shift 0) */ + term.charset = 1 - (ascii - '\016'); + return; + case '\032': /* SUB */ + tsetchar('?', &term.c.attr, term.c.x, term.c.y); + /* FALLTHROUGH */ + case '\030': /* CAN */ + csireset(); + break; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + return; + case 0x80: /* TODO: PAD */ + case 0x81: /* TODO: HOP */ + case 0x82: /* TODO: BPH */ + case 0x83: /* TODO: NBH */ + case 0x84: /* TODO: IND */ + break; + case 0x85: /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 0x86: /* TODO: SSA */ + case 0x87: /* TODO: ESA */ + break; + case 0x88: /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 0x89: /* TODO: HTJ */ + case 0x8a: /* TODO: VTS */ + case 0x8b: /* TODO: PLD */ + case 0x8c: /* TODO: PLU */ + case 0x8d: /* TODO: RI */ + case 0x8e: /* TODO: SS2 */ + case 0x8f: /* TODO: SS3 */ + case 0x91: /* TODO: PU1 */ + case 0x92: /* TODO: PU2 */ + case 0x93: /* TODO: STS */ + case 0x94: /* TODO: CCH */ + case 0x95: /* TODO: MW */ + case 0x96: /* TODO: SPA */ + case 0x97: /* TODO: EPA */ + case 0x98: /* TODO: SOS */ + case 0x99: /* TODO: SGCI */ + break; + case 0x9a: /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 0x9b: /* TODO: CSI */ + case 0x9c: /* TODO: ST */ + break; + case 0x90: /* DCS -- Device Control String */ + case 0x9d: /* OSC -- Operating System Command */ + case 0x9e: /* PM -- Privacy Message */ + case 0x9f: /* APC -- Application Program Command */ + tstrsequence(ascii); + return; + } + /* only CAN, SUB, \a and C1 chars interrupt a sequence */ + term.esc &= ~(ESC_STR_END|ESC_STR); +} + +/* + * returns 1 when the sequence is finished and it hasn't to read + * more characters for this sequence, otherwise 0 + */ +int +eschandle(uchar ascii) +{ + switch (ascii) { + case '[': + term.esc |= ESC_CSI; + return 0; + case '#': + term.esc |= ESC_TEST; + return 0; + case '%': + term.esc |= ESC_UTF8; + return 0; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + case ']': /* OSC -- Operating System Command */ + case 'k': /* old title set compatibility */ + tstrsequence(ascii); + return 0; + case 'n': /* LS2 -- Locking shift 2 */ + case 'o': /* LS3 -- Locking shift 3 */ + term.charset = 2 + (ascii - 'n'); + break; + case '(': /* GZD4 -- set primary charset G0 */ + case ')': /* G1D4 -- set secondary charset G1 */ + case '*': /* G2D4 -- set tertiary charset G2 */ + case '+': /* G3D4 -- set quaternary charset G3 */ + term.icharset = ascii - '('; + term.esc |= ESC_ALTCHARSET; + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { + tscrollup(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { + tscrolldown(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'c': /* RIS -- Reset to initial state */ + treset(); + resettitle(); + xloadcols(); + break; + case '=': /* DECPAM -- Application keypad */ + xsetmode(1, MODE_APPKEYPAD); + break; + case '>': /* DECPNM -- Normal keypad */ + xsetmode(0, MODE_APPKEYPAD); + break; + case '7': /* DECSC -- Save Cursor */ + tcursor(CURSOR_SAVE); + break; + case '8': /* DECRC -- Restore Cursor */ + tcursor(CURSOR_LOAD); + break; + case '\\': /* ST -- String Terminator */ + if (term.esc & ESC_STR_END) + strhandle(); + break; + default: + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", + (uchar) ascii, isprint(ascii)? ascii:'.'); + break; + } + return 1; +} + +void +tputc(Rune u) +{ + char c[UTF_SIZ]; + int control; + int width, len; + Glyph *gp; + + control = ISCONTROL(u); + if (u < 127 || !IS_SET(MODE_UTF8)) { + c[0] = u; + width = len = 1; + } else { + len = utf8encode(u, c); + if (!control && (width = wcwidth(u)) == -1) + width = 1; + } + + if (IS_SET(MODE_PRINT)) + tprinter(c, len); + + /* + * STR sequence must be checked before anything else + * because it uses all following characters until it + * receives a ESC, a SUB, a ST or any other C1 control + * character. + */ + 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_STR_END; + goto check_control_code; + } + + if (strescseq.len+len >= strescseq.siz) { + /* + * Here is a bug in terminals. If the user never sends + * some code to stop the str or esc command, then st + * will stop responding. But this is better than + * silently failing with unknown characters. At least + * then users will report back. + * + * In the case users ever get fixed, here is the code: + */ + /* + * term.esc = 0; + * strhandle(); + */ + if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2) + return; + strescseq.siz *= 2; + strescseq.buf = xrealloc(strescseq.buf, strescseq.siz); + } + + memmove(&strescseq.buf[strescseq.len], c, len); + strescseq.len += len; + return; + } + +check_control_code: + /* + * Actions of control codes must be performed as soon they arrive + * because they can be embedded inside a control sequence, and + * they must not cause conflicts with sequences. + */ + if (control) { + tcontrolcode(u); + /* + * control codes are not shown ever + */ + if (!term.esc) + term.lastc = 0; + return; + } else if (term.esc & ESC_START) { + if (term.esc & ESC_CSI) { + csiescseq.buf[csiescseq.len++] = u; + if (BETWEEN(u, 0x40, 0x7E) + || csiescseq.len >= \ + sizeof(csiescseq.buf)-1) { + term.esc = 0; + csiparse(); + csihandle(); + } + return; + } else if (term.esc & ESC_UTF8) { + tdefutf8(u); + } else if (term.esc & ESC_ALTCHARSET) { + tdeftran(u); + } else if (term.esc & ESC_TEST) { + tdectest(u); + } else { + if (!eschandle(u)) + return; + /* sequence already finished */ + } + term.esc = 0; + /* + * All characters which form part of a sequence are not + * printed + */ + return; + } + if (selected(term.c.x, term.c.y)) + selclear(); + + gp = &term.line[term.c.y][term.c.x]; + if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + gp->mode |= ATTR_WRAP; + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) + memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); + + if (term.c.x+width > term.col) { + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + tsetchar(u, &term.c.attr, term.c.x, term.c.y); + term.lastc = u; + + if (width == 2) { + gp->mode |= ATTR_WIDE; + if (term.c.x+1 < term.col) { + gp[1].u = '\0'; + gp[1].mode = ATTR_WDUMMY; + } + } + if (term.c.x+width < term.col) { + tmoveto(term.c.x+width, term.c.y); + } else { + term.c.state |= CURSOR_WRAPNEXT; + } +} + +int +twrite(const char *buf, int buflen, int show_ctrl) +{ + int charsize; + Rune u; + int n; + + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8)) { + /* process a complete utf8 char */ + charsize = utf8decode(buf + n, &u, buflen - n); + if (charsize == 0) + break; + } else { + u = buf[n] & 0xFF; + charsize = 1; + } + if (show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; + tputc('^'); + tputc('['); + } else if (u != '\n' && u != '\r' && u != '\t') { + u ^= 0x40; + tputc('^'); + } + } + tputc(u); + } + return n; +} + +void +tresize(int col, int row) +{ + int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; + TCursor c; + + if (col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to + * memmove because we're freeing the earlier lines + */ + for (i = 0; i <= term.c.y - row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + /* ensure that both src and dst are not NULL */ + if (i > 0) { + memmove(term.line, term.line + i, row * sizeof(Line)); + memmove(term.alt, term.alt + i, row * sizeof(Line)); + } + for (i += row; i < term.row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + for (i = 0; i < HISTSIZE; i++) { + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); + for (j = mincol; j < col; j++) { + term.hist[i][j] = term.c.attr; + term.hist[i][j].u = ' '; + } + } + + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); + } + + /* allocate any new rows */ + for (/* i = minrow */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + term.alt[i] = xmalloc(col * sizeof(Glyph)); + } + if (col > term.col) { + bp = term.tabs + term.col; + + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + while (--bp > term.tabs && !*bp) + /* nothing */ ; + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } + /* update terminal size */ + term.col = col; + term.row = row; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); + /* Clearing both screens (it makes dirty all lines) */ + c = term.c; + for (i = 0; i < 2; i++) { + if (mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if (0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + tcursor(CURSOR_LOAD); + } + term.c = c; +} + +void +resettitle(void) +{ + xsettitle(NULL); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y; + + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(TLINE(y), x1, y, x2); + } +} + +void +draw(void) +{ + int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; + + if (!xstartdraw()) + return; + + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); + if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) + term.ocx--; + if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); + if (term.scr == 0) + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); + if (ocx != term.ocx || ocy != term.ocy) + xximspot(term.ocx, term.ocy); +} + +void +redraw(void) +{ + tfulldirt(); + draw(); +} diff --git a/st.h b/st.h index 7ea2fd3..1ecf257 100644 --- a/st.h +++ b/st.h @@ -33,6 +33,7 @@ enum glyph_attribute { ATTR_WRAP = 1 << 8, ATTR_WIDE = 1 << 9, ATTR_WDUMMY = 1 << 10, + ATTR_BOXDRAW = 1 << 11, ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, }; @@ -113,6 +114,14 @@ void *xmalloc(size_t); void *xrealloc(void *, size_t); char *xstrdup(char *); +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 + /* config.h globals */ extern char *utmp; extern char *scroll; @@ -125,4 +134,5 @@ extern char *termname; extern unsigned int tabspaces; extern unsigned int defaultfg; extern unsigned int defaultbg; +extern const int boxdraw, boxdraw_bold, boxdraw_braille; extern float alpha; diff --git a/st.h.orig b/st.h.orig new file mode 100644 index 0000000..7ea2fd3 --- /dev/null +++ b/st.h.orig @@ -0,0 +1,128 @@ +/* See LICENSE for license details. */ + +#include +#include + +/* 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, + 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 kscrolldown(const Arg *); +void kscrollup(const Arg *); +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 *); + +/* config.h globals */ +extern char *utmp; +extern char *scroll; +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; +extern unsigned int defaultbg; +extern float alpha; diff --git a/st.h.rej b/st.h.rej new file mode 100644 index 0000000..e8c85b5 --- /dev/null +++ b/st.h.rej @@ -0,0 +1,7 @@ +--- st.h ++++ st.h +@@ -131,3 +140,4 @@ extern char *termname; + extern unsigned int tabspaces; + extern unsigned int defaultfg; + extern unsigned int defaultbg; ++extern const int boxdraw, boxdraw_bold, boxdraw_braille; diff --git a/st.o b/st.o index 5580b18f6892f10ebbadf3e38a34438be4460759..1925b0600f2c1ac2166e57e8578609ba54b239c0 100644 GIT binary patch literal 77264 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 CtmQBjsGkRgyrIxHhguoIyS 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 diff --git a/x.c b/x.c index 8c478f9..854b59f 100644 --- a/x.c +++ b/x.c @@ -1223,6 +1223,8 @@ xinit(int cols, int rows) xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); if (xsel.xtarget == None) xsel.xtarget = XA_STRING; + + boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); } int @@ -1269,8 +1271,13 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x yp = winy + font->ascent; } - /* Lookup character index with default font. */ - glyphidx = XftCharIndex(xw.dpy, font->match, rune); + if (mode & ATTR_BOXDRAW) { + /* minor shoehorning: boxdraw uses only this ushort */ + glyphidx = boxdrawindex(&glyphs[i]); + } else { + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + } if (glyphidx) { specs[numspecs].font = font->match; specs[numspecs].glyph = glyphidx; @@ -1474,8 +1481,12 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i r.width = width; XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); - /* Render the glyphs. */ - XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + if (base.mode & ATTR_BOXDRAW) { + drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); + } else { + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + } /* Render underline and strikethrough. */ if (base.mode & ATTR_UNDERLINE) { @@ -1518,7 +1529,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) /* * Select the right color for the right mode. */ - g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW; if (IS_SET(MODE_REVERSE)) { g.mode |= ATTR_REVERSE; diff --git a/x.c.orig b/x.c.orig new file mode 100644 index 0000000..8c478f9 --- /dev/null +++ b/x.c.orig @@ -0,0 +1,2070 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *argv0; +#include "arg.h" +#include "st.h" +#include "win.h" + +/* types used in config.h */ +typedef struct { + uint mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Shortcut; + +typedef struct { + uint mod; + uint button; + void (*func)(const Arg *); + const Arg arg; + uint release; +} MouseShortcut; + +typedef struct { + KeySym k; + uint mask; + char *s; + /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ + signed char appkey; /* application keypad */ + signed char appcursor; /* application cursor */ +} Key; + +/* X modifiers */ +#define XK_ANY_MOD UINT_MAX +#define XK_NO_MOD 0 +#define XK_SWITCH_MOD (1<<13) + +/* function definitions used in config.h */ +static void clipcopy(const Arg *); +static void clippaste(const Arg *); +static void numlock(const Arg *); +static void selpaste(const Arg *); +static void zoom(const Arg *); +static void zoomabs(const Arg *); +static void zoomreset(const Arg *); +static void ttysend(const Arg *); + +/* config.h for applying patches and the configuration. */ +#include "config.h" + +/* XEMBED messages */ +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 + +/* macros */ +#define IS_SET(flag) ((win.mode & (flag)) != 0) +#define TRUERED(x) (((x) & 0xff0000) >> 8) +#define TRUEGREEN(x) (((x) & 0xff00)) +#define TRUEBLUE(x) (((x) & 0xff) << 8) + +typedef XftDraw *Draw; +typedef XftColor Color; +typedef XftGlyphFontSpec GlyphFontSpec; + +/* Purely graphic info */ +typedef struct { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ + int ch; /* char height */ + int cw; /* char width */ + int mode; /* window state/mode flags */ + int cursor; /* cursor style */ +} TermWindow; + +typedef struct { + Display *dpy; + Colormap cmap; + Window win; + Drawable buf; + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ + Atom xembed, wmdeletewin, netwmname, netwmpid; + struct { + XIM xim; + XIC xic; + XPoint spot; + XVaNestedList spotlist; + } ime; + Draw draw; + Visual *vis; + XSetWindowAttributes attrs; + int scr; + int isfixed; /* is fixed geometry? */ + int depth; /* bit depth */ + int l, t; /* left and top offset */ + int gm; /* geometry mask */ +} XWindow; + +typedef struct { + Atom xtarget; + char *primary, *clipboard; + struct timespec tclick1; + struct timespec tclick2; +} XSelection; + +/* Font structure */ +#define Font Font_ +typedef struct { + int height; + int width; + int ascent; + int descent; + int badslant; + int badweight; + short lbearing; + short rbearing; + XftFont *match; + FcFontSet *set; + FcPattern *pattern; +} Font; + +/* Drawing Context */ +typedef struct { + Color *col; + size_t collen; + Font font, bfont, ifont, ibfont; + GC gc; +} DC; + +static inline ushort sixd_to_16bit(int); +static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); +static void xdrawglyph(Glyph, int, int); +static void xclear(int, int, int, int); +static int xgeommasktogravity(int); +static int ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, XPointer, XPointer); +static int xicdestroy(XIC, XPointer, XPointer); +static void xinit(int, int); +static void cresize(int, int); +static void xresize(int, int); +static void xhints(void); +static int xloadcolor(int, const char *, Color *); +static int xloadfont(Font *, FcPattern *); +static void xloadfonts(char *, double); +static void xunloadfont(Font *); +static void xunloadfonts(void); +static void xsetenv(void); +static void xseturgency(int); +static int evcol(XEvent *); +static int evrow(XEvent *); + +static void expose(XEvent *); +static void visibility(XEvent *); +static void unmap(XEvent *); +static void kpress(XEvent *); +static void cmessage(XEvent *); +static void resize(XEvent *); +static void focus(XEvent *); +static uint buttonmask(uint); +static int mouseaction(XEvent *, uint); +static void brelease(XEvent *); +static void bpress(XEvent *); +static void bmotion(XEvent *); +static void propnotify(XEvent *); +static void selnotify(XEvent *); +static void selclear_(XEvent *); +static void selrequest(XEvent *); +static void setsel(char *, Time); +static void mousesel(XEvent *, int); +static void mousereport(XEvent *); +static char *kmap(KeySym, uint); +static int match(uint, uint); + +static void run(void); +static void usage(void); + +static void (*handler[LASTEvent])(XEvent *) = { + [KeyPress] = kpress, + [ClientMessage] = cmessage, + [ConfigureNotify] = resize, + [VisibilityNotify] = visibility, + [UnmapNotify] = unmap, + [Expose] = expose, + [FocusIn] = focus, + [FocusOut] = focus, + [MotionNotify] = bmotion, + [ButtonPress] = bpress, + [ButtonRelease] = brelease, +/* + * Uncomment if you want the selection to disappear when you select something + * different in another window. + */ +/* [SelectionClear] = selclear_, */ + [SelectionNotify] = selnotify, +/* + * PropertyNotify is only turned on when there is some INCR transfer happening + * for the selection retrieval. + */ + [PropertyNotify] = propnotify, + [SelectionRequest] = selrequest, +}; + +/* Globals */ +static DC dc; +static XWindow xw; +static XSelection xsel; +static TermWindow win; + +/* Font Ring Cache */ +enum { + FRC_NORMAL, + FRC_ITALIC, + FRC_BOLD, + FRC_ITALICBOLD +}; + +typedef struct { + XftFont *font; + int flags; + Rune unicodep; +} Fontcache; + +/* Fontcache is an array now. A new font will be appended to the array. */ +static Fontcache *frc = NULL; +static int frclen = 0; +static int frccap = 0; +static char *usedfont = NULL; +static double usedfontsize = 0; +static double defaultfontsize = 0; + +static char *opt_alpha = NULL; +static char *opt_class = NULL; +static char **opt_cmd = NULL; +static char *opt_embed = NULL; +static char *opt_font = NULL; +static char *opt_io = NULL; +static char *opt_line = NULL; +static char *opt_name = NULL; +static char *opt_title = NULL; + +static int oldbutton = 3; /* button event on startup: 3 = release */ + +void +clipcopy(const Arg *dummy) +{ + Atom clipboard; + + free(xsel.clipboard); + xsel.clipboard = NULL; + + if (xsel.primary != NULL) { + xsel.clipboard = xstrdup(xsel.primary); + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); + } +} + +void +clippaste(const Arg *dummy) +{ + Atom clipboard; + + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, + xw.win, CurrentTime); +} + +void +selpaste(const Arg *dummy) +{ + XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, + xw.win, CurrentTime); +} + +void +numlock(const Arg *dummy) +{ + win.mode ^= MODE_NUMLOCK; +} + +void +zoom(const Arg *arg) +{ + Arg larg; + + larg.f = usedfontsize + arg->f; + zoomabs(&larg); +} + +void +zoomabs(const Arg *arg) +{ + xunloadfonts(); + xloadfonts(usedfont, arg->f); + cresize(0, 0); + redraw(); + xhints(); +} + +void +zoomreset(const Arg *arg) +{ + Arg larg; + + if (defaultfontsize > 0) { + larg.f = defaultfontsize; + zoomabs(&larg); + } +} + +void +ttysend(const Arg *arg) +{ + ttywrite(arg->s, strlen(arg->s), 1); +} + +int +evcol(XEvent *e) +{ + int x = e->xbutton.x - borderpx; + LIMIT(x, 0, win.tw - 1); + return x / win.cw; +} + +int +evrow(XEvent *e) +{ + int y = e->xbutton.y - borderpx; + LIMIT(y, 0, win.th - 1); + return y / win.ch; +} + +void +mousesel(XEvent *e, int done) +{ + int type, seltype = SEL_REGULAR; + uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); + + for (type = 1; type < LEN(selmasks); ++type) { + if (match(selmasks[type], state)) { + seltype = type; + break; + } + } + selextend(evcol(e), evrow(e), seltype, done); + if (done) + setsel(getsel(), e->xbutton.time); +} + +void +mousereport(XEvent *e) +{ + int len, x = evcol(e), y = evrow(e), + button = e->xbutton.button, state = e->xbutton.state; + char buf[40]; + static int ox, oy; + + /* from urxvt */ + if (e->xbutton.type == MotionNotify) { + if (x == ox && y == oy) + return; + if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) + return; + /* MOUSE_MOTION: no reporting if no button is pressed */ + if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) + return; + + button = oldbutton + 32; + ox = x; + oy = y; + } else { + if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) { + button = 3; + } else { + button -= Button1; + if (button >= 3) + button += 64 - 3; + } + if (e->xbutton.type == ButtonPress) { + oldbutton = button; + ox = x; + oy = y; + } else if (e->xbutton.type == ButtonRelease) { + oldbutton = 3; + /* MODE_MOUSEX10: no button release reporting */ + if (IS_SET(MODE_MOUSEX10)) + return; + if (button == 64 || button == 65) + return; + } + } + + if (!IS_SET(MODE_MOUSEX10)) { + button += ((state & ShiftMask ) ? 4 : 0) + + ((state & Mod4Mask ) ? 8 : 0) + + ((state & ControlMask) ? 16 : 0); + } + + if (IS_SET(MODE_MOUSESGR)) { + len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", + button, x+1, y+1, + e->xbutton.type == ButtonRelease ? 'm' : 'M'); + } else if (x < 223 && y < 223) { + len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", + 32+button, 32+x+1, 32+y+1); + } else { + return; + } + + ttywrite(buf, len, 0); +} + +uint +buttonmask(uint button) +{ + return button == Button1 ? Button1Mask + : button == Button2 ? Button2Mask + : button == Button3 ? Button3Mask + : button == Button4 ? Button4Mask + : button == Button5 ? Button5Mask + : 0; +} + +int +mouseaction(XEvent *e, uint release) +{ + MouseShortcut *ms; + + /* ignore Buttonmask for Button - it's set on release */ + uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); + + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { + if (ms->release == release && + ms->button == e->xbutton.button && + (match(ms->mod, state) || /* exact or forced */ + match(ms->mod, state & ~forcemousemod))) { + ms->func(&(ms->arg)); + return 1; + } + } + + return 0; +} + +void +bpress(XEvent *e) +{ + struct timespec now; + int snap; + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 0)) + return; + + if (e->xbutton.button == Button1) { + /* + * If the user clicks below predefined timeouts specific + * snapping behaviour is exposed. + */ + clock_gettime(CLOCK_MONOTONIC, &now); + if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { + snap = SNAP_LINE; + } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { + snap = SNAP_WORD; + } else { + snap = 0; + } + xsel.tclick2 = xsel.tclick1; + xsel.tclick1 = now; + + selstart(evcol(e), evrow(e), snap); + } +} + +void +propnotify(XEvent *e) +{ + XPropertyEvent *xpev; + Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + + xpev = &e->xproperty; + if (xpev->state == PropertyNewValue && + (xpev->atom == XA_PRIMARY || + xpev->atom == clipboard)) { + selnotify(e); + } +} + +void +selnotify(XEvent *e) +{ + ulong nitems, ofs, rem; + int format; + uchar *data, *last, *repl; + Atom type, incratom, property = None; + + incratom = XInternAtom(xw.dpy, "INCR", 0); + + ofs = 0; + if (e->type == SelectionNotify) + property = e->xselection.property; + else if (e->type == PropertyNotify) + property = e->xproperty.atom; + + if (property == None) + return; + + do { + if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, + BUFSIZ/4, False, AnyPropertyType, + &type, &format, &nitems, &rem, + &data)) { + fprintf(stderr, "Clipboard allocation failed\n"); + return; + } + + if (e->type == PropertyNotify && nitems == 0 && rem == 0) { + /* + * If there is some PropertyNotify with no data, then + * this is the signal of the selection owner that all + * data has been transferred. We won't need to receive + * PropertyNotify events anymore. + */ + MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + } + + if (type == incratom) { + /* + * Activate the PropertyNotify events so we receive + * when the selection owner does send us the next + * chunk of data. + */ + MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + + /* + * Deleting the property is the transfer start signal. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); + continue; + } + + /* + * As seen in getsel: + * Line endings are inconsistent in the terminal and GUI world + * copy and pasting. When receiving some selection data, + * replace all '\n' with '\r'. + * FIXME: Fix the computer world. + */ + repl = data; + last = data + nitems * format / 8; + while ((repl = memchr(repl, '\n', last - repl))) { + *repl++ = '\r'; + } + + if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) + ttywrite("\033[200~", 6, 0); + ttywrite((char *)data, nitems * format / 8, 1); + if (IS_SET(MODE_BRCKTPASTE) && rem == 0) + ttywrite("\033[201~", 6, 0); + XFree(data); + /* number of 32-bit chunks returned */ + ofs += nitems * format / 32; + } while (rem > 0); + + /* + * Deleting the property again tells the selection owner to send the + * next data chunk in the property. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); +} + +void +xclipcopy(void) +{ + clipcopy(NULL); +} + +void +selclear_(XEvent *e) +{ + selclear(); +} + +void +selrequest(XEvent *e) +{ + XSelectionRequestEvent *xsre; + XSelectionEvent xev; + Atom xa_targets, string, clipboard; + char *seltext; + + xsre = (XSelectionRequestEvent *) e; + xev.type = SelectionNotify; + xev.requestor = xsre->requestor; + xev.selection = xsre->selection; + xev.target = xsre->target; + xev.time = xsre->time; + if (xsre->property == None) + xsre->property = xsre->target; + + /* reject */ + xev.property = None; + + xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); + if (xsre->target == xa_targets) { + /* respond with the supported type */ + string = xsel.xtarget; + XChangeProperty(xsre->display, xsre->requestor, xsre->property, + XA_ATOM, 32, PropModeReplace, + (uchar *) &string, 1); + xev.property = xsre->property; + } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { + /* + * xith XA_STRING non ascii characters may be incorrect in the + * requestor. It is not our problem, use utf8. + */ + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + if (xsre->selection == XA_PRIMARY) { + seltext = xsel.primary; + } else if (xsre->selection == clipboard) { + seltext = xsel.clipboard; + } else { + fprintf(stderr, + "Unhandled clipboard selection 0x%lx\n", + xsre->selection); + return; + } + if (seltext != NULL) { + XChangeProperty(xsre->display, xsre->requestor, + xsre->property, xsre->target, + 8, PropModeReplace, + (uchar *)seltext, strlen(seltext)); + xev.property = xsre->property; + } + } + + /* all done, send a notification to the listener */ + if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) + fprintf(stderr, "Error sending SelectionNotify event\n"); +} + +void +setsel(char *str, Time t) +{ + if (!str) + return; + + free(xsel.primary); + xsel.primary = str; + + XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); + if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) + selclear(); + clipcopy(NULL); +} + +void +xsetsel(char *str) +{ + setsel(str, CurrentTime); +} + +void +brelease(XEvent *e) +{ + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 1)) + return; + if (e->xbutton.button == Button1) + mousesel(e, 1); +} + +void +bmotion(XEvent *e) +{ + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + mousesel(e, 0); +} + +void +cresize(int width, int height) +{ + int col, row; + + if (width != 0) + win.w = width; + if (height != 0) + win.h = height; + + col = (win.w - 2 * borderpx) / win.cw; + row = (win.h - 2 * borderpx) / win.ch; + col = MAX(1, col); + row = MAX(1, row); + + tresize(col, row); + xresize(col, row); + ttyresize(win.tw, win.th); +} + +void +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, + xw.depth); + XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + + /* resize to new width */ + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); +} + +ushort +sixd_to_16bit(int x) +{ + return x == 0 ? 0 : 0x3737 + 0x2828 * x; +} + +int +xloadcolor(int i, const char *name, Color *ncolor) +{ + XRenderColor color = { .alpha = 0xffff }; + + if (!name) { + if (BETWEEN(i, 16, 255)) { /* 256 color */ + if (i < 6*6*6+16) { /* same colors as xterm */ + color.red = sixd_to_16bit( ((i-16)/36)%6 ); + color.green = sixd_to_16bit( ((i-16)/6) %6 ); + color.blue = sixd_to_16bit( ((i-16)/1) %6 ); + } else { /* greyscale */ + color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); + color.green = color.blue = color.red; + } + return XftColorAllocValue(xw.dpy, xw.vis, + xw.cmap, &color, ncolor); + } else + name = colorname[i]; + } + + return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); +} + +void +xloadcols(void) +{ + int i; + static int loaded; + Color *cp; + + if (loaded) { + for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) + XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); + } else { + dc.collen = MAX(LEN(colorname), 256); + dc.col = xmalloc(dc.collen * sizeof(Color)); + } + + for (i = 0; i < dc.collen; i++) + if (!xloadcolor(i, NULL, &dc.col[i])) { + if (colorname[i]) + die("could not allocate color '%s'\n", colorname[i]); + else + die("could not allocate color %d\n", i); + } + + /* set alpha value of bg color */ + if (opt_alpha) + alpha = strtof(opt_alpha, NULL); + dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); + dc.col[defaultbg].pixel &= 0x00FFFFFF; + dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; + loaded = 1; +} + +int +xsetcolorname(int x, const char *name) +{ + Color ncolor; + + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + if (!xloadcolor(x, name, &ncolor)) + return 1; + + XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); + dc.col[x] = ncolor; + + return 0; +} + +/* + * Absolute coordinates. + */ +void +xclear(int x1, int y1, int x2, int y2) +{ + XftDrawRect(xw.draw, + &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], + x1, y1, x2-x1, y2-y1); +} + +void +xhints(void) +{ + XClassHint class = {opt_name ? opt_name : termname, + opt_class ? opt_class : termname}; + XWMHints wm = {.flags = InputHint, .input = 1}; + XSizeHints *sizeh; + + sizeh = XAllocSizeHints(); + + sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; + sizeh->height = win.h; + sizeh->width = win.w; + 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; + sizeh->min_width = win.cw + 2 * borderpx; + if (xw.isfixed) { + sizeh->flags |= PMaxSize; + sizeh->min_width = sizeh->max_width = win.w; + sizeh->min_height = sizeh->max_height = win.h; + } + if (xw.gm & (XValue|YValue)) { + sizeh->flags |= USPosition | PWinGravity; + sizeh->x = xw.l; + sizeh->y = xw.t; + sizeh->win_gravity = xgeommasktogravity(xw.gm); + } + + XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, + &class); + XFree(sizeh); +} + +int +xgeommasktogravity(int mask) +{ + switch (mask & (XNegative|YNegative)) { + case 0: + return NorthWestGravity; + case XNegative: + return NorthEastGravity; + case YNegative: + return SouthWestGravity; + } + + return SouthEastGravity; +} + +int +xloadfont(Font *f, FcPattern *pattern) +{ + FcPattern *configured; + FcPattern *match; + FcResult result; + XGlyphInfo extents; + int wantattr, haveattr; + + /* + * Manually configure instead of calling XftMatchFont + * so that we can use the configured pattern for + * "missing glyph" lookups. + */ + configured = FcPatternDuplicate(pattern); + if (!configured) + return 1; + + FcConfigSubstitute(NULL, configured, FcMatchPattern); + XftDefaultSubstitute(xw.dpy, xw.scr, configured); + + match = FcFontMatch(NULL, configured, &result); + if (!match) { + FcPatternDestroy(configured); + return 1; + } + + if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { + FcPatternDestroy(configured); + FcPatternDestroy(match); + return 1; + } + + if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == + XftResultMatch)) { + /* + * Check if xft was unable to find a font with the appropriate + * slant but gave us one anyway. Try to mitigate. + */ + if ((XftPatternGetInteger(f->match->pattern, "slant", 0, + &haveattr) != XftResultMatch) || haveattr < wantattr) { + f->badslant = 1; + fputs("font slant does not match\n", stderr); + } + } + + if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == + XftResultMatch)) { + if ((XftPatternGetInteger(f->match->pattern, "weight", 0, + &haveattr) != XftResultMatch) || haveattr != wantattr) { + f->badweight = 1; + fputs("font weight does not match\n", stderr); + } + } + + XftTextExtentsUtf8(xw.dpy, f->match, + (const FcChar8 *) ascii_printable, + strlen(ascii_printable), &extents); + + f->set = NULL; + f->pattern = configured; + + f->ascent = f->match->ascent; + f->descent = f->match->descent; + f->lbearing = 0; + f->rbearing = f->match->max_advance_width; + + f->height = f->ascent + f->descent; + f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); + + return 0; +} + +void +xloadfonts(char *fontstr, double fontsize) +{ + FcPattern *pattern; + double fontval; + + if (fontstr[0] == '-') + pattern = XftXlfdParse(fontstr, False, False); + else + pattern = FcNameParse((FcChar8 *)fontstr); + + if (!pattern) + die("can't open font %s\n", fontstr); + + if (fontsize > 1) { + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternDel(pattern, FC_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); + usedfontsize = fontsize; + } else { + if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = fontval; + } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = -1; + } else { + /* + * Default font size is 12, if none given. This is to + * have a known usedfontsize value. + */ + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); + usedfontsize = 12; + } + defaultfontsize = usedfontsize; + } + + if (xloadfont(&dc.font, pattern)) + die("can't open font %s\n", fontstr); + + if (usedfontsize < 0) { + FcPatternGetDouble(dc.font.match->pattern, + FC_PIXEL_SIZE, 0, &fontval); + usedfontsize = fontval; + if (fontsize == 0) + defaultfontsize = fontval; + } + + /* Setting character width and height. */ + win.cw = ceilf(dc.font.width * cwscale); + win.ch = ceilf(dc.font.height * chscale); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + if (xloadfont(&dc.ifont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_WEIGHT); + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + if (xloadfont(&dc.ibfont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + if (xloadfont(&dc.bfont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDestroy(pattern); +} + +void +xunloadfont(Font *f) +{ + XftFontClose(xw.dpy, f->match); + FcPatternDestroy(f->pattern); + if (f->set) + FcFontSetDestroy(f->set); +} + +void +xunloadfonts(void) +{ + /* Free the loaded fonts in the font cache. */ + while (frclen > 0) + XftFontClose(xw.dpy, frc[--frclen].font); + + xunloadfont(&dc.font); + xunloadfont(&dc.bfont); + xunloadfont(&dc.ifont); + xunloadfont(&dc.ibfont); +} + +int +ximopen(Display *dpy) +{ + XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; + XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; + + xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); + if (xw.ime.xim == NULL) + return 0; + + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) + fprintf(stderr, "XSetIMValues: " + "Could not set XNDestroyCallback.\n"); + + xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, + NULL); + + if (xw.ime.xic == NULL) { + xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, + XNDestroyCallback, &icdestroy, + NULL); + } + if (xw.ime.xic == NULL) + fprintf(stderr, "XCreateIC: Could not create input context.\n"); + + return 1; +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + if (ximopen(dpy)) + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.ime.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + XFree(xw.ime.spotlist); +} + +int +xicdestroy(XIC xim, XPointer client, XPointer call) +{ + xw.ime.xic = NULL; + return 1; +} + +void +xinit(int cols, int rows) +{ + XGCValues gcvalues; + Cursor cursor; + Window parent; + pid_t thispid = getpid(); + XColor xmousefg, xmousebg; + XWindowAttributes attr; + XVisualInfo vis; + + if (!(xw.dpy = XOpenDisplay(NULL))) + die("can't open display\n"); + xw.scr = XDefaultScreen(xw.dpy); + + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { + parent = XRootWindow(xw.dpy, xw.scr); + xw.depth = 32; + } else { + XGetWindowAttributes(xw.dpy, parent, &attr); + xw.depth = attr.depth; + } + + XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); + xw.vis = vis.visual; + + /* font */ + if (!FcInit()) + die("could not init fontconfig.\n"); + + usedfont = (opt_font == NULL)? font : opt_font; + xloadfonts(usedfont, 0); + + /* colors */ + xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); + xloadcols(); + + /* adjust fixed window geometry */ + 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) + xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; + + /* Events */ + xw.attrs.background_pixel = dc.col[defaultbg].pixel; + xw.attrs.border_pixel = dc.col[defaultbg].pixel; + xw.attrs.bit_gravity = NorthWestGravity; + xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask + | ExposureMask | VisibilityChangeMask | StructureNotifyMask + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + xw.attrs.colormap = xw.cmap; + + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, + win.w, win.h, 0, xw.depth, InputOutput, + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &xw.attrs); + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); + dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + + /* font spec buffer */ + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); + + /* Xft rendering context */ + xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); + + /* input methods */ + if (!ximopen(xw.dpy)) { + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + } + + /* white cursor, black outline */ + cursor = XCreateFontCursor(xw.dpy, mouseshape); + XDefineCursor(xw.dpy, xw.win, cursor); + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { + xmousefg.red = 0xffff; + xmousefg.green = 0xffff; + xmousefg.blue = 0xffff; + } + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { + xmousebg.red = 0x0000; + xmousebg.green = 0x0000; + xmousebg.blue = 0x0000; + } + + 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); + XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); + + xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); + XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, + PropModeReplace, (uchar *)&thispid, 1); + + win.mode = MODE_NUMLOCK; + resettitle(); + xhints(); + XMapWindow(xw.dpy, xw.win); + XSync(xw.dpy, False); + + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); + xsel.primary = NULL; + xsel.clipboard = NULL; + xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); + if (xsel.xtarget == None) + xsel.xtarget = XA_STRING; +} + +int +xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) +{ + 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; + float runewidth = win.cw; + Rune rune; + FT_UInt glyphidx; + FcResult fcres; + FcPattern *fcpattern, *fontpattern; + FcFontSet *fcsets[] = { NULL }; + FcCharSet *fccharset; + int i, f, numspecs = 0; + + for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { + /* Fetch rune and mode for current glyph. */ + rune = glyphs[i].u; + mode = glyphs[i].mode; + + /* Skip dummy wide-character spacing. */ + if (mode == ATTR_WDUMMY) + continue; + + /* Determine font for glyph if different from previous glyph. */ + if (prevmode != mode) { + prevmode = mode; + font = &dc.font; + frcflags = FRC_NORMAL; + runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { + font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } else if (mode & ATTR_ITALIC) { + font = &dc.ifont; + frcflags = FRC_ITALIC; + } else if (mode & ATTR_BOLD) { + font = &dc.bfont; + frcflags = FRC_BOLD; + } + yp = winy + font->ascent; + } + + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + if (glyphidx) { + specs[numspecs].font = font->match; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + continue; + } + + /* Fallback on font cache, search the font cache for match. */ + for (f = 0; f < frclen; f++) { + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); + /* Everything correct. */ + if (glyphidx && frc[f].flags == frcflags) + break; + /* We got a default font for a not found glyph. */ + if (!glyphidx && frc[f].flags == frcflags + && frc[f].unicodep == rune) { + break; + } + } + + /* Nothing was found. Use fontconfig to find matching font. */ + if (f >= frclen) { + if (!font->set) + font->set = FcFontSort(0, font->pattern, + 1, 0, &fcres); + fcsets[0] = font->set; + + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + * + * Xft and fontconfig are design failures. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, + fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + + FcConfigSubstitute(0, fcpattern, + FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + fontpattern = FcFontSetMatch(0, fcsets, 1, + fcpattern, &fcres); + + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } + + frc[frclen].font = XftFontOpenPattern(xw.dpy, + fontpattern); + if (!frc[frclen].font) + die("XftFontOpenPattern failed seeking fallback font: %s\n", + strerror(errno)); + frc[frclen].flags = frcflags; + frc[frclen].unicodep = rune; + + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); + + f = frclen; + frclen++; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + } + + specs[numspecs].font = frc[f].font; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + } + + return numspecs; +} + +void +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) +{ + int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); + 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; + XRectangle r; + + /* Fallback on color display for attributes not supported by the font */ + if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { + if (dc.ibfont.badslant || dc.ibfont.badweight) + base.fg = defaultattr; + } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || + (base.mode & ATTR_BOLD && dc.bfont.badweight)) { + base.fg = defaultattr; + } + + if (IS_TRUECOL(base.fg)) { + colfg.alpha = 0xffff; + colfg.red = TRUERED(base.fg); + colfg.green = TRUEGREEN(base.fg); + colfg.blue = TRUEBLUE(base.fg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); + fg = &truefg; + } else { + fg = &dc.col[base.fg]; + } + + if (IS_TRUECOL(base.bg)) { + colbg.alpha = 0xffff; + colbg.green = TRUEGREEN(base.bg); + colbg.red = TRUERED(base.bg); + colbg.blue = TRUEBLUE(base.bg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); + bg = &truebg; + } else { + bg = &dc.col[base.bg]; + } + + /* Change basic system colors [0-7] to bright system colors [8-15] */ + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) + fg = &dc.col[base.fg + 8]; + + if (IS_SET(MODE_REVERSE)) { + if (fg == &dc.col[defaultfg]) { + fg = &dc.col[defaultbg]; + } else { + colfg.red = ~fg->color.red; + colfg.green = ~fg->color.green; + colfg.blue = ~fg->color.blue; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, + &revfg); + fg = &revfg; + } + + if (bg == &dc.col[defaultbg]) { + bg = &dc.col[defaultfg]; + } else { + colbg.red = ~bg->color.red; + colbg.green = ~bg->color.green; + colbg.blue = ~bg->color.blue; + colbg.alpha = bg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, + &revbg); + bg = &revbg; + } + } + + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { + colfg.red = fg->color.red / 2; + colfg.green = fg->color.green / 2; + colfg.blue = fg->color.blue / 2; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); + fg = &revfg; + } + + if (base.mode & ATTR_REVERSE) { + temp = fg; + fg = bg; + bg = temp; + } + + if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) + fg = bg; + + if (base.mode & ATTR_INVISIBLE) + fg = bg; + + /* Intelligent cleaning up of the borders. */ + if (x == 0) { + xclear(0, (y == 0)? 0 : winy, borderpx, + winy + win.ch + + ((winy + win.ch >= borderpx + win.th)? win.h : 0)); + } + if (winx + width >= borderpx + win.tw) { + xclear(winx + width, (y == 0)? 0 : winy, win.w, + ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); + } + if (y == 0) + 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. */ + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + + /* Set the clip region because Xft is sometimes dirty. */ + r.x = 0; + r.y = 0; + r.height = win.ch; + r.width = width; + XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + + /* Render underline and strikethrough. */ + if (base.mode & ATTR_UNDERLINE) { + XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, + width, 1); + } + + if (base.mode & ATTR_STRUCK) { + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, + width, 1); + } + + /* Reset clip to none. */ + XftDrawSetClip(xw.draw, 0); +} + +void +xdrawglyph(Glyph g, int x, int y) +{ + int numspecs; + XftGlyphFontSpec spec; + + numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); + xdrawglyphfontspecs(&spec, g, numspecs, x, y); +} + +void +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) +{ + Color drawcol; + + /* remove the old cursor */ + if (selected(ox, oy)) + og.mode ^= ATTR_REVERSE; + xdrawglyph(og, ox, oy); + + if (IS_SET(MODE_HIDE)) + return; + + /* + * Select the right color for the right mode. + */ + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; + + if (IS_SET(MODE_REVERSE)) { + g.mode |= ATTR_REVERSE; + g.bg = defaultfg; + if (selected(cx, cy)) { + drawcol = dc.col[defaultcs]; + g.fg = defaultrcs; + } else { + drawcol = dc.col[defaultrcs]; + g.fg = defaultcs; + } + } else { + if (selected(cx, cy)) { + g.fg = defaultfg; + g.bg = defaultrcs; + } else { + g.fg = defaultbg; + g.bg = defaultcs; + } + drawcol = dc.col[g.bg]; + } + + /* draw the new one */ + if (IS_SET(MODE_FOCUSED)) { + switch (win.cursor) { + case 7: /* st extension */ + g.u = 0x2603; /* snowman (U+2603) */ + /* FALLTHROUGH */ + case 0: /* Blinking Block */ + case 1: /* Blinking Block (Default) */ + case 2: /* Steady Block */ + xdrawglyph(g, cx, cy); + break; + case 3: /* Blinking Underline */ + case 4: /* Steady Underline */ + XftDrawRect(xw.draw, &drawcol, + 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, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + cursorthickness, win.ch); + break; + } + } else { + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + win.cw - 1, 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + (cx + 1) * win.cw - 1, + borderpx + cy * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - 1, + win.cw, 1); + } +} + +void +xsetenv(void) +{ + char buf[sizeof(long) * 8 + 1]; + + snprintf(buf, sizeof(buf), "%lu", xw.win); + setenv("WINDOWID", buf, 1); +} + +void +xsettitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop); + XSetWMName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); + XFree(prop.value); +} + +int +xstartdraw(void) +{ + return IS_SET(MODE_VISIBLE); +} + +void +xdrawline(Line line, int x1, int y1, int x2) +{ + int i, x, ox, numspecs; + Glyph base, new; + XftGlyphFontSpec *specs = xw.specbuf; + + numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + if (new.mode == ATTR_WDUMMY) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1); +} + +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); +} + +void +xximspot(int x, int y) +{ + if (xw.ime.xic == NULL) + return; + + xw.ime.spot.x = borderpx + x * win.cw; + xw.ime.spot.y = borderpx + (y + 1) * win.ch; + + XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); +} + +void +expose(XEvent *ev) +{ + redraw(); +} + +void +visibility(XEvent *ev) +{ + XVisibilityEvent *e = &ev->xvisibility; + + MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); +} + +void +unmap(XEvent *ev) +{ + win.mode &= ~MODE_VISIBLE; +} + +void +xsetpointermotion(int set) +{ + MODBIT(xw.attrs.event_mask, set, PointerMotionMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); +} + +void +xsetmode(int set, unsigned int flags) +{ + int mode = win.mode; + MODBIT(win.mode, set, flags); + if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) + redraw(); +} + +int +xsetcursor(int cursor) +{ + if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ + return 1; + win.cursor = cursor; + return 0; +} + +void +xseturgency(int add) +{ + XWMHints *h = XGetWMHints(xw.dpy, xw.win); + + MODBIT(h->flags, add, XUrgencyHint); + XSetWMHints(xw.dpy, xw.win, h); + XFree(h); +} + +void +xbell(void) +{ + if (!(IS_SET(MODE_FOCUSED))) + xseturgency(1); + if (bellvolume) + XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); +} + +void +focus(XEvent *ev) +{ + XFocusChangeEvent *e = &ev->xfocus; + + if (e->mode == NotifyGrab) + return; + + if (ev->type == FocusIn) { + if (xw.ime.xic) + XSetICFocus(xw.ime.xic); + win.mode |= MODE_FOCUSED; + xseturgency(0); + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[I", 3, 0); + } else { + if (xw.ime.xic) + XUnsetICFocus(xw.ime.xic); + win.mode &= ~MODE_FOCUSED; + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[O", 3, 0); + } +} + +int +match(uint mask, uint state) +{ + return mask == XK_ANY_MOD || mask == (state & ~ignoremod); +} + +char* +kmap(KeySym k, uint state) +{ + Key *kp; + int i; + + /* Check for mapped keys out of X11 function keys. */ + for (i = 0; i < LEN(mappedkeys); i++) { + if (mappedkeys[i] == k) + break; + } + if (i == LEN(mappedkeys)) { + if ((k & 0xFFFF) < 0xFD00) + return NULL; + } + + for (kp = key; kp < key + LEN(key); kp++) { + if (kp->k != k) + continue; + + if (!match(kp->mask, state)) + continue; + + if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) + continue; + if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) + continue; + + if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) + continue; + + return kp->s; + } + + return NULL; +} + +void +kpress(XEvent *ev) +{ + XKeyEvent *e = &ev->xkey; + KeySym ksym; + char buf[64], *customkey; + int len; + Rune c; + Status status; + Shortcut *bp; + + if (IS_SET(MODE_KBDLOCK)) + return; + + if (xw.ime.xic) + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); + else + len = XLookupString(e, buf, sizeof buf, &ksym, NULL); + /* 1. shortcuts */ + for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { + if (ksym == bp->keysym && match(bp->mod, e->state)) { + bp->func(&(bp->arg)); + return; + } + } + + /* 2. custom keys from config.h */ + if ((customkey = kmap(ksym, e->state))) { + ttywrite(customkey, strlen(customkey), 1); + return; + } + + /* 3. composed string from input method */ + if (len == 0) + return; + if (len == 1 && e->state & Mod1Mask) { + if (IS_SET(MODE_8BIT)) { + if (*buf < 0177) { + c = *buf | 0x80; + len = utf8encode(c, buf); + } + } else { + buf[1] = buf[0]; + buf[0] = '\033'; + len = 2; + } + } + ttywrite(buf, len, 1); +} + +void +cmessage(XEvent *e) +{ + /* + * See xembed specs + * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html + */ + if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { + if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { + win.mode |= MODE_FOCUSED; + xseturgency(0); + } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { + win.mode &= ~MODE_FOCUSED; + } + } else if (e->xclient.data.l[0] == xw.wmdeletewin) { + ttyhangup(); + exit(0); + } +} + +void +resize(XEvent *e) +{ + if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) + return; + + cresize(e->xconfigure.width, e->xconfigure.height); +} + +void +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; + + /* Waiting for window mapping */ + do { + XNextEvent(xw.dpy, &ev); + /* + * This XFilterEvent call is required because of XOpenIM. It + * does filter out the key event and some client message for + * the input method too. + */ + if (XFilterEvent(&ev, None)) + continue; + if (ev.type == ConfigureNotify) { + w = ev.xconfigure.width; + h = ev.xconfigure.height; + } + } while (ev.type != MapNotify); + + ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); + cresize(w, h); + + 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)); + } + clock_gettime(CLOCK_MONOTONIC, &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); + } + + /* + * 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 */ + } + + /* 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; + } +} + +void +usage(void) +{ + die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid]" + " [[-e] command [args ...]]\n" + " %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid] -l line" + " [stty_args ...]\n", argv0, argv0); +} + +int +main(int argc, char *argv[]) +{ + xw.l = xw.t = 0; + xw.isfixed = False; + xsetcursor(cursorshape); + + ARGBEGIN { + case 'a': + allowaltscreen = 0; + break; + case 'A': + opt_alpha = EARGF(usage()); + break; + case 'c': + opt_class = EARGF(usage()); + break; + case 'e': + if (argc > 0) + --argc, ++argv; + goto run; + case 'f': + opt_font = EARGF(usage()); + break; + case 'g': + xw.gm = XParseGeometry(EARGF(usage()), + &xw.l, &xw.t, &cols, &rows); + break; + case 'i': + xw.isfixed = 1; + break; + case 'o': + opt_io = EARGF(usage()); + break; + case 'l': + opt_line = EARGF(usage()); + break; + case 'n': + opt_name = EARGF(usage()); + break; + case 't': + case 'T': + opt_title = EARGF(usage()); + break; + case 'w': + opt_embed = EARGF(usage()); + break; + case 'v': + die("%s " VERSION "\n", argv0); + break; + default: + usage(); + } ARGEND; + +run: + if (argc > 0) /* eat all remaining arguments */ + opt_cmd = argv; + + if (!opt_title) + opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; + + setlocale(LC_CTYPE, ""); + XSetLocaleModifiers(""); + cols = MAX(cols, 1); + rows = MAX(rows, 1); + tnew(cols, rows); + xinit(cols, rows); + xsetenv(); + selinit(); + run(); + + return 0; +} diff --git a/x.o b/x.o index b0198021c9840c51698c138c37cfa137ea9cb920..777dd20893da19a2d9af6d9c1a505d1aaf9ced5a 100644 GIT binary patch literal 75520 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 literal 75280 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 bVYmFP{Wl6C21?(IKW>8Xjrs2T(&_&n;WJ`N