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 85e7da2..d0068c9 100755 Binary files a/st and b/st differ 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());