st

st fork
git clone git://git.rr3.xyz/st
Log | Files | Refs | README | LICENSE

commit 7674c7b79530f6ee8b84dc865fe9f399e29b089c
parent 9963ae674f248068768124f0e999da4eec2d80e0
Author: robert <robertrussell.72001@gmail.com>
Date:   Sun,  6 Jun 2021 22:57:31 -0700

Reorganize (untested)

Diffstat:
Aconfig.def.c | 368+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mconfig.def.h | 492++++++++++++++-----------------------------------------------------------------
Mst.c | 281++++++++++---------------------------------------------------------------------
Mst.h | 79+++++++++++++------------------------------------------------------------------
Autil.c | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autil.h | 34++++++++++++++++++++++++++++++++++
Mwin.h | 10+++++++++-
Mx.c | 118+++++++++++++++++++++++++++----------------------------------------------------
8 files changed, 817 insertions(+), 796 deletions(-)

diff --git a/config.def.c b/config.def.c @@ -0,0 +1,368 @@ +/* See LICENSE file for license details. */ +#include <stdint.h> +#include <stdlib.h> +#include <wchar.h> +#include <X11/Xlib.h> +#include <X11/cursorfont.h> +#include <X11/keysym.h> + +#include "util.h" +#include "config.h" +#include "st.h" +#include "win.h" + +static void clipcopy(uint, Arg); +static void clippaste(uint, Arg); +static void selpaste(uint, Arg); +static void zoomrel(uint, Arg); +static void zoomrst(uint, Arg); +static void numlock(uint, Arg); +static void sendstr(uint, Arg); +static void sendcsi(uint, Arg); +static void printscreen(uint, Arg); +static void selprint(uint, Arg); +static void sendbreak(uint, Arg); +static void togprinter(uint, Arg); + +/* See: http://freedesktop.org/software/fontconfig/fontconfig-user.html */ +char *font = "Fira Mono:pixelsize=18:antialias=true:autohint=true"; +int borderpx = 2; +/* Kerning / character bounding-box multipliers */ +float cwscale = 1.0; +float chscale = 1.0; + +/* 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 */ +char *shell = "/bin/sh"; +char *utmp = 0; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = 0; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +char *vtiden = "\033[?6c"; + +wchar_t *worddelimiters = L" "; /* E.g., L" `'\"()[]{}" */ + +/* selection timeouts (in milliseconds) */ +uint doubleclicktimeout = 300; +uint tripleclicktimeout = 600; + +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. */ +double minlatency = 8; +double maxlatency = 33; + +/* blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. */ +uint blinktimeout = 800; + +/* thickness of underline and bar cursors */ +uint cursorthickness = 2; + +/* bell volume. It must be a value between -100 and 100. + * Use 0 to disabling it. */ +int bellvolume = 0; + +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. + * 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 `stty tabs`. */ +uint tabspaces = 4; + +/* Terminal colors (16 first used in escape sequence) */ +const char *colorname[] = { + /* 8 normal colors */ + "black", + "red3", + "green3", + "yellow3", + "blue2", + "magenta3", + "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", + "red", + "green", + "yellow", + "#5c5cff", + "magenta", + "cyan", + "white", + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#cccccc", + "#555555", +}; + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +uint defaultfg = 7; +uint defaultbg = 0; +uint defaultcs = 256; +uint defaultrcs = 257; + +/* Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") */ +uint cursorshape = 2; + +uint cols = 80; +uint rows = 24; + +/* Default colour and shape of the mouse cursor */ +uint mouseshape = XC_xterm; +uint mousefg = 7; +uint mousebg = 0; + +/* Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. */ +uint 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. */ +uint forcemousemod = ShiftMask; // TODO + +#define R RELS +#define S SHFT +#define C CTRL +#define A ALT +#define TERMMOD (CTRL|SHFT) +#define SENDSTR(s_) sendstr, .arg = ARG_STR((s_)) +#define SENDCSI(n,m,c) sendcsi, .arg = ARG_CSI((n),(m),(c)) +#define SENDTILDE(n) SENDCSI((n),0,'~') +#define SENDUNICODE(cp) SENDCSI((cp),S,'u') +#define ARG_DUMMY {.i = 0} + +/* Beware that overloading Button1 will disable the selection. */ +Btn btns[] = { + { Button2, R, 0, selpaste, ARG_DUMMY }, + { Button4, S, KEXCL(S)|R, SENDSTR("\033[5;2~") }, + { Button4, 0, R, SENDSTR("\031") }, + { Button5, S, KEXCL(S)|R, SENDSTR("\033[6;2~") }, + { Button5, 0, R, SENDSTR("\005") }, +}; + +/* TODO: add RELS to clr */ +Key keys[] = { + /* Shortcuts (must be first to get precedence) */ + { XK_Home, TERMMOD, KEXCL(TERMMOD), zoomrst, ARG_DUMMY }, + { XK_Prior, TERMMOD, KEXCL(TERMMOD), zoomrel, {.d = +1} }, + { XK_Next, TERMMOD, KEXCL(TERMMOD), zoomrel, {.d = -1} }, + { XK_Print, C, KEXCL(C), togprinter, ARG_DUMMY }, + { XK_Print, S, KEXCL(S), printscreen, ARG_DUMMY }, + { XK_Print, 0, 0, selprint, ARG_DUMMY }, + { XK_Insert, S, KEXCL(S), selpaste, ARG_DUMMY }, + { XK_Break, 0, 0, sendbreak, ARG_DUMMY }, + { XK_Num_Lock, TERMMOD, KEXCL(TERMMOD), numlock, ARG_DUMMY }, + { XK_C, TERMMOD, KEXCL(TERMMOD), clipcopy, ARG_DUMMY }, + { XK_V, TERMMOD, KEXCL(TERMMOD), clippaste, ARG_DUMMY }, + { XK_Y, TERMMOD, KEXCL(TERMMOD), selpaste, ARG_DUMMY }, + + /* Latin1 special cases (kpress handles most cases already) */ + { XK_space, C, KEXCL(C), SENDSTR("\0") }, + { XK_space, C|A, KEXCL(C|A), SENDSTR("\033\0") }, + { XK_at, C, 0, SENDUNICODE('@') }, /* '@'-64 is NUL */ + { XK_O, A, 0, SENDUNICODE('O') }, /* ESC O is SS3 */ + { XK_i, C, 0, SENDUNICODE('i') }, /* 'i'-64 is tab */ + { XK_m, C, 0, SENDUNICODE('m') }, /* 'm'-64 is CR */ + { XK_bracketleft, C, 0, SENDUNICODE('[') }, /* '['-64 is ESC */ + { XK_bracketleft, A, 0, SENDUNICODE('[') }, /* ESC [ is CSI */ + + /* Misc */ + { XK_BackSpace, 0, KMOD, SENDSTR("\177") }, + { XK_BackSpace, A, KEXCL(A), SENDSTR("\033\177") }, + { XK_BackSpace, 0, 0, SENDCSI(127,0,'u') }, + { XK_Tab, 0, KMOD, SENDSTR("\t") }, + { XK_Tab, A, KEXCL(A), SENDSTR("\033\t") }, + { XK_Tab, S, 0, SENDCSI(1,S,'Z') }, + { XK_Tab, 0, 0, SENDCSI('\t',0,'u') }, + { XK_Return, 0, KMOD, SENDSTR("\r") }, + { XK_Return, A, KEXCL(A), SENDSTR("\033\r") }, + { XK_Return, 0, 0, SENDCSI('\r',0,'u') }, + { XK_Escape, 0, KMOD, SENDSTR("\033") }, + { XK_Escape, A, KEXCL(A), SENDSTR("\033\033") }, + { XK_Escape, 0, 0, SENDCSI(27,0,'u') }, + { XK_Delete, 0, 0, SENDTILDE(3) }, + { XK_Home, CURS, KMOD, SENDSTR("\033OH") }, + { XK_Home, 0, 0, SENDCSI(1,0,'H') }, + { XK_Left, CURS, KMOD, SENDSTR("\033OD") }, + { XK_Left, 0, 0, SENDCSI(1,0,'D') }, + { XK_Up, CURS, KMOD, SENDSTR("\033OA") }, + { XK_Up, 0, 0, SENDCSI(1,0,'A') }, + { XK_Right, CURS, KMOD, SENDSTR("\033OC") }, + { XK_Right, 0, 0, SENDCSI(1,0,'C') }, + { XK_Down, CURS, KMOD, SENDSTR("\033OB") }, + { XK_Down, 0, 0, SENDCSI(1,0,'B') }, + { XK_Prior, 0, 0, SENDTILDE(5) }, + { XK_Next, 0, 0, SENDTILDE(6) }, + { XK_End, CURS, KMOD, SENDSTR("\033OF") }, + { XK_End, 0, 0, SENDCSI(1,0,'F') }, + { XK_Begin, 0, 0, SENDCSI(1,0,'E') }, + { XK_Select, 0, 0, SENDTILDE(4) }, + { XK_Insert, 0, 0, SENDTILDE(2) }, + { XK_Find, 0, 0, SENDTILDE(1) }, + + /* Keypad */ + { XK_KP_Enter, KPAD, NMLK|KMOD, SENDSTR("\033OM") }, + { XK_KP_Enter, 0, KMOD, SENDSTR("\r") }, + { XK_KP_Enter, A, KEXCL(A), SENDSTR("\033\r") }, + { XK_KP_Enter, 0, 0, SENDCSI('\r',0,'u') }, + { XK_KP_F1, 0, KMOD, SENDSTR("\033OP") }, + { XK_KP_F1, 0, 0, SENDCSI(1,0,'P') }, + { XK_KP_F2, 0, KMOD, SENDSTR("\033OQ") }, + { XK_KP_F2, 0, 0, SENDCSI(1,0,'Q') }, + { XK_KP_F3, 0, KMOD, SENDSTR("\033OR") }, + { XK_KP_F3, 0, 0, SENDCSI(1,0,'R') }, + { XK_KP_F4, 0, KMOD, SENDSTR("\033OS") }, + { XK_KP_F4, 0, 0, SENDCSI(1,0,'S') }, + { XK_KP_Home, CURS, KMOD, SENDSTR("\033OH") }, + { XK_KP_Home, 0, 0, SENDCSI(1,0,'H') }, + { XK_KP_Left, CURS, KMOD, SENDSTR("\033OD") }, + { XK_KP_Left, 0, 0, SENDCSI(1,0,'D') }, + { XK_KP_Up, CURS, KMOD, SENDSTR("\033OA") }, + { XK_KP_Up, 0, 0, SENDCSI(1,0,'A') }, + { XK_KP_Right, CURS, KMOD, SENDSTR("\033OC") }, + { XK_KP_Right, 0, 0, SENDCSI(1,0,'C') }, + { XK_KP_Down, CURS, KMOD, SENDSTR("\033OB") }, + { XK_KP_Down, 0, 0, SENDCSI(1,0,'B') }, + { XK_KP_Prior, 0, 0, SENDTILDE(5) }, + { XK_KP_Next, 0, 0, SENDTILDE(6) }, + { XK_KP_End, CURS, KMOD, SENDSTR("\033OF") }, + { XK_KP_End, 0, 0, SENDCSI(1,0,'F') }, + { XK_KP_Begin, 0, 0, SENDCSI(1,0,'E') }, + { XK_KP_Insert, 0, 0, SENDTILDE(2) }, + { XK_KP_Delete, 0, 0, SENDTILDE(3) }, + { XK_KP_Equal, KPAD, NMLK|KMOD, SENDSTR("\033OX") }, + { XK_KP_Multiply, KPAD, NMLK|KMOD, SENDSTR("\033Oj") }, + { XK_KP_Add, KPAD, NMLK|KMOD, SENDSTR("\033Ok") }, + { XK_KP_Separator, KPAD, NMLK|KMOD, SENDSTR("\033Ol") }, + { XK_KP_Subtract, KPAD, NMLK|KMOD, SENDSTR("\033Om") }, + { XK_KP_Decimal, KPAD, NMLK|KMOD, SENDSTR("\033On") }, + { XK_KP_Divide, KPAD, NMLK|KMOD, SENDSTR("\033Oo") }, + { XK_KP_0, KPAD, NMLK|KMOD, SENDSTR("\033Op") }, + { XK_KP_1, KPAD, NMLK|KMOD, SENDSTR("\033Oq") }, + { XK_KP_2, KPAD, NMLK|KMOD, SENDSTR("\033Or") }, + { XK_KP_3, KPAD, NMLK|KMOD, SENDSTR("\033Os") }, + { XK_KP_4, KPAD, NMLK|KMOD, SENDSTR("\033Ot") }, + { XK_KP_5, KPAD, NMLK|KMOD, SENDSTR("\033Ou") }, + { XK_KP_6, KPAD, NMLK|KMOD, SENDSTR("\033Ov") }, + { XK_KP_7, KPAD, NMLK|KMOD, SENDSTR("\033Ow") }, + { XK_KP_8, KPAD, NMLK|KMOD, SENDSTR("\033Ox") }, + { XK_KP_9, KPAD, NMLK|KMOD, SENDSTR("\033Oy") }, + + /* Function */ + { XK_F1, 0, 0, SENDTILDE(11) }, + { XK_F2, 0, 0, SENDTILDE(12) }, + { XK_F3, 0, 0, SENDTILDE(13) }, + { XK_F4, 0, 0, SENDTILDE(14) }, + { XK_F5, 0, 0, SENDTILDE(15) }, + { XK_F6, 0, 0, SENDTILDE(17) }, + { XK_F7, 0, 0, SENDTILDE(18) }, + { XK_F8, 0, 0, SENDTILDE(19) }, + { XK_F9, 0, 0, SENDTILDE(20) }, + { XK_F10, 0, 0, SENDTILDE(21) }, + { XK_F11, 0, 0, SENDTILDE(23) }, + { XK_F12, 0, 0, SENDTILDE(24) }, + { XK_F13, 0, 0, SENDTILDE(25) }, + { XK_F14, 0, 0, SENDTILDE(26) }, + { XK_F15, 0, 0, SENDTILDE(28) }, + { XK_F16, 0, 0, SENDTILDE(29) }, + { XK_F17, 0, 0, SENDTILDE(31) }, + { XK_F18, 0, 0, SENDTILDE(32) }, + { XK_F19, 0, 0, SENDTILDE(33) }, + { XK_F20, 0, 0, SENDTILDE(34) }, + /* libtermkey only recognizes up to F20. */ +}; + +/* + * 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. + */ +// TODO: fix +uint selmasks[] = { + [SEL_RECTANGULAR] = Mod1Mask, +}; + +void +clipcopy(uint state, Arg arg) +{ xclipcopy(); } + +void +clippaste(uint state, Arg arg) +{ xclippaste(); } + +void +selpaste(uint state, Arg arg) +{ xselpaste(); } + +void +zoomrel(uint state, Arg arg) +{ xzoomrel(arg.d); } + +void +zoomrst(uint state, Arg arg) +{ xzoomrst(); } + +void +numlock(uint state, Arg arg) +{ xtogmode(MODE_NUMLOCK); } + +void +sendstr(uint state, Arg arg) +{ ttywrite(arg.str.s, arg.str.l, 1); } + +void +sendcsi(uint state, Arg arg) +{ + char buf[64]; + size_t len; + + len = csienc(buf, sizeof buf, state, arg.csi.n, arg.csi.m, arg.csi.c); + ttywrite(buf, len, 1); +} + +void +printscreen(uint state, Arg arg) +{ tdump(); } + +void +selprint(uint state, Arg arg) +{ tdumpsel(); } + +void +sendbreak(uint state, Arg arg) +{ tsendbreak(); } + +void +togprinter(uint state, Arg arg) +{ ttogprinter(); } diff --git a/config.def.h b/config.def.h @@ -1,420 +1,100 @@ -/* See LICENSE file for copyright and license details. */ - -/* - * appearance - * - * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html - */ -static char *font = "Fira Mono:pixelsize=18: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 = 8; -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 = 4; - -/* Terminal colors (16 first used in escape sequence) */ -static const char *colorname[] = { - /* 8 normal colors */ - "black", - "red3", - "green3", - "yellow3", - "blue2", - "magenta3", - "cyan3", - "gray90", - - /* 8 bright colors */ - "gray50", - "red", - "green", - "yellow", - "#5c5cff", - "magenta", - "cyan", - "white", - - [255] = 0, - - /* more colors can be added after 255 to use with DefaultXX */ - "#cccccc", - "#555555", -}; - - -/* - * Default colors (colorname index) - * foreground, background, cursor, reverse cursor - */ -unsigned int defaultfg = 7; -unsigned int defaultbg = 0; -static 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, ttysend, {.s = "\031"} }, - { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, - { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, -}; - -#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} }, -}; - -/* - * 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; - -#define CURS (1<<0) -#define KPAD (1<<1) -#define NMLK (1<<2) -#define MODOFFS 3 -#define S (1<<3) -#define A (1<<4) -#define C (1<<5) -#define ALLM (S|A|C) - -#define STR(s_) kencstr, .arg.str.l = sizeof(s_)-1, .arg.str.s = (s_) -#define CSI(n_,m_,c_) kenccsi, .arg.csi.n = (n_), .arg.csi.m = (m_), .arg.csi.c = (c_) -#define TILDE(n) CSI((n),0,'~') -#define UNICODE(cp) CSI((cp),S,'u') +/* See LICENSE file for license details. */ +/* Requires: wchar.h, X11/X.h, util.h */ + +#define CURS (1<<0) /* Application cursor mode */ +#define KPAD (1<<1) /* Application keypad mode */ +#define NMLK (1<<2) /* Num lock */ +#define RELS (1<<3) /* Key/buttom release */ +/* To allow checking more boolean properties when matching key/button events, + * define them here and modify MODOFFS and TODO. */ +#define MODOFFS 4 +#define SHFT (ShiftMask<<MODOFFS) +#define CTRL (ControlMask<<MODOFFS) +#define ALT (Mod1Mask<<MODOFFS) +#define BTN1 (Button1Mask<<MODOFFS) +#define BTN2 (Button2Mask<<MODOFFS) +#define BTN3 (Button3Mask<<MODOFFS) +#define BTN4 (Button4Mask<<MODOFFS) +#define BTN5 (Button5Mask<<MODOFFS) +#define KMOD (SHFT|CTRL|ALT) +#define KEXCL(m) (KMOD&~(m)) typedef union { + int i; + uint u; + double d; + const void *v; struct { - uint l; - char *s; + uint l; /* Length of s, excluding null */ + const char *s; } str; struct { - uint n; - uchar m; + uint n; /* First parameter; ignored if 0 or 1 */ + uint m; /* State mask */ char c; } csi; -} KeyArg; +} Arg; +#define ARG_STR(s_) { .str.l = sizeof(s_)-1, .str.s = (s_) } +#define ARG_CSI(n_,m_,c_) { .csi.n = (n_), .csi.m = (m_), .csi.c = (c_) } + +typedef void (*Handler)(uint state, Arg arg); -typedef int (*KeyEncoder)(char *buf, size_t len, - KeySym sym, uint mod, KeyArg arg); +/* Tristate logic: each bit in set/clr is interpreted as follows: + * set clr + * 0 0 don't care + * 0 1 bit must be clear + * 1 0 bit must be set + * 1 1 unsatisfiable (i.e., useless configuration) */ + +typedef struct { + uint btn; + uint set, clr; + Handler fn; + Arg arg; +} Btn; typedef struct { - /* Tristate logic. Each bit in set/clr is interpreted as follows: - * set clr - * 0 0 don't care - * 0 1 bit must be clear - * 1 0 bit must be set - * 1 1 unsatisfiable (i.e., useless configuration) */ KeySym sym; uint set, clr; - KeyEncoder fn; - KeyArg arg; + Handler fn; + Arg arg; } Key; -int -kencstr(char *buf, size_t len, KeySym sym, uint state, KeyArg arg) -{ - size_t i; - - for (i = 0; i < len-1 && i < arg.str.l; i++) - buf[i] = arg.str.s[i]; - if (len > 0) - buf[i] = '\0'; - return i; -} - -int -kcsi(char *buf, size_t len, uint state, uint n, uchar m, char c) -{ - uint mod; - - mod = (state & ~m) >> MODOFFS; - if (mod > 0) - return snprintf(buf, len, "\033[%d;%d%c", n, mod+1, c); - else if (n > 1) - return snprintf(buf, len, "\033[%d%c", n, c); - else - return snprintf(buf, len, "\033[%c", c); -} - -int -kenccsi(char *buf, size_t len, KeySym sym, uint state, KeyArg arg) -{ - kcsi(buf, len, state, arg.csi.n, arg.csi.m, arg.csi.c); -} - -Key keys[] = { - /* SHORTCUTS (must be first) */ - /* XK_Print */ - /* XK_Break */ - /* XK_Num_Lock */ - - /* LATIN1 */ - { XK_space, C, A|S, STR("\0") }, - { XK_space, C|A, S, STR("\033\0") }, - { XK_at, C, 0, UNICODE('@') }, /* '@'-64 is NUL */ - { XK_O, A, 0, UNICODE('O') }, /* ESC O is SS3 */ - { XK_i, C, 0, UNICODE('i') }, /* 'i'-64 is tab */ - { XK_m, C, 0, UNICODE('m') }, /* 'm'-64 is CR */ - { XK_bracketleft, C, 0, UNICODE('[') }, /* '['-64 is ESC */ - { XK_bracketleft, A, 0, UNICODE('[') }, /* ESC [ is CSI */ - - /* MISC */ - { XK_BackSpace, 0, ALLM, STR("\177") }, - { XK_BackSpace, A, C|S, STR("\033\177") }, - { XK_BackSpace, 0, 0, CSI(127,0,'u') }, - { XK_Tab, 0, ALLM, STR("\t") }, - { XK_Tab, A, C|S, STR("\033\t") }, - { XK_Tab, S, 0, CSI(1,S,'Z') }, - { XK_Tab, 0, 0, CSI('\t',0,'u') }, - { XK_Return, 0, ALLM, STR("\r") }, - { XK_Return, A, C|S, STR("\033\r") }, - { XK_Return, 0, 0, CSI('\r',0,'u') }, - { XK_Escape, 0, ALLM, STR("\033") }, - { XK_Escape, A, C|S, STR("\033\033") }, - { XK_Escape, 0, 0, CSI(27,0,'u') }, - { XK_Delete, 0, 0, TILDE(3) }, - { XK_Home, CURS, ALLM, STR("\033OH") }, - { XK_Home, 0, 0, CSI(1,0,'H') }, - { XK_Left, CURS, ALLM, STR("\033OD") }, - { XK_Left, 0, 0, CSI(1,0,'D') }, - { XK_Up, CURS, ALLM, STR("\033OA") }, - { XK_Up, 0, 0, CSI(1,0,'A') }, - { XK_Right, CURS, ALLM, STR("\033OC") }, - { XK_Right, 0, 0, CSI(1,0,'C') }, - { XK_Down, CURS, ALLM, STR("\033OB") }, - { XK_Down, 0, 0, CSI(1,0,'B') }, - { XK_Prior, 0, 0, TILDE(5) }, - { XK_Next, 0, 0, TILDE(6) }, - { XK_End, CURS, ALLM, STR("\033OF") }, - { XK_End, 0, 0, CSI(1,0,'F') }, - { XK_Begin, 0, 0, CSI(1,0,'E') }, - { XK_Select, 0, 0, TILDE(4) }, - { XK_Insert, 0, 0, TILDE(2) }, - { XK_Find, 0, 0, TILDE(1) }, - - /* KEYPAD */ - { XK_KP_Enter, KPAD, NMLK|ALLM, STR("\033OM") }, - { XK_KP_Enter, 0, ALLM, STR("\r") }, - { XK_KP_Enter, A, C|S, STR("\033\r") }, - { XK_KP_Enter, 0, 0, CSI('\r',0,'u') }, - { XK_KP_F1, 0, ALLM, STR("\033OP") }, - { XK_KP_F1, 0, 0, CSI(1,0,'P') }, - { XK_KP_F2, 0, ALLM, STR("\033OQ") }, - { XK_KP_F2, 0, 0, CSI(1,0,'Q') }, - { XK_KP_F3, 0, ALLM, STR("\033OR") }, - { XK_KP_F3, 0, 0, CSI(1,0,'R') }, - { XK_KP_F4, 0, ALLM, STR("\033OS") }, - { XK_KP_F4, 0, 0, CSI(1,0,'S') }, - { XK_KP_Home, CURS, ALLM, STR("\033OH") }, - { XK_KP_Home, 0, 0, CSI(1,0,'H') }, - { XK_KP_Left, CURS, ALLM, STR("\033OD") }, - { XK_KP_Left, 0, 0, CSI(1,0,'D') }, - { XK_KP_Up, CURS, ALLM, STR("\033OA") }, - { XK_KP_Up, 0, 0, CSI(1,0,'A') }, - { XK_KP_Right, CURS, ALLM, STR("\033OC") }, - { XK_KP_Right, 0, 0, CSI(1,0,'C') }, - { XK_KP_Down, CURS, ALLM, STR("\033OB") }, - { XK_KP_Down, 0, 0, CSI(1,0,'B') }, - { XK_KP_Prior, 0, 0, TILDE(5) }, - { XK_KP_Next, 0, 0, TILDE(6) }, - { XK_KP_End, CURS, ALLM, STR("\033OF") }, - { XK_KP_End, 0, 0, CSI(1,0,'F') }, - { XK_KP_Begin, 0, 0, CSI(1,0,'E') }, - { XK_KP_Insert, 0, 0, TILDE(2) }, - { XK_KP_Delete, 0, 0, TILDE(3) }, - { XK_KP_Equal, KPAD, NMLK|ALLM, STR("\033OX") }, - { XK_KP_Multiply, KPAD, NMLK|ALLM, STR("\033Oj") }, - { XK_KP_Add, KPAD, NMLK|ALLM, STR("\033Ok") }, - { XK_KP_Separator, KPAD, NMLK|ALLM, STR("\033Ol") }, - { XK_KP_Subtract, KPAD, NMLK|ALLM, STR("\033Om") }, - { XK_KP_Decimal, KPAD, NMLK|ALLM, STR("\033On") }, - { XK_KP_Divide, KPAD, NMLK|ALLM, STR("\033Oo") }, - { XK_KP_0, KPAD, NMLK|ALLM, STR("\033Op") }, - { XK_KP_1, KPAD, NMLK|ALLM, STR("\033Oq") }, - { XK_KP_2, KPAD, NMLK|ALLM, STR("\033Or") }, - { XK_KP_3, KPAD, NMLK|ALLM, STR("\033Os") }, - { XK_KP_4, KPAD, NMLK|ALLM, STR("\033Ot") }, - { XK_KP_5, KPAD, NMLK|ALLM, STR("\033Ou") }, - { XK_KP_6, KPAD, NMLK|ALLM, STR("\033Ov") }, - { XK_KP_7, KPAD, NMLK|ALLM, STR("\033Ow") }, - { XK_KP_8, KPAD, NMLK|ALLM, STR("\033Ox") }, - { XK_KP_9, KPAD, NMLK|ALLM, STR("\033Oy") }, - - /* FUNCTION */ - { XK_F1, 0, 0, TILDE(11) }, - { XK_F2, 0, 0, TILDE(12) }, - { XK_F3, 0, 0, TILDE(13) }, - { XK_F4, 0, 0, TILDE(14) }, - { XK_F5, 0, 0, TILDE(15) }, - { XK_F6, 0, 0, TILDE(17) }, - { XK_F7, 0, 0, TILDE(18) }, - { XK_F8, 0, 0, TILDE(19) }, - { XK_F9, 0, 0, TILDE(20) }, - { XK_F10, 0, 0, TILDE(21) }, - { XK_F11, 0, 0, TILDE(23) }, - { XK_F12, 0, 0, TILDE(24) }, - { XK_F13, 0, 0, TILDE(25) }, - { XK_F14, 0, 0, TILDE(26) }, - { XK_F15, 0, 0, TILDE(28) }, - { XK_F16, 0, 0, TILDE(29) }, - { XK_F17, 0, 0, TILDE(31) }, - { XK_F18, 0, 0, TILDE(32) }, - { XK_F19, 0, 0, TILDE(33) }, - { XK_F20, 0, 0, TILDE(34) }, -}; - -/* - * 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{|}~"; +extern char *font; +extern int borderpx; +extern float cwscale; +extern float chscale; +extern char *shell; +extern char *utmp; +extern char *scroll; +extern char *stty_args; +extern char *vtiden; +extern wchar_t *worddelimiters; +extern uint doubleclicktimeout; +extern uint tripleclicktimeout; +extern int allowaltscreen; +extern int allowwindowops; +extern double minlatency; +extern double maxlatency; +extern uint blinktimeout; +extern uint cursorthickness; +extern int bellvolume; +extern char *termname; +extern uint tabspaces; +extern const char *colorname[]; +extern uint defaultfg; +extern uint defaultbg; +extern uint defaultcs; +extern uint defaultrcs; +extern uint cursorshape; +extern uint cols; +extern uint rows; +extern uint mouseshape; +extern uint mousefg; +extern uint mousebg; +extern uint defaultattr; +extern uint forcemousemod; +extern uint ignoremod; +extern Btn btns[]; +extern Key keys[]; +extern uint selmasks[]; diff --git a/st.c b/st.c @@ -5,6 +5,7 @@ #include <limits.h> #include <pwd.h> #include <stdarg.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -17,6 +18,8 @@ #include <unistd.h> #include <wchar.h> +#include "util.h" +#include "config.h" #include "st.h" #include "win.h" @@ -29,27 +32,26 @@ #endif /* Arbitrary sizes */ -#define UTF_SIZ 4 -#define ESC_BUF_SIZ (128*UTF_SIZ) -#define ESC_ARG_SIZ 16 -#define STR_BUF_SIZ ESC_BUF_SIZ -#define STR_ARG_SIZ ESC_ARG_SIZ +#define 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)) +#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, + 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 { @@ -110,23 +112,23 @@ typedef struct { /* 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 */ + 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 charset; /* current charset */ + int icharset; /* selected charset for sequence */ int *tabs; - Rune lastc; /* last printed char outside of sequence, 0 if control */ + Rune lastc; /* last printed char outside of sequence, 0 if control */ } Term; /* CSI Escape sequence structs */ @@ -167,9 +169,7 @@ 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); @@ -206,15 +206,6 @@ static void selnormalize(void); static void selscroll(int, int); static void selsnap(int *, int *, int); -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; @@ -224,183 +215,6 @@ 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; -} - -static const char base64_digits[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, - 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -char -base64dec_getc(const char **src) -{ - while (**src && !isprint(**src)) - (*src)++; - return **src ? *((*src)++) : '='; /* emulate padding if string ends */ -} - -char * -base64dec(const char *src) -{ - size_t in_len = strlen(src); - char *result, *dst; - - if (in_len % 4) - in_len += 4 - (in_len % 4); - result = dst = xmalloc(in_len / 4 * 3 + 1); - while (*src) { - int a = base64_digits[(unsigned char) base64dec_getc(&src)]; - int b = base64_digits[(unsigned char) base64dec_getc(&src)]; - int c = base64_digits[(unsigned char) base64dec_getc(&src)]; - int d = base64_digits[(unsigned char) base64dec_getc(&src)]; - - /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ - if (a == -1 || b == -1) - break; - - *dst++ = (a << 2) | ((b & 0x30) >> 4); - if (c == -1) - break; - *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); - if (d == -1) - break; - *dst++ = ((c & 0x03) << 6) | d; - } - *dst = '\0'; - return result; -} - void selinit(void) { @@ -652,17 +466,6 @@ selclear(void) } 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; @@ -1971,7 +1774,7 @@ strreset(void) } void -sendbreak(const Arg *arg) +tsendbreak(void) { if (tcsendbreak(cmdfd, 0)) perror("Error sending break"); @@ -1988,24 +1791,12 @@ tprinter(char *s, size_t len) } void -toggleprinter(const Arg *arg) +ttogprinter(void) { term.mode ^= MODE_PRINT; } void -printscreen(const Arg *arg) -{ - tdump(); -} - -void -printsel(const Arg *arg) -{ - tdumpsel(); -} - -void tdumpsel(void) { char *ptr; diff --git a/st.h b/st.h @@ -1,26 +1,10 @@ /* See LICENSE for license details. */ +/* Requires: stdint.h, size_t, util.h */ -#include <stdint.h> -#include <sys/types.h> - -#define UTF_INVALID 0xFFFD - -/* macros */ -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define MAX(a, b) ((a) < (b) ? (b) : (a)) -#define LEN(a) (sizeof(a) / sizeof(a)[0]) -#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) -#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) -#define DEFAULT(a, b) (a) = (a) ? (a) : (b) -#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) -#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ - (a).bg != (b).bg) -#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ - (t1.tv_nsec-t2.tv_nsec)/1E6) -#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) - -#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) -#define IS_TRUECOL(x) (1 << 24 & (x)) +#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ + (a).bg != (b).bg) +#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) +#define IS_TRUECOL(x) (1 << 24 & (x)) enum glyph_attribute { ATTR_NULL = 0, @@ -54,40 +38,23 @@ enum selection_snap { SNAP_LINE = 2 }; -typedef unsigned char uchar; -typedef unsigned int uint; -typedef unsigned long ulong; -typedef unsigned short ushort; - -typedef uint_least32_t Rune; - #define Glyph Glyph_ typedef struct { - Rune u; /* character code */ - ushort mode; /* attribute flags */ - uint32_t fg; /* foreground */ - uint32_t bg; /* background */ + Rune u; /* character code */ + ushort mode; /* attribute flags */ + uint32_t fg; /* foreground */ + uint32_t bg; /* background */ } Glyph; typedef Glyph *Line; -typedef union { - int i; - uint ui; - float f; - const void *v; - const char *s; -} Arg; - -void die(const char *, ...); void redraw(void); void draw(void); -void printscreen(const Arg *); -void printsel(const Arg *); -void sendbreak(const Arg *); -void toggleprinter(const Arg *); - +void ttogprinter(void); +void tdump(void); +void tdumpsel(void); +void tsendbreak(void); int tattrset(int); void tnew(int, int); void tresize(int, int); @@ -106,23 +73,3 @@ void selstart(int, int, int); void selextend(int, int, int, int); int selected(int, int); char *getsel(void); - -size_t utf8decode(const char *, Rune *, size_t); -size_t utf8encode(Rune, char *); - -void *xmalloc(size_t); -void *xrealloc(void *, size_t); -char *xstrdup(const char *); - -/* config.h globals */ -extern char *utmp; -extern char *scroll; -extern char *stty_args; -extern char *vtiden; -extern wchar_t *worddelimiters; -extern int allowaltscreen; -extern int allowwindowops; -extern char *termname; -extern unsigned int tabspaces; -extern unsigned int defaultfg; -extern unsigned int defaultbg; diff --git a/util.c b/util.c @@ -0,0 +1,231 @@ +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" +#include "config.h" + +static Rune utf8decodebyte(char, size_t *); +static char utf8encodebyte(Rune, size_t); +static size_t utf8validate(Rune *, size_t); +static char base64dec_getc(const char **); +static uint termkeymod(uint xmod); + +static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static const char base64_digits[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, + 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void * +xmalloc(size_t len) +{ + void *p; + + if ((p = malloc(len)) == NULL) + 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; +} + +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; +} + +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(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + + if (in_len % 4) + in_len += 4 - (in_len % 4); + result = dst = xmalloc(in_len / 4 * 3 + 1); + while (*src) { + int a = base64_digits[(uchar) base64dec_getc(&src)]; + int b = base64_digits[(uchar) base64dec_getc(&src)]; + int c = base64_digits[(uchar) base64dec_getc(&src)]; + int d = base64_digits[(uchar) 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; +} + +char +base64dec_getc(const char **src) +{ + while (**src && !isprint(**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +size_t +csienc(char *buf, size_t len, uint state, uint n, uint m, char c) +{ + uint mod; + + mod = termkeymod(state & ~m); + if (mod > 1) + return snprintf(buf, len, "\033[%d;%d%c", n, mod, c); + else if (n > 1) + return snprintf(buf, len, "\033[%d%c", n, c); + else + return snprintf(buf, len, "\033[%c", c); +} + +uint +termkeymod(uint xmod) +{ + return 1 + ((xmod&SHFT ? 1<<0 : 0) + | (xmod&ALT ? 1<<1 : 0) + | (xmod&CTRL ? 1<<2 : 0)); +} diff --git a/util.h b/util.h @@ -0,0 +1,34 @@ +/* See LICENSE for license details. */ +/* Requires: stdint.h, size_t, ssize_t */ + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a) / sizeof(a)[0]) +#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) +#define DEFAULT(a, b) (a) = (a) ? (a) : (b) +#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) +#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ + (t1.tv_nsec-t2.tv_nsec)/1E6) +#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +typedef uint_least32_t Rune; + +void die(const char *, ...); +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(const char *); +ssize_t xwrite(int, const char *, size_t); +/* TODO: utf8 spells out decode, but base64 doesn't... */ +size_t utf8decode(const char *, Rune *, size_t); +size_t utf8encode(Rune, char *); +char *base64dec(const char *); +size_t csienc(char *, size_t, uint, uint, uint, char); diff --git a/win.h b/win.h @@ -1,4 +1,5 @@ /* See LICENSE for license details. */ +/* Requires: util.h, st.h */ enum win_mode { MODE_VISIBLE = 1 << 0, @@ -23,6 +24,12 @@ enum win_mode { |MODE_MOUSEMANY, }; +void xclipcopy(void); +void xclippaste(void); +void xselpaste(void); +void xzoomabs(double); +void xzoomrel(double); +void xzoomrst(void); void xbell(void); void xclipcopy(void); void xdrawcursor(int, int, Glyph, int, int, Glyph); @@ -33,7 +40,8 @@ int xsetcolorname(int, const char *); void xseticontitle(char *); void xsettitle(char *); int xsetcursor(int); -void xsetmode(int, unsigned int); +void xsetmode(int, uint); +void xtogmode(uint); void xsetpointermotion(int); void xsetsel(char *); int xstartdraw(void); diff --git a/x.c b/x.c @@ -4,65 +4,35 @@ #include <limits.h> #include <locale.h> #include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <sys/select.h> #include <time.h> #include <unistd.h> #include <libgen.h> #include <X11/Xatom.h> #include <X11/Xlib.h> -#include <X11/cursorfont.h> #include <X11/keysym.h> #include <X11/Xft/Xft.h> #include <X11/XKBlib.h> char *argv0; #include "arg.h" +#include "util.h" +#include "config.h" #include "st.h" #include "win.h" -/* types used in config.h */ -typedef struct { - uint mod; - KeySym keysym; - void (*func)(const Arg *); - const Arg arg; -} Shortcut; - -typedef struct { - uint mod; - uint button; - void (*func)(const Arg *); - const Arg arg; - uint release; -} MouseShortcut; - -/* X modifiers */ -#define XK_ANY_MOD UINT_MAX -#define XK_NO_MOD 0 -#define XK_SWITCH_MOD (1<<13) - -/* function definitions used in config.h */ -static void clipcopy(const Arg *); -static void clippaste(const Arg *); -static void numlock(const Arg *); -static void selpaste(const Arg *); -static void zoom(const Arg *); -static void zoomabs(const Arg *); -static void zoomreset(const Arg *); -static void ttysend(const Arg *); - -/* config.h for applying patches and the configuration. */ -#include "config.h" - /* XEMBED messages */ #define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_OUT 5 -/* macros */ -#define IS_SET(flag) ((win.mode & (flag)) != 0) -#define TRUERED(x) (((x) & 0xff0000) >> 8) -#define TRUEGREEN(x) (((x) & 0xff00)) -#define TRUEBLUE(x) (((x) & 0xff) << 8) +#define IS_SET(flag) ((win.mode & (flag)) != 0) +#define TRUERED(x) (((x) & 0xff0000) >> 8) +#define TRUEGREEN(x) (((x) & 0xff00)) +#define TRUEBLUE(x) (((x) & 0xff) << 8) typedef XftDraw *Draw; typedef XftColor Color; @@ -174,7 +144,6 @@ static void selrequest(XEvent *); static void setsel(char *, Time); static void mousesel(XEvent *, int); static void mousereport(XEvent *); -static int match(uint, uint); static void run(void); static void usage(void); @@ -244,8 +213,15 @@ static char *opt_title = NULL; static int oldbutton = 3; /* button event on startup: 3 = release */ +/* Printable characters in ASCII. Used to estimate the advance width + * of single wide characters. */ +static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; + void -clipcopy(const Arg *dummy) +xclipcopy(void) { Atom clipboard; @@ -260,7 +236,7 @@ clipcopy(const Arg *dummy) } void -clippaste(const Arg *dummy) +xclippaste(void) { Atom clipboard; @@ -270,52 +246,33 @@ clippaste(const Arg *dummy) } void -selpaste(const Arg *dummy) +xselpaste(void) { XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, xw.win, CurrentTime); } void -numlock(const Arg *dummy) -{ - win.mode ^= MODE_NUMLOCK; -} - -void -zoom(const Arg *arg) -{ - Arg larg; - - larg.f = usedfontsize + arg->f; - zoomabs(&larg); -} - -void -zoomabs(const Arg *arg) +xzoomabs(double fontsize) { xunloadfonts(); - xloadfonts(usedfont, arg->f); + xloadfonts(usedfont, fontsize); cresize(0, 0); redraw(); xhints(); } void -zoomreset(const Arg *arg) +xzoomrel(double delta) { - Arg larg; - - if (defaultfontsize > 0) { - larg.f = defaultfontsize; - zoomabs(&larg); - } + xzoomabs(usedfontsize+delta); } void -ttysend(const Arg *arg) +xzoomrst(void) { - ttywrite(arg->s, strlen(arg->s), 1); + if (defaultfontsize > 0) + xzoomabs(defaultfontsize); } int @@ -584,12 +541,6 @@ selnotify(XEvent *e) } void -xclipcopy(void) -{ - clipcopy(NULL); -} - -void selclear_(XEvent *e) { selclear(); @@ -1684,8 +1635,9 @@ xsetpointermotion(int set) XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); } +/* TODO combine xsetmode and xtogmode into one general function? */ void -xsetmode(int set, unsigned int flags) +xsetmode(int set, uint flags) { int mode = win.mode; MODBIT(win.mode, set, flags); @@ -1693,6 +1645,15 @@ xsetmode(int set, unsigned int flags) redraw(); } +void +xtogmode(uint flags) +{ + int mode = win.mode; + win.mode ^= flags; + if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) + redraw(); +} + int xsetcursor(int cursor) { @@ -1748,6 +1709,7 @@ focus(XEvent *ev) int match(uint mask, uint state) { + uint ignoremod = Mod2Mask|XK_SWITCH_MOD; return mask == XK_ANY_MOD || mask == (state & ~ignoremod); } @@ -1795,7 +1757,7 @@ kpress(XEvent *ev) | (e->state&ControlMask ? C : 0); for (k = keys; k < keys + LEN(keys); k++) { if (k->sym == ksym && scmatch(state, k->set, k->clr)) { - len = k->fn(buf, sizeof buf, ksym, state, k->arg); + len = k->enc(buf, sizeof buf, ksym, state, k->arg); ttywrite(buf, len, 1); return; }