From 4de41c65c1a68f8ff5799719b004989ca7bb89cf Mon Sep 17 00:00:00 2001 From: z3rOR0ne Date: Fri, 17 Feb 2023 22:59:37 -0800 Subject: [PATCH] :sparkles: Added box to main st, kept old bst --- bst/FAQ | 250 +++ bst/LEGACY | 17 + bst/LICENSE | 34 + bst/Makefile | 56 + bst/README | 34 + bst/TODO | 28 + bst/arg.h | 50 + bst/bst | Bin 0 -> 105896 bytes bst/config.def.h | 479 ++++++ bst/config.h | 482 ++++++ bst/config.mk | 35 + bst/st | Bin 0 -> 111048 bytes bst/st-alpha-20220206-0.8.5.diff | 146 ++ bst/st-scrollback-0.8.5.diff | 350 ++++ bst/st.1 | 177 ++ bst/st.c | 2761 ++++++++++++++++++++++++++++++ bst/st.h | 131 ++ bst/st.h.orig | 130 ++ bst/st.info | 239 +++ bst/st.o | Bin 0 -> 81544 bytes bst/win.h | 40 + bst/x.c | 2100 +++++++++++++++++++++++ bst/x.o | Bin 0 -> 75856 bytes st/Makefile | 3 +- st/b/boxdraw.c | 194 +++ st/b/boxdraw_data.h | 214 +++ st/boxdraw.c | 194 +++ st/boxdraw.o | Bin 0 -> 7432 bytes st/boxdraw_data.h | 214 +++ st/bst | Bin 111032 -> 0 bytes st/config.def.h | 12 + st/st | Bin 111048 -> 110312 bytes st/st-boxdraw_v2-0.8.5.diff | 583 +++++++ st/st.c | 3 + st/st.c.orig | 2761 ++++++++++++++++++++++++++++++ st/st.h | 9 + st/st.h.orig | 1 + st/st.h.rej | 7 + st/st.o | Bin 81536 -> 81664 bytes st/x.c | 21 +- st/x.c.orig | 2100 +++++++++++++++++++++++ st/x.o | Bin 75848 -> 76296 bytes 42 files changed, 13849 insertions(+), 6 deletions(-) create mode 100644 bst/FAQ create mode 100644 bst/LEGACY create mode 100644 bst/LICENSE create mode 100644 bst/Makefile create mode 100644 bst/README create mode 100644 bst/TODO create mode 100644 bst/arg.h create mode 100755 bst/bst create mode 100644 bst/config.def.h create mode 100644 bst/config.h create mode 100644 bst/config.mk create mode 100755 bst/st create mode 100644 bst/st-alpha-20220206-0.8.5.diff create mode 100644 bst/st-scrollback-0.8.5.diff create mode 100644 bst/st.1 create mode 100644 bst/st.c create mode 100644 bst/st.h create mode 100644 bst/st.h.orig create mode 100644 bst/st.info create mode 100644 bst/st.o create mode 100644 bst/win.h create mode 100644 bst/x.c create mode 100644 bst/x.o create mode 100644 st/b/boxdraw.c create mode 100644 st/b/boxdraw_data.h create mode 100644 st/boxdraw.c create mode 100644 st/boxdraw.o create mode 100644 st/boxdraw_data.h delete mode 100755 st/bst create mode 100644 st/st-boxdraw_v2-0.8.5.diff create mode 100644 st/st.c.orig create mode 100644 st/st.h.rej create mode 100644 st/x.c.orig diff --git a/bst/FAQ b/bst/FAQ new file mode 100644 index 00000000..969b195e --- /dev/null +++ b/bst/FAQ @@ -0,0 +1,250 @@ +## Why does st not handle utmp entries? + +Use the excellent tool of [utmp](https://git.suckless.org/utmp/) for this task. + + +## Some _random program_ complains that st is unknown/not recognised/unsupported/whatever! + +It means that st doesn’t have any terminfo entry on your system. Chances are +you did not `make install`. If you just want to test it without installing it, +you can manually run `tic -sx st.info`. + + +## Nothing works, and nothing is said about an unknown terminal! + +* Some programs just assume they’re running in xterm i.e. they don’t rely on + terminfo. What you see is the current state of the “xterm compliance”. +* Some programs don’t complain about the lacking st description and default to + another terminal. In that case see the question about terminfo. + + +## How do I scroll back up? + +* Using a terminal multiplexer. + * `st -e tmux` using C-b [ + * `st -e screen` using C-a ESC +* Using the excellent tool of [scroll](https://git.suckless.org/scroll/). +* Using the scrollback [patch](https://st.suckless.org/patches/scrollback/). + + +## I would like to have utmp and/or scroll functionality by default + +You can add the absolute path of both programs in your config.h file. You only +have to modify the value of utmp and scroll variables. + + +## Why doesn't the Del key work in some programs? + +Taken from the terminfo manpage: + + If the terminal has a keypad that transmits codes when the keys + are pressed, this information can be given. Note that it is not + possible to handle terminals where the keypad only works in + local (this applies, for example, to the unshifted HP 2621 keys). + If the keypad can be set to transmit or not transmit, give these + codes as smkx and rmkx. Otherwise the keypad is assumed to + always transmit. + +In the st case smkx=E[?1hE= and rmkx=E[?1lE>, so it is mandatory that +applications which want to test against keypad keys send these +sequences. + +But buggy applications (like bash and irssi, for example) don't do this. A fast +solution for them is to use the following command: + + $ printf '\033[?1h\033=' >/dev/tty + +or + $ tput smkx + +In the case of bash, readline is used. Readline has a different note in its +manpage about this issue: + + enable-keypad (Off) + When set to On, readline will try to enable the + application keypad when it is called. Some systems + need this to enable arrow keys. + +Adding this option to your .inputrc will fix the keypad problem for all +applications using readline. + +If you are using zsh, then read the zsh FAQ +: + + It should be noted that the O / [ confusion can occur with other keys + such as Home and End. Some systems let you query the key sequences + sent by these keys from the system's terminal database, terminfo. + Unfortunately, the key sequences given there typically apply to the + mode that is not the one zsh uses by default (it's the "application" + mode rather than the "raw" mode). Explaining the use of terminfo is + outside of the scope of this FAQ, but if you wish to use the key + sequences given there you can tell the line editor to turn on + "application" mode when it starts and turn it off when it stops: + + function zle-line-init () { echoti smkx } + function zle-line-finish () { echoti rmkx } + zle -N zle-line-init + zle -N zle-line-finish + +Putting these lines into your .zshrc will fix the problems. + + +## How can I use meta in 8bit mode? + +St supports meta in 8bit mode, but the default terminfo entry doesn't +use this capability. If you want it, you have to use the 'st-meta' value +in TERM. + + +## I cannot compile st in OpenBSD + +OpenBSD lacks librt, despite it being mandatory in POSIX +. +If you want to compile st for OpenBSD you have to remove -lrt from config.mk, and +st will compile without any loss of functionality, because all the functions are +included in libc on this platform. + + +## The Backspace Case + +St is emulating the Linux way of handling backspace being delete and delete being +backspace. + +This is an issue that was discussed in suckless mailing list +. Here is why some old grumpy +terminal users wants its backspace to be how he feels it: + + Well, I am going to comment why I want to change the behaviour + of this key. When ASCII was defined in 1968, communication + with computers was done using punched cards, or hardcopy + terminals (basically a typewriter machine connected with the + computer using a serial port). ASCII defines DELETE as 7F, + because, in punched-card terms, it means all the holes of the + card punched; it is thus a kind of 'physical delete'. In the + same way, the BACKSPACE key was a non-destructive backspace, + as on a typewriter. So, if you wanted to delete a character, + you had to BACKSPACE and then DELETE. Another use of BACKSPACE + was to type accented characters, for example 'a BACKSPACE `'. + The VT100 had no BACKSPACE key; it was generated using the + CONTROL key as another control character (CONTROL key sets to + 0 b7 b6 b5, so it converts H (code 0x48) into BACKSPACE (code + 0x08)), but it had a DELETE key in a similar position where + the BACKSPACE key is located today on common PC keyboards. + All the terminal emulators emulated the difference between + these keys correctly: the backspace key generated a BACKSPACE + (^H) and delete key generated a DELETE (^?). + + But a problem arose when Linus Torvalds wrote Linux. Unlike + earlier terminals, the Linux virtual terminal (the terminal + emulator integrated in the kernel) returned a DELETE when + backspace was pressed, due to the VT100 having a DELETE key in + the same position. This created a lot of problems (see [1] + and [2]). Since Linux has become the king, a lot of terminal + emulators today generate a DELETE when the backspace key is + pressed in order to avoid problems with Linux. The result is + that the only way of generating a BACKSPACE on these systems + is by using CONTROL + H. (I also think that emacs had an + important point here because the CONTROL + H prefix is used + in emacs in some commands (help commands).) + + From point of view of the kernel, you can change the key + for deleting a previous character with stty erase. When you + connect a real terminal into a machine you describe the type + of terminal, so getty configures the correct value of stty + erase for this terminal. In the case of terminal emulators, + however, you don't have any getty that can set the correct + value of stty erase, so you always get the default value. + For this reason, it is necessary to add 'stty erase ^H' to your + profile if you have changed the value of the backspace key. + Of course, another solution is for st itself to modify the + value of stty erase. I usually have the inverse problem: + when I connect to non-Unix machines, I have to press CONTROL + + h to get a BACKSPACE. The inverse problem occurs when a user + connects to my Unix machines from a different system with a + correct backspace key. + + [1] http://www.ibb.net/~anne/keyboard.html + [2] http://www.tldp.org/HOWTO/Keyboard-and-Console-HOWTO-5.html + + +## But I really want the old grumpy behaviour of my terminal + +Apply [1]. + +[1] https://st.suckless.org/patches/delkey + + +## Why do images not work in st using the w3m image hack? + +w3mimg uses a hack that draws an image on top of the terminal emulator Drawable +window. The hack relies on the terminal to use a single buffer to draw its +contents directly. + +st uses double-buffered drawing so the image is quickly replaced and may show a +short flicker effect. + +Below is a patch example to change st double-buffering to a single Drawable +buffer. + +diff --git a/x.c b/x.c +--- a/x.c ++++ b/x.c +@@ -732,10 +732,6 @@ xresize(int col, int row) + win.tw = col * win.cw; + win.th = row * win.ch; + +- XFreePixmap(xw.dpy, xw.buf); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); +- XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + + /* resize to new width */ +@@ -1148,8 +1144,7 @@ xinit(int cols, int rows) + gcvalues.graphics_exposures = False; + dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, + &gcvalues); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); ++ xw.buf = xw.win; + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + +@@ -1632,8 +1627,6 @@ xdrawline(Line line, int x1, int y1, int x2) + void + xfinishdraw(void) + { +- XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, +- win.h, 0, 0); + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(MODE_REVERSE)? + defaultfg : defaultbg].pixel); + + +## BadLength X error in Xft when trying to render emoji + +Xft makes st crash when rendering color emojis with the following error: + +"X Error of failed request: BadLength (poly request too large or internal Xlib length error)" + Major opcode of failed request: 139 (RENDER) + Minor opcode of failed request: 20 (RenderAddGlyphs) + Serial number of failed request: 1595 + Current serial number in output stream: 1818" + +This is a known bug in Xft (not st) which happens on some platforms and +combination of particular fonts and fontconfig settings. + +See also: +https://gitlab.freedesktop.org/xorg/lib/libxft/issues/6 +https://bugs.freedesktop.org/show_bug.cgi?id=107534 +https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + +The solution is to remove color emoji fonts or disable this in the fontconfig +XML configuration. As an ugly workaround (which may work only on newer +fontconfig versions (FC_COLOR)), the following code can be used to mask color +fonts: + + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + +Please don't bother reporting this bug to st, but notify the upstream Xft +developers about fixing this bug. diff --git a/bst/LEGACY b/bst/LEGACY new file mode 100644 index 00000000..bf28b1eb --- /dev/null +++ b/bst/LEGACY @@ -0,0 +1,17 @@ +A STATEMENT ON LEGACY SUPPORT + +In the terminal world there is much cruft that comes from old and unsup‐ +ported terminals that inherit incompatible modes and escape sequences +which noone is able to know, except when he/she comes from that time and +developed a graphical vt100 emulator at that time. + +One goal of st is to only support what is really needed. When you en‐ +counter a sequence which you really need, implement it. But while you +are at it, do not add the other cruft you might encounter while sneek‐ +ing at other terminal emulators. History has bloated them and there is +no real evidence that most of the sequences are used today. + + +Christoph Lohmann <20h@r-36.net> +2012-09-13T07:00:36.081271045+02:00 + diff --git a/bst/LICENSE b/bst/LICENSE new file mode 100644 index 00000000..d80eb47f --- /dev/null +++ b/bst/LICENSE @@ -0,0 +1,34 @@ +MIT/X Consortium License + +© 2014-2020 Hiltjo Posthuma +© 2018 Devin J. Pohly +© 2014-2017 Quentin Rameau +© 2009-2012 Aurélien APTEL +© 2008-2017 Anselm R Garbe +© 2012-2017 Roberto E. Vargas Caballero +© 2012-2016 Christoph Lohmann <20h at r-36 dot net> +© 2013 Eon S. Jeon +© 2013 Alexander Sedov +© 2013 Mark Edgar +© 2013-2014 Eric Pruitt +© 2013 Michael Forney +© 2013-2014 Markus Teich +© 2014-2015 Laslo Hunhold + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/bst/Makefile b/bst/Makefile new file mode 100644 index 00000000..01a6d06e --- /dev/null +++ b/bst/Makefile @@ -0,0 +1,56 @@ +# st - simple terminal +# See LICENSE file for copyright and license details. +.POSIX: + +include config.mk + +SRC = st.c x.c +OBJ = $(SRC:.c=.o) + +all: options st + +options: + @echo st build options: + @echo "CFLAGS = $(STCFLAGS)" + @echo "LDFLAGS = $(STLDFLAGS)" + @echo "CC = $(CC)" + +config.h: + cp config.def.h config.h + +.c.o: + $(CC) $(STCFLAGS) -c $< + +st.o: config.h st.h win.h +x.o: arg.h config.h st.h win.h + +$(OBJ): config.h config.mk + +st: $(OBJ) + $(CC) -o $@ $(OBJ) $(STLDFLAGS) + +clean: + rm -f st $(OBJ) st-$(VERSION).tar.gz + +dist: clean + 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 $(SRC)\ + st-$(VERSION) + tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz + rm -rf st-$(VERSION) + +install: st + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f st $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/st + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 + @echo Please see the README file regarding the terminfo entry of st. + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/st + rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 + +.PHONY: all options clean dist install uninstall diff --git a/bst/README b/bst/README new file mode 100644 index 00000000..6a846ed0 --- /dev/null +++ b/bst/README @@ -0,0 +1,34 @@ +st - simple terminal +-------------------- +st is a simple terminal emulator for X which sucks less. + + +Requirements +------------ +In order to build st you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (st is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install st (if +necessary as root): + + make clean install + + +Running st +---------- +If you did not install st with make clean install, you must compile +the st terminfo entry with the following command: + + tic -sx st.info + +See the man page for additional details. + +Credits +------- +Based on Aurélien APTEL bt source code. + diff --git a/bst/TODO b/bst/TODO new file mode 100644 index 00000000..5f74cd52 --- /dev/null +++ b/bst/TODO @@ -0,0 +1,28 @@ +vt emulation +------------ + +* double-height support + +code & interface +---------------- + +* add a simple way to do multiplexing + +drawing +------- +* add diacritics support to xdraws() + * switch to a suckless font drawing library +* make the font cache simpler +* add better support for brightening of the upper colors + +bugs +---- + +* fix shift up/down (shift selection in emacs) +* remove DEC test sequence when appropriate + +misc +---- + + $ grep -nE 'XXX|TODO' st.c + diff --git a/bst/arg.h b/bst/arg.h new file mode 100644 index 00000000..a22e019e --- /dev/null +++ b/bst/arg.h @@ -0,0 +1,50 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + int i_;\ + for (i_ = 1, brk_ = 0, argv_ = argv;\ + argv[0][i_] && !brk_;\ + i_++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][i_];\ + switch (argc_) + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define EARGF(x) ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][i_+1] != '\0')?\ + (&argv[0][i_+1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][i_+1] != '\0')?\ + (&argv[0][i_+1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/bst/bst b/bst/bst new file mode 100755 index 0000000000000000000000000000000000000000..f5036745561a5c6f93196b8846633ad15a7c0fd5 GIT binary patch literal 105896 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

VJ7PYl8AZYJIvj{gSRI;{asjcQ78ytw-?lM;f@L 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 uJ@`eBa)_Lk<+qlIZ#-D?14zc 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 literal 0 HcmV?d00001 diff --git a/bst/config.def.h b/bst/config.def.h new file mode 100644 index 00000000..279890fe --- /dev/null +++ b/bst/config.def.h @@ -0,0 +1,479 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; +static int borderpx = 2; + +/* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e + * 2: scroll and/or utmp + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/sh"; +char *utmp = NULL; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = NULL; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +char *vtiden = "\033[?6c"; + +/* Kerning / character bounding-box multipliers */ +static float cwscale = 1.0; +static float chscale = 1.0; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +wchar_t *worddelimiters = L" "; + +/* selection timeouts (in milliseconds) */ +static unsigned int doubleclicktimeout = 300; +static unsigned int tripleclicktimeout = 600; + +/* alt screens */ +int allowaltscreen = 1; + +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 8; +static double maxlatency = 33; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 800; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 2; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "st-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +/* bg opacity */ +float alpha = 0.8; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "black", + "red3", + "green3", + "yellow3", + "blue2", + "magenta3", + "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", + "red", + "green", + "yellow", + "#5c5cff", + "magenta", + "cyan", + "white", + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#cccccc", + "#555555", + "gray90", /* default foreground colour */ + "black", /* default background colour */ +}; + + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 258; +unsigned int defaultbg = 259; +unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 2; + +/* + * Default columns and rows numbers + */ + +static unsigned int cols = 80; +static unsigned int rows = 24; + +/* + * Default colour and shape of the mouse cursor + */ +static unsigned int mouseshape = XC_xterm; +static unsigned int mousefg = 7; +static unsigned int mousebg = 0; + +/* + * Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. + */ +static unsigned int defaultattr = 11; + +/* + * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). + * Note that if you want to use ShiftMask with selmasks, set this to an other + * modifier, set to 0 to not use it. + */ +static uint forcemousemod = ShiftMask; + +/* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. + */ +static MouseShortcut mshortcuts[] = { + /* mask button function argument release */ + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, + { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, +}; + +/* Internal keyboard shortcuts. */ +#define MODKEY Mod1Mask +#define TERMMOD (ControlMask|ShiftMask) + +static Shortcut shortcuts[] = { + /* mask keysym function argument */ + { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, + { ControlMask, XK_Print, toggleprinter, {.i = 0} }, + { ShiftMask, XK_Print, printscreen, {.i = 0} }, + { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, + { TERMMOD, XK_Prior, zoom, {.f = +1} }, + { TERMMOD, XK_Next, zoom, {.f = -1} }, + { TERMMOD, XK_Home, zoomreset, {.f = 0} }, + { TERMMOD, XK_C, clipcopy, {.i = 0} }, + { TERMMOD, XK_V, clippaste, {.i = 0} }, + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, +}; + +/* + * Special keys (change & recompile st.info accordingly) + * + * Mask value: + * * Use XK_ANY_MOD to match the key no matter modifiers state + * * Use XK_NO_MOD to match the key alone (no modifiers) + * appkey value: + * * 0: no value + * * > 0: keypad application mode enabled + * * = 2: term.numlock = 1 + * * < 0: keypad application mode disabled + * appcursor value: + * * 0: no value + * * > 0: cursor application mode enabled + * * < 0: cursor application mode disabled + * + * Be careful with the order of the definitions because st searches in + * this table sequentially, so any XK_ANY_MOD must be in the last + * position for a key. + */ + +/* + * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) + * to be mapped below, add them to this array. + */ +static KeySym mappedkeys[] = { -1 }; + +/* + * State bits to ignore when matching key or button events. By default, + * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. + */ +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; + +/* + * This is the huge key array which defines all compatibility to the Linux + * world. Please decide about changes wisely. + */ +static Key key[] = { + /* keysym mask string appkey appcursor */ + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + { XK_KP_End, ControlMask, "\033[J", -1, 0}, + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + { XK_Return, Mod1Mask, "\033\r", 0, 0}, + { XK_Return, XK_ANY_MOD, "\r", 0, 0}, + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_Insert, ControlMask, "\033[L", -1, 0}, + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_Delete, ControlMask, "\033[M", -1, 0}, + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, +}; + +/* + * Selection types' masks. + * Use the same masks as usual. + * Button1Mask is always unset, to make masks match between ButtonPress. + * ButtonRelease and MotionNotify. + * If no match is found, regular selection is used. + */ +static uint selmasks[] = { + [SEL_RECTANGULAR] = Mod1Mask, +}; + +/* + * Printable characters in ASCII, used to estimate the advance width + * of single wide characters. + */ +static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; diff --git a/bst/config.h b/bst/config.h new file mode 100644 index 00000000..b71c44f3 --- /dev/null +++ b/bst/config.h @@ -0,0 +1,482 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "Source Code Pro:pixelsize=18.5:antialias=true:autohint=true"; +// static char *font = "mono:pixelsize=18.5:antialias=true:autohint=true"; +static int borderpx = 2; + +/* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e + * 2: scroll and/or utmp + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/sh"; +char *utmp = NULL; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = NULL; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +char *vtiden = "\033[?6c"; + +/* Kerning / character bounding-box multipliers */ +static float cwscale = 1.0; +static float chscale = 1.0; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +wchar_t *worddelimiters = L" "; + +/* selection timeouts (in milliseconds) */ +static unsigned int doubleclicktimeout = 300; +static unsigned int tripleclicktimeout = 600; + +/* alt screens */ +int allowaltscreen = 1; + +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 8; +static double maxlatency = 33; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 800; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 2; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "st-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +/* bg opacity */ +float alpha = 0.7; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "#3b4252", /* black */ + "#bf616a", /* red */ + "#a3be8c", /* green */ + "#ebcb8b", /* yellow */ + "#81a1c1", /* blue */ + "#b48ead", /* magenta */ + "#88c0d0", /* cyan */ + "#e5e9f0", /* white */ + + /* 8 bright colors */ + "#4c566a", /* black */ + "#bf616a", /* red */ + "#a3be8c", /* green */ + "#ebcb8b", /* yellow */ + "#81a1c1", /* blue */ + "#b48ead", /* magenta */ + "#8fbcbb", /* cyan */ + "#eceff4", /* white */ + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + // "#2e3440", // background // + // "#1d222b", // background // + "#1a1e26", // background // + "#d8dee9", /* foreground */ +}; + + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 257; +unsigned int defaultbg = 256; +unsigned int defaultcs = 257; +static unsigned int defaultrcs = 256; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 2; + +/* + * Default columns and rows numbers + */ + +// static unsigned int cols = 80; +static unsigned int cols = 140; +// static unsigned int rows = 24; +static unsigned int rows = 36; + +/* + * Default colour and shape of the mouse cursor + */ +static unsigned int mouseshape = XC_xterm; +static unsigned int mousefg = 7; +static unsigned int mousebg = 0; + +/* + * Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. + */ +static unsigned int defaultattr = 11; + +/* + * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). + * Note that if you want to use ShiftMask with selmasks, set this to an other + * modifier, set to 0 to not use it. + */ +static uint forcemousemod = ShiftMask; + +/* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. + */ +static MouseShortcut mshortcuts[] = { + /* mask button function argument release */ + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, + { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, +}; + +/* Internal keyboard shortcuts. */ +#define MODKEY Mod1Mask +#define TERMMOD (ControlMask|ShiftMask) + +static Shortcut shortcuts[] = { + /* mask keysym function argument */ + { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, + { ControlMask, XK_Print, toggleprinter, {.i = 0} }, + { ShiftMask, XK_Print, printscreen, {.i = 0} }, + { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, + { TERMMOD, XK_Prior, zoom, {.f = +1} }, + { TERMMOD, XK_Next, zoom, {.f = -1} }, + { TERMMOD, XK_Home, zoomreset, {.f = 0} }, + { TERMMOD, XK_C, clipcopy, {.i = 0} }, + { TERMMOD, XK_V, clippaste, {.i = 0} }, + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, +}; + +/* + * Special keys (change & recompile st.info accordingly) + * + * Mask value: + * * Use XK_ANY_MOD to match the key no matter modifiers state + * * Use XK_NO_MOD to match the key alone (no modifiers) + * appkey value: + * * 0: no value + * * > 0: keypad application mode enabled + * * = 2: term.numlock = 1 + * * < 0: keypad application mode disabled + * appcursor value: + * * 0: no value + * * > 0: cursor application mode enabled + * * < 0: cursor application mode disabled + * + * Be careful with the order of the definitions because st searches in + * this table sequentially, so any XK_ANY_MOD must be in the last + * position for a key. + */ + +/* + * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) + * to be mapped below, add them to this array. + */ +static KeySym mappedkeys[] = { -1 }; + +/* + * State bits to ignore when matching key or button events. By default, + * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. + */ +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; + +/* + * This is the huge key array which defines all compatibility to the Linux + * world. Please decide about changes wisely. + */ +static Key key[] = { + /* keysym mask string appkey appcursor */ + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + { XK_KP_End, ControlMask, "\033[J", -1, 0}, + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + { XK_Return, Mod1Mask, "\033\r", 0, 0}, + { XK_Return, XK_ANY_MOD, "\r", 0, 0}, + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_Insert, ControlMask, "\033[L", -1, 0}, + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_Delete, ControlMask, "\033[M", -1, 0}, + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, +}; + +/* + * Selection types' masks. + * Use the same masks as usual. + * Button1Mask is always unset, to make masks match between ButtonPress. + * ButtonRelease and MotionNotify. + * If no match is found, regular selection is used. + */ +static uint selmasks[] = { + [SEL_RECTANGULAR] = Mod1Mask, +}; + +/* + * Printable characters in ASCII, used to estimate the advance width + * of single wide characters. + */ +static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; diff --git a/bst/config.mk b/bst/config.mk new file mode 100644 index 00000000..0114bad3 --- /dev/null +++ b/bst/config.mk @@ -0,0 +1,35 @@ +# st version +VERSION = 0.8.5 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +PKG_CONFIG = pkg-config + +# includes and libs +INCS = -I$(X11INC) \ + `$(PKG_CONFIG) --cflags fontconfig` \ + `$(PKG_CONFIG) --cflags freetype2` +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ + `$(PKG_CONFIG) --libs fontconfig` \ + `$(PKG_CONFIG) --libs freetype2` + +# flags +STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 +STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) +STLDFLAGS = $(LIBS) $(LDFLAGS) + +# OpenBSD: +#CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE +#LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ +# `$(PKG_CONFIG) --libs fontconfig` \ +# `$(PKG_CONFIG) --libs freetype2` + +# compiler and linker +# CC = c99 diff --git a/bst/st b/bst/st new file mode 100755 index 0000000000000000000000000000000000000000..8a54739c09fa7eabedf559edf1713018eaf78fdf GIT binary patch literal 111048 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 literal 0 HcmV?d00001 diff --git a/bst/st-alpha-20220206-0.8.5.diff b/bst/st-alpha-20220206-0.8.5.diff new file mode 100644 index 00000000..ab029f63 --- /dev/null +++ b/bst/st-alpha-20220206-0.8.5.diff @@ -0,0 +1,146 @@ +diff --git a/config.def.h b/config.def.h +index 91ab8ca..6af616e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -93,6 +93,9 @@ char *termname = "st-256color"; + */ + unsigned int tabspaces = 8; + ++/* bg opacity */ ++float alpha = 0.8; ++ + /* Terminal colors (16 first used in escape sequence) */ + static const char *colorname[] = { + /* 8 normal colors */ +diff --git a/config.mk b/config.mk +index 4c4c5d5..0114bad 100644 +--- a/config.mk ++++ b/config.mk +@@ -16,7 +16,7 @@ PKG_CONFIG = pkg-config + INCS = -I$(X11INC) \ + `$(PKG_CONFIG) --cflags fontconfig` \ + `$(PKG_CONFIG) --cflags freetype2` +-LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ ++LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ + `$(PKG_CONFIG) --libs fontconfig` \ + `$(PKG_CONFIG) --libs freetype2` + +diff --git a/st.h b/st.h +index 519b9bd..8bb533d 100644 +--- a/st.h ++++ b/st.h +@@ -126,3 +126,4 @@ extern unsigned int tabspaces; + extern unsigned int defaultfg; + extern unsigned int defaultbg; + extern unsigned int defaultcs; ++extern float alpha; +diff --git a/x.c b/x.c +index 8a16faa..ddf4178 100644 +--- a/x.c ++++ b/x.c +@@ -105,6 +105,7 @@ typedef struct { + XSetWindowAttributes attrs; + int scr; + int isfixed; /* is fixed geometry? */ ++ int depth; /* bit depth */ + int l, t; /* left and top offset */ + int gm; /* geometry mask */ + } XWindow; +@@ -243,6 +244,7 @@ static char *usedfont = NULL; + static double usedfontsize = 0; + static double defaultfontsize = 0; + ++static char *opt_alpha = NULL; + static char *opt_class = NULL; + static char **opt_cmd = NULL; + static char *opt_embed = NULL; +@@ -736,7 +738,7 @@ xresize(int col, int row) + + XFreePixmap(xw.dpy, xw.buf); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); ++ xw.depth); + XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + +@@ -796,6 +798,13 @@ xloadcols(void) + else + die("could not allocate color %d\n", i); + } ++ ++ /* set alpha value of bg color */ ++ if (opt_alpha) ++ alpha = strtof(opt_alpha, NULL); ++ dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); ++ dc.col[defaultbg].pixel &= 0x00FFFFFF; ++ dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; + loaded = 1; + } + +@@ -1118,11 +1127,23 @@ xinit(int cols, int rows) + Window parent; + pid_t thispid = getpid(); + XColor xmousefg, xmousebg; ++ XWindowAttributes attr; ++ XVisualInfo vis; + + if (!(xw.dpy = XOpenDisplay(NULL))) + die("can't open display\n"); + xw.scr = XDefaultScreen(xw.dpy); +- xw.vis = XDefaultVisual(xw.dpy, xw.scr); ++ ++ if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { ++ parent = XRootWindow(xw.dpy, xw.scr); ++ xw.depth = 32; ++ } else { ++ XGetWindowAttributes(xw.dpy, parent, &attr); ++ xw.depth = attr.depth; ++ } ++ ++ XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); ++ xw.vis = vis.visual; + + /* font */ + if (!FcInit()) +@@ -1132,7 +1153,7 @@ xinit(int cols, int rows) + xloadfonts(usedfont, 0); + + /* colors */ +- xw.cmap = XDefaultColormap(xw.dpy, xw.scr); ++ xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); + xloadcols(); + + /* adjust fixed window geometry */ +@@ -1152,19 +1173,15 @@ xinit(int cols, int rows) + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + xw.attrs.colormap = xw.cmap; + +- if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) +- parent = XRootWindow(xw.dpy, xw.scr); + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, +- win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, ++ win.w, win.h, 0, xw.depth, InputOutput, + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &xw.attrs); + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; +- dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, +- &gcvalues); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); ++ xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); ++ dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + +@@ -2019,6 +2036,9 @@ main(int argc, char *argv[]) + case 'a': + allowaltscreen = 0; + break; ++ case 'A': ++ opt_alpha = EARGF(usage()); ++ break; + case 'c': + opt_class = EARGF(usage()); + break; diff --git a/bst/st-scrollback-0.8.5.diff b/bst/st-scrollback-0.8.5.diff new file mode 100644 index 00000000..750111d5 --- /dev/null +++ b/bst/st-scrollback-0.8.5.diff @@ -0,0 +1,350 @@ +diff --git a/config.def.h b/config.def.h +index 91ab8ca..e3b469b 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -201,6 +201,8 @@ static Shortcut shortcuts[] = { + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, ++ { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, ++ { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, + }; + + /* +diff --git a/st.c b/st.c +index 51049ba..cd750f2 100644 +--- a/st.c ++++ b/st.c +@@ -35,6 +35,7 @@ + #define ESC_ARG_SIZ 16 + #define STR_BUF_SIZ ESC_BUF_SIZ + #define STR_ARG_SIZ ESC_ARG_SIZ ++#define HISTSIZE 2000 + + /* macros */ + #define IS_SET(flag) ((term.mode & (flag)) != 0) +@@ -42,6 +43,9 @@ + #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) + #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) + #define ISDELIM(u) (u && wcschr(worddelimiters, u)) ++#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ ++ term.scr + HISTSIZE + 1) % HISTSIZE] : \ ++ term.line[(y) - term.scr]) + + enum term_mode { + MODE_WRAP = 1 << 0, +@@ -115,6 +119,9 @@ typedef struct { + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ ++ Line hist[HISTSIZE]; /* history buffer */ ++ int histi; /* history index */ ++ int scr; /* scroll back */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ +@@ -184,8 +191,8 @@ static void tnewline(int); + static void tputtab(int); + static void tputc(Rune); + static void treset(void); +-static void tscrollup(int, int); +-static void tscrolldown(int, int); ++static void tscrollup(int, int, int); ++static void tscrolldown(int, int, int); + static void tsetattr(const int *, int); + static void tsetchar(Rune, const Glyph *, int, int); + static void tsetdirt(int, int); +@@ -416,10 +423,10 @@ tlinelen(int y) + { + int i = term.col; + +- if (term.line[y][i - 1].mode & ATTR_WRAP) ++ if (TLINE(y)[i - 1].mode & ATTR_WRAP) + return i; + +- while (i > 0 && term.line[y][i - 1].u == ' ') ++ while (i > 0 && TLINE(y)[i - 1].u == ' ') + --i; + + return i; +@@ -528,7 +535,7 @@ selsnap(int *x, int *y, int direction) + * Snap around if the word wraps around at the end or + * beginning of a line. + */ +- prevgp = &term.line[*y][*x]; ++ prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; +@@ -543,14 +550,14 @@ selsnap(int *x, int *y, int direction) + yt = *y, xt = *x; + else + yt = newy, xt = newx; +- if (!(term.line[yt][xt].mode & ATTR_WRAP)) ++ if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + +- gp = &term.line[newy][newx]; ++ gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) +@@ -571,14 +578,14 @@ selsnap(int *x, int *y, int direction) + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { +- if (!(term.line[*y-1][term.col-1].mode ++ if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { +- if (!(term.line[*y][term.col-1].mode ++ if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } +@@ -609,13 +616,13 @@ getsel(void) + } + + if (sel.type == SEL_RECTANGULAR) { +- gp = &term.line[y][sel.nb.x]; ++ gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { +- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; ++ gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } +- last = &term.line[y][MIN(lastx, linelen-1)]; ++ last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + +@@ -851,6 +858,9 @@ void + ttywrite(const char *s, size_t n, int may_echo) + { + const char *next; ++ Arg arg = (Arg) { .i = term.scr }; ++ ++ kscrolldown(&arg); + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); +@@ -1062,12 +1072,52 @@ tswapscreen(void) + } + + void +-tscrolldown(int orig, int n) ++kscrolldown(const Arg* a) ++{ ++ int n = a->i; ++ ++ if (n < 0) ++ n = term.row + n; ++ ++ if (n > term.scr) ++ n = term.scr; ++ ++ if (term.scr > 0) { ++ term.scr -= n; ++ selscroll(0, -n); ++ tfulldirt(); ++ } ++} ++ ++void ++kscrollup(const Arg* a) ++{ ++ int n = a->i; ++ ++ if (n < 0) ++ n = term.row + n; ++ ++ if (term.scr <= HISTSIZE-n) { ++ term.scr += n; ++ selscroll(0, n); ++ tfulldirt(); ++ } ++} ++ ++void ++tscrolldown(int orig, int n, int copyhist) + { + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); ++ if (copyhist) { ++ term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; ++ temp = term.hist[term.histi]; ++ term.hist[term.histi] = term.line[term.bot]; ++ term.line[term.bot] = temp; ++ } ++ + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); +@@ -1078,17 +1128,28 @@ tscrolldown(int orig, int n) + term.line[i-n] = temp; + } + +- selscroll(orig, n); ++ if (term.scr == 0) ++ selscroll(orig, n); + } + + void +-tscrollup(int orig, int n) ++tscrollup(int orig, int n, int copyhist) + { + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + ++ if (copyhist) { ++ term.histi = (term.histi + 1) % HISTSIZE; ++ temp = term.hist[term.histi]; ++ term.hist[term.histi] = term.line[orig]; ++ term.line[orig] = temp; ++ } ++ ++ if (term.scr > 0 && term.scr < HISTSIZE) ++ term.scr = MIN(term.scr + n, HISTSIZE-1); ++ + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + +@@ -1098,7 +1159,8 @@ tscrollup(int orig, int n) + term.line[i+n] = temp; + } + +- selscroll(orig, -n); ++ if (term.scr == 0) ++ selscroll(orig, -n); + } + + void +@@ -1127,7 +1189,7 @@ tnewline(int first_col) + int y = term.c.y; + + if (y == term.bot) { +- tscrollup(term.top, 1); ++ tscrollup(term.top, 1, 1); + } else { + y++; + } +@@ -1292,14 +1354,14 @@ void + tinsertblankline(int n) + { + if (BETWEEN(term.c.y, term.top, term.bot)) +- tscrolldown(term.c.y, n); ++ tscrolldown(term.c.y, n, 0); + } + + void + tdeleteline(int n) + { + if (BETWEEN(term.c.y, term.top, term.bot)) +- tscrollup(term.c.y, n); ++ tscrollup(term.c.y, n, 0); + } + + int32_t +@@ -1736,11 +1798,11 @@ csihandle(void) + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); +- tscrollup(term.top, csiescseq.arg[0]); ++ tscrollup(term.top, csiescseq.arg[0], 0); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); +- tscrolldown(term.top, csiescseq.arg[0]); ++ tscrolldown(term.top, csiescseq.arg[0], 0); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); +@@ -2330,7 +2392,7 @@ eschandle(uchar ascii) + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { +- tscrollup(term.top, 1); ++ tscrollup(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } +@@ -2343,7 +2405,7 @@ eschandle(uchar ascii) + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { +- tscrolldown(term.top, 1); ++ tscrolldown(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } +@@ -2557,7 +2619,7 @@ twrite(const char *buf, int buflen, int show_ctrl) + void + tresize(int col, int row) + { +- int i; ++ int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; +@@ -2594,6 +2656,14 @@ tresize(int col, int row) + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + ++ for (i = 0; i < HISTSIZE; i++) { ++ term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); ++ for (j = mincol; j < col; j++) { ++ term.hist[i][j] = term.c.attr; ++ term.hist[i][j].u = ' '; ++ } ++ } ++ + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); +@@ -2652,7 +2722,7 @@ drawregion(int x1, int y1, int x2, int y2) + continue; + + term.dirty[y] = 0; +- xdrawline(term.line[y], x1, y, x2); ++ xdrawline(TLINE(y), x1, y, x2); + } + } + +@@ -2673,8 +2743,9 @@ draw(void) + cx--; + + drawregion(0, 0, term.col, term.row); +- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], +- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); ++ if (term.scr == 0) ++ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], ++ term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); +diff --git a/st.h b/st.h +index 519b9bd..da36b34 100644 +--- a/st.h ++++ b/st.h +@@ -81,6 +81,8 @@ void die(const char *, ...); + void redraw(void); + void draw(void); + ++void kscrolldown(const Arg *); ++void kscrollup(const Arg *); + void printscreen(const Arg *); + void printsel(const Arg *); + void sendbreak(const Arg *); diff --git a/bst/st.1 b/bst/st.1 new file mode 100644 index 00000000..39120b48 --- /dev/null +++ b/bst/st.1 @@ -0,0 +1,177 @@ +.TH ST 1 st\-VERSION +.SH NAME +st \- simple terminal +.SH SYNOPSIS +.B st +.RB [ \-aiv ] +.RB [ \-c +.IR class ] +.RB [ \-f +.IR font ] +.RB [ \-g +.IR geometry ] +.RB [ \-n +.IR name ] +.RB [ \-o +.IR iofile ] +.RB [ \-T +.IR title ] +.RB [ \-t +.IR title ] +.RB [ \-l +.IR line ] +.RB [ \-w +.IR windowid ] +.RB [[ \-e ] +.IR command +.RI [ arguments ...]] +.PP +.B st +.RB [ \-aiv ] +.RB [ \-c +.IR class ] +.RB [ \-f +.IR font ] +.RB [ \-g +.IR geometry ] +.RB [ \-n +.IR name ] +.RB [ \-o +.IR iofile ] +.RB [ \-T +.IR title ] +.RB [ \-t +.IR title ] +.RB [ \-w +.IR windowid ] +.RB \-l +.IR line +.RI [ stty_args ...] +.SH DESCRIPTION +.B st +is a simple terminal emulator. +.SH OPTIONS +.TP +.B \-a +disable alternate screens in terminal +.TP +.BI \-c " class" +defines the window class (default $TERM). +.TP +.BI \-f " font" +defines the +.I font +to use when st is run. +.TP +.BI \-g " geometry" +defines the X11 geometry string. +The form is [=][{xX}][{+-}{+-}]. See +.BR XParseGeometry (3) +for further details. +.TP +.B \-i +will fixate the position given with the -g option. +.TP +.BI \-n " name" +defines the window instance name (default $TERM). +.TP +.BI \-o " iofile" +writes all the I/O to +.I iofile. +This feature is useful when recording st sessions. A value of "-" means +standard output. +.TP +.BI \-T " title" +defines the window title (default 'st'). +.TP +.BI \-t " title" +defines the window title (default 'st'). +.TP +.BI \-w " windowid" +embeds st within the window identified by +.I windowid +.TP +.BI \-l " line" +use a tty +.I line +instead of a pseudo terminal. +.I line +should be a (pseudo-)serial device (e.g. /dev/ttyS0 on Linux for serial port +0). +When this flag is given +remaining arguments are used as flags for +.BR stty(1). +By default st initializes the serial line to 8 bits, no parity, 1 stop bit +and a 38400 baud rate. The speed is set by appending it as last argument +(e.g. 'st -l /dev/ttyS0 115200'). Arguments before the last one are +.BR stty(1) +flags. If you want to set odd parity on 115200 baud use for example 'st -l +/dev/ttyS0 parenb parodd 115200'. Set the number of bits by using for +example 'st -l /dev/ttyS0 cs7 115200'. See +.BR stty(1) +for more arguments and cases. +.TP +.B \-v +prints version information to stderr, then exits. +.TP +.BI \-e " command " [ " arguments " "... ]" +st executes +.I command +instead of the shell. If this is used it +.B must be the last option +on the command line, as in xterm / rxvt. +This option is only intended for compatibility, +and all the remaining arguments are used as a command +even without it. +.SH SHORTCUTS +.TP +.B Break +Send a break in the serial line. +Break key is obtained in PC keyboards +pressing at the same time control and pause. +.TP +.B Ctrl-Print Screen +Toggle if st should print to the +.I iofile. +.TP +.B Shift-Print Screen +Print the full screen to the +.I iofile. +.TP +.B Print Screen +Print the selection to the +.I iofile. +.TP +.B Ctrl-Shift-Page Up +Increase font size. +.TP +.B Ctrl-Shift-Page Down +Decrease font size. +.TP +.B Ctrl-Shift-Home +Reset to default font size. +.TP +.B Ctrl-Shift-y +Paste from primary selection (middle mouse button). +.TP +.B Ctrl-Shift-c +Copy the selected text to the clipboard selection. +.TP +.B Ctrl-Shift-v +Paste from the clipboard selection. +.SH CUSTOMIZATION +.B st +can be customized by creating a custom config.h and (re)compiling the source +code. This keeps it fast, secure and simple. +.SH AUTHORS +See the LICENSE file for the authors. +.SH LICENSE +See the LICENSE file for the terms of redistribution. +.SH SEE ALSO +.BR tabbed (1), +.BR utmp (1), +.BR stty (1), +.BR scroll (1) +.SH BUGS +See the TODO file in the distribution. + diff --git a/bst/st.c b/bst/st.c new file mode 100644 index 00000000..cd750f20 --- /dev/null +++ b/bst/st.c @@ -0,0 +1,2761 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st.h" +#include "win.h" + +#if defined(__linux) + #include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + #include +#elif defined(__FreeBSD__) || defined(__DragonFly__) + #include +#endif + +/* Arbitrary sizes */ +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 +#define ESC_BUF_SIZ (128*UTF_SIZ) +#define ESC_ARG_SIZ 16 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ +#define HISTSIZE 2000 + +/* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ + term.scr + HISTSIZE + 1) % HISTSIZE] : \ + term.line[(y) - term.scr]) + +enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_ALTSCREEN = 1 << 2, + MODE_CRLF = 1 << 3, + MODE_ECHO = 1 << 4, + MODE_PRINT = 1 << 5, + MODE_UTF8 = 1 << 6, +}; + +enum cursor_movement { + CURSOR_SAVE, + CURSOR_LOAD +}; + +enum cursor_state { + CURSOR_DEFAULT = 0, + CURSOR_WRAPNEXT = 1, + CURSOR_ORIGIN = 2 +}; + +enum charset { + CS_GRAPHIC0, + CS_GRAPHIC1, + CS_UK, + CS_USA, + CS_MULTI, + CS_GER, + CS_FIN +}; + +enum escape_state { + ESC_START = 1, + ESC_CSI = 2, + ESC_STR = 4, /* DCS, OSC, PM, APC */ + ESC_ALTCHARSET = 8, + ESC_STR_END = 16, /* a final string was encountered */ + ESC_TEST = 32, /* Enter in test mode */ + ESC_UTF8 = 64, +}; + +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +typedef struct { + int mode; + int type; + int snap; + /* + * Selection variables: + * nb – normalized coordinates of the beginning of the selection + * ne – normalized coordinates of the end of the selection + * ob – original coordinates of the beginning of the selection + * oe – original coordinates of the end of the selection + */ + struct { + int x, y; + } nb, ne, ob, oe; + + int alt; +} Selection; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ + Line hist[HISTSIZE]; /* history buffer */ + int histi; /* history index */ + int scr; /* scroll back */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int *tabs; + Rune lastc; /* last printed char outside of sequence, 0 if control */ +} Term; + +/* CSI Escape sequence structs */ +/* ESC '[' [[ [] [;]] []] */ +typedef struct { + char buf[ESC_BUF_SIZ]; /* raw string */ + size_t len; /* raw string length */ + char priv; + int arg[ESC_ARG_SIZ]; + int narg; /* nb of args */ + char mode[2]; +} CSIEscape; + +/* STR Escape sequence structs */ +/* ESC type [[ [] [;]] ] ESC '\' */ +typedef struct { + char type; /* ESC type ... */ + char *buf; /* allocated raw string */ + size_t siz; /* allocation size */ + size_t len; /* raw string length */ + char *args[STR_ARG_SIZ]; + int narg; /* nb of args */ +} STREscape; + +static void execsh(char *, char **); +static void stty(char **); +static void sigchld(int); +static void ttywriteraw(const char *, size_t); + +static void csidump(void); +static void csihandle(void); +static void csiparse(void); +static void csireset(void); +static int eschandle(uchar); +static void strdump(void); +static void strhandle(void); +static void strparse(void); +static void strreset(void); + +static void tprinter(char *, size_t); +static void tdumpsel(void); +static void tdumpline(int); +static void tdump(void); +static void tclearregion(int, int, int, int); +static void tcursor(int); +static void tdeletechar(int); +static void tdeleteline(int); +static void tinsertblank(int); +static void tinsertblankline(int); +static int tlinelen(int); +static void tmoveto(int, int); +static void tmoveato(int, int); +static void tnewline(int); +static void tputtab(int); +static void tputc(Rune); +static void treset(void); +static void tscrollup(int, int, int); +static void tscrolldown(int, int, int); +static void tsetattr(const int *, int); +static void tsetchar(Rune, const Glyph *, int, int); +static void tsetdirt(int, int); +static void tsetscroll(int, int); +static void tswapscreen(void); +static void tsetmode(int, int, const int *, int); +static int twrite(const char *, int, int); +static void tfulldirt(void); +static void tcontrolcode(uchar ); +static void tdectest(char ); +static void tdefutf8(char); +static int32_t tdefcolor(const int *, int *, int); +static void tdeftran(char); +static void tstrsequence(uchar); + +static void drawregion(int, int, int, int); + +static void selnormalize(void); +static void selscroll(int, int); +static void selsnap(int *, int *, int); + +static size_t utf8decode(const char *, Rune *, size_t); +static Rune utf8decodebyte(char, size_t *); +static char utf8encodebyte(Rune, size_t); +static size_t utf8validate(Rune *, size_t); + +static char *base64dec(const char *); +static char base64dec_getc(const char **); + +static ssize_t xwrite(int, const char *, size_t); + +/* Globals */ +static Term term; +static Selection sel; +static CSIEscape csiescseq; +static STREscape strescseq; +static int iofd = 1; +static int cmdfd; +static pid_t pid; + +static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +ssize_t +xwrite(int fd, const char *s, size_t len) +{ + size_t aux = len; + ssize_t r; + + while (len > 0) { + r = write(fd, s, len); + if (r < 0) + return r; + len -= r; + s += r; + } + + return aux; +} + +void * +xmalloc(size_t len) +{ + void *p; + + if (!(p = malloc(len))) + die("malloc: %s\n", strerror(errno)); + + return p; +} + +void * +xrealloc(void *p, size_t len) +{ + if ((p = realloc(p, len)) == NULL) + die("realloc: %s\n", strerror(errno)); + + return p; +} + +char * +xstrdup(const char *s) +{ + char *p; + + if ((p = strdup(s)) == NULL) + die("strdup: %s\n", strerror(errno)); + + return p; +} + +size_t +utf8decode(const char *c, Rune *u, size_t clen) +{ + size_t i, j, len, type; + Rune udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type != 0) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Rune +utf8decodebyte(char c, size_t *i) +{ + for (*i = 0; *i < LEN(utfmask); ++(*i)) + if (((uchar)c & utfmask[*i]) == utfbyte[*i]) + return (uchar)c & ~utfmask[*i]; + + return 0; +} + +size_t +utf8encode(Rune u, char *c) +{ + size_t len, i; + + len = utf8validate(&u, 0); + if (len > UTF_SIZ) + return 0; + + for (i = len - 1; i != 0; --i) { + c[i] = utf8encodebyte(u, 0); + u >>= 6; + } + c[0] = utf8encodebyte(u, len); + + return len; +} + +char +utf8encodebyte(Rune u, size_t i) +{ + return utfbyte[i] | (u & ~utfmask[i]); +} + +size_t +utf8validate(Rune *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + + return i; +} + +static const char base64_digits[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +char +base64dec_getc(const char **src) +{ + while (**src && !isprint(**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char * +base64dec(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + + if (in_len % 4) + in_len += 4 - (in_len % 4); + result = dst = xmalloc(in_len / 4 * 3 + 1); + while (*src) { + int a = base64_digits[(unsigned char) base64dec_getc(&src)]; + int b = base64_digits[(unsigned char) base64dec_getc(&src)]; + int c = base64_digits[(unsigned char) base64dec_getc(&src)]; + int d = base64_digits[(unsigned char) base64dec_getc(&src)]; + + /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ + if (a == -1 || b == -1) + break; + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (c == -1) + break; + *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == -1) + break; + *dst++ = ((c & 0x03) << 6) | d; + } + *dst = '\0'; + return result; +} + +void +selinit(void) +{ + sel.mode = SEL_IDLE; + sel.snap = 0; + sel.ob.x = -1; +} + +int +tlinelen(int y) +{ + int i = term.col; + + if (TLINE(y)[i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && TLINE(y)[i - 1].u == ' ') + --i; + + return i; +} + +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type, int done) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + + if (sel.mode == SEL_IDLE) + return; + if (done && sel.mode == SEL_EMPTY) { + selclear(); + return; + } + + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + + sel.mode = done ? SEL_IDLE : SEL_READY; +} + +void +selnormalize(void) +{ + int i; + + if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { + sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; + sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; + } else { + sel.nb.x = MIN(sel.ob.x, sel.oe.x); + sel.ne.x = MAX(sel.ob.x, sel.oe.x); + } + sel.nb.y = MIN(sel.ob.y, sel.oe.y); + sel.ne.y = MAX(sel.ob.y, sel.oe.y); + + selsnap(&sel.nb.x, &sel.nb.y, -1); + selsnap(&sel.ne.x, &sel.ne.y, +1); + + /* expand selection over line breaks */ + if (sel.type == SEL_RECTANGULAR) + return; + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) + sel.nb.x = i; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; +} + +int +selected(int x, int y) +{ + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) + return 0; + + if (sel.type == SEL_RECTANGULAR) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); + + return BETWEEN(y, sel.nb.y, sel.ne.y) + && (y != sel.nb.y || x >= sel.nb.x) + && (y != sel.ne.y || x <= sel.ne.x); +} + +void +selsnap(int *x, int *y, int direction) +{ + int newx, newy, xt, yt; + int delim, prevdelim; + const Glyph *gp, *prevgp; + + switch (sel.snap) { + case SNAP_WORD: + /* + * Snap around if the word wraps around at the end or + * beginning of a line. + */ + prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; + newy = *y; + if (!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) + break; + + if (direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + + gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) + break; + + *x = newx; + *y = newy; + prevgp = gp; + prevdelim = delim; + } + break; + case SNAP_LINE: + /* + * Snap around if the the previous line or the current one + * has set ATTR_WRAP at its end. Then the whole next or + * previous line will be selected. + */ + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { + if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { + if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } + break; + } +} + +char * +getsel(void) +{ + char *str, *ptr; + int y, bufsize, lastx, linelen; + const Glyph *gp, *last; + + if (sel.ob.x == -1) + return NULL; + + bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; + ptr = str = xmalloc(bufsize); + + /* append every set & selected glyph to the selection */ + for (y = sel.nb.y; y <= sel.ne.y; y++) { + if ((linelen = tlinelen(y)) == 0) { + *ptr++ = '\n'; + continue; + } + + if (sel.type == SEL_RECTANGULAR) { + gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + + for ( ; gp <= last; ++gp) { + if (gp->mode & ATTR_WDUMMY) + continue; + + ptr += utf8encode(gp->u, ptr); + } + + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if ((y < sel.ne.y || lastx >= linelen) && + (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) + *ptr++ = '\n'; + } + *ptr = 0; + return str; +} + +void +selclear(void) +{ + if (sel.ob.x == -1) + return; + sel.mode = SEL_IDLE; + sel.ob.x = -1; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void +execsh(char *cmd, char **args) +{ + char *sh, *prog, *arg; + const struct passwd *pw; + + errno = 0; + if ((pw = getpwuid(getuid())) == NULL) { + if (errno) + die("getpwuid: %s\n", strerror(errno)); + else + die("who are you?\n"); + } + + if ((sh = getenv("SHELL")) == NULL) + sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; + + if (args) { + prog = args[0]; + arg = NULL; + } else if (scroll) { + prog = scroll; + arg = utmp ? utmp : sh; + } else if (utmp) { + prog = utmp; + arg = NULL; + } else { + prog = sh; + arg = NULL; + } + DEFAULT(args, ((char *[]) {prog, arg, NULL})); + + unsetenv("COLUMNS"); + unsetenv("LINES"); + unsetenv("TERMCAP"); + setenv("LOGNAME", pw->pw_name, 1); + setenv("USER", pw->pw_name, 1); + setenv("SHELL", sh, 1); + setenv("HOME", pw->pw_dir, 1); + setenv("TERM", termname, 1); + + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + execvp(prog, args); + _exit(1); +} + +void +sigchld(int a) +{ + int stat; + pid_t p; + + if ((p = waitpid(pid, &stat, WNOHANG)) < 0) + die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); + + if (pid != p) + return; + + if (WIFEXITED(stat) && WEXITSTATUS(stat)) + die("child exited with status %d\n", WEXITSTATUS(stat)); + else if (WIFSIGNALED(stat)) + die("child terminated due to signal %d\n", WTERMSIG(stat)); + _exit(0); +} + +void +stty(char **args) +{ + char cmd[_POSIX_ARG_MAX], **p, *q, *s; + size_t n, siz; + + if ((n = strlen(stty_args)) > sizeof(cmd)-1) + die("incorrect stty parameters\n"); + memcpy(cmd, stty_args, n); + q = cmd + n; + siz = sizeof(cmd) - n; + for (p = args; p && (s = *p); ++p) { + if ((n = strlen(s)) > siz-1) + die("stty parameter length too long\n"); + *q++ = ' '; + memcpy(q, s, n); + q += n; + siz -= n + 1; + } + *q = '\0'; + if (system(cmd) != 0) + perror("Couldn't call stty"); +} + +int +ttynew(const char *line, char *cmd, const char *out, char **args) +{ + int m, s; + + if (out) { + term.mode |= MODE_PRINT; + iofd = (!strcmp(out, "-")) ? + 1 : open(out, O_WRONLY | O_CREAT, 0666); + if (iofd < 0) { + fprintf(stderr, "Error opening %s:%s\n", + out, strerror(errno)); + } + } + + if (line) { + if ((cmdfd = open(line, O_RDWR)) < 0) + die("open line '%s' failed: %s\n", + line, strerror(errno)); + dup2(cmdfd, 0); + stty(args); + return cmdfd; + } + + /* seems to work fine on linux, openbsd and freebsd */ + if (openpty(&m, &s, NULL, NULL, NULL) < 0) + die("openpty failed: %s\n", strerror(errno)); + + switch (pid = fork()) { + case -1: + die("fork failed: %s\n", strerror(errno)); + break; + case 0: + close(iofd); + close(m); + setsid(); /* create a new process group */ + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + if (ioctl(s, TIOCSCTTY, NULL) < 0) + die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); + if (s > 2) + close(s); +#ifdef __OpenBSD__ + if (pledge("stdio getpw proc exec", NULL) == -1) + die("pledge\n"); +#endif + execsh(cmd, args); + break; + default: +#ifdef __OpenBSD__ + if (pledge("stdio rpath tty proc", NULL) == -1) + die("pledge\n"); +#endif + close(s); + cmdfd = m; + signal(SIGCHLD, sigchld); + break; + } + return cmdfd; +} + +size_t +ttyread(void) +{ + static char buf[BUFSIZ]; + static int buflen = 0; + int ret, written; + + /* append read bytes to unprocessed bytes */ + ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); + + switch (ret) { + case 0: + exit(0); + case -1: + die("couldn't read from shell: %s\n", strerror(errno)); + default: + buflen += ret; + written = twrite(buf, buflen, 0); + buflen -= written; + /* keep any incomplete UTF-8 byte sequence for the next call */ + if (buflen > 0) + memmove(buf, buf + written, buflen); + return ret; + } +} + +void +ttywrite(const char *s, size_t n, int may_echo) +{ + const char *next; + Arg arg = (Arg) { .i = term.scr }; + + kscrolldown(&arg); + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); + + if (!IS_SET(MODE_CRLF)) { + ttywriteraw(s, n); + return; + } + + /* This is similar to how the kernel handles ONLCR for ttys */ + while (n > 0) { + if (*s == '\r') { + next = s + 1; + ttywriteraw("\r\n", 2); + } else { + next = memchr(s, '\r', n); + DEFAULT(next, s + n); + ttywriteraw(s, next - s); + } + n -= next - s; + s = next; + } +} + +void +ttywriteraw(const char *s, size_t n) +{ + fd_set wfd, rfd; + ssize_t r; + size_t lim = 256; + + /* + * Remember that we are using a pty, which might be a modem line. + * Writing too much will clog the line. That's why we are doing this + * dance. + * FIXME: Migrate the world to Plan 9. + */ + while (n > 0) { + FD_ZERO(&wfd); + FD_ZERO(&rfd); + FD_SET(cmdfd, &wfd); + FD_SET(cmdfd, &rfd); + + /* Check if we can write. */ + if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(cmdfd, &wfd)) { + /* + * Only write the bytes written by ttywrite() or the + * default of 256. This seems to be a reasonable value + * for a serial line. Bigger values might clog the I/O. + */ + if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) + goto write_error; + if (r < n) { + /* + * We weren't able to write out everything. + * This means the buffer is getting full + * again. Empty it. + */ + if (n < lim) + lim = ttyread(); + n -= r; + s += r; + } else { + /* All bytes have been written. */ + break; + } + } + if (FD_ISSET(cmdfd, &rfd)) + lim = ttyread(); + } + return; + +write_error: + die("write error on tty: %s\n", strerror(errno)); +} + +void +ttyresize(int tw, int th) +{ + struct winsize w; + + w.ws_row = term.row; + w.ws_col = term.col; + w.ws_xpixel = tw; + w.ws_ypixel = th; + if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) + fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); +} + +void +ttyhangup() +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + +int +tattrset(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) + return 1; + } + } + + return 0; +} + +void +tsetdirt(int top, int bot) +{ + int i; + + LIMIT(top, 0, term.row-1); + LIMIT(bot, 0, term.row-1); + + for (i = top; i <= bot; i++) + term.dirty[i] = 1; +} + +void +tsetdirtattr(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) { + tsetdirt(i, i); + break; + } + } + } +} + +void +tfulldirt(void) +{ + tsetdirt(0, term.row-1); +} + +void +tcursor(int mode) +{ + static TCursor c[2]; + int alt = IS_SET(MODE_ALTSCREEN); + + if (mode == CURSOR_SAVE) { + c[alt] = term.c; + } else if (mode == CURSOR_LOAD) { + term.c = c[alt]; + tmoveto(c[alt].x, c[alt].y); + } +} + +void +treset(void) +{ + uint i; + + term.c = (TCursor){{ + .mode = ATTR_NULL, + .fg = defaultfg, + .bg = defaultbg + }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) + term.tabs[i] = 1; + term.top = 0; + term.bot = term.row - 1; + term.mode = MODE_WRAP|MODE_UTF8; + memset(term.trantbl, CS_USA, sizeof(term.trantbl)); + term.charset = 0; + + for (i = 0; i < 2; i++) { + tmoveto(0, 0); + tcursor(CURSOR_SAVE); + tclearregion(0, 0, term.col-1, term.row-1); + tswapscreen(); + } +} + +void +tnew(int col, int row) +{ + term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; + tresize(col, row); + treset(); +} + +void +tswapscreen(void) +{ + Line *tmp = term.line; + + term.line = term.alt; + term.alt = tmp; + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); +} + +void +kscrolldown(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (n > term.scr) + n = term.scr; + + if (term.scr > 0) { + term.scr -= n; + selscroll(0, -n); + tfulldirt(); + } +} + +void +kscrollup(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (term.scr <= HISTSIZE-n) { + term.scr += n; + selscroll(0, n); + tfulldirt(); + } +} + +void +tscrolldown(int orig, int n, int copyhist) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + if (copyhist) { + term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[term.bot]; + term.line[term.bot] = temp; + } + + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + + for (i = term.bot; i >= orig+n; i--) { + temp = term.line[i]; + term.line[i] = term.line[i-n]; + term.line[i-n] = temp; + } + + if (term.scr == 0) + selscroll(orig, n); +} + +void +tscrollup(int orig, int n, int copyhist) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + if (copyhist) { + term.histi = (term.histi + 1) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[orig]; + term.line[orig] = temp; + } + + if (term.scr > 0 && term.scr < HISTSIZE) + term.scr = MIN(term.scr + n, HISTSIZE-1); + + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + + for (i = orig; i <= term.bot-n; i++) { + temp = term.line[i]; + term.line[i] = term.line[i+n]; + term.line[i+n] = temp; + } + + if (term.scr == 0) + selscroll(orig, -n); +} + +void +selscroll(int orig, int n) +{ + if (sel.ob.x == -1) + return; + + if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { + selclear(); + } else if (BETWEEN(sel.nb.y, orig, term.bot)) { + sel.ob.y += n; + sel.oe.y += n; + if (sel.ob.y < term.top || sel.ob.y > term.bot || + sel.oe.y < term.top || sel.oe.y > term.bot) { + selclear(); + } else { + selnormalize(); + } + } +} + +void +tnewline(int first_col) +{ + int y = term.c.y; + + if (y == term.bot) { + tscrollup(term.top, 1, 1); + } else { + y++; + } + tmoveto(first_col ? 0 : term.c.x, y); +} + +void +csiparse(void) +{ + char *p = csiescseq.buf, *np; + long int v; + + csiescseq.narg = 0; + if (*p == '?') { + csiescseq.priv = 1; + p++; + } + + csiescseq.buf[csiescseq.len] = '\0'; + while (p < csiescseq.buf+csiescseq.len) { + np = NULL; + v = strtol(p, &np, 10); + if (np == p) + v = 0; + if (v == LONG_MAX || v == LONG_MIN) + v = -1; + csiescseq.arg[csiescseq.narg++] = v; + p = np; + if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + break; + p++; + } + csiescseq.mode[0] = *p++; + csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; +} + +/* for absolute user moves, when decom is set */ +void +tmoveato(int x, int y) +{ + tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0)); +} + +void +tmoveto(int x, int y) +{ + int miny, maxy; + + if (term.c.state & CURSOR_ORIGIN) { + miny = term.top; + maxy = term.bot; + } else { + miny = 0; + maxy = term.row - 1; + } + term.c.state &= ~CURSOR_WRAPNEXT; + term.c.x = LIMIT(x, 0, term.col-1); + term.c.y = LIMIT(y, miny, maxy); +} + +void +tsetchar(Rune u, const Glyph *attr, int x, int y) +{ + static const char *vt100_0[62] = { /* 0x41 - 0x7e */ + "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ + 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ + "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ + "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ + }; + + /* + * The table is proudly stolen from rxvt. + */ + if (term.trantbl[term.charset] == CS_GRAPHIC0 && + BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) + utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); + + if (term.line[y][x].mode & ATTR_WIDE) { + if (x+1 < term.col) { + term.line[y][x+1].u = ' '; + term.line[y][x+1].mode &= ~ATTR_WDUMMY; + } + } else if (term.line[y][x].mode & ATTR_WDUMMY) { + term.line[y][x-1].u = ' '; + term.line[y][x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; + term.line[y][x] = *attr; + term.line[y][x].u = u; +} + +void +tclearregion(int x1, int y1, int x2, int y2) +{ + int x, y, temp; + Glyph *gp; + + if (x1 > x2) + temp = x1, x1 = x2, x2 = temp; + if (y1 > y2) + temp = y1, y1 = y2, y2 = temp; + + LIMIT(x1, 0, term.col-1); + LIMIT(x2, 0, term.col-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { + gp = &term.line[y][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + gp->u = ' '; + } + } +} + +void +tdeletechar(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +} + +void +tinsertblank(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +} + +void +tinsertblankline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrolldown(term.c.y, n, 0); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n, 0); +} + +int32_t +tdefcolor(const int *attr, int *npar, int l) +{ + int32_t idx = -1; + uint r, g, b; + + switch (attr[*npar + 1]) { + case 2: /* direct color in RGB space */ + if (*npar + 4 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + r = attr[*npar + 2]; + g = attr[*npar + 3]; + b = attr[*npar + 4]; + *npar += 4; + if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) + fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n", + r, g, b); + else + idx = TRUECOLOR(r, g, b); + break; + case 5: /* indexed color */ + if (*npar + 2 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + *npar += 2; + if (!BETWEEN(attr[*npar], 0, 255)) + fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]); + else + idx = attr[*npar]; + break; + case 0: /* implemented defined (only foreground) */ + case 1: /* transparent */ + case 3: /* direct color in CMY space */ + case 4: /* direct color in CMYK space */ + default: + fprintf(stderr, + "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; + } + + return idx; +} + +void +tsetattr(const int *attr, int l) +{ + int i; + int32_t idx; + + for (i = 0; i < l; i++) { + switch (attr[i]) { + case 0: + term.c.attr.mode &= ~( + ATTR_BOLD | + ATTR_FAINT | + ATTR_ITALIC | + ATTR_UNDERLINE | + ATTR_BLINK | + ATTR_REVERSE | + ATTR_INVISIBLE | + ATTR_STRUCK ); + term.c.attr.fg = defaultfg; + term.c.attr.bg = defaultbg; + break; + case 1: + term.c.attr.mode |= ATTR_BOLD; + break; + case 2: + term.c.attr.mode |= ATTR_FAINT; + break; + case 3: + term.c.attr.mode |= ATTR_ITALIC; + break; + case 4: + term.c.attr.mode |= ATTR_UNDERLINE; + break; + case 5: /* slow blink */ + /* FALLTHROUGH */ + case 6: /* rapid blink */ + term.c.attr.mode |= ATTR_BLINK; + break; + case 7: + term.c.attr.mode |= ATTR_REVERSE; + break; + case 8: + term.c.attr.mode |= ATTR_INVISIBLE; + break; + case 9: + term.c.attr.mode |= ATTR_STRUCK; + break; + case 22: + term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); + break; + case 23: + term.c.attr.mode &= ~ATTR_ITALIC; + break; + case 24: + term.c.attr.mode &= ~ATTR_UNDERLINE; + break; + case 25: + term.c.attr.mode &= ~ATTR_BLINK; + break; + case 27: + term.c.attr.mode &= ~ATTR_REVERSE; + break; + case 28: + term.c.attr.mode &= ~ATTR_INVISIBLE; + break; + case 29: + term.c.attr.mode &= ~ATTR_STRUCK; + break; + case 38: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.fg = idx; + break; + case 39: + term.c.attr.fg = defaultfg; + break; + case 48: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.bg = idx; + break; + case 49: + term.c.attr.bg = defaultbg; + break; + default: + if (BETWEEN(attr[i], 30, 37)) { + term.c.attr.fg = attr[i] - 30; + } else if (BETWEEN(attr[i], 40, 47)) { + term.c.attr.bg = attr[i] - 40; + } else if (BETWEEN(attr[i], 90, 97)) { + term.c.attr.fg = attr[i] - 90 + 8; + } else if (BETWEEN(attr[i], 100, 107)) { + term.c.attr.bg = attr[i] - 100 + 8; + } else { + fprintf(stderr, + "erresc(default): gfx attr %d unknown\n", + attr[i]); + csidump(); + } + break; + } + } +} + +void +tsetscroll(int t, int b) +{ + int temp; + + LIMIT(t, 0, term.row-1); + LIMIT(b, 0, term.row-1); + if (t > b) { + temp = t; + t = b; + b = temp; + } + term.top = t; + term.bot = b; +} + +void +tsetmode(int priv, int set, const int *args, int narg) +{ + int alt; const int *lim; + + for (lim = args + narg; args < lim; ++args) { + if (priv) { + switch (*args) { + case 1: /* DECCKM -- Cursor key */ + xsetmode(set, MODE_APPCURSOR); + break; + case 5: /* DECSCNM -- Reverse video */ + xsetmode(set, MODE_REVERSE); + break; + case 6: /* DECOM -- Origin */ + MODBIT(term.c.state, set, CURSOR_ORIGIN); + tmoveato(0, 0); + break; + case 7: /* DECAWM -- Auto wrap */ + MODBIT(term.mode, set, MODE_WRAP); + break; + case 0: /* Error (IGNORED) */ + case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ + case 3: /* DECCOLM -- Column (IGNORED) */ + case 4: /* DECSCLM -- Scroll (IGNORED) */ + case 8: /* DECARM -- Auto repeat (IGNORED) */ + case 18: /* DECPFF -- Printer feed (IGNORED) */ + case 19: /* DECPEX -- Printer extent (IGNORED) */ + case 42: /* DECNRCM -- National characters (IGNORED) */ + case 12: /* att610 -- Start blinking cursor (IGNORED) */ + break; + case 25: /* DECTCEM -- Text Cursor Enable Mode */ + xsetmode(!set, MODE_HIDE); + break; + case 9: /* X10 mouse compatibility mode */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEX10); + break; + case 1000: /* 1000: report button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEBTN); + break; + case 1002: /* 1002: report motion on button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMOTION); + break; + case 1003: /* 1003: enable all mouse motions */ + xsetpointermotion(set); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMANY); + break; + case 1004: /* 1004: send focus events to tty */ + xsetmode(set, MODE_FOCUS); + break; + case 1006: /* 1006: extended reporting mode */ + xsetmode(set, MODE_MOUSESGR); + break; + case 1034: + xsetmode(set, MODE_8BIT); + break; + case 1049: /* swap screen & set/restore cursor as xterm */ + if (!allowaltscreen) + break; + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + /* FALLTHROUGH */ + case 47: /* swap screen */ + case 1047: + if (!allowaltscreen) + break; + alt = IS_SET(MODE_ALTSCREEN); + if (alt) { + tclearregion(0, 0, term.col-1, + term.row-1); + } + if (set ^ alt) /* set is always 1 or 0 */ + tswapscreen(); + if (*args != 1049) + break; + /* FALLTHROUGH */ + case 1048: + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + break; + case 2004: /* 2004: bracketed paste mode */ + xsetmode(set, MODE_BRCKTPASTE); + break; + /* Not implemented mouse modes. See comments there. */ + case 1001: /* mouse highlight mode; can hang the + terminal by design when implemented. */ + case 1005: /* UTF-8 mouse mode; will confuse + applications not supporting UTF-8 + and luit. */ + case 1015: /* urxvt mangled mouse mode; incompatible + and can be mistaken for other control + codes. */ + break; + default: + fprintf(stderr, + "erresc: unknown private set/reset mode %d\n", + *args); + break; + } + } else { + switch (*args) { + case 0: /* Error (IGNORED) */ + break; + case 2: + xsetmode(set, MODE_KBDLOCK); + break; + case 4: /* IRM -- Insertion-replacement */ + MODBIT(term.mode, set, MODE_INSERT); + break; + case 12: /* SRM -- Send/Receive */ + MODBIT(term.mode, !set, MODE_ECHO); + break; + case 20: /* LNM -- Linefeed/new line */ + MODBIT(term.mode, set, MODE_CRLF); + break; + default: + fprintf(stderr, + "erresc: unknown set/reset mode %d\n", + *args); + break; + } + } + } +} + +void +csihandle(void) +{ + char buf[40]; + int len; + + switch (csiescseq.mode[0]) { + default: + unknown: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* die(""); */ + break; + case '@': /* ICH -- Insert blank char */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblank(csiescseq.arg[0]); + break; + case 'A': /* CUU -- Cursor Up */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); + break; + case 'B': /* CUD -- Cursor Down */ + case 'e': /* VPR --Cursor Down */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); + break; + case 'i': /* MC -- Media Copy */ + switch (csiescseq.arg[0]) { + case 0: + tdump(); + break; + case 1: + tdumpline(term.c.y); + break; + case 2: + tdumpsel(); + break; + case 4: + term.mode &= ~MODE_PRINT; + break; + case 5: + term.mode |= MODE_PRINT; + break; + } + break; + case 'c': /* DA -- Device Attributes */ + if (csiescseq.arg[0] == 0) + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'b': /* REP -- if last char is printable print it more times */ + DEFAULT(csiescseq.arg[0], 1); + if (term.lastc) + while (csiescseq.arg[0]-- > 0) + tputc(term.lastc); + break; + case 'C': /* CUF -- Cursor Forward */ + case 'a': /* HPR -- Cursor Forward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x+csiescseq.arg[0], term.c.y); + break; + case 'D': /* CUB -- Cursor Backward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x-csiescseq.arg[0], term.c.y); + break; + case 'E': /* CNL -- Cursor Down and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y+csiescseq.arg[0]); + break; + case 'F': /* CPL -- Cursor Up and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y-csiescseq.arg[0]); + break; + case 'g': /* TBC -- Tabulation clear */ + switch (csiescseq.arg[0]) { + case 0: /* clear current tab stop */ + term.tabs[term.c.x] = 0; + break; + case 3: /* clear all the tabs */ + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + break; + default: + goto unknown; + } + break; + case 'G': /* CHA -- Move to */ + case '`': /* HPA */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(csiescseq.arg[0]-1, term.c.y); + break; + case 'H': /* CUP -- Move to */ + case 'f': /* HVP */ + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], 1); + tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); + break; + case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(csiescseq.arg[0]); + break; + case 'J': /* ED -- Clear screen */ + switch (csiescseq.arg[0]) { + case 0: /* below */ + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); + if (term.c.y < term.row-1) { + tclearregion(0, term.c.y+1, term.col-1, + term.row-1); + } + break; + case 1: /* above */ + if (term.c.y > 1) + tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, 0, term.col-1, term.row-1); + break; + default: + goto unknown; + } + break; + case 'K': /* EL -- Clear line */ + switch (csiescseq.arg[0]) { + case 0: /* right */ + tclearregion(term.c.x, term.c.y, term.col-1, + term.c.y); + break; + case 1: /* left */ + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, term.c.y, term.col-1, term.c.y); + break; + } + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); + tscrollup(term.top, csiescseq.arg[0], 0); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[0], 0); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblankline(csiescseq.arg[0]); + break; + case 'l': /* RM -- Reset Mode */ + tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + break; + case 'M': /* DL -- Delete lines */ + DEFAULT(csiescseq.arg[0], 1); + tdeleteline(csiescseq.arg[0]); + break; + case 'X': /* ECH -- Erase char */ + DEFAULT(csiescseq.arg[0], 1); + tclearregion(term.c.x, term.c.y, + term.c.x + csiescseq.arg[0] - 1, term.c.y); + break; + case 'P': /* DCH -- Delete char */ + DEFAULT(csiescseq.arg[0], 1); + tdeletechar(csiescseq.arg[0]); + break; + case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(-csiescseq.arg[0]); + break; + case 'd': /* VPA -- Move to */ + DEFAULT(csiescseq.arg[0], 1); + tmoveato(term.c.x, csiescseq.arg[0]-1); + break; + case 'h': /* SM -- Set terminal mode */ + tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); + break; + case 'm': /* SGR -- Terminal attribute (color) */ + tsetattr(csiescseq.arg, csiescseq.narg); + break; + case 'n': /* DSR – Device Status Report (cursor position) */ + if (csiescseq.arg[0] == 6) { + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + } + break; + case 'r': /* DECSTBM -- Set Scrolling Region */ + if (csiescseq.priv) { + goto unknown; + } else { + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], term.row); + tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); + tmoveato(0, 0); + } + break; + case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ + tcursor(CURSOR_SAVE); + break; + case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ + tcursor(CURSOR_LOAD); + break; + case ' ': + switch (csiescseq.mode[1]) { + case 'q': /* DECSCUSR -- Set Cursor Style */ + if (xsetcursor(csiescseq.arg[0])) + goto unknown; + break; + default: + goto unknown; + } + break; + } +} + +void +csidump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC["); + for (i = 0; i < csiescseq.len; i++) { + c = csiescseq.buf[i] & 0xff; + if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + putc('\n', stderr); +} + +void +csireset(void) +{ + memset(&csiescseq, 0, sizeof(csiescseq)); +} + +void +osc4_color_response(int num) +{ + 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); + 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]%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + num, r, r, g, g, b, b); + + ttywrite(buf, n, 1); +} + +void +strhandle(void) +{ + char *p = NULL, *dec; + int j, narg, par; + + term.esc &= ~(ESC_STR_END|ESC_STR); + strparse(); + par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; + + switch (strescseq.type) { + case ']': /* OSC -- Operating System Command */ + switch (par) { + case 0: + if (narg > 1) { + xsettitle(strescseq.args[1]); + xseticontitle(strescseq.args[1]); + } + return; + case 1: + if (narg > 1) + xseticontitle(strescseq.args[1]); + return; + case 2: + if (narg > 1) + xsettitle(strescseq.args[1]); + return; + case 52: + if (narg > 2 && allowwindowops) { + dec = base64dec(strescseq.args[2]); + if (dec) { + xsetsel(dec); + xclipcopy(); + } else { + fprintf(stderr, "erresc: invalid base64\n"); + } + } + return; + case 10: + if (narg < 2) + break; + + p = strescseq.args[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.args[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 (!strcmp(p, "?")) + osc_color_response(defaultcs, 12); + else if (xsetcolorname(defaultcs, p)) + fprintf(stderr, "erresc: invalid cursor color: %s\n", p); + else + redraw(); + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + + if (p && !strcmp(p, "?")) + osc4_color_response(j); + 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 { + /* + * TODO if defaultbg color is changed, borders + * are dirty + */ + redraw(); + } + return; + } + break; + case 'k': /* old title set compatibility */ + xsettitle(strescseq.args[0]); + return; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + return; + } + + fprintf(stderr, "erresc: unknown str "); + strdump(); +} + +void +strparse(void) +{ + int c; + char *p = strescseq.buf; + + strescseq.narg = 0; + strescseq.buf[strescseq.len] = '\0'; + + if (*p == '\0') + return; + + while (strescseq.narg < STR_ARG_SIZ) { + strescseq.args[strescseq.narg++] = p; + while ((c = *p) != ';' && c != '\0') + ++p; + if (c == '\0') + return; + *p++ = '\0'; + } +} + +void +strdump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC%c", strescseq.type); + for (i = 0; i < strescseq.len; i++) { + c = strescseq.buf[i] & 0xff; + if (c == '\0') { + putc('\n', stderr); + return; + } else if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + fprintf(stderr, "ESC\\\n"); +} + +void +strreset(void) +{ + strescseq = (STREscape){ + .buf = xrealloc(strescseq.buf, STR_BUF_SIZ), + .siz = STR_BUF_SIZ, + }; +} + +void +sendbreak(const Arg *arg) +{ + if (tcsendbreak(cmdfd, 0)) + perror("Error sending break"); +} + +void +tprinter(char *s, size_t len) +{ + if (iofd != -1 && xwrite(iofd, s, len) < 0) { + perror("Error writing to output file"); + close(iofd); + iofd = -1; + } +} + +void +toggleprinter(const Arg *arg) +{ + term.mode ^= MODE_PRINT; +} + +void +printscreen(const Arg *arg) +{ + tdump(); +} + +void +printsel(const Arg *arg) +{ + tdumpsel(); +} + +void +tdumpsel(void) +{ + char *ptr; + + if ((ptr = getsel())) { + tprinter(ptr, strlen(ptr)); + free(ptr); + } +} + +void +tdumpline(int n) +{ + char buf[UTF_SIZ]; + const Glyph *bp, *end; + + bp = &term.line[n][0]; + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if (bp != end || bp->u != ' ') { + for ( ; bp <= end; ++bp) + tprinter(buf, utf8encode(bp->u, buf)); + } + tprinter("\n", 1); +} + +void +tdump(void) +{ + int i; + + for (i = 0; i < term.row; ++i) + tdumpline(i); +} + +void +tputtab(int n) +{ + uint x = term.c.x; + + if (n > 0) { + while (x < term.col && n--) + for (++x; x < term.col && !term.tabs[x]; ++x) + /* nothing */ ; + } else if (n < 0) { + while (x > 0 && n++) + for (--x; x > 0 && !term.tabs[x]; --x) + /* nothing */ ; + } + term.c.x = LIMIT(x, 0, term.col-1); +} + +void +tdefutf8(char ascii) +{ + if (ascii == 'G') + term.mode |= MODE_UTF8; + else if (ascii == '@') + term.mode &= ~MODE_UTF8; +} + +void +tdeftran(char ascii) +{ + static char cs[] = "0B"; + static int vcs[] = {CS_GRAPHIC0, CS_USA}; + char *p; + + if ((p = strchr(cs, ascii)) == NULL) { + fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); + } else { + term.trantbl[term.icharset] = vcs[p - cs]; + } +} + +void +tdectest(char c) +{ + int x, y; + + if (c == '8') { /* DEC screen alignment test. */ + for (x = 0; x < term.col; ++x) { + for (y = 0; y < term.row; ++y) + tsetchar('E', &term.c.attr, x, y); + } + } +} + +void +tstrsequence(uchar c) +{ + switch (c) { + case 0x90: /* DCS -- Device Control String */ + c = 'P'; + break; + case 0x9f: /* APC -- Application Program Command */ + c = '_'; + break; + case 0x9e: /* PM -- Privacy Message */ + c = '^'; + break; + case 0x9d: /* OSC -- Operating System Command */ + c = ']'; + break; + } + strreset(); + strescseq.type = c; + term.esc |= ESC_STR; +} + +void +tcontrolcode(uchar ascii) +{ + switch (ascii) { + case '\t': /* HT */ + tputtab(1); + return; + case '\b': /* BS */ + tmoveto(term.c.x-1, term.c.y); + return; + case '\r': /* CR */ + tmoveto(0, term.c.y); + return; + case '\f': /* LF */ + case '\v': /* VT */ + case '\n': /* LF */ + /* go to first col if the mode is set */ + tnewline(IS_SET(MODE_CRLF)); + return; + case '\a': /* BEL */ + if (term.esc & ESC_STR_END) { + /* backwards compatibility to xterm */ + strhandle(); + } else { + xbell(); + } + break; + case '\033': /* ESC */ + csireset(); + term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); + term.esc |= ESC_START; + return; + case '\016': /* SO (LS1 -- Locking shift 1) */ + case '\017': /* SI (LS0 -- Locking shift 0) */ + term.charset = 1 - (ascii - '\016'); + return; + case '\032': /* SUB */ + tsetchar('?', &term.c.attr, term.c.x, term.c.y); + /* FALLTHROUGH */ + case '\030': /* CAN */ + csireset(); + break; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + return; + case 0x80: /* TODO: PAD */ + case 0x81: /* TODO: HOP */ + case 0x82: /* TODO: BPH */ + case 0x83: /* TODO: NBH */ + case 0x84: /* TODO: IND */ + break; + case 0x85: /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 0x86: /* TODO: SSA */ + case 0x87: /* TODO: ESA */ + break; + case 0x88: /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 0x89: /* TODO: HTJ */ + case 0x8a: /* TODO: VTS */ + case 0x8b: /* TODO: PLD */ + case 0x8c: /* TODO: PLU */ + case 0x8d: /* TODO: RI */ + case 0x8e: /* TODO: SS2 */ + case 0x8f: /* TODO: SS3 */ + case 0x91: /* TODO: PU1 */ + case 0x92: /* TODO: PU2 */ + case 0x93: /* TODO: STS */ + case 0x94: /* TODO: CCH */ + case 0x95: /* TODO: MW */ + case 0x96: /* TODO: SPA */ + case 0x97: /* TODO: EPA */ + case 0x98: /* TODO: SOS */ + case 0x99: /* TODO: SGCI */ + break; + case 0x9a: /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 0x9b: /* TODO: CSI */ + case 0x9c: /* TODO: ST */ + break; + case 0x90: /* DCS -- Device Control String */ + case 0x9d: /* OSC -- Operating System Command */ + case 0x9e: /* PM -- Privacy Message */ + case 0x9f: /* APC -- Application Program Command */ + tstrsequence(ascii); + return; + } + /* only CAN, SUB, \a and C1 chars interrupt a sequence */ + term.esc &= ~(ESC_STR_END|ESC_STR); +} + +/* + * returns 1 when the sequence is finished and it hasn't to read + * more characters for this sequence, otherwise 0 + */ +int +eschandle(uchar ascii) +{ + switch (ascii) { + case '[': + term.esc |= ESC_CSI; + return 0; + case '#': + term.esc |= ESC_TEST; + return 0; + case '%': + term.esc |= ESC_UTF8; + return 0; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + case ']': /* OSC -- Operating System Command */ + case 'k': /* old title set compatibility */ + tstrsequence(ascii); + return 0; + case 'n': /* LS2 -- Locking shift 2 */ + case 'o': /* LS3 -- Locking shift 3 */ + term.charset = 2 + (ascii - 'n'); + break; + case '(': /* GZD4 -- set primary charset G0 */ + case ')': /* G1D4 -- set secondary charset G1 */ + case '*': /* G2D4 -- set tertiary charset G2 */ + case '+': /* G3D4 -- set quaternary charset G3 */ + term.icharset = ascii - '('; + term.esc |= ESC_ALTCHARSET; + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { + tscrollup(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { + tscrolldown(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'c': /* RIS -- Reset to initial state */ + treset(); + resettitle(); + xloadcols(); + break; + case '=': /* DECPAM -- Application keypad */ + xsetmode(1, MODE_APPKEYPAD); + break; + case '>': /* DECPNM -- Normal keypad */ + xsetmode(0, MODE_APPKEYPAD); + break; + case '7': /* DECSC -- Save Cursor */ + tcursor(CURSOR_SAVE); + break; + case '8': /* DECRC -- Restore Cursor */ + tcursor(CURSOR_LOAD); + break; + case '\\': /* ST -- String Terminator */ + if (term.esc & ESC_STR_END) + strhandle(); + break; + default: + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", + (uchar) ascii, isprint(ascii)? ascii:'.'); + break; + } + return 1; +} + +void +tputc(Rune u) +{ + char c[UTF_SIZ]; + int control; + int width, len; + Glyph *gp; + + control = ISCONTROL(u); + if (u < 127 || !IS_SET(MODE_UTF8)) { + c[0] = u; + width = len = 1; + } else { + len = utf8encode(u, c); + if (!control && (width = wcwidth(u)) == -1) + width = 1; + } + + if (IS_SET(MODE_PRINT)) + tprinter(c, len); + + /* + * STR sequence must be checked before anything else + * because it uses all following characters until it + * receives a ESC, a SUB, a ST or any other C1 control + * character. + */ + if (term.esc & ESC_STR) { + if (u == '\a' || u == 030 || u == 032 || u == 033 || + ISCONTROLC1(u)) { + term.esc &= ~(ESC_START|ESC_STR); + term.esc |= ESC_STR_END; + goto check_control_code; + } + + if (strescseq.len+len >= strescseq.siz) { + /* + * Here is a bug in terminals. If the user never sends + * some code to stop the str or esc command, then st + * will stop responding. But this is better than + * silently failing with unknown characters. At least + * then users will report back. + * + * In the case users ever get fixed, here is the code: + */ + /* + * term.esc = 0; + * strhandle(); + */ + if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2) + return; + strescseq.siz *= 2; + strescseq.buf = xrealloc(strescseq.buf, strescseq.siz); + } + + memmove(&strescseq.buf[strescseq.len], c, len); + strescseq.len += len; + return; + } + +check_control_code: + /* + * Actions of control codes must be performed as soon they arrive + * because they can be embedded inside a control sequence, and + * they must not cause conflicts with sequences. + */ + if (control) { + tcontrolcode(u); + /* + * control codes are not shown ever + */ + if (!term.esc) + term.lastc = 0; + return; + } else if (term.esc & ESC_START) { + if (term.esc & ESC_CSI) { + csiescseq.buf[csiescseq.len++] = u; + if (BETWEEN(u, 0x40, 0x7E) + || csiescseq.len >= \ + sizeof(csiescseq.buf)-1) { + term.esc = 0; + csiparse(); + csihandle(); + } + return; + } else if (term.esc & ESC_UTF8) { + tdefutf8(u); + } else if (term.esc & ESC_ALTCHARSET) { + tdeftran(u); + } else if (term.esc & ESC_TEST) { + tdectest(u); + } else { + if (!eschandle(u)) + return; + /* sequence already finished */ + } + term.esc = 0; + /* + * All characters which form part of a sequence are not + * printed + */ + return; + } + if (selected(term.c.x, term.c.y)) + selclear(); + + gp = &term.line[term.c.y][term.c.x]; + if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + gp->mode |= ATTR_WRAP; + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) + memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); + + if (term.c.x+width > term.col) { + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + tsetchar(u, &term.c.attr, term.c.x, term.c.y); + term.lastc = u; + + if (width == 2) { + gp->mode |= ATTR_WIDE; + if (term.c.x+1 < term.col) { + if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) { + gp[2].u = ' '; + gp[2].mode &= ~ATTR_WDUMMY; + } + gp[1].u = '\0'; + gp[1].mode = ATTR_WDUMMY; + } + } + if (term.c.x+width < term.col) { + tmoveto(term.c.x+width, term.c.y); + } else { + term.c.state |= CURSOR_WRAPNEXT; + } +} + +int +twrite(const char *buf, int buflen, int show_ctrl) +{ + int charsize; + Rune u; + int n; + + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8)) { + /* process a complete utf8 char */ + charsize = utf8decode(buf + n, &u, buflen - n); + if (charsize == 0) + break; + } else { + u = buf[n] & 0xFF; + charsize = 1; + } + if (show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; + tputc('^'); + tputc('['); + } else if (u != '\n' && u != '\r' && u != '\t') { + u ^= 0x40; + tputc('^'); + } + } + tputc(u); + } + return n; +} + +void +tresize(int col, int row) +{ + int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; + TCursor c; + + if (col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to + * memmove because we're freeing the earlier lines + */ + for (i = 0; i <= term.c.y - row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + /* ensure that both src and dst are not NULL */ + if (i > 0) { + memmove(term.line, term.line + i, row * sizeof(Line)); + memmove(term.alt, term.alt + i, row * sizeof(Line)); + } + for (i += row; i < term.row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + for (i = 0; i < HISTSIZE; i++) { + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); + for (j = mincol; j < col; j++) { + term.hist[i][j] = term.c.attr; + term.hist[i][j].u = ' '; + } + } + + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); + } + + /* allocate any new rows */ + for (/* i = minrow */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + term.alt[i] = xmalloc(col * sizeof(Glyph)); + } + if (col > term.col) { + bp = term.tabs + term.col; + + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + while (--bp > term.tabs && !*bp) + /* nothing */ ; + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } + /* update terminal size */ + term.col = col; + term.row = row; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); + /* Clearing both screens (it makes dirty all lines) */ + c = term.c; + for (i = 0; i < 2; i++) { + if (mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if (0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + tcursor(CURSOR_LOAD); + } + term.c = c; +} + +void +resettitle(void) +{ + xsettitle(NULL); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y; + + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(TLINE(y), x1, y, x2); + } +} + +void +draw(void) +{ + int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; + + if (!xstartdraw()) + return; + + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); + if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) + term.ocx--; + if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); + if (term.scr == 0) + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); + if (ocx != term.ocx || ocy != term.ocy) + xximspot(term.ocx, term.ocy); +} + +void +redraw(void) +{ + tfulldirt(); + draw(); +} diff --git a/bst/st.h b/bst/st.h new file mode 100644 index 00000000..67bfb74f --- /dev/null +++ b/bst/st.h @@ -0,0 +1,131 @@ +/* See LICENSE for license details. */ + +#include +#include + +/* macros */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a) / sizeof(a)[0]) +#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) +#define DEFAULT(a, b) (a) = (a) ? (a) : (b) +#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) +#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ + (a).bg != (b).bg) +#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ + (t1.tv_nsec-t2.tv_nsec)/1E6) +#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) + +#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) +#define IS_TRUECOL(x) (1 << 24 & (x)) + +enum glyph_attribute { + ATTR_NULL = 0, + ATTR_BOLD = 1 << 0, + ATTR_FAINT = 1 << 1, + ATTR_ITALIC = 1 << 2, + ATTR_UNDERLINE = 1 << 3, + ATTR_BLINK = 1 << 4, + ATTR_REVERSE = 1 << 5, + ATTR_INVISIBLE = 1 << 6, + ATTR_STRUCK = 1 << 7, + ATTR_WRAP = 1 << 8, + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, +}; + +enum selection_mode { + SEL_IDLE = 0, + SEL_EMPTY = 1, + SEL_READY = 2 +}; + +enum selection_type { + SEL_REGULAR = 1, + SEL_RECTANGULAR = 2 +}; + +enum selection_snap { + SNAP_WORD = 1, + SNAP_LINE = 2 +}; + +typedef unsigned char uchar; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned short ushort; + +typedef uint_least32_t Rune; + +#define Glyph Glyph_ +typedef struct { + Rune u; /* character code */ + ushort mode; /* attribute flags */ + uint32_t fg; /* foreground */ + uint32_t bg; /* background */ +} Glyph; + +typedef Glyph *Line; + +typedef union { + int i; + uint ui; + float f; + const void *v; + const char *s; +} Arg; + +void die(const char *, ...); +void redraw(void); +void draw(void); + +void kscrolldown(const Arg *); +void kscrollup(const Arg *); +void printscreen(const Arg *); +void printsel(const Arg *); +void sendbreak(const Arg *); +void toggleprinter(const Arg *); + +int tattrset(int); +void tnew(int, int); +void tresize(int, int); +void tsetdirtattr(int); +void ttyhangup(void); +int ttynew(const char *, char *, const char *, char **); +size_t ttyread(void); +void ttyresize(int, int); +void ttywrite(const char *, size_t, int); + +void resettitle(void); + +void selclear(void); +void selinit(void); +void selstart(int, int, int); +void selextend(int, int, int, int); +int selected(int, int); +char *getsel(void); + +size_t utf8encode(Rune, char *); + +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(const char *); + +int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b); + +/* config.h globals */ +extern char *utmp; +extern char *scroll; +extern char *stty_args; +extern char *vtiden; +extern wchar_t *worddelimiters; +extern int allowaltscreen; +extern int allowwindowops; +extern char *termname; +extern unsigned int tabspaces; +extern unsigned int defaultfg; +extern unsigned int defaultbg; +extern unsigned int defaultcs; +extern float alpha; diff --git a/bst/st.h.orig b/bst/st.h.orig new file mode 100644 index 00000000..da36b34a --- /dev/null +++ b/bst/st.h.orig @@ -0,0 +1,130 @@ +/* See LICENSE for license details. */ + +#include +#include + +/* macros */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a) / sizeof(a)[0]) +#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) +#define DEFAULT(a, b) (a) = (a) ? (a) : (b) +#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) +#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ + (a).bg != (b).bg) +#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ + (t1.tv_nsec-t2.tv_nsec)/1E6) +#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) + +#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) +#define IS_TRUECOL(x) (1 << 24 & (x)) + +enum glyph_attribute { + ATTR_NULL = 0, + ATTR_BOLD = 1 << 0, + ATTR_FAINT = 1 << 1, + ATTR_ITALIC = 1 << 2, + ATTR_UNDERLINE = 1 << 3, + ATTR_BLINK = 1 << 4, + ATTR_REVERSE = 1 << 5, + ATTR_INVISIBLE = 1 << 6, + ATTR_STRUCK = 1 << 7, + ATTR_WRAP = 1 << 8, + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, +}; + +enum selection_mode { + SEL_IDLE = 0, + SEL_EMPTY = 1, + SEL_READY = 2 +}; + +enum selection_type { + SEL_REGULAR = 1, + SEL_RECTANGULAR = 2 +}; + +enum selection_snap { + SNAP_WORD = 1, + SNAP_LINE = 2 +}; + +typedef unsigned char uchar; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned short ushort; + +typedef uint_least32_t Rune; + +#define Glyph Glyph_ +typedef struct { + Rune u; /* character code */ + ushort mode; /* attribute flags */ + uint32_t fg; /* foreground */ + uint32_t bg; /* background */ +} Glyph; + +typedef Glyph *Line; + +typedef union { + int i; + uint ui; + float f; + const void *v; + const char *s; +} Arg; + +void die(const char *, ...); +void redraw(void); +void draw(void); + +void kscrolldown(const Arg *); +void kscrollup(const Arg *); +void printscreen(const Arg *); +void printsel(const Arg *); +void sendbreak(const Arg *); +void toggleprinter(const Arg *); + +int tattrset(int); +void tnew(int, int); +void tresize(int, int); +void tsetdirtattr(int); +void ttyhangup(void); +int ttynew(const char *, char *, const char *, char **); +size_t ttyread(void); +void ttyresize(int, int); +void ttywrite(const char *, size_t, int); + +void resettitle(void); + +void selclear(void); +void selinit(void); +void selstart(int, int, int); +void selextend(int, int, int, int); +int selected(int, int); +char *getsel(void); + +size_t utf8encode(Rune, char *); + +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(const char *); + +int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b); + +/* config.h globals */ +extern char *utmp; +extern char *scroll; +extern char *stty_args; +extern char *vtiden; +extern wchar_t *worddelimiters; +extern int allowaltscreen; +extern int allowwindowops; +extern char *termname; +extern unsigned int tabspaces; +extern unsigned int defaultfg; +extern unsigned int defaultbg; +extern unsigned int defaultcs; diff --git a/bst/st.info b/bst/st.info new file mode 100644 index 00000000..8201ad62 --- /dev/null +++ b/bst/st.info @@ -0,0 +1,239 @@ +st-mono| simpleterm monocolor, + acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + am, + bce, + bel=^G, + blink=\E[5m, + bold=\E[1m, + cbt=\E[Z, + cvvis=\E[?25h, + civis=\E[?25l, + clear=\E[H\E[2J, + cnorm=\E[?12l\E[?25h, + colors#2, + cols#80, + cr=^M, + csr=\E[%i%p1%d;%p2%dr, + cub=\E[%p1%dD, + cub1=^H, + cud1=^J, + cud=\E[%p1%dB, + cuf1=\E[C, + cuf=\E[%p1%dC, + cup=\E[%i%p1%d;%p2%dH, + cuu1=\E[A, + cuu=\E[%p1%dA, + dch=\E[%p1%dP, + dch1=\E[P, + dim=\E[2m, + dl=\E[%p1%dM, + dl1=\E[M, + ech=\E[%p1%dX, + ed=\E[J, + el=\E[K, + el1=\E[1K, + enacs=\E)0, + flash=\E[?5h$<80/>\E[?5l, + fsl=^G, + home=\E[H, + hpa=\E[%i%p1%dG, + hs, + ht=^I, + hts=\EH, + ich=\E[%p1%d@, + il1=\E[L, + il=\E[%p1%dL, + ind=^J, + indn=\E[%p1%dS, + invis=\E[8m, + is2=\E[4l\E>\E[?1034l, + it#8, + kel=\E[1;2F, + ked=\E[1;5F, + ka1=\E[1~, + ka3=\E[5~, + kc1=\E[4~, + kc3=\E[6~, + kbs=\177, + kcbt=\E[Z, + kb2=\EOu, + kcub1=\EOD, + kcud1=\EOB, + kcuf1=\EOC, + kcuu1=\EOA, + kDC=\E[3;2~, + kent=\EOM, + kEND=\E[1;2F, + kIC=\E[2;2~, + kNXT=\E[6;2~, + kPRV=\E[5;2~, + kHOM=\E[1;2H, + kLFT=\E[1;2D, + kRIT=\E[1;2C, + kind=\E[1;2B, + kri=\E[1;2A, + kclr=\E[3;5~, + kdl1=\E[3;2~, + kdch1=\E[3~, + kich1=\E[2~, + kend=\E[4~, + kf1=\EOP, + kf2=\EOQ, + kf3=\EOR, + kf4=\EOS, + kf5=\E[15~, + kf6=\E[17~, + kf7=\E[18~, + kf8=\E[19~, + kf9=\E[20~, + kf10=\E[21~, + kf11=\E[23~, + kf12=\E[24~, + kf13=\E[1;2P, + kf14=\E[1;2Q, + kf15=\E[1;2R, + kf16=\E[1;2S, + kf17=\E[15;2~, + kf18=\E[17;2~, + kf19=\E[18;2~, + kf20=\E[19;2~, + kf21=\E[20;2~, + kf22=\E[21;2~, + kf23=\E[23;2~, + kf24=\E[24;2~, + kf25=\E[1;5P, + kf26=\E[1;5Q, + kf27=\E[1;5R, + kf28=\E[1;5S, + kf29=\E[15;5~, + kf30=\E[17;5~, + kf31=\E[18;5~, + kf32=\E[19;5~, + kf33=\E[20;5~, + kf34=\E[21;5~, + kf35=\E[23;5~, + kf36=\E[24;5~, + kf37=\E[1;6P, + kf38=\E[1;6Q, + kf39=\E[1;6R, + kf40=\E[1;6S, + kf41=\E[15;6~, + kf42=\E[17;6~, + kf43=\E[18;6~, + kf44=\E[19;6~, + kf45=\E[20;6~, + kf46=\E[21;6~, + kf47=\E[23;6~, + kf48=\E[24;6~, + kf49=\E[1;3P, + kf50=\E[1;3Q, + kf51=\E[1;3R, + kf52=\E[1;3S, + kf53=\E[15;3~, + kf54=\E[17;3~, + kf55=\E[18;3~, + kf56=\E[19;3~, + kf57=\E[20;3~, + kf58=\E[21;3~, + kf59=\E[23;3~, + kf60=\E[24;3~, + kf61=\E[1;4P, + kf62=\E[1;4Q, + kf63=\E[1;4R, + khome=\E[1~, + kil1=\E[2;5~, + krmir=\E[2;2~, + knp=\E[6~, + kmous=\E[M, + kpp=\E[5~, + lines#24, + mir, + msgr, + npc, + op=\E[39;49m, + pairs#64, + mc0=\E[i, + mc4=\E[4i, + mc5=\E[5i, + rc=\E8, + rev=\E[7m, + ri=\EM, + rin=\E[%p1%dT, + ritm=\E[23m, + rmacs=\E(B, + rmcup=\E[?1049l, + rmir=\E[4l, + rmkx=\E[?1l\E>, + rmso=\E[27m, + rmul=\E[24m, + rs1=\Ec, + rs2=\E[4l\E>\E[?1034l, + sc=\E7, + sitm=\E[3m, + sgr0=\E[0m, + smacs=\E(0, + smcup=\E[?1049h, + smir=\E[4h, + smkx=\E[?1h\E=, + smso=\E[7m, + smul=\E[4m, + tbc=\E[3g, + tsl=\E]0;, + xenl, + vpa=\E[%i%p1%dd, +# XTerm extensions + rmxx=\E[29m, + smxx=\E[9m, +# disabled rep for now: causes some issues with older ncurses versions. +# rep=%p1%c\E[%p2%{1}%-%db, +# tmux extensions, see TERMINFO EXTENSIONS in tmux(1) + Tc, + Ms=\E]52;%p1%s;%p2%s\007, + Se=\E[2 q, + Ss=\E[%p1%d q, + +st| simpleterm, + use=st-mono, + colors#8, + setab=\E[4%p1%dm, + setaf=\E[3%p1%dm, + setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, + setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, + sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, + +st-256color| simpleterm with 256 colors, + use=st, + ccc, + colors#256, + oc=\E]104\007, + pairs#32767, +# Nicked from xterm-256color + initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\, + setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, + setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, + +st-meta| simpleterm with meta key, + use=st, + km, + rmm=\E[?1034l, + smm=\E[?1034h, + rs2=\E[4l\E>\E[?1034h, + is2=\E[4l\E>\E[?1034h, + +st-meta-256color| simpleterm with meta key and 256 colors, + use=st-256color, + km, + rmm=\E[?1034l, + smm=\E[?1034h, + rs2=\E[4l\E>\E[?1034h, + is2=\E[4l\E>\E[?1034h, + +st-bs| simpleterm with backspace as backspace, + use=st, + kbs=\010, + kdch1=\177, + +st-bs-256color| simpleterm with backspace as backspace and 256colors, + use=st-256color, + kbs=\010, + kdch1=\177, diff --git a/bst/st.o b/bst/st.o new file mode 100644 index 0000000000000000000000000000000000000000..8f57de6df3edaf2d19b5f4c87dab2f28dfcae9df GIT binary patch literal 81544 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *argv0; +#include "arg.h" +#include "st.h" +#include "win.h" + +/* types used in config.h */ +typedef struct { + uint mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Shortcut; + +typedef struct { + uint mod; + uint button; + void (*func)(const Arg *); + const Arg arg; + uint release; +} MouseShortcut; + +typedef struct { + KeySym k; + uint mask; + char *s; + /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ + signed char appkey; /* application keypad */ + signed char appcursor; /* application cursor */ +} Key; + +/* X modifiers */ +#define XK_ANY_MOD UINT_MAX +#define XK_NO_MOD 0 +#define XK_SWITCH_MOD (1<<13|1<<14) + +/* function definitions used in config.h */ +static void clipcopy(const Arg *); +static void clippaste(const Arg *); +static void numlock(const Arg *); +static void selpaste(const Arg *); +static void zoom(const Arg *); +static void zoomabs(const Arg *); +static void zoomreset(const Arg *); +static void ttysend(const Arg *); + +/* config.h for applying patches and the configuration. */ +#include "config.h" + +/* XEMBED messages */ +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 + +/* macros */ +#define IS_SET(flag) ((win.mode & (flag)) != 0) +#define TRUERED(x) (((x) & 0xff0000) >> 8) +#define TRUEGREEN(x) (((x) & 0xff00)) +#define TRUEBLUE(x) (((x) & 0xff) << 8) + +typedef XftDraw *Draw; +typedef XftColor Color; +typedef XftGlyphFontSpec GlyphFontSpec; + +/* Purely graphic info */ +typedef struct { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ + int ch; /* char height */ + int cw; /* char width */ + int mode; /* window state/mode flags */ + int cursor; /* cursor style */ +} TermWindow; + +typedef struct { + Display *dpy; + Colormap cmap; + Window win; + Drawable buf; + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ + Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; + struct { + XIM xim; + XIC xic; + XPoint spot; + XVaNestedList spotlist; + } ime; + Draw draw; + Visual *vis; + XSetWindowAttributes attrs; + int scr; + int isfixed; /* is fixed geometry? */ + int depth; /* bit depth */ + int l, t; /* left and top offset */ + int gm; /* geometry mask */ +} XWindow; + +typedef struct { + Atom xtarget; + char *primary, *clipboard; + struct timespec tclick1; + struct timespec tclick2; +} XSelection; + +/* Font structure */ +#define Font Font_ +typedef struct { + int height; + int width; + int ascent; + int descent; + int badslant; + int badweight; + short lbearing; + short rbearing; + XftFont *match; + FcFontSet *set; + FcPattern *pattern; +} Font; + +/* Drawing Context */ +typedef struct { + Color *col; + size_t collen; + Font font, bfont, ifont, ibfont; + GC gc; +} DC; + +static inline ushort sixd_to_16bit(int); +static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); +static void xdrawglyph(Glyph, int, int); +static void xclear(int, int, int, int); +static int xgeommasktogravity(int); +static int ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, XPointer, XPointer); +static int xicdestroy(XIC, XPointer, XPointer); +static void xinit(int, int); +static void cresize(int, int); +static void xresize(int, int); +static void xhints(void); +static int xloadcolor(int, const char *, Color *); +static int xloadfont(Font *, FcPattern *); +static void xloadfonts(const char *, double); +static void xunloadfont(Font *); +static void xunloadfonts(void); +static void xsetenv(void); +static void xseturgency(int); +static int evcol(XEvent *); +static int evrow(XEvent *); + +static void expose(XEvent *); +static void visibility(XEvent *); +static void unmap(XEvent *); +static void kpress(XEvent *); +static void cmessage(XEvent *); +static void resize(XEvent *); +static void focus(XEvent *); +static uint buttonmask(uint); +static int mouseaction(XEvent *, uint); +static void brelease(XEvent *); +static void bpress(XEvent *); +static void bmotion(XEvent *); +static void propnotify(XEvent *); +static void selnotify(XEvent *); +static void selclear_(XEvent *); +static void selrequest(XEvent *); +static void setsel(char *, Time); +static void mousesel(XEvent *, int); +static void mousereport(XEvent *); +static char *kmap(KeySym, uint); +static int match(uint, uint); + +static void run(void); +static void usage(void); + +static void (*handler[LASTEvent])(XEvent *) = { + [KeyPress] = kpress, + [ClientMessage] = cmessage, + [ConfigureNotify] = resize, + [VisibilityNotify] = visibility, + [UnmapNotify] = unmap, + [Expose] = expose, + [FocusIn] = focus, + [FocusOut] = focus, + [MotionNotify] = bmotion, + [ButtonPress] = bpress, + [ButtonRelease] = brelease, +/* + * Uncomment if you want the selection to disappear when you select something + * different in another window. + */ +/* [SelectionClear] = selclear_, */ + [SelectionNotify] = selnotify, +/* + * PropertyNotify is only turned on when there is some INCR transfer happening + * for the selection retrieval. + */ + [PropertyNotify] = propnotify, + [SelectionRequest] = selrequest, +}; + +/* Globals */ +static DC dc; +static XWindow xw; +static XSelection xsel; +static TermWindow win; + +/* Font Ring Cache */ +enum { + FRC_NORMAL, + FRC_ITALIC, + FRC_BOLD, + FRC_ITALICBOLD +}; + +typedef struct { + XftFont *font; + int flags; + Rune unicodep; +} Fontcache; + +/* Fontcache is an array now. A new font will be appended to the array. */ +static Fontcache *frc = NULL; +static int frclen = 0; +static int frccap = 0; +static char *usedfont = NULL; +static double usedfontsize = 0; +static double defaultfontsize = 0; + +static char *opt_alpha = NULL; +static char *opt_class = NULL; +static char **opt_cmd = NULL; +static char *opt_embed = NULL; +static char *opt_font = NULL; +static char *opt_io = NULL; +static char *opt_line = NULL; +static char *opt_name = NULL; +static char *opt_title = NULL; + +static int oldbutton = 3; /* button event on startup: 3 = release */ + +void +clipcopy(const Arg *dummy) +{ + Atom clipboard; + + free(xsel.clipboard); + xsel.clipboard = NULL; + + if (xsel.primary != NULL) { + xsel.clipboard = xstrdup(xsel.primary); + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); + } +} + +void +clippaste(const Arg *dummy) +{ + Atom clipboard; + + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, + xw.win, CurrentTime); +} + +void +selpaste(const Arg *dummy) +{ + XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, + xw.win, CurrentTime); +} + +void +numlock(const Arg *dummy) +{ + win.mode ^= MODE_NUMLOCK; +} + +void +zoom(const Arg *arg) +{ + Arg larg; + + larg.f = usedfontsize + arg->f; + zoomabs(&larg); +} + +void +zoomabs(const Arg *arg) +{ + xunloadfonts(); + xloadfonts(usedfont, arg->f); + cresize(0, 0); + redraw(); + xhints(); +} + +void +zoomreset(const Arg *arg) +{ + Arg larg; + + if (defaultfontsize > 0) { + larg.f = defaultfontsize; + zoomabs(&larg); + } +} + +void +ttysend(const Arg *arg) +{ + ttywrite(arg->s, strlen(arg->s), 1); +} + +int +evcol(XEvent *e) +{ + int x = e->xbutton.x - borderpx; + LIMIT(x, 0, win.tw - 1); + return x / win.cw; +} + +int +evrow(XEvent *e) +{ + int y = e->xbutton.y - borderpx; + LIMIT(y, 0, win.th - 1); + return y / win.ch; +} + +void +mousesel(XEvent *e, int done) +{ + int type, seltype = SEL_REGULAR; + uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); + + for (type = 1; type < LEN(selmasks); ++type) { + if (match(selmasks[type], state)) { + seltype = type; + break; + } + } + selextend(evcol(e), evrow(e), seltype, done); + if (done) + setsel(getsel(), e->xbutton.time); +} + +void +mousereport(XEvent *e) +{ + int len, x = evcol(e), y = evrow(e), + button = e->xbutton.button, state = e->xbutton.state; + char buf[40]; + static int ox, oy; + + /* from urxvt */ + if (e->xbutton.type == MotionNotify) { + if (x == ox && y == oy) + return; + if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) + return; + /* MOUSE_MOTION: no reporting if no button is pressed */ + if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) + return; + + button = oldbutton + 32; + ox = x; + oy = y; + } else { + if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) { + button = 3; + } else { + button -= Button1; + if (button >= 7) + button += 128 - 7; + else if (button >= 3) + button += 64 - 3; + } + if (e->xbutton.type == ButtonPress) { + oldbutton = button; + ox = x; + oy = y; + } else if (e->xbutton.type == ButtonRelease) { + oldbutton = 3; + /* MODE_MOUSEX10: no button release reporting */ + if (IS_SET(MODE_MOUSEX10)) + return; + if (button == 64 || button == 65) + return; + } + } + + if (!IS_SET(MODE_MOUSEX10)) { + button += ((state & ShiftMask ) ? 4 : 0) + + ((state & Mod4Mask ) ? 8 : 0) + + ((state & ControlMask) ? 16 : 0); + } + + if (IS_SET(MODE_MOUSESGR)) { + len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", + button, x+1, y+1, + e->xbutton.type == ButtonRelease ? 'm' : 'M'); + } else if (x < 223 && y < 223) { + len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", + 32+button, 32+x+1, 32+y+1); + } else { + return; + } + + ttywrite(buf, len, 0); +} + +uint +buttonmask(uint button) +{ + return button == Button1 ? Button1Mask + : button == Button2 ? Button2Mask + : button == Button3 ? Button3Mask + : button == Button4 ? Button4Mask + : button == Button5 ? Button5Mask + : 0; +} + +int +mouseaction(XEvent *e, uint release) +{ + MouseShortcut *ms; + + /* ignore Buttonmask for Button - it's set on release */ + uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); + + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { + if (ms->release == release && + ms->button == e->xbutton.button && + (match(ms->mod, state) || /* exact or forced */ + match(ms->mod, state & ~forcemousemod))) { + ms->func(&(ms->arg)); + return 1; + } + } + + return 0; +} + +void +bpress(XEvent *e) +{ + struct timespec now; + int snap; + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 0)) + return; + + if (e->xbutton.button == Button1) { + /* + * If the user clicks below predefined timeouts specific + * snapping behaviour is exposed. + */ + clock_gettime(CLOCK_MONOTONIC, &now); + if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { + snap = SNAP_LINE; + } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { + snap = SNAP_WORD; + } else { + snap = 0; + } + xsel.tclick2 = xsel.tclick1; + xsel.tclick1 = now; + + selstart(evcol(e), evrow(e), snap); + } +} + +void +propnotify(XEvent *e) +{ + XPropertyEvent *xpev; + Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + + xpev = &e->xproperty; + if (xpev->state == PropertyNewValue && + (xpev->atom == XA_PRIMARY || + xpev->atom == clipboard)) { + selnotify(e); + } +} + +void +selnotify(XEvent *e) +{ + ulong nitems, ofs, rem; + int format; + uchar *data, *last, *repl; + Atom type, incratom, property = None; + + incratom = XInternAtom(xw.dpy, "INCR", 0); + + ofs = 0; + if (e->type == SelectionNotify) + property = e->xselection.property; + else if (e->type == PropertyNotify) + property = e->xproperty.atom; + + if (property == None) + return; + + do { + if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, + BUFSIZ/4, False, AnyPropertyType, + &type, &format, &nitems, &rem, + &data)) { + fprintf(stderr, "Clipboard allocation failed\n"); + return; + } + + if (e->type == PropertyNotify && nitems == 0 && rem == 0) { + /* + * If there is some PropertyNotify with no data, then + * this is the signal of the selection owner that all + * data has been transferred. We won't need to receive + * PropertyNotify events anymore. + */ + MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + } + + if (type == incratom) { + /* + * Activate the PropertyNotify events so we receive + * when the selection owner does send us the next + * chunk of data. + */ + MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + + /* + * Deleting the property is the transfer start signal. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); + continue; + } + + /* + * As seen in getsel: + * Line endings are inconsistent in the terminal and GUI world + * copy and pasting. When receiving some selection data, + * replace all '\n' with '\r'. + * FIXME: Fix the computer world. + */ + repl = data; + last = data + nitems * format / 8; + while ((repl = memchr(repl, '\n', last - repl))) { + *repl++ = '\r'; + } + + if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) + ttywrite("\033[200~", 6, 0); + ttywrite((char *)data, nitems * format / 8, 1); + if (IS_SET(MODE_BRCKTPASTE) && rem == 0) + ttywrite("\033[201~", 6, 0); + XFree(data); + /* number of 32-bit chunks returned */ + ofs += nitems * format / 32; + } while (rem > 0); + + /* + * Deleting the property again tells the selection owner to send the + * next data chunk in the property. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); +} + +void +xclipcopy(void) +{ + clipcopy(NULL); +} + +void +selclear_(XEvent *e) +{ + selclear(); +} + +void +selrequest(XEvent *e) +{ + XSelectionRequestEvent *xsre; + XSelectionEvent xev; + Atom xa_targets, string, clipboard; + char *seltext; + + xsre = (XSelectionRequestEvent *) e; + xev.type = SelectionNotify; + xev.requestor = xsre->requestor; + xev.selection = xsre->selection; + xev.target = xsre->target; + xev.time = xsre->time; + if (xsre->property == None) + xsre->property = xsre->target; + + /* reject */ + xev.property = None; + + xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); + if (xsre->target == xa_targets) { + /* respond with the supported type */ + string = xsel.xtarget; + XChangeProperty(xsre->display, xsre->requestor, xsre->property, + XA_ATOM, 32, PropModeReplace, + (uchar *) &string, 1); + xev.property = xsre->property; + } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { + /* + * xith XA_STRING non ascii characters may be incorrect in the + * requestor. It is not our problem, use utf8. + */ + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + if (xsre->selection == XA_PRIMARY) { + seltext = xsel.primary; + } else if (xsre->selection == clipboard) { + seltext = xsel.clipboard; + } else { + fprintf(stderr, + "Unhandled clipboard selection 0x%lx\n", + xsre->selection); + return; + } + if (seltext != NULL) { + XChangeProperty(xsre->display, xsre->requestor, + xsre->property, xsre->target, + 8, PropModeReplace, + (uchar *)seltext, strlen(seltext)); + xev.property = xsre->property; + } + } + + /* all done, send a notification to the listener */ + if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) + fprintf(stderr, "Error sending SelectionNotify event\n"); +} + +void +setsel(char *str, Time t) +{ + if (!str) + return; + + free(xsel.primary); + xsel.primary = str; + + XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); + if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) + selclear(); +} + +void +xsetsel(char *str) +{ + setsel(str, CurrentTime); +} + +void +brelease(XEvent *e) +{ + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 1)) + return; + if (e->xbutton.button == Button1) + mousesel(e, 1); +} + +void +bmotion(XEvent *e) +{ + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + mousesel(e, 0); +} + +void +cresize(int width, int height) +{ + int col, row; + + if (width != 0) + win.w = width; + if (height != 0) + win.h = height; + + col = (win.w - 2 * borderpx) / win.cw; + row = (win.h - 2 * borderpx) / win.ch; + col = MAX(1, col); + row = MAX(1, row); + + tresize(col, row); + xresize(col, row); + ttyresize(win.tw, win.th); +} + +void +xresize(int col, int row) +{ + win.tw = col * win.cw; + win.th = row * win.ch; + + XFreePixmap(xw.dpy, xw.buf); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, + xw.depth); + XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + + /* resize to new width */ + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); +} + +ushort +sixd_to_16bit(int x) +{ + return x == 0 ? 0 : 0x3737 + 0x2828 * x; +} + +int +xloadcolor(int i, const char *name, Color *ncolor) +{ + XRenderColor color = { .alpha = 0xffff }; + + if (!name) { + if (BETWEEN(i, 16, 255)) { /* 256 color */ + if (i < 6*6*6+16) { /* same colors as xterm */ + color.red = sixd_to_16bit( ((i-16)/36)%6 ); + color.green = sixd_to_16bit( ((i-16)/6) %6 ); + color.blue = sixd_to_16bit( ((i-16)/1) %6 ); + } else { /* greyscale */ + color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); + color.green = color.blue = color.red; + } + return XftColorAllocValue(xw.dpy, xw.vis, + xw.cmap, &color, ncolor); + } else + name = colorname[i]; + } + + return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); +} + +void +xloadcols(void) +{ + int i; + static int loaded; + Color *cp; + + if (loaded) { + for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) + XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); + } else { + dc.collen = MAX(LEN(colorname), 256); + dc.col = xmalloc(dc.collen * sizeof(Color)); + } + + for (i = 0; i < dc.collen; i++) + if (!xloadcolor(i, NULL, &dc.col[i])) { + if (colorname[i]) + die("could not allocate color '%s'\n", colorname[i]); + else + die("could not allocate color %d\n", i); + } + + /* set alpha value of bg color */ + if (opt_alpha) + alpha = strtof(opt_alpha, NULL); + dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); + dc.col[defaultbg].pixel &= 0x00FFFFFF; + dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; + loaded = 1; +} + +int +xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) +{ + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + *r = dc.col[x].color.red >> 8; + *g = dc.col[x].color.green >> 8; + *b = dc.col[x].color.blue >> 8; + + return 0; +} + +int +xsetcolorname(int x, const char *name) +{ + Color ncolor; + + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + if (!xloadcolor(x, name, &ncolor)) + return 1; + + XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); + dc.col[x] = ncolor; + + return 0; +} + +/* + * Absolute coordinates. + */ +void +xclear(int x1, int y1, int x2, int y2) +{ + XftDrawRect(xw.draw, + &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], + x1, y1, x2-x1, y2-y1); +} + +void +xhints(void) +{ + XClassHint class = {opt_name ? opt_name : termname, + opt_class ? opt_class : termname}; + XWMHints wm = {.flags = InputHint, .input = 1}; + XSizeHints *sizeh; + + sizeh = XAllocSizeHints(); + + sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; + sizeh->height = win.h; + sizeh->width = win.w; + sizeh->height_inc = win.ch; + sizeh->width_inc = win.cw; + sizeh->base_height = 2 * borderpx; + sizeh->base_width = 2 * borderpx; + sizeh->min_height = win.ch + 2 * borderpx; + sizeh->min_width = win.cw + 2 * borderpx; + if (xw.isfixed) { + sizeh->flags |= PMaxSize; + sizeh->min_width = sizeh->max_width = win.w; + sizeh->min_height = sizeh->max_height = win.h; + } + if (xw.gm & (XValue|YValue)) { + sizeh->flags |= USPosition | PWinGravity; + sizeh->x = xw.l; + sizeh->y = xw.t; + sizeh->win_gravity = xgeommasktogravity(xw.gm); + } + + XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, + &class); + XFree(sizeh); +} + +int +xgeommasktogravity(int mask) +{ + switch (mask & (XNegative|YNegative)) { + case 0: + return NorthWestGravity; + case XNegative: + return NorthEastGravity; + case YNegative: + return SouthWestGravity; + } + + return SouthEastGravity; +} + +int +xloadfont(Font *f, FcPattern *pattern) +{ + FcPattern *configured; + FcPattern *match; + FcResult result; + XGlyphInfo extents; + int wantattr, haveattr; + + /* + * Manually configure instead of calling XftMatchFont + * so that we can use the configured pattern for + * "missing glyph" lookups. + */ + configured = FcPatternDuplicate(pattern); + if (!configured) + return 1; + + FcConfigSubstitute(NULL, configured, FcMatchPattern); + XftDefaultSubstitute(xw.dpy, xw.scr, configured); + + match = FcFontMatch(NULL, configured, &result); + if (!match) { + FcPatternDestroy(configured); + return 1; + } + + if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { + FcPatternDestroy(configured); + FcPatternDestroy(match); + return 1; + } + + if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == + XftResultMatch)) { + /* + * Check if xft was unable to find a font with the appropriate + * slant but gave us one anyway. Try to mitigate. + */ + if ((XftPatternGetInteger(f->match->pattern, "slant", 0, + &haveattr) != XftResultMatch) || haveattr < wantattr) { + f->badslant = 1; + fputs("font slant does not match\n", stderr); + } + } + + if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == + XftResultMatch)) { + if ((XftPatternGetInteger(f->match->pattern, "weight", 0, + &haveattr) != XftResultMatch) || haveattr != wantattr) { + f->badweight = 1; + fputs("font weight does not match\n", stderr); + } + } + + XftTextExtentsUtf8(xw.dpy, f->match, + (const FcChar8 *) ascii_printable, + strlen(ascii_printable), &extents); + + f->set = NULL; + f->pattern = configured; + + f->ascent = f->match->ascent; + f->descent = f->match->descent; + f->lbearing = 0; + f->rbearing = f->match->max_advance_width; + + f->height = f->ascent + f->descent; + f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); + + return 0; +} + +void +xloadfonts(const char *fontstr, double fontsize) +{ + FcPattern *pattern; + double fontval; + + if (fontstr[0] == '-') + pattern = XftXlfdParse(fontstr, False, False); + else + pattern = FcNameParse((const FcChar8 *)fontstr); + + if (!pattern) + die("can't open font %s\n", fontstr); + + if (fontsize > 1) { + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternDel(pattern, FC_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); + usedfontsize = fontsize; + } else { + if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = fontval; + } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = -1; + } else { + /* + * Default font size is 12, if none given. This is to + * have a known usedfontsize value. + */ + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); + usedfontsize = 12; + } + defaultfontsize = usedfontsize; + } + + if (xloadfont(&dc.font, pattern)) + die("can't open font %s\n", fontstr); + + if (usedfontsize < 0) { + FcPatternGetDouble(dc.font.match->pattern, + FC_PIXEL_SIZE, 0, &fontval); + usedfontsize = fontval; + if (fontsize == 0) + defaultfontsize = fontval; + } + + /* Setting character width and height. */ + win.cw = ceilf(dc.font.width * cwscale); + win.ch = ceilf(dc.font.height * chscale); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + if (xloadfont(&dc.ifont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_WEIGHT); + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + if (xloadfont(&dc.ibfont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + if (xloadfont(&dc.bfont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDestroy(pattern); +} + +void +xunloadfont(Font *f) +{ + XftFontClose(xw.dpy, f->match); + FcPatternDestroy(f->pattern); + if (f->set) + FcFontSetDestroy(f->set); +} + +void +xunloadfonts(void) +{ + /* Free the loaded fonts in the font cache. */ + while (frclen > 0) + XftFontClose(xw.dpy, frc[--frclen].font); + + xunloadfont(&dc.font); + xunloadfont(&dc.bfont); + xunloadfont(&dc.ifont); + xunloadfont(&dc.ibfont); +} + +int +ximopen(Display *dpy) +{ + XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; + XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; + + xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); + if (xw.ime.xim == NULL) + return 0; + + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) + fprintf(stderr, "XSetIMValues: " + "Could not set XNDestroyCallback.\n"); + + xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, + NULL); + + if (xw.ime.xic == NULL) { + xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, + XNDestroyCallback, &icdestroy, + NULL); + } + if (xw.ime.xic == NULL) + fprintf(stderr, "XCreateIC: Could not create input context.\n"); + + return 1; +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + if (ximopen(dpy)) + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.ime.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + XFree(xw.ime.spotlist); +} + +int +xicdestroy(XIC xim, XPointer client, XPointer call) +{ + xw.ime.xic = NULL; + return 1; +} + +void +xinit(int cols, int rows) +{ + XGCValues gcvalues; + Cursor cursor; + Window parent; + pid_t thispid = getpid(); + XColor xmousefg, xmousebg; + XWindowAttributes attr; + XVisualInfo vis; + + if (!(xw.dpy = XOpenDisplay(NULL))) + die("can't open display\n"); + xw.scr = XDefaultScreen(xw.dpy); + + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { + parent = XRootWindow(xw.dpy, xw.scr); + xw.depth = 32; + } else { + XGetWindowAttributes(xw.dpy, parent, &attr); + xw.depth = attr.depth; + } + + XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); + xw.vis = vis.visual; + + /* font */ + if (!FcInit()) + die("could not init fontconfig.\n"); + + usedfont = (opt_font == NULL)? font : opt_font; + xloadfonts(usedfont, 0); + + /* colors */ + xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); + xloadcols(); + + /* adjust fixed window geometry */ + win.w = 2 * borderpx + cols * win.cw; + win.h = 2 * borderpx + rows * win.ch; + if (xw.gm & XNegative) + xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; + if (xw.gm & YNegative) + xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; + + /* Events */ + xw.attrs.background_pixel = dc.col[defaultbg].pixel; + xw.attrs.border_pixel = dc.col[defaultbg].pixel; + xw.attrs.bit_gravity = NorthWestGravity; + xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask + | ExposureMask | VisibilityChangeMask | StructureNotifyMask + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + xw.attrs.colormap = xw.cmap; + + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, + win.w, win.h, 0, xw.depth, InputOutput, + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &xw.attrs); + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); + dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + + /* font spec buffer */ + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); + + /* Xft rendering context */ + xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); + + /* input methods */ + if (!ximopen(xw.dpy)) { + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + } + + /* white cursor, black outline */ + cursor = XCreateFontCursor(xw.dpy, mouseshape); + XDefineCursor(xw.dpy, xw.win, cursor); + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { + xmousefg.red = 0xffff; + xmousefg.green = 0xffff; + xmousefg.blue = 0xffff; + } + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { + xmousebg.red = 0x0000; + xmousebg.green = 0x0000; + xmousebg.blue = 0x0000; + } + + XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); + + xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); + xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); + xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); + xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); + XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); + + xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); + XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, + PropModeReplace, (uchar *)&thispid, 1); + + win.mode = MODE_NUMLOCK; + resettitle(); + xhints(); + XMapWindow(xw.dpy, xw.win); + XSync(xw.dpy, False); + + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); + xsel.primary = NULL; + xsel.clipboard = NULL; + xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); + if (xsel.xtarget == None) + xsel.xtarget = XA_STRING; +} + +int +xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) +{ + float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; + ushort mode, prevmode = USHRT_MAX; + Font *font = &dc.font; + int frcflags = FRC_NORMAL; + float runewidth = win.cw; + Rune rune; + FT_UInt glyphidx; + FcResult fcres; + FcPattern *fcpattern, *fontpattern; + FcFontSet *fcsets[] = { NULL }; + FcCharSet *fccharset; + int i, f, numspecs = 0; + + for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { + /* Fetch rune and mode for current glyph. */ + rune = glyphs[i].u; + mode = glyphs[i].mode; + + /* Skip dummy wide-character spacing. */ + if (mode == ATTR_WDUMMY) + continue; + + /* Determine font for glyph if different from previous glyph. */ + if (prevmode != mode) { + prevmode = mode; + font = &dc.font; + frcflags = FRC_NORMAL; + runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { + font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } else if (mode & ATTR_ITALIC) { + font = &dc.ifont; + frcflags = FRC_ITALIC; + } else if (mode & ATTR_BOLD) { + font = &dc.bfont; + frcflags = FRC_BOLD; + } + yp = winy + font->ascent; + } + + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + if (glyphidx) { + specs[numspecs].font = font->match; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + continue; + } + + /* Fallback on font cache, search the font cache for match. */ + for (f = 0; f < frclen; f++) { + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); + /* Everything correct. */ + if (glyphidx && frc[f].flags == frcflags) + break; + /* We got a default font for a not found glyph. */ + if (!glyphidx && frc[f].flags == frcflags + && frc[f].unicodep == rune) { + break; + } + } + + /* Nothing was found. Use fontconfig to find matching font. */ + if (f >= frclen) { + if (!font->set) + font->set = FcFontSort(0, font->pattern, + 1, 0, &fcres); + fcsets[0] = font->set; + + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + * + * Xft and fontconfig are design failures. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, + fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + + FcConfigSubstitute(0, fcpattern, + FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + fontpattern = FcFontSetMatch(0, fcsets, 1, + fcpattern, &fcres); + + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } + + frc[frclen].font = XftFontOpenPattern(xw.dpy, + fontpattern); + if (!frc[frclen].font) + die("XftFontOpenPattern failed seeking fallback font: %s\n", + strerror(errno)); + frc[frclen].flags = frcflags; + frc[frclen].unicodep = rune; + + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); + + f = frclen; + frclen++; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + } + + specs[numspecs].font = frc[f].font; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + } + + return numspecs; +} + +void +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) +{ + int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); + int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, + width = charlen * win.cw; + Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; + XRenderColor colfg, colbg; + XRectangle r; + + /* Fallback on color display for attributes not supported by the font */ + if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { + if (dc.ibfont.badslant || dc.ibfont.badweight) + base.fg = defaultattr; + } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || + (base.mode & ATTR_BOLD && dc.bfont.badweight)) { + base.fg = defaultattr; + } + + if (IS_TRUECOL(base.fg)) { + colfg.alpha = 0xffff; + colfg.red = TRUERED(base.fg); + colfg.green = TRUEGREEN(base.fg); + colfg.blue = TRUEBLUE(base.fg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); + fg = &truefg; + } else { + fg = &dc.col[base.fg]; + } + + if (IS_TRUECOL(base.bg)) { + colbg.alpha = 0xffff; + colbg.green = TRUEGREEN(base.bg); + colbg.red = TRUERED(base.bg); + colbg.blue = TRUEBLUE(base.bg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); + bg = &truebg; + } else { + bg = &dc.col[base.bg]; + } + + /* Change basic system colors [0-7] to bright system colors [8-15] */ + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) + fg = &dc.col[base.fg + 8]; + + if (IS_SET(MODE_REVERSE)) { + if (fg == &dc.col[defaultfg]) { + fg = &dc.col[defaultbg]; + } else { + colfg.red = ~fg->color.red; + colfg.green = ~fg->color.green; + colfg.blue = ~fg->color.blue; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, + &revfg); + fg = &revfg; + } + + if (bg == &dc.col[defaultbg]) { + bg = &dc.col[defaultfg]; + } else { + colbg.red = ~bg->color.red; + colbg.green = ~bg->color.green; + colbg.blue = ~bg->color.blue; + colbg.alpha = bg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, + &revbg); + bg = &revbg; + } + } + + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { + colfg.red = fg->color.red / 2; + colfg.green = fg->color.green / 2; + colfg.blue = fg->color.blue / 2; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); + fg = &revfg; + } + + if (base.mode & ATTR_REVERSE) { + temp = fg; + fg = bg; + bg = temp; + } + + if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) + fg = bg; + + if (base.mode & ATTR_INVISIBLE) + fg = bg; + + /* Intelligent cleaning up of the borders. */ + if (x == 0) { + xclear(0, (y == 0)? 0 : winy, borderpx, + winy + win.ch + + ((winy + win.ch >= borderpx + win.th)? win.h : 0)); + } + if (winx + width >= borderpx + win.tw) { + xclear(winx + width, (y == 0)? 0 : winy, win.w, + ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); + } + if (y == 0) + xclear(winx, 0, winx + width, borderpx); + if (winy + win.ch >= borderpx + win.th) + xclear(winx, winy + win.ch, winx + width, win.h); + + /* Clean up the region we want to draw to. */ + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + + /* Set the clip region because Xft is sometimes dirty. */ + r.x = 0; + r.y = 0; + r.height = win.ch; + r.width = width; + XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + + /* Render underline and strikethrough. */ + if (base.mode & ATTR_UNDERLINE) { + XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, + width, 1); + } + + if (base.mode & ATTR_STRUCK) { + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, + width, 1); + } + + /* Reset clip to none. */ + XftDrawSetClip(xw.draw, 0); +} + +void +xdrawglyph(Glyph g, int x, int y) +{ + int numspecs; + XftGlyphFontSpec spec; + + numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); + xdrawglyphfontspecs(&spec, g, numspecs, x, y); +} + +void +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) +{ + Color drawcol; + + /* remove the old cursor */ + if (selected(ox, oy)) + og.mode ^= ATTR_REVERSE; + xdrawglyph(og, ox, oy); + + if (IS_SET(MODE_HIDE)) + return; + + /* + * Select the right color for the right mode. + */ + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; + + if (IS_SET(MODE_REVERSE)) { + g.mode |= ATTR_REVERSE; + g.bg = defaultfg; + if (selected(cx, cy)) { + drawcol = dc.col[defaultcs]; + g.fg = defaultrcs; + } else { + drawcol = dc.col[defaultrcs]; + g.fg = defaultcs; + } + } else { + if (selected(cx, cy)) { + g.fg = defaultfg; + g.bg = defaultrcs; + } else { + g.fg = defaultbg; + g.bg = defaultcs; + } + drawcol = dc.col[g.bg]; + } + + /* draw the new one */ + if (IS_SET(MODE_FOCUSED)) { + switch (win.cursor) { + case 7: /* st extension */ + g.u = 0x2603; /* snowman (U+2603) */ + /* FALLTHROUGH */ + case 0: /* Blinking Block */ + case 1: /* Blinking Block (Default) */ + case 2: /* Steady Block */ + xdrawglyph(g, cx, cy); + break; + case 3: /* Blinking Underline */ + case 4: /* Steady Underline */ + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - \ + cursorthickness, + win.cw, cursorthickness); + break; + case 5: /* Blinking bar */ + case 6: /* Steady bar */ + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + cursorthickness, win.ch); + break; + } + } else { + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + win.cw - 1, 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + (cx + 1) * win.cw - 1, + borderpx + cy * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - 1, + win.cw, 1); + } +} + +void +xsetenv(void) +{ + char buf[sizeof(long) * 8 + 1]; + + snprintf(buf, sizeof(buf), "%lu", xw.win); + setenv("WINDOWID", buf, 1); +} + +void +xseticontitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; + XSetWMIconName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); + XFree(prop.value); +} + +void +xsettitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; + XSetWMName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); + XFree(prop.value); +} + +int +xstartdraw(void) +{ + return IS_SET(MODE_VISIBLE); +} + +void +xdrawline(Line line, int x1, int y1, int x2) +{ + int i, x, ox, numspecs; + Glyph base, new; + XftGlyphFontSpec *specs = xw.specbuf; + + numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + if (new.mode == ATTR_WDUMMY) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1); +} + +void +xfinishdraw(void) +{ + XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, + win.h, 0, 0); + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(MODE_REVERSE)? + defaultfg : defaultbg].pixel); +} + +void +xximspot(int x, int y) +{ + if (xw.ime.xic == NULL) + return; + + xw.ime.spot.x = borderpx + x * win.cw; + xw.ime.spot.y = borderpx + (y + 1) * win.ch; + + XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); +} + +void +expose(XEvent *ev) +{ + redraw(); +} + +void +visibility(XEvent *ev) +{ + XVisibilityEvent *e = &ev->xvisibility; + + MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); +} + +void +unmap(XEvent *ev) +{ + win.mode &= ~MODE_VISIBLE; +} + +void +xsetpointermotion(int set) +{ + MODBIT(xw.attrs.event_mask, set, PointerMotionMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); +} + +void +xsetmode(int set, unsigned int flags) +{ + int mode = win.mode; + MODBIT(win.mode, set, flags); + if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) + redraw(); +} + +int +xsetcursor(int cursor) +{ + if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ + return 1; + win.cursor = cursor; + return 0; +} + +void +xseturgency(int add) +{ + XWMHints *h = XGetWMHints(xw.dpy, xw.win); + + MODBIT(h->flags, add, XUrgencyHint); + XSetWMHints(xw.dpy, xw.win, h); + XFree(h); +} + +void +xbell(void) +{ + if (!(IS_SET(MODE_FOCUSED))) + xseturgency(1); + if (bellvolume) + XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); +} + +void +focus(XEvent *ev) +{ + XFocusChangeEvent *e = &ev->xfocus; + + if (e->mode == NotifyGrab) + return; + + if (ev->type == FocusIn) { + if (xw.ime.xic) + XSetICFocus(xw.ime.xic); + win.mode |= MODE_FOCUSED; + xseturgency(0); + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[I", 3, 0); + } else { + if (xw.ime.xic) + XUnsetICFocus(xw.ime.xic); + win.mode &= ~MODE_FOCUSED; + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[O", 3, 0); + } +} + +int +match(uint mask, uint state) +{ + return mask == XK_ANY_MOD || mask == (state & ~ignoremod); +} + +char* +kmap(KeySym k, uint state) +{ + Key *kp; + int i; + + /* Check for mapped keys out of X11 function keys. */ + for (i = 0; i < LEN(mappedkeys); i++) { + if (mappedkeys[i] == k) + break; + } + if (i == LEN(mappedkeys)) { + if ((k & 0xFFFF) < 0xFD00) + return NULL; + } + + for (kp = key; kp < key + LEN(key); kp++) { + if (kp->k != k) + continue; + + if (!match(kp->mask, state)) + continue; + + if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) + continue; + if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) + continue; + + if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) + continue; + + return kp->s; + } + + return NULL; +} + +void +kpress(XEvent *ev) +{ + XKeyEvent *e = &ev->xkey; + KeySym ksym; + char buf[64], *customkey; + int len; + Rune c; + Status status; + Shortcut *bp; + + if (IS_SET(MODE_KBDLOCK)) + return; + + if (xw.ime.xic) + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); + else + len = XLookupString(e, buf, sizeof buf, &ksym, NULL); + /* 1. shortcuts */ + for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { + if (ksym == bp->keysym && match(bp->mod, e->state)) { + bp->func(&(bp->arg)); + return; + } + } + + /* 2. custom keys from config.h */ + if ((customkey = kmap(ksym, e->state))) { + ttywrite(customkey, strlen(customkey), 1); + return; + } + + /* 3. composed string from input method */ + if (len == 0) + return; + if (len == 1 && e->state & Mod1Mask) { + if (IS_SET(MODE_8BIT)) { + if (*buf < 0177) { + c = *buf | 0x80; + len = utf8encode(c, buf); + } + } else { + buf[1] = buf[0]; + buf[0] = '\033'; + len = 2; + } + } + ttywrite(buf, len, 1); +} + +void +cmessage(XEvent *e) +{ + /* + * See xembed specs + * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html + */ + if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { + if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { + win.mode |= MODE_FOCUSED; + xseturgency(0); + } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { + win.mode &= ~MODE_FOCUSED; + } + } else if (e->xclient.data.l[0] == xw.wmdeletewin) { + ttyhangup(); + exit(0); + } +} + +void +resize(XEvent *e) +{ + if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) + return; + + cresize(e->xconfigure.width, e->xconfigure.height); +} + +void +run(void) +{ + XEvent ev; + int w = win.w, h = win.h; + fd_set rfd; + int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; + struct timespec seltv, *tv, now, lastblink, trigger; + double timeout; + + /* Waiting for window mapping */ + do { + XNextEvent(xw.dpy, &ev); + /* + * This XFilterEvent call is required because of XOpenIM. It + * does filter out the key event and some client message for + * the input method too. + */ + if (XFilterEvent(&ev, None)) + continue; + if (ev.type == ConfigureNotify) { + w = ev.xconfigure.width; + h = ev.xconfigure.height; + } + } while (ev.type != MapNotify); + + ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); + cresize(w, h); + + for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { + FD_ZERO(&rfd); + FD_SET(ttyfd, &rfd); + FD_SET(xfd, &rfd); + + if (XPending(xw.dpy)) + timeout = 0; /* existing events might not set xfd */ + + seltv.tv_sec = timeout / 1E3; + seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); + tv = timeout >= 0 ? &seltv : NULL; + + if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + clock_gettime(CLOCK_MONOTONIC, &now); + + if (FD_ISSET(ttyfd, &rfd)) + ttyread(); + + xev = 0; + while (XPending(xw.dpy)) { + xev = 1; + XNextEvent(xw.dpy, &ev); + if (XFilterEvent(&ev, None)) + continue; + if (handler[ev.type]) + (handler[ev.type])(&ev); + } + + /* + * To reduce flicker and tearing, when new content or event + * triggers drawing, we first wait a bit to ensure we got + * everything, and if nothing new arrives - we draw. + * We start with trying to wait minlatency ms. If more content + * arrives sooner, we retry with shorter and shorter periods, + * and eventually draw even without idle after maxlatency ms. + * Typically this results in low latency while interacting, + * maximum latency intervals during `cat huge.txt`, and perfect + * sync with periodic updates from animations/key-repeats/etc. + */ + if (FD_ISSET(ttyfd, &rfd) || xev) { + if (!drawing) { + trigger = now; + drawing = 1; + } + timeout = (maxlatency - TIMEDIFF(now, trigger)) \ + / maxlatency * minlatency; + if (timeout > 0) + continue; /* we have time, try to find idle */ + } + + /* idle detected or maxlatency exhausted -> draw */ + timeout = -1; + if (blinktimeout && tattrset(ATTR_BLINK)) { + timeout = blinktimeout - TIMEDIFF(now, lastblink); + if (timeout <= 0) { + if (-timeout > blinktimeout) /* start visible */ + win.mode |= MODE_BLINK; + win.mode ^= MODE_BLINK; + tsetdirtattr(ATTR_BLINK); + lastblink = now; + timeout = blinktimeout; + } + } + + draw(); + XFlush(xw.dpy); + drawing = 0; + } +} + +void +usage(void) +{ + die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid]" + " [[-e] command [args ...]]\n" + " %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid] -l line" + " [stty_args ...]\n", argv0, argv0); +} + +int +main(int argc, char *argv[]) +{ + xw.l = xw.t = 0; + xw.isfixed = False; + xsetcursor(cursorshape); + + ARGBEGIN { + case 'a': + allowaltscreen = 0; + break; + case 'A': + opt_alpha = EARGF(usage()); + break; + case 'c': + opt_class = EARGF(usage()); + break; + case 'e': + if (argc > 0) + --argc, ++argv; + goto run; + case 'f': + opt_font = EARGF(usage()); + break; + case 'g': + xw.gm = XParseGeometry(EARGF(usage()), + &xw.l, &xw.t, &cols, &rows); + break; + case 'i': + xw.isfixed = 1; + break; + case 'o': + opt_io = EARGF(usage()); + break; + case 'l': + opt_line = EARGF(usage()); + break; + case 'n': + opt_name = EARGF(usage()); + break; + case 't': + case 'T': + opt_title = EARGF(usage()); + break; + case 'w': + opt_embed = EARGF(usage()); + break; + case 'v': + die("%s " VERSION "\n", argv0); + break; + default: + usage(); + } ARGEND; + +run: + if (argc > 0) /* eat all remaining arguments */ + opt_cmd = argv; + + if (!opt_title) + opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; + + setlocale(LC_CTYPE, ""); + XSetLocaleModifiers(""); + cols = MAX(cols, 1); + rows = MAX(rows, 1); + tnew(cols, rows); + xinit(cols, rows); + xsetenv(); + selinit(); + run(); + + return 0; +} diff --git a/bst/x.o b/bst/x.o new file mode 100644 index 0000000000000000000000000000000000000000..5555c62a35627c7f952eef8ce8094135c509a956 GIT binary patch literal 75856 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 V2kM(*(| +#include "st.h" +#include "boxdraw_data.h" + +/* Rounded non-negative integers division of n / d */ +#define DIV(n, d) (((n) + (d) / 2) / (d)) + +static Display *xdpy; +static Colormap xcmap; +static XftDraw *xd; +static Visual *xvis; + +static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort); +static void drawboxlines(int, int, int, int, XftColor *, ushort); + +/* public API */ + +void +boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis) +{ + xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis; +} + +int +isboxdraw(Rune u) +{ + Rune block = u & ~0xff; + return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) || + (boxdraw_braille && block == 0x2800); +} + +/* the "index" is actually the entire shape data encoded as ushort */ +ushort +boxdrawindex(const Glyph *g) +{ + if (boxdraw_braille && (g->u & ~0xff) == 0x2800) + return BRL | (uint8_t)g->u; + if (boxdraw_bold && (g->mode & ATTR_BOLD)) + return BDB | boxdata[(uint8_t)g->u]; + return boxdata[(uint8_t)g->u]; +} + +void +drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg, + const XftGlyphFontSpec *specs, int len) +{ + for ( ; len-- > 0; x += cw, specs++) + drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph); +} + +/* implementation */ + +void +drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd) +{ + ushort cat = bd & ~(BDB | 0xff); /* mask out bold and data */ + if (bd & (BDL | BDA)) { + /* lines (light/double/heavy/arcs) */ + drawboxlines(x, y, w, h, fg, bd); + + } else if (cat == BBD) { + /* lower (8-X)/8 block */ + int d = DIV((uint8_t)bd * h, 8); + XftDrawRect(xd, fg, x, y + d, w, h - d); + + } else if (cat == BBU) { + /* upper X/8 block */ + XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8)); + + } else if (cat == BBL) { + /* left X/8 block */ + XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h); + + } else if (cat == BBR) { + /* right (8-X)/8 block */ + int d = DIV((uint8_t)bd * w, 8); + XftDrawRect(xd, fg, x + d, y, w - d, h); + + } else if (cat == BBQ) { + /* Quadrants */ + int w2 = DIV(w, 2), h2 = DIV(h, 2); + if (bd & TL) + XftDrawRect(xd, fg, x, y, w2, h2); + if (bd & TR) + XftDrawRect(xd, fg, x + w2, y, w - w2, h2); + if (bd & BL) + XftDrawRect(xd, fg, x, y + h2, w2, h - h2); + if (bd & BR) + XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2); + + } else if (bd & BBS) { + /* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */ + int d = (uint8_t)bd; + XftColor xfc; + XRenderColor xrc = { .alpha = 0xffff }; + + xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4); + xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4); + xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4); + + XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc); + XftDrawRect(xd, &xfc, x, y, w, h); + XftColorFree(xdpy, xvis, xcmap, &xfc); + + } else if (cat == BRL) { + /* braille, each data bit corresponds to one dot at 2x4 grid */ + int w1 = DIV(w, 2); + int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4); + + if (bd & 1) XftDrawRect(xd, fg, x, y, w1, h1); + if (bd & 2) XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1); + if (bd & 4) XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2); + if (bd & 8) XftDrawRect(xd, fg, x + w1, y, w - w1, h1); + if (bd & 16) XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1); + if (bd & 32) XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2); + if (bd & 64) XftDrawRect(xd, fg, x, y + h3, w1, h - h3); + if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3); + + } +} + +void +drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd) +{ + /* s: stem thickness. width/8 roughly matches underscore thickness. */ + /* We draw bold as 1.5 * normal-stem and at least 1px thicker. */ + /* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */ + int mwh = MIN(w, h); + int base_s = MAX(1, DIV(mwh, 8)); + int bold = (bd & BDB) && mwh >= 6; /* possibly ignore boldness */ + int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s; + int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2); + /* the s-by-s square (x + w2, y + h2, s, s) is the center texel. */ + /* The base length (per direction till edge) includes this square. */ + + int light = bd & (LL | LU | LR | LD); + int double_ = bd & (DL | DU | DR | DD); + + if (light) { + /* d: additional (negative) length to not-draw the center */ + /* texel - at arcs and avoid drawing inside (some) doubles */ + int arc = bd & BDA; + int multi_light = light & (light - 1); + int multi_double = double_ & (double_ - 1); + /* light crosses double only at DH+LV, DV+LH (ref. shapes) */ + int d = arc || (multi_double && !multi_light) ? -s : 0; + + if (bd & LL) + XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s); + if (bd & LU) + XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d); + if (bd & LR) + XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s); + if (bd & LD) + XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d); + } + + /* double lines - also align with light to form heavy when combined */ + if (double_) { + /* + * going clockwise, for each double-ray: p is additional length + * to the single-ray nearer to the previous direction, and n to + * the next. p and n adjust from the base length to lengths + * which consider other doubles - shorter to avoid intersections + * (p, n), or longer to draw the far-corner texel (n). + */ + int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD; + if (dl) { + int p = dd ? -s : 0, n = du ? -s : dd ? s : 0; + XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s); + XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s); + } + if (du) { + int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0; + XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p); + XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n); + } + if (dr) { + int p = du ? -s : 0, n = dd ? -s : du ? s : 0; + XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s); + XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s); + } + if (dd) { + int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0; + XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p); + XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n); + } + } +} diff --git a/st/b/boxdraw_data.h b/st/b/boxdraw_data.h new file mode 100644 index 00000000..78905008 --- /dev/null +++ b/st/b/boxdraw_data.h @@ -0,0 +1,214 @@ +/* + * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih + * MIT/X Consortium License + */ + +/* + * U+25XX codepoints data + * + * References: + * http://www.unicode.org/charts/PDF/U2500.pdf + * http://www.unicode.org/charts/PDF/U2580.pdf + * + * Test page: + * https://github.com/GNOME/vte/blob/master/doc/boxes.txt + */ + +/* Each shape is encoded as 16-bits. Higher bits are category, lower are data */ +/* Categories (mutually exclusive except BDB): */ +/* For convenience, BDL/BDA/BBS/BDB are 1 bit each, the rest are enums */ +#define BDL (1<<8) /* Box Draw Lines (light/double/heavy) */ +#define BDA (1<<9) /* Box Draw Arc (light) */ + +#define BBD (1<<10) /* Box Block Down (lower) X/8 */ +#define BBL (2<<10) /* Box Block Left X/8 */ +#define BBU (3<<10) /* Box Block Upper X/8 */ +#define BBR (4<<10) /* Box Block Right X/8 */ +#define BBQ (5<<10) /* Box Block Quadrants */ +#define BRL (6<<10) /* Box Braille (data is lower byte of U28XX) */ + +#define BBS (1<<14) /* Box Block Shades */ +#define BDB (1<<15) /* Box Draw is Bold */ + +/* (BDL/BDA) Light/Double/Heavy x Left/Up/Right/Down/Horizontal/Vertical */ +/* Heavy is light+double (literally drawing light+double align to form heavy) */ +#define LL (1<<0) +#define LU (1<<1) +#define LR (1<<2) +#define LD (1<<3) +#define LH (LL+LR) +#define LV (LU+LD) + +#define DL (1<<4) +#define DU (1<<5) +#define DR (1<<6) +#define DD (1<<7) +#define DH (DL+DR) +#define DV (DU+DD) + +#define HL (LL+DL) +#define HU (LU+DU) +#define HR (LR+DR) +#define HD (LD+DD) +#define HH (HL+HR) +#define HV (HU+HD) + +/* (BBQ) Quadrants Top/Bottom x Left/Right */ +#define TL (1<<0) +#define TR (1<<1) +#define BL (1<<2) +#define BR (1<<3) + +/* Data for U+2500 - U+259F except dashes/diagonals */ +static const unsigned short boxdata[256] = { + /* light lines */ + [0x00] = BDL + LH, /* light horizontal */ + [0x02] = BDL + LV, /* light vertical */ + [0x0c] = BDL + LD + LR, /* light down and right */ + [0x10] = BDL + LD + LL, /* light down and left */ + [0x14] = BDL + LU + LR, /* light up and right */ + [0x18] = BDL + LU + LL, /* light up and left */ + [0x1c] = BDL + LV + LR, /* light vertical and right */ + [0x24] = BDL + LV + LL, /* light vertical and left */ + [0x2c] = BDL + LH + LD, /* light horizontal and down */ + [0x34] = BDL + LH + LU, /* light horizontal and up */ + [0x3c] = BDL + LV + LH, /* light vertical and horizontal */ + [0x74] = BDL + LL, /* light left */ + [0x75] = BDL + LU, /* light up */ + [0x76] = BDL + LR, /* light right */ + [0x77] = BDL + LD, /* light down */ + + /* heavy [+light] lines */ + [0x01] = BDL + HH, + [0x03] = BDL + HV, + [0x0d] = BDL + HR + LD, + [0x0e] = BDL + HD + LR, + [0x0f] = BDL + HD + HR, + [0x11] = BDL + HL + LD, + [0x12] = BDL + HD + LL, + [0x13] = BDL + HD + HL, + [0x15] = BDL + HR + LU, + [0x16] = BDL + HU + LR, + [0x17] = BDL + HU + HR, + [0x19] = BDL + HL + LU, + [0x1a] = BDL + HU + LL, + [0x1b] = BDL + HU + HL, + [0x1d] = BDL + HR + LV, + [0x1e] = BDL + HU + LD + LR, + [0x1f] = BDL + HD + LR + LU, + [0x20] = BDL + HV + LR, + [0x21] = BDL + HU + HR + LD, + [0x22] = BDL + HD + HR + LU, + [0x23] = BDL + HV + HR, + [0x25] = BDL + HL + LV, + [0x26] = BDL + HU + LD + LL, + [0x27] = BDL + HD + LU + LL, + [0x28] = BDL + HV + LL, + [0x29] = BDL + HU + HL + LD, + [0x2a] = BDL + HD + HL + LU, + [0x2b] = BDL + HV + HL, + [0x2d] = BDL + HL + LD + LR, + [0x2e] = BDL + HR + LL + LD, + [0x2f] = BDL + HH + LD, + [0x30] = BDL + HD + LH, + [0x31] = BDL + HD + HL + LR, + [0x32] = BDL + HR + HD + LL, + [0x33] = BDL + HH + HD, + [0x35] = BDL + HL + LU + LR, + [0x36] = BDL + HR + LU + LL, + [0x37] = BDL + HH + LU, + [0x38] = BDL + HU + LH, + [0x39] = BDL + HU + HL + LR, + [0x3a] = BDL + HU + HR + LL, + [0x3b] = BDL + HH + HU, + [0x3d] = BDL + HL + LV + LR, + [0x3e] = BDL + HR + LV + LL, + [0x3f] = BDL + HH + LV, + [0x40] = BDL + HU + LH + LD, + [0x41] = BDL + HD + LH + LU, + [0x42] = BDL + HV + LH, + [0x43] = BDL + HU + HL + LD + LR, + [0x44] = BDL + HU + HR + LD + LL, + [0x45] = BDL + HD + HL + LU + LR, + [0x46] = BDL + HD + HR + LU + LL, + [0x47] = BDL + HH + HU + LD, + [0x48] = BDL + HH + HD + LU, + [0x49] = BDL + HV + HL + LR, + [0x4a] = BDL + HV + HR + LL, + [0x4b] = BDL + HV + HH, + [0x78] = BDL + HL, + [0x79] = BDL + HU, + [0x7a] = BDL + HR, + [0x7b] = BDL + HD, + [0x7c] = BDL + HR + LL, + [0x7d] = BDL + HD + LU, + [0x7e] = BDL + HL + LR, + [0x7f] = BDL + HU + LD, + + /* double [+light] lines */ + [0x50] = BDL + DH, + [0x51] = BDL + DV, + [0x52] = BDL + DR + LD, + [0x53] = BDL + DD + LR, + [0x54] = BDL + DR + DD, + [0x55] = BDL + DL + LD, + [0x56] = BDL + DD + LL, + [0x57] = BDL + DL + DD, + [0x58] = BDL + DR + LU, + [0x59] = BDL + DU + LR, + [0x5a] = BDL + DU + DR, + [0x5b] = BDL + DL + LU, + [0x5c] = BDL + DU + LL, + [0x5d] = BDL + DL + DU, + [0x5e] = BDL + DR + LV, + [0x5f] = BDL + DV + LR, + [0x60] = BDL + DV + DR, + [0x61] = BDL + DL + LV, + [0x62] = BDL + DV + LL, + [0x63] = BDL + DV + DL, + [0x64] = BDL + DH + LD, + [0x65] = BDL + DD + LH, + [0x66] = BDL + DD + DH, + [0x67] = BDL + DH + LU, + [0x68] = BDL + DU + LH, + [0x69] = BDL + DH + DU, + [0x6a] = BDL + DH + LV, + [0x6b] = BDL + DV + LH, + [0x6c] = BDL + DH + DV, + + /* (light) arcs */ + [0x6d] = BDA + LD + LR, + [0x6e] = BDA + LD + LL, + [0x6f] = BDA + LU + LL, + [0x70] = BDA + LU + LR, + + /* Lower (Down) X/8 block (data is 8 - X) */ + [0x81] = BBD + 7, [0x82] = BBD + 6, [0x83] = BBD + 5, [0x84] = BBD + 4, + [0x85] = BBD + 3, [0x86] = BBD + 2, [0x87] = BBD + 1, [0x88] = BBD + 0, + + /* Left X/8 block (data is X) */ + [0x89] = BBL + 7, [0x8a] = BBL + 6, [0x8b] = BBL + 5, [0x8c] = BBL + 4, + [0x8d] = BBL + 3, [0x8e] = BBL + 2, [0x8f] = BBL + 1, + + /* upper 1/2 (4/8), 1/8 block (X), right 1/2, 1/8 block (8-X) */ + [0x80] = BBU + 4, [0x94] = BBU + 1, + [0x90] = BBR + 4, [0x95] = BBR + 7, + + /* Quadrants */ + [0x96] = BBQ + BL, + [0x97] = BBQ + BR, + [0x98] = BBQ + TL, + [0x99] = BBQ + TL + BL + BR, + [0x9a] = BBQ + TL + BR, + [0x9b] = BBQ + TL + TR + BL, + [0x9c] = BBQ + TL + TR + BR, + [0x9d] = BBQ + TR, + [0x9e] = BBQ + BL + TR, + [0x9f] = BBQ + BL + TR + BR, + + /* Shades, data is an alpha value in 25% units (1/4, 1/2, 3/4) */ + [0x91] = BBS + 1, [0x92] = BBS + 2, [0x93] = BBS + 3, + + /* U+2504 - U+250B, U+254C - U+254F: unsupported (dashes) */ + /* U+2571 - U+2573: unsupported (diagonals) */ +}; diff --git a/st/boxdraw.c b/st/boxdraw.c new file mode 100644 index 00000000..74e49203 --- /dev/null +++ b/st/boxdraw.c @@ -0,0 +1,194 @@ +/* + * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih + * MIT/X Consortium License + */ + +#include +#include "st.h" +#include "boxdraw_data.h" + +/* Rounded non-negative integers division of n / d */ +#define DIV(n, d) (((n) + (d) / 2) / (d)) + +static Display *xdpy; +static Colormap xcmap; +static XftDraw *xd; +static Visual *xvis; + +static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort); +static void drawboxlines(int, int, int, int, XftColor *, ushort); + +/* public API */ + +void +boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis) +{ + xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis; +} + +int +isboxdraw(Rune u) +{ + Rune block = u & ~0xff; + return (block == 0x2500 && boxdata[(uint8_t)u]) || + (block == 0x2800); +} + +/* the "index" is actually the entire shape data encoded as ushort */ +ushort +boxdrawindex(const Glyph *g) +{ + if ((g->u & ~0xff) == 0x2800) + return BRL | (uint8_t)g->u; + if ((g->mode & ATTR_BOLD)) + return BDB | boxdata[(uint8_t)g->u]; + return boxdata[(uint8_t)g->u]; +} + +void +drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg, + const XftGlyphFontSpec *specs, int len) +{ + for ( ; len-- > 0; x += cw, specs++) + drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph); +} + +/* implementation */ + +void +drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd) +{ + ushort cat = bd & ~(BDB | 0xff); /* mask out bold and data */ + if (bd & (BDL | BDA)) { + /* lines (light/double/heavy/arcs) */ + drawboxlines(x, y, w, h, fg, bd); + + } else if (cat == BBD) { + /* lower (8-X)/8 block */ + int d = DIV((uint8_t)bd * h, 8); + XftDrawRect(xd, fg, x, y + d, w, h - d); + + } else if (cat == BBU) { + /* upper X/8 block */ + XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8)); + + } else if (cat == BBL) { + /* left X/8 block */ + XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h); + + } else if (cat == BBR) { + /* right (8-X)/8 block */ + int d = DIV((uint8_t)bd * w, 8); + XftDrawRect(xd, fg, x + d, y, w - d, h); + + } else if (cat == BBQ) { + /* Quadrants */ + int w2 = DIV(w, 2), h2 = DIV(h, 2); + if (bd & TL) + XftDrawRect(xd, fg, x, y, w2, h2); + if (bd & TR) + XftDrawRect(xd, fg, x + w2, y, w - w2, h2); + if (bd & BL) + XftDrawRect(xd, fg, x, y + h2, w2, h - h2); + if (bd & BR) + XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2); + + } else if (bd & BBS) { + /* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */ + int d = (uint8_t)bd; + XftColor xfc; + XRenderColor xrc = { .alpha = 0xffff }; + + xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4); + xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4); + xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4); + + XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc); + XftDrawRect(xd, &xfc, x, y, w, h); + XftColorFree(xdpy, xvis, xcmap, &xfc); + + } else if (cat == BRL) { + /* braille, each data bit corresponds to one dot at 2x4 grid */ + int w1 = DIV(w, 2); + int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4); + + if (bd & 1) XftDrawRect(xd, fg, x, y, w1, h1); + if (bd & 2) XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1); + if (bd & 4) XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2); + if (bd & 8) XftDrawRect(xd, fg, x + w1, y, w - w1, h1); + if (bd & 16) XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1); + if (bd & 32) XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2); + if (bd & 64) XftDrawRect(xd, fg, x, y + h3, w1, h - h3); + if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3); + + } +} + +void +drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd) +{ + /* s: stem thickness. width/8 roughly matches underscore thickness. */ + /* We draw bold as 1.5 * normal-stem and at least 1px thicker. */ + /* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */ + int mwh = MIN(w, h); + int base_s = MAX(1, DIV(mwh, 8)); + int bold = (bd & BDB) && mwh >= 6; /* possibly ignore boldness */ + int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s; + int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2); + /* the s-by-s square (x + w2, y + h2, s, s) is the center texel. */ + /* The base length (per direction till edge) includes this square. */ + + int light = bd & (LL | LU | LR | LD); + int double_ = bd & (DL | DU | DR | DD); + + if (light) { + /* d: additional (negative) length to not-draw the center */ + /* texel - at arcs and avoid drawing inside (some) doubles */ + int arc = bd & BDA; + int multi_light = light & (light - 1); + int multi_double = double_ & (double_ - 1); + /* light crosses double only at DH+LV, DV+LH (ref. shapes) */ + int d = arc || (multi_double && !multi_light) ? -s : 0; + + if (bd & LL) + XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s); + if (bd & LU) + XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d); + if (bd & LR) + XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s); + if (bd & LD) + XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d); + } + + /* double lines - also align with light to form heavy when combined */ + if (double_) { + /* + * going clockwise, for each double-ray: p is additional length + * to the single-ray nearer to the previous direction, and n to + * the next. p and n adjust from the base length to lengths + * which consider other doubles - shorter to avoid intersections + * (p, n), or longer to draw the far-corner texel (n). + */ + int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD; + if (dl) { + int p = dd ? -s : 0, n = du ? -s : dd ? s : 0; + XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s); + XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s); + } + if (du) { + int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0; + XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p); + XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n); + } + if (dr) { + int p = du ? -s : 0, n = dd ? -s : du ? s : 0; + XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s); + XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s); + } + if (dd) { + int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0; + XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p); + XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n); + } + } +} diff --git a/st/boxdraw.o b/st/boxdraw.o new file mode 100644 index 0000000000000000000000000000000000000000..2ac1a101c76fbecdc56acc7e0ae6600227b2fa5e GIT binary patch literal 7432 zcmdT}eQ;FO6@O>U2b{Q#T1n#t{W)6ma}NM>|;%1*e68%J!W1?oHO0 zy|y#`t9NGio_p@^eB5)+{n%tjB(~B4An^uLtu!^_TuIt>*%Tcz$&locW=T6-ay27OPceT&bgGRolQnUbXG7d8F$ zWiRPRlr`s#ZKaw%p$%kYO=&nie9<|OE7hJM&oyQB>97&I7b5zYHauPtfidmyMA%pj zk!52M<%Y2^+FYtXf2CkLuD*>e2z6Em8)b z#9>$;4;&rHfIc#Oq5=*bom1HYwG}h))BTDL4B=w``j-tNG(AoWn@<5&b>UEYn=U4Czg35LuOI?4Dvt z%HD}zn7e3p)x})y00x3w&pw6&@*9rgs?1fx8C(kvDsj2NLd(haSu%c823O#{8ofoh z+a+=>y9PzDQZTY=U?R_w;jJZ8flsaQ2B$4KTVWaVp{U;yHQbiW8PbN>hdv@SX(rZaVSxOX!<^DgPlgT)&?s@ zH<7{(7PKhbU^iJ*nUd!JY`IImW=MW|Rn0?T&4_?LI()JMMh}06Y4+g>h+))*KdR7RpLY0j3TW>t3TAJs z707f7?x3Mfy=D!ZmlT^l&`@T>lPX3$%jTkG&h5hc$O_}96!EIAWU-l-%Qcl_r zgw;E9x#whbK5)W}g|c_0OBvc9#j_-&?7bf8mtf3@TIDp1<+a4we0jQfuJw2FkLIX;Oe~sRkK2&s zRd#L_48=(VnI{%)B)3@}el0L8irkA+a6iH+Z({Io5X`rT`c=70PH814xC}WW+E%xd z2;F3~;oT*yK)ynth6Gqy{-^qwjac)@L<^OvoRU0`1){%u?p=I0;H5$5rW*KyAM{~E z^`QOQu>M})n7&?(>2CyH)U7i=TqI4~Vssa#5r*Cb10&|uPzc6ux4{c|m|8b$4PJKF z((s*g@8IjDhHh3LdVz8JC&dCTraSSDhJWFts-FoA(~WAl4FSTL1I%p=L{+zGn%j^h=S*o2JA>|^H;jQ2A7;>Vy|W7eKg>B+M|bv!K~ z=qANtC7q~(w?TH{d8{S_qAEYUimt^B_chi9>n~K|h>Mvon1~@)76N25 zxDdL{`dXulWb-B3J3HFHZx<&0_B*sD?GEiut$EaZ3?WOHMa3w4O53GhN7=%B=7I+0dhEkXdiy#euglpgt_#4cE%i#vN z0}S{j9E7)_1ilXSa0@Ji@4?-$4HT$=HP8&-fd^m+u7fW46}$>N;9YnDK7rh%KWpLF z5QQXUpd9+)L8t>2?ty$)}&*W)#n|381#uUPSYpTB-}W5Bn#x~jTrv9CHt@1Q+XdE@+fCZDpx8mn^}In8YG39Da#-E7pv8_z)vJJ)~(y6zLw4Ltw>6J_jdG{ zlkr5HgPQv~wsa(N~qbegN9SX+(4Pc*N8DpP8l@3w~0C9LOso>^We zoR3$z+3ND}Gmz%|4gr6MfWMX$W8?a{-A2Zzu?Ig~|5gFMSAc(3fd9Jye?)+POMt(Q z1)8Rb<7@zFX zdCl=x+3?(->O6eDKTQIBT!7aF_(1{wDFJ@30RN!?e?ox2lnzsDJZ^lwa~Yq;`RlC{ z;OhnWM+NxD1^6)m{-6NAfL$Fmdx^sD*`;ewacIm@g4I?<8zlWkWK7S82+4#G;qN<+ln-lSkQdLi) z+v+!Qs=KW-UX^I;OGs6%^jfLf*wZ6bb$3~Vmafg4+qU4`maarw6~48vG9MgleD{{# zs;%8!Tid!5+fjxzwQXu{?~ZS7^X4au%|N zQ{U{tvxl8uq=sbKww!z)FJOFjeNM_hZnMiL?Di{}PP_aQOy0)@SmHsPbmytWK!k`Lzbh|HM(pPKsZ!>76bysyIvPICEv> rV7m9{sIkkFPJ8Z=C2e9qN`6X%yk?Idm1!aQGWLJVM4l{>cKLq;*UCfx literal 0 HcmV?d00001 diff --git a/st/boxdraw_data.h b/st/boxdraw_data.h new file mode 100644 index 00000000..78905008 --- /dev/null +++ b/st/boxdraw_data.h @@ -0,0 +1,214 @@ +/* + * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih + * MIT/X Consortium License + */ + +/* + * U+25XX codepoints data + * + * References: + * http://www.unicode.org/charts/PDF/U2500.pdf + * http://www.unicode.org/charts/PDF/U2580.pdf + * + * Test page: + * https://github.com/GNOME/vte/blob/master/doc/boxes.txt + */ + +/* Each shape is encoded as 16-bits. Higher bits are category, lower are data */ +/* Categories (mutually exclusive except BDB): */ +/* For convenience, BDL/BDA/BBS/BDB are 1 bit each, the rest are enums */ +#define BDL (1<<8) /* Box Draw Lines (light/double/heavy) */ +#define BDA (1<<9) /* Box Draw Arc (light) */ + +#define BBD (1<<10) /* Box Block Down (lower) X/8 */ +#define BBL (2<<10) /* Box Block Left X/8 */ +#define BBU (3<<10) /* Box Block Upper X/8 */ +#define BBR (4<<10) /* Box Block Right X/8 */ +#define BBQ (5<<10) /* Box Block Quadrants */ +#define BRL (6<<10) /* Box Braille (data is lower byte of U28XX) */ + +#define BBS (1<<14) /* Box Block Shades */ +#define BDB (1<<15) /* Box Draw is Bold */ + +/* (BDL/BDA) Light/Double/Heavy x Left/Up/Right/Down/Horizontal/Vertical */ +/* Heavy is light+double (literally drawing light+double align to form heavy) */ +#define LL (1<<0) +#define LU (1<<1) +#define LR (1<<2) +#define LD (1<<3) +#define LH (LL+LR) +#define LV (LU+LD) + +#define DL (1<<4) +#define DU (1<<5) +#define DR (1<<6) +#define DD (1<<7) +#define DH (DL+DR) +#define DV (DU+DD) + +#define HL (LL+DL) +#define HU (LU+DU) +#define HR (LR+DR) +#define HD (LD+DD) +#define HH (HL+HR) +#define HV (HU+HD) + +/* (BBQ) Quadrants Top/Bottom x Left/Right */ +#define TL (1<<0) +#define TR (1<<1) +#define BL (1<<2) +#define BR (1<<3) + +/* Data for U+2500 - U+259F except dashes/diagonals */ +static const unsigned short boxdata[256] = { + /* light lines */ + [0x00] = BDL + LH, /* light horizontal */ + [0x02] = BDL + LV, /* light vertical */ + [0x0c] = BDL + LD + LR, /* light down and right */ + [0x10] = BDL + LD + LL, /* light down and left */ + [0x14] = BDL + LU + LR, /* light up and right */ + [0x18] = BDL + LU + LL, /* light up and left */ + [0x1c] = BDL + LV + LR, /* light vertical and right */ + [0x24] = BDL + LV + LL, /* light vertical and left */ + [0x2c] = BDL + LH + LD, /* light horizontal and down */ + [0x34] = BDL + LH + LU, /* light horizontal and up */ + [0x3c] = BDL + LV + LH, /* light vertical and horizontal */ + [0x74] = BDL + LL, /* light left */ + [0x75] = BDL + LU, /* light up */ + [0x76] = BDL + LR, /* light right */ + [0x77] = BDL + LD, /* light down */ + + /* heavy [+light] lines */ + [0x01] = BDL + HH, + [0x03] = BDL + HV, + [0x0d] = BDL + HR + LD, + [0x0e] = BDL + HD + LR, + [0x0f] = BDL + HD + HR, + [0x11] = BDL + HL + LD, + [0x12] = BDL + HD + LL, + [0x13] = BDL + HD + HL, + [0x15] = BDL + HR + LU, + [0x16] = BDL + HU + LR, + [0x17] = BDL + HU + HR, + [0x19] = BDL + HL + LU, + [0x1a] = BDL + HU + LL, + [0x1b] = BDL + HU + HL, + [0x1d] = BDL + HR + LV, + [0x1e] = BDL + HU + LD + LR, + [0x1f] = BDL + HD + LR + LU, + [0x20] = BDL + HV + LR, + [0x21] = BDL + HU + HR + LD, + [0x22] = BDL + HD + HR + LU, + [0x23] = BDL + HV + HR, + [0x25] = BDL + HL + LV, + [0x26] = BDL + HU + LD + LL, + [0x27] = BDL + HD + LU + LL, + [0x28] = BDL + HV + LL, + [0x29] = BDL + HU + HL + LD, + [0x2a] = BDL + HD + HL + LU, + [0x2b] = BDL + HV + HL, + [0x2d] = BDL + HL + LD + LR, + [0x2e] = BDL + HR + LL + LD, + [0x2f] = BDL + HH + LD, + [0x30] = BDL + HD + LH, + [0x31] = BDL + HD + HL + LR, + [0x32] = BDL + HR + HD + LL, + [0x33] = BDL + HH + HD, + [0x35] = BDL + HL + LU + LR, + [0x36] = BDL + HR + LU + LL, + [0x37] = BDL + HH + LU, + [0x38] = BDL + HU + LH, + [0x39] = BDL + HU + HL + LR, + [0x3a] = BDL + HU + HR + LL, + [0x3b] = BDL + HH + HU, + [0x3d] = BDL + HL + LV + LR, + [0x3e] = BDL + HR + LV + LL, + [0x3f] = BDL + HH + LV, + [0x40] = BDL + HU + LH + LD, + [0x41] = BDL + HD + LH + LU, + [0x42] = BDL + HV + LH, + [0x43] = BDL + HU + HL + LD + LR, + [0x44] = BDL + HU + HR + LD + LL, + [0x45] = BDL + HD + HL + LU + LR, + [0x46] = BDL + HD + HR + LU + LL, + [0x47] = BDL + HH + HU + LD, + [0x48] = BDL + HH + HD + LU, + [0x49] = BDL + HV + HL + LR, + [0x4a] = BDL + HV + HR + LL, + [0x4b] = BDL + HV + HH, + [0x78] = BDL + HL, + [0x79] = BDL + HU, + [0x7a] = BDL + HR, + [0x7b] = BDL + HD, + [0x7c] = BDL + HR + LL, + [0x7d] = BDL + HD + LU, + [0x7e] = BDL + HL + LR, + [0x7f] = BDL + HU + LD, + + /* double [+light] lines */ + [0x50] = BDL + DH, + [0x51] = BDL + DV, + [0x52] = BDL + DR + LD, + [0x53] = BDL + DD + LR, + [0x54] = BDL + DR + DD, + [0x55] = BDL + DL + LD, + [0x56] = BDL + DD + LL, + [0x57] = BDL + DL + DD, + [0x58] = BDL + DR + LU, + [0x59] = BDL + DU + LR, + [0x5a] = BDL + DU + DR, + [0x5b] = BDL + DL + LU, + [0x5c] = BDL + DU + LL, + [0x5d] = BDL + DL + DU, + [0x5e] = BDL + DR + LV, + [0x5f] = BDL + DV + LR, + [0x60] = BDL + DV + DR, + [0x61] = BDL + DL + LV, + [0x62] = BDL + DV + LL, + [0x63] = BDL + DV + DL, + [0x64] = BDL + DH + LD, + [0x65] = BDL + DD + LH, + [0x66] = BDL + DD + DH, + [0x67] = BDL + DH + LU, + [0x68] = BDL + DU + LH, + [0x69] = BDL + DH + DU, + [0x6a] = BDL + DH + LV, + [0x6b] = BDL + DV + LH, + [0x6c] = BDL + DH + DV, + + /* (light) arcs */ + [0x6d] = BDA + LD + LR, + [0x6e] = BDA + LD + LL, + [0x6f] = BDA + LU + LL, + [0x70] = BDA + LU + LR, + + /* Lower (Down) X/8 block (data is 8 - X) */ + [0x81] = BBD + 7, [0x82] = BBD + 6, [0x83] = BBD + 5, [0x84] = BBD + 4, + [0x85] = BBD + 3, [0x86] = BBD + 2, [0x87] = BBD + 1, [0x88] = BBD + 0, + + /* Left X/8 block (data is X) */ + [0x89] = BBL + 7, [0x8a] = BBL + 6, [0x8b] = BBL + 5, [0x8c] = BBL + 4, + [0x8d] = BBL + 3, [0x8e] = BBL + 2, [0x8f] = BBL + 1, + + /* upper 1/2 (4/8), 1/8 block (X), right 1/2, 1/8 block (8-X) */ + [0x80] = BBU + 4, [0x94] = BBU + 1, + [0x90] = BBR + 4, [0x95] = BBR + 7, + + /* Quadrants */ + [0x96] = BBQ + BL, + [0x97] = BBQ + BR, + [0x98] = BBQ + TL, + [0x99] = BBQ + TL + BL + BR, + [0x9a] = BBQ + TL + BR, + [0x9b] = BBQ + TL + TR + BL, + [0x9c] = BBQ + TL + TR + BR, + [0x9d] = BBQ + TR, + [0x9e] = BBQ + BL + TR, + [0x9f] = BBQ + BL + TR + BR, + + /* Shades, data is an alpha value in 25% units (1/4, 1/2, 3/4) */ + [0x91] = BBS + 1, [0x92] = BBS + 2, [0x93] = BBS + 3, + + /* U+2504 - U+250B, U+254C - U+254F: unsupported (dashes) */ + /* U+2571 - U+2573: unsupported (diagonals) */ +}; diff --git a/st/bst b/st/bst deleted file mode 100755 index befb02c0f6abe7f8fda915866ab03c3364707c78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 111032 zcmeFadt6l2`aeDcvlR{8qcTaQNd^U83QdYhCm9`dW1x5|%gW0LNg)u-c1sgQhRZVPrZiKGDcy8~Dbdsg;THVU zhy{ZeN8qX|9$!xfCzl-=?ujh~}EC3Z{4*d2kY zc+J(vW9hZ1@eFS4aD~@geLO0goMDRPIiowfRW~c~HaxBBRqdqWqIi`KMZPk-vzxA# zr{-$qNjB2IG_^d9ZaTu~FP;6GOvZSN)ObeMrOInAr9ppFj(X<)U((A_P%r zY66<8r8f!js9gTrKeaeblhyRX)xkC!_3%!emEK0j| z$gRU~DlNKc7>hu5rQjd6sc{pgnIg(e)x>G>%_cL$-N4abgo?Frl8wbbDt3zh`46*F zmrmaEaOm#5KPNuYW*hdxUA;+$@K`cK;Q7Bcon{23VhNBnI_{Ev8Z*FWYLBh5dOkAmfY#7_yM|K~9LsxbU+6y6`{ zUl%4{Ys2U{ABNu-hWCWw=Y=Vs`Y`tNL%GHOL4A3{)T0n4zq`WNbA1?l-VbBX$T0e6 zgsI0T!`OKtOnZw8W9N5a7u&+u)RGOM*e7+a`-h&y63~#e>nA%PD>bj(!N%z_?@|j`M?GlFH6~>;Y!ssswW6#TB z>gDS&^*c69x(Q+0&nIEZ*%wCsEbQ-Ty4qwafdKxreZOgD=G5$&b8|~_AIvZHX}{QnK@Gt{X@a&Ik|;%^9vs|O&?Q|JKK{xrl_E( zWd7`8)AX_V1<;zYAh*zCnm(~Ox6qYeT3j%Du_igTsL(UUTT)t7Vwyg!P*a|nomq%f z3qARyY<5AxoZ0S&P17?Ap{vm8DVncE&&glJl9__^)kMbSdZy$SO>66u*hDqbD*-92BFn9XinM8ZR#DNz-r_0HL}ivUyQDOCTyD|) zTu%uKrbBB^Nl|fbi3il!0&nR&Ezje|n5MgO^JaSsJX72yxw(Y|3vxZVm*tf#WGaIB zg}GV@GutxVMTLrelv1{kU+95#W1)$a)YRNXp39mLBdf?gyC65aXl{O9er|~>suld? zq9U~nOw*kO1x4;D`H$wRD#sKRE`a?VwZU3L!DLhoiWALi`UE7U)*903C@5AQtO+rL zI!qi>RJ_=U_N7$l7_CAPwId0&!n0?4-1F|rFZIqY$Slk&LWISIZWe895!zTuk+*QJ zs$0!!KGi2H9uS@)cToW_wb#sS({!&VFO7`~*_N?fT4t=>^0 zg-Xku;GI8*n&$jDo!Y`!cg}1NHFB4?xFFw+)^F;doO9=nE-EUxOrZK)MPBNt9WrPL z093oI5HANnmU_>FNK+?V>M1E&3<7rL??KxpYToP;l!pu5K#@@mgB6z* zQSW7CeOW2OJB-p=sa|hpVLp0Po`*^?W042rLg_RVFHx?N*$a{6n1Xx^9C;qHAhU38 z?jkLWS`G`K0%nbkH32PHDQZG&P$?)XMH7P>l2%%`l7v!Y6g0gcZ>~~KnjF)rbPh#$ zT*2bvd8BVjaV}brqMO=8O@)_>SuJ-pGpc-1iRW^a zpoB@NQ<1B&Or^OV^g(o2lgC|(F>wyM%fqJPQZ^uBJa;deJu@%AaCQNDsR?0oO{iY? zyb{xV46;RT(}TI5;{3U$`MLAw7cHO(0LPzhbpn__JHL>L&o4$m>EhA_GfVRygf1dI zMR|%-K#^evCFWUNoI7*QOj2Cz^)T&TMLQ*#MU=swzLk8_ zhYr2$&+{l}%fy$U0&`0U4<)%gv_cHs=%|+|6d@IIM|`*98<&+idd$pWHw_(b(!7^_ zx9Geb!9#EB@D95vl~xEixyFAHbYiW#dS3)itQCpNGMD8v$;%RYpQclhzx-=a)6!%W zmvkvHxQ=j*(_Br5l5XetUDVj5MIX``bq>9(S4o*LrQVgFV<(jjvXp-_0`cPyeRK&ol7PseG}4e@W#R8TeOJzRbYCuJWr5 z{F^FYY2g2%@;PHVuTSq#`RNA!U6r3@;6GIPc?SMtl`l5%pQ-#J17D-^Wd?qm%C9!? z+f}~O!0%M~XAS&UD!<;qf35Nx4E$b|uQKr8tNbPde@Nx44g8NP-)P{EseF@x532k= z1Aj{8n+^OKm6r|tIhAiQ@aI*&&A^9L-sI}s9wJ+nb{l2jyQ;j^z+a*AaR#1O`FI20 zTji4sd>@rhG4R)@e42s3R^`(T`~Z~~41A)>7aREVJC*#F8TdE8Q25mb{@4zMuQc$V zsQd;4zh=84UloS0Ht^j(R^;~?_%UxOe2amftMb;2&h@zZ8$~|dz>nCb@Hqzly#|G! zZscnfex8A!pz>t~enzBX$7%!rNLPigH1La6{#gURLgm*Rcv0my82BetzRJKqr}CQ& z{1nyBY6Jg@D&J_}HT#9(nSih;kN z@;L_HWKrT58+eP#R~q;zm9H}J(JJ3$;H@g(V&G#{-g-5g{k5c&*13yOPa}4}=l`l5%6IH&_z~80v zRR(^B$~PJKIV#^`;2%_Z>ph+Ae^}*H419^o=NR~fDqn2im#BQDfiF|}Dg(bt<(mxr z8kKJ`@PAf$>%E=re@5j~4E&2KpJU+Ht9-G6e@*2p4gAxbl3ta8-=NAj8TfZpzQw>- zsl0VYXZt@;`4j`cN#%14d`JH^@Sm#kl?Gnxr&R{NT9t1y@LGRtG4NYec`H5O#h*5Q zYU5Ihfp668H}E@EzSzKjt@4!ye!t3B8Ti90-(=urm2WZdr&Qj0e`ot!RX)YQYvXE; zf!D^>Vgr9s)l+HUEn53A@K>mOlY#H0@+}7bDwVfB(AoZYl}|D7H>!M&f!D_QVgs*@ z^OXkvCRI}>ydl}|D7lT<#(z)x5CVgs+uJCz1rn|G=V z{7h9(lY!4u`4$5|U*)Z{I@|A2`4j{HCza1J@GDim*uaY_UuodAd9cdB$E)qF$-q~t zdRh$pQz~zr-PwL^d`mI#+B}kD;I(gySmm1x{0fzCG4P_wTk|{HzfR>- z4E!@HpJU)(R{3HB|96$IH1PjY`6>hdp~^QI_|H_n#lUYV4g8HNUuEELR{16af1Ap;82C{tZ!PF-|2UOTG4RqN23{LatVNye z*ZO;kf!F$Dj)B+uOR<61+Vg4yKUh7N-eBOLQqOOz47|2(X*BSfe3OBnueMLwz-#g? z23}jwL=|@~2Tk5;;EPoK@dkdo$|o84Mcoy>DF%LzDxYTH1yw%Bz`wha4YQ_V1Ajo} zD-HZlDqm&bPpf>Bfq!L>qQ4~!Z+)b5`AksdQw;o+w-r4(27abm@5KgQYY&xS_$mW` z@J&U3lYxIk%~y+oAFAbdqGCTDx|(_`D%kHn9X~|JH|zK`9WU$n5jws_$BQ~%yHAJf zcv$>)?N8A0 zWRv!nqvN$RCJLLbG&Qx`PDj} z*YTA)UVHXV!O!aWo;vyUI$nRze1nds_wls9DjjdrNVuDH{FOSsTF3X%@r^n@PRBRt z_^WjMJ{^x+n;n16I{umt)?|`(y#7VM79CITjA?&uI{sRXBzMx~cH3XaN9p)@9dFg~ z*Xj5;9X~+F$Lsj(b$pVJPtfrxIzCayr|I|`bbPvwzfs2vIzCCq=jixk9Y0;i57hCq zbo?M4KTpTgJ3`uDv5vn-eEMzEZ~z)A7&h_*-=RdL6I6 zvdls^==fBfe3g#BRmX4A@we&tY8`La@r^qEb{*fO<45ZFeLCKuG<_J{w^KALC4>%g?*73`Ae2b1R)A4OO zeua)VsrOH4jl5FFN9lN#lM@#}Q_JRM)D-g0={wW>* zKkxra;D06XzY_Rg3H+}F{#OG3D}n!&!2hiT&hXc^3%*~X1c|E&nM^`OwI?FfB>1*Q z)iH&k)FYrni9g|&@1G7IVYVX+G>7n)xSud>9s^Ab?KdMR*p&&l9Gj>Oc;|PZI7j*b7yqoXlxlmID>E{!|xFu zOSp{Tw+N3TT+HyRgayL07=E5`CgB{0pCmkE;q?sPPMD4= z1Cfii{%5~iccKrzDu2-A^dU>3ty6Q-lcKn}yb2-6W{Af4gvgz4xpkiu{T zVLEaQ#53G}0r-BxR)$X#et@uv;p2p75^njO)j#1`gqs=OPk1)rCWdzto&Lv#Q@Oy;w2$wPZ7U2g87c=}SVVZyfvlxD!a6aK2hMy$-5aD!& zA1C}U;S`2f5-uPd&+wy!=M%OvTtc{zu!-S^2p17TAU7(WTVT9?3E>OnsK*Dr17bs?U0AV_k3(R8pYQl6B z7sz3_7hyVr3#2pLoiH7}1yUG}AiRWdJj3nhftM1tGJKlwGQuW?j}u-_xaAzHf5K&i zn;G6ucm?4mhIbQQNw}Kf9fW;^s~D~&TuyjB!E@SvD!m9`uGyE#y z)r4m;{5)Zia1O&y5`LU;I>V0>mI$XXypr%5!to40O4v`>%5VwcwS-N;v_2YoB2(P; zfFK?bd_T41OwAZtJ#@R^cWg$aknQbOlj&^UO?-dK5*AQ?$D46xINYn`*xNidEUgM5 zL`$n~p=Q2+8OhbDF60+8`P33}k}nBji%@fNlu*+aDVXbpU6(wuP>`xBhzd35@%=G+ zYVpawGKT|kP2O9k2|hlPUtDd7txs=L61} zb(!KN!S6|k?in@RN`b< zlo*#Ko)p9exe>9m#a{y12x0nzP0mj#oxlh%nFzTBu3Uh^k!)ArZZ=8WJB!RFd)qR1 zq~ovfB{!^S*o6WV+zmp0_1D>RtY}j=ioe(V-aol9zq$!Q$qj-#gr>UJj;1HM+TsMC zY!%!)gqp(^!Q9xI&Y8WvDt0gH;Y*A6cB4SwakKZd*i@&)l(^SfOiLSu1-Dg%7TqIp zA~=6e$lk_JXs>8plrC`#Ri=fXaN2%pNrvFwEBGx>Q|{5={aM4zNHIc?Y$q3*O%0X^ z$|h>sBG?PoQl@PRSN#3PU8BL-r^7ok(Z-5>d;ksF_Ub)$q{W9=5QPw zUurGXTjZ@I$Zw2!1*o+q!jmr9UWHfU*5lWA_|oc_Rq!A$w@EQIX=anl?+n?0ck&b3 zL`N<{>@EDndi!a9LX&8_2N;I@1!6wgyqQ`0DYGZITWo3NMj||d8O&#-5-``xD=2-5 zyA`NLj_@^`D?;8B;aR~SyN%)t$+f7G@>rPfw^W|ti@4?mW)t5NU9%|WF!Ku0b-s|< zW3HoEG50W0HM01vkX$eLhrbKyEBT(b*AM~T2cr3&{m3sc0m<`jEYgvdJYkhjqoD;q zR6CGWT#^tih`)(nJH?%Xn|q0(_-z|+Gn>jjjW}=z?DGy1+kCRq*XCGwomh>)tA2vf z(#Tq&BIMzz(?oB!Ku(^61y)ZJsx-mFVvBf5YzQ7?titU=i*J#*3h1-fFKrUfDfDKE8;JN7p=DF0m;wrH zEKT(g~>Vs9*IksL$F*1JRY zT7Dc_;MX3QwK#yu<*0w6S&&jsg9t_g@A1``eZQMMU!rAt8kx4gkhY%$-*F2#@9E%s zokq_j0kx0OvsK(9aZ)Opbeq`|=WC1L%Uu|dQKm_{5mM?R*=|Q#_NJxYNNzBE$xWOx z;4}&OyOjZ_{YJ5d2Am!FyV-!#L@n0#Aao1T)Y>>Uor2;$d%2T*YWL&Z*vbP{(>DOEJTmge5bm~KGH zH7jbAGOE!J$~$V>`zJT4gEGs7pBoL0Fr_h0aJLFIKU-Q|DV&)vUqI=J$KZ< zi$P6{KdY+=XKMy$;VU-62s?_7uh@${GpeclH851Rajp@g`0~F11wN&q-8}n(io`vL zB+&>32@|&11^n6<5$;5*atH{b2HwQ4`2sZ;@F75KNp7U-j=2Saf)rgN_yU-!xOjqS zJ*_S?mCkgCnvXlhE6pcKWhS5$^VB1#hFwcO6?a`=-Q)ppt_F+m+4K<dXlXnR2-7w;xEFgN9VubAlf;C5AL{Bo}YYcp5 z*(o9`OA(@E#s3*tDJF_+saf`cSQ}|;D~2sQR@5pDVu#+CZ)+~~PySUkh9&Bc8LC*b zOR;7(Yo-k-ACpPC(qBSFyN*t<0#lLD6sceRP!#Zam{Ua(++IM*wjICrCUF-(p;3%^ z2Y}D26}h)5jQf~LkiSD>ttR1zXET&zwUD|Pe!;)&g8Vu5$D7%h>`yJw$hScH&PRAg zNRAwE@=PqE?VJ2T2;M6N$PGcY|~4_C4!;@95f zDP4NM6cYo#7;`QKd`vgyKLdY%nQV+q6#$9dB@>^(EWRfhaFau(syQUjZ>%n%eaO>fudna?mMnk2WhE zRW3Un3TJ-8mh7A5h|I)WoRf>-#R5lHz~vCC+rXkiKQ zkIjQ&GzX=Aj=dKY`#NH7|5Y_R;364HyiiGeW{1q{9Wr!i+p*q{a!H9NPhgPuT?l!v zpi^sQf~ph5i<#nY0%nkF1@Vj^?ia+9G-@t|9!w*PVg&OxA-T?bG`r%&;s$KOoUlsn zk5I|+5){hc1VW((ZY{i2puyMJ`bg@}Zyc0fe!??4Q@kKg`USf9Eqeqhs?{~gobK%- zU_Fy5_DxWvyhk=UP@q98#pomW#~{ptut>HrQNqqD&WTR*Fb?Bm=Hh(;wv5`MbTG$f z*uu(pv``~)JT?R*Uw#PM}#_u6a=(6Y+5{hG0#}U zUwwy1`L>s>j_zG^61!DwW`4nj(Sjw3j6%SsHsajw0aJ;HIRK)2ta&x|wn(v#ZEAyO zv}JOq{bkh9YF2jKGlS91rM^w_L+A{WtsD>?VJ_wpTHpBRwFu^ef<5S&DcBE|T%YBi zn_!VfWQjk^sgzZ*J_{|B4y92?R)~}y(JDVcC5kPqvPbOLyn6d}taAnTaiQj@Mev=l z2A}hdi1)q{td%FCzRxY_}~3>Al(Km;g7zbE9ON3=b_f^-&{aI(7OuagX=~7Tn&bitT&_bxD_$dIO|glH(fY zB$}8r#0*E`jwQno7dAVNi{wi^k6)RCG2)P&@Ve}&0XSaXWp^APPZxVZLbNAS;%b4M z_H9l+dz)AHY5VuO4;EUoySe~QZ8TrF}-5sEezr(5^S6_}}l$9;}j2s3+k z1>dPd+kijBm%jzG*(AnI1z~SnvQ&y0kB5VjEsJpbl4V#9|FwqSxZQ3W2A+SZmfyJB zzQg+vWFMAn*MhS*EuSs^8W}SHF~pd@_>D}xmi(!4_>~;34?<=K|6;ZMcmBniZjJU! zaH1N$7W?U?U1b@aBzT>Cp%u>6l5I~Cg!eAD(jkv2__)1s`SbEN*26GEc0N0bb7C_` zi_xhG(N6J8#Og|$VW;@5G&Ld0Wk0^WyGz8XZVyU5inf`kr`2?>DRD0#71}*E@)LJa z^bBkQ7f4*WLc8oOE^G%fDo*gL&ocQ3wE!6b$8kUuWm+^r;&K!kx}!8zNVgy%ieIxI z@iN`pFpfkBm_!6OYMu=$Fvc4E6Lw6|kT3-+oXURlNib22*&=Zp@=$tXq9pEb%oCm0 zFa{g+hRApb;8ZFTdu6Bn(XzI>S%=p<=h^9t_X}V5-D@kV{2x7TmS^{S;e1x8%3b zT3&2QZV|aPki$6atHxo0rzT2pW7>8cy8)y>7SrDC*!qGMhe=!>(->OO{%=HH*vq_c z^H=a-Gcpk?xuO5B}5$@@@J z;{;ZJky-99GHVV;iUAmq#|#+WHB0I;-XDv3e`Fs)yaSV7HUhFl%az!|!{#W-W+65E z?K_rk_c`k5n+qO}jVR~77k@xCJxaB6lAVD@i5I1QH4w4)`wG9F{wTMK6U;!~<#-L0 z_=9}0V=Ap<%TYGbNRHi@GQA|TqS zCLBq`NVa>R3ANF#uZ`4bN|SQ99_t(_<|00qA#sBsNyYx}QO1D;N-w&LAla3|AAY4E4#j$;i2}rt zJ=xkpj>@vvdYgQXZ2D4%rz=|y++W8^-GWU;@T6fF#}I;Jh%A4?<5}2{`=&*r30@RL z^ajT|EaMt8T}y*p%sZRhY8Oa;SMl`!^_4zG47) z^lW4vgN1wukBPAqk+j+3^0gUD36i|RZ)THQry0h#qf>uqo zj%2YrRAN5VS@aEprZP3~SrY96(JiKUm8u}>40LUwS)3(~FW=))7o#w0GHNDKUxM;E z`W%77msb1FpxnEpvyQ#k zgaqeF+-(EUss#)t!^o2p($$l9Xg`Lg z%&(mQzkg*Mx|^?U=)yH(HPk(pWo`*ID-EWbFXmo+6e%M3FJIy$crFItU;$nPIDxIf zzxWc<;5ilaD>2Z-l7OQEk76$HCH95?T%eHV1%GN^B|pN$*|2b()$>UE| z^F6uu@j)p#o8aKNTQl;@ZD9Tfn14O|g4-5|hf{MzIhsd#eFj>t{GJBv(U2R#HA0|t z0LHgZ;1TNu=?VD`%$dp=bR^f0gq)U=qYc~~M5RB8tAr>!!?+t%12+l3P6RY?6XA=* zUjvt^3V(vug*JvirAf~}7E&K${0N`KWk40H@pSTZs_}6+H<4o@CT?sjSa#q4DK6z?FF`xWmLK))l$*(0; zGWVT?_00VsfliC~87$qnJUtfb6&wdbwqZ9sLZ8gUt@N7$7Lk$H+#kr&*HIQ=Q zkT(`9>d)ZMuwUfYO@*3Faccl)YpB5|K~omknfDz;+3v=;gT)`e_6I(tWjYEJL)Ki{;Bpph_^;W!O*QJeMVW55%bHU>zsfe!@5PSd#f#lK%$tUnkj( zC5gjar+;jS4KX-^#*sMMeGOYP;^+hW&)&YJrg**rt0?sR6KubE!ez(Po248KTh<3B zyJk;U^iXSrx4X06lF&UH8&)%|09nbQs8JrL_&3Xs~+oL&E)&m2EV$@by2y%GKfOY zW<(`D3`^Ymnx@sAG&PYFJs_a`D4X{{19`C}?-kfbK4pW>ulxd_bqr_mo=`UO1vt3K zG;a<5Kt-nCEg4_OpPive^541^x|Kgl8I&nyR5b?*MhVLEb9r{>!4=-QEk z>yQIPiR&~D(P;}FGFsTWRdT%C2L_LY4xH-UYUe5ey^o;CEn>az=jGlge&aW-TSH!_ zuX#B}hLPrF<1xbYSfCZsmUfJ$4=N_4GZSj1)H|UBQjNHkqZH+{kivsgzhmJdjj8~p zjNO56VI}Q{E}zR#<;2y=UqP>Kb-3b}I4g}dmvs3lPly7CEJ;J&Jc}AYhx%?W^f^Mf za=LIHnnb2}loq1m$t;9@iP1FEeDE{+e**}~2eWa&nU^iykX`YU=K(rBdWqtq8PPQb z`@Rx;wtuPFS)XCT$h;p1O?0;QI8Z5|6B5NNn)Ux*0?@;1*^z6v~ zb0A0y%sVFU*h6LQO%x=>_RG1B7glVg9O@1Wpk@j5%G*%q!7t@4DB+I7TU^$lL)uJn zr>;D3Hv@%%E#8e-GW%kz)6KBmpE@2&1aYtIhXN#C*ub6Ht9)QXz_z-4H(J*gv_~pG zrN*`*L9s#P%5ChtWwoCq@XsVa`gJHoMde1R4;_oZQh9H zzQxJ(A5!$sCH-6pjKIJrE<}fsuiM8OayvB#v4+l^{i#o(p`lm(1y!rWJAft;5J>zq zstWD?3notWi?VvOqg~#BlN|OeolZ5QoGdas#9(&}Teiv-+>dP(s(WMSR%J}2ui6NT9}o$5dRK%WgW=s=F27x!cddEr2|r_$^YtrXrU%~ zeFwx?VUnNdfISGV1Y}+Ql)MTht*pTuR7H!BCR*MkR&Z49W3U%f%Kl%!A$%WV2G&vc zSc)49o~z^=J0w#(Br70^sKYu44-wMCNo7kh#__Hb0SOwiq#-M(no;&AJcDpjvbW9IJf10T6U3(A&P?${ut8o7^Z#g8 zkT@QlIZw1CNL)AOb0tLN(Zhi}tc|;1S-K^b8k(cHr}hlv(pRh-;fh|T;rk|K_|8GL zQBKL|N;CrdQy;^=552h&^`{h94|EWmvmc}K!Q`y8bVoGw%U`3UX-33?Wf6KHPIi|~ za!E18@Z-M8d^rB}OWFRZ)lp9WBbVp_zu>Enb>hOtva2&CET^5g5%DYkk+q`Tdo9j@ zut00{wq)WkDBIrT-OJKfPUh=2QCq3Q;r98@s2F{YF3@gX-fsquDNczexFh}GN78LLleAKltqg;+S#ljUz=B=B+#*Ao_lN~PEk z$Ccb{mzVCOqq^_;by&cg&>~~abtC$XK%Aww3hp2t@8HymyG7BFB(GF-4$^Q5Fycce zZme)%m+unX_rVVT;Dc0NG{Z=avprE}?^9P5IT?P1{h8R86m~zc{^2jNIFe&V4uV!H zK@;J}q!O3q4&rF-ABtAC?ZXDyv6v*ME0Xi!&&2a6JeGVC`m+5WB$VAjN7xNCHPZD$ zY)zxYgRbO@^dy!f#lZjt|Noh^6?M9IZHeuUx^V6A-s|mMn%$~_IHdeIPOLlI_nvYXQi3a$OJ(eo*=(+ z76BMJu(MhA0~-wlsWRpq~0-idj1Xb(ZCB zCYxAW5X6H*;pvWntR2Oo+7UHXm%CsNw){#=WPqS>I;BFH9 zefLmNvsUFy-kNFthUJ{8Nq`z!82+tAul!1t^=m8?<7mhGg~Xjgr$G_N;R(Z#LiMB{M_%J#BIzm|}I5G7CbB6p*&g#TgIG5L>?8$e>GCE2;FaCz(jg>f%#C3>; za5lH0ud!7H2E_Il)Y^xQv{fGRSEe=b1vq%Rb4aEOFmi3jvjDpN%tlMa_80G^){Zf| z(;$gE7egK(JyNQhIgVLye4D19|}@w%_V~Dv>MhFpjyOA~*GgN?%4SCN|FKEZlu@OHNP3_OI!!J1#N zLfoewCVUBbjNG*@@vwNBm86``N&^F|{h;Ro48)_!0>9;1Uz*8tO#DGvluEW6LEHB) z`%v5$$@PK%zdp8^%#vJE8y9@s=V%^}V)Tqfm4)DwxL@%ra_8_Xzl(yF95TEv`#Enn zJl`c@&lOVYJ_^3(KBdz1n=r|7@d_%2n2i(*&wSxXsW0JIaDRKb`0yOeDo8?9wtH8m zyPD2P#z$KAhKi#?DRj^?1B)j#2WudYdMVAKkJ4)L5FKCqM=Qa8N_PVvXNSH-0qF%% za6R#quz`D-Ju8Gp$!3MO-eNUEJ|->qppXVFWQ!8wr;ryZ-<^)cigduO*hLXJQ1M<(_xe{A6);YsOvFvi6W?6wdP^YmRp5c6{{| z%qutmrsh}iAnFT~3l>%NktkGHPRDfp+1nWcR+&}!3HVWQ5QzwWk7M41sNgqvfcW>H z(AxS?Yg1aZH(@}s)7Sp#RyWr1dc|L)TI!hGW-X|X`FA^woqG@9RI|q zBCogx$=pE6h(Ab<7l^XwP^ox4j)UCUdu+B z^#8~~n%&ei?OK{wurzO_1Y#)7_eh-5yq>6Scn9)wnsb#jKl@QjGm_FQqcjiW)Gn|< zOY0BMd?(tMg)FY>t!ZH48Nt}RH8E%k`^+|@|)4lT{MiJH_Y%{V2^%#Jjv{|2Hd z&H0pO1bkUi;&+kQ*IY!4U5unVb^4DGB0s+k^Sro?9S1B#@t`C^xzrzz$u~2r_=;Je zsdG-f3KqQsEpiO$f0bn=b^8%KwP6?1)E6$L=jp!P`4~@{@BB5MY3#TTm3kl4DwX;w zswwyyT305h%lGZ5liWx_B7$z}6okqD2^5(eg<$qh7bK!PNzlsiI5Hi`q#<33Ie8eW zF_d&P+z6D6+(;6ygJnA%jF#gfFj~&!*R23I@_v3DZfmlkPsQaWC@pAPPugO2+Mb&W zZI415zr_{_9Vq%2Kp~r6h$`9q1i`rCc;0NLab_OuVyR*y@zop}Cx+B9C-przfxltR zHu7QbpP}JvM39e9B5hcNT|SiLF*oUt-sRh={`Jq41o3>Q$tn$#RjB`hG+$`B=NA9C zGG+Mowe?>(NO=RRm0LWTaqGd!c|E*?#w4Wk?3)R7%O zxOkTwNJiR%`!_nWjF#`lp-3PBjDJd*uf0FNY7G3o=5}ABRpcC_@%*jgC-1#@rVTHi zA31$LM#gnTE)TWDVL2bz6}2O$>pIlhHs;wzql+6L|i0{9D5 z04llP`8~|UDS>S*!Z1jE1gF7LNpiSMAob%9pukZ3<36W0Q_q&mgtK}>;5qr_qlo?- z7I5cGWZY=nE9QP--k778(BAv@^aay>=kHh$EjTWbC69GymW)I)^w+?3F)KbignAz_ z${g?DcN7+R{+PePSMQTuf_K$(E0CgD@Xt2~x8oX5 zn&4}{Bs5?Ufyw-s2I;dmzSUm<`6~q9)(Y%@)%X0k)%&-M7B9%xR zeJ@3@f6My@p+)l>GtH}G20+Ls$B&%Gulf=OV3h;$c%@A$t4gQ~{tI{75SLD^k7IJA zEs_PL79b#T&#@pU@4_z055JVU_YUka>T%B%u6zUX9e5qxL*j~bl5Y4Txqmvz$(!idWdeme=27HUtos9Z0LypKZH(chCNUQw zhXP6DkGTi__&{IsJE$q35%K_NQlEYY2Tkbgr=6SB?|5z$2x3;1xDOG5vVSGA?Q;x- zDog}T2%?>}?HMI>-Bna5R6n?9$ta~z7Qr9MZE+y04N~f6xN=X*Y9*7TZ*^2uE0Po7 zmmE()A{mmEBzX)|Lm-byrsyOSn9f4dS^qgT3wa9(I+$Qro#1)Yy_A|kDpkR!NidcP z{(wNNp9bRXVZLG~w4mS3LFCH^8M`8;+$kdIP?cYPqcAD*XVkauEL9dao#nEJ-2dPL+ zf{7AG-_)Sm{8e)FCYwt!S4dnmg~ibKGJFBN0m=Kn#|u89H(zb8ha>zo0u4V(?Anja9QjN61xRc zci;&oHbW;i5q`<>93!gE1ttUXTO55TPRum?VuI7(rSNUj4aUDQ_G~+FloMgG|HLA$@}022uR!`Iz_qgpW7!k$cwOK39MpKC+Wg7 z;K#eZl4CVek;h^M6qo}cyH5-exlL&eR)j#@( zudVCCD@5)?qG)&EAH7ed-XN-yQEw2{!*_hLXj?<DUYV zi*K|Y%2l6WNTcOfU^FCr4hu}|!dKj6p@t_QreDB90EZ%Y#~;Y$Pp5Uk*LS1&B%%3W z_Jh^P)zJ5$-`%pA08NRl7$5_3BrSvfaSj|`@kTpk@^t{MQ#=R1WP65~-Mq_oEs_>( zs{z%4{3g8Eq8s8Ce-103hDv2%Sw-+Mf~*}cBDjze{QVKW!cK`f(x6NJhrl{i`3!^w zt^;3oew?RA+4*#D5!wk}KmR<4hU9@KJ$OIA1y|5fy>m8F%#FwsCWJwl+;S0)h1_`X z0$$$r#|X1weDn&OYULr7)oZNuJ%M0fkuFMnfTc1@c|O+S@+M4xL9Xn4nul-Tcxa)& zz`wC(a*5dVBsQgD)ORF8hiQ3o@PPQ8cpRr`pJxEe!;vQ(*o^2GiQpwaVJALf$c0vu zY3YY95i8PiUQ+2%RRkak@$7K60O$uXb!8rq(dsakAV6{i@QZKEAQjYIBY=E8 zrLqkoms62bNa4HGoTYvvnv~X7O07?~reB1yIpBwk{2s<*x%C)+eoF}t%vAs}*aQ!7^s((J*m{B`p16&nzxm3s#u!V@RiR>LE4|2~UXZ2i##?KS)wjM2)S z&-n-%!M%?leq94dDP|@K+os_crrrSuJ@_LmYQ=tw0dN$FuO@7#=#fKGK)6N>@lxzH zIQB4=#Uy~jyYH@qw2qSsz9Juu2d5Q+?&w~Qqb+A5+CtunY>UaPlxXa~S=%@;jAeBf zIZ9TqL=u!$_Et@DlPg)K=ZAQhpXt6R)EvW&f>U_lE1!E0N@;7ZsN41>Q|DLIt-P$R z;f$&-Ml&En;^rV+vTZ;R#yR^g-hVXP;=Y#vC)c#OIXKbaLVGDhOq=DOXTDj8Y5?k=dW-L;04N5#SlwMcc>f$Fh zOZ|Q(U_O9qZiWwan@AQ)mL|#8hs2YQC{GG4g8K}+Qem#obnj%(oBboB*e?>X@Cf==gP|8dQdE;#%EZJFXlr+5}G!hJ%zovhIW=eoor$@{6e=`yC^KA&aA z(+mv%;x-)Y&Sb~sy0*O@G1;9u{i}o<%Dj{OIp=T*WdY}Fi}B9Jvr&sY6VD^fL8tw| zvdyg=k9?%#fX`irZ%(;56P_PfT;fH%VdvS6?qk8R+=5NMw&kAp{W)~ZwD410^dzCr zA3%z7Z|M3QMTcD#e8HD}E$Np7Tk+OQa1F~;APbFH?vDzZg1Q}dg4U+-DDmXen61*j zgwKqf^KIbe=ikA|kGpnwJ*W;j9jJ_X!@=Fe(VM1QZzux_7&*lTG7@3w_^8l4!OpyUlcJuM&Wo5a=4d~Cfol7 zvw!ON>eY&0@UWli2%qiIonw|K;L#T@mg4jhuHb($p$x@--cwzv-~)ACHAcNwXP!Z*a>f5phZ)`NL<5UOf`4|6pt$&!LkI; z6S}o>0gzU2pH{9Jo+wW|_8=c-vqy+tZ?GRLAKXZw%f;On20NPnBisq-yQitu2wlL?lcWv7$0 z#MucqUkaY|B@Ti2l5$Vc7YccLtoZrV%lItJCFMnP!~GKRH)&#yOInC0xtF+wy|EfV zVNJaYb)(IH$`hI34REn!3SJ8ry(4%f+$gv`;9?mP>;e}SI?sLMylyA<g(?)^-{kbsug3()uTC1OS~#)Ey#pRA!N zQ7r30jx{#4p(t(C#HR!4Y6P0muf9#e^=v%E8T%NDCpqZ*0rG8cAqa`qk)k=uJ09pxm;fNL8*jZBxp z)or(7u__M7oA!I5Mu~GgUTph6D)J;mDBof1NWGnX^2;BaZr;tRSZqsf(o$coq@G2e zH93qj8b#%fd*jS;B*T#tPSW9etRZAL`ycZ$(2?RGv5z7wy=k=Oqad!7cK5 zDor#=B=D$GshTPnEIh7Xsb;)k>PqL&e;4{eoFheAL9uYdb$B3%5xxc)#+YzXkG z=J#iu{J?#5^*aaVo9N}uGyFiV0x6#1r`OTPy9T3AgWnE*JKWuHzh(DFv^Bo+!MJ$n zh$S6Pwh}`>PlFQdD(K^_J#o_!zqn3`U%VTM-}@4V2cEl}xD^ly->O{UJ$#BqoKw+ml7MiHBTHJ4Ht>R z*8}S3vV;+emlRP{xK%whp_kyp%#Qn3_*L#I;>5c=9VagAlXQaE#(y*sH`lWgdgZP7 z-DL56m50q%`3z)BEezW?I2c5#+^6?q8;j2=k_p_~0B87nnw0$VA1z7fCARyHaL&)6 z8f&zEwWx$VUpqb~)sE()tmXy(jK=iV1}ya0=j{9=M>&1RBEaC}>P`%KlN#|hj=*Pp z7pSK=pHgxA9JkQHXy;~nZs;-V;*SR5@#xSU6=wHN8?_bP3`a6|K1h&cTjc(S|}J~DjH zUy%%2aGLxqGKc1irmVaV3KC7ZE!%y9UD=KtG80iUrD+)Jt>QdtRroR@8uXq#F^3xV zRBG5c)UXR!!$v5+Q-PMMH0;0twqz+yVI{-{H(eP8VWA+7NeYZdOTtxZkqZn!PSyRg z0V?F|@< zsbijQik4H+?sQ|=ieE5`AiH?k6)&0~Cvh2Kg9|REAW4~m1o5ea)estro5&~QGsUx5 z4GT5F_;dBN266IFZ_D5}o}jm5CEEztE$(&VHDv$t2pWqq_{9tWnIZn$>8q!)Fo1u@ z#nb4R)E7O2(_8B;o*3MFLD6HnyBnR=*}Bz?kAXTqAj%Vq>_y`lswwyouE#{9vQ6IU zXxGtif-`%j(X@IKD!;boc+`8Cx~3*1iM5JnU`;co1y{mgS~t(4^=eY4D5J|AyACBU zrRKmgG@xBT@=g?Vu#c9OgipDsjrMB@ovWj?_zGOOL^J&n&9vXApmEm@7wss8Yw@~p zbk~L5>HaNWehOl(+)${E=gSWuDw13Sd5QaWJt;st#}JNZ`{e;h@*ibHooga3N$_@8 zyaVBlR=k7Z?Lon~c2kg$uFFQek`0kLj_z<|j#?SMC&W^lt52K@E?s) zQyT`an%Z!9S!%b^QGO1kmXe7rj%0f;o)y|PN@_0x$mQjUaT}G0PJ6?WRHpAk5cuAYrjO5#d6^iUF3Bc> zp_T2SbLfcl?Fzp92ec}TlaZ-j7T>MLujl=TC`5|6A54ZQBMlcu4V=WZD=(0c{c_n$ zC2tYagKQ3(V{FbI)^B#>Z$NC2aNq0{Lk zElGFmg(Qj^44`crb=-Ai#2IDWw^7_R?6~8GI&O%Lb|yOFhPct+?^K;iI+tAa{XgIH zf4}c}xXJBP_ncFwPMzB8)~ym+P71xVIg%vZ8Jj;rsX=dnGC)I226*UCA_ELX$WY)f zc#2#P?K{55&>9x*-&UK%Wrl2iz>)zDyKQY0oP0i5K@;fb3EdZHk)OVVEY$INPr#&<0gy`Qn^G$E10}NVFJP`98J4YhHnr17 z9PZ~AFIu#yXmf|1I?bC4Okeln{Uy?FHhDi0-XrVRBe~^X?itvE2h$t@d@}Mz=w;Ny z@!YjU1%XjFK4zxR1@l6?La&MDh1xh5ln!mq3w7jCQAO{aMiE4yqb;aiNbAZTw4eF5 z49XWd(pj16~M9tIm-a<&Vb>i12=0x`mfPKV0eGf3K5 zH$e*W8M!~ap7raRF+!*3XEa7V%!AeTMWG)eCJH{ZT@*I)l{5t^HVAjK$hVh;~yYK9#80uwWEYYsw1n$Qa8{GC{>!hgP9&@k(U{v`%2G7Nma zHCuMDA~(w}^v6aFJDFh!6vX?zqMN0^-8VuJ^v06*JPLPm}Cd9=!dUE z`SFhnNysgj1n(MienQf_R*zzDhOZ(cuEVz?ly;r3)1wuSG`E$|g?@@GfDS!u5^fr- zEO7^3kVQU5LSjzAO4T+#?n<(?eT6K)GoL11Vf^HCHz9e3!u_m3wB-cJ*J+5V=ymrh zxSm)p)eBzpm=HXW1rH{{!(s%VO1{3KLU#ofx-^)-D?`x~WOcFa!fidW6s90sW~AHJ zv_fR$Mv*}<`S6}?O$Z3Ed)}1*gIQ=Nic7KS2?hx{sX&DzZr0)~eOz;ZuWY?tg}3=J4d!HObHM$lr?^P|Wdg!W_=tijKHMVY2x8z|Vn{UU>XRK#LT|89>* zk;h%#9$$mmq`qGA=w^@CNskW~bcOKFHQj>=ivNvW9(N$eM`-6Vl&!U54j$wuu7+G{ zs=Mkc+gcxlA}jLCldU3OiCQ8ujjD&x)(#I+lGQ;GDxKE^OSGiueObU z*LK;D$RM`0-{O}T!eHq53=cA>#;%~=$2+NxNnO@CyNj@u2n)Ihg%Sb`)Kpkj#=RXoT`0zS?1@w%JCApZR3R&*OqpW-| zmFGdVD}2C~Z_b1X4xwH_-5MBy>S4jdD8&;XUsVsynE8#2L#@VMQX3!(KJSK4QN7Dd z@E>Y3LY5^I5ONW+QO>DwYk?uuI#Pou2ROF1ErPl&d+Rw6Q!sikhPcUdAq?NEZ%-xB z$xHF{WKulEF>4vzsE<2A;oNqm2xuR%;xjSm9i(-EH$MbFC&lRfbrJLqzyd;@j+Ig6 zs)H-?7m6}k$;cWc07S?SySp07kHK@eQ3oN+NZy|@ba{<-1TO87UPVYRtglCH2$k^S z!jkp?G!Nk&3ijFR#vU!ONJy$g#FP>JjH91yk+x(!q%pE8=`XCh74O2z@f=Rhg`iN; zz;M=Nax|obQzwz5bR70ZBu9SQ&in!xPTQHEO8}=igvy5FGpSJi;MSaC=ugM!?2K!I zBjmS-{DVWMjSihQ{D36~w75V)(dvFCGxey);#utGIZ`;VU(|3oTK8o<9!GM2N(rRW zqCmLpfqr~(fdHkCSg2NFxp;PcG%lCSxbbo-WmXF;kz7w2%XD_ z1hrGGvFr-CQ8yKJ)lEU{qr9zj$=1j|D)eIN+wuVUo;o`%a5PMOw|vMBoD9Y!ye-JE z2Qm1Zol6@uI)Gw9!~WBP*a*UtA_lTC1#W%p#|0i@{jk=vfdOI*ZAi@bv1V}|W0 znb?TCM`ELJs+w-+43R;9IvR0Ugt{AV?efC$qgxLi8|dG9aCk6hE{3;hofZPwv_r+X zXDARl$7gWI9Hav>?+5oI^9~1A6}m%ItcDxro=>Wtg#yq)L;j-G$!4%?S}qm6f5xz%EZObE zeiGP=X*biXkrLaNuxn8kJ1;0|YclP?wzLI7_f!8RiyVhf`=u8y=7r_z(|sGdMu&=Z+|7B1b%%Knl0zyq<#; zylfwg+z)k057M(l@)BtjNYbh=qI(8S9s!6|9!jvSp>hz;`4AuJ|FDxLp76owA&cynyHVxD?TBMw-q(D9*XDh ziHHSDgW&Ua=`$A{wKZ;}u#{q3^9!hu^f=xcYtPo>2cH<=9Wxc^(t@@*rUJ|*;x*jg z5l^v-QC9h$%b?*7Bo1E2V0QI$VU9>TDrtT35E)t&kF8VF!&a<}n-WewhLqWy(t^70 z)dQPT@M(9>NmpR~eqeJrc@!M2o9&_Wv%wrnU5MX~9Hh$B)M7Y;TRPX{O~UlnAI*8T z`Mbp8xlr;~ZKyoc(}jmoW~tUKz*DDV^{*dN zq^~T%)9Rn3I~#7OfOx?IBVmD&u)qV;dRU+?gx6@4vpQ%teW!&^lB2Mmhz*eX`2~EO z1I~48-J66L*LkfU;}g_f*qAJ(z|?Nq+;J{ttXq)9%8J6rQu^->!;V;@Cki5--};Dl zffG5WR-bdq#TtQ>V-Ym!RxuThQPFG+qN(``&qh|mhLA0O|_f@ylu^&i5FF=ZOtX*3j5PA z@jjSFYI3h_p>*Va6kQrN{w#Q6r)6zx`yg7#IhdBk=-4ulzV#Q~sBjt7l;X}omV>}j z)lAtT<_T&&BmWldP<}3XDX09`(Tv1WF&H*g#6Y7MED@Y#Fh=_0{SgM0#QrFh!MVWP zPZ-)jY#MyifNmKwG+ybzV%=JlE>hv6hSDPE92WE;BpmEkoGU`T`%cx#-4D(V3rWTq zG{8PN8va#7k`L~Phd4f=biW;SC|;yv*7PbcQL$^C(I+qtb;{L3$7sM3Nr7@<%f5an z4_jLI{TfPr8%~;PM8i%mgR!4<7jFT{OMVm#+?JmB71iN)0AUa87KVD3vM4IVud<;@ z^9LvekN3d8M|cO}H*D=19ws5<(RLFxrFpAArHFU!bn&OXqQ7<;A%vllfj|k9UpwZt zegFf16uAj`I4`t~oNhR=_ziny{0Dv_dpE%UmqB^nf;fn8tMuBrvnaeIJMx`)AC5lr zhY<@Zn9jG!NIzI!?Q7Box1$D0I54}^6tKxrQ3+X{j1sv9{qxT6sILCO^7o#+Lu6V& zmGhwr8pVXx^B1B;L}z9h`TC)Y{wpHd8KD5VBZ|EvAxq4LVM_Q6xFv5mCk%IF@j4Ek zyr^8cc8bU!=MckoS+u*Pxqzg}iEwH^a^d@a;<70*JSB#zsAqeDnVugkFQX*ISRhka zfDC-chcU52Zx?;nu5O!c?Lm>xw%O$rSly3^;rp0XdyZC*n2n3!q#I3Q*4z>V4`XXCLYRo?2)X|>N74Ix62{?Ork)X&htJ`_6CRImOu`b*>U z$O%a_s-*UB4fW5c>z72I?aEE-A}ijdFA zMM9dMLRrVq5i?6?P!UE_zOaU1cPsenJ4nO|5c3XfPqMAagX@5W_PdA=98asm?9(s8 zGIR1wwzfUn-oFK}ec!+|2_=o_DG0Vp3r3IJ0rp{N|B@)5b{wFbG3*lAvnvWW+L7i@ z(?%HoSWV`@d*we#M;hevVQJLSK(t2U@tCsqJ<(L!=KTu|<^0_^USba-?fNIweFsY>_FGWfTlV1B_CWea z^pO5Ce$hofg+jpDAOMjAP`V&&NknD{CqD{$IQ1UM)n9UbkEclKrNWY!3^*i+IrGWy z-Kb>3$t}XuO8hI=rC^j8A5E&)39a19?z!tnf_ZB=;XK8s+ z+qJ@KJyz*%!Onrs9VLBMZCg$QHSA4C)q0;2Jce^V_Uiz50>TNC6?VneQF$dmTtm3CAOK{#WydJPpIf% ztk9OT|2Wk_%KJNTh-0a2bT(W_gRUJVp&yGzLA79#G)iZ3q~>+BPG8H76`=9)r15b= zW9;36rOkUO_TqI^e%wKr`)T&I9#xu=79nagBso4c@H+&jR~?( zA?*^n9^z0b4cCI`)zX?0B$%CC<>X33;;zN=FiKoxF|C7Usw5xGYNv0V+`7h1cm**H z#27oCoje9|sHLPPu9%jb+0tHvS$D(gfM=$TARK1nF}*Hkz<1j&8HCWDqlw1I->@zf zx;&451tN4-mkQaxr9y^Yg}=eOQel&DFQw$Y@Ffm%vwvvhXH-Y9`vWEzcMgn;*P_Ea z+J2Q;a3I5B~`6}s$Hf;@0v}S0rh9Xd*^zLP^MXEAAw-M zaO$^!Q_q-A=SqK(6mFc3`ulpCW<~w7bt`q{!dQ4o+lO5^LF~7G4<4|XTrQmX8$Hl6 zt{jcj?L414%^|^#9TD4dhMIvLO5)mbW|7+}-9B=grTeesK7cL}+2C8-S#gkd3PeQS z`iyHI8i%9C0(<1^NAR<@lXvJw7c1sXWzxiUDoy{5l8L^;GPVhu8}+-54o18NZ~ex0 z7D74-T`s(<8jMod`A+0YWL}uyFp;cPB9hJW7l`iRmzKhifxBNHh&fbjM~$7iNnDcD zebHO=MB@gaM2#A`PodPu1+6)s96~$;tMK92>%y*F;- z$Qq~Med$@?M3QLF*^%)hkKGE%Axj>RosWk;#11%*llRCse-$BO@0}6pp!p}##22mA zi%U(#vxph8_^ zaM0@OMSW6sT3UXUAM&VP)mG8fU;RZ3VoQgW#pI?nqiJ$@VtZuQd7>}V|2YxspYQ~0 z3^+u3W30W*LP?Pmhv&!@tH5cA5GkBfjG!oH!{jJWcy0p&zC$37YqQ`nob{As-UjB# zMGy_@lXmciQttwVYWNH>1P^EZS#sV?oNqNl31m9VH&{%-M;3~D*S023YO@{lO9&gU zMy{C%m#yGnQ79(@BxU8uMW3jb82El4;^@s>I46$;KTU$MUktgcQQ_oa@d>&`^vyoz zf3N_>vIS1t10%Kq6ttZNO!6aehAjzDYY3AAyv zZ$Bna(L3w_OE~!k%*BLL+wcn$e+UqndZ7~XIblQZB7aa7n$u3ypGEefAT|pPjyWGZ zvQV{Cm|@KE=Uh*mr&Cf9LPN37saJ{~akQ=V|@1(1Py;zAJMt%AIrNZ#b6@gpPoqzU5vUWJ<=bu2l zK1lqSenkn-qCE;g*hhq9kzo29=n`Kylc2P*kHQ36zK6dwF_t#dk)e2i}Fp1ykVc%fh(;ctN?#B>?lPa;C_ zob`w#A4lX1Bzaa0d7~u95&3LMo)AO6R+2yc5#$+?JQ!p-mC=^^3-B<)TLR=fOI#f> zs?|#08wqa`zA*=2`3R44u5Mt@xjHfB9?rT%N^2lrC&AZF%Mdteq#C&bJ@7~orCm5{ zxnwIQws_?1XGMhZO*$baQ#Qf5vvAAaqAws2aa{-pA$D3`IY{0Ri=CDi+4-b+Zp+#< zRv0z{bR?hhHS!Cpz9)|*cG{~D`(lAs6qZ$O*WfpJA(oL*jV$I$&~NA%0|~7+Vjz0; zPo%}QqxX{tu`uQ`l0F8~!`F_+7(?sWB3N>sCqMJa&lf=O%ukyoMb&*mNfqTn60#h* zwj&djUSu7tK_e;b%+fT2zI&LOa~k69n=C%+#XbXgz@Rp}&!q{dRnqvW-=$_e4dtig zOHGfH9ngVl{lXs8t{`hqY**6z7dbyFnmXc3gzq0jFNkT%XrNem6{Lw)JH5o%7qPv9 zhGmQN^m+g*;<0oM9f7#yd*KmvX4?-0vJME4(+CimiEIRQ!eFSjd2tZ#QStInoX#5~ z8$pX1Hnx6Ilo$F1U$bBkG`J|#e>B$3mSAMp0`*J6PmjJ7utx;4@rrlzf9%AMT_mwp zcBj1{^hyCb>-`JRd7Ks|ZA0(0?ziK^g2H!fkG%(t?M08HeMZxaMsDPGbOMU{e1yer zh;#pv(4k21UFtXn4xmYBv8_#q^=*&g3$E(bpR}3LNSxRDq8;n3`>#aPD2^81gV|Yh z!zYF<`-6zQYz@{U=9sEVqO+F}9`r_Nu*0bM(PH&j{OHc}VU(4U?JD|giB4#;WBg7+ zdUz=6v%4q+4|8OA)gnCGnoW?2vu2Ihaq=Qm$B{U!Q9l_Jg29;PfaRL+Q4DQsP6Q9q z1`n+V6Kq#f&jN2}ZCAc09OetpNBiLdwQVjAS4utuu0q=bd+94`Z0<=@T4G%xYSmvr ziZuEusWBApDnYije?th!KSipegAb*`sV@W!A_>dh4VpL+8edvIjD_iTtmiA~^WVeL zPQLgXxf7{JbIJQ6Prr?>Tnjeyu&qV5ipCldS-mJZj%op9jlrFFp^XY-eh(|PJKE;b zvg|P}^w=G($7x~+(HcY1+TcTZ91W3S%b8%q-d_(Sm%xFqTS8kP9FF>4)|jh>RD0BHwoV zA>EJ!sqX;Sx&^Q0diuJ6d>uSj%mzIQ7MWiEAXl-eE6JpOGMv-M#}4=q3-TkwQHfFo zk$YJALhlXdN520J84s(4DcO^#JD4K6gZ#EQ=W+xU*$Htd3aCF~^FVM8(mxmV2TYVA zS511r4f~uyo~~}7Op_cY6#DJqtkWg&6e3;+iImVOk@xD6x*H<_WU=4UGxK-!{1o)a zv42!g+6w@BK0u!CmU_M?0w6u_lEkZt_@y9{o|C1XFNqW!4_fP9Q|#23sN;UXw^&ri zVXI%mw8Oh%D&hbXXZ!xKZhaH7JN(AP+>5*&d;+sGE+=&~yoA(P32x2~82 zC;AxEk?Z7K6P>P+)bTVPZI59kHbmKro~4?H>Yt)fG~3$;eL8|jBRkPi?+q{NgV)WN zrY-<-I(;{|YIH{AP1N=B>{2XGMi)Qtg#UtfT+ea^dAsv$kG+Ymp}pvdu8>CV615ga zqqO|n4zUAK@`htEc-3gEd|x$s`ZJ_6zM9Cxw^1})ESQR~Q7-i30ND#X1-8eW@zr^& zf5F`DN8vd592d?{0>BY&EBfO^@4>WweHo@<@tPbTAyD;RU@P3a_R9-Tg6hK|Y9P*A zq*2~K zM~>V>Cj4FNUH>wIxR3h9=V<81TaH0bp%iX{%RTb(69&NP6? zMr0a*2as9AfP8{d2^yr5-?psg5dfi` zqIrs(P{-kjnFI_lry!6LV#G!|=u(OB+LFJ4K(*dwCEO@a6#$WQP(p;7@HI&I+WTDR zuCV2&|Dx9Wrq7li_Ke(tQ#T5~31xi_)WrRF+Jhz$RHTvS4=IMC-fWH1)=xP*z$6sg zdcg81+~$Dfmc7PNud|n~H=5<>YT)$v9`zlTjP0~M1MTSAX?dW_wVqwiQM(X9#zV5i zA&&QbNp@M0l-K$Nwspy;jiPO9@Vba*F&-L%9bdB3ZEMjHLocn=wB~i&8t`i^Kcxqb zDZ+PFwoNllYv92VhnWy+Iyxt|G?}&FCv=zxiR_?$;ZDmx>H%<(ACwi7ZBzVE@;4Ix zS|fSWr`Rs(4<~k(>^?yjlfpwy*v=Yf#fJ;U2G)Irl5NY9FtIvn8+w;XZbSpUrjX@S zs3oZ@P%+48tj$F%J9B?q&U;%Sr@dCm`R^Bq!zRq7UWgNIU=c)v9ukW}=`VtWxu0tQ zG~1d+*by(OKKm0uh|h}&c}dJikoAs8H}84HlmvEPeqj9u34={2}snz4V2J|4*+V z10+8oK8yc3L&#gubHIR@xSY92>Cix ztzr{TzP`rTUVwJN4~D^@^vZsTKl`c8p+qvE??)7uleLF zoHZV9B;gN()tiOS(K+sT>lb*)gRhPk;X*2P*ohr*un5eu4gAQ9$H2FSMIm@E)&ZGy zLT2i5BoikNM2f=Z&QHFz{plyFFGY0&_9NF33mmb)5exkP-2!;8BBJ2(`CNWy+T`pL zPO%qzoL)RS19nfazS8Bhd#mjY4xgjm6>$0d_OvloC-hbEmi|t$S30WfzM4wA(_82D zfh9Oj{C6?N%CD(zvO59+pM6Y~J?L5C@iux6lgD^_gU`Lv5pdc4uD}Fn=nB~Dy;Uyw zWFIPg#c#yd=x_(zo*H{Kv}fIiMS7omX zy6ge3-S4jPIO@8?^9KUW-NvxjxjZ$2S}=O;bzV3vyX8qab4kd zr_;GXm&fT6oEc4HGA5PVk00YaUU^V@$}}SLRl5StTD#Zp%;Zcaj5%ys=CmC5z_HE7MAf=M|P|3kw&_&Ce^*h-)+~1Eg9pUHpaa`b1E|M^-{H^?K|` z7s;C3SKGJl&$zF_eKqc@w0+lKiu-15-;J%>z8luT4g6Ih+?V5S!+n{yh}c-qn;p8H%I zjSkuvjb8DOXmm6~a+kmzdlT-D-e-fiz8C%}z|H9ePjYE1p})q2e_xDSg}XBbHz~%g z=)J*1;7h{23Sp2Rb;t4^XP}efi4E&i$e{bNo1)PNb+^K$0skrT#9O$JhFjgg;<+Br zbdz7juVAdao0w-6;0$)h(vP?gxBvewoAj1m6gI7=RyuqX<5sv>c+Ab+ys>z_`#_8w z@}q9YZPDo8|MI_g{}*|=4|JQ1`%loT2)7USUvM7{y-vox0{11jTW^U*XRnV&@4~Hg z9`x64_m804V`#(^;pd4le9A9D3WLJ^65&6M`*iS;E)?!I;8)^#8J?A&b&z)xaO*d8 zANOgnqYXAX7xxN0)BRzLdo$>?kBOoYe30os#_+|`DGUntQQT{A4?)_KE)?$N zxKG9N7~qwkd644(&IR2Sj^N{X?su!i4Fy!aFiiFTPp(tJJR>t}%E{SNPnkA72Twwh z$s8ANNk~lU)3;y$WUDRZsG|oAOdWL0V7#@UYv{1yBmQ)(edKYYMvobLeA)>oo-{6f z{Dh2&lO{|0|MD+VRl5i}&~ZBQ?Pf}^8nqBMiGEC4d&}Pa2V1oEy?ER7D86U-0QWPv zx8r^p_glE1|128a)$#w%J>&mC&NUqVQJ+g43IHt;^v!USURN+A_Z#f)1NZtEywYEV ze-G$E-1#NNr8DQ}EhxaOhZwJGevAzrl^CS@>l~hdR_*l!?80fU^1A$Xj~CM}4h+Bh zYK<;;O)Yas_hBqf2aPtxU{VO85m|$~$yG-KG_A^oL9MqrAA?bv%h3D{-av`h=?J*J z9?e7HEcA#q;tPX!&*ShDxu)S7jF?WZ{EHRDCfHo0O4pp#T#J zOvf-@{A2hwY{}d)PWsap<}H|AxUdYEV%+n!36*Zo1b?j@yoqSCXjjanKumn9-2R3- zM>D32XtXChxIJ!+>tHaa*Hi7T8Q)hM<;rqRt4KC@-8lv#>xrV{S!3 zVM*b_!iqDB=M~I9L#voqxUd3PF~C(1#rgB+^~97G7ifzX&dRPRTezTj-fXSG=WVldK0RCW>Iry14ZBh{c-DI8lO>pD^((QwL zCLrAbxZ!C&-7DeF2gJQ3YuY3-Y8L$z;+8VAX2E$HZipvmCXuH|)+ywkB;A>HaF>W@ zAtAGt+{7>!_vC2;If(>KmL5vU3HtnVaIe4(zWMdIJ-EHN8*rbC+mAblyAgLYJV6me z31G4WnG$44FhzosCCHXw65>rBB*>H?OM)p9oGd}M1e3_B@E}2^1X&VHk>F$rvL%>A z7K8^0G9}28V2T7MOOQ?AENxh_HtZs}vZQ6E2r!?Fg7`_0CBYO4CQFbBF#mMPCP9`2 zQzV!yK_&)@l46MWa7#4 zSEla7lbP$}X_D(?=E@da+02#AT-nT(&0JFj*Hq@3%3M>KYpUd$#C3t>nv@~A0A;?+ zU@lQrOhN=nCZNoT6PXKXCh+{rTq?g#X0FN1C363yOy8h^I z)JB2bHED`Asw%t6<(i5*AbnES6fqZxxXhU1q_z(GL--vvVphz)B;Da&xeUN*ch))l zesWfenu}aD_8ONL6a2nrdh*yknC&MQ=3Oy)xva0f>spe&&>nCHfCM?v?QGkF>GJr@YVS3((B~B<`SCztmsk)lg;JTfmx`$=VN}e z6z?QlJ`Xo3c;nz&A>J5pOC?Mtr|4kU@-kPTc&FWdNy)s z6w324FBotY=f^TT1gaQJfq zzM$(AM=;>6b$bHhNob43!t`Fm=V-*c5x+m%?y0k45hF zz6sb47`HqceG_mUAoX>ZIik^pfT@*`4|uN=V+p{2S79szIMEf24(x+f?bVpO1N_*H zaS7nR&Ve3)m#lzXz$XFU2fPUja?+4jwgDCZ{s6cfa2*z_Tn_jI;GKZYSOmEZ@UnAZ zw|<)TD%P==06JDiqqTtN0k#0{1H2ZnxDk2*h5&a1J`WfH{0=a_zoz{HI2f>R6YK^! z7_bQNwN>yBc;W?^e+0|{{1mYCBAg1ItZ7cbg@D#|(dY)iK^RNz2Rs4exan5-2V4rc z9%HTzfNdC`z7Lp-@z!9QrqQOU<$&6)upi(Uz!tzcfY$;p0lX7%72wl=YXElxt_S=S za0SNO<1ifA16TlfD#qLaz)HZ&0T2BR{sHG>?7a)VOodG8R zJ_vXT;QN5D0)7W*e+c$CTGLK>1o{Eq_h>YF6<`P8y@1t^MWcHGUjY0Cuj6Aqio z2;2#9(?vKta8HAeyBPwP30%Lq(I^&&ii`M)fEx(hRgB93TngM(z|ne0ir3}lJnP_f zad}pIYkYw40CK*wR=Z&D<2O2g;{JnV2w(ho@ z_Zh@98{R*KEFb!Qg~$VBvtH_`<>tQ~UP+OTcJwpPKPMW!8g8Luh18MC#B|WU22IQ$ zBb^q)dQ_$t0{;#0wd4nv9d|8ovsQ4u-ktwYHOdNyXw*&u> z!dF>SmHJq`T3z>D-l_~o2WNQZYoi-1PyNf*ia0=V~qL)TB_t74WP52n|3cuE60 z*L@f8;=`xkt8W|PHpCm0gG|V(g{&;}m(QfIdryN5b5YNBf^^;he~aM{-8NYkpz}gF zsegYu;r~cE$c@{GGTv*i0aZrPOlxV0HCNgw1$~rTT57H~l16=j6pUy74Ow@> zE$amHm7J}#T%a;QufH7Q5Dx1KYVX^quXZ`L_v@&y)|xQe+HN+_=_`6^X!&D$X;dC* zr2c&y#wc(b&S0+IV!L&H|JVw^Q;Yd)}}mbi>cDOyehUX znhy3a;Maa7-cud!cjj;TC3Y%P|`;v&A=fx8ztQP1GH05^>z9|Gi9~To2dR;1=_r;@t&9cm~$nq1kX2u)(YA-uCsr0Ot8Tew=v!*SL53x$csSU z57(ogg*NYZ?1lEk6!%`7vAhh3$j113#w~a!4L8Y)?W<5BL*}!`_U*|=b?~zqeq!t2 zG~m|&-w1p;$%@U>cJu5QTZ=mULGb<^yhX%I>6LFyqH?hd_&0z*o$JG1+PKmCrRET@N?nq7~{iD z^3G;?^t_w&0WI`L+Jy%5pAAwuuD{9JGz;~}EbFDXuflz!X{L39$!%>AZErT_JwD%v zHDaWL(ED7W1;uG0Xi5KwMu(FQvFTZgUUPXKY9?!GZA`*aoGyi*{g@ZQ2VyL5DM}Ec zhx>NWz5(rDY@@FJR*T8}LX4cVSXU?XHZR3P9uz+t50F81`xlV2=5DO#fLohqO=%Ds zo2fgYQ9kI8$|ef>wL~{+qg{t(Az_aq_&wtu%yW=mq{(tl6OsO))qqw=G-0cHL8UU$ z0@_ueseYyh$`JlW;O_x`Hu<4^jxl})oDTy3FW~8;;KR#Zu6d!Z+)>&86n=ib7y0{- z)a$fPV$}TL>>~Y`&QSBHE{*&BNST z8PQM&uZ&3>4fADdL7UGsbD<RanPunLTNx@5k3?6f9mkrz?T4jKk(;s+VtKx zh+AP2l~7lwK&yk_OX2rR%;yc^{#9&!I@`J-F7A7i_0G6L>w|H#tdFC+(H=M3x(jf3 z9C~mY;>;^RoeiqFk(kdT=2`UkARdw1?6Gw*vV(RI;a&d}cEo_2E<5i2i0F&JiFp=b zLyYp9Rm~&Ykqq#h`gAmU*&i7jqVM+ZZ{#HkljbLzFz( z?_zi#!`}<%|Eu&2_b{)ypn49771y>$8crJfucUzE?EzaZgL z410264&-@>QBcwQJfU1YVgB7cpVd>90bbEBeN(yq7@W4fC*Q+6<@3W^dgABlo)vY8 zSlp#)=i=xVx|$iTW*B1lXNEU1EMtGS@$(<`e;4yV#8ByR1Rk-#5epo#z!3`^vA_`v z{NJ;{6Wk7OW+;0Bct3?TX>_UQ7Tq(giHoJv_aL#)cj}&>(mlV#&;6x0xj+_fESC^h zTBn3+ZB6M+smU;=;}xp7uxYBe%9&2@)9F(5?+-=2^xRJG1#!_b6S@xK-78)6T9Yn1 z43e(C5@^S;fPVZ;@5ty%m7wdFy+MDeAHB+^tCk}|@5Jch=xX>pKwPC$B~y(@w*OT{&d z;bMl>3|BH-$MAZF8yRk5xP##yhMzG!z%b!-8O|VvV;N3iIE&$8hSdyLGF->-dWIVr zZeqBD;U0#cF+9L9VF8EFa4bW73oGN#a52MbhASDaV|YEojSM$2+`(`U!_OEVV3<(G z;WHe|5Fgpf@EI;D!vhQx7IOFu$1N0yq@7khMO4fV7Q0j zXABQ8Ojyj}GaSos3d30p7c;D8xRT*IhSxLP$Z!+G9Srv{{EXoNh6!hI_zcG~oWgJx z!^I4%8Lniwj^XtTH!|GBa0kOZ3_oLdfMG&8htF^lj|oa3jM_ z40kZx!|*eP2N)*sJkcPAQk%nn?_4PBsf?N3Z6a}X)&GZk5g26eJ1U)3uIidv_c<%->OJ0ubAA3m zaAjjt^Lgj5y5K^wj)giu8I{T-i=jc-Yzhk=6M^8Vf{Ifqj{TD@N0XOO7YjorhE+t=2k7v4y zzn-3?=q%uRo&5ef`FeVa^3VSC^nr@b@^941AFPwFrw>>D*`JfFz~PF`3C-#esh>E zwb3*>o{}!*f4%`-`CqJ~|5fLIi7tFSy^`rF{)(>bQ}a9Y6$bK^em(>Lir%R6e=94Z z!e6EHucx;d_*eG3#K6CzUvA)E>35BRe?`AR=l^cjU(s)2y6R`&qoZ%o(c5(N+kc0C z9nN^Bi@j<0>HOcR^MAjNey@)HfR6s4j{cyI{)mqLkdFSij{dNYuIfK!pMUD;PwV_Y zqNA(&P5FORM{n2p-=w3f`b+tLOh@0Q^Z&SxuG$CX{|Oy^m(KqJrmOt-64ON@;z!Zd zxati9x*BKw%Yd%R&&LLIHLi>p(A7BiD+9V3_Z~2ytNgETA65JmUEe+~)Y<1(oqR=C z_KD~Guk5Gj%06^#dhDg>%05RM(3O3L7|@k{>;`mYpW_YaD*pQRUFoOj`u6>FoqfjZ zrn66lPQIcm z`&1jym3`_B=*m6;1G=)$`37`lpNkFX%04(AI5zugN`Do9eg8^Nx2y8QV(f>cI3@Ke zo&03BpR&)j26Pp^YJZfHPM!Vr{R=%^-#<}wWnX>&Lr>TDKNMZ*r|*C0>H7YM95vuu zCXSH4|DmV1uzi(16TDKa$w~Dm`x0rJtg!^u0kxFV&@= ze*C4U>&IV;uJqTBzw~td`0ES~U)f*ZKiAXs{c}ZE_Sg5%^>lszT+x;O`u_PMoqjj# z?5F4|eEs-E(UpAt_+_z9{%tz>imv4A#|Mh8^L-+-?4JI#Qu^eZ!36OHUFp|sKv(*$ z*3q}=^a~l#m3~(m(3O5S7|@k|w;Irue*e(Xx9jwKz<{pwd(?og^n1pDuJqe(Kv(*` ztfTMH>GzfaUFr9p0bS|$i2+^d*J(gk`hBaT@6_q{vjJV{XVR^EQvQ{GeGKSIzoQK3 zO1~jG`YxS*BMsoqn?o=t{r226Uz0A_Kb8ZF3hXU(o5NZ@*vE(e>@)OFFu~{drkO*Vpf_=;-?T^;I2RU%&0v(e?GqYdX5Ve7~-v z>&w?0I=Vi8zp10^^VeHCx;}mP=;-?X(c3z@zW?!#j;?Rt-__Ce?dQLAbbb5vo{p|> zKi=2T_4W4$I=a68{7^^N*Iys$==%C&ua2%Sztpd#xwdh<4i-*vsq#jU%GR|zC0^hE zCjYVYdR_S;|1@@2{`LK9@*hk0>ij>?&rN-$MsM+R5kHfE(rg9C^P!9Bsryp(f`jSr z>*y+;ivFpNuH-BFH#)kSmr&!7+ul-PV^3LJimt{HG4$tR>1rG|ugkxbqx|Q~N3HB0 zb}&$Mex0jT=+aTq)$2{8bXMbdJze>~i0${mT|L8B_EYgcm+hnMrsyjE6Ls|2`Ph*- zZT7rHC)g)W8b5J-Mo;nDI6RvLPS((Nj>uRYpL(SAKW6d8xvJ3>MM6nGgly3}eY)^J z1JPzWuM-)>V$`~%-hE^5G2y?m%l{zuuk4u7DFvx#7TX0z!)4J-LcFkDj|kHuo-Ev^ zme&yj;v0x3YaV_^5T166pfCI5Oi9~ewK#L49WSWk;uGi6Q&M6P0aLMj{|Qb0SRx=!!HA- zBh(}<{&SK?Kjw7ckN!7)h9}0Gk}iiM$xK2LOj2ahe@K3!Nynmx4wfK`E%S1osKjSBH1*q|VAsqdf?;zYGgnNK+^kXK&C!S5Xt%Rc= z^UH)g0YC9?6OMk&9~15x;_DLT)W6sPcF)%=0wY(Y9-nwDB~Zf8a3_wmT?aR~?k2ADkG=!$0V#-zoX&Q`rL#cLW)dh)B2EqqKM~2tq&y>B@gHSem3|`Nqmp;LBe;4 za^k)|JAr~ED)S|ZGf^A32k69jD%X96B%BLEvKcC9$qo25C6firs5Zo{xN`7Iwb~5GG3DW5#0hXwt@a*na|YN=$Ml;9Hxhh504|&!M72|VCOsUBn*;<% z0PXeDABY+7)?ITdmUYBJStcDUruCcJj~Eh?V;-~n!NdcAm_7tCXNrXwiPFbp zDjr~hpS~j@X#z0)2AJZk{rU-MBa?{KUw9gssKh6yLE3PzT8}b8$9{3g4m&pG7}VCR z7n+ci0VW{=;gE!*SU(Xg@=jL}OdoKJWQYF5j#oi()>HfSgN`y%giK|T%rhu6CXSby z`k4olsd_48j^&v#gjkgApd;H3o?!Du$HZFvFbuW*7F24q5sk@Y`q_%A7Z*JV#IclX zHChR4HXUoWnJHyW;z#RaRt;%fwrPG`%J`Y1PMte?RL;b>{()Mv33AQ@GYR*Rr{0A3 zp?p}2yAk(V+?1)W#r+8GZMa{=EduUa0GH`Qdi)GOao^DoEnJH4gP*v8__2(@PXh5J zQk#`D2`&o#M8IA{H&N&^T2v-9@#AD)f7Bou@M#i1Fo@Wb7y{yEm`X~5ITKLkVaq;d z)y0W>T{E4A3ddwR`+peB$rOzEfjHFziaQ?C$k4#4UQpagj2l0Gg1^~cX4j1+X#+TuUq##blo={Wgu5?cD`<(1@`hyyJjrP1U`=s%cvxPf<;zaI5+SBVD zK4)zXWTj8Z#8V*XaplywJT9NxX~$tB>GLPq)BS;}oElFMzS64!v!|xJJ>vM`^lFbc zy`e4uysOFSYM`^B{CI#)&K2o&dP!}1gU=hlk-BsmMtZfgI_5d28Yle%S?}`IxYBXl zMLLewfLBj|WQa4iajpk6#1YC$CQI}Bpp*{>d4m8I=l~NTpLo-8RIdjIT2wc6y%cYCH( zRM&ZNo^f>yU(mx$6Q>YUt=AX8ks)w(No{D7e*I3Ix>ez>qNj>#N4>kQSu3ooo|0K{ zhSyi6d1|YClCc3YAe6!jY6#I0&=!;xo?ek(Rxzt2Z+6AJ`4tNa%WzuO!U~*5Rl13#}&=+tzaQcYSSep4za@)M_UG_nzk-w3U&7$-iL z$iNT{@~Y8kG(z9N#7WsQ+$wKS99ZVyh(c&rxOA+Uu)%pQpI7r#kaz!i-MS;*z~Uq_ zCt+m}()iM5!Yu;!J1VLi0S8;XD}Wy6ud353W_rDK8f#05q>{#kL(M!Df#wF+Z$)`- z@$3b8I2Wvkftw~}i8yqnmCPgvgfJ9{ih6f&NXIUFPoAXVEcSCNoPOjfM~%Ot(j7z1 z2BV|WT@i59bbEJuIJ?XfIfWz9O?Qw^WHzm$czy+nFO&)TT~*rQ)rZp;aHICHfpQxRxT$RC^m^!Mb7t*J}>vuQd;J1LEo)mP| zRmUnD^neF7;>roAkb>RpI2=7N0XIS7r)p4yjRV;0f>kcbu2F*f$Jat}m?(g1E#T>X zqVi6U2$GO&Wp=s(aJY#~i6`9d03yit1R(3t$0^iW(Uqv$kwJ;7|z?(IzR?71TEJn1ugm2N|sYgoq zb{)P9coTY+Nj}n;-14dr?tz;69*NN!4Nggp+Mht-uNM3RH1&Ox!i!N?FLv0(a)z8G zIZN2Xc0d!l+uyM!%EB)*{zo02rkO||7t2}7a`rL*UgjUj_zuRm>+sYGCpnv0j#{4m zGvl`{kpe;zh~v7!ywYbl<6mX`3CvGtVG#d8 z##0Z8F6#6Ue#tWVO!Ej9KoetO;M-i1QJfJ1=f%wbXtl)qCD6jan{YtfBMafch2X?>74xUD;;gPlQ$-Z+ zC5+FO-n4%JlAgIVXvbA9y5!gsoB1cW1zLMp0_`crf6VyNjHej}l3!RS5sx$eMaKWi zc$#IQON{Bke=TPmaaINJ`u$~^lKU7<;)3Z_GeavW={w`oSPuwA&#Xk3N(tgst z)$?u4ujZ>UP8Qd7EWhO~Dc{cco0vaxmqd(VJWXs+x+n9#9STpAT7;j(_@kKrL6*Ol zl7mUyzZ^|19Ixz97nO+ZbQR@`o`03yfdO_*WRe2Y4#KEnXSm2FBBQ45X)e zZ?J;#G_fY?6V_AJZv}~x|Ji#aV=eQ4#^H{=S0Yq7{etmx7*8{AbPa*QP54^2Ma-dM zDuN^S1M`1&pX57T0__m%qrEK!9Avx+8I1Hl`F@EI`_99e3Owl_VEt9OptFGp-=yr% za%di$@C`bCni?eeKXZj7_MwL#njR$lHC*wmWI3}Kul9YhGJY=Ok9k5O${9}+i6qC% z_#(zvG5)%CiI~oKKjR-`ez9*nXcqua>ARQ9-E!t{WB#P4B|_{K59f`7ALUDDhdUTw zuERgda*lgW%JFkLKEwFe+9jT5!Rgw@at7<1Kx^^vv`86GXD)5wE>V4&( zWH1`d;S*l%uOs$+2aV2OBD~rc=r7Dap7F=OAQ9Ix{$$3heTK#{p5`b?&gaZu$oL}0 ztM_t~8GkzC)%(8Lj9Kv+R_wavLOLJM+82Px$35z&=z4 zP|tEwUzN`^yGBOpH`e3}k_E#vJv{Ca^$ zzpFqhZfE_^1D@=u_ES1h2Cv=0_=n$=h+7zcAIq=R$=}5M>b)%W`{`Ki51Bt($4}>FQ9jSm;lE~liVlBCADNB?i=<(| zCa#|aKhj-?r+#s7?J0(P2={0$Wi%U@{6`~w@BTnyikUD+;1yRlTxfuI8Q@nM;IA^k-)4aS zrvd&6;EzJQN_F<%%J>W}kE&d}U?ArMfwyW^pUAkvT;hr_-op((XD{s=138Ha(w^%5 zd@jowAn^PU1I8NQCj(FMD%Zse2g8f>QnT=5SyqmL96Fbl(s%SZlJQ37Ka=HW>+It+ zkP~D%!8@gZf3lpA0smhN@b?+u+YRvV8sI+z-p)G2g6|FZFI+l^$gADM~kzsn{ zKSA(YHQy>JK%BV-XNG~CeCChaBl!n#xTi7x=u3Oz7ZcuW)6^6YvujHYrMajI9#n&N)~6I!FjO(|8>lN)E$zalV4kJz<<91ezO7oT?0Iwg;^40sm75_+1A0cMb5L7~sDJ{wVZY_P;6HcbHvV2Mzf9p}nv(Pb?V2?MU%z ziB;p`3BacyU+Mb^)0zLV7Ri4#3;O^jrt}?isYHnL3gMgue#)O|y7py}z+>K$^;hXq zCFHZv7|_h&KE(CFNla@s;QtHo$Hn+p&P@jV+YRt<8sPT{yj8pLGbwpI2heH2{{!=< z-6a_xW&XYx*Y}p+QU%_searnZHJ>@kfIoxzpSVN{K9l8V8SvA2en**++G<{i`RQEu z-t<{&fNwIuhYavH8{i)>z;7|Yzh;2{6nKi4+FypFrG0I{Pv_B7zTN+C$+?U@Bn!Oq z-3{ryK;nO{R^l3%G|GTK(*U1qfIpq(S8{peV6?M>aT%_`%)`_cj>S%$NwvGKA%lvcM{--f9+kk(r0e-0g-fMtwF~DDHfWO`V|2G5t zCIkHQ2Ke_4@Er#D9}Mt`D5q48?7H?h#i0C-0-o~oL7q2OX_IEapDFN|kLCXJWcDzf z@g9EvqVNRf%~L@J6Vp4@lWgUUIRI;2KXBd@b>{v^{OhrDu1>c@V{Yz|HJ_Q ztpPr2fKL(qC}s6-INAVzvH|`yfwyYsa(^k8MJ+PmcN^d@GQeLAJoOKi-IS&`8SsxY z=$HP(fd5eg{1^lL>jH1p?roNusQgB&lqr29x_0wD=Fh%N@(|=cAITFFcW37|% zTY?gQAM=03c=a6%ilMj;3Oql=fWCOK)mwW#!T>+s0H0-m&ojW!HNY0tiLK9qP`H%?c*f&8@w_?uYH3*67Ckz%xsjDMQ#KY{TN7|79L z77cZ;fTGoUa*+)-_p7dkv&!Xj)wulum#-pFU*W{!HkThurmDOZHFe%fM_ol#!0W@( zv0#&iRe}w5u7IlwAJ6p4QBe)~6%L=z(Olv31bod}wGS)ODyo9@_08}S^MoaN?m!P# zKPeUrRA*PYoLE^{*&J}uW4*(_f}ncNUg@ZFW2u@XVj-8{X^Qcw1!!$$oy(&kQuSKE z@516FECUSCa!Kj(H#!>pPM?c->b)yn0k4D(IGp1r3qN9Q-O9kkjEss5M3}_Di?^;$ z3p4}+0Y|0gch&hljs}7rudg0@o#)a5*hJCkt@HZ8=EDLgmrrwhtE;q1hu<|N6LG7k zaRso<$?t|$8VJ(rJlDDKh-cDVx`cEN&spb!QISkoWXDPb8?=DO)hPL;URB;kBD)Zy zDhTnp;FPkUAP50kXccsMoGvWa0#$^H1&q$xx+<-K7E=ZS&6=~m3fh99*5RqDbFtnP zRqh&ENJdd7<;PFbR^oS}7HIUjkrtJ~Y8ZTc7CFXeY7ivwK1ZY0gk<#u>+7)mOT&Us z&9xHd!LQE?nj3pg(n8c`t*KF4>Gr!T-F0qg5%koB8a9c z=&0lB#%Zo5tfE5zFl7*nY&}jW>aOq7*h%yWEiX&JT=z4~1ENt~?O|{55e$Cqu5KB6RgH{9kkvuNHh_lFa0e5w?h9$iX zSRF`CVJe?XtfcfdA$h!YRh7uB$T!|*%EWAS=Sk6lkK!y^~?G-@e zSedtc+Ie1Yy`!?*C;}lfE|IVR)=`D(W|HvfsB5To!2GB<#Iviu5-Tz(2oWwh0`35; zjhsbQ*sKM4a|Ybh&eS+Jun zcXmnf%>0T;<0qkDR0SI*^+2%LS;#N>L7yKLM)@q%NL_iNMq3$hS0OXhyXuksdiZS; z3q>jGQu?@>0wPg8BEwbp;9LVQz7j&eEbG=GD`iTb}gUO`kzPRiUA zjuUmis3lNw*B~#FuO8yOerINduv!IDu>q^mU8wz>fu5#tRAOCk4}XZFv)0!G1>w#W zR96Pv_1%F?JUK=;WY>y{8Z2DL3SF$5MKYI{c)crv4P^n;yfxjNNUj38ikeobuWaZM z5XuB9(r)J2t^hMrsi!g_GNjB}qJ;x58EvkpoyzmQo|P_NpiJ0D)X-c2$|30tw+Gd7 zDM~0V^8*Q-yNqSkVKI3>I+J_dtns=k`ogUZRkdFY>6S zy?AbMcRiJluqyF=fmSYJu-NSnI_ipPB|fZ8@vU$*xqGG$=T#XSM4yX+EEpP7^Qp8@ zwOx)9Qk=ioQHKhKWTI{9p)Q%sBR^@F7epcUsXSjt*}FjXnXS ztAa8gm1Ke2-%#gh76CK{bwN}VFPw{Y?<)Qa=jKz#W}&Ez5k*mls76X#S=~#)X#g8^ z?5X$y?ke?ePu*H^)hhXgLC%=Vxd83GMycKCsH3)=`XFSmGv*fK>y3G|{=a;dyAG8~ z;Y#$O&`!C!vp-cNuwFxR94;ZB$-V`CEke7De#pC+Bqo1dZQ6Xp7u5%Ue$pD)IqbUk);w*hDH_@1?O7Gbkh3p zipq-EygixlO<23 zSPkOSPy^{JAZcA;S@I$>lK9R7{wO0Cd#YSbqBo_L&qG}*?OsuVOv9~*lmI&iyzq^9 zQ#W-XSWD1xUg#xPS0b!%*Y&LJ$a4=4q!7Na@%F6lfDCwhR^3Qm(K{%UnS?q94T0t^ zb4|{qMDu$qxT7W{^bEQQ{Rz~<0V?SYTzE-!^kb-tD6283r&=#jW6doReGlsBb%jOw zE`XK}JxFwJd^IaGDD6do&!dV<7#Ec|^`(1wf>Qd5z0u+BrA9!04!CCLQ)za2R`!q~ z5>2*{O*Fvp9#*P9z0IyaI#}6|y5?WGMkl5#*?x^W8eP$mhHGF4$?(MD!5?DP_`xE=?pFBt%OW zi9diAi6#|tLBaRUd*ANtvmzle((Rj>oo{}=dGmI6-p#CE`PKXurcD-ZDC>B9+)P%} zUh~+ui+O%bam9~KalX5=x5TP<2aA8pDYoVvkC$;&+clx(CCcdSYic1T+95Rc;m!y= zlo=-tzZ}^D@)?w;dcwKX=c?S(qC9db?CxHfn+SCDdpZPdQSHWNh z^|kq=*>5N(#w6Z5Hoc8>J`<}={Y-pHUs=ZE{vt?S(=46PGeQ8UbQ?>c0t zP1&qJ?lfq{0&4wHUv1bLpB>iy{E*fw7W43oCgM6IR*80|fw48y&_>RzgZ{*az7s{< zZk;X6TwCyZ`pj`nVt1F;m#)suMol+YA?;4QZqS~j`DVQ@O_~}HL11DkFH|KLos@j< zgTx*W(~h#V*X*tFHXkcEiZ@&UZe^+FJhlT?p-Ra*rTp%~o*c)nx!cH?h%Lso>@O}g z$UZ-z^DP9$DYa^QV6}pGt4Eo$@1_BsvVDkh)8sx(!TyN4*kC@PZgK#yHggDnHQxQA!K5drRl!D_0q3RSHz zrBX$yjC=DuzEwT-7d^DCYeR;xH$H}tpC}fA403f~nm9%kux#;C<LcEHOnPIRDz zP9y#X;{V23P-_3LUcRWxKj-*Z{}JNxiMnmS&d(BE^B2o(Bl}Kx?EK8G!Ied-e4WQ7 zs`IxjwdEl{>23V*A46^Zb-tD8WgjrrU*(A21DDXsF4o^-&Gw&tD!k6` z64m)#DnGWL`y+_I;u-2ZFj1W!Mm+m0;vXZ9O;r9#@4cOpze^X#pZlIm{J?Rd?ZIQ7 z$y%U4M*LTnK>Isi{muaar&xZED9hKrK~a33?YUV0XO36@YrmA}pT1vRnGED{`ZFlY z7he06MK6Z}Loodo@E2ZVZWt@4|Hx1=$9CiA!Sp*koFYE$TKmtP_bnfdx6&&t`Uh^{ zlU$3Jz3K2j;{O7cC8>PbE#7oI_ve&uPzK;X#gDuvy!LHBpn)XZhIcII51r~QKH|T2 zl;uDF+;GqN*DrAI-@d6VfY7Oz=RX!Ffpa-uYF^W+>YR;Hd1&M23=LM z?{$Egm+PztJUt3Dq*Qf#!KXiQTzw-x{lK)~YY_7t81EmUoMUPoG%I`vDMw>0jdjvA6&L diff --git a/st/config.def.h b/st/config.def.h index 279890fe..86d7b180 100644 --- a/st/config.def.h +++ b/st/config.def.h @@ -67,6 +67,18 @@ static unsigned int blinktimeout = 800; */ static unsigned int cursorthickness = 2; +/* + * 1: render most of the lines/blocks characters without using the font for + * perfect alignment between cells (U2500 - U259F except dashes/diagonals). + * Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored. + * 0: disable (render all U25XX glyphs normally from the font). + */ +const int boxdraw = 0; +const int boxdraw_bold = 0; + +/* braille (U28XX): 1: render as adjacent "pixels", 0: use font */ +const int boxdraw_braille = 0; + /* * bell volume. It must be a value between -100 and 100. Use 0 for disabling * it diff --git a/st/st b/st/st index 8a54739c09fa7eabedf559edf1713018eaf78fdf..f04d3339938c10f36e85a1eb282c6dc268bf2ab6 100755 GIT binary patch literal 110312 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 diff --git a/st/st-boxdraw_v2-0.8.5.diff b/st/st-boxdraw_v2-0.8.5.diff new file mode 100644 index 00000000..1ba0e461 --- /dev/null +++ b/st/st-boxdraw_v2-0.8.5.diff @@ -0,0 +1,583 @@ +From 46a1124957b8de5e7f827656b64bfc3baeaa097f Mon Sep 17 00:00:00 2001 +From: wael <40663@protonmail.com> +Date: Mon, 11 Apr 2022 17:04:30 +0300 +Subject: [PATCH] [st][patch][boxdraw] update to 0.8.5 + +--- + Makefile | 3 +- + boxdraw.c | 194 ++++++++++++++++++++++++++++++++++++++++++++ + boxdraw_data.h | 214 +++++++++++++++++++++++++++++++++++++++++++++++++ + config.def.h | 12 +++ + st.c | 3 + + st.h | 10 +++ + x.c | 21 +++-- + 7 files changed, 451 insertions(+), 6 deletions(-) + create mode 100644 boxdraw.c + create mode 100644 boxdraw_data.h + +diff --git a/Makefile b/Makefile +index 470ac86..6dfa212 100644 +--- a/Makefile ++++ b/Makefile +@@ -4,7 +4,7 @@ + + include config.mk + +-SRC = st.c x.c ++SRC = st.c x.c boxdraw.c + OBJ = $(SRC:.c=.o) + + all: options st +@@ -23,6 +23,7 @@ config.h: + + st.o: config.h st.h win.h + x.o: arg.h config.h st.h win.h ++boxdraw.o: config.h st.h boxdraw_data.h + + $(OBJ): config.h config.mk + +diff --git a/boxdraw.c b/boxdraw.c +new file mode 100644 +index 0000000..28a92d0 +--- /dev/null ++++ b/boxdraw.c +@@ -0,0 +1,194 @@ ++/* ++ * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih ++ * MIT/X Consortium License ++ */ ++ ++#include ++#include "st.h" ++#include "boxdraw_data.h" ++ ++/* Rounded non-negative integers division of n / d */ ++#define DIV(n, d) (((n) + (d) / 2) / (d)) ++ ++static Display *xdpy; ++static Colormap xcmap; ++static XftDraw *xd; ++static Visual *xvis; ++ ++static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort); ++static void drawboxlines(int, int, int, int, XftColor *, ushort); ++ ++/* public API */ ++ ++void ++boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis) ++{ ++ xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis; ++} ++ ++int ++isboxdraw(Rune u) ++{ ++ Rune block = u & ~0xff; ++ return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) || ++ (boxdraw_braille && block == 0x2800); ++} ++ ++/* the "index" is actually the entire shape data encoded as ushort */ ++ushort ++boxdrawindex(const Glyph *g) ++{ ++ if (boxdraw_braille && (g->u & ~0xff) == 0x2800) ++ return BRL | (uint8_t)g->u; ++ if (boxdraw_bold && (g->mode & ATTR_BOLD)) ++ return BDB | boxdata[(uint8_t)g->u]; ++ return boxdata[(uint8_t)g->u]; ++} ++ ++void ++drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg, ++ const XftGlyphFontSpec *specs, int len) ++{ ++ for ( ; len-- > 0; x += cw, specs++) ++ drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph); ++} ++ ++/* implementation */ ++ ++void ++drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd) ++{ ++ ushort cat = bd & ~(BDB | 0xff); /* mask out bold and data */ ++ if (bd & (BDL | BDA)) { ++ /* lines (light/double/heavy/arcs) */ ++ drawboxlines(x, y, w, h, fg, bd); ++ ++ } else if (cat == BBD) { ++ /* lower (8-X)/8 block */ ++ int d = DIV((uint8_t)bd * h, 8); ++ XftDrawRect(xd, fg, x, y + d, w, h - d); ++ ++ } else if (cat == BBU) { ++ /* upper X/8 block */ ++ XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8)); ++ ++ } else if (cat == BBL) { ++ /* left X/8 block */ ++ XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h); ++ ++ } else if (cat == BBR) { ++ /* right (8-X)/8 block */ ++ int d = DIV((uint8_t)bd * w, 8); ++ XftDrawRect(xd, fg, x + d, y, w - d, h); ++ ++ } else if (cat == BBQ) { ++ /* Quadrants */ ++ int w2 = DIV(w, 2), h2 = DIV(h, 2); ++ if (bd & TL) ++ XftDrawRect(xd, fg, x, y, w2, h2); ++ if (bd & TR) ++ XftDrawRect(xd, fg, x + w2, y, w - w2, h2); ++ if (bd & BL) ++ XftDrawRect(xd, fg, x, y + h2, w2, h - h2); ++ if (bd & BR) ++ XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2); ++ ++ } else if (bd & BBS) { ++ /* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */ ++ int d = (uint8_t)bd; ++ XftColor xfc; ++ XRenderColor xrc = { .alpha = 0xffff }; ++ ++ xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4); ++ xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4); ++ xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4); ++ ++ XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc); ++ XftDrawRect(xd, &xfc, x, y, w, h); ++ XftColorFree(xdpy, xvis, xcmap, &xfc); ++ ++ } else if (cat == BRL) { ++ /* braille, each data bit corresponds to one dot at 2x4 grid */ ++ int w1 = DIV(w, 2); ++ int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4); ++ ++ if (bd & 1) XftDrawRect(xd, fg, x, y, w1, h1); ++ if (bd & 2) XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1); ++ if (bd & 4) XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2); ++ if (bd & 8) XftDrawRect(xd, fg, x + w1, y, w - w1, h1); ++ if (bd & 16) XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1); ++ if (bd & 32) XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2); ++ if (bd & 64) XftDrawRect(xd, fg, x, y + h3, w1, h - h3); ++ if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3); ++ ++ } ++} ++ ++void ++drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd) ++{ ++ /* s: stem thickness. width/8 roughly matches underscore thickness. */ ++ /* We draw bold as 1.5 * normal-stem and at least 1px thicker. */ ++ /* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */ ++ int mwh = MIN(w, h); ++ int base_s = MAX(1, DIV(mwh, 8)); ++ int bold = (bd & BDB) && mwh >= 6; /* possibly ignore boldness */ ++ int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s; ++ int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2); ++ /* the s-by-s square (x + w2, y + h2, s, s) is the center texel. */ ++ /* The base length (per direction till edge) includes this square. */ ++ ++ int light = bd & (LL | LU | LR | LD); ++ int double_ = bd & (DL | DU | DR | DD); ++ ++ if (light) { ++ /* d: additional (negative) length to not-draw the center */ ++ /* texel - at arcs and avoid drawing inside (some) doubles */ ++ int arc = bd & BDA; ++ int multi_light = light & (light - 1); ++ int multi_double = double_ & (double_ - 1); ++ /* light crosses double only at DH+LV, DV+LH (ref. shapes) */ ++ int d = arc || (multi_double && !multi_light) ? -s : 0; ++ ++ if (bd & LL) ++ XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s); ++ if (bd & LU) ++ XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d); ++ if (bd & LR) ++ XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s); ++ if (bd & LD) ++ XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d); ++ } ++ ++ /* double lines - also align with light to form heavy when combined */ ++ if (double_) { ++ /* ++ * going clockwise, for each double-ray: p is additional length ++ * to the single-ray nearer to the previous direction, and n to ++ * the next. p and n adjust from the base length to lengths ++ * which consider other doubles - shorter to avoid intersections ++ * (p, n), or longer to draw the far-corner texel (n). ++ */ ++ int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD; ++ if (dl) { ++ int p = dd ? -s : 0, n = du ? -s : dd ? s : 0; ++ XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s); ++ XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s); ++ } ++ if (du) { ++ int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0; ++ XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p); ++ XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n); ++ } ++ if (dr) { ++ int p = du ? -s : 0, n = dd ? -s : du ? s : 0; ++ XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s); ++ XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s); ++ } ++ if (dd) { ++ int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0; ++ XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p); ++ XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n); ++ } ++ } ++} +diff --git a/boxdraw_data.h b/boxdraw_data.h +new file mode 100644 +index 0000000..7890500 +--- /dev/null ++++ b/boxdraw_data.h +@@ -0,0 +1,214 @@ ++/* ++ * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih ++ * MIT/X Consortium License ++ */ ++ ++/* ++ * U+25XX codepoints data ++ * ++ * References: ++ * http://www.unicode.org/charts/PDF/U2500.pdf ++ * http://www.unicode.org/charts/PDF/U2580.pdf ++ * ++ * Test page: ++ * https://github.com/GNOME/vte/blob/master/doc/boxes.txt ++ */ ++ ++/* Each shape is encoded as 16-bits. Higher bits are category, lower are data */ ++/* Categories (mutually exclusive except BDB): */ ++/* For convenience, BDL/BDA/BBS/BDB are 1 bit each, the rest are enums */ ++#define BDL (1<<8) /* Box Draw Lines (light/double/heavy) */ ++#define BDA (1<<9) /* Box Draw Arc (light) */ ++ ++#define BBD (1<<10) /* Box Block Down (lower) X/8 */ ++#define BBL (2<<10) /* Box Block Left X/8 */ ++#define BBU (3<<10) /* Box Block Upper X/8 */ ++#define BBR (4<<10) /* Box Block Right X/8 */ ++#define BBQ (5<<10) /* Box Block Quadrants */ ++#define BRL (6<<10) /* Box Braille (data is lower byte of U28XX) */ ++ ++#define BBS (1<<14) /* Box Block Shades */ ++#define BDB (1<<15) /* Box Draw is Bold */ ++ ++/* (BDL/BDA) Light/Double/Heavy x Left/Up/Right/Down/Horizontal/Vertical */ ++/* Heavy is light+double (literally drawing light+double align to form heavy) */ ++#define LL (1<<0) ++#define LU (1<<1) ++#define LR (1<<2) ++#define LD (1<<3) ++#define LH (LL+LR) ++#define LV (LU+LD) ++ ++#define DL (1<<4) ++#define DU (1<<5) ++#define DR (1<<6) ++#define DD (1<<7) ++#define DH (DL+DR) ++#define DV (DU+DD) ++ ++#define HL (LL+DL) ++#define HU (LU+DU) ++#define HR (LR+DR) ++#define HD (LD+DD) ++#define HH (HL+HR) ++#define HV (HU+HD) ++ ++/* (BBQ) Quadrants Top/Bottom x Left/Right */ ++#define TL (1<<0) ++#define TR (1<<1) ++#define BL (1<<2) ++#define BR (1<<3) ++ ++/* Data for U+2500 - U+259F except dashes/diagonals */ ++static const unsigned short boxdata[256] = { ++ /* light lines */ ++ [0x00] = BDL + LH, /* light horizontal */ ++ [0x02] = BDL + LV, /* light vertical */ ++ [0x0c] = BDL + LD + LR, /* light down and right */ ++ [0x10] = BDL + LD + LL, /* light down and left */ ++ [0x14] = BDL + LU + LR, /* light up and right */ ++ [0x18] = BDL + LU + LL, /* light up and left */ ++ [0x1c] = BDL + LV + LR, /* light vertical and right */ ++ [0x24] = BDL + LV + LL, /* light vertical and left */ ++ [0x2c] = BDL + LH + LD, /* light horizontal and down */ ++ [0x34] = BDL + LH + LU, /* light horizontal and up */ ++ [0x3c] = BDL + LV + LH, /* light vertical and horizontal */ ++ [0x74] = BDL + LL, /* light left */ ++ [0x75] = BDL + LU, /* light up */ ++ [0x76] = BDL + LR, /* light right */ ++ [0x77] = BDL + LD, /* light down */ ++ ++ /* heavy [+light] lines */ ++ [0x01] = BDL + HH, ++ [0x03] = BDL + HV, ++ [0x0d] = BDL + HR + LD, ++ [0x0e] = BDL + HD + LR, ++ [0x0f] = BDL + HD + HR, ++ [0x11] = BDL + HL + LD, ++ [0x12] = BDL + HD + LL, ++ [0x13] = BDL + HD + HL, ++ [0x15] = BDL + HR + LU, ++ [0x16] = BDL + HU + LR, ++ [0x17] = BDL + HU + HR, ++ [0x19] = BDL + HL + LU, ++ [0x1a] = BDL + HU + LL, ++ [0x1b] = BDL + HU + HL, ++ [0x1d] = BDL + HR + LV, ++ [0x1e] = BDL + HU + LD + LR, ++ [0x1f] = BDL + HD + LR + LU, ++ [0x20] = BDL + HV + LR, ++ [0x21] = BDL + HU + HR + LD, ++ [0x22] = BDL + HD + HR + LU, ++ [0x23] = BDL + HV + HR, ++ [0x25] = BDL + HL + LV, ++ [0x26] = BDL + HU + LD + LL, ++ [0x27] = BDL + HD + LU + LL, ++ [0x28] = BDL + HV + LL, ++ [0x29] = BDL + HU + HL + LD, ++ [0x2a] = BDL + HD + HL + LU, ++ [0x2b] = BDL + HV + HL, ++ [0x2d] = BDL + HL + LD + LR, ++ [0x2e] = BDL + HR + LL + LD, ++ [0x2f] = BDL + HH + LD, ++ [0x30] = BDL + HD + LH, ++ [0x31] = BDL + HD + HL + LR, ++ [0x32] = BDL + HR + HD + LL, ++ [0x33] = BDL + HH + HD, ++ [0x35] = BDL + HL + LU + LR, ++ [0x36] = BDL + HR + LU + LL, ++ [0x37] = BDL + HH + LU, ++ [0x38] = BDL + HU + LH, ++ [0x39] = BDL + HU + HL + LR, ++ [0x3a] = BDL + HU + HR + LL, ++ [0x3b] = BDL + HH + HU, ++ [0x3d] = BDL + HL + LV + LR, ++ [0x3e] = BDL + HR + LV + LL, ++ [0x3f] = BDL + HH + LV, ++ [0x40] = BDL + HU + LH + LD, ++ [0x41] = BDL + HD + LH + LU, ++ [0x42] = BDL + HV + LH, ++ [0x43] = BDL + HU + HL + LD + LR, ++ [0x44] = BDL + HU + HR + LD + LL, ++ [0x45] = BDL + HD + HL + LU + LR, ++ [0x46] = BDL + HD + HR + LU + LL, ++ [0x47] = BDL + HH + HU + LD, ++ [0x48] = BDL + HH + HD + LU, ++ [0x49] = BDL + HV + HL + LR, ++ [0x4a] = BDL + HV + HR + LL, ++ [0x4b] = BDL + HV + HH, ++ [0x78] = BDL + HL, ++ [0x79] = BDL + HU, ++ [0x7a] = BDL + HR, ++ [0x7b] = BDL + HD, ++ [0x7c] = BDL + HR + LL, ++ [0x7d] = BDL + HD + LU, ++ [0x7e] = BDL + HL + LR, ++ [0x7f] = BDL + HU + LD, ++ ++ /* double [+light] lines */ ++ [0x50] = BDL + DH, ++ [0x51] = BDL + DV, ++ [0x52] = BDL + DR + LD, ++ [0x53] = BDL + DD + LR, ++ [0x54] = BDL + DR + DD, ++ [0x55] = BDL + DL + LD, ++ [0x56] = BDL + DD + LL, ++ [0x57] = BDL + DL + DD, ++ [0x58] = BDL + DR + LU, ++ [0x59] = BDL + DU + LR, ++ [0x5a] = BDL + DU + DR, ++ [0x5b] = BDL + DL + LU, ++ [0x5c] = BDL + DU + LL, ++ [0x5d] = BDL + DL + DU, ++ [0x5e] = BDL + DR + LV, ++ [0x5f] = BDL + DV + LR, ++ [0x60] = BDL + DV + DR, ++ [0x61] = BDL + DL + LV, ++ [0x62] = BDL + DV + LL, ++ [0x63] = BDL + DV + DL, ++ [0x64] = BDL + DH + LD, ++ [0x65] = BDL + DD + LH, ++ [0x66] = BDL + DD + DH, ++ [0x67] = BDL + DH + LU, ++ [0x68] = BDL + DU + LH, ++ [0x69] = BDL + DH + DU, ++ [0x6a] = BDL + DH + LV, ++ [0x6b] = BDL + DV + LH, ++ [0x6c] = BDL + DH + DV, ++ ++ /* (light) arcs */ ++ [0x6d] = BDA + LD + LR, ++ [0x6e] = BDA + LD + LL, ++ [0x6f] = BDA + LU + LL, ++ [0x70] = BDA + LU + LR, ++ ++ /* Lower (Down) X/8 block (data is 8 - X) */ ++ [0x81] = BBD + 7, [0x82] = BBD + 6, [0x83] = BBD + 5, [0x84] = BBD + 4, ++ [0x85] = BBD + 3, [0x86] = BBD + 2, [0x87] = BBD + 1, [0x88] = BBD + 0, ++ ++ /* Left X/8 block (data is X) */ ++ [0x89] = BBL + 7, [0x8a] = BBL + 6, [0x8b] = BBL + 5, [0x8c] = BBL + 4, ++ [0x8d] = BBL + 3, [0x8e] = BBL + 2, [0x8f] = BBL + 1, ++ ++ /* upper 1/2 (4/8), 1/8 block (X), right 1/2, 1/8 block (8-X) */ ++ [0x80] = BBU + 4, [0x94] = BBU + 1, ++ [0x90] = BBR + 4, [0x95] = BBR + 7, ++ ++ /* Quadrants */ ++ [0x96] = BBQ + BL, ++ [0x97] = BBQ + BR, ++ [0x98] = BBQ + TL, ++ [0x99] = BBQ + TL + BL + BR, ++ [0x9a] = BBQ + TL + BR, ++ [0x9b] = BBQ + TL + TR + BL, ++ [0x9c] = BBQ + TL + TR + BR, ++ [0x9d] = BBQ + TR, ++ [0x9e] = BBQ + BL + TR, ++ [0x9f] = BBQ + BL + TR + BR, ++ ++ /* Shades, data is an alpha value in 25% units (1/4, 1/2, 3/4) */ ++ [0x91] = BBS + 1, [0x92] = BBS + 2, [0x93] = BBS + 3, ++ ++ /* U+2504 - U+250B, U+254C - U+254F: unsupported (dashes) */ ++ /* U+2571 - U+2573: unsupported (diagonals) */ ++}; +diff --git a/config.def.h b/config.def.h +index 91ab8ca..7bb3ff7 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -67,6 +67,18 @@ static unsigned int blinktimeout = 800; + */ + static unsigned int cursorthickness = 2; + ++/* ++ * 1: render most of the lines/blocks characters without using the font for ++ * perfect alignment between cells (U2500 - U259F except dashes/diagonals). ++ * Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored. ++ * 0: disable (render all U25XX glyphs normally from the font). ++ */ ++const int boxdraw = 0; ++const int boxdraw_bold = 0; ++ ++/* braille (U28XX): 1: render as adjacent "pixels", 0: use font */ ++const int boxdraw_braille = 0; ++ + /* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it +diff --git a/st.c b/st.c +index f43cfd3..baa2bed 100644 +--- a/st.c ++++ b/st.c +@@ -1214,6 +1214,9 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) + term.dirty[y] = 1; + term.line[y][x] = *attr; + term.line[y][x].u = u; ++ ++ if (isboxdraw(u)) ++ term.line[y][x].mode |= ATTR_BOXDRAW; + } + + void +diff --git a/st.h b/st.h +index 519b9bd..07a7c66 100644 +--- a/st.h ++++ b/st.h +@@ -33,6 +33,7 @@ enum glyph_attribute { + ATTR_WRAP = 1 << 8, + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, ++ ATTR_BOXDRAW = 1 << 11, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, + }; + +@@ -113,6 +114,14 @@ char *xstrdup(const char *); + + int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b); + ++int isboxdraw(Rune); ++ushort boxdrawindex(const Glyph *); ++#ifdef XFT_VERSION ++/* only exposed to x.c, otherwise we'll need Xft.h for the types */ ++void boxdraw_xinit(Display *, Colormap, XftDraw *, Visual *); ++void drawboxes(int, int, int, int, XftColor *, XftColor *, const XftGlyphFontSpec *, int); ++#endif ++ + /* config.h globals */ + extern char *utmp; + extern char *scroll; +@@ -126,3 +135,4 @@ extern unsigned int tabspaces; + extern unsigned int defaultfg; + extern unsigned int defaultbg; + extern unsigned int defaultcs; ++extern const int boxdraw, boxdraw_bold, boxdraw_braille; +diff --git a/x.c b/x.c +index 2a3bd38..bf6bbf9 100644 +--- a/x.c ++++ b/x.c +@@ -1237,6 +1237,8 @@ xinit(int cols, int rows) + xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); + if (xsel.xtarget == None) + xsel.xtarget = XA_STRING; ++ ++ boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); + } + + int +@@ -1283,8 +1285,13 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x + yp = winy + font->ascent; + } + +- /* Lookup character index with default font. */ +- glyphidx = XftCharIndex(xw.dpy, font->match, rune); ++ if (mode & ATTR_BOXDRAW) { ++ /* minor shoehorning: boxdraw uses only this ushort */ ++ glyphidx = boxdrawindex(&glyphs[i]); ++ } else { ++ /* Lookup character index with default font. */ ++ glyphidx = XftCharIndex(xw.dpy, font->match, rune); ++ } + if (glyphidx) { + specs[numspecs].font = font->match; + specs[numspecs].glyph = glyphidx; +@@ -1488,8 +1495,12 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i + r.width = width; + XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + +- /* Render the glyphs. */ +- XftDrawGlyphFontSpec(xw.draw, fg, specs, len); ++ if (base.mode & ATTR_BOXDRAW) { ++ drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); ++ } else { ++ /* Render the glyphs. */ ++ XftDrawGlyphFontSpec(xw.draw, fg, specs, len); ++ } + + /* Render underline and strikethrough. */ + if (base.mode & ATTR_UNDERLINE) { +@@ -1532,7 +1543,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) + /* + * Select the right color for the right mode. + */ +- g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; ++ g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW; + + if (IS_SET(MODE_REVERSE)) { + g.mode |= ATTR_REVERSE; +-- +2.35.1 + diff --git a/st/st.c b/st/st.c index cd750f20..512d8099 100644 --- a/st/st.c +++ b/st/st.c @@ -1284,6 +1284,9 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) term.dirty[y] = 1; term.line[y][x] = *attr; term.line[y][x].u = u; + + if (isboxdraw(u)) + term.line[y][x].mode |= ATTR_BOXDRAW; } void diff --git a/st/st.c.orig b/st/st.c.orig new file mode 100644 index 00000000..cd750f20 --- /dev/null +++ b/st/st.c.orig @@ -0,0 +1,2761 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st.h" +#include "win.h" + +#if defined(__linux) + #include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + #include +#elif defined(__FreeBSD__) || defined(__DragonFly__) + #include +#endif + +/* Arbitrary sizes */ +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 +#define ESC_BUF_SIZ (128*UTF_SIZ) +#define ESC_ARG_SIZ 16 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ +#define HISTSIZE 2000 + +/* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ + term.scr + HISTSIZE + 1) % HISTSIZE] : \ + term.line[(y) - term.scr]) + +enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_ALTSCREEN = 1 << 2, + MODE_CRLF = 1 << 3, + MODE_ECHO = 1 << 4, + MODE_PRINT = 1 << 5, + MODE_UTF8 = 1 << 6, +}; + +enum cursor_movement { + CURSOR_SAVE, + CURSOR_LOAD +}; + +enum cursor_state { + CURSOR_DEFAULT = 0, + CURSOR_WRAPNEXT = 1, + CURSOR_ORIGIN = 2 +}; + +enum charset { + CS_GRAPHIC0, + CS_GRAPHIC1, + CS_UK, + CS_USA, + CS_MULTI, + CS_GER, + CS_FIN +}; + +enum escape_state { + ESC_START = 1, + ESC_CSI = 2, + ESC_STR = 4, /* DCS, OSC, PM, APC */ + ESC_ALTCHARSET = 8, + ESC_STR_END = 16, /* a final string was encountered */ + ESC_TEST = 32, /* Enter in test mode */ + ESC_UTF8 = 64, +}; + +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +typedef struct { + int mode; + int type; + int snap; + /* + * Selection variables: + * nb – normalized coordinates of the beginning of the selection + * ne – normalized coordinates of the end of the selection + * ob – original coordinates of the beginning of the selection + * oe – original coordinates of the end of the selection + */ + struct { + int x, y; + } nb, ne, ob, oe; + + int alt; +} Selection; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ + Line hist[HISTSIZE]; /* history buffer */ + int histi; /* history index */ + int scr; /* scroll back */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int *tabs; + Rune lastc; /* last printed char outside of sequence, 0 if control */ +} Term; + +/* CSI Escape sequence structs */ +/* ESC '[' [[ [] [;]] []] */ +typedef struct { + char buf[ESC_BUF_SIZ]; /* raw string */ + size_t len; /* raw string length */ + char priv; + int arg[ESC_ARG_SIZ]; + int narg; /* nb of args */ + char mode[2]; +} CSIEscape; + +/* STR Escape sequence structs */ +/* ESC type [[ [] [;]] ] ESC '\' */ +typedef struct { + char type; /* ESC type ... */ + char *buf; /* allocated raw string */ + size_t siz; /* allocation size */ + size_t len; /* raw string length */ + char *args[STR_ARG_SIZ]; + int narg; /* nb of args */ +} STREscape; + +static void execsh(char *, char **); +static void stty(char **); +static void sigchld(int); +static void ttywriteraw(const char *, size_t); + +static void csidump(void); +static void csihandle(void); +static void csiparse(void); +static void csireset(void); +static int eschandle(uchar); +static void strdump(void); +static void strhandle(void); +static void strparse(void); +static void strreset(void); + +static void tprinter(char *, size_t); +static void tdumpsel(void); +static void tdumpline(int); +static void tdump(void); +static void tclearregion(int, int, int, int); +static void tcursor(int); +static void tdeletechar(int); +static void tdeleteline(int); +static void tinsertblank(int); +static void tinsertblankline(int); +static int tlinelen(int); +static void tmoveto(int, int); +static void tmoveato(int, int); +static void tnewline(int); +static void tputtab(int); +static void tputc(Rune); +static void treset(void); +static void tscrollup(int, int, int); +static void tscrolldown(int, int, int); +static void tsetattr(const int *, int); +static void tsetchar(Rune, const Glyph *, int, int); +static void tsetdirt(int, int); +static void tsetscroll(int, int); +static void tswapscreen(void); +static void tsetmode(int, int, const int *, int); +static int twrite(const char *, int, int); +static void tfulldirt(void); +static void tcontrolcode(uchar ); +static void tdectest(char ); +static void tdefutf8(char); +static int32_t tdefcolor(const int *, int *, int); +static void tdeftran(char); +static void tstrsequence(uchar); + +static void drawregion(int, int, int, int); + +static void selnormalize(void); +static void selscroll(int, int); +static void selsnap(int *, int *, int); + +static size_t utf8decode(const char *, Rune *, size_t); +static Rune utf8decodebyte(char, size_t *); +static char utf8encodebyte(Rune, size_t); +static size_t utf8validate(Rune *, size_t); + +static char *base64dec(const char *); +static char base64dec_getc(const char **); + +static ssize_t xwrite(int, const char *, size_t); + +/* Globals */ +static Term term; +static Selection sel; +static CSIEscape csiescseq; +static STREscape strescseq; +static int iofd = 1; +static int cmdfd; +static pid_t pid; + +static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +ssize_t +xwrite(int fd, const char *s, size_t len) +{ + size_t aux = len; + ssize_t r; + + while (len > 0) { + r = write(fd, s, len); + if (r < 0) + return r; + len -= r; + s += r; + } + + return aux; +} + +void * +xmalloc(size_t len) +{ + void *p; + + if (!(p = malloc(len))) + die("malloc: %s\n", strerror(errno)); + + return p; +} + +void * +xrealloc(void *p, size_t len) +{ + if ((p = realloc(p, len)) == NULL) + die("realloc: %s\n", strerror(errno)); + + return p; +} + +char * +xstrdup(const char *s) +{ + char *p; + + if ((p = strdup(s)) == NULL) + die("strdup: %s\n", strerror(errno)); + + return p; +} + +size_t +utf8decode(const char *c, Rune *u, size_t clen) +{ + size_t i, j, len, type; + Rune udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type != 0) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Rune +utf8decodebyte(char c, size_t *i) +{ + for (*i = 0; *i < LEN(utfmask); ++(*i)) + if (((uchar)c & utfmask[*i]) == utfbyte[*i]) + return (uchar)c & ~utfmask[*i]; + + return 0; +} + +size_t +utf8encode(Rune u, char *c) +{ + size_t len, i; + + len = utf8validate(&u, 0); + if (len > UTF_SIZ) + return 0; + + for (i = len - 1; i != 0; --i) { + c[i] = utf8encodebyte(u, 0); + u >>= 6; + } + c[0] = utf8encodebyte(u, len); + + return len; +} + +char +utf8encodebyte(Rune u, size_t i) +{ + return utfbyte[i] | (u & ~utfmask[i]); +} + +size_t +utf8validate(Rune *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + + return i; +} + +static const char base64_digits[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +char +base64dec_getc(const char **src) +{ + while (**src && !isprint(**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char * +base64dec(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + + if (in_len % 4) + in_len += 4 - (in_len % 4); + result = dst = xmalloc(in_len / 4 * 3 + 1); + while (*src) { + int a = base64_digits[(unsigned char) base64dec_getc(&src)]; + int b = base64_digits[(unsigned char) base64dec_getc(&src)]; + int c = base64_digits[(unsigned char) base64dec_getc(&src)]; + int d = base64_digits[(unsigned char) base64dec_getc(&src)]; + + /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ + if (a == -1 || b == -1) + break; + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (c == -1) + break; + *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == -1) + break; + *dst++ = ((c & 0x03) << 6) | d; + } + *dst = '\0'; + return result; +} + +void +selinit(void) +{ + sel.mode = SEL_IDLE; + sel.snap = 0; + sel.ob.x = -1; +} + +int +tlinelen(int y) +{ + int i = term.col; + + if (TLINE(y)[i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && TLINE(y)[i - 1].u == ' ') + --i; + + return i; +} + +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type, int done) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + + if (sel.mode == SEL_IDLE) + return; + if (done && sel.mode == SEL_EMPTY) { + selclear(); + return; + } + + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + + sel.mode = done ? SEL_IDLE : SEL_READY; +} + +void +selnormalize(void) +{ + int i; + + if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { + sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; + sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; + } else { + sel.nb.x = MIN(sel.ob.x, sel.oe.x); + sel.ne.x = MAX(sel.ob.x, sel.oe.x); + } + sel.nb.y = MIN(sel.ob.y, sel.oe.y); + sel.ne.y = MAX(sel.ob.y, sel.oe.y); + + selsnap(&sel.nb.x, &sel.nb.y, -1); + selsnap(&sel.ne.x, &sel.ne.y, +1); + + /* expand selection over line breaks */ + if (sel.type == SEL_RECTANGULAR) + return; + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) + sel.nb.x = i; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; +} + +int +selected(int x, int y) +{ + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) + return 0; + + if (sel.type == SEL_RECTANGULAR) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); + + return BETWEEN(y, sel.nb.y, sel.ne.y) + && (y != sel.nb.y || x >= sel.nb.x) + && (y != sel.ne.y || x <= sel.ne.x); +} + +void +selsnap(int *x, int *y, int direction) +{ + int newx, newy, xt, yt; + int delim, prevdelim; + const Glyph *gp, *prevgp; + + switch (sel.snap) { + case SNAP_WORD: + /* + * Snap around if the word wraps around at the end or + * beginning of a line. + */ + prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; + newy = *y; + if (!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) + break; + + if (direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + + gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) + break; + + *x = newx; + *y = newy; + prevgp = gp; + prevdelim = delim; + } + break; + case SNAP_LINE: + /* + * Snap around if the the previous line or the current one + * has set ATTR_WRAP at its end. Then the whole next or + * previous line will be selected. + */ + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { + if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { + if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } + break; + } +} + +char * +getsel(void) +{ + char *str, *ptr; + int y, bufsize, lastx, linelen; + const Glyph *gp, *last; + + if (sel.ob.x == -1) + return NULL; + + bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; + ptr = str = xmalloc(bufsize); + + /* append every set & selected glyph to the selection */ + for (y = sel.nb.y; y <= sel.ne.y; y++) { + if ((linelen = tlinelen(y)) == 0) { + *ptr++ = '\n'; + continue; + } + + if (sel.type == SEL_RECTANGULAR) { + gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + + for ( ; gp <= last; ++gp) { + if (gp->mode & ATTR_WDUMMY) + continue; + + ptr += utf8encode(gp->u, ptr); + } + + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if ((y < sel.ne.y || lastx >= linelen) && + (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) + *ptr++ = '\n'; + } + *ptr = 0; + return str; +} + +void +selclear(void) +{ + if (sel.ob.x == -1) + return; + sel.mode = SEL_IDLE; + sel.ob.x = -1; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void +execsh(char *cmd, char **args) +{ + char *sh, *prog, *arg; + const struct passwd *pw; + + errno = 0; + if ((pw = getpwuid(getuid())) == NULL) { + if (errno) + die("getpwuid: %s\n", strerror(errno)); + else + die("who are you?\n"); + } + + if ((sh = getenv("SHELL")) == NULL) + sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; + + if (args) { + prog = args[0]; + arg = NULL; + } else if (scroll) { + prog = scroll; + arg = utmp ? utmp : sh; + } else if (utmp) { + prog = utmp; + arg = NULL; + } else { + prog = sh; + arg = NULL; + } + DEFAULT(args, ((char *[]) {prog, arg, NULL})); + + unsetenv("COLUMNS"); + unsetenv("LINES"); + unsetenv("TERMCAP"); + setenv("LOGNAME", pw->pw_name, 1); + setenv("USER", pw->pw_name, 1); + setenv("SHELL", sh, 1); + setenv("HOME", pw->pw_dir, 1); + setenv("TERM", termname, 1); + + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + execvp(prog, args); + _exit(1); +} + +void +sigchld(int a) +{ + int stat; + pid_t p; + + if ((p = waitpid(pid, &stat, WNOHANG)) < 0) + die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); + + if (pid != p) + return; + + if (WIFEXITED(stat) && WEXITSTATUS(stat)) + die("child exited with status %d\n", WEXITSTATUS(stat)); + else if (WIFSIGNALED(stat)) + die("child terminated due to signal %d\n", WTERMSIG(stat)); + _exit(0); +} + +void +stty(char **args) +{ + char cmd[_POSIX_ARG_MAX], **p, *q, *s; + size_t n, siz; + + if ((n = strlen(stty_args)) > sizeof(cmd)-1) + die("incorrect stty parameters\n"); + memcpy(cmd, stty_args, n); + q = cmd + n; + siz = sizeof(cmd) - n; + for (p = args; p && (s = *p); ++p) { + if ((n = strlen(s)) > siz-1) + die("stty parameter length too long\n"); + *q++ = ' '; + memcpy(q, s, n); + q += n; + siz -= n + 1; + } + *q = '\0'; + if (system(cmd) != 0) + perror("Couldn't call stty"); +} + +int +ttynew(const char *line, char *cmd, const char *out, char **args) +{ + int m, s; + + if (out) { + term.mode |= MODE_PRINT; + iofd = (!strcmp(out, "-")) ? + 1 : open(out, O_WRONLY | O_CREAT, 0666); + if (iofd < 0) { + fprintf(stderr, "Error opening %s:%s\n", + out, strerror(errno)); + } + } + + if (line) { + if ((cmdfd = open(line, O_RDWR)) < 0) + die("open line '%s' failed: %s\n", + line, strerror(errno)); + dup2(cmdfd, 0); + stty(args); + return cmdfd; + } + + /* seems to work fine on linux, openbsd and freebsd */ + if (openpty(&m, &s, NULL, NULL, NULL) < 0) + die("openpty failed: %s\n", strerror(errno)); + + switch (pid = fork()) { + case -1: + die("fork failed: %s\n", strerror(errno)); + break; + case 0: + close(iofd); + close(m); + setsid(); /* create a new process group */ + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + if (ioctl(s, TIOCSCTTY, NULL) < 0) + die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); + if (s > 2) + close(s); +#ifdef __OpenBSD__ + if (pledge("stdio getpw proc exec", NULL) == -1) + die("pledge\n"); +#endif + execsh(cmd, args); + break; + default: +#ifdef __OpenBSD__ + if (pledge("stdio rpath tty proc", NULL) == -1) + die("pledge\n"); +#endif + close(s); + cmdfd = m; + signal(SIGCHLD, sigchld); + break; + } + return cmdfd; +} + +size_t +ttyread(void) +{ + static char buf[BUFSIZ]; + static int buflen = 0; + int ret, written; + + /* append read bytes to unprocessed bytes */ + ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); + + switch (ret) { + case 0: + exit(0); + case -1: + die("couldn't read from shell: %s\n", strerror(errno)); + default: + buflen += ret; + written = twrite(buf, buflen, 0); + buflen -= written; + /* keep any incomplete UTF-8 byte sequence for the next call */ + if (buflen > 0) + memmove(buf, buf + written, buflen); + return ret; + } +} + +void +ttywrite(const char *s, size_t n, int may_echo) +{ + const char *next; + Arg arg = (Arg) { .i = term.scr }; + + kscrolldown(&arg); + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); + + if (!IS_SET(MODE_CRLF)) { + ttywriteraw(s, n); + return; + } + + /* This is similar to how the kernel handles ONLCR for ttys */ + while (n > 0) { + if (*s == '\r') { + next = s + 1; + ttywriteraw("\r\n", 2); + } else { + next = memchr(s, '\r', n); + DEFAULT(next, s + n); + ttywriteraw(s, next - s); + } + n -= next - s; + s = next; + } +} + +void +ttywriteraw(const char *s, size_t n) +{ + fd_set wfd, rfd; + ssize_t r; + size_t lim = 256; + + /* + * Remember that we are using a pty, which might be a modem line. + * Writing too much will clog the line. That's why we are doing this + * dance. + * FIXME: Migrate the world to Plan 9. + */ + while (n > 0) { + FD_ZERO(&wfd); + FD_ZERO(&rfd); + FD_SET(cmdfd, &wfd); + FD_SET(cmdfd, &rfd); + + /* Check if we can write. */ + if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(cmdfd, &wfd)) { + /* + * Only write the bytes written by ttywrite() or the + * default of 256. This seems to be a reasonable value + * for a serial line. Bigger values might clog the I/O. + */ + if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) + goto write_error; + if (r < n) { + /* + * We weren't able to write out everything. + * This means the buffer is getting full + * again. Empty it. + */ + if (n < lim) + lim = ttyread(); + n -= r; + s += r; + } else { + /* All bytes have been written. */ + break; + } + } + if (FD_ISSET(cmdfd, &rfd)) + lim = ttyread(); + } + return; + +write_error: + die("write error on tty: %s\n", strerror(errno)); +} + +void +ttyresize(int tw, int th) +{ + struct winsize w; + + w.ws_row = term.row; + w.ws_col = term.col; + w.ws_xpixel = tw; + w.ws_ypixel = th; + if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) + fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); +} + +void +ttyhangup() +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + +int +tattrset(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) + return 1; + } + } + + return 0; +} + +void +tsetdirt(int top, int bot) +{ + int i; + + LIMIT(top, 0, term.row-1); + LIMIT(bot, 0, term.row-1); + + for (i = top; i <= bot; i++) + term.dirty[i] = 1; +} + +void +tsetdirtattr(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) { + tsetdirt(i, i); + break; + } + } + } +} + +void +tfulldirt(void) +{ + tsetdirt(0, term.row-1); +} + +void +tcursor(int mode) +{ + static TCursor c[2]; + int alt = IS_SET(MODE_ALTSCREEN); + + if (mode == CURSOR_SAVE) { + c[alt] = term.c; + } else if (mode == CURSOR_LOAD) { + term.c = c[alt]; + tmoveto(c[alt].x, c[alt].y); + } +} + +void +treset(void) +{ + uint i; + + term.c = (TCursor){{ + .mode = ATTR_NULL, + .fg = defaultfg, + .bg = defaultbg + }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) + term.tabs[i] = 1; + term.top = 0; + term.bot = term.row - 1; + term.mode = MODE_WRAP|MODE_UTF8; + memset(term.trantbl, CS_USA, sizeof(term.trantbl)); + term.charset = 0; + + for (i = 0; i < 2; i++) { + tmoveto(0, 0); + tcursor(CURSOR_SAVE); + tclearregion(0, 0, term.col-1, term.row-1); + tswapscreen(); + } +} + +void +tnew(int col, int row) +{ + term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; + tresize(col, row); + treset(); +} + +void +tswapscreen(void) +{ + Line *tmp = term.line; + + term.line = term.alt; + term.alt = tmp; + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); +} + +void +kscrolldown(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (n > term.scr) + n = term.scr; + + if (term.scr > 0) { + term.scr -= n; + selscroll(0, -n); + tfulldirt(); + } +} + +void +kscrollup(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (term.scr <= HISTSIZE-n) { + term.scr += n; + selscroll(0, n); + tfulldirt(); + } +} + +void +tscrolldown(int orig, int n, int copyhist) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + if (copyhist) { + term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[term.bot]; + term.line[term.bot] = temp; + } + + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + + for (i = term.bot; i >= orig+n; i--) { + temp = term.line[i]; + term.line[i] = term.line[i-n]; + term.line[i-n] = temp; + } + + if (term.scr == 0) + selscroll(orig, n); +} + +void +tscrollup(int orig, int n, int copyhist) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + if (copyhist) { + term.histi = (term.histi + 1) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[orig]; + term.line[orig] = temp; + } + + if (term.scr > 0 && term.scr < HISTSIZE) + term.scr = MIN(term.scr + n, HISTSIZE-1); + + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + + for (i = orig; i <= term.bot-n; i++) { + temp = term.line[i]; + term.line[i] = term.line[i+n]; + term.line[i+n] = temp; + } + + if (term.scr == 0) + selscroll(orig, -n); +} + +void +selscroll(int orig, int n) +{ + if (sel.ob.x == -1) + return; + + if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { + selclear(); + } else if (BETWEEN(sel.nb.y, orig, term.bot)) { + sel.ob.y += n; + sel.oe.y += n; + if (sel.ob.y < term.top || sel.ob.y > term.bot || + sel.oe.y < term.top || sel.oe.y > term.bot) { + selclear(); + } else { + selnormalize(); + } + } +} + +void +tnewline(int first_col) +{ + int y = term.c.y; + + if (y == term.bot) { + tscrollup(term.top, 1, 1); + } else { + y++; + } + tmoveto(first_col ? 0 : term.c.x, y); +} + +void +csiparse(void) +{ + char *p = csiescseq.buf, *np; + long int v; + + csiescseq.narg = 0; + if (*p == '?') { + csiescseq.priv = 1; + p++; + } + + csiescseq.buf[csiescseq.len] = '\0'; + while (p < csiescseq.buf+csiescseq.len) { + np = NULL; + v = strtol(p, &np, 10); + if (np == p) + v = 0; + if (v == LONG_MAX || v == LONG_MIN) + v = -1; + csiescseq.arg[csiescseq.narg++] = v; + p = np; + if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + break; + p++; + } + csiescseq.mode[0] = *p++; + csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; +} + +/* for absolute user moves, when decom is set */ +void +tmoveato(int x, int y) +{ + tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0)); +} + +void +tmoveto(int x, int y) +{ + int miny, maxy; + + if (term.c.state & CURSOR_ORIGIN) { + miny = term.top; + maxy = term.bot; + } else { + miny = 0; + maxy = term.row - 1; + } + term.c.state &= ~CURSOR_WRAPNEXT; + term.c.x = LIMIT(x, 0, term.col-1); + term.c.y = LIMIT(y, miny, maxy); +} + +void +tsetchar(Rune u, const Glyph *attr, int x, int y) +{ + static const char *vt100_0[62] = { /* 0x41 - 0x7e */ + "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ + 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ + "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ + "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ + }; + + /* + * The table is proudly stolen from rxvt. + */ + if (term.trantbl[term.charset] == CS_GRAPHIC0 && + BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) + utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); + + if (term.line[y][x].mode & ATTR_WIDE) { + if (x+1 < term.col) { + term.line[y][x+1].u = ' '; + term.line[y][x+1].mode &= ~ATTR_WDUMMY; + } + } else if (term.line[y][x].mode & ATTR_WDUMMY) { + term.line[y][x-1].u = ' '; + term.line[y][x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; + term.line[y][x] = *attr; + term.line[y][x].u = u; +} + +void +tclearregion(int x1, int y1, int x2, int y2) +{ + int x, y, temp; + Glyph *gp; + + if (x1 > x2) + temp = x1, x1 = x2, x2 = temp; + if (y1 > y2) + temp = y1, y1 = y2, y2 = temp; + + LIMIT(x1, 0, term.col-1); + LIMIT(x2, 0, term.col-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { + gp = &term.line[y][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + gp->u = ' '; + } + } +} + +void +tdeletechar(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +} + +void +tinsertblank(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +} + +void +tinsertblankline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrolldown(term.c.y, n, 0); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n, 0); +} + +int32_t +tdefcolor(const int *attr, int *npar, int l) +{ + int32_t idx = -1; + uint r, g, b; + + switch (attr[*npar + 1]) { + case 2: /* direct color in RGB space */ + if (*npar + 4 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + r = attr[*npar + 2]; + g = attr[*npar + 3]; + b = attr[*npar + 4]; + *npar += 4; + if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) + fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n", + r, g, b); + else + idx = TRUECOLOR(r, g, b); + break; + case 5: /* indexed color */ + if (*npar + 2 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + *npar += 2; + if (!BETWEEN(attr[*npar], 0, 255)) + fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]); + else + idx = attr[*npar]; + break; + case 0: /* implemented defined (only foreground) */ + case 1: /* transparent */ + case 3: /* direct color in CMY space */ + case 4: /* direct color in CMYK space */ + default: + fprintf(stderr, + "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; + } + + return idx; +} + +void +tsetattr(const int *attr, int l) +{ + int i; + int32_t idx; + + for (i = 0; i < l; i++) { + switch (attr[i]) { + case 0: + term.c.attr.mode &= ~( + ATTR_BOLD | + ATTR_FAINT | + ATTR_ITALIC | + ATTR_UNDERLINE | + ATTR_BLINK | + ATTR_REVERSE | + ATTR_INVISIBLE | + ATTR_STRUCK ); + term.c.attr.fg = defaultfg; + term.c.attr.bg = defaultbg; + break; + case 1: + term.c.attr.mode |= ATTR_BOLD; + break; + case 2: + term.c.attr.mode |= ATTR_FAINT; + break; + case 3: + term.c.attr.mode |= ATTR_ITALIC; + break; + case 4: + term.c.attr.mode |= ATTR_UNDERLINE; + break; + case 5: /* slow blink */ + /* FALLTHROUGH */ + case 6: /* rapid blink */ + term.c.attr.mode |= ATTR_BLINK; + break; + case 7: + term.c.attr.mode |= ATTR_REVERSE; + break; + case 8: + term.c.attr.mode |= ATTR_INVISIBLE; + break; + case 9: + term.c.attr.mode |= ATTR_STRUCK; + break; + case 22: + term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); + break; + case 23: + term.c.attr.mode &= ~ATTR_ITALIC; + break; + case 24: + term.c.attr.mode &= ~ATTR_UNDERLINE; + break; + case 25: + term.c.attr.mode &= ~ATTR_BLINK; + break; + case 27: + term.c.attr.mode &= ~ATTR_REVERSE; + break; + case 28: + term.c.attr.mode &= ~ATTR_INVISIBLE; + break; + case 29: + term.c.attr.mode &= ~ATTR_STRUCK; + break; + case 38: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.fg = idx; + break; + case 39: + term.c.attr.fg = defaultfg; + break; + case 48: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.bg = idx; + break; + case 49: + term.c.attr.bg = defaultbg; + break; + default: + if (BETWEEN(attr[i], 30, 37)) { + term.c.attr.fg = attr[i] - 30; + } else if (BETWEEN(attr[i], 40, 47)) { + term.c.attr.bg = attr[i] - 40; + } else if (BETWEEN(attr[i], 90, 97)) { + term.c.attr.fg = attr[i] - 90 + 8; + } else if (BETWEEN(attr[i], 100, 107)) { + term.c.attr.bg = attr[i] - 100 + 8; + } else { + fprintf(stderr, + "erresc(default): gfx attr %d unknown\n", + attr[i]); + csidump(); + } + break; + } + } +} + +void +tsetscroll(int t, int b) +{ + int temp; + + LIMIT(t, 0, term.row-1); + LIMIT(b, 0, term.row-1); + if (t > b) { + temp = t; + t = b; + b = temp; + } + term.top = t; + term.bot = b; +} + +void +tsetmode(int priv, int set, const int *args, int narg) +{ + int alt; const int *lim; + + for (lim = args + narg; args < lim; ++args) { + if (priv) { + switch (*args) { + case 1: /* DECCKM -- Cursor key */ + xsetmode(set, MODE_APPCURSOR); + break; + case 5: /* DECSCNM -- Reverse video */ + xsetmode(set, MODE_REVERSE); + break; + case 6: /* DECOM -- Origin */ + MODBIT(term.c.state, set, CURSOR_ORIGIN); + tmoveato(0, 0); + break; + case 7: /* DECAWM -- Auto wrap */ + MODBIT(term.mode, set, MODE_WRAP); + break; + case 0: /* Error (IGNORED) */ + case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ + case 3: /* DECCOLM -- Column (IGNORED) */ + case 4: /* DECSCLM -- Scroll (IGNORED) */ + case 8: /* DECARM -- Auto repeat (IGNORED) */ + case 18: /* DECPFF -- Printer feed (IGNORED) */ + case 19: /* DECPEX -- Printer extent (IGNORED) */ + case 42: /* DECNRCM -- National characters (IGNORED) */ + case 12: /* att610 -- Start blinking cursor (IGNORED) */ + break; + case 25: /* DECTCEM -- Text Cursor Enable Mode */ + xsetmode(!set, MODE_HIDE); + break; + case 9: /* X10 mouse compatibility mode */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEX10); + break; + case 1000: /* 1000: report button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEBTN); + break; + case 1002: /* 1002: report motion on button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMOTION); + break; + case 1003: /* 1003: enable all mouse motions */ + xsetpointermotion(set); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMANY); + break; + case 1004: /* 1004: send focus events to tty */ + xsetmode(set, MODE_FOCUS); + break; + case 1006: /* 1006: extended reporting mode */ + xsetmode(set, MODE_MOUSESGR); + break; + case 1034: + xsetmode(set, MODE_8BIT); + break; + case 1049: /* swap screen & set/restore cursor as xterm */ + if (!allowaltscreen) + break; + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + /* FALLTHROUGH */ + case 47: /* swap screen */ + case 1047: + if (!allowaltscreen) + break; + alt = IS_SET(MODE_ALTSCREEN); + if (alt) { + tclearregion(0, 0, term.col-1, + term.row-1); + } + if (set ^ alt) /* set is always 1 or 0 */ + tswapscreen(); + if (*args != 1049) + break; + /* FALLTHROUGH */ + case 1048: + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + break; + case 2004: /* 2004: bracketed paste mode */ + xsetmode(set, MODE_BRCKTPASTE); + break; + /* Not implemented mouse modes. See comments there. */ + case 1001: /* mouse highlight mode; can hang the + terminal by design when implemented. */ + case 1005: /* UTF-8 mouse mode; will confuse + applications not supporting UTF-8 + and luit. */ + case 1015: /* urxvt mangled mouse mode; incompatible + and can be mistaken for other control + codes. */ + break; + default: + fprintf(stderr, + "erresc: unknown private set/reset mode %d\n", + *args); + break; + } + } else { + switch (*args) { + case 0: /* Error (IGNORED) */ + break; + case 2: + xsetmode(set, MODE_KBDLOCK); + break; + case 4: /* IRM -- Insertion-replacement */ + MODBIT(term.mode, set, MODE_INSERT); + break; + case 12: /* SRM -- Send/Receive */ + MODBIT(term.mode, !set, MODE_ECHO); + break; + case 20: /* LNM -- Linefeed/new line */ + MODBIT(term.mode, set, MODE_CRLF); + break; + default: + fprintf(stderr, + "erresc: unknown set/reset mode %d\n", + *args); + break; + } + } + } +} + +void +csihandle(void) +{ + char buf[40]; + int len; + + switch (csiescseq.mode[0]) { + default: + unknown: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* die(""); */ + break; + case '@': /* ICH -- Insert blank char */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblank(csiescseq.arg[0]); + break; + case 'A': /* CUU -- Cursor Up */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); + break; + case 'B': /* CUD -- Cursor Down */ + case 'e': /* VPR --Cursor Down */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); + break; + case 'i': /* MC -- Media Copy */ + switch (csiescseq.arg[0]) { + case 0: + tdump(); + break; + case 1: + tdumpline(term.c.y); + break; + case 2: + tdumpsel(); + break; + case 4: + term.mode &= ~MODE_PRINT; + break; + case 5: + term.mode |= MODE_PRINT; + break; + } + break; + case 'c': /* DA -- Device Attributes */ + if (csiescseq.arg[0] == 0) + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'b': /* REP -- if last char is printable print it more times */ + DEFAULT(csiescseq.arg[0], 1); + if (term.lastc) + while (csiescseq.arg[0]-- > 0) + tputc(term.lastc); + break; + case 'C': /* CUF -- Cursor Forward */ + case 'a': /* HPR -- Cursor Forward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x+csiescseq.arg[0], term.c.y); + break; + case 'D': /* CUB -- Cursor Backward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x-csiescseq.arg[0], term.c.y); + break; + case 'E': /* CNL -- Cursor Down and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y+csiescseq.arg[0]); + break; + case 'F': /* CPL -- Cursor Up and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y-csiescseq.arg[0]); + break; + case 'g': /* TBC -- Tabulation clear */ + switch (csiescseq.arg[0]) { + case 0: /* clear current tab stop */ + term.tabs[term.c.x] = 0; + break; + case 3: /* clear all the tabs */ + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + break; + default: + goto unknown; + } + break; + case 'G': /* CHA -- Move to */ + case '`': /* HPA */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(csiescseq.arg[0]-1, term.c.y); + break; + case 'H': /* CUP -- Move to */ + case 'f': /* HVP */ + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], 1); + tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); + break; + case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(csiescseq.arg[0]); + break; + case 'J': /* ED -- Clear screen */ + switch (csiescseq.arg[0]) { + case 0: /* below */ + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); + if (term.c.y < term.row-1) { + tclearregion(0, term.c.y+1, term.col-1, + term.row-1); + } + break; + case 1: /* above */ + if (term.c.y > 1) + tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, 0, term.col-1, term.row-1); + break; + default: + goto unknown; + } + break; + case 'K': /* EL -- Clear line */ + switch (csiescseq.arg[0]) { + case 0: /* right */ + tclearregion(term.c.x, term.c.y, term.col-1, + term.c.y); + break; + case 1: /* left */ + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, term.c.y, term.col-1, term.c.y); + break; + } + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); + tscrollup(term.top, csiescseq.arg[0], 0); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[0], 0); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblankline(csiescseq.arg[0]); + break; + case 'l': /* RM -- Reset Mode */ + tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + break; + case 'M': /* DL -- Delete lines */ + DEFAULT(csiescseq.arg[0], 1); + tdeleteline(csiescseq.arg[0]); + break; + case 'X': /* ECH -- Erase char */ + DEFAULT(csiescseq.arg[0], 1); + tclearregion(term.c.x, term.c.y, + term.c.x + csiescseq.arg[0] - 1, term.c.y); + break; + case 'P': /* DCH -- Delete char */ + DEFAULT(csiescseq.arg[0], 1); + tdeletechar(csiescseq.arg[0]); + break; + case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(-csiescseq.arg[0]); + break; + case 'd': /* VPA -- Move to */ + DEFAULT(csiescseq.arg[0], 1); + tmoveato(term.c.x, csiescseq.arg[0]-1); + break; + case 'h': /* SM -- Set terminal mode */ + tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); + break; + case 'm': /* SGR -- Terminal attribute (color) */ + tsetattr(csiescseq.arg, csiescseq.narg); + break; + case 'n': /* DSR – Device Status Report (cursor position) */ + if (csiescseq.arg[0] == 6) { + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + } + break; + case 'r': /* DECSTBM -- Set Scrolling Region */ + if (csiescseq.priv) { + goto unknown; + } else { + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], term.row); + tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); + tmoveato(0, 0); + } + break; + case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ + tcursor(CURSOR_SAVE); + break; + case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ + tcursor(CURSOR_LOAD); + break; + case ' ': + switch (csiescseq.mode[1]) { + case 'q': /* DECSCUSR -- Set Cursor Style */ + if (xsetcursor(csiescseq.arg[0])) + goto unknown; + break; + default: + goto unknown; + } + break; + } +} + +void +csidump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC["); + for (i = 0; i < csiescseq.len; i++) { + c = csiescseq.buf[i] & 0xff; + if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + putc('\n', stderr); +} + +void +csireset(void) +{ + memset(&csiescseq, 0, sizeof(csiescseq)); +} + +void +osc4_color_response(int num) +{ + 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); + 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]%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + num, r, r, g, g, b, b); + + ttywrite(buf, n, 1); +} + +void +strhandle(void) +{ + char *p = NULL, *dec; + int j, narg, par; + + term.esc &= ~(ESC_STR_END|ESC_STR); + strparse(); + par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; + + switch (strescseq.type) { + case ']': /* OSC -- Operating System Command */ + switch (par) { + case 0: + if (narg > 1) { + xsettitle(strescseq.args[1]); + xseticontitle(strescseq.args[1]); + } + return; + case 1: + if (narg > 1) + xseticontitle(strescseq.args[1]); + return; + case 2: + if (narg > 1) + xsettitle(strescseq.args[1]); + return; + case 52: + if (narg > 2 && allowwindowops) { + dec = base64dec(strescseq.args[2]); + if (dec) { + xsetsel(dec); + xclipcopy(); + } else { + fprintf(stderr, "erresc: invalid base64\n"); + } + } + return; + case 10: + if (narg < 2) + break; + + p = strescseq.args[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.args[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 (!strcmp(p, "?")) + osc_color_response(defaultcs, 12); + else if (xsetcolorname(defaultcs, p)) + fprintf(stderr, "erresc: invalid cursor color: %s\n", p); + else + redraw(); + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + + if (p && !strcmp(p, "?")) + osc4_color_response(j); + 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 { + /* + * TODO if defaultbg color is changed, borders + * are dirty + */ + redraw(); + } + return; + } + break; + case 'k': /* old title set compatibility */ + xsettitle(strescseq.args[0]); + return; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + return; + } + + fprintf(stderr, "erresc: unknown str "); + strdump(); +} + +void +strparse(void) +{ + int c; + char *p = strescseq.buf; + + strescseq.narg = 0; + strescseq.buf[strescseq.len] = '\0'; + + if (*p == '\0') + return; + + while (strescseq.narg < STR_ARG_SIZ) { + strescseq.args[strescseq.narg++] = p; + while ((c = *p) != ';' && c != '\0') + ++p; + if (c == '\0') + return; + *p++ = '\0'; + } +} + +void +strdump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC%c", strescseq.type); + for (i = 0; i < strescseq.len; i++) { + c = strescseq.buf[i] & 0xff; + if (c == '\0') { + putc('\n', stderr); + return; + } else if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + fprintf(stderr, "ESC\\\n"); +} + +void +strreset(void) +{ + strescseq = (STREscape){ + .buf = xrealloc(strescseq.buf, STR_BUF_SIZ), + .siz = STR_BUF_SIZ, + }; +} + +void +sendbreak(const Arg *arg) +{ + if (tcsendbreak(cmdfd, 0)) + perror("Error sending break"); +} + +void +tprinter(char *s, size_t len) +{ + if (iofd != -1 && xwrite(iofd, s, len) < 0) { + perror("Error writing to output file"); + close(iofd); + iofd = -1; + } +} + +void +toggleprinter(const Arg *arg) +{ + term.mode ^= MODE_PRINT; +} + +void +printscreen(const Arg *arg) +{ + tdump(); +} + +void +printsel(const Arg *arg) +{ + tdumpsel(); +} + +void +tdumpsel(void) +{ + char *ptr; + + if ((ptr = getsel())) { + tprinter(ptr, strlen(ptr)); + free(ptr); + } +} + +void +tdumpline(int n) +{ + char buf[UTF_SIZ]; + const Glyph *bp, *end; + + bp = &term.line[n][0]; + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if (bp != end || bp->u != ' ') { + for ( ; bp <= end; ++bp) + tprinter(buf, utf8encode(bp->u, buf)); + } + tprinter("\n", 1); +} + +void +tdump(void) +{ + int i; + + for (i = 0; i < term.row; ++i) + tdumpline(i); +} + +void +tputtab(int n) +{ + uint x = term.c.x; + + if (n > 0) { + while (x < term.col && n--) + for (++x; x < term.col && !term.tabs[x]; ++x) + /* nothing */ ; + } else if (n < 0) { + while (x > 0 && n++) + for (--x; x > 0 && !term.tabs[x]; --x) + /* nothing */ ; + } + term.c.x = LIMIT(x, 0, term.col-1); +} + +void +tdefutf8(char ascii) +{ + if (ascii == 'G') + term.mode |= MODE_UTF8; + else if (ascii == '@') + term.mode &= ~MODE_UTF8; +} + +void +tdeftran(char ascii) +{ + static char cs[] = "0B"; + static int vcs[] = {CS_GRAPHIC0, CS_USA}; + char *p; + + if ((p = strchr(cs, ascii)) == NULL) { + fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); + } else { + term.trantbl[term.icharset] = vcs[p - cs]; + } +} + +void +tdectest(char c) +{ + int x, y; + + if (c == '8') { /* DEC screen alignment test. */ + for (x = 0; x < term.col; ++x) { + for (y = 0; y < term.row; ++y) + tsetchar('E', &term.c.attr, x, y); + } + } +} + +void +tstrsequence(uchar c) +{ + switch (c) { + case 0x90: /* DCS -- Device Control String */ + c = 'P'; + break; + case 0x9f: /* APC -- Application Program Command */ + c = '_'; + break; + case 0x9e: /* PM -- Privacy Message */ + c = '^'; + break; + case 0x9d: /* OSC -- Operating System Command */ + c = ']'; + break; + } + strreset(); + strescseq.type = c; + term.esc |= ESC_STR; +} + +void +tcontrolcode(uchar ascii) +{ + switch (ascii) { + case '\t': /* HT */ + tputtab(1); + return; + case '\b': /* BS */ + tmoveto(term.c.x-1, term.c.y); + return; + case '\r': /* CR */ + tmoveto(0, term.c.y); + return; + case '\f': /* LF */ + case '\v': /* VT */ + case '\n': /* LF */ + /* go to first col if the mode is set */ + tnewline(IS_SET(MODE_CRLF)); + return; + case '\a': /* BEL */ + if (term.esc & ESC_STR_END) { + /* backwards compatibility to xterm */ + strhandle(); + } else { + xbell(); + } + break; + case '\033': /* ESC */ + csireset(); + term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); + term.esc |= ESC_START; + return; + case '\016': /* SO (LS1 -- Locking shift 1) */ + case '\017': /* SI (LS0 -- Locking shift 0) */ + term.charset = 1 - (ascii - '\016'); + return; + case '\032': /* SUB */ + tsetchar('?', &term.c.attr, term.c.x, term.c.y); + /* FALLTHROUGH */ + case '\030': /* CAN */ + csireset(); + break; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + return; + case 0x80: /* TODO: PAD */ + case 0x81: /* TODO: HOP */ + case 0x82: /* TODO: BPH */ + case 0x83: /* TODO: NBH */ + case 0x84: /* TODO: IND */ + break; + case 0x85: /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 0x86: /* TODO: SSA */ + case 0x87: /* TODO: ESA */ + break; + case 0x88: /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 0x89: /* TODO: HTJ */ + case 0x8a: /* TODO: VTS */ + case 0x8b: /* TODO: PLD */ + case 0x8c: /* TODO: PLU */ + case 0x8d: /* TODO: RI */ + case 0x8e: /* TODO: SS2 */ + case 0x8f: /* TODO: SS3 */ + case 0x91: /* TODO: PU1 */ + case 0x92: /* TODO: PU2 */ + case 0x93: /* TODO: STS */ + case 0x94: /* TODO: CCH */ + case 0x95: /* TODO: MW */ + case 0x96: /* TODO: SPA */ + case 0x97: /* TODO: EPA */ + case 0x98: /* TODO: SOS */ + case 0x99: /* TODO: SGCI */ + break; + case 0x9a: /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 0x9b: /* TODO: CSI */ + case 0x9c: /* TODO: ST */ + break; + case 0x90: /* DCS -- Device Control String */ + case 0x9d: /* OSC -- Operating System Command */ + case 0x9e: /* PM -- Privacy Message */ + case 0x9f: /* APC -- Application Program Command */ + tstrsequence(ascii); + return; + } + /* only CAN, SUB, \a and C1 chars interrupt a sequence */ + term.esc &= ~(ESC_STR_END|ESC_STR); +} + +/* + * returns 1 when the sequence is finished and it hasn't to read + * more characters for this sequence, otherwise 0 + */ +int +eschandle(uchar ascii) +{ + switch (ascii) { + case '[': + term.esc |= ESC_CSI; + return 0; + case '#': + term.esc |= ESC_TEST; + return 0; + case '%': + term.esc |= ESC_UTF8; + return 0; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + case ']': /* OSC -- Operating System Command */ + case 'k': /* old title set compatibility */ + tstrsequence(ascii); + return 0; + case 'n': /* LS2 -- Locking shift 2 */ + case 'o': /* LS3 -- Locking shift 3 */ + term.charset = 2 + (ascii - 'n'); + break; + case '(': /* GZD4 -- set primary charset G0 */ + case ')': /* G1D4 -- set secondary charset G1 */ + case '*': /* G2D4 -- set tertiary charset G2 */ + case '+': /* G3D4 -- set quaternary charset G3 */ + term.icharset = ascii - '('; + term.esc |= ESC_ALTCHARSET; + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { + tscrollup(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { + tscrolldown(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'c': /* RIS -- Reset to initial state */ + treset(); + resettitle(); + xloadcols(); + break; + case '=': /* DECPAM -- Application keypad */ + xsetmode(1, MODE_APPKEYPAD); + break; + case '>': /* DECPNM -- Normal keypad */ + xsetmode(0, MODE_APPKEYPAD); + break; + case '7': /* DECSC -- Save Cursor */ + tcursor(CURSOR_SAVE); + break; + case '8': /* DECRC -- Restore Cursor */ + tcursor(CURSOR_LOAD); + break; + case '\\': /* ST -- String Terminator */ + if (term.esc & ESC_STR_END) + strhandle(); + break; + default: + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", + (uchar) ascii, isprint(ascii)? ascii:'.'); + break; + } + return 1; +} + +void +tputc(Rune u) +{ + char c[UTF_SIZ]; + int control; + int width, len; + Glyph *gp; + + control = ISCONTROL(u); + if (u < 127 || !IS_SET(MODE_UTF8)) { + c[0] = u; + width = len = 1; + } else { + len = utf8encode(u, c); + if (!control && (width = wcwidth(u)) == -1) + width = 1; + } + + if (IS_SET(MODE_PRINT)) + tprinter(c, len); + + /* + * STR sequence must be checked before anything else + * because it uses all following characters until it + * receives a ESC, a SUB, a ST or any other C1 control + * character. + */ + if (term.esc & ESC_STR) { + if (u == '\a' || u == 030 || u == 032 || u == 033 || + ISCONTROLC1(u)) { + term.esc &= ~(ESC_START|ESC_STR); + term.esc |= ESC_STR_END; + goto check_control_code; + } + + if (strescseq.len+len >= strescseq.siz) { + /* + * Here is a bug in terminals. If the user never sends + * some code to stop the str or esc command, then st + * will stop responding. But this is better than + * silently failing with unknown characters. At least + * then users will report back. + * + * In the case users ever get fixed, here is the code: + */ + /* + * term.esc = 0; + * strhandle(); + */ + if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2) + return; + strescseq.siz *= 2; + strescseq.buf = xrealloc(strescseq.buf, strescseq.siz); + } + + memmove(&strescseq.buf[strescseq.len], c, len); + strescseq.len += len; + return; + } + +check_control_code: + /* + * Actions of control codes must be performed as soon they arrive + * because they can be embedded inside a control sequence, and + * they must not cause conflicts with sequences. + */ + if (control) { + tcontrolcode(u); + /* + * control codes are not shown ever + */ + if (!term.esc) + term.lastc = 0; + return; + } else if (term.esc & ESC_START) { + if (term.esc & ESC_CSI) { + csiescseq.buf[csiescseq.len++] = u; + if (BETWEEN(u, 0x40, 0x7E) + || csiescseq.len >= \ + sizeof(csiescseq.buf)-1) { + term.esc = 0; + csiparse(); + csihandle(); + } + return; + } else if (term.esc & ESC_UTF8) { + tdefutf8(u); + } else if (term.esc & ESC_ALTCHARSET) { + tdeftran(u); + } else if (term.esc & ESC_TEST) { + tdectest(u); + } else { + if (!eschandle(u)) + return; + /* sequence already finished */ + } + term.esc = 0; + /* + * All characters which form part of a sequence are not + * printed + */ + return; + } + if (selected(term.c.x, term.c.y)) + selclear(); + + gp = &term.line[term.c.y][term.c.x]; + if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + gp->mode |= ATTR_WRAP; + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) + memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); + + if (term.c.x+width > term.col) { + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + tsetchar(u, &term.c.attr, term.c.x, term.c.y); + term.lastc = u; + + if (width == 2) { + gp->mode |= ATTR_WIDE; + if (term.c.x+1 < term.col) { + if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) { + gp[2].u = ' '; + gp[2].mode &= ~ATTR_WDUMMY; + } + gp[1].u = '\0'; + gp[1].mode = ATTR_WDUMMY; + } + } + if (term.c.x+width < term.col) { + tmoveto(term.c.x+width, term.c.y); + } else { + term.c.state |= CURSOR_WRAPNEXT; + } +} + +int +twrite(const char *buf, int buflen, int show_ctrl) +{ + int charsize; + Rune u; + int n; + + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8)) { + /* process a complete utf8 char */ + charsize = utf8decode(buf + n, &u, buflen - n); + if (charsize == 0) + break; + } else { + u = buf[n] & 0xFF; + charsize = 1; + } + if (show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; + tputc('^'); + tputc('['); + } else if (u != '\n' && u != '\r' && u != '\t') { + u ^= 0x40; + tputc('^'); + } + } + tputc(u); + } + return n; +} + +void +tresize(int col, int row) +{ + int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; + TCursor c; + + if (col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to + * memmove because we're freeing the earlier lines + */ + for (i = 0; i <= term.c.y - row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + /* ensure that both src and dst are not NULL */ + if (i > 0) { + memmove(term.line, term.line + i, row * sizeof(Line)); + memmove(term.alt, term.alt + i, row * sizeof(Line)); + } + for (i += row; i < term.row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + for (i = 0; i < HISTSIZE; i++) { + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); + for (j = mincol; j < col; j++) { + term.hist[i][j] = term.c.attr; + term.hist[i][j].u = ' '; + } + } + + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); + } + + /* allocate any new rows */ + for (/* i = minrow */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + term.alt[i] = xmalloc(col * sizeof(Glyph)); + } + if (col > term.col) { + bp = term.tabs + term.col; + + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + while (--bp > term.tabs && !*bp) + /* nothing */ ; + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } + /* update terminal size */ + term.col = col; + term.row = row; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); + /* Clearing both screens (it makes dirty all lines) */ + c = term.c; + for (i = 0; i < 2; i++) { + if (mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if (0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + tcursor(CURSOR_LOAD); + } + term.c = c; +} + +void +resettitle(void) +{ + xsettitle(NULL); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y; + + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(TLINE(y), x1, y, x2); + } +} + +void +draw(void) +{ + int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; + + if (!xstartdraw()) + return; + + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); + if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) + term.ocx--; + if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); + if (term.scr == 0) + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); + if (ocx != term.ocx || ocy != term.ocy) + xximspot(term.ocx, term.ocy); +} + +void +redraw(void) +{ + tfulldirt(); + draw(); +} diff --git a/st/st.h b/st/st.h index 67bfb74f..96d4416e 100644 --- a/st/st.h +++ b/st/st.h @@ -33,6 +33,7 @@ enum glyph_attribute { ATTR_WRAP = 1 << 8, ATTR_WIDE = 1 << 9, ATTR_WDUMMY = 1 << 10, + ATTR_BOXDRAW = 1 << 11, ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, }; @@ -115,6 +116,14 @@ char *xstrdup(const char *); int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b); +int isboxdraw(Rune); +ushort boxdrawindex(const Glyph *); +#ifdef XFT_VERSION +/* only exposed to x.c, otherwise we'll need Xft.h for the types */ +void boxdraw_xinit(Display *, Colormap, XftDraw *, Visual *); +void drawboxes(int, int, int, int, XftColor *, XftColor *, const XftGlyphFontSpec *, int); +#endif + /* config.h globals */ extern char *utmp; extern char *scroll; diff --git a/st/st.h.orig b/st/st.h.orig index da36b34a..67bfb74f 100644 --- a/st/st.h.orig +++ b/st/st.h.orig @@ -128,3 +128,4 @@ extern unsigned int tabspaces; extern unsigned int defaultfg; extern unsigned int defaultbg; extern unsigned int defaultcs; +extern float alpha; diff --git a/st/st.h.rej b/st/st.h.rej new file mode 100644 index 00000000..7ac152cf --- /dev/null +++ b/st/st.h.rej @@ -0,0 +1,7 @@ +--- a/st.h ++++ b/st.h +@@ -135,3 +144,4 @@ extern unsigned int tabspaces; + extern unsigned int defaultfg; + extern unsigned int defaultbg; + extern unsigned int defaultcs; ++extern const int boxdraw, boxdraw_bold, boxdraw_braille; diff --git a/st/st.o b/st/st.o index 7819f18c82d1c3975424d3235f942b38af73a48c..8da26b5007805e71627527f0adcbf6f3913a644e 100644 GIT binary patch literal 81664 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
>F2B=$k&d;<66b zNPB30zBT0P{Og7dA8F<14Ietv8a4FVVMB69Sov39F+6wJQ0uypL$9^$tKkgqv?L1K zglWa41(T;iWyP9@3DG%GURYXysT&V2z0JxjttctUYAy*}^Tv-WFYMp9gLPT+o#rwM zO%!HZ_aJ_c?r@Aiu)Nr3opDq5;(^_ZueE~gW{3^SMcL#@zVeBMB_-OT^1>2qQBC(@ zK`Hb>VSwqD4>aVC|lFH+=4a&!6D4*xG&X0{A>&?OqzTeAe#!=D`)*S?s`P zA$%h6xzk$rjJ57rIC~5}wbt$>OW^}ymw?2QCGc5o?OwDBJ}<+075w7$D{%fGd?3t{ z7vQrTK69reH*4LC)H3|%=RY3!j|cwaf&X~mKOXpx2ma%M|9Ig4 zwFlsd-2dc&2W-}5ef#ylXn_CXfrD@ju?d5F5)zZzv~8E%zQYMAC#HJSI-b<2^U0^2 zntmGIw5-$5=+gDfZoae5J}0C5x#wkO{qFo87xcU^yI1c%`u_jp@1~|SYc|+`TPX1V z55Vi=9aRXE@ylb?RqZ-(sLHC_bpWsbbNv6x16K?gaiU+?VR zeX@IJ_fl@al~o^n@SzL{!7e~g9Wq`w^UyVHU{VKea>B!N$F-KHGR<>FO1os-G>N6K zi@F25K6PZmMry61Y3uCXkwa zO^4J>0J)~$dw_T{Jg>!L41Ph_g$W=s55tC&67o8vrh_e%O)AX;NPYmu`CDnuN$lo1 zUZz7(w7Z&gok_ZmV|Px%zGDsdLD)J9I|a_QzXGZ03E<<-I(1GN80JdMhZ{g&gDO*f>0URy2M3lFo7hrCG#gFqLSmWlJB3&nnXo0{ zkL+g48UeQ-B-!^+cEoSgs}TtgxfHBxQo)y_aJ%)_!5P7I{_O0=xbN(*J5 zA=%%d?pCsir;uhPjcNLbWO0|!ZJ;pZIyKI6D*s*Re#1NzMSBaxfsuS(se$ZYuk#^G-bms z7aKC+{?Bv|_xrc@Wx}nd<5l`$Hy|D|EqE_L_v%mpaxGznPAH~xEZu|6@4uDKD^j=l z5_Y?!a{vTiPP&F-zJkw=(@u;{c=XtI0&R>^*gwI!vT=;I5$i+}jJ@zsBSy%lQQ$qS z(_M&9RCjg1noUgg2`%LhnpNH4puSNglY`-IPE8vR`jD5HydX90Cg5mWHF1@H5dRHS zPSA#BbPw|r57hGlG1!f-`hYyrzvVa_^I#A0m&k9w-ajWUOLD0PeL+_?8FC}Wef&Jg zPOw||F=+Ed3fBq4VO;>5N5N?o^M9uvIF8ss zx}Jn{YOn61VrAt3Iz;!?m;r2GgkyyWj@cICSYaNH*}`qFNUcjqxUz#9t-(;h7_DJ_ z97GSj4#2rfS;$G4cWiycu$2_{c?{c9j{>P<6LL}~=B7@`O|8sLt@4ad9a~_wMT0>6 z1q%DGW5W*qwXi7ecA(3XtZ!fjcI=VZJbG?@FVUjTv^54w9go4WxWQPwnW(-Fu5i

1z_}W`!|zqZ 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 z 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 z z`!7#`ug$e_iSSnxp9FtRo(z9oo&tYUo(g|Uo(BJiJOF=3o(?Zl{WIYIR(vLWwc8VR<*aO5OvnmG{CQlJ~(MmiNPt%Lm|($_L?Jl@G(+^9x4ck12i> z{)GH2{3-bu{Dk}hyi-07e^x#LKPjJtKQEtxzpL%cG(1(Gml^m@`7Hcndd|fhyj(sH ze_Xx*ACh}(uHOFNq53DlT|Z2OyY@_ipHe=_^zF916!@cI>#6W*c^dq(@(;k@kf*~- z@3#48z~5GUCj5##1TR#5vf+=&bKtAAzPa#gYoq4R`CV!Zp9bEH!8je z{tO->vo^hj+>+ z;Jxxm_$m1md{jOSzbK!9Uy{$lr{#0-EAn}Gg8I(_d_wLe+V)%JkIZXQo&cYcC&H)Y zN$?qYGJIB^0-uwo!sq2_@CA7QzWC2k{q;zPC+PSy1D+_)gzr@TA@~FGYCiX2>ed@C|&!Vv+z}6eOTYjr+gOf&Zp+!?tE$M zw{*BC&w!s-KhK26w7wzu{ff_qA5eXA;BTtjT=@5te;D4P{PW-!m480`lDq)^YrVgP z@Ntz}1fSG)CIWZoQ^oMVsoWB{JD;k6@6hpN6n;V;ga1qU*T7#=yzRA~_a1*D&3~1d z&g&2A=1YD^o@^H{`C++k3t93ic>wXX@(lPx@(}!Cxx0VI)$_P~9`TLx#ftdLTzr!} z(ZDiy*P$lETNIxPch{i?;O;uq47j@vH3WZJ`RBkpZbqPYo>g+W%f(q*+h52Q2P$Ocn)NkdOEt&9}i{Pj0wgJ^G!d0e)HT-bYvN zkhYJm{hi0x*?J}z@CWA|Np?OAU;jSqHSj7quirlTej7iD`0LhNPtkh2a!2JM_>4Rc zzD^tL2>dyD41P}D4&V0yTOVFumwB!AgakWw^_-Fy!XHVoalBrr|AW?f{fu{=bzV<$ z%k|dN@q1j~M(bht4>wtl!1tS-oDjNnQv~)ebxgkH{P0?Q(wqc3R$t z_?PAUp6U;BeqZ#uJTuX*pX-NRS$4hx{;s?Mo)on4Jg?atvd;61t#Tf({!#avG~>AI zFT0gLd|uDlnS*!fxiC3vZ92DJv3u=&9A2#FarD90>$w$PuCsBNj{nc!fS=gx z0`zfO$||g>5!Wu7*Y08|&TKZ!CM>)pc4G1ycHDf#j?HG%q*ZtF-0`sa7#i2+)St3L zLZKbI_$7A}8fW@r6T|cYr5`A}Iziy-Sf_oW{6Es)yVqv* zj_Hug2A9r_i~MC~8ev|^ew%1W{nTX137kV+TUzKT>V}9m-=SP z$7*^3v%nu_&BA3vVA-^pEcRb3l^?jxCUo}!nDWg>m*vNOGm#Mc-|{C+yRiK;l%Dl+^>^vN?@yRKnLeWQ(|G@wzROR$Qu~c= zwKdJx2h+5lITUi~-TSvv*?yQ8t}=i9|KF+O26K3F+c~4gRFAk|qR^0F7ZaLe} Sascent; } - /* Lookup character index with default font. */ - glyphidx = XftCharIndex(xw.dpy, font->match, rune); + if (mode & ATTR_BOXDRAW) { + /* minor shoehorning: boxdraw uses only this ushort */ + glyphidx = boxdrawindex(&glyphs[i]); + } else { + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + } if (glyphidx) { specs[numspecs].font = font->match; specs[numspecs].glyph = glyphidx; @@ -1489,8 +1496,12 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i r.width = width; XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); - /* Render the glyphs. */ - XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + if (base.mode & ATTR_BOXDRAW) { + drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); + } else { + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + } /* Render underline and strikethrough. */ if (base.mode & ATTR_UNDERLINE) { @@ -1533,7 +1544,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) /* * Select the right color for the right mode. */ - g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW; if (IS_SET(MODE_REVERSE)) { g.mode |= ATTR_REVERSE; diff --git a/st/x.c.orig b/st/x.c.orig new file mode 100644 index 00000000..ddf41787 --- /dev/null +++ b/st/x.c.orig @@ -0,0 +1,2100 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *argv0; +#include "arg.h" +#include "st.h" +#include "win.h" + +/* types used in config.h */ +typedef struct { + uint mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Shortcut; + +typedef struct { + uint mod; + uint button; + void (*func)(const Arg *); + const Arg arg; + uint release; +} MouseShortcut; + +typedef struct { + KeySym k; + uint mask; + char *s; + /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ + signed char appkey; /* application keypad */ + signed char appcursor; /* application cursor */ +} Key; + +/* X modifiers */ +#define XK_ANY_MOD UINT_MAX +#define XK_NO_MOD 0 +#define XK_SWITCH_MOD (1<<13|1<<14) + +/* function definitions used in config.h */ +static void clipcopy(const Arg *); +static void clippaste(const Arg *); +static void numlock(const Arg *); +static void selpaste(const Arg *); +static void zoom(const Arg *); +static void zoomabs(const Arg *); +static void zoomreset(const Arg *); +static void ttysend(const Arg *); + +/* config.h for applying patches and the configuration. */ +#include "config.h" + +/* XEMBED messages */ +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 + +/* macros */ +#define IS_SET(flag) ((win.mode & (flag)) != 0) +#define TRUERED(x) (((x) & 0xff0000) >> 8) +#define TRUEGREEN(x) (((x) & 0xff00)) +#define TRUEBLUE(x) (((x) & 0xff) << 8) + +typedef XftDraw *Draw; +typedef XftColor Color; +typedef XftGlyphFontSpec GlyphFontSpec; + +/* Purely graphic info */ +typedef struct { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ + int ch; /* char height */ + int cw; /* char width */ + int mode; /* window state/mode flags */ + int cursor; /* cursor style */ +} TermWindow; + +typedef struct { + Display *dpy; + Colormap cmap; + Window win; + Drawable buf; + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ + Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; + struct { + XIM xim; + XIC xic; + XPoint spot; + XVaNestedList spotlist; + } ime; + Draw draw; + Visual *vis; + XSetWindowAttributes attrs; + int scr; + int isfixed; /* is fixed geometry? */ + int depth; /* bit depth */ + int l, t; /* left and top offset */ + int gm; /* geometry mask */ +} XWindow; + +typedef struct { + Atom xtarget; + char *primary, *clipboard; + struct timespec tclick1; + struct timespec tclick2; +} XSelection; + +/* Font structure */ +#define Font Font_ +typedef struct { + int height; + int width; + int ascent; + int descent; + int badslant; + int badweight; + short lbearing; + short rbearing; + XftFont *match; + FcFontSet *set; + FcPattern *pattern; +} Font; + +/* Drawing Context */ +typedef struct { + Color *col; + size_t collen; + Font font, bfont, ifont, ibfont; + GC gc; +} DC; + +static inline ushort sixd_to_16bit(int); +static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); +static void xdrawglyph(Glyph, int, int); +static void xclear(int, int, int, int); +static int xgeommasktogravity(int); +static int ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, XPointer, XPointer); +static int xicdestroy(XIC, XPointer, XPointer); +static void xinit(int, int); +static void cresize(int, int); +static void xresize(int, int); +static void xhints(void); +static int xloadcolor(int, const char *, Color *); +static int xloadfont(Font *, FcPattern *); +static void xloadfonts(const char *, double); +static void xunloadfont(Font *); +static void xunloadfonts(void); +static void xsetenv(void); +static void xseturgency(int); +static int evcol(XEvent *); +static int evrow(XEvent *); + +static void expose(XEvent *); +static void visibility(XEvent *); +static void unmap(XEvent *); +static void kpress(XEvent *); +static void cmessage(XEvent *); +static void resize(XEvent *); +static void focus(XEvent *); +static uint buttonmask(uint); +static int mouseaction(XEvent *, uint); +static void brelease(XEvent *); +static void bpress(XEvent *); +static void bmotion(XEvent *); +static void propnotify(XEvent *); +static void selnotify(XEvent *); +static void selclear_(XEvent *); +static void selrequest(XEvent *); +static void setsel(char *, Time); +static void mousesel(XEvent *, int); +static void mousereport(XEvent *); +static char *kmap(KeySym, uint); +static int match(uint, uint); + +static void run(void); +static void usage(void); + +static void (*handler[LASTEvent])(XEvent *) = { + [KeyPress] = kpress, + [ClientMessage] = cmessage, + [ConfigureNotify] = resize, + [VisibilityNotify] = visibility, + [UnmapNotify] = unmap, + [Expose] = expose, + [FocusIn] = focus, + [FocusOut] = focus, + [MotionNotify] = bmotion, + [ButtonPress] = bpress, + [ButtonRelease] = brelease, +/* + * Uncomment if you want the selection to disappear when you select something + * different in another window. + */ +/* [SelectionClear] = selclear_, */ + [SelectionNotify] = selnotify, +/* + * PropertyNotify is only turned on when there is some INCR transfer happening + * for the selection retrieval. + */ + [PropertyNotify] = propnotify, + [SelectionRequest] = selrequest, +}; + +/* Globals */ +static DC dc; +static XWindow xw; +static XSelection xsel; +static TermWindow win; + +/* Font Ring Cache */ +enum { + FRC_NORMAL, + FRC_ITALIC, + FRC_BOLD, + FRC_ITALICBOLD +}; + +typedef struct { + XftFont *font; + int flags; + Rune unicodep; +} Fontcache; + +/* Fontcache is an array now. A new font will be appended to the array. */ +static Fontcache *frc = NULL; +static int frclen = 0; +static int frccap = 0; +static char *usedfont = NULL; +static double usedfontsize = 0; +static double defaultfontsize = 0; + +static char *opt_alpha = NULL; +static char *opt_class = NULL; +static char **opt_cmd = NULL; +static char *opt_embed = NULL; +static char *opt_font = NULL; +static char *opt_io = NULL; +static char *opt_line = NULL; +static char *opt_name = NULL; +static char *opt_title = NULL; + +static int oldbutton = 3; /* button event on startup: 3 = release */ + +void +clipcopy(const Arg *dummy) +{ + Atom clipboard; + + free(xsel.clipboard); + xsel.clipboard = NULL; + + if (xsel.primary != NULL) { + xsel.clipboard = xstrdup(xsel.primary); + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); + } +} + +void +clippaste(const Arg *dummy) +{ + Atom clipboard; + + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, + xw.win, CurrentTime); +} + +void +selpaste(const Arg *dummy) +{ + XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, + xw.win, CurrentTime); +} + +void +numlock(const Arg *dummy) +{ + win.mode ^= MODE_NUMLOCK; +} + +void +zoom(const Arg *arg) +{ + Arg larg; + + larg.f = usedfontsize + arg->f; + zoomabs(&larg); +} + +void +zoomabs(const Arg *arg) +{ + xunloadfonts(); + xloadfonts(usedfont, arg->f); + cresize(0, 0); + redraw(); + xhints(); +} + +void +zoomreset(const Arg *arg) +{ + Arg larg; + + if (defaultfontsize > 0) { + larg.f = defaultfontsize; + zoomabs(&larg); + } +} + +void +ttysend(const Arg *arg) +{ + ttywrite(arg->s, strlen(arg->s), 1); +} + +int +evcol(XEvent *e) +{ + int x = e->xbutton.x - borderpx; + LIMIT(x, 0, win.tw - 1); + return x / win.cw; +} + +int +evrow(XEvent *e) +{ + int y = e->xbutton.y - borderpx; + LIMIT(y, 0, win.th - 1); + return y / win.ch; +} + +void +mousesel(XEvent *e, int done) +{ + int type, seltype = SEL_REGULAR; + uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); + + for (type = 1; type < LEN(selmasks); ++type) { + if (match(selmasks[type], state)) { + seltype = type; + break; + } + } + selextend(evcol(e), evrow(e), seltype, done); + if (done) + setsel(getsel(), e->xbutton.time); +} + +void +mousereport(XEvent *e) +{ + int len, x = evcol(e), y = evrow(e), + button = e->xbutton.button, state = e->xbutton.state; + char buf[40]; + static int ox, oy; + + /* from urxvt */ + if (e->xbutton.type == MotionNotify) { + if (x == ox && y == oy) + return; + if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) + return; + /* MOUSE_MOTION: no reporting if no button is pressed */ + if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) + return; + + button = oldbutton + 32; + ox = x; + oy = y; + } else { + if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) { + button = 3; + } else { + button -= Button1; + if (button >= 7) + button += 128 - 7; + else if (button >= 3) + button += 64 - 3; + } + if (e->xbutton.type == ButtonPress) { + oldbutton = button; + ox = x; + oy = y; + } else if (e->xbutton.type == ButtonRelease) { + oldbutton = 3; + /* MODE_MOUSEX10: no button release reporting */ + if (IS_SET(MODE_MOUSEX10)) + return; + if (button == 64 || button == 65) + return; + } + } + + if (!IS_SET(MODE_MOUSEX10)) { + button += ((state & ShiftMask ) ? 4 : 0) + + ((state & Mod4Mask ) ? 8 : 0) + + ((state & ControlMask) ? 16 : 0); + } + + if (IS_SET(MODE_MOUSESGR)) { + len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", + button, x+1, y+1, + e->xbutton.type == ButtonRelease ? 'm' : 'M'); + } else if (x < 223 && y < 223) { + len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", + 32+button, 32+x+1, 32+y+1); + } else { + return; + } + + ttywrite(buf, len, 0); +} + +uint +buttonmask(uint button) +{ + return button == Button1 ? Button1Mask + : button == Button2 ? Button2Mask + : button == Button3 ? Button3Mask + : button == Button4 ? Button4Mask + : button == Button5 ? Button5Mask + : 0; +} + +int +mouseaction(XEvent *e, uint release) +{ + MouseShortcut *ms; + + /* ignore Buttonmask for Button - it's set on release */ + uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); + + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { + if (ms->release == release && + ms->button == e->xbutton.button && + (match(ms->mod, state) || /* exact or forced */ + match(ms->mod, state & ~forcemousemod))) { + ms->func(&(ms->arg)); + return 1; + } + } + + return 0; +} + +void +bpress(XEvent *e) +{ + struct timespec now; + int snap; + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 0)) + return; + + if (e->xbutton.button == Button1) { + /* + * If the user clicks below predefined timeouts specific + * snapping behaviour is exposed. + */ + clock_gettime(CLOCK_MONOTONIC, &now); + if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { + snap = SNAP_LINE; + } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { + snap = SNAP_WORD; + } else { + snap = 0; + } + xsel.tclick2 = xsel.tclick1; + xsel.tclick1 = now; + + selstart(evcol(e), evrow(e), snap); + } +} + +void +propnotify(XEvent *e) +{ + XPropertyEvent *xpev; + Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + + xpev = &e->xproperty; + if (xpev->state == PropertyNewValue && + (xpev->atom == XA_PRIMARY || + xpev->atom == clipboard)) { + selnotify(e); + } +} + +void +selnotify(XEvent *e) +{ + ulong nitems, ofs, rem; + int format; + uchar *data, *last, *repl; + Atom type, incratom, property = None; + + incratom = XInternAtom(xw.dpy, "INCR", 0); + + ofs = 0; + if (e->type == SelectionNotify) + property = e->xselection.property; + else if (e->type == PropertyNotify) + property = e->xproperty.atom; + + if (property == None) + return; + + do { + if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, + BUFSIZ/4, False, AnyPropertyType, + &type, &format, &nitems, &rem, + &data)) { + fprintf(stderr, "Clipboard allocation failed\n"); + return; + } + + if (e->type == PropertyNotify && nitems == 0 && rem == 0) { + /* + * If there is some PropertyNotify with no data, then + * this is the signal of the selection owner that all + * data has been transferred. We won't need to receive + * PropertyNotify events anymore. + */ + MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + } + + if (type == incratom) { + /* + * Activate the PropertyNotify events so we receive + * when the selection owner does send us the next + * chunk of data. + */ + MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + + /* + * Deleting the property is the transfer start signal. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); + continue; + } + + /* + * As seen in getsel: + * Line endings are inconsistent in the terminal and GUI world + * copy and pasting. When receiving some selection data, + * replace all '\n' with '\r'. + * FIXME: Fix the computer world. + */ + repl = data; + last = data + nitems * format / 8; + while ((repl = memchr(repl, '\n', last - repl))) { + *repl++ = '\r'; + } + + if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) + ttywrite("\033[200~", 6, 0); + ttywrite((char *)data, nitems * format / 8, 1); + if (IS_SET(MODE_BRCKTPASTE) && rem == 0) + ttywrite("\033[201~", 6, 0); + XFree(data); + /* number of 32-bit chunks returned */ + ofs += nitems * format / 32; + } while (rem > 0); + + /* + * Deleting the property again tells the selection owner to send the + * next data chunk in the property. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); +} + +void +xclipcopy(void) +{ + clipcopy(NULL); +} + +void +selclear_(XEvent *e) +{ + selclear(); +} + +void +selrequest(XEvent *e) +{ + XSelectionRequestEvent *xsre; + XSelectionEvent xev; + Atom xa_targets, string, clipboard; + char *seltext; + + xsre = (XSelectionRequestEvent *) e; + xev.type = SelectionNotify; + xev.requestor = xsre->requestor; + xev.selection = xsre->selection; + xev.target = xsre->target; + xev.time = xsre->time; + if (xsre->property == None) + xsre->property = xsre->target; + + /* reject */ + xev.property = None; + + xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); + if (xsre->target == xa_targets) { + /* respond with the supported type */ + string = xsel.xtarget; + XChangeProperty(xsre->display, xsre->requestor, xsre->property, + XA_ATOM, 32, PropModeReplace, + (uchar *) &string, 1); + xev.property = xsre->property; + } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { + /* + * xith XA_STRING non ascii characters may be incorrect in the + * requestor. It is not our problem, use utf8. + */ + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + if (xsre->selection == XA_PRIMARY) { + seltext = xsel.primary; + } else if (xsre->selection == clipboard) { + seltext = xsel.clipboard; + } else { + fprintf(stderr, + "Unhandled clipboard selection 0x%lx\n", + xsre->selection); + return; + } + if (seltext != NULL) { + XChangeProperty(xsre->display, xsre->requestor, + xsre->property, xsre->target, + 8, PropModeReplace, + (uchar *)seltext, strlen(seltext)); + xev.property = xsre->property; + } + } + + /* all done, send a notification to the listener */ + if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) + fprintf(stderr, "Error sending SelectionNotify event\n"); +} + +void +setsel(char *str, Time t) +{ + if (!str) + return; + + free(xsel.primary); + xsel.primary = str; + + XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); + if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) + selclear(); +} + +void +xsetsel(char *str) +{ + setsel(str, CurrentTime); +} + +void +brelease(XEvent *e) +{ + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 1)) + return; + if (e->xbutton.button == Button1) + mousesel(e, 1); +} + +void +bmotion(XEvent *e) +{ + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + mousesel(e, 0); +} + +void +cresize(int width, int height) +{ + int col, row; + + if (width != 0) + win.w = width; + if (height != 0) + win.h = height; + + col = (win.w - 2 * borderpx) / win.cw; + row = (win.h - 2 * borderpx) / win.ch; + col = MAX(1, col); + row = MAX(1, row); + + tresize(col, row); + xresize(col, row); + ttyresize(win.tw, win.th); +} + +void +xresize(int col, int row) +{ + win.tw = col * win.cw; + win.th = row * win.ch; + + XFreePixmap(xw.dpy, xw.buf); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, + xw.depth); + XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + + /* resize to new width */ + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); +} + +ushort +sixd_to_16bit(int x) +{ + return x == 0 ? 0 : 0x3737 + 0x2828 * x; +} + +int +xloadcolor(int i, const char *name, Color *ncolor) +{ + XRenderColor color = { .alpha = 0xffff }; + + if (!name) { + if (BETWEEN(i, 16, 255)) { /* 256 color */ + if (i < 6*6*6+16) { /* same colors as xterm */ + color.red = sixd_to_16bit( ((i-16)/36)%6 ); + color.green = sixd_to_16bit( ((i-16)/6) %6 ); + color.blue = sixd_to_16bit( ((i-16)/1) %6 ); + } else { /* greyscale */ + color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); + color.green = color.blue = color.red; + } + return XftColorAllocValue(xw.dpy, xw.vis, + xw.cmap, &color, ncolor); + } else + name = colorname[i]; + } + + return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); +} + +void +xloadcols(void) +{ + int i; + static int loaded; + Color *cp; + + if (loaded) { + for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) + XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); + } else { + dc.collen = MAX(LEN(colorname), 256); + dc.col = xmalloc(dc.collen * sizeof(Color)); + } + + for (i = 0; i < dc.collen; i++) + if (!xloadcolor(i, NULL, &dc.col[i])) { + if (colorname[i]) + die("could not allocate color '%s'\n", colorname[i]); + else + die("could not allocate color %d\n", i); + } + + /* set alpha value of bg color */ + if (opt_alpha) + alpha = strtof(opt_alpha, NULL); + dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); + dc.col[defaultbg].pixel &= 0x00FFFFFF; + dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; + loaded = 1; +} + +int +xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) +{ + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + *r = dc.col[x].color.red >> 8; + *g = dc.col[x].color.green >> 8; + *b = dc.col[x].color.blue >> 8; + + return 0; +} + +int +xsetcolorname(int x, const char *name) +{ + Color ncolor; + + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + if (!xloadcolor(x, name, &ncolor)) + return 1; + + XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); + dc.col[x] = ncolor; + + return 0; +} + +/* + * Absolute coordinates. + */ +void +xclear(int x1, int y1, int x2, int y2) +{ + XftDrawRect(xw.draw, + &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], + x1, y1, x2-x1, y2-y1); +} + +void +xhints(void) +{ + XClassHint class = {opt_name ? opt_name : termname, + opt_class ? opt_class : termname}; + XWMHints wm = {.flags = InputHint, .input = 1}; + XSizeHints *sizeh; + + sizeh = XAllocSizeHints(); + + sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; + sizeh->height = win.h; + sizeh->width = win.w; + sizeh->height_inc = win.ch; + sizeh->width_inc = win.cw; + sizeh->base_height = 2 * borderpx; + sizeh->base_width = 2 * borderpx; + sizeh->min_height = win.ch + 2 * borderpx; + sizeh->min_width = win.cw + 2 * borderpx; + if (xw.isfixed) { + sizeh->flags |= PMaxSize; + sizeh->min_width = sizeh->max_width = win.w; + sizeh->min_height = sizeh->max_height = win.h; + } + if (xw.gm & (XValue|YValue)) { + sizeh->flags |= USPosition | PWinGravity; + sizeh->x = xw.l; + sizeh->y = xw.t; + sizeh->win_gravity = xgeommasktogravity(xw.gm); + } + + XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, + &class); + XFree(sizeh); +} + +int +xgeommasktogravity(int mask) +{ + switch (mask & (XNegative|YNegative)) { + case 0: + return NorthWestGravity; + case XNegative: + return NorthEastGravity; + case YNegative: + return SouthWestGravity; + } + + return SouthEastGravity; +} + +int +xloadfont(Font *f, FcPattern *pattern) +{ + FcPattern *configured; + FcPattern *match; + FcResult result; + XGlyphInfo extents; + int wantattr, haveattr; + + /* + * Manually configure instead of calling XftMatchFont + * so that we can use the configured pattern for + * "missing glyph" lookups. + */ + configured = FcPatternDuplicate(pattern); + if (!configured) + return 1; + + FcConfigSubstitute(NULL, configured, FcMatchPattern); + XftDefaultSubstitute(xw.dpy, xw.scr, configured); + + match = FcFontMatch(NULL, configured, &result); + if (!match) { + FcPatternDestroy(configured); + return 1; + } + + if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { + FcPatternDestroy(configured); + FcPatternDestroy(match); + return 1; + } + + if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == + XftResultMatch)) { + /* + * Check if xft was unable to find a font with the appropriate + * slant but gave us one anyway. Try to mitigate. + */ + if ((XftPatternGetInteger(f->match->pattern, "slant", 0, + &haveattr) != XftResultMatch) || haveattr < wantattr) { + f->badslant = 1; + fputs("font slant does not match\n", stderr); + } + } + + if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == + XftResultMatch)) { + if ((XftPatternGetInteger(f->match->pattern, "weight", 0, + &haveattr) != XftResultMatch) || haveattr != wantattr) { + f->badweight = 1; + fputs("font weight does not match\n", stderr); + } + } + + XftTextExtentsUtf8(xw.dpy, f->match, + (const FcChar8 *) ascii_printable, + strlen(ascii_printable), &extents); + + f->set = NULL; + f->pattern = configured; + + f->ascent = f->match->ascent; + f->descent = f->match->descent; + f->lbearing = 0; + f->rbearing = f->match->max_advance_width; + + f->height = f->ascent + f->descent; + f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); + + return 0; +} + +void +xloadfonts(const char *fontstr, double fontsize) +{ + FcPattern *pattern; + double fontval; + + if (fontstr[0] == '-') + pattern = XftXlfdParse(fontstr, False, False); + else + pattern = FcNameParse((const FcChar8 *)fontstr); + + if (!pattern) + die("can't open font %s\n", fontstr); + + if (fontsize > 1) { + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternDel(pattern, FC_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); + usedfontsize = fontsize; + } else { + if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = fontval; + } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = -1; + } else { + /* + * Default font size is 12, if none given. This is to + * have a known usedfontsize value. + */ + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); + usedfontsize = 12; + } + defaultfontsize = usedfontsize; + } + + if (xloadfont(&dc.font, pattern)) + die("can't open font %s\n", fontstr); + + if (usedfontsize < 0) { + FcPatternGetDouble(dc.font.match->pattern, + FC_PIXEL_SIZE, 0, &fontval); + usedfontsize = fontval; + if (fontsize == 0) + defaultfontsize = fontval; + } + + /* Setting character width and height. */ + win.cw = ceilf(dc.font.width * cwscale); + win.ch = ceilf(dc.font.height * chscale); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + if (xloadfont(&dc.ifont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_WEIGHT); + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + if (xloadfont(&dc.ibfont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + if (xloadfont(&dc.bfont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDestroy(pattern); +} + +void +xunloadfont(Font *f) +{ + XftFontClose(xw.dpy, f->match); + FcPatternDestroy(f->pattern); + if (f->set) + FcFontSetDestroy(f->set); +} + +void +xunloadfonts(void) +{ + /* Free the loaded fonts in the font cache. */ + while (frclen > 0) + XftFontClose(xw.dpy, frc[--frclen].font); + + xunloadfont(&dc.font); + xunloadfont(&dc.bfont); + xunloadfont(&dc.ifont); + xunloadfont(&dc.ibfont); +} + +int +ximopen(Display *dpy) +{ + XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; + XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; + + xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); + if (xw.ime.xim == NULL) + return 0; + + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) + fprintf(stderr, "XSetIMValues: " + "Could not set XNDestroyCallback.\n"); + + xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, + NULL); + + if (xw.ime.xic == NULL) { + xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, + XNDestroyCallback, &icdestroy, + NULL); + } + if (xw.ime.xic == NULL) + fprintf(stderr, "XCreateIC: Could not create input context.\n"); + + return 1; +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + if (ximopen(dpy)) + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.ime.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + XFree(xw.ime.spotlist); +} + +int +xicdestroy(XIC xim, XPointer client, XPointer call) +{ + xw.ime.xic = NULL; + return 1; +} + +void +xinit(int cols, int rows) +{ + XGCValues gcvalues; + Cursor cursor; + Window parent; + pid_t thispid = getpid(); + XColor xmousefg, xmousebg; + XWindowAttributes attr; + XVisualInfo vis; + + if (!(xw.dpy = XOpenDisplay(NULL))) + die("can't open display\n"); + xw.scr = XDefaultScreen(xw.dpy); + + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { + parent = XRootWindow(xw.dpy, xw.scr); + xw.depth = 32; + } else { + XGetWindowAttributes(xw.dpy, parent, &attr); + xw.depth = attr.depth; + } + + XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); + xw.vis = vis.visual; + + /* font */ + if (!FcInit()) + die("could not init fontconfig.\n"); + + usedfont = (opt_font == NULL)? font : opt_font; + xloadfonts(usedfont, 0); + + /* colors */ + xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); + xloadcols(); + + /* adjust fixed window geometry */ + win.w = 2 * borderpx + cols * win.cw; + win.h = 2 * borderpx + rows * win.ch; + if (xw.gm & XNegative) + xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; + if (xw.gm & YNegative) + xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; + + /* Events */ + xw.attrs.background_pixel = dc.col[defaultbg].pixel; + xw.attrs.border_pixel = dc.col[defaultbg].pixel; + xw.attrs.bit_gravity = NorthWestGravity; + xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask + | ExposureMask | VisibilityChangeMask | StructureNotifyMask + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + xw.attrs.colormap = xw.cmap; + + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, + win.w, win.h, 0, xw.depth, InputOutput, + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &xw.attrs); + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); + dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + + /* font spec buffer */ + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); + + /* Xft rendering context */ + xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); + + /* input methods */ + if (!ximopen(xw.dpy)) { + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + } + + /* white cursor, black outline */ + cursor = XCreateFontCursor(xw.dpy, mouseshape); + XDefineCursor(xw.dpy, xw.win, cursor); + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { + xmousefg.red = 0xffff; + xmousefg.green = 0xffff; + xmousefg.blue = 0xffff; + } + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { + xmousebg.red = 0x0000; + xmousebg.green = 0x0000; + xmousebg.blue = 0x0000; + } + + XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); + + xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); + xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); + xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); + xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); + XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); + + xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); + XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, + PropModeReplace, (uchar *)&thispid, 1); + + win.mode = MODE_NUMLOCK; + resettitle(); + xhints(); + XMapWindow(xw.dpy, xw.win); + XSync(xw.dpy, False); + + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); + xsel.primary = NULL; + xsel.clipboard = NULL; + xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); + if (xsel.xtarget == None) + xsel.xtarget = XA_STRING; +} + +int +xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) +{ + float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; + ushort mode, prevmode = USHRT_MAX; + Font *font = &dc.font; + int frcflags = FRC_NORMAL; + float runewidth = win.cw; + Rune rune; + FT_UInt glyphidx; + FcResult fcres; + FcPattern *fcpattern, *fontpattern; + FcFontSet *fcsets[] = { NULL }; + FcCharSet *fccharset; + int i, f, numspecs = 0; + + for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { + /* Fetch rune and mode for current glyph. */ + rune = glyphs[i].u; + mode = glyphs[i].mode; + + /* Skip dummy wide-character spacing. */ + if (mode == ATTR_WDUMMY) + continue; + + /* Determine font for glyph if different from previous glyph. */ + if (prevmode != mode) { + prevmode = mode; + font = &dc.font; + frcflags = FRC_NORMAL; + runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { + font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } else if (mode & ATTR_ITALIC) { + font = &dc.ifont; + frcflags = FRC_ITALIC; + } else if (mode & ATTR_BOLD) { + font = &dc.bfont; + frcflags = FRC_BOLD; + } + yp = winy + font->ascent; + } + + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + if (glyphidx) { + specs[numspecs].font = font->match; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + continue; + } + + /* Fallback on font cache, search the font cache for match. */ + for (f = 0; f < frclen; f++) { + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); + /* Everything correct. */ + if (glyphidx && frc[f].flags == frcflags) + break; + /* We got a default font for a not found glyph. */ + if (!glyphidx && frc[f].flags == frcflags + && frc[f].unicodep == rune) { + break; + } + } + + /* Nothing was found. Use fontconfig to find matching font. */ + if (f >= frclen) { + if (!font->set) + font->set = FcFontSort(0, font->pattern, + 1, 0, &fcres); + fcsets[0] = font->set; + + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + * + * Xft and fontconfig are design failures. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, + fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + + FcConfigSubstitute(0, fcpattern, + FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + fontpattern = FcFontSetMatch(0, fcsets, 1, + fcpattern, &fcres); + + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } + + frc[frclen].font = XftFontOpenPattern(xw.dpy, + fontpattern); + if (!frc[frclen].font) + die("XftFontOpenPattern failed seeking fallback font: %s\n", + strerror(errno)); + frc[frclen].flags = frcflags; + frc[frclen].unicodep = rune; + + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); + + f = frclen; + frclen++; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + } + + specs[numspecs].font = frc[f].font; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + } + + return numspecs; +} + +void +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) +{ + int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); + int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, + width = charlen * win.cw; + Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; + XRenderColor colfg, colbg; + XRectangle r; + + /* Fallback on color display for attributes not supported by the font */ + if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { + if (dc.ibfont.badslant || dc.ibfont.badweight) + base.fg = defaultattr; + } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || + (base.mode & ATTR_BOLD && dc.bfont.badweight)) { + base.fg = defaultattr; + } + + if (IS_TRUECOL(base.fg)) { + colfg.alpha = 0xffff; + colfg.red = TRUERED(base.fg); + colfg.green = TRUEGREEN(base.fg); + colfg.blue = TRUEBLUE(base.fg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); + fg = &truefg; + } else { + fg = &dc.col[base.fg]; + } + + if (IS_TRUECOL(base.bg)) { + colbg.alpha = 0xffff; + colbg.green = TRUEGREEN(base.bg); + colbg.red = TRUERED(base.bg); + colbg.blue = TRUEBLUE(base.bg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); + bg = &truebg; + } else { + bg = &dc.col[base.bg]; + } + + /* Change basic system colors [0-7] to bright system colors [8-15] */ + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) + fg = &dc.col[base.fg + 8]; + + if (IS_SET(MODE_REVERSE)) { + if (fg == &dc.col[defaultfg]) { + fg = &dc.col[defaultbg]; + } else { + colfg.red = ~fg->color.red; + colfg.green = ~fg->color.green; + colfg.blue = ~fg->color.blue; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, + &revfg); + fg = &revfg; + } + + if (bg == &dc.col[defaultbg]) { + bg = &dc.col[defaultfg]; + } else { + colbg.red = ~bg->color.red; + colbg.green = ~bg->color.green; + colbg.blue = ~bg->color.blue; + colbg.alpha = bg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, + &revbg); + bg = &revbg; + } + } + + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { + colfg.red = fg->color.red / 2; + colfg.green = fg->color.green / 2; + colfg.blue = fg->color.blue / 2; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); + fg = &revfg; + } + + if (base.mode & ATTR_REVERSE) { + temp = fg; + fg = bg; + bg = temp; + } + + if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) + fg = bg; + + if (base.mode & ATTR_INVISIBLE) + fg = bg; + + /* Intelligent cleaning up of the borders. */ + if (x == 0) { + xclear(0, (y == 0)? 0 : winy, borderpx, + winy + win.ch + + ((winy + win.ch >= borderpx + win.th)? win.h : 0)); + } + if (winx + width >= borderpx + win.tw) { + xclear(winx + width, (y == 0)? 0 : winy, win.w, + ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); + } + if (y == 0) + xclear(winx, 0, winx + width, borderpx); + if (winy + win.ch >= borderpx + win.th) + xclear(winx, winy + win.ch, winx + width, win.h); + + /* Clean up the region we want to draw to. */ + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + + /* Set the clip region because Xft is sometimes dirty. */ + r.x = 0; + r.y = 0; + r.height = win.ch; + r.width = width; + XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + + /* Render underline and strikethrough. */ + if (base.mode & ATTR_UNDERLINE) { + XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, + width, 1); + } + + if (base.mode & ATTR_STRUCK) { + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, + width, 1); + } + + /* Reset clip to none. */ + XftDrawSetClip(xw.draw, 0); +} + +void +xdrawglyph(Glyph g, int x, int y) +{ + int numspecs; + XftGlyphFontSpec spec; + + numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); + xdrawglyphfontspecs(&spec, g, numspecs, x, y); +} + +void +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) +{ + Color drawcol; + + /* remove the old cursor */ + if (selected(ox, oy)) + og.mode ^= ATTR_REVERSE; + xdrawglyph(og, ox, oy); + + if (IS_SET(MODE_HIDE)) + return; + + /* + * Select the right color for the right mode. + */ + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; + + if (IS_SET(MODE_REVERSE)) { + g.mode |= ATTR_REVERSE; + g.bg = defaultfg; + if (selected(cx, cy)) { + drawcol = dc.col[defaultcs]; + g.fg = defaultrcs; + } else { + drawcol = dc.col[defaultrcs]; + g.fg = defaultcs; + } + } else { + if (selected(cx, cy)) { + g.fg = defaultfg; + g.bg = defaultrcs; + } else { + g.fg = defaultbg; + g.bg = defaultcs; + } + drawcol = dc.col[g.bg]; + } + + /* draw the new one */ + if (IS_SET(MODE_FOCUSED)) { + switch (win.cursor) { + case 7: /* st extension */ + g.u = 0x2603; /* snowman (U+2603) */ + /* FALLTHROUGH */ + case 0: /* Blinking Block */ + case 1: /* Blinking Block (Default) */ + case 2: /* Steady Block */ + xdrawglyph(g, cx, cy); + break; + case 3: /* Blinking Underline */ + case 4: /* Steady Underline */ + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - \ + cursorthickness, + win.cw, cursorthickness); + break; + case 5: /* Blinking bar */ + case 6: /* Steady bar */ + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + cursorthickness, win.ch); + break; + } + } else { + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + win.cw - 1, 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + (cx + 1) * win.cw - 1, + borderpx + cy * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - 1, + win.cw, 1); + } +} + +void +xsetenv(void) +{ + char buf[sizeof(long) * 8 + 1]; + + snprintf(buf, sizeof(buf), "%lu", xw.win); + setenv("WINDOWID", buf, 1); +} + +void +xseticontitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; + XSetWMIconName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); + XFree(prop.value); +} + +void +xsettitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; + XSetWMName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); + XFree(prop.value); +} + +int +xstartdraw(void) +{ + return IS_SET(MODE_VISIBLE); +} + +void +xdrawline(Line line, int x1, int y1, int x2) +{ + int i, x, ox, numspecs; + Glyph base, new; + XftGlyphFontSpec *specs = xw.specbuf; + + numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + if (new.mode == ATTR_WDUMMY) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1); +} + +void +xfinishdraw(void) +{ + XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, + win.h, 0, 0); + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(MODE_REVERSE)? + defaultfg : defaultbg].pixel); +} + +void +xximspot(int x, int y) +{ + if (xw.ime.xic == NULL) + return; + + xw.ime.spot.x = borderpx + x * win.cw; + xw.ime.spot.y = borderpx + (y + 1) * win.ch; + + XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); +} + +void +expose(XEvent *ev) +{ + redraw(); +} + +void +visibility(XEvent *ev) +{ + XVisibilityEvent *e = &ev->xvisibility; + + MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); +} + +void +unmap(XEvent *ev) +{ + win.mode &= ~MODE_VISIBLE; +} + +void +xsetpointermotion(int set) +{ + MODBIT(xw.attrs.event_mask, set, PointerMotionMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); +} + +void +xsetmode(int set, unsigned int flags) +{ + int mode = win.mode; + MODBIT(win.mode, set, flags); + if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) + redraw(); +} + +int +xsetcursor(int cursor) +{ + if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ + return 1; + win.cursor = cursor; + return 0; +} + +void +xseturgency(int add) +{ + XWMHints *h = XGetWMHints(xw.dpy, xw.win); + + MODBIT(h->flags, add, XUrgencyHint); + XSetWMHints(xw.dpy, xw.win, h); + XFree(h); +} + +void +xbell(void) +{ + if (!(IS_SET(MODE_FOCUSED))) + xseturgency(1); + if (bellvolume) + XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); +} + +void +focus(XEvent *ev) +{ + XFocusChangeEvent *e = &ev->xfocus; + + if (e->mode == NotifyGrab) + return; + + if (ev->type == FocusIn) { + if (xw.ime.xic) + XSetICFocus(xw.ime.xic); + win.mode |= MODE_FOCUSED; + xseturgency(0); + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[I", 3, 0); + } else { + if (xw.ime.xic) + XUnsetICFocus(xw.ime.xic); + win.mode &= ~MODE_FOCUSED; + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[O", 3, 0); + } +} + +int +match(uint mask, uint state) +{ + return mask == XK_ANY_MOD || mask == (state & ~ignoremod); +} + +char* +kmap(KeySym k, uint state) +{ + Key *kp; + int i; + + /* Check for mapped keys out of X11 function keys. */ + for (i = 0; i < LEN(mappedkeys); i++) { + if (mappedkeys[i] == k) + break; + } + if (i == LEN(mappedkeys)) { + if ((k & 0xFFFF) < 0xFD00) + return NULL; + } + + for (kp = key; kp < key + LEN(key); kp++) { + if (kp->k != k) + continue; + + if (!match(kp->mask, state)) + continue; + + if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) + continue; + if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) + continue; + + if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) + continue; + + return kp->s; + } + + return NULL; +} + +void +kpress(XEvent *ev) +{ + XKeyEvent *e = &ev->xkey; + KeySym ksym; + char buf[64], *customkey; + int len; + Rune c; + Status status; + Shortcut *bp; + + if (IS_SET(MODE_KBDLOCK)) + return; + + if (xw.ime.xic) + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); + else + len = XLookupString(e, buf, sizeof buf, &ksym, NULL); + /* 1. shortcuts */ + for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { + if (ksym == bp->keysym && match(bp->mod, e->state)) { + bp->func(&(bp->arg)); + return; + } + } + + /* 2. custom keys from config.h */ + if ((customkey = kmap(ksym, e->state))) { + ttywrite(customkey, strlen(customkey), 1); + return; + } + + /* 3. composed string from input method */ + if (len == 0) + return; + if (len == 1 && e->state & Mod1Mask) { + if (IS_SET(MODE_8BIT)) { + if (*buf < 0177) { + c = *buf | 0x80; + len = utf8encode(c, buf); + } + } else { + buf[1] = buf[0]; + buf[0] = '\033'; + len = 2; + } + } + ttywrite(buf, len, 1); +} + +void +cmessage(XEvent *e) +{ + /* + * See xembed specs + * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html + */ + if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { + if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { + win.mode |= MODE_FOCUSED; + xseturgency(0); + } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { + win.mode &= ~MODE_FOCUSED; + } + } else if (e->xclient.data.l[0] == xw.wmdeletewin) { + ttyhangup(); + exit(0); + } +} + +void +resize(XEvent *e) +{ + if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) + return; + + cresize(e->xconfigure.width, e->xconfigure.height); +} + +void +run(void) +{ + XEvent ev; + int w = win.w, h = win.h; + fd_set rfd; + int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; + struct timespec seltv, *tv, now, lastblink, trigger; + double timeout; + + /* Waiting for window mapping */ + do { + XNextEvent(xw.dpy, &ev); + /* + * This XFilterEvent call is required because of XOpenIM. It + * does filter out the key event and some client message for + * the input method too. + */ + if (XFilterEvent(&ev, None)) + continue; + if (ev.type == ConfigureNotify) { + w = ev.xconfigure.width; + h = ev.xconfigure.height; + } + } while (ev.type != MapNotify); + + ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); + cresize(w, h); + + for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { + FD_ZERO(&rfd); + FD_SET(ttyfd, &rfd); + FD_SET(xfd, &rfd); + + if (XPending(xw.dpy)) + timeout = 0; /* existing events might not set xfd */ + + seltv.tv_sec = timeout / 1E3; + seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); + tv = timeout >= 0 ? &seltv : NULL; + + if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + clock_gettime(CLOCK_MONOTONIC, &now); + + if (FD_ISSET(ttyfd, &rfd)) + ttyread(); + + xev = 0; + while (XPending(xw.dpy)) { + xev = 1; + XNextEvent(xw.dpy, &ev); + if (XFilterEvent(&ev, None)) + continue; + if (handler[ev.type]) + (handler[ev.type])(&ev); + } + + /* + * To reduce flicker and tearing, when new content or event + * triggers drawing, we first wait a bit to ensure we got + * everything, and if nothing new arrives - we draw. + * We start with trying to wait minlatency ms. If more content + * arrives sooner, we retry with shorter and shorter periods, + * and eventually draw even without idle after maxlatency ms. + * Typically this results in low latency while interacting, + * maximum latency intervals during `cat huge.txt`, and perfect + * sync with periodic updates from animations/key-repeats/etc. + */ + if (FD_ISSET(ttyfd, &rfd) || xev) { + if (!drawing) { + trigger = now; + drawing = 1; + } + timeout = (maxlatency - TIMEDIFF(now, trigger)) \ + / maxlatency * minlatency; + if (timeout > 0) + continue; /* we have time, try to find idle */ + } + + /* idle detected or maxlatency exhausted -> draw */ + timeout = -1; + if (blinktimeout && tattrset(ATTR_BLINK)) { + timeout = blinktimeout - TIMEDIFF(now, lastblink); + if (timeout <= 0) { + if (-timeout > blinktimeout) /* start visible */ + win.mode |= MODE_BLINK; + win.mode ^= MODE_BLINK; + tsetdirtattr(ATTR_BLINK); + lastblink = now; + timeout = blinktimeout; + } + } + + draw(); + XFlush(xw.dpy); + drawing = 0; + } +} + +void +usage(void) +{ + die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid]" + " [[-e] command [args ...]]\n" + " %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid] -l line" + " [stty_args ...]\n", argv0, argv0); +} + +int +main(int argc, char *argv[]) +{ + xw.l = xw.t = 0; + xw.isfixed = False; + xsetcursor(cursorshape); + + ARGBEGIN { + case 'a': + allowaltscreen = 0; + break; + case 'A': + opt_alpha = EARGF(usage()); + break; + case 'c': + opt_class = EARGF(usage()); + break; + case 'e': + if (argc > 0) + --argc, ++argv; + goto run; + case 'f': + opt_font = EARGF(usage()); + break; + case 'g': + xw.gm = XParseGeometry(EARGF(usage()), + &xw.l, &xw.t, &cols, &rows); + break; + case 'i': + xw.isfixed = 1; + break; + case 'o': + opt_io = EARGF(usage()); + break; + case 'l': + opt_line = EARGF(usage()); + break; + case 'n': + opt_name = EARGF(usage()); + break; + case 't': + case 'T': + opt_title = EARGF(usage()); + break; + case 'w': + opt_embed = EARGF(usage()); + break; + case 'v': + die("%s " VERSION "\n", argv0); + break; + default: + usage(); + } ARGEND; + +run: + if (argc > 0) /* eat all remaining arguments */ + opt_cmd = argv; + + if (!opt_title) + opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; + + setlocale(LC_CTYPE, ""); + XSetLocaleModifiers(""); + cols = MAX(cols, 1); + rows = MAX(rows, 1); + tnew(cols, rows); + xinit(cols, rows); + xsetenv(); + selinit(); + run(); + + return 0; +} diff --git a/st/x.o b/st/x.o index d2b800743fc2d1a04b7a974b9a5e982b36d87407..4d4f3c37249d9490b983040bae3a50301d83bf09 100644 GIT binary patch delta 22109 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 delta 21620 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