From d5015c215274b36862049446c0da8710f41bccef Mon Sep 17 00:00:00 2001 From: tomit4 Date: Mon, 23 May 2022 10:48:05 -0700 Subject: [PATCH] reconfigured to mainly use st terminal instead of kitty --- bashrc.txt | 2 +- i3/config | 9 +- st/FAQ | 250 +++ st/LEGACY | 17 + st/LICENSE | 34 + st/Makefile | 56 + st/README | 34 + st/TODO | 28 + st/arg.h | 50 + st/bst | Bin 0 -> 111032 bytes st/config.def.h | 479 ++++++ st/config.h | 480 ++++++ st/config.mk | 35 + st/st | Bin 0 -> 111032 bytes st/st-alpha-20220206-0.8.5.diff | 146 ++ st/st-scrollback-0.8.5.diff | 350 ++++ st/st.1 | 177 ++ st/st.c | 2761 +++++++++++++++++++++++++++++++ st/st.h | 131 ++ st/st.h.orig | 130 ++ st/st.info | 239 +++ st/st.o | Bin 0 -> 81536 bytes st/win.h | 40 + st/x.c | 2100 +++++++++++++++++++++++ st/x.o | Bin 0 -> 75848 bytes zshrc.txt | 2 +- 26 files changed, 7545 insertions(+), 5 deletions(-) create mode 100644 st/FAQ create mode 100644 st/LEGACY create mode 100644 st/LICENSE create mode 100644 st/Makefile create mode 100644 st/README create mode 100644 st/TODO create mode 100644 st/arg.h create mode 100755 st/bst create mode 100644 st/config.def.h create mode 100644 st/config.h create mode 100644 st/config.mk create mode 100755 st/st create mode 100644 st/st-alpha-20220206-0.8.5.diff create mode 100644 st/st-scrollback-0.8.5.diff create mode 100644 st/st.1 create mode 100644 st/st.c create mode 100644 st/st.h create mode 100644 st/st.h.orig create mode 100644 st/st.info create mode 100644 st/st.o create mode 100644 st/win.h create mode 100644 st/x.c create mode 100644 st/x.o diff --git a/bashrc.txt b/bashrc.txt index be7c670f..f1f213fe 100644 --- a/bashrc.txt +++ b/bashrc.txt @@ -173,7 +173,7 @@ ex () HISTTIMEFORMAT="%d/%m/%y %T " # Displays current git branch if there is any and also styles bash prompt -export PS1='\[\033[01;34m\][\u@\h\[\033[01;37m\] \W\[\033[01;34m\]]\$\033[01;34m\] $(git branch 2>/dev/null | grep '^*' | colrm 1 2)\n\033[01;34m└─▶\033[37m ' +export PS1='\[\033[01;34m\][\u@\h\[\033[01;37m\] \W\[\033[01;34m\]]\$\033[01;34m\] $(git branch 2>/dev/null | grep '^*' | colrm 1 2)\n\033[01;34m└─>\033[37m ' # Configure thefuck eval "$(thefuck --alias)" diff --git a/i3/config b/i3/config index ede4edf8..f3df4a8f 100644 --- a/i3/config +++ b/i3/config @@ -38,7 +38,8 @@ exec feh --bg-fill /home/brian/Pictures/debian_wide_sand2.png exec xbanish # Automatically open kitty terminal and btop at full screen -exec kitty -o font_size=12 -o background_opacity=0.50 -e btop; fullscreen enable +# exec kitty -o font_size=12 -o background_opacity=0.50 -e btop; fullscreen enable +exec bst -e btop # Get rid of window title bars # for_window [class="^.*"] default_border pixel 1 @@ -84,7 +85,8 @@ floating_modifier $mod # start a terminal # bindsym $mod+Return exec i3-sensible-terminal -bindsym $mod+Return exec kitty +# bindsym $mod+Return exec kitty +bindsym $mod Return exec st # custom keybindings bindsym $mod+a exec pavucontrol @@ -97,7 +99,8 @@ bindsym $mod+G exec gimp --no-splash bindsym $mod+K exec krita --nosplash bindsym $mod+T exec tootle bindsym $mod+P exec pcmanfm -bindsym $mod+B exec kitty -o font_size=12 -o background_opacity=0.50 -e btop; fullscreen enable +# bindsym $mod+B exec kitty -o font_size=12 -o background_opacity=0.50 -e btop; fullscreen enable +bindsym $mod+B exec bst -e btop bindsym $mod+M exec mousepad bindsym $mod+O exec gcolor3 bindsym $mod+U exec galculator diff --git a/st/FAQ b/st/FAQ new file mode 100644 index 00000000..969b195e --- /dev/null +++ b/st/FAQ @@ -0,0 +1,250 @@ +## Why does st not handle utmp entries? + +Use the excellent tool of [utmp](https://git.suckless.org/utmp/) for this task. + + +## Some _random program_ complains that st is unknown/not recognised/unsupported/whatever! + +It means that st doesn’t have any terminfo entry on your system. Chances are +you did not `make install`. If you just want to test it without installing it, +you can manually run `tic -sx st.info`. + + +## Nothing works, and nothing is said about an unknown terminal! + +* Some programs just assume they’re running in xterm i.e. they don’t rely on + terminfo. What you see is the current state of the “xterm compliance”. +* Some programs don’t complain about the lacking st description and default to + another terminal. In that case see the question about terminfo. + + +## How do I scroll back up? + +* Using a terminal multiplexer. + * `st -e tmux` using C-b [ + * `st -e screen` using C-a ESC +* Using the excellent tool of [scroll](https://git.suckless.org/scroll/). +* Using the scrollback [patch](https://st.suckless.org/patches/scrollback/). + + +## I would like to have utmp and/or scroll functionality by default + +You can add the absolute path of both programs in your config.h file. You only +have to modify the value of utmp and scroll variables. + + +## Why doesn't the Del key work in some programs? + +Taken from the terminfo manpage: + + If the terminal has a keypad that transmits codes when the keys + are pressed, this information can be given. Note that it is not + possible to handle terminals where the keypad only works in + local (this applies, for example, to the unshifted HP 2621 keys). + If the keypad can be set to transmit or not transmit, give these + codes as smkx and rmkx. Otherwise the keypad is assumed to + always transmit. + +In the st case smkx=E[?1hE= and rmkx=E[?1lE>, so it is mandatory that +applications which want to test against keypad keys send these +sequences. + +But buggy applications (like bash and irssi, for example) don't do this. A fast +solution for them is to use the following command: + + $ printf '\033[?1h\033=' >/dev/tty + +or + $ tput smkx + +In the case of bash, readline is used. Readline has a different note in its +manpage about this issue: + + enable-keypad (Off) + When set to On, readline will try to enable the + application keypad when it is called. Some systems + need this to enable arrow keys. + +Adding this option to your .inputrc will fix the keypad problem for all +applications using readline. + +If you are using zsh, then read the zsh FAQ +: + + It should be noted that the O / [ confusion can occur with other keys + such as Home and End. Some systems let you query the key sequences + sent by these keys from the system's terminal database, terminfo. + Unfortunately, the key sequences given there typically apply to the + mode that is not the one zsh uses by default (it's the "application" + mode rather than the "raw" mode). Explaining the use of terminfo is + outside of the scope of this FAQ, but if you wish to use the key + sequences given there you can tell the line editor to turn on + "application" mode when it starts and turn it off when it stops: + + function zle-line-init () { echoti smkx } + function zle-line-finish () { echoti rmkx } + zle -N zle-line-init + zle -N zle-line-finish + +Putting these lines into your .zshrc will fix the problems. + + +## How can I use meta in 8bit mode? + +St supports meta in 8bit mode, but the default terminfo entry doesn't +use this capability. If you want it, you have to use the 'st-meta' value +in TERM. + + +## I cannot compile st in OpenBSD + +OpenBSD lacks librt, despite it being mandatory in POSIX +. +If you want to compile st for OpenBSD you have to remove -lrt from config.mk, and +st will compile without any loss of functionality, because all the functions are +included in libc on this platform. + + +## The Backspace Case + +St is emulating the Linux way of handling backspace being delete and delete being +backspace. + +This is an issue that was discussed in suckless mailing list +. Here is why some old grumpy +terminal users wants its backspace to be how he feels it: + + Well, I am going to comment why I want to change the behaviour + of this key. When ASCII was defined in 1968, communication + with computers was done using punched cards, or hardcopy + terminals (basically a typewriter machine connected with the + computer using a serial port). ASCII defines DELETE as 7F, + because, in punched-card terms, it means all the holes of the + card punched; it is thus a kind of 'physical delete'. In the + same way, the BACKSPACE key was a non-destructive backspace, + as on a typewriter. So, if you wanted to delete a character, + you had to BACKSPACE and then DELETE. Another use of BACKSPACE + was to type accented characters, for example 'a BACKSPACE `'. + The VT100 had no BACKSPACE key; it was generated using the + CONTROL key as another control character (CONTROL key sets to + 0 b7 b6 b5, so it converts H (code 0x48) into BACKSPACE (code + 0x08)), but it had a DELETE key in a similar position where + the BACKSPACE key is located today on common PC keyboards. + All the terminal emulators emulated the difference between + these keys correctly: the backspace key generated a BACKSPACE + (^H) and delete key generated a DELETE (^?). + + But a problem arose when Linus Torvalds wrote Linux. Unlike + earlier terminals, the Linux virtual terminal (the terminal + emulator integrated in the kernel) returned a DELETE when + backspace was pressed, due to the VT100 having a DELETE key in + the same position. This created a lot of problems (see [1] + and [2]). Since Linux has become the king, a lot of terminal + emulators today generate a DELETE when the backspace key is + pressed in order to avoid problems with Linux. The result is + that the only way of generating a BACKSPACE on these systems + is by using CONTROL + H. (I also think that emacs had an + important point here because the CONTROL + H prefix is used + in emacs in some commands (help commands).) + + From point of view of the kernel, you can change the key + for deleting a previous character with stty erase. When you + connect a real terminal into a machine you describe the type + of terminal, so getty configures the correct value of stty + erase for this terminal. In the case of terminal emulators, + however, you don't have any getty that can set the correct + value of stty erase, so you always get the default value. + For this reason, it is necessary to add 'stty erase ^H' to your + profile if you have changed the value of the backspace key. + Of course, another solution is for st itself to modify the + value of stty erase. I usually have the inverse problem: + when I connect to non-Unix machines, I have to press CONTROL + + h to get a BACKSPACE. The inverse problem occurs when a user + connects to my Unix machines from a different system with a + correct backspace key. + + [1] http://www.ibb.net/~anne/keyboard.html + [2] http://www.tldp.org/HOWTO/Keyboard-and-Console-HOWTO-5.html + + +## But I really want the old grumpy behaviour of my terminal + +Apply [1]. + +[1] https://st.suckless.org/patches/delkey + + +## Why do images not work in st using the w3m image hack? + +w3mimg uses a hack that draws an image on top of the terminal emulator Drawable +window. The hack relies on the terminal to use a single buffer to draw its +contents directly. + +st uses double-buffered drawing so the image is quickly replaced and may show a +short flicker effect. + +Below is a patch example to change st double-buffering to a single Drawable +buffer. + +diff --git a/x.c b/x.c +--- a/x.c ++++ b/x.c +@@ -732,10 +732,6 @@ xresize(int col, int row) + win.tw = col * win.cw; + win.th = row * win.ch; + +- XFreePixmap(xw.dpy, xw.buf); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); +- XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + + /* resize to new width */ +@@ -1148,8 +1144,7 @@ xinit(int cols, int rows) + gcvalues.graphics_exposures = False; + dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, + &gcvalues); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); ++ xw.buf = xw.win; + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + +@@ -1632,8 +1627,6 @@ xdrawline(Line line, int x1, int y1, int x2) + void + xfinishdraw(void) + { +- XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, +- win.h, 0, 0); + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(MODE_REVERSE)? + defaultfg : defaultbg].pixel); + + +## BadLength X error in Xft when trying to render emoji + +Xft makes st crash when rendering color emojis with the following error: + +"X Error of failed request: BadLength (poly request too large or internal Xlib length error)" + Major opcode of failed request: 139 (RENDER) + Minor opcode of failed request: 20 (RenderAddGlyphs) + Serial number of failed request: 1595 + Current serial number in output stream: 1818" + +This is a known bug in Xft (not st) which happens on some platforms and +combination of particular fonts and fontconfig settings. + +See also: +https://gitlab.freedesktop.org/xorg/lib/libxft/issues/6 +https://bugs.freedesktop.org/show_bug.cgi?id=107534 +https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + +The solution is to remove color emoji fonts or disable this in the fontconfig +XML configuration. As an ugly workaround (which may work only on newer +fontconfig versions (FC_COLOR)), the following code can be used to mask color +fonts: + + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + +Please don't bother reporting this bug to st, but notify the upstream Xft +developers about fixing this bug. diff --git a/st/LEGACY b/st/LEGACY new file mode 100644 index 00000000..bf28b1eb --- /dev/null +++ b/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/st/LICENSE b/st/LICENSE new file mode 100644 index 00000000..d80eb47f --- /dev/null +++ b/st/LICENSE @@ -0,0 +1,34 @@ +MIT/X Consortium License + +© 2014-2020 Hiltjo Posthuma +© 2018 Devin J. Pohly +© 2014-2017 Quentin Rameau +© 2009-2012 Aurélien APTEL +© 2008-2017 Anselm R Garbe +© 2012-2017 Roberto E. Vargas Caballero +© 2012-2016 Christoph Lohmann <20h at r-36 dot net> +© 2013 Eon S. Jeon +© 2013 Alexander Sedov +© 2013 Mark Edgar +© 2013-2014 Eric Pruitt +© 2013 Michael Forney +© 2013-2014 Markus Teich +© 2014-2015 Laslo Hunhold + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/st/Makefile b/st/Makefile new file mode 100644 index 00000000..01a6d06e --- /dev/null +++ b/st/Makefile @@ -0,0 +1,56 @@ +# st - simple terminal +# See LICENSE file for copyright and license details. +.POSIX: + +include config.mk + +SRC = st.c x.c +OBJ = $(SRC:.c=.o) + +all: options st + +options: + @echo st build options: + @echo "CFLAGS = $(STCFLAGS)" + @echo "LDFLAGS = $(STLDFLAGS)" + @echo "CC = $(CC)" + +config.h: + cp config.def.h config.h + +.c.o: + $(CC) $(STCFLAGS) -c $< + +st.o: config.h st.h win.h +x.o: arg.h config.h st.h win.h + +$(OBJ): config.h config.mk + +st: $(OBJ) + $(CC) -o $@ $(OBJ) $(STLDFLAGS) + +clean: + rm -f st $(OBJ) st-$(VERSION).tar.gz + +dist: clean + mkdir -p st-$(VERSION) + cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ + config.def.h st.info st.1 arg.h st.h win.h $(SRC)\ + st-$(VERSION) + tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz + rm -rf st-$(VERSION) + +install: st + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f st $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/st + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 + @echo Please see the README file regarding the terminfo entry of st. + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/st + rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 + +.PHONY: all options clean dist install uninstall diff --git a/st/README b/st/README new file mode 100644 index 00000000..6a846ed0 --- /dev/null +++ b/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/st/TODO b/st/TODO new file mode 100644 index 00000000..5f74cd52 --- /dev/null +++ b/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/st/arg.h b/st/arg.h new file mode 100644 index 00000000..a22e019e --- /dev/null +++ b/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/st/bst b/st/bst new file mode 100755 index 0000000000000000000000000000000000000000..baa9e0e599f094372e88a20c32672cafe617c684 GIT binary patch literal 111032 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 literal 0 HcmV?d00001 diff --git a/st/config.def.h b/st/config.def.h new file mode 100644 index 00000000..279890fe --- /dev/null +++ b/st/config.def.h @@ -0,0 +1,479 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; +static int borderpx = 2; + +/* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e + * 2: scroll and/or utmp + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/sh"; +char *utmp = NULL; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = NULL; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +char *vtiden = "\033[?6c"; + +/* Kerning / character bounding-box multipliers */ +static float cwscale = 1.0; +static float chscale = 1.0; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +wchar_t *worddelimiters = L" "; + +/* selection timeouts (in milliseconds) */ +static unsigned int doubleclicktimeout = 300; +static unsigned int tripleclicktimeout = 600; + +/* alt screens */ +int allowaltscreen = 1; + +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 8; +static double maxlatency = 33; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 800; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 2; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "st-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +/* bg opacity */ +float alpha = 0.8; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "black", + "red3", + "green3", + "yellow3", + "blue2", + "magenta3", + "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", + "red", + "green", + "yellow", + "#5c5cff", + "magenta", + "cyan", + "white", + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#cccccc", + "#555555", + "gray90", /* default foreground colour */ + "black", /* default background colour */ +}; + + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 258; +unsigned int defaultbg = 259; +unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 2; + +/* + * Default columns and rows numbers + */ + +static unsigned int cols = 80; +static unsigned int rows = 24; + +/* + * Default colour and shape of the mouse cursor + */ +static unsigned int mouseshape = XC_xterm; +static unsigned int mousefg = 7; +static unsigned int mousebg = 0; + +/* + * Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. + */ +static unsigned int defaultattr = 11; + +/* + * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). + * Note that if you want to use ShiftMask with selmasks, set this to an other + * modifier, set to 0 to not use it. + */ +static uint forcemousemod = ShiftMask; + +/* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. + */ +static MouseShortcut mshortcuts[] = { + /* mask button function argument release */ + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, + { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, +}; + +/* Internal keyboard shortcuts. */ +#define MODKEY Mod1Mask +#define TERMMOD (ControlMask|ShiftMask) + +static Shortcut shortcuts[] = { + /* mask keysym function argument */ + { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, + { ControlMask, XK_Print, toggleprinter, {.i = 0} }, + { ShiftMask, XK_Print, printscreen, {.i = 0} }, + { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, + { TERMMOD, XK_Prior, zoom, {.f = +1} }, + { TERMMOD, XK_Next, zoom, {.f = -1} }, + { TERMMOD, XK_Home, zoomreset, {.f = 0} }, + { TERMMOD, XK_C, clipcopy, {.i = 0} }, + { TERMMOD, XK_V, clippaste, {.i = 0} }, + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, +}; + +/* + * Special keys (change & recompile st.info accordingly) + * + * Mask value: + * * Use XK_ANY_MOD to match the key no matter modifiers state + * * Use XK_NO_MOD to match the key alone (no modifiers) + * appkey value: + * * 0: no value + * * > 0: keypad application mode enabled + * * = 2: term.numlock = 1 + * * < 0: keypad application mode disabled + * appcursor value: + * * 0: no value + * * > 0: cursor application mode enabled + * * < 0: cursor application mode disabled + * + * Be careful with the order of the definitions because st searches in + * this table sequentially, so any XK_ANY_MOD must be in the last + * position for a key. + */ + +/* + * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) + * to be mapped below, add them to this array. + */ +static KeySym mappedkeys[] = { -1 }; + +/* + * State bits to ignore when matching key or button events. By default, + * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. + */ +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; + +/* + * This is the huge key array which defines all compatibility to the Linux + * world. Please decide about changes wisely. + */ +static Key key[] = { + /* keysym mask string appkey appcursor */ + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + { XK_KP_End, ControlMask, "\033[J", -1, 0}, + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + { XK_Return, Mod1Mask, "\033\r", 0, 0}, + { XK_Return, XK_ANY_MOD, "\r", 0, 0}, + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_Insert, ControlMask, "\033[L", -1, 0}, + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_Delete, ControlMask, "\033[M", -1, 0}, + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, +}; + +/* + * Selection types' masks. + * Use the same masks as usual. + * Button1Mask is always unset, to make masks match between ButtonPress. + * ButtonRelease and MotionNotify. + * If no match is found, regular selection is used. + */ +static uint selmasks[] = { + [SEL_RECTANGULAR] = Mod1Mask, +}; + +/* + * Printable characters in ASCII, used to estimate the advance width + * of single wide characters. + */ +static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; diff --git a/st/config.h b/st/config.h new file mode 100644 index 00000000..b2bd261f --- /dev/null +++ b/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 = "Source Code Pro:pixelsize=20:antialias=true:autohint=true"; +static char *font = "mono:pixelsize=18.5:antialias=true:autohint=true"; +static int borderpx = 2; + +/* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e + * 2: scroll and/or utmp + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/sh"; +char *utmp = NULL; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = NULL; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +char *vtiden = "\033[?6c"; + +/* Kerning / character bounding-box multipliers */ +static float cwscale = 1.0; +static float chscale = 1.0; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +wchar_t *worddelimiters = L" "; + +/* selection timeouts (in milliseconds) */ +static unsigned int doubleclicktimeout = 300; +static unsigned int tripleclicktimeout = 600; + +/* alt screens */ +int allowaltscreen = 1; + +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 8; +static double maxlatency = 33; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 800; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 2; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "st-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +/* bg opacity */ +float alpha = 0.6; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "#3b4252", /* black */ + "#bf616a", /* red */ + "#a3be8c", /* green */ + "#ebcb8b", /* yellow */ + "#81a1c1", /* blue */ + "#b48ead", /* magenta */ + "#88c0d0", /* cyan */ + "#e5e9f0", /* white */ + + /* 8 bright colors */ + "#4c566a", /* black */ + "#bf616a", /* red */ + "#a3be8c", /* green */ + "#ebcb8b", /* yellow */ + "#81a1c1", /* blue */ + "#b48ead", /* magenta */ + "#8fbcbb", /* cyan */ + "#eceff4", /* white */ + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + // "#2e3440", // background // + // "#1d222b", // background // + "#1a1e26", // background // + "#d8dee9", /* foreground */ +}; + + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 257; +unsigned int defaultbg = 256; +unsigned int defaultcs = 257; +static unsigned int defaultrcs = 256; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 2; + +/* + * Default columns and rows numbers + */ + +static unsigned int cols = 80; +static unsigned int rows = 24; + +/* + * Default colour and shape of the mouse cursor + */ +static unsigned int mouseshape = XC_xterm; +static unsigned int mousefg = 7; +static unsigned int mousebg = 0; + +/* + * Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. + */ +static unsigned int defaultattr = 11; + +/* + * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). + * Note that if you want to use ShiftMask with selmasks, set this to an other + * modifier, set to 0 to not use it. + */ +static uint forcemousemod = ShiftMask; + +/* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. + */ +static MouseShortcut mshortcuts[] = { + /* mask button function argument release */ + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, + { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, +}; + +/* Internal keyboard shortcuts. */ +#define MODKEY Mod1Mask +#define TERMMOD (ControlMask|ShiftMask) + +static Shortcut shortcuts[] = { + /* mask keysym function argument */ + { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, + { ControlMask, XK_Print, toggleprinter, {.i = 0} }, + { ShiftMask, XK_Print, printscreen, {.i = 0} }, + { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, + { TERMMOD, XK_Prior, zoom, {.f = +1} }, + { TERMMOD, XK_Next, zoom, {.f = -1} }, + { TERMMOD, XK_Home, zoomreset, {.f = 0} }, + { TERMMOD, XK_C, clipcopy, {.i = 0} }, + { TERMMOD, XK_V, clippaste, {.i = 0} }, + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, +}; + +/* + * Special keys (change & recompile st.info accordingly) + * + * Mask value: + * * Use XK_ANY_MOD to match the key no matter modifiers state + * * Use XK_NO_MOD to match the key alone (no modifiers) + * appkey value: + * * 0: no value + * * > 0: keypad application mode enabled + * * = 2: term.numlock = 1 + * * < 0: keypad application mode disabled + * appcursor value: + * * 0: no value + * * > 0: cursor application mode enabled + * * < 0: cursor application mode disabled + * + * Be careful with the order of the definitions because st searches in + * this table sequentially, so any XK_ANY_MOD must be in the last + * position for a key. + */ + +/* + * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) + * to be mapped below, add them to this array. + */ +static KeySym mappedkeys[] = { -1 }; + +/* + * State bits to ignore when matching key or button events. By default, + * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. + */ +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; + +/* + * This is the huge key array which defines all compatibility to the Linux + * world. Please decide about changes wisely. + */ +static Key key[] = { + /* keysym mask string appkey appcursor */ + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + { XK_KP_End, ControlMask, "\033[J", -1, 0}, + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + { XK_Return, Mod1Mask, "\033\r", 0, 0}, + { XK_Return, XK_ANY_MOD, "\r", 0, 0}, + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_Insert, ControlMask, "\033[L", -1, 0}, + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_Delete, ControlMask, "\033[M", -1, 0}, + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, +}; + +/* + * Selection types' masks. + * Use the same masks as usual. + * Button1Mask is always unset, to make masks match between ButtonPress. + * ButtonRelease and MotionNotify. + * If no match is found, regular selection is used. + */ +static uint selmasks[] = { + [SEL_RECTANGULAR] = Mod1Mask, +}; + +/* + * Printable characters in ASCII, used to estimate the advance width + * of single wide characters. + */ +static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; diff --git a/st/config.mk b/st/config.mk new file mode 100644 index 00000000..0114bad3 --- /dev/null +++ b/st/config.mk @@ -0,0 +1,35 @@ +# st version +VERSION = 0.8.5 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +PKG_CONFIG = pkg-config + +# includes and libs +INCS = -I$(X11INC) \ + `$(PKG_CONFIG) --cflags fontconfig` \ + `$(PKG_CONFIG) --cflags freetype2` +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ + `$(PKG_CONFIG) --libs fontconfig` \ + `$(PKG_CONFIG) --libs freetype2` + +# flags +STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 +STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) +STLDFLAGS = $(LIBS) $(LDFLAGS) + +# OpenBSD: +#CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE +#LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ +# `$(PKG_CONFIG) --libs fontconfig` \ +# `$(PKG_CONFIG) --libs freetype2` + +# compiler and linker +# CC = c99 diff --git a/st/st b/st/st new file mode 100755 index 0000000000000000000000000000000000000000..1fe9c018f080bc95450f957448c101cb03715b2e GIT binary patch literal 111032 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 SZh5yz`0LM>@<9NEVEPwPDXtIz literal 0 HcmV?d00001 diff --git a/st/st-alpha-20220206-0.8.5.diff b/st/st-alpha-20220206-0.8.5.diff new file mode 100644 index 00000000..ab029f63 --- /dev/null +++ b/st/st-alpha-20220206-0.8.5.diff @@ -0,0 +1,146 @@ +diff --git a/config.def.h b/config.def.h +index 91ab8ca..6af616e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -93,6 +93,9 @@ char *termname = "st-256color"; + */ + unsigned int tabspaces = 8; + ++/* bg opacity */ ++float alpha = 0.8; ++ + /* Terminal colors (16 first used in escape sequence) */ + static const char *colorname[] = { + /* 8 normal colors */ +diff --git a/config.mk b/config.mk +index 4c4c5d5..0114bad 100644 +--- a/config.mk ++++ b/config.mk +@@ -16,7 +16,7 @@ PKG_CONFIG = pkg-config + INCS = -I$(X11INC) \ + `$(PKG_CONFIG) --cflags fontconfig` \ + `$(PKG_CONFIG) --cflags freetype2` +-LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ ++LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ + `$(PKG_CONFIG) --libs fontconfig` \ + `$(PKG_CONFIG) --libs freetype2` + +diff --git a/st.h b/st.h +index 519b9bd..8bb533d 100644 +--- a/st.h ++++ b/st.h +@@ -126,3 +126,4 @@ extern unsigned int tabspaces; + extern unsigned int defaultfg; + extern unsigned int defaultbg; + extern unsigned int defaultcs; ++extern float alpha; +diff --git a/x.c b/x.c +index 8a16faa..ddf4178 100644 +--- a/x.c ++++ b/x.c +@@ -105,6 +105,7 @@ typedef struct { + XSetWindowAttributes attrs; + int scr; + int isfixed; /* is fixed geometry? */ ++ int depth; /* bit depth */ + int l, t; /* left and top offset */ + int gm; /* geometry mask */ + } XWindow; +@@ -243,6 +244,7 @@ static char *usedfont = NULL; + static double usedfontsize = 0; + static double defaultfontsize = 0; + ++static char *opt_alpha = NULL; + static char *opt_class = NULL; + static char **opt_cmd = NULL; + static char *opt_embed = NULL; +@@ -736,7 +738,7 @@ xresize(int col, int row) + + XFreePixmap(xw.dpy, xw.buf); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); ++ xw.depth); + XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + +@@ -796,6 +798,13 @@ xloadcols(void) + else + die("could not allocate color %d\n", i); + } ++ ++ /* set alpha value of bg color */ ++ if (opt_alpha) ++ alpha = strtof(opt_alpha, NULL); ++ dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); ++ dc.col[defaultbg].pixel &= 0x00FFFFFF; ++ dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; + loaded = 1; + } + +@@ -1118,11 +1127,23 @@ xinit(int cols, int rows) + Window parent; + pid_t thispid = getpid(); + XColor xmousefg, xmousebg; ++ XWindowAttributes attr; ++ XVisualInfo vis; + + if (!(xw.dpy = XOpenDisplay(NULL))) + die("can't open display\n"); + xw.scr = XDefaultScreen(xw.dpy); +- xw.vis = XDefaultVisual(xw.dpy, xw.scr); ++ ++ if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { ++ parent = XRootWindow(xw.dpy, xw.scr); ++ xw.depth = 32; ++ } else { ++ XGetWindowAttributes(xw.dpy, parent, &attr); ++ xw.depth = attr.depth; ++ } ++ ++ XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); ++ xw.vis = vis.visual; + + /* font */ + if (!FcInit()) +@@ -1132,7 +1153,7 @@ xinit(int cols, int rows) + xloadfonts(usedfont, 0); + + /* colors */ +- xw.cmap = XDefaultColormap(xw.dpy, xw.scr); ++ xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); + xloadcols(); + + /* adjust fixed window geometry */ +@@ -1152,19 +1173,15 @@ xinit(int cols, int rows) + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + xw.attrs.colormap = xw.cmap; + +- if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) +- parent = XRootWindow(xw.dpy, xw.scr); + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, +- win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, ++ win.w, win.h, 0, xw.depth, InputOutput, + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &xw.attrs); + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; +- dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, +- &gcvalues); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); ++ xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); ++ dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + +@@ -2019,6 +2036,9 @@ main(int argc, char *argv[]) + case 'a': + allowaltscreen = 0; + break; ++ case 'A': ++ opt_alpha = EARGF(usage()); ++ break; + case 'c': + opt_class = EARGF(usage()); + break; diff --git a/st/st-scrollback-0.8.5.diff b/st/st-scrollback-0.8.5.diff new file mode 100644 index 00000000..750111d5 --- /dev/null +++ b/st/st-scrollback-0.8.5.diff @@ -0,0 +1,350 @@ +diff --git a/config.def.h b/config.def.h +index 91ab8ca..e3b469b 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -201,6 +201,8 @@ static Shortcut shortcuts[] = { + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, ++ { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, ++ { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, + }; + + /* +diff --git a/st.c b/st.c +index 51049ba..cd750f2 100644 +--- a/st.c ++++ b/st.c +@@ -35,6 +35,7 @@ + #define ESC_ARG_SIZ 16 + #define STR_BUF_SIZ ESC_BUF_SIZ + #define STR_ARG_SIZ ESC_ARG_SIZ ++#define HISTSIZE 2000 + + /* macros */ + #define IS_SET(flag) ((term.mode & (flag)) != 0) +@@ -42,6 +43,9 @@ + #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) + #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) + #define ISDELIM(u) (u && wcschr(worddelimiters, u)) ++#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ ++ term.scr + HISTSIZE + 1) % HISTSIZE] : \ ++ term.line[(y) - term.scr]) + + enum term_mode { + MODE_WRAP = 1 << 0, +@@ -115,6 +119,9 @@ typedef struct { + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ ++ Line hist[HISTSIZE]; /* history buffer */ ++ int histi; /* history index */ ++ int scr; /* scroll back */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ +@@ -184,8 +191,8 @@ static void tnewline(int); + static void tputtab(int); + static void tputc(Rune); + static void treset(void); +-static void tscrollup(int, int); +-static void tscrolldown(int, int); ++static void tscrollup(int, int, int); ++static void tscrolldown(int, int, int); + static void tsetattr(const int *, int); + static void tsetchar(Rune, const Glyph *, int, int); + static void tsetdirt(int, int); +@@ -416,10 +423,10 @@ tlinelen(int y) + { + int i = term.col; + +- if (term.line[y][i - 1].mode & ATTR_WRAP) ++ if (TLINE(y)[i - 1].mode & ATTR_WRAP) + return i; + +- while (i > 0 && term.line[y][i - 1].u == ' ') ++ while (i > 0 && TLINE(y)[i - 1].u == ' ') + --i; + + return i; +@@ -528,7 +535,7 @@ selsnap(int *x, int *y, int direction) + * Snap around if the word wraps around at the end or + * beginning of a line. + */ +- prevgp = &term.line[*y][*x]; ++ prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; +@@ -543,14 +550,14 @@ selsnap(int *x, int *y, int direction) + yt = *y, xt = *x; + else + yt = newy, xt = newx; +- if (!(term.line[yt][xt].mode & ATTR_WRAP)) ++ if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + +- gp = &term.line[newy][newx]; ++ gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) +@@ -571,14 +578,14 @@ selsnap(int *x, int *y, int direction) + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { +- if (!(term.line[*y-1][term.col-1].mode ++ if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { +- if (!(term.line[*y][term.col-1].mode ++ if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } +@@ -609,13 +616,13 @@ getsel(void) + } + + if (sel.type == SEL_RECTANGULAR) { +- gp = &term.line[y][sel.nb.x]; ++ gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { +- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; ++ gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } +- last = &term.line[y][MIN(lastx, linelen-1)]; ++ last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + +@@ -851,6 +858,9 @@ void + ttywrite(const char *s, size_t n, int may_echo) + { + const char *next; ++ Arg arg = (Arg) { .i = term.scr }; ++ ++ kscrolldown(&arg); + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); +@@ -1062,12 +1072,52 @@ tswapscreen(void) + } + + void +-tscrolldown(int orig, int n) ++kscrolldown(const Arg* a) ++{ ++ int n = a->i; ++ ++ if (n < 0) ++ n = term.row + n; ++ ++ if (n > term.scr) ++ n = term.scr; ++ ++ if (term.scr > 0) { ++ term.scr -= n; ++ selscroll(0, -n); ++ tfulldirt(); ++ } ++} ++ ++void ++kscrollup(const Arg* a) ++{ ++ int n = a->i; ++ ++ if (n < 0) ++ n = term.row + n; ++ ++ if (term.scr <= HISTSIZE-n) { ++ term.scr += n; ++ selscroll(0, n); ++ tfulldirt(); ++ } ++} ++ ++void ++tscrolldown(int orig, int n, int copyhist) + { + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); ++ if (copyhist) { ++ term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; ++ temp = term.hist[term.histi]; ++ term.hist[term.histi] = term.line[term.bot]; ++ term.line[term.bot] = temp; ++ } ++ + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); +@@ -1078,17 +1128,28 @@ tscrolldown(int orig, int n) + term.line[i-n] = temp; + } + +- selscroll(orig, n); ++ if (term.scr == 0) ++ selscroll(orig, n); + } + + void +-tscrollup(int orig, int n) ++tscrollup(int orig, int n, int copyhist) + { + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + ++ if (copyhist) { ++ term.histi = (term.histi + 1) % HISTSIZE; ++ temp = term.hist[term.histi]; ++ term.hist[term.histi] = term.line[orig]; ++ term.line[orig] = temp; ++ } ++ ++ if (term.scr > 0 && term.scr < HISTSIZE) ++ term.scr = MIN(term.scr + n, HISTSIZE-1); ++ + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + +@@ -1098,7 +1159,8 @@ tscrollup(int orig, int n) + term.line[i+n] = temp; + } + +- selscroll(orig, -n); ++ if (term.scr == 0) ++ selscroll(orig, -n); + } + + void +@@ -1127,7 +1189,7 @@ tnewline(int first_col) + int y = term.c.y; + + if (y == term.bot) { +- tscrollup(term.top, 1); ++ tscrollup(term.top, 1, 1); + } else { + y++; + } +@@ -1292,14 +1354,14 @@ void + tinsertblankline(int n) + { + if (BETWEEN(term.c.y, term.top, term.bot)) +- tscrolldown(term.c.y, n); ++ tscrolldown(term.c.y, n, 0); + } + + void + tdeleteline(int n) + { + if (BETWEEN(term.c.y, term.top, term.bot)) +- tscrollup(term.c.y, n); ++ tscrollup(term.c.y, n, 0); + } + + int32_t +@@ -1736,11 +1798,11 @@ csihandle(void) + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); +- tscrollup(term.top, csiescseq.arg[0]); ++ tscrollup(term.top, csiescseq.arg[0], 0); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); +- tscrolldown(term.top, csiescseq.arg[0]); ++ tscrolldown(term.top, csiescseq.arg[0], 0); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); +@@ -2330,7 +2392,7 @@ eschandle(uchar ascii) + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { +- tscrollup(term.top, 1); ++ tscrollup(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } +@@ -2343,7 +2405,7 @@ eschandle(uchar ascii) + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { +- tscrolldown(term.top, 1); ++ tscrolldown(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } +@@ -2557,7 +2619,7 @@ twrite(const char *buf, int buflen, int show_ctrl) + void + tresize(int col, int row) + { +- int i; ++ int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; +@@ -2594,6 +2656,14 @@ tresize(int col, int row) + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + ++ for (i = 0; i < HISTSIZE; i++) { ++ term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); ++ for (j = mincol; j < col; j++) { ++ term.hist[i][j] = term.c.attr; ++ term.hist[i][j].u = ' '; ++ } ++ } ++ + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); +@@ -2652,7 +2722,7 @@ drawregion(int x1, int y1, int x2, int y2) + continue; + + term.dirty[y] = 0; +- xdrawline(term.line[y], x1, y, x2); ++ xdrawline(TLINE(y), x1, y, x2); + } + } + +@@ -2673,8 +2743,9 @@ draw(void) + cx--; + + drawregion(0, 0, term.col, term.row); +- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], +- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); ++ if (term.scr == 0) ++ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], ++ term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); +diff --git a/st.h b/st.h +index 519b9bd..da36b34 100644 +--- a/st.h ++++ b/st.h +@@ -81,6 +81,8 @@ void die(const char *, ...); + void redraw(void); + void draw(void); + ++void kscrolldown(const Arg *); ++void kscrollup(const Arg *); + void printscreen(const Arg *); + void printsel(const Arg *); + void sendbreak(const Arg *); diff --git a/st/st.1 b/st/st.1 new file mode 100644 index 00000000..39120b48 --- /dev/null +++ b/st/st.1 @@ -0,0 +1,177 @@ +.TH ST 1 st\-VERSION +.SH NAME +st \- simple terminal +.SH SYNOPSIS +.B st +.RB [ \-aiv ] +.RB [ \-c +.IR class ] +.RB [ \-f +.IR font ] +.RB [ \-g +.IR geometry ] +.RB [ \-n +.IR name ] +.RB [ \-o +.IR iofile ] +.RB [ \-T +.IR title ] +.RB [ \-t +.IR title ] +.RB [ \-l +.IR line ] +.RB [ \-w +.IR windowid ] +.RB [[ \-e ] +.IR command +.RI [ arguments ...]] +.PP +.B st +.RB [ \-aiv ] +.RB [ \-c +.IR class ] +.RB [ \-f +.IR font ] +.RB [ \-g +.IR geometry ] +.RB [ \-n +.IR name ] +.RB [ \-o +.IR iofile ] +.RB [ \-T +.IR title ] +.RB [ \-t +.IR title ] +.RB [ \-w +.IR windowid ] +.RB \-l +.IR line +.RI [ stty_args ...] +.SH DESCRIPTION +.B st +is a simple terminal emulator. +.SH OPTIONS +.TP +.B \-a +disable alternate screens in terminal +.TP +.BI \-c " class" +defines the window class (default $TERM). +.TP +.BI \-f " font" +defines the +.I font +to use when st is run. +.TP +.BI \-g " geometry" +defines the X11 geometry string. +The form is [=][{xX}][{+-}{+-}]. See +.BR XParseGeometry (3) +for further details. +.TP +.B \-i +will fixate the position given with the -g option. +.TP +.BI \-n " name" +defines the window instance name (default $TERM). +.TP +.BI \-o " iofile" +writes all the I/O to +.I iofile. +This feature is useful when recording st sessions. A value of "-" means +standard output. +.TP +.BI \-T " title" +defines the window title (default 'st'). +.TP +.BI \-t " title" +defines the window title (default 'st'). +.TP +.BI \-w " windowid" +embeds st within the window identified by +.I windowid +.TP +.BI \-l " line" +use a tty +.I line +instead of a pseudo terminal. +.I line +should be a (pseudo-)serial device (e.g. /dev/ttyS0 on Linux for serial port +0). +When this flag is given +remaining arguments are used as flags for +.BR stty(1). +By default st initializes the serial line to 8 bits, no parity, 1 stop bit +and a 38400 baud rate. The speed is set by appending it as last argument +(e.g. 'st -l /dev/ttyS0 115200'). Arguments before the last one are +.BR stty(1) +flags. If you want to set odd parity on 115200 baud use for example 'st -l +/dev/ttyS0 parenb parodd 115200'. Set the number of bits by using for +example 'st -l /dev/ttyS0 cs7 115200'. See +.BR stty(1) +for more arguments and cases. +.TP +.B \-v +prints version information to stderr, then exits. +.TP +.BI \-e " command " [ " arguments " "... ]" +st executes +.I command +instead of the shell. If this is used it +.B must be the last option +on the command line, as in xterm / rxvt. +This option is only intended for compatibility, +and all the remaining arguments are used as a command +even without it. +.SH SHORTCUTS +.TP +.B Break +Send a break in the serial line. +Break key is obtained in PC keyboards +pressing at the same time control and pause. +.TP +.B Ctrl-Print Screen +Toggle if st should print to the +.I iofile. +.TP +.B Shift-Print Screen +Print the full screen to the +.I iofile. +.TP +.B Print Screen +Print the selection to the +.I iofile. +.TP +.B Ctrl-Shift-Page Up +Increase font size. +.TP +.B Ctrl-Shift-Page Down +Decrease font size. +.TP +.B Ctrl-Shift-Home +Reset to default font size. +.TP +.B Ctrl-Shift-y +Paste from primary selection (middle mouse button). +.TP +.B Ctrl-Shift-c +Copy the selected text to the clipboard selection. +.TP +.B Ctrl-Shift-v +Paste from the clipboard selection. +.SH CUSTOMIZATION +.B st +can be customized by creating a custom config.h and (re)compiling the source +code. This keeps it fast, secure and simple. +.SH AUTHORS +See the LICENSE file for the authors. +.SH LICENSE +See the LICENSE file for the terms of redistribution. +.SH SEE ALSO +.BR tabbed (1), +.BR utmp (1), +.BR stty (1), +.BR scroll (1) +.SH BUGS +See the TODO file in the distribution. + diff --git a/st/st.c b/st/st.c new file mode 100644 index 00000000..cd750f20 --- /dev/null +++ b/st/st.c @@ -0,0 +1,2761 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st.h" +#include "win.h" + +#if defined(__linux) + #include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + #include +#elif defined(__FreeBSD__) || defined(__DragonFly__) + #include +#endif + +/* Arbitrary sizes */ +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 +#define ESC_BUF_SIZ (128*UTF_SIZ) +#define ESC_ARG_SIZ 16 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ +#define HISTSIZE 2000 + +/* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ + term.scr + HISTSIZE + 1) % HISTSIZE] : \ + term.line[(y) - term.scr]) + +enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_ALTSCREEN = 1 << 2, + MODE_CRLF = 1 << 3, + MODE_ECHO = 1 << 4, + MODE_PRINT = 1 << 5, + MODE_UTF8 = 1 << 6, +}; + +enum cursor_movement { + CURSOR_SAVE, + CURSOR_LOAD +}; + +enum cursor_state { + CURSOR_DEFAULT = 0, + CURSOR_WRAPNEXT = 1, + CURSOR_ORIGIN = 2 +}; + +enum charset { + CS_GRAPHIC0, + CS_GRAPHIC1, + CS_UK, + CS_USA, + CS_MULTI, + CS_GER, + CS_FIN +}; + +enum escape_state { + ESC_START = 1, + ESC_CSI = 2, + ESC_STR = 4, /* DCS, OSC, PM, APC */ + ESC_ALTCHARSET = 8, + ESC_STR_END = 16, /* a final string was encountered */ + ESC_TEST = 32, /* Enter in test mode */ + ESC_UTF8 = 64, +}; + +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +typedef struct { + int mode; + int type; + int snap; + /* + * Selection variables: + * nb – normalized coordinates of the beginning of the selection + * ne – normalized coordinates of the end of the selection + * ob – original coordinates of the beginning of the selection + * oe – original coordinates of the end of the selection + */ + struct { + int x, y; + } nb, ne, ob, oe; + + int alt; +} Selection; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ + Line hist[HISTSIZE]; /* history buffer */ + int histi; /* history index */ + int scr; /* scroll back */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int *tabs; + Rune lastc; /* last printed char outside of sequence, 0 if control */ +} Term; + +/* CSI Escape sequence structs */ +/* ESC '[' [[ [] [;]] []] */ +typedef struct { + char buf[ESC_BUF_SIZ]; /* raw string */ + size_t len; /* raw string length */ + char priv; + int arg[ESC_ARG_SIZ]; + int narg; /* nb of args */ + char mode[2]; +} CSIEscape; + +/* STR Escape sequence structs */ +/* ESC type [[ [] [;]] ] ESC '\' */ +typedef struct { + char type; /* ESC type ... */ + char *buf; /* allocated raw string */ + size_t siz; /* allocation size */ + size_t len; /* raw string length */ + char *args[STR_ARG_SIZ]; + int narg; /* nb of args */ +} STREscape; + +static void execsh(char *, char **); +static void stty(char **); +static void sigchld(int); +static void ttywriteraw(const char *, size_t); + +static void csidump(void); +static void csihandle(void); +static void csiparse(void); +static void csireset(void); +static int eschandle(uchar); +static void strdump(void); +static void strhandle(void); +static void strparse(void); +static void strreset(void); + +static void tprinter(char *, size_t); +static void tdumpsel(void); +static void tdumpline(int); +static void tdump(void); +static void tclearregion(int, int, int, int); +static void tcursor(int); +static void tdeletechar(int); +static void tdeleteline(int); +static void tinsertblank(int); +static void tinsertblankline(int); +static int tlinelen(int); +static void tmoveto(int, int); +static void tmoveato(int, int); +static void tnewline(int); +static void tputtab(int); +static void tputc(Rune); +static void treset(void); +static void tscrollup(int, int, int); +static void tscrolldown(int, int, int); +static void tsetattr(const int *, int); +static void tsetchar(Rune, const Glyph *, int, int); +static void tsetdirt(int, int); +static void tsetscroll(int, int); +static void tswapscreen(void); +static void tsetmode(int, int, const int *, int); +static int twrite(const char *, int, int); +static void tfulldirt(void); +static void tcontrolcode(uchar ); +static void tdectest(char ); +static void tdefutf8(char); +static int32_t tdefcolor(const int *, int *, int); +static void tdeftran(char); +static void tstrsequence(uchar); + +static void drawregion(int, int, int, int); + +static void selnormalize(void); +static void selscroll(int, int); +static void selsnap(int *, int *, int); + +static size_t utf8decode(const char *, Rune *, size_t); +static Rune utf8decodebyte(char, size_t *); +static char utf8encodebyte(Rune, size_t); +static size_t utf8validate(Rune *, size_t); + +static char *base64dec(const char *); +static char base64dec_getc(const char **); + +static ssize_t xwrite(int, const char *, size_t); + +/* Globals */ +static Term term; +static Selection sel; +static CSIEscape csiescseq; +static STREscape strescseq; +static int iofd = 1; +static int cmdfd; +static pid_t pid; + +static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +ssize_t +xwrite(int fd, const char *s, size_t len) +{ + size_t aux = len; + ssize_t r; + + while (len > 0) { + r = write(fd, s, len); + if (r < 0) + return r; + len -= r; + s += r; + } + + return aux; +} + +void * +xmalloc(size_t len) +{ + void *p; + + if (!(p = malloc(len))) + die("malloc: %s\n", strerror(errno)); + + return p; +} + +void * +xrealloc(void *p, size_t len) +{ + if ((p = realloc(p, len)) == NULL) + die("realloc: %s\n", strerror(errno)); + + return p; +} + +char * +xstrdup(const char *s) +{ + char *p; + + if ((p = strdup(s)) == NULL) + die("strdup: %s\n", strerror(errno)); + + return p; +} + +size_t +utf8decode(const char *c, Rune *u, size_t clen) +{ + size_t i, j, len, type; + Rune udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type != 0) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Rune +utf8decodebyte(char c, size_t *i) +{ + for (*i = 0; *i < LEN(utfmask); ++(*i)) + if (((uchar)c & utfmask[*i]) == utfbyte[*i]) + return (uchar)c & ~utfmask[*i]; + + return 0; +} + +size_t +utf8encode(Rune u, char *c) +{ + size_t len, i; + + len = utf8validate(&u, 0); + if (len > UTF_SIZ) + return 0; + + for (i = len - 1; i != 0; --i) { + c[i] = utf8encodebyte(u, 0); + u >>= 6; + } + c[0] = utf8encodebyte(u, len); + + return len; +} + +char +utf8encodebyte(Rune u, size_t i) +{ + return utfbyte[i] | (u & ~utfmask[i]); +} + +size_t +utf8validate(Rune *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + + return i; +} + +static const char base64_digits[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +char +base64dec_getc(const char **src) +{ + while (**src && !isprint(**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char * +base64dec(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + + if (in_len % 4) + in_len += 4 - (in_len % 4); + result = dst = xmalloc(in_len / 4 * 3 + 1); + while (*src) { + int a = base64_digits[(unsigned char) base64dec_getc(&src)]; + int b = base64_digits[(unsigned char) base64dec_getc(&src)]; + int c = base64_digits[(unsigned char) base64dec_getc(&src)]; + int d = base64_digits[(unsigned char) base64dec_getc(&src)]; + + /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ + if (a == -1 || b == -1) + break; + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (c == -1) + break; + *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == -1) + break; + *dst++ = ((c & 0x03) << 6) | d; + } + *dst = '\0'; + return result; +} + +void +selinit(void) +{ + sel.mode = SEL_IDLE; + sel.snap = 0; + sel.ob.x = -1; +} + +int +tlinelen(int y) +{ + int i = term.col; + + if (TLINE(y)[i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && TLINE(y)[i - 1].u == ' ') + --i; + + return i; +} + +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type, int done) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + + if (sel.mode == SEL_IDLE) + return; + if (done && sel.mode == SEL_EMPTY) { + selclear(); + return; + } + + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + + sel.mode = done ? SEL_IDLE : SEL_READY; +} + +void +selnormalize(void) +{ + int i; + + if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { + sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; + sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; + } else { + sel.nb.x = MIN(sel.ob.x, sel.oe.x); + sel.ne.x = MAX(sel.ob.x, sel.oe.x); + } + sel.nb.y = MIN(sel.ob.y, sel.oe.y); + sel.ne.y = MAX(sel.ob.y, sel.oe.y); + + selsnap(&sel.nb.x, &sel.nb.y, -1); + selsnap(&sel.ne.x, &sel.ne.y, +1); + + /* expand selection over line breaks */ + if (sel.type == SEL_RECTANGULAR) + return; + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) + sel.nb.x = i; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; +} + +int +selected(int x, int y) +{ + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) + return 0; + + if (sel.type == SEL_RECTANGULAR) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); + + return BETWEEN(y, sel.nb.y, sel.ne.y) + && (y != sel.nb.y || x >= sel.nb.x) + && (y != sel.ne.y || x <= sel.ne.x); +} + +void +selsnap(int *x, int *y, int direction) +{ + int newx, newy, xt, yt; + int delim, prevdelim; + const Glyph *gp, *prevgp; + + switch (sel.snap) { + case SNAP_WORD: + /* + * Snap around if the word wraps around at the end or + * beginning of a line. + */ + prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; + newy = *y; + if (!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) + break; + + if (direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + + gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) + break; + + *x = newx; + *y = newy; + prevgp = gp; + prevdelim = delim; + } + break; + case SNAP_LINE: + /* + * Snap around if the the previous line or the current one + * has set ATTR_WRAP at its end. Then the whole next or + * previous line will be selected. + */ + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { + if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { + if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } + break; + } +} + +char * +getsel(void) +{ + char *str, *ptr; + int y, bufsize, lastx, linelen; + const Glyph *gp, *last; + + if (sel.ob.x == -1) + return NULL; + + bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; + ptr = str = xmalloc(bufsize); + + /* append every set & selected glyph to the selection */ + for (y = sel.nb.y; y <= sel.ne.y; y++) { + if ((linelen = tlinelen(y)) == 0) { + *ptr++ = '\n'; + continue; + } + + if (sel.type == SEL_RECTANGULAR) { + gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + + for ( ; gp <= last; ++gp) { + if (gp->mode & ATTR_WDUMMY) + continue; + + ptr += utf8encode(gp->u, ptr); + } + + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if ((y < sel.ne.y || lastx >= linelen) && + (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) + *ptr++ = '\n'; + } + *ptr = 0; + return str; +} + +void +selclear(void) +{ + if (sel.ob.x == -1) + return; + sel.mode = SEL_IDLE; + sel.ob.x = -1; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void +execsh(char *cmd, char **args) +{ + char *sh, *prog, *arg; + const struct passwd *pw; + + errno = 0; + if ((pw = getpwuid(getuid())) == NULL) { + if (errno) + die("getpwuid: %s\n", strerror(errno)); + else + die("who are you?\n"); + } + + if ((sh = getenv("SHELL")) == NULL) + sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; + + if (args) { + prog = args[0]; + arg = NULL; + } else if (scroll) { + prog = scroll; + arg = utmp ? utmp : sh; + } else if (utmp) { + prog = utmp; + arg = NULL; + } else { + prog = sh; + arg = NULL; + } + DEFAULT(args, ((char *[]) {prog, arg, NULL})); + + unsetenv("COLUMNS"); + unsetenv("LINES"); + unsetenv("TERMCAP"); + setenv("LOGNAME", pw->pw_name, 1); + setenv("USER", pw->pw_name, 1); + setenv("SHELL", sh, 1); + setenv("HOME", pw->pw_dir, 1); + setenv("TERM", termname, 1); + + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + execvp(prog, args); + _exit(1); +} + +void +sigchld(int a) +{ + int stat; + pid_t p; + + if ((p = waitpid(pid, &stat, WNOHANG)) < 0) + die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); + + if (pid != p) + return; + + if (WIFEXITED(stat) && WEXITSTATUS(stat)) + die("child exited with status %d\n", WEXITSTATUS(stat)); + else if (WIFSIGNALED(stat)) + die("child terminated due to signal %d\n", WTERMSIG(stat)); + _exit(0); +} + +void +stty(char **args) +{ + char cmd[_POSIX_ARG_MAX], **p, *q, *s; + size_t n, siz; + + if ((n = strlen(stty_args)) > sizeof(cmd)-1) + die("incorrect stty parameters\n"); + memcpy(cmd, stty_args, n); + q = cmd + n; + siz = sizeof(cmd) - n; + for (p = args; p && (s = *p); ++p) { + if ((n = strlen(s)) > siz-1) + die("stty parameter length too long\n"); + *q++ = ' '; + memcpy(q, s, n); + q += n; + siz -= n + 1; + } + *q = '\0'; + if (system(cmd) != 0) + perror("Couldn't call stty"); +} + +int +ttynew(const char *line, char *cmd, const char *out, char **args) +{ + int m, s; + + if (out) { + term.mode |= MODE_PRINT; + iofd = (!strcmp(out, "-")) ? + 1 : open(out, O_WRONLY | O_CREAT, 0666); + if (iofd < 0) { + fprintf(stderr, "Error opening %s:%s\n", + out, strerror(errno)); + } + } + + if (line) { + if ((cmdfd = open(line, O_RDWR)) < 0) + die("open line '%s' failed: %s\n", + line, strerror(errno)); + dup2(cmdfd, 0); + stty(args); + return cmdfd; + } + + /* seems to work fine on linux, openbsd and freebsd */ + if (openpty(&m, &s, NULL, NULL, NULL) < 0) + die("openpty failed: %s\n", strerror(errno)); + + switch (pid = fork()) { + case -1: + die("fork failed: %s\n", strerror(errno)); + break; + case 0: + close(iofd); + close(m); + setsid(); /* create a new process group */ + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + if (ioctl(s, TIOCSCTTY, NULL) < 0) + die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); + if (s > 2) + close(s); +#ifdef __OpenBSD__ + if (pledge("stdio getpw proc exec", NULL) == -1) + die("pledge\n"); +#endif + execsh(cmd, args); + break; + default: +#ifdef __OpenBSD__ + if (pledge("stdio rpath tty proc", NULL) == -1) + die("pledge\n"); +#endif + close(s); + cmdfd = m; + signal(SIGCHLD, sigchld); + break; + } + return cmdfd; +} + +size_t +ttyread(void) +{ + static char buf[BUFSIZ]; + static int buflen = 0; + int ret, written; + + /* append read bytes to unprocessed bytes */ + ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); + + switch (ret) { + case 0: + exit(0); + case -1: + die("couldn't read from shell: %s\n", strerror(errno)); + default: + buflen += ret; + written = twrite(buf, buflen, 0); + buflen -= written; + /* keep any incomplete UTF-8 byte sequence for the next call */ + if (buflen > 0) + memmove(buf, buf + written, buflen); + return ret; + } +} + +void +ttywrite(const char *s, size_t n, int may_echo) +{ + const char *next; + Arg arg = (Arg) { .i = term.scr }; + + kscrolldown(&arg); + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); + + if (!IS_SET(MODE_CRLF)) { + ttywriteraw(s, n); + return; + } + + /* This is similar to how the kernel handles ONLCR for ttys */ + while (n > 0) { + if (*s == '\r') { + next = s + 1; + ttywriteraw("\r\n", 2); + } else { + next = memchr(s, '\r', n); + DEFAULT(next, s + n); + ttywriteraw(s, next - s); + } + n -= next - s; + s = next; + } +} + +void +ttywriteraw(const char *s, size_t n) +{ + fd_set wfd, rfd; + ssize_t r; + size_t lim = 256; + + /* + * Remember that we are using a pty, which might be a modem line. + * Writing too much will clog the line. That's why we are doing this + * dance. + * FIXME: Migrate the world to Plan 9. + */ + while (n > 0) { + FD_ZERO(&wfd); + FD_ZERO(&rfd); + FD_SET(cmdfd, &wfd); + FD_SET(cmdfd, &rfd); + + /* Check if we can write. */ + if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(cmdfd, &wfd)) { + /* + * Only write the bytes written by ttywrite() or the + * default of 256. This seems to be a reasonable value + * for a serial line. Bigger values might clog the I/O. + */ + if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) + goto write_error; + if (r < n) { + /* + * We weren't able to write out everything. + * This means the buffer is getting full + * again. Empty it. + */ + if (n < lim) + lim = ttyread(); + n -= r; + s += r; + } else { + /* All bytes have been written. */ + break; + } + } + if (FD_ISSET(cmdfd, &rfd)) + lim = ttyread(); + } + return; + +write_error: + die("write error on tty: %s\n", strerror(errno)); +} + +void +ttyresize(int tw, int th) +{ + struct winsize w; + + w.ws_row = term.row; + w.ws_col = term.col; + w.ws_xpixel = tw; + w.ws_ypixel = th; + if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) + fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); +} + +void +ttyhangup() +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + +int +tattrset(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) + return 1; + } + } + + return 0; +} + +void +tsetdirt(int top, int bot) +{ + int i; + + LIMIT(top, 0, term.row-1); + LIMIT(bot, 0, term.row-1); + + for (i = top; i <= bot; i++) + term.dirty[i] = 1; +} + +void +tsetdirtattr(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) { + tsetdirt(i, i); + break; + } + } + } +} + +void +tfulldirt(void) +{ + tsetdirt(0, term.row-1); +} + +void +tcursor(int mode) +{ + static TCursor c[2]; + int alt = IS_SET(MODE_ALTSCREEN); + + if (mode == CURSOR_SAVE) { + c[alt] = term.c; + } else if (mode == CURSOR_LOAD) { + term.c = c[alt]; + tmoveto(c[alt].x, c[alt].y); + } +} + +void +treset(void) +{ + uint i; + + term.c = (TCursor){{ + .mode = ATTR_NULL, + .fg = defaultfg, + .bg = defaultbg + }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) + term.tabs[i] = 1; + term.top = 0; + term.bot = term.row - 1; + term.mode = MODE_WRAP|MODE_UTF8; + memset(term.trantbl, CS_USA, sizeof(term.trantbl)); + term.charset = 0; + + for (i = 0; i < 2; i++) { + tmoveto(0, 0); + tcursor(CURSOR_SAVE); + tclearregion(0, 0, term.col-1, term.row-1); + tswapscreen(); + } +} + +void +tnew(int col, int row) +{ + term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; + tresize(col, row); + treset(); +} + +void +tswapscreen(void) +{ + Line *tmp = term.line; + + term.line = term.alt; + term.alt = tmp; + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); +} + +void +kscrolldown(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (n > term.scr) + n = term.scr; + + if (term.scr > 0) { + term.scr -= n; + selscroll(0, -n); + tfulldirt(); + } +} + +void +kscrollup(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (term.scr <= HISTSIZE-n) { + term.scr += n; + selscroll(0, n); + tfulldirt(); + } +} + +void +tscrolldown(int orig, int n, int copyhist) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + if (copyhist) { + term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[term.bot]; + term.line[term.bot] = temp; + } + + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + + for (i = term.bot; i >= orig+n; i--) { + temp = term.line[i]; + term.line[i] = term.line[i-n]; + term.line[i-n] = temp; + } + + if (term.scr == 0) + selscroll(orig, n); +} + +void +tscrollup(int orig, int n, int copyhist) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + if (copyhist) { + term.histi = (term.histi + 1) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[orig]; + term.line[orig] = temp; + } + + if (term.scr > 0 && term.scr < HISTSIZE) + term.scr = MIN(term.scr + n, HISTSIZE-1); + + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + + for (i = orig; i <= term.bot-n; i++) { + temp = term.line[i]; + term.line[i] = term.line[i+n]; + term.line[i+n] = temp; + } + + if (term.scr == 0) + selscroll(orig, -n); +} + +void +selscroll(int orig, int n) +{ + if (sel.ob.x == -1) + return; + + if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { + selclear(); + } else if (BETWEEN(sel.nb.y, orig, term.bot)) { + sel.ob.y += n; + sel.oe.y += n; + if (sel.ob.y < term.top || sel.ob.y > term.bot || + sel.oe.y < term.top || sel.oe.y > term.bot) { + selclear(); + } else { + selnormalize(); + } + } +} + +void +tnewline(int first_col) +{ + int y = term.c.y; + + if (y == term.bot) { + tscrollup(term.top, 1, 1); + } else { + y++; + } + tmoveto(first_col ? 0 : term.c.x, y); +} + +void +csiparse(void) +{ + char *p = csiescseq.buf, *np; + long int v; + + csiescseq.narg = 0; + if (*p == '?') { + csiescseq.priv = 1; + p++; + } + + csiescseq.buf[csiescseq.len] = '\0'; + while (p < csiescseq.buf+csiescseq.len) { + np = NULL; + v = strtol(p, &np, 10); + if (np == p) + v = 0; + if (v == LONG_MAX || v == LONG_MIN) + v = -1; + csiescseq.arg[csiescseq.narg++] = v; + p = np; + if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + break; + p++; + } + csiescseq.mode[0] = *p++; + csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; +} + +/* for absolute user moves, when decom is set */ +void +tmoveato(int x, int y) +{ + tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0)); +} + +void +tmoveto(int x, int y) +{ + int miny, maxy; + + if (term.c.state & CURSOR_ORIGIN) { + miny = term.top; + maxy = term.bot; + } else { + miny = 0; + maxy = term.row - 1; + } + term.c.state &= ~CURSOR_WRAPNEXT; + term.c.x = LIMIT(x, 0, term.col-1); + term.c.y = LIMIT(y, miny, maxy); +} + +void +tsetchar(Rune u, const Glyph *attr, int x, int y) +{ + static const char *vt100_0[62] = { /* 0x41 - 0x7e */ + "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ + 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ + "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ + "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ + }; + + /* + * The table is proudly stolen from rxvt. + */ + if (term.trantbl[term.charset] == CS_GRAPHIC0 && + BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) + utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); + + if (term.line[y][x].mode & ATTR_WIDE) { + if (x+1 < term.col) { + term.line[y][x+1].u = ' '; + term.line[y][x+1].mode &= ~ATTR_WDUMMY; + } + } else if (term.line[y][x].mode & ATTR_WDUMMY) { + term.line[y][x-1].u = ' '; + term.line[y][x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; + term.line[y][x] = *attr; + term.line[y][x].u = u; +} + +void +tclearregion(int x1, int y1, int x2, int y2) +{ + int x, y, temp; + Glyph *gp; + + if (x1 > x2) + temp = x1, x1 = x2, x2 = temp; + if (y1 > y2) + temp = y1, y1 = y2, y2 = temp; + + LIMIT(x1, 0, term.col-1); + LIMIT(x2, 0, term.col-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { + gp = &term.line[y][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + gp->u = ' '; + } + } +} + +void +tdeletechar(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +} + +void +tinsertblank(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +} + +void +tinsertblankline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrolldown(term.c.y, n, 0); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n, 0); +} + +int32_t +tdefcolor(const int *attr, int *npar, int l) +{ + int32_t idx = -1; + uint r, g, b; + + switch (attr[*npar + 1]) { + case 2: /* direct color in RGB space */ + if (*npar + 4 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + r = attr[*npar + 2]; + g = attr[*npar + 3]; + b = attr[*npar + 4]; + *npar += 4; + if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) + fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n", + r, g, b); + else + idx = TRUECOLOR(r, g, b); + break; + case 5: /* indexed color */ + if (*npar + 2 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + *npar += 2; + if (!BETWEEN(attr[*npar], 0, 255)) + fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]); + else + idx = attr[*npar]; + break; + case 0: /* implemented defined (only foreground) */ + case 1: /* transparent */ + case 3: /* direct color in CMY space */ + case 4: /* direct color in CMYK space */ + default: + fprintf(stderr, + "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; + } + + return idx; +} + +void +tsetattr(const int *attr, int l) +{ + int i; + int32_t idx; + + for (i = 0; i < l; i++) { + switch (attr[i]) { + case 0: + term.c.attr.mode &= ~( + ATTR_BOLD | + ATTR_FAINT | + ATTR_ITALIC | + ATTR_UNDERLINE | + ATTR_BLINK | + ATTR_REVERSE | + ATTR_INVISIBLE | + ATTR_STRUCK ); + term.c.attr.fg = defaultfg; + term.c.attr.bg = defaultbg; + break; + case 1: + term.c.attr.mode |= ATTR_BOLD; + break; + case 2: + term.c.attr.mode |= ATTR_FAINT; + break; + case 3: + term.c.attr.mode |= ATTR_ITALIC; + break; + case 4: + term.c.attr.mode |= ATTR_UNDERLINE; + break; + case 5: /* slow blink */ + /* FALLTHROUGH */ + case 6: /* rapid blink */ + term.c.attr.mode |= ATTR_BLINK; + break; + case 7: + term.c.attr.mode |= ATTR_REVERSE; + break; + case 8: + term.c.attr.mode |= ATTR_INVISIBLE; + break; + case 9: + term.c.attr.mode |= ATTR_STRUCK; + break; + case 22: + term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); + break; + case 23: + term.c.attr.mode &= ~ATTR_ITALIC; + break; + case 24: + term.c.attr.mode &= ~ATTR_UNDERLINE; + break; + case 25: + term.c.attr.mode &= ~ATTR_BLINK; + break; + case 27: + term.c.attr.mode &= ~ATTR_REVERSE; + break; + case 28: + term.c.attr.mode &= ~ATTR_INVISIBLE; + break; + case 29: + term.c.attr.mode &= ~ATTR_STRUCK; + break; + case 38: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.fg = idx; + break; + case 39: + term.c.attr.fg = defaultfg; + break; + case 48: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.bg = idx; + break; + case 49: + term.c.attr.bg = defaultbg; + break; + default: + if (BETWEEN(attr[i], 30, 37)) { + term.c.attr.fg = attr[i] - 30; + } else if (BETWEEN(attr[i], 40, 47)) { + term.c.attr.bg = attr[i] - 40; + } else if (BETWEEN(attr[i], 90, 97)) { + term.c.attr.fg = attr[i] - 90 + 8; + } else if (BETWEEN(attr[i], 100, 107)) { + term.c.attr.bg = attr[i] - 100 + 8; + } else { + fprintf(stderr, + "erresc(default): gfx attr %d unknown\n", + attr[i]); + csidump(); + } + break; + } + } +} + +void +tsetscroll(int t, int b) +{ + int temp; + + LIMIT(t, 0, term.row-1); + LIMIT(b, 0, term.row-1); + if (t > b) { + temp = t; + t = b; + b = temp; + } + term.top = t; + term.bot = b; +} + +void +tsetmode(int priv, int set, const int *args, int narg) +{ + int alt; const int *lim; + + for (lim = args + narg; args < lim; ++args) { + if (priv) { + switch (*args) { + case 1: /* DECCKM -- Cursor key */ + xsetmode(set, MODE_APPCURSOR); + break; + case 5: /* DECSCNM -- Reverse video */ + xsetmode(set, MODE_REVERSE); + break; + case 6: /* DECOM -- Origin */ + MODBIT(term.c.state, set, CURSOR_ORIGIN); + tmoveato(0, 0); + break; + case 7: /* DECAWM -- Auto wrap */ + MODBIT(term.mode, set, MODE_WRAP); + break; + case 0: /* Error (IGNORED) */ + case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ + case 3: /* DECCOLM -- Column (IGNORED) */ + case 4: /* DECSCLM -- Scroll (IGNORED) */ + case 8: /* DECARM -- Auto repeat (IGNORED) */ + case 18: /* DECPFF -- Printer feed (IGNORED) */ + case 19: /* DECPEX -- Printer extent (IGNORED) */ + case 42: /* DECNRCM -- National characters (IGNORED) */ + case 12: /* att610 -- Start blinking cursor (IGNORED) */ + break; + case 25: /* DECTCEM -- Text Cursor Enable Mode */ + xsetmode(!set, MODE_HIDE); + break; + case 9: /* X10 mouse compatibility mode */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEX10); + break; + case 1000: /* 1000: report button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEBTN); + break; + case 1002: /* 1002: report motion on button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMOTION); + break; + case 1003: /* 1003: enable all mouse motions */ + xsetpointermotion(set); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMANY); + break; + case 1004: /* 1004: send focus events to tty */ + xsetmode(set, MODE_FOCUS); + break; + case 1006: /* 1006: extended reporting mode */ + xsetmode(set, MODE_MOUSESGR); + break; + case 1034: + xsetmode(set, MODE_8BIT); + break; + case 1049: /* swap screen & set/restore cursor as xterm */ + if (!allowaltscreen) + break; + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + /* FALLTHROUGH */ + case 47: /* swap screen */ + case 1047: + if (!allowaltscreen) + break; + alt = IS_SET(MODE_ALTSCREEN); + if (alt) { + tclearregion(0, 0, term.col-1, + term.row-1); + } + if (set ^ alt) /* set is always 1 or 0 */ + tswapscreen(); + if (*args != 1049) + break; + /* FALLTHROUGH */ + case 1048: + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + break; + case 2004: /* 2004: bracketed paste mode */ + xsetmode(set, MODE_BRCKTPASTE); + break; + /* Not implemented mouse modes. See comments there. */ + case 1001: /* mouse highlight mode; can hang the + terminal by design when implemented. */ + case 1005: /* UTF-8 mouse mode; will confuse + applications not supporting UTF-8 + and luit. */ + case 1015: /* urxvt mangled mouse mode; incompatible + and can be mistaken for other control + codes. */ + break; + default: + fprintf(stderr, + "erresc: unknown private set/reset mode %d\n", + *args); + break; + } + } else { + switch (*args) { + case 0: /* Error (IGNORED) */ + break; + case 2: + xsetmode(set, MODE_KBDLOCK); + break; + case 4: /* IRM -- Insertion-replacement */ + MODBIT(term.mode, set, MODE_INSERT); + break; + case 12: /* SRM -- Send/Receive */ + MODBIT(term.mode, !set, MODE_ECHO); + break; + case 20: /* LNM -- Linefeed/new line */ + MODBIT(term.mode, set, MODE_CRLF); + break; + default: + fprintf(stderr, + "erresc: unknown set/reset mode %d\n", + *args); + break; + } + } + } +} + +void +csihandle(void) +{ + char buf[40]; + int len; + + switch (csiescseq.mode[0]) { + default: + unknown: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* die(""); */ + break; + case '@': /* ICH -- Insert blank char */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblank(csiescseq.arg[0]); + break; + case 'A': /* CUU -- Cursor Up */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); + break; + case 'B': /* CUD -- Cursor Down */ + case 'e': /* VPR --Cursor Down */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); + break; + case 'i': /* MC -- Media Copy */ + switch (csiescseq.arg[0]) { + case 0: + tdump(); + break; + case 1: + tdumpline(term.c.y); + break; + case 2: + tdumpsel(); + break; + case 4: + term.mode &= ~MODE_PRINT; + break; + case 5: + term.mode |= MODE_PRINT; + break; + } + break; + case 'c': /* DA -- Device Attributes */ + if (csiescseq.arg[0] == 0) + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'b': /* REP -- if last char is printable print it more times */ + DEFAULT(csiescseq.arg[0], 1); + if (term.lastc) + while (csiescseq.arg[0]-- > 0) + tputc(term.lastc); + break; + case 'C': /* CUF -- Cursor Forward */ + case 'a': /* HPR -- Cursor Forward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x+csiescseq.arg[0], term.c.y); + break; + case 'D': /* CUB -- Cursor Backward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x-csiescseq.arg[0], term.c.y); + break; + case 'E': /* CNL -- Cursor Down and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y+csiescseq.arg[0]); + break; + case 'F': /* CPL -- Cursor Up and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y-csiescseq.arg[0]); + break; + case 'g': /* TBC -- Tabulation clear */ + switch (csiescseq.arg[0]) { + case 0: /* clear current tab stop */ + term.tabs[term.c.x] = 0; + break; + case 3: /* clear all the tabs */ + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + break; + default: + goto unknown; + } + break; + case 'G': /* CHA -- Move to */ + case '`': /* HPA */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(csiescseq.arg[0]-1, term.c.y); + break; + case 'H': /* CUP -- Move to */ + case 'f': /* HVP */ + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], 1); + tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); + break; + case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(csiescseq.arg[0]); + break; + case 'J': /* ED -- Clear screen */ + switch (csiescseq.arg[0]) { + case 0: /* below */ + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); + if (term.c.y < term.row-1) { + tclearregion(0, term.c.y+1, term.col-1, + term.row-1); + } + break; + case 1: /* above */ + if (term.c.y > 1) + tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, 0, term.col-1, term.row-1); + break; + default: + goto unknown; + } + break; + case 'K': /* EL -- Clear line */ + switch (csiescseq.arg[0]) { + case 0: /* right */ + tclearregion(term.c.x, term.c.y, term.col-1, + term.c.y); + break; + case 1: /* left */ + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, term.c.y, term.col-1, term.c.y); + break; + } + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); + tscrollup(term.top, csiescseq.arg[0], 0); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[0], 0); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblankline(csiescseq.arg[0]); + break; + case 'l': /* RM -- Reset Mode */ + tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + break; + case 'M': /* DL -- Delete lines */ + DEFAULT(csiescseq.arg[0], 1); + tdeleteline(csiescseq.arg[0]); + break; + case 'X': /* ECH -- Erase char */ + DEFAULT(csiescseq.arg[0], 1); + tclearregion(term.c.x, term.c.y, + term.c.x + csiescseq.arg[0] - 1, term.c.y); + break; + case 'P': /* DCH -- Delete char */ + DEFAULT(csiescseq.arg[0], 1); + tdeletechar(csiescseq.arg[0]); + break; + case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(-csiescseq.arg[0]); + break; + case 'd': /* VPA -- Move to */ + DEFAULT(csiescseq.arg[0], 1); + tmoveato(term.c.x, csiescseq.arg[0]-1); + break; + case 'h': /* SM -- Set terminal mode */ + tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); + break; + case 'm': /* SGR -- Terminal attribute (color) */ + tsetattr(csiescseq.arg, csiescseq.narg); + break; + case 'n': /* DSR – Device Status Report (cursor position) */ + if (csiescseq.arg[0] == 6) { + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + } + break; + case 'r': /* DECSTBM -- Set Scrolling Region */ + if (csiescseq.priv) { + goto unknown; + } else { + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], term.row); + tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); + tmoveato(0, 0); + } + break; + case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ + tcursor(CURSOR_SAVE); + break; + case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ + tcursor(CURSOR_LOAD); + break; + case ' ': + switch (csiescseq.mode[1]) { + case 'q': /* DECSCUSR -- Set Cursor Style */ + if (xsetcursor(csiescseq.arg[0])) + goto unknown; + break; + default: + goto unknown; + } + break; + } +} + +void +csidump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC["); + for (i = 0; i < csiescseq.len; i++) { + c = csiescseq.buf[i] & 0xff; + if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + putc('\n', stderr); +} + +void +csireset(void) +{ + memset(&csiescseq, 0, sizeof(csiescseq)); +} + +void +osc4_color_response(int num) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(num, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch osc4 color %d\n", num); + return; + } + + n = snprintf(buf, sizeof buf, "\033]4;%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + num, r, r, g, g, b, b); + + ttywrite(buf, n, 1); +} + +void +osc_color_response(int index, int num) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(index, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch osc color %d\n", index); + return; + } + + n = snprintf(buf, sizeof buf, "\033]%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + num, r, r, g, g, b, b); + + ttywrite(buf, n, 1); +} + +void +strhandle(void) +{ + char *p = NULL, *dec; + int j, narg, par; + + term.esc &= ~(ESC_STR_END|ESC_STR); + strparse(); + par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; + + switch (strescseq.type) { + case ']': /* OSC -- Operating System Command */ + switch (par) { + case 0: + if (narg > 1) { + xsettitle(strescseq.args[1]); + xseticontitle(strescseq.args[1]); + } + return; + case 1: + if (narg > 1) + xseticontitle(strescseq.args[1]); + return; + case 2: + if (narg > 1) + xsettitle(strescseq.args[1]); + return; + case 52: + if (narg > 2 && allowwindowops) { + dec = base64dec(strescseq.args[2]); + if (dec) { + xsetsel(dec); + xclipcopy(); + } else { + fprintf(stderr, "erresc: invalid base64\n"); + } + } + return; + case 10: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultfg, 10); + else if (xsetcolorname(defaultfg, p)) + fprintf(stderr, "erresc: invalid foreground color: %s\n", p); + else + redraw(); + return; + case 11: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultbg, 11); + else if (xsetcolorname(defaultbg, p)) + fprintf(stderr, "erresc: invalid background color: %s\n", p); + else + redraw(); + return; + case 12: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultcs, 12); + else if (xsetcolorname(defaultcs, p)) + fprintf(stderr, "erresc: invalid cursor color: %s\n", p); + else + redraw(); + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + + if (p && !strcmp(p, "?")) + osc4_color_response(j); + else if (xsetcolorname(j, p)) { + if (par == 104 && narg <= 1) + return; /* color reset without parameter */ + fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", + j, p ? p : "(null)"); + } else { + /* + * TODO if defaultbg color is changed, borders + * are dirty + */ + redraw(); + } + return; + } + break; + case 'k': /* old title set compatibility */ + xsettitle(strescseq.args[0]); + return; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + return; + } + + fprintf(stderr, "erresc: unknown str "); + strdump(); +} + +void +strparse(void) +{ + int c; + char *p = strescseq.buf; + + strescseq.narg = 0; + strescseq.buf[strescseq.len] = '\0'; + + if (*p == '\0') + return; + + while (strescseq.narg < STR_ARG_SIZ) { + strescseq.args[strescseq.narg++] = p; + while ((c = *p) != ';' && c != '\0') + ++p; + if (c == '\0') + return; + *p++ = '\0'; + } +} + +void +strdump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC%c", strescseq.type); + for (i = 0; i < strescseq.len; i++) { + c = strescseq.buf[i] & 0xff; + if (c == '\0') { + putc('\n', stderr); + return; + } else if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + fprintf(stderr, "ESC\\\n"); +} + +void +strreset(void) +{ + strescseq = (STREscape){ + .buf = xrealloc(strescseq.buf, STR_BUF_SIZ), + .siz = STR_BUF_SIZ, + }; +} + +void +sendbreak(const Arg *arg) +{ + if (tcsendbreak(cmdfd, 0)) + perror("Error sending break"); +} + +void +tprinter(char *s, size_t len) +{ + if (iofd != -1 && xwrite(iofd, s, len) < 0) { + perror("Error writing to output file"); + close(iofd); + iofd = -1; + } +} + +void +toggleprinter(const Arg *arg) +{ + term.mode ^= MODE_PRINT; +} + +void +printscreen(const Arg *arg) +{ + tdump(); +} + +void +printsel(const Arg *arg) +{ + tdumpsel(); +} + +void +tdumpsel(void) +{ + char *ptr; + + if ((ptr = getsel())) { + tprinter(ptr, strlen(ptr)); + free(ptr); + } +} + +void +tdumpline(int n) +{ + char buf[UTF_SIZ]; + const Glyph *bp, *end; + + bp = &term.line[n][0]; + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if (bp != end || bp->u != ' ') { + for ( ; bp <= end; ++bp) + tprinter(buf, utf8encode(bp->u, buf)); + } + tprinter("\n", 1); +} + +void +tdump(void) +{ + int i; + + for (i = 0; i < term.row; ++i) + tdumpline(i); +} + +void +tputtab(int n) +{ + uint x = term.c.x; + + if (n > 0) { + while (x < term.col && n--) + for (++x; x < term.col && !term.tabs[x]; ++x) + /* nothing */ ; + } else if (n < 0) { + while (x > 0 && n++) + for (--x; x > 0 && !term.tabs[x]; --x) + /* nothing */ ; + } + term.c.x = LIMIT(x, 0, term.col-1); +} + +void +tdefutf8(char ascii) +{ + if (ascii == 'G') + term.mode |= MODE_UTF8; + else if (ascii == '@') + term.mode &= ~MODE_UTF8; +} + +void +tdeftran(char ascii) +{ + static char cs[] = "0B"; + static int vcs[] = {CS_GRAPHIC0, CS_USA}; + char *p; + + if ((p = strchr(cs, ascii)) == NULL) { + fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); + } else { + term.trantbl[term.icharset] = vcs[p - cs]; + } +} + +void +tdectest(char c) +{ + int x, y; + + if (c == '8') { /* DEC screen alignment test. */ + for (x = 0; x < term.col; ++x) { + for (y = 0; y < term.row; ++y) + tsetchar('E', &term.c.attr, x, y); + } + } +} + +void +tstrsequence(uchar c) +{ + switch (c) { + case 0x90: /* DCS -- Device Control String */ + c = 'P'; + break; + case 0x9f: /* APC -- Application Program Command */ + c = '_'; + break; + case 0x9e: /* PM -- Privacy Message */ + c = '^'; + break; + case 0x9d: /* OSC -- Operating System Command */ + c = ']'; + break; + } + strreset(); + strescseq.type = c; + term.esc |= ESC_STR; +} + +void +tcontrolcode(uchar ascii) +{ + switch (ascii) { + case '\t': /* HT */ + tputtab(1); + return; + case '\b': /* BS */ + tmoveto(term.c.x-1, term.c.y); + return; + case '\r': /* CR */ + tmoveto(0, term.c.y); + return; + case '\f': /* LF */ + case '\v': /* VT */ + case '\n': /* LF */ + /* go to first col if the mode is set */ + tnewline(IS_SET(MODE_CRLF)); + return; + case '\a': /* BEL */ + if (term.esc & ESC_STR_END) { + /* backwards compatibility to xterm */ + strhandle(); + } else { + xbell(); + } + break; + case '\033': /* ESC */ + csireset(); + term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); + term.esc |= ESC_START; + return; + case '\016': /* SO (LS1 -- Locking shift 1) */ + case '\017': /* SI (LS0 -- Locking shift 0) */ + term.charset = 1 - (ascii - '\016'); + return; + case '\032': /* SUB */ + tsetchar('?', &term.c.attr, term.c.x, term.c.y); + /* FALLTHROUGH */ + case '\030': /* CAN */ + csireset(); + break; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + return; + case 0x80: /* TODO: PAD */ + case 0x81: /* TODO: HOP */ + case 0x82: /* TODO: BPH */ + case 0x83: /* TODO: NBH */ + case 0x84: /* TODO: IND */ + break; + case 0x85: /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 0x86: /* TODO: SSA */ + case 0x87: /* TODO: ESA */ + break; + case 0x88: /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 0x89: /* TODO: HTJ */ + case 0x8a: /* TODO: VTS */ + case 0x8b: /* TODO: PLD */ + case 0x8c: /* TODO: PLU */ + case 0x8d: /* TODO: RI */ + case 0x8e: /* TODO: SS2 */ + case 0x8f: /* TODO: SS3 */ + case 0x91: /* TODO: PU1 */ + case 0x92: /* TODO: PU2 */ + case 0x93: /* TODO: STS */ + case 0x94: /* TODO: CCH */ + case 0x95: /* TODO: MW */ + case 0x96: /* TODO: SPA */ + case 0x97: /* TODO: EPA */ + case 0x98: /* TODO: SOS */ + case 0x99: /* TODO: SGCI */ + break; + case 0x9a: /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 0x9b: /* TODO: CSI */ + case 0x9c: /* TODO: ST */ + break; + case 0x90: /* DCS -- Device Control String */ + case 0x9d: /* OSC -- Operating System Command */ + case 0x9e: /* PM -- Privacy Message */ + case 0x9f: /* APC -- Application Program Command */ + tstrsequence(ascii); + return; + } + /* only CAN, SUB, \a and C1 chars interrupt a sequence */ + term.esc &= ~(ESC_STR_END|ESC_STR); +} + +/* + * returns 1 when the sequence is finished and it hasn't to read + * more characters for this sequence, otherwise 0 + */ +int +eschandle(uchar ascii) +{ + switch (ascii) { + case '[': + term.esc |= ESC_CSI; + return 0; + case '#': + term.esc |= ESC_TEST; + return 0; + case '%': + term.esc |= ESC_UTF8; + return 0; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + case ']': /* OSC -- Operating System Command */ + case 'k': /* old title set compatibility */ + tstrsequence(ascii); + return 0; + case 'n': /* LS2 -- Locking shift 2 */ + case 'o': /* LS3 -- Locking shift 3 */ + term.charset = 2 + (ascii - 'n'); + break; + case '(': /* GZD4 -- set primary charset G0 */ + case ')': /* G1D4 -- set secondary charset G1 */ + case '*': /* G2D4 -- set tertiary charset G2 */ + case '+': /* G3D4 -- set quaternary charset G3 */ + term.icharset = ascii - '('; + term.esc |= ESC_ALTCHARSET; + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { + tscrollup(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { + tscrolldown(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'c': /* RIS -- Reset to initial state */ + treset(); + resettitle(); + xloadcols(); + break; + case '=': /* DECPAM -- Application keypad */ + xsetmode(1, MODE_APPKEYPAD); + break; + case '>': /* DECPNM -- Normal keypad */ + xsetmode(0, MODE_APPKEYPAD); + break; + case '7': /* DECSC -- Save Cursor */ + tcursor(CURSOR_SAVE); + break; + case '8': /* DECRC -- Restore Cursor */ + tcursor(CURSOR_LOAD); + break; + case '\\': /* ST -- String Terminator */ + if (term.esc & ESC_STR_END) + strhandle(); + break; + default: + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", + (uchar) ascii, isprint(ascii)? ascii:'.'); + break; + } + return 1; +} + +void +tputc(Rune u) +{ + char c[UTF_SIZ]; + int control; + int width, len; + Glyph *gp; + + control = ISCONTROL(u); + if (u < 127 || !IS_SET(MODE_UTF8)) { + c[0] = u; + width = len = 1; + } else { + len = utf8encode(u, c); + if (!control && (width = wcwidth(u)) == -1) + width = 1; + } + + if (IS_SET(MODE_PRINT)) + tprinter(c, len); + + /* + * STR sequence must be checked before anything else + * because it uses all following characters until it + * receives a ESC, a SUB, a ST or any other C1 control + * character. + */ + if (term.esc & ESC_STR) { + if (u == '\a' || u == 030 || u == 032 || u == 033 || + ISCONTROLC1(u)) { + term.esc &= ~(ESC_START|ESC_STR); + term.esc |= ESC_STR_END; + goto check_control_code; + } + + if (strescseq.len+len >= strescseq.siz) { + /* + * Here is a bug in terminals. If the user never sends + * some code to stop the str or esc command, then st + * will stop responding. But this is better than + * silently failing with unknown characters. At least + * then users will report back. + * + * In the case users ever get fixed, here is the code: + */ + /* + * term.esc = 0; + * strhandle(); + */ + if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2) + return; + strescseq.siz *= 2; + strescseq.buf = xrealloc(strescseq.buf, strescseq.siz); + } + + memmove(&strescseq.buf[strescseq.len], c, len); + strescseq.len += len; + return; + } + +check_control_code: + /* + * Actions of control codes must be performed as soon they arrive + * because they can be embedded inside a control sequence, and + * they must not cause conflicts with sequences. + */ + if (control) { + tcontrolcode(u); + /* + * control codes are not shown ever + */ + if (!term.esc) + term.lastc = 0; + return; + } else if (term.esc & ESC_START) { + if (term.esc & ESC_CSI) { + csiescseq.buf[csiescseq.len++] = u; + if (BETWEEN(u, 0x40, 0x7E) + || csiescseq.len >= \ + sizeof(csiescseq.buf)-1) { + term.esc = 0; + csiparse(); + csihandle(); + } + return; + } else if (term.esc & ESC_UTF8) { + tdefutf8(u); + } else if (term.esc & ESC_ALTCHARSET) { + tdeftran(u); + } else if (term.esc & ESC_TEST) { + tdectest(u); + } else { + if (!eschandle(u)) + return; + /* sequence already finished */ + } + term.esc = 0; + /* + * All characters which form part of a sequence are not + * printed + */ + return; + } + if (selected(term.c.x, term.c.y)) + selclear(); + + gp = &term.line[term.c.y][term.c.x]; + if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + gp->mode |= ATTR_WRAP; + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) + memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); + + if (term.c.x+width > term.col) { + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + tsetchar(u, &term.c.attr, term.c.x, term.c.y); + term.lastc = u; + + if (width == 2) { + gp->mode |= ATTR_WIDE; + if (term.c.x+1 < term.col) { + if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) { + gp[2].u = ' '; + gp[2].mode &= ~ATTR_WDUMMY; + } + gp[1].u = '\0'; + gp[1].mode = ATTR_WDUMMY; + } + } + if (term.c.x+width < term.col) { + tmoveto(term.c.x+width, term.c.y); + } else { + term.c.state |= CURSOR_WRAPNEXT; + } +} + +int +twrite(const char *buf, int buflen, int show_ctrl) +{ + int charsize; + Rune u; + int n; + + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8)) { + /* process a complete utf8 char */ + charsize = utf8decode(buf + n, &u, buflen - n); + if (charsize == 0) + break; + } else { + u = buf[n] & 0xFF; + charsize = 1; + } + if (show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; + tputc('^'); + tputc('['); + } else if (u != '\n' && u != '\r' && u != '\t') { + u ^= 0x40; + tputc('^'); + } + } + tputc(u); + } + return n; +} + +void +tresize(int col, int row) +{ + int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; + TCursor c; + + if (col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to + * memmove because we're freeing the earlier lines + */ + for (i = 0; i <= term.c.y - row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + /* ensure that both src and dst are not NULL */ + if (i > 0) { + memmove(term.line, term.line + i, row * sizeof(Line)); + memmove(term.alt, term.alt + i, row * sizeof(Line)); + } + for (i += row; i < term.row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + for (i = 0; i < HISTSIZE; i++) { + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); + for (j = mincol; j < col; j++) { + term.hist[i][j] = term.c.attr; + term.hist[i][j].u = ' '; + } + } + + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); + } + + /* allocate any new rows */ + for (/* i = minrow */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + term.alt[i] = xmalloc(col * sizeof(Glyph)); + } + if (col > term.col) { + bp = term.tabs + term.col; + + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + while (--bp > term.tabs && !*bp) + /* nothing */ ; + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } + /* update terminal size */ + term.col = col; + term.row = row; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); + /* Clearing both screens (it makes dirty all lines) */ + c = term.c; + for (i = 0; i < 2; i++) { + if (mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if (0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + tcursor(CURSOR_LOAD); + } + term.c = c; +} + +void +resettitle(void) +{ + xsettitle(NULL); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y; + + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(TLINE(y), x1, y, x2); + } +} + +void +draw(void) +{ + int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; + + if (!xstartdraw()) + return; + + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); + if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) + term.ocx--; + if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); + if (term.scr == 0) + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); + if (ocx != term.ocx || ocy != term.ocy) + xximspot(term.ocx, term.ocy); +} + +void +redraw(void) +{ + tfulldirt(); + draw(); +} diff --git a/st/st.h b/st/st.h new file mode 100644 index 00000000..67bfb74f --- /dev/null +++ b/st/st.h @@ -0,0 +1,131 @@ +/* See LICENSE for license details. */ + +#include +#include + +/* macros */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a) / sizeof(a)[0]) +#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) +#define DEFAULT(a, b) (a) = (a) ? (a) : (b) +#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) +#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ + (a).bg != (b).bg) +#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ + (t1.tv_nsec-t2.tv_nsec)/1E6) +#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) + +#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) +#define IS_TRUECOL(x) (1 << 24 & (x)) + +enum glyph_attribute { + ATTR_NULL = 0, + ATTR_BOLD = 1 << 0, + ATTR_FAINT = 1 << 1, + ATTR_ITALIC = 1 << 2, + ATTR_UNDERLINE = 1 << 3, + ATTR_BLINK = 1 << 4, + ATTR_REVERSE = 1 << 5, + ATTR_INVISIBLE = 1 << 6, + ATTR_STRUCK = 1 << 7, + ATTR_WRAP = 1 << 8, + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, +}; + +enum selection_mode { + SEL_IDLE = 0, + SEL_EMPTY = 1, + SEL_READY = 2 +}; + +enum selection_type { + SEL_REGULAR = 1, + SEL_RECTANGULAR = 2 +}; + +enum selection_snap { + SNAP_WORD = 1, + SNAP_LINE = 2 +}; + +typedef unsigned char uchar; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned short ushort; + +typedef uint_least32_t Rune; + +#define Glyph Glyph_ +typedef struct { + Rune u; /* character code */ + ushort mode; /* attribute flags */ + uint32_t fg; /* foreground */ + uint32_t bg; /* background */ +} Glyph; + +typedef Glyph *Line; + +typedef union { + int i; + uint ui; + float f; + const void *v; + const char *s; +} Arg; + +void die(const char *, ...); +void redraw(void); +void draw(void); + +void kscrolldown(const Arg *); +void kscrollup(const Arg *); +void printscreen(const Arg *); +void printsel(const Arg *); +void sendbreak(const Arg *); +void toggleprinter(const Arg *); + +int tattrset(int); +void tnew(int, int); +void tresize(int, int); +void tsetdirtattr(int); +void ttyhangup(void); +int ttynew(const char *, char *, const char *, char **); +size_t ttyread(void); +void ttyresize(int, int); +void ttywrite(const char *, size_t, int); + +void resettitle(void); + +void selclear(void); +void selinit(void); +void selstart(int, int, int); +void selextend(int, int, int, int); +int selected(int, int); +char *getsel(void); + +size_t utf8encode(Rune, char *); + +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(const char *); + +int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b); + +/* config.h globals */ +extern char *utmp; +extern char *scroll; +extern char *stty_args; +extern char *vtiden; +extern wchar_t *worddelimiters; +extern int allowaltscreen; +extern int allowwindowops; +extern char *termname; +extern unsigned int tabspaces; +extern unsigned int defaultfg; +extern unsigned int defaultbg; +extern unsigned int defaultcs; +extern float alpha; diff --git a/st/st.h.orig b/st/st.h.orig new file mode 100644 index 00000000..da36b34a --- /dev/null +++ b/st/st.h.orig @@ -0,0 +1,130 @@ +/* See LICENSE for license details. */ + +#include +#include + +/* macros */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a) / sizeof(a)[0]) +#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) +#define DEFAULT(a, b) (a) = (a) ? (a) : (b) +#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) +#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ + (a).bg != (b).bg) +#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ + (t1.tv_nsec-t2.tv_nsec)/1E6) +#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) + +#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) +#define IS_TRUECOL(x) (1 << 24 & (x)) + +enum glyph_attribute { + ATTR_NULL = 0, + ATTR_BOLD = 1 << 0, + ATTR_FAINT = 1 << 1, + ATTR_ITALIC = 1 << 2, + ATTR_UNDERLINE = 1 << 3, + ATTR_BLINK = 1 << 4, + ATTR_REVERSE = 1 << 5, + ATTR_INVISIBLE = 1 << 6, + ATTR_STRUCK = 1 << 7, + ATTR_WRAP = 1 << 8, + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, +}; + +enum selection_mode { + SEL_IDLE = 0, + SEL_EMPTY = 1, + SEL_READY = 2 +}; + +enum selection_type { + SEL_REGULAR = 1, + SEL_RECTANGULAR = 2 +}; + +enum selection_snap { + SNAP_WORD = 1, + SNAP_LINE = 2 +}; + +typedef unsigned char uchar; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned short ushort; + +typedef uint_least32_t Rune; + +#define Glyph Glyph_ +typedef struct { + Rune u; /* character code */ + ushort mode; /* attribute flags */ + uint32_t fg; /* foreground */ + uint32_t bg; /* background */ +} Glyph; + +typedef Glyph *Line; + +typedef union { + int i; + uint ui; + float f; + const void *v; + const char *s; +} Arg; + +void die(const char *, ...); +void redraw(void); +void draw(void); + +void kscrolldown(const Arg *); +void kscrollup(const Arg *); +void printscreen(const Arg *); +void printsel(const Arg *); +void sendbreak(const Arg *); +void toggleprinter(const Arg *); + +int tattrset(int); +void tnew(int, int); +void tresize(int, int); +void tsetdirtattr(int); +void ttyhangup(void); +int ttynew(const char *, char *, const char *, char **); +size_t ttyread(void); +void ttyresize(int, int); +void ttywrite(const char *, size_t, int); + +void resettitle(void); + +void selclear(void); +void selinit(void); +void selstart(int, int, int); +void selextend(int, int, int, int); +int selected(int, int); +char *getsel(void); + +size_t utf8encode(Rune, char *); + +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(const char *); + +int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b); + +/* config.h globals */ +extern char *utmp; +extern char *scroll; +extern char *stty_args; +extern char *vtiden; +extern wchar_t *worddelimiters; +extern int allowaltscreen; +extern int allowwindowops; +extern char *termname; +extern unsigned int tabspaces; +extern unsigned int defaultfg; +extern unsigned int defaultbg; +extern unsigned int defaultcs; diff --git a/st/st.info b/st/st.info new file mode 100644 index 00000000..8201ad62 --- /dev/null +++ b/st/st.info @@ -0,0 +1,239 @@ +st-mono| simpleterm monocolor, + acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + am, + bce, + bel=^G, + blink=\E[5m, + bold=\E[1m, + cbt=\E[Z, + cvvis=\E[?25h, + civis=\E[?25l, + clear=\E[H\E[2J, + cnorm=\E[?12l\E[?25h, + colors#2, + cols#80, + cr=^M, + csr=\E[%i%p1%d;%p2%dr, + cub=\E[%p1%dD, + cub1=^H, + cud1=^J, + cud=\E[%p1%dB, + cuf1=\E[C, + cuf=\E[%p1%dC, + cup=\E[%i%p1%d;%p2%dH, + cuu1=\E[A, + cuu=\E[%p1%dA, + dch=\E[%p1%dP, + dch1=\E[P, + dim=\E[2m, + dl=\E[%p1%dM, + dl1=\E[M, + ech=\E[%p1%dX, + ed=\E[J, + el=\E[K, + el1=\E[1K, + enacs=\E)0, + flash=\E[?5h$<80/>\E[?5l, + fsl=^G, + home=\E[H, + hpa=\E[%i%p1%dG, + hs, + ht=^I, + hts=\EH, + ich=\E[%p1%d@, + il1=\E[L, + il=\E[%p1%dL, + ind=^J, + indn=\E[%p1%dS, + invis=\E[8m, + is2=\E[4l\E>\E[?1034l, + it#8, + kel=\E[1;2F, + ked=\E[1;5F, + ka1=\E[1~, + ka3=\E[5~, + kc1=\E[4~, + kc3=\E[6~, + kbs=\177, + kcbt=\E[Z, + kb2=\EOu, + kcub1=\EOD, + kcud1=\EOB, + kcuf1=\EOC, + kcuu1=\EOA, + kDC=\E[3;2~, + kent=\EOM, + kEND=\E[1;2F, + kIC=\E[2;2~, + kNXT=\E[6;2~, + kPRV=\E[5;2~, + kHOM=\E[1;2H, + kLFT=\E[1;2D, + kRIT=\E[1;2C, + kind=\E[1;2B, + kri=\E[1;2A, + kclr=\E[3;5~, + kdl1=\E[3;2~, + kdch1=\E[3~, + kich1=\E[2~, + kend=\E[4~, + kf1=\EOP, + kf2=\EOQ, + kf3=\EOR, + kf4=\EOS, + kf5=\E[15~, + kf6=\E[17~, + kf7=\E[18~, + kf8=\E[19~, + kf9=\E[20~, + kf10=\E[21~, + kf11=\E[23~, + kf12=\E[24~, + kf13=\E[1;2P, + kf14=\E[1;2Q, + kf15=\E[1;2R, + kf16=\E[1;2S, + kf17=\E[15;2~, + kf18=\E[17;2~, + kf19=\E[18;2~, + kf20=\E[19;2~, + kf21=\E[20;2~, + kf22=\E[21;2~, + kf23=\E[23;2~, + kf24=\E[24;2~, + kf25=\E[1;5P, + kf26=\E[1;5Q, + kf27=\E[1;5R, + kf28=\E[1;5S, + kf29=\E[15;5~, + kf30=\E[17;5~, + kf31=\E[18;5~, + kf32=\E[19;5~, + kf33=\E[20;5~, + kf34=\E[21;5~, + kf35=\E[23;5~, + kf36=\E[24;5~, + kf37=\E[1;6P, + kf38=\E[1;6Q, + kf39=\E[1;6R, + kf40=\E[1;6S, + kf41=\E[15;6~, + kf42=\E[17;6~, + kf43=\E[18;6~, + kf44=\E[19;6~, + kf45=\E[20;6~, + kf46=\E[21;6~, + kf47=\E[23;6~, + kf48=\E[24;6~, + kf49=\E[1;3P, + kf50=\E[1;3Q, + kf51=\E[1;3R, + kf52=\E[1;3S, + kf53=\E[15;3~, + kf54=\E[17;3~, + kf55=\E[18;3~, + kf56=\E[19;3~, + kf57=\E[20;3~, + kf58=\E[21;3~, + kf59=\E[23;3~, + kf60=\E[24;3~, + kf61=\E[1;4P, + kf62=\E[1;4Q, + kf63=\E[1;4R, + khome=\E[1~, + kil1=\E[2;5~, + krmir=\E[2;2~, + knp=\E[6~, + kmous=\E[M, + kpp=\E[5~, + lines#24, + mir, + msgr, + npc, + op=\E[39;49m, + pairs#64, + mc0=\E[i, + mc4=\E[4i, + mc5=\E[5i, + rc=\E8, + rev=\E[7m, + ri=\EM, + rin=\E[%p1%dT, + ritm=\E[23m, + rmacs=\E(B, + rmcup=\E[?1049l, + rmir=\E[4l, + rmkx=\E[?1l\E>, + rmso=\E[27m, + rmul=\E[24m, + rs1=\Ec, + rs2=\E[4l\E>\E[?1034l, + sc=\E7, + sitm=\E[3m, + sgr0=\E[0m, + smacs=\E(0, + smcup=\E[?1049h, + smir=\E[4h, + smkx=\E[?1h\E=, + smso=\E[7m, + smul=\E[4m, + tbc=\E[3g, + tsl=\E]0;, + xenl, + vpa=\E[%i%p1%dd, +# XTerm extensions + rmxx=\E[29m, + smxx=\E[9m, +# disabled rep for now: causes some issues with older ncurses versions. +# rep=%p1%c\E[%p2%{1}%-%db, +# tmux extensions, see TERMINFO EXTENSIONS in tmux(1) + Tc, + Ms=\E]52;%p1%s;%p2%s\007, + Se=\E[2 q, + Ss=\E[%p1%d q, + +st| simpleterm, + use=st-mono, + colors#8, + setab=\E[4%p1%dm, + setaf=\E[3%p1%dm, + setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, + setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, + sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, + +st-256color| simpleterm with 256 colors, + use=st, + ccc, + colors#256, + oc=\E]104\007, + pairs#32767, +# Nicked from xterm-256color + initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\, + setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, + setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, + +st-meta| simpleterm with meta key, + use=st, + km, + rmm=\E[?1034l, + smm=\E[?1034h, + rs2=\E[4l\E>\E[?1034h, + is2=\E[4l\E>\E[?1034h, + +st-meta-256color| simpleterm with meta key and 256 colors, + use=st-256color, + km, + rmm=\E[?1034l, + smm=\E[?1034h, + rs2=\E[4l\E>\E[?1034h, + is2=\E[4l\E>\E[?1034h, + +st-bs| simpleterm with backspace as backspace, + use=st, + kbs=\010, + kdch1=\177, + +st-bs-256color| simpleterm with backspace as backspace and 256colors, + use=st-256color, + kbs=\010, + kdch1=\177, diff --git a/st/st.o b/st/st.o new file mode 100644 index 0000000000000000000000000000000000000000..5938ab39fcf71cea7551d8e5ff0ff8143494eaa3 GIT binary patch literal 81536 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 literal 0 HcmV?d00001 diff --git a/st/win.h b/st/win.h new file mode 100644 index 00000000..e6e43695 --- /dev/null +++ b/st/win.h @@ -0,0 +1,40 @@ +/* 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 xseticontitle(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/st/x.c b/st/x.c new file mode 100644 index 00000000..ddf41787 --- /dev/null +++ b/st/x.c @@ -0,0 +1,2100 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *argv0; +#include "arg.h" +#include "st.h" +#include "win.h" + +/* types used in config.h */ +typedef struct { + uint mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Shortcut; + +typedef struct { + uint mod; + uint button; + void (*func)(const Arg *); + const Arg arg; + uint release; +} MouseShortcut; + +typedef struct { + KeySym k; + uint mask; + char *s; + /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ + signed char appkey; /* application keypad */ + signed char appcursor; /* application cursor */ +} Key; + +/* X modifiers */ +#define XK_ANY_MOD UINT_MAX +#define XK_NO_MOD 0 +#define XK_SWITCH_MOD (1<<13|1<<14) + +/* function definitions used in config.h */ +static void clipcopy(const Arg *); +static void clippaste(const Arg *); +static void numlock(const Arg *); +static void selpaste(const Arg *); +static void zoom(const Arg *); +static void zoomabs(const Arg *); +static void zoomreset(const Arg *); +static void ttysend(const Arg *); + +/* config.h for applying patches and the configuration. */ +#include "config.h" + +/* XEMBED messages */ +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 + +/* macros */ +#define IS_SET(flag) ((win.mode & (flag)) != 0) +#define TRUERED(x) (((x) & 0xff0000) >> 8) +#define TRUEGREEN(x) (((x) & 0xff00)) +#define TRUEBLUE(x) (((x) & 0xff) << 8) + +typedef XftDraw *Draw; +typedef XftColor Color; +typedef XftGlyphFontSpec GlyphFontSpec; + +/* Purely graphic info */ +typedef struct { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ + int ch; /* char height */ + int cw; /* char width */ + int mode; /* window state/mode flags */ + int cursor; /* cursor style */ +} TermWindow; + +typedef struct { + Display *dpy; + Colormap cmap; + Window win; + Drawable buf; + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ + Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; + struct { + XIM xim; + XIC xic; + XPoint spot; + XVaNestedList spotlist; + } ime; + Draw draw; + Visual *vis; + XSetWindowAttributes attrs; + int scr; + int isfixed; /* is fixed geometry? */ + int depth; /* bit depth */ + int l, t; /* left and top offset */ + int gm; /* geometry mask */ +} XWindow; + +typedef struct { + Atom xtarget; + char *primary, *clipboard; + struct timespec tclick1; + struct timespec tclick2; +} XSelection; + +/* Font structure */ +#define Font Font_ +typedef struct { + int height; + int width; + int ascent; + int descent; + int badslant; + int badweight; + short lbearing; + short rbearing; + XftFont *match; + FcFontSet *set; + FcPattern *pattern; +} Font; + +/* Drawing Context */ +typedef struct { + Color *col; + size_t collen; + Font font, bfont, ifont, ibfont; + GC gc; +} DC; + +static inline ushort sixd_to_16bit(int); +static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); +static void xdrawglyph(Glyph, int, int); +static void xclear(int, int, int, int); +static int xgeommasktogravity(int); +static int ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, XPointer, XPointer); +static int xicdestroy(XIC, XPointer, XPointer); +static void xinit(int, int); +static void cresize(int, int); +static void xresize(int, int); +static void xhints(void); +static int xloadcolor(int, const char *, Color *); +static int xloadfont(Font *, FcPattern *); +static void xloadfonts(const char *, double); +static void xunloadfont(Font *); +static void xunloadfonts(void); +static void xsetenv(void); +static void xseturgency(int); +static int evcol(XEvent *); +static int evrow(XEvent *); + +static void expose(XEvent *); +static void visibility(XEvent *); +static void unmap(XEvent *); +static void kpress(XEvent *); +static void cmessage(XEvent *); +static void resize(XEvent *); +static void focus(XEvent *); +static uint buttonmask(uint); +static int mouseaction(XEvent *, uint); +static void brelease(XEvent *); +static void bpress(XEvent *); +static void bmotion(XEvent *); +static void propnotify(XEvent *); +static void selnotify(XEvent *); +static void selclear_(XEvent *); +static void selrequest(XEvent *); +static void setsel(char *, Time); +static void mousesel(XEvent *, int); +static void mousereport(XEvent *); +static char *kmap(KeySym, uint); +static int match(uint, uint); + +static void run(void); +static void usage(void); + +static void (*handler[LASTEvent])(XEvent *) = { + [KeyPress] = kpress, + [ClientMessage] = cmessage, + [ConfigureNotify] = resize, + [VisibilityNotify] = visibility, + [UnmapNotify] = unmap, + [Expose] = expose, + [FocusIn] = focus, + [FocusOut] = focus, + [MotionNotify] = bmotion, + [ButtonPress] = bpress, + [ButtonRelease] = brelease, +/* + * Uncomment if you want the selection to disappear when you select something + * different in another window. + */ +/* [SelectionClear] = selclear_, */ + [SelectionNotify] = selnotify, +/* + * PropertyNotify is only turned on when there is some INCR transfer happening + * for the selection retrieval. + */ + [PropertyNotify] = propnotify, + [SelectionRequest] = selrequest, +}; + +/* Globals */ +static DC dc; +static XWindow xw; +static XSelection xsel; +static TermWindow win; + +/* Font Ring Cache */ +enum { + FRC_NORMAL, + FRC_ITALIC, + FRC_BOLD, + FRC_ITALICBOLD +}; + +typedef struct { + XftFont *font; + int flags; + Rune unicodep; +} Fontcache; + +/* Fontcache is an array now. A new font will be appended to the array. */ +static Fontcache *frc = NULL; +static int frclen = 0; +static int frccap = 0; +static char *usedfont = NULL; +static double usedfontsize = 0; +static double defaultfontsize = 0; + +static char *opt_alpha = NULL; +static char *opt_class = NULL; +static char **opt_cmd = NULL; +static char *opt_embed = NULL; +static char *opt_font = NULL; +static char *opt_io = NULL; +static char *opt_line = NULL; +static char *opt_name = NULL; +static char *opt_title = NULL; + +static int oldbutton = 3; /* button event on startup: 3 = release */ + +void +clipcopy(const Arg *dummy) +{ + Atom clipboard; + + free(xsel.clipboard); + xsel.clipboard = NULL; + + if (xsel.primary != NULL) { + xsel.clipboard = xstrdup(xsel.primary); + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); + } +} + +void +clippaste(const Arg *dummy) +{ + Atom clipboard; + + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, + xw.win, CurrentTime); +} + +void +selpaste(const Arg *dummy) +{ + XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, + xw.win, CurrentTime); +} + +void +numlock(const Arg *dummy) +{ + win.mode ^= MODE_NUMLOCK; +} + +void +zoom(const Arg *arg) +{ + Arg larg; + + larg.f = usedfontsize + arg->f; + zoomabs(&larg); +} + +void +zoomabs(const Arg *arg) +{ + xunloadfonts(); + xloadfonts(usedfont, arg->f); + cresize(0, 0); + redraw(); + xhints(); +} + +void +zoomreset(const Arg *arg) +{ + Arg larg; + + if (defaultfontsize > 0) { + larg.f = defaultfontsize; + zoomabs(&larg); + } +} + +void +ttysend(const Arg *arg) +{ + ttywrite(arg->s, strlen(arg->s), 1); +} + +int +evcol(XEvent *e) +{ + int x = e->xbutton.x - borderpx; + LIMIT(x, 0, win.tw - 1); + return x / win.cw; +} + +int +evrow(XEvent *e) +{ + int y = e->xbutton.y - borderpx; + LIMIT(y, 0, win.th - 1); + return y / win.ch; +} + +void +mousesel(XEvent *e, int done) +{ + int type, seltype = SEL_REGULAR; + uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); + + for (type = 1; type < LEN(selmasks); ++type) { + if (match(selmasks[type], state)) { + seltype = type; + break; + } + } + selextend(evcol(e), evrow(e), seltype, done); + if (done) + setsel(getsel(), e->xbutton.time); +} + +void +mousereport(XEvent *e) +{ + int len, x = evcol(e), y = evrow(e), + button = e->xbutton.button, state = e->xbutton.state; + char buf[40]; + static int ox, oy; + + /* from urxvt */ + if (e->xbutton.type == MotionNotify) { + if (x == ox && y == oy) + return; + if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) + return; + /* MOUSE_MOTION: no reporting if no button is pressed */ + if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) + return; + + button = oldbutton + 32; + ox = x; + oy = y; + } else { + if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) { + button = 3; + } else { + button -= Button1; + if (button >= 7) + button += 128 - 7; + else if (button >= 3) + button += 64 - 3; + } + if (e->xbutton.type == ButtonPress) { + oldbutton = button; + ox = x; + oy = y; + } else if (e->xbutton.type == ButtonRelease) { + oldbutton = 3; + /* MODE_MOUSEX10: no button release reporting */ + if (IS_SET(MODE_MOUSEX10)) + return; + if (button == 64 || button == 65) + return; + } + } + + if (!IS_SET(MODE_MOUSEX10)) { + button += ((state & ShiftMask ) ? 4 : 0) + + ((state & Mod4Mask ) ? 8 : 0) + + ((state & ControlMask) ? 16 : 0); + } + + if (IS_SET(MODE_MOUSESGR)) { + len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", + button, x+1, y+1, + e->xbutton.type == ButtonRelease ? 'm' : 'M'); + } else if (x < 223 && y < 223) { + len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", + 32+button, 32+x+1, 32+y+1); + } else { + return; + } + + ttywrite(buf, len, 0); +} + +uint +buttonmask(uint button) +{ + return button == Button1 ? Button1Mask + : button == Button2 ? Button2Mask + : button == Button3 ? Button3Mask + : button == Button4 ? Button4Mask + : button == Button5 ? Button5Mask + : 0; +} + +int +mouseaction(XEvent *e, uint release) +{ + MouseShortcut *ms; + + /* ignore Buttonmask for Button - it's set on release */ + uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); + + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { + if (ms->release == release && + ms->button == e->xbutton.button && + (match(ms->mod, state) || /* exact or forced */ + match(ms->mod, state & ~forcemousemod))) { + ms->func(&(ms->arg)); + return 1; + } + } + + return 0; +} + +void +bpress(XEvent *e) +{ + struct timespec now; + int snap; + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 0)) + return; + + if (e->xbutton.button == Button1) { + /* + * If the user clicks below predefined timeouts specific + * snapping behaviour is exposed. + */ + clock_gettime(CLOCK_MONOTONIC, &now); + if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { + snap = SNAP_LINE; + } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { + snap = SNAP_WORD; + } else { + snap = 0; + } + xsel.tclick2 = xsel.tclick1; + xsel.tclick1 = now; + + selstart(evcol(e), evrow(e), snap); + } +} + +void +propnotify(XEvent *e) +{ + XPropertyEvent *xpev; + Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + + xpev = &e->xproperty; + if (xpev->state == PropertyNewValue && + (xpev->atom == XA_PRIMARY || + xpev->atom == clipboard)) { + selnotify(e); + } +} + +void +selnotify(XEvent *e) +{ + ulong nitems, ofs, rem; + int format; + uchar *data, *last, *repl; + Atom type, incratom, property = None; + + incratom = XInternAtom(xw.dpy, "INCR", 0); + + ofs = 0; + if (e->type == SelectionNotify) + property = e->xselection.property; + else if (e->type == PropertyNotify) + property = e->xproperty.atom; + + if (property == None) + return; + + do { + if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, + BUFSIZ/4, False, AnyPropertyType, + &type, &format, &nitems, &rem, + &data)) { + fprintf(stderr, "Clipboard allocation failed\n"); + return; + } + + if (e->type == PropertyNotify && nitems == 0 && rem == 0) { + /* + * If there is some PropertyNotify with no data, then + * this is the signal of the selection owner that all + * data has been transferred. We won't need to receive + * PropertyNotify events anymore. + */ + MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + } + + if (type == incratom) { + /* + * Activate the PropertyNotify events so we receive + * when the selection owner does send us the next + * chunk of data. + */ + MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + + /* + * Deleting the property is the transfer start signal. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); + continue; + } + + /* + * As seen in getsel: + * Line endings are inconsistent in the terminal and GUI world + * copy and pasting. When receiving some selection data, + * replace all '\n' with '\r'. + * FIXME: Fix the computer world. + */ + repl = data; + last = data + nitems * format / 8; + while ((repl = memchr(repl, '\n', last - repl))) { + *repl++ = '\r'; + } + + if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) + ttywrite("\033[200~", 6, 0); + ttywrite((char *)data, nitems * format / 8, 1); + if (IS_SET(MODE_BRCKTPASTE) && rem == 0) + ttywrite("\033[201~", 6, 0); + XFree(data); + /* number of 32-bit chunks returned */ + ofs += nitems * format / 32; + } while (rem > 0); + + /* + * Deleting the property again tells the selection owner to send the + * next data chunk in the property. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); +} + +void +xclipcopy(void) +{ + clipcopy(NULL); +} + +void +selclear_(XEvent *e) +{ + selclear(); +} + +void +selrequest(XEvent *e) +{ + XSelectionRequestEvent *xsre; + XSelectionEvent xev; + Atom xa_targets, string, clipboard; + char *seltext; + + xsre = (XSelectionRequestEvent *) e; + xev.type = SelectionNotify; + xev.requestor = xsre->requestor; + xev.selection = xsre->selection; + xev.target = xsre->target; + xev.time = xsre->time; + if (xsre->property == None) + xsre->property = xsre->target; + + /* reject */ + xev.property = None; + + xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); + if (xsre->target == xa_targets) { + /* respond with the supported type */ + string = xsel.xtarget; + XChangeProperty(xsre->display, xsre->requestor, xsre->property, + XA_ATOM, 32, PropModeReplace, + (uchar *) &string, 1); + xev.property = xsre->property; + } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { + /* + * xith XA_STRING non ascii characters may be incorrect in the + * requestor. It is not our problem, use utf8. + */ + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + if (xsre->selection == XA_PRIMARY) { + seltext = xsel.primary; + } else if (xsre->selection == clipboard) { + seltext = xsel.clipboard; + } else { + fprintf(stderr, + "Unhandled clipboard selection 0x%lx\n", + xsre->selection); + return; + } + if (seltext != NULL) { + XChangeProperty(xsre->display, xsre->requestor, + xsre->property, xsre->target, + 8, PropModeReplace, + (uchar *)seltext, strlen(seltext)); + xev.property = xsre->property; + } + } + + /* all done, send a notification to the listener */ + if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) + fprintf(stderr, "Error sending SelectionNotify event\n"); +} + +void +setsel(char *str, Time t) +{ + if (!str) + return; + + free(xsel.primary); + xsel.primary = str; + + XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); + if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) + selclear(); +} + +void +xsetsel(char *str) +{ + setsel(str, CurrentTime); +} + +void +brelease(XEvent *e) +{ + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 1)) + return; + if (e->xbutton.button == Button1) + mousesel(e, 1); +} + +void +bmotion(XEvent *e) +{ + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + mousesel(e, 0); +} + +void +cresize(int width, int height) +{ + int col, row; + + if (width != 0) + win.w = width; + if (height != 0) + win.h = height; + + col = (win.w - 2 * borderpx) / win.cw; + row = (win.h - 2 * borderpx) / win.ch; + col = MAX(1, col); + row = MAX(1, row); + + tresize(col, row); + xresize(col, row); + ttyresize(win.tw, win.th); +} + +void +xresize(int col, int row) +{ + win.tw = col * win.cw; + win.th = row * win.ch; + + XFreePixmap(xw.dpy, xw.buf); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, + xw.depth); + XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + + /* resize to new width */ + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); +} + +ushort +sixd_to_16bit(int x) +{ + return x == 0 ? 0 : 0x3737 + 0x2828 * x; +} + +int +xloadcolor(int i, const char *name, Color *ncolor) +{ + XRenderColor color = { .alpha = 0xffff }; + + if (!name) { + if (BETWEEN(i, 16, 255)) { /* 256 color */ + if (i < 6*6*6+16) { /* same colors as xterm */ + color.red = sixd_to_16bit( ((i-16)/36)%6 ); + color.green = sixd_to_16bit( ((i-16)/6) %6 ); + color.blue = sixd_to_16bit( ((i-16)/1) %6 ); + } else { /* greyscale */ + color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); + color.green = color.blue = color.red; + } + return XftColorAllocValue(xw.dpy, xw.vis, + xw.cmap, &color, ncolor); + } else + name = colorname[i]; + } + + return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); +} + +void +xloadcols(void) +{ + int i; + static int loaded; + Color *cp; + + if (loaded) { + for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) + XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); + } else { + dc.collen = MAX(LEN(colorname), 256); + dc.col = xmalloc(dc.collen * sizeof(Color)); + } + + for (i = 0; i < dc.collen; i++) + if (!xloadcolor(i, NULL, &dc.col[i])) { + if (colorname[i]) + die("could not allocate color '%s'\n", colorname[i]); + else + die("could not allocate color %d\n", i); + } + + /* set alpha value of bg color */ + if (opt_alpha) + alpha = strtof(opt_alpha, NULL); + dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); + dc.col[defaultbg].pixel &= 0x00FFFFFF; + dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; + loaded = 1; +} + +int +xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) +{ + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + *r = dc.col[x].color.red >> 8; + *g = dc.col[x].color.green >> 8; + *b = dc.col[x].color.blue >> 8; + + return 0; +} + +int +xsetcolorname(int x, const char *name) +{ + Color ncolor; + + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + if (!xloadcolor(x, name, &ncolor)) + return 1; + + XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); + dc.col[x] = ncolor; + + return 0; +} + +/* + * Absolute coordinates. + */ +void +xclear(int x1, int y1, int x2, int y2) +{ + XftDrawRect(xw.draw, + &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], + x1, y1, x2-x1, y2-y1); +} + +void +xhints(void) +{ + XClassHint class = {opt_name ? opt_name : termname, + opt_class ? opt_class : termname}; + XWMHints wm = {.flags = InputHint, .input = 1}; + XSizeHints *sizeh; + + sizeh = XAllocSizeHints(); + + sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; + sizeh->height = win.h; + sizeh->width = win.w; + sizeh->height_inc = win.ch; + sizeh->width_inc = win.cw; + sizeh->base_height = 2 * borderpx; + sizeh->base_width = 2 * borderpx; + sizeh->min_height = win.ch + 2 * borderpx; + sizeh->min_width = win.cw + 2 * borderpx; + if (xw.isfixed) { + sizeh->flags |= PMaxSize; + sizeh->min_width = sizeh->max_width = win.w; + sizeh->min_height = sizeh->max_height = win.h; + } + if (xw.gm & (XValue|YValue)) { + sizeh->flags |= USPosition | PWinGravity; + sizeh->x = xw.l; + sizeh->y = xw.t; + sizeh->win_gravity = xgeommasktogravity(xw.gm); + } + + XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, + &class); + XFree(sizeh); +} + +int +xgeommasktogravity(int mask) +{ + switch (mask & (XNegative|YNegative)) { + case 0: + return NorthWestGravity; + case XNegative: + return NorthEastGravity; + case YNegative: + return SouthWestGravity; + } + + return SouthEastGravity; +} + +int +xloadfont(Font *f, FcPattern *pattern) +{ + FcPattern *configured; + FcPattern *match; + FcResult result; + XGlyphInfo extents; + int wantattr, haveattr; + + /* + * Manually configure instead of calling XftMatchFont + * so that we can use the configured pattern for + * "missing glyph" lookups. + */ + configured = FcPatternDuplicate(pattern); + if (!configured) + return 1; + + FcConfigSubstitute(NULL, configured, FcMatchPattern); + XftDefaultSubstitute(xw.dpy, xw.scr, configured); + + match = FcFontMatch(NULL, configured, &result); + if (!match) { + FcPatternDestroy(configured); + return 1; + } + + if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { + FcPatternDestroy(configured); + FcPatternDestroy(match); + return 1; + } + + if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == + XftResultMatch)) { + /* + * Check if xft was unable to find a font with the appropriate + * slant but gave us one anyway. Try to mitigate. + */ + if ((XftPatternGetInteger(f->match->pattern, "slant", 0, + &haveattr) != XftResultMatch) || haveattr < wantattr) { + f->badslant = 1; + fputs("font slant does not match\n", stderr); + } + } + + if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == + XftResultMatch)) { + if ((XftPatternGetInteger(f->match->pattern, "weight", 0, + &haveattr) != XftResultMatch) || haveattr != wantattr) { + f->badweight = 1; + fputs("font weight does not match\n", stderr); + } + } + + XftTextExtentsUtf8(xw.dpy, f->match, + (const FcChar8 *) ascii_printable, + strlen(ascii_printable), &extents); + + f->set = NULL; + f->pattern = configured; + + f->ascent = f->match->ascent; + f->descent = f->match->descent; + f->lbearing = 0; + f->rbearing = f->match->max_advance_width; + + f->height = f->ascent + f->descent; + f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); + + return 0; +} + +void +xloadfonts(const char *fontstr, double fontsize) +{ + FcPattern *pattern; + double fontval; + + if (fontstr[0] == '-') + pattern = XftXlfdParse(fontstr, False, False); + else + pattern = FcNameParse((const FcChar8 *)fontstr); + + if (!pattern) + die("can't open font %s\n", fontstr); + + if (fontsize > 1) { + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternDel(pattern, FC_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); + usedfontsize = fontsize; + } else { + if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = fontval; + } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = -1; + } else { + /* + * Default font size is 12, if none given. This is to + * have a known usedfontsize value. + */ + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); + usedfontsize = 12; + } + defaultfontsize = usedfontsize; + } + + if (xloadfont(&dc.font, pattern)) + die("can't open font %s\n", fontstr); + + if (usedfontsize < 0) { + FcPatternGetDouble(dc.font.match->pattern, + FC_PIXEL_SIZE, 0, &fontval); + usedfontsize = fontval; + if (fontsize == 0) + defaultfontsize = fontval; + } + + /* Setting character width and height. */ + win.cw = ceilf(dc.font.width * cwscale); + win.ch = ceilf(dc.font.height * chscale); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + if (xloadfont(&dc.ifont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_WEIGHT); + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + if (xloadfont(&dc.ibfont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + if (xloadfont(&dc.bfont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDestroy(pattern); +} + +void +xunloadfont(Font *f) +{ + XftFontClose(xw.dpy, f->match); + FcPatternDestroy(f->pattern); + if (f->set) + FcFontSetDestroy(f->set); +} + +void +xunloadfonts(void) +{ + /* Free the loaded fonts in the font cache. */ + while (frclen > 0) + XftFontClose(xw.dpy, frc[--frclen].font); + + xunloadfont(&dc.font); + xunloadfont(&dc.bfont); + xunloadfont(&dc.ifont); + xunloadfont(&dc.ibfont); +} + +int +ximopen(Display *dpy) +{ + XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; + XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; + + xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); + if (xw.ime.xim == NULL) + return 0; + + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) + fprintf(stderr, "XSetIMValues: " + "Could not set XNDestroyCallback.\n"); + + xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, + NULL); + + if (xw.ime.xic == NULL) { + xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, + XNDestroyCallback, &icdestroy, + NULL); + } + if (xw.ime.xic == NULL) + fprintf(stderr, "XCreateIC: Could not create input context.\n"); + + return 1; +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + if (ximopen(dpy)) + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.ime.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + XFree(xw.ime.spotlist); +} + +int +xicdestroy(XIC xim, XPointer client, XPointer call) +{ + xw.ime.xic = NULL; + return 1; +} + +void +xinit(int cols, int rows) +{ + XGCValues gcvalues; + Cursor cursor; + Window parent; + pid_t thispid = getpid(); + XColor xmousefg, xmousebg; + XWindowAttributes attr; + XVisualInfo vis; + + if (!(xw.dpy = XOpenDisplay(NULL))) + die("can't open display\n"); + xw.scr = XDefaultScreen(xw.dpy); + + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { + parent = XRootWindow(xw.dpy, xw.scr); + xw.depth = 32; + } else { + XGetWindowAttributes(xw.dpy, parent, &attr); + xw.depth = attr.depth; + } + + XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); + xw.vis = vis.visual; + + /* font */ + if (!FcInit()) + die("could not init fontconfig.\n"); + + usedfont = (opt_font == NULL)? font : opt_font; + xloadfonts(usedfont, 0); + + /* colors */ + xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); + xloadcols(); + + /* adjust fixed window geometry */ + win.w = 2 * borderpx + cols * win.cw; + win.h = 2 * borderpx + rows * win.ch; + if (xw.gm & XNegative) + xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; + if (xw.gm & YNegative) + xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; + + /* Events */ + xw.attrs.background_pixel = dc.col[defaultbg].pixel; + xw.attrs.border_pixel = dc.col[defaultbg].pixel; + xw.attrs.bit_gravity = NorthWestGravity; + xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask + | ExposureMask | VisibilityChangeMask | StructureNotifyMask + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + xw.attrs.colormap = xw.cmap; + + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, + win.w, win.h, 0, xw.depth, InputOutput, + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &xw.attrs); + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); + dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + + /* font spec buffer */ + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); + + /* Xft rendering context */ + xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); + + /* input methods */ + if (!ximopen(xw.dpy)) { + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + } + + /* white cursor, black outline */ + cursor = XCreateFontCursor(xw.dpy, mouseshape); + XDefineCursor(xw.dpy, xw.win, cursor); + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { + xmousefg.red = 0xffff; + xmousefg.green = 0xffff; + xmousefg.blue = 0xffff; + } + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { + xmousebg.red = 0x0000; + xmousebg.green = 0x0000; + xmousebg.blue = 0x0000; + } + + XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); + + xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); + xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); + xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); + xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); + XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); + + xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); + XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, + PropModeReplace, (uchar *)&thispid, 1); + + win.mode = MODE_NUMLOCK; + resettitle(); + xhints(); + XMapWindow(xw.dpy, xw.win); + XSync(xw.dpy, False); + + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); + xsel.primary = NULL; + xsel.clipboard = NULL; + xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); + if (xsel.xtarget == None) + xsel.xtarget = XA_STRING; +} + +int +xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) +{ + float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; + ushort mode, prevmode = USHRT_MAX; + Font *font = &dc.font; + int frcflags = FRC_NORMAL; + float runewidth = win.cw; + Rune rune; + FT_UInt glyphidx; + FcResult fcres; + FcPattern *fcpattern, *fontpattern; + FcFontSet *fcsets[] = { NULL }; + FcCharSet *fccharset; + int i, f, numspecs = 0; + + for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { + /* Fetch rune and mode for current glyph. */ + rune = glyphs[i].u; + mode = glyphs[i].mode; + + /* Skip dummy wide-character spacing. */ + if (mode == ATTR_WDUMMY) + continue; + + /* Determine font for glyph if different from previous glyph. */ + if (prevmode != mode) { + prevmode = mode; + font = &dc.font; + frcflags = FRC_NORMAL; + runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { + font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } else if (mode & ATTR_ITALIC) { + font = &dc.ifont; + frcflags = FRC_ITALIC; + } else if (mode & ATTR_BOLD) { + font = &dc.bfont; + frcflags = FRC_BOLD; + } + yp = winy + font->ascent; + } + + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + if (glyphidx) { + specs[numspecs].font = font->match; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + continue; + } + + /* Fallback on font cache, search the font cache for match. */ + for (f = 0; f < frclen; f++) { + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); + /* Everything correct. */ + if (glyphidx && frc[f].flags == frcflags) + break; + /* We got a default font for a not found glyph. */ + if (!glyphidx && frc[f].flags == frcflags + && frc[f].unicodep == rune) { + break; + } + } + + /* Nothing was found. Use fontconfig to find matching font. */ + if (f >= frclen) { + if (!font->set) + font->set = FcFontSort(0, font->pattern, + 1, 0, &fcres); + fcsets[0] = font->set; + + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + * + * Xft and fontconfig are design failures. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, + fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + + FcConfigSubstitute(0, fcpattern, + FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + fontpattern = FcFontSetMatch(0, fcsets, 1, + fcpattern, &fcres); + + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } + + frc[frclen].font = XftFontOpenPattern(xw.dpy, + fontpattern); + if (!frc[frclen].font) + die("XftFontOpenPattern failed seeking fallback font: %s\n", + strerror(errno)); + frc[frclen].flags = frcflags; + frc[frclen].unicodep = rune; + + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); + + f = frclen; + frclen++; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + } + + specs[numspecs].font = frc[f].font; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + } + + return numspecs; +} + +void +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) +{ + int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); + int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, + width = charlen * win.cw; + Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; + XRenderColor colfg, colbg; + XRectangle r; + + /* Fallback on color display for attributes not supported by the font */ + if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { + if (dc.ibfont.badslant || dc.ibfont.badweight) + base.fg = defaultattr; + } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || + (base.mode & ATTR_BOLD && dc.bfont.badweight)) { + base.fg = defaultattr; + } + + if (IS_TRUECOL(base.fg)) { + colfg.alpha = 0xffff; + colfg.red = TRUERED(base.fg); + colfg.green = TRUEGREEN(base.fg); + colfg.blue = TRUEBLUE(base.fg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); + fg = &truefg; + } else { + fg = &dc.col[base.fg]; + } + + if (IS_TRUECOL(base.bg)) { + colbg.alpha = 0xffff; + colbg.green = TRUEGREEN(base.bg); + colbg.red = TRUERED(base.bg); + colbg.blue = TRUEBLUE(base.bg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); + bg = &truebg; + } else { + bg = &dc.col[base.bg]; + } + + /* Change basic system colors [0-7] to bright system colors [8-15] */ + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) + fg = &dc.col[base.fg + 8]; + + if (IS_SET(MODE_REVERSE)) { + if (fg == &dc.col[defaultfg]) { + fg = &dc.col[defaultbg]; + } else { + colfg.red = ~fg->color.red; + colfg.green = ~fg->color.green; + colfg.blue = ~fg->color.blue; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, + &revfg); + fg = &revfg; + } + + if (bg == &dc.col[defaultbg]) { + bg = &dc.col[defaultfg]; + } else { + colbg.red = ~bg->color.red; + colbg.green = ~bg->color.green; + colbg.blue = ~bg->color.blue; + colbg.alpha = bg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, + &revbg); + bg = &revbg; + } + } + + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { + colfg.red = fg->color.red / 2; + colfg.green = fg->color.green / 2; + colfg.blue = fg->color.blue / 2; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); + fg = &revfg; + } + + if (base.mode & ATTR_REVERSE) { + temp = fg; + fg = bg; + bg = temp; + } + + if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) + fg = bg; + + if (base.mode & ATTR_INVISIBLE) + fg = bg; + + /* Intelligent cleaning up of the borders. */ + if (x == 0) { + xclear(0, (y == 0)? 0 : winy, borderpx, + winy + win.ch + + ((winy + win.ch >= borderpx + win.th)? win.h : 0)); + } + if (winx + width >= borderpx + win.tw) { + xclear(winx + width, (y == 0)? 0 : winy, win.w, + ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); + } + if (y == 0) + xclear(winx, 0, winx + width, borderpx); + if (winy + win.ch >= borderpx + win.th) + xclear(winx, winy + win.ch, winx + width, win.h); + + /* Clean up the region we want to draw to. */ + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + + /* Set the clip region because Xft is sometimes dirty. */ + r.x = 0; + r.y = 0; + r.height = win.ch; + r.width = width; + XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + + /* Render underline and strikethrough. */ + if (base.mode & ATTR_UNDERLINE) { + XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, + width, 1); + } + + if (base.mode & ATTR_STRUCK) { + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, + width, 1); + } + + /* Reset clip to none. */ + XftDrawSetClip(xw.draw, 0); +} + +void +xdrawglyph(Glyph g, int x, int y) +{ + int numspecs; + XftGlyphFontSpec spec; + + numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); + xdrawglyphfontspecs(&spec, g, numspecs, x, y); +} + +void +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) +{ + Color drawcol; + + /* remove the old cursor */ + if (selected(ox, oy)) + og.mode ^= ATTR_REVERSE; + xdrawglyph(og, ox, oy); + + if (IS_SET(MODE_HIDE)) + return; + + /* + * Select the right color for the right mode. + */ + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; + + if (IS_SET(MODE_REVERSE)) { + g.mode |= ATTR_REVERSE; + g.bg = defaultfg; + if (selected(cx, cy)) { + drawcol = dc.col[defaultcs]; + g.fg = defaultrcs; + } else { + drawcol = dc.col[defaultrcs]; + g.fg = defaultcs; + } + } else { + if (selected(cx, cy)) { + g.fg = defaultfg; + g.bg = defaultrcs; + } else { + g.fg = defaultbg; + g.bg = defaultcs; + } + drawcol = dc.col[g.bg]; + } + + /* draw the new one */ + if (IS_SET(MODE_FOCUSED)) { + switch (win.cursor) { + case 7: /* st extension */ + g.u = 0x2603; /* snowman (U+2603) */ + /* FALLTHROUGH */ + case 0: /* Blinking Block */ + case 1: /* Blinking Block (Default) */ + case 2: /* Steady Block */ + xdrawglyph(g, cx, cy); + break; + case 3: /* Blinking Underline */ + case 4: /* Steady Underline */ + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - \ + cursorthickness, + win.cw, cursorthickness); + break; + case 5: /* Blinking bar */ + case 6: /* Steady bar */ + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + cursorthickness, win.ch); + break; + } + } else { + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + win.cw - 1, 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + (cx + 1) * win.cw - 1, + borderpx + cy * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - 1, + win.cw, 1); + } +} + +void +xsetenv(void) +{ + char buf[sizeof(long) * 8 + 1]; + + snprintf(buf, sizeof(buf), "%lu", xw.win); + setenv("WINDOWID", buf, 1); +} + +void +xseticontitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; + XSetWMIconName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); + XFree(prop.value); +} + +void +xsettitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; + XSetWMName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); + XFree(prop.value); +} + +int +xstartdraw(void) +{ + return IS_SET(MODE_VISIBLE); +} + +void +xdrawline(Line line, int x1, int y1, int x2) +{ + int i, x, ox, numspecs; + Glyph base, new; + XftGlyphFontSpec *specs = xw.specbuf; + + numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + if (new.mode == ATTR_WDUMMY) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1); +} + +void +xfinishdraw(void) +{ + XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, + win.h, 0, 0); + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(MODE_REVERSE)? + defaultfg : defaultbg].pixel); +} + +void +xximspot(int x, int y) +{ + if (xw.ime.xic == NULL) + return; + + xw.ime.spot.x = borderpx + x * win.cw; + xw.ime.spot.y = borderpx + (y + 1) * win.ch; + + XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); +} + +void +expose(XEvent *ev) +{ + redraw(); +} + +void +visibility(XEvent *ev) +{ + XVisibilityEvent *e = &ev->xvisibility; + + MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); +} + +void +unmap(XEvent *ev) +{ + win.mode &= ~MODE_VISIBLE; +} + +void +xsetpointermotion(int set) +{ + MODBIT(xw.attrs.event_mask, set, PointerMotionMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); +} + +void +xsetmode(int set, unsigned int flags) +{ + int mode = win.mode; + MODBIT(win.mode, set, flags); + if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) + redraw(); +} + +int +xsetcursor(int cursor) +{ + if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ + return 1; + win.cursor = cursor; + return 0; +} + +void +xseturgency(int add) +{ + XWMHints *h = XGetWMHints(xw.dpy, xw.win); + + MODBIT(h->flags, add, XUrgencyHint); + XSetWMHints(xw.dpy, xw.win, h); + XFree(h); +} + +void +xbell(void) +{ + if (!(IS_SET(MODE_FOCUSED))) + xseturgency(1); + if (bellvolume) + XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); +} + +void +focus(XEvent *ev) +{ + XFocusChangeEvent *e = &ev->xfocus; + + if (e->mode == NotifyGrab) + return; + + if (ev->type == FocusIn) { + if (xw.ime.xic) + XSetICFocus(xw.ime.xic); + win.mode |= MODE_FOCUSED; + xseturgency(0); + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[I", 3, 0); + } else { + if (xw.ime.xic) + XUnsetICFocus(xw.ime.xic); + win.mode &= ~MODE_FOCUSED; + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[O", 3, 0); + } +} + +int +match(uint mask, uint state) +{ + return mask == XK_ANY_MOD || mask == (state & ~ignoremod); +} + +char* +kmap(KeySym k, uint state) +{ + Key *kp; + int i; + + /* Check for mapped keys out of X11 function keys. */ + for (i = 0; i < LEN(mappedkeys); i++) { + if (mappedkeys[i] == k) + break; + } + if (i == LEN(mappedkeys)) { + if ((k & 0xFFFF) < 0xFD00) + return NULL; + } + + for (kp = key; kp < key + LEN(key); kp++) { + if (kp->k != k) + continue; + + if (!match(kp->mask, state)) + continue; + + if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) + continue; + if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) + continue; + + if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) + continue; + + return kp->s; + } + + return NULL; +} + +void +kpress(XEvent *ev) +{ + XKeyEvent *e = &ev->xkey; + KeySym ksym; + char buf[64], *customkey; + int len; + Rune c; + Status status; + Shortcut *bp; + + if (IS_SET(MODE_KBDLOCK)) + return; + + if (xw.ime.xic) + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); + else + len = XLookupString(e, buf, sizeof buf, &ksym, NULL); + /* 1. shortcuts */ + for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { + if (ksym == bp->keysym && match(bp->mod, e->state)) { + bp->func(&(bp->arg)); + return; + } + } + + /* 2. custom keys from config.h */ + if ((customkey = kmap(ksym, e->state))) { + ttywrite(customkey, strlen(customkey), 1); + return; + } + + /* 3. composed string from input method */ + if (len == 0) + return; + if (len == 1 && e->state & Mod1Mask) { + if (IS_SET(MODE_8BIT)) { + if (*buf < 0177) { + c = *buf | 0x80; + len = utf8encode(c, buf); + } + } else { + buf[1] = buf[0]; + buf[0] = '\033'; + len = 2; + } + } + ttywrite(buf, len, 1); +} + +void +cmessage(XEvent *e) +{ + /* + * See xembed specs + * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html + */ + if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { + if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { + win.mode |= MODE_FOCUSED; + xseturgency(0); + } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { + win.mode &= ~MODE_FOCUSED; + } + } else if (e->xclient.data.l[0] == xw.wmdeletewin) { + ttyhangup(); + exit(0); + } +} + +void +resize(XEvent *e) +{ + if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) + return; + + cresize(e->xconfigure.width, e->xconfigure.height); +} + +void +run(void) +{ + XEvent ev; + int w = win.w, h = win.h; + fd_set rfd; + int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; + struct timespec seltv, *tv, now, lastblink, trigger; + double timeout; + + /* Waiting for window mapping */ + do { + XNextEvent(xw.dpy, &ev); + /* + * This XFilterEvent call is required because of XOpenIM. It + * does filter out the key event and some client message for + * the input method too. + */ + if (XFilterEvent(&ev, None)) + continue; + if (ev.type == ConfigureNotify) { + w = ev.xconfigure.width; + h = ev.xconfigure.height; + } + } while (ev.type != MapNotify); + + ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); + cresize(w, h); + + for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { + FD_ZERO(&rfd); + FD_SET(ttyfd, &rfd); + FD_SET(xfd, &rfd); + + if (XPending(xw.dpy)) + timeout = 0; /* existing events might not set xfd */ + + seltv.tv_sec = timeout / 1E3; + seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); + tv = timeout >= 0 ? &seltv : NULL; + + if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + clock_gettime(CLOCK_MONOTONIC, &now); + + if (FD_ISSET(ttyfd, &rfd)) + ttyread(); + + xev = 0; + while (XPending(xw.dpy)) { + xev = 1; + XNextEvent(xw.dpy, &ev); + if (XFilterEvent(&ev, None)) + continue; + if (handler[ev.type]) + (handler[ev.type])(&ev); + } + + /* + * To reduce flicker and tearing, when new content or event + * triggers drawing, we first wait a bit to ensure we got + * everything, and if nothing new arrives - we draw. + * We start with trying to wait minlatency ms. If more content + * arrives sooner, we retry with shorter and shorter periods, + * and eventually draw even without idle after maxlatency ms. + * Typically this results in low latency while interacting, + * maximum latency intervals during `cat huge.txt`, and perfect + * sync with periodic updates from animations/key-repeats/etc. + */ + if (FD_ISSET(ttyfd, &rfd) || xev) { + if (!drawing) { + trigger = now; + drawing = 1; + } + timeout = (maxlatency - TIMEDIFF(now, trigger)) \ + / maxlatency * minlatency; + if (timeout > 0) + continue; /* we have time, try to find idle */ + } + + /* idle detected or maxlatency exhausted -> draw */ + timeout = -1; + if (blinktimeout && tattrset(ATTR_BLINK)) { + timeout = blinktimeout - TIMEDIFF(now, lastblink); + if (timeout <= 0) { + if (-timeout > blinktimeout) /* start visible */ + win.mode |= MODE_BLINK; + win.mode ^= MODE_BLINK; + tsetdirtattr(ATTR_BLINK); + lastblink = now; + timeout = blinktimeout; + } + } + + draw(); + XFlush(xw.dpy); + drawing = 0; + } +} + +void +usage(void) +{ + die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid]" + " [[-e] command [args ...]]\n" + " %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid] -l line" + " [stty_args ...]\n", argv0, argv0); +} + +int +main(int argc, char *argv[]) +{ + xw.l = xw.t = 0; + xw.isfixed = False; + xsetcursor(cursorshape); + + ARGBEGIN { + case 'a': + allowaltscreen = 0; + break; + case 'A': + opt_alpha = EARGF(usage()); + break; + case 'c': + opt_class = EARGF(usage()); + break; + case 'e': + if (argc > 0) + --argc, ++argv; + goto run; + case 'f': + opt_font = EARGF(usage()); + break; + case 'g': + xw.gm = XParseGeometry(EARGF(usage()), + &xw.l, &xw.t, &cols, &rows); + break; + case 'i': + xw.isfixed = 1; + break; + case 'o': + opt_io = EARGF(usage()); + break; + case 'l': + opt_line = EARGF(usage()); + break; + case 'n': + opt_name = EARGF(usage()); + break; + case 't': + case 'T': + opt_title = EARGF(usage()); + break; + case 'w': + opt_embed = EARGF(usage()); + break; + case 'v': + die("%s " VERSION "\n", argv0); + break; + default: + usage(); + } ARGEND; + +run: + if (argc > 0) /* eat all remaining arguments */ + opt_cmd = argv; + + if (!opt_title) + opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; + + setlocale(LC_CTYPE, ""); + XSetLocaleModifiers(""); + cols = MAX(cols, 1); + rows = MAX(rows, 1); + tnew(cols, rows); + xinit(cols, rows); + xsetenv(); + selinit(); + run(); + + return 0; +} diff --git a/st/x.o b/st/x.o new file mode 100644 index 0000000000000000000000000000000000000000..d77bc9255ba1b20f0cb4cdd0458a8677d51751d1 GIT binary patch literal 75848 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 literal 0 HcmV?d00001 diff --git a/zshrc.txt b/zshrc.txt index ea6d0839..8464e2be 100644 --- a/zshrc.txt +++ b/zshrc.txt @@ -72,7 +72,7 @@ unsetopt BEEP # PROMPT='[%n@%m %1~]%F{green}(${vcs_info_msg_0_})%F{white}$ ' PROMPT='%B%F{blue}[%n@%M %B%F{white}'%1~'%B%F{blue}]$ %B%F{white}${vcs_info_msg_0_}%B%F{blue} -└─▶%B%F{white} ' +└─>%B%F{white} ' # function erase_history { local HISTSIZE=0; } # erase_history && hash -r