From cfc0713137568055157b2567d977852fe62d1eaf Mon Sep 17 00:00:00 2001 From: speediegq Date: Tue, 16 Aug 2022 22:45:40 +0200 Subject: [PATCH] Add new wip build, should be fairly stable. --- Makefile | 48 +- README.md | 129 --- autocomplete.h | 16 - config.def.h | 1032 ++++++++++++++++++---- config.mk | 10 +- docs/bindlist | 17 - docs/example.Xresources | 33 - docs/keybinds | 23 - docs/patchlist | 28 - docs/st-keybinds | 4 - hb.c | 9 +- st | Bin 134752 -> 165352 bytes st-autocomplete | 300 ------- st.c | 1844 +++++++++++++++++---------------------- st.desktop | 2 +- st.h | 51 +- st.info | 5 +- win.h | 8 +- x.c | 331 ++++--- 19 files changed, 1849 insertions(+), 2041 deletions(-) delete mode 100644 README.md delete mode 100644 autocomplete.h delete mode 100644 docs/bindlist delete mode 100644 docs/example.Xresources delete mode 100644 docs/keybinds delete mode 100644 docs/patchlist delete mode 100644 docs/st-keybinds delete mode 100644 st-autocomplete diff --git a/Makefile b/Makefile index 5cc659a..13f3e91 100644 --- a/Makefile +++ b/Makefile @@ -32,64 +32,34 @@ st: $(OBJ) $(CC) -o $@ $(OBJ) $(STLDFLAGS) clean: - rm -f st $(OBJ) st-spde-$(VERSION).tar.gz + rm -f st $(OBJ) st-$(VERSION).tar.gz dist: clean - mkdir -p st-spde-$(VERSION) - cp -R arg.h autocomplete.h boxdraw.c boxdraw_data.h config.def.h config.mk st.png st.desktop docs hb.c hb.h Makefile st.1 st-autocomplete st.c st.h st.info win.h x.c docs/st-keybinds st-spde-$(VERSION) - tar -cf - st-spde-$(VERSION) | gzip > st-spde-$(VERSION).tar.gz - rm -rf st-spde-$(VERSION) + mkdir -p st-$(VERSION) + cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ + config.def.h st.info st.1 arg.h st.h win.h st.png st.desktop $(SRC)\ + st-$(VERSION) + tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz + rm -rf st-$(VERSION) install: st - touch config.h boxdraw.o hb.o st.o x.o mkdir -p $(DESTDIR)$(PREFIX)/bin cp -f st $(DESTDIR)$(PREFIX)/bin - cp -f docs/bindlist $(DESTDIR)$(PREFIX)/bin/stbindlist - cp -f docs/st-keybinds $(DESTDIR)$(PREFIX)/bin - chmod +x $(DESTDIR)$(PREFIX)/bin/st-keybinds chmod 755 $(DESTDIR)$(PREFIX)/bin/st - cp -f st-autocomplete $(DESTDIR)$(PREFIX)/bin - chmod 755 $(DESTDIR)$(PREFIX)/bin/st-autocomplete mkdir -p $(DESTDIR)$(MANPREFIX)/man1 sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 - rm -f config.h boxdraw.o hb.o st.o x.o tic -sx st.info @echo Please see the README file regarding the terminfo entry of st. mkdir -p $(DESTDIR)$(ICONPREFIX) + [ -f $(ICONNAME) ] && cp -f $(ICONNAME) $(DESTDIR)$(ICONPREFIX) || : mkdir -p $(DESTDIR)$(APPPREFIX) cp -f st.desktop $(DESTDIR)$(APPPREFIX) - [ -f $(ICONNAME) ] && cp -f $(ICONNAME) $(DESTDIR)$(ICONPREFIX) || : uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/st rm -f $(DESTDIR)$(APPPREFIX)/st.desktop - rm -f $(DESTDIR)$(PREFIX)/bin/st-autocomplete rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 rm -f $(DESTDIR)$(ICONPREFIX)/$(ICONNAME) -libxftfix: - git clone https://github.com/uditkarode/libxft-bgra && cd libxft-bgra - sh autogen.sh --sysconfdir=/etc --prefix=/usr --mandir=/usr/share/man - make install - cd .. && rm -r libxft-bgra - -gentoo-libxftfix: - mkdir -pv /etc/portage/patches/x11-libs/libXft - curl -o /etc/portage/patches/x11-libs/libXft/bgra.diff https://gitlab.freedesktop.org/xorg/lib/libxft/-/merge_requests/1.patch - emerge x11-libs/libXft - -arch-libxftfix: - git clone https://aur.archlinux.org/libxft-bgra - cd libxft-bgra - makepkg -i - -help: - @echo install: Installs st. You may need to run this as root. - @echo uninstall: Uninstalls st. You may need to run this as root. - @echo libxftfix: This option compiles and installs libXft-bgra which is necessary to prevent st from crashing. - @echo gentoo-libxftfix: This option installs libXft-bgra by patching it for Gentoo only. - @echo arch-libxftfix: This option installs libXft-bgra using the AUR on Arch Linux only. - @echo help: Displays this help sheet. - -.PHONY: all options clean dist install uninstall libxftfix arch-libxftfix gentoo-libxftfix help +.PHONY: all options clean dist install uninstall diff --git a/README.md b/README.md deleted file mode 100644 index 3733de7..0000000 --- a/README.md +++ /dev/null @@ -1,129 +0,0 @@ -# st -speedie's simple terminal configuration - -![image](https://user-images.githubusercontent.com/71722170/163290759-e171e5f0-3e9b-4c23-85bf-b45f19a37b34.png) - -This is my personal always-changing build of [suckless.org](https://suckless.org)'s simple terminal or [st](https://st.suckless.org). -It features many different patches and tweaks to enhance the experience. - -### Important -- This build REQUIRES libXft-bgra. If you don't know what that is, it's a patched build of libXft which fixes a bug which causes st to crash when displaying certain characters. -- This build does not keep object files or config.h. Those are automatically deleted after a successful compile. If a compile is not successful, they will not be deleted. If that's the case, do NOT edit config.h because if you do and it compiles successfully then your changes will be lost. - -### Installation -- Install dev-vcs/git using your favorite package manager -- git clone -- cd st -- sudo make install -- Follow "Installing libXft-bgra" - -### Installation (Package manager) -If you're running Gentoo Linux, you can add my overlay and then simply emerge it. -- emerge layman -- layman -o https://raw.githubusercontent.com/spoverlay/splay/main/splay.xml -f -a splay -- emerge st-spde - -### Installing libXft-bgra -Thankfully, I made this process a bit easier by building it into the Makefile. -- cd st -- make libxftfix # For all distros except Gentoo -- make gentoo-libxftfix # For Gentoo -- make arch-libxftfix # For Arch - -### Features -This build of dwm has been patched pretty heavily with the following patches -- st-alpha -- st-anysize -- st-autocomplete -- st-bold-is-not-bright -- st-boxdraw -- st-charoffsets -- st-clipboard -- st-delkey -- st-externalpipe -- st-font2 -- st-fontfix -- st-glyph-wide-support-boxdraw -- st-ligatures-alpha-scrollback -- st-newterm -- st-nobadweight -- st-scrollback -- st-scrollback-mouse -- st-spoiler -- st-w3m -- st-xclearwin -- st-xresources -- st-keyboard-select -- st-undercurl -- st-csi -- st-copyurl -- st-dynamic-cursor-color - - -This build of st has full compatibility with .Xresources and allows defining these options: -- !! st resources -- ! special -- *.foreground: #e7e7e7 -- *.background: #202020 -- *.cursorColor: #e7e7e7 -- ! black -- *.color0: #262626 -- *.color8: #737373 -- ! red -- *.color1: #ff3534 -- *.color9: #ff4f4c -- ! green -- *.color2: #35b723 -- *.color10: #82ff6e -- ! yellow -- *.color3: #eae804 -- *.color11: #dfff52 -- ! blue -- *.color4: #362be4 -- *.color12: #5767ff -- ! magenta -- *.color5: #fa9cf8 -- *.color13: #cf89cf -- ! cyan -- *.color6: #4ed4d4 -- *.color14: #68efef -- ! white -- *.color7: #bebebe -- *.color15: #f7f5f5 -- ! alpha -- *.alpha: 0.7 -- ! font -- *.font: Terminus:pixelsize=15.5:antialias=true:autohint=true; -- *.font2: JoyPixels:pixelsize=12 -- *.cols: 80 -- *.rows: 24 -- *.mousefg: 7 -- *.mousebg: 0 -- *.defaultattr: 11 -- *.cwscale: 1.0 -- *.chscale: 1.0 - -Pywal support is also a thing and will automatically be used. No special scripts like my build of dmenu requires. - -It also supports color emojis as long as libXft is patched, otherwise it will CRASH. It has an emoji picker. To use it, add a keybind to your build of dwm which runs /usr/bin/emojilist (Requires dmenu). It allows copying the output of a command. To use it, add a keybind to your build of dwm which runs /usr/bin/copyout (Requires dmenu) Keep in mind if you're using my build of dwm, these binds are already added. - -### Keybinds (not complete) -- CTRL+y | Copy to the clipboard -- CTRL+p | Paste from the clipboard -- CTRL+= | Increase font size -- CTRL+- | Decrease font size -- CTRL+0 | Reset font size to default -- CTRL+j | Scroll down -- CTRL+k | Scroll up -- CTRL+Shift+k| Copy the last URL -- CTRL+Enter | Opens a new terminal window in the same directory -- Scroll+up | Scroll up (Mouse scrollback support) -- Scroll+down | Scroll down (Mouse scrollback support) -- Shift+Esc | Select mode -- -- If you're using my zsh config: -- I | Enter Insert mode -- Esc | Enter Normal mode -- Tab | Autocomplete - - diff --git a/autocomplete.h b/autocomplete.h deleted file mode 100644 index fc88447..0000000 --- a/autocomplete.h +++ /dev/null @@ -1,16 +0,0 @@ -# ifndef __ST_AUTOCOMPLETE_H -# define __ST_AUTOCOMPLETE_H - -enum { - ACMPL_DEACTIVATE, - ACMPL_WORD, - ACMPL_WWORD, - ACMPL_FUZZY_WORD, - ACMPL_FUZZY_WWORD, - ACMPL_FUZZY, - ACMPL_SUFFIX, - ACMPL_SURROUND, - ACMPL_UNDO, -}; - -# endif // __ST_AUTOCOMPLETE_H diff --git a/config.def.h b/config.def.h index 200e676..cbc44a9 100644 --- a/config.def.h +++ b/config.def.h @@ -1,106 +1,213 @@ -/* speedie's simple terminal configuration file - * https://github.com/speedie-de/st - * - * - If you want 'copyout' support, download the script from my GitHub and add a keybind to your WM. - * - If you want an emoji picker, download dboard and add a keybind to your WM. - * - If you're using my build then the above is already done so you don't need to worry about it. - * - * WARNING: This build of st does NOT prevent colored emojis from displaying. - * If you view colored emojis and don't have patched libXft then it will crash. - * To install libXft-bgra (the patched libXft), run one of these commands - * - make libxftfix (For all distributions except Gentoo and Arch) - * - make gentoo-libxftfix (For all Gentoo based distributions) - * - make arch-libxftfix (For all Arch based distributions) - * - * If you have any questions, read the 'man' page. - * - * As for fonts, I recommend using Terminus for text and JoyPixels for Emojis - * You can set all of these in your .Xresources or if you don't have one, by editing the values below. */ +/* See LICENSE file for copyright and license details. */ -/* Options - * Most of these can be configured through .Xresources. - * You only need to change them if you won't be using .Xresources. - * See example.Xresources for more information. */ -static char *font = "DejaVu Sans Mono:pixelsize=12:antialias=true:autohint=true"; -static char *font2[] = { "fontawesome:pixelsize=8:antialias=true:autohint=true", - "DejaVuSansMonoForPowerline Nerd Font:style=Book:antialias=true:pixelsize=8" +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; +static char *font2[] = { + "Inconsolata for Powerline:pixelsize=12:antialias=true:autohint=true", + "Hack Nerd Font Mono:pixelsize=11:antialias=true:autohint=true", }; -static char *shell = "/bin/sh"; -static char *plumb_cmd[] = {"plumb", "-c", NULL, NULL}; -static float cwscale = 1.0; -static float chscale = 1.0; -static short cxoffset = 0; -static short cyoffset = 0; -char *utmp = NULL; -char *scroll = NULL; -char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; -char *vtiden = "\033[?6c"; -char *termname = "xterm-256color"; -wchar_t *worddelimiters = L" "; + +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; -int allowaltscreen = 1; -int allowwindowops = 0; -static double minlatency = 8; -static double maxlatency = 33; -static unsigned int blinktimeout = 800; -static unsigned int cursorthickness = 2; -const int boxdraw = 0; -const int boxdraw_bold = 0; -const int boxdraw_braille = 0; -static int borderpx = 2; -static int bellvolume = 0; + +/* 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; + +/* + * Synchronized-Update timeout in ms + * https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec + */ +static uint su_timeout = 200; + +/* + * 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; + +/* + * 1: render most of the lines/blocks characters without using the font for + * perfect alignment between cells (U2500 - U259F except dashes/diagonals). + * Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored. + * 0: disable (render all U25XX glyphs normally from the font). + */ +const int boxdraw = 0; +const int boxdraw_bold = 0; + +/* braille (U28XX): 1: render as adjacent "pixels", 0: use font */ +const int boxdraw_braille = 0; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "xterm-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; -float alpha = 0.85; + +/* bg opacity */ +float alpha = 0.8; /* Terminal colors (16 first used in escape sequence) */ static const char *colorname[] = { /* 8 normal colors */ - "#5c5c5c", /* black */ - "#e57373", /* red3 */ - "#02982e", /* green3 */ - "#fac863", /* yellow3 */ - "#6699cc", /* blue2 */ - "#a36ac7", /* magenta3 */ - "#5fb3b3", /* cyan3 */ - "#c0c5ce", /* gray90 */ + "black", + "red3", + "green3", + "yellow3", + "blue2", + "magenta3", + "cyan3", + "gray90", /* 8 bright colors */ - "#00ffaa", /* gray50 */ - "#e57373", /* red */ - "#a6bc69", /* green */ - "#fac863", /* yellow */ - "#6699cc", /* #5c5cff */ - "#c594c5", /* magenta */ - "#5fb3b3", /* cyan */ - "#ffffff", /* white */ + "gray50", + "red", + "green", + "yellow", + "#5c5cff", + "magenta", + "cyan", + "white", + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ "#cccccc", "#555555", - "#c0c5ce", /* default foreground colour (#c0c5ce) */ - "#696969", /* default background colour (#212121) */ + "gray90", /* default foreground colour */ + "black", /* default background colour */ }; + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ unsigned int defaultfg = 258; -unsigned int defaultbg = 232; -static unsigned int defaultcs = 256; +unsigned int defaultbg = 259; +unsigned int defaultcs = 256; static unsigned int defaultrcs = 257; +/* + * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81 + * Default style of cursor + * 0: blinking block + * 1: blinking block (default) + * 2: steady block ("█") + * 3: blinking underline + * 4: steady underline ("_") + * 5: blinking bar + * 6: steady bar ("|") + * 7: blinking st cursor + * 8: steady st cursor + */ static unsigned int cursorstyle = 1; -static Rune stcursor = 0x2603; +static Rune stcursor = 0x2603; /* snowman ("☃") */ + +/* + * 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; -static unsigned int mouseshape = XC_xterm; + +/* + * 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; + /* * Xresources preferences to load at startup */ ResourcePref resources[] = { { "font", STRING, &font }, - { "font2", STRING, &font2 }, { "color0", STRING, &colorname[0] }, { "color1", STRING, &colorname[1] }, { "color2", STRING, &colorname[2] }, @@ -117,9 +224,9 @@ ResourcePref resources[] = { { "color13", STRING, &colorname[13] }, { "color14", STRING, &colorname[14] }, { "color15", STRING, &colorname[15] }, - { "background", STRING, &colorname[259] }, - { "foreground", STRING, &colorname[258] }, - { "cursorColor", STRING, &colorname[256] }, + { "background", STRING, &colorname[256] }, + { "foreground", STRING, &colorname[257] }, + { "cursorColor", STRING, &colorname[258] }, { "termname", STRING, &termname }, { "shell", STRING, &shell }, { "minlatency", INTEGER, &minlatency }, @@ -128,25 +235,10 @@ ResourcePref resources[] = { { "bellvolume", INTEGER, &bellvolume }, { "tabspaces", INTEGER, &tabspaces }, { "borderpx", INTEGER, &borderpx }, - { "cols", INTEGER, &cols }, - { "rows", INTEGER, &rows }, - { "mousefg", INTEGER, &mousefg }, - { "mousebg", INTEGER, &mousebg }, - { "defaultattr", INTEGER, &defaultattr }, { "cwscale", FLOAT, &cwscale }, { "chscale", FLOAT, &chscale }, - { "alpha", FLOAT, &alpha }, }; -/* - * 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; - -#include "autocomplete.h" - /* * Internal mouse shortcuts. * Beware that overloading Button1 will disable the selection. @@ -162,36 +254,24 @@ static MouseShortcut mshortcuts[] = { { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, }; -#define MODKEY (ControlMask) -#define TERMMOD (ShiftMask) +/* 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} }, - { ControlMask|ShiftMask, XK_Return, newterm, {.i = 0} }, - { ControlMask, XK_equal, zoom, {.f = +1} }, - { ControlMask, XK_minus, zoom, {.f = -1} }, - { ControlMask, XK_0, zoomreset, {.f = 0} }, - { ControlMask, XK_y, clipcopy, {.i = 1} }, - { ControlMask, XK_p, clippaste, {.i = 0} }, - { TERMMOD, XK_Escape, keyboard_select,{.i = 0} }, - { TERMMOD, XK_y, selpaste, {.i = 0} }, - { ShiftMask, XK_Insert, selpaste, {.i = 0} }, - { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, - { ControlMask|ShiftMask, XK_k, copyurl, {.i = 0} }, - { ControlMask, XK_k, kscrollup, {.i = +1} }, - { ControlMask, XK_j, kscrolldown, {.i = +1} }, - { ShiftMask, XK_Tab, autocomplete, {.i = ACMPL_WORD } }, - { ShiftMask, XK_period, autocomplete, {.i = ACMPL_FUZZY_WORD } }, - { ShiftMask, XK_comma, autocomplete, {.i = ACMPL_FUZZY } }, - { ShiftMask, XK_apostrophe, autocomplete, {.i = ACMPL_SUFFIX } }, - { ShiftMask, XK_semicolon, autocomplete, {.i = ACMPL_SURROUND } }, - { ShiftMask, XK_bracketright,autocomplete, {.i = ACMPL_WWORD } }, - { ShiftMask, XK_bracketleft, autocomplete, {.i = ACMPL_FUZZY_WWORD } }, - { ShiftMask, XK_equal, autocomplete, {.i = ACMPL_UNDO } }, + /* 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} }, + { ControlMask, XK_equal, zoom, {.f = +1} }, + { ControlMask, XK_minus, zoom, {.f = -1} }, + { ControlMask, XK_0, zoomreset, {.f = 0} }, + { ControlMask, XK_y, clipcopy, {.i = 0} }, + { ControlMask, XK_p, clippaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { ControlMask, XK_k, kscrollup, {.i = +1} }, + { ControlMask, XK_j, kscrolldown, {.i = +1} }, }; /* @@ -219,7 +299,80 @@ static Shortcut shortcuts[] = { * 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 }; +static KeySym mappedkeys[] = { + XK_space, + XK_m, + XK_i, + XK_A, + XK_B, + XK_C, + XK_D, + XK_E, + XK_F, + XK_G, + XK_H, + XK_I, + XK_K, + XK_J, + XK_L, + XK_M, + XK_N, + XK_O, + XK_P, + XK_Q, + XK_R, + XK_S, + XK_T, + XK_U, + XK_V, + XK_W, + XK_X, + XK_Y, + XK_Z, + XK_Z, + XK_0, + XK_1, + XK_2, + XK_3, + XK_4, + XK_5, + XK_6, + XK_7, + XK_8, + XK_9, + XK_exclam, + XK_quotedbl, + XK_numbersign, + XK_dollar, + XK_percent, + XK_ampersand, + XK_apostrophe, + XK_parenleft, + XK_parenright, + XK_asterisk, + XK_plus, + XK_comma, + XK_minus, + XK_period, + XK_slash, + XK_colon, + XK_semicolon, + XK_less, + XK_equal, + XK_greater, + XK_question, + XK_at, + XK_bracketleft, + XK_backslash, + XK_bracketright, + XK_asciicircum, + XK_underscore, + XK_grave, + XK_braceleft, + XK_bar, + XK_braceright, + XK_asciitilde, +}; /* * State bits to ignore when matching key or button events. By default, @@ -235,59 +388,20 @@ 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[3~", -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}, @@ -326,36 +440,27 @@ static Key key[] = { { 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_Return, XK_NO_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[3~", -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}, @@ -442,6 +547,572 @@ static Key key[] = { { 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}, + + // libtermkey compatible keyboard input + { XK_KP_Home, XK_NO_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_NO_MOD, "\033[1~", 0, +1}, + { XK_KP_Home, ControlMask, "\033[149;5u", 0, 0}, + { XK_KP_Home, ControlMask|ShiftMask, "\033[149;6u", 0, 0}, + { XK_KP_Home, Mod1Mask, "\033[149;3u", 0, 0}, + { XK_KP_Home, Mod1Mask|ControlMask, "\033[149;7u", 0, 0}, + { XK_KP_Home, Mod1Mask|ControlMask|ShiftMask, "\033[149;8u", 0, 0}, + { XK_KP_Home, Mod1Mask|ShiftMask, "\033[149;4u", 0, 0}, + { XK_KP_Home, ShiftMask, "\033[149;2u", 0, 0}, + { XK_KP_Up, XK_NO_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_NO_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_NO_MOD, "\033OA", 0, +1}, + { XK_KP_Up, ControlMask, "\033[151;5u", 0, 0}, + { XK_KP_Up, ControlMask|ShiftMask, "\033[151;6u", 0, 0}, + { XK_KP_Up, Mod1Mask, "\033[151;3u", 0, 0}, + { XK_KP_Up, Mod1Mask|ControlMask, "\033[151;7u", 0, 0}, + { XK_KP_Up, Mod1Mask|ControlMask|ShiftMask, "\033[151;8u", 0, 0}, + { XK_KP_Up, Mod1Mask|ShiftMask, "\033[151;4u", 0, 0}, + { XK_KP_Up, ShiftMask, "\033[151;2u", 0, 0}, + { XK_KP_Down, XK_NO_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_NO_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_NO_MOD, "\033OB", 0, +1}, + { XK_KP_Down, ControlMask, "\033[153;5u", 0, 0}, + { XK_KP_Down, ControlMask|ShiftMask, "\033[153;6u", 0, 0}, + { XK_KP_Down, Mod1Mask, "\033[153;3u", 0, 0}, + { XK_KP_Down, Mod1Mask|ControlMask, "\033[153;7u", 0, 0}, + { XK_KP_Down, Mod1Mask|ControlMask|ShiftMask, "\033[153;8u", 0, 0}, + { XK_KP_Down, Mod1Mask|ShiftMask, "\033[153;4u", 0, 0}, + { XK_KP_Down, ShiftMask, "\033[153;2u", 0, 0}, + { XK_KP_Left, XK_NO_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_NO_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_NO_MOD, "\033OD", 0, +1}, + { XK_KP_Left, ControlMask, "\033[150;5u", 0, 0}, + { XK_KP_Left, ControlMask|ShiftMask, "\033[150;6u", 0, 0}, + { XK_KP_Left, Mod1Mask, "\033[150;3u", 0, 0}, + { XK_KP_Left, Mod1Mask|ControlMask, "\033[150;7u", 0, 0}, + { XK_KP_Left, Mod1Mask|ControlMask|ShiftMask, "\033[150;8u", 0, 0}, + { XK_KP_Left, Mod1Mask|ShiftMask, "\033[150;4u", 0, 0}, + { XK_KP_Left, ShiftMask, "\033[150;2u", 0, 0}, + { XK_KP_Right, XK_NO_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_NO_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_NO_MOD, "\033OC", 0, +1}, + { XK_KP_Right, ControlMask, "\033[152;5u", 0, 0}, + { XK_KP_Right, ControlMask|ShiftMask, "\033[152;6u", 0, 0}, + { XK_KP_Right, Mod1Mask, "\033[152;3u", 0, 0}, + { XK_KP_Right, Mod1Mask|ControlMask, "\033[152;7u", 0, 0}, + { XK_KP_Right, Mod1Mask|ControlMask|ShiftMask, "\033[152;8u", 0, 0}, + { XK_KP_Right, Mod1Mask|ShiftMask, "\033[152;4u", 0, 0}, + { XK_KP_Right, ShiftMask, "\033[152;2u", 0, 0}, + { XK_KP_Prior, XK_NO_MOD, "\033[5~", 0, 0}, + { XK_KP_Prior, ControlMask, "\033[154;5u", 0, 0}, + { XK_KP_Prior, ControlMask|ShiftMask, "\033[154;6u", 0, 0}, + { XK_KP_Prior, Mod1Mask, "\033[154;3u", 0, 0}, + { XK_KP_Prior, Mod1Mask|ControlMask, "\033[154;7u", 0, 0}, + { XK_KP_Prior, Mod1Mask|ControlMask|ShiftMask, "\033[154;8u", 0, 0}, + { XK_KP_Prior, Mod1Mask|ShiftMask, "\033[154;4u", 0, 0}, + { XK_KP_Begin, XK_NO_MOD, "\033[E", 0, 0}, + { XK_KP_Begin, ControlMask, "\033[157;5u", 0, 0}, + { XK_KP_Begin, ControlMask|ShiftMask, "\033[157;6u", 0, 0}, + { XK_KP_Begin, Mod1Mask, "\033[157;3u", 0, 0}, + { XK_KP_Begin, Mod1Mask|ControlMask, "\033[157;7u", 0, 0}, + { XK_KP_Begin, Mod1Mask|ControlMask|ShiftMask, "\033[157;8u", 0, 0}, + { XK_KP_Begin, Mod1Mask|ShiftMask, "\033[157;4u", 0, 0}, + { XK_KP_Begin, ShiftMask, "\033[157;2u", 0, 0}, + { XK_KP_End, XK_NO_MOD, "\033[4~", 0, 0}, + { XK_KP_End, ControlMask|ShiftMask, "\033[156;6u", 0, 0}, + { XK_KP_End, Mod1Mask, "\033[156;3u", 0, 0}, + { XK_KP_End, Mod1Mask|ControlMask, "\033[156;7u", 0, 0}, + { XK_KP_End, Mod1Mask|ControlMask|ShiftMask, "\033[156;8u", 0, 0}, + { XK_KP_End, Mod1Mask|ShiftMask, "\033[156;4u", 0, 0}, + { XK_KP_Next, XK_NO_MOD, "\033[6~", 0, 0}, + { XK_KP_Next, ControlMask, "\033[155;5u", 0, 0}, + { XK_KP_Next, ControlMask|ShiftMask, "\033[155;6u", 0, 0}, + { XK_KP_Next, Mod1Mask, "\033[155;3u", 0, 0}, + { XK_KP_Next, Mod1Mask|ControlMask, "\033[155;7u", 0, 0}, + { XK_KP_Next, Mod1Mask|ControlMask|ShiftMask, "\033[155;8u", 0, 0}, + { XK_KP_Next, Mod1Mask|ShiftMask, "\033[155;4u", 0, 0}, + { XK_KP_Insert, XK_NO_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_NO_MOD, "\033[2~", +1, 0}, + { XK_KP_Insert, ControlMask|ShiftMask, "\033[158;6u", 0, 0}, + { XK_KP_Insert, Mod1Mask, "\033[158;3u", 0, 0}, + { XK_KP_Insert, Mod1Mask|ControlMask, "\033[158;7u", 0, 0}, + { XK_KP_Insert, Mod1Mask|ControlMask|ShiftMask, "\033[158;8u", 0, 0}, + { XK_KP_Insert, Mod1Mask|ShiftMask, "\033[158;4u", 0, 0}, + { XK_KP_Delete, XK_NO_MOD, "\033[P", -1, 0}, + { XK_KP_Delete, XK_NO_MOD, "\033[3~", +1, 0}, + { XK_KP_Delete, ControlMask|ShiftMask, "\033[159;6u", 0, 0}, + { XK_KP_Delete, Mod1Mask, "\033[159;3u", 0, 0}, + { XK_KP_Delete, Mod1Mask|ControlMask, "\033[159;7u", 0, 0}, + { XK_KP_Delete, Mod1Mask|ControlMask|ShiftMask, "\033[159;8u", 0, 0}, + { XK_KP_Delete, Mod1Mask|ShiftMask, "\033[159;4u", 0, 0}, + { XK_KP_Multiply, XK_NO_MOD, "\033Oj", +2, 0}, + { XK_KP_Multiply, ControlMask, "\033[170;5u", 0, 0}, + { XK_KP_Multiply, ControlMask|ShiftMask, "\033[170;6u", 0, 0}, + { XK_KP_Multiply, Mod1Mask, "\033[170;3u", 0, 0}, + { XK_KP_Multiply, Mod1Mask|ControlMask, "\033[170;7u", 0, 0}, + { XK_KP_Multiply, Mod1Mask|ControlMask|ShiftMask, "\033[170;8u", 0, 0}, + { XK_KP_Multiply, Mod1Mask|ShiftMask, "\033[170;4u", 0, 0}, + { XK_KP_Multiply, ShiftMask, "\033[170;2u", 0, 0}, + { XK_KP_Add, XK_NO_MOD, "\033Ok", +2, 0}, + { XK_KP_Add, ControlMask, "\033[171;5u", 0, 0}, + { XK_KP_Add, ControlMask|ShiftMask, "\033[171;6u", 0, 0}, + { XK_KP_Add, Mod1Mask, "\033[171;3u", 0, 0}, + { XK_KP_Add, Mod1Mask|ControlMask, "\033[171;7u", 0, 0}, + { XK_KP_Add, Mod1Mask|ControlMask|ShiftMask, "\033[171;8u", 0, 0}, + { XK_KP_Add, Mod1Mask|ShiftMask, "\033[171;4u", 0, 0}, + { XK_KP_Add, ShiftMask, "\033[171;2u", 0, 0}, + { XK_KP_Enter, XK_NO_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_NO_MOD, "\r", -1, 0}, + { XK_KP_Enter, XK_NO_MOD, "\r\n", -1, 0}, + { XK_KP_Enter, ControlMask, "\033[141;5u", 0, 0}, + { XK_KP_Enter, ControlMask|ShiftMask, "\033[141;6u", 0, 0}, + { XK_KP_Enter, Mod1Mask, "\033[141;3u", 0, 0}, + { XK_KP_Enter, Mod1Mask|ControlMask, "\033[141;7u", 0, 0}, + { XK_KP_Enter, Mod1Mask|ControlMask|ShiftMask, "\033[141;8u", 0, 0}, + { XK_KP_Enter, Mod1Mask|ShiftMask, "\033[141;4u", 0, 0}, + { XK_KP_Enter, ShiftMask, "\033[141;2u", 0, 0}, + { XK_KP_Subtract, XK_NO_MOD, "\033Om", +2, 0}, + { XK_KP_Subtract, ControlMask, "\033[173;5u", 0, 0}, + { XK_KP_Subtract, ControlMask|ShiftMask, "\033[173;6u", 0, 0}, + { XK_KP_Subtract, Mod1Mask, "\033[173;3u", 0, 0}, + { XK_KP_Subtract, Mod1Mask|ControlMask, "\033[173;7u", 0, 0}, + { XK_KP_Subtract, Mod1Mask|ControlMask|ShiftMask, "\033[173;8u", 0, 0}, + { XK_KP_Subtract, Mod1Mask|ShiftMask, "\033[173;4u", 0, 0}, + { XK_KP_Subtract, ShiftMask, "\033[173;2u", 0, 0}, + { XK_KP_Decimal, XK_NO_MOD, "\033On", +2, 0}, + { XK_KP_Decimal, ControlMask, "\033[174;5u", 0, 0}, + { XK_KP_Decimal, ControlMask|ShiftMask, "\033[174;6u", 0, 0}, + { XK_KP_Decimal, Mod1Mask, "\033[174;3u", 0, 0}, + { XK_KP_Decimal, Mod1Mask|ControlMask, "\033[174;7u", 0, 0}, + { XK_KP_Decimal, Mod1Mask|ControlMask|ShiftMask, "\033[174;8u", 0, 0}, + { XK_KP_Decimal, Mod1Mask|ShiftMask, "\033[174;4u", 0, 0}, + { XK_KP_Decimal, ShiftMask, "\033[174;2u", 0, 0}, + { XK_KP_Divide, XK_NO_MOD, "\033Oo", +2, 0}, + { XK_KP_Divide, ControlMask, "\033[175;5u", 0, 0}, + { XK_KP_Divide, ControlMask|ShiftMask, "\033[175;6u", 0, 0}, + { XK_KP_Divide, Mod1Mask, "\033[175;3u", 0, 0}, + { XK_KP_Divide, Mod1Mask|ControlMask, "\033[175;7u", 0, 0}, + { XK_KP_Divide, Mod1Mask|ControlMask|ShiftMask, "\033[175;8u", 0, 0}, + { XK_KP_Divide, Mod1Mask|ShiftMask, "\033[175;4u", 0, 0}, + { XK_KP_Divide, ShiftMask, "\033[175;2u", 0, 0}, + { XK_KP_0, XK_NO_MOD, "\033Op", +2, 0}, + { XK_KP_0, ControlMask, "\033[176;5u", 0, 0}, + { XK_KP_0, ControlMask|ShiftMask, "\033[176;6u", 0, 0}, + { XK_KP_0, Mod1Mask, "\033[176;3u", 0, 0}, + { XK_KP_0, Mod1Mask|ControlMask, "\033[176;7u", 0, 0}, + { XK_KP_0, Mod1Mask|ControlMask|ShiftMask, "\033[176;8u", 0, 0}, + { XK_KP_0, Mod1Mask|ShiftMask, "\033[176;4u", 0, 0}, + { XK_KP_0, ShiftMask, "\033[176;2u", 0, 0}, + { XK_KP_1, XK_NO_MOD, "\033Oq", +2, 0}, + { XK_KP_0, ControlMask, "\033[177;5u", 0, 0}, + { XK_KP_0, ControlMask|ShiftMask, "\033[177;6u", 0, 0}, + { XK_KP_0, Mod1Mask, "\033[177;3u", 0, 0}, + { XK_KP_0, Mod1Mask|ControlMask, "\033[177;7u", 0, 0}, + { XK_KP_0, Mod1Mask|ControlMask|ShiftMask, "\033[177;8u", 0, 0}, + { XK_KP_0, Mod1Mask|ShiftMask, "\033[177;4u", 0, 0}, + { XK_KP_0, ShiftMask, "\033[177;2u", 0, 0}, + { XK_KP_2, XK_NO_MOD, "\033Or", +2, 0}, + { XK_KP_2, ControlMask, "\033[178;5u", 0, 0}, + { XK_KP_2, ControlMask|ShiftMask, "\033[178;6u", 0, 0}, + { XK_KP_2, Mod1Mask, "\033[178;3u", 0, 0}, + { XK_KP_2, Mod1Mask|ControlMask, "\033[178;7u", 0, 0}, + { XK_KP_2, Mod1Mask|ControlMask|ShiftMask, "\033[178;8u", 0, 0}, + { XK_KP_2, Mod1Mask|ShiftMask, "\033[178;4u", 0, 0}, + { XK_KP_2, ShiftMask, "\033[178;2u", 0, 0}, + { XK_KP_3, XK_NO_MOD, "\033Os", +2, 0}, + { XK_KP_3, ControlMask, "\033[179;5u", 0, 0}, + { XK_KP_3, ControlMask|ShiftMask, "\033[179;6u", 0, 0}, + { XK_KP_3, Mod1Mask, "\033[179;3u", 0, 0}, + { XK_KP_3, Mod1Mask|ControlMask, "\033[179;7u", 0, 0}, + { XK_KP_3, Mod1Mask|ControlMask|ShiftMask, "\033[179;8u", 0, 0}, + { XK_KP_3, Mod1Mask|ShiftMask, "\033[179;4u", 0, 0}, + { XK_KP_3, ShiftMask, "\033[179;2u", 0, 0}, + { XK_KP_4, XK_NO_MOD, "\033Ot", +2, 0}, + { XK_KP_4, ControlMask, "\033[180;5u", 0, 0}, + { XK_KP_4, ControlMask|ShiftMask, "\033[180;6u", 0, 0}, + { XK_KP_4, Mod1Mask, "\033[180;3u", 0, 0}, + { XK_KP_4, Mod1Mask|ControlMask, "\033[180;7u", 0, 0}, + { XK_KP_4, Mod1Mask|ControlMask|ShiftMask, "\033[180;8u", 0, 0}, + { XK_KP_4, Mod1Mask|ShiftMask, "\033[180;4u", 0, 0}, + { XK_KP_4, ShiftMask, "\033[180;2u", 0, 0}, + { XK_KP_5, XK_NO_MOD, "\033Ou", +2, 0}, + { XK_KP_5, ControlMask, "\033[181;5u", 0, 0}, + { XK_KP_5, ControlMask|ShiftMask, "\033[181;6u", 0, 0}, + { XK_KP_5, Mod1Mask, "\033[181;3u", 0, 0}, + { XK_KP_5, Mod1Mask|ControlMask, "\033[181;7u", 0, 0}, + { XK_KP_5, Mod1Mask|ControlMask|ShiftMask, "\033[181;8u", 0, 0}, + { XK_KP_5, Mod1Mask|ShiftMask, "\033[181;4u", 0, 0}, + { XK_KP_5, ShiftMask, "\033[181;2u", 0, 0}, + { XK_KP_6, XK_NO_MOD, "\033Ov", +2, 0}, + { XK_KP_6, ControlMask, "\033[182;5u", 0, 0}, + { XK_KP_6, ControlMask|ShiftMask, "\033[182;6u", 0, 0}, + { XK_KP_6, Mod1Mask, "\033[182;3u", 0, 0}, + { XK_KP_6, Mod1Mask|ControlMask, "\033[182;7u", 0, 0}, + { XK_KP_6, Mod1Mask|ControlMask|ShiftMask, "\033[182;8u", 0, 0}, + { XK_KP_6, Mod1Mask|ShiftMask, "\033[182;4u", 0, 0}, + { XK_KP_6, ShiftMask, "\033[182;2u", 0, 0}, + { XK_KP_7, XK_NO_MOD, "\033Ow", +2, 0}, + { XK_KP_7, ControlMask, "\033[183;5u", 0, 0}, + { XK_KP_7, ControlMask|ShiftMask, "\033[183;6u", 0, 0}, + { XK_KP_7, Mod1Mask, "\033[183;3u", 0, 0}, + { XK_KP_7, Mod1Mask|ControlMask, "\033[183;7u", 0, 0}, + { XK_KP_7, Mod1Mask|ControlMask|ShiftMask, "\033[183;8u", 0, 0}, + { XK_KP_7, Mod1Mask|ShiftMask, "\033[183;4u", 0, 0}, + { XK_KP_7, ShiftMask, "\033[183;2u", 0, 0}, + { XK_KP_8, XK_NO_MOD, "\033Ox", +2, 0}, + { XK_KP_8, ControlMask, "\033[184;5u", 0, 0}, + { XK_KP_8, ControlMask|ShiftMask, "\033[184;6u", 0, 0}, + { XK_KP_8, Mod1Mask, "\033[184;3u", 0, 0}, + { XK_KP_8, Mod1Mask|ControlMask, "\033[184;7u", 0, 0}, + { XK_KP_8, Mod1Mask|ControlMask|ShiftMask, "\033[184;8u", 0, 0}, + { XK_KP_8, Mod1Mask|ShiftMask, "\033[184;4u", 0, 0}, + { XK_KP_8, ShiftMask, "\033[184;2u", 0, 0}, + { XK_KP_9, XK_NO_MOD, "\033Oy", +2, 0}, + { XK_KP_9, ControlMask, "\033[185;5u", 0, 0}, + { XK_KP_9, ControlMask|ShiftMask, "\033[185;6u", 0, 0}, + { XK_KP_9, Mod1Mask, "\033[185;3u", 0, 0}, + { XK_KP_9, Mod1Mask|ControlMask, "\033[185;7u", 0, 0}, + { XK_KP_9, Mod1Mask|ControlMask|ShiftMask, "\033[185;8u", 0, 0}, + { XK_KP_9, Mod1Mask|ShiftMask, "\033[185;4u", 0, 0}, + { XK_KP_9, ShiftMask, "\033[185;2u", 0, 0}, + { XK_BackSpace, ControlMask, "\033[127;5u", 0, 0}, + { XK_BackSpace, ControlMask|ShiftMask, "\033[127;6u", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033[127;3u", 0, 0}, + { XK_BackSpace, Mod1Mask|ControlMask, "\033[127;7u", 0, 0}, + { XK_BackSpace, Mod1Mask|ControlMask|ShiftMask, "\033[127;8u", 0, 0}, + { XK_BackSpace, Mod1Mask|ShiftMask, "\033[127;4u", 0, 0}, + { XK_BackSpace, ShiftMask, "\033[127;2u", 0, 0}, + { XK_Tab, ControlMask, "\033[9;5u", 0, 0}, + { XK_Tab, ControlMask|ShiftMask, "\033[1;5Z", 0, 0}, + { XK_Tab, Mod1Mask, "\033[1;3Z", 0, 0}, + { XK_Tab, Mod1Mask|ControlMask, "\033[1;7Z", 0, 0}, + { XK_Tab, Mod1Mask|ControlMask|ShiftMask, "\033[1;8Z", 0, 0}, + { XK_Tab, Mod1Mask|ShiftMask, "\033[1;4Z", 0, 0}, + { XK_Return, ControlMask, "\033[13;5u", 0, 0}, + { XK_Return, ControlMask|ShiftMask, "\033[13;6u", 0, 0}, + { XK_Return, Mod1Mask, "\033[13;3u", 0, 0}, + { XK_Return, Mod1Mask|ControlMask, "\033[13;7u", 0, 0}, + { XK_Return, Mod1Mask|ControlMask|ShiftMask, "\033[13;8u", 0, 0}, + { XK_Return, Mod1Mask|ShiftMask, "\033[13;4u", 0, 0}, + { XK_Return, ShiftMask, "\033[13;2u", 0, 0}, + { XK_Pause, ControlMask, "\033[18;5u", 0, 0}, + { XK_Pause, ControlMask|ShiftMask, "\033[18;6u", 0, 0}, + { XK_Pause, Mod1Mask, "\033[18;3u", 0, 0}, + { XK_Pause, Mod1Mask|ControlMask, "\033[18;7u", 0, 0}, + { XK_Pause, Mod1Mask|ControlMask|ShiftMask, "\033[18;8u", 0, 0}, + { XK_Pause, Mod1Mask|ShiftMask, "\033[18;4u", 0, 0}, + { XK_Pause, ShiftMask, "\033[18;2u", 0, 0}, + { XK_Scroll_Lock, ControlMask, "\033[20;5u", 0, 0}, + { XK_Scroll_Lock, ControlMask|ShiftMask, "\033[20;6u", 0, 0}, + { XK_Scroll_Lock, Mod1Mask, "\033[20;3u", 0, 0}, + { XK_Scroll_Lock, Mod1Mask|ControlMask, "\033[20;7u", 0, 0}, + { XK_Scroll_Lock, Mod1Mask|ControlMask|ShiftMask, "\033[20;8u", 0, 0}, + { XK_Scroll_Lock, Mod1Mask|ShiftMask, "\033[20;4u", 0, 0}, + { XK_Scroll_Lock, ShiftMask, "\033[20;2u", 0, 0}, + { XK_Escape, ControlMask, "\033[27;5u", 0, 0}, + { XK_Escape, ControlMask|ShiftMask, "\033[27;6u", 0, 0}, + { XK_Escape, Mod1Mask, "\033[27;3u", 0, 0}, + { XK_Escape, Mod1Mask|ControlMask, "\033[27;7u", 0, 0}, + { XK_Escape, Mod1Mask|ControlMask|ShiftMask, "\033[27;8u", 0, 0}, + { XK_Escape, Mod1Mask|ShiftMask, "\033[27;4u", 0, 0}, + { XK_Escape, ShiftMask, "\033[27;2u", 0, 0}, + { XK_Home, XK_NO_MOD, "\033[H", 0, -1}, + { XK_Home, XK_NO_MOD, "\033[1~", 0, +1}, + { XK_Home, ControlMask|ShiftMask, "\033[80;6u", 0, 0}, + { XK_Home, Mod1Mask, "\033[80;3u", 0, 0}, + { XK_Home, Mod1Mask|ControlMask, "\033[80;7u", 0, 0}, + { XK_Home, Mod1Mask|ControlMask|ShiftMask, "\033[80;8u", 0, 0}, + { XK_Home, Mod1Mask|ShiftMask, "\033[80;4u", 0, 0}, + { XK_End, XK_NO_MOD, "\033[4~", 0, 0}, + { XK_End, ControlMask|ShiftMask, "\033[87;6u", 0, 0}, + { XK_End, Mod1Mask, "\033[87;3u", 0, 0}, + { XK_End, Mod1Mask|ControlMask, "\033[87;7u", 0, 0}, + { XK_End, Mod1Mask|ControlMask|ShiftMask, "\033[87;8u", 0, 0}, + { XK_End, Mod1Mask|ShiftMask, "\033[87;4u", 0, 0}, + { XK_Prior, XK_NO_MOD, "\033[5~", 0, 0}, + { XK_Prior, ControlMask|ShiftMask, "\033[85;6u", 0, 0}, + { XK_Prior, Mod1Mask, "\033[85;3u", 0, 0}, + { XK_Prior, Mod1Mask|ControlMask, "\033[85;7u", 0, 0}, + { XK_Prior, Mod1Mask|ControlMask|ShiftMask, "\033[85;8u", 0, 0}, + { XK_Prior, Mod1Mask|ShiftMask, "\033[85;4u", 0, 0}, + { XK_Next, XK_NO_MOD, "\033[6~", 0, 0}, + { XK_Next, ControlMask|ShiftMask, "\033[86;6u", 0, 0}, + { XK_Next, Mod1Mask, "\033[86;3u", 0, 0}, + { XK_Next, Mod1Mask|ControlMask, "\033[86;7u", 0, 0}, + { XK_Next, Mod1Mask|ControlMask|ShiftMask, "\033[86;8u", 0, 0}, + { XK_Next, Mod1Mask|ShiftMask, "\033[86;4u", 0, 0}, + { XK_Print, ControlMask, "\033[97;5u", 0, 0}, + { XK_Print, ControlMask|ShiftMask, "\033[97;6u", 0, 0}, + { XK_Print, Mod1Mask, "\033[97;3u", 0, 0}, + { XK_Print, Mod1Mask|ControlMask, "\033[97;7u", 0, 0}, + { XK_Print, Mod1Mask|ControlMask|ShiftMask, "\033[97;8u", 0, 0}, + { XK_Print, Mod1Mask|ShiftMask, "\033[97;4u", 0, 0}, + { XK_Print, ShiftMask, "\033[97;2u", 0, 0}, + { XK_Insert, XK_NO_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_NO_MOD, "\033[2~", +1, 0}, + { XK_Insert, ControlMask|ShiftMask, "\033[99;6u", 0, 0}, + { XK_Insert, Mod1Mask, "\033[99;3u", 0, 0}, + { XK_Insert, Mod1Mask|ControlMask, "\033[99;7u", 0, 0}, + { XK_Insert, Mod1Mask|ControlMask|ShiftMask, "\033[99;8u", 0, 0}, + { XK_Insert, Mod1Mask|ShiftMask, "\033[99;4u", 0, 0}, + { XK_Menu, ControlMask, "\033[103;5u", 0, 0}, + { XK_Menu, ControlMask|ShiftMask, "\033[103;6u", 0, 0}, + { XK_Menu, Mod1Mask, "\033[103;3u", 0, 0}, + { XK_Menu, Mod1Mask|ControlMask, "\033[103;7u", 0, 0}, + { XK_Menu, Mod1Mask|ControlMask|ShiftMask, "\033[103;8u", 0, 0}, + { XK_Menu, Mod1Mask|ShiftMask, "\033[103;4u", 0, 0}, + { XK_Menu, ShiftMask, "\033[103;2u", 0, 0}, + { XK_Delete, XK_NO_MOD, "\033[P", -1, 0}, + { XK_Delete, XK_NO_MOD, "\033[3~", +1, 0}, + { XK_Delete, ControlMask|ShiftMask, "\033[255;6u", 0, 0}, + { XK_Delete, Mod1Mask, "\033[255;3u", 0, 0}, + { XK_Delete, Mod1Mask|ControlMask, "\033[255;7u", 0, 0}, + { XK_Delete, Mod1Mask|ControlMask|ShiftMask, "\033[255;8u", 0, 0}, + { XK_Delete, Mod1Mask|ShiftMask, "\033[255;4u", 0, 0}, + { XK_i, ControlMask, "\033[105;5u", 0, 0}, + { XK_i, Mod1Mask|ControlMask, "\033[105;7u", 0, 0}, + { XK_m, ControlMask, "\033[109;5u", 0, 0}, + { XK_m, Mod1Mask|ControlMask, "\033[109;7u", 0, 0}, + { XK_space, ControlMask|ShiftMask, "\033[32;6u", 0, 0}, + { XK_space, Mod1Mask, "\033[32;3u", 0, 0}, + { XK_space, Mod1Mask|ControlMask, "\033[32;7u", 0, 0}, + { XK_space, Mod1Mask|ControlMask|ShiftMask, "\033[32;8u", 0, 0}, + { XK_space, Mod1Mask|ShiftMask, "\033[32;4u", 0, 0}, + { XK_space, ShiftMask, "\033[32;2u", 0, 0}, + { XK_0, ControlMask, "\033[48;5u", 0, 0}, + { XK_A, ControlMask|ShiftMask, "\033[65;6u", 0, 0}, + { XK_B, ControlMask|ShiftMask, "\033[66;6u", 0, 0}, + { XK_C, ControlMask|ShiftMask, "\033[67;6u", 0, 0}, + { XK_D, ControlMask|ShiftMask, "\033[68;6u", 0, 0}, + { XK_E, ControlMask|ShiftMask, "\033[69;6u", 0, 0}, + { XK_F, ControlMask|ShiftMask, "\033[70;6u", 0, 0}, + { XK_G, ControlMask|ShiftMask, "\033[71;6u", 0, 0}, + { XK_H, ControlMask|ShiftMask, "\033[72;6u", 0, 0}, + { XK_I, ControlMask|ShiftMask, "\033[73;6u", 0, 0}, + { XK_I, Mod1Mask|ControlMask|ShiftMask, "\033[73;8u", 0, 0}, + { XK_J, ControlMask|ShiftMask, "\033[75;6u", 0, 0}, + { XK_K, ControlMask|ShiftMask, "\033[74;6u", 0, 0}, + { XK_L, ControlMask|ShiftMask, "\033[76;6u", 0, 0}, + { XK_M, ControlMask|ShiftMask, "\033[77;6u", 0, 0}, + { XK_M, Mod1Mask|ControlMask|ShiftMask, "\033[77;8u", 0, 0}, + { XK_N, ControlMask|ShiftMask, "\033[78;6u", 0, 0}, + { XK_O, ControlMask|ShiftMask, "\033[79;6u", 0, 0}, + { XK_P, ControlMask|ShiftMask, "\033[80;6u", 0, 0}, + { XK_Q, ControlMask|ShiftMask, "\033[81;6u", 0, 0}, + { XK_R, ControlMask|ShiftMask, "\033[82;6u", 0, 0}, + { XK_S, ControlMask|ShiftMask, "\033[83;6u", 0, 0}, + { XK_T, ControlMask|ShiftMask, "\033[84;6u", 0, 0}, + { XK_U, ControlMask|ShiftMask, "\033[85;6u", 0, 0}, + { XK_V, ControlMask|ShiftMask, "\033[86;6u", 0, 0}, + { XK_W, ControlMask|ShiftMask, "\033[87;6u", 0, 0}, + { XK_X, ControlMask|ShiftMask, "\033[88;6u", 0, 0}, + { XK_Y, ControlMask|ShiftMask, "\033[89;6u", 0, 0}, + { XK_Z, ControlMask|ShiftMask, "\033[90;6u", 0, 0}, + { XK_Z, ControlMask|ShiftMask, "\033[90;6u", 0, 0}, + { XK_0, Mod1Mask|ControlMask, "\033[48;7u", 0, 0}, + { XK_1, ControlMask, "\033[49;5u", 0, 0}, + { XK_1, Mod1Mask|ControlMask, "\033[49;7u", 0, 0}, + { XK_2, ControlMask, "\033[50;5u", 0, 0}, + { XK_2, Mod1Mask|ControlMask, "\033[50;7u", 0, 0}, + { XK_3, ControlMask, "\033[51;5u", 0, 0}, + { XK_3, Mod1Mask|ControlMask, "\033[51;7u", 0, 0}, + { XK_4, ControlMask, "\033[52;5u", 0, 0}, + { XK_4, Mod1Mask|ControlMask, "\033[52;7u", 0, 0}, + { XK_5, ControlMask, "\033[53;5u", 0, 0}, + { XK_5, Mod1Mask|ControlMask, "\033[53;7u", 0, 0}, + { XK_6, ControlMask, "\033[54;5u", 0, 0}, + { XK_6, Mod1Mask|ControlMask, "\033[54;7u", 0, 0}, + { XK_7, ControlMask, "\033[55;5u", 0, 0}, + { XK_7, Mod1Mask|ControlMask, "\033[55;7u", 0, 0}, + { XK_8, ControlMask, "\033[56;5u", 0, 0}, + { XK_8, Mod1Mask|ControlMask, "\033[56;7u", 0, 0}, + { XK_9, ControlMask, "\033[57;5u", 0, 0}, + { XK_9, Mod1Mask|ControlMask, "\033[57;7u", 0, 0}, + { XK_ampersand, ControlMask, "\033[38;5u", 0, 0}, + { XK_ampersand, ControlMask|ShiftMask, "\033[38;6u", 0, 0}, + { XK_ampersand, Mod1Mask, "\033[38;3u", 0, 0}, + { XK_ampersand, Mod1Mask|ControlMask, "\033[38;7u", 0, 0}, + { XK_ampersand, Mod1Mask|ControlMask|ShiftMask, "\033[38;8u", 0, 0}, + { XK_ampersand, Mod1Mask|ShiftMask, "\033[38;4u", 0, 0}, + { XK_apostrophe, ControlMask, "\033[39;5u", 0, 0}, + { XK_apostrophe, ControlMask|ShiftMask, "\033[39;6u", 0, 0}, + { XK_apostrophe, Mod1Mask, "\033[39;3u", 0, 0}, + { XK_apostrophe, Mod1Mask|ControlMask, "\033[39;7u", 0, 0}, + { XK_apostrophe, Mod1Mask|ControlMask|ShiftMask, "\033[39;8u", 0, 0}, + { XK_apostrophe, Mod1Mask|ShiftMask, "\033[39;4u", 0, 0}, + { XK_asciicircum, ControlMask, "\033[94;5u", 0, 0}, + { XK_asciicircum, ControlMask|ShiftMask, "\033[94;6u", 0, 0}, + { XK_asciicircum, Mod1Mask, "\033[94;3u", 0, 0}, + { XK_asciicircum, Mod1Mask|ControlMask, "\033[94;7u", 0, 0}, + { XK_asciicircum, Mod1Mask|ControlMask|ShiftMask, "\033[94;8u", 0, 0}, + { XK_asciicircum, Mod1Mask|ShiftMask, "\033[94;4u", 0, 0}, + { XK_asciitilde, ControlMask, "\033[126;5u", 0, 0}, + { XK_asciitilde, ControlMask|ShiftMask, "\033[126;6u", 0, 0}, + { XK_asciitilde, Mod1Mask, "\033[126;3u", 0, 0}, + { XK_asciitilde, Mod1Mask|ControlMask, "\033[126;7u", 0, 0}, + { XK_asciitilde, Mod1Mask|ControlMask|ShiftMask, "\033[126;8u", 0, 0}, + { XK_asciitilde, Mod1Mask|ShiftMask, "\033[126;4u", 0, 0}, + { XK_asterisk, ControlMask, "\033[42;5u", 0, 0}, + { XK_asterisk, ControlMask|ShiftMask, "\033[42;6u", 0, 0}, + { XK_asterisk, Mod1Mask, "\033[42;3u", 0, 0}, + { XK_asterisk, Mod1Mask|ControlMask, "\033[42;7u", 0, 0}, + { XK_asterisk, Mod1Mask|ControlMask|ShiftMask, "\033[42;8u", 0, 0}, + { XK_asterisk, Mod1Mask|ShiftMask, "\033[42;4u", 0, 0}, + { XK_at, ControlMask, "\033[64;5u", 0, 0}, + { XK_at, ControlMask|ShiftMask, "\033[64;6u", 0, 0}, + { XK_at, Mod1Mask, "\033[64;3u", 0, 0}, + { XK_at, Mod1Mask|ControlMask, "\033[64;7u", 0, 0}, + { XK_at, Mod1Mask|ControlMask|ShiftMask, "\033[64;8u", 0, 0}, + { XK_at, Mod1Mask|ShiftMask, "\033[64;4u", 0, 0}, + { XK_backslash, ControlMask, "\033[92;5u", 0, 0}, + { XK_backslash, ControlMask|ShiftMask, "\033[92;6u", 0, 0}, + { XK_backslash, Mod1Mask, "\033[92;3u", 0, 0}, + { XK_backslash, Mod1Mask|ControlMask, "\033[92;7u", 0, 0}, + { XK_backslash, Mod1Mask|ControlMask|ShiftMask, "\033[92;8u", 0, 0}, + { XK_backslash, Mod1Mask|ShiftMask, "\033[92;4u", 0, 0}, + { XK_bar, ControlMask, "\033[124;5u", 0, 0}, + { XK_bar, ControlMask|ShiftMask, "\033[124;6u", 0, 0}, + { XK_bar, Mod1Mask, "\033[124;3u", 0, 0}, + { XK_bar, Mod1Mask|ControlMask, "\033[124;7u", 0, 0}, + { XK_bar, Mod1Mask|ControlMask|ShiftMask, "\033[124;8u", 0, 0}, + { XK_bar, Mod1Mask|ShiftMask, "\033[124;4u", 0, 0}, + { XK_braceleft, ControlMask, "\033[123;5u", 0, 0}, + { XK_braceleft, ControlMask|ShiftMask, "\033[123;6u", 0, 0}, + { XK_braceleft, Mod1Mask, "\033[123;3u", 0, 0}, + { XK_braceleft, Mod1Mask|ControlMask, "\033[123;7u", 0, 0}, + { XK_braceleft, Mod1Mask|ControlMask|ShiftMask, "\033[123;8u", 0, 0}, + { XK_braceleft, Mod1Mask|ShiftMask, "\033[123;4u", 0, 0}, + { XK_braceright, ControlMask, "\033[125;5u", 0, 0}, + { XK_braceright, ControlMask|ShiftMask, "\033[125;6u", 0, 0}, + { XK_braceright, Mod1Mask, "\033[125;3u", 0, 0}, + { XK_braceright, Mod1Mask|ControlMask, "\033[125;7u", 0, 0}, + { XK_braceright, Mod1Mask|ControlMask|ShiftMask, "\033[125;8u", 0, 0}, + { XK_braceright, Mod1Mask|ShiftMask, "\033[125;4u", 0, 0}, + { XK_bracketleft, ControlMask, "\033[91;5u", 0, 0}, + { XK_bracketleft, ControlMask|ShiftMask, "\033[91;6u", 0, 0}, + { XK_bracketleft, Mod1Mask, "\033[91;3u", 0, 0}, + { XK_bracketleft, Mod1Mask|ControlMask, "\033[91;7u", 0, 0}, + { XK_bracketleft, Mod1Mask|ControlMask|ShiftMask, "\033[91;8u", 0, 0}, + { XK_bracketleft, Mod1Mask|ShiftMask, "\033[91;4u", 0, 0}, + { XK_bracketright, ControlMask, "\033[93;5u", 0, 0}, + { XK_bracketright, ControlMask|ShiftMask, "\033[93;6u", 0, 0}, + { XK_bracketright, Mod1Mask, "\033[93;3u", 0, 0}, + { XK_bracketright, Mod1Mask|ControlMask, "\033[93;7u", 0, 0}, + { XK_bracketright, Mod1Mask|ControlMask|ShiftMask, "\033[93;8u", 0, 0}, + { XK_bracketright, Mod1Mask|ShiftMask, "\033[93;4u", 0, 0}, + { XK_colon, ControlMask, "\033[58;5u", 0, 0}, + { XK_colon, ControlMask|ShiftMask, "\033[58;6u", 0, 0}, + { XK_colon, Mod1Mask, "\033[58;3u", 0, 0}, + { XK_colon, Mod1Mask|ControlMask, "\033[58;7u", 0, 0}, + { XK_colon, Mod1Mask|ControlMask|ShiftMask, "\033[58;8u", 0, 0}, + { XK_colon, Mod1Mask|ShiftMask, "\033[58;4u", 0, 0}, + { XK_comma, ControlMask, "\033[44;5u", 0, 0}, + { XK_comma, ControlMask|ShiftMask, "\033[44;6u", 0, 0}, + { XK_comma, Mod1Mask, "\033[44;3u", 0, 0}, + { XK_comma, Mod1Mask|ControlMask, "\033[44;7u", 0, 0}, + { XK_comma, Mod1Mask|ControlMask|ShiftMask, "\033[44;8u", 0, 0}, + { XK_comma, Mod1Mask|ShiftMask, "\033[44;4u", 0, 0}, + { XK_dollar, ControlMask, "\033[36;5u", 0, 0}, + { XK_dollar, ControlMask|ShiftMask, "\033[36;6u", 0, 0}, + { XK_dollar, Mod1Mask, "\033[36;3u", 0, 0}, + { XK_dollar, Mod1Mask|ControlMask, "\033[36;7u", 0, 0}, + { XK_dollar, Mod1Mask|ControlMask|ShiftMask, "\033[36;8u", 0, 0}, + { XK_dollar, Mod1Mask|ShiftMask, "\033[36;4u", 0, 0}, + { XK_equal, ControlMask, "\033[61;5u", 0, 0}, + { XK_equal, ControlMask|ShiftMask, "\033[61;6u", 0, 0}, + { XK_equal, Mod1Mask, "\033[61;3u", 0, 0}, + { XK_equal, Mod1Mask|ControlMask, "\033[61;7u", 0, 0}, + { XK_equal, Mod1Mask|ControlMask|ShiftMask, "\033[61;8u", 0, 0}, + { XK_equal, Mod1Mask|ShiftMask, "\033[61;4u", 0, 0}, + { XK_exclam, ControlMask, "\033[33;5u", 0, 0}, + { XK_exclam, ControlMask|ShiftMask, "\033[33;6u", 0, 0}, + { XK_exclam, Mod1Mask, "\033[33;3u", 0, 0}, + { XK_exclam, Mod1Mask|ControlMask, "\033[33;7u", 0, 0}, + { XK_exclam, Mod1Mask|ControlMask|ShiftMask, "\033[33;8u", 0, 0}, + { XK_exclam, Mod1Mask|ShiftMask, "\033[33;4u", 0, 0}, + { XK_grave, ControlMask, "\033[96;5u", 0, 0}, + { XK_grave, ControlMask|ShiftMask, "\033[96;6u", 0, 0}, + { XK_grave, Mod1Mask, "\033[96;3u", 0, 0}, + { XK_grave, Mod1Mask|ControlMask, "\033[96;7u", 0, 0}, + { XK_grave, Mod1Mask|ControlMask|ShiftMask, "\033[96;8u", 0, 0}, + { XK_grave, Mod1Mask|ShiftMask, "\033[96;4u", 0, 0}, + { XK_greater, ControlMask, "\033[62;5u", 0, 0}, + { XK_greater, ControlMask|ShiftMask, "\033[62;6u", 0, 0}, + { XK_greater, Mod1Mask, "\033[62;3u", 0, 0}, + { XK_greater, Mod1Mask|ControlMask, "\033[62;7u", 0, 0}, + { XK_greater, Mod1Mask|ControlMask|ShiftMask, "\033[62;8u", 0, 0}, + { XK_greater, Mod1Mask|ShiftMask, "\033[62;4u", 0, 0}, + { XK_less, ControlMask, "\033[60;5u", 0, 0}, + { XK_less, ControlMask|ShiftMask, "\033[60;6u", 0, 0}, + { XK_less, Mod1Mask, "\033[60;3u", 0, 0}, + { XK_less, Mod1Mask|ControlMask, "\033[60;7u", 0, 0}, + { XK_less, Mod1Mask|ControlMask|ShiftMask, "\033[60;8u", 0, 0}, + { XK_less, Mod1Mask|ShiftMask, "\033[60;4u", 0, 0}, + { XK_minus, ControlMask, "\033[45;5u", 0, 0}, + { XK_minus, ControlMask|ShiftMask, "\033[45;6u", 0, 0}, + { XK_minus, Mod1Mask, "\033[45;3u", 0, 0}, + { XK_minus, Mod1Mask|ControlMask, "\033[45;7u", 0, 0}, + { XK_minus, Mod1Mask|ControlMask|ShiftMask, "\033[45;8u", 0, 0}, + { XK_minus, Mod1Mask|ShiftMask, "\033[45;4u", 0, 0}, + { XK_numbersign, ControlMask, "\033[35;5u", 0, 0}, + { XK_numbersign, ControlMask|ShiftMask, "\033[35;6u", 0, 0}, + { XK_numbersign, Mod1Mask, "\033[35;3u", 0, 0}, + { XK_numbersign, Mod1Mask|ControlMask, "\033[35;7u", 0, 0}, + { XK_numbersign, Mod1Mask|ControlMask|ShiftMask, "\033[35;8u", 0, 0}, + { XK_numbersign, Mod1Mask|ShiftMask, "\033[35;4u", 0, 0}, + { XK_parenleft, ControlMask, "\033[40;5u", 0, 0}, + { XK_parenleft, ControlMask|ShiftMask, "\033[40;6u", 0, 0}, + { XK_parenleft, Mod1Mask, "\033[40;3u", 0, 0}, + { XK_parenleft, Mod1Mask|ControlMask, "\033[40;7u", 0, 0}, + { XK_parenleft, Mod1Mask|ControlMask|ShiftMask, "\033[40;8u", 0, 0}, + { XK_parenleft, Mod1Mask|ShiftMask, "\033[40;4u", 0, 0}, + { XK_parenright, ControlMask, "\033[41;5u", 0, 0}, + { XK_parenright, ControlMask|ShiftMask, "\033[41;6u", 0, 0}, + { XK_parenright, Mod1Mask, "\033[41;3u", 0, 0}, + { XK_parenright, Mod1Mask|ControlMask, "\033[41;7u", 0, 0}, + { XK_parenright, Mod1Mask|ControlMask|ShiftMask, "\033[41;8u", 0, 0}, + { XK_parenright, Mod1Mask|ShiftMask, "\033[41;4u", 0, 0}, + { XK_percent, ControlMask, "\033[37;5u", 0, 0}, + { XK_percent, ControlMask|ShiftMask, "\033[37;6u", 0, 0}, + { XK_percent, Mod1Mask, "\033[37;3u", 0, 0}, + { XK_percent, Mod1Mask|ControlMask, "\033[37;7u", 0, 0}, + { XK_percent, Mod1Mask|ControlMask|ShiftMask, "\033[37;8u", 0, 0}, + { XK_percent, Mod1Mask|ShiftMask, "\033[37;4u", 0, 0}, + { XK_period, ControlMask, "\033[46;5u", 0, 0}, + { XK_period, ControlMask|ShiftMask, "\033[46;6u", 0, 0}, + { XK_period, Mod1Mask|ControlMask, "\033[46;7u", 0, 0}, + { XK_period, Mod1Mask|ControlMask|ShiftMask, "\033[46;8u", 0, 0}, + { XK_period, Mod1Mask|ShiftMask, "\033[46;4u", 0, 0}, + { XK_plus, ControlMask, "\033[43;5u", 0, 0}, + { XK_plus, ControlMask|ShiftMask, "\033[43;6u", 0, 0}, + { XK_plus, Mod1Mask, "\033[43;3u", 0, 0}, + { XK_plus, Mod1Mask|ControlMask, "\033[43;7u", 0, 0}, + { XK_plus, Mod1Mask|ControlMask|ShiftMask, "\033[43;8u", 0, 0}, + { XK_plus, Mod1Mask|ShiftMask, "\033[43;4u", 0, 0}, + { XK_question, ControlMask, "\033[63;5u", 0, 0}, + { XK_question, ControlMask|ShiftMask, "\033[63;6u", 0, 0}, + { XK_question, Mod1Mask, "\033[63;3u", 0, 0}, + { XK_question, Mod1Mask|ControlMask, "\033[63;7u", 0, 0}, + { XK_question, Mod1Mask|ControlMask|ShiftMask, "\033[63;8u", 0, 0}, + { XK_question, Mod1Mask|ShiftMask, "\033[63;4u", 0, 0}, + { XK_quotedbl, ControlMask, "\033[34;5u", 0, 0}, + { XK_quotedbl, ControlMask|ShiftMask, "\033[34;6u", 0, 0}, + { XK_quotedbl, Mod1Mask, "\033[34;3u", 0, 0}, + { XK_quotedbl, Mod1Mask|ControlMask, "\033[34;7u", 0, 0}, + { XK_quotedbl, Mod1Mask|ControlMask|ShiftMask, "\033[34;8u", 0, 0}, + { XK_quotedbl, Mod1Mask|ShiftMask, "\033[34;4u", 0, 0}, + { XK_semicolon, ControlMask, "\033[59;5u", 0, 0}, + { XK_semicolon, ControlMask|ShiftMask, "\033[59;6u", 0, 0}, + { XK_semicolon, Mod1Mask, "\033[59;3u", 0, 0}, + { XK_semicolon, Mod1Mask|ControlMask, "\033[59;7u", 0, 0}, + { XK_semicolon, Mod1Mask|ControlMask|ShiftMask, "\033[59;8u", 0, 0}, + { XK_semicolon, Mod1Mask|ShiftMask, "\033[59;4u", 0, 0}, + { XK_slash, ControlMask|ShiftMask, "\033[47;6u", 0, 0}, + { XK_slash, Mod1Mask, "\033[47;3u", 0, 0}, + { XK_slash, Mod1Mask|ControlMask, "\033[47;7u", 0, 0}, + { XK_slash, Mod1Mask|ControlMask|ShiftMask, "\033[47;8u", 0, 0}, + { XK_slash, Mod1Mask|ShiftMask, "\033[47;4u", 0, 0}, + { XK_underscore, ControlMask, "\033[95;5u", 0, 0}, + { XK_underscore, ControlMask|ShiftMask, "\033[95;6u", 0, 0}, + { XK_underscore, Mod1Mask, "\033[95;3u", 0, 0}, + { XK_underscore, Mod1Mask|ControlMask, "\033[95;7u", 0, 0}, + { XK_underscore, Mod1Mask|ControlMask|ShiftMask, "\033[95;8u", 0, 0}, + { XK_underscore, Mod1Mask|ShiftMask, "\033[95;4u", 0, 0}, }; /* @@ -464,26 +1135,5 @@ static char ascii_printable[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" "`abcdefghijklmnopqrstuvwxyz{|}~"; -/** - * Undercurl style. Set UNDERCURL_STYLE to one of the available styles. - * - * Curly: Dunno how to draw it *shrug* - * _ _ _ _ - * ( ) ( ) ( ) ( ) - * (_) (_) (_) (_) - * - * Spiky: - * /\ /\ /\ /\ - * \/ \/ \/ - * - * Capped: - * _ _ _ - * / \ / \ / \ - * \_/ \_/ - */ -// Available styles -#define UNDERCURL_CURLY 0 -#define UNDERCURL_SPIKY 1 -#define UNDERCURL_CAPPED 2 -// Active style -#define UNDERCURL_STYLE UNDERCURL_SPIKY +/* URL Opener */ +static char *url_opener = "xdg-open"; diff --git a/config.mk b/config.mk index 5ff50bb..d1ce164 100644 --- a/config.mk +++ b/config.mk @@ -4,11 +4,11 @@ VERSION = 0.8.5 # Customize below to fit your system # paths -PREFIX = /usr +PREFIX = /usr/local +APPPREFIX = $(PREFIX)/share/applications MANPREFIX = $(PREFIX)/share/man ICONPREFIX = $(PREFIX)/share/pixmaps ICONNAME = st.png -APPPREFIX = $(PREFIX)/share/applications X11INC = /usr/X11R6/include X11LIB = /usr/X11R6/lib @@ -17,12 +17,11 @@ PKG_CONFIG = pkg-config # includes and libs INCS = -I$(X11INC) \ - `$(PKG_CONFIG) --cflags glib-2.0` \ `$(PKG_CONFIG) --cflags fontconfig` \ `$(PKG_CONFIG) --cflags freetype2` \ `$(PKG_CONFIG) --cflags harfbuzz` -LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender -lgd \ - `$(PKG_CONFIG) --libs glib-2.0` \ + +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lgd \ `$(PKG_CONFIG) --libs fontconfig` \ `$(PKG_CONFIG) --libs freetype2` \ `$(PKG_CONFIG) --libs harfbuzz` @@ -37,6 +36,7 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ # `$(PKG_CONFIG) --libs fontconfig` \ # `$(PKG_CONFIG) --libs freetype2` +#MANPREFIX = ${PREFIX}/man # compiler and linker # CC = c99 diff --git a/docs/bindlist b/docs/bindlist deleted file mode 100644 index 857b37b..0000000 --- a/docs/bindlist +++ /dev/null @@ -1,17 +0,0 @@ -CTRL+y | Copy to the clipboard -CTRL+p | Paste from the clipboard -CTRL+= | Increase font size -CTRL+- | Decrease font size -CTRL+0 | Reset font size to default -CTRL+j | Scroll down -CTRL+k | Scroll up -CTRL+Shift+k| Copy the last URL -CTRL+Enter | Opens a new terminal window in the same directory -Scroll+up | Scroll up (Mouse scrollback support) -Scroll+down | Scroll down (Mouse scrollback support) -Shift+Esc | Select mode - -If you're using my zsh config: -I | Enter Insert mode -Esc | Enter Normal mode -Tab | Autocomplete diff --git a/docs/example.Xresources b/docs/example.Xresources deleted file mode 100644 index 6f7254d..0000000 --- a/docs/example.Xresources +++ /dev/null @@ -1,33 +0,0 @@ -!! st resources -! special -*.foreground: #e7e7e7 -*.background: #202020 -*.cursorColor: #e7e7e7 -! black -*.color0: #262626 -*.color8: #737373 -! red -*.color1: #ff3534 -*.color9: #ff4f4c -! green -*.color2: #35b723 -*.color10: #82ff6e -! yellow -*.color3: #eae804 -*.color11: #dfff52 -! blue -*.color4: #362be4 -*.color12: #5767ff -! magenta -*.color5: #fa9cf8 -*.color13: #cf89cf -! cyan -*.color6: #4ed4d4 -*.color14: #68efef -! white -*.color7: #bebebe -*.color15: #f7f5f5 -! alpha -*.alpha: 0.9 -! font -*.font: DejaVu Sans Mono:pixelsize=12:antialias=true:autohint=true; diff --git a/docs/keybinds b/docs/keybinds deleted file mode 100644 index 4eb3150..0000000 --- a/docs/keybinds +++ /dev/null @@ -1,23 +0,0 @@ -Here's an (unfinished) list of keybinds my build of st comes with -NOTE: May me slightly out of date. -For more information, read the 'man' page. - -CTRL+y | Copy (yank) to the clipboard -CTRL+p | Paste from the clipboard -CTRL+= | Increase font size -CTRL+- | Decrease font size -CTRL+0 | Reset font size to default -CTRL+j | Scroll down -CTRL+k | Scroll up -CTRL+Enter | Opens a new terminal window in the same directory -Scroll+up | Scroll up (Mouse scrollback support) -Scroll+down | Scroll down (Mouse scrollback support) - -If you're using my zsh config (szsh): -I | Enter Insert mode -Esc | Enter Normal mode -Tab | Autocomplete - -If you're using my build of dwm: -Alt+Shift+e | Open emoji picker -Alt+Shift+c | Open copyout selection diff --git a/docs/patchlist b/docs/patchlist deleted file mode 100644 index 6d0e672..0000000 --- a/docs/patchlist +++ /dev/null @@ -1,28 +0,0 @@ -st-alpha -st-anysize -st-autocomplete -st-bold-is-not-bright -st-boxdraw -st-charoffsets -st-clipboard -st-delkey -st-externalpipe -st-font2 -st-fontfix -st-glyph-wide-support-boxdraw -st-ligatures-alpha-scrollback -st-newterm -st-nobadweight -st-scrollback -st-scrollback-mouse -st-spoiler -st-w3m -st-xclearwin -st-xresources -st-keyboard-select -st-undercurl -st-csi -st-copyurl -st-dynamic-cursor-color -st-desktopentry -st-netwmicon diff --git a/docs/st-keybinds b/docs/st-keybinds deleted file mode 100644 index 10dde77..0000000 --- a/docs/st-keybinds +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -# Simple script which allows you to read all st keybinds - -less /usr/bin/stbindlist diff --git a/hb.c b/hb.c index 4846d22..9ae6c9f 100644 --- a/hb.c +++ b/hb.c @@ -21,7 +21,8 @@ static int hbfontslen = 0; static HbFontMatch *hbfontcache = NULL; /* - * Replace 0 with a list of font features, wrapped in FEATURE macro, e.g. + * Poplulate the array with a list of font features, wrapped in FEATURE macro, + * e. g. * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') */ hb_feature_t features[] = { 0 }; @@ -96,6 +97,10 @@ hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, int x, int for (int i = 0, specidx = 0; i < len; i++) { if (glyphs[i].mode & ATTR_WDUMMY) continue; + if (glyphs[i].mode & ATTR_BOXDRAW) { + specidx++; + continue; + } if (codepoints[i] != specs[specidx].glyph) ((Glyph *)glyphs)[i].mode |= ATTR_LIGA; @@ -128,7 +133,7 @@ hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoin } /* Shape the segment. */ - hb_shape(font, buffer, features, sizeof(features)); + hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t)); /* Get new glyph info. */ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL); diff --git a/st b/st index 85e7da244504609840ee2816c8013780a032d492..d0068c9817ae2c221e33315f3eb7070d156e3203 100755 GIT binary patch literal 165352 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 literal 134752 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 LhFS@%OZC41(-`0` diff --git a/st-autocomplete b/st-autocomplete deleted file mode 100644 index cf724c8..0000000 --- a/st-autocomplete +++ /dev/null @@ -1,300 +0,0 @@ -#!/usr/bin/perl -######################################################################### -# Copyright (C) 2012-2021 Wojciech Siewierski, Gaspar Vardanyan # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -######################################################################### - -my ($cmd, $cursor_row, $cursor_column) = @ARGV; - -# A reference to a function that transforms the completed word -# into a regex matching the completions. Usually generated by -# generate_matcher(). -# -# For example -# $fun = generate_matcher(".*"); -# $fun->("foo"); -# would return "f.*o.*o" -# -# In other words, indirectly decides which characters can -# appear in the completion. -my $matcher; - -# A regular expression matching a character before each match. -# For example, it you want to match the text after a -# whitespace, set it to "\s". -my $char_class_before; - -# A regular expression matching every character in the entered -# text that will be used to find matching completions. Usually -# "\w" or similar. -my $char_class_to_complete; - -# A regular expression matching every allowed last character -# of the completion (uses greedy matching). -my $char_class_at_end; - -if ($cmd eq 'word-complete') { - # Basic word completion. Completes the current word - # without any special matching. - $char_class_before = '[^-\w]'; - $matcher = sub { quotemeta shift }; # identity - $char_class_at_end = '[-\w]'; - $char_class_to_complete = '[-\w]'; -} elsif ($cmd eq 'WORD-complete') { - # The same as above but in the Vim meaning of a "WORD" -- - # whitespace delimited. - $char_class_before = '\s'; - $matcher = sub { quotemeta shift }; - $char_class_at_end = '\S'; - $char_class_to_complete = '\S'; -} elsif ($cmd eq 'fuzzy-word-complete' || - $cmd eq 'skeleton-word-complete') { - # Fuzzy completion of the current word. - $char_class_before = '[^-\w]'; - $matcher = generate_matcher('[-\w]*'); - $char_class_at_end = '[-\w]'; - $char_class_to_complete = '[-\w]'; -} elsif ($cmd eq 'fuzzy-WORD-complete') { - # Fuzzy completion of the current WORD. - $char_class_before = '\s'; - $matcher = generate_matcher('\S*'); - $char_class_at_end = '\S'; - $char_class_to_complete = '\S'; -} elsif ($cmd eq 'fuzzy-complete' || - $cmd eq 'skeleton-complete') { - # Fuzzy completion of an arbitrary text. - $char_class_before = '\W'; - $matcher = generate_matcher('.*?'); - $char_class_at_end = '\w'; - $char_class_to_complete = '\S'; -} elsif ($cmd eq 'suffix-complete') { - # Fuzzy completion of an completing suffixes, like - # completing test=hello from /blah/hello. - $char_class_before = '\S'; - $matcher = generate_matcher('\S*'); - $char_class_at_end = '\S'; - $char_class_to_complete = '\S'; -} elsif ($cmd eq 'surround-complete') { - # Completing contents of quotes and braces. - - # Here we are using three named groups: s, b, p for quotes, braces - # and parenthesis. - $char_class_before = '((?["\'`])|(?\[)|(?

\())'; - - $matcher = generate_matcher('.*?'); - - # Here we match text till enclosing pair, using perl conditionals in - # regexps (?(condition)yes-expression|no-expression). - # \0 is used to hack concatenation with '*' later in the code. - $char_class_at_end = '.*?(.(?=(?()\]|((?(

)\)|\g{q})))))\0'; - $char_class_to_complete = '\S'; -} - -my $lines = []; - -my $last_line = -1; -my $lines_after_cursor = 0; - -while () -{ - $last_line++; - - if ($last_line <= $cursor_row) - { - push @{$lines}, $_; - } - else - { - unshift @{$lines}, $_; - $lines_after_cursor++; - } -} - -$cursor_row = $last_line; - -# read the word behind the cursor -$_ = substr(@{$lines} [$cursor_row], 0, $cursor_column); # get the current line up to the cursor... -s/.*?($char_class_to_complete*)$/$1/; # ...and read the last word from it -my $word_to_complete = quotemeta; - -# ignore the completed word itself -$self->{already_completed}{$word_to_complete} = 1; - -print stdout "$word_to_complete\n"; - -# search for matches -while (my $completion = find_match($self, - $word_to_complete, - $self->{next_row} // $cursor_row, - $matcher->($word_to_complete), - $char_class_before, - $char_class_at_end) -) { - calc_match_coords($self, - $self->{next_row}+1, - $completion); - print stdout "$completion @{$self->{highlight}}\n"; -} - -leave($self); - - - -###################################################################### - -# Finds the next matching completion in the row current row or above -# while skipping duplicates using skip_duplicates(). -sub find_match { - my ($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end) = @_; - $self->{matches_in_row} //= []; - - # cycle through all the matches in the current row if not starting a new search - if (@{$self->{matches_in_row}}) { - return skip_duplicates($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end); - } - - - my $i; - # search through all the rows starting with current one or one above the last checked - for ($i = $current_row; $i >= 0; --$i) { - my $line = @{$lines} [$i]; # get the line of text from the row - - if ($i == $cursor_row) { - $line = substr $line, 0, $cursor_column; - } - - $_ = $line; - - # find all the matches in the current line - my $match; - push @{$self->{matches_in_row}}, $+{match} while ($_, $match) = / - (.*${char_class_before}) - (? - ${regexp} - ${char_class_at_end}* - ) - /ix; - # corner case: match at the very beginning of line - push @{$self->{matches_in_row}}, $+{match} if $line =~ /^(${char_class_before}){0}(?$regexp$char_class_at_end*)/i; - - if (@{$self->{matches_in_row}}) { - # remember which row should be searched next - $self->{next_row} = --$i; - - # arguments needed for find_match() mutual recursion - return skip_duplicates($self, $word_to_match, $i, $regexp, $char_class_before, $char_class_at_end); - } - } - - # no more possible completions, revert to the original word - $self->{next_row} = -1 if $i < 0; - - return undef; -} - -###################################################################### - -# Checks whether the completion found by find_match() was already -# found and if it was, calls find_match() again to find the next -# completion. -# -# Takes all the arguments that find_match() would take, to make a -# mutually recursive call. -sub skip_duplicates { - my $self = $_[0]; - my $current_row = $_[2]; - my $completion; - if ($current_row >= $lines_after_cursor) - { - $completion = shift @{$self->{matches_in_row}}; # get the rightmost one - } - else - { - $completion = pop @{$self->{matches_in_row}}; # get the rightmost one - } - - # check for duplicates - if (exists $self->{already_completed}{$completion}) { - # skip this completion - return find_match(@_); - } else { - $self->{already_completed}{$completion} = 1; - return $completion; - } -} - -###################################################################### - -# Returns a function that takes a string and returns that string with -# this function's argument inserted between its every two characters. -# The resulting string is used as a regular expression matching the -# completion candidates. -sub generate_matcher { - my $regex_between = shift; - - sub { - $_ = shift; - - # sorry for this lispy code, I couldn't resist ;) - (join "$regex_between", - (map quotemeta, - (split //))) - } -} - -###################################################################### - -sub calc_match_coords { - my ($self, $linenum, $completion) = @_; - - my $line = @{$lines} [$linenum]; - my $re = quotemeta $completion; - - $line =~ /$re/; - - #my ($beg_row, $beg_col) = $line->coord_of($-[0]); - #my ($end_row, $end_col) = $line->coord_of($+[0]); - my $beg = $-[0]; - my $end = $+[0]; - - if (exists $self->{highlight}) { - delete $self->{highlight}; - } - # () # TODO: what does () do in perl ???? - - if ($linenum >= $lines_after_cursor) - { - $linenum -= $lines_after_cursor; - } - else - { - $linenum = $last_line - $linenum; - } - - # ACMPL_ISSUE: multi-line completions don't work - # $self->{highlight} = [$beg_row, $beg_col, $end_row, $end_col]; - $self->{highlight} = [$linenum, $beg, $end]; -} - -###################################################################### - -sub leave { - my ($self) = @_; - - delete $self->{next_row}; - delete $self->{matches_in_row}; - delete $self->{already_completed}; - delete $self->{highlight}; -} diff --git a/st.c b/st.c index 4b15b71..9b0eebe 100644 --- a/st.c +++ b/st.c @@ -16,10 +16,7 @@ #include #include #include -#include -#include -#include "autocomplete.h" #include "st.h" #include "win.h" @@ -40,6 +37,7 @@ #define STR_BUF_SIZ ESC_BUF_SIZ #define STR_ARG_SIZ ESC_ARG_SIZ #define HISTSIZE 2000 +#define RESIZEBUFFER 1000 /* macros */ #define IS_SET(flag) ((term.mode & (flag)) != 0) @@ -47,11 +45,24 @@ #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) #define ISDELIM(u) (u && wcschr(worddelimiters, u)) -#define STRESCARGREST(n) ((n) == 0 ? strescseq.buf : strescseq.argp[(n)-1] + 1) -#define STRESCARGJUST(n) (*(strescseq.argp[n]) = '\0', STRESCARGREST(n)) -#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ - term.scr + HISTSIZE + 1) % HISTSIZE] : \ - term.line[(y) - term.scr]) +#define STRESCARGREST(n) ((n) == 0 ? strescseq.buf : strescseq.args[(n)-1] + 1) +#define STRESCARGJUST(n) (*(strescseq.args[n]) = '\0', STRESCARGREST(n)) + +#define TLINE(y) ( \ + (y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + HISTSIZE) % HISTSIZE] \ + : term.line[(y) - term.scr] \ +) + +#define TLINEABS(y) ( \ + (y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZE] : term.line[(y)] \ +) + +#define UPDATEWRAPNEXT(alt, col) do { \ + if ((term.c.state & CURSOR_WRAPNEXT) && term.c.x + term.wrapcwidth[alt] < col) { \ + term.c.x += term.wrapcwidth[alt]; \ + term.c.state &= ~CURSOR_WRAPNEXT; \ + } \ +} while (0); enum term_mode { MODE_WRAP = 1 << 0, @@ -63,6 +74,12 @@ enum term_mode { MODE_UTF8 = 1 << 6, }; +enum scroll_mode { + SCROLL_RESIZE = -1, + SCROLL_NOSAVEHIST = 0, + SCROLL_SAVEHIST = 1 +}; + enum cursor_movement { CURSOR_SAVE, CURSOR_LOAD @@ -124,10 +141,11 @@ typedef struct { int row; /* nb row */ int col; /* nb col */ Line *line; /* screen */ - Line *alt; /* alternate screen */ - Line hist[HISTSIZE]; /* history buffer */ - int histi; /* history index */ - int scr; /* scroll back */ + Line hist[HISTSIZE]; /* history buffer */ + int histi; /* history index */ + int histf; /* nb history available */ + int scr; /* scroll back */ + int wrapcwidth[2]; /* used in updating WRAPNEXT when resizing */ int *dirty; /* dirtyness of lines */ TCursor c; /* cursor */ int ocx; /* old cursor col */ @@ -162,12 +180,11 @@ typedef struct { char *buf; /* allocated raw string */ size_t siz; /* allocation size */ size_t len; /* raw string length */ - char *argp[STR_ARG_SIZ]; /* pointers to the end of nth argument */ + char *args[STR_ARG_SIZ]; int narg; /* nb of args */ } STREscape; static void execsh(char *, char **); -static char *getcwd_by_pid(pid_t pid); static void stty(char **); static void sigchld(int); static void ttywriteraw(const char *, size_t); @@ -177,6 +194,7 @@ static void csihandle(void); static void readcolonargs(char **, int, int[][CAR_PER_ARG]); 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); @@ -187,26 +205,37 @@ 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 tclearregion(int, int, int, int, int); static void tcursor(int); +static void tclearglyph(Glyph *, int); +static void tresetcursor(void); static void tdeletechar(int); static void tdeleteline(int); static void tinsertblank(int); static void tinsertblankline(int); -static int tlinelen(int); +static int tlinelen(Line len); +static int tiswrapped(Line line); +static char *tgetglyphs(char *, const Glyph *, const Glyph *); +static size_t tgetline(char *, const Glyph *); static void tmoveto(int, int); static void tmoveato(int, int); static void tnewline(int); static void tputtab(int); static void tputc(Rune); static void treset(void); -static void tscrollup(int, int, int); -static void tscrolldown(int, int, int); +static void tscrollup(int, int, int, int); +static void tscrolldown(int, int); +static void treflow(int, int); +static void rscrolldown(int); +static void tresizedef(int, int); +static void tresizealt(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 tloaddefscreen(int, int); +static void tloadaltscreen(int, int); static void tsetmode(int, int, const int *, int); static int twrite(const char *, int, int); static void tfulldirt(void); @@ -216,12 +245,14 @@ static void tdefutf8(char); static int32_t tdefcolor(const int *, int *, int); static void tdeftran(char); static void tstrsequence(uchar); -static const char *findlastany(const char *, const char**, size_t); static void drawregion(int, int, int, int); static void selnormalize(void); -static void selscroll(int, int); +static void selscroll(int, int, int); +static void selmove(int); +static void selremove(void); +static int regionselected(int, int, int, int); static void selsnap(int *, int *, int); static size_t utf8decode(const char *, Rune *, size_t); @@ -248,6 +279,33 @@ 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}; +#include +static int su = 0; +struct timespec sutv; + +static void +tsync_begin() +{ + clock_gettime(CLOCK_MONOTONIC, &sutv); + su = 1; +} + +static void +tsync_end() +{ + su = 0; +} + +int +tinsync(uint timeout) +{ + struct timespec now; + if (su && !clock_gettime(CLOCK_MONOTONIC, &now) + && TIMEDIFF(now, sutv) >= timeout) + su = 0; + return su; +} + ssize_t xwrite(int fd, const char *s, size_t len) { @@ -352,7 +410,7 @@ utf8encode(Rune u, char *c) char utf8encodebyte(Rune u, size_t i) { - return utfbyte[i]; + return utfbyte[i] | (u & ~utfmask[i]); } size_t @@ -366,25 +424,10 @@ utf8validate(Rune *u, size_t 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)) + while (**src && !isprint((unsigned char)**src)) (*src)++; return **src ? *((*src)++) : '='; /* emulate padding if string ends */ } @@ -394,6 +437,13 @@ 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); @@ -429,17 +479,46 @@ selinit(void) } int -tlinelen(int y) +tlinelen(Line line) { - int i = term.col; + int i = term.col - 1; - if (TLINE(y)[i - 1].mode & ATTR_WRAP) - return i; + for (; i >= 0 && !(line[i].mode & (ATTR_SET | ATTR_WRAP)); i--); + return i + 1; +} - while (i > 0 && TLINE(y)[i - 1].u == ' ') - --i; +int +tiswrapped(Line line) +{ + int len = tlinelen(line); - return i; + return len > 0 && (line[len - 1].mode & ATTR_WRAP); +} + +char * +tgetglyphs(char *buf, const Glyph *gp, const Glyph *lgp) +{ + while (gp <= lgp) + if (gp->mode & ATTR_WDUMMY) { + gp++; + } else { + buf += utf8encode((gp++)->u, buf); + } + return buf; +} + +size_t +tgetline(char *buf, const Glyph *fgp) +{ + char *ptr; + const Glyph *lgp = &fgp[term.col - 1]; + + while (lgp > fgp && !(lgp->mode & (ATTR_SET | ATTR_WRAP))) + lgp--; + ptr = tgetglyphs(buf, fgp, lgp); + if (!(lgp->mode & ATTR_WRAP)) + *(ptr++) = '\n'; + return ptr - buf; } void @@ -479,10 +558,11 @@ selextend(int col, int row, int type, int done) sel.oe.x = col; sel.oe.y = row; - selnormalize(); sel.type = type; + selnormalize(); - if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) + 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; @@ -506,39 +586,46 @@ selnormalize(void) selsnap(&sel.nb.x, &sel.nb.y, -1); selsnap(&sel.ne.x, &sel.ne.y, +1); - /* expand selection over line breaks */ + /* expand selection over line breaks */ if (sel.type == SEL_RECTANGULAR) return; - i = tlinelen(sel.nb.y); - if (i < sel.nb.x) + + i = tlinelen(TLINE(sel.nb.y)); + if (sel.nb.x > i) sel.nb.x = i; - if (tlinelen(sel.ne.y) <= sel.ne.x) - sel.ne.x = term.col - 1; + if (sel.ne.x >= tlinelen(TLINE(sel.ne.y))) + sel.ne.x = term.col - 1; +} + +int +regionselected(int x1, int y1, int x2, int y2) +{ + if (sel.ob.x == -1 || sel.mode == SEL_EMPTY || + sel.alt != IS_SET(MODE_ALTSCREEN) || sel.nb.y > y2 || sel.ne.y < y1) + return 0; + + return (sel.type == SEL_RECTANGULAR) ? sel.nb.x <= x2 && sel.ne.x >= x1 + : (sel.nb.y != y2 || sel.nb.x <= x2) && + (sel.ne.y != y1 || sel.ne.x >= x1); } 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); + return regionselected(x, y, x, y); } void selsnap(int *x, int *y, int direction) { int newx, newy, xt, yt; + int rtop = 0, rbot = term.row - 1; int delim, prevdelim; const Glyph *gp, *prevgp; + if (!IS_SET(MODE_ALTSCREEN)) + rtop += -term.histf + term.scr, rbot += term.scr; + switch (sel.snap) { case SNAP_WORD: /* @@ -553,7 +640,7 @@ selsnap(int *x, int *y, int direction) if (!BETWEEN(newx, 0, term.col - 1)) { newy += direction; newx = (newx + term.col) % term.col; - if (!BETWEEN(newy, 0, term.row - 1)) + if (!BETWEEN(newy, rtop, rbot)) break; if (direction > 0) @@ -564,13 +651,13 @@ selsnap(int *x, int *y, int direction) break; } - if (newx >= tlinelen(newy)) + if (newx >= tlinelen(TLINE(newy))) break; gp = &TLINE(newy)[newx]; delim = ISDELIM(gp->u); - if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim - || (delim && gp->u != prevgp->u))) + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim || + (delim && !(gp->u == ' ' && prevgp->u == ' ')))) break; *x = newx; @@ -587,18 +674,14 @@ selsnap(int *x, int *y, int direction) */ *x = (direction < 0) ? 0 : term.col - 1; if (direction < 0) { - for (; *y > 0; *y += direction) { - if (!(TLINE(*y-1)[term.col-1].mode - & ATTR_WRAP)) { + for (; *y > rtop; *y -= 1) { + if (!tiswrapped(TLINE(*y-1))) break; - } } } else if (direction > 0) { - for (; *y < term.row-1; *y += direction) { - if (!(TLINE(*y)[term.col-1].mode - & ATTR_WRAP)) { + for (; *y < rbot; *y += 1) { + if (!tiswrapped(TLINE(*y))) break; - } } } break; @@ -609,40 +692,34 @@ char * getsel(void) { char *str, *ptr; - int y, bufsize, lastx, linelen; - const Glyph *gp, *last; + int y, lastx, linelen; + const Glyph *gp, *lgp; - if (sel.ob.x == -1) + if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN)) return NULL; - bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; - ptr = str = xmalloc(bufsize); + str = xmalloc((term.col + 1) * (sel.ne.y - sel.nb.y + 1) * UTF_SIZ); + ptr = str; /* append every set & selected glyph to the selection */ for (y = sel.nb.y; y <= sel.ne.y; y++) { - if ((linelen = tlinelen(y)) == 0) { + Line line = TLINE(y); + + if ((linelen = tlinelen(line)) == 0) { *ptr++ = '\n'; continue; } if (sel.type == SEL_RECTANGULAR) { - gp = &TLINE(y)[sel.nb.x]; + gp = &line[sel.nb.x]; lastx = sel.ne.x; } else { - gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + gp = &line[sel.nb.y == y ? sel.nb.x : 0]; lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; } - last = &TLINE(y)[MIN(lastx, linelen-1)]; - while (last >= gp && last->u == ' ') - --last; - - for ( ; gp <= last; ++gp) { - if (gp->mode & ATTR_WDUMMY) - continue; - - ptr += utf8encode(gp->u, ptr); - } + lgp = &line[MIN(lastx, linelen-1)]; + ptr = tgetglyphs(ptr, gp, lgp); /* * Copy and pasting of line endings is inconsistent * in the inconsistent terminal and GUI world. @@ -653,10 +730,10 @@ getsel(void) * FIXME: Fix the computer world. */ if ((y < sel.ne.y || lastx >= linelen) && - (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) + (!(lgp->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) *ptr++ = '\n'; } - *ptr = 0; + *ptr = '\0'; return str; } @@ -665,9 +742,15 @@ selclear(void) { if (sel.ob.x == -1) return; + selremove(); + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selremove(void) +{ sel.mode = SEL_IDLE; sel.ob.x = -1; - tsetdirt(sel.nb.y, sel.ne.y); } void @@ -742,14 +825,8 @@ sigchld(int a) if ((p = waitpid(pid, &stat, WNOHANG)) < 0) die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); - if (pid != p) { - if (p == 0 && wait(&stat) < 0) - die("wait: %s\n", strerror(errno)); - - /* reinstall sigchld handler */ - signal(SIGCHLD, sigchld); + if (pid != p) return; - } if (WIFEXITED(stat) && WEXITSTATUS(stat)) die("child exited with status %d\n", WEXITSTATUS(stat)); @@ -844,6 +921,9 @@ ttynew(const char *line, char *cmd, const char *out, char **args) return cmdfd; } +static int twrite_aborted = 0; +int ttyread_pending() { return twrite_aborted; } + size_t ttyread(void) { @@ -852,7 +932,7 @@ ttyread(void) int ret, written; /* append read bytes to unprocessed bytes */ - ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); + ret = twrite_aborted ? 1 : read(cmdfd, buf+buflen, LEN(buf)-buflen); switch (ret) { case 0: @@ -860,7 +940,7 @@ ttyread(void) case -1: die("couldn't read from shell: %s\n", strerror(errno)); default: - buflen += ret; + buflen += twrite_aborted ? 0 : ret; written = twrite(buf, buflen, 0); buflen -= written; /* keep any incomplete UTF-8 byte sequence for the next call */ @@ -874,10 +954,8 @@ void ttywrite(const char *s, size_t n, int may_echo) { const char *next; - Arg arg = (Arg) { .i =term.scr }; - - kscrolldown(&arg); + kscrolldown(&((Arg){ .i = term.scr })); if (may_echo && IS_SET(MODE_ECHO)) twrite(s, n, 1); @@ -1013,7 +1091,7 @@ tsetdirtattr(int attr) 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); + term.dirty[i] = 1; break; } } @@ -1023,7 +1101,10 @@ tsetdirtattr(int attr) void tfulldirt(void) { - tsetdirt(0, term.row-1); + for (int i = 0; i < term.row; i++) + term.dirty[i] = 1; + + tsync_end(); } void @@ -1040,82 +1121,116 @@ tcursor(int mode) } } +void +tresetcursor(void) +{ + term.c = (TCursor){ { .mode = ATTR_NULL, .fg = defaultfg, .bg = defaultbg }, + .x = 0, .y = 0, .state = CURSOR_DEFAULT }; +} + void treset(void) { uint i; + int x, y; - term.c = (TCursor){{ - .mode = ATTR_NULL, - .fg = defaultfg, - .bg = defaultbg - }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; + tresetcursor(); 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.histf = 0; + term.scr = 0; term.bot = term.row - 1; term.mode = MODE_WRAP|MODE_UTF8; memset(term.trantbl, CS_USA, sizeof(term.trantbl)); term.charset = 0; + selremove(); for (i = 0; i < 2; i++) { - tmoveto(0, 0); - tcursor(CURSOR_SAVE); - tclearregion(0, 0, term.col-1, term.row-1); + tcursor(CURSOR_SAVE); /* reset saved cursor */ + for (y = 0; y < term.row; y++) + for (x = 0; x < term.col; x++) + tclearglyph(&term.line[y][x], 0); tswapscreen(); } + tfulldirt(); } void tnew(int col, int row) { - term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; - tresize(col, row); - treset(); + int i, j; + + for (i = 0; i < 2; i++) { + term.line = xmalloc(row * sizeof(Line)); + for (j = 0; j < row; j++) + term.line[j] = xmalloc(col * sizeof(Glyph)); + term.col = col, term.row = row; + tswapscreen(); + } + term.dirty = xmalloc(row * sizeof(*term.dirty)); + term.tabs = xmalloc(col * sizeof(*term.tabs)); + for (i = 0; i < HISTSIZE; i++) + term.hist[i] = xmalloc(col * sizeof(Glyph)); + treset(); } +/* handle it with care */ void tswapscreen(void) { - Line *tmp = term.line; + static Line *altline; + static int altcol, altrow; + Line *tmpline = term.line; + int tmpcol = term.col, tmprow = term.row; - term.line = term.alt; - term.alt = tmp; + term.line = altline; + term.col = altcol, term.row = altrow; + altline = tmpline; + altcol = tmpcol, altrow = tmprow; term.mode ^= MODE_ALTSCREEN; - tfulldirt(); } void -newterm(const Arg* a) +tloaddefscreen(int clear, int loadcursor) { - int res; - switch (fork()) { - case -1: - die("fork failed: %s\n", strerror(errno)); - break; - case 0: - switch (fork()) { - case -1: - die("fork failed: %s\n", strerror(errno)); - break; - case 0: - res = chdir(getcwd_by_pid(pid)); - execlp("st", "./st", NULL); - break; - default: - exit(0); - } - default: - wait(NULL); + int col, row, alt = IS_SET(MODE_ALTSCREEN); + + if (alt) { + if (clear) + tclearregion(0, 0, term.col-1, term.row-1, 1); + col = term.col, row = term.row; + tswapscreen(); } + if (loadcursor) + tcursor(CURSOR_LOAD); + if (alt) + tresizedef(col, row); } -static char *getcwd_by_pid(pid_t pid) { - char buf[32]; - snprintf(buf, sizeof buf, "/proc/%d/cwd", pid); - return realpath(buf, NULL); +void +tloadaltscreen(int clear, int savecursor) +{ + int col, row, def = !IS_SET(MODE_ALTSCREEN); + + if (savecursor) + tcursor(CURSOR_SAVE); + if (def) { + col = term.col, row = term.row; + tswapscreen(); + term.scr = 0; + tresizealt(col, row); + } + if (clear) + tclearregion(0, 0, term.col-1, term.row-1, 1); +} + +int +tisaltscreen(void) +{ + return IS_SET(MODE_ALTSCREEN); } void @@ -1123,17 +1238,22 @@ kscrolldown(const Arg* a) { int n = a->i; + if (!term.scr || IS_SET(MODE_ALTSCREEN)) + return; + if (n < 0) - n = term.row + n; + n = MAX(term.row / -n, 1); - if (n > term.scr) - n = term.scr; - - if (term.scr > 0) { + if (n <= term.scr) { term.scr -= n; - selscroll(0, -n); - tfulldirt(); + } else { + n = term.scr; + term.scr = 0; } + + if (sel.ob.x != -1 && !sel.alt) + selmove(-n); /* negate change in term.scr */ + tfulldirt(); } void @@ -1141,92 +1261,119 @@ kscrollup(const Arg* a) { int n = a->i; - if (n < 0) - n = term.row + n; + if (!term.histf || IS_SET(MODE_ALTSCREEN)) + return; - if (term.scr <= HISTSIZE-n) { + if (n < 0) + n = MAX(term.row / -n, 1); + + if (term.scr + n <= term.histf) { term.scr += n; - selscroll(0, n); - tfulldirt(); + } else { + n = term.histf - term.scr; + term.scr = term.histf; } + + if (sel.ob.x != -1 && !sel.alt) + selmove(n); /* negate change in term.scr */ + tfulldirt(); } void -tscrolldown(int orig, int n, int copyhist) +tscrolldown(int top, int n) { - int i; + int i, bot = term.bot; Line temp; - LIMIT(n, 0, term.bot-orig+1); + if (n <= 0) + return; + n = MIN(n, bot-top+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(top, bot-n); + tclearregion(0, bot-n+1, term.col-1, bot, 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--) { + for (i = bot; i >= top+n; i--) { temp = term.line[i]; term.line[i] = term.line[i-n]; term.line[i-n] = temp; } - if (term.scr == 0) - selscroll(orig, n); + if (sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN)) + selscroll(top, bot, n); } void -tscrollup(int orig, int n, int copyhist) +tscrollup(int top, int bot, int n, int mode) { - int i; + int i, j, s; + int alt = IS_SET(MODE_ALTSCREEN); + int savehist = !alt && top == 0 && mode != SCROLL_NOSAVEHIST; Line temp; - LIMIT(n, 0, term.bot-orig+1); + if (n <= 0) + return; + n = MIN(n, bot-top+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 (savehist) { + for (i = 0; i < n; i++) { + term.histi = (term.histi + 1) % HISTSIZE; + temp = term.hist[term.histi]; + for (j = 0; j < term.col; j++) + tclearglyph(&temp[j], 1); + term.hist[term.histi] = term.line[i]; + term.line[i] = temp; + } + term.histf = MIN(term.histf + n, HISTSIZE); + s = n; + if (term.scr) { + j = term.scr; + term.scr = MIN(j + n, HISTSIZE); + s = j + n - term.scr; + } + if (mode != SCROLL_RESIZE) + tfulldirt(); + } else { + tclearregion(0, top, term.col-1, top+n-1, 1); + tsetdirt(top+n, bot); } - if (term.scr > 0 && term.scr < HISTSIZE) - term.scr = MIN(term.scr + n, HISTSIZE-1); - tclearregion(0, orig, term.col-1, orig+n-1); - tsetdirt(orig+n, term.bot); - - for (i = orig; i <= term.bot-n; i++) { + for (i = top; i <= bot-n; i++) { temp = term.line[i]; term.line[i] = term.line[i+n]; term.line[i+n] = temp; } - if (term.scr == 0) - selscroll(orig, -n); + if (sel.ob.x != -1 && sel.alt == alt) { + if (!savehist) { + selscroll(top, bot, -n); + } else if (s > 0) { + selmove(-s); + if (-term.scr + sel.nb.y < -term.histf) + selremove(); + } + } } void -selscroll(int orig, int n) +selmove(int n) { - if (sel.ob.x == -1) - return; + sel.ob.y += n, sel.nb.y += n; + sel.oe.y += n, sel.ne.y += n; +} - if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { +void +selscroll(int top, int bot, int n) +{ + /* turn absolute coordinates into relative */ + top += term.scr, bot += term.scr; + + if (BETWEEN(sel.nb.y, top, bot) != BETWEEN(sel.ne.y, top, 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) { + } else if (BETWEEN(sel.nb.y, top, bot)) { + selmove(n); + if (sel.nb.y < top || sel.ne.y > bot) selclear(); - } else { - selnormalize(); - } } } @@ -1236,7 +1383,7 @@ tnewline(int first_col) int y = term.c.y; if (y == term.bot) { - tscrollup(term.top, 1, 1); + tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST); } else { y++; } @@ -1349,92 +1496,96 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) } 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; + term.line[y][x].mode |= ATTR_SET; if (isboxdraw(u)) - term.line[y][x].mode |= ATTR_BOXDRAW; + term.line[y][x-1].mode |= ATTR_BOXDRAW; } void -tclearregion(int x1, int y1, int x2, int y2) +tclearglyph(Glyph *gp, int usecurattr) { - int x, y, temp; - Glyph *gp; + if (usecurattr) { + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + } else { + gp->fg = defaultfg; + gp->bg = defaultbg; + } + gp->mode = ATTR_NULL; + gp->u = ' '; +} - if (x1 > x2) - temp = x1, x1 = x2, x2 = temp; - if (y1 > y2) - temp = y1, y1 = y2, y2 = temp; +void +tclearregion(int x1, int y1, int x2, int y2, int usecurattr) +{ + int x, y; - LIMIT(x1, 0, term.col-1); - LIMIT(x2, 0, term.col-1); - LIMIT(y1, 0, term.row-1); - LIMIT(y2, 0, term.row-1); + /* regionselected() takes relative coordinates */ + if (regionselected(x1+term.scr, y1+term.scr, x2+term.scr, y2+term.scr)) + selremove(); 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 = ' '; - } + for (x = x1; x <= x2; x++) + tclearglyph(&term.line[y][x], usecurattr); } } void tdeletechar(int n) { - int dst, src, size; - Glyph *line; - - LIMIT(n, 0, term.col - term.c.x); + int src, dst, size; + Line line; + if (n <= 0) + return; dst = term.c.x; - src = term.c.x + n; + src = MIN(term.c.x + n, term.col); 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); + if (size > 0) { /* otherwise src would point beyond the array + https://stackoverflow.com/questions/29844298 */ + line = term.line[term.c.y]; + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + } + tclearregion(dst + size, term.c.y, term.col - 1, term.c.y, 1); } void tinsertblank(int n) { - int dst, src, size; - Glyph *line; + int src, dst, size; + Line line; - LIMIT(n, 0, term.col - term.c.x); - - dst = term.c.x + n; + if (n <= 0) + return; + dst = MIN(term.c.x + n, term.col); 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); + if (size > 0) { /* otherwise dst would point beyond the array */ + line = term.line[term.c.y]; + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + } + tclearregion(src, term.c.y, dst - 1, term.c.y, 1); } void tinsertblankline(int n) { if (BETWEEN(term.c.y, term.top, term.bot)) - tscrolldown(term.c.y, n, 0); + tscrolldown(term.c.y, n); } void tdeleteline(int n) { if (BETWEEN(term.c.y, term.top, term.bot)) - tscrollup(term.c.y, n, 0); + tscrollup(term.c.y, term.bot, n, SCROLL_NOSAVEHIST); } int32_t @@ -1631,7 +1782,7 @@ tsetscroll(int t, int b) void tsetmode(int priv, int set, const int *args, int narg) { - int alt; const int *lim; + const int *lim; for (lim = args + narg; args < lim; ++args) { if (priv) { @@ -1692,25 +1843,18 @@ tsetmode(int priv, int set, const int *args, int narg) 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: + case 1047: /* swap screen, clearing alternate screen */ 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 */ + if (set) + tloadaltscreen(*args == 1049, *args == 1049); + else + tloaddefscreen(*args == 1047, *args == 1049); + break; case 1048: + if (!allowaltscreen) + break; tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); break; case 2004: /* 2004: bracketed paste mode */ @@ -1762,7 +1906,7 @@ void csihandle(void) { char buf[40]; - int len; + int n, x; switch (csiescseq.mode[0]) { default: @@ -1860,20 +2004,30 @@ csihandle(void) 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); + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1); if (term.c.y < term.row-1) { - tclearregion(0, term.c.y+1, term.col-1, - term.row-1); + tclearregion(0, term.c.y+1, term.col-1, term.row-1, 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); + if (term.c.y >= 1) + tclearregion(0, 0, term.col-1, term.c.y-1, 1); + tclearregion(0, term.c.y, term.c.x, term.c.y, 1); break; case 2: /* all */ - tclearregion(0, 0, term.col-1, term.row-1); - break; + if (IS_SET(MODE_ALTSCREEN)) { + tclearregion(0, 0, term.col-1, term.row-1, 1); + break; + } + /* vte does this: + tscrollup(0, term.row-1, term.row, SCROLL_SAVEHIST); */ + + /* alacritty does this: */ + for (n = term.row-1; n >= 0 && tlinelen(term.line[n]) == 0; n--); + if (n >= 0) + tscrollup(0, term.row-1, n+1, SCROLL_SAVEHIST); + tscrollup(0, term.row-1, term.row-n-1, SCROLL_NOSAVEHIST); + break; default: goto unknown; } @@ -1881,24 +2035,24 @@ csihandle(void) 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); + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1); break; case 1: /* left */ - tclearregion(0, term.c.y, term.c.x, term.c.y); + tclearregion(0, term.c.y, term.c.x, term.c.y, 1); break; case 2: /* all */ - tclearregion(0, term.c.y, term.col-1, term.c.y); + tclearregion(0, term.c.y, term.col-1, term.c.y, 1); break; } break; case 'S': /* SU -- Scroll line up */ DEFAULT(csiescseq.arg[0], 1); - tscrollup(term.top, csiescseq.arg[0], 0); + /* xterm, urxvt, alacritty save this in history */ + tscrollup(term.top, term.bot, csiescseq.arg[0], SCROLL_SAVEHIST); break; case 'T': /* SD -- Scroll line down */ DEFAULT(csiescseq.arg[0], 1); - tscrolldown(term.top, csiescseq.arg[0], 0); + tscrolldown(term.top, csiescseq.arg[0]); break; case 'L': /* IL -- Insert blank lines */ DEFAULT(csiescseq.arg[0], 1); @@ -1912,9 +2066,11 @@ csihandle(void) tdeleteline(csiescseq.arg[0]); break; case 'X': /* ECH -- Erase char */ + if (csiescseq.arg[0] < 0) + return; DEFAULT(csiescseq.arg[0], 1); - tclearregion(term.c.x, term.c.y, - term.c.x + csiescseq.arg[0] - 1, term.c.y); + x = MIN(term.c.x + csiescseq.arg[0], term.col) - 1; + tclearregion(term.c.x, term.c.y, x, term.c.y, 1); break; case 'P': /* DCH -- Delete char */ DEFAULT(csiescseq.arg[0], 1); @@ -1936,9 +2092,9 @@ csihandle(void) break; case 'n': /* DSR – Device Status Report (cursor position) */ if (csiescseq.arg[0] == 6) { - len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + n = snprintf(buf, sizeof(buf), "\033[%i;%iR", term.c.y+1, term.c.x+1); - ttywrite(buf, len, 0); + ttywrite(buf, n, 0); } break; case 'r': /* DECSTBM -- Set Scrolling Region */ @@ -1967,33 +2123,6 @@ csihandle(void) goto unknown; } break; - case 't': /* title stack operations */ - switch (csiescseq.arg[0]) { - case 22: /* pust current title on stack */ - switch (csiescseq.arg[1]) { - case 0: - case 1: - case 2: - xpushtitle(); - break; - default: - goto unknown; - } - break; - case 23: /* pop last title from stack */ - switch (csiescseq.arg[1]) { - case 0: - case 1: - case 2: - xsettitle(NULL, 1); - break; - default: - goto unknown; - } - break; - default: - goto unknown; - } } } @@ -2028,39 +2157,28 @@ csireset(void) } void -osc4_color_response(int num) +osc_color_response(int num, int index, int is_osc4) { int n; char buf[32]; unsigned char r, g, b; - if (xgetcolor(num, &r, &g, &b)) { - fprintf(stderr, "erresc: failed to fetch osc4 color %d\n", num); + 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]4;%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", - num, r, r, g, g, b, b); - - ttywrite(buf, n, 1); -} - -void -osc_color_response(int index, int num) -{ - int n; - char buf[32]; - unsigned char r, g, b; - - if (xgetcolor(index, &r, &g, &b)) { - fprintf(stderr, "erresc: failed to fetch osc color %d\n", 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); } - - n = snprintf(buf, sizeof buf, "\033]%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", - num, r, r, g, g, b, b); - - ttywrite(buf, n, 1); } void @@ -2068,31 +2186,37 @@ 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); - strescseq.buf[strescseq.len] = '\0'; + strescseq.buf[strescseq.len] = '\0'; + switch (strescseq.type) { case ']': /* OSC -- Operating System Command */ - strparse(); - par = (narg = strescseq.narg) ? atoi(STRESCARGJUST(0)) : 0; + strparse(); + par = (narg = strescseq.narg) ? atoi(STRESCARGJUST(0)) : 0; switch (par) { case 0: if (narg > 1) { - xsettitle(strescseq.argp[1], 0); - xseticontitle(strescseq.argp[1]); + xsettitle(STRESCARGREST(1)); + xseticontitle(STRESCARGREST(1)); } return; case 1: if (narg > 1) - xseticontitle(strescseq.argp[1]); + xseticontitle(STRESCARGREST(1)); return; case 2: if (narg > 1) - xsettitle(STRESCARGREST(1), 0); + xsettitle(STRESCARGREST(1)); return; case 52: if (narg > 2 && allowwindowops) { - dec = base64dec(STRESCARGJUST(2)); + dec = base64dec(STRESCARGREST(1)); if (dec) { xsetsel(dec); xclipcopy(); @@ -2102,71 +2226,56 @@ strhandle(void) } return; case 10: - if (narg < 2) - break; - - p = strescseq.argp[1]; - - if (!strcmp(p, "?")) - osc_color_response(defaultfg, 10); - else if (xsetcolorname(defaultfg, p)) - fprintf(stderr, "erresc: invalid foreground color: %s\n", p); - else - redraw(); - return; case 11: - if (narg < 2) - break; - - p = strescseq.argp[1]; - - if (!strcmp(p, "?")) - osc_color_response(defaultbg, 11); - else if (xsetcolorname(defaultbg, p)) - fprintf(stderr, "erresc: invalid background color: %s\n", p); - else - redraw(); - return; case 12: if (narg < 2) break; + p = strescseq.args[1]; + if ((j = par - 10) < 0 || j >= LEN(osc_table)) + break; /* shouldn't be possible */ - p = strescseq.argp[1]; - - if (!strcmp(p, "?")) - osc_color_response(defaultcs, 12); - else if (xsetcolorname(defaultcs, p)) - fprintf(stderr, "erresc: invalid cursor color: %s\n", p); - else - redraw(); + 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 = STRESCARGJUST(2); + p = STRESCARGREST(2); /* FALLTHROUGH */ case 104: /* color reset */ - j = (narg > 1) ? atoi(STRESCARGJUST(1)) : -1; + j = (narg > 1) ? atoi(STRESCARGREST(1)) : -1; - if (p && !strcmp(p, "?")) - osc4_color_response(j); - else if (xsetcolorname(j, p)) { + if (p && !strcmp(p, "?")) { + osc_color_response(j, 0, 1); + } else if (xsetcolorname(j, p)) { if (par == 104 && narg <= 1) return; /* color reset without parameter */ fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", j, p ? p : "(null)"); } else { - if (j == defaultbg) - xclearwin(); - redraw(); + if (j == defaultbg) + xclearwin(); + tfulldirt(); } return; } break; case 'k': /* old title set compatibility */ - xsettitle(strescseq.buf, 0); + xsettitle(STRESCARGREST(0)); return; case 'P': /* DCS -- Device Control String */ + /* https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec */ + if (strstr(strescseq.buf, "=1s") == strescseq.buf) + tsync_begin(); /* BSU */ + else if (strstr(strescseq.buf, "=2s") == strescseq.buf) + tsync_end(); /* ESU */ + return; case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ return; @@ -2190,64 +2299,11 @@ strparse(void) while (strescseq.narg < STR_ARG_SIZ) { while ((c = *p) != ';' && c != '\0') p++; - strescseq.argp[strescseq.narg++] = p; + strescseq.args[strescseq.narg++] = p; if (c == '\0') return; p++; - } -} - -void -externalpipe(const Arg *arg) -{ - int to[2]; - char buf[UTF_SIZ]; - void (*oldsigpipe)(int); - Glyph *bp, *end; - int lastpos, n, newline; - - if (pipe(to) == -1) - return; - - switch (fork()) { - case -1: - close(to[0]); - close(to[1]); - return; - case 0: - dup2(to[0], STDIN_FILENO); - close(to[0]); - close(to[1]); - execvp(((char **)arg->v)[0], (char **)arg->v); - fprintf(stderr, "st: execvp %s\n", ((char **)arg->v)[0]); - perror("failed"); - exit(0); - } - - close(to[0]); - /* ignore sigpipe for now, in case child exists early */ - oldsigpipe = signal(SIGPIPE, SIG_IGN); - newline = 0; - for (n = 0; n < term.row; n++) { - bp = term.line[n]; - lastpos = MIN(tlinelen(n) + 1, term.col) - 1; - if (lastpos < 0) - break; - end = &bp[lastpos + 1]; - for (; bp < end; ++bp) - if (xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0) - break; - if ((newline = term.line[n][lastpos].mode & ATTR_WRAP)) - continue; - if (xwrite(to[1], "\n", 1) < 0) - break; - newline = 0; - } - if (newline) - (void)xwrite(to[1], "\n", 1); - close(to[1]); - /* restore */ - signal(SIGPIPE, oldsigpipe); + } } void @@ -2335,16 +2391,8 @@ tdumpsel(void) 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); + char str[(term.col + 1) * UTF_SIZ]; + tprinter(str, tgetline(str, &term.line[n][0])); } void @@ -2565,7 +2613,7 @@ eschandle(uchar ascii) return 0; case 'D': /* IND -- Linefeed */ if (term.c.y == term.bot) { - tscrollup(term.top, 1, 1); + tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST); } else { tmoveto(term.c.x, term.c.y+1); } @@ -2578,7 +2626,7 @@ eschandle(uchar ascii) break; case 'M': /* RI -- Reverse index */ if (term.c.y == term.top) { - tscrolldown(term.top, 1, 1); + tscrolldown(term.top, 1); } else { tmoveto(term.c.x, term.c.y-1); } @@ -2588,7 +2636,6 @@ eschandle(uchar ascii) break; case 'c': /* RIS -- Reset to initial state */ treset(); - xfreetitlestack(); resettitle(); xloadcols(); break; @@ -2719,7 +2766,8 @@ check_control_code: */ return; } - if (selected(term.c.x, term.c.y)) + /* selected() takes relative coordinates */ + if (selected(term.c.x + term.scr, term.c.y + term.scr)) selclear(); gp = &term.line[term.c.y][term.c.x]; @@ -2754,6 +2802,7 @@ check_control_code: if (term.c.x+width < term.col) { tmoveto(term.c.x+width, term.c.y); } else { + term.wrapcwidth[IS_SET(MODE_ALTSCREEN)] = width; term.c.state |= CURSOR_WRAPNEXT; } } @@ -2765,6 +2814,9 @@ twrite(const char *buf, int buflen, int show_ctrl) Rune u; int n; + int su0 = su; + twrite_aborted = 0; + for (n = 0; n < buflen; n += charsize) { if (IS_SET(MODE_UTF8)) { /* process a complete utf8 char */ @@ -2775,6 +2827,10 @@ twrite(const char *buf, int buflen, int show_ctrl) u = buf[n] & 0xFF; charsize = 1; } + if (su0 && !su) { + twrite_aborted = 1; + break; // ESU - allow rendering before a new BSU + } if (show_ctrl && ISCONTROL(u)) { if (u & 0x80) { u &= 0x7f; @@ -2791,105 +2847,281 @@ twrite(const char *buf, int buflen, int show_ctrl) } void -tresize(int col, int row) +treflow(int col, int row) { int i, j; - int minrow = MIN(row, term.row); - int mincol = MIN(col, term.col); - int *bp; - TCursor c; + int oce, nce, bot, scr; + int ox = 0, oy = -term.histf, nx = 0, ny = -1, len; + int cy = -1; /* proxy for new y coordinate of cursor */ + int nlines; + Line *buf, line; - if ( row < term.row || col < term.col ) - toggle_winmode(trt_kbdselect(XK_Escape, NULL, 0)); + /* y coordinate of cursor line end */ + for (oce = term.c.y; oce < term.row - 1 && + tiswrapped(term.line[oce]); oce++); - if (col < 1 || row < 1) { - fprintf(stderr, - "tresize: error resizing to %dx%d\n", col, row); - return; - } - - if ( row < term.row || col < term.col ) - autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); - - /* - * slide screen to keep cursor where we expect it - - * tscrollup would work here, but we can optimize to - * memmove because we're freeing the earlier lines - */ - for (i = 0; i <= term.c.y - row; i++) { - free(term.line[i]); - free(term.alt[i]); - } - /* ensure that both src and dst are not NULL */ - if (i > 0) { - memmove(term.line, term.line + i, row * sizeof(Line)); - memmove(term.alt, term.alt + i, row * sizeof(Line)); - } - for (i += row; i < term.row; i++) { - free(term.line[i]); - free(term.alt[i]); - } - - /* resize to new height */ - term.line = xrealloc(term.line, row * sizeof(Line)); - term.alt = xrealloc(term.alt, row * sizeof(Line)); - term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); - term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); - - for (i = 0; i < HISTSIZE; i++) { - term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); - for (j = mincol; j < col; j++) { - term.hist[i][j] = term.c.attr; - term.hist[i][j].u = ' '; + nlines = term.histf + oce + 1; + if (col < term.col) { + /* each line can take this many lines after reflow */ + j = (term.col + col - 1) / col; + nlines = j * nlines; + if (nlines > HISTSIZE + RESIZEBUFFER + row) { + nlines = HISTSIZE + RESIZEBUFFER + row; + oy = -(nlines / j - oce - 1); } } + buf = xmalloc(nlines * sizeof(Line)); + do { + if (!nx) + buf[++ny] = xmalloc(col * sizeof(Glyph)); + if (!ox) { + line = TLINEABS(oy); + len = tlinelen(line); + } + if (oy == term.c.y) { + if (!ox) + len = MAX(len, term.c.x + 1); + /* update cursor */ + if (cy < 0 && term.c.x - ox < col - nx) { + term.c.x = nx + term.c.x - ox, cy = ny; + UPDATEWRAPNEXT(0, col); + } + } + /* get reflowed lines in buf */ + if (col - nx > len - ox) { + memcpy(&buf[ny][nx], &line[ox], (len-ox) * sizeof(Glyph)); + nx += len - ox; + if (len == 0 || !(line[len - 1].mode & ATTR_WRAP)) { + for (j = nx; j < col; j++) + tclearglyph(&buf[ny][j], 0); + nx = 0; + } else if (nx > 0) { + buf[ny][nx - 1].mode &= ~ATTR_WRAP; + } + ox = 0, oy++; + } else if (col - nx == len - ox) { + memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph)); + ox = 0, oy++, nx = 0; + } else/* if (col - nx < len - ox) */ { + memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph)); + ox += col - nx; + buf[ny][col - 1].mode |= ATTR_WRAP; + nx = 0; + } + } while (oy <= oce); + if (nx) + for (j = nx; j < col; j++) + tclearglyph(&buf[ny][j], 0); - /* 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)); + /* free extra lines */ + for (i = row; i < term.row; i++) + free(term.line[i]); + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + + bot = MIN(ny, row - 1); + scr = MAX(row - term.row, 0); + /* update y coordinate of cursor line end */ + nce = MIN(oce + scr, bot); + /* update cursor y coordinate */ + term.c.y = nce - (ny - cy); + if (term.c.y < 0) { + j = nce, nce = MIN(nce + -term.c.y, bot); + term.c.y += nce - j; + while (term.c.y < 0) { + free(buf[ny--]); + term.c.y++; + } } - - /* allocate any new rows */ - for (/* i = minrow */; i < row; i++) { + /* allocate new rows */ + for (i = row - 1; i > nce; i--) { term.line[i] = xmalloc(col * sizeof(Glyph)); - term.alt[i] = xmalloc(col * sizeof(Glyph)); + for (j = 0; j < col; j++) + tclearglyph(&term.line[i][j], 0); } + /* fill visible area */ + for (/*i = nce */; i >= term.row; i--, ny--) + term.line[i] = buf[ny]; + for (/*i = term.row - 1 */; i >= 0; i--, ny--) { + free(term.line[i]); + term.line[i] = buf[ny]; + } + /* fill lines in history buffer and update term.histf */ + for (/*i = -1 */; ny >= 0 && i >= -HISTSIZE; i--, ny--) { + j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE; + free(term.hist[j]); + term.hist[j] = buf[ny]; + } + term.histf = -i - 1; + term.scr = MIN(term.scr, term.histf); + /* resize rest of the history lines */ + for (/*i = -term.histf - 1 */; i >= -HISTSIZE; i--) { + j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE; + term.hist[j] = xrealloc(term.hist[j], col * sizeof(Glyph)); + } + free(buf); +} + +void +rscrolldown(int n) +{ + int i; + Line temp; + + /* can never be true as of now + if (IS_SET(MODE_ALTSCREEN)) + return; */ + + if ((n = MIN(n, term.histf)) <= 0) + return; + + for (i = term.c.y + n; i >= n; i--) { + temp = term.line[i]; + term.line[i] = term.line[i-n]; + term.line[i-n] = temp; + } + for (/*i = n - 1 */; i >= 0; i--) { + temp = term.line[i]; + term.line[i] = term.hist[term.histi]; + term.hist[term.histi] = temp; + term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; + } + term.c.y += n; + term.histf -= n; + if ((i = term.scr - n) >= 0) { + term.scr = i; + } else { + term.scr = 0; + if (sel.ob.x != -1 && !sel.alt) + selmove(-i); + } +} + +void +tresize(int col, int row) +{ + int *bp; + + /* col and row are always MAX(_, 1) + if (col < 1 || row < 1) { + fprintf(stderr, "tresize: error resizing to %dx%d\n", col, row); + return; + } */ + + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); 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); + + if (IS_SET(MODE_ALTSCREEN)) + tresizealt(col, row); + else + tresizedef(col, row); +} + +void +tresizedef(int col, int row) +{ + int i, j; + + /* return if dimensions haven't changed */ + if (term.col == col && term.row == row) { + tfulldirt(); + return; } - term.c = c; + if (col != term.col) { + if (!sel.alt) + selremove(); + treflow(col, row); + } else { + /* slide screen up if otherwise cursor would get out of the screen */ + if (term.c.y >= row) { + tscrollup(0, term.row - 1, term.c.y - row + 1, SCROLL_RESIZE); + term.c.y = row - 1; + } + for (i = row; i < term.row; i++) + free(term.line[i]); + + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + /* allocate any new rows */ + for (i = term.row; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + for (j = 0; j < col; j++) + tclearglyph(&term.line[i][j], 0); + } + /* scroll down as much as height has increased */ + rscrolldown(row - term.row); + } + /* update terminal size */ + term.col = col, term.row = row; + /* reset scrolling region */ + term.top = 0, term.bot = row - 1; + /* dirty all lines */ + tfulldirt(); +} + +void +tresizealt(int col, int row) +{ + int i, j; + + /* return if dimensions haven't changed */ + if (term.col == col && term.row == row) { + tfulldirt(); + return; + } + if (sel.alt) + selremove(); + /* slide screen up if otherwise cursor would get out of the screen */ + for (i = 0; i <= term.c.y - row; i++) + free(term.line[i]); + if (i > 0) { + /* ensure that both src and dst are not NULL */ + memmove(term.line, term.line + i, row * sizeof(Line)); + term.c.y = row - 1; + } + for (i += row; i < term.row; i++) + free(term.line[i]); + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + /* resize to new width */ + for (i = 0; i < MIN(row, term.row); i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + for (j = term.col; j < col; j++) + tclearglyph(&term.line[i][j], 0); + } + /* allocate any new rows */ + for (/*i = MIN(row, term.row) */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + for (j = 0; j < col; j++) + tclearglyph(&term.line[i][j], 0); + } + /* update cursor */ + if (term.c.x >= col) { + term.c.state &= ~CURSOR_WRAPNEXT; + term.c.x = col - 1; + } else { + UPDATEWRAPNEXT(1, col); + } + /* update terminal size */ + term.col = col, term.row = row; + /* reset scrolling region */ + term.top = 0, term.bot = row - 1; + /* dirty all lines */ + tfulldirt(); } void resettitle(void) { - xsettitle(NULL, 0); + xsettitle(NULL); } void @@ -2923,10 +3155,9 @@ draw(void) cx--; drawregion(0, 0, term.col, term.row); - if (term.scr == 0) - xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], - term.ocx, term.ocy, term.line[term.ocy][term.ocx], - term.line[term.ocy], term.col); + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx], + term.line[term.ocy], term.col); term.ocx = cx; term.ocy = term.c.y; xfinishdraw(); @@ -2941,510 +3172,61 @@ redraw(void) draw(); } -void autocomplete (const Arg * arg) -{ - static _Bool active = 0; - - int acmpl_cmdindex = arg -> i; - - static int acmpl_cmdindex_prev; - - if (active == 0) - acmpl_cmdindex_prev = acmpl_cmdindex; - - static const char * const (acmpl_cmd []) = { - [ACMPL_DEACTIVATE] = "__DEACTIVATE__", - [ACMPL_WORD] = "word-complete", - [ACMPL_WWORD] = "WORD-complete", - [ACMPL_FUZZY_WORD] = "fuzzy-word-complete", - [ACMPL_FUZZY_WWORD] = "fuzzy-WORD-complete", - [ACMPL_FUZZY] = "fuzzy-complete", - [ACMPL_SUFFIX] = "suffix-complete", - [ACMPL_SURROUND] = "surround-complete", - [ACMPL_UNDO] = "__UNDO__", - }; - - static char acmpl [1000]; // ACMPL_ISSUE: why 1000? - - static FILE * acmpl_exec = NULL; - static int acmpl_status; - - static const char * stbuffile; - static char target [1000]; // ACMPL_ISSUE: why 1000? dynamically allocate char array of size term.col - static size_t targetlen; - - static char completion [1000] = {0}; // ACMPL_ISSUE: why 1000? dynamically allocate char array of size term.col - static size_t complen_prev = 0; // NOTE: always clear this variable after clearing completion - - static int cx, cy; - -// Check for deactivation - - if (acmpl_cmdindex == ACMPL_DEACTIVATE) - { - -// Deactivate autocomplete mode keeping current completion - - if (active) - { - active = 0; - pclose (acmpl_exec); - remove (stbuffile); - - if (complen_prev) - { - selclear (); - complen_prev = 0; - } - } - - return; - } - -// Check for undo - - if (acmpl_cmdindex == ACMPL_UNDO) - { - -// Deactivate autocomplete mode recovering target - - if (active) - { - active = 0; - pclose (acmpl_exec); - remove (stbuffile); - - if (complen_prev) - { - selclear (); - for (size_t i = 0; i < complen_prev; i++) - ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is the right way - complen_prev = 0; - ttywrite (target, targetlen, 0); // ACMPL_ISSUE: I'm not sure that this is a right solution - } - } - - return; - } - -// Check for command change - - if (acmpl_cmdindex != acmpl_cmdindex_prev) - { - -// If command is changed, goto acmpl_begin avoiding rewriting st buffer - - if (active) - { - acmpl_cmdindex_prev = acmpl_cmdindex; - - goto acmpl_begin; - } - } - -// If not active - - if (active == 0) - { - acmpl_cmdindex_prev = acmpl_cmdindex; - cx = term.c.x; - cy = term.c.y; - -// Write st buffer to a temp file - - //stbuffile = mkstemp (NULL); // check for return value ... - // ACMPL_ISSUE: use coprocesses instead of temp files - - FILE * stbuf = fopen (stbuffile, "w"); // check for opening error ... - char * stbufline = malloc (term.col + 2); // check for allocating error ... - - for (size_t y = 0; y < term.row; y++) - { - size_t x = 0; - for (; x < term.col; x++) - utf8encode (term.line [y] [x].u, stbufline + x); - stbufline [x] = '\n'; - stbufline [x + 1] = 0; - fputs (stbufline, stbuf); - } - - free (stbufline); - fclose (stbuf); - -acmpl_begin: - -// Run st-autocomplete - - sprintf ( - acmpl, - "cat %100s | st-autocomplete %500s %d %d", // ACMPL_ISSUE: why 100 and 500? - stbuffile, - acmpl_cmd [acmpl_cmdindex], - cy, - cx - ); - - acmpl_exec = popen (acmpl, "r"); // ACMPL_ISSUE: popen isn't defined by The Standard. Does it work in BSDs for example? - // check for popen error ... - -// Read the target, targetlen - - fscanf (acmpl_exec, "%500s\n", target); // check for scanning error ... - targetlen = strlen (target); - } - -// Read a completion if exists (acmpl_status) - - unsigned line, beg, end; - - acmpl_status = fscanf (acmpl_exec, "%500s %u %u %u\n", completion, & line, & beg, & end); - // ACMPL_ISSUE: why 500? use term.col instead - -// Exit if no completions found - - if (active == 0 && acmpl_status == EOF) - { - -// Close st-autocomplete and exit without activating the autocomplete mode - - pclose (acmpl_exec); - remove (stbuffile); - return; - } - -// If completions found, enable autocomplete mode and autocomplete the target - - active = 1; - -// Clear target before first completion - - if (complen_prev == 0) - { - for (size_t i = 0; i < targetlen; i++) - ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is a right solution - } - -// Clear previuos completion if this is not the first - - else - { - selclear (); - for (size_t i = 0; i < complen_prev; i++) - ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is a right solution - complen_prev = 0; - } - -// If no more completions found, reset and restart - - if (acmpl_status == EOF) - { - active = 0; - pclose (acmpl_exec); - ttywrite (target, targetlen, 0); - goto acmpl_begin; - } - -// Read the new completion and autcomplete - - selstart (beg, line, 0); - selextend (end - 1, line, 1, 0); - xsetsel (getsel ()); - - complen_prev = strlen (completion); - ttywrite (completion, complen_prev, 0); -} - -void set_notifmode(int type, KeySym ksym) { - static char *lib[] = { " MOVE ", " SEL "}; - static Glyph *g, *deb, *fin; - static int col, bot; - - if ( ksym == -1 ) { - free(g); - col = term.col, bot = term.bot; - g = xmalloc(col * sizeof(Glyph)); - memcpy(g, term.line[bot], col * sizeof(Glyph)); - - } - else if ( ksym == -2 ) - memcpy(term.line[bot], g, col * sizeof(Glyph)); - - if ( type < 2 ) { - char *z = lib[type]; - for (deb = &term.line[bot][col - 6], fin = &term.line[bot][col]; deb < fin; z++, deb++) - deb->mode = ATTR_REVERSE, - deb->u = *z, - deb->fg = defaultfg, deb->bg = defaultbg; - } - else if ( type < 5 ) - memcpy(term.line[bot], g, col * sizeof(Glyph)); - else { - for (deb = &term.line[bot][0], fin = &term.line[bot][col]; deb < fin; deb++) - deb->mode = ATTR_REVERSE, - deb->u = ' ', - deb->fg = defaultfg, deb->bg = defaultbg; - term.line[bot][0].u = ksym; - } - - term.dirty[bot] = 1; - drawregion(0, bot, col, bot + 1); -} - -void select_or_drawcursor(int selectsearch_mode, int type) { - int done = 0; - - if ( selectsearch_mode & 1 ) { - selextend(term.c.x, term.c.y, type, done); - xsetsel(getsel()); - } -} - -void search(int selectsearch_mode, Rune *target, int ptarget, int incr, int type, TCursor *cu) { - Rune *r; - int i, bound = (term.col * cu->y + cu->x) * (incr > 0) + incr; - - for (i = term.col * term.c.y + term.c.x + incr; i != bound; i += incr) { - for (r = target; r - target < ptarget; r++) { - if ( *r == term.line[(i + r - target) / term.col][(i + r - target) % term.col].u ) { - if ( r - target == ptarget - 1 ) break; - } else { - r = NULL; - break; - } - } - if ( r != NULL ) break; - } - - if ( i != bound ) { - term.c.y = i / term.col, term.c.x = i % term.col; - select_or_drawcursor(selectsearch_mode, type); - } -} - -int trt_kbdselect(KeySym ksym, char *buf, int len) { - static TCursor cu; - static Rune target[64]; - static int type = 1, ptarget, in_use; - static int sens, quant; - static char selectsearch_mode; - int i, bound, *xy; - - - if ( selectsearch_mode & 2 ) { - if ( ksym == XK_Return ) { - selectsearch_mode ^= 2; - set_notifmode(selectsearch_mode, -2); - if ( ksym == XK_Escape ) ptarget = 0; - return 0; - } - else if ( ksym == XK_BackSpace ) { - if ( !ptarget ) return 0; - term.line[term.bot][ptarget--].u = ' '; - } - else if ( len < 1 ) { - return 0; - } - else if ( ptarget == term.col || ksym == XK_Escape ) { - return 0; - } - else { - utf8decode(buf, &target[ptarget++], len); - term.line[term.bot][ptarget].u = target[ptarget - 1]; - } - - if ( ksym != XK_BackSpace ) - search(selectsearch_mode, &target[0], ptarget, sens, type, &cu); - - term.dirty[term.bot] = 1; - drawregion(0, term.bot, term.col, term.bot + 1); - return 0; - } - - switch ( ksym ) { - case -1 : - in_use = 1; - cu.x = term.c.x, cu.y = term.c.y; - set_notifmode(0, ksym); - return MODE_KBDSELECT; - case XK_s : - if ( selectsearch_mode & 1 ) - selclear(); - else - selstart(term.c.x, term.c.y, 0); - set_notifmode(selectsearch_mode ^= 1, ksym); - break; - case XK_t : - selextend(term.c.x, term.c.y, type ^= 3, i = 0); /* 2 fois */ - selextend(term.c.x, term.c.y, type, i = 0); - break; - case XK_slash : - case XK_KP_Divide : - case XK_question : - ksym &= XK_question; /* Divide to slash */ - sens = (ksym == XK_slash) ? -1 : 1; - ptarget = 0; - set_notifmode(15, ksym); - selectsearch_mode ^= 2; - break; - case XK_Escape : - if ( !in_use ) break; - selclear(); - case XK_Return : - set_notifmode(4, ksym); - term.c.x = cu.x, term.c.y = cu.y; - select_or_drawcursor(selectsearch_mode = 0, type); - in_use = quant = 0; - return MODE_KBDSELECT; - case XK_n : - case XK_N : - if ( ptarget ) - search(selectsearch_mode, &target[0], ptarget, (ksym == XK_n) ? -1 : 1, type, &cu); - break; - case XK_BackSpace : - term.c.x = 0; - select_or_drawcursor(selectsearch_mode, type); - break; - case XK_dollar : - term.c.x = term.col - 1; - select_or_drawcursor(selectsearch_mode, type); - break; - case XK_Home : - term.c.x = 0, term.c.y = 0; - select_or_drawcursor(selectsearch_mode, type); - break; - case XK_End : - term.c.x = cu.x, term.c.y = cu.y; - select_or_drawcursor(selectsearch_mode, type); - break; - case XK_Page_Up : - case XK_Page_Down : - term.c.y = (ksym == XK_Prior ) ? 0 : cu.y; - select_or_drawcursor(selectsearch_mode, type); - break; - case XK_exclam : - term.c.x = term.col >> 1; - select_or_drawcursor(selectsearch_mode, type); - break; - case XK_asterisk : - case XK_KP_Multiply : - term.c.x = term.col >> 1; - case XK_underscore : - term.c.y = cu.y >> 1; - select_or_drawcursor(selectsearch_mode, type); - break; - default : - if ( ksym >= XK_0 && ksym <= XK_9 ) { /* 0-9 keyboard */ - quant = (quant * 10) + (ksym ^ XK_0); - return 0; - } - else if ( ksym >= XK_KP_0 && ksym <= XK_KP_9 ) { /* 0-9 numpad */ - quant = (quant * 10) + (ksym ^ XK_KP_0); - return 0; - } - else if ( ksym == XK_k || ksym == XK_h ) - i = ksym & 1; - else if ( ksym == XK_l || ksym == XK_j ) - i = ((ksym & 6) | 4) >> 1; - else if ( (XK_Home & ksym) != XK_Home || (i = (ksym ^ XK_Home) - 1) > 3 ) - break; - - xy = (i & 1) ? &term.c.y : &term.c.x; - sens = (i & 2) ? 1 : -1; - bound = (i >> 1 ^ 1) ? 0 : (i ^ 3) ? term.col - 1 : term.bot; - - if ( quant == 0 ) - quant++; - - if ( *xy == bound && ((sens < 0 && bound == 0) || (sens > 0 && bound > 0)) ) - break; - - *xy += quant * sens; - if ( *xy < 0 || ( bound > 0 && *xy > bound) ) - *xy = bound; - - select_or_drawcursor(selectsearch_mode, type); - } - quant = 0; - return 0; -} - -const char * -findlastany(const char *str, const char**find, size_t len) -{ - const char *found = NULL; - int i = 0; - - for (found = str + strlen(str) - 1; found >= str; --found) { - for(i = 0; i < len; i++) { - if (strncmp(found, find[i], strlen(find[i])) == 0) { - return found; - } - } - } - - return NULL; -} - -/* -** Select and copy the previous url on screen (do nothing if there's no url). -** -** FIXME: doesn't handle urls that span multiple lines; will need to add support -** for multiline "getsel()" first -*/ void -copyurl(const Arg *arg) { - /* () and [] can appear in urls, but excluding them here will reduce false - * positives when figuring out where a given url ends. - */ - static const char URLCHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789-._~:/?#@!$&'*+,;=%"; +openUrlOnClick(int col, int row, char* url_opener) +{ + int row_start = row; + int col_start = col; + int row_end = row; + int col_end = col; - static const char* URLSTRINGS[] = {"http://", "https://"}; + if (term.line[row][col].u == ' ') + return; - int row = 0, /* row of current URL */ - col = 0, /* column of current URL start */ - colend = 0, /* column of last occurrence */ - passes = 0; /* how many rows have been scanned */ - - char linestr[term.col + 1]; - const char *c = NULL, - *match = NULL; - - row = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.y : term.bot; - LIMIT(row, term.top, term.bot); - - colend = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.x : term.col; - LIMIT(colend, 0, term.col); - - /* - ** Scan from (term.row - 1,term.col - 1) to (0,0) and find - ** next occurrance of a URL - */ - for (passes = 0; passes < term.row; passes++) { - /* Read in each column of every row until - ** we hit previous occurrence of URL - */ - for (col = 0; col < colend; ++col) - linestr[col] = term.line[row][col].u < 128 ? term.line[row][col].u : ' '; - linestr[col] = '\0'; - - if ((match = findlastany(linestr, URLSTRINGS, - sizeof(URLSTRINGS)/sizeof(URLSTRINGS[0])))) - break; - - if (--row < 0) - row = term.row - 1; - - colend = term.col; - }; - - if (match) { - size_t l = strspn(match, URLCHARS); - selstart(match - linestr, row, 0); - selextend(match - linestr + l - 1, row, SEL_REGULAR, 0); - selextend(match - linestr + l - 1, row, SEL_REGULAR, 1); - xsetsel(getsel()); - xclipcopy(); + /* while previous character is not space */ + while (term.line[row_start][col_start-1].u != ' ') { + if (col_start == 0) + { + // Before moving start pointer to the previous line we check if it ends with space + if (term.line[row_start - 1][term.col - 1].u == ' ') + break; + col_start=term.col - 1; + row_start--; + } else { + col_start--; + } } + + /* while next character is not space nor end of line */ + while (term.line[row_end][col_end].u != ' ') { + col_end++; + if (col_end == term.col - 1) + { + if (term.line[row_end + 1][0].u == ' ') + break; + col_end=0; + row_end++; + } + } + + char url[200] = ""; + int url_index=0; + do { + url[url_index] = term.line[row_start][col_start].u; + url_index++; + col_start++; + if (col_start == term.col) + { + col_start = 0; + row_start++; + } + } while (row_start != row_end || col_start != col_end); + + if (strncmp("http", url, 4) != 0) { + return; + } + + char command[strlen(url_opener)+1+strlen(url)]; + sprintf(command, "%s %s", url_opener, url); + system(command); } diff --git a/st.desktop b/st.desktop index ae3f344..0fb9692 100644 --- a/st.desktop +++ b/st.desktop @@ -3,7 +3,7 @@ Type=Application Exec=st TryExec=st Icon=st -Terminal=true +Terminal=false Categories=System;TerminalEmulator; Name=st diff --git a/st.h b/st.h index 7848557..e8b7a8b 100644 --- a/st.h +++ b/st.h @@ -4,9 +4,10 @@ #include #include -#include /* macros */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) #define LEN(a) (sizeof(a) / sizeof(a)[0]) #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) @@ -23,30 +24,26 @@ #define IS_TRUECOL(x) (1 << 24 & (x)) enum glyph_attribute { - ATTR_NULL = 0, - ATTR_BOLD = 1 << 0, - ATTR_FAINT = 1 << 1, - ATTR_ITALIC = 1 << 2, - ATTR_UNDERLINE = 1 << 3, - ATTR_BLINK = 1 << 4, - ATTR_REVERSE = 1 << 5, - ATTR_INVISIBLE = 1 << 6, - ATTR_STRUCK = 1 << 7, - ATTR_WRAP = 1 << 8, - ATTR_WIDE = 1 << 9, - ATTR_WDUMMY = 1 << 10, - ATTR_LIGA = 1 << 11, - ATTR_BOXDRAW = 1 << 11, + ATTR_NULL = 0, + ATTR_SET = 1 << 0, + ATTR_BOLD = 1 << 1, + ATTR_FAINT = 1 << 2, + ATTR_ITALIC = 1 << 3, + ATTR_UNDERLINE = 1 << 4, + ATTR_BLINK = 1 << 5, + ATTR_REVERSE = 1 << 6, + ATTR_INVISIBLE = 1 << 7, + ATTR_STRUCK = 1 << 8, + ATTR_WRAP = 1 << 9, + ATTR_WIDE = 1 << 10, + ATTR_WDUMMY = 1 << 11, + ATTR_BOXDRAW = 1 << 12, + ATTR_LIGA = 1 << 13, + ATTR_SELECTED = 1 << 14, ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, ATTR_DIRTYUNDERLINE = 1 << 15, }; -enum drawing_mode { - DRAW_NONE = 0, - DRAW_BG = 1 << 0, - DRAW_FG = 1 << 1, -}; - enum selection_mode { SEL_IDLE = 0, SEL_EMPTY = 1, @@ -90,25 +87,20 @@ typedef union { const char *s; } Arg; -void autocomplete (const Arg *); - void die(const char *, ...); void redraw(void); void draw(void); void kscrolldown(const Arg *); void kscrollup(const Arg *); -void newterm(const Arg *); -void externalpipe(const Arg *); void printscreen(const Arg *); void printsel(const Arg *); void sendbreak(const Arg *); void toggleprinter(const Arg *); -void copyurl(const Arg *); int tattrset(int); -int trt_kbdselect(KeySym, char *, int); void tnew(int, int); +int tisaltscreen(void); void tresize(int, int); void tsetdirtattr(int); void ttyhangup(void); @@ -132,7 +124,7 @@ void *xmalloc(size_t); void *xrealloc(void *, size_t); char *xstrdup(const char *); -int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b); +void openUrlOnClick(int, int, char *); int isboxdraw(Rune); ushort boxdrawindex(const Glyph *); @@ -154,6 +146,5 @@ extern char *termname; extern unsigned int tabspaces; extern unsigned int defaultfg; extern unsigned int defaultbg; -static unsigned int defaultcs; +extern unsigned int defaultcs; extern const int boxdraw, boxdraw_bold, boxdraw_braille; -extern float alpha; diff --git a/st.info b/st.info index c3bfbb1..7df13e8 100644 --- a/st.info +++ b/st.info @@ -162,7 +162,7 @@ st-mono| simpleterm monocolor, rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B, - rmcup=\E[?1049l\E[23;0;0t, + rmcup=\E[?1049l, rmir=\E[4l, rmkx=\E[?1l\E>, rmso=\E[27m, @@ -173,7 +173,7 @@ st-mono| simpleterm monocolor, sitm=\E[3m, sgr0=\E[0m, smacs=\E(0, - smcup=\E[?1049h\E[22;0;0t, + smcup=\E[?1049h, smir=\E[4h, smkx=\E[?1h\E=, smso=\E[7m, @@ -192,6 +192,7 @@ st-mono| simpleterm monocolor, Ms=\E]52;%p1%s;%p2%s\007, Se=\E[2 q, Ss=\E[%p1%d q, + Sync=\EP=%p1%ds\E\\, st| simpleterm, use=st-mono, diff --git a/win.h b/win.h index 8047e53..4a1f59b 100644 --- a/win.h +++ b/win.h @@ -21,7 +21,6 @@ enum win_mode { MODE_NUMLOCK = 1 << 17, MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ |MODE_MOUSEMANY, - MODE_KBDSELECT = 1 << 18, }; void xbell(void); @@ -31,16 +30,13 @@ void xdrawline(Line, int, int, int); void xfinishdraw(void); void xloadcols(void); int xsetcolorname(int, const char *); +int xgetcolor(int, unsigned char *, unsigned char *, unsigned char *); void xseticontitle(char *); -void xfreetitlestack(void); -void xsettitle(char *, int); -void xpushtitle(void); +void xsettitle(char *); int xsetcursor(int); void xsetmode(int, unsigned int); void xsetpointermotion(int); void xsetsel(char *); int xstartdraw(void); -void toggle_winmode(int); -void keyboard_select(const Arg *); void xximspot(int, int); void xclearwin(void); diff --git a/x.c b/x.c index 29d4edc..7509830 100644 --- a/x.c +++ b/x.c @@ -73,6 +73,8 @@ enum undercurl_slope_type { #define XK_NO_MOD 0 #define XK_SWITCH_MOD (1<<13|1<<14) +static int cursorblinks = 0; + /* function definitions used in config.h */ static void clipcopy(const Arg *); static void clippaste(const Arg *); @@ -86,9 +88,6 @@ static void ttysend(const Arg *); /* config.h for applying patches and the configuration. */ #include "config.h" -/* size of title stack */ -#define TITLESTACKSIZE 8 - /* XEMBED messages */ #define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_OUT 5 @@ -103,8 +102,6 @@ typedef XftDraw *Draw; typedef XftColor Color; typedef XftGlyphFontSpec GlyphFontSpec; -typedef unsigned long int CARD32; - /* Purely graphic info */ typedef struct { int tw, th; /* tty width and height */ @@ -136,6 +133,7 @@ typedef struct { int depth; /* bit depth */ int l, t; /* left and top offset */ int gm; /* geometry mask */ + int cyo; } XWindow; typedef struct { @@ -171,7 +169,7 @@ typedef struct { static inline ushort sixd_to_16bit(int); static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); -static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int); +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); static void xdrawglyph(Glyph, int, int); static void xclear(int, int, int, int); static int xgeommasktogravity(int); @@ -186,14 +184,15 @@ static void xhints(void); static int xloadcolor(int, const char *, Color *); static int xloadfont(Font *, FcPattern *); static void xloadfonts(const char *, double); +static int xloadsparefont(FcPattern *, int); +static void xloadsparefonts(void); static void xunloadfont(Font *); static void xunloadfonts(void); static void xsetenv(void); static void xseturgency(int); static int evcol(XEvent *); static int evrow(XEvent *); -static int xloadsparefont(FcPattern *, int); -static void xloadsparefonts(void); + static void expose(XEvent *); static void visibility(XEvent *); static void unmap(XEvent *); @@ -250,8 +249,6 @@ static DC dc; static XWindow xw; static XSelection xsel; static TermWindow win; -static int tstki; /* title stack index */ -static char *titlestack[TITLESTACKSIZE]; /* title stack */ /* Font Ring Cache */ enum { @@ -275,8 +272,6 @@ static char *usedfont = NULL; static double usedfontsize = 0; static double defaultfontsize = 0; -static int cursorblinks = 0; -static char *opt_alpha = NULL; static char *opt_class = NULL; static char **opt_cmd = NULL; static char *opt_embed = NULL; @@ -747,6 +742,7 @@ brelease(XEvent *e) return; if (btn == Button1) mousesel(e, 1); + openUrlOnClick(evcol(e), evrow(e), url_opener); } void @@ -849,9 +845,6 @@ xloadcols(void) die("could not allocate color %d\n", i); } - /* set alpha value of bg color */ - if (opt_alpha) - alpha = strtof(opt_alpha, NULL); dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); dc.col[defaultbg].pixel &= 0x00FFFFFF; dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; @@ -885,6 +878,12 @@ xsetcolorname(int x, const char *name) XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); dc.col[x] = ncolor; + if (x == defaultbg) { + dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); + dc.col[defaultbg].pixel &= 0x00FFFFFF; + dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; + } + return 0; } @@ -1006,6 +1005,7 @@ xloadfont(Font *f, FcPattern *pattern) XftResultMatch)) { if ((XftPatternGetInteger(f->match->pattern, "weight", 0, &haveattr) != XftResultMatch) || haveattr != wantattr) { + f->badweight = 1; fputs("font weight does not match\n", stderr); } } @@ -1079,6 +1079,7 @@ xloadfonts(const char *fontstr, double fontsize) /* Setting character width and height. */ win.cw = ceilf(dc.font.width * cwscale); win.ch = ceilf(dc.font.height * chscale); + xw.cyo = ceilf(dc.font.height * (chscale - 1) / 2); FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); @@ -1103,7 +1104,7 @@ xloadsparefont(FcPattern *pattern, int flags) { FcPattern *match; FcResult result; - + match = FcFontMatch(NULL, pattern, &result); if (!match) { return 1; @@ -1145,50 +1146,50 @@ xloadsparefonts(void) } for (fp = font2; fp - font2 < fc; ++fp) { - + if (**fp == '-') pattern = XftXlfdParse(*fp, False, False); else pattern = FcNameParse((FcChar8 *)*fp); - + if (!pattern) die("can't open spare font %s\n", *fp); - + if (defaultfontsize > 0) { sizeshift = usedfontsize - defaultfontsize; if (sizeshift != 0 && FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == - FcResultMatch) { + FcResultMatch) { fontval += sizeshift; FcPatternDel(pattern, FC_PIXEL_SIZE); FcPatternDel(pattern, FC_SIZE); FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontval); } } - + FcPatternAddBool(pattern, FC_SCALABLE, 1); - + FcConfigSubstitute(NULL, pattern, FcMatchPattern); XftDefaultSubstitute(xw.dpy, xw.scr, pattern); - + if (xloadsparefont(pattern, FRC_NORMAL)) die("can't open spare font %s\n", *fp); - + FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); if (xloadsparefont(pattern, FRC_ITALIC)) die("can't open spare font %s\n", *fp); - + FcPatternDel(pattern, FC_WEIGHT); FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); if (xloadsparefont(pattern, FRC_ITALICBOLD)) die("can't open spare font %s\n", *fp); - + FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); if (xloadsparefont(pattern, FRC_BOLD)) die("can't open spare font %s\n", *fp); - + FcPatternDestroy(pattern); } } @@ -1272,51 +1273,6 @@ xicdestroy(XIC xim, XPointer client, XPointer call) return 1; } -static int isSlopeRising (int x, int iPoint, int waveWidth) -{ - // . . . . - // / \ / \ / \ / \ - // / \ / \ / \ / \ - // . . . . . - - // Find absolute `x` of point - x += iPoint * (waveWidth/2); - - // Find index of absolute wave - int absSlope = x / ((float)waveWidth/2); - - return (absSlope % 2); -} - -static int getSlope (int x, int iPoint, int waveWidth) -{ - // Sizes: Caps are half width of slopes - // 1_2 1_2 1_2 1_2 - // / \ / \ / \ / \ - // / \ / \ / \ / \ - // 0 3_0 3_0 3_0 3_ - // <2-> <1> <---6----> - - // Find type of first point - int firstType; - x -= (x / waveWidth) * waveWidth; - if (x < (waveWidth * (2.f/6.f))) - firstType = UNDERCURL_SLOPE_ASCENDING; - else if (x < (waveWidth * (3.f/6.f))) - firstType = UNDERCURL_SLOPE_TOP_CAP; - else if (x < (waveWidth * (5.f/6.f))) - firstType = UNDERCURL_SLOPE_DESCENDING; - else - firstType = UNDERCURL_SLOPE_BOTTOM_CAP; - - // Find type of given point - int pointType = (iPoint % 4); - pointType += firstType; - pointType %= 4; - - return pointType; -} - void xinit(int cols, int rows) { @@ -1423,27 +1379,28 @@ xinit(int cols, int rows) /* use a png-image to set _NET_WM_ICON */ FILE* file = fopen(ICON, "r"); if (file) { - /* inititialize variables */ + /* load image in rgba-format */ const gdImagePtr icon_rgba = gdImageCreateFromPng(file); fclose(file); - const int width = gdImageSX(icon_rgba); + /* declare icon-variable which will store the image in argb-format */ + const int width = gdImageSX(icon_rgba); const int height = gdImageSY(icon_rgba); const int icon_n = width * height + 2; - CARD32 *icon_argb = g_new0(CARD32, icon_n); + long icon_argb[icon_n]; /* set width and height of the icon */ int i = 0; icon_argb[i++] = width; icon_argb[i++] = height; - /* RGBA -> ARGB */ + /* rgba -> argb */ for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { const int pixel_rgba = gdImageGetPixel(icon_rgba, x, y); - guint8* pixel_argb = (guint8*) &icon_argb[i++]; + unsigned char *pixel_argb = (unsigned char *) &icon_argb[i++]; pixel_argb[0] = gdImageBlue(icon_rgba, pixel_rgba); pixel_argb[1] = gdImageGreen(icon_rgba, pixel_rgba); pixel_argb[2] = gdImageRed(icon_rgba, pixel_rgba); /* scale alpha from 0-127 to 0-255 */ - const int alpha = 127 - gdImageAlpha(icon_rgba, pixel_rgba); + const unsigned char alpha = 127 - gdImageAlpha(icon_rgba, pixel_rgba); pixel_argb[3] = alpha == 127 ? 255 : alpha * 2; } } @@ -1451,7 +1408,7 @@ xinit(int cols, int rows) /* set _NET_WM_ICON */ xw.netwmicon = XInternAtom(xw.dpy, "_NET_WM_ICON", False); XChangeProperty(xw.dpy, xw.win, xw.netwmicon, XA_CARDINAL, 32, - PropModeReplace, (uchar *)icon_argb, icon_n); + PropModeReplace, (uchar *) icon_argb, icon_n); } xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); @@ -1516,7 +1473,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x font = &dc.bfont; frcflags = FRC_BOLD; } - yp = winy + font->ascent; + yp = winy + font->ascent + xw.cyo; } if (mode & ATTR_BOXDRAW) { @@ -1529,8 +1486,8 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x if (glyphidx) { specs[numspecs].font = font->match; specs[numspecs].glyph = glyphidx; - specs[numspecs].x = (short)xp + cxoffset; - specs[numspecs].y = (short)yp + cyoffset; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; xp += runewidth; numspecs++; continue; @@ -1615,8 +1572,53 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x return numspecs; } +static int isSlopeRising (int x, int iPoint, int waveWidth) +{ + // . . . . + // / \ / \ / \ / \ + // / \ / \ / \ / \ + // . . . . . + + // Find absolute `x` of point + x += iPoint * (waveWidth/2); + + // Find index of absolute wave + int absSlope = x / ((float)waveWidth/2); + + return (absSlope % 2); +} + +static int getSlope (int x, int iPoint, int waveWidth) +{ + // Sizes: Caps are half width of slopes + // 1_2 1_2 1_2 1_2 + // / \ / \ / \ / \ + // / \ / \ / \ / \ + // 0 3_0 3_0 3_0 3_ + // <2-> <1> <---6----> + + // Find type of first point + int firstType; + x -= (x / waveWidth) * waveWidth; + if (x < (waveWidth * (2.f/6.f))) + firstType = UNDERCURL_SLOPE_ASCENDING; + else if (x < (waveWidth * (3.f/6.f))) + firstType = UNDERCURL_SLOPE_TOP_CAP; + else if (x < (waveWidth * (5.f/6.f))) + firstType = UNDERCURL_SLOPE_DESCENDING; + else + firstType = UNDERCURL_SLOPE_BOTTOM_CAP; + + // Find type of given point + int pointType = (iPoint % 4); + pointType += firstType; + pointType %= 4; + + return pointType; +} + void -xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int dmode) +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) { int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, @@ -1656,6 +1658,10 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i bg = &dc.col[base.bg]; } + /* Change basic system colors [0-7] to bright system colors [8-15] */ + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) + fg = &dc.col[base.fg + 8]; + if (IS_SET(MODE_REVERSE)) { if (fg == &dc.col[defaultfg]) { fg = &dc.col[defaultbg]; @@ -1692,14 +1698,9 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i } if (base.mode & ATTR_REVERSE) { - if (bg == fg) { - bg = &dc.col[defaultfg]; - fg = &dc.col[defaultbg]; - } else { - temp = fg; - fg = bg; - bg = temp; - } + temp = fg; + fg = bg; + bg = temp; } if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) @@ -1737,11 +1738,13 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); } else { /* Render the glyphs. */ - XftDrawGlyphFontSpec(xw.draw, fg, specs, len); - } + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + } /* Render underline and strikethrough. */ if (base.mode & ATTR_UNDERLINE) { + //XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, + // width, 1); // Underline Color const int widthThreshold = 28; // +1 width every widthThreshold px of font int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width @@ -1837,7 +1840,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i */ wh *= 2; - // Set the angle of the slope to 45° + // Set the angle of the slope to 45° ww = wh; // Position of wave is independent of word, it's absolute @@ -1937,7 +1940,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i // Make the underline corridor larger wh *= 2; - // Set the angle of the slope to 45° + // Set the angle of the slope to 45° ww = wh; ww *= 1 + capRatio; // Add a bit of width for the cap @@ -2096,7 +2099,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i } if (base.mode & ATTR_STRUCK) { - XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, + XftDrawRect(xw.draw, fg, winx, winy + xw.cyo + 2 * dc.font.ascent * chscale / 3, width, 1); } @@ -2111,7 +2114,7 @@ xdrawglyph(Glyph g, int x, int y) XftGlyphFontSpec spec; numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); - xdrawglyphfontspecs(&spec, g, numspecs, x, y, DRAW_BG | DRAW_FG); + xdrawglyphfontspecs(&spec, g, numspecs, x, y); } void @@ -2123,9 +2126,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int le /* remove the old cursor */ if (selected(ox, oy)) og.mode ^= ATTR_REVERSE; - - /* Redraw the line where cursor was previously. - * It will restore the ligatures broken by the cursor. */ + //xdrawglyph(og, ox, oy); xdrawline(line, 0, oy, len); if (IS_SET(MODE_HIDE)) @@ -2257,30 +2258,10 @@ xseticontitle(char *p) } void -xfreetitlestack(void) +xsettitle(char *p) { - for (int i = 0; i < LEN(titlestack); i++) { - free(titlestack[i]); - titlestack[i] = NULL; - } -} - -void -xsettitle(char *p, int pop) -{ - XTextProperty prop; - - free(titlestack[tstki]); - if (pop) { - titlestack[tstki] = NULL; - tstki = (tstki - 1 + TITLESTACKSIZE) % TITLESTACKSIZE; - p = titlestack[tstki] ? titlestack[tstki] : opt_title; - } else if (p) { - titlestack[tstki] = xstrdup(p); - } else { - titlestack[tstki] = NULL; - p = opt_title; - } + XTextProperty prop; + DEFAULT(p, opt_title); if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop) != Success) @@ -2290,16 +2271,6 @@ xsettitle(char *p, int pop) XFree(prop.value); } -void -xpushtitle(void) -{ - int tstkin = (tstki + 1) % TITLESTACKSIZE; - - free(titlestack[tstkin]); - titlestack[tstkin] = titlestack[tstki] ? xstrdup(titlestack[tstki]) : NULL; - tstki = tstkin; -} - int xstartdraw(void) { @@ -2311,39 +2282,32 @@ xstartdraw(void) void xdrawline(Line line, int x1, int y1, int x2) { - int i, x, ox, numspecs, numspecs_cached; + int i, x, ox, numspecs; Glyph base, new; - XftGlyphFontSpec *specs; + XftGlyphFontSpec *specs = xw.specbuf; - numspecs_cached = xmakeglyphfontspecs(xw.specbuf, &line[x1], x2 - x1, x1, y1); - - /* Draw line in 2 passes: background and foreground. This way wide glyphs - won't get truncated (#223) */ - for (int dmode = DRAW_BG; dmode <= DRAW_FG; dmode <<= 1) { - specs = xw.specbuf; - numspecs = numspecs_cached; - i = ox = 0; - for (x = x1; x < x2 && i < numspecs; x++) { - new = line[x]; - if (new.mode == ATTR_WDUMMY) - continue; - if (selected(x, y1)) - new.mode ^= ATTR_REVERSE; - if (i > 0 && ATTRCMP(base, new)) { - xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); - specs += i; - numspecs -= i; - i = 0; - } - if (i == 0) { - ox = x; - base = new; - } - i++; + numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + if (new.mode == ATTR_WDUMMY) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + specs += i; + numspecs -= i; + i = 0; } - if (i > 0) - xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); + if (i == 0) { + ox = x; + base = new; + } + i++; } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1); } void @@ -2520,24 +2484,14 @@ kpress(XEvent *ev) len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); else len = XLookupString(e, buf, sizeof buf, &ksym, NULL); - if ( IS_SET(MODE_KBDSELECT) ) { - if ( match(XK_NO_MOD, e->state) || - (XK_Shift_L | XK_Shift_R) & e->state ) - win.mode ^= trt_kbdselect(ksym, buf, len); - return; - } /* 1. shortcuts */ for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { if (ksym == bp->keysym && match(bp->mod, e->state)) { - if (bp -> func != autocomplete) - autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); bp->func(&(bp->arg)); return; } } - autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); - /* 2. custom keys from config.h */ if ((customkey = kmap(ksym, e->state))) { ttywrite(customkey, strlen(customkey), 1); @@ -2591,6 +2545,9 @@ resize(XEvent *e) cresize(e->xconfigure.width, e->xconfigure.height); } +int tinsync(uint); +int ttyread_pending(); + void run(void) { @@ -2625,7 +2582,7 @@ run(void) FD_SET(ttyfd, &rfd); FD_SET(xfd, &rfd); - if (XPending(xw.dpy)) + if (XPending(xw.dpy) || ttyread_pending()) timeout = 0; /* existing events might not set xfd */ seltv.tv_sec = timeout / 1E3; @@ -2639,7 +2596,8 @@ run(void) } clock_gettime(CLOCK_MONOTONIC, &now); - if (FD_ISSET(ttyfd, &rfd)) + int ttyin = FD_ISSET(ttyfd, &rfd) || ttyread_pending(); + if (ttyin) ttyread(); xev = 0; @@ -2663,7 +2621,7 @@ run(void) * maximum latency intervals during `cat huge.txt`, and perfect * sync with periodic updates from animations/key-repeats/etc. */ - if (FD_ISSET(ttyfd, &rfd) || xev) { + if (ttyin || xev) { if (!drawing) { trigger = now; if (IS_SET(MODE_BLINK)) { @@ -2678,6 +2636,18 @@ run(void) continue; /* we have time, try to find idle */ } + if (tinsync(su_timeout)) { + /* + * on synchronized-update draw-suspension: don't reset + * drawing so that we draw ASAP once we can (just after + * ESU). it won't be too soon because we already can + * draw now but we skip. we set timeout > 0 to draw on + * SU-timeout even without new content. + */ + timeout = minlatency; + continue; + } + /* idle detected or maxlatency exhausted -> draw */ timeout = -1; if (blinktimeout && (cursorblinks || tattrset(ATTR_BLINK))) { @@ -2764,14 +2734,6 @@ usage(void) " [stty_args ...]\n", argv0, argv0); } -void toggle_winmode(int flag) { - win.mode ^= flag; -} - -void keyboard_select(const Arg *dummy) { - win.mode ^= trt_kbdselect(-1, NULL, 0); -} - int main(int argc, char *argv[]) { @@ -2784,7 +2746,8 @@ main(int argc, char *argv[]) allowaltscreen = 0; break; case 'A': - opt_alpha = EARGF(usage()); + alpha = strtof(EARGF(usage()), NULL); + LIMIT(alpha, 0.0, 1.0); break; case 'c': opt_class = EARGF(usage());