From 817c8b6cfbcb0ca4246670060270c10ba65c5d9f Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Tue, 9 Jul 2024 18:18:54 +0700 Subject: [PATCH] dwm default terminal is st and st added --- dwm/dwm/config.def.h | 4 +- dwm/dwm/config.h | 2 +- dwm/dwm/dwm | Bin 68376 -> 68376 bytes dwm/dwm/dwm.o | Bin 62256 -> 62256 bytes dwm/st/FAQ | 253 ++ dwm/st/LEGACY | 17 + dwm/st/LICENSE | 34 + dwm/st/Makefile | 57 + dwm/st/README.md | 93 + dwm/st/TODO | 28 + dwm/st/arg.h | 50 + dwm/st/config.def.h | 484 +++ dwm/st/config.def.h.orig | 482 +++ dwm/st/config.h | 484 +++ dwm/st/config.mk | 36 + .../0001-Apply-alpha-on-st-version-0.9.patch | 161 + .../0002-Apply-font2-on-st-version-0.9.patch | 167 + ...-Apply-colorscheme-on-st-version-0.9.patch | 88 + ...04-Apply-clipboard-on-st-version-0.9.patch | 24 + ...wide-glyph-support-on-st-version-0.9.patch | 206 ++ .../st-scrollback-ringbuffer-0.8.5.diff | 730 +++++ dwm/st/st | Bin 0 -> 109920 bytes dwm/st/st.1 | 177 ++ dwm/st/st.c | 2775 +++++++++++++++++ dwm/st/st.c.orig | 2656 ++++++++++++++++ dwm/st/st.h | 134 + dwm/st/st.info | 239 ++ dwm/st/st.o | Bin 0 -> 82000 bytes dwm/st/win.h | 41 + dwm/st/x.c | 2223 +++++++++++++ dwm/st/x.o | Bin 0 -> 79088 bytes 31 files changed, 11642 insertions(+), 3 deletions(-) create mode 100644 dwm/st/FAQ create mode 100644 dwm/st/LEGACY create mode 100644 dwm/st/LICENSE create mode 100644 dwm/st/Makefile create mode 100644 dwm/st/README.md create mode 100644 dwm/st/TODO create mode 100644 dwm/st/arg.h create mode 100644 dwm/st/config.def.h create mode 100644 dwm/st/config.def.h.orig create mode 100644 dwm/st/config.h create mode 100644 dwm/st/config.mk create mode 100644 dwm/st/patches/0001-Apply-alpha-on-st-version-0.9.patch create mode 100644 dwm/st/patches/0002-Apply-font2-on-st-version-0.9.patch create mode 100644 dwm/st/patches/0003-Apply-colorscheme-on-st-version-0.9.patch create mode 100644 dwm/st/patches/0004-Apply-clipboard-on-st-version-0.9.patch create mode 100644 dwm/st/patches/0005-Apply-wide-glyph-support-on-st-version-0.9.patch create mode 100644 dwm/st/patches/st-scrollback-ringbuffer-0.8.5.diff create mode 100755 dwm/st/st create mode 100644 dwm/st/st.1 create mode 100644 dwm/st/st.c create mode 100644 dwm/st/st.c.orig create mode 100644 dwm/st/st.h create mode 100644 dwm/st/st.info create mode 100644 dwm/st/st.o create mode 100644 dwm/st/win.h create mode 100644 dwm/st/x.c create mode 100644 dwm/st/x.o diff --git a/dwm/dwm/config.def.h b/dwm/dwm/config.def.h index cca1033..3bcb155 100644 --- a/dwm/dwm/config.def.h +++ b/dwm/dwm/config.def.h @@ -2,7 +2,7 @@ /* appearance */ static const unsigned int borderpx = 2; /* border pixel of windows */ -static const unsigned int gap = 5; /* gap pixel between windows */ +static const unsigned int gap = 5; /* gap pixel between windows */ static const unsigned int snap = 32; /* snap pixel */ static const int showbar = 1; /* 0 means no bar */ static const int topbar = 1; /* 0 means bottom bar */ @@ -68,7 +68,7 @@ static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, //--------------------------------------------------------------------------------------------------------- //-------------------------- applications --------------------------------------------------------- -static const char *termcmd[] = { "kitty", NULL }; +static const char *termcmd[] = { "st", NULL }; static const char *roficmd[] = { "/home/tola/dotfiles/rofi/launcher/launcher.sh", NULL }; static const char *powermenucmd[] = { "/home/tola/dotfiles/rofi/powermenu/powermenu.sh", NULL }; static const char *clipboardcmd[] = { "/home/tola/dotfiles/rofi/clipboard/clipboard.sh", NULL }; diff --git a/dwm/dwm/config.h b/dwm/dwm/config.h index d1459d1..3bcb155 100644 --- a/dwm/dwm/config.h +++ b/dwm/dwm/config.h @@ -68,7 +68,7 @@ static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, //--------------------------------------------------------------------------------------------------------- //-------------------------- applications --------------------------------------------------------- -static const char *termcmd[] = { "kitty", NULL }; +static const char *termcmd[] = { "st", NULL }; static const char *roficmd[] = { "/home/tola/dotfiles/rofi/launcher/launcher.sh", NULL }; static const char *powermenucmd[] = { "/home/tola/dotfiles/rofi/powermenu/powermenu.sh", NULL }; static const char *clipboardcmd[] = { "/home/tola/dotfiles/rofi/clipboard/clipboard.sh", NULL }; diff --git a/dwm/dwm/dwm b/dwm/dwm/dwm index 7228b9e37ff7d478ea6ce374f1651ca05c0bfc1b..bbc067602af2fe28c2f8d299f5897032df7cea47 100755 GIT binary patch delta 562 zcmbO+jb+9(mJJ=uBHX%stqFdNKl5_pPG{Ynzd|f#-qOu;m}BG_y(a%u(wFpG!T<&h zry(>4gklJq?5M2I7&f^ONJdRw2qfbsUsM*COopmgID@J_dorVnxa16|tO1&A?qow1 zamM_~o+|p1g-|sCXljZlPXwA!3YASjlP%wTP~|v_q$X5)#Z^@6jVCYE)StXSlVkD? zO$|n?$&6b1jCPX^fuz&qL?G!ld7{>S#>&ls+I5AD$2VV?6UNR|TrydE$<)nzmn>Fu z^n#juAs%8bELfnS!4L$Mf1Csn34`(v&W7-TL3COk7(@q8t1~78Nr}mfXXG8Tp{iiP zk_+YML+M!yA-Z8fQVbP`1w#4e#54AskuXmiL!~cVhG;W`@*9>y_*PJU29$3H4gklJt?5M2I7&*BRNXAZH2qY6HUsM*COoOUdID@J_Z!)8bxa4f8tO1&A!DK@f zamJ#_o+|p1B~Ud1XllwPPXwA!0hLWaldal(P~|uarw&wl#pHvU;*(csYA{+(zNo3s zXgirvOP|qkvLTRkoty|HJtt4p+Rs?CIZ(T)=O zj=oUCF2qCp0t*Xh5HN&7!gV7Q!?2ZqbR)9Q?AKvH5d;~9C!Jg6#I zSQJ3{MNoRyLWpiyIFv!fVLq?goOs5bGZN-gGpO{1%MfjrP=3Qw2;UaU&w%nBq5KFa V-xbRDfbu;j|GXLw3YTlgO#oqSbj|<( diff --git a/dwm/dwm/dwm.o b/dwm/dwm/dwm.o index 21dcbb7b1446440774490fd2ec86988cf28d4aaa..f41f455cbf46d21ffff8499f51740200700a07af 100644 GIT binary patch delta 293 zcmdn+jClhP{dZ+5F4@fDR>Q)~z`(G%!E47vM(4?vcl0IQK>|RKzzv}pfs_Ogdrxk> zqtEC+c_WYvp8OC60(MGiNN{%=uoO1pse7L8Jfx delta 292 zcmdn+jClhP{dZ-}&MYaZ+|1}!!?L--YsW-Jx5<`w^d-HR8NeWc8$vSzDG4CtyK;arTfCSQDK&RDgX^SwF?0P!3{Jpcdz diff --git a/dwm/st/FAQ b/dwm/st/FAQ new file mode 100644 index 0000000..6287a27 --- /dev/null +++ b/dwm/st/FAQ @@ -0,0 +1,253 @@ +## 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. + +As of 2022-09-05 this now seems to be finally fixed in libXft 2.3.5: +https://gitlab.freedesktop.org/xorg/lib/libxft/-/blob/libXft-2.3.5/NEWS diff --git a/dwm/st/LEGACY b/dwm/st/LEGACY new file mode 100644 index 0000000..bf28b1e --- /dev/null +++ b/dwm/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/dwm/st/LICENSE b/dwm/st/LICENSE new file mode 100644 index 0000000..3cbf420 --- /dev/null +++ b/dwm/st/LICENSE @@ -0,0 +1,34 @@ +MIT/X Consortium License + +© 2014-2022 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/dwm/st/Makefile b/dwm/st/Makefile new file mode 100644 index 0000000..4d49649 --- /dev/null +++ b/dwm/st/Makefile @@ -0,0 +1,57 @@ +# st - simple terminal +# See LICENSE file for copyright and license details. +.POSIX: + +include config.mk + +SRC = st.c x.c +OBJ = $(SRC:.c=.o) + +all: options st + +options: + @echo st build options: + @echo "CFLAGS = $(STCFLAGS)" + @echo "LDFLAGS = $(STLDFLAGS)" + @echo "CC = $(CC)" + +config.h: + cp config.def.h config.h + +.c.o: + $(CC) $(STCFLAGS) -c $< + +st.o: config.h st.h win.h +x.o: arg.h config.h st.h win.h + +$(OBJ): config.h config.mk + +st: $(OBJ) + $(CC) -o $@ $(OBJ) $(STLDFLAGS) + +clean: + rm -f st $(OBJ) st-$(VERSION).tar.gz config.h + +dist: clean + mkdir -p st-$(VERSION) + cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ + config.def.h st.info st.1 arg.h st.h win.h $(SRC)\ + st-$(VERSION) + tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz + rm -rf st-$(VERSION) + +install: st + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f st $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/st + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 + tic -sx st.info + @echo Please see the README file regarding the terminfo entry of st. + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/st + rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 + +.PHONY: all options clean dist install uninstall diff --git a/dwm/st/README.md b/dwm/st/README.md new file mode 100644 index 0000000..15e4ec1 --- /dev/null +++ b/dwm/st/README.md @@ -0,0 +1,93 @@ +# st - simple terminal + +st is a simple terminal emulator for X which sucks less. + +## Notes about this fork + +This is a fork of st, the original repo can be found here: [https://git.suckless.org/st/](https://git.suckless.org/st/) + +The original st webpage is here: [https://st.suckless.org/](https://st.suckless.org/) + +### Available patches + +* [Alpha](https://st.suckless.org/patches/alpha/) : This patch allows users to change the opacity of the background. Note that you need an X composite manager (e.g. compton, xcompmgr) to make this patch effective. +* [Clipboard](https://st.suckless.org/patches/clipboard/) : This trivial patch sets CLIPBOARD on selection. +* [font2](https://st.suckless.org/patches/font2/) : This patch allows to add spare font besides default. +* [Wide glyph](https://st.suckless.org/patches/glyph_wide_support/) : This patch allows to display the full glyph instead of it being cut off. See also [here](https://github.com/LukeSmithxyz/st/pull/349) + +#### Colorscheme patch +The colorscheme patch is a custom patch, it is not the ones from [colorscheme](https://st.suckless.org/patches/colorschemes/) +It is `solarized.dark` exported from https://terminal.sexy with a different bg and fg color. +Also the `defaultfg`, `defaultbg` and `defaultcs` are not `static`s, otherwise it won't compile. + +### Previous available patches + +* [Fix Keyboard Input](https://st.suckless.org/patches/fix_keyboard_input/) : Add a few previously undefined keys. +Removed as this was used for additional keys for zooming. + +### Applying/Removing Patches +Applying patches from this repository use: + +- applied all at once without committing: +``` +git apply patches/* +``` + +- applied as individual commits +``` +git am patches/* +``` + +Applying patches from https://st.suckless.org/patches/ use: +``` +# Add custom patch +patch -Np1 -i patches/custom.diff +or +patch < patch.diff + +# Remove st-alpha-201806-16-0.81 patch +patch -R patches/st-alpha-20180616-0.8.1.diff +``` + +## 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): + +```bash +make clean install +``` + +## Running st + +Start `st` from a launcher like dmenu or rofi. + +If you did not install st with make clean install, you must compile +the st terminfo entry with the following command: + +```bash +tic -sx st.info +``` + +See the man page for additional details. + +### Keyboard Shortcuts +Action | Key Combination +--- | --- +Copy | `ctrl` + `shift` + `c` +Paste | `ctrl` + `shift` + `v` +Zoom In | `ctrl` + `shift` + `PageUp` +Zoom Out | `ctrl` + `shift` + `PageDown` +Reset Zoom | `ctrl` + `shift` + `Home` + +## Credits + +* Forked from [https://st.suckless.org/](https://st.suckless.org/) +* Based on Aurélien APTEL aurelien.aptel@gmail.com bt source code. diff --git a/dwm/st/TODO b/dwm/st/TODO new file mode 100644 index 0000000..5f74cd5 --- /dev/null +++ b/dwm/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/dwm/st/arg.h b/dwm/st/arg.h new file mode 100644 index 0000000..a22e019 --- /dev/null +++ b/dwm/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/dwm/st/config.def.h b/dwm/st/config.def.h new file mode 100644 index 0000000..671ed01 --- /dev/null +++ b/dwm/st/config.def.h @@ -0,0 +1,484 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "JetbrainsMono Nerd Font Mono:pixelsize=18:antialias=true:autohint=true"; +/* Spare fonts */ +static char *font2[] = { + "Droid Sans Mono:style=Regular:pixelsize=14:antialias=true:autohint=true", + "Menlo:style=Regular:pixelsize=14:antialias=true:autohint=true", + "Inconsolata for Powerline:pixelsize=14:antialias=true:autohint=true", + "Hack Nerd Font Mono:pixelsize=14:antialias=true:autohint=true", +}; + +static int borderpx = 2; + +/* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e + * 2: scroll and/or utmp + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/sh"; +char *utmp = NULL; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = NULL; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +char *vtiden = "\033[?6c"; + +/* Kerning / character bounding-box multipliers */ +static float cwscale = 1.0; +static float chscale = 1.0; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +wchar_t *worddelimiters = L" "; + +/* selection timeouts (in milliseconds) */ +static unsigned int doubleclicktimeout = 300; +static unsigned int tripleclicktimeout = 600; + +/* alt screens */ +int allowaltscreen = 1; + +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 8; +static double maxlatency = 33; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 800; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 2; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "st-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +/* bg opacity */ +float alpha = 0.7; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + + /* 8 normal colors */ + [0] = "#002b36", /* black */ + [1] = "#dc322f", /* red */ + [2] = "#859900", /* green */ + [3] = "#b58900", /* yellow */ + [4] = "#268bd2", /* blue */ + [5] = "#6c71c4", /* magenta */ + [6] = "#2aa198", /* cyan */ + [7] = "#93a1a1", /* white */ + + /* 8 bright colors */ + [8] = "#657b83", /* black */ + [9] = "#dc322f", /* red */ + [10] = "#859900", /* green */ + [11] = "#b58900", /* yellow */ + [12] = "#268bd2", /* blue */ + [13] = "#6c71c4", /* magenta */ + [14] = "#2aa198", /* cyan */ + [15] = "#fdf6e3", /* white */ + + /* special colors */ + [256] = "#000000", /* background */ + [257] = "#ffffff", /* 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 = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 6; + +/* + * 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/dwm/st/config.def.h.orig b/dwm/st/config.def.h.orig new file mode 100644 index 0000000..1e92958 --- /dev/null +++ b/dwm/st/config.def.h.orig @@ -0,0 +1,482 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "Jetbrains Mono:pixelsize=16:antialias=true:autohint=true"; +/* Spare fonts */ +static char *font2[] = { + "Droid Sans Mono:style=Regular:pixelsize=14:antialias=true:autohint=true", + "Menlo:style=Regular:pixelsize=14:antialias=true:autohint=true", + "Inconsolata for Powerline:pixelsize=14:antialias=true:autohint=true", + "Hack Nerd Font Mono:pixelsize=14:antialias=true:autohint=true", +}; + +static int borderpx = 2; + +/* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e + * 2: scroll and/or utmp + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/sh"; +char *utmp = NULL; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = NULL; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +char *vtiden = "\033[?6c"; + +/* Kerning / character bounding-box multipliers */ +static float cwscale = 1.0; +static float chscale = 1.0; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +wchar_t *worddelimiters = L" "; + +/* selection timeouts (in milliseconds) */ +static unsigned int doubleclicktimeout = 300; +static unsigned int tripleclicktimeout = 600; + +/* alt screens */ +int allowaltscreen = 1; + +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 8; +static double maxlatency = 33; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 800; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 2; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "st-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +/* bg opacity */ +float alpha = 0.7; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + + /* 8 normal colors */ + [0] = "#002b36", /* black */ + [1] = "#dc322f", /* red */ + [2] = "#859900", /* green */ + [3] = "#b58900", /* yellow */ + [4] = "#268bd2", /* blue */ + [5] = "#6c71c4", /* magenta */ + [6] = "#2aa198", /* cyan */ + [7] = "#93a1a1", /* white */ + + /* 8 bright colors */ + [8] = "#657b83", /* black */ + [9] = "#dc322f", /* red */ + [10] = "#859900", /* green */ + [11] = "#b58900", /* yellow */ + [12] = "#268bd2", /* blue */ + [13] = "#6c71c4", /* magenta */ + [14] = "#2aa198", /* cyan */ + [15] = "#fdf6e3", /* white */ + + /* special colors */ + [256] = "#000000", /* background */ + [257] = "#ffffff", /* 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 = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 6; + +/* + * 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} }, +}; + +/* + * 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/dwm/st/config.h b/dwm/st/config.h new file mode 100644 index 0000000..671ed01 --- /dev/null +++ b/dwm/st/config.h @@ -0,0 +1,484 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "JetbrainsMono Nerd Font Mono:pixelsize=18:antialias=true:autohint=true"; +/* Spare fonts */ +static char *font2[] = { + "Droid Sans Mono:style=Regular:pixelsize=14:antialias=true:autohint=true", + "Menlo:style=Regular:pixelsize=14:antialias=true:autohint=true", + "Inconsolata for Powerline:pixelsize=14:antialias=true:autohint=true", + "Hack Nerd Font Mono:pixelsize=14:antialias=true:autohint=true", +}; + +static int borderpx = 2; + +/* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e + * 2: scroll and/or utmp + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/sh"; +char *utmp = NULL; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = NULL; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +char *vtiden = "\033[?6c"; + +/* Kerning / character bounding-box multipliers */ +static float cwscale = 1.0; +static float chscale = 1.0; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +wchar_t *worddelimiters = L" "; + +/* selection timeouts (in milliseconds) */ +static unsigned int doubleclicktimeout = 300; +static unsigned int tripleclicktimeout = 600; + +/* alt screens */ +int allowaltscreen = 1; + +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 8; +static double maxlatency = 33; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 800; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 2; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "st-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +/* bg opacity */ +float alpha = 0.7; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + + /* 8 normal colors */ + [0] = "#002b36", /* black */ + [1] = "#dc322f", /* red */ + [2] = "#859900", /* green */ + [3] = "#b58900", /* yellow */ + [4] = "#268bd2", /* blue */ + [5] = "#6c71c4", /* magenta */ + [6] = "#2aa198", /* cyan */ + [7] = "#93a1a1", /* white */ + + /* 8 bright colors */ + [8] = "#657b83", /* black */ + [9] = "#dc322f", /* red */ + [10] = "#859900", /* green */ + [11] = "#b58900", /* yellow */ + [12] = "#268bd2", /* blue */ + [13] = "#6c71c4", /* magenta */ + [14] = "#2aa198", /* cyan */ + [15] = "#fdf6e3", /* white */ + + /* special colors */ + [256] = "#000000", /* background */ + [257] = "#ffffff", /* 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 = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 6; + +/* + * 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/dwm/st/config.mk b/dwm/st/config.mk new file mode 100644 index 0000000..47c615e --- /dev/null +++ b/dwm/st/config.mk @@ -0,0 +1,36 @@ +# st version +VERSION = 0.9 + +# 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` +#MANPREFIX = ${PREFIX}/man + +# compiler and linker +# CC = c99 diff --git a/dwm/st/patches/0001-Apply-alpha-on-st-version-0.9.patch b/dwm/st/patches/0001-Apply-alpha-on-st-version-0.9.patch new file mode 100644 index 0000000..3ae019a --- /dev/null +++ b/dwm/st/patches/0001-Apply-alpha-on-st-version-0.9.patch @@ -0,0 +1,161 @@ +From 307b030a02f8bfda47600acc881fdd4876767204 Mon Sep 17 00:00:00 2001 +From: Shourai <10200748+Shourai@users.noreply.github.com> +Date: Tue, 8 Nov 2022 19:30:52 +0100 +Subject: Apply alpha on st version 0.9 + +--- + config.def.h | 3 +++ + config.mk | 2 +- + st.h | 1 + + x.c | 40 ++++++++++++++++++++++++++++++---------- + 4 files changed, 35 insertions(+), 11 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 91ab8ca..38e5154 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.7; ++ + /* Terminal colors (16 first used in escape sequence) */ + static const char *colorname[] = { + /* 8 normal colors */ +diff --git a/config.mk b/config.mk +index 1e306f8..47c615e 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 fd3b0d8..9f91e2a 100644 +--- a/st.h ++++ b/st.h +@@ -124,3 +124,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 aa09997..8a65792 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; +@@ -752,7 +754,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); + +@@ -812,6 +814,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; + } + +@@ -1134,11 +1143,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()) +@@ -1148,7 +1169,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 */ +@@ -1168,19 +1189,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); + +@@ -2038,6 +2055,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; +-- +2.38.1 + diff --git a/dwm/st/patches/0002-Apply-font2-on-st-version-0.9.patch b/dwm/st/patches/0002-Apply-font2-on-st-version-0.9.patch new file mode 100644 index 0000000..16bda74 --- /dev/null +++ b/dwm/st/patches/0002-Apply-font2-on-st-version-0.9.patch @@ -0,0 +1,167 @@ +From 2898f59f9b446b9ca748e544e61407e832ce5361 Mon Sep 17 00:00:00 2001 +From: Shourai <10200748+Shourai@users.noreply.github.com> +Date: Tue, 8 Nov 2022 19:35:52 +0100 +Subject: Apply font2 on st version 0.9 + +--- + config.def.h | 10 ++++- + x.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 110 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index 38e5154..2ac0781 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,7 +5,15 @@ + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +-static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; ++static char *font = "Jetbrains Mono:pixelsize=14:antialias=true:autohint=true"; ++/* Spare fonts */ ++static char *font2[] = { ++ "Droid Sans Mono:style=Regular:pixelsize=14:antialias=true:autohint=true", ++ "Menlo:style=Regular:pixelsize=14:antialias=true:autohint=true", ++ "Inconsolata for Powerline:pixelsize=14:antialias=true:autohint=true", ++ "Hack Nerd Font Mono:pixelsize=14:antialias=true:autohint=true", ++}; ++ + static int borderpx = 2; + + /* +diff --git a/x.c b/x.c +index 8a65792..db17dcf 100644 +--- a/x.c ++++ b/x.c +@@ -158,6 +158,8 @@ static void xhints(void); + static int xloadcolor(int, const char *, Color *); + static int xloadfont(Font *, FcPattern *); + static void xloadfonts(const char *, double); ++static int xloadsparefont(FcPattern *, int); ++static void xloadsparefonts(void); + static void xunloadfont(Font *); + static void xunloadfonts(void); + static void xsetenv(void); +@@ -308,6 +310,7 @@ zoomabs(const Arg *arg) + { + xunloadfonts(); + xloadfonts(usedfont, arg->f); ++ xloadsparefonts(); + cresize(0, 0); + redraw(); + xhints(); +@@ -1059,6 +1062,101 @@ xloadfonts(const char *fontstr, double fontsize) + FcPatternDestroy(pattern); + } + ++int ++xloadsparefont(FcPattern *pattern, int flags) ++{ ++ FcPattern *match; ++ FcResult result; ++ ++ match = FcFontMatch(NULL, pattern, &result); ++ if (!match) { ++ return 1; ++ } ++ ++ if (!(frc[frclen].font = XftFontOpenPattern(xw.dpy, match))) { ++ FcPatternDestroy(match); ++ return 1; ++ } ++ ++ frc[frclen].flags = flags; ++ /* Believe U+0000 glyph will present in each default font */ ++ frc[frclen].unicodep = 0; ++ frclen++; ++ ++ return 0; ++} ++ ++void ++xloadsparefonts(void) ++{ ++ FcPattern *pattern; ++ double sizeshift, fontval; ++ int fc; ++ char **fp; ++ ++ if (frclen != 0) ++ die("can't embed spare fonts. cache isn't empty"); ++ ++ /* Calculate count of spare fonts */ ++ fc = sizeof(font2) / sizeof(*font2); ++ if (fc == 0) ++ return; ++ ++ /* Allocate memory for cache entries. */ ++ if (frccap < 4 * fc) { ++ frccap += 4 * fc - frccap; ++ frc = xrealloc(frc, frccap * sizeof(Fontcache)); ++ } ++ ++ for (fp = font2; fp - font2 < fc; ++fp) { ++ ++ if (**fp == '-') ++ pattern = XftXlfdParse(*fp, False, False); ++ else ++ pattern = FcNameParse((FcChar8 *)*fp); ++ ++ if (!pattern) ++ die("can't open spare font %s\n", *fp); ++ ++ if (defaultfontsize > 0) { ++ sizeshift = usedfontsize - defaultfontsize; ++ if (sizeshift != 0 && ++ FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == ++ FcResultMatch) { ++ fontval += sizeshift; ++ FcPatternDel(pattern, FC_PIXEL_SIZE); ++ FcPatternDel(pattern, FC_SIZE); ++ FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontval); ++ } ++ } ++ ++ FcPatternAddBool(pattern, FC_SCALABLE, 1); ++ ++ FcConfigSubstitute(NULL, pattern, FcMatchPattern); ++ XftDefaultSubstitute(xw.dpy, xw.scr, pattern); ++ ++ if (xloadsparefont(pattern, FRC_NORMAL)) ++ die("can't open spare font %s\n", *fp); ++ ++ FcPatternDel(pattern, FC_SLANT); ++ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); ++ if (xloadsparefont(pattern, FRC_ITALIC)) ++ die("can't open spare font %s\n", *fp); ++ ++ FcPatternDel(pattern, FC_WEIGHT); ++ FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); ++ if (xloadsparefont(pattern, FRC_ITALICBOLD)) ++ die("can't open spare font %s\n", *fp); ++ ++ FcPatternDel(pattern, FC_SLANT); ++ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); ++ if (xloadsparefont(pattern, FRC_BOLD)) ++ die("can't open spare font %s\n", *fp); ++ ++ FcPatternDestroy(pattern); ++ } ++} ++ + void + xunloadfont(Font *f) + { +@@ -1168,6 +1266,9 @@ xinit(int cols, int rows) + usedfont = (opt_font == NULL)? font : opt_font; + xloadfonts(usedfont, 0); + ++ /* spare fonts */ ++ xloadsparefonts(); ++ + /* colors */ + xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); + xloadcols(); +-- +2.38.1 + diff --git a/dwm/st/patches/0003-Apply-colorscheme-on-st-version-0.9.patch b/dwm/st/patches/0003-Apply-colorscheme-on-st-version-0.9.patch new file mode 100644 index 0000000..1a18754 --- /dev/null +++ b/dwm/st/patches/0003-Apply-colorscheme-on-st-version-0.9.patch @@ -0,0 +1,88 @@ +From 66e82b1d4db5c6ad7d62f809c38ff6f27773238a Mon Sep 17 00:00:00 2001 +From: Shourai <10200748+Shourai@users.noreply.github.com> +Date: Tue, 8 Nov 2022 19:45:28 +0100 +Subject: Apply colorscheme on st version 0.9 + +Apply colorscheme on st version 0.9 +--- + config.def.h | 57 +++++++++++++++++++++++++--------------------------- + 1 file changed, 27 insertions(+), 30 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 2ac0781..21a5b0a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -106,33 +106,30 @@ float alpha = 0.7; + + /* 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 */ ++ ++ /* 8 normal colors */ ++ [0] = "#002b36", /* black */ ++ [1] = "#dc322f", /* red */ ++ [2] = "#859900", /* green */ ++ [3] = "#b58900", /* yellow */ ++ [4] = "#268bd2", /* blue */ ++ [5] = "#6c71c4", /* magenta */ ++ [6] = "#2aa198", /* cyan */ ++ [7] = "#93a1a1", /* white */ ++ ++ /* 8 bright colors */ ++ [8] = "#657b83", /* black */ ++ [9] = "#dc322f", /* red */ ++ [10] = "#859900", /* green */ ++ [11] = "#b58900", /* yellow */ ++ [12] = "#268bd2", /* blue */ ++ [13] = "#6c71c4", /* magenta */ ++ [14] = "#2aa198", /* cyan */ ++ [15] = "#fdf6e3", /* white */ ++ ++ /* special colors */ ++ [256] = "#272727", /* background */ ++ [257] = "#ffffff", /* foreground */ + }; + + +@@ -140,9 +137,9 @@ static const char *colorname[] = { + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +-unsigned int defaultfg = 258; +-unsigned int defaultbg = 259; +-unsigned int defaultcs = 256; ++unsigned int defaultfg = 257; ++unsigned int defaultbg = 256; ++unsigned int defaultcs = 257; + static unsigned int defaultrcs = 257; + + /* +-- +2.38.1 + diff --git a/dwm/st/patches/0004-Apply-clipboard-on-st-version-0.9.patch b/dwm/st/patches/0004-Apply-clipboard-on-st-version-0.9.patch new file mode 100644 index 0000000..8ed2ae3 --- /dev/null +++ b/dwm/st/patches/0004-Apply-clipboard-on-st-version-0.9.patch @@ -0,0 +1,24 @@ +From a248b1eedb9de154751faf9760f15edca141c353 Mon Sep 17 00:00:00 2001 +From: Shourai <10200748+Shourai@users.noreply.github.com> +Date: Tue, 8 Nov 2022 19:49:03 +0100 +Subject: Apply clipboard on st version 0.9 + +--- + x.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/x.c b/x.c +index db17dcf..c7ef7fc 100644 +--- a/x.c ++++ b/x.c +@@ -691,6 +691,7 @@ setsel(char *str, Time t) + XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); + if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) + selclear(); ++ clipcopy(NULL); + } + + void +-- +2.38.1 + diff --git a/dwm/st/patches/0005-Apply-wide-glyph-support-on-st-version-0.9.patch b/dwm/st/patches/0005-Apply-wide-glyph-support-on-st-version-0.9.patch new file mode 100644 index 0000000..74ded71 --- /dev/null +++ b/dwm/st/patches/0005-Apply-wide-glyph-support-on-st-version-0.9.patch @@ -0,0 +1,206 @@ +From fbe883a76d35524c678ab4c0e5e89fb606a4fe71 Mon Sep 17 00:00:00 2001 +From: Shourai <10200748+Shourai@users.noreply.github.com> +Date: Fri, 10 Nov 2023 11:14:12 +0100 +Subject: [PATCH] Apply wide glyph support on st version 0.9 + +--- + st.h | 6 +++ + x.c | 134 +++++++++++++++++++++++++++++------------------------------ + 2 files changed, 73 insertions(+), 67 deletions(-) + +diff --git a/st.h b/st.h +index 9f91e2a..0a3fa6d 100644 +--- a/st.h ++++ b/st.h +@@ -36,6 +36,12 @@ enum glyph_attribute { + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, + }; + ++enum drawing_mode { ++ DRAW_NONE = 0, ++ DRAW_BG = 1 << 0, ++ DRAW_FG = 1 << 1, ++}; ++ + enum selection_mode { + SEL_IDLE = 0, + SEL_EMPTY = 1, +diff --git a/x.c b/x.c +index c7ef7fc..48adbb1 100644 +--- a/x.c ++++ b/x.c +@@ -143,7 +143,7 @@ typedef struct { + + static inline ushort sixd_to_16bit(int); + static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); +-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); ++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int); + static void xdrawglyph(Glyph, int, int); + static void xclear(int, int, int, int); + static int xgeommasktogravity(int); +@@ -1491,7 +1491,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x + } + + void +-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) ++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int dmode) + { + int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); + int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, +@@ -1582,47 +1582,40 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i + 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 * chscale + 1, +- width, 1); +- } +- +- if (base.mode & ATTR_STRUCK) { +- XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3, +- width, 1); +- } +- +- /* Reset clip to none. */ +- XftDrawSetClip(xw.draw, 0); ++ if (dmode & DRAW_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); ++ /* Fill the background */ ++ XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); ++ } ++ ++ if (dmode & DRAW_FG) { ++ /* 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); ++ } ++ } + } + + void +@@ -1632,7 +1625,7 @@ xdrawglyph(Glyph g, int x, int y) + XftGlyphFontSpec spec; + + numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); +- xdrawglyphfontspecs(&spec, g, numspecs, x, y); ++ xdrawglyphfontspecs(&spec, g, numspecs, x, y, DRAW_BG | DRAW_FG); + } + + void +@@ -1767,32 +1760,39 @@ xstartdraw(void) + void + xdrawline(Line line, int x1, int y1, int x2) + { +- int i, x, ox, numspecs; ++ int i, x, ox, numspecs, numspecs_cached; + 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; ++ XftGlyphFontSpec *specs; ++ ++ numspecs_cached = xmakeglyphfontspecs(xw.specbuf, &line[x1], x2 - x1, x1, y1); ++ ++ /* Draw line in 2 passes: background and foreground. This way wide glyphs ++ won't get truncated (#223) */ ++ for (int dmode = DRAW_BG; dmode <= DRAW_FG; dmode <<= 1) { ++ specs = xw.specbuf; ++ numspecs = numspecs_cached; ++ i = ox = 0; ++ for (x = x1; x < x2 && i < numspecs; x++) { ++ new = line[x]; ++ if (new.mode == ATTR_WDUMMY) ++ continue; ++ if (selected(x, y1)) ++ new.mode ^= ATTR_REVERSE; ++ if (i > 0 && ATTRCMP(base, new)) { ++ xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); ++ specs += i; ++ numspecs -= i; ++ i = 0; ++ } ++ if (i == 0) { ++ ox = x; ++ base = new; ++ } ++ i++; + } +- i++; ++ if (i > 0) ++ xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); + } +- if (i > 0) +- xdrawglyphfontspecs(specs, base, i, ox, y1); + } + + void +-- +2.42.1 + diff --git a/dwm/st/patches/st-scrollback-ringbuffer-0.8.5.diff b/dwm/st/patches/st-scrollback-ringbuffer-0.8.5.diff new file mode 100644 index 0000000..7c060c5 --- /dev/null +++ b/dwm/st/patches/st-scrollback-ringbuffer-0.8.5.diff @@ -0,0 +1,730 @@ +commit 0663bdf11a409961da5b1120741a69814da8ce65 +Author: Timo Röhling +Date: Tue Nov 23 19:45:33 2021 +0100 + + Terminal scrollback with ring buffer + + This patch adds a ring buffer for scrollback to the terminal. The + advantage of using a ring buffer is that the common case, scrolling with + no static screen content, can be achieved very efficiently by + incrementing and decrementing the starting line (modulo buffer size). + + The scrollback buffer is limited to HISTSIZE lines in order to bound + memory usage. As the lines are allocated on demand, it is possible to + implement unlimited scrollback with few changes. If the terminal is + reset, the scroll back buffer is reset, too. + +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..f9e24ba 100644 +--- a/st.c ++++ b/st.c +@@ -43,6 +43,10 @@ + #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) + #define ISDELIM(u) (u && wcschr(worddelimiters, u)) + ++#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)] ++#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size) ++#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)]) ++ + enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, +@@ -109,12 +113,21 @@ typedef struct { + int alt; + } Selection; + ++/* Screen lines */ ++typedef struct { ++ Line* buffer; /* ring buffer */ ++ int size; /* size of buffer */ ++ int cur; /* start of active screen */ ++ int off; /* scrollback line offset */ ++ TCursor sc; /* saved cursor */ ++} LineBuffer; ++ + /* Internal representation of the screen */ + typedef struct { + int row; /* nb row */ + int col; /* nb col */ +- Line *line; /* screen */ +- Line *alt; /* alternate screen */ ++ LineBuffer screen[2]; /* screen and alternate screen */ ++ int linelen; /* allocated line length */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ +@@ -202,6 +215,8 @@ static void tdeftran(char); + static void tstrsequence(uchar); + + static void drawregion(int, int, int, int); ++static void clearline(Line, Glyph, int, int); ++static Line ensureline(Line); + + static void selnormalize(void); + static void selscroll(int, int); +@@ -415,11 +430,12 @@ int + tlinelen(int y) + { + int i = term.col; ++ Line line = TLINE(y); + +- if (term.line[y][i - 1].mode & ATTR_WRAP) ++ if (line[i - 1].mode & ATTR_WRAP) + return i; + +- while (i > 0 && term.line[y][i - 1].u == ' ') ++ while (i > 0 && line[i - 1].u == ' ') + --i; + + return i; +@@ -528,7 +544,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 +559,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 +587,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 +625,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; + +@@ -956,12 +972,15 @@ int + tattrset(int attr) + { + int i, j; ++ int y = TLINEOFFSET(0); + + for (i = 0; i < term.row-1; i++) { ++ Line line = TSCREEN.buffer[y]; + for (j = 0; j < term.col-1; j++) { +- if (term.line[i][j].mode & attr) ++ if (line[j].mode & attr) + return 1; + } ++ y = (y+1) % TSCREEN.size; + } + + return 0; +@@ -983,14 +1002,17 @@ void + tsetdirtattr(int attr) + { + int i, j; ++ int y = TLINEOFFSET(0); + + for (i = 0; i < term.row-1; i++) { ++ Line line = TSCREEN.buffer[y]; + for (j = 0; j < term.col-1; j++) { +- if (term.line[i][j].mode & attr) { ++ if (line[j].mode & attr) { + tsetdirt(i, i); + break; + } + } ++ y = (y+1) % TSCREEN.size; + } + } + +@@ -1003,27 +1025,19 @@ tfulldirt(void) + void + tcursor(int mode) + { +- static TCursor c[2]; +- int alt = IS_SET(MODE_ALTSCREEN); +- + if (mode == CURSOR_SAVE) { +- c[alt] = term.c; ++ TSCREEN.sc = term.c; + } else if (mode == CURSOR_LOAD) { +- term.c = c[alt]; +- tmoveto(c[alt].x, c[alt].y); ++ term.c = TSCREEN.sc; ++ tmoveto(term.c.x, term.c.y); + } + } + + void + treset(void) + { +- uint i; +- +- term.c = (TCursor){{ +- .mode = ATTR_NULL, +- .fg = defaultfg, +- .bg = defaultbg +- }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; ++ int i, j; ++ Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) +@@ -1035,17 +1049,37 @@ treset(void) + 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(); ++ term.screen[i].sc = (TCursor){{ ++ .fg = defaultfg, ++ .bg = defaultbg ++ }}; ++ term.screen[i].cur = 0; ++ term.screen[i].off = 0; ++ for (j = 0; j < term.row; ++j) { ++ if (term.col != term.linelen) ++ term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph)); ++ clearline(term.screen[i].buffer[j], g, 0, term.col); ++ } ++ for (j = term.row; j < term.screen[i].size; ++j) { ++ free(term.screen[i].buffer[j]); ++ term.screen[i].buffer[j] = NULL; ++ } + } ++ tcursor(CURSOR_LOAD); ++ term.linelen = term.col; ++ tfulldirt(); + } + + void + tnew(int col, int row) + { +- term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; ++ int i; ++ term = (Term){}; ++ term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line)); ++ term.screen[0].size = HISTSIZE; ++ term.screen[1].buffer = NULL; ++ for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL; ++ + tresize(col, row); + treset(); + } +@@ -1053,14 +1087,42 @@ tnew(int col, int row) + void + tswapscreen(void) + { +- Line *tmp = term.line; +- +- term.line = term.alt; +- term.alt = tmp; + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); + } + ++void ++kscrollup(const Arg *a) ++{ ++ int n = a->i; ++ ++ if (IS_SET(MODE_ALTSCREEN)) ++ return; ++ ++ if (n < 0) n = (-n) * term.row; ++ if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off; ++ while (!TLINE(-n)) --n; ++ TSCREEN.off += n; ++ selscroll(0, n); ++ tfulldirt(); ++} ++ ++void ++kscrolldown(const Arg *a) ++{ ++ ++ int n = a->i; ++ ++ if (IS_SET(MODE_ALTSCREEN)) ++ return; ++ ++ if (n < 0) n = (-n) * term.row; ++ if (n > TSCREEN.off) n = TSCREEN.off; ++ TSCREEN.off -= n; ++ selscroll(0, -n); ++ tfulldirt(); ++} ++ + void + tscrolldown(int orig, int n) + { +@@ -1069,15 +1131,29 @@ tscrolldown(int orig, int n) + + LIMIT(n, 0, term.bot-orig+1); + +- tsetdirt(orig, term.bot-n); +- tclearregion(0, term.bot-n+1, term.col-1, term.bot); ++ /* Ensure that lines are allocated */ ++ for (i = -n; i < 0; i++) { ++ TLINE(i) = ensureline(TLINE(i)); ++ } + +- for (i = term.bot; i >= orig+n; i--) { +- temp = term.line[i]; +- term.line[i] = term.line[i-n]; +- term.line[i-n] = temp; ++ /* Shift non-scrolling areas in ring buffer */ ++ for (i = term.bot+1; i < term.row; i++) { ++ temp = TLINE(i); ++ TLINE(i) = TLINE(i-n); ++ TLINE(i-n) = temp; ++ } ++ for (i = 0; i < orig; i++) { ++ temp = TLINE(i); ++ TLINE(i) = TLINE(i-n); ++ TLINE(i-n) = temp; + } + ++ /* Scroll buffer */ ++ TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size; ++ /* Clear lines that have entered the view */ ++ tclearregion(0, orig, term.linelen-1, orig+n-1); ++ /* Redraw portion of the screen that has scrolled */ ++ tsetdirt(orig+n-1, term.bot); + selscroll(orig, n); + } + +@@ -1089,15 +1165,29 @@ tscrollup(int orig, int n) + + LIMIT(n, 0, term.bot-orig+1); + +- tclearregion(0, orig, term.col-1, orig+n-1); +- tsetdirt(orig+n, term.bot); ++ /* Ensure that lines are allocated */ ++ for (i = term.row; i < term.row + n; i++) { ++ TLINE(i) = ensureline(TLINE(i)); ++ } + +- for (i = orig; i <= term.bot-n; i++) { +- temp = term.line[i]; +- term.line[i] = term.line[i+n]; +- term.line[i+n] = temp; ++ /* Shift non-scrolling areas in ring buffer */ ++ for (i = orig-1; i >= 0; i--) { ++ temp = TLINE(i); ++ TLINE(i) = TLINE(i+n); ++ TLINE(i+n) = temp; ++ } ++ for (i = term.row-1; i >term.bot; i--) { ++ temp = TLINE(i); ++ TLINE(i) = TLINE(i+n); ++ TLINE(i+n) = temp; + } + ++ /* Scroll buffer */ ++ TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size; ++ /* Clear lines that have entered the view */ ++ tclearregion(0, term.bot-n+1, term.linelen-1, term.bot); ++ /* Redraw portion of the screen that has scrolled */ ++ tsetdirt(orig, term.bot-n+1); + selscroll(orig, -n); + } + +@@ -1201,6 +1291,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ + }; ++ Line line = TLINE(y); + + /* + * The table is proudly stolen from rxvt. +@@ -1209,25 +1300,25 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) + 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 (line[x].mode & ATTR_WIDE) { + if (x+1 < term.col) { +- term.line[y][x+1].u = ' '; +- term.line[y][x+1].mode &= ~ATTR_WDUMMY; ++ line[x+1].u = ' '; ++ line[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; ++ } else if (line[x].mode & ATTR_WDUMMY) { ++ line[x-1].u = ' '; ++ line[x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; +- term.line[y][x] = *attr; +- term.line[y][x].u = u; ++ line[x] = *attr; ++ line[x].u = u; + } + + void + tclearregion(int x1, int y1, int x2, int y2) + { +- int x, y, temp; ++ int x, y, L, S, temp; + Glyph *gp; + + if (x1 > x2) +@@ -1235,15 +1326,16 @@ tclearregion(int x1, int y1, int x2, int y2) + if (y1 > y2) + temp = y1, y1 = y2, y2 = temp; + +- LIMIT(x1, 0, term.col-1); +- LIMIT(x2, 0, term.col-1); ++ LIMIT(x1, 0, term.linelen-1); ++ LIMIT(x2, 0, term.linelen-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + ++ L = TLINEOFFSET(y1); + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { +- gp = &term.line[y][x]; ++ gp = &TSCREEN.buffer[L][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; +@@ -1251,6 +1343,7 @@ tclearregion(int x1, int y1, int x2, int y2) + gp->mode = 0; + gp->u = ' '; + } ++ L = (L + 1) % TSCREEN.size; + } + } + +@@ -1265,7 +1358,7 @@ tdeletechar(int n) + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; +- line = term.line[term.c.y]; ++ line = TLINE(term.c.y); + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +@@ -1282,7 +1375,7 @@ tinsertblank(int n) + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; +- line = term.line[term.c.y]; ++ line = TLINE(term.c.y); + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +@@ -2103,7 +2196,7 @@ tdumpline(int n) + char buf[UTF_SIZ]; + const Glyph *bp, *end; + +- bp = &term.line[n][0]; ++ bp = &TLINE(n)[0]; + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if (bp != end || bp->u != ' ') { + for ( ; bp <= end; ++bp) +@@ -2486,11 +2579,11 @@ check_control_code: + if (selected(term.c.x, term.c.y)) + selclear(); + +- gp = &term.line[term.c.y][term.c.x]; ++ gp = &TLINE(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]; ++ gp = &TLINE(term.c.y)[term.c.x]; + } + + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) +@@ -2498,7 +2591,7 @@ check_control_code: + + if (term.c.x+width > term.col) { + tnewline(1); +- gp = &term.line[term.c.y][term.c.x]; ++ gp = &TLINE(term.c.y)[term.c.x]; + } + + tsetchar(u, &term.c.attr, term.c.x, term.c.y); +@@ -2529,6 +2622,11 @@ twrite(const char *buf, int buflen, int show_ctrl) + Rune u; + int n; + ++ if (TSCREEN.off) { ++ TSCREEN.off = 0; ++ tfulldirt(); ++ } ++ + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8)) { + /* process a complete utf8 char */ +@@ -2555,56 +2653,85 @@ twrite(const char *buf, int buflen, int show_ctrl) + } + + void +-tresize(int col, int row) ++clearline(Line line, Glyph g, int x, int xend) + { + int i; ++ g.mode = 0; ++ g.u = ' '; ++ for (i = x; i < xend; ++i) { ++ line[i] = g; ++ } ++} ++ ++Line ++ensureline(Line line) ++{ ++ if (!line) { ++ line = xmalloc(term.linelen * sizeof(Glyph)); ++ } ++ return line; ++} ++ ++void ++tresize(int col, int row) ++{ ++ int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); ++ int linelen = MAX(col, term.linelen); + int *bp; +- TCursor c; + +- if (col < 1 || row < 1) { ++ if (col < 1 || row < 1 || row > HISTSIZE) { + 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]); ++ /* Shift buffer to keep the cursor where we expect it */ ++ if (row <= term.c.y) { ++ term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size; ++ } ++ ++ /* Resize and clear line buffers as needed */ ++ if (linelen > term.linelen) { ++ for (i = 0; i < term.screen[0].size; ++i) { ++ if (term.screen[0].buffer[i]) { ++ term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph)); ++ clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen); ++ } ++ } ++ for (i = 0; i < minrow; ++i) { ++ term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph)); ++ clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen); ++ } + } +- /* 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)); ++ /* Allocate all visible lines for regular line buffer */ ++ for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size) ++ { ++ if (!term.screen[0].buffer[j]) { ++ term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph)); ++ } ++ if (i >= term.row) { ++ clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen); ++ } + } +- for (i += row; i < term.row; i++) { +- free(term.line[i]); +- free(term.alt[i]); ++ /* Resize alt screen */ ++ term.screen[1].cur = 0; ++ term.screen[1].size = row; ++ for (i = row; i < term.row; ++i) { ++ free(term.screen[1].buffer[i]); ++ } ++ term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line)); ++ for (i = term.row; i < row; ++i) { ++ term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph)); ++ clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen); + } + + /* resize to new height */ +- term.line = xrealloc(term.line, row * sizeof(Line)); +- term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + +- /* resize each row to new width, zero-pad if needed */ +- for (i = 0; i < minrow; i++) { +- term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); +- term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); +- } +- +- /* allocate any new rows */ +- for (/* i = minrow */; i < row; i++) { +- term.line[i] = xmalloc(col * sizeof(Glyph)); +- term.alt[i] = xmalloc(col * sizeof(Glyph)); +- } ++ /* fix tabstops */ + if (col > term.col) { + bp = term.tabs + term.col; + +@@ -2614,26 +2741,16 @@ tresize(int col, int row) + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } ++ + /* update terminal size */ + term.col = col; + term.row = row; ++ term.linelen = linelen; + /* 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; ++ tfulldirt(); + } + + void +@@ -2645,14 +2762,15 @@ resettitle(void) + void + drawregion(int x1, int y1, int x2, int y2) + { +- int y; ++ int y, L; + ++ L = TLINEOFFSET(y1); + for (y = y1; y < y2; y++) { +- if (!term.dirty[y]) +- continue; +- +- term.dirty[y] = 0; +- xdrawline(term.line[y], x1, y, x2); ++ if (term.dirty[y]) { ++ term.dirty[y] = 0; ++ xdrawline(TSCREEN.buffer[L], x1, y, x2); ++ } ++ L = (L + 1) % TSCREEN.size; + } + } + +@@ -2667,14 +2785,15 @@ draw(void) + /* 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) ++ if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY) + term.ocx--; +- if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) ++ if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); +- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], +- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); ++ if (TSCREEN.off == 0) ++ xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], ++ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); +diff --git a/st.h b/st.h +index 519b9bd..b48e810 100644 +--- a/st.h ++++ b/st.h +@@ -19,6 +19,7 @@ + + #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) + #define IS_TRUECOL(x) (1 << 24 & (x)) ++#define HISTSIZE 2000 + + enum glyph_attribute { + ATTR_NULL = 0, +diff --git a/x.c b/x.c +index 8a16faa..1bb5853 100644 +--- a/x.c ++++ b/x.c +@@ -59,6 +59,8 @@ static void zoom(const Arg *); + static void zoomabs(const Arg *); + static void zoomreset(const Arg *); + static void ttysend(const Arg *); ++void kscrollup(const Arg *); ++void kscrolldown(const Arg *); + + /* config.h for applying patches and the configuration. */ + #include "config.h" diff --git a/dwm/st/st b/dwm/st/st new file mode 100755 index 0000000000000000000000000000000000000000..2a9e424864fb6180927a000cb45f7f474659b7d0 GIT binary patch literal 109920 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

8KdyV{B7|X`KGyu( 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 literal 0 HcmV?d00001 diff --git a/dwm/st/st.1 b/dwm/st/st.1 new file mode 100644 index 0000000..39120b4 --- /dev/null +++ b/dwm/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/dwm/st/st.c b/dwm/st/st.c new file mode 100644 index 0000000..c44797b --- /dev/null +++ b/dwm/st/st.c @@ -0,0 +1,2775 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st.h" +#include "win.h" + +#if defined(__linux) + #include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + #include +#elif defined(__FreeBSD__) || defined(__DragonFly__) + #include +#endif + +/* Arbitrary sizes */ +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 +#define ESC_BUF_SIZ (128*UTF_SIZ) +#define ESC_ARG_SIZ 16 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ + +/* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) + +#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)] +#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size) +#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)]) + +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; + +/* Screen lines */ +typedef struct { + Line* buffer; /* ring buffer */ + int size; /* size of buffer */ + int cur; /* start of active screen */ + int off; /* scrollback line offset */ + TCursor sc; /* saved cursor */ +} LineBuffer; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + LineBuffer screen[2]; /* screen and alternate screen */ + int linelen; /* allocated line length */ + 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 void osc_color_response(int, int, int); +static int eschandle(uchar); +static void strdump(void); +static void strhandle(void); +static void strparse(void); +static void strreset(void); + +static void tprinter(char *, size_t); +static void tdumpsel(void); +static void tdumpline(int); +static void tdump(void); +static void tclearregion(int, int, int, int); +static void tcursor(int); +static void tdeletechar(int); +static void tdeleteline(int); +static void tinsertblank(int); +static void tinsertblankline(int); +static int tlinelen(int); +static void tmoveto(int, int); +static void tmoveato(int, int); +static void tnewline(int); +static void tputtab(int); +static void tputc(Rune); +static void treset(void); +static void tscrollup(int, int); +static void tscrolldown(int, int); +static void tsetattr(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 clearline(Line, Glyph, int, int); +static Line ensureline(Line); + +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; +} + +char +base64dec_getc(const char **src) +{ + while (**src && !isprint((unsigned char)**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char * +base64dec(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + static const char base64_digits[256] = { + [43] = 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 0, 0, 0, -1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, + 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; + + if (in_len % 4) + in_len += 4 - (in_len % 4); + 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; + Line line = TLINE(y); + + if (line[i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && line[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; + + 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(void) +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + +int +tattrset(int attr) +{ + int i, j; + int y = TLINEOFFSET(0); + + for (i = 0; i < term.row-1; i++) { + Line line = TSCREEN.buffer[y]; + for (j = 0; j < term.col-1; j++) { + if (line[j].mode & attr) + return 1; + } + y = (y+1) % TSCREEN.size; + } + + 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; + int y = TLINEOFFSET(0); + + for (i = 0; i < term.row-1; i++) { + Line line = TSCREEN.buffer[y]; + for (j = 0; j < term.col-1; j++) { + if (line[j].mode & attr) { + tsetdirt(i, i); + break; + } + } + y = (y+1) % TSCREEN.size; + } +} + +void +tfulldirt(void) +{ + tsetdirt(0, term.row-1); +} + +void +tcursor(int mode) +{ + if (mode == CURSOR_SAVE) { + TSCREEN.sc = term.c; + } else if (mode == CURSOR_LOAD) { + term.c = TSCREEN.sc; + tmoveto(term.c.x, term.c.y); + } +} + +void +treset(void) +{ + int i, j; + Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg}; + + 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++) { + term.screen[i].sc = (TCursor){{ + .fg = defaultfg, + .bg = defaultbg + }}; + term.screen[i].cur = 0; + term.screen[i].off = 0; + for (j = 0; j < term.row; ++j) { + if (term.col != term.linelen) + term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph)); + clearline(term.screen[i].buffer[j], g, 0, term.col); + } + for (j = term.row; j < term.screen[i].size; ++j) { + free(term.screen[i].buffer[j]); + term.screen[i].buffer[j] = NULL; + } + } + tcursor(CURSOR_LOAD); + term.linelen = term.col; + tfulldirt(); +} + +void +tnew(int col, int row) +{ + int i; + term = (Term){}; + term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line)); + term.screen[0].size = HISTSIZE; + term.screen[1].buffer = NULL; + for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL; + + tresize(col, row); + treset(); +} + +void +tswapscreen(void) +{ + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); +} + +void +kscrollup(const Arg *a) +{ + int n = a->i; + + if (IS_SET(MODE_ALTSCREEN)) + return; + + if (n < 0) n = (-n) * term.row; + if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off; + while (!TLINE(-n)) --n; + TSCREEN.off += n; + selscroll(0, n); + tfulldirt(); +} + +void +kscrolldown(const Arg *a) +{ + + int n = a->i; + + if (IS_SET(MODE_ALTSCREEN)) + return; + + if (n < 0) n = (-n) * term.row; + if (n > TSCREEN.off) n = TSCREEN.off; + TSCREEN.off -= n; + selscroll(0, -n); + tfulldirt(); +} + +void +tscrolldown(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + /* Ensure that lines are allocated */ + for (i = -n; i < 0; i++) { + TLINE(i) = ensureline(TLINE(i)); + } + + /* Shift non-scrolling areas in ring buffer */ + for (i = term.bot+1; i < term.row; i++) { + temp = TLINE(i); + TLINE(i) = TLINE(i-n); + TLINE(i-n) = temp; + } + for (i = 0; i < orig; i++) { + temp = TLINE(i); + TLINE(i) = TLINE(i-n); + TLINE(i-n) = temp; + } + + /* Scroll buffer */ + TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size; + /* Clear lines that have entered the view */ + tclearregion(0, orig, term.linelen-1, orig+n-1); + /* Redraw portion of the screen that has scrolled */ + tsetdirt(orig+n-1, term.bot); + selscroll(orig, n); +} + +void +tscrollup(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + /* Ensure that lines are allocated */ + for (i = term.row; i < term.row + n; i++) { + TLINE(i) = ensureline(TLINE(i)); + } + + /* Shift non-scrolling areas in ring buffer */ + for (i = orig-1; i >= 0; i--) { + temp = TLINE(i); + TLINE(i) = TLINE(i+n); + TLINE(i+n) = temp; + } + for (i = term.row-1; i >term.bot; i--) { + temp = TLINE(i); + TLINE(i) = TLINE(i+n); + TLINE(i+n) = temp; + } + + /* Scroll buffer */ + TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size; + /* Clear lines that have entered the view */ + tclearregion(0, term.bot-n+1, term.linelen-1, term.bot); + /* Redraw portion of the screen that has scrolled */ + tsetdirt(orig, term.bot-n+1); + 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); + } 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 - ~ */ + }; + Line line = TLINE(y); + + /* + * 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 (line[x].mode & ATTR_WIDE) { + if (x+1 < term.col) { + line[x+1].u = ' '; + line[x+1].mode &= ~ATTR_WDUMMY; + } + } else if (line[x].mode & ATTR_WDUMMY) { + line[x-1].u = ' '; + line[x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; + line[x] = *attr; + line[x].u = u; +} + +void +tclearregion(int x1, int y1, int x2, int y2) +{ + int x, y, L, S, 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.linelen-1); + LIMIT(x2, 0, term.linelen-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + + L = TLINEOFFSET(y1); + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { + gp = &TSCREEN.buffer[L][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + gp->u = ' '; + } + L = (L + 1) % TSCREEN.size; + } +} + +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 = TLINE(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 = TLINE(term.c.y); + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +} + +void +tinsertblankline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrolldown(term.c.y, n); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n); +} + +int32_t +tdefcolor(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]); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[0]); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblankline(csiescseq.arg[0]); + break; + case 'l': /* RM -- Reset Mode */ + tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + break; + case 'M': /* DL -- Delete lines */ + DEFAULT(csiescseq.arg[0], 1); + tdeleteline(csiescseq.arg[0]); + break; + case 'X': /* ECH -- Erase char */ + DEFAULT(csiescseq.arg[0], 1); + tclearregion(term.c.x, term.c.y, + term.c.x + csiescseq.arg[0] - 1, term.c.y); + break; + case 'P': /* DCH -- Delete char */ + DEFAULT(csiescseq.arg[0], 1); + tdeletechar(csiescseq.arg[0]); + break; + case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(-csiescseq.arg[0]); + break; + case 'd': /* VPA -- Move to */ + DEFAULT(csiescseq.arg[0], 1); + tmoveato(term.c.x, csiescseq.arg[0]-1); + break; + case 'h': /* SM -- Set terminal mode */ + tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); + break; + case 'm': /* SGR -- Terminal attribute (color) */ + tsetattr(csiescseq.arg, csiescseq.narg); + break; + case 'n': /* DSR – Device Status Report (cursor position) */ + if (csiescseq.arg[0] == 6) { + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + } + break; + case 'r': /* DECSTBM -- Set Scrolling Region */ + if (csiescseq.priv) { + goto unknown; + } else { + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], term.row); + tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); + tmoveato(0, 0); + } + break; + case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ + tcursor(CURSOR_SAVE); + break; + case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ + tcursor(CURSOR_LOAD); + break; + case ' ': + switch (csiescseq.mode[1]) { + case 'q': /* DECSCUSR -- Set Cursor Style */ + if (xsetcursor(csiescseq.arg[0])) + goto unknown; + break; + default: + goto unknown; + } + break; + } +} + +void +csidump(void) +{ + 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 +osc_color_response(int num, int index, int is_osc4) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(is_osc4 ? num : index, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch %s color %d\n", + is_osc4 ? "osc4" : "osc", + is_osc4 ? num : index); + return; + } + + n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + is_osc4 ? "4;" : "", num, r, r, g, g, b, b); + if (n < 0 || n >= sizeof(buf)) { + fprintf(stderr, "error: %s while printing %s response\n", + n < 0 ? "snprintf failed" : "truncation occurred", + is_osc4 ? "osc4" : "osc"); + } else { + ttywrite(buf, n, 1); + } +} + +void +strhandle(void) +{ + char *p = NULL, *dec; + int j, narg, par; + const struct { int idx; char *str; } osc_table[] = { + { defaultfg, "foreground" }, + { defaultbg, "background" }, + { defaultcs, "cursor" } + }; + + term.esc &= ~(ESC_STR_END|ESC_STR); + 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: + case 11: + case 12: + if (narg < 2) + break; + p = strescseq.args[1]; + if ((j = par - 10) < 0 || j >= LEN(osc_table)) + break; /* shouldn't be possible */ + + if (!strcmp(p, "?")) { + osc_color_response(par, osc_table[j].idx, 0); + } else if (xsetcolorname(osc_table[j].idx, p)) { + fprintf(stderr, "erresc: invalid %s color: %s\n", + osc_table[j].str, p); + } else { + tfulldirt(); + } + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + + if (p && !strcmp(p, "?")) { + osc_color_response(j, 0, 1); + } else if (xsetcolorname(j, p)) { + if (par == 104 && narg <= 1) + return; /* color reset without parameter */ + fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", + j, p ? p : "(null)"); + } else { + /* + * TODO if defaultbg color is changed, borders + * are dirty + */ + tfulldirt(); + } + 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 = &TLINE(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); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { + tscrolldown(term.top, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'c': /* RIS -- Reset to initial state */ + treset(); + resettitle(); + xloadcols(); + break; + case '=': /* DECPAM -- Application keypad */ + xsetmode(1, MODE_APPKEYPAD); + break; + case '>': /* DECPNM -- Normal keypad */ + xsetmode(0, MODE_APPKEYPAD); + break; + case '7': /* DECSC -- Save Cursor */ + tcursor(CURSOR_SAVE); + break; + case '8': /* DECRC -- Restore Cursor */ + tcursor(CURSOR_LOAD); + break; + case '\\': /* ST -- String Terminator */ + if (term.esc & ESC_STR_END) + strhandle(); + break; + default: + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", + (uchar) ascii, isprint(ascii)? ascii:'.'); + break; + } + return 1; +} + +void +tputc(Rune u) +{ + char c[UTF_SIZ]; + int control; + int width, len; + Glyph *gp; + + control = ISCONTROL(u); + if (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 = &TLINE(term.c.y)[term.c.x]; + if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + gp->mode |= ATTR_WRAP; + tnewline(1); + gp = &TLINE(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 = &TLINE(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; + + if (TSCREEN.off) { + TSCREEN.off = 0; + tfulldirt(); + } + + 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 +clearline(Line line, Glyph g, int x, int xend) +{ + int i; + g.mode = 0; + g.u = ' '; + for (i = x; i < xend; ++i) { + line[i] = g; + } +} + +Line +ensureline(Line line) +{ + if (!line) { + line = xmalloc(term.linelen * sizeof(Glyph)); + } + return line; +} + +void +tresize(int col, int row) +{ + int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int linelen = MAX(col, term.linelen); + int *bp; + + if (col < 1 || row < 1 || row > HISTSIZE) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + /* Shift buffer to keep the cursor where we expect it */ + if (row <= term.c.y) { + term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size; + } + + /* Resize and clear line buffers as needed */ + if (linelen > term.linelen) { + for (i = 0; i < term.screen[0].size; ++i) { + if (term.screen[0].buffer[i]) { + term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph)); + clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen); + } + } + for (i = 0; i < minrow; ++i) { + term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph)); + clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen); + } + } + /* Allocate all visible lines for regular line buffer */ + for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size) + { + if (!term.screen[0].buffer[j]) { + term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph)); + } + if (i >= term.row) { + clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen); + } + } + /* Resize alt screen */ + term.screen[1].cur = 0; + term.screen[1].size = row; + for (i = row; i < term.row; ++i) { + free(term.screen[1].buffer[i]); + } + term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line)); + for (i = term.row; i < row; ++i) { + term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph)); + clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen); + } + + /* resize to new height */ + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + /* fix tabstops */ + 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; + term.linelen = linelen; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); + tfulldirt(); +} + +void +resettitle(void) +{ + xsettitle(NULL); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y, L; + + L = TLINEOFFSET(y1); + for (y = y1; y < y2; y++) { + if (term.dirty[y]) { + term.dirty[y] = 0; + xdrawline(TSCREEN.buffer[L], x1, y, x2); + } + L = (L + 1) % TSCREEN.size; + } +} + +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 (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY) + term.ocx--; + if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); + if (TSCREEN.off == 0) + xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], + term.ocx, term.ocy, TLINE(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/dwm/st/st.c.orig b/dwm/st/st.c.orig new file mode 100644 index 0000000..62def59 --- /dev/null +++ b/dwm/st/st.c.orig @@ -0,0 +1,2656 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st.h" +#include "win.h" + +#if defined(__linux) + #include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + #include +#elif defined(__FreeBSD__) || defined(__DragonFly__) + #include +#endif + +/* Arbitrary sizes */ +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 +#define ESC_BUF_SIZ (128*UTF_SIZ) +#define ESC_ARG_SIZ 16 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ + +/* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) + +enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_ALTSCREEN = 1 << 2, + MODE_CRLF = 1 << 3, + MODE_ECHO = 1 << 4, + MODE_PRINT = 1 << 5, + MODE_UTF8 = 1 << 6, +}; + +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 */ + 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 void osc_color_response(int, int, int); +static int eschandle(uchar); +static void strdump(void); +static void strhandle(void); +static void strparse(void); +static void strreset(void); + +static void tprinter(char *, size_t); +static void tdumpsel(void); +static void tdumpline(int); +static void tdump(void); +static void tclearregion(int, int, int, int); +static void tcursor(int); +static void tdeletechar(int); +static void tdeleteline(int); +static void tinsertblank(int); +static void tinsertblankline(int); +static int tlinelen(int); +static void tmoveto(int, int); +static void tmoveato(int, int); +static void tnewline(int); +static void tputtab(int); +static void tputc(Rune); +static void treset(void); +static void tscrollup(int, int); +static void tscrolldown(int, int); +static void tsetattr(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; +} + +char +base64dec_getc(const char **src) +{ + while (**src && !isprint((unsigned char)**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char * +base64dec(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + static const char base64_digits[256] = { + [43] = 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 0, 0, 0, -1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, + 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; + + if (in_len % 4) + in_len += 4 - (in_len % 4); + 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 (term.line[y][i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && term.line[y][i - 1].u == ' ') + --i; + + return i; +} + +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type, int done) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + + if (sel.mode == SEL_IDLE) + return; + if (done && sel.mode == SEL_EMPTY) { + selclear(); + return; + } + + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + + sel.mode = done ? SEL_IDLE : SEL_READY; +} + +void +selnormalize(void) +{ + int i; + + if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { + sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; + sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; + } else { + sel.nb.x = MIN(sel.ob.x, sel.oe.x); + sel.ne.x = MAX(sel.ob.x, sel.oe.x); + } + sel.nb.y = MIN(sel.ob.y, sel.oe.y); + sel.ne.y = MAX(sel.ob.y, sel.oe.y); + + selsnap(&sel.nb.x, &sel.nb.y, -1); + selsnap(&sel.ne.x, &sel.ne.y, +1); + + /* expand selection over line breaks */ + if (sel.type == SEL_RECTANGULAR) + return; + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) + sel.nb.x = i; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; +} + +int +selected(int x, int y) +{ + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) + return 0; + + if (sel.type == SEL_RECTANGULAR) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); + + return BETWEEN(y, sel.nb.y, sel.ne.y) + && (y != sel.nb.y || x >= sel.nb.x) + && (y != sel.ne.y || x <= sel.ne.x); +} + +void +selsnap(int *x, int *y, int direction) +{ + int newx, newy, xt, yt; + int delim, prevdelim; + 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 = &term.line[*y][*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; + newy = *y; + if (!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) + break; + + if (direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if (!(term.line[yt][xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + + gp = &term.line[newy][newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) + break; + + *x = newx; + *y = newy; + prevgp = gp; + prevdelim = delim; + } + break; + case SNAP_LINE: + /* + * Snap around if the the previous line or the current one + * has set ATTR_WRAP at its end. Then the whole next or + * previous line will be selected. + */ + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { + if (!(term.line[*y-1][term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { + if (!(term.line[*y][term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } + break; + } +} + +char * +getsel(void) +{ + char *str, *ptr; + int y, bufsize, lastx, linelen; + 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 = &term.line[y][sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &term.line[y][MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + + for ( ; gp <= last; ++gp) { + if (gp->mode & ATTR_WDUMMY) + continue; + + ptr += utf8encode(gp->u, ptr); + } + + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if ((y < sel.ne.y || lastx >= linelen) && + (!(last->mode & ATTR_WRAP) || 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; + + 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(void) +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + +int +tattrset(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) + return 1; + } + } + + return 0; +} + +void +tsetdirt(int top, int bot) +{ + int i; + + LIMIT(top, 0, term.row-1); + LIMIT(bot, 0, term.row-1); + + for (i = top; i <= bot; i++) + term.dirty[i] = 1; +} + +void +tsetdirtattr(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) { + tsetdirt(i, i); + break; + } + } + } +} + +void +tfulldirt(void) +{ + tsetdirt(0, term.row-1); +} + +void +tcursor(int mode) +{ + static TCursor c[2]; + int alt = IS_SET(MODE_ALTSCREEN); + + if (mode == CURSOR_SAVE) { + c[alt] = term.c; + } else if (mode == CURSOR_LOAD) { + term.c = c[alt]; + tmoveto(c[alt].x, c[alt].y); + } +} + +void +treset(void) +{ + uint i; + + term.c = (TCursor){{ + .mode = ATTR_NULL, + .fg = defaultfg, + .bg = defaultbg + }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) + term.tabs[i] = 1; + term.top = 0; + term.bot = term.row - 1; + term.mode = MODE_WRAP|MODE_UTF8; + memset(term.trantbl, CS_USA, sizeof(term.trantbl)); + term.charset = 0; + + for (i = 0; i < 2; i++) { + tmoveto(0, 0); + tcursor(CURSOR_SAVE); + tclearregion(0, 0, term.col-1, term.row-1); + tswapscreen(); + } +} + +void +tnew(int col, int row) +{ + term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; + tresize(col, row); + treset(); +} + +void +tswapscreen(void) +{ + Line *tmp = term.line; + + term.line = term.alt; + term.alt = tmp; + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); +} + +void +tscrolldown(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + + for (i = term.bot; i >= orig+n; i--) { + temp = term.line[i]; + term.line[i] = term.line[i-n]; + term.line[i-n] = temp; + } + + selscroll(orig, n); +} + +void +tscrollup(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + + for (i = orig; i <= term.bot-n; i++) { + temp = term.line[i]; + term.line[i] = term.line[i+n]; + term.line[i+n] = temp; + } + + selscroll(orig, -n); +} + +void +selscroll(int orig, int n) +{ + if (sel.ob.x == -1) + return; + + if (BETWEEN(sel.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); + } 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); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n); +} + +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]); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[0]); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblankline(csiescseq.arg[0]); + break; + case 'l': /* RM -- Reset Mode */ + tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + break; + case 'M': /* DL -- Delete lines */ + DEFAULT(csiescseq.arg[0], 1); + tdeleteline(csiescseq.arg[0]); + break; + case 'X': /* ECH -- Erase char */ + DEFAULT(csiescseq.arg[0], 1); + tclearregion(term.c.x, term.c.y, + term.c.x + csiescseq.arg[0] - 1, term.c.y); + break; + case 'P': /* DCH -- Delete char */ + DEFAULT(csiescseq.arg[0], 1); + tdeletechar(csiescseq.arg[0]); + break; + case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(-csiescseq.arg[0]); + break; + case 'd': /* VPA -- Move to */ + DEFAULT(csiescseq.arg[0], 1); + tmoveato(term.c.x, csiescseq.arg[0]-1); + break; + case 'h': /* SM -- Set terminal mode */ + tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); + break; + case 'm': /* SGR -- Terminal attribute (color) */ + tsetattr(csiescseq.arg, csiescseq.narg); + break; + case 'n': /* DSR – Device Status Report (cursor position) */ + if (csiescseq.arg[0] == 6) { + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + } + break; + case 'r': /* DECSTBM -- Set Scrolling Region */ + if (csiescseq.priv) { + goto unknown; + } else { + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], term.row); + tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); + tmoveato(0, 0); + } + break; + case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ + tcursor(CURSOR_SAVE); + break; + case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ + tcursor(CURSOR_LOAD); + break; + case ' ': + switch (csiescseq.mode[1]) { + case 'q': /* DECSCUSR -- Set Cursor Style */ + if (xsetcursor(csiescseq.arg[0])) + goto unknown; + break; + default: + goto unknown; + } + break; + } +} + +void +csidump(void) +{ + 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 +osc_color_response(int num, int index, int is_osc4) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(is_osc4 ? num : index, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch %s color %d\n", + is_osc4 ? "osc4" : "osc", + is_osc4 ? num : index); + return; + } + + n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + is_osc4 ? "4;" : "", num, r, r, g, g, b, b); + if (n < 0 || n >= sizeof(buf)) { + fprintf(stderr, "error: %s while printing %s response\n", + n < 0 ? "snprintf failed" : "truncation occurred", + is_osc4 ? "osc4" : "osc"); + } else { + ttywrite(buf, n, 1); + } +} + +void +strhandle(void) +{ + char *p = NULL, *dec; + int j, narg, par; + const struct { int idx; char *str; } osc_table[] = { + { defaultfg, "foreground" }, + { defaultbg, "background" }, + { defaultcs, "cursor" } + }; + + term.esc &= ~(ESC_STR_END|ESC_STR); + 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: + case 11: + case 12: + if (narg < 2) + break; + p = strescseq.args[1]; + if ((j = par - 10) < 0 || j >= LEN(osc_table)) + break; /* shouldn't be possible */ + + if (!strcmp(p, "?")) { + osc_color_response(par, osc_table[j].idx, 0); + } else if (xsetcolorname(osc_table[j].idx, p)) { + fprintf(stderr, "erresc: invalid %s color: %s\n", + osc_table[j].str, p); + } else { + tfulldirt(); + } + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + + if (p && !strcmp(p, "?")) { + osc_color_response(j, 0, 1); + } else if (xsetcolorname(j, p)) { + if (par == 104 && narg <= 1) + return; /* color reset without parameter */ + fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", + j, p ? p : "(null)"); + } else { + /* + * TODO if defaultbg color is changed, borders + * are dirty + */ + tfulldirt(); + } + 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); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { + tscrolldown(term.top, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'c': /* RIS -- Reset to initial state */ + treset(); + resettitle(); + xloadcols(); + break; + case '=': /* DECPAM -- Application keypad */ + xsetmode(1, MODE_APPKEYPAD); + break; + case '>': /* DECPNM -- Normal keypad */ + xsetmode(0, MODE_APPKEYPAD); + break; + case '7': /* DECSC -- Save Cursor */ + tcursor(CURSOR_SAVE); + break; + case '8': /* DECRC -- Restore Cursor */ + tcursor(CURSOR_LOAD); + break; + case '\\': /* ST -- String Terminator */ + if (term.esc & ESC_STR_END) + strhandle(); + break; + default: + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", + (uchar) ascii, isprint(ascii)? ascii:'.'); + break; + } + return 1; +} + +void +tputc(Rune u) +{ + char c[UTF_SIZ]; + int control; + int width, len; + Glyph *gp; + + control = ISCONTROL(u); + if (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; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; + TCursor c; + + if (col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to + * memmove because we're freeing the earlier lines + */ + for (i = 0; i <= term.c.y - row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + /* ensure that both src and dst are not NULL */ + if (i > 0) { + memmove(term.line, term.line + i, row * sizeof(Line)); + memmove(term.alt, term.alt + i, row * sizeof(Line)); + } + for (i += row; i < term.row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); + } + + /* allocate any new rows */ + for (/* i = minrow */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + term.alt[i] = xmalloc(col * sizeof(Glyph)); + } + if (col > term.col) { + bp = term.tabs + term.col; + + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + while (--bp > term.tabs && !*bp) + /* nothing */ ; + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } + /* update terminal size */ + term.col = col; + term.row = row; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); + /* Clearing both screens (it makes dirty all lines) */ + c = term.c; + for (i = 0; i < 2; i++) { + if (mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if (0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + tcursor(CURSOR_LOAD); + } + term.c = c; +} + +void +resettitle(void) +{ + xsettitle(NULL); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y; + + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(term.line[y], x1, y, x2); + } +} + +void +draw(void) +{ + int cx = term.c.x, 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); + 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/dwm/st/st.h b/dwm/st/st.h new file mode 100644 index 0000000..1ea0062 --- /dev/null +++ b/dwm/st/st.h @@ -0,0 +1,134 @@ +/* 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)) +#define HISTSIZE 2000 + +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 drawing_mode { + DRAW_NONE = 0, + DRAW_BG = 1 << 0, + DRAW_FG = 1 << 1, +}; + +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 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 *); + +/* 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/dwm/st/st.info b/dwm/st/st.info new file mode 100644 index 0000000..8201ad6 --- /dev/null +++ b/dwm/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/dwm/st/st.o b/dwm/st/st.o new file mode 100644 index 0000000000000000000000000000000000000000..e6002509bac8f3b347906d04936e2b67b15ab5aa GIT binary patch literal 82000 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 z zFIgS-ixcvt_=tQNep+pQ}db~jX z`*@*zBVHumgcr*<<0bN~cv{|tm&#wm%j7%pa``U2Qob9nlJCK*<@@j&`F=bje-p2j zAHwV8Z{hXwxA6w~J9zwm)#LVk7jIJhQM_4x3~!Mi$6Mtm@pkzsyhDBl@06d#v+{HJ z4*7Y!TYeGmkzc}l6otJ}ZA3pOde_=jChhTk>b|1^ILMqWpP0_0ym3=f8&M$=Bie^7VLuybUjuZ^Vn_ zoA6@!X1qkc6;I2%@KX7o<7M)lc)5HRUMc?$UM1gySIhU|HS+y^3#&u>?v`I9 zzDIru@0EXo-Dq4$2F@7`$Izh!4n%@IiSoepnu#Pjp0{CVog> ziVw@n@DuWKd_-P}pO#nQqw;EeOkRVJ%QN@|c`ZI6ufs3P(Zg1nRXMR^uaE&X)=AD;u2C+{XcU*3Zk$b0cZ`M24> zisW&>E|$*|Un2iE_OG=35!PEO{}Ns%kM&tDU&`}|D&@a|SIJ+&tL5LsYvl3yK^ghV zny@`;<=?>T>vX-;dYJ-^3f_hww)ETX>Uvl6q^F z{{U~1-@#kupCkWv`7yjhp2j=ntMIIRExtp32Je=i#e3xE@Lu_Oe6Rc>-Y36=ACOAku?zBeon%7ZN&IE`OB}x@<=?`mD^H1mG-zR=v{#lMM zx8&3Kg8Xg1r!C5VjHmKH-T!A;Z=Sps&zEoKcu^pa=Tn99cs^Ame~t1K%Lnihc|4y= z%UdXCsk{|0leba+a(O(Ts+6B3zDgd?r>f=ge5yts&!;l-9?DrO@5Srn*YSG!2eiWm z`BA)4{s!J8zsu)smM^j&wa9;ocGxQaGs@X6|5c6`9rD$br&B&mK3Vw*e22V=@^{NO zv)&$g7yVMNe2o0}%E$3Oc|4ywAb&qz|B;tx!hYB{Pl*A{|R}#A96&#j(kqb*W;t|c%S5$d?WGW^8e;O=L_;5;}i1FQ2xvE zF7lt0$NMd(hww%DTX>55+T-{Cc>iaf z{2k)+~%~IQOEN-}ke4VR-w#pTmpg=kc`sB3>rHgjdS1;PdjU z_<}r+FT7X4+)e24royhpwg-z$F{KOlbs@0UM`56Ua>Bl4&4 zVfku&ME*2BDqn+-%h%!)@@Mf$`B(92`S0Mf@&-J8uhsr!94U(_hBMw9bDA`fX5Oe% z{QE4&_mkMC`O?D~#JK#{UIgN_nII^XEc*Mm|*#yj%XCj|3l-|K9T89!LGv zp9voOmAKw5cpmi;y$>&zm(pRE%fF5{$v?n7j`7&fhVpnk;igA}7iwI@w~K-|%j5l+ z9(Qz>1IPize}a45NZ-$e{5?+N@9`@APWR1~A-+X^5%=%HfBEwv-oL|6;Qn3mrN={j zrLGUZS{l4wK7hB&*Z*RO_wNF)JQ3X2v4@@vUZCp|YkoO+sr*H}MP6DS;(a}>@C(6x zo$5W@*MC02eZ3`J5%S4nzl`m+3@?$d#;fGDc#r(!RUv<$mvuZ9+~-H%!2P{?|Fa?9 z-_vHk61-dAqx$NCkI9>!3!b9?h@W@KZwGIY@2(F%EdPY>f%)_|F`s{Zf&44BzrH2A zVPmRdd$ub~+v8kUTgQfqY}<}(s-ks6c0;OSTimq+RLK42JqWaT|t8$A;nh?Um zz33e0w4!6%%NsgU6)$hwvZZZnF59i!vTYS#dH#hjY|n0Z`43orW$PbSbarj)Z0pLt zP9AOTFKz1Du%(SM|34<_T-v0oXScbZQ--mieS?_ufBH-$uBSHWVsIBg`oR+CEe#o`R@H{_N|rQ;0K6z1nrIJ8B@be5J{BiV-*~_t44otWgO<$4bkV<+n@Q@$^*>K~mn)V( zrvIzlOp>S5r(X>b#q={V-suN(Z#=C2#`$7a%#NAV-}2mFOdq%3!`7V(OI0Ton|tN2 w2-D>2#_nV}EZ*P$l5OH`pM+%gg}{GM$Kd))@_ktP>8C^F--c}N|LgSs3l}?j#{d8T literal 0 HcmV?d00001 diff --git a/dwm/st/win.h b/dwm/st/win.h new file mode 100644 index 0000000..6de960d --- /dev/null +++ b/dwm/st/win.h @@ -0,0 +1,41 @@ +/* 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 *); +int xgetcolor(int, unsigned char *, unsigned char *, unsigned 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/dwm/st/x.c b/dwm/st/x.c new file mode 100644 index 0000000..50c9560 --- /dev/null +++ b/dwm/st/x.c @@ -0,0 +1,2223 @@ +/* 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 *); +void kscrollup(const Arg *); +void kscrolldown(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, 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 int xloadsparefont(FcPattern *, int); +static void xloadsparefonts(void); +static void xunloadfont(Font *); +static void xunloadfonts(void); +static void xsetenv(void); +static void xseturgency(int); +static int evcol(XEvent *); +static int evrow(XEvent *); + +static 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 uint buttons; /* bit field of pressed buttons */ + +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); + xloadsparefonts(); + 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, btn, code; + int x = evcol(e), y = evrow(e); + int state = e->xbutton.state; + char buf[40]; + static int ox, oy; + + if (e->type == MotionNotify) { + if (x == ox && y == oy) + return; + if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) + return; + /* MODE_MOUSEMOTION: no reporting if no button is pressed */ + if (IS_SET(MODE_MOUSEMOTION) && buttons == 0) + return; + /* Set btn to lowest-numbered pressed button, or 12 if no + * buttons are pressed. */ + for (btn = 1; btn <= 11 && !(buttons & (1<<(btn-1))); btn++) + ; + code = 32; + } else { + btn = e->xbutton.button; + /* Only buttons 1 through 11 can be encoded */ + if (btn < 1 || btn > 11) + return; + if (e->type == ButtonRelease) { + /* MODE_MOUSEX10: no button release reporting */ + if (IS_SET(MODE_MOUSEX10)) + return; + /* Don't send release events for the scroll wheel */ + if (btn == 4 || btn == 5) + return; + } + code = 0; + } + + ox = x; + oy = y; + + /* Encode btn into code. If no button is pressed for a motion event in + * MODE_MOUSEMANY, then encode it as a release. */ + if ((!IS_SET(MODE_MOUSESGR) && e->type == ButtonRelease) || btn == 12) + code += 3; + else if (btn >= 8) + code += 128 + btn - 8; + else if (btn >= 4) + code += 64 + btn - 4; + else + code += btn - 1; + + if (!IS_SET(MODE_MOUSEX10)) { + code += ((state & ShiftMask ) ? 4 : 0) + + ((state & Mod1Mask ) ? 8 : 0) /* meta key: alt */ + + ((state & ControlMask) ? 16 : 0); + } + + if (IS_SET(MODE_MOUSESGR)) { + len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", + code, x+1, y+1, + e->type == ButtonRelease ? 'm' : 'M'); + } else if (x < 223 && y < 223) { + len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", + 32+code, 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) +{ + int btn = e->xbutton.button; + struct timespec now; + int snap; + + if (1 <= btn && btn <= 11) + buttons |= 1 << (btn-1); + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 0)) + return; + + if (btn == 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(); + clipcopy(NULL); +} + +void +xsetsel(char *str) +{ + setsel(str, CurrentTime); +} + +void +brelease(XEvent *e) +{ + int btn = e->xbutton.button; + + if (1 <= btn && btn <= 11) + buttons &= ~(1 << (btn-1)); + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 1)) + return; + if (btn == 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); +} + +int +xloadsparefont(FcPattern *pattern, int flags) +{ + FcPattern *match; + FcResult result; + + match = FcFontMatch(NULL, pattern, &result); + if (!match) { + return 1; + } + + if (!(frc[frclen].font = XftFontOpenPattern(xw.dpy, match))) { + FcPatternDestroy(match); + return 1; + } + + frc[frclen].flags = flags; + /* Believe U+0000 glyph will present in each default font */ + frc[frclen].unicodep = 0; + frclen++; + + return 0; +} + +void +xloadsparefonts(void) +{ + FcPattern *pattern; + double sizeshift, fontval; + int fc; + char **fp; + + if (frclen != 0) + die("can't embed spare fonts. cache isn't empty"); + + /* Calculate count of spare fonts */ + fc = sizeof(font2) / sizeof(*font2); + if (fc == 0) + return; + + /* Allocate memory for cache entries. */ + if (frccap < 4 * fc) { + frccap += 4 * fc - frccap; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } + + for (fp = font2; fp - font2 < fc; ++fp) { + + if (**fp == '-') + pattern = XftXlfdParse(*fp, False, False); + else + pattern = FcNameParse((FcChar8 *)*fp); + + if (!pattern) + die("can't open spare font %s\n", *fp); + + if (defaultfontsize > 0) { + sizeshift = usedfontsize - defaultfontsize; + if (sizeshift != 0 && + FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == + FcResultMatch) { + fontval += sizeshift; + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternDel(pattern, FC_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontval); + } + } + + FcPatternAddBool(pattern, FC_SCALABLE, 1); + + FcConfigSubstitute(NULL, pattern, FcMatchPattern); + XftDefaultSubstitute(xw.dpy, xw.scr, pattern); + + if (xloadsparefont(pattern, FRC_NORMAL)) + die("can't open spare font %s\n", *fp); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + if (xloadsparefont(pattern, FRC_ITALIC)) + die("can't open spare font %s\n", *fp); + + FcPatternDel(pattern, FC_WEIGHT); + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + if (xloadsparefont(pattern, FRC_ITALICBOLD)) + die("can't open spare font %s\n", *fp); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + if (xloadsparefont(pattern, FRC_BOLD)) + die("can't open spare font %s\n", *fp); + + FcPatternDestroy(pattern); + } +} + +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); + + /* spare fonts */ + xloadsparefonts(); + + /* 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 dmode) +{ + 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; + + if (dmode & DRAW_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); + /* Fill the background */ + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + } + + if (dmode & DRAW_FG) { + /* 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); + } + } +} + +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, DRAW_BG | DRAW_FG); +} + +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, numspecs_cached; + Glyph base, new; + XftGlyphFontSpec *specs; + + numspecs_cached = xmakeglyphfontspecs(xw.specbuf, &line[x1], x2 - x1, x1, y1); + + /* Draw line in 2 passes: background and foreground. This way wide glyphs + won't get truncated (#223) */ + for (int dmode = DRAW_BG; dmode <= DRAW_FG; dmode <<= 1) { + specs = xw.specbuf; + numspecs = numspecs_cached; + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + if (new.mode == ATTR_WDUMMY) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); + } +} + +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 = NoSymbol; + 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); + if (status == XBufferOverflow) + return; + } 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/dwm/st/x.o b/dwm/st/x.o new file mode 100644 index 0000000000000000000000000000000000000000..71301932a5e16929ded75483f5315312c4d905b4 GIT binary patch literal 79088 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 V`|*=!