From 5042946b6276d9496aa6f0d29d999c6d0c756ccd Mon Sep 17 00:00:00 2001 From: WanderingPenwing Date: Mon, 29 Jul 2024 09:22:49 +0200 Subject: [PATCH] initial commit --- config.def.h | 56 +- config.h | 474 +++++++ shell.nix | 13 + st | Bin 0 -> 96688 bytes st-scrollback-0.8.4.diff | 351 +++++ st.c | 125 +- st.c.orig | 2678 ++++++++++++++++++++++++++++++++++++++ st.c.rej | 13 + st.h | 2 + st.o | Bin 0 -> 82264 bytes x.o | Bin 0 -> 75216 bytes 11 files changed, 3657 insertions(+), 55 deletions(-) create mode 100644 config.h create mode 100644 shell.nix create mode 100755 st create mode 100644 st-scrollback-0.8.4.diff create mode 100644 st.c.orig create mode 100644 st.c.rej create mode 100644 st.o create mode 100644 x.o diff --git a/config.def.h b/config.def.h index 2cd740a..203722e 100644 --- a/config.def.h +++ b/config.def.h @@ -5,7 +5,7 @@ * * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html */ -static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; +static char *font = "DejaVu Sans Mono:pixelsize=24:antialias=true:autohint=true"; static int borderpx = 2; /* @@ -19,7 +19,7 @@ static int borderpx = 2; static char *shell = "/bin/sh"; char *utmp = NULL; /* scroll program: to enable use a string like "scroll" */ -char *scroll = NULL; +char *scroll = "scroll"; char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; /* identification sequence returned in DA and DECID */ @@ -95,33 +95,31 @@ unsigned int tabspaces = 8; /* Terminal colors (16 first used in escape sequence) */ static const char *colorname[] = { - /* 8 normal colors */ - "black", - "red3", - "green3", - "yellow3", - "blue2", - "magenta3", - "cyan3", - "gray90", + /* 8 normal colors */ + "#3B4252", /* nord1 */ + "#BF616A", /* nord11 */ + "#A3BE8C", /* nord14 */ + "#EBCB8B", /* nord13 */ + "#81A1C1", /* nord9 */ + "#B48EAD", /* nord15 */ + "#88C0D0", /* nord8 */ + "#E5E9F0", /* nord5 */ - /* 8 bright colors */ - "gray50", - "red", - "green", - "yellow", - "#5c5cff", - "magenta", - "cyan", - "white", + /* 8 bright colors */ + "#4C566A", /* nord3 */ + "#D08770", /* nord12 */ + "#8FBCBB", /* nord7 */ + "#EBCB8B", /* nord13 */ + "#5E81AC", /* nord10 */ + "#B48EAD", /* nord15 (same as normal magenta) */ + "#88C0D0", /* nord8 (same as normal cyan) */ + "#ECEFF4", /* nord6 */ - [255] = 0, + [255] = 0, - /* more colors can be added after 255 to use with DefaultXX */ - "#cccccc", - "#555555", - "gray90", /* default foreground colour */ - "black", /* default background colour */ + /* more colors can be added after 255 to use with DefaultXX */ + "#D8DEE9", /* nord4 (default foreground colour) */ + "#2E3440", /* nord0 (default background colour) */ }; @@ -129,8 +127,8 @@ static const char *colorname[] = { * Default colors (colorname index) * foreground, background, cursor, reverse cursor */ -unsigned int defaultfg = 258; -unsigned int defaultbg = 259; +unsigned int defaultfg = 256; +unsigned int defaultbg = 257; unsigned int defaultcs = 256; static unsigned int defaultrcs = 257; @@ -201,6 +199,8 @@ static Shortcut shortcuts[] = { { TERMMOD, XK_Y, selpaste, {.i = 0} }, { ShiftMask, XK_Insert, selpaste, {.i = 0} }, { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, }; /* diff --git a/config.h b/config.h new file mode 100644 index 0000000..a3615ef --- /dev/null +++ b/config.h @@ -0,0 +1,474 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "DejaVu Sans Mono:pixelsize=16:antialias=true:autohint=true"; +static int borderpx = 2; + +/* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e + * 2: scroll and/or utmp + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/sh"; +char *utmp = NULL; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = NULL; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +char *vtiden = "\033[?6c"; + +/* Kerning / character bounding-box multipliers */ +static float cwscale = 1.0; +static float chscale = 1.0; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +wchar_t *worddelimiters = L" "; + +/* selection timeouts (in milliseconds) */ +static unsigned int doubleclicktimeout = 300; +static unsigned int tripleclicktimeout = 600; + +/* alt screens */ +int allowaltscreen = 1; + +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 2; +static double maxlatency = 33; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 800; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 2; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "st-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +static const int alpha = 0xff; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "#3B4252", /* nord1 */ + "#BF616A", /* nord11 */ + "#A3BE8C", /* nord14 */ + "#EBCB8B", /* nord13 */ + "#81A1C1", /* nord9 */ + "#B48EAD", /* nord15 */ + "#88C0D0", /* nord8 */ + "#E5E9F0", /* nord5 */ + + /* 8 bright colors */ + "#4C566A", /* nord3 */ + "#D08770", /* nord12 */ + "#8FBCBB", /* nord7 */ + "#EBCB8B", /* nord13 */ + "#5E81AC", /* nord10 */ + "#B48EAD", /* nord15 (same as normal magenta) */ + "#88C0D0", /* nord8 (same as normal cyan) */ + "#ECEFF4", /* nord6 */ + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#D8DEE9", /* nord4 (default foreground colour) */ + "#2E3440", /* nord0 (default background colour) */ +}; + + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 256; +unsigned int defaultbg = 257; +unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 2; + +/* + * Default columns and rows numbers + */ + +static unsigned int cols = 80; +static unsigned int rows = 24; + +/* + * Default colour and shape of the mouse cursor + */ +static unsigned int mouseshape = XC_xterm; +static unsigned int mousefg = 7; +static unsigned int mousebg = 0; + +/* + * Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. + */ +static unsigned int defaultattr = 11; + +/* + * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). + * Note that if you want to use ShiftMask with selmasks, set this to an other + * modifier, set to 0 to not use it. + */ +static uint forcemousemod = ShiftMask; + +/* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. + */ +static MouseShortcut mshortcuts[] = { + /* mask button function argument release */ + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, + { XK_ANY_MOD, Button4, kscrollup, {.i = 3}, 0 }, + { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, + { XK_ANY_MOD, Button5, kscrolldown, {.i = 3}, 0 }, +}; + +/* Internal keyboard shortcuts. */ +#define MODKEY Mod1Mask +#define TERMMOD (ControlMask|ShiftMask) + +static Shortcut shortcuts[] = { + /* mask keysym function argument */ + { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, + { ControlMask, XK_Print, toggleprinter, {.i = 0} }, + { ShiftMask, XK_Print, printscreen, {.i = 0} }, + { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, + { TERMMOD, XK_Prior, zoom, {.f = +1} }, + { TERMMOD, XK_Next, zoom, {.f = -1} }, + { TERMMOD, XK_Home, zoomreset, {.f = 0} }, + { TERMMOD, XK_C, clipcopy, {.i = 0} }, + { TERMMOD, XK_V, clippaste, {.i = 0} }, + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, +}; + +/* + * Special keys (change & recompile st.info accordingly) + * + * Mask value: + * * Use XK_ANY_MOD to match the key no matter modifiers state + * * Use XK_NO_MOD to match the key alone (no modifiers) + * appkey value: + * * 0: no value + * * > 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/shell.nix b/shell.nix new file mode 100644 index 0000000..b847e35 --- /dev/null +++ b/shell.nix @@ -0,0 +1,13 @@ +{ pkgs ? import {} }: + +pkgs.mkShell { + buildInputs = [ + pkgs.gnumake + pkgs.gcc + pkgs.pkg-config + pkgs.xorg.libX11 + pkgs.xorg.libXft + pkgs.fontconfig + pkgs.freetype + ]; +} diff --git a/st b/st new file mode 100755 index 0000000000000000000000000000000000000000..794bc8afc48ee47328b9d9c795978e1ae72d89f1 GIT binary patch literal 96688 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%0AU^PlBfbv2=|N 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 0 && term.line[y][i - 1].u == ' ') ++ while (i > 0 && TLINE(y)[i - 1].u == ' ') + --i; + + return i; +@@ -526,7 +533,7 @@ selsnap(int *x, int *y, int direction) + * Snap around if the word wraps around at the end or + * beginning of a line. + */ +- prevgp = &term.line[*y][*x]; ++ prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; +@@ -541,14 +548,14 @@ selsnap(int *x, int *y, int direction) + yt = *y, xt = *x; + else + yt = newy, xt = newx; +- if (!(term.line[yt][xt].mode & ATTR_WRAP)) ++ if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + +- gp = &term.line[newy][newx]; ++ gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) +@@ -569,14 +576,14 @@ selsnap(int *x, int *y, int direction) + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { +- if (!(term.line[*y-1][term.col-1].mode ++ if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { +- if (!(term.line[*y][term.col-1].mode ++ if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } +@@ -607,13 +614,13 @@ getsel(void) + } + + if (sel.type == SEL_RECTANGULAR) { +- gp = &term.line[y][sel.nb.x]; ++ gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { +- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; ++ gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } +- last = &term.line[y][MIN(lastx, linelen-1)]; ++ last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + +@@ -848,6 +855,9 @@ 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); +@@ -1059,13 +1069,53 @@ tswapscreen(void) + } + + void +-tscrolldown(int orig, int n) ++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); + +@@ -1075,17 +1125,28 @@ tscrolldown(int orig, int n) + term.line[i-n] = temp; + } + +- selscroll(orig, n); ++ if (term.scr == 0) ++ selscroll(orig, n); + } + + void +-tscrollup(int orig, int n) ++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); + +@@ -1095,7 +1156,8 @@ tscrollup(int orig, int n) + term.line[i+n] = temp; + } + +- selscroll(orig, -n); ++ if (term.scr == 0) ++ selscroll(orig, -n); + } + + void +@@ -1124,7 +1186,7 @@ tnewline(int first_col) + int y = term.c.y; + + if (y == term.bot) { +- tscrollup(term.top, 1); ++ tscrollup(term.top, 1, 1); + } else { + y++; + } +@@ -1289,14 +1351,14 @@ void + tinsertblankline(int n) + { + if (BETWEEN(term.c.y, term.top, term.bot)) +- tscrolldown(term.c.y, n); ++ tscrolldown(term.c.y, n, 0); + } + + void + tdeleteline(int n) + { + if (BETWEEN(term.c.y, term.top, term.bot)) +- tscrollup(term.c.y, n); ++ tscrollup(term.c.y, n, 0); + } + + int32_t +@@ -1733,11 +1795,11 @@ csihandle(void) + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); +- tscrollup(term.top, csiescseq.arg[0]); ++ 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]); ++ tscrolldown(term.top, csiescseq.arg[0], 0); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); +@@ -2241,7 +2303,7 @@ eschandle(uchar ascii) + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { +- tscrollup(term.top, 1); ++ tscrollup(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } +@@ -2254,7 +2316,7 @@ eschandle(uchar ascii) + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { +- tscrolldown(term.top, 1); ++ tscrolldown(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } +@@ -2464,7 +2526,7 @@ twrite(const char *buf, int buflen, int show_ctrl) + void + tresize(int col, int row) + { +- int i; ++ int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; +@@ -2501,6 +2563,14 @@ tresize(int col, int row) + 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)); +@@ -2559,7 +2629,7 @@ drawregion(int x1, int y1, int x2, int y2) + continue; + + term.dirty[y] = 0; +- xdrawline(term.line[y], x1, y, x2); ++ xdrawline(TLINE(y), x1, y, x2); + } + } + +@@ -2580,8 +2650,9 @@ draw(void) + cx--; + + drawregion(0, 0, term.col, term.row); +- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], +- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); ++ 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(); +diff --git a/st.h b/st.h +index 3d351b6..f44e1d3 100644 +--- a/st.h ++++ b/st.h +@@ -81,6 +81,8 @@ 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 *); diff --git a/st.c b/st.c index 57c6e96..2c766e6 100644 --- a/st.c +++ b/st.c @@ -35,6 +35,7 @@ #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) @@ -42,6 +43,9 @@ #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, @@ -115,6 +119,9 @@ typedef struct { 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 */ @@ -185,8 +192,8 @@ static void tnewline(int); static void tputtab(int); static void tputc(Rune); static void treset(void); -static void tscrollup(int, int); -static void tscrolldown(int, int); +static void tscrollup(int, int, int); +static void tscrolldown(int, int, int); static void tsetattr(const int *, int); static void tsetchar(Rune, const Glyph *, int, int); static void tsetdirt(int, int); @@ -409,10 +416,10 @@ tlinelen(int y) { int i = term.col; - if (term.line[y][i - 1].mode & ATTR_WRAP) + if (TLINE(y)[i - 1].mode & ATTR_WRAP) return i; - while (i > 0 && term.line[y][i - 1].u == ' ') + while (i > 0 && TLINE(y)[i - 1].u == ' ') --i; return i; @@ -521,7 +528,7 @@ selsnap(int *x, int *y, int direction) * Snap around if the word wraps around at the end or * beginning of a line. */ - prevgp = &term.line[*y][*x]; + prevgp = &TLINE(*y)[*x]; prevdelim = ISDELIM(prevgp->u); for (;;) { newx = *x + direction; @@ -536,14 +543,14 @@ selsnap(int *x, int *y, int direction) yt = *y, xt = *x; else yt = newy, xt = newx; - if (!(term.line[yt][xt].mode & ATTR_WRAP)) + if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) break; } if (newx >= tlinelen(newy)) break; - gp = &term.line[newy][newx]; + gp = &TLINE(newy)[newx]; delim = ISDELIM(gp->u); if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim || (delim && gp->u != prevgp->u))) @@ -564,14 +571,14 @@ selsnap(int *x, int *y, int direction) *x = (direction < 0) ? 0 : term.col - 1; if (direction < 0) { for (; *y > 0; *y += direction) { - if (!(term.line[*y-1][term.col-1].mode + if (!(TLINE(*y-1)[term.col-1].mode & ATTR_WRAP)) { break; } } } else if (direction > 0) { for (; *y < term.row-1; *y += direction) { - if (!(term.line[*y][term.col-1].mode + if (!(TLINE(*y)[term.col-1].mode & ATTR_WRAP)) { break; } @@ -602,13 +609,13 @@ getsel(void) } if (sel.type == SEL_RECTANGULAR) { - gp = &term.line[y][sel.nb.x]; + gp = &TLINE(y)[sel.nb.x]; lastx = sel.ne.x; } else { - gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; + gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; } - last = &term.line[y][MIN(lastx, linelen-1)]; + last = &TLINE(y)[MIN(lastx, linelen-1)]; while (last >= gp && last->u == ' ') --last; @@ -844,6 +851,9 @@ 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); @@ -1055,13 +1065,53 @@ tswapscreen(void) } void -tscrolldown(int orig, int n) +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); @@ -1071,17 +1121,28 @@ tscrolldown(int orig, int n) term.line[i-n] = temp; } - selscroll(orig, n); + if (term.scr == 0) + selscroll(orig, n); } void -tscrollup(int orig, int n) +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); @@ -1091,7 +1152,8 @@ tscrollup(int orig, int n) term.line[i+n] = temp; } - selscroll(orig, -n); + if (term.scr == 0) + selscroll(orig, -n); } void @@ -1120,7 +1182,7 @@ tnewline(int first_col) int y = term.c.y; if (y == term.bot) { - tscrollup(term.top, 1); + tscrollup(term.top, 1, 1); } else { y++; } @@ -1288,14 +1350,14 @@ void tinsertblankline(int n) { if (BETWEEN(term.c.y, term.top, term.bot)) - tscrolldown(term.c.y, n); + tscrolldown(term.c.y, n, 0); } void tdeleteline(int n) { if (BETWEEN(term.c.y, term.top, term.bot)) - tscrollup(term.c.y, n); + tscrollup(term.c.y, n, 0); } int32_t @@ -1733,11 +1795,11 @@ csihandle(void) case 'S': /* SU -- Scroll line up */ if (csiescseq.priv) break; DEFAULT(csiescseq.arg[0], 1); - tscrollup(term.top, csiescseq.arg[0]); + 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]); + tscrolldown(term.top, csiescseq.arg[0], 0); break; case 'L': /* IL -- Insert blank lines */ DEFAULT(csiescseq.arg[0], 1); @@ -2309,7 +2371,7 @@ eschandle(uchar ascii) return 0; case 'D': /* IND -- Linefeed */ if (term.c.y == term.bot) { - tscrollup(term.top, 1); + tscrollup(term.top, 1, 1); } else { tmoveto(term.c.x, term.c.y+1); } @@ -2322,7 +2384,7 @@ eschandle(uchar ascii) break; case 'M': /* RI -- Reverse index */ if (term.c.y == term.top) { - tscrolldown(term.top, 1); + tscrolldown(term.top, 1, 1); } else { tmoveto(term.c.x, term.c.y-1); } @@ -2545,7 +2607,7 @@ twrite(const char *buf, int buflen, int show_ctrl) void tresize(int col, int row) { - int i; + int i, j; int minrow = MIN(row, term.row); int mincol = MIN(col, term.col); int *bp; @@ -2582,6 +2644,14 @@ tresize(int col, int row) 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)); @@ -2640,7 +2710,7 @@ drawregion(int x1, int y1, int x2, int y2) continue; term.dirty[y] = 0; - xdrawline(term.line[y], x1, y, x2); + xdrawline(TLINE(y), x1, y, x2); } } @@ -2661,8 +2731,9 @@ draw(void) cx--; drawregion(0, 0, term.col, term.row); - xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], - term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + 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(); diff --git a/st.c.orig b/st.c.orig new file mode 100644 index 0000000..57c6e96 --- /dev/null +++ b/st.c.orig @@ -0,0 +1,2678 @@ +/* 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 + +/* 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)) + +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 */ + 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 void osc_color_response(int, int, int); +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); +static void tscrolldown(int, int); +static void tsetattr(const int *, int); +static void tsetchar(Rune, const Glyph *, int, int); +static void tsetdirt(int, int); +static void tsetscroll(int, int); +static void tswapscreen(void); +static void tsetmode(int, int, const int *, int); +static int twrite(const char *, int, int); +static void tfulldirt(void); +static void tcontrolcode(uchar ); +static void tdectest(char ); +static void tdefutf8(char); +static int32_t tdefcolor(const int *, int *, int); +static 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 const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +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(const char *s) +{ + char *p; + + if ((p = strdup(s)) == NULL) + die("strdup: %s\n", strerror(errno)); + + return p; +} + +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; +} + +char +base64dec_getc(const char **src) +{ + while (**src && !isprint((unsigned char)**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char * +base64dec(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + static const char base64_digits[256] = { + [43] = 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 + }; + + 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 (term.line[y][i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && term.line[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; + const 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 = &term.line[*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 (!(term.line[yt][xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + + gp = &term.line[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 (!(term.line[*y-1][term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { + if (!(term.line[*y][term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } + break; + } +} + +char * +getsel(void) +{ + char *str, *ptr; + int y, bufsize, lastx, linelen; + const 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 = &term.line[y][sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &term.line[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(const char *line, char *cmd, const 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); + close(m); + setsid(); /* create a new process group */ + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + if (ioctl(s, TIOCSCTTY, NULL) < 0) + die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); + if (s > 2) + close(s); +#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; + + 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(void) +{ + /* 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 +tscrolldown(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + 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; + } + + selscroll(orig, n); +} + +void +tscrollup(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+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; + } + + selscroll(orig, -n); +} + +void +selscroll(int orig, int n) +{ + if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN)) + 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); + } else { + y++; + } + tmoveto(first_col ? 0 : term.c.x, y); +} + +void +csiparse(void) +{ + char *p = csiescseq.buf, *np; + long int v; + int sep = ';'; /* colon or semi-colon, but not both */ + + 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 (sep == ';' && *p == ':') + sep = ':'; /* allow override to colon once */ + if (*p != sep || 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, const Glyph *attr, int x, int y) +{ + static const 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); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n); +} + +int32_t +tdefcolor(const 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(const 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, const int *args, int narg) +{ + int alt; const int *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 */ + LIMIT(csiescseq.arg[0], 1, 65535); + 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 */ + if (csiescseq.priv) break; + DEFAULT(csiescseq.arg[0], 1); + tscrollup(term.top, csiescseq.arg[0]); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[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 */ + switch (csiescseq.arg[0]) { + case 5: /* Status Report "OK" `0n` */ + ttywrite("\033[0n", sizeof("\033[0n") - 1, 0); + break; + case 6: /* Report Cursor Position (CPR) ";R" */ + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + break; + default: + goto unknown; + } + 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 +osc_color_response(int num, int index, int is_osc4) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(is_osc4 ? num : index, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch %s color %d\n", + is_osc4 ? "osc4" : "osc", + is_osc4 ? num : index); + return; + } + + n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + is_osc4 ? "4;" : "", num, r, r, g, g, b, b); + if (n < 0 || n >= sizeof(buf)) { + fprintf(stderr, "error: %s while printing %s response\n", + n < 0 ? "snprintf failed" : "truncation occurred", + is_osc4 ? "osc4" : "osc"); + } else { + ttywrite(buf, n, 1); + } +} + +void +strhandle(void) +{ + char *p = NULL, *dec; + int j, narg, par; + const struct { int idx; char *str; } osc_table[] = { + { defaultfg, "foreground" }, + { defaultbg, "background" }, + { defaultcs, "cursor" } + }; + + 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: + if (narg > 1) { + xsettitle(strescseq.args[1]); + xseticontitle(strescseq.args[1]); + } + return; + case 1: + if (narg > 1) + xseticontitle(strescseq.args[1]); + return; + case 2: + if (narg > 1) + xsettitle(strescseq.args[1]); + return; + case 52: + if (narg > 2 && allowwindowops) { + dec = base64dec(strescseq.args[2]); + if (dec) { + xsetsel(dec); + xclipcopy(); + } else { + fprintf(stderr, "erresc: invalid base64\n"); + } + } + return; + case 10: + case 11: + case 12: + if (narg < 2) + break; + p = strescseq.args[1]; + if ((j = par - 10) < 0 || j >= LEN(osc_table)) + break; /* shouldn't be possible */ + + if (!strcmp(p, "?")) { + osc_color_response(par, osc_table[j].idx, 0); + } else if (xsetcolorname(osc_table[j].idx, p)) { + fprintf(stderr, "erresc: invalid %s color: %s\n", + osc_table[j].str, p); + } else { + tfulldirt(); + } + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + + if (p && !strcmp(p, "?")) { + osc_color_response(j, 0, 1); + } else if (xsetcolorname(j, p)) { + if (par == 104 && narg <= 1) { + xloadcols(); + 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 + */ + tfulldirt(); + } + 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]; + const 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); + } 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); + } 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(); + xsetmode(0, MODE_HIDE); + 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) { + /* in UTF-8 mode ignore handling C1 control characters */ + if (IS_SET(MODE_UTF8) && ISCONTROLC1(u)) + return; + 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)); + gp->mode &= ~ATTR_WIDE; + } + + if (term.c.x+width > term.col) { + if (IS_SET(MODE_WRAP)) + tnewline(1); + else + tmoveto(term.col - width, term.c.y); + 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) { + if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) { + gp[2].u = ' '; + gp[2].mode &= ~ATTR_WDUMMY; + } + 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; + 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)); + + /* 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(term.line[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); + 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.c.rej b/st.c.rej new file mode 100644 index 0000000..406fd0e --- /dev/null +++ b/st.c.rej @@ -0,0 +1,13 @@ +--- st.c ++++ st.c +@@ -191,8 +198,8 @@ static void tnewline(int); + static void tputtab(int); + static void tputc(Rune); + static void treset(void); +-static void tscrollup(int, int); +-static void tscrolldown(int, int); ++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); diff --git a/st.h b/st.h index fd3b0d8..818a6f8 100644 --- a/st.h +++ b/st.h @@ -81,6 +81,8 @@ 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 *); diff --git a/st.o b/st.o new file mode 100644 index 0000000000000000000000000000000000000000..946e60423dfd34bf88cc19e1bb778d010654d1de GIT binary patch literal 82264 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 literal 0 HcmV?d00001 diff --git a/x.o b/x.o new file mode 100644 index 0000000000000000000000000000000000000000..80e71b05a7484fd7c5897d3c48eeecc42120d93a GIT binary patch literal 75216 zcmeIb3s_ZE`uM*OP@)kB%Ni=1j){e)hKLGj#gT(<3YEMRrKQM4Nd$s}VmhUBpxi^0 z-p`s_8P(-JCAq-EKp7yIYn)n30uIsr=q|t@k}FUfJ>eet*yZ|2)6vnXTvS zy+7++?|Rp}u6ymxKG$bvPm1t(EZ#iU1y01oNk?JonoyTqh%tjO?8*0 zug8D%scRPZJ` z-*u#IUuXylAK9eDL!F($*j0PJxx2G7?TGi*M(@*&oBWsgFZIvz&zv>G4(*M0eyE$4 z6R2A{(;qr=%+eDBPP<)y5^AvXqke09nmc#-pTpC(vtSI}KOG}w*FUrtfn|#@6ZNc-?7R6WX4~(yjs@fZ<8y|4?1f0&6 zOFBC{Ya6RhfYZ>PBSZD;U89j3jtLCMvtqUsja+4`&ss?!cIwL+qsP|@Pv zT5TQu1uQO8;wWH`SA|#AM`vl1vaxMHWl4qEbxF3f->z5rQ`y%UQRam9#@o)fSx$3- zT^BVZ5tDNQ3DKh(w?!pjK8m&l5}>4TcWh^;U3(xnnryS{j?R4D%!KFy`5Pfb6aF|@gUK)UA*Rmx7s&}L43wN5=dA zb@x&0yGJ@Zi;6q@^($KSO?3YvXF@`evmzl1zarW;Q_(=1>TnJY{nqd77`oB#G?TLr zD9`zVLr8z!33k(GQMTtD=SOFwSx>MwZ0aV|_FlI(yU>R85^bj)$~}e3J(!R?>VPg) z@1w7Unj!+;M^%FUP-8@q_nQyC^LvNww4o~GpzDD&e&-#gB);?JZmNC5d-E`?E8cH* z0N(-oH(>vcs!(?R8`{)nJoSN&0jR_OB7Rd#-vAA^^E+?%9);C*Ih!U=W=+D4pR2j8g7XQyrV z)?5&YZFB`7r^EZqG$_gJgcFKZ{b)r6|6P>!y0>OFWQ&}kIt55oIqkrfh#;Ck9=yC+ z=hy!tC}f{_pIMP`g7eeCHx6t(##?g;>W=?K)hX1^-kyG7Ta>r91z4bd!iE0Omk~fb zcKyZBg>o7==jXE-$Vy5%Br84;7L=aU3XEI z^VSsSi>#(Ez+K9)i|SGmkMeEs1*%+=(pC(Mu6jPx`NG-KHXt*tDyFJIpY(CIWY(7? zcmioEDeo=gRd297(el=;!Na-?sDDtG?7HPis@i8cRQ;VT7?2up4%p5%-6EW?Y~9&r zJ6|fYL3=^BCSeWUmvQ>go@lTCI4$IC@RdXx>?Y9$2kkJPL-%?{dSR)gVlN_R_h$B z)+MT1LujbU1NBr_>(+$B(6`rBCMgf8o=y13b}|xMFM^sBYK+qZpq<}tH|>E+Vho!) zq_q*`w8`3X+u3^5=+pWk2yrif4JsGLjVRR-RhVut+qpGiH3*G@o@XC4_#bR%lik$jJGd3+Al^T2^gjJYXrqT* zA8@|$dmrtnzdizVMnTVaJVdoJo!9)Kt=JK^!sjbzziNoV;pqpqMg@DprP$*n^;vYRO*<20P_TA;ea}_v(wqw^i}lZFlfz8NOU&pgJDg( zqKiOucy@h3!aTbkI;|!MJHr|P)SiiDUx-Wv{s6b&%m8XS+qNvf({2P&YRdz!P-HhXJ+0AauP z8R!TPHlwOUb<$6t3{xjD`ee8|iAAB2n4|RGbL5h+owmta^Ls42XEG?+Bsio*li`q( zOo2m{;8?qU4s?*q+qPm%Px`!5bP9$pJv+on?&}AkO~Tj%z0N-9wxG+5RJjYQP21|N z{{}{xCW6a}1;gbC=!RKJ=>LkAXc zj+k+wdy2PV{s+zRW0QlsqP?{>U;!Mh^wu`Qk(#-gLUvt^YO3Krz+3wn$l0qlV=Fhh z0j}w-{h`M~JI6vB+SvdND3Fls>}+a@*2hUryP*xiv;iwXdG8gVwdIg1l9)jKylA5Z zaeJt3hw#=#v}NNDVCF#x(xnYy3O|JSC%`m;(j|-YG?>x72E#%Vd=!MiV!QuY z@I7>WEl`{=neA4btz)6z)-}V^NZMc)cvVjtd(m_N?9tu2zKn#clnDv86HJJ9-gI7t zZd?xsI2A}7)o43=u=j}bJKLSt)#!vnWFlnDY4SU*er(<_^T#U?40!!V9MK?VxJp5r z4sA}ty0l59iWhx!0CQ!!kUiYfRgJT$_UqtCyUtqqipO>$ZRh zA8Bh=mzG`E-+nc?X4fBcav#gm11-3^rv_mRt0Ay2e6bC4rk}H%@4LAt9^G?LC+}=g z4b-mv+FLUa%28jaI{UijJGS#x50`GvXSOK>lOh|Z&>dhMj^Eve`3#>5Yr`<;6QazE zVV<~P_$o>y>*F33o4-mRf>rwY=kX5OKs}-yQ94^Z8Ubneh#e#K! zl^2fi)`UQ-lE*1j?FZf(2LOndzE$KsS6w)GZ@2|ecKrxQ56W_%?ND=|B@#NtIGc^& zY)2J7b~2Rr6QNgtx>MVPDZmvbB%t~h?~U7ubX{`}Z`3nRvsZ%-jA*!G?O|g!6e#1u z=SH%3LjR7*KBaO&H8L!?VY_R@e2VzTo+D~^gcTVn4x@-w5Rna3SC-)(%UfH4VX%Ng z6;jnM)!B1lw3cEm0e8#Qoz|;Vz^7(~~$ShpF}c6X1Kouv~AH-H{mjk1ufg3{fA_2$DO@5uMOzxxp?h`07T_-29D zJ#f-vQps-KneG-)0Rh^%p}ncy+5(P2v?vBESP4I2lF+Dan1sO6C6rVBF@N|ri6f)FQ1aG>(EiZo)J<1IHUbIpaH*_ji`kUEKHCrtt2}^&qoa9w*w{BCA20Q|WUmLk_yE;OReVq(j z%|f7Hqv2WzR^M`FXqWm`@pXB$r)f=Sv#+ad2NLXX?^Kllg_AQIW;#?yt=4<6Rdt=- zZ?q*CA+=cAr*ajJ|%*C+X^TI{;X>2|0iD(HppW2(N;_aP3qeHc>JF+xvb zat?HMw(U?Q(KV|v)&`*EW;uttRRd_e-FiY}T9E~pXLV5tp^h_xF`KojLR6+&)dgA&*80| zj=`&jU>cpBFT#+ovbw>F#*~h_Qf(cq#FY$OQbBY4Me(fsw|h|1_8Yst+OyR<9;dQH ze|n)Gq^&D9w7Ji9&GQ8NsyXEQ5N78G;X=UsC``>^DJ4=by}^Y=yo$u~F*aO?#=(W? zMvM{;w)0{71Cg<>Y-%~i_H1Ffr$EksneK49z6Yv0nx1pD6|SV{3a2$2)&P%o2zWLL z2ks)MZ2r)Mgn{0gyWlW8Etuf**1Vx=Bv@+a&rw#)K`YuKZO_lGoe-u+)ML?LbMAE< zbTlqOgCVGAueL^lLgkSeFh_T*ZlUF=R+Sg#uZecr#$dD!4K)}6tq3MaX)uhneXp)W zQbB{Pz%BcB!^I9PjegS7=W|>-3e;uf)EyW4@z`Kt-SU}ENBz>y(2;)L8|Ok53H=Dm z#n;1uGXgSaIckWYPU_&QFD>X%RJ->pI|KFE(bb`jKHgi_fiRv;2z5kyZ}}6Qb++CF zN3@Vzb1JCSst#_ATM5fCHD5z0EQ!T+Es52<4@kE%T$J|K-UgA@!v{(gzn!K~qbJl6 z6YLvsUVd|T?a#rlO-miJ5hT2|9V(U&zm>KAP*Wr<;-y4^>7gy2Sum_%{AUy4OD)m7 zfJa^98eIEe7nl!A(O;_6RSC;Bz7OK)Hol2gk!#gJZBRv~dvRXwa<8 z`YgDD`sG}Kt#L14wQoHWBKKsBN>~?|PW^kRon+Xna7Y!s?u_v0;B3_$15Nd3>uc(T zvL@%ky+!Ew&VaayCTQ?c1egGB;>n^ixSQX8&L)WKURj>>JBLl>< zAsSuN_H^?VZh~c`+G)+fOKmELn*YLtpm+Dy+^CYQOoST_@py?Dr$;`ljw&}eL5PGi zEZnZ;Xm2fEQ-Q-^IXXPpshMBh3@}rxyBco0fWjQ^vV5TPSsd=Ewq&GZu5!`st;@jU z%B9_>QY3jXI72mAJM^O`7-Oe3RZU@n*Yx96Ve1Q1xRC`O>#`ze7N;NGbJ9nk28D;O zRJuw30`vt&g>OA<3Po>7fY77HxYnH@q%4MK2|RE^2ZXR$g)ZzH98MSQc&qyuC8LLa zXJhEI!@46nu-R7ylM;20QZ00J$#xs*i}rPP(h^*-S^4Y*oa>l1=O z(Anm+2Ar=Tj>=u_*HtHCUg#oCM{V0?*UjeJA28$pxFup2_6O*sgQ{)DH(FNk*aMSu zJVCF%rpv~nMW?iM!0Hu^1ZaC*+06P(Fst8EuhX<1?C*TDagb8dO;rY{I$akPSb8~F z8anK$ih>J?{@Ok@%G`;%zX^-s?dY~$XqK)!H04*FG%c8GS!2qiOAeyAR%4h`sm8?@ z5U>oRr>or-bTF^<3<*p!yAODf3X+M=K9v0nPtNUpYY5gJ)w)#s!`z zbz{j}`!iM^=+3dIlrwIDmH4*zU~N&|R8_@k*Wu*>uIa!n^>g&4Rq-1wQ19^iV887h zw$XN8q7Xnk5WTeDdzG4q?T58g|8v-);2KsDSism`IoUo(84eE*qrHo`t$F+Wy&INf3+*}mJLks_CFnxgBj8%E#G{=GlY$J1UJM!bM~oRwSEib ztd6=(l&iZ1LPitz8a|hv~G@sMBq@VGKhI+&;u^H2&FikcV3dNVT4T-E{zGcPdPSyUp<#5AxU(12_Rie8y;pvy1 zC|A7`ZvF(1g~N#8tB~0kD>%~objB%-Eq!VltNsNoAFhEv2oA;~0aKu8x(|(Q!&WXo z#q!?W*wzdc6z&iOPk}xMo~(g+cC-x_8a_3W>bM}WFkFSJE`{n_ZCpwC;r2>!n=Y)V zpwIhsAi+1``ruQ_yWSTXV^-1~bmi8&RQT#Z!l11ncN&y+G*;EN8&py_ZMEx``nt7I zzBvzrW@1+_O@n(hy1`LBIQZsRXN=0Nz9i8FP@j9q*?IfHWdj_3zN{q*PUC7nP+9a& zeIG{MY0b{t&O78RXD2;%qE|`0kE+p7Pf>cB|LcCK>vnq|JMu=%PM|J5RbQTS&!ep4 z0-;41Gq0w!>QD3-QTict%lY5A-0jvAJkdGAOu=YotM=kN%4lgd2@e2(4o)>(8tx4h zP*!o-_3ILLVcGNxJ*N&%44x-$*C`u$r5mo?-XY18t6r(QQ!Q82J)D3`>}{}qulgD8 zgpZy&K|_I52RQqn6b3<8p=N$r*n@nV_2!q*`r@2#?KgMfEJiJPC8?5z3Y3Zm*>yv3 z2d8*NY(0-(@DyMm?SOa6R+#8RESPaA|F>OaJ1uYvLO+s%{YSc<@WEuy##%cs3m$*N z|6heKA)$&mZ-Gixc6F(tPI&C)Avni5*F3xaR^=a9k-^OlHAcggRaJX-ehKtXv{s+p zcg5y;c0OF6>hf3pd*5tehTHKnaxM(6(DACy8W$a3fgX7~^zg%*xU1j3-+TFXwT`{t zJEsXwlq`xgtK8ryLCclr?#%X1+|oJ_bcU;)T&Pt~rgV)yVS}9??ZIGQRkU_g+wn?6 z+qeGbpg^HpRgF$D=sqs6>+jS~=zg~j7e%RALH*cl=hrByth%VJut*nt0s=dm+a7i{ z9hGd~%-YZ4N*a=a{%xRD1(RlcR0j%D9p&PY&nR{@sr=X-gBEOPu0a}I3MmY4{QyBA>m2` za!dNHQq2w5aOi1XIO;N0+Xo(m**jhpz)=#J-}YCy0-|R+kJBAj+GP`hFu~j#UAJ@s zbmk#kc@DwMX%qI>x|gkOyzbmk3q3ky-)26Lgb&Ac)fE=M{&k*1_d#{E_VF{`G^OKj zjN{#Kj6V3zdqpH`+xSIir_o7%4I;`pE!D6H4(l24V5!mLL9a0i%9+{~F6e`smY#&! zkM0`W;vrx0oA!IJfRy^fEDUZ5#zWg_+o|2&gG#ch4^$DVLPlJC#7Lxo3(E0b_kVQ< zfA~%EC^}i?sb$t%gu*w-qG_?#TeC{FKh<^fwUl}=W+yzKQVCbD?{P1yhX!^S#DdOg zQ=A!x9vVyAQ~?tnRZ6g!aw`b_3#Dmxt;RpTv!-vBwoRZ%lp~S5QjI-lc zdsKQ*u|s=(-D(e94RA`Zl=lfmvg>nnN_N`&l_%p(4V>)jn;KPkvF*c!wa;2khTJ(D zwSqT-@GK}i3I)ZapWOL{($e{XNp=)WvZLaaac#FLmm0%+Xs_*e4*Q+&alInmc489X zsWBUd-j(40?7HU?R>QtqZc<^M-Sm|Y<>7IeHhd^8;Jg-qZXhPW)3uGx&QQA#o~g~; z>3!w{Fxh9Xhqlm(tHkyoSOxsX?)wRh&XDPtZ0AVJGavJF84s+4r)OYV=Y3{QLR`_R z-SrU<_~4E$7!i6I=60%`)Fr~zD?IY1@&aAvdCc`gUb?=f^6>Dpecne~{PnXU^o59e zmd0$Wt#@HA$z45XghLZs^T0Q?jmr{Siy;y&q}I;E*oNO=24o)}c@{t@W_V2K3m-nN z(s$p{g^SZ3d(bvjyr054L(7LS0d2*H2KX5+@1vh$M)0vj)j4$KVm(ghyX9gh0G)|| z^C{&V9_@BE!qbj#hc-g)=fGu{y4(*y!^IoaM~xTogpK#nSMY&w=PU1{s^fr|fqJ{s zuNu`aM*{1>OWkhz#RSNac~Hi8n`fN>Dh7XlW>SnqxJ(e zKKljis<5NX9<l5=Knu+r zx(C7s*lLP^`mzQc&g#3x`^*-V#Iu}4)}U7bgJ>Mon?%@0!QNK{&r9|TK-Egn2Kqyr zJ%0F00-OK@(qP5ly7J7l?|-w%`^=&!ShRpY75KC+5+V+$%ZP?uRNe9zh#Uu91U}@6 z83muT(16eabMxzl7LA7_ytVLGIi1jB_&_OK6?MRr2hI}V3~nuc1!rwFRljXb1|f)a zJ+=_@K4h26HgJCM;haJ5fY*9=+2S`q{UbPOy4t#$I++|@`r(CeG+kVL_Ao7?D|8sJ zd9`;a_pN$Bsv7_kg)v%L*K8JYgMa0sM>Hka2DdwPD~1*di$Z(hrv9TULTFn=kzT0? zZS??D%HYxNBE4v#Rx5cbz|SmgQ~kGcze-B|RTV6GjO9c^+u`C>r}xYEVNFZz(5@HT zwrew;{e3qcd}q*BxKow6Ut2P&{Q+NQ3ZrJFq&jDB2&|>w()X z(dt>iJs?I`_$yOKcx&H*bMMnRo|-qjH8=>b{AomRApV7d_vwkAZ0BoecmD#bELbn| zXFDxzD^^xVSXCcqg|;ZYt;UoTlOw38sD!92%(4>&&iS4C}k6g8`tT1#^;J zzcAXaOINw6O@r3Zc5=%z+v%dFcE@!CI2ty-KH0P$6`r(z*azyl{)0v6x%71fYUj&u zJ$BUU>8dx-p^H&h>DmL{K)Wt{nRLM2mTk{7@))<#y6-NgejB2RT z1l?y%c~=}*(Br?x1DkAZ6kb!c?1pO8Lxa=WwNdYf2lXg@SBGXJ^_fvxOb>Sln&RN$ z*?ir2oA&e}bM44yP)*&Ecx`HZaNLoh?`SU^X!2ncHD!Q{6^L@Y@@3?IsVLJ2>ht=U z{f`XYsMDsZNq?k3*Hv&v?$A5q3PvA#*AC_>9!qL#aHaEC`od7rz z=<>aXFx@d+vhdVIXoNGb_P~P;G>@YhkIoErfzbONU39qKKJQsv=}_(7cHaHYeh9jh zBpV)uhQIKDZ1xz9@{+HsMANSS-qI*lAhi?zgp1c6I$>CuP{Qyhz^a^#$BMW|?IBwJ z&vgwXKQa=pSv=?pz4|R7RT>il=;iGMa*>fEKjq=gK za3y>a{&4{;KH%uym8^{Jsy-a#v7)0!?9;j@s9rM2)e{S+CZJt|4)PUEk61~ z?W3LGo;KY7Qst#@dJhPX^*(U_v+7xzqqXta5~15t%XBQeqs7#EM7JW;V{z94DL>|j zgNLp>tQy?hb?9n6+<}JyYr`2>RlEVt3a+0v0Ls=im>-|;1JVzL;1TrYs{N^C=~;*H zbITA;S&L|a8?U(Ha@5ch9z$s8X}(>9+8^ku*5^9@+g5!AqAmYo9skX&JcW^N_#Z}EiIFb( zA4a+uBMtl?MmiZIec1?KSWrBXfyGg|9^d*0_=~ER=Rva7fg_k5w0;D89wzyb%@}F} zt$jc-_j0vw1Y7L7h_>Clh>B^o&Or%URaM}60Cao2{+Q$6{>5inThIO(FJ;WlH|Wg4 z2}900J8|f+bB2!?d2Z6E^5*9k6c#O5SbX)OlEtNE<=0eH2CJ4VtzLHR@)f_i&WeY` zaxg)tH@NJA`)IvG`v1(9k)4$@ahiX6z{+2kS5a9Qv?}xSO7iBH6k3%fd8I+CsH`;T zQ-{8SvcgJVX<5*>I4_vLu%ETGuz0~jlF-LTiR9;%o*nd+l^2%!l%gS({jBoh>cW!B z;%gxV{3QC@LLVL?CZwAsl?N!O{}DBMjLQZQcqACeDe zQ-}56#Xmt12l_*{9SBefec2 zWfi_5kXm^~VPQdW&>sv|6wj{;7FJr7L2F2*FKJ}j$YgkO)bK+=abPvhl9Ls%X3dA!X7_CIEF~m@UsMtGhh#&*<;2hqoc>*S0;S4&M}kV@M8FY zdOS?Q;IUeCEFLH8~BT z*7eqD@E<&{yfaqqrlDgXhk7?g@5bugXuV5;-Ly-znBI-iyRmvVTJKU|H*LBW)4MTx zH&*XP>s<=$rePC;MD=cr-i_6}(Q2nG97S~m_I$4D0Gwk{&K@;Rf9nz+HIAgljn`7+ zNGerHrIJ)CNu`ohDoLd&sWg&GBdIi!O4Cxw)JJHkn%}5GBH<6MtW-Q7Fl2XTwgY(o$5CP=JWI_y#pOHCfQVN8p z1~N0#tij2dqf=6nppzPrJZ7xw#vor8j?K5KD)SZ;Vu$COJtD7o$(69n_vM%5RaW9* zk?N)JXn}7*VcFusV8t>#EA^G;EiS~PGG7t&zgPD2g}2!wX8MA~L4Xh(>^fZPTdKPC z;sOYmJpzLB%N8%rD=qNN&Z}5Z=^Ht6xa~MMt;mFg=k@r@|VUs&cFQ4D#4*}{nY%3xXfeBbEQl%ymbWIz<)gO9}oP;1OFEu_#>^C;nG#Njks$#Mc{CKN8HdL;vW@G@&8P6GMw#k3%4kq zYd9&my%+s%;jG^^ob>;x7yWMGtlu@9^xxKtez$Pe?;1|}?;!njY80S8rgRm+<=k zWjNdSNH6jK*-QANy@WUP68=~(;g9zc{zNa~Pxcc2R4?IA_Y(e0FX8KZ34gYi@aYuJ z^+nf;u5S*X*)a9RJk|91^1 zhs^B7KOFAn9}ai(4~M(?hr`|c!{KiJ;cz$qaJZX)T*Jvfm-ONv4tMhphr9WQ!`=MD z;couna5w*OxSM}C+|56(;pCqgz4(X2-TcGhZvNqLH~(0cRNhRgWUJ{ezzbNo&cl=?Zo zTeytR=j>n^UxxEJ>zDS)_%fX1N3$R&EcP7kR=znDpY?^K6L$@#2plf$A3mdEu8J;a6V`I*)4A2EYIOb*-wgQx!A|)a5(E@&U+4b(|>6%`Z?T9KZm>Ne}Nsr z28lg~yXog}Sw5_W!_ygvJ%_v1KMwbaczpU|FZOe|oBbT_We;coVGxKG66 z)5c!x=WsXsIo!>D4o~L<#h%06?B{Twh{vZ*z1Yv;ZuWDyoBbS~&IyV=hr8Lou^0O| z+|51?ce9Vf-R$FVH~TjAVjqXQ*~j5-_HnqIeH`v)-{xNI<8U|oINZ%X4tKMU!`(u^l-b&a|!2m>lV)X-NO00pXd2q=B~UDk2##@ z=|=dAW;kCbb%pCAj-Md|=$ykZ?IrwjmvGL1zKefY|Mf26=pSC|Hj`kzxaTr3!}%EF zo8gfhKzoeOHi#f~it){G8Q&Lrt9h)qdSbsyVgpLOpY$YA@ zc!O!+J5F#rMei)s{{S7ohWm-JakoTFjE%oJa$>BnK5AlY;!Vf+W0P(;)*qW1>Jx}f zk9aCJ6@WiB5d=UGB20{p#=N$Zo<+D1#KuJw#YPW@!)S8QLgbTT<8FxX$Hs>u1F^n{ z>HT8k{jqWW*l7Q8lVhVdZACq)B!4!_gP!@Z(fV&Ai2n)q7<+y{jHkj%C``2-OnagZ z%LdWa6t)oytGE2E^+c|V>eo{iAtm@bCHU+leeYsL_+@>QW1BsZdm`QSVVNgVqgw*U zsP889&kd3OSYIeA5StTG&@a{p1pEY!1}o4Zis?n4;=$WMdo|@#U=s*!$3qChH<|q zou^{s?Ky6GjUv5YVgZ+`KCvFtIBXpL=9w6~)>9l?uIg?g)=SvbpUW$|uTfS6fT=gw z!~S8k!7R_5$a$XFTz~AmK^&O)PnBYqQ`3z=USn*-4>pEpq&zlLM&n>kQp zKtFsQps+8{?W*tl|)i-_5n z5Y`R-5-RG0dafHu5rs!pD`kH+4Gh?yV7bqua>6>lkit~IWBSM!k!6^#>in%FJDT*A zf^pcM9wt7SI8^y)Y@3^jUxS$ho4dEo^oYfxZDRk>L2*v~@6?+HPvle398!<&QdNJ0 zWoP4`cEUCSzf}ELq{*?N2LA*Hl+(~g~!7*+=_%sl^7lF!CAT|jN)Z>%t z``;p&e_-G6%RUT1%ZRM|g}&bhD&Pm?)TUm8L22eh6o=D98M_x5%ufl)s0Zya4t!P; zQ|-DpTgxL?yH&$P(De%Gx&@B0{i%W9H#s(Oa%@s{Y@F_}APCE=gJiEn*&G;)FOH4I zvX7&Q(Z$F?$1n>74janm5>s!HfPMz+WJhD|Wa`nJc}b)d&OQaW(}P5HBIF=B>a=LBGf9 zYxzij966|rw!ks+D&n~+ z)UsYD{wi^s_=m(hsnKJ-!L1EGsOKc?7+_QLQ`r9iACJ+F=D|5`_$$;XUvh#zS94N0 z#5cNOeycSO`{8ur<${kOJvW@B1JwK!4#yB5j|~boHSdJ|1k#hLsQw#nJj^7{^@)nz z!rw+hJKu=c5eg_?F7dI{P}DpW!sZj-c$yYe^F}yaOuUo0nll1NdojNWS{#n_b{%n_ z;P|6+D6i&P5XSO#r2io`iL@-*S$`nDgMb=uARfNQ5A*d;NkF zr@;sN8QxZc9B!Y$zN=TYEPRhSZo5g(va|HLnyrX%8=|cWQ#i{W^7^mA#>2{u@BdEC@9G(X9=!c=?cr~{JKA7~c z)Z$@#hLijjl2`LRkQqxno*aOF#BDt3Nz|v|dKDm^M4aOFZduXIlAIy+Qi#jm_7D2i=3a1zh4;K;V{BrO$ ziXT0GEm7RGTE|$fcpOI$?^lu@o(pn7Eph4&8C$<2{aimd;1<%){$+kE>7j0hv2{Cf z?youEZsLO&(0(2948b2%yuX^Kn?ZW+)yJwdAsjzVdVV9g`ZWlc56l&OBk32|+D=@0 z`Zd!3TM^+c#iP~l(V0Q-ll<>Z2*wJ?9bknGlZCi6;s^iS&Dg{%n#@7xL3d{#Qaim*j0Be-&|_4{&e+aqh2} zUrqXl3H^F~E80>&9l^q|UqbSeh5QQQMS}lUaab=@^`cABYLe%942RrI{BEJ=R^s;y zeh2YK1z$_NQSkeSzbg2{#Bsbex5tUM39f#Q5$ZFJtLCPDx(GOqtLCWLtRa=dV{2G!k5%PDq$lve6H@NVZT=)kr{2LdJm#jVc z=R_BNwhJHY!n0iX6)t>{3t!^GuXo{VT=?xS{BakKFGuUiKkvBkk6d`G3;)4||Lnp~ zh4spw>^awkk9XnIT==Cfyx4`8yYQthJmkW0y|<@)-Rr`iapAZI+>@SnUHB(1e6I_K zuDM73{apAtE_}QT&j#Kf=JR}=#`C*bF7k6-c!3LF;lgin;rQ;@p8Sbx(mnAK!hdk#9&lh!b{^-#PjKP5_T7`7WEVc(gZ z7rxGgKkmYxbK$SK@b_H!9vA+#3;)4|d*D*Jr}FCO!cTDFJ6-zW(_G}wbm8X$KLM`n z7tlJxi8L=rBYv&me&Y4S`Fi(a7yWpjp{M-LBR!9d)CuxEq^pTXoU8f0WKT8mzY|yY zjv%bYMSneToEI&m_%O{-+n-6#R9fhImiXUY^fbBfW*7b`>F+}e7OcO`Mg9QEHwl~mU{>J7kq%I{4s{7)|Y zK^Ok03xC>$Z*t*pxbRP1c#8}F(S_p@TTgz7ap9-B@I)7$?85QJdQa(2bK!X|yv&6! zcj3Qt;di+3zq{}T7yh&hf8K>}ap7;d@Q+>iUKifs!aXrP`~Nr>{wv_vj)I~c4N{zY z$FBC3r1)`GBCX5AaHh5ki6;p@iFm5uIi#QC^Zqj8T!)xj)g$w*(yGPq-q=MJyok^$ zTmr8_gzpM?(QI`wy!%(ZmDZ|WYAq?QES_InQXE`nRh2HzE4S3^Z>_~;Rh5N#`RX;b ziz^qFRRr^^f|XV^yme2%MbWA*Da$L+FITM2FDcBcunO|6>cx4B3Kx_tD_@8&e61`m z%&)YHDj>KNclgPNB&rMG-FQN(i&=%$8 zAV{GZn3$DUnO|Hyx4Z&g#i!oz8xE-iiwl(uy^^;muc{=d48<f^K|;uT_mN@vF4T z%7b(9eUIuuy&KZXUtCy;Z;r$l=fWF;;a!68O5Cz?I#jNzC@cpbS!LBDM_Fad;5kir zPj0ZRw9Jf=Z*^qTG!BDO^0iu)rFboso=Rqtoy^3BN|EbNoud zuQB*F7Qe>fS1NvuQi3YVC?!5hNsUrsqmvPv*n#T~8Uj@AZhvsK*DD(+|%ceIK-TE!i$;-)D16eXXcKs zlA>azDESl>H$}yQR~kd;7$q}C$&68Pm8q z;$u>v4HaVB2v!tSm0P)4@S4nuQhyLi4qw7KgI{?$ZE0y?g_S$`CN6`|<75Q?WsGBkEqL2v$n4bC>_}NtKkKv=q&I`VMRr0*<3i*FZzbk0#XJAqMd^IgSL&n$tzt@ z0x#62fXUc(DuvK>G9+oMq7eH{Rj8Luu~lbli~M| zV>&K=&`t=K2~-`dTnnHYf%({jL)@A0{?bf*g=w&IRw7SCTVoU?i3iaFD zjaQ@R2A~ZF3rWK=YjNS?{Dl=b4rnJ~o79oQBG8IT)worJ4GFVXT9An^tK~!t$s$v5 zR%s=aQpO}TpkQ6#V`$~-O|u2og2Eu2fTFU}C6J?V2LXc%Ob+HkaIhHKHpoCrhDw~f zcz$+S*`liQ8St|AQfRQ<&Z?l$VL&b`P`v?G)Ql1wt0(1COX6k$6dlB8kF~HJNXc+k3-YOVppwUdlTBrRnskj6RM4v#M(!!-yIc63*nYnX| z3JObt+J!m#9l%;|UV#;a*BPrH0LYzGQU$NT2d4xJiYpYhVE$5Eo?liDqbgu@7f)_f z!&Kd%YFv;F)xfL4Z8Y_P2w)M<_$oxL;ia@87RnsC$kK z$fF;6Db<1|l|jp>D64|WNj1!Y@Rtd6%gWF%a#rS+mq9-REkI9EFxkae_;^71te#(3 zQUbF^m}R1KmgbcND`7$c{YBn<>=i*0`$5bwn3}B)r<4^G7Zn#)Kz?h->0cag+ zPafCDP=6NzC@U#}zKV|E7bZ%f5zMbF&&!8SO_kJA^@j4Y@=9xISw#V~9hg}|Syx(1 zg2e?;e&N@H!>?wbHmfSQxLjSo@a>#?Jwueq4K7LH6)- zd7SPm6d#{+!wr4*XZWz5TH-9f9fe@SztO^t`DT4;-C^jt4H)bBn~>*p@p(4fP|xM? zVfiNwjy4^F4{pyI`frDGmftSqW9a6AF7kaaL$I-b z*dBb|%;aoOJaNpcX-|TS{7^$4pBZF7Bnf#=_X0y6>psh86CVcbE`wig=()n+`Gy{R zMu+v>Ysh0BqR21$Ii)T?1c^T;xjYcG5D`g5H?PCAlY}i;M`8nCXRl-7e1_iypZR=(ZlBoaKqg? z_^^C7an$p7gRdmc_HaLTv*6rcH3*(U_WvL_KEH_D`EWl1H`dR7n@Aj^-3K4G6Yodk z#`0{>7Y3gV;Vi%3;FlS^FWe8sjrHi~$t-KO;5TY9Yk}Z55w8|}HSs#ZS^jpxIlp%i zm-+pNkY_z_2+ryLVCcCYY+*kfHuwVukAwS+xM8~gfDg-`N*w(&$KYog@(&t(q#=*@ z1X#~lL;fLy^WPO=x>$EuKF5%M#NZ_c|EIyLjC4^~4SaB0E;##VwF}4l!nkpLU_bv= za4xTVh+~?M!iVjDM96cx>jh{1c)u7oPM78RZ=IO4e5;UWd-f4Wd+vh|+jCIJvwpmf zj2r7`-j_J)!M4SEPBi#q22T-s*v>VE{NslFJwl$#t3hzq^OT_n`#iP>@6Y1K_Otv} zLmuZoEdQ#(pECHnhMubo{;45vjz=fMeK*|DKli|g^;ZyQd$?a(E%?J28McQ7e+VPO z_M+fRiN7g0k6Wko)A2Z69v^*z^EidS^@1DAbNv}k9P?$4Q+WRlHuUo!~r9 z;r+Fq^!$PNFbFiquQnmi`Pwfy=L_$1nff_jM~KUO9Rv4AOnJ^1-v7dF7<~Q?A1>cH zLZ0o(7o6?6T5z@p?_1#}?fH|C=Xv{Mf^+?OM)0*tEBwu@;13XQ7yM4*cs~d?E??H) zNAR^u%Cb%rob?Y8d>zUA1%H_MWWibg<$|;PHG=beW(jfhC#K2e^*bTYdL9v+^*nCq zdD_sk#YO%lL;e{<{wo*xZw&eMhWwSs!wGEcf38;rf^)rEBskZra^kXHtrqfJ&ufi06|7(NaW9Z4pi*49YhsjF}j?ad$ohym6|6}O- z!*HW+&D6Y{L*5y4r{l3p22s!$nQ7g@sgDN^OGUpWNOF9%-3dv`-o$@rk{rx z@{NXkCUKeW<%XVoL;fm5kD2a5gWqGw-)8VF27k=ZUtsVT3|?sP9fqE*25&axiwwTg zMZU%0+YI?{3_a-6AbfE9QE*-#IN?Mcj(#@f69nh=sgZ(X-NNlW!C8MmaO?|kyV&6K z;GEmVrNq$|(?3N9-)_j48+tI#otP2WY7B1HpIZz)rvAGPZtDMsp$GNzI>z$`p9h@n z-)`tJ_3t#essA%W59%)<`wtrOX8Rp70EA#e*&XoVbjK0L=nD+)H{{LhtXYPLI~r0{oUZj27k=prv1+v`ptIH zXvmv-UN^Yuw+{?Grr*9YLOq5BL7>1-veoLdA)7uL7RB|df$+L*^vKS$Y*;X5VpexzZ%Y2e*`_S zin?Dhcr5W@z|3*yOycP0MTY#@hMvs^A8p8E8vOTj=NTN!gzd>P^t@{D977)U@H}dc z;4>({HyYf`@685BJv?vxqrn#&>Hf{&r3Qb%;HZcF{5WxBuNm^s3i+92=MLi1&mS4` zWrm*54E>u8{=w!-xGpL&&FKWY`u6UZAmcm!YQ_80-0` zkbi*WpAmc=@kYTPCcZ;(9v}A#j_*ytO}#t__MBh-JI?rc6&&W@ll&mTJBSYx{1EZq z2+sAVMsW7CLmd75I(*nae>C`W27jD5)`vF?-sHkxGxV6{{vL7mLju{qNATgqk3R(x zgN@~%(5KePE5cf6kEq z^=YWBt6bRrA%e60!-%8*--8dQyUdWUF!*(bylLmHhJ2+Vf4htP1BN`N&2~O2INSN8 zp~v*|3kJW(NcS~E&*cVx+u)ck>wn+SbGsp*GZ0Q-w%} zZ^1cVUkdJnIJkW+IO~~A?>|EOtKh?S&Lb}E3>xwn=7SGz*9y-1?>F?A_2&tLH$i+( zcay_L9yU4$0$bV?a z?=|GjcJZCTO*=abJ*J(JK5ZD+tIhh(YKQj1@#HF6U5Jx@LhWuLMs0Z`J`X4m-JqF)Qoc+M_)z=L9j}7^^ggm#4 zPh5Dr;H>{A;%Mh5@L@aog@J4j&rkXf0wLI#bGkzez6`=y&oJVs->m278}ip0@)KR; zA2#?dL;e+me`@gE#Mw@s$0nW$La;ICes84UEWb){9$)GO=k|5G;D>Z_)?LKW51+w@ z{quw&Z?>Zi2FEz8=S4%0*^d4#bGerJIYY$$K?F~nuM z(+&AAAr7ZoWpLAPH9`;9bH|WxG4%Z2;8>Sg|5}5$8vJpge;tJ3_MG4kXl!i~{7&LK z1ZO?(3(k6eCXRml3O;OS6ix8hZ`>aH3C`*E7o67tekJ%?vh&x15Af)KA%e4?hY?3R z+u*}?j&hMt6?`x0pCUN>VTR!Bw<`qa{%|gFvZ0B8q4 z`w>I`H->&+B3jkepRoK8;#e;2@L_wB4gRgcFB5v^Kp1XU2_DecDm3_Yz*v8Y!7=R# zB!3NYWcv;Irw#dq27lk+GYlR(6i#46J>S8H(>=}L*rzhjG5GfepX6|42H$OP4^F6IL;slhjW_rK2xI@8 zO2$N`BdWU z{~*cdyU5=zU6jsvi?Pav;GRfx&AB@ zoa@g@!MXm_5|{PoCLz!1{>_lT2TbO2_atfiQBNe)S>_XnbH1L`V%9w3IGo%IAC_P2 zqNm!B$Goz9t0<~Ya2?E=BqYi#8T z-ax!S@P~w&!v~zv;K%3VF_#BRK21+0fI^$k!i*d9^+$Zu)Jb(Ep@%m-Ut4Jl{BBtQJ83o8!x~#M#e0Pi_=^9qE5d@R&#)@PXvypU(v6 z^^;b?*+2V;qn)va|9=qjJYIAP{sh_Ccbq$5vxuWT_^x?wNATJt zwaIb8FZ9eLKTH>#`=vR8&ms8*f^&a$jo??2{BHy=AYNne8^B_=|G$W1zWReK^ZN{W z4BJe49&_RA1z$n(n+4}~@v`9T=Vs!V=G%sy9}0Q4r$unC|3AC%<5EE>Y^dM7-tiIV z`oQyw;evC$x<>HtDZk4F$KPwgtyb{8#Q#7X?ZGy|{{Jg+tXFrzhxv1c9xuj$4{kAO zdXMt>+XgJZh&bn~K#N(o5XW@m4f*?p{2Y@1r{HuMZaru48$p)S-C^iI#nAt{!A~{# zyM`X@O6F0zU%K$G1!p@uT=-$)nBUWkbR*Bx388;5UDnfA@OfIyiY1PEuutXu4m3Fa z78Ucsh8~Q=^?bAoPc`)X+R&3ONrGno|e2(C^5nmuUKaW=-IG6h}!9OHDD+QlP<$Eh}tXF4( zEZ38JggpE6AA++#|7qwMZ0K1p zzu;USIt1r>7%`!HePcWO5J!IwG3<;L@-YyGTbkgk=K@2|S%#iWA-bn9f|XlJ5f=jlSegzQNWob~u!_#{LBP(%L=AITCOGSV+C~3v z7x@-Lez;-J_lA6(Vb9M7M|(J3>jF4|4cYDRVgK|Y&h-cTR@_cB_)T!m@~0Zy)PIJH z{rXSdss92O{h2~Oh4f!y@S6?&xdu1&U+JR1K*&!Z{pEslzLpBk z_4zu%xm-fT(UuXQoBeQ$A%BZu|DOzQ+W!|rk6FHd7xDqJe~aL3=PQDQlep~+#ob&aR;Bz1jZd(ohM>uCYUnY*aO?%!oO&@i^ih*kfJi-(&X)&cENz?S?s;>|HSFO5wAI}!QzIy{_x@7@5O6X+?fCA7=4a&Kirt#MLbpT`-rCt{!ikz z;7=3J5&Q+>xq|cW56=^PE6Fbu{59g`g5&Rr;#Ms<+rLuqk4b*D;B5aI!P)-X1m8n? ze0_EL7>xekM?6vR1H|LBc-a3@*a%=t(z`J4OFT#LIO64k4hK>C{n0S)l!-(ey zK8kp`;Hkvd2!0{)2Eiv0Zx;L_;_ZT8LOkw-?)K*rPZE4C@f^X6h?fgqLVS(jLE;U9 zuOQwmcn$G(!EYiScVc(@e@{F~@H>d-2!0Q7eqSh;Hi}HweC-c(dS*#M=em zPCRZvcl&w%oh0~9lFt$RW8&q4?;*ZM@O{J^1V2E$S@0vo+XauJ`E=Y#-R+Mdo+S7H z;yHp3Bwj9f0`WD14}2y>SsL<2>FL-oxH)| zIMnmJal65JO&il_F*xdHJ?#cZJv?uWMj_ZRIE`fR+=pMVVK9lP^Az>n#p&|LvIQSW z=kk8mCB(~x{CqmE5j;faw+UWH=MM|Mj?TXqoXeM=gW!C9M0w`tdYJR|<4g)?UV;a( zarrWjpz-u3AruM_-L;(UF^`cI0}_IC*R z5}Jpl(7c=FZ=mrtSMW|6w}OI?r}5wc!TCPUX2JQs&v5b|r_1+`Y{5I}zS0`Oxt>2P zINxvjUhseVv>$l>!s%uW(wxT==6v6+g!)V7eb3bLw+YVex#> z3k0txzE9j)cF1@A*0Te;wk#A^h9i8!w-vi?WM zYW;_V`~~AQA3*J%<^MuFN$@?yZNUemYCUrVA4WVV_+;YN$jW7l;n#a%4?l3M_6zx_ zqOc@yWDtHl+8SAq7tFIp&abSrMk-oSritET+)*P(L72k&G?heA5F(ymr|C<+l&wV;4@%A*?N(j!u{uM(TjN`!K|A3%j87FwqCQ?$i7BnR<`iC&s1f z6MjzTEaKFCYGL@EN9po&7aZW@1KMMAMx8SLE{Y#70(=yYcUY~_t_TLS=lBl@PTg&o zj)J3}^56Wk7UBD7n1Ad7W&SS!hUq86$J|zD=(9LcFzkQcPXV!>;@_5~{u^Li3L zD*Ye?^q77GYPGBmTF1cjaZV@GzZqCh={J@^0BkYjYb^1J?#q^Jd zk4*nIU??fef7dktVPkoHrqjY>OcS^J;e+vMnuADfE&t8