From ec5eae09849c6f2f60c5037173b68a0bd59729b3 Mon Sep 17 00:00:00 2001 From: lickthecheese Date: Tue, 1 Oct 2019 14:07:56 -0400 Subject: [PATCH] changed st opacity --- .bashrc | 2 +- git/st/.xinitrc | 3 + git/st/FAQ | 194 ++ git/st/LEGACY | 17 + git/st/LICENSE | 34 + git/st/Makefile | 57 + git/st/README | 34 + git/st/TODO | 28 + git/st/arg.h | 50 + git/st/config.def.h | 480 +++ git/st/config.def.h.orig | 473 +++ git/st/config.h | 480 +++ git/st/config.mk | 35 + git/st/st | Bin 0 -> 102176 bytes git/st/st-alpha-0.8.2.diff | 163 ++ ...-visualbell2-basic-2018-10-16-30ec9a3.diff | 204 ++ git/st/st.1 | 176 ++ git/st/st.c | 2587 +++++++++++++++++ git/st/st.h | 124 + git/st/st.h.orig | 123 + git/st/st.info | 222 ++ git/st/st.o | Bin 0 -> 72808 bytes git/st/win.h | 39 + git/st/x.c | 2045 +++++++++++++ git/st/x.c.orig | 2025 +++++++++++++ git/st/x.o | Bin 0 -> 74464 bytes 26 files changed, 9594 insertions(+), 1 deletion(-) create mode 100644 git/st/.xinitrc create mode 100644 git/st/FAQ create mode 100644 git/st/LEGACY create mode 100644 git/st/LICENSE create mode 100644 git/st/Makefile create mode 100644 git/st/README create mode 100644 git/st/TODO create mode 100644 git/st/arg.h create mode 100644 git/st/config.def.h create mode 100644 git/st/config.def.h.orig create mode 100644 git/st/config.h create mode 100644 git/st/config.mk create mode 100755 git/st/st create mode 100644 git/st/st-alpha-0.8.2.diff create mode 100644 git/st/st-visualbell2-basic-2018-10-16-30ec9a3.diff create mode 100644 git/st/st.1 create mode 100644 git/st/st.c create mode 100644 git/st/st.h create mode 100644 git/st/st.h.orig create mode 100644 git/st/st.info create mode 100644 git/st/st.o create mode 100644 git/st/win.h create mode 100644 git/st/x.c create mode 100644 git/st/x.c.orig create mode 100644 git/st/x.o diff --git a/.bashrc b/.bashrc index 4fe7c77..b30ccd4 100755 --- a/.bashrc +++ b/.bashrc @@ -57,7 +57,7 @@ if [ -n "$force_color_prompt" ]; then fi if [ "$color_prompt" = yes ]; then - export PS1="\[$(tput bold)\]\[$(tput setaf 34)\][\[$(tput setaf 28)\]\u\[$(tput setaf 40)\]@\[$(tput setaf 28)\]\h \[$(tput setaf 40)\]\W\[$(tput setaf 34)\]]\[$(tput setaf 22)\]\\$ \[$(tput sgr0)\]" + export PS1="\[$(tput bold)\]\[$(tput setaf 34)\][\[$(tput setaf 28)\]\u\[$(tput setaf 40)\]@\[$(tput setaf 28)\]\h \[$(tput setaf 40)\]\W\[$(tput setaf 34)\]]\[$(tput setaf 28)\]\\$ \[$(tput sgr0)\]" else PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' fi diff --git a/git/st/.xinitrc b/git/st/.xinitrc new file mode 100644 index 0000000..c8bd36d --- /dev/null +++ b/git/st/.xinitrc @@ -0,0 +1,3 @@ + + +exec i3 diff --git a/git/st/FAQ b/git/st/FAQ new file mode 100644 index 0000000..ecf7af8 --- /dev/null +++ b/git/st/FAQ @@ -0,0 +1,194 @@ +## Why does st not handle utmp entries? + +Use the excellent tool of [utmp](http://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. + +## I get some weird glitches/visual bug on _random program_! + +Try launching it with a different TERM: $ TERM=xterm myapp. toe(1) will give +you a list of available terminals, but you’ll most likely switch between xterm, +st or st-256color. The default value for TERM can be changed in config.h +(TNAME). + +## How do I scroll back up? + +Using a terminal multiplexer. + +* `st -e tmux` using C-b [ +* `st -e screen` using C-a ESC + +## 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] http://st.suckless.org/patches/delkey + +## 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/git/st/LEGACY b/git/st/LEGACY new file mode 100644 index 0000000..bf28b1e --- /dev/null +++ b/git/st/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/git/st/LICENSE b/git/st/LICENSE new file mode 100644 index 0000000..c356c39 --- /dev/null +++ b/git/st/LICENSE @@ -0,0 +1,34 @@ +MIT/X Consortium License + +© 2014-2018 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/git/st/Makefile b/git/st/Makefile new file mode 100644 index 0000000..470ac86 --- /dev/null +++ b/git/st/Makefile @@ -0,0 +1,57 @@ +# 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 + tic -sx st.info + @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/git/st/README b/git/st/README new file mode 100644 index 0000000..6a846ed --- /dev/null +++ b/git/st/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/git/st/TODO b/git/st/TODO new file mode 100644 index 0000000..5f74cd5 --- /dev/null +++ b/git/st/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/git/st/arg.h b/git/st/arg.h new file mode 100644 index 0000000..a22e019 --- /dev/null +++ b/git/st/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/git/st/config.def.h b/git/st/config.def.h new file mode 100644 index 0000000..7f84b54 --- /dev/null +++ b/git/st/config.def.h @@ -0,0 +1,480 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "monospace:pixelsize=11"; +static char *font2[] = { + "Symbola: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: utmp option + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/bash"; +char *utmp = 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; + +/* frames per second st should at maximum draw to the screen */ +static unsigned int xfps = 120; +static unsigned int actionfps = 30; + +/* + * 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; + +/* + * visual-bell timeout (set to 0 to disable visual-bell). + */ +static int vbelltimeout = 0; +/* + * visual bell mode when enabled: + * 1: Inverse whole screen + * 2: Inverse outer (border) cells + */ +static int vbellmode = 1; + +/* 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.4; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "black", + "red3", + "green3", + "yellow3", + "blue2", + "magenta3", + "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", + "red", + "green", + "yellow", + "#5c5cff", + "magenta", + "cyan", + "white", + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#cccccc", + "#555555", + "black", +}; + + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 7; +unsigned int defaultbg = 258; +static unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 2; + +/* + * Default columns and rows numbers + */ + +static unsigned int cols = 80; +static unsigned int rows = 24; + +/* + * Default colour and shape of the mouse cursor + */ +static unsigned int mouseshape = XC_xterm; +static unsigned int mousefg = 7; +static unsigned int mousebg = 0; + +/* + * Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. + */ +static unsigned int defaultattr = 11; + +/* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. + */ +static MouseShortcut mshortcuts[] = { + /* button mask string */ + { Button4, XK_ANY_MOD, "\031" }, + { Button5, XK_ANY_MOD, "\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} }, +}; + +/* + * 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 + * crlf value + * * 0: no value + * * > 0: crlf mode is enabled + * * < 0: crlf mode is 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; + +/* + * Override mouse-select 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 forceselmod = ShiftMask; + +/* + * 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/git/st/config.def.h.orig b/git/st/config.def.h.orig new file mode 100644 index 0000000..e73b83e --- /dev/null +++ b/git/st/config.def.h.orig @@ -0,0 +1,473 @@ +/* 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: utmp option + * 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; +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; + +/* frames per second st should at maximum draw to the screen */ +static unsigned int xfps = 120; +static unsigned int actionfps = 30; + +/* + * 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; + +/* + * visual-bell timeout (set to 0 to disable visual-bell). + */ +static int vbelltimeout = 0; +/* + * visual bell mode when enabled: + * 1: Inverse whole screen + * 2: Inverse outer (border) cells + */ +static int vbellmode = 1; + +/* 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; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "black", + "red3", + "green3", + "yellow3", + "blue2", + "magenta3", + "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", + "red", + "green", + "yellow", + "#5c5cff", + "magenta", + "cyan", + "white", + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#cccccc", + "#555555", +}; + + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 7; +unsigned int defaultbg = 0; +static unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 2; + +/* + * Default columns and rows numbers + */ + +static unsigned int cols = 80; +static unsigned int rows = 24; + +/* + * Default colour and shape of the mouse cursor + */ +static unsigned int mouseshape = XC_xterm; +static unsigned int mousefg = 7; +static unsigned int mousebg = 0; + +/* + * Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. + */ +static unsigned int defaultattr = 11; + +/* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. + */ +static MouseShortcut mshortcuts[] = { + /* button mask string */ + { Button4, XK_ANY_MOD, "\031" }, + { Button5, XK_ANY_MOD, "\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} }, +}; + +/* + * 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 + * crlf value + * * 0: no value + * * > 0: crlf mode is enabled + * * < 0: crlf mode is 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; + +/* + * Override mouse-select 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 forceselmod = ShiftMask; + +/* + * 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/git/st/config.h b/git/st/config.h new file mode 100644 index 0000000..7f84b54 --- /dev/null +++ b/git/st/config.h @@ -0,0 +1,480 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "monospace:pixelsize=11"; +static char *font2[] = { + "Symbola: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: utmp option + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/bash"; +char *utmp = 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; + +/* frames per second st should at maximum draw to the screen */ +static unsigned int xfps = 120; +static unsigned int actionfps = 30; + +/* + * 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; + +/* + * visual-bell timeout (set to 0 to disable visual-bell). + */ +static int vbelltimeout = 0; +/* + * visual bell mode when enabled: + * 1: Inverse whole screen + * 2: Inverse outer (border) cells + */ +static int vbellmode = 1; + +/* 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.4; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "black", + "red3", + "green3", + "yellow3", + "blue2", + "magenta3", + "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", + "red", + "green", + "yellow", + "#5c5cff", + "magenta", + "cyan", + "white", + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#cccccc", + "#555555", + "black", +}; + + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 7; +unsigned int defaultbg = 258; +static unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 2; + +/* + * Default columns and rows numbers + */ + +static unsigned int cols = 80; +static unsigned int rows = 24; + +/* + * Default colour and shape of the mouse cursor + */ +static unsigned int mouseshape = XC_xterm; +static unsigned int mousefg = 7; +static unsigned int mousebg = 0; + +/* + * Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. + */ +static unsigned int defaultattr = 11; + +/* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. + */ +static MouseShortcut mshortcuts[] = { + /* button mask string */ + { Button4, XK_ANY_MOD, "\031" }, + { Button5, XK_ANY_MOD, "\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} }, +}; + +/* + * 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 + * crlf value + * * 0: no value + * * > 0: crlf mode is enabled + * * < 0: crlf mode is 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; + +/* + * Override mouse-select 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 forceselmod = ShiftMask; + +/* + * 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/git/st/config.mk b/git/st/config.mk new file mode 100644 index 0000000..1d2f0e2 --- /dev/null +++ b/git/st/config.mk @@ -0,0 +1,35 @@ +# st version +VERSION = 0.8.2 + +# 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/git/st/st b/git/st/st new file mode 100755 index 0000000000000000000000000000000000000000..75489e285e745acd59dac020fe91d9398b596bed GIT binary patch literal 102176 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�fft!FyvCeoaid~@I@;LM4KuX+qh?^CM;=hOd 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 +Date: Mon, 15 Oct 2018 01:06:01 +0300 +Subject: [PATCH] [vbell2] add visual bell with two rendering modes + +- Inverse the whole terminal - "standard" visual-bell, a bit jarring. +- Inverse outer (border) cells - much less jarring, yet plenty visible. + +Note: blink used a timeout of 1us after drawing, probably to +re-calculate the timeout without being affected by draw speed. This was +changed to 1ms for code simplicity, and should be inconsequential. +--- + config.def.h | 11 ++++++++ + st.c | 1 - + st.h | 1 + + x.c | 77 +++++++++++++++++++++++++++++++++++++++------------- + 4 files changed, 70 insertions(+), 20 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 823e79f..0915ce5 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -62,6 +62,17 @@ static unsigned int cursorthickness = 2; + */ + static int bellvolume = 0; + ++/* ++ * visual-bell timeout (set to 0 to disable visual-bell). ++ */ ++static int vbelltimeout = 0; ++/* ++ * visual bell mode when enabled: ++ * 1: Inverse whole screen ++ * 2: Inverse outer (border) cells ++ */ ++static int vbellmode = 1; ++ + /* default TERM value */ + char *termname = "st-256color"; + +diff --git a/st.c b/st.c +index 46cf2da..1229479 100644 +--- a/st.c ++++ b/st.c +@@ -193,7 +193,6 @@ static void tsetscroll(int, int); + static void tswapscreen(void); + static void tsetmode(int, int, 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); +diff --git a/st.h b/st.h +index 38c61c4..619d716 100644 +--- a/st.h ++++ b/st.h +@@ -89,6 +89,7 @@ int tattrset(int); + void tnew(int, int); + void tresize(int, int); + void tsetdirtattr(int); ++void tfulldirt(); + void ttyhangup(void); + int ttynew(char *, char *, char *, char **); + size_t ttyread(void); +diff --git a/x.c b/x.c +index 00cb6b1..7e66c6d 100644 +--- a/x.c ++++ b/x.c +@@ -82,6 +82,8 @@ typedef struct { + int cw; /* char width */ + int mode; /* window state/mode flags */ + int cursor; /* cursor style */ ++ int vbellset; /* 1 during visual bell, 0 otherwise */ ++ struct timespec lastvbell; + } TermWindow; + + typedef struct { +@@ -173,6 +175,9 @@ static void mousereport(XEvent *); + static char *kmap(KeySym, uint); + static int match(uint, uint); + ++static void vbellbegin(); ++static int isvbellcell(int x, int y); ++ + static void run(void); + static void usage(void); + +@@ -1528,6 +1533,8 @@ xdrawline(Line line, int x1, int y1, int x2) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; ++ if (win.vbellset && isvbellcell(x, y1)) ++ new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + specs += i; +@@ -1610,6 +1617,28 @@ xseturgency(int add) + XFree(h); + } + ++int ++isvbellcell(int x, int y) ++{ ++ if (vbellmode == 1) ++ return 1; ++ if (vbellmode == 2) ++ return y == 0 || y == win.th / win.ch - 1 || ++ x == 0 || x == win.tw / win.cw - 1; ++ return 0; ++} ++ ++void ++vbellbegin() { ++ clock_gettime(CLOCK_MONOTONIC, &win.lastvbell); ++ if (win.vbellset) /* already visible, just extend win.lastvbell */ ++ return; ++ win.vbellset = 1; ++ tfulldirt(); ++ draw(); ++ XFlush(xw.dpy); ++} ++ + void + xbell(void) + { +@@ -1617,6 +1646,8 @@ xbell(void) + xseturgency(1); + if (bellvolume) + XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); ++ if (vbelltimeout) ++ vbellbegin(); + } + + void +@@ -1770,7 +1801,7 @@ run(void) + int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0; + int ttyfd; + struct timespec drawtimeout, *tv = NULL, now, last, lastblink; +- long deltatime; ++ long deltatime, to_ms, remain; + + /* Waiting for window mapping */ + do { +@@ -1822,11 +1853,28 @@ run(void) + tv = &drawtimeout; + + dodraw = 0; +- if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) { +- tsetdirtattr(ATTR_BLINK); +- win.mode ^= MODE_BLINK; +- lastblink = now; +- dodraw = 1; ++ to_ms = -1; /* timeout in ms, indefinite if negative */ ++ if (blinkset) { ++ remain = blinktimeout - TIMEDIFF(now, lastblink); ++ if (remain <= 0) { ++ dodraw = 1; ++ remain = 1; /* draw, wait 1ms, and re-calc */ ++ tsetdirtattr(ATTR_BLINK); ++ win.mode ^= MODE_BLINK; ++ lastblink = now; ++ } ++ to_ms = remain; ++ } ++ if (win.vbellset) { ++ remain = vbelltimeout - TIMEDIFF(now, win.lastvbell); ++ if (remain <= 0) { ++ dodraw = 1; ++ remain = -1; /* draw (clear), and that's it */ ++ tfulldirt(); ++ win.vbellset = 0; ++ } ++ if (remain >= 0 && (to_ms < 0 || remain < to_ms)) ++ to_ms = remain; + } + deltatime = TIMEDIFF(now, last); + if (deltatime > 1000 / (xev ? xfps : actionfps)) { +@@ -1849,19 +1897,10 @@ run(void) + if (xev && !FD_ISSET(xfd, &rfd)) + xev--; + if (!FD_ISSET(ttyfd, &rfd) && !FD_ISSET(xfd, &rfd)) { +- if (blinkset) { +- if (TIMEDIFF(now, lastblink) \ +- > blinktimeout) { +- drawtimeout.tv_nsec = 1000; +- } else { +- drawtimeout.tv_nsec = (1E6 * \ +- (blinktimeout - \ +- TIMEDIFF(now, +- lastblink))); +- } +- drawtimeout.tv_sec = \ +- drawtimeout.tv_nsec / 1E9; +- drawtimeout.tv_nsec %= (long)1E9; ++ if (to_ms >= 0) { ++ static const long k = 1E3, m = 1E6; ++ drawtimeout.tv_sec = to_ms / k; ++ drawtimeout.tv_nsec = (to_ms % k) * m; + } else { + tv = NULL; + } +-- +2.19.1 + diff --git a/git/st/st.1 b/git/st/st.1 new file mode 100644 index 0000000..e8d6059 --- /dev/null +++ b/git/st/st.1 @@ -0,0 +1,176 @@ +.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) +.SH BUGS +See the TODO file in the distribution. + diff --git a/git/st/st.c b/git/st/st.c new file mode 100644 index 0000000..8a70985 --- /dev/null +++ b/git/st/st.c @@ -0,0 +1,2587 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st.h" +#include "win.h" + +#if defined(__linux) + #include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + #include +#elif defined(__FreeBSD__) || defined(__DragonFly__) + #include +#endif + +/* Arbitrary sizes */ +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 +#define ESC_BUF_SIZ (128*UTF_SIZ) +#define ESC_ARG_SIZ 16 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ + +/* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) + +enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_ALTSCREEN = 1 << 2, + MODE_CRLF = 1 << 3, + MODE_ECHO = 1 << 4, + MODE_PRINT = 1 << 5, + MODE_UTF8 = 1 << 6, + MODE_SIXEL = 1 << 7, +}; + +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, /* 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, + ESC_DCS =128, +}; + +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +typedef struct { + int mode; + int type; + int snap; + /* + * Selection variables: + * nb – normalized coordinates of the beginning of the selection + * ne – normalized coordinates of the end of the selection + * ob – original coordinates of the beginning of the selection + * oe – original coordinates of the end of the selection + */ + struct { + int x, y; + } nb, ne, ob, oe; + + int alt; +} Selection; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int *tabs; +} Term; + +/* CSI Escape sequence structs */ +/* ESC '[' [[ [] [;]] []] */ +typedef struct { + char buf[ESC_BUF_SIZ]; /* raw string */ + int 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[STR_BUF_SIZ]; /* raw string */ + int 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); +static void tscrolldown(int, int); +static void tsetattr(int *, int); +static void tsetchar(Rune, Glyph *, int, int); +static void tsetdirt(int, int); +static void tsetscroll(int, int); +static void tswapscreen(void); +static void tsetmode(int, int, int *, int); +static int twrite(const char *, int, int); +static void tcontrolcode(uchar ); +static void tdectest(char ); +static void tdefutf8(char); +static int32_t tdefcolor(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 uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static 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(char *s) +{ + if ((s = strdup(s)) == NULL) + die("strdup: %s\n", strerror(errno)); + + return s; +} + +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)++); +} + +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)]; + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (c == -1) + break; + *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == -1) + break; + *dst++ = ((c & 0x03) << 6) | d; + } + *dst = '\0'; + return result; +} + +void +selinit(void) +{ + sel.mode = SEL_IDLE; + sel.snap = 0; + sel.ob.x = -1; +} + +int +tlinelen(int y) +{ + int i = term.col; + + if (term.line[y][i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && term.line[y][i - 1].u == ' ') + --i; + + return i; +} + +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type, int done) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + + if (sel.mode == SEL_IDLE) + return; + if (done && sel.mode == SEL_EMPTY) { + selclear(); + return; + } + + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + + sel.mode = done ? SEL_IDLE : SEL_READY; +} + +void +selnormalize(void) +{ + int i; + + if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { + sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; + sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; + } else { + sel.nb.x = MIN(sel.ob.x, sel.oe.x); + sel.ne.x = MAX(sel.ob.x, sel.oe.x); + } + sel.nb.y = MIN(sel.ob.y, sel.oe.y); + sel.ne.y = MAX(sel.ob.y, sel.oe.y); + + selsnap(&sel.nb.x, &sel.nb.y, -1); + selsnap(&sel.ne.x, &sel.ne.y, +1); + + /* expand selection over line breaks */ + if (sel.type == SEL_RECTANGULAR) + return; + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) + sel.nb.x = i; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; +} + +int +selected(int x, int y) +{ + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) + return 0; + + if (sel.type == SEL_RECTANGULAR) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); + + return BETWEEN(y, sel.nb.y, sel.ne.y) + && (y != sel.nb.y || x >= sel.nb.x) + && (y != sel.ne.y || x <= sel.ne.x); +} + +void +selsnap(int *x, int *y, int direction) +{ + int newx, newy, xt, yt; + int delim, prevdelim; + Glyph *gp, *prevgp; + + switch (sel.snap) { + case SNAP_WORD: + /* + * Snap around if the word wraps around at the end or + * beginning of a line. + */ + prevgp = &term.line[*y][*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; + newy = *y; + if (!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) + break; + + if (direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if (!(term.line[yt][xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + + gp = &term.line[newy][newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) + break; + + *x = newx; + *y = newy; + prevgp = gp; + prevdelim = delim; + } + break; + case SNAP_LINE: + /* + * Snap around if the the previous line or the current one + * has set ATTR_WRAP at its end. Then the whole next or + * previous line will be selected. + */ + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { + if (!(term.line[*y-1][term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { + if (!(term.line[*y][term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } + break; + } +} + +char * +getsel(void) +{ + char *str, *ptr; + int y, bufsize, lastx, linelen; + Glyph *gp, *last; + + if (sel.ob.x == -1) + return NULL; + + bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; + ptr = str = xmalloc(bufsize); + + /* append every set & selected glyph to the selection */ + for (y = sel.nb.y; y <= sel.ne.y; y++) { + if ((linelen = tlinelen(y)) == 0) { + *ptr++ = '\n'; + continue; + } + + if (sel.type == SEL_RECTANGULAR) { + gp = &term.line[y][sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &term.line[y][MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + + for ( ; gp <= last; ++gp) { + if (gp->mode & ATTR_WDUMMY) + continue; + + ptr += utf8encode(gp->u, ptr); + } + + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if ((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP)) + *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; + 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]; + else if (utmp) + prog = utmp; + else + prog = sh; + DEFAULT(args, ((char *[]) {prog, 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(char *line, char *cmd, 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); + 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)); + close(s); + close(m); +#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 written; + int ret; + + /* append read bytes to unprocessed bytes */ + if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0) + die("couldn't read from shell: %s\n", strerror(errno)); + buflen += ret; + + written = twrite(buf, buflen, 0); + buflen -= written; + /* keep any uncomplete utf8 char for the next call */ + if (buflen > 0) + memmove(buf, buf + written, buflen); + + return ret; +} + +void +ttywrite(const char *s, size_t n, int may_echo) +{ + const char *next; + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); + + if (!IS_SET(MODE_CRLF)) { + ttywriteraw(s, n); + return; + } + + /* This is similar to how the kernel handles ONLCR for ttys */ + while (n > 0) { + if (*s == '\r') { + next = s + 1; + ttywriteraw("\r\n", 2); + } else { + next = memchr(s, '\r', n); + DEFAULT(next, s + n); + ttywriteraw(s, next - s); + } + n -= next - s; + s = next; + } +} + +void +ttywriteraw(const char *s, size_t n) +{ + fd_set wfd, rfd; + ssize_t r; + size_t lim = 256; + + /* + * Remember that we are using a pty, which might be a modem line. + * Writing too much will clog the line. That's why we are doing this + * dance. + * FIXME: Migrate the world to Plan 9. + */ + while (n > 0) { + FD_ZERO(&wfd); + FD_ZERO(&rfd); + FD_SET(cmdfd, &wfd); + FD_SET(cmdfd, &rfd); + + /* Check if we can write. */ + if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(cmdfd, &wfd)) { + /* + * Only write the bytes written by ttywrite() or the + * default of 256. This seems to be a reasonable value + * for a serial line. Bigger values might clog the I/O. + */ + if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) + goto write_error; + if (r < n) { + /* + * We weren't able to write out everything. + * This means the buffer is getting full + * again. Empty it. + */ + if (n < lim) + lim = ttyread(); + n -= r; + s += r; + } else { + /* All bytes have been written. */ + break; + } + } + if (FD_ISSET(cmdfd, &rfd)) + lim = ttyread(); + } + return; + +write_error: + die("write error on tty: %s\n", strerror(errno)); +} + +void +ttyresize(int tw, int th) +{ + struct winsize w; + + w.ws_row = term.row; + w.ws_col = term.col; + w.ws_xpixel = tw; + w.ws_ypixel = th; + if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) + fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); +} + +void +ttyhangup() +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + +int +tattrset(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) + return 1; + } + } + + return 0; +} + +void +tsetdirt(int top, int bot) +{ + int i; + + LIMIT(top, 0, term.row-1); + LIMIT(bot, 0, term.row-1); + + for (i = top; i <= bot; i++) + term.dirty[i] = 1; +} + +void +tsetdirtattr(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) { + tsetdirt(i, i); + break; + } + } + } +} + +void +tfulldirt(void) +{ + tsetdirt(0, term.row-1); +} + +void +tcursor(int mode) +{ + static TCursor c[2]; + int alt = IS_SET(MODE_ALTSCREEN); + + if (mode == CURSOR_SAVE) { + c[alt] = term.c; + } else if (mode == CURSOR_LOAD) { + term.c = c[alt]; + tmoveto(c[alt].x, c[alt].y); + } +} + +void +treset(void) +{ + uint i; + + term.c = (TCursor){{ + .mode = ATTR_NULL, + .fg = defaultfg, + .bg = defaultbg + }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) + term.tabs[i] = 1; + term.top = 0; + term.bot = term.row - 1; + term.mode = MODE_WRAP|MODE_UTF8; + memset(term.trantbl, CS_USA, sizeof(term.trantbl)); + term.charset = 0; + + for (i = 0; i < 2; i++) { + tmoveto(0, 0); + tcursor(CURSOR_SAVE); + tclearregion(0, 0, term.col-1, term.row-1); + tswapscreen(); + } +} + +void +tnew(int col, int row) +{ + term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; + tresize(col, row); + treset(); +} + +void +tswapscreen(void) +{ + Line *tmp = term.line; + + term.line = term.alt; + term.alt = tmp; + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); +} + +void +tscrolldown(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + + for (i = term.bot; i >= orig+n; i--) { + temp = term.line[i]; + term.line[i] = term.line[i-n]; + term.line[i-n] = temp; + } + + selscroll(orig, n); +} + +void +tscrollup(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + + for (i = orig; i <= term.bot-n; i++) { + temp = term.line[i]; + term.line[i] = term.line[i+n]; + term.line[i+n] = temp; + } + + selscroll(orig, -n); +} + +void +selscroll(int orig, int n) +{ + if (sel.ob.x == -1) + return; + + if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) { + if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) { + selclear(); + return; + } + if (sel.type == SEL_RECTANGULAR) { + if (sel.ob.y < term.top) + sel.ob.y = term.top; + if (sel.oe.y > term.bot) + sel.oe.y = term.bot; + } else { + if (sel.ob.y < term.top) { + sel.ob.y = term.top; + sel.ob.x = 0; + } + if (sel.oe.y > term.bot) { + sel.oe.y = term.bot; + sel.oe.x = term.col; + } + } + selnormalize(); + } +} + +void +tnewline(int first_col) +{ + int y = term.c.y; + + if (y == term.bot) { + tscrollup(term.top, 1); + } else { + y++; + } + tmoveto(first_col ? 0 : term.c.x, y); +} + +void +csiparse(void) +{ + char *p = csiescseq.buf, *np; + long int v; + + 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, Glyph *attr, int x, int y) +{ + static char *vt100_0[62] = { /* 0x41 - 0x7e */ + "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ + 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ + "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ + "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ + }; + + /* + * The table is proudly stolen from rxvt. + */ + if (term.trantbl[term.charset] == CS_GRAPHIC0 && + BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) + utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); + + if (term.line[y][x].mode & ATTR_WIDE) { + if (x+1 < term.col) { + term.line[y][x+1].u = ' '; + term.line[y][x+1].mode &= ~ATTR_WDUMMY; + } + } else if (term.line[y][x].mode & ATTR_WDUMMY) { + term.line[y][x-1].u = ' '; + term.line[y][x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; + term.line[y][x] = *attr; + term.line[y][x].u = u; +} + +void +tclearregion(int x1, int y1, int x2, int y2) +{ + int x, y, temp; + Glyph *gp; + + if (x1 > x2) + temp = x1, x1 = x2, x2 = temp; + if (y1 > y2) + temp = y1, y1 = y2, y2 = temp; + + LIMIT(x1, 0, term.col-1); + LIMIT(x2, 0, term.col-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { + gp = &term.line[y][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + gp->u = ' '; + } + } +} + +void +tdeletechar(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +} + +void +tinsertblank(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +} + +void +tinsertblankline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrolldown(term.c.y, n); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n); +} + +int32_t +tdefcolor(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(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, int *args, int narg) +{ + int alt, *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 '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]); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[0]); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblankline(csiescseq.arg[0]); + break; + case 'l': /* RM -- Reset Mode */ + tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + break; + case 'M': /* DL -- Delete lines */ + DEFAULT(csiescseq.arg[0], 1); + tdeleteline(csiescseq.arg[0]); + break; + case 'X': /* ECH -- Erase char */ + DEFAULT(csiescseq.arg[0], 1); + tclearregion(term.c.x, term.c.y, + term.c.x + csiescseq.arg[0] - 1, term.c.y); + break; + case 'P': /* DCH -- Delete char */ + DEFAULT(csiescseq.arg[0], 1); + tdeletechar(csiescseq.arg[0]); + break; + case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(-csiescseq.arg[0]); + break; + case 'd': /* VPA -- Move to */ + DEFAULT(csiescseq.arg[0], 1); + tmoveato(term.c.x, csiescseq.arg[0]-1); + break; + case 'h': /* SM -- Set terminal mode */ + tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); + break; + case 'm': /* SGR -- Terminal attribute (color) */ + tsetattr(csiescseq.arg, csiescseq.narg); + break; + case 'n': /* DSR – Device Status Report (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) +{ + int 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 +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: + case 1: + case 2: + if (narg > 1) + xsettitle(strescseq.args[1]); + return; + case 52: + if (narg > 2) { + dec = base64dec(strescseq.args[2]); + if (dec) { + xsetsel(dec); + xclipcopy(); + } else { + fprintf(stderr, "erresc: invalid base64\n"); + } + } + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset, here p = NULL */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + 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 */ + term.mode |= ESC_DCS; + 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) +{ + int 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) +{ + memset(&strescseq, 0, sizeof(strescseq)); +} + +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]; + 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) +{ + strreset(); + + switch (c) { + case 0x90: /* DCS -- Device Control String */ + c = 'P'; + term.esc |= ESC_DCS; + break; + case 0x9f: /* APC -- Application Program Command */ + c = '_'; + break; + case 0x9e: /* PM -- Privacy Message */ + c = '^'; + break; + case 0x9d: /* OSC -- Operating System Command */ + c = ']'; + break; + } + 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); + case '\030': /* CAN */ + csireset(); + break; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + return; + case 0x80: /* TODO: PAD */ + case 0x81: /* TODO: HOP */ + case 0x82: /* TODO: BPH */ + case 0x83: /* TODO: NBH */ + case 0x84: /* TODO: IND */ + break; + case 0x85: /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 0x86: /* TODO: SSA */ + case 0x87: /* TODO: ESA */ + break; + case 0x88: /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 0x89: /* TODO: HTJ */ + case 0x8a: /* TODO: VTS */ + case 0x8b: /* TODO: PLD */ + case 0x8c: /* TODO: PLU */ + case 0x8d: /* TODO: RI */ + case 0x8e: /* TODO: SS2 */ + case 0x8f: /* TODO: SS3 */ + case 0x91: /* TODO: PU1 */ + case 0x92: /* TODO: PU2 */ + case 0x93: /* TODO: STS */ + case 0x94: /* TODO: CCH */ + case 0x95: /* TODO: MW */ + case 0x96: /* TODO: SPA */ + case 0x97: /* TODO: EPA */ + case 0x98: /* TODO: SOS */ + case 0x99: /* TODO: SGCI */ + break; + case 0x9a: /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 0x9b: /* TODO: CSI */ + case 0x9c: /* TODO: ST */ + break; + case 0x90: /* DCS -- Device Control String */ + case 0x9d: /* OSC -- Operating System Command */ + case 0x9e: /* PM -- Privacy Message */ + case 0x9f: /* APC -- Application Program Command */ + tstrsequence(ascii); + return; + } + /* only CAN, SUB, \a and C1 chars interrupt a sequence */ + term.esc &= ~(ESC_STR_END|ESC_STR); +} + +/* + * returns 1 when the sequence is finished and it hasn't to read + * more characters for this sequence, otherwise 0 + */ +int +eschandle(uchar ascii) +{ + switch (ascii) { + case '[': + term.esc |= ESC_CSI; + return 0; + case '#': + term.esc |= ESC_TEST; + return 0; + case '%': + term.esc |= ESC_UTF8; + return 0; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + case ']': /* OSC -- Operating System Command */ + case 'k': /* old title set compatibility */ + tstrsequence(ascii); + return 0; + case 'n': /* LS2 -- Locking shift 2 */ + case 'o': /* LS3 -- Locking shift 3 */ + term.charset = 2 + (ascii - 'n'); + break; + case '(': /* GZD4 -- set primary charset G0 */ + case ')': /* G1D4 -- set secondary charset G1 */ + case '*': /* G2D4 -- set tertiary charset G2 */ + case '+': /* G3D4 -- set quaternary charset G3 */ + term.icharset = ascii - '('; + term.esc |= ESC_ALTCHARSET; + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { + tscrollup(term.top, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { + tscrolldown(term.top, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'c': /* RIS -- Reset to initial state */ + treset(); + resettitle(); + xloadcols(); + 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 (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { + c[0] = u; + width = len = 1; + } else { + len = utf8encode(u, c); + if (!control && (width = wcwidth(u)) == -1) { + memcpy(c, "\357\277\275", 4); /* UTF_INVALID */ + 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|ESC_DCS); + if (IS_SET(MODE_SIXEL)) { + /* TODO: render sixel */; + term.mode &= ~MODE_SIXEL; + return; + } + term.esc |= ESC_STR_END; + goto check_control_code; + } + + if (IS_SET(MODE_SIXEL)) { + /* TODO: implement sixel mode */ + return; + } + if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q') + term.mode |= MODE_SIXEL; + + if (strescseq.len+len >= sizeof(strescseq.buf)-1) { + /* + * 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(); + */ + return; + } + + 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 + */ + 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 (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.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); + + if (width == 2) { + gp->mode |= ATTR_WIDE; + if (term.c.x+1 < term.col) { + 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) && !IS_SET(MODE_SIXEL)) { + /* process a complete utf8 char */ + charsize = utf8decode(buf + n, &u, buflen - n); + if (charsize == 0) + break; + } else { + u = buf[n] & 0xFF; + charsize = 1; + } + if (show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; + tputc('^'); + tputc('['); + } else if (u != '\n' && u != '\r' && u != '\t') { + u ^= 0x40; + tputc('^'); + } + } + tputc(u); + } + return n; +} + +void +tresize(int col, int row) +{ + int i; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; + TCursor c; + + if (col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to + * memmove because we're freeing the earlier lines + */ + for (i = 0; i <= term.c.y - row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + /* ensure that both src and dst are not NULL */ + if (i > 0) { + memmove(term.line, term.line + i, row * sizeof(Line)); + memmove(term.alt, term.alt + i, row * sizeof(Line)); + } + for (i += row; i < term.row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); + } + + /* allocate any new rows */ + for (/* i = minrow */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + term.alt[i] = xmalloc(col * sizeof(Glyph)); + } + if (col > term.col) { + bp = term.tabs + term.col; + + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + while (--bp > term.tabs && !*bp) + /* nothing */ ; + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } + /* update terminal size */ + term.col = col; + term.row = row; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); + /* Clearing both screens (it makes dirty all lines) */ + c = term.c; + for (i = 0; i < 2; i++) { + if (mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if (0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + tcursor(CURSOR_LOAD); + } + term.c = c; +} + +void +resettitle(void) +{ + xsettitle(NULL); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y; + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(term.line[y], x1, y, x2); + } +} + +void +draw(void) +{ + int cx = term.c.x; + + if (!xstartdraw()) + return; + + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); + if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) + term.ocx--; + if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx, term.ocy = term.c.y; + xfinishdraw(); + xximspot(term.ocx, term.ocy); +} + +void +redraw(void) +{ + tfulldirt(); + draw(); +} diff --git a/git/st/st.h b/git/st/st.h new file mode 100644 index 0000000..f18ac32 --- /dev/null +++ b/git/st/st.h @@ -0,0 +1,124 @@ +/* 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; +} Arg; + +void die(const char *, ...); +void redraw(void); +void draw(void); + +void printscreen(const Arg *); +void printsel(const Arg *); +void sendbreak(const Arg *); +void toggleprinter(const Arg *); + +int tattrset(int); +void tnew(int, int); +void tresize(int, int); +void tsetdirtattr(int); +void tfulldirt(); +void ttyhangup(void); +int ttynew(char *, char *, 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(char *); + +/* config.h globals */ +extern char *utmp; +extern char *stty_args; +extern char *vtiden; +extern wchar_t *worddelimiters; +extern int allowaltscreen; +extern char *termname; +extern unsigned int tabspaces; +extern unsigned int defaultfg; +extern unsigned int defaultbg; +extern float alpha; diff --git a/git/st/st.h.orig b/git/st/st.h.orig new file mode 100644 index 0000000..e3626d7 --- /dev/null +++ b/git/st/st.h.orig @@ -0,0 +1,123 @@ +/* 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; +} Arg; + +void die(const char *, ...); +void redraw(void); +void draw(void); + +void printscreen(const Arg *); +void printsel(const Arg *); +void sendbreak(const Arg *); +void toggleprinter(const Arg *); + +int tattrset(int); +void tnew(int, int); +void tresize(int, int); +void tsetdirtattr(int); +void tfulldirt(); +void ttyhangup(void); +int ttynew(char *, char *, 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(char *); + +/* config.h globals */ +extern char *utmp; +extern char *stty_args; +extern char *vtiden; +extern wchar_t *worddelimiters; +extern int allowaltscreen; +extern char *termname; +extern unsigned int tabspaces; +extern unsigned int defaultfg; +extern unsigned int defaultbg; diff --git a/git/st/st.info b/git/st/st.info new file mode 100644 index 0000000..52fc617 --- /dev/null +++ b/git/st/st.info @@ -0,0 +1,222 @@ +st| simpleterm, + 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#8, + 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, + 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, + 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, + sgr0=\E[0m, + 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, + sitm=\E[3m, + 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, +# tmux extensions, see TERMINFO EXTENSIONS in tmux(1) + Se, + Ss, + Tc, + Ms=\E]52;%p1%s;%p2%s\007, + +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, diff --git a/git/st/st.o b/git/st/st.o new file mode 100644 index 0000000000000000000000000000000000000000..241296257f8e340a2c052ff45815dd4b30906f50 GIT binary patch literal 72808 zcmeFa4RjPm_VC@43>YwGqDGB~GHTF75yO{eAZi9C+Q9)4qOvQ>$0Q-qKq8qybRh&M zLE13LDk{3H;x4-A;^(pmh>#E1)~#FL)zx8kw*ST$hr>{Z!?@n)xKh+GBIk9`EP=C(vy3y1+SAPNIe6`H zzA>W*oezQYgz!QmwXJQ=(YDhJW5xxIr~TSy7_0H?x;FT7Jz3{kRcD6kVjDuOvuAW& zjp`e&6T+XZj=_uCE~wWGw3)Rc2D3K)s2SL9*6(v3_MQ2&qx{^Oy2`Um$Gf2iTHNK` z%+P_vP{dLGLqknl#b|kRcvPFG)iv^H&EbkZo|f6M;nFtGiJ7TppwX;viXpv?&KQh5 z)aU?noSq|<-!!bpn4`Pp?*?)49_VP1S_SZInYmhqyyRLXEL|QG^h!%>&%j$kO(*7w z0I(-R-suZxfk9r^$X4q*6!H8x`!vrF){m}{J1mjL2D-J`u@=LlZ^!D(5}b&x0$xZ3=e$$Cs#>=D@_L0RwkM<9ktVwEn$)=1+5=z zgRdvm&irFys#722LaZTq1-IlzQh~-f`Z$ymx!YH<*D0v(}dqX9f%?vrd(3vCel*%a@W|o8IV4Ny5CY zIUF2735|%nuc>oAj+G3=lA_68>*wf9vlgPZdSzREO{+L18AF8f+Bk3RHP(AR$Cnjx zo(u8r*&mP#oV}hp(q`?-w!ZQ@es;~f8w#Aya}|oX7TgLK*){v2Jpb$(`K}o_YzE#j z>%Raag~QC)_?E-oE)G{UGz4uv>-$LB@wPV49@m0oxXuY|HUm+!{;Q5O_1WmFon@>> z<=HiD!DOFxqSF)|&hFxL)m#lqJdgrc%{x%WAhp#O0>LCE`-a@$s&>Iu^D7v%v!a|l z9f7~t-76vDi<=e7qyUWY4z@FNo=oG%M`tTW+o*3Xcx zQ6TMe?DJaZnvP#IJu}~J9t1(cw~d$_qxM{k(XDHTf)~HJ6;1w)7{2>ei=k%c>|M=! zfj}{e3yCrfM!bpYg^jWvd$wCUTqCzyqLRA&RC@nsiM3!#n;Di#3|AhHWJ64Av1!y{ zIzSn9OmK)SBazWZAt!_dm0e|Bp#TpA3=sjdUG46X5>U+J8mPGx65##nL14kG3#B9} z5@GVZIRk=E4UB$)?x;;L1NG2=JN)7J8sTy0Lr|xJ>7mvxuIjgd46V&|9al+_28yC4 zYRhVoc(lnEzSZgV?3`tyymM%+-xYUbto!~qdG~!zD zEJ(Um4KV}%F>AAp!@f~q-oHX^-Gi5f+Rg}Oh1xC%-en!N%Z=zOziD3dk>3n%o&p~G zZHb$OgeVFdy0@pXBm1nQ)(L-DxPQ%P2p1*aZ#Dj8{=MqxGr=2X^r||#FnD>@(Lb-g z3sdTA$(kp$g+w#rY^XZ=?CS9{B)Wf&fK_n0eiVE~cdfr0f41dVKQwm5F`ikov)ltpeI1I82`x26hXz|iQDHBESqGSP)=TJRhtqtzpPLVpMw_864%73oY3(s#JYjlTrr%%xMOW7<=e1s| z&I>;AR<%|IH&oZ%KS+Wm0Cm9u@}dPUnt}u6MJrsi1TE8Q(MAa)tbN|qXl!)O2_3fHQn zm?Jwc$weS4P4^_nyt9Ql4WbT4e=*7?;KMs}oI)sIg{k&+U+9Mf)XHt~Ti--3gnrlzH6)sz zPs{enLO>04EQa9tGS3_!V=uoDtVT#%G~#6QfH)L)_)hMK=ny)SOoK7$d5m z^?Br4C|!_yG0pInbUbR#{XgF8J-`5SBn{>=Z9c=gGV%6vyaH%ncsV^Ln~%~BY$0g#zAod!JW}Y z9Y;4ROL})^wH)_ThxYgnkPOT`>sRY17|aXL$h)v$cAUu}i=3ayk&Mq0J&z^2TKX2{ z-V)!Do`=;S%LsOtim>51Df)9EdXY;~J&u33flNn(3&l9u`p|k0rWfULHSf4;L<1{z z_+%PCtkRPx`F9tEV_6J~)CV1U~CK*9hTz zpGWGozHyBd&W{vA8&va&YZ3b1Q763s}JD1;9Y#l`i=TsX%n>}ZkEs7f#RgM zYVOE;&yTatl2dc?jxZwf2W%}vFj|yGAA(!E%3I-vFXRo;kF?$o401zLnh4V{D>o&{ z@~6am{!#6et>D!bE#*^dDXjHABu6r*8<@ zJv+C(Gl@@WbYdcm+G21G80^_Rd#67KZh_ zMc=BKOWGc=?Iu%rJJug~F9)WWRx?d=VTI*1e{I*{;iNdTHuF5wx}FB5e#?0-EHuFU zCQcUTw2wXS&U)v_C)Q`+p$}nNEt&$czz}EsD$SIQaj+K{Tl1~3%H?o?`QlPwdLhH*b~5K<<3w)XO7y`T0OjCZ6TegWI0$>I1bJ@3vO z=?`oi9(Ha4<^nnpQs={+d?inWF!V#Q8lN z%eTw)A8SvHIp-h_@lEK0VWA~ACEbLomf?qjoaKizv$`&Ltt-u!GRl?aPnqCK%S|bD zr7cORf_q$P%TgAK-xVoK#qY|L*TnC-lsfo5seexHSJ&uFi31EJ-kA=+X1F?~34bB3 zI5X@^apM`RknJ;1UdGsf1y$4wY^CH0li3}56o?s_8@9pj5D$psj z@Jew5EN05=fC;hd)zGOuuXU!2qLJ+>Npw}ehanDHg_I48(+4`Mu>iE+(kH#vnF66u6`h{# zt_81RU|^$YW*nPg^fKe@&@994s(B6rV8j7v`nM26^6YN4@hKh{$IVQHg&#+GckdQw zO7{^k&~adc;H@o5&IxUa!zkxJ4mabhw5}QYEN2RIiZz*7zuWJCIZ9xByjlOHTBO2u z##?(+66BS$8K=}Lycrj*+Cv7y1uTogc(r*1o`P{_HQ$YAVptV*J%o#4hlh1>mVetZ z{K|1`ihhP|P#jtS&cLE^a<6AX`H2Iojp!F+WClT5xYD#5%*J}q*e0^xv_Ajw74rzp zZSI3X`sa}m(3|2f-gjri|3xwSzq=**q$+?zR58BC@=Waq8U%|0SIgC)^7|mcPHRi( z>)GXTu2s7aZ)z(a9BP^kg)-AIdzfq>@D`Df_ENu5HZP}?eOO!O)!|ftF76o1Vx>x! zopk#W?3{y%(1)4UuUcxVQ{sR@f+%X~d(%2XZvS1IIpOANFYp!A4RdXaf#SB_&ZtbfEsS5hiP*eNOA5(893B6H3r zOtI?K8gzBh4}{kCWt0r?OOEw(j1kBgl~3&$_<|!`)jL6My%4s(?`X%YMmacx8agkG67Z4506mNAsKR2B2yV^Z3~WodR+vI)A7=naj*(~A>IU*JO_Q`hBM(@ z1U1KsaD`EjUHez~?gqUKva zAU`|;O2qCb8O$K$VDflU3jnRX98nZl&J=a#ONlAK1)>5NZgqu`&iW)Q zHo~(VWJ7Ly;?Sw_RBriOxtL%|e3nMhI({%jcdNZ-+X!%7ju!aH0*7BAd7-5g_ zY%l+huwSlHG{}e3FwsBuHpWUb)Y%un;MqD634ej3fX!;a6>)eXm0wxk%Rz7GsH1XU z$2=v1(S}EZIs0Hi$bV)M#{Y5on)djk|CGsrC)Kihyc)DOv?~x(TvN_3O=??2xpj(M zJ9)yH43Fbr67;a&^P?-21x3?q{pz)jKpRdg-)2}00Gl6%QXD#Zwd zgPK=DNXMvReRADNmqwOmmi3#WaiFa|1$Y0ZDD!BAR0^EckCW&UiImdKtZEISe1z05v2GPzO&u6Z=PYYwd4Q{GEd(Kf|-y@X}%bcU% zK=E}gxC73^4<*U-+Xwn88FbtKGS-mrjej5@Em0M0%*1`l$wBBMS z5+l~up)EgYdYj?3PC$Dl7agq@*GTC?7~sq0M+>(0C=_4Q=2|cTYER8!*MhO|D_UwX zOO^LmWVSb=nGxB=$%Mi}i@Xls1?wxS8TX~NdLpy7!II@8S7DuW9HR#l*T_u^uuvhQ zhPzXlHhB$;E;x9n1@Rp@fzQowpHI=rIGohHMa{Sk4G;XYxaZUxasr>3);Q?F!%isz zGuUJkOUiY4hct@H9=HsKVGEW(@xiHYj`b;QMDmAIHe)*{oMc*`nz5oc@S}ON)cx6D zu5C!D0TyAtEN_5Sw>Yoo`$`k1x`{dU`<-UsQ-}zb5>Sm={Wwm!Km?G2Z-ud*%s1j?%@SCs0~k72(At!lg86bX{ba4n|&EV=#6 z-F~L}r+L1VD@6stnPk{(gH@wA7%BC!Ha8y?w*2C=eza{SMuCe*1ik?uKnW2Up~_2m z;MIO>AC(muqM_!ep=o;?^c&4@!Whg9Y*i1o!aW~`2-7y2ft@gFpaz5H{z@hkb;8rd zvM!+^M({_OA}AIi6IO|nVS}6r3oap4+bH(-$&m>NEHq(nK&)(wJlm&z(fk<5UlEoc3cgY7YNRPn|o_YA7L8^wL0n5iD zXycw_Fx0J-Lr_?#fwlS(TV!I?vch7yr^pFAqqe}#C?|AT2OKn-qdq|_41<94->x^a zoCUoNX8{atLmyJJOl2E(KK|YpE|gEdqZf)fTk|yNqJ%4OOUW>YAD##f!i6Mh8|3%m zP=*oQFQ;jW)@JVUSzlNm%Z!B78PSbYa%nW}llRlgNZ{Xc?HWyltq_T3tr<5W0PR~H zE{+Wc*j5_XmQJ%9%5&LDi*MoAld3Gn30VMpJvugdJ}-21NboEexI5q)&))V!*rcLgu6Jg@5Lc-MlrLAdJZy{_s{;G`LwgQ}yWT-A@_6)u>D zS_itSFMaJsN+UZr&V&V;&9EPUszGp#S@Vf0_m7w^-yYNY$U0!#2p$F})zsa8x7xCT`xC{~ zO&<`r*2{*+X8)2J<}L_;GSM*ltw0>+;o`@URvZcd{I^{GlhHGr&n~VpRr|paw{g zUm7mv&A4XO2f8A7xS8!*pV<|!d$wQq5!dnZ+Ur{1Q(Pa1o(kl>utT6LJoT$@b%vT^ zy|7a6?bhg|)Mh^_y!YqwqV?h=ph zg;_PFkoV9NKu@|Q8{L97!>Us+$V`zCSro>C??5Id`mu=}cXXG^rt><>rsgyhmld{S zXHoOn9aQy25UtmFbNuWfnRm@j&Ky-9 z-?0fQ8kc#Ot0n<@aP&E>o2ac6cSD<|Z|2Rs*H!Zk%wF^o8-4FLYG6mJY=$oc%b;4h zYG#9m=HBon+Y&vMl~d^)aBU|^i9<|(*X z?snCD4Zp5cQLnW(6uq?SnBVo#dg$?8tDrH$o*$a!q^Dqn)3&eb-749UHjjgdU8{~& z9edXG&<`LYgk@V3a6WB1wzoCOk_B z8=VU==b}4kwM=gxVPhaPk@T#hv`q{zfXOs1jl7Ea`!d)n?Sih9hB?qj%50g}^UP2b z>h<;0PrvR2MB|!U3E~~ybH^F2LCMbf26*x%HZXQoDEP> z@t6&3SW$C}7*IIO@DxXMt8i}ZV}tT_v6RwuHsokH$_Az2^F zehr4nzP5|&p^xA~-pFl>bc6ENjH7M3`9_!)G`|Sfk?b$w>{(RP8@>2>^J91pAH#4r zKZNJPBZjNy0l4@-EN#shHqyN_h0 zzZgX^3pz1DY6I6x(Zlb7=weMYcm?i4t-&=rRW{n^Z{$SuQF9*LqXc9$P&Gx!a7~+* z=icxv)l$vDNH5jIye{8nSsB%w1#Ub*g#{Ls@!rTQc-ObkvUZXxe0u&*O!dFo!(sm6 zs(uhG67Q&Bmjr`~%T<@!>EJGsw-Z!gi!Qw+Bf{-5KTy)Yfbt}%Y~n)A@H&nwd`J5& zFu@F$(k7CXSokt^gJxeS6@xxe?PMOzN4X@BgF$_JlyY2*4viE;cytSebS{#EDt5qt zT}naPgMINmf*Y~tU4vZIxOVOB2};Bp7q<(XY}ij&TmH}J*eu z*(%H7$dliI$SWWslW~%W+T1ODD?(;DD?B{yuLWme2#K209V^3RC0X+bxae{^ON>54XYS}C zI?IYaT%g9SVh>+|Y_yLzmq5ox7sr|>bzB^2zOCcpd+~Uj#6E8x(h;(uxi4PGB+qVZ zcEgo*06J5A&N&X+6R1A(PDdyl&}_h|%rpYu!_rgx4Oh|)A0w1Ed;q5>-LM`&c<8=R z>uEDiCz-zsv`0sa1$qHdLg>I~8X`U@=n|sih!z92o6|-f!_IOzNXXu-tE*;3S5@x9 zCJaJgDOTDCFL}bHXqDEdV%^dAC76L0F`81lcbW@8i&~keS$HL~G)9!WNX~GeN0rSeo!M1}{Y+-hr1tftIRY?sL`P>seF*ut?*e<3AAAd@=B< zV>bm8s*Yuq7t0c|AsUdyFDv?cctywhOrOi+EEC?U6=_xj^R%sWRvSNuRu1O*6LX_I z`9v>*aYwsU9m@#1I>v>~Bft`$^({TBq%p+oGopK8o;gHR!C5)2M8o<|ALOB=HNrEdr1b8k$nIAX*8!k_SOXWPN zjK=28-$HqlMBeUc#RycMRbvG2f{2J9awF<`3oidphgc86J&~)wqWPrAj^;8{3yGt0 zgIUx(zMbKvl)D+2ENRoxnoPVDC2cZZUImxSVT)jI_>Bxc2;>4h!`pG!M)WxuAh4`$aguiJt>q)+4~v)S>|^{xdkffDkgKhPT^5sxCIus=3syPTsr zf@jc)t7Z-e$%~lad2%En#l!JecCU8qDX!%kXC4 zS9o33>34hs#b0X8X)j<0cKuJ<`gK?kBhwB`nv~Y?wGilqnLf-U4Wj_hk_>0_#X#jS zC}Pw@rv3$TT-IF7diD6_N|6QGo`c!0TfUNOjb7N%My-U53YKxVOSk*d_Q@w4@a*U( zv0KBjMZSm&+cf0NQ$`8nf)p4RfV-c;8Tn=Z8*vM-4-GpnHUnaSp&pymX8tH6S5H?U z?iLyM3H2(oLvE0xQJ)x@z-!^Yv~OT&av>V4z?U4nIP&lndc-)+S z^8O-M%?qGLY!lR9Fj-zzen)uJ5!jM5y-TR|jPl9wM9Udj2h9>Z-t~N1Nq;_|cOBdu zYRwAHhviCG!*n`vZ*aDC1m3I&ZUZMeL#@50zX5-?BKUH66gq9jD%jMDLL2UeTO;>? z#tnDk*#tO)Z3it`9f2bd%@s*&z7gt?@XHt~m}?C;G+}A*c(DuI4hs}pPU@a3Jn%Tj z+M>V4A_o(+Q|`~uxT*pz^Bmq7?+vNAQ*ME6-)nX1<}!3GXy z*47rQQ}Bfg+!lHmUe<)u9P3wjPZ0LP!nsIZRBlzP%?PzQT{YqrOxzDT1;V|!js^px zeSQlzWB$k5a~#&h%<#&TD!_srK_U|$zUc;dxS!Kl-`dq{wR-DYdwJ_4J%)zkPlGq_ zd};Mwu{|qiSChHe-_5zn->rHRs(~%=Ua>1v+n_0&jU!PU;GUx0Tx=b!KiaGQK#y?z zd7wLOhukW-YoA$@w5j}V+31J8H^6H-IrU%;-0-fv!LftMFA_Gms=KKyhF$f|$5rQt zkLjha#j=$!%kTLLo)5$8p&!B%ctLVNN^G99` z^gXd77@Xcw^%HSNFc4MyX@j)-3l(wlU2k~C)lD`(iRI_Xlel0H^%MpcbcaRH6Ap^t zT6FYN=zVZh`wlLH!xKB&pKFHib%+h7I1zXm!{D{Wv^_a#n}Q#iRb6gQxC9Qo6x<(CCg2V|rTcIQkDl)z;m z=$=|WWm5h$cS(V}bmFv$Q}ToP)5_ebz4OvwRxA&B-(dHoiFxj6lP9?YCB-GvK%~5n z_}4C|>z`au;hq=_PILFpbC*xOcWTLusqOk^Oe_i(O`Ys604=3OdG6kYdG3OVMaB7f zgWbK$x*LJQqT)O^*fFJO>cn7vo;$BR-yJM*mlaK(IvsnaJGLpr73?zygaULSYqb(m7!zo~io1ry7QgOE8k&V$SYUlvZB zng_0O2MQ-n0}BVcv&Rf`r@DIwl)CnKRl3Xa?<>!r8ps!t=@q@x`;T>B-aBx4hlem{ z3nokZ(EekOaTVEGQkp*%^PqRxV00{Ax{HgZ=0ifuE=yrRBSwxf{5d1D z#~8WUqel$$jxzkW+%(cVBHOraO!jEQyamoMq$5#SAZHX!%`2G!B^`?wCPd~$S^m^K zOx+}K>Agnk)biruwDxRP#cR^UvixfXc4TUM_CqbiOzR!M?{TLJ1j>rs#szowE*jFi zXtWVfPKLOkR7@+G;w~%9FD{n$l;sy=qoF$wi%CA!g_5am$Q3DiMt5W1+&{wSG59vo*Wqj#d}@t-ix$HN?p*{D zix$CWow0AhTKK#L=T-2F*Kfo568OM9i&nyC1$-Vf_E~UkJ#TyfV%Ab)<6n)9_!nUQ zd;He}|MkFsJ@8)-{MQ5j^}v5U@c$tX#Qr~H>-C`KhJk~wxi-Tyct|F$WVE4TM@+1< zOV`ulx^+K2{)_}y;+bciopjE*=Ov$yfrioJf}R&%)XRPGC6}i3zU=bUv@5Rcb5-A~ z)BE)wAjAJ({%&haG-iVh_=i#P@228)XGaynIQ()LbyZC*tyM-{Qwv`I_xS&n2W}cR zY_L1^rjfU$xjlXR^-ZV5<1P(691n~(92JR<3*t|U!~I`antF;5SPr6JWa>VT#(Z>M zszJv*Z?g<$Hya}lw0D^GC(1bWIYzj$++r?jrfnOIcBKZ;$ zV;Z`PKnzQvg{7e?%#>jn5SB$@WpIu*gwclivEBstTqi^o>f%dCzqNZpDgdt@XDW!# zqNt7P8Rya2&-iuuD&aFCjJN-R>gcGA-b2H;4u49#LK(BoyOINRFA* zJt5I6e2#h-lgvNh{8YW^F=3_`^JyF1lY;jQN4;Y~UxV;Lgu;4Z7#!%AxUxK^1yO5OGX<&X7lB{?FaFT4y8%h@2qJ<>)GU~i3p$U_Od5aI! z@esW))X$N!3d;0T*xvu0^pA>N4aK?BmH^{P6c1Qrox?PRVy2^;g$;cujN5?snZFU1 zM`3j!2W^WxW|cHP3offD47c>4zZYQp5ym!bKDHlou?-7#y(yv29rL5Ch0r``tyc!z zwuy9{iGBbbowPv{VqQG85kk8S*aCtFzP}`F8!v6cyy!_`VnhT!&jn9ly%|Wn5buG* z4Ih)3XtO)=7n(j1!fg z_?SuNXKaiE(t%;eDeOu($2NZ~l_|6<8OBw3VBX__HuoV0yJ&@c4(LJd>x5%E#u0~S z`2TY{N@GuV+N1;HSWdcz!8zJmNpaT^Kb!bS;8}3Khj^IERVQU4J$4wh1Va!c52y=0x$=VAO!!oj zp64;1&g*Y_O!leu7sc%kG|6fFAZ9vTCKl!bogJ|TbtEh>7Buk#t4@rjM~xA&tT>F* zj3!Exs_Q+7*D-!E=~4Hqape_4-f1lD4JU9E${z#!WsG+bU6;}?wu8t;J%{rE@hsw# zDamT=>3{^Fd^PdEl6(&Qf)=`>egLjU!3X6pr!m81l0dtWFCebQrx-u--sDm-Mg``B z_)z}@N>@7RxtHXpW1WISj6>xqs@{t6D6qf5NBHehIDumn`QvpsNBQNH?*$@M zzLY5Z;Fe$^$!{h31bV_J#;3q`5&sCr>v)LqCj9P&kHaA4aNa`s*(~^J#vuw)9>f=a zuzdc)_%FcG&S7FtkYW4-uJMQkeaH_JPm$Q@M*QwX3F?UB`)??J5xi`KhZvK=ubX%i zwsUZZ@fG}DL3{yL95`;0KL-B65Y+P_aWQU!up5Z`@ufUCu-xNe5^#G9NI&^AujgEwNt~8 z>Fa9bXbew(Ao;fzUoJ^ z;9ZP)I)w9!NPY(6DI~v~ar~P|s9*I*Dmb0ww<|!u*O2_HjAs(x$oNp=n;6H}*f8FY z7#~Ue5aYSTmg~$G`un9|gq6GhR#_|0;rhOe2oFyY!=icsb*h#2;Y1 zn)qzS@lRBtAI@i7_9tD9L5x2^^1~QkLR^gpl&yaeyo=GI0R1i}?q+&kAw7R#d?m?a zJFOpUNgn@Np?=hnJhqqm0e{sIj;_XsjLWr;uEqhzcar?ujPD_?`b!nZXT)WFRTS2c z1n*)jW_k{g{ORJiL%xON>zF+L2>@)zB8FF2*s| zSc3)+ctJ-J$Ws@tkodjShI?#ymJNT?hU4E|=q%owZ1_kUo@>J=+3OXrrfwz8uwhX5Z-j|^h%Y8RPY~ZhT&=G?OWbft!4SzWCmtgH3Gp|G zFQkcuSSNz8Rlqw-*E|@&bjCN3p6kT^WLj4OJ#{vE8g2L<8~z39FD3nAZ3^^9Y~;Ts z`Dz-!4WRUXXCr@@G+rQu>N3Ql|5$X{W@ zueRaW+HhQh?99&LHvCo_euoXe+lEiJ;ZtpRsSTfD!>ergLpD5Y!yg5X`Lc&5zG9sZ z@?{C}doGgsMcHP&V58^nBp)LALV269l6aK(Wa9P2M^nQ<)zx^Vx&XS^qH^qW|`hBRGC>FQx4e;LVNnIZ+%{9+*S0mP~KGqQ+#8P6ep z3vsb_2r6#{-kF`hBl)sa8SgN%^KRm+h|@IED6-M>candHz~&g@CF;XQz3yf;!oc#7h^&_@0e8-9fiH_C#214enUAVci)hArlJ0h`yw z&%_ENh&z#Cd%A&p+KnJ=^vx@p7BqrWN~Y%rOXTlFI0^I}c+DV>5GV^wD=98EaMyKk z;v@sM-Ih(ASc<<>OQyjlYuF%c1oQF>#GY*sn^rUxlp94Q1$l%qxo<2Uk@4#yZ^q0vg zyBB{a-4Ce@qFzj!JQG%u-GSo#iST~GInj6Epo|PW$CGOfh){%1n>fSh>mN1%ztZumAAa@6uYveA z2){h|bq#)9i(eV|)lW(b@qR+SpHR|I+}2Or*H7HjPu$c`-X&D^7x(s;QHgtDKMm;Y zFYfIxB>M}=0YY+skc8|2nE^s_fRG#@V(BlxMO^(wTm!`U0C_I74G`J}3T*?0wt+&= zK%r-#&@fP@THHHO+&fU*3oeCt!Jqgo?j0l~2MG;>goZ(bj2R`<^78VFi>AQd@M&en z_?!H<4E6fQ-*V%PW3qF{=X!_wv&S1V0%d{1X^@e5@Fs;(Dqg2B0>vd|`NsJ1f#Ch6 z`Qs;zht~;=0-1MMK;cCOECb`m!z%-Ud&dU~?;Veu(Xs3mO)Y{_AvUPzLn+EH7KKoh z%HTw(Zx~b&gst>OUQxa=y+Bl&0;58{vj8f=fT<-I;lv=8TdW!QB7_0e2<|lspconz z^j3pW0m1M_gMn|ez*{V$)Q+DxZE~40C4Wkw^nRo4{<2^`RQKThQ0m1S2?ogb7iaJS zf`Km`U^?zEn?7E?+5#2^p|V0PhPMweR(Q(*f5htsc#=PLx=}tAr0@hAld15+j*(xH zADCWhjK>!$pl(7{hdAXsDtHE!WHQ)!ZxOa9_)ZR19@PlpwGk|U7Yam^Q~_b4j^RmZ ziD*=&ln8I&yBsqn7Gq_`+6ZkAzO7O)S)EOqj0qRICr&CWofyc6stE1M)I9mtLa=1= zEksCC|aZSh2BT13lO@isr%)l|5J#gf~ynD0PN8nuqnZ0>4G0BF+Nk)5;*r zDhi-9lod*eilQlHr6oa7C9Wakf+fX5n`l0T6Hzy2az&uHs5DRl`8OjlqbM&}XjDvs zw;!M(EXu=1h*}DxY^o|ga3e(uim^Ob6qiiQ1I>_r%!i421~zZ-+Jq<>AV3FnZ{omd zc|Z8A9Kl?Zokagx2*a`|o9Z7s*#JuI!)7 zc!cEt#yGsmCyswGzK-P8`jSeQvcHkZD?fb9xYB=waiu>76~n>xt92*Em45u=%(}et z&v3?-A4V~*@^Kd9N{?J$Jk_43nY_whxn9^If1LdGk&XN}j4OY(GOqOC`jejCJknpr zxT-II)OZElsQi!XGu1Gm`c}P8;juCEvohvh$2CXnlwMO3zJ|Df?r#If4U)OadA0><)|rSYpZuIup-=X!3^ zj$(`5M>t zTuK~m)XRAv8~N)rexs&;sHR8v!_At!9`87f-=yi8XrpI}Ca>$M)VQhX3EAj*M3dL` zJf+F&{(M!FM<0!I!xtQ%+VC$KkM@@Gt&B&Ax6mUz%rDe`i1-o4Rlj^DJ5Q)=d#V{%^7w~9JJYk#hWDhcAQ&&sk6OqtS26xG z@rfGO(^bm2O4px=qihI1%0F*u@;Ms+QIoIMc&jFly8c9dIFlBDR<)!mHGBl!Zx)%c`@aWy`vWL(+#1mjBn8OD|T3yiD& z`W41izyB8FN`5orN`4pP%0GJ8=6>m?* z$5Fmq#<zw7 zl|O43SN{12bOW;3q*6J}h= zKg+n%|2*SL{#C}6{(su=w-|3CJ2x|~+RvSgD|_}ZuJRH89KW8w%ASLaFQs^YWL%XK z2Q4`A{5pfUDnH7e^B7n07Ba5leb9#gneioL=f$)inA>wXab?dYl22!REAi2cFC!k% z^v?wcs{Xx-$*&~&m5eJpe_(t+$#m)v8f|<~{!#L0Fs}S|Ipa!x4CBklo?;t*1?@Y+c=d7n z)x@#9)oSIzqw)C~AFA;M8oyQJk7#_X#usXQ5^=O=xW+yD{4erYSS#G!q4i|Cs=acYJemmbreyAp|*O!|# zuG>FSSar<R!zRrM&8mmJ}XrAys7CKt?_l5yuO~+ zpmE&~hcrEywr{8%aP)u+I51sf;G^O_N8`Ber}#kPsAGx7Gc|b(^SR**j**)DpW#~R z8D}HENR!v~Kds55J?i+ICa;(0S2V7d=eIOHxu8VZvr*%Ef3!{0qpzp^TjP4XpK4r> z_kgDVHZ9&mn!H|q&b~mV5PkWKCf`%z&uaV<;=J7$sLAW=sMj-jm0!1L@=G=Sztgy$ z-U%Al(|eDm|8`AJP?Oiwd%wnY{Z$&*^*^lXzeCf%NR!v~Kds5@<>y&VUiZ&3jX$U9 ze?#MXd9JhJ@7wUNHU1Y({}GMr_NYI0g5^QyU3vn51G(EKlfPTjUuPq~O_N`)$?wwmc#ZF|(esVQmum8dZFrXpK?n{k zKTF}G%1M$9zto2Jx8c`m{6$UwO&Whm<9?0n<#~+8^>*Mc;#h7cXnH1V@_IW^rg2@* zEKScuP0vG`ysl?~CO=7&Uu+})nkN5uO@6l~U#;=)GdKtG_o zYR`KSNA?%^sQkT7$T$ef&CnRC!pV$@c@E=#cT&F|Oo)(e%8b={emk??e4MztD#F zC9eFuo6>u&4Igd8Co!(lJJp6;#JQb|HU1By!#{u1xSp<+HoQUOx}KdH*Y$jE!@twG zuIE>c>w3ChY>0!W>pYF?dM?wru4jM^&$i)r*zh779wd(Wz7jsiX#d_HZTK@9$F!;E zDIYVg>erWyt9shy65$?Euk+wkmFIYk>+RUN#4%329qXgXV|`gC)fz)JdEK9nX!58_ zmCwaC`~@5SstteJhHtUqyEL8;_9#Dmta05B`-yWu7?;X~$G{JUt)}{WI^${`_#DR7 z`e!eV|5LN4x5iQbCenYU##d?b{WOm9D&7pnRe3NOKTi5b5Jw$vYWl}$@~bueJB{N# zO8+FrRrxH}xUQ#4sz>>ZD?6v!@CwG2{c~*i^Ng!< z`wHW#Uw)Ig6Ks4`vwuC4SN(OPjr?vVui`z(xJvJF#+5x?C_kOdo_OLazmz@a*vMbP zbWJi{~o4CJwGb3(KFpf{&B{S)AiFfdY-e9 zf1h#XpO0yV9n%4OhOIF!87--kHnyZ#(uFq2n)9%>^$mdUI7QfR|V7+2+_ zhH+J%YZ>20>3ov$b;O@#ypH(ujBg_TD&wkOSi`uAw}EkG=lhJS@#;~HPX=GB_RVoQ z=8DW0y+1vjIOdB!uDD2($1s(T{WSRvz?J?CCa?0*XCr?XlUL>W0Zm@-@8;OZKdy1T z|5|0kH)$NxrQ+?K3Kwvwa;x(9D#lg*Udy=3UoYb-e{W`7b z^@zsxam(Wx$Mh;Y|DHS!f-=xV$nEW0} z*I~w0eRrn8&2XsvQu5sy-%EOu7*~F~gmERG#<(i~*^H}r{fw)8g#VN0Bs*2SWsIwI zy}-EYFV-=x?D>E==F4XIsC?ODBmX($D!pGbuF`doah0wMu8`@%c(-WrUQC?l%jJwK zJy$WV?CG!RZ`AZ>*vMbcxUzqkre~|B=T435>kkt&-k`}_OuyQHyO8mwOW-Cr{>1nM z;(uX$oLh(+FWd0*uax1+pKAWopYc4>e=Tv$mv`Z#%ER?cUd8)_CjXu$|5uH_qw)1j zPmtpMfbnADyEQ$!Jzr@2Jx%}jnw~<9w`sgc<6Zl}1sv!fyv}%b_!LdgJ{vtz zOffepc%>-E8>ToL?gEznXECj~>Re$W9;Q$`8XCKSug<8CUX^jJKsod-f2= z^y>9DW`GP=={3ooGZ&0YEUeB+f4WGrh@=u6ym9EDaSN>UQ!(U)r>3@}R zrGEqCO8+(+{vqQ^&jH4jo+CE=SH_i|a|U(zU-xGk<7)ovVO+Iu*Aqt_`nu3?Ca?T+ zE8|K}fN_f4AYQZTLpUl|8!{SNZi3<37r-CdQThKQpf6Pxsj5m&S2CqyCSii!_e)cRj^> zna2N#^0d#SkH%4c6UoCL_Y()!-&OEY@);UO`K=^h!uST__uKH04G-J!CvEt1HvI24 z{GW^~|1>bJ?B8y~KegcpZ1@oy?i?)K+&&Ld@t(=JvU33AD!;DRxSqd0jqCZF$9Q`_ zYFy9va*gZxK96x_&z~4q`d?>U$u}~t?e*k>g!#HGe{+P?Slj_k#8$OxwIFc`AT#X;5 zGp^(t7+3LbC5|@k($e)6lUMVj?`-(58pov^)tZ#-Pav+w53Q80b2P5& z>7{XO50}vTaGJ(34&{dojo+umdxOURpm85@)G<@zw`%ej79l(H7+**H9>$*~UcvZM z;vvS>zTBmZt8~50_(9V1mJNT$hJVSpT95jHag{Ge8CUUkxelBHhssA)zfNa-KiPRM zkUc#^2ERF&jSUdJuvG!nmop#-t(bIan&xZW?cQf&CQG}`S%%D@$P0^)zfbnSMuL7 zuH=7VT&1^-aiyp04d8S*R6SDd$wiDS`4q-gyy=W9KMWy`dHWE2RQdTGlUH_5U|hv} zFXJj-{;28Spy~e`lUMp*VO;57!?@DFopB}q5#!1a#~4@fo}LB9!J+(YP`NrcOW<~{ z^jyfeiuVc|K9F%0?-0gSy0RHp@fH%t{MrB?m0wetywd*=<0@T`GOqML#kfk>3pV_9 z##Owl8CU7r$heAkKXIO}1594&Z(&^NiSrubQ2A(5K3=4894Bl#P09@*j`8ZxzlLk_ z7^d2XNjAKM@q-laBR2eT#?|=YZ#I0l4gZpH^}POP#??NM?n7m|m4C*$<#kWunAWG@ zqsrB#OkU}^LX)r7HiNP zW&d<0uk_Ef;qy4Bcpu}O;{B5ie~~z*=@HGI*EGIR<7+iNWg6e4$v>>|?V7ys8-(HU zktVOV4;T4lIJaj!apiyI&sR11J#ee?=i4^&-`Viq*&XrzThr5D6)HfadQS7sN*B}sC+?&2ilLH8!y2tDTlv`cXi`c$N!tH_(+nEm%;7wqlqUnemn6b z##7xABr|?J@t%y&AWlE+@fMK&6ehol1XCHmg5>)!-j{efPC5?@7-%#_uLRf$>Si^BCXj zmY|UFdq}>R@u|d18J|WR>o^`U^ilazA!rAGfaGy*f`^i?Bwi(G2Y--wHRE%M&tv>y z;`13-`Ld94l`o4Je}eQZX8dX5OBjEK_)^CILVOwHFA!hO_;i9?iEn3Iy*IdKD5%`m3R`9f7>n0X#anjmHhBaB=5=OTS#92|2XiTUKdOG6ejN{p31mt z-}*3KK=SE~Uqto{Wc(818H`^>Jd<(N?_@FFm*jnn_a|;LehqOyoHm-^Tb_;@cTl{myR2 zm7n)8ehul_%lN8H>Hj9iGiW_-KjYg-KEikw$sc6gN4$k`<2o7dA;z7=TN#fdew^`m z;-b+9l`-@gOYu4p5W~}P#N!yBKwSPm?Hzj5x^Nqt*AZn25+wIsil z@%6;FF}{)bcE(f5&fSdnA-;!k_55Qm<69|RO^j!dp8bqx5|1#RMf@P+KH@Emo5T+> z?kC>L_$cDX8UK;|qyBRm493q`l6S^pspx2j#}SWXT-6KpzJ=10K>kT&@_+YAy-AEO z#>I6wk{Mq@yeH#JiMttJMm&Y_4HR!G<10wM597Uwr!)Rly0mj3;~x{xVEh8olgW55 z;#rKV_d|S)pGEQ}<8z7o8Go4gD8_da&t?33^3Pbt_mccL#*dKv1jhH1d>-Qw;)RSK zBwoyT3-MCMKP4Vyyp?zb<6DW(WPCpHD#o4tW&Tz(9!Gp0&EJ7crhp zd@mo zw=fulNeu2 zT>pP0G43VAdouZ@#NCW9Bc8(ea^k6suOQxs@z;o_Grp4eK*m=S&tQBV@l3|+h-Wdr ziMWsPt;9{nw-NU7&nP; zW!z7E8{?yhZ)ZH0_-@9>65qr4IO2O5pFq5c@jT-D880LrVZ505LB>mow=fP=*P5y>YpzLWlgk-Di{w`@elGFX81F%RCF2(nU(NU>#Md!?8Sy&CuOPmO@xH{jGTxu~HpZ_Z zzMb*G#CJ1(J@Gw^4<){r@f(RZF+QC5e#S=mh0 zKhAgoasB_}#xdDF#GUlMyBeoWB_7B4G~)4$PbZ$p_ztS4NsRwMJel!}sUG!Y{0HK0 z#xJINl)|`rzdDuiDw5a#e{f8*dJj3B$){4f1~RVvoWb~9lF#Iv-nZ2MpK*-WpCRkJ zkIBDI++_R>((h+{74cDwUrFzkPr+JDAlsGKZeJe}m1GQNiVvyAbVslF^{d@0$pg7K?~zs7hA>0ilsGwEN=_$6fj zI>s*}UdMQM(!YuE%jtc*t&Cqm&sDcEehum0&iG*ByBWWp_#Vc?q<=5tU#sW8j1MP0 z`x*aLJ^y7~jf)O4ewfnD!Al{SlwZz?wZzZ0>_`iv#GX4UUt3HgsL_D4GSBMW} z{0-t6jISb|$@p60S&XkI?qhr-ag*^a#QluFOMDdL?-S2ud?)d-j3*Nx$9Nj?35*XW zp2zsj#0we!ns_ne6Nr~Gejo85;|~(AVEi!gnT#JLUd8wc;?<1D(71gb<6Vi*XS_S{ zg^VYt@jv5d5ns&sO0s7O<9moNW&9%2vyAaeh%aaSGU6*3zk>K{jQ1tJlJWk;S2KPM z@pX(3CSJ$*8N@d+-jDcJ#&4x|avS6K5#P@EbHsNuejl}mdl-L?_+G|$5N~4KpniEj zdJ;@Jb zT+OF47+3SDOvcrGDvR-HWT%hu>BLROzo2sBXIzcPMlt>%>B(jMH7d_z8TU}XIgatG zNPYt2ZqlE}_^V`RA>(RXQOtM|=`UscFQh-n_zT1<7=M`ZYbN7=qjXg<{t~q_)r_wq z{qq=KOME`#YCg4)@iS-~xQOur;)@x7m-H`Td=|;e+G`jNI5`JC6R;w~LE`N^k9eXC zZs&!>lNm22?qxZ0;x z%DCF6Rl)c=va^cuI^y#fSNpUUGOqS%EoNNp(^|^7+NZUgakWqDHOAFGt<{Y0B|GaF zZz8^xakWotJL77f)*i;Aq^F7T?}>@uW}LIi1!SQyH(J`KOQZMjDr>@}~3* zrUs1fOIPi+^0Sg(-c1_F_cyaSk4Yu#XLp+J`9mG=^H(XLrF5@BMd_T|K)WGw7G8dgG^(3+9fK6vfp2hfrvn4NP z{6pf47+;tq<@s~u>#$RTgFm+|C7#6ArTyng`E&tN=6+|PKFcq!wfdr3Xjj294J%=jb3`TCN2U(8_Rd?Q8b z@5%U;#M2ocK-^^9OMC+3w-D#^$1)lq@Oj)S;(R{!4skctdsV+~zk>YCcoA_vPJ8fb zDZiV^_Z=#^o7xGb|HfgGXEFXc@p+8Dmo4RuzGe4M2~L~@zrktpw@{r;%P*eTH<(`$ zH2UUE3{EuqPAV%i`c5nPU!_{HO2j}A-9qdHwNbE1Wu<}ZEK})$pcWohA|XTs?=FW1 zxk5j|#@Y|C^b_(4ho#kFWoct8H!m|U#4()=1MaxN8 z_@!X*oz-6Y1@{e>I;KuJ+$=3Nt6%cw=wAC5q~nw*Z+x=8No{#kWvlA$6-l3QIy{wYGA3u%@UHtxr KdOnkA((@M$(|H2` literal 0 HcmV?d00001 diff --git a/git/st/win.h b/git/st/win.h new file mode 100644 index 0000000..a6ef1b9 --- /dev/null +++ b/git/st/win.h @@ -0,0 +1,39 @@ +/* See LICENSE for license details. */ + +enum win_mode { + MODE_VISIBLE = 1 << 0, + MODE_FOCUSED = 1 << 1, + MODE_APPKEYPAD = 1 << 2, + MODE_MOUSEBTN = 1 << 3, + MODE_MOUSEMOTION = 1 << 4, + MODE_REVERSE = 1 << 5, + MODE_KBDLOCK = 1 << 6, + MODE_HIDE = 1 << 7, + MODE_APPCURSOR = 1 << 8, + MODE_MOUSESGR = 1 << 9, + MODE_8BIT = 1 << 10, + MODE_BLINK = 1 << 11, + MODE_FBLINK = 1 << 12, + MODE_FOCUS = 1 << 13, + MODE_MOUSEX10 = 1 << 14, + MODE_MOUSEMANY = 1 << 15, + MODE_BRCKTPASTE = 1 << 16, + MODE_NUMLOCK = 1 << 17, + MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ + |MODE_MOUSEMANY, +}; + +void xbell(void); +void xclipcopy(void); +void xdrawcursor(int, int, Glyph, int, int, Glyph); +void xdrawline(Line, int, int, int); +void xfinishdraw(void); +void xloadcols(void); +int xsetcolorname(int, const char *); +void xsettitle(char *); +int xsetcursor(int); +void xsetmode(int, unsigned int); +void xsetpointermotion(int); +void xsetsel(char *); +int xstartdraw(void); +void xximspot(int, int); diff --git a/git/st/x.c b/git/st/x.c new file mode 100644 index 0000000..7767cb6 --- /dev/null +++ b/git/st/x.c @@ -0,0 +1,2045 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static 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 b; + uint mask; + char *s; +} 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) + +/* 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 *); + +/* 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 */ + int vbellset; /* 1 during visual bell, 0 otherwise */ + struct timespec lastvbell; +} TermWindow; + +typedef struct { + Display *dpy; + Colormap cmap; + Window win; + Drawable buf; + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ + Atom xembed, wmdeletewin, netwmname, netwmpid; + XIM xim; + XIC xic; + 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 void ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, 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(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 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 vbellbegin(); +static int isvbellcell(int x, int y); + +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); + } +} + +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 | forceselmod); + + 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 >= 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); +} + +void +bpress(XEvent *e) +{ + struct timespec now; + MouseShortcut *ms; + int snap; + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + mousereport(e); + return; + } + + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { + if (e->xbutton.button == ms->b + && match(ms->mask, e->xbutton.state)) { + ttywrite(ms->s, strlen(ms->s), 1); + 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 & forceselmod)) { + mousereport(e); + return; + } + + if (e->xbutton.button == Button2) + selpaste(NULL); + else if (e->xbutton.button == Button1) + mousesel(e, 1); +} + +void +bmotion(XEvent *e) +{ + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + 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 +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(char *fontstr, double fontsize) +{ + FcPattern *pattern; + double fontval; + + if (fontstr[0] == '-') + pattern = XftXlfdParse(fontstr, False, False); + else + pattern = FcNameParse((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); +} + +void +ximopen(Display *dpy) +{ + XIMCallback destroy = { .client_data = NULL, .callback = ximdestroy }; + + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im=local"); + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im="); + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) + die("XOpenIM failed. Could not open input device.\n"); + } + } + if (XSetIMValues(xw.xim, XNDestroyCallback, &destroy, NULL) != NULL) + die("XSetIMValues failed. Could not set input method value.\n"); + xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, XNFocusWindow, xw.win, NULL); + if (xw.xic == NULL) + die("XCreateIC failed. Could not obtain input method.\n"); +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + ximopen(dpy); + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +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 */ + ximopen(xw.dpy); + + /* 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); + 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(); + XMapWindow(xw.dpy, xw.win); + xhints(); + 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: snowman (U+2603) */ + g.u = 0x2603; + 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 +xsettitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop); + 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 (win.vbellset && isvbellcell(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) +{ + XPoint spot = { borderpx + x * win.cw, borderpx + (y + 1) * win.ch }; + XVaNestedList attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); + + XSetICValues(xw.xic, XNPreeditAttributes, attr, NULL); + XFree(attr); +} + +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) +{ + DEFAULT(cursor, 1); + if (!BETWEEN(cursor, 0, 6)) + 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); +} + +int +isvbellcell(int x, int y) +{ + if (vbellmode == 1) + return 1; + if (vbellmode == 2) + return y == 0 || y == win.th / win.ch - 1 || + x == 0 || x == win.tw / win.cw - 1; + return 0; +} + +void +vbellbegin() { + clock_gettime(CLOCK_MONOTONIC, &win.lastvbell); + if (win.vbellset) /* already visible, just extend win.lastvbell */ + return; + win.vbellset = 1; + tfulldirt(); + draw(); + XFlush(xw.dpy); +} + +void +xbell(void) +{ + if (!(IS_SET(MODE_FOCUSED))) + xseturgency(1); + if (bellvolume) + XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); + if (vbelltimeout) + vbellbegin(); +} + +void +focus(XEvent *ev) +{ + XFocusChangeEvent *e = &ev->xfocus; + + if (e->mode == NotifyGrab) + return; + + if (ev->type == FocusIn) { + XSetICFocus(xw.xic); + win.mode |= MODE_FOCUSED; + xseturgency(0); + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[I", 3, 0); + } else { + XUnsetICFocus(xw.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[32], *customkey; + int len; + Rune c; + Status status; + Shortcut *bp; + + if (IS_SET(MODE_KBDLOCK)) + return; + + len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status); + /* 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), xev, blinkset = 0, dodraw = 0; + int ttyfd; + struct timespec drawtimeout, *tv = NULL, now, last, lastblink; + long deltatime, to_ms, remain; + + /* 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); + + clock_gettime(CLOCK_MONOTONIC, &last); + lastblink = last; + + for (xev = actionfps;;) { + FD_ZERO(&rfd); + FD_SET(ttyfd, &rfd); + FD_SET(xfd, &rfd); + + if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(ttyfd, &rfd)) { + ttyread(); + if (blinktimeout) { + blinkset = tattrset(ATTR_BLINK); + if (!blinkset) + MODBIT(win.mode, 0, MODE_BLINK); + } + } + + if (FD_ISSET(xfd, &rfd)) + xev = actionfps; + + clock_gettime(CLOCK_MONOTONIC, &now); + drawtimeout.tv_sec = 0; + drawtimeout.tv_nsec = (1000 * 1E6)/ xfps; + tv = &drawtimeout; + + dodraw = 0; + to_ms = -1; /* timeout in ms, indefinite if negative */ + if (blinkset) { + remain = blinktimeout - TIMEDIFF(now, lastblink); + if (remain <= 0) { + dodraw = 1; + remain = 1; /* draw, wait 1ms, and re-calc */ + tsetdirtattr(ATTR_BLINK); + win.mode ^= MODE_BLINK; + lastblink = now; + } + to_ms = remain; + } + if (win.vbellset) { + remain = vbelltimeout - TIMEDIFF(now, win.lastvbell); + if (remain <= 0) { + dodraw = 1; + remain = -1; /* draw (clear), and that's it */ + tfulldirt(); + win.vbellset = 0; + } + if (remain >= 0 && (to_ms < 0 || remain < to_ms)) + to_ms = remain; + } + deltatime = TIMEDIFF(now, last); + if (deltatime > 1000 / (xev ? xfps : actionfps)) { + dodraw = 1; + last = now; + } + + if (dodraw) { + while (XPending(xw.dpy)) { + XNextEvent(xw.dpy, &ev); + if (XFilterEvent(&ev, None)) + continue; + if (handler[ev.type]) + (handler[ev.type])(&ev); + } + + draw(); + XFlush(xw.dpy); + + if (xev && !FD_ISSET(xfd, &rfd)) + xev--; + if (!FD_ISSET(ttyfd, &rfd) && !FD_ISSET(xfd, &rfd)) { + if (to_ms >= 0) { + static const long k = 1E3, m = 1E6; + drawtimeout.tv_sec = to_ms / k; + drawtimeout.tv_nsec = (to_ms % k) * m; + } else { + tv = NULL; + } + } + } + } +} + +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; + win.cursor = 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/git/st/x.c.orig b/git/st/x.c.orig new file mode 100644 index 0000000..d9a4efd --- /dev/null +++ b/git/st/x.c.orig @@ -0,0 +1,2025 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static 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 b; + uint mask; + char *s; +} 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) + +/* 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 *); + +/* 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 */ + int vbellset; /* 1 during visual bell, 0 otherwise */ + struct timespec lastvbell; +} TermWindow; + +typedef struct { + Display *dpy; + Colormap cmap; + Window win; + Drawable buf; + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ + Atom xembed, wmdeletewin, netwmname, netwmpid; + XIM xim; + XIC xic; + Draw draw; + Visual *vis; + XSetWindowAttributes attrs; + int scr; + int isfixed; /* is fixed geometry? */ + 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 void ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, 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(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 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 vbellbegin(); +static int isvbellcell(int x, int y); + +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_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); + } +} + +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 | forceselmod); + + 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 >= 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); +} + +void +bpress(XEvent *e) +{ + struct timespec now; + MouseShortcut *ms; + int snap; + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + mousereport(e); + return; + } + + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { + if (e->xbutton.button == ms->b + && match(ms->mask, e->xbutton.state)) { + ttywrite(ms->s, strlen(ms->s), 1); + 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 & forceselmod)) { + mousereport(e); + return; + } + + if (e->xbutton.button == Button2) + selpaste(NULL); + else if (e->xbutton.button == Button1) + mousesel(e, 1); +} + +void +bmotion(XEvent *e) +{ + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + 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, + DefaultDepth(xw.dpy, xw.scr)); + 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); + } + loaded = 1; +} + +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(char *fontstr, double fontsize) +{ + FcPattern *pattern; + double fontval; + + if (fontstr[0] == '-') + pattern = XftXlfdParse(fontstr, False, False); + else + pattern = FcNameParse((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); +} + +void +ximopen(Display *dpy) +{ + XIMCallback destroy = { .client_data = NULL, .callback = ximdestroy }; + + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im=local"); + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im="); + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) + die("XOpenIM failed. Could not open input device.\n"); + } + } + if (XSetIMValues(xw.xim, XNDestroyCallback, &destroy, NULL) != NULL) + die("XSetIMValues failed. Could not set input method value.\n"); + xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, XNFocusWindow, xw.win, NULL); + if (xw.xic == NULL) + die("XCreateIC failed. Could not obtain input method.\n"); +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + ximopen(dpy); + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +xinit(int cols, int rows) +{ + XGCValues gcvalues; + Cursor cursor; + Window parent; + pid_t thispid = getpid(); + XColor xmousefg, xmousebg; + + if (!(xw.dpy = XOpenDisplay(NULL))) + die("can't open display\n"); + xw.scr = XDefaultScreen(xw.dpy); + xw.vis = XDefaultVisual(xw.dpy, xw.scr); + + /* font */ + if (!FcInit()) + die("could not init fontconfig.\n"); + + usedfont = (opt_font == NULL)? font : opt_font; + xloadfonts(usedfont, 0); + + /* colors */ + xw.cmap = XDefaultColormap(xw.dpy, xw.scr); + 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; + + 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, + 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)); + 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 */ + ximopen(xw.dpy); + + /* 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); + 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(); + XMapWindow(xw.dpy, xw.win); + xhints(); + 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: snowman (U+2603) */ + g.u = 0x2603; + 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 +xsettitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop); + 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 (win.vbellset && isvbellcell(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) +{ + XPoint spot = { borderpx + x * win.cw, borderpx + (y + 1) * win.ch }; + XVaNestedList attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); + + XSetICValues(xw.xic, XNPreeditAttributes, attr, NULL); + XFree(attr); +} + +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) +{ + DEFAULT(cursor, 1); + if (!BETWEEN(cursor, 0, 6)) + 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); +} + +int +isvbellcell(int x, int y) +{ + if (vbellmode == 1) + return 1; + if (vbellmode == 2) + return y == 0 || y == win.th / win.ch - 1 || + x == 0 || x == win.tw / win.cw - 1; + return 0; +} + +void +vbellbegin() { + clock_gettime(CLOCK_MONOTONIC, &win.lastvbell); + if (win.vbellset) /* already visible, just extend win.lastvbell */ + return; + win.vbellset = 1; + tfulldirt(); + draw(); + XFlush(xw.dpy); +} + +void +xbell(void) +{ + if (!(IS_SET(MODE_FOCUSED))) + xseturgency(1); + if (bellvolume) + XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); + if (vbelltimeout) + vbellbegin(); +} + +void +focus(XEvent *ev) +{ + XFocusChangeEvent *e = &ev->xfocus; + + if (e->mode == NotifyGrab) + return; + + if (ev->type == FocusIn) { + XSetICFocus(xw.xic); + win.mode |= MODE_FOCUSED; + xseturgency(0); + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[I", 3, 0); + } else { + XUnsetICFocus(xw.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[32], *customkey; + int len; + Rune c; + Status status; + Shortcut *bp; + + if (IS_SET(MODE_KBDLOCK)) + return; + + len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status); + /* 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), xev, blinkset = 0, dodraw = 0; + int ttyfd; + struct timespec drawtimeout, *tv = NULL, now, last, lastblink; + long deltatime, to_ms, remain; + + /* 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); + + clock_gettime(CLOCK_MONOTONIC, &last); + lastblink = last; + + for (xev = actionfps;;) { + FD_ZERO(&rfd); + FD_SET(ttyfd, &rfd); + FD_SET(xfd, &rfd); + + if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(ttyfd, &rfd)) { + ttyread(); + if (blinktimeout) { + blinkset = tattrset(ATTR_BLINK); + if (!blinkset) + MODBIT(win.mode, 0, MODE_BLINK); + } + } + + if (FD_ISSET(xfd, &rfd)) + xev = actionfps; + + clock_gettime(CLOCK_MONOTONIC, &now); + drawtimeout.tv_sec = 0; + drawtimeout.tv_nsec = (1000 * 1E6)/ xfps; + tv = &drawtimeout; + + dodraw = 0; + to_ms = -1; /* timeout in ms, indefinite if negative */ + if (blinkset) { + remain = blinktimeout - TIMEDIFF(now, lastblink); + if (remain <= 0) { + dodraw = 1; + remain = 1; /* draw, wait 1ms, and re-calc */ + tsetdirtattr(ATTR_BLINK); + win.mode ^= MODE_BLINK; + lastblink = now; + } + to_ms = remain; + } + if (win.vbellset) { + remain = vbelltimeout - TIMEDIFF(now, win.lastvbell); + if (remain <= 0) { + dodraw = 1; + remain = -1; /* draw (clear), and that's it */ + tfulldirt(); + win.vbellset = 0; + } + if (remain >= 0 && (to_ms < 0 || remain < to_ms)) + to_ms = remain; + } + deltatime = TIMEDIFF(now, last); + if (deltatime > 1000 / (xev ? xfps : actionfps)) { + dodraw = 1; + last = now; + } + + if (dodraw) { + while (XPending(xw.dpy)) { + XNextEvent(xw.dpy, &ev); + if (XFilterEvent(&ev, None)) + continue; + if (handler[ev.type]) + (handler[ev.type])(&ev); + } + + draw(); + XFlush(xw.dpy); + + if (xev && !FD_ISSET(xfd, &rfd)) + xev--; + if (!FD_ISSET(ttyfd, &rfd) && !FD_ISSET(xfd, &rfd)) { + if (to_ms >= 0) { + static const long k = 1E3, m = 1E6; + drawtimeout.tv_sec = to_ms / k; + drawtimeout.tv_nsec = (to_ms % k) * m; + } else { + tv = NULL; + } + } + } + } +} + +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; + win.cursor = cursorshape; + + ARGBEGIN { + case 'a': + allowaltscreen = 0; + 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/git/st/x.o b/git/st/x.o new file mode 100644 index 0000000000000000000000000000000000000000..c50ad8929e7ae9deaf6cbf6032792eaad1258ede GIT binary patch literal 74464 zcmeIbd3;pm_4t3YfB_*hDAuU>aj1g^6$}s{5!4Ksz>Nlo0vZbnAqzx85|ar8MFA(V zPGgkTrPkU?TiRN+m1DbBiyRwmxr+BhRC z+7@5;qsF5UHJ#I$xy3iD06zhncw?vYW6+Yyw#2FO>!Ge%55m>?ld~gp~Y@P zXXI=fX7xW}0-H>JdPI%w*lgpOeDw&5ZcEyViWbkCV^WNL=f=M3ArU*?#ErGP)-8{t z#ZsadaHkkkYY%fl`}~Wy8XG%4rz~0XVxPNBCUeHCHW$X0=-w1J_E~PMwb*S;9F>Y` z#lpd)am_mugJ?+U&Tuf%?gn;tx(#1PCXiunWAB_dMuJJj?u2xs$k#9tYzL!Kwl>Y! zcjl*{ZH3=0>U5ioy>8=>10DCT>--S7?}m@yJGRp;YIVz=%e?;G47cUm6I%|PFnWty zw8brJzWc7bPH|h>0=_`6v)O@PDZ&5{!3?cFNShZTa?u(XCCxv*MmJMI1XYx|LJE)~22pOmt&!d;6&U zZhv8ln**y>;+e#Lh@P6f0#Y4m5I*ps8b4aHjXY<2kB z_%09`Wh=MavL`Vwt~K^q>{~asLsk>44AcTpsN>bpg`o>V^Fnh?&0Fe1La9B>qN`krtm;NR=NO09ga~$rewLA zV0CLYUi_7fd4Ep(Ico4yIex;l@SE*S;Bc%P zE$<|{33G!W56axe+<+S!9GeE({BwgTU-(YjQW~2!1omJs93?|4bAxG+3WP>?#)0RC zhr!>`#GoG3htUkk;$jWJzz zgQqe~PCAP65=LRZax$Sa^wr-3hi-IhT&6M3cm1v4`P|s+xh?M{x;8uKG^Y2iu#wHw zg-p+k{5YxhvDkK2a3LswdJ7f#*{+Hld&Rv~X0H(%SbXeKtWgV0jo#X;MlUcm8bYHj zaWPN_RjqyWq0Y{n%<|w6Uqdd`*Vy)spSe)Rja6x;NKBX4t5C92+h2fq(PqCL7TuzK zZp#OdIa9N!Rv0I@&j!;CXtchZKSK0*^}$;~bX;?(uQD-M8uo2( zi+5u$x-B0Epha$X;zPchx8?XAd@mH;7H5kpwk71dx3%df@iA~5^ogcD=fwUMitdPq z)3@QXCmd^w?io-TJuo10YUu0jiIJh)#u_eyb_^&kGJ;U#NEl`h2O~NvT|%{&V55so z4MLybIB}5+a0EUJ3gbXaThb;N-k`z74%mY;0PcaoBOILSHZ2HNxlMV&j9jP;cC6@Q zgzh+s5^Nk1bi3OOxBBWo0!iOP*-86bjoolK*dCq%hePb)R5(n=;Il9f`Tggi@6@*Y z>aRDcLy@0RbUXXLwZNqjhIs&TL?LUUj2+f=hZKln(lWDQGuEvwq@;f0138#gCchP!DbcreEU4Wjbk7I+oIxLZ2vnDYz?K) zwskcJIyUWrj{lDvM0+tP-i&n!%FQ?s%dqXyufVQ>I9izEg)oEvh z?BSqm2V0Xc;QMc2ZUv1dwK#V|VzBmb7$Guj#jv9TUTGNv%$(R>dBqO((L2Im*YI&< ztlQ|U-5rM$huCXYJ}$3f??9{U7RrrncX4X_BWw?9_8-d*n6N<@28}aOUaj)11tWK2 z1^pp`$)F*x$yDUrojY(M9(okw!2InqaDF(p=kve0`=52A?Qw2nO#mkfL-Kk~zuleL zSi)fAGMG=z2{gvXvXdImNsOI4q;zL?3Unvu4=Ih!IW}%rEJ0~)x~$XnJ%OWsQ#kH> zn}_{h$FdX6b*$S6!!UFVBj|ebeM}^}Ex^43Oi#^)mSf!A(aNV-Y0w~xH)Hl;nhPFl ztV@x8GC~c_waHMq5;Ni2!q@aHROX%8e((v5dZt}Hax5IvEWCTl(|#ui{vRl=|CLcZ zTq-`UPsLT-oJ>WAsrjML@zM&eyTt`O)V8%}kMY%C2l}Ya!MekLeD%Ko@VU=#E}~91 z_WQ2+B`}(`yD_TUw5&m=5CgBQdO=VFi5oT8Y1p}3+vm_1Le*+`8T8_%TB+}J(|h=S z`X-U?3&Ycz?X0oqcA_MTRVbT6lL|J6G`kL>!vm{}C_uYJUXh(D0#K#GZu z?s6Uu;9|To*D29$snA)N+GVOKqZ;IwRg%+yT6v-=6+aY!@h+Uv z-kzI@G}b_`_;B%RBDl5$Iv%Ib-PP+R<%( z)5+sy3=V*@(%p;^DJG1{~v~NuLcSmb))8Il-%mt7nmRLh3 zg}kN#fqsmP;`%8L=UB#Fu|5K|KV&1u!u8ht9(|+>Wlq()p&6D&s4>7Sqz{VSA51D? zumZQj0UBx!Qu!c_4>I^5ix13jfw7@pb$7=*odP(wX~nXK^MT*a;A3-6W0JZvI}EkK z*_oY%yXcn0*qjm3=EPWb3SUQ_;O=Aadnh*)Q>kf~DnIeZJ@xc(A zYV@77hjFnf`mH~5)We6Fqd?^F=(lmlG)MOwh&B(3WxHnO8pin~xL5;EF$~Js4J4x2u<|9SD^*e}4CbjSC@jMr!1U?vsqjWHXD4u1O2j!e!fxEH`=} zG2(;o0k!YjW&T4QuSZQ=innv6!C!ZFc06h7UH9ZkEEz=i!bS7J9(4mIp*?zCaY2#` z*K9CVjUG55G9-FnMD3X~8_R>K9RqNeF%#yGr#%L4gDDft!Q0Qlo`mKJBY^grC)jx_ zuW*ph%K?csS_yHJp#Z>qcP$ot;ncisB7YJ-+=!BW`@*I9br2 zm_sk|+@crzP||Uw+f)~~!x@UR;nCkdJ{Z!rZ5YO&_iV+AgR5ek%)SO;Zczl5zrnPUiO@(2x<4ZFKgv+V#bnR`s<2_u!L&-AtdjzKMvNw#V2Z}G2%vwwfx;*SinU1CC{#fC*&0x=_O z$ISLKph)Zja_yFm-^7NZo*n|nfPlUEhaDkiE(>zfPzku{vgYXbalUJc;JBl3-Jv%@ zz<0wuI6KoGo&$%FAmjq_I}-9cvhVypj+Ll=2uOFQw7Exe(7ZdtO+#)5Ij7~@gy=i4 zCjGDI2Z@7T3Ps=cPiq>U9QSPd)!lU{x_6PT1#m+L#vK0%pn=!$;qq%nnpv}l1E@CP zV3yl>p^J;8`EGPgmQ$PP#-K}ucrK>wRWIGx7Pwx@a5GyY^ITevuV0S|LAU6uzs4k4 z1B*Uaq~Hai-wp~;e75JnITPNG!2&Mm8UlO`c*__HcoEcaTSL)8xH6jqR$6VBLInbS z`P|wApmH+~id12;voJ~l)~5E$!G)d~Xq=Nqb1&1VhVV(|mfD}pMEj;fY^|HV=VuPs9LNVBZc4>>r1s z+hMuotu{Z?s*Hkubr`WxC;Z3j0p`N*B*)aprxJAV_2U$cDjx8`EJ5axEO5v z4Jhe&89SZ*13Ts31Y zU8~gHb8M4JR|%x%#t!uu3E;Y>mluYG8kf&?n~q24#vVFs+*11$W&5)CcU*;{}e1EPoGA}3(mme(VA z`yY&ji(tHU14GycPjvMMZsTy6bmJmStkpf$lp5T1Z-WjImjym|eWx^I%d;VXoRg>eMYO23mwxy?{Q%S~Zc~WYT3Vb(i^*#8XFMO3o zi*K0O!JVQJLEkXzI#@(n_y!KLxSW)RM>}J$#Xd20wfzt`*BEqv|V8VFJ-}JW;sk^xP(Du>mrhMOao32Rdoa1k=fl8VKQ&|`f4#B1PV2EoQ zMF{F63_zjS&d@rT3dB33@VOsIZb(;3e+V24DV?zPiWJ9pM{~!dwO1Tt4$iS@zw4{t zfd)hM`lRs+7|=dxE4Qh?l>-l!?D9RBozmQPSbcNtzu^u_WFY8?k8Fc^;t(hD4+uQQ z2Hpo%(GPueWcB~5KDPf3iUMynL@u`8PK*S655jfJ)HRWF@qTl__jvP=wR8g;-Pe(B z!mkYnNA3W*<6tyQ!ur)QqIBJ%&V#<6Vpk63m4xF&HXLJn{|(N2VTlaxJDTQTtHCI) z1~|)e8&?Ko9}L5zy`c^VsrXdT0268cR=Y%vE{K_y4$c`En+O*04BnPVQ^CR5E5F*m zYIuwvb!tn5Q@`mLVX<%48(`q5Z?qlLlb3HpM*~`N| zq;TPxmLo(bDI=Lq*Y<#;p_RBrUwGUfwihr$3wCa>XeOG_y1lq&D_T)~@J;~m=!$8R zbiYNYfbxX4vN5;^vf42?`lvZLGI9nyeISd%*x0=+Ui%VB{-}0$k$j%O*r2q+-9^q64jsl^k8E3=&8L0IkU+&J9 z0|NmjhFUsL2;*(vW1EJ=N8dhsbc-GGX2w3ref|Tu`7yj{+S%|xP3{&O!5?dd`!Pa1 z_xbnDGagWX@!Hb~J^DZJ?b@~cJbMY#{y&f@V|HVPneOAf%$)ama~`i}P3RMTP-Tw5 zRJf7pYY0Ghy{;ss6Ye_VRh&Psp?upSU&D)l(F0k+17J}1YROTlYaHl{M&P6~2|Az7 z_AE%G``Tb7h-^rIdoAjHLxf+YGY|N#GY_r7o#?cNuYC;@z!bE8k+EkpW*Y9|9Q?w)+gz#U4T@}A zWOF+RatnzM$LmK>GxGpEDP+fQxFK)nznBf2MQj)x=LWNUqw!iov_R;nbKIueO~b>*dJ$w=9nSX9GcZ5g z^VuAS{A|C@;B!kDiBGiPK(;F+JjNsTL~Hm=EN+XD|sm`N|J=*GUY17Sn+Rj0!> z84W`{pM-)-UNeT8+QZ^&VA^VFF{-2E0&$!tjyGNf$%-1iemX;>oM4F9k# z1uq$1F`97!+yBp0C<9Oox2BCwGV3sy0~_5vBL4^LHYJf@7Fhjafz=wG_&m82U2HPc zwtx{nE6NMZ6)rv9U|nb*@_7Xw@~MGkgjabm?7aku*A!fo_^>BEf0wzndKW+O!4Y-D>O}B_N$R*FGY*Vj`|onrA%Cq}}8S zIvGFY3p$w>a~soN#|N3=zHfKUz`I}O4Ci4-SVy2KP!h(mBHK0#;$clA0FR5pLr(e0 z?z#Y!nM3oXZY%nbZ*JL>0?+LYinohE*t~GMztudWyKXN|+hd1fUx<2CAH+ream~gZ zPy>e8LPSG(5(dq>9v=wjii%-9=4Y6} zS#q&i>@AK}4i=e=SB!Qv=^a?4>Gm)`+v^J-aP1TqD{|W@Z_|Aos!wW?CUu5lZQ_~a z#IQ}yckl6-r*PA`K+H7FmfM~F$mwp8sSQvu4&y0Y!q3sZ#`9Ccqqo>uUDMpb-`i!A zI|CTSWCAKXM6t0Gt~OD!MubQ2vZyKS|K6tFtB$~X9`?%;saS zLM4ggLNf)~9onmUyNz&@rn@&ILvG85iIkXiOV8a85+K&Bw<((CZ9WzGQB zFkHv(Lz}JVxJY0#(A)8Cv4Bw`mwA81gvxtk}PVNQ`^dV`KzO{s{>>gZlK6#G8*g&Tu~ICx4L)@HWn z#CG?vIm0|CJ;XdH{Y^Y9jNKGm3t7PDray{@n-gJPLD_;cjKk$t%hjxr}9oN(aypD87wz@;{FV)OksbfudludpU1(tq# zM=?+Sfu4@pGC^NGs@O)-}Q zS#(K2HF8g!TyBf*T^fpg1%G`8m%$mK*uS7_m`Z<$mIo-`Eqc@RL~-B6c8%T_+Y zhP1rc%WXG2iGNA*iSPPfLydz4F_;2gza9=@AY6ck*hfg)gMjcV{(AiDqwthSFwJ-K z7U%;5nef<}@8;&DEwzW2PAP{~h5EN4mg&YyecRyr)bu1-yyP%E2#wKHW>>bzUGAV( z>{8R*mbU{i{ttQzujiA(fHK11iUrUK*(Zrt2lLw=dIoRSZ?l&+u(p&l`a_T@FgGX6 zJli(&Z%s2F9)z@^GxTkH2225q;S?IVZ}f;DG#Wp)7`%WUc>qkvgR_=*0@%!2O*0F_ zqt9vZ3E+e1Sz-8EU>y>Izcev7!{DkZ41b6-^{R_v99$gq1AynSeWOncLUzEx_;h&BE)N`xOC*%PEgrs|55rf-FK||P zsROfE(|707j_>7Sz2KQS%`f|^%chy)z{qzjPqKPUxit=t@2des;bvdARL3i3fjy=7 z#q_>PGiG(Xq=O$F>ameE&-$cQkbR zaz3{=5XSAu`g%v8HqJgpXIzevA=x3OpM+;bRvytYz|6qxm6Yww~?}g*F-;RkK?KaK>I4!OoB9y^e-9I6c(=l#sUA$BKt{dH+6pDTv*Zvs5ZaXjz zE*tnBOgt9jjfQy9w$Ri`zMl?Efi0p=~#Ap+4UHS&%6O8@dcus^P9FURmRy+mZFddd}^wr{lq#hL5irZqz|8@B~&T z?HwChn(@`I1rPSFj$(0xBu%OLa!eh!Woz44!`G(hec^q!j+pVv2GNjt>8DWAv3I$3 z(lb0Ea3@?@cGqt^Saq$=7(MnY^8=hk|Bw@Bwe)gg`-8ZE)wCL1f3|V`mEg#f(r{S`pNtS7hke) zQQ_jE;*!!OOUo`@R=&KVvg)$xnn>-6m36Bwzhd>3SHT-9948+>@lZeT`PXN5?>;LD z_E=qT%g)QqpEf&me%L8mT3B6E5^-vZ3d;)@mzOv-<%Jayr?j#n5-^8>;>wbmKt*LF zu)Hu*v~;kuvZQRuQj)O8y+n!%D^89CDyvE=0!GoOn!!$0SzSqaP1)s;0)AzcEkCoo z5)w5>PH{<1q`Godc42w>;=-b3kbY5Z&4pzZ#g!|aqVlqmiU^&QRaDhR=0sM3rMa`R z&v&xR%c>Sv7FHJrAS&pKlvP5ag=OU>#e<#W=8sQHyUOgw;cn`v;wk3;s3JI@ZE~_|wabe9;r>3O5q$on_&In*OiYjZ%i;X)dy^=stWqD0UNo}W8whEr2j8ObBJI91gpCB-u)`0!aCRLWgkX=KYB-(-d;CP;7$VKa&k8ushCO`dPnEyj z;j=G=&ocPTbcVwBa`;rhrxHF@@VN{=HSnp0&r0~Lf@ok0WD0f@>@MBzCfeO3yPIrx z8Fn`wa*h$~F5T`X+TA3(n{0O(b~hgV3K8rs-R>sZ-6Xr4Y@FR4v$0J86)M;3$@o82H_O@=O zk(B9E#zO{CC+ux)97j@6YKGG{NpYQ>KvEM(%Cvy-=_Hj-y3$E19i@(O1~}Cv#S@@^ zC@HC!;H)Zv9&hCY=qGAR#yiUkmq0IBIKe4eRS1FDhfGXEc`HuR&WRI?CKi>Jvbd3U zRxT}zlsG3AnGHnD2A#v0@e?PR-VO41_M{?s%DM(p!HzI6e@tQ7ii=@a6eub$tf|4n zQqzs$(UQQDlFH>Jk?K`=RuQNuTwa1lm4Q;|nJ*q3=-TFwnHz|dMF2u@r2BAXV5RB( z%Zedj{ul@@s$9Oju%b9Hzp#2qOPDvzp<^_dN@-=-#f&o2X<3JfKtt<|#KuL&MkX>C;7%9ok?jus= z;z(gxMUQwUEu1gRt5^!IhcOijo5x!_IO1@dRyV4=4$X3Ms;glrtSPA|#=Ook!zW10 zs*IGCt_qZ_fEsG#JV()!N7bAR66n}Kb>YfD6;!Q^Kt*|AOi9tw%D|YilDY`YD#jGm zL@KKm2PR~sr=?-S|K0xUf&Y5ozaIFn2mb4U|9arR9{8^Z{_BAs@W9QTHjvlDx^?Qbs{#*Ld?-kDaJ;O== zt^Mft3TOSE;iUgI(od&d#m@&EuFHqRf8${vhu==&c=KCsgdMMNwui&{oOS5(#i-yDwjg!%_o136AfQ~s_*MIK+J;TW%bNlfRhkN;l!@c~&;a>jXa4-LGxR-x8 z+{-^4?&TlPaPrT2{rHE&z5K)BUjE^5FaL13mw!0i%Re0MmLBKOFAm9}f5O4~KjChr_-6;~7r=34CuGDxLOf!9&S^UjE_mUg?vjA`kx) zcD%yb9uC+3)$w(>j<4<0@pU-I?<7I3pW}Ol>-c=m4%YE?IG?kAZJ&;>!#REu3v$A0 z&*5I>n@{msUsrVEp5YXM!?k_f|9gg0{0k`^FaLXmv%MV7=WIW_#VefUIlPzsq-dUp zeVh)5vp(j$=Ws9m7xbf_!@cx#xR?IN*%54z+H<&J_>&HG0_p*<}z3k&~FZ(#$%f9XX*vH{s_HnqEeH`v(ABTI{x1%5XINZxV z4)?N;!@ca|a4-9I_G2H1d)dd~UiNXgmwg=WW#6uT?Bj4R`#9XoJ`VS?kHfv}d$J$< zINZxV4)?N;!@ca|a4-9w>c_sP`w8FOPxv$ag#W9b@PGFc-r7(2v;BlW*H8HK{e-{J zPxy=dgum2J_{;r-ztT_mtNnz()=&6<`U!u%pYS*O34gPn@VEL2-_uVxwi`LO#p`T& z4yQl3vFtLBbhup>dW3Vk_6leHUg5khz}ErY#;!dZk2##@@gn>Q8P3;H-Qo6#<7evt zI_K~U`U$_tBb@VJ?lV4coP8#*j*|*9-P_vw zu_1HTe07%HSKGZ4%W`I9XP*&BoiS_P=s@P!abx+}GxLjYARKr3`A%G&Kkm38hb7@( z3i^SWw^qR(e<2AsWE^jlA(n;dSh50QBZXW!9dqA|`qmOVhW2RBO?12r_u*uJd}(sh zX>fQC@uj$*p6tISK9rmiO$a9k;?EzPoDxd*hmw;*htEh(+PVYvyiW2#+=HHl$w}t# z3%(?NI~EqkUOX7%nK1LNaKN)s2I5?0gPc4#Jq_hydp!}prX~Ath@Y06a(%+Iuqy&Fb1u9mPzCU55_+foB zlJ~?Vv?X}!%K#O-NnaftqrN7z@4AFga^RXoupv4ioV*D0nhFFw*JTa*%DiC@_Q@C* z@|_F5N`=D!-Iy`u2_6tQ6F1nn z1q=~x!SvRXjw$$uC^EgQgu5Y9S0M)=_)(HwXUj1y{`%yMX~|hDlG6%JsbRkMkjzv# z#(b@Wd?7zTd=l}?fMebEW4f^2gE}GoXgoMA;hebSrPGtErYF~hlGny9PhPZOFjck` z5DQbBVvGoBTtamQ&lgcx4AcAJG7cwK#sA!+gcF9uCD%=dw5KOu3!nAyxixMYRI;+< zDpPNFQ99=De~jIi87nYP?@`!cXoK`=e*Eu~3qr|@ij(t~3g4!KSPIs0*!BuPRK-8V z+5U;OrU*=~Os<;-hFy|eh5bc7#rru%o|WuhW~_+60Nm?f9j&79n=!oaI+_+=+PjXT zpYNf#*cYIm7m=T_Ja@W+|;x9+t4VqH;=;RU>NkPE6CRYI9*C% z*za+>3dCn|eKKiYM>0pE43zIIP>f}}u@9aK{88d{=mW^tbvPbegZ*7JF`>$O0gBWq zhu}7nZ6zN_*%`?j5)!_ROTIH9CwWuC^yG)3%W6)Tk-Q7`tqGuXLqhyA2%Q0;@y#ea z7=@?f=_WW`KG^i9;0@cm2Ozxy{Npv)eu-bkPvPXWRbAP&{XfZ6VEg>xatT8VOL!hS zz`kn>^wXeXH|g5|$DC$<{7qen;&{7{WX!|taE|4Xh?NSqpZst7C>Yn?`2jnyT;`I# z7%GKwG+lgPMsn(m@6rO`=+$`Yeko2>eoFadoJk(cjB;Z>c}n% z!}{)&+VKnXyNCElH^cj3aUj2eWU(K^Z5Vt|p5;av>NrOeH~xZS z=I9fYpGR`+w^ZUQh?5JQvBa+;&ht1tP3W4fQy}b)03V}%(=OnA83o~(gZA&CbGAQ| zcz_(gkHSZg9{f8|+?Lv%`A%@Cu?vRdKc;&r$pYqoz%rSwX-;sYuH&9vM?}^u#2_Ve_f&GQ}I^qRzj@tw9 zK|QY$H*-Eq5H_~|LpVmh2qA3D z_ro#jX~2dIo0g(I9T4e;S02pPa{6bV&@p*b;Qkl55fY#G2H;kak`_4 z=PQnHs73wur-|U@kQ6?Z__Zk(nE4+ZPA5JLe_akXGhc)KdBkTCH}ePJ7XwFohM+KP zT<)cYQ{@r7isadDmS08s=b28A=5-LSo_Od4)b2QD-Uj=h5r2-jnXduAk@%7ToWO?t zCT{qm8T7-A0dwk@c^DjSB))_A3>)m=Praf1(2*7}e~9>8;@s}=wf)$O;VaH@3z%=m zd6MM!{n!Hb8@_A-w3C$Mtc7#jS`Fvk5cH6~7f8?kpp`RoE;!snJV+g+nQsAaBR=6I zD{tmjaES9|EHBsMu6p|=aqe%qe7+-|s^k-)zrcQmw*biD_BQO%Z>i*X%C<8E%dr2cC&YG^8a)ZlTE+1+ zLHL=1%`Ty9)RYwEyX%zmb1~GvKasKCm-$KC}B} zkgx7lvF@-c!+Vc|wT5#kcFSK*oc+ZiYe+xO7n#=+KY{`6Z9g}#YkcHj{BdZsle#y? z&W*&mzvh5jiH~GJ``-}HR{Rf!)8Hb){zCFKg5lx4#IIER0pbf4H@|QJ<;C@tgIg$F z?a`gYx!$t;Go+vUS?13hKG3HjJ5-zwfj{Ev#? zNc?Wae?|O$#qS`#P4Pbxe^&9kiNB%v--%Z!{t)r26@QF4{!pOYwi5qZ@h6ENrTqMF z;y+USMdHUQ{vYCpEB-cdUcZ$#B{;fwcAO7EUiVcP=a2`FhXJ84`N1CiM;?5%2cPP} zb3Ayi2cPf3D?Iob;KN`f=Y9`P%@*^JznSE(B{`PA!$Tg|D*N)=6CV8E9(<1n|I&jG z7|?gRDIWYZ5039J@5}yK9(|MB4Oc<@g=__rP$ zmn8b~=TRPfqz9kq!CenN+k;==!54e*N)LXu2fxLG|G|Sl=)v))8vF9w(;oaq58m#< zli(7fFa4u|4}4 z8$9@Z9{e#6{*(uQ)`P$5!Qb)Vdp$U=+4tqoH$3{=Lmu*pus+b2o?*a`fR%#=TKM5B z@Z*Tzs`!b-?G!T;sK-|*la9{i98KO7c3`l=^Kd+?Jy_#_V=^5A(M{Cp3-1US~) zFYpEmYq-O&0 zQsQaErxL$~_(T0Z?;0U!5{YEEx^$aHz_|nZTR7itL9^`5zklrZQ=!rcNosz?OqRe>c$p1 z6}8LZO{~itc!8f&vI5>82;bH49@iCRHD!y-%F80FoZ5=zg;nqlLU^I9QwJ}SbLv*w zcMCf3HoX;TPF;CrVX=MlU|mspNny29T;$X(FI-l#qWbmLaZ0M2IVE*fl{F=pXr#6p-k4al$|i;?ox+-;va*F$)n)J^MSK^oF~cMV z)|C{SBYb%+yn45=wmi~xURSmp-_z&7o?c{&hh-Ht5qOtw8N6`Msapy~Num(<`}74c=W@RnV8PaeFdwX%v1msdi<)g@Jx)e)z%ZtS>m8I#hT%JO1( zeQTt$!l_(k&_s+}U2++`4G>?<3$MqO!vBs-=bCICvkfIWJkh7~WoqAtr4+ zFmH-et5g%vd$C(K5l3olo z@y3~m<4nx)R=bfOZ{)`t`SDiL>NM)d8_nZQ-0?Q92_0`jCz#L)M%x5ytkE{XXq#ZP zO)zmMn79*6+zBRbx`~@^;-;Ip={Bb(R=UxUZsMk!xalVDL?b`Z$WJu#6OH6V6KkT8 zpJ?SxDo_UCfr&P*A(KqpNhU5-ItZO)bWTch7S70DBZfM^dktrB`W&|==?Jg0VX4OCco(K4X0 z?J|0>>CoqZhMA@t?EUJ{Zp~r)c5Kva?dt|c2lfti@MceR7I>wky1JrrAsm~xVZ+Nb ztASL4)2&l5f6(;NH-!~T%HcK86fgt(R-+L5R)!>vZ;}Ovnu5D0tMxz#YzEKKym2pt3BST+Wo3jgY&?dK0>Jn&D zC3Vm-7C;TCSW;r&hA!UZT@Z!_7Ac{KtDNN}%Zrv)V{Gd~Y;ZPGmq<;88R<$fgB8U& z_;OZO-z8JZBJ(P0pv1DLo597rewL1**_wB&fo2OewqW_OH=bwibpLsC=`q>e;+cFDr*~u_q9xqGY9w zT3GBv;BCI4^D`n0LE}&_EJ_=R9OYX zC}4CCPi;)AGku(CIgkm{dZDpKDofFqRb|EYy})7n&Ugok*i1X-z$grp4I>HeKEJY( zUyfg38rcP9HMNE1xfP|A;JREKP02YXHQ_8fn&_u5N-m z1nL_}rzxh|>Kg1`z&@A&WYd{xZYD`=Ek)RybCBsbppHc<0oOp+0=}_BJLI5>&6=~S z0(zmYdMheB`qC&gO~}mdifpR3Okmo8BBT=Q6?QOr&|v3Q;!#)iwmp~~U%;pyVK9L7<@HA`(*m<{C`f^OHTgV`RwY2PWhpwPN!7WCL9#h9Y)RkH27nvGdi zmC$=a`?vElG;}%Ed6Oxdi@L?|YHyex!vq>@#md6+NR2J@!o}FvLlo@Yp}UJ`ARXhS~}9Uh@M6HO|V)>f&P1 zxEy+5=hdYh7Li&!*T7_jon1iO!FrJ#&7JCa5^epP~2$I?q-79y-5R z@qf^HsRv)~!Rr*ipU!`)_+C2y1MM-dCKZ}!P2VJrYy*5)|Hs5x|7j%urQ)pT8{(+vckp37KcaZ7=RJ}; zjyTFo|NPiPp3`SNe4e4?*`70n{@)3Eay{f{3Hjd(`FTp7(=8J6Sk|21%alC+v=VMt z2)+Q$S-wH&*(msSA&))^()n)1SJV0PinAYHA&xr!DD)3NN5Y1?Iq+fmcEM%3`xNJN z?Q<(u56ZKigCvjaPw?UVo<#mY{%67Y`BaQ%^icc|$usA6caGq93Ox%2M?DeBf3ZmS zFGAivmtpP4w#jz>R_WpX=MlxZpMgK)FdN&MPWGRMM!?1#pX{(Bt^z8gMl z&-Y56`$-?H$K!@-v9DwK69m6R@Tr2M$`{C)9*h?-l%jkY6M?KWdKYVwtd>BjA1`Zpi-*ALhpjexKlz1jjnX@@Et0 z@`^whZaIqccyPW4zgY3rR*kbnaW40A;%GmPQEbl&CC}-uQ=IkxLUES=jp8hSpWvh2&6#RXqhwc1UaZWcG?oZ;z<;C(N6z6=6B(C#y znv!QdQxxZP7b(v2PYeAIfnA*6H>DigQ2tmEw0(|C0pwg>d6?TuB`72TJ}f z@g<7$_*kL%YU=-M73cbJIdQc8Pw-*C{anjax_5f;Cl%*$>Se`8kRH5mh8v#V2_H`P zRJgB#8*;RX^M${+w*6|*`9ld zYkQto^4Ry`_KD(Lf4)?FgOP@3(BVD zlD|rE)?cqU>%T>Dmj9dLJfFFrIQsK1@Zs{>spMJDM~btaPlcX;3O(^d-~=}IC(9p3 z9MgRiKAi5!9`dIN`NxF(?Mj~O`5zSLdUcoLT(9mG`epmwuH~toKdm^o(>DaaOW66T z;If@g9STCQv47b9Y{l9Ba};O$XA{@$qE^XseY-(%o@f3=aW1dF3H`F3Y*zBDr%iFz z^Rb7X{esJJp_2Yi4f7>=o#2l{7}uZch_gTWI_pNo*>ATBJ$Q}GdhS#5tY@?0tmko| zr&;LPA$W`6or2#Z_>bvNQ82%9{&^~K&exYT9*tL=?U^j}Y!Uir2)gF zxmL(87J9A~^4kReoscgQ{4XBz4-0v`BpLx9+&&Tg4|7CvvD&%E;pAqsqAP(E}rr^bbzbo|Y6g-apGzQa^ej7*}eIV04Nyy7| zCktL8^iLIfWV+J@m+h`b@LfVrlh9u(_-%qO5&ZW;&y#}xMaVA|{O=y}j|+LJe}|BN zO6Yk($fNJD3~+l>alXFZuQ8{_;+rgXPEefnk5(M}0^HIBm-Q`! zIHoE67WRWmh%Mhx~F6`IR2>>jeL^NcRb$2W{eUcAJp@ zmymy2$rsc7T=S%Qme-22&RBYeh0(<5G!DW3rl{ou9 zO#aDGJdgN7#qT$%!TWJM_~pdW&XHbd1vz|K@XFY!vdR`Ou?-2YyfnF<}OC0TQ5%SBGJlh{pob6vF^t>VT>=N>If93q>*I-|-{uSXpx~&B^^X>MHVFBjD*1mv7;Y^dJbr`?$8;}; z58FA6IHrluU@$*faOqE1=)pKi@WCx#aki&Oa9oG^j-Ed$SDe$W6{A!59 z_CG2(ror{3Md;ZeSc6$6N4W{iBIB_R|)wE zLXRA$&J(|^KnOO}^B#Oy&jkTEfercl@L|3@04K058-dbA^6se~pr7`+p|*T(pzMzc+*)S-$=sgAiF(+g~o^=L!CR;24M7^-F?dxwD?- zK{$a8Wo5ZvMV#$qJ--(6A3+?}(=NEw^M&Bj5AmZg)$Vr9_4zR3i4fKSAJ*>|9OtCW z|3`2fLzt&2{Tm?+w=)#y>&=kjw_9;%hT^QJKylWyk~rG`F?`sbwMw4b<28zNx=o7n zb@z>mZy-BwQ#_s8-R~4Gu;yJ0FG*m+#RY^2aN_ z0pj3xqT*ccsY3r}AjkSIQu1u)Ld7{>rHZqkmkIr!3;j(V^1l%BUkLd-1joLR?cApH zgdq&KrxicVV&^%*>w&SJR|Q932TA@-;>f-d@`nVM<5d00HX@fJ`?*o@IYQ6P#8Lld z!T%`uBZA*6^fU;*S;%h`e5>GJ3;vAY`vvb5{6oRVq{0boXy*a=u-|HlV}92O?u>>L z*iil(_^|vLf=m4yh-*DSDf!7|4nhe z&$~tOa>jg`M9k`2{3DaEv`bf1(b~*F@r|=g)%wM95=UF??`a=OO=hC4aaT zcmAn3KZm(R@gN!o+bf#W`0|$GtBHT0IG1AwakM=SWI4ZID|t>gZmdbZyFHRULlr-f zctCLMCpq2Gf@9hNlAlZ*8NSP%?Kw-yvww00|0%>_`8h(*M!_!;@|Xtat3q+sf4$(> z3OzS^=)Yaaqkfz#<94@@$9I#mo%c(5!8Z$j7sO}zZGxkIwsW`OF(Lng;CL;OUy>&xHQO)8Pa*EIz6KN0LKW5(>dKojB?qAnchd_&~wuD?MC)mU-|hq31B6 zXN8jg62fr1Qt_!4J3kkCl7yZ=D0$X%m*Q;ay+Y3*q32;Ge}L@St@uIW&neF7zN$E< z`zCSpGrsel%l#uIznb)aD&!9r^4}}@bn;J9nmxdBZ-fu)KT>c^oBQEZ#W`Jg+l1Ln z{edI4e=>2@folM)|12dRB>UkN8fHUzcwJAI{46EE$%;7_DZY{TBE|0^zD)5uiC?BT z-ydA9IG5K?73cikrnqTPkj6$2ey`%J{}IJm|1*jwQh)oh2Y*9w**~-ij=tvp=X1rc zqjLF1@!N@aD!zgE;BnTmTp##8+EI#gxsOo%J<@Zc;`PK&S9~?`$%-E&o~1bZZMx!I zUipf%|K|}$pW!>Gxjrvc@_e0pwc>2gwTg2+yk2px&o?X1_S{As?fDcwY|lm|pH6-b2`8R@N z-q>#&6=(f_5&DM<{m%*cCZYdT!KMB^inIRrJ@g+j0hGeV<;(I%6Gz?nZh6kvi9-H* z2xESV;24MXoG$ci5c2T$BeQY3VMqhFX@cJX=d3?haH)TmhyHm=K9BSl3cggOFdV z4Z-oZGuZ#fOtSGfU;nVD4!lp!Y{+r$!18AkM|-56bA-GpFS6%K55CDm&*MT~rn^^g zssB60?>AP%b@pU?!1=;^47i=7IQI`}iu3i(Ig0aqU^a2I*Gg)zN?^B6m z`JN!!>2xK}|pCLKbtr4jQWXMv@aE zfepE=hbIx|da|4JkMrOeir15TSaGf==PJ(r&nJ%dyb2%o!vZDG_AFJL+shRmyh-Sn z_kI4V`b++KDFek*YH+bhIT_D#V*6!O0ke6NrnFYNqG@Ckw+p9PU&L;bS;oScQUdp?$K zPn|hg_CU*DOdRVEmJ9o{Sn;(GhTF0%v+J7g@%n0nIO@l-h5c~3lIQjI2F1DEJs|YR zd_AG$Pjl?n;UWK(k`I#n$dEnIemIplx1$jxf1!tbp^_g?@=YG{zf|%x95{baocoiz z1iuY*vp@eK^ludWH6j0N!QU1f^GuHgIvt9${reRULmauGfAI5w=)W+Wc?-o)LI@l4 z?Zk&z+{K?H?pOR7;%pb|d6syJl7EpnmmAB!N<5(C-yj}Toc)!m`1>T!^^Wzo6Him} z9~0;H$MT;M&rtGT5$Afr^4}28Qu2q0bNR4*+%zlbD)|A#d3<2`wWJ5fEZmq6CHc7) zckyAw3lu+!_yWa`A-+iQ7&Y$=|7X z3GsUrkC2{CiZ3Vme<*$*$v>?4Im91Td==?wR{Toh+Z10%e3#-4#CI$15N}nyiTDeO zM~J_o_%z~v;&HH#hY$a*c|h^GBP~c(dq{Fb7xBh_Q*QQvIgoePz_5_LJngH`5aPUVu$7?9bHxIDq zI7gNIapD<@ZzrCm_%p;^#h)jhulVc43l!(y&0VB8{vIW6OBHV?UZwcQ#OoCQg7{j+ z@%JlnyH@e|ftIgVd@%7_755Pj46^Ys82yjGcZnPRPCIVQk0+j{IG2~-ig(GMh>Zib zG`s8Ksl@XYKb?4$;uDCkS3HCGCdH=`Z&f^t_&&wM#Qn-YF7Y(Q`Mo0fiszGjmEv=W zuUEW)_$I{{5N}m{5%GPB7Zdl>9)qzQm(n@EuZVdiakiIvgm{(GQ%C3P6<~{msPF6yHWXU-74j^ZT4x5BqVwl7E@xH!1!f;;oABA-+%X_lWzcU*UAy zh^HyOmw3M7pAfH7{Bz>#75|#}CdI!c-m3Wb#P=!Q)qbgeV*BH%oYEA}Cq4O!48=c^Lyw0gM0d)$DK6AdECiY{EuwE z;`gxqir>%nE6(q`Z&kd7?N@vkaX&qH&+Yf$#M2b#agg7y%kn%9Rw?hO>v&L zOKZ8S#CJR}uFg(bN8w#QFWA z+%B#np0DH^h*v4zNPNBGHxl2Z_^*k#D*gxJ`xL*MxSwuRv7LV>o~HO_;`xfV5U*1F zN#g4jf0pwDSj03 zR>hAezEANJiTjW0Y5%Fj(-faTJYVrC#H$p~Cca+rT;iJ)KaY5;;ujI$r+6W8|Brgw zUq(Dl@k-+PimxPIrT7}+>lObg@lA@~K)hA)n~CpJ{5Qn?NB6Y^L)j*9#$#N^<=%`T#lO* zXFse&iyUFzmD_E{b7}o=l*KF;@qEXQk>g+tK!@q_bJZp z#ZT{jVSBhfrz-vc)t?N-A0nQm_$azw<=^#2{TEnQnQ#7GZ%%g{$@A}eBgY|&=RY+3 zntAd>45TfM_J}44o-g=h!K(!4wO>T*1;?pm<`@e$dGJ=nKRC^TeTx5*&i!Nq?oj{G zkv1Sr@f!n{=X>xf#jUjCP&A{ProtvDodl5Khb}`LSMfAD*Z1k>5wBA6E9ksl@vG?k zR>j#5n-s^trNM2F`R=~Ycq#D%ieFEBIPE!Kr|`U2@m0jvDn5^{=UWy3j;<4ikP}%y z-yaDozK-VQygtP8uM)pW$)7{>Fuo3B`34$a-&67fsoy`S_%!1DTmb8-BkoeZne%-U z{(U;;e4k~vlIQz1ZHmvL`#2+L-obkKKF(al`94%c@#jvk1-nV{B>~HKE6(lWJ;isB zyhHsE+r#ZEMRC5rHdk@JAGbmA{XuKbqlyPdS>C4jgp({ELH!xq!}rC~6+egijeNzq zKjGhNV?Eq2tyl65)L-4H_^rg76~CMKUd2y5-P*r^*6TRkFQ_9~tN70*T6tdgWBCIa zmVc?_Z_Tuve=m&Xe^0!C+9mUY^jvJc;tf-+9$v>|`Qzw$zTHZ`l=yp!-$ML=;tvt` z)4Cb!e}T9&wr16G_-$I)!|x-T{Zc-vE-5b@8-ZW-b;cGKMhcy=i)(6}vDKA$g5u-G z!q3*rQ}~U06UQ<-G17oRa;*7nUCsM~Qg8a$^sdvQnn;fmNU^AL`EvY$?XeY=k&>}9 zX3ZO8e!HGhDOtL(v>Jcq{{N#b0D7>irRvtDHH(f0OzT4CfV$-_h}{l0{j4rx58OZ6 zebpSh$LhILDu?9K-qNeU?Z3j9jbm7ObFR#KlAEX1nNYVp(0p z9z09~bSm|O=znbToId-%5E!Pf{nwla5Vj0zXZFl-0(?MwoR{=f{(BBtffQ1V>7y#0 z{wiR7rO%VnMKpi4+IpuSfq=f!KVY=NeQz4SF@2n4>GZD$)>rzAXkxV%1H~2)$9Ncn zPhaT=j))Qs0K&%d_N=@7xgPu(0xhD(_Z->2E4EMo9G9&+bJIJwEzAe*3jD{ literal 0 HcmV?d00001