From bd4e72743ca24c0c380c221a6bc7376da1911f7e Mon Sep 17 00:00:00 2001 From: dcc Date: Sat, 27 May 2023 21:45:28 -0700 Subject: [PATCH] first --- based-simple-term/FAQ | 250 ++ based-simple-term/LEGACY | 17 + based-simple-term/LICENSE | 34 + based-simple-term/Makefile | 57 + based-simple-term/README | 34 + based-simple-term/arg.h | 50 + based-simple-term/config.def.h | 520 +++ based-simple-term/config.h | 520 +++ based-simple-term/config.mk | 35 + based-simple-term/config.mk~ | 35 + .../patches/st-alpha-20220206-0.8.5.diff | 146 + .../patches/st-appsync-20200618-b27a383.diff | 259 ++ ...-20211215-181216-st-0.8.4-testrelease.diff | 601 ++++ .../patches/st-background-image-0.8.4.diff | 262 ++ .../patches/st-delkey-20201112-4ef0cbd.diff | 20 + .../st-dynamic-cursor-color-0.8.4.diff | 52 + .../st-scrollback-20210507-4536f46.diff | 351 ++ .../st-scrollback-mouse-20191024-a2c479c.diff | 13 + ...back-mouse-altscreen-20220127-2c5edf2.diff | 78 + .../patches/st-undercurl-0.8.4-20210822.diff | 605 ++++ .../patches/st-undercurl-0.8.4-20210822.diff_ | 605 ++++ based-simple-term/patches/st-w3m-0.8.3.diff | 42 + based-simple-term/patches/st-w3m-0.8.3.diff_ | 42 + based-simple-term/st | Bin 0 -> 122688 bytes based-simple-term/st.1 | 177 + based-simple-term/st.c | 2859 +++++++++++++++++ based-simple-term/st.h | 135 + based-simple-term/st.info | 241 ++ based-simple-term/st.o | Bin 0 -> 102048 bytes based-simple-term/win.h | 40 + based-simple-term/x.c | 2553 +++++++++++++++ based-simple-term/x.o | Bin 0 -> 96144 bytes bashs/.bashrc | 14 + bashs/shell scrips/.dwmba | 6 + bashs/shell scrips/bat32 | 6 + bashs/shell scrips/check | 9 + bashs/shell scrips/elemnt | 3 + bashs/shell scrips/keyboardlayoutswitcher.sh | 23 + bashs/shell scrips/keyboardlayoutswitcher.sh~ | 23 + bashs/shell scrips/long-live | 5 + bashs/shell scrips/multilib-install | 8 + bashs/shell scrips/multilib-install-current | 8 + bashs/shell scrips/pm | 1 + bashs/shell scrips/sepk | 6 + bdmenu-based/LICENSE | 30 + bdmenu-based/Makefile | 64 + bdmenu-based/README | 24 + bdmenu-based/arg.h | 49 + bdmenu-based/config.def.h | 33 + bdmenu-based/config.h | 33 + bdmenu-based/config.mk | 31 + bdmenu-based/config.mk~ | 31 + bdmenu-based/dmenu | Bin 0 -> 42384 bytes bdmenu-based/dmenu.1 | 197 ++ bdmenu-based/dmenu.c | 883 +++++ bdmenu-based/dmenu.o | Bin 0 -> 38336 bytes bdmenu-based/dmenu_path | 13 + bdmenu-based/dmenu_run | 2 + bdmenu-based/drw.c | 438 +++ bdmenu-based/drw.h | 60 + bdmenu-based/drw.o | Bin 0 -> 16016 bytes bdmenu-based/stest | Bin 0 -> 10080 bytes bdmenu-based/stest.1 | 90 + bdmenu-based/stest.c | 109 + bdmenu-based/stest.o | Bin 0 -> 5800 bytes bdmenu-based/util.c | 35 + bdmenu-based/util.h | 8 + bdmenu-based/util.o | Bin 0 -> 2592 bytes bdwm/.config.def.h.un~ | Bin 0 -> 3393 bytes bdwm/.dwm.c.un~ | Bin 0 -> 2830 bytes bdwm/LICENSE | 38 + bdwm/Makefile | 51 + bdwm/config.def.h | 125 + bdwm/config.def.h~ | 124 + bdwm/config.h | 125 + bdwm/config.mk | 39 + bdwm/config.mk~ | 39 + bdwm/drw.c | 464 +++ bdwm/drw.h | 58 + bdwm/drw.o | Bin 0 -> 17136 bytes bdwm/dwm | Bin 0 -> 72344 bytes bdwm/dwm-steam-6.2.diff | 63 + bdwm/dwm.1 | 176 + bdwm/dwm.c | 2163 +++++++++++++ bdwm/dwm.c.orig | 2157 +++++++++++++ bdwm/dwm.c.rej | 10 + bdwm/dwm.c~ | 2162 +++++++++++++ bdwm/dwm.o | Bin 0 -> 82320 bytes bdwm/transient.c | 42 + bdwm/util.c | 36 + bdwm/util.h | 8 + bdwm/util.o | Bin 0 -> 2592 bytes devour/LICENSE | 339 ++ devour/Makefile | 26 + devour/README.md | 128 + devour/devour-shellalias-10.0.diff | 17 + devour/devour.c | 42 + slock/1.diff | 31 + slock/LICENSE | 24 + slock/Makefile | 61 + slock/README | 24 + slock/arg.h | 65 + slock/config.def.h | 21 + slock/config.h | 21 + slock/config.mk | 32 + slock/config.mk~ | 32 + slock/explicit_bzero.c | 19 + slock/explicit_bzero.o | Bin 0 -> 1408 bytes slock/slock-message-20191002-b46028b.diff | 250 ++ slock/slock.1 | 46 + slock/slock.c | 520 +++ slock/slock.c.orig | 408 +++ slock/slock.o | Bin 0 -> 14624 bytes slock/util.h | 2 + 114 files changed, 22863 insertions(+) create mode 100755 based-simple-term/FAQ create mode 100755 based-simple-term/LEGACY create mode 100755 based-simple-term/LICENSE create mode 100755 based-simple-term/Makefile create mode 100755 based-simple-term/README create mode 100755 based-simple-term/arg.h create mode 100755 based-simple-term/config.def.h create mode 100755 based-simple-term/config.h create mode 100755 based-simple-term/config.mk create mode 100755 based-simple-term/config.mk~ create mode 100755 based-simple-term/patches/st-alpha-20220206-0.8.5.diff create mode 100755 based-simple-term/patches/st-appsync-20200618-b27a383.diff create mode 100755 based-simple-term/patches/st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff create mode 100755 based-simple-term/patches/st-background-image-0.8.4.diff create mode 100755 based-simple-term/patches/st-delkey-20201112-4ef0cbd.diff create mode 100755 based-simple-term/patches/st-dynamic-cursor-color-0.8.4.diff create mode 100755 based-simple-term/patches/st-scrollback-20210507-4536f46.diff create mode 100755 based-simple-term/patches/st-scrollback-mouse-20191024-a2c479c.diff create mode 100755 based-simple-term/patches/st-scrollback-mouse-altscreen-20220127-2c5edf2.diff create mode 100755 based-simple-term/patches/st-undercurl-0.8.4-20210822.diff create mode 100755 based-simple-term/patches/st-undercurl-0.8.4-20210822.diff_ create mode 100755 based-simple-term/patches/st-w3m-0.8.3.diff create mode 100755 based-simple-term/patches/st-w3m-0.8.3.diff_ create mode 100755 based-simple-term/st create mode 100755 based-simple-term/st.1 create mode 100755 based-simple-term/st.c create mode 100755 based-simple-term/st.h create mode 100755 based-simple-term/st.info create mode 100644 based-simple-term/st.o create mode 100755 based-simple-term/win.h create mode 100755 based-simple-term/x.c create mode 100644 based-simple-term/x.o create mode 100755 bashs/.bashrc create mode 100755 bashs/shell scrips/.dwmba create mode 100755 bashs/shell scrips/bat32 create mode 100755 bashs/shell scrips/check create mode 100755 bashs/shell scrips/elemnt create mode 100755 bashs/shell scrips/keyboardlayoutswitcher.sh create mode 100755 bashs/shell scrips/keyboardlayoutswitcher.sh~ create mode 100755 bashs/shell scrips/long-live create mode 100755 bashs/shell scrips/multilib-install create mode 100755 bashs/shell scrips/multilib-install-current create mode 100755 bashs/shell scrips/pm create mode 100755 bashs/shell scrips/sepk create mode 100755 bdmenu-based/LICENSE create mode 100755 bdmenu-based/Makefile create mode 100755 bdmenu-based/README create mode 100755 bdmenu-based/arg.h create mode 100755 bdmenu-based/config.def.h create mode 100755 bdmenu-based/config.h create mode 100755 bdmenu-based/config.mk create mode 100755 bdmenu-based/config.mk~ create mode 100755 bdmenu-based/dmenu create mode 100755 bdmenu-based/dmenu.1 create mode 100755 bdmenu-based/dmenu.c create mode 100644 bdmenu-based/dmenu.o create mode 100755 bdmenu-based/dmenu_path create mode 100755 bdmenu-based/dmenu_run create mode 100755 bdmenu-based/drw.c create mode 100755 bdmenu-based/drw.h create mode 100644 bdmenu-based/drw.o create mode 100755 bdmenu-based/stest create mode 100755 bdmenu-based/stest.1 create mode 100755 bdmenu-based/stest.c create mode 100644 bdmenu-based/stest.o create mode 100755 bdmenu-based/util.c create mode 100755 bdmenu-based/util.h create mode 100644 bdmenu-based/util.o create mode 100755 bdwm/.config.def.h.un~ create mode 100755 bdwm/.dwm.c.un~ create mode 100755 bdwm/LICENSE create mode 100755 bdwm/Makefile create mode 100755 bdwm/config.def.h create mode 100755 bdwm/config.def.h~ create mode 100755 bdwm/config.h create mode 100755 bdwm/config.mk create mode 100755 bdwm/config.mk~ create mode 100755 bdwm/drw.c create mode 100755 bdwm/drw.h create mode 100644 bdwm/drw.o create mode 100755 bdwm/dwm create mode 100755 bdwm/dwm-steam-6.2.diff create mode 100755 bdwm/dwm.1 create mode 100755 bdwm/dwm.c create mode 100755 bdwm/dwm.c.orig create mode 100755 bdwm/dwm.c.rej create mode 100755 bdwm/dwm.c~ create mode 100644 bdwm/dwm.o create mode 100755 bdwm/transient.c create mode 100755 bdwm/util.c create mode 100755 bdwm/util.h create mode 100644 bdwm/util.o create mode 100755 devour/LICENSE create mode 100755 devour/Makefile create mode 100755 devour/README.md create mode 100755 devour/devour-shellalias-10.0.diff create mode 100755 devour/devour.c create mode 100755 slock/1.diff create mode 100755 slock/LICENSE create mode 100755 slock/Makefile create mode 100755 slock/README create mode 100755 slock/arg.h create mode 100755 slock/config.def.h create mode 100755 slock/config.h create mode 100755 slock/config.mk create mode 100755 slock/config.mk~ create mode 100755 slock/explicit_bzero.c create mode 100755 slock/explicit_bzero.o create mode 100755 slock/slock-message-20191002-b46028b.diff create mode 100755 slock/slock.1 create mode 100755 slock/slock.c create mode 100755 slock/slock.c.orig create mode 100644 slock/slock.o create mode 100755 slock/util.h diff --git a/based-simple-term/FAQ b/based-simple-term/FAQ new file mode 100755 index 0000000..969b195 --- /dev/null +++ b/based-simple-term/FAQ @@ -0,0 +1,250 @@ +## Why does st not handle utmp entries? + +Use the excellent tool of [utmp](https://git.suckless.org/utmp/) for this task. + + +## Some _random program_ complains that st is unknown/not recognised/unsupported/whatever! + +It means that st doesn’t have any terminfo entry on your system. Chances are +you did not `make install`. If you just want to test it without installing it, +you can manually run `tic -sx st.info`. + + +## Nothing works, and nothing is said about an unknown terminal! + +* Some programs just assume they’re running in xterm i.e. they don’t rely on + terminfo. What you see is the current state of the “xterm compliance”. +* Some programs don’t complain about the lacking st description and default to + another terminal. In that case see the question about terminfo. + + +## How do I scroll back up? + +* Using a terminal multiplexer. + * `st -e tmux` using C-b [ + * `st -e screen` using C-a ESC +* Using the excellent tool of [scroll](https://git.suckless.org/scroll/). +* Using the scrollback [patch](https://st.suckless.org/patches/scrollback/). + + +## I would like to have utmp and/or scroll functionality by default + +You can add the absolute path of both programs in your config.h file. You only +have to modify the value of utmp and scroll variables. + + +## Why doesn't the Del key work in some programs? + +Taken from the terminfo manpage: + + If the terminal has a keypad that transmits codes when the keys + are pressed, this information can be given. Note that it is not + possible to handle terminals where the keypad only works in + local (this applies, for example, to the unshifted HP 2621 keys). + If the keypad can be set to transmit or not transmit, give these + codes as smkx and rmkx. Otherwise the keypad is assumed to + always transmit. + +In the st case smkx=E[?1hE= and rmkx=E[?1lE>, so it is mandatory that +applications which want to test against keypad keys send these +sequences. + +But buggy applications (like bash and irssi, for example) don't do this. A fast +solution for them is to use the following command: + + $ printf '\033[?1h\033=' >/dev/tty + +or + $ tput smkx + +In the case of bash, readline is used. Readline has a different note in its +manpage about this issue: + + enable-keypad (Off) + When set to On, readline will try to enable the + application keypad when it is called. Some systems + need this to enable arrow keys. + +Adding this option to your .inputrc will fix the keypad problem for all +applications using readline. + +If you are using zsh, then read the zsh FAQ +: + + It should be noted that the O / [ confusion can occur with other keys + such as Home and End. Some systems let you query the key sequences + sent by these keys from the system's terminal database, terminfo. + Unfortunately, the key sequences given there typically apply to the + mode that is not the one zsh uses by default (it's the "application" + mode rather than the "raw" mode). Explaining the use of terminfo is + outside of the scope of this FAQ, but if you wish to use the key + sequences given there you can tell the line editor to turn on + "application" mode when it starts and turn it off when it stops: + + function zle-line-init () { echoti smkx } + function zle-line-finish () { echoti rmkx } + zle -N zle-line-init + zle -N zle-line-finish + +Putting these lines into your .zshrc will fix the problems. + + +## How can I use meta in 8bit mode? + +St supports meta in 8bit mode, but the default terminfo entry doesn't +use this capability. If you want it, you have to use the 'st-meta' value +in TERM. + + +## I cannot compile st in OpenBSD + +OpenBSD lacks librt, despite it being mandatory in POSIX +. +If you want to compile st for OpenBSD you have to remove -lrt from config.mk, and +st will compile without any loss of functionality, because all the functions are +included in libc on this platform. + + +## The Backspace Case + +St is emulating the Linux way of handling backspace being delete and delete being +backspace. + +This is an issue that was discussed in suckless mailing list +. Here is why some old grumpy +terminal users wants its backspace to be how he feels it: + + Well, I am going to comment why I want to change the behaviour + of this key. When ASCII was defined in 1968, communication + with computers was done using punched cards, or hardcopy + terminals (basically a typewriter machine connected with the + computer using a serial port). ASCII defines DELETE as 7F, + because, in punched-card terms, it means all the holes of the + card punched; it is thus a kind of 'physical delete'. In the + same way, the BACKSPACE key was a non-destructive backspace, + as on a typewriter. So, if you wanted to delete a character, + you had to BACKSPACE and then DELETE. Another use of BACKSPACE + was to type accented characters, for example 'a BACKSPACE `'. + The VT100 had no BACKSPACE key; it was generated using the + CONTROL key as another control character (CONTROL key sets to + 0 b7 b6 b5, so it converts H (code 0x48) into BACKSPACE (code + 0x08)), but it had a DELETE key in a similar position where + the BACKSPACE key is located today on common PC keyboards. + All the terminal emulators emulated the difference between + these keys correctly: the backspace key generated a BACKSPACE + (^H) and delete key generated a DELETE (^?). + + But a problem arose when Linus Torvalds wrote Linux. Unlike + earlier terminals, the Linux virtual terminal (the terminal + emulator integrated in the kernel) returned a DELETE when + backspace was pressed, due to the VT100 having a DELETE key in + the same position. This created a lot of problems (see [1] + and [2]). Since Linux has become the king, a lot of terminal + emulators today generate a DELETE when the backspace key is + pressed in order to avoid problems with Linux. The result is + that the only way of generating a BACKSPACE on these systems + is by using CONTROL + H. (I also think that emacs had an + important point here because the CONTROL + H prefix is used + in emacs in some commands (help commands).) + + From point of view of the kernel, you can change the key + for deleting a previous character with stty erase. When you + connect a real terminal into a machine you describe the type + of terminal, so getty configures the correct value of stty + erase for this terminal. In the case of terminal emulators, + however, you don't have any getty that can set the correct + value of stty erase, so you always get the default value. + For this reason, it is necessary to add 'stty erase ^H' to your + profile if you have changed the value of the backspace key. + Of course, another solution is for st itself to modify the + value of stty erase. I usually have the inverse problem: + when I connect to non-Unix machines, I have to press CONTROL + + h to get a BACKSPACE. The inverse problem occurs when a user + connects to my Unix machines from a different system with a + correct backspace key. + + [1] http://www.ibb.net/~anne/keyboard.html + [2] http://www.tldp.org/HOWTO/Keyboard-and-Console-HOWTO-5.html + + +## But I really want the old grumpy behaviour of my terminal + +Apply [1]. + +[1] https://st.suckless.org/patches/delkey + + +## Why do images not work in st using the w3m image hack? + +w3mimg uses a hack that draws an image on top of the terminal emulator Drawable +window. The hack relies on the terminal to use a single buffer to draw its +contents directly. + +st uses double-buffered drawing so the image is quickly replaced and may show a +short flicker effect. + +Below is a patch example to change st double-buffering to a single Drawable +buffer. + +diff --git a/x.c b/x.c +--- a/x.c ++++ b/x.c +@@ -732,10 +732,6 @@ xresize(int col, int row) + win.tw = col * win.cw; + win.th = row * win.ch; + +- XFreePixmap(xw.dpy, xw.buf); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); +- XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + + /* resize to new width */ +@@ -1148,8 +1144,7 @@ xinit(int cols, int rows) + gcvalues.graphics_exposures = False; + dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, + &gcvalues); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); ++ xw.buf = xw.win; + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + +@@ -1632,8 +1627,6 @@ xdrawline(Line line, int x1, int y1, int x2) + void + xfinishdraw(void) + { +- XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, +- win.h, 0, 0); + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(MODE_REVERSE)? + defaultfg : defaultbg].pixel); + + +## BadLength X error in Xft when trying to render emoji + +Xft makes st crash when rendering color emojis with the following error: + +"X Error of failed request: BadLength (poly request too large or internal Xlib length error)" + Major opcode of failed request: 139 (RENDER) + Minor opcode of failed request: 20 (RenderAddGlyphs) + Serial number of failed request: 1595 + Current serial number in output stream: 1818" + +This is a known bug in Xft (not st) which happens on some platforms and +combination of particular fonts and fontconfig settings. + +See also: +https://gitlab.freedesktop.org/xorg/lib/libxft/issues/6 +https://bugs.freedesktop.org/show_bug.cgi?id=107534 +https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + +The solution is to remove color emoji fonts or disable this in the fontconfig +XML configuration. As an ugly workaround (which may work only on newer +fontconfig versions (FC_COLOR)), the following code can be used to mask color +fonts: + + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + +Please don't bother reporting this bug to st, but notify the upstream Xft +developers about fixing this bug. diff --git a/based-simple-term/LEGACY b/based-simple-term/LEGACY new file mode 100755 index 0000000..bf28b1e --- /dev/null +++ b/based-simple-term/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/based-simple-term/LICENSE b/based-simple-term/LICENSE new file mode 100755 index 0000000..3cbf420 --- /dev/null +++ b/based-simple-term/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/based-simple-term/Makefile b/based-simple-term/Makefile new file mode 100755 index 0000000..470ac86 --- /dev/null +++ b/based-simple-term/Makefile @@ -0,0 +1,57 @@ +# st - simple terminal +# See LICENSE file for copyright and license details. +.POSIX: + +include config.mk + +SRC = st.c x.c +OBJ = $(SRC:.c=.o) + +all: options st + +options: + @echo st build options: + @echo "CFLAGS = $(STCFLAGS)" + @echo "LDFLAGS = $(STLDFLAGS)" + @echo "CC = $(CC)" + +config.h: + cp config.def.h config.h + +.c.o: + $(CC) $(STCFLAGS) -c $< + +st.o: config.h st.h win.h +x.o: arg.h config.h st.h win.h + +$(OBJ): config.h config.mk + +st: $(OBJ) + $(CC) -o $@ $(OBJ) $(STLDFLAGS) + +clean: + rm -f st $(OBJ) st-$(VERSION).tar.gz + +dist: clean + mkdir -p st-$(VERSION) + cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ + config.def.h st.info st.1 arg.h st.h win.h $(SRC)\ + st-$(VERSION) + tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz + rm -rf st-$(VERSION) + +install: st + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f st $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/st + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 + tic -sx st.info + @echo Please see the README file regarding the terminfo entry of st. + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/st + rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 + +.PHONY: all options clean dist install uninstall diff --git a/based-simple-term/README b/based-simple-term/README new file mode 100755 index 0000000..6a846ed --- /dev/null +++ b/based-simple-term/README @@ -0,0 +1,34 @@ +st - simple terminal +-------------------- +st is a simple terminal emulator for X which sucks less. + + +Requirements +------------ +In order to build st you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (st is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install st (if +necessary as root): + + make clean install + + +Running st +---------- +If you did not install st with make clean install, you must compile +the st terminfo entry with the following command: + + tic -sx st.info + +See the man page for additional details. + +Credits +------- +Based on Aurélien APTEL bt source code. + diff --git a/based-simple-term/arg.h b/based-simple-term/arg.h new file mode 100755 index 0000000..a22e019 --- /dev/null +++ b/based-simple-term/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/based-simple-term/config.def.h b/based-simple-term/config.def.h new file mode 100755 index 0000000..b2c51ab --- /dev/null +++ b/based-simple-term/config.def.h @@ -0,0 +1,520 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "Liberation Mono:pixelsize=14:antialias=true:autohint=true"; +static int borderpx = 1; + +/* + * background image + * expects farbfeld format + * pseudo transparency fixes coordinates to the screen origin + */ +static const char *bgfile = "/path/to/image.ff"; +static const int pseudotransparency = 0; + +/* + * 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/bash"; +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; + +/* + * Synchronized-Update timeout in ms + * https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec + */ +static uint su_timeout = 200; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 1; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 7; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 40; + +/* default TERM value */ +char *termname = "st-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +/* bg opacity */ +float alpha = 0.8; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "black", + "red3", + "green3", + "yellow3", + "blue2", + "magenta3", + "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", + "red", + "green", + "yellow", + "#5c5cff", + "magenta", + "cyan", + "white", + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#cccccc", + "#555555", + "gray90", /* default foreground colour */ + "black", /* default background colour */ +}; + + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 258; +unsigned int defaultbg = 259; +unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 7; + +/* + * 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, Button4, kscrollup, {.i = 1}, 0, /* !alt */ -1 }, + { XK_ANY_MOD, Button5, kscrolldown, {.i = 1}, 0, /* !alt */ -1 }, + { 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[3~", -1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { 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[3~", -1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { 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{|}~"; + +/** + * Undercurl style. Set UNDERCURL_STYLE to one of the available styles. + * + * Curly: Dunno how to draw it *shrug* + * _ _ _ _ + * ( ) ( ) ( ) ( ) + * (_) (_) (_) (_) + * + * Spiky: + * /\ /\ /\ /\ + * \/ \/ \/ + * + * Capped: + * _ _ _ + * / \ / \ / \ + * \_/ \_/ + */ +// Available styles +#define UNDERCURL_CURLY 0 +#define UNDERCURL_SPIKY 1 +#define UNDERCURL_CAPPED 2 +// Active style +#define UNDERCURL_STYLE UNDERCURL_SPIKY diff --git a/based-simple-term/config.h b/based-simple-term/config.h new file mode 100755 index 0000000..7f20cde --- /dev/null +++ b/based-simple-term/config.h @@ -0,0 +1,520 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "Liberation Mono:pixelsize=14:antialias=true:autohint=true"; +static int borderpx = 1; + +/* + * background image + * expects farbfeld format + * pseudo transparency fixes coordinates to the screen origin + */ +static const char *bgfile = "/path/to/image.ff"; +static const int pseudotransparency = 0; + +/* + * 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/bash"; +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; + +/* + * Synchronized-Update timeout in ms + * https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec + */ +static uint su_timeout = 200; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 5; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 7; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 40; + +/* default TERM value */ +char *termname = "st-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +/* bg opacity */ +float alpha = 0.8; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "black", + "red3", + "green3", + "yellow3", + "blue2", + "magenta3", + "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", + "red", + "green", + "yellow", + "#5c5cff", + "magenta", + "cyan", + "white", + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#cccccc", + "#555555", + "gray90", /* default foreground colour */ + "black", /* default background colour */ +}; + + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 258; +unsigned int defaultbg = 259; +unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 7; + +/* + * 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, Button4, kscrollup, {.i = 1}, 0, /* !alt */ -1 }, + { XK_ANY_MOD, Button5, kscrolldown, {.i = 1}, 0, /* !alt */ -1 }, + { 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[3~", -1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { 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[3~", -1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { 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{|}~"; + +/** + * Undercurl style. Set UNDERCURL_STYLE to one of the available styles. + * + * Curly: Dunno how to draw it *shrug* + * _ _ _ _ + * ( ) ( ) ( ) ( ) + * (_) (_) (_) (_) + * + * Spiky: + * /\ /\ /\ /\ + * \/ \/ \/ + * + * Capped: + * _ _ _ + * / \ / \ / \ + * \_/ \_/ + */ +// Available styles +#define UNDERCURL_CURLY 0 +#define UNDERCURL_SPIKY 1 +#define UNDERCURL_CAPPED 2 +// Active style +#define UNDERCURL_STYLE UNDERCURL_SPIKY diff --git a/based-simple-term/config.mk b/based-simple-term/config.mk new file mode 100755 index 0000000..f0ccc70 --- /dev/null +++ b/based-simple-term/config.mk @@ -0,0 +1,35 @@ +# st version +VERSION = based + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +PKG_CONFIG = pkg-config + +# includes and libs +INCS = -I$(X11INC) \ + `$(PKG_CONFIG) --cflags fontconfig` \ + `$(PKG_CONFIG) --cflags freetype2` +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ + `$(PKG_CONFIG) --libs fontconfig` \ + `$(PKG_CONFIG) --libs freetype2` + +# flags +STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 +STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) +STLDFLAGS = $(LIBS) $(LDFLAGS) + +# OpenBSD: +CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE +LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ + `$(PKG_CONFIG) --libs fontconfig` \ + `$(PKG_CONFIG) --libs freetype2` + +# compiler and linker +# CC = c99 diff --git a/based-simple-term/config.mk~ b/based-simple-term/config.mk~ new file mode 100755 index 0000000..761461a --- /dev/null +++ b/based-simple-term/config.mk~ @@ -0,0 +1,35 @@ +# st version +VERSION = based + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +PKG_CONFIG = pkg-config + +# includes and libs +INCS = -I$(X11INC) \ + `$(PKG_CONFIG) --cflags fontconfig` \ + `$(PKG_CONFIG) --cflags freetype2` +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ + `$(PKG_CONFIG) --libs fontconfig` \ + `$(PKG_CONFIG) --libs freetype2` + +# flags +STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 +STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) +STLDFLAGS = $(LIBS) $(LDFLAGS) + +# OpenBSD: +#CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE +#LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ +# `$(PKG_CONFIG) --libs fontconfig` \ +# `$(PKG_CONFIG) --libs freetype2` + +# compiler and linker +# CC = c99 diff --git a/based-simple-term/patches/st-alpha-20220206-0.8.5.diff b/based-simple-term/patches/st-alpha-20220206-0.8.5.diff new file mode 100755 index 0000000..ab029f6 --- /dev/null +++ b/based-simple-term/patches/st-alpha-20220206-0.8.5.diff @@ -0,0 +1,146 @@ +diff --git a/config.def.h b/config.def.h +index 91ab8ca..6af616e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -93,6 +93,9 @@ char *termname = "st-256color"; + */ + unsigned int tabspaces = 8; + ++/* bg opacity */ ++float alpha = 0.8; ++ + /* Terminal colors (16 first used in escape sequence) */ + static const char *colorname[] = { + /* 8 normal colors */ +diff --git a/config.mk b/config.mk +index 4c4c5d5..0114bad 100644 +--- a/config.mk ++++ b/config.mk +@@ -16,7 +16,7 @@ PKG_CONFIG = pkg-config + INCS = -I$(X11INC) \ + `$(PKG_CONFIG) --cflags fontconfig` \ + `$(PKG_CONFIG) --cflags freetype2` +-LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ ++LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ + `$(PKG_CONFIG) --libs fontconfig` \ + `$(PKG_CONFIG) --libs freetype2` + +diff --git a/st.h b/st.h +index 519b9bd..8bb533d 100644 +--- a/st.h ++++ b/st.h +@@ -126,3 +126,4 @@ extern unsigned int tabspaces; + extern unsigned int defaultfg; + extern unsigned int defaultbg; + extern unsigned int defaultcs; ++extern float alpha; +diff --git a/x.c b/x.c +index 8a16faa..ddf4178 100644 +--- a/x.c ++++ b/x.c +@@ -105,6 +105,7 @@ typedef struct { + XSetWindowAttributes attrs; + int scr; + int isfixed; /* is fixed geometry? */ ++ int depth; /* bit depth */ + int l, t; /* left and top offset */ + int gm; /* geometry mask */ + } XWindow; +@@ -243,6 +244,7 @@ static char *usedfont = NULL; + static double usedfontsize = 0; + static double defaultfontsize = 0; + ++static char *opt_alpha = NULL; + static char *opt_class = NULL; + static char **opt_cmd = NULL; + static char *opt_embed = NULL; +@@ -736,7 +738,7 @@ xresize(int col, int row) + + XFreePixmap(xw.dpy, xw.buf); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); ++ xw.depth); + XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + +@@ -796,6 +798,13 @@ xloadcols(void) + else + die("could not allocate color %d\n", i); + } ++ ++ /* set alpha value of bg color */ ++ if (opt_alpha) ++ alpha = strtof(opt_alpha, NULL); ++ dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); ++ dc.col[defaultbg].pixel &= 0x00FFFFFF; ++ dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; + loaded = 1; + } + +@@ -1118,11 +1127,23 @@ xinit(int cols, int rows) + Window parent; + pid_t thispid = getpid(); + XColor xmousefg, xmousebg; ++ XWindowAttributes attr; ++ XVisualInfo vis; + + if (!(xw.dpy = XOpenDisplay(NULL))) + die("can't open display\n"); + xw.scr = XDefaultScreen(xw.dpy); +- xw.vis = XDefaultVisual(xw.dpy, xw.scr); ++ ++ if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { ++ parent = XRootWindow(xw.dpy, xw.scr); ++ xw.depth = 32; ++ } else { ++ XGetWindowAttributes(xw.dpy, parent, &attr); ++ xw.depth = attr.depth; ++ } ++ ++ XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); ++ xw.vis = vis.visual; + + /* font */ + if (!FcInit()) +@@ -1132,7 +1153,7 @@ xinit(int cols, int rows) + xloadfonts(usedfont, 0); + + /* colors */ +- xw.cmap = XDefaultColormap(xw.dpy, xw.scr); ++ xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); + xloadcols(); + + /* adjust fixed window geometry */ +@@ -1152,19 +1173,15 @@ xinit(int cols, int rows) + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + xw.attrs.colormap = xw.cmap; + +- if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) +- parent = XRootWindow(xw.dpy, xw.scr); + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, +- win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, ++ win.w, win.h, 0, xw.depth, InputOutput, + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &xw.attrs); + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; +- dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, +- &gcvalues); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); ++ xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); ++ dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + +@@ -2019,6 +2036,9 @@ main(int argc, char *argv[]) + case 'a': + allowaltscreen = 0; + break; ++ case 'A': ++ opt_alpha = EARGF(usage()); ++ break; + case 'c': + opt_class = EARGF(usage()); + break; diff --git a/based-simple-term/patches/st-appsync-20200618-b27a383.diff b/based-simple-term/patches/st-appsync-20200618-b27a383.diff new file mode 100755 index 0000000..4736325 --- /dev/null +++ b/based-simple-term/patches/st-appsync-20200618-b27a383.diff @@ -0,0 +1,259 @@ +From 8c9c920325fa10440a96736ba58ec647a0365e22 Mon Sep 17 00:00:00 2001 +From: "Avi Halachmi (:avih)" +Date: Sat, 18 Apr 2020 13:56:11 +0300 +Subject: [PATCH] application-sync: support Synchronized-Updates + +See https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec + +In a nutshell: allow an application to suspend drawing until it has +completed some output - so that the terminal will not flicker/tear by +rendering partial content. If the end-of-suspension sequence doesn't +arrive, the terminal bails out after a timeout (default: 200 ms). + +The feature is supported and pioneered by iTerm2. There are probably +very few other terminals or applications which support this feature +currently. + +One notable application which does support it is tmux (master as of +2020-04-18) - where cursor flicker is completely avoided when a pane +has new content. E.g. run in one pane: `while :; do cat x.c; done' +while the cursor is at another pane. + +The terminfo string `Sync' added to `st.info' is also a tmux extension +which tmux detects automatically when `st.info` is installed. + +Notes: + +- Draw-suspension begins on BSU sequence (Begin-Synchronized-Update), + and ends on ESU sequence (End-Synchronized-Update). + +- BSU, ESU are "\033P=1s\033\\", "\033P=2s\033\\" respectively (DCS). + +- SU doesn't support nesting - BSU begins or extends, ESU always ends. + +- ESU without BSU is ignored. + +- BSU after BSU extends (resets the timeout), so an application could + send BSU in a loop and keep drawing suspended - exactly like it can + not-draw anything in a loop. But as soon as it exits/aborted then + drawing is resumed according to the timeout even without ESU. + +- This implementation focuses on ESU and doesn't really care about BSU + in the sense that it tries hard to draw exactly once ESU arrives (if + it's not too soon after the last draw - according to minlatency), + and doesn't try to draw the content just up-to BSU. These two sides + complement eachother - not-drawing on BSU increases the chance that + ESU is not too soon after the last draw. This approach was chosen + because the application's main focus is that ESU indicates to the + terminal that the content is now ready - and that's when we try to + draw. +--- + config.def.h | 6 ++++++ + st.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- + st.info | 1 + + x.c | 22 +++++++++++++++++++--- + 4 files changed, 72 insertions(+), 5 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 6f05dce..80d768e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -56,6 +56,12 @@ int allowwindowops = 0; + static double minlatency = 8; + static double maxlatency = 33; + ++/* ++ * Synchronized-Update timeout in ms ++ * https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec ++ */ ++static uint su_timeout = 200; ++ + /* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. +diff --git a/st.c b/st.c +index 76b7e0d..0582e77 100644 +--- a/st.c ++++ b/st.c +@@ -231,6 +231,33 @@ static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; + static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; + static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + ++#include ++static int su = 0; ++struct timespec sutv; ++ ++static void ++tsync_begin() ++{ ++ clock_gettime(CLOCK_MONOTONIC, &sutv); ++ su = 1; ++} ++ ++static void ++tsync_end() ++{ ++ su = 0; ++} ++ ++int ++tinsync(uint timeout) ++{ ++ struct timespec now; ++ if (su && !clock_gettime(CLOCK_MONOTONIC, &now) ++ && TIMEDIFF(now, sutv) >= timeout) ++ su = 0; ++ return su; ++} ++ + ssize_t + xwrite(int fd, const char *s, size_t len) + { +@@ -818,6 +845,9 @@ ttynew(char *line, char *cmd, char *out, char **args) + return cmdfd; + } + ++static int twrite_aborted = 0; ++int ttyread_pending() { return twrite_aborted; } ++ + size_t + ttyread(void) + { +@@ -826,7 +856,7 @@ ttyread(void) + int ret, written; + + /* append read bytes to unprocessed bytes */ +- ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); ++ ret = twrite_aborted ? 1 : read(cmdfd, buf+buflen, LEN(buf)-buflen); + + switch (ret) { + case 0: +@@ -834,7 +864,7 @@ ttyread(void) + case -1: + die("couldn't read from shell: %s\n", strerror(errno)); + default: +- buflen += ret; ++ buflen += twrite_aborted ? 0 : ret; + written = twrite(buf, buflen, 0); + buflen -= written; + /* keep any incomplete UTF-8 byte sequence for the next call */ +@@ -994,6 +1024,7 @@ tsetdirtattr(int attr) + void + tfulldirt(void) + { ++ tsync_end(); + tsetdirt(0, term.row-1); + } + +@@ -1895,6 +1926,12 @@ strhandle(void) + xsettitle(strescseq.args[0]); + return; + case 'P': /* DCS -- Device Control String */ ++ /* https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec */ ++ if (strstr(strescseq.buf, "=1s") == strescseq.buf) ++ tsync_begin(); /* BSU */ ++ else if (strstr(strescseq.buf, "=2s") == strescseq.buf) ++ tsync_end(); /* ESU */ ++ return; + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + return; +@@ -2436,6 +2473,9 @@ twrite(const char *buf, int buflen, int show_ctrl) + Rune u; + int n; + ++ int su0 = su; ++ twrite_aborted = 0; ++ + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8)) { + /* process a complete utf8 char */ +@@ -2446,6 +2486,10 @@ twrite(const char *buf, int buflen, int show_ctrl) + u = buf[n] & 0xFF; + charsize = 1; + } ++ if (su0 && !su) { ++ twrite_aborted = 1; ++ break; // ESU - allow rendering before a new BSU ++ } + if (show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; +diff --git a/st.info b/st.info +index 8201ad6..b32b446 100644 +--- a/st.info ++++ b/st.info +@@ -191,6 +191,7 @@ st-mono| simpleterm monocolor, + Ms=\E]52;%p1%s;%p2%s\007, + Se=\E[2 q, + Ss=\E[%p1%d q, ++ Sync=\EP=%p1%ds\E\\, + + st| simpleterm, + use=st-mono, +diff --git a/x.c b/x.c +index 210f184..27ff4e2 100644 +--- a/x.c ++++ b/x.c +@@ -1861,6 +1861,9 @@ resize(XEvent *e) + cresize(e->xconfigure.width, e->xconfigure.height); + } + ++int tinsync(uint); ++int ttyread_pending(); ++ + void + run(void) + { +@@ -1895,7 +1898,7 @@ run(void) + FD_SET(ttyfd, &rfd); + FD_SET(xfd, &rfd); + +- if (XPending(xw.dpy)) ++ if (XPending(xw.dpy) || ttyread_pending()) + timeout = 0; /* existing events might not set xfd */ + + seltv.tv_sec = timeout / 1E3; +@@ -1909,7 +1912,8 @@ run(void) + } + clock_gettime(CLOCK_MONOTONIC, &now); + +- if (FD_ISSET(ttyfd, &rfd)) ++ int ttyin = FD_ISSET(ttyfd, &rfd) || ttyread_pending(); ++ if (ttyin) + ttyread(); + + xev = 0; +@@ -1933,7 +1937,7 @@ run(void) + * maximum latency intervals during `cat huge.txt`, and perfect + * sync with periodic updates from animations/key-repeats/etc. + */ +- if (FD_ISSET(ttyfd, &rfd) || xev) { ++ if (ttyin || xev) { + if (!drawing) { + trigger = now; + drawing = 1; +@@ -1944,6 +1948,18 @@ run(void) + continue; /* we have time, try to find idle */ + } + ++ if (tinsync(su_timeout)) { ++ /* ++ * on synchronized-update draw-suspension: don't reset ++ * drawing so that we draw ASAP once we can (just after ++ * ESU). it won't be too soon because we already can ++ * draw now but we skip. we set timeout > 0 to draw on ++ * SU-timeout even without new content. ++ */ ++ timeout = minlatency; ++ continue; ++ } ++ + /* idle detected or maxlatency exhausted -> draw */ + timeout = -1; + if (blinktimeout && tattrset(ATTR_BLINK)) { + +base-commit: b27a383a3acc7decf00e6e889fca265430b5d329 +-- +2.17.1 + diff --git a/based-simple-term/patches/st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff b/based-simple-term/patches/st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff new file mode 100755 index 0000000..c41506f --- /dev/null +++ b/based-simple-term/patches/st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff @@ -0,0 +1,601 @@ +diff -uraN st-0.8.4/autocomplete.h st-autocomplete/autocomplete.h +--- st-0.8.4/autocomplete.h 1970-01-01 04:00:00.000000000 +0400 ++++ st-autocomplete/autocomplete.h 2021-12-14 20:20:03.322050025 +0400 +@@ -0,0 +1,16 @@ ++# ifndef __ST_AUTOCOMPLETE_H ++# define __ST_AUTOCOMPLETE_H ++ ++enum { ++ ACMPL_DEACTIVATE, ++ ACMPL_WORD, ++ ACMPL_WWORD, ++ ACMPL_FUZZY_WORD, ++ ACMPL_FUZZY_WWORD, ++ ACMPL_FUZZY, ++ ACMPL_SUFFIX, ++ ACMPL_SURROUND, ++ ACMPL_UNDO, ++}; ++ ++# endif // __ST_AUTOCOMPLETE_H +diff -uraN st-0.8.4/config.def.h st-autocomplete/config.def.h +--- st-0.8.4/config.def.h 2021-12-14 20:03:03.981322164 +0400 ++++ st-autocomplete/config.def.h 2021-12-14 20:22:30.088821478 +0400 +@@ -168,6 +168,8 @@ + */ + static uint forcemousemod = ShiftMask; + ++# include "autocomplete.h" ++ + /* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. +@@ -199,6 +201,14 @@ + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, ++ { ControlMask|Mod1Mask, XK_slash, autocomplete, { .i = ACMPL_WORD } }, ++ { ControlMask|Mod1Mask, XK_period, autocomplete, { .i = ACMPL_FUZZY_WORD } }, ++ { ControlMask|Mod1Mask, XK_comma, autocomplete, { .i = ACMPL_FUZZY } }, ++ { ControlMask|Mod1Mask, XK_apostrophe, autocomplete, { .i = ACMPL_SUFFIX } }, ++ { ControlMask|Mod1Mask, XK_semicolon, autocomplete, { .i = ACMPL_SURROUND } }, ++ { ControlMask|Mod1Mask, XK_bracketright,autocomplete, { .i = ACMPL_WWORD } }, ++ { ControlMask|Mod1Mask, XK_bracketleft, autocomplete, { .i = ACMPL_FUZZY_WWORD } }, ++ { ControlMask|Mod1Mask, XK_equal, autocomplete, { .i = ACMPL_UNDO } }, + }; + + /* +diff -uraN st-0.8.4/Makefile st-autocomplete/Makefile +--- st-0.8.4/Makefile 2021-12-14 20:03:03.981322164 +0400 ++++ st-autocomplete/Makefile 2021-12-15 07:12:40.291573671 +0400 +@@ -44,6 +44,8 @@ + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f st $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/st ++ cp -f st-autocomplete $(DESTDIR)$(PREFIX)/bin ++ chmod 755 $(DESTDIR)$(PREFIX)/bin/st-autocomplete + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 +@@ -52,6 +54,7 @@ + + uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/st ++ rm -f $(DESTDIR)$(PREFIX)/bin/st-autocomplete + rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 + + .PHONY: all options clean dist install uninstall +diff -uraN st-0.8.4/st-autocomplete st-autocomplete/st-autocomplete +--- st-0.8.4/st-autocomplete 1970-01-01 04:00:00.000000000 +0400 ++++ st-autocomplete/st-autocomplete 2021-12-14 20:18:13.171971360 +0400 +@@ -0,0 +1,266 @@ ++#!/usr/bin/perl ++######################################################################### ++# Copyright (C) 2012-2021 Wojciech Siewierski, Gaspar Vardanyan # ++# # ++# This program is free software: you can redistribute it and/or modify # ++# it under the terms of the GNU General Public License as published by # ++# the Free Software Foundation, either version 3 of the License, or # ++# (at your option) any later version. # ++# # ++# This program is distributed in the hope that it will be useful, # ++# but WITHOUT ANY WARRANTY; without even the implied warranty of # ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # ++# GNU General Public License for more details. # ++# # ++# You should have received a copy of the GNU General Public License # ++# along with this program. If not, see . # ++######################################################################### ++ ++my ($cmd, $cursor_row, $cursor_column) = @ARGV; ++ ++# A reference to a function that transforms the completed word ++# into a regex matching the completions. Usually generated by ++# generate_matcher(). ++# ++# For example ++# $fun = generate_matcher(".*"); ++# $fun->("foo"); ++# would return "f.*o.*o" ++# ++# In other words, indirectly decides which characters can ++# appear in the completion. ++my $matcher; ++ ++# A regular expression matching a character before each match. ++# For example, it you want to match the text after a ++# whitespace, set it to "\s". ++my $char_class_before; ++ ++# A regular expression matching every character in the entered ++# text that will be used to find matching completions. Usually ++# "\w" or similar. ++my $char_class_to_complete; ++ ++# A regular expression matching every allowed last character ++# of the completion (uses greedy matching). ++my $char_class_at_end; ++ ++if ($cmd eq 'word-complete') { ++ # Basic word completion. Completes the current word ++ # without any special matching. ++ $char_class_before = '[^-\w]'; ++ $matcher = sub { quotemeta shift }; # identity ++ $char_class_at_end = '[-\w]'; ++ $char_class_to_complete = '[-\w]'; ++} elsif ($cmd eq 'WORD-complete') { ++ # The same as above but in the Vim meaning of a "WORD" -- ++ # whitespace delimited. ++ $char_class_before = '\s'; ++ $matcher = sub { quotemeta shift }; ++ $char_class_at_end = '\S'; ++ $char_class_to_complete = '\S'; ++} elsif ($cmd eq 'fuzzy-word-complete' || ++ $cmd eq 'skeleton-word-complete') { ++ # Fuzzy completion of the current word. ++ $char_class_before = '[^-\w]'; ++ $matcher = generate_matcher('[-\w]*'); ++ $char_class_at_end = '[-\w]'; ++ $char_class_to_complete = '[-\w]'; ++} elsif ($cmd eq 'fuzzy-WORD-complete') { ++ # Fuzzy completion of the current WORD. ++ $char_class_before = '\s'; ++ $matcher = generate_matcher('\S*'); ++ $char_class_at_end = '\S'; ++ $char_class_to_complete = '\S'; ++} elsif ($cmd eq 'fuzzy-complete' || ++ $cmd eq 'skeleton-complete') { ++ # Fuzzy completion of an arbitrary text. ++ $char_class_before = '\W'; ++ $matcher = generate_matcher('.*?'); ++ $char_class_at_end = '\w'; ++ $char_class_to_complete = '\S'; ++} elsif ($cmd eq 'suffix-complete') { ++ # Fuzzy completion of an completing suffixes, like ++ # completing test=hello from /blah/hello. ++ $char_class_before = '\S'; ++ $matcher = generate_matcher('\S*'); ++ $char_class_at_end = '\S'; ++ $char_class_to_complete = '\S'; ++} elsif ($cmd eq 'surround-complete') { ++ # Completing contents of quotes and braces. ++ ++ # Here we are using three named groups: s, b, p for quotes, braces ++ # and parenthesis. ++ $char_class_before = '((?["\'`])|(?\[)|(?

\())'; ++ ++ $matcher = generate_matcher('.*?'); ++ ++ # Here we match text till enclosing pair, using perl conditionals in ++ # regexps (?(condition)yes-expression|no-expression). ++ # \0 is used to hack concatenation with '*' later in the code. ++ $char_class_at_end = '.*?(.(?=(?()\]|((?(

)\)|\g{q})))))\0'; ++ $char_class_to_complete = '\S'; ++} ++ ++my $lines = []; ++ ++while () ++{ ++ push @{$lines}, $_; ++} ++ ++# read the word behind the cursor ++$_ = substr(@{$lines} [$cursor_row], 0, $cursor_column); # get the current line up to the cursor... ++s/.*?($char_class_to_complete*)$/$1/; # ...and read the last word from it ++my $word_to_complete = $_; ++ ++# ignore the completed word itself ++$self->{already_completed}{$word_to_complete} = 1; ++ ++print stdout "$word_to_complete\n"; ++ ++# search for matches ++while (my $completion = find_match($self, ++ $word_to_complete, ++ $self->{next_row} // $cursor_row, ++ $matcher->($word_to_complete), ++ $char_class_before, ++ $char_class_at_end) ++) { ++ calc_match_coords($self, ++ $self->{next_row}+1, ++ $completion); ++ print stdout "$completion @{$self->{highlight}}\n"; ++} ++ ++leave($self); ++ ++ ++ ++###################################################################### ++ ++# Finds the next matching completion in the row current row or above ++# while skipping duplicates using skip_duplicates(). ++sub find_match { ++ my ($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end) = @_; ++ $self->{matches_in_row} //= []; ++ ++ # cycle through all the matches in the current row if not starting a new search ++ if (@{$self->{matches_in_row}}) { ++ return skip_duplicates($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end); ++ } ++ ++ ++ my $i; ++ # search through all the rows starting with current one or one above the last checked ++ for ($i = $current_row; $i >= 0; --$i) { ++ my $line = @{$lines} [$i]; # get the line of text from the row ++ ++ if ($i == $cursor_row) { ++ $line = substr $line, 0, $cursor_column; ++ } ++ ++ $_ = $line; ++ ++ # find all the matches in the current line ++ my $match; ++ push @{$self->{matches_in_row}}, $+{match} while ($_, $match) = / ++ (.*${char_class_before}) ++ (? ++ ${regexp} ++ ${char_class_at_end}* ++ ) ++ /ix; ++ # corner case: match at the very beginning of line ++ push @{$self->{matches_in_row}}, $+{match} if $line =~ /^(${char_class_before}){0}(?$regexp$char_class_at_end*)/i; ++ ++ if (@{$self->{matches_in_row}}) { ++ # remember which row should be searched next ++ $self->{next_row} = --$i; ++ ++ # arguments needed for find_match() mutual recursion ++ return skip_duplicates($self, $word_to_match, $i, $regexp, $char_class_before, $char_class_at_end); ++ } ++ } ++ ++ # no more possible completions, revert to the original word ++ $self->{next_row} = -1 if $i < 0; ++ ++ return undef; ++} ++ ++###################################################################### ++ ++# Checks whether the completion found by find_match() was already ++# found and if it was, calls find_match() again to find the next ++# completion. ++# ++# Takes all the arguments that find_match() would take, to make a ++# mutually recursive call. ++sub skip_duplicates { ++ my $self = $_[0]; ++ my $completion = shift @{$self->{matches_in_row}}; # get the rightmost one ++ ++ # check for duplicates ++ if (exists $self->{already_completed}{$completion}) { ++ # skip this completion ++ return find_match(@_); ++ } else { ++ $self->{already_completed}{$completion} = 1; ++ return $completion; ++ } ++} ++ ++###################################################################### ++ ++# Returns a function that takes a string and returns that string with ++# this function's argument inserted between its every two characters. ++# The resulting string is used as a regular expression matching the ++# completion candidates. ++sub generate_matcher { ++ my $regex_between = shift; ++ ++ sub { ++ $_ = shift; ++ ++ # sorry for this lispy code, I couldn't resist ;) ++ (join "$regex_between", ++ (map quotemeta, ++ (split //))) ++ } ++} ++ ++###################################################################### ++ ++sub calc_match_coords { ++ my ($self, $linenum, $completion) = @_; ++ ++ my $line = @{$lines} [$linenum]; ++ my $re = quotemeta $completion; ++ ++ $line =~ /$re/; ++ ++ #my ($beg_row, $beg_col) = $line->coord_of($-[0]); ++ #my ($end_row, $end_col) = $line->coord_of($+[0]); ++ my $beg = $-[0]; ++ my $end = $+[0]; ++ ++ if (exists $self->{highlight}) { ++ delete $self->{highlight}; ++ } ++ # () # TODO: what does () do in perl ???? ++ ++ # $self->{highlight} = [$beg_row, $beg_col, $end_row, $end_col]; ++ $self->{highlight} = [$linenum, $beg, $end]; ++} ++ ++###################################################################### ++ ++sub leave { ++ my ($self) = @_; ++ ++ delete $self->{next_row}; ++ delete $self->{matches_in_row}; ++ delete $self->{already_completed}; ++ delete $self->{highlight}; ++} +diff -uraN st-0.8.4/st.c st-autocomplete/st.c +--- st-0.8.4/st.c 2021-12-14 20:03:03.981322164 +0400 ++++ st-autocomplete/st.c 2021-12-15 07:44:05.609586643 +0400 +@@ -17,6 +17,7 @@ + #include + #include + ++#include "autocomplete.h" + #include "st.h" + #include "win.h" + +@@ -2476,6 +2477,9 @@ + return; + } + ++ if ( row < term.row || col < term.col ) ++ autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); ++ + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to +@@ -2595,3 +2599,211 @@ + tfulldirt(); + draw(); + } ++ ++void autocomplete (const Arg * arg) ++{ ++ static _Bool active = 0; ++ ++ int acmpl_cmdindex = arg -> i; ++ ++ static int acmpl_cmdindex_prev; ++ ++ if (active == 0) ++ acmpl_cmdindex_prev = acmpl_cmdindex; ++ ++ static const char * const (acmpl_cmd []) = { ++ [ACMPL_DEACTIVATE] = "__DEACTIVATE__", ++ [ACMPL_WORD] = "word-complete", ++ [ACMPL_WWORD] = "WORD-complete", ++ [ACMPL_FUZZY_WORD] = "fuzzy-word-complete", ++ [ACMPL_FUZZY_WWORD] = "fuzzy-WORD-complete", ++ [ACMPL_FUZZY] = "fuzzy-complete", ++ [ACMPL_SUFFIX] = "suffix-complete", ++ [ACMPL_SURROUND] = "surround-complete", ++ [ACMPL_UNDO] = "__UNDO__", ++ }; ++ ++ static char acmpl [1000]; // ACMPL_ISSUE: why 1000? ++ ++ static FILE * acmpl_exec = NULL; ++ static int acmpl_status; ++ ++ static const char * stbuffile; ++ static char target [1000]; // ACMPL_ISSUE: why 1000? dynamically allocate char array of size term.col ++ static size_t targetlen; ++ ++ static char completion [1000] = {0}; // ACMPL_ISSUE: why 1000? dynamically allocate char array of size term.col ++ static size_t complen_prev = 0; // NOTE: always clear this variable after clearing completion ++ ++// Check for deactivation ++ ++ if (acmpl_cmdindex == ACMPL_DEACTIVATE) ++ { ++ ++// Deactivate autocomplete mode keeping current completion ++ ++ if (active) ++ { ++ active = 0; ++ pclose (acmpl_exec); ++ remove (stbuffile); ++ ++ if (complen_prev) ++ { ++ selclear (); ++ complen_prev = 0; ++ } ++ } ++ ++ return; ++ } ++ ++// Check for undo ++ ++ if (acmpl_cmdindex == ACMPL_UNDO) ++ { ++ ++// Deactivate autocomplete mode recovering target ++ ++ if (active) ++ { ++ active = 0; ++ pclose (acmpl_exec); ++ remove (stbuffile); ++ ++ if (complen_prev) ++ { ++ selclear (); ++ for (size_t i = 0; i < complen_prev; i++) ++ ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is the right way ++ complen_prev = 0; ++ ttywrite (target, targetlen, 0); // ACMPL_ISSUE: I'm not sure that this is a right solution ++ } ++ } ++ ++ return; ++ } ++ ++// Check for command change ++ ++ if (acmpl_cmdindex != acmpl_cmdindex_prev) ++ { ++ ++// If command is changed, goto acmpl_begin avoiding rewriting st buffer ++ ++ if (active) ++ { ++ acmpl_cmdindex_prev = acmpl_cmdindex; ++ ++ goto acmpl_begin; ++ } ++ } ++ ++// If not active ++ ++ if (active == 0) ++ { ++ acmpl_cmdindex_prev = acmpl_cmdindex; ++ ++// Write st buffer to a temp file ++ ++ stbuffile = tmpnam (NULL); // check for return value ... ++ // ACMPL_ISSUE: use coprocesses instead of temp files ++ sprintf ( ++ acmpl, ++ "cat %100s | st-autocomplete %500s %d %d", // ACMPL_ISSUE: why 100 and 500? ++ stbuffile, ++ acmpl_cmd [acmpl_cmdindex], ++ term.c.y, ++ term.c.x ++ ); ++ ++ FILE * stbuf = fopen (stbuffile, "w"); // check for opening error ... ++ char * stbufline = malloc (term.col + 2); // check for allocating error ... ++ ++ for (size_t y = 0; y < term.row; y++) ++ { ++ size_t x = 0; ++ for (; x < term.col; x++) ++ utf8encode (term.line [y] [x].u, stbufline + x); ++ stbufline [x] = '\n'; ++ stbufline [x + 1] = 0; ++ fputs (stbufline, stbuf); ++ } ++ ++ free (stbufline); ++ fclose (stbuf); ++ ++acmpl_begin: ++ ++// Run st-autocomplete ++ ++ acmpl_exec = popen (acmpl, "r"); // ACMPL_ISSUE: popen isn't defined by The Standard. Does it work in BSDs for example? ++ // check for popen error ... ++ ++// Read the target, targetlen ++ ++ fscanf (acmpl_exec, "%500s\n", target); // check for scanning error ... ++ targetlen = strlen (target); ++ } ++ ++// Read a completion if exists (acmpl_status) ++ ++ unsigned line, beg, end; ++ ++ acmpl_status = fscanf (acmpl_exec, "%500s %u %u %u\n", completion, & line, & beg, & end); ++ // ACMPL_ISSUE: why 500? use term.col instead ++ ++// Exit if no completions found ++ ++ if (active == 0 && acmpl_status == EOF) ++ { ++ ++// Close st-autocomplete and exit without activating the autocomplete mode ++ ++ pclose (acmpl_exec); ++ remove (stbuffile); ++ return; ++ } ++ ++// If completions found, enable autocomplete mode and autocomplete the target ++ ++ active = 1; ++ ++// Clear target before first completion ++ ++ if (complen_prev == 0) ++ { ++ for (size_t i = 0; i < targetlen; i++) ++ ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is a right solution ++ } ++ ++// Clear previuos completion if this is not the first ++ ++ else ++ { ++ selclear (); ++ for (size_t i = 0; i < complen_prev; i++) ++ ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is a right solution ++ complen_prev = 0; ++ } ++ ++// If no more completions found, reset and restart ++ ++ if (acmpl_status == EOF) ++ { ++ active = 0; ++ pclose (acmpl_exec); ++ ttywrite (target, targetlen, 0); ++ goto acmpl_begin; ++ } ++ ++// Read the new completion and autcomplete ++ ++ selstart (beg, line, 0); ++ selextend (end - 1, line, 1, 0); ++ xsetsel (getsel ()); ++ ++ complen_prev = strlen (completion); ++ ttywrite (completion, complen_prev, 0); ++} +diff -uraN st-0.8.4/st.h st-autocomplete/st.h +--- st-0.8.4/st.h 2021-12-14 20:03:03.981322164 +0400 ++++ st-autocomplete/st.h 2021-12-14 20:28:59.272432749 +0400 +@@ -77,6 +77,8 @@ + const char *s; + } Arg; + ++void autocomplete (const Arg *); ++ + void die(const char *, ...); + void redraw(void); + void draw(void); +diff -uraN st-0.8.4/x.c st-autocomplete/x.c +--- st-0.8.4/x.c 2021-12-14 20:03:03.981322164 +0400 ++++ st-autocomplete/x.c 2021-12-14 20:30:30.045830893 +0400 +@@ -1803,11 +1803,15 @@ + /* 1. shortcuts */ + for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { + if (ksym == bp->keysym && match(bp->mod, e->state)) { ++ if (bp -> func != autocomplete) ++ autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); + bp->func(&(bp->arg)); + return; + } + } + ++ autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); ++ + /* 2. custom keys from config.h */ + if ((customkey = kmap(ksym, e->state))) { + ttywrite(customkey, strlen(customkey), 1); diff --git a/based-simple-term/patches/st-background-image-0.8.4.diff b/based-simple-term/patches/st-background-image-0.8.4.diff new file mode 100755 index 0000000..f32c947 --- /dev/null +++ b/based-simple-term/patches/st-background-image-0.8.4.diff @@ -0,0 +1,262 @@ +From 2c984d74ca15806dcfa174b7e75f48c0d01a49bf Mon Sep 17 00:00:00 2001 +From: Matthias Schoth +Date: Thu, 17 Feb 2022 00:23:23 +0100 +Subject: [PATCH] Implements background image and pseudo transparancy support. + +--- + config.def.h | 8 +++ + x.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++---- + 2 files changed, 139 insertions(+), 10 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 6f05dce..3d352db 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -8,6 +8,14 @@ + static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; + static int borderpx = 2; + ++/* ++ * background image ++ * expects farbfeld format ++ * pseudo transparency fixes coordinates to the screen origin ++ */ ++static const char *bgfile = "/path/to/image.ff"; ++static const int pseudotransparency = 0; ++ + /* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e +diff --git a/x.c b/x.c +index 210f184..5ecb8e5 100644 +--- a/x.c ++++ b/x.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + char *argv0; + #include "arg.h" +@@ -81,6 +82,7 @@ typedef XftGlyphFontSpec GlyphFontSpec; + typedef struct { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ ++ int x, y; /* window location */ + int ch; /* char height */ + int cw; /* char width */ + int mode; /* window state/mode flags */ +@@ -101,6 +103,7 @@ typedef struct { + XVaNestedList spotlist; + } ime; + Draw draw; ++ GC bggc; /* Graphics Context for background */ + Visual *vis; + XSetWindowAttributes attrs; + int scr; +@@ -151,6 +154,9 @@ 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 updatexy(void); ++static XImage *loadff(const char *); ++static void bginit(); + static void cresize(int, int); + static void xresize(int, int); + static void xhints(void); +@@ -502,6 +508,12 @@ propnotify(XEvent *e) + xpev->atom == clipboard)) { + selnotify(e); + } ++ ++ if (pseudotransparency && ++ !strncmp(XGetAtomName(xw.dpy, e->xproperty.atom), "_NET_WM_STATE", 13)) { ++ updatexy(); ++ redraw(); ++ } + } + + void +@@ -532,7 +544,8 @@ selnotify(XEvent *e) + return; + } + +- if (e->type == PropertyNotify && nitems == 0 && rem == 0) { ++ if (e->type == PropertyNotify && nitems == 0 && rem == 0 && ++ !pseudotransparency) { + /* + * If there is some PropertyNotify with no data, then + * this is the signal of the selection owner that all +@@ -550,9 +563,11 @@ selnotify(XEvent *e) + * 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, ++ if (!pseudotransparency) { ++ MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); ++ XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); ++ } + + /* + * Deleting the property is the transfer start signal. +@@ -820,9 +835,9 @@ xsetcolorname(int x, const char *name) + 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); ++ if (pseudotransparency) ++ XSetTSOrigin(xw.dpy, xw.bggc, -win.x, -win.y); ++ XFillRectangle(xw.dpy, xw.buf, xw.bggc, x1, y1, x2-x1, y2-y1); + } + + void +@@ -1207,6 +1222,100 @@ xinit(int cols, int rows) + xsel.xtarget = XA_STRING; + } + ++void ++updatexy() ++{ ++ Window child; ++ XTranslateCoordinates(xw.dpy, xw.win, DefaultRootWindow(xw.dpy), 0, 0, ++ &win.x, &win.y, &child); ++} ++ ++/* ++ * load farbfeld file to XImage ++ */ ++XImage* ++loadff(const char *filename) ++{ ++ uint32_t i, hdr[4], w, h, size; ++ uint64_t *data; ++ FILE *f = fopen(filename, "rb"); ++ ++ if (f == NULL) { ++ fprintf(stderr, "could not load background image.\n"); ++ return NULL; ++ } ++ ++ if (fread(hdr, sizeof(*hdr), LEN(hdr), f) != LEN(hdr)) ++ if (ferror(f)) { ++ fprintf(stderr, "fread:"); ++ return NULL; ++ } ++ else { ++ fprintf(stderr, "fread: Unexpected end of file\n"); ++ return NULL; ++ } ++ ++ if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) { ++ fprintf(stderr, "Invalid magic value"); ++ return NULL; ++ } ++ ++ w = ntohl(hdr[2]); ++ h = ntohl(hdr[3]); ++ size = w * h; ++ data = malloc(size * sizeof(uint64_t)); ++ ++ if (fread(data, sizeof(uint64_t), size, f) != size) ++ if (ferror(f)) { ++ fprintf(stderr, "fread:"); ++ return NULL; ++ } ++ else { ++ fprintf(stderr, "fread: Unexpected end of file"); ++ return NULL; ++ } ++ ++ fclose(f); ++ ++ for (i = 0; i < size; i++) ++ data[i] = (data[i] & 0x00000000000000FF) << 16 | ++ (data[i] & 0x0000000000FF0000) >> 8 | ++ (data[i] & 0x000000FF00000000) >> 32; ++ ++ XImage *xi = XCreateImage(xw.dpy, DefaultVisual(xw.dpy, xw.scr), ++ DefaultDepth(xw.dpy, xw.scr), ZPixmap, 0, ++ (char *)data, w, h, 32, w * 8); ++ xi->bits_per_pixel = 64; ++ return xi; ++} ++ ++/* ++ * initialize background image ++ */ ++void ++bginit() ++{ ++ XGCValues gcvalues; ++ Drawable bgimg; ++ XImage *bgxi = loadff(bgfile); ++ ++ memset(&gcvalues, 0, sizeof(gcvalues)); ++ xw.bggc = XCreateGC(xw.dpy, xw.win, 0, &gcvalues); ++ if (!bgxi) return; ++ bgimg = XCreatePixmap(xw.dpy, xw.win, bgxi->width, bgxi->height, ++ DefaultDepth(xw.dpy, xw.scr)); ++ XPutImage(xw.dpy, bgimg, dc.gc, bgxi, 0, 0, 0, 0, bgxi->width, ++ bgxi->height); ++ XDestroyImage(bgxi); ++ XSetTile(xw.dpy, xw.bggc, bgimg); ++ XSetFillStyle(xw.dpy, xw.bggc, FillTiled); ++ if (pseudotransparency) { ++ updatexy(); ++ MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); ++ XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); ++ } ++} ++ + int + xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) + { +@@ -1447,7 +1556,10 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i + 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); ++ if (bg == &dc.col[defaultbg]) ++ xclear(winx, winy, winx + width, winy + win.ch); ++ else ++ XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + + /* Set the clip region because Xft is sometimes dirty. */ + r.x = 0; +@@ -1855,9 +1967,17 @@ cmessage(XEvent *e) + void + resize(XEvent *e) + { +- if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) +- return; +- ++ if (pseudotransparency) { ++ if (e->xconfigure.width == win.w && ++ e->xconfigure.height == win.h && ++ e->xconfigure.x == win.x && e->xconfigure.y == win.y) ++ return; ++ updatexy(); ++ } else { ++ if (e->xconfigure.width == win.w && ++ e->xconfigure.height == win.h) ++ return; ++ } + cresize(e->xconfigure.width, e->xconfigure.height); + } + +@@ -2041,6 +2161,7 @@ run: + rows = MAX(rows, 1); + tnew(cols, rows); + xinit(cols, rows); ++ bginit(); + xsetenv(); + selinit(); + run(); +-- +2.35.1 + diff --git a/based-simple-term/patches/st-delkey-20201112-4ef0cbd.diff b/based-simple-term/patches/st-delkey-20201112-4ef0cbd.diff new file mode 100755 index 0000000..c334b0d --- /dev/null +++ b/based-simple-term/patches/st-delkey-20201112-4ef0cbd.diff @@ -0,0 +1,20 @@ +--- config.def.h.orig 2020-11-12 20:23:48.867954750 +0100 ++++ config.def.h 2020-11-12 20:21:15.055922720 +0100 +@@ -276,7 +276,7 @@ + { 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_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}, +@@ -344,7 +344,7 @@ + { 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_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, diff --git a/based-simple-term/patches/st-dynamic-cursor-color-0.8.4.diff b/based-simple-term/patches/st-dynamic-cursor-color-0.8.4.diff new file mode 100755 index 0000000..a763d6d --- /dev/null +++ b/based-simple-term/patches/st-dynamic-cursor-color-0.8.4.diff @@ -0,0 +1,52 @@ +From 01e706efbc13194a4a4404e91b93a9638a3c1bea Mon Sep 17 00:00:00 2001 +From: Kipras Melnikovas +Date: Thu, 25 Feb 2021 14:31:26 +0200 +Subject: [PATCH] refactor dynamic-cursor-color patch + +Signed-off-by: Kipras Melnikovas +--- + x.c | 19 +++++++++++++++++-- + 1 file changed, 17 insertions(+), 2 deletions(-) + +diff --git a/x.c b/x.c +index 120e495..ab66364 100644 +--- a/x.c ++++ b/x.c +@@ -1489,6 +1489,7 @@ void + xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) + { + Color drawcol; ++ XRenderColor colbg; + + /* remove the old cursor */ + if (selected(ox, oy)) +@@ -1518,10 +1519,24 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) + g.fg = defaultfg; + g.bg = defaultrcs; + } else { ++ /** this is the main part of the dynamic cursor color patch */ ++ g.bg = g.fg; + g.fg = defaultbg; +- g.bg = defaultcs; + } +- drawcol = dc.col[g.bg]; ++ ++ /** ++ * and this is the second part of the dynamic cursor color patch. ++ * it handles the `drawcol` variable ++ */ ++ if (IS_TRUECOL(g.bg)) { ++ colbg.alpha = 0xffff; ++ colbg.red = TRUERED(g.bg); ++ colbg.green = TRUEGREEN(g.bg); ++ colbg.blue = TRUEBLUE(g.bg); ++ XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &drawcol); ++ } else { ++ drawcol = dc.col[g.bg]; ++ } + } + + /* draw the new one */ +-- +2.30.1 + diff --git a/based-simple-term/patches/st-scrollback-20210507-4536f46.diff b/based-simple-term/patches/st-scrollback-20210507-4536f46.diff new file mode 100755 index 0000000..f960f6b --- /dev/null +++ b/based-simple-term/patches/st-scrollback-20210507-4536f46.diff @@ -0,0 +1,351 @@ +diff --git a/config.def.h b/config.def.h +index 6f05dce..93cbcc0 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -199,6 +199,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 ebdf360..817cc47 100644 +--- a/st.c ++++ b/st.c +@@ -35,6 +35,7 @@ + #define ESC_ARG_SIZ 16 + #define STR_BUF_SIZ ESC_BUF_SIZ + #define STR_ARG_SIZ ESC_ARG_SIZ ++#define HISTSIZE 2000 + + /* macros */ + #define IS_SET(flag) ((term.mode & (flag)) != 0) +@@ -42,6 +43,9 @@ + #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) + #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) + #define ISDELIM(u) (u && wcschr(worddelimiters, u)) ++#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ ++ term.scr + HISTSIZE + 1) % HISTSIZE] : \ ++ term.line[(y) - term.scr]) + + enum term_mode { + MODE_WRAP = 1 << 0, +@@ -115,6 +119,9 @@ typedef struct { + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ ++ Line hist[HISTSIZE]; /* history buffer */ ++ int histi; /* history index */ ++ int scr; /* scroll back */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ +@@ -184,8 +191,8 @@ static void tnewline(int); + static void tputtab(int); + static void tputc(Rune); + static void treset(void); +-static void tscrollup(int, int); +-static void tscrolldown(int, int); ++static void tscrollup(int, int, int); ++static void tscrolldown(int, int, int); + static void tsetattr(const int *, int); + static void tsetchar(Rune, const Glyph *, int, int); + static void tsetdirt(int, int); +@@ -416,10 +423,10 @@ tlinelen(int y) + { + int i = term.col; + +- if (term.line[y][i - 1].mode & ATTR_WRAP) ++ if (TLINE(y)[i - 1].mode & ATTR_WRAP) + return i; + +- while (i > 0 && term.line[y][i - 1].u == ' ') ++ while (i > 0 && TLINE(y)[i - 1].u == ' ') + --i; + + return i; +@@ -528,7 +535,7 @@ selsnap(int *x, int *y, int direction) + * Snap around if the word wraps around at the end or + * beginning of a line. + */ +- prevgp = &term.line[*y][*x]; ++ prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; +@@ -543,14 +550,14 @@ selsnap(int *x, int *y, int direction) + yt = *y, xt = *x; + else + yt = newy, xt = newx; +- if (!(term.line[yt][xt].mode & ATTR_WRAP)) ++ if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + +- gp = &term.line[newy][newx]; ++ gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) +@@ -571,14 +578,14 @@ selsnap(int *x, int *y, int direction) + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { +- if (!(term.line[*y-1][term.col-1].mode ++ if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { +- if (!(term.line[*y][term.col-1].mode ++ if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } +@@ -609,13 +616,13 @@ getsel(void) + } + + if (sel.type == SEL_RECTANGULAR) { +- gp = &term.line[y][sel.nb.x]; ++ gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { +- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; ++ gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } +- last = &term.line[y][MIN(lastx, linelen-1)]; ++ last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + +@@ -850,6 +857,9 @@ void + ttywrite(const char *s, size_t n, int may_echo) + { + const char *next; ++ Arg arg = (Arg) { .i = term.scr }; ++ ++ kscrolldown(&arg); + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); +@@ -1061,13 +1071,53 @@ tswapscreen(void) + } + + void +-tscrolldown(int orig, int n) ++kscrolldown(const Arg* a) ++{ ++ int n = a->i; ++ ++ if (n < 0) ++ n = term.row + n; ++ ++ if (n > term.scr) ++ n = term.scr; ++ ++ if (term.scr > 0) { ++ term.scr -= n; ++ selscroll(0, -n); ++ tfulldirt(); ++ } ++} ++ ++void ++kscrollup(const Arg* a) ++{ ++ int n = a->i; ++ ++ if (n < 0) ++ n = term.row + n; ++ ++ if (term.scr <= HISTSIZE-n) { ++ term.scr += n; ++ selscroll(0, n); ++ tfulldirt(); ++ } ++} ++ ++void ++tscrolldown(int orig, int n, int copyhist) + { + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + ++ if (copyhist) { ++ term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; ++ temp = term.hist[term.histi]; ++ term.hist[term.histi] = term.line[term.bot]; ++ term.line[term.bot] = temp; ++ } ++ + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + +@@ -1077,17 +1127,28 @@ tscrolldown(int orig, int n) + term.line[i-n] = temp; + } + +- selscroll(orig, n); ++ if (term.scr == 0) ++ selscroll(orig, n); + } + + void +-tscrollup(int orig, int n) ++tscrollup(int orig, int n, int copyhist) + { + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + ++ if (copyhist) { ++ term.histi = (term.histi + 1) % HISTSIZE; ++ temp = term.hist[term.histi]; ++ term.hist[term.histi] = term.line[orig]; ++ term.line[orig] = temp; ++ } ++ ++ if (term.scr > 0 && term.scr < HISTSIZE) ++ term.scr = MIN(term.scr + n, HISTSIZE-1); ++ + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + +@@ -1097,7 +1158,8 @@ tscrollup(int orig, int n) + term.line[i+n] = temp; + } + +- selscroll(orig, -n); ++ if (term.scr == 0) ++ selscroll(orig, -n); + } + + void +@@ -1126,7 +1188,7 @@ tnewline(int first_col) + int y = term.c.y; + + if (y == term.bot) { +- tscrollup(term.top, 1); ++ tscrollup(term.top, 1, 1); + } else { + y++; + } +@@ -1291,14 +1353,14 @@ void + tinsertblankline(int n) + { + if (BETWEEN(term.c.y, term.top, term.bot)) +- tscrolldown(term.c.y, n); ++ tscrolldown(term.c.y, n, 0); + } + + void + tdeleteline(int n) + { + if (BETWEEN(term.c.y, term.top, term.bot)) +- tscrollup(term.c.y, n); ++ tscrollup(term.c.y, n, 0); + } + + int32_t +@@ -1735,11 +1797,11 @@ csihandle(void) + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); +- tscrollup(term.top, csiescseq.arg[0]); ++ tscrollup(term.top, csiescseq.arg[0], 0); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); +- tscrolldown(term.top, csiescseq.arg[0]); ++ tscrolldown(term.top, csiescseq.arg[0], 0); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); +@@ -2251,7 +2313,7 @@ eschandle(uchar ascii) + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { +- tscrollup(term.top, 1); ++ tscrollup(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } +@@ -2264,7 +2326,7 @@ eschandle(uchar ascii) + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { +- tscrolldown(term.top, 1); ++ tscrolldown(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } +@@ -2474,7 +2536,7 @@ twrite(const char *buf, int buflen, int show_ctrl) + void + tresize(int col, int row) + { +- int i; ++ int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; +@@ -2511,6 +2573,14 @@ tresize(int col, int row) + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + ++ for (i = 0; i < HISTSIZE; i++) { ++ term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); ++ for (j = mincol; j < col; j++) { ++ term.hist[i][j] = term.c.attr; ++ term.hist[i][j].u = ' '; ++ } ++ } ++ + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); +@@ -2569,7 +2639,7 @@ drawregion(int x1, int y1, int x2, int y2) + continue; + + term.dirty[y] = 0; +- xdrawline(term.line[y], x1, y, x2); ++ xdrawline(TLINE(y), x1, y, x2); + } + } + +@@ -2590,8 +2660,9 @@ draw(void) + cx--; + + drawregion(0, 0, term.col, term.row); +- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], +- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); ++ if (term.scr == 0) ++ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], ++ term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); +diff --git a/st.h b/st.h +index fa2eddf..adda2db 100644 +--- a/st.h ++++ b/st.h +@@ -81,6 +81,8 @@ void die(const char *, ...); + void redraw(void); + void draw(void); + ++void kscrolldown(const Arg *); ++void kscrollup(const Arg *); + void printscreen(const Arg *); + void printsel(const Arg *); + void sendbreak(const Arg *); diff --git a/based-simple-term/patches/st-scrollback-mouse-20191024-a2c479c.diff b/based-simple-term/patches/st-scrollback-mouse-20191024-a2c479c.diff new file mode 100755 index 0000000..49eba8e --- /dev/null +++ b/based-simple-term/patches/st-scrollback-mouse-20191024-a2c479c.diff @@ -0,0 +1,13 @@ +diff --git a/config.def.h b/config.def.h +index ec1b576..4b3bf15 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -163,6 +163,8 @@ static uint forcemousemod = ShiftMask; + */ + static MouseShortcut mshortcuts[] = { + /* mask button function argument release */ ++ { ShiftMask, Button4, kscrollup, {.i = 1} }, ++ { ShiftMask, Button5, kscrolldown, {.i = 1} }, + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, diff --git a/based-simple-term/patches/st-scrollback-mouse-altscreen-20220127-2c5edf2.diff b/based-simple-term/patches/st-scrollback-mouse-altscreen-20220127-2c5edf2.diff new file mode 100755 index 0000000..6a8722b --- /dev/null +++ b/based-simple-term/patches/st-scrollback-mouse-altscreen-20220127-2c5edf2.diff @@ -0,0 +1,78 @@ +From 580e3f386e9215707100e9ba44797701943fd927 Mon Sep 17 00:00:00 2001 +From: asparagii +Date: Thu, 27 Jan 2022 15:49:27 +0100 +Subject: [PATCH] st-scrollback-mouse-altscreen + +--- + config.def.h | 4 ++-- + st.c | 5 +++++ + st.h | 1 + + x.c | 2 ++ + 4 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/config.def.h b/config.def.h +index c217315..c223706 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -176,8 +176,8 @@ static uint forcemousemod = ShiftMask; + */ + static MouseShortcut mshortcuts[] = { + /* mask button function argument release */ +- { ShiftMask, Button4, kscrollup, {.i = 1} }, +- { ShiftMask, Button5, kscrolldown, {.i = 1} }, ++ { XK_ANY_MOD, Button4, kscrollup, {.i = 1}, 0, /* !alt */ -1 }, ++ { XK_ANY_MOD, Button5, kscrolldown, {.i = 1}, 0, /* !alt */ -1 }, + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, +diff --git a/st.c b/st.c +index f3af82b..876a6bf 100644 +--- a/st.c ++++ b/st.c +@@ -1060,6 +1060,11 @@ tnew(int col, int row) + treset(); + } + ++int tisaltscr(void) ++{ ++ return IS_SET(MODE_ALTSCREEN); ++} ++ + void + tswapscreen(void) + { +diff --git a/st.h b/st.h +index da36b34..e95c6f8 100644 +--- a/st.h ++++ b/st.h +@@ -89,6 +89,7 @@ void sendbreak(const Arg *); + void toggleprinter(const Arg *); + + int tattrset(int); ++int tisaltscr(void); + void tnew(int, int); + void tresize(int, int); + void tsetdirtattr(int); +diff --git a/x.c b/x.c +index cd96575..9274556 100644 +--- a/x.c ++++ b/x.c +@@ -34,6 +34,7 @@ typedef struct { + void (*func)(const Arg *); + const Arg arg; + uint release; ++ int altscrn; /* 0: don't care, -1: not alt screen, 1: alt screen */ + } MouseShortcut; + + typedef struct { +@@ -455,6 +456,7 @@ mouseaction(XEvent *e, uint release) + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { + if (ms->release == release && + ms->button == e->xbutton.button && ++ (!ms->altscrn || (ms->altscrn == (tisaltscr() ? 1 : -1))) && + (match(ms->mod, state) || /* exact or forced */ + match(ms->mod, state & ~forcemousemod))) { + ms->func(&(ms->arg)); +-- +2.34.1 + diff --git a/based-simple-term/patches/st-undercurl-0.8.4-20210822.diff b/based-simple-term/patches/st-undercurl-0.8.4-20210822.diff new file mode 100755 index 0000000..fab2ddc --- /dev/null +++ b/based-simple-term/patches/st-undercurl-0.8.4-20210822.diff @@ -0,0 +1,605 @@ +diff --git a/config.def.h b/config.def.h +index 6f05dce..7ae1b92 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -470,3 +470,27 @@ static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; ++ ++/** ++ * Undercurl style. Set UNDERCURL_STYLE to one of the available styles. ++ * ++ * Curly: Dunno how to draw it *shrug* ++ * _ _ _ _ ++ * ( ) ( ) ( ) ( ) ++ * (_) (_) (_) (_) ++ * ++ * Spiky: ++ * /\ /\ /\ /\ ++ * \/ \/ \/ ++ * ++ * Capped: ++ * _ _ _ ++ * / \ / \ / \ ++ * \_/ \_/ ++ */ ++// Available styles ++#define UNDERCURL_CURLY 0 ++#define UNDERCURL_SPIKY 1 ++#define UNDERCURL_CAPPED 2 ++// Active style ++#define UNDERCURL_STYLE UNDERCURL_SPIKY +diff --git a/st.c b/st.c +index 76b7e0d..542ab3a 100644 +--- a/st.c ++++ b/st.c +@@ -33,6 +33,7 @@ + #define UTF_SIZ 4 + #define ESC_BUF_SIZ (128*UTF_SIZ) + #define ESC_ARG_SIZ 16 ++#define CAR_PER_ARG 4 + #define STR_BUF_SIZ ESC_BUF_SIZ + #define STR_ARG_SIZ ESC_ARG_SIZ + +@@ -139,6 +140,7 @@ typedef struct { + int arg[ESC_ARG_SIZ]; + int narg; /* nb of args */ + char mode[2]; ++ int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */ + } CSIEscape; + + /* STR Escape sequence structs */ +@@ -159,6 +161,7 @@ static void ttywriteraw(const char *, size_t); + + static void csidump(void); + static void csihandle(void); ++static void readcolonargs(char **, int, int[][CAR_PER_ARG]); + static void csiparse(void); + static void csireset(void); + static int eschandle(uchar); +@@ -1131,6 +1134,28 @@ tnewline(int first_col) + tmoveto(first_col ? 0 : term.c.x, y); + } + ++void ++readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG]) ++{ ++ int i = 0; ++ for (; i < CAR_PER_ARG; i++) ++ params[cursor][i] = -1; ++ ++ if (**p != ':') ++ return; ++ ++ char *np = NULL; ++ i = 0; ++ ++ while (**p == ':' && i < CAR_PER_ARG) { ++ while (**p == ':') ++ (*p)++; ++ params[cursor][i] = strtol(*p, &np, 10); ++ *p = np; ++ i++; ++ } ++} ++ + void + csiparse(void) + { +@@ -1153,6 +1178,7 @@ csiparse(void) + v = -1; + csiescseq.arg[csiescseq.narg++] = v; + p = np; ++ readcolonargs(&p, csiescseq.narg-1, csiescseq.carg); + if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + break; + p++; +@@ -1369,6 +1395,10 @@ tsetattr(int *attr, int l) + ATTR_STRUCK ); + term.c.attr.fg = defaultfg; + term.c.attr.bg = defaultbg; ++ term.c.attr.ustyle = -1; ++ term.c.attr.ucolor[0] = -1; ++ term.c.attr.ucolor[1] = -1; ++ term.c.attr.ucolor[2] = -1; + break; + case 1: + term.c.attr.mode |= ATTR_BOLD; +@@ -1380,7 +1410,14 @@ tsetattr(int *attr, int l) + term.c.attr.mode |= ATTR_ITALIC; + break; + case 4: +- term.c.attr.mode |= ATTR_UNDERLINE; ++ term.c.attr.ustyle = csiescseq.carg[i][0]; ++ ++ if (term.c.attr.ustyle != 0) ++ term.c.attr.mode |= ATTR_UNDERLINE; ++ else ++ term.c.attr.mode &= ~ATTR_UNDERLINE; ++ ++ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; + break; + case 5: /* slow blink */ + /* FALLTHROUGH */ +@@ -1431,6 +1468,18 @@ tsetattr(int *attr, int l) + case 49: + term.c.attr.bg = defaultbg; + break; ++ case 58: ++ term.c.attr.ucolor[0] = csiescseq.carg[i][1]; ++ term.c.attr.ucolor[1] = csiescseq.carg[i][2]; ++ term.c.attr.ucolor[2] = csiescseq.carg[i][3]; ++ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; ++ break; ++ case 59: ++ term.c.attr.ucolor[0] = -1; ++ term.c.attr.ucolor[1] = -1; ++ term.c.attr.ucolor[2] = -1; ++ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; ++ break; + default: + if (BETWEEN(attr[i], 30, 37)) { + term.c.attr.fg = attr[i] - 30; +diff --git a/st.h b/st.h +index 3d351b6..95bdcbd 100644 +--- a/st.h ++++ b/st.h +@@ -34,6 +34,7 @@ enum glyph_attribute { + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, ++ ATTR_DIRTYUNDERLINE = 1 << 15, + }; + + enum selection_mode { +@@ -65,6 +66,8 @@ typedef struct { + ushort mode; /* attribute flags */ + uint32_t fg; /* foreground */ + uint32_t bg; /* background */ ++ int ustyle; /* underline style */ ++ int ucolor[3]; /* underline color */ + } Glyph; + + typedef Glyph *Line; +diff --git a/st.info b/st.info +index 8201ad6..659878c 100644 +--- a/st.info ++++ b/st.info +@@ -1,4 +1,5 @@ + st-mono| simpleterm monocolor, ++ Su, + acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + am, + bce, +diff --git a/x.c b/x.c +index 210f184..3a0e79e 100644 +--- a/x.c ++++ b/x.c +@@ -45,6 +45,14 @@ typedef struct { + signed char appcursor; /* application cursor */ + } Key; + ++/* Undercurl slope types */ ++enum undercurl_slope_type { ++ UNDERCURL_SLOPE_ASCENDING = 0, ++ UNDERCURL_SLOPE_TOP_CAP = 1, ++ UNDERCURL_SLOPE_DESCENDING = 2, ++ UNDERCURL_SLOPE_BOTTOM_CAP = 3 ++}; ++ + /* X modifiers */ + #define XK_ANY_MOD UINT_MAX + #define XK_NO_MOD 0 +@@ -1339,6 +1347,51 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x + return numspecs; + } + ++static int isSlopeRising (int x, int iPoint, int waveWidth) ++{ ++ // . . . . ++ // / \ / \ / \ / \ ++ // / \ / \ / \ / \ ++ // . . . . . ++ ++ // Find absolute `x` of point ++ x += iPoint * (waveWidth/2); ++ ++ // Find index of absolute wave ++ int absSlope = x / ((float)waveWidth/2); ++ ++ return (absSlope % 2); ++} ++ ++static int getSlope (int x, int iPoint, int waveWidth) ++{ ++ // Sizes: Caps are half width of slopes ++ // 1_2 1_2 1_2 1_2 ++ // / \ / \ / \ / \ ++ // / \ / \ / \ / \ ++ // 0 3_0 3_0 3_0 3_ ++ // <2-> <1> <---6----> ++ ++ // Find type of first point ++ int firstType; ++ x -= (x / waveWidth) * waveWidth; ++ if (x < (waveWidth * (2.f/6.f))) ++ firstType = UNDERCURL_SLOPE_ASCENDING; ++ else if (x < (waveWidth * (3.f/6.f))) ++ firstType = UNDERCURL_SLOPE_TOP_CAP; ++ else if (x < (waveWidth * (5.f/6.f))) ++ firstType = UNDERCURL_SLOPE_DESCENDING; ++ else ++ firstType = UNDERCURL_SLOPE_BOTTOM_CAP; ++ ++ // Find type of given point ++ int pointType = (iPoint % 4); ++ pointType += firstType; ++ pointType %= 4; ++ ++ return pointType; ++} ++ + void + xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) + { +@@ -1461,8 +1514,357 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i + + /* Render underline and strikethrough. */ + if (base.mode & ATTR_UNDERLINE) { +- XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, +- width, 1); ++ // Underline Color ++ const int widthThreshold = 28; // +1 width every widthThreshold px of font ++ int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width ++ int linecolor; ++ if ((base.ucolor[0] >= 0) && ++ !(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) && ++ !(base.mode & ATTR_INVISIBLE) ++ ) { ++ // Special color for underline ++ // Index ++ if (base.ucolor[1] < 0) { ++ linecolor = dc.col[base.ucolor[0]].pixel; ++ } ++ // RGB ++ else { ++ XColor lcolor; ++ lcolor.red = base.ucolor[0] * 257; ++ lcolor.green = base.ucolor[1] * 257; ++ lcolor.blue = base.ucolor[2] * 257; ++ lcolor.flags = DoRed | DoGreen | DoBlue; ++ XAllocColor(xw.dpy, xw.cmap, &lcolor); ++ linecolor = lcolor.pixel; ++ } ++ } else { ++ // Foreground color for underline ++ linecolor = fg->pixel; ++ } ++ ++ XGCValues ugcv = { ++ .foreground = linecolor, ++ .line_width = wlw, ++ .line_style = LineSolid, ++ .cap_style = CapNotLast ++ }; ++ ++ GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw), ++ GCForeground | GCLineWidth | GCLineStyle | GCCapStyle, ++ &ugcv); ++ ++ // Underline Style ++ if (base.ustyle != 3) { ++ //XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1); ++ XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx, ++ winy + dc.font.ascent + 1, width, wlw); ++ } else if (base.ustyle == 3) { ++ int ww = win.cw;//width; ++ int wh = dc.font.descent - wlw/2 - 1;//r.height/7; ++ int wx = winx; ++ int wy = winy + win.ch - dc.font.descent; ++ ++#if UNDERCURL_STYLE == UNDERCURL_CURLY ++ // Draw waves ++ int narcs = charlen * 2 + 1; ++ XArc *arcs = xmalloc(sizeof(XArc) * narcs); ++ ++ int i = 0; ++ for (i = 0; i < charlen-1; i++) { ++ arcs[i*2] = (XArc) { ++ .x = wx + win.cw * i + ww / 4, ++ .y = wy, ++ .width = win.cw / 2, ++ .height = wh, ++ .angle1 = 0, ++ .angle2 = 180 * 64 ++ }; ++ arcs[i*2+1] = (XArc) { ++ .x = wx + win.cw * i + ww * 0.75, ++ .y = wy, ++ .width = win.cw/2, ++ .height = wh, ++ .angle1 = 180 * 64, ++ .angle2 = 180 * 64 ++ }; ++ } ++ // Last wave ++ arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh, ++ 0, 180 * 64 }; ++ // Last wave tail ++ arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.), ++ wh, 180 * 64, 90 * 64}; ++ // First wave tail ++ i++; ++ arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64, ++ 90 * 64 }; ++ ++ XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs); ++ ++ free(arcs); ++#elif UNDERCURL_STYLE == UNDERCURL_SPIKY ++ // Make the underline corridor larger ++ /* ++ wy -= wh; ++ */ ++ wh *= 2; ++ ++ // Set the angle of the slope to 45° ++ ww = wh; ++ ++ // Position of wave is independent of word, it's absolute ++ wx = (wx / (ww/2)) * (ww/2); ++ ++ int marginStart = winx - wx; ++ ++ // Calculate number of points with floating precision ++ float n = width; // Width of word in pixels ++ n = (n / ww) * 2; // Number of slopes (/ or \) ++ n += 2; // Add two last points ++ int npoints = n; // Convert to int ++ ++ // Total length of underline ++ float waveLength = 0; ++ ++ if (npoints >= 3) { ++ // We add an aditional slot in case we use a bonus point ++ XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); ++ ++ // First point (Starts with the word bounds) ++ points[0] = (XPoint) { ++ .x = wx + marginStart, ++ .y = (isSlopeRising(wx, 0, ww)) ++ ? (wy - marginStart + ww/2.f) ++ : (wy + marginStart) ++ }; ++ ++ // Second point (Goes back to the absolute point coordinates) ++ points[1] = (XPoint) { ++ .x = (ww/2.f) - marginStart, ++ .y = (isSlopeRising(wx, 1, ww)) ++ ? (ww/2.f - marginStart) ++ : (-ww/2.f + marginStart) ++ }; ++ waveLength += (ww/2.f) - marginStart; ++ ++ // The rest of the points ++ for (int i = 2; i < npoints-1; i++) { ++ points[i] = (XPoint) { ++ .x = ww/2, ++ .y = (isSlopeRising(wx, i, ww)) ++ ? wh/2 ++ : -wh/2 ++ }; ++ waveLength += ww/2; ++ } ++ ++ // Last point ++ points[npoints-1] = (XPoint) { ++ .x = ww/2, ++ .y = (isSlopeRising(wx, npoints-1, ww)) ++ ? wh/2 ++ : -wh/2 ++ }; ++ waveLength += ww/2; ++ ++ // End ++ if (waveLength < width) { // Add a bonus point? ++ int marginEnd = width - waveLength; ++ points[npoints] = (XPoint) { ++ .x = marginEnd, ++ .y = (isSlopeRising(wx, npoints, ww)) ++ ? (marginEnd) ++ : (-marginEnd) ++ }; ++ ++ npoints++; ++ } else if (waveLength > width) { // Is last point too far? ++ int marginEnd = waveLength - width; ++ points[npoints-1].x -= marginEnd; ++ if (isSlopeRising(wx, npoints-1, ww)) ++ points[npoints-1].y -= (marginEnd); ++ else ++ points[npoints-1].y += (marginEnd); ++ } ++ ++ // Draw the lines ++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, ++ CoordModePrevious); ++ ++ // Draw a second underline with an offset of 1 pixel ++ if ( ((win.ch / (widthThreshold/2)) % 2)) { ++ points[0].x++; ++ ++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, ++ npoints, CoordModePrevious); ++ } ++ ++ // Free resources ++ free(points); ++ } ++#else // UNDERCURL_CAPPED ++ // Cap is half of wave width ++ float capRatio = 0.5f; ++ ++ // Make the underline corridor larger ++ wh *= 2; ++ ++ // Set the angle of the slope to 45° ++ ww = wh; ++ ww *= 1 + capRatio; // Add a bit of width for the cap ++ ++ // Position of wave is independent of word, it's absolute ++ wx = (wx / ww) * ww; ++ ++ float marginStart; ++ switch(getSlope(winx, 0, ww)) { ++ case UNDERCURL_SLOPE_ASCENDING: ++ marginStart = winx - wx; ++ break; ++ case UNDERCURL_SLOPE_TOP_CAP: ++ marginStart = winx - (wx + (ww * (2.f/6.f))); ++ break; ++ case UNDERCURL_SLOPE_DESCENDING: ++ marginStart = winx - (wx + (ww * (3.f/6.f))); ++ break; ++ case UNDERCURL_SLOPE_BOTTOM_CAP: ++ marginStart = winx - (wx + (ww * (5.f/6.f))); ++ break; ++ } ++ ++ // Calculate number of points with floating precision ++ float n = width; // Width of word in pixels ++ // ._. ++ n = (n / ww) * 4; // Number of points (./ \.) ++ n += 2; // Add two last points ++ int npoints = n; // Convert to int ++ ++ // Position of the pen to draw the lines ++ float penX = 0; ++ float penY = 0; ++ ++ if (npoints >= 3) { ++ XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); ++ ++ // First point (Starts with the word bounds) ++ penX = winx; ++ switch (getSlope(winx, 0, ww)) { ++ case UNDERCURL_SLOPE_ASCENDING: ++ penY = wy + wh/2.f - marginStart; ++ break; ++ case UNDERCURL_SLOPE_TOP_CAP: ++ penY = wy; ++ break; ++ case UNDERCURL_SLOPE_DESCENDING: ++ penY = wy + marginStart; ++ break; ++ case UNDERCURL_SLOPE_BOTTOM_CAP: ++ penY = wy + wh/2.f; ++ break; ++ } ++ points[0].x = penX; ++ points[0].y = penY; ++ ++ // Second point (Goes back to the absolute point coordinates) ++ switch (getSlope(winx, 1, ww)) { ++ case UNDERCURL_SLOPE_ASCENDING: ++ penX += ww * (1.f/6.f) - marginStart; ++ penY += 0; ++ break; ++ case UNDERCURL_SLOPE_TOP_CAP: ++ penX += ww * (2.f/6.f) - marginStart; ++ penY += -wh/2.f + marginStart; ++ break; ++ case UNDERCURL_SLOPE_DESCENDING: ++ penX += ww * (1.f/6.f) - marginStart; ++ penY += 0; ++ break; ++ case UNDERCURL_SLOPE_BOTTOM_CAP: ++ penX += ww * (2.f/6.f) - marginStart; ++ penY += -marginStart + wh/2.f; ++ break; ++ } ++ points[1].x = penX; ++ points[1].y = penY; ++ ++ // The rest of the points ++ for (int i = 2; i < npoints; i++) { ++ switch (getSlope(winx, i, ww)) { ++ case UNDERCURL_SLOPE_ASCENDING: ++ case UNDERCURL_SLOPE_DESCENDING: ++ penX += ww * (1.f/6.f); ++ penY += 0; ++ break; ++ case UNDERCURL_SLOPE_TOP_CAP: ++ penX += ww * (2.f/6.f); ++ penY += -wh / 2.f; ++ break; ++ case UNDERCURL_SLOPE_BOTTOM_CAP: ++ penX += ww * (2.f/6.f); ++ penY += wh / 2.f; ++ break; ++ } ++ points[i].x = penX; ++ points[i].y = penY; ++ } ++ ++ // End ++ float waveLength = penX - winx; ++ if (waveLength < width) { // Add a bonus point? ++ int marginEnd = width - waveLength; ++ penX += marginEnd; ++ switch(getSlope(winx, npoints, ww)) { ++ case UNDERCURL_SLOPE_ASCENDING: ++ case UNDERCURL_SLOPE_DESCENDING: ++ //penY += 0; ++ break; ++ case UNDERCURL_SLOPE_TOP_CAP: ++ penY += -marginEnd; ++ break; ++ case UNDERCURL_SLOPE_BOTTOM_CAP: ++ penY += marginEnd; ++ break; ++ } ++ ++ points[npoints].x = penX; ++ points[npoints].y = penY; ++ ++ npoints++; ++ } else if (waveLength > width) { // Is last point too far? ++ int marginEnd = waveLength - width; ++ points[npoints-1].x -= marginEnd; ++ switch(getSlope(winx, npoints-1, ww)) { ++ case UNDERCURL_SLOPE_TOP_CAP: ++ points[npoints-1].y += marginEnd; ++ break; ++ case UNDERCURL_SLOPE_BOTTOM_CAP: ++ points[npoints-1].y -= marginEnd; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ // Draw the lines ++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, ++ CoordModeOrigin); ++ ++ // Draw a second underline with an offset of 1 pixel ++ if ( ((win.ch / (widthThreshold/2)) % 2)) { ++ for (int i = 0; i < npoints; i++) ++ points[i].x++; ++ ++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, ++ npoints, CoordModeOrigin); ++ } ++ ++ // Free resources ++ free(points); ++ } ++#endif ++ } ++ ++ XFreeGC(xw.dpy, ugc); + } + + if (base.mode & ATTR_STRUCK) { diff --git a/based-simple-term/patches/st-undercurl-0.8.4-20210822.diff_ b/based-simple-term/patches/st-undercurl-0.8.4-20210822.diff_ new file mode 100755 index 0000000..fab2ddc --- /dev/null +++ b/based-simple-term/patches/st-undercurl-0.8.4-20210822.diff_ @@ -0,0 +1,605 @@ +diff --git a/config.def.h b/config.def.h +index 6f05dce..7ae1b92 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -470,3 +470,27 @@ static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; ++ ++/** ++ * Undercurl style. Set UNDERCURL_STYLE to one of the available styles. ++ * ++ * Curly: Dunno how to draw it *shrug* ++ * _ _ _ _ ++ * ( ) ( ) ( ) ( ) ++ * (_) (_) (_) (_) ++ * ++ * Spiky: ++ * /\ /\ /\ /\ ++ * \/ \/ \/ ++ * ++ * Capped: ++ * _ _ _ ++ * / \ / \ / \ ++ * \_/ \_/ ++ */ ++// Available styles ++#define UNDERCURL_CURLY 0 ++#define UNDERCURL_SPIKY 1 ++#define UNDERCURL_CAPPED 2 ++// Active style ++#define UNDERCURL_STYLE UNDERCURL_SPIKY +diff --git a/st.c b/st.c +index 76b7e0d..542ab3a 100644 +--- a/st.c ++++ b/st.c +@@ -33,6 +33,7 @@ + #define UTF_SIZ 4 + #define ESC_BUF_SIZ (128*UTF_SIZ) + #define ESC_ARG_SIZ 16 ++#define CAR_PER_ARG 4 + #define STR_BUF_SIZ ESC_BUF_SIZ + #define STR_ARG_SIZ ESC_ARG_SIZ + +@@ -139,6 +140,7 @@ typedef struct { + int arg[ESC_ARG_SIZ]; + int narg; /* nb of args */ + char mode[2]; ++ int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */ + } CSIEscape; + + /* STR Escape sequence structs */ +@@ -159,6 +161,7 @@ static void ttywriteraw(const char *, size_t); + + static void csidump(void); + static void csihandle(void); ++static void readcolonargs(char **, int, int[][CAR_PER_ARG]); + static void csiparse(void); + static void csireset(void); + static int eschandle(uchar); +@@ -1131,6 +1134,28 @@ tnewline(int first_col) + tmoveto(first_col ? 0 : term.c.x, y); + } + ++void ++readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG]) ++{ ++ int i = 0; ++ for (; i < CAR_PER_ARG; i++) ++ params[cursor][i] = -1; ++ ++ if (**p != ':') ++ return; ++ ++ char *np = NULL; ++ i = 0; ++ ++ while (**p == ':' && i < CAR_PER_ARG) { ++ while (**p == ':') ++ (*p)++; ++ params[cursor][i] = strtol(*p, &np, 10); ++ *p = np; ++ i++; ++ } ++} ++ + void + csiparse(void) + { +@@ -1153,6 +1178,7 @@ csiparse(void) + v = -1; + csiescseq.arg[csiescseq.narg++] = v; + p = np; ++ readcolonargs(&p, csiescseq.narg-1, csiescseq.carg); + if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + break; + p++; +@@ -1369,6 +1395,10 @@ tsetattr(int *attr, int l) + ATTR_STRUCK ); + term.c.attr.fg = defaultfg; + term.c.attr.bg = defaultbg; ++ term.c.attr.ustyle = -1; ++ term.c.attr.ucolor[0] = -1; ++ term.c.attr.ucolor[1] = -1; ++ term.c.attr.ucolor[2] = -1; + break; + case 1: + term.c.attr.mode |= ATTR_BOLD; +@@ -1380,7 +1410,14 @@ tsetattr(int *attr, int l) + term.c.attr.mode |= ATTR_ITALIC; + break; + case 4: +- term.c.attr.mode |= ATTR_UNDERLINE; ++ term.c.attr.ustyle = csiescseq.carg[i][0]; ++ ++ if (term.c.attr.ustyle != 0) ++ term.c.attr.mode |= ATTR_UNDERLINE; ++ else ++ term.c.attr.mode &= ~ATTR_UNDERLINE; ++ ++ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; + break; + case 5: /* slow blink */ + /* FALLTHROUGH */ +@@ -1431,6 +1468,18 @@ tsetattr(int *attr, int l) + case 49: + term.c.attr.bg = defaultbg; + break; ++ case 58: ++ term.c.attr.ucolor[0] = csiescseq.carg[i][1]; ++ term.c.attr.ucolor[1] = csiescseq.carg[i][2]; ++ term.c.attr.ucolor[2] = csiescseq.carg[i][3]; ++ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; ++ break; ++ case 59: ++ term.c.attr.ucolor[0] = -1; ++ term.c.attr.ucolor[1] = -1; ++ term.c.attr.ucolor[2] = -1; ++ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; ++ break; + default: + if (BETWEEN(attr[i], 30, 37)) { + term.c.attr.fg = attr[i] - 30; +diff --git a/st.h b/st.h +index 3d351b6..95bdcbd 100644 +--- a/st.h ++++ b/st.h +@@ -34,6 +34,7 @@ enum glyph_attribute { + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, ++ ATTR_DIRTYUNDERLINE = 1 << 15, + }; + + enum selection_mode { +@@ -65,6 +66,8 @@ typedef struct { + ushort mode; /* attribute flags */ + uint32_t fg; /* foreground */ + uint32_t bg; /* background */ ++ int ustyle; /* underline style */ ++ int ucolor[3]; /* underline color */ + } Glyph; + + typedef Glyph *Line; +diff --git a/st.info b/st.info +index 8201ad6..659878c 100644 +--- a/st.info ++++ b/st.info +@@ -1,4 +1,5 @@ + st-mono| simpleterm monocolor, ++ Su, + acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + am, + bce, +diff --git a/x.c b/x.c +index 210f184..3a0e79e 100644 +--- a/x.c ++++ b/x.c +@@ -45,6 +45,14 @@ typedef struct { + signed char appcursor; /* application cursor */ + } Key; + ++/* Undercurl slope types */ ++enum undercurl_slope_type { ++ UNDERCURL_SLOPE_ASCENDING = 0, ++ UNDERCURL_SLOPE_TOP_CAP = 1, ++ UNDERCURL_SLOPE_DESCENDING = 2, ++ UNDERCURL_SLOPE_BOTTOM_CAP = 3 ++}; ++ + /* X modifiers */ + #define XK_ANY_MOD UINT_MAX + #define XK_NO_MOD 0 +@@ -1339,6 +1347,51 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x + return numspecs; + } + ++static int isSlopeRising (int x, int iPoint, int waveWidth) ++{ ++ // . . . . ++ // / \ / \ / \ / \ ++ // / \ / \ / \ / \ ++ // . . . . . ++ ++ // Find absolute `x` of point ++ x += iPoint * (waveWidth/2); ++ ++ // Find index of absolute wave ++ int absSlope = x / ((float)waveWidth/2); ++ ++ return (absSlope % 2); ++} ++ ++static int getSlope (int x, int iPoint, int waveWidth) ++{ ++ // Sizes: Caps are half width of slopes ++ // 1_2 1_2 1_2 1_2 ++ // / \ / \ / \ / \ ++ // / \ / \ / \ / \ ++ // 0 3_0 3_0 3_0 3_ ++ // <2-> <1> <---6----> ++ ++ // Find type of first point ++ int firstType; ++ x -= (x / waveWidth) * waveWidth; ++ if (x < (waveWidth * (2.f/6.f))) ++ firstType = UNDERCURL_SLOPE_ASCENDING; ++ else if (x < (waveWidth * (3.f/6.f))) ++ firstType = UNDERCURL_SLOPE_TOP_CAP; ++ else if (x < (waveWidth * (5.f/6.f))) ++ firstType = UNDERCURL_SLOPE_DESCENDING; ++ else ++ firstType = UNDERCURL_SLOPE_BOTTOM_CAP; ++ ++ // Find type of given point ++ int pointType = (iPoint % 4); ++ pointType += firstType; ++ pointType %= 4; ++ ++ return pointType; ++} ++ + void + xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) + { +@@ -1461,8 +1514,357 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i + + /* Render underline and strikethrough. */ + if (base.mode & ATTR_UNDERLINE) { +- XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, +- width, 1); ++ // Underline Color ++ const int widthThreshold = 28; // +1 width every widthThreshold px of font ++ int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width ++ int linecolor; ++ if ((base.ucolor[0] >= 0) && ++ !(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) && ++ !(base.mode & ATTR_INVISIBLE) ++ ) { ++ // Special color for underline ++ // Index ++ if (base.ucolor[1] < 0) { ++ linecolor = dc.col[base.ucolor[0]].pixel; ++ } ++ // RGB ++ else { ++ XColor lcolor; ++ lcolor.red = base.ucolor[0] * 257; ++ lcolor.green = base.ucolor[1] * 257; ++ lcolor.blue = base.ucolor[2] * 257; ++ lcolor.flags = DoRed | DoGreen | DoBlue; ++ XAllocColor(xw.dpy, xw.cmap, &lcolor); ++ linecolor = lcolor.pixel; ++ } ++ } else { ++ // Foreground color for underline ++ linecolor = fg->pixel; ++ } ++ ++ XGCValues ugcv = { ++ .foreground = linecolor, ++ .line_width = wlw, ++ .line_style = LineSolid, ++ .cap_style = CapNotLast ++ }; ++ ++ GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw), ++ GCForeground | GCLineWidth | GCLineStyle | GCCapStyle, ++ &ugcv); ++ ++ // Underline Style ++ if (base.ustyle != 3) { ++ //XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1); ++ XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx, ++ winy + dc.font.ascent + 1, width, wlw); ++ } else if (base.ustyle == 3) { ++ int ww = win.cw;//width; ++ int wh = dc.font.descent - wlw/2 - 1;//r.height/7; ++ int wx = winx; ++ int wy = winy + win.ch - dc.font.descent; ++ ++#if UNDERCURL_STYLE == UNDERCURL_CURLY ++ // Draw waves ++ int narcs = charlen * 2 + 1; ++ XArc *arcs = xmalloc(sizeof(XArc) * narcs); ++ ++ int i = 0; ++ for (i = 0; i < charlen-1; i++) { ++ arcs[i*2] = (XArc) { ++ .x = wx + win.cw * i + ww / 4, ++ .y = wy, ++ .width = win.cw / 2, ++ .height = wh, ++ .angle1 = 0, ++ .angle2 = 180 * 64 ++ }; ++ arcs[i*2+1] = (XArc) { ++ .x = wx + win.cw * i + ww * 0.75, ++ .y = wy, ++ .width = win.cw/2, ++ .height = wh, ++ .angle1 = 180 * 64, ++ .angle2 = 180 * 64 ++ }; ++ } ++ // Last wave ++ arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh, ++ 0, 180 * 64 }; ++ // Last wave tail ++ arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.), ++ wh, 180 * 64, 90 * 64}; ++ // First wave tail ++ i++; ++ arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64, ++ 90 * 64 }; ++ ++ XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs); ++ ++ free(arcs); ++#elif UNDERCURL_STYLE == UNDERCURL_SPIKY ++ // Make the underline corridor larger ++ /* ++ wy -= wh; ++ */ ++ wh *= 2; ++ ++ // Set the angle of the slope to 45° ++ ww = wh; ++ ++ // Position of wave is independent of word, it's absolute ++ wx = (wx / (ww/2)) * (ww/2); ++ ++ int marginStart = winx - wx; ++ ++ // Calculate number of points with floating precision ++ float n = width; // Width of word in pixels ++ n = (n / ww) * 2; // Number of slopes (/ or \) ++ n += 2; // Add two last points ++ int npoints = n; // Convert to int ++ ++ // Total length of underline ++ float waveLength = 0; ++ ++ if (npoints >= 3) { ++ // We add an aditional slot in case we use a bonus point ++ XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); ++ ++ // First point (Starts with the word bounds) ++ points[0] = (XPoint) { ++ .x = wx + marginStart, ++ .y = (isSlopeRising(wx, 0, ww)) ++ ? (wy - marginStart + ww/2.f) ++ : (wy + marginStart) ++ }; ++ ++ // Second point (Goes back to the absolute point coordinates) ++ points[1] = (XPoint) { ++ .x = (ww/2.f) - marginStart, ++ .y = (isSlopeRising(wx, 1, ww)) ++ ? (ww/2.f - marginStart) ++ : (-ww/2.f + marginStart) ++ }; ++ waveLength += (ww/2.f) - marginStart; ++ ++ // The rest of the points ++ for (int i = 2; i < npoints-1; i++) { ++ points[i] = (XPoint) { ++ .x = ww/2, ++ .y = (isSlopeRising(wx, i, ww)) ++ ? wh/2 ++ : -wh/2 ++ }; ++ waveLength += ww/2; ++ } ++ ++ // Last point ++ points[npoints-1] = (XPoint) { ++ .x = ww/2, ++ .y = (isSlopeRising(wx, npoints-1, ww)) ++ ? wh/2 ++ : -wh/2 ++ }; ++ waveLength += ww/2; ++ ++ // End ++ if (waveLength < width) { // Add a bonus point? ++ int marginEnd = width - waveLength; ++ points[npoints] = (XPoint) { ++ .x = marginEnd, ++ .y = (isSlopeRising(wx, npoints, ww)) ++ ? (marginEnd) ++ : (-marginEnd) ++ }; ++ ++ npoints++; ++ } else if (waveLength > width) { // Is last point too far? ++ int marginEnd = waveLength - width; ++ points[npoints-1].x -= marginEnd; ++ if (isSlopeRising(wx, npoints-1, ww)) ++ points[npoints-1].y -= (marginEnd); ++ else ++ points[npoints-1].y += (marginEnd); ++ } ++ ++ // Draw the lines ++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, ++ CoordModePrevious); ++ ++ // Draw a second underline with an offset of 1 pixel ++ if ( ((win.ch / (widthThreshold/2)) % 2)) { ++ points[0].x++; ++ ++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, ++ npoints, CoordModePrevious); ++ } ++ ++ // Free resources ++ free(points); ++ } ++#else // UNDERCURL_CAPPED ++ // Cap is half of wave width ++ float capRatio = 0.5f; ++ ++ // Make the underline corridor larger ++ wh *= 2; ++ ++ // Set the angle of the slope to 45° ++ ww = wh; ++ ww *= 1 + capRatio; // Add a bit of width for the cap ++ ++ // Position of wave is independent of word, it's absolute ++ wx = (wx / ww) * ww; ++ ++ float marginStart; ++ switch(getSlope(winx, 0, ww)) { ++ case UNDERCURL_SLOPE_ASCENDING: ++ marginStart = winx - wx; ++ break; ++ case UNDERCURL_SLOPE_TOP_CAP: ++ marginStart = winx - (wx + (ww * (2.f/6.f))); ++ break; ++ case UNDERCURL_SLOPE_DESCENDING: ++ marginStart = winx - (wx + (ww * (3.f/6.f))); ++ break; ++ case UNDERCURL_SLOPE_BOTTOM_CAP: ++ marginStart = winx - (wx + (ww * (5.f/6.f))); ++ break; ++ } ++ ++ // Calculate number of points with floating precision ++ float n = width; // Width of word in pixels ++ // ._. ++ n = (n / ww) * 4; // Number of points (./ \.) ++ n += 2; // Add two last points ++ int npoints = n; // Convert to int ++ ++ // Position of the pen to draw the lines ++ float penX = 0; ++ float penY = 0; ++ ++ if (npoints >= 3) { ++ XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); ++ ++ // First point (Starts with the word bounds) ++ penX = winx; ++ switch (getSlope(winx, 0, ww)) { ++ case UNDERCURL_SLOPE_ASCENDING: ++ penY = wy + wh/2.f - marginStart; ++ break; ++ case UNDERCURL_SLOPE_TOP_CAP: ++ penY = wy; ++ break; ++ case UNDERCURL_SLOPE_DESCENDING: ++ penY = wy + marginStart; ++ break; ++ case UNDERCURL_SLOPE_BOTTOM_CAP: ++ penY = wy + wh/2.f; ++ break; ++ } ++ points[0].x = penX; ++ points[0].y = penY; ++ ++ // Second point (Goes back to the absolute point coordinates) ++ switch (getSlope(winx, 1, ww)) { ++ case UNDERCURL_SLOPE_ASCENDING: ++ penX += ww * (1.f/6.f) - marginStart; ++ penY += 0; ++ break; ++ case UNDERCURL_SLOPE_TOP_CAP: ++ penX += ww * (2.f/6.f) - marginStart; ++ penY += -wh/2.f + marginStart; ++ break; ++ case UNDERCURL_SLOPE_DESCENDING: ++ penX += ww * (1.f/6.f) - marginStart; ++ penY += 0; ++ break; ++ case UNDERCURL_SLOPE_BOTTOM_CAP: ++ penX += ww * (2.f/6.f) - marginStart; ++ penY += -marginStart + wh/2.f; ++ break; ++ } ++ points[1].x = penX; ++ points[1].y = penY; ++ ++ // The rest of the points ++ for (int i = 2; i < npoints; i++) { ++ switch (getSlope(winx, i, ww)) { ++ case UNDERCURL_SLOPE_ASCENDING: ++ case UNDERCURL_SLOPE_DESCENDING: ++ penX += ww * (1.f/6.f); ++ penY += 0; ++ break; ++ case UNDERCURL_SLOPE_TOP_CAP: ++ penX += ww * (2.f/6.f); ++ penY += -wh / 2.f; ++ break; ++ case UNDERCURL_SLOPE_BOTTOM_CAP: ++ penX += ww * (2.f/6.f); ++ penY += wh / 2.f; ++ break; ++ } ++ points[i].x = penX; ++ points[i].y = penY; ++ } ++ ++ // End ++ float waveLength = penX - winx; ++ if (waveLength < width) { // Add a bonus point? ++ int marginEnd = width - waveLength; ++ penX += marginEnd; ++ switch(getSlope(winx, npoints, ww)) { ++ case UNDERCURL_SLOPE_ASCENDING: ++ case UNDERCURL_SLOPE_DESCENDING: ++ //penY += 0; ++ break; ++ case UNDERCURL_SLOPE_TOP_CAP: ++ penY += -marginEnd; ++ break; ++ case UNDERCURL_SLOPE_BOTTOM_CAP: ++ penY += marginEnd; ++ break; ++ } ++ ++ points[npoints].x = penX; ++ points[npoints].y = penY; ++ ++ npoints++; ++ } else if (waveLength > width) { // Is last point too far? ++ int marginEnd = waveLength - width; ++ points[npoints-1].x -= marginEnd; ++ switch(getSlope(winx, npoints-1, ww)) { ++ case UNDERCURL_SLOPE_TOP_CAP: ++ points[npoints-1].y += marginEnd; ++ break; ++ case UNDERCURL_SLOPE_BOTTOM_CAP: ++ points[npoints-1].y -= marginEnd; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ // Draw the lines ++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, ++ CoordModeOrigin); ++ ++ // Draw a second underline with an offset of 1 pixel ++ if ( ((win.ch / (widthThreshold/2)) % 2)) { ++ for (int i = 0; i < npoints; i++) ++ points[i].x++; ++ ++ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, ++ npoints, CoordModeOrigin); ++ } ++ ++ // Free resources ++ free(points); ++ } ++#endif ++ } ++ ++ XFreeGC(xw.dpy, ugc); + } + + if (base.mode & ATTR_STRUCK) { diff --git a/based-simple-term/patches/st-w3m-0.8.3.diff b/based-simple-term/patches/st-w3m-0.8.3.diff new file mode 100755 index 0000000..a4e104b --- /dev/null +++ b/based-simple-term/patches/st-w3m-0.8.3.diff @@ -0,0 +1,42 @@ +From 69cffc587b54b0a9cd81adb87abad8e526d5b25b Mon Sep 17 00:00:00 2001 +From: "Avi Halachmi (:avih)" +Date: Thu, 4 Jun 2020 17:35:08 +0300 +Subject: [PATCH] support w3m images + +w3m images are a hack which renders on top of the terminal's drawable, +which didn't work in st because when using double buffering, the front +buffer (on which w3m draws its images) is ignored, and st draws only +on the back buffer, which is then copied to the front buffer. + +There's a patch to make it work at the FAQ already, but that patch +canceles double-buffering, which can have negative side effects on +some cases such as flickering. + +This patch achieves the same goal but instead of canceling the double +buffer it first copies the front buffer to the back buffer. + +This has the same issues as the FAQ patch in that the cursor line is +deleted at the image (because st renders always full lines), but +otherwise it's simpler and does keeps double buffering. +--- + x.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/x.c b/x.c +index e5f1737..b6ae162 100644 +--- a/x.c ++++ b/x.c +@@ -1594,6 +1594,8 @@ xsettitle(char *p) + int + xstartdraw(void) + { ++ if (IS_SET(MODE_VISIBLE)) ++ XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0); + return IS_SET(MODE_VISIBLE); + } + + +base-commit: 43a395ae91f7d67ce694e65edeaa7bbc720dd027 +-- +2.17.1 + diff --git a/based-simple-term/patches/st-w3m-0.8.3.diff_ b/based-simple-term/patches/st-w3m-0.8.3.diff_ new file mode 100755 index 0000000..a4e104b --- /dev/null +++ b/based-simple-term/patches/st-w3m-0.8.3.diff_ @@ -0,0 +1,42 @@ +From 69cffc587b54b0a9cd81adb87abad8e526d5b25b Mon Sep 17 00:00:00 2001 +From: "Avi Halachmi (:avih)" +Date: Thu, 4 Jun 2020 17:35:08 +0300 +Subject: [PATCH] support w3m images + +w3m images are a hack which renders on top of the terminal's drawable, +which didn't work in st because when using double buffering, the front +buffer (on which w3m draws its images) is ignored, and st draws only +on the back buffer, which is then copied to the front buffer. + +There's a patch to make it work at the FAQ already, but that patch +canceles double-buffering, which can have negative side effects on +some cases such as flickering. + +This patch achieves the same goal but instead of canceling the double +buffer it first copies the front buffer to the back buffer. + +This has the same issues as the FAQ patch in that the cursor line is +deleted at the image (because st renders always full lines), but +otherwise it's simpler and does keeps double buffering. +--- + x.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/x.c b/x.c +index e5f1737..b6ae162 100644 +--- a/x.c ++++ b/x.c +@@ -1594,6 +1594,8 @@ xsettitle(char *p) + int + xstartdraw(void) + { ++ if (IS_SET(MODE_VISIBLE)) ++ XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0); + return IS_SET(MODE_VISIBLE); + } + + +base-commit: 43a395ae91f7d67ce694e65edeaa7bbc720dd027 +-- +2.17.1 + diff --git a/based-simple-term/st b/based-simple-term/st new file mode 100755 index 0000000000000000000000000000000000000000..22eb5bbe461ea1d7d015264024db5630b8c427ff GIT binary patch literal 122688 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�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 literal 0 HcmV?d00001 diff --git a/based-simple-term/st.1 b/based-simple-term/st.1 new file mode 100755 index 0000000..39120b4 --- /dev/null +++ b/based-simple-term/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/based-simple-term/st.c b/based-simple-term/st.c new file mode 100755 index 0000000..216b529 --- /dev/null +++ b/based-simple-term/st.c @@ -0,0 +1,2859 @@ +/* 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 CAR_PER_ARG 4 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ +#define HISTSIZE 2000 + +/* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ + term.scr + HISTSIZE + 1) % HISTSIZE] : \ + term.line[(y) - term.scr]) + +enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_ALTSCREEN = 1 << 2, + MODE_CRLF = 1 << 3, + MODE_ECHO = 1 << 4, + MODE_PRINT = 1 << 5, + MODE_UTF8 = 1 << 6, +}; + +enum cursor_movement { + CURSOR_SAVE, + CURSOR_LOAD +}; + +enum cursor_state { + CURSOR_DEFAULT = 0, + CURSOR_WRAPNEXT = 1, + CURSOR_ORIGIN = 2 +}; + +enum charset { + CS_GRAPHIC0, + CS_GRAPHIC1, + CS_UK, + CS_USA, + CS_MULTI, + CS_GER, + CS_FIN +}; + +enum escape_state { + ESC_START = 1, + ESC_CSI = 2, + ESC_STR = 4, /* DCS, OSC, PM, APC */ + ESC_ALTCHARSET = 8, + ESC_STR_END = 16, /* a final string was encountered */ + ESC_TEST = 32, /* Enter in test mode */ + ESC_UTF8 = 64, +}; + +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +typedef struct { + int mode; + int type; + int snap; + /* + * Selection variables: + * nb – normalized coordinates of the beginning of the selection + * ne – normalized coordinates of the end of the selection + * ob – original coordinates of the beginning of the selection + * oe – original coordinates of the end of the selection + */ + struct { + int x, y; + } nb, ne, ob, oe; + + int alt; +} Selection; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ + Line hist[HISTSIZE]; /* history buffer */ + int histi; /* history index */ + int scr; /* scroll back */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int *tabs; + Rune lastc; /* last printed char outside of sequence, 0 if control */ +} Term; + +/* CSI Escape sequence structs */ +/* ESC '[' [[ [] [;]] []] */ +typedef struct { + char buf[ESC_BUF_SIZ]; /* raw string */ + size_t len; /* raw string length */ + char priv; + int arg[ESC_ARG_SIZ]; + int narg; /* nb of args */ + char mode[2]; + int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */ +} 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 readcolonargs(char **, int, int[][CAR_PER_ARG]); +static void csiparse(void); +static void csireset(void); +static int eschandle(uchar); +static void strdump(void); +static void strhandle(void); +static void strparse(void); +static void strreset(void); + +static void tprinter(char *, size_t); +static void tdumpsel(void); +static void tdumpline(int); +static void tdump(void); +static void tclearregion(int, int, int, int); +static void tcursor(int); +static void tdeletechar(int); +static void tdeleteline(int); +static void tinsertblank(int); +static void tinsertblankline(int); +static int tlinelen(int); +static void tmoveto(int, int); +static void tmoveato(int, int); +static void tnewline(int); +static void tputtab(int); +static void tputc(Rune); +static void treset(void); +static void tscrollup(int, int, int); +static void tscrolldown(int, int, int); +static void tsetattr(const int *, int); +static void tsetchar(Rune, const Glyph *, int, int); +static void tsetdirt(int, int); +static void tsetscroll(int, int); +static void tswapscreen(void); +static void tsetmode(int, int, const int *, int); +static int twrite(const char *, int, int); +static void tfulldirt(void); +static void tcontrolcode(uchar ); +static void tdectest(char ); +static void tdefutf8(char); +static int32_t tdefcolor(const int *, int *, int); +static void tdeftran(char); +static void tstrsequence(uchar); + +static void drawregion(int, int, int, int); + +static void selnormalize(void); +static void selscroll(int, int); +static void selsnap(int *, int *, int); + +static size_t utf8decode(const char *, Rune *, size_t); +static Rune utf8decodebyte(char, size_t *); +static char utf8encodebyte(Rune, size_t); +static size_t utf8validate(Rune *, size_t); + +static char *base64dec(const char *); +static char base64dec_getc(const char **); + +static ssize_t xwrite(int, const char *, size_t); + +/* Globals */ +static Term term; +static Selection sel; +static CSIEscape csiescseq; +static STREscape strescseq; +static int iofd = 1; +static int cmdfd; +static pid_t pid; + +static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +#include +static int su = 0; +struct timespec sutv; + +static void +tsync_begin() +{ + clock_gettime(CLOCK_MONOTONIC, &sutv); + su = 1; +} + +static void +tsync_end() +{ + su = 0; +} + +int +tinsync(uint timeout) +{ + struct timespec now; + if (su && !clock_gettime(CLOCK_MONOTONIC, &now) + && TIMEDIFF(now, sutv) >= timeout) + su = 0; + return su; +} + +ssize_t +xwrite(int fd, const char *s, size_t len) +{ + size_t aux = len; + ssize_t r; + + while (len > 0) { + r = write(fd, s, len); + if (r < 0) + return r; + len -= r; + s += r; + } + + return aux; +} + +void * +xmalloc(size_t len) +{ + void *p; + + if (!(p = malloc(len))) + die("malloc: %s\n", strerror(errno)); + + return p; +} + +void * +xrealloc(void *p, size_t len) +{ + if ((p = realloc(p, len)) == NULL) + die("realloc: %s\n", strerror(errno)); + + return p; +} + +char * +xstrdup(const char *s) +{ + char *p; + + if ((p = strdup(s)) == NULL) + die("strdup: %s\n", strerror(errno)); + + return p; +} + +size_t +utf8decode(const char *c, Rune *u, size_t clen) +{ + size_t i, j, len, type; + Rune udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type != 0) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Rune +utf8decodebyte(char c, size_t *i) +{ + for (*i = 0; *i < LEN(utfmask); ++(*i)) + if (((uchar)c & utfmask[*i]) == utfbyte[*i]) + return (uchar)c & ~utfmask[*i]; + + return 0; +} + +size_t +utf8encode(Rune u, char *c) +{ + size_t len, i; + + len = utf8validate(&u, 0); + if (len > UTF_SIZ) + return 0; + + for (i = len - 1; i != 0; --i) { + c[i] = utf8encodebyte(u, 0); + u >>= 6; + } + c[0] = utf8encodebyte(u, len); + + return len; +} + +char +utf8encodebyte(Rune u, size_t i) +{ + return utfbyte[i] | (u & ~utfmask[i]); +} + +size_t +utf8validate(Rune *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + + return i; +} + +static const char base64_digits[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +char +base64dec_getc(const char **src) +{ + while (**src && !isprint(**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char * +base64dec(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + + if (in_len % 4) + in_len += 4 - (in_len % 4); + result = dst = xmalloc(in_len / 4 * 3 + 1); + while (*src) { + int a = base64_digits[(unsigned char) base64dec_getc(&src)]; + int b = base64_digits[(unsigned char) base64dec_getc(&src)]; + int c = base64_digits[(unsigned char) base64dec_getc(&src)]; + int d = base64_digits[(unsigned char) base64dec_getc(&src)]; + + /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ + if (a == -1 || b == -1) + break; + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (c == -1) + break; + *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == -1) + break; + *dst++ = ((c & 0x03) << 6) | d; + } + *dst = '\0'; + return result; +} + +void +selinit(void) +{ + sel.mode = SEL_IDLE; + sel.snap = 0; + sel.ob.x = -1; +} + +int +tlinelen(int y) +{ + int i = term.col; + + if (TLINE(y)[i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && TLINE(y)[i - 1].u == ' ') + --i; + + return i; +} + +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type, int done) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + + if (sel.mode == SEL_IDLE) + return; + if (done && sel.mode == SEL_EMPTY) { + selclear(); + return; + } + + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + + sel.mode = done ? SEL_IDLE : SEL_READY; +} + +void +selnormalize(void) +{ + int i; + + if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { + sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; + sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; + } else { + sel.nb.x = MIN(sel.ob.x, sel.oe.x); + sel.ne.x = MAX(sel.ob.x, sel.oe.x); + } + sel.nb.y = MIN(sel.ob.y, sel.oe.y); + sel.ne.y = MAX(sel.ob.y, sel.oe.y); + + selsnap(&sel.nb.x, &sel.nb.y, -1); + selsnap(&sel.ne.x, &sel.ne.y, +1); + + /* expand selection over line breaks */ + if (sel.type == SEL_RECTANGULAR) + return; + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) + sel.nb.x = i; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; +} + +int +selected(int x, int y) +{ + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) + return 0; + + if (sel.type == SEL_RECTANGULAR) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); + + return BETWEEN(y, sel.nb.y, sel.ne.y) + && (y != sel.nb.y || x >= sel.nb.x) + && (y != sel.ne.y || x <= sel.ne.x); +} + +void +selsnap(int *x, int *y, int direction) +{ + int newx, newy, xt, yt; + int delim, prevdelim; + const Glyph *gp, *prevgp; + + switch (sel.snap) { + case SNAP_WORD: + /* + * Snap around if the word wraps around at the end or + * beginning of a line. + */ + prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; + newy = *y; + if (!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) + break; + + if (direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + + gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) + break; + + *x = newx; + *y = newy; + prevgp = gp; + prevdelim = delim; + } + break; + case SNAP_LINE: + /* + * Snap around if the the previous line or the current one + * has set ATTR_WRAP at its end. Then the whole next or + * previous line will be selected. + */ + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { + if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { + if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } + break; + } +} + +char * +getsel(void) +{ + char *str, *ptr; + int y, bufsize, lastx, linelen; + const Glyph *gp, *last; + + if (sel.ob.x == -1) + return NULL; + + bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; + ptr = str = xmalloc(bufsize); + + /* append every set & selected glyph to the selection */ + for (y = sel.nb.y; y <= sel.ne.y; y++) { + if ((linelen = tlinelen(y)) == 0) { + *ptr++ = '\n'; + continue; + } + + if (sel.type == SEL_RECTANGULAR) { + gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + + for ( ; gp <= last; ++gp) { + if (gp->mode & ATTR_WDUMMY) + continue; + + ptr += utf8encode(gp->u, ptr); + } + + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if ((y < sel.ne.y || lastx >= linelen) && + (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) + *ptr++ = '\n'; + } + *ptr = 0; + return str; +} + +void +selclear(void) +{ + if (sel.ob.x == -1) + return; + sel.mode = SEL_IDLE; + sel.ob.x = -1; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void +execsh(char *cmd, char **args) +{ + char *sh, *prog, *arg; + const struct passwd *pw; + + errno = 0; + if ((pw = getpwuid(getuid())) == NULL) { + if (errno) + die("getpwuid: %s\n", strerror(errno)); + else + die("who are you?\n"); + } + + if ((sh = getenv("SHELL")) == NULL) + sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; + + if (args) { + prog = args[0]; + arg = NULL; + } else if (scroll) { + prog = scroll; + arg = utmp ? utmp : sh; + } else if (utmp) { + prog = utmp; + arg = NULL; + } else { + prog = sh; + arg = NULL; + } + DEFAULT(args, ((char *[]) {prog, arg, NULL})); + + unsetenv("COLUMNS"); + unsetenv("LINES"); + unsetenv("TERMCAP"); + setenv("LOGNAME", pw->pw_name, 1); + setenv("USER", pw->pw_name, 1); + setenv("SHELL", sh, 1); + setenv("HOME", pw->pw_dir, 1); + setenv("TERM", termname, 1); + + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + execvp(prog, args); + _exit(1); +} + +void +sigchld(int a) +{ + int stat; + pid_t p; + + if ((p = waitpid(pid, &stat, WNOHANG)) < 0) + die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); + + if (pid != p) + return; + + if (WIFEXITED(stat) && WEXITSTATUS(stat)) + die("child exited with status %d\n", WEXITSTATUS(stat)); + else if (WIFSIGNALED(stat)) + die("child terminated due to signal %d\n", WTERMSIG(stat)); + _exit(0); +} + +void +stty(char **args) +{ + char cmd[_POSIX_ARG_MAX], **p, *q, *s; + size_t n, siz; + + if ((n = strlen(stty_args)) > sizeof(cmd)-1) + die("incorrect stty parameters\n"); + memcpy(cmd, stty_args, n); + q = cmd + n; + siz = sizeof(cmd) - n; + for (p = args; p && (s = *p); ++p) { + if ((n = strlen(s)) > siz-1) + die("stty parameter length too long\n"); + *q++ = ' '; + memcpy(q, s, n); + q += n; + siz -= n + 1; + } + *q = '\0'; + if (system(cmd) != 0) + perror("Couldn't call stty"); +} + +int +ttynew(const char *line, char *cmd, const char *out, char **args) +{ + int m, s; + + if (out) { + term.mode |= MODE_PRINT; + iofd = (!strcmp(out, "-")) ? + 1 : open(out, O_WRONLY | O_CREAT, 0666); + if (iofd < 0) { + fprintf(stderr, "Error opening %s:%s\n", + out, strerror(errno)); + } + } + + if (line) { + if ((cmdfd = open(line, O_RDWR)) < 0) + die("open line '%s' failed: %s\n", + line, strerror(errno)); + dup2(cmdfd, 0); + stty(args); + return cmdfd; + } + + /* seems to work fine on linux, openbsd and freebsd */ + if (openpty(&m, &s, NULL, NULL, NULL) < 0) + die("openpty failed: %s\n", strerror(errno)); + + switch (pid = fork()) { + case -1: + die("fork failed: %s\n", strerror(errno)); + break; + case 0: + close(iofd); + close(m); + setsid(); /* create a new process group */ + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + if (ioctl(s, TIOCSCTTY, NULL) < 0) + die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); + if (s > 2) + close(s); +#ifdef __OpenBSD__ + if (pledge("stdio getpw proc exec", NULL) == -1) + die("pledge\n"); +#endif + execsh(cmd, args); + break; + default: +#ifdef __OpenBSD__ + if (pledge("stdio rpath tty proc", NULL) == -1) + die("pledge\n"); +#endif + close(s); + cmdfd = m; + signal(SIGCHLD, sigchld); + break; + } + return cmdfd; +} + +static int twrite_aborted = 0; +int ttyread_pending() { return twrite_aborted; } + +size_t +ttyread(void) +{ + static char buf[BUFSIZ]; + static int buflen = 0; + int ret, written; + + /* append read bytes to unprocessed bytes */ + ret = twrite_aborted ? 1 : 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 += twrite_aborted ? 0 : ret; + written = twrite(buf, buflen, 0); + buflen -= written; + /* keep any incomplete UTF-8 byte sequence for the next call */ + if (buflen > 0) + memmove(buf, buf + written, buflen); + return ret; + } +} + +void +ttywrite(const char *s, size_t n, int may_echo) +{ + const char *next; + Arg arg = (Arg) { .i = term.scr }; + + kscrolldown(&arg); + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); + + if (!IS_SET(MODE_CRLF)) { + ttywriteraw(s, n); + return; + } + + /* This is similar to how the kernel handles ONLCR for ttys */ + while (n > 0) { + if (*s == '\r') { + next = s + 1; + ttywriteraw("\r\n", 2); + } else { + next = memchr(s, '\r', n); + DEFAULT(next, s + n); + ttywriteraw(s, next - s); + } + n -= next - s; + s = next; + } +} + +void +ttywriteraw(const char *s, size_t n) +{ + fd_set wfd, rfd; + ssize_t r; + size_t lim = 256; + + /* + * Remember that we are using a pty, which might be a modem line. + * Writing too much will clog the line. That's why we are doing this + * dance. + * FIXME: Migrate the world to Plan 9. + */ + while (n > 0) { + FD_ZERO(&wfd); + FD_ZERO(&rfd); + FD_SET(cmdfd, &wfd); + FD_SET(cmdfd, &rfd); + + /* Check if we can write. */ + if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(cmdfd, &wfd)) { + /* + * Only write the bytes written by ttywrite() or the + * default of 256. This seems to be a reasonable value + * for a serial line. Bigger values might clog the I/O. + */ + if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) + goto write_error; + if (r < n) { + /* + * We weren't able to write out everything. + * This means the buffer is getting full + * again. Empty it. + */ + if (n < lim) + lim = ttyread(); + n -= r; + s += r; + } else { + /* All bytes have been written. */ + break; + } + } + if (FD_ISSET(cmdfd, &rfd)) + lim = ttyread(); + } + return; + +write_error: + die("write error on tty: %s\n", strerror(errno)); +} + +void +ttyresize(int tw, int th) +{ + struct winsize w; + + w.ws_row = term.row; + w.ws_col = term.col; + w.ws_xpixel = tw; + w.ws_ypixel = th; + if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) + fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); +} + +void +ttyhangup() +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + +int +tattrset(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) + return 1; + } + } + + return 0; +} + +void +tsetdirt(int top, int bot) +{ + int i; + + LIMIT(top, 0, term.row-1); + LIMIT(bot, 0, term.row-1); + + for (i = top; i <= bot; i++) + term.dirty[i] = 1; +} + +void +tsetdirtattr(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) { + tsetdirt(i, i); + break; + } + } + } +} + +void +tfulldirt(void) +{ + tsync_end(); + 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(); +} + +int tisaltscr(void) +{ + return IS_SET(MODE_ALTSCREEN); +} + +void +tswapscreen(void) +{ + Line *tmp = term.line; + + term.line = term.alt; + term.alt = tmp; + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); +} + +void +kscrolldown(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (n > term.scr) + n = term.scr; + + if (term.scr > 0) { + term.scr -= n; + selscroll(0, -n); + tfulldirt(); + } +} + +void +kscrollup(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (term.scr <= HISTSIZE-n) { + term.scr += n; + selscroll(0, n); + tfulldirt(); + } +} + +void +tscrolldown(int orig, int n, int copyhist) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + if (copyhist) { + term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[term.bot]; + term.line[term.bot] = temp; + } + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + + for (i = term.bot; i >= orig+n; i--) { + temp = term.line[i]; + term.line[i] = term.line[i-n]; + term.line[i-n] = temp; + } + + if (term.scr == 0) + selscroll(orig, n); +} + +void +tscrollup(int orig, int n, int copyhist) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + if (copyhist) { + term.histi = (term.histi + 1) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[orig]; + term.line[orig] = temp; + } + + if (term.scr > 0 && term.scr < HISTSIZE) + term.scr = MIN(term.scr + n, HISTSIZE-1); + + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + + for (i = orig; i <= term.bot-n; i++) { + temp = term.line[i]; + term.line[i] = term.line[i+n]; + term.line[i+n] = temp; + } + + if (term.scr == 0) + selscroll(orig, -n); +} + +void +selscroll(int orig, int n) +{ + if (sel.ob.x == -1) + return; + + if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { + selclear(); + } else if (BETWEEN(sel.nb.y, orig, term.bot)) { + sel.ob.y += n; + sel.oe.y += n; + if (sel.ob.y < term.top || sel.ob.y > term.bot || + sel.oe.y < term.top || sel.oe.y > term.bot) { + selclear(); + } else { + selnormalize(); + } + } +} + +void +tnewline(int first_col) +{ + int y = term.c.y; + + if (y == term.bot) { + tscrollup(term.top, 1, 1); + } else { + y++; + } + tmoveto(first_col ? 0 : term.c.x, y); +} + +void +readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG]) +{ + int i = 0; + for (; i < CAR_PER_ARG; i++) + params[cursor][i] = -1; + + if (**p != ':') + return; + + char *np = NULL; + i = 0; + + while (**p == ':' && i < CAR_PER_ARG) { + while (**p == ':') + (*p)++; + params[cursor][i] = strtol(*p, &np, 10); + *p = np; + i++; + } +} + +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; + readcolonargs(&p, csiescseq.narg-1, csiescseq.carg); + if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + break; + p++; + } + csiescseq.mode[0] = *p++; + csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; +} + +/* for absolute user moves, when decom is set */ +void +tmoveato(int x, int y) +{ + tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0)); +} + +void +tmoveto(int x, int y) +{ + int miny, maxy; + + if (term.c.state & CURSOR_ORIGIN) { + miny = term.top; + maxy = term.bot; + } else { + miny = 0; + maxy = term.row - 1; + } + term.c.state &= ~CURSOR_WRAPNEXT; + term.c.x = LIMIT(x, 0, term.col-1); + term.c.y = LIMIT(y, miny, maxy); +} + +void +tsetchar(Rune u, const Glyph *attr, int x, int y) +{ + static const char *vt100_0[62] = { /* 0x41 - 0x7e */ + "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ + 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ + "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ + "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ + }; + + /* + * The table is proudly stolen from rxvt. + */ + if (term.trantbl[term.charset] == CS_GRAPHIC0 && + BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) + utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); + + if (term.line[y][x].mode & ATTR_WIDE) { + if (x+1 < term.col) { + term.line[y][x+1].u = ' '; + term.line[y][x+1].mode &= ~ATTR_WDUMMY; + } + } else if (term.line[y][x].mode & ATTR_WDUMMY) { + term.line[y][x-1].u = ' '; + term.line[y][x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; + term.line[y][x] = *attr; + term.line[y][x].u = u; +} + +void +tclearregion(int x1, int y1, int x2, int y2) +{ + int x, y, temp; + Glyph *gp; + + if (x1 > x2) + temp = x1, x1 = x2, x2 = temp; + if (y1 > y2) + temp = y1, y1 = y2, y2 = temp; + + LIMIT(x1, 0, term.col-1); + LIMIT(x2, 0, term.col-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { + gp = &term.line[y][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + gp->u = ' '; + } + } +} + +void +tdeletechar(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +} + +void +tinsertblank(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +} + +void +tinsertblankline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrolldown(term.c.y, n, 0); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n, 0); +} + +int32_t +tdefcolor(const int *attr, int *npar, int l) +{ + int32_t idx = -1; + uint r, g, b; + + switch (attr[*npar + 1]) { + case 2: /* direct color in RGB space */ + if (*npar + 4 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + r = attr[*npar + 2]; + g = attr[*npar + 3]; + b = attr[*npar + 4]; + *npar += 4; + if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) + fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n", + r, g, b); + else + idx = TRUECOLOR(r, g, b); + break; + case 5: /* indexed color */ + if (*npar + 2 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + *npar += 2; + if (!BETWEEN(attr[*npar], 0, 255)) + fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]); + else + idx = attr[*npar]; + break; + case 0: /* implemented defined (only foreground) */ + case 1: /* transparent */ + case 3: /* direct color in CMY space */ + case 4: /* direct color in CMYK space */ + default: + fprintf(stderr, + "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; + } + + return idx; +} + +void +tsetattr(const int *attr, int l) +{ + int i; + int32_t idx; + + for (i = 0; i < l; i++) { + switch (attr[i]) { + case 0: + term.c.attr.mode &= ~( + ATTR_BOLD | + ATTR_FAINT | + ATTR_ITALIC | + ATTR_UNDERLINE | + ATTR_BLINK | + ATTR_REVERSE | + ATTR_INVISIBLE | + ATTR_STRUCK ); + term.c.attr.fg = defaultfg; + term.c.attr.bg = defaultbg; + term.c.attr.ustyle = -1; + term.c.attr.ucolor[0] = -1; + term.c.attr.ucolor[1] = -1; + term.c.attr.ucolor[2] = -1; + 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.ustyle = csiescseq.carg[i][0]; + + if (term.c.attr.ustyle != 0) + term.c.attr.mode |= ATTR_UNDERLINE; + else + term.c.attr.mode &= ~ATTR_UNDERLINE; + + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; + 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; + case 58: + term.c.attr.ucolor[0] = csiescseq.carg[i][1]; + term.c.attr.ucolor[1] = csiescseq.carg[i][2]; + term.c.attr.ucolor[2] = csiescseq.carg[i][3]; + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; + break; + case 59: + term.c.attr.ucolor[0] = -1; + term.c.attr.ucolor[1] = -1; + term.c.attr.ucolor[2] = -1; + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; + break; + default: + if (BETWEEN(attr[i], 30, 37)) { + term.c.attr.fg = attr[i] - 30; + } else if (BETWEEN(attr[i], 40, 47)) { + term.c.attr.bg = attr[i] - 40; + } else if (BETWEEN(attr[i], 90, 97)) { + term.c.attr.fg = attr[i] - 90 + 8; + } else if (BETWEEN(attr[i], 100, 107)) { + term.c.attr.bg = attr[i] - 100 + 8; + } else { + fprintf(stderr, + "erresc(default): gfx attr %d unknown\n", + attr[i]); + csidump(); + } + break; + } + } +} + +void +tsetscroll(int t, int b) +{ + int temp; + + LIMIT(t, 0, term.row-1); + LIMIT(b, 0, term.row-1); + if (t > b) { + temp = t; + t = b; + b = temp; + } + term.top = t; + term.bot = b; +} + +void +tsetmode(int priv, int set, const int *args, int narg) +{ + int alt; const int *lim; + + for (lim = args + narg; args < lim; ++args) { + if (priv) { + switch (*args) { + case 1: /* DECCKM -- Cursor key */ + xsetmode(set, MODE_APPCURSOR); + break; + case 5: /* DECSCNM -- Reverse video */ + xsetmode(set, MODE_REVERSE); + break; + case 6: /* DECOM -- Origin */ + MODBIT(term.c.state, set, CURSOR_ORIGIN); + tmoveato(0, 0); + break; + case 7: /* DECAWM -- Auto wrap */ + MODBIT(term.mode, set, MODE_WRAP); + break; + case 0: /* Error (IGNORED) */ + case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ + case 3: /* DECCOLM -- Column (IGNORED) */ + case 4: /* DECSCLM -- Scroll (IGNORED) */ + case 8: /* DECARM -- Auto repeat (IGNORED) */ + case 18: /* DECPFF -- Printer feed (IGNORED) */ + case 19: /* DECPEX -- Printer extent (IGNORED) */ + case 42: /* DECNRCM -- National characters (IGNORED) */ + case 12: /* att610 -- Start blinking cursor (IGNORED) */ + break; + case 25: /* DECTCEM -- Text Cursor Enable Mode */ + xsetmode(!set, MODE_HIDE); + break; + case 9: /* X10 mouse compatibility mode */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEX10); + break; + case 1000: /* 1000: report button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEBTN); + break; + case 1002: /* 1002: report motion on button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMOTION); + break; + case 1003: /* 1003: enable all mouse motions */ + xsetpointermotion(set); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMANY); + break; + case 1004: /* 1004: send focus events to tty */ + xsetmode(set, MODE_FOCUS); + break; + case 1006: /* 1006: extended reporting mode */ + xsetmode(set, MODE_MOUSESGR); + break; + case 1034: + xsetmode(set, MODE_8BIT); + break; + case 1049: /* swap screen & set/restore cursor as xterm */ + if (!allowaltscreen) + break; + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + /* FALLTHROUGH */ + case 47: /* swap screen */ + case 1047: + if (!allowaltscreen) + break; + alt = IS_SET(MODE_ALTSCREEN); + if (alt) { + tclearregion(0, 0, term.col-1, + term.row-1); + } + if (set ^ alt) /* set is always 1 or 0 */ + tswapscreen(); + if (*args != 1049) + break; + /* FALLTHROUGH */ + case 1048: + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + break; + case 2004: /* 2004: bracketed paste mode */ + xsetmode(set, MODE_BRCKTPASTE); + break; + /* Not implemented mouse modes. See comments there. */ + case 1001: /* mouse highlight mode; can hang the + terminal by design when implemented. */ + case 1005: /* UTF-8 mouse mode; will confuse + applications not supporting UTF-8 + and luit. */ + case 1015: /* urxvt mangled mouse mode; incompatible + and can be mistaken for other control + codes. */ + break; + default: + fprintf(stderr, + "erresc: unknown private set/reset mode %d\n", + *args); + break; + } + } else { + switch (*args) { + case 0: /* Error (IGNORED) */ + break; + case 2: + xsetmode(set, MODE_KBDLOCK); + break; + case 4: /* IRM -- Insertion-replacement */ + MODBIT(term.mode, set, MODE_INSERT); + break; + case 12: /* SRM -- Send/Receive */ + MODBIT(term.mode, !set, MODE_ECHO); + break; + case 20: /* LNM -- Linefeed/new line */ + MODBIT(term.mode, set, MODE_CRLF); + break; + default: + fprintf(stderr, + "erresc: unknown set/reset mode %d\n", + *args); + break; + } + } + } +} + +void +csihandle(void) +{ + char buf[40]; + int len; + + switch (csiescseq.mode[0]) { + default: + unknown: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* die(""); */ + break; + case '@': /* ICH -- Insert blank char */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblank(csiescseq.arg[0]); + break; + case 'A': /* CUU -- Cursor Up */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); + break; + case 'B': /* CUD -- Cursor Down */ + case 'e': /* VPR --Cursor Down */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); + break; + case 'i': /* MC -- Media Copy */ + switch (csiescseq.arg[0]) { + case 0: + tdump(); + break; + case 1: + tdumpline(term.c.y); + break; + case 2: + tdumpsel(); + break; + case 4: + term.mode &= ~MODE_PRINT; + break; + case 5: + term.mode |= MODE_PRINT; + break; + } + break; + case 'c': /* DA -- Device Attributes */ + if (csiescseq.arg[0] == 0) + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'b': /* REP -- if last char is printable print it more times */ + DEFAULT(csiescseq.arg[0], 1); + if (term.lastc) + while (csiescseq.arg[0]-- > 0) + tputc(term.lastc); + break; + case 'C': /* CUF -- Cursor Forward */ + case 'a': /* HPR -- Cursor Forward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x+csiescseq.arg[0], term.c.y); + break; + case 'D': /* CUB -- Cursor Backward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x-csiescseq.arg[0], term.c.y); + break; + case 'E': /* CNL -- Cursor Down and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y+csiescseq.arg[0]); + break; + case 'F': /* CPL -- Cursor Up and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y-csiescseq.arg[0]); + break; + case 'g': /* TBC -- Tabulation clear */ + switch (csiescseq.arg[0]) { + case 0: /* clear current tab stop */ + term.tabs[term.c.x] = 0; + break; + case 3: /* clear all the tabs */ + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + break; + default: + goto unknown; + } + break; + case 'G': /* CHA -- Move to */ + case '`': /* HPA */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(csiescseq.arg[0]-1, term.c.y); + break; + case 'H': /* CUP -- Move to */ + case 'f': /* HVP */ + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], 1); + tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); + break; + case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(csiescseq.arg[0]); + break; + case 'J': /* ED -- Clear screen */ + switch (csiescseq.arg[0]) { + case 0: /* below */ + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); + if (term.c.y < term.row-1) { + tclearregion(0, term.c.y+1, term.col-1, + term.row-1); + } + break; + case 1: /* above */ + if (term.c.y > 1) + tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, 0, term.col-1, term.row-1); + break; + default: + goto unknown; + } + break; + case 'K': /* EL -- Clear line */ + switch (csiescseq.arg[0]) { + case 0: /* right */ + tclearregion(term.c.x, term.c.y, term.col-1, + term.c.y); + break; + case 1: /* left */ + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, term.c.y, term.col-1, term.c.y); + break; + } + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); + tscrollup(term.top, csiescseq.arg[0], 0); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[0], 0); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblankline(csiescseq.arg[0]); + break; + case 'l': /* RM -- Reset Mode */ + tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + break; + case 'M': /* DL -- Delete lines */ + DEFAULT(csiescseq.arg[0], 1); + tdeleteline(csiescseq.arg[0]); + break; + case 'X': /* ECH -- Erase char */ + DEFAULT(csiescseq.arg[0], 1); + tclearregion(term.c.x, term.c.y, + term.c.x + csiescseq.arg[0] - 1, term.c.y); + break; + case 'P': /* DCH -- Delete char */ + DEFAULT(csiescseq.arg[0], 1); + tdeletechar(csiescseq.arg[0]); + break; + case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(-csiescseq.arg[0]); + break; + case 'd': /* VPA -- Move to */ + DEFAULT(csiescseq.arg[0], 1); + tmoveato(term.c.x, csiescseq.arg[0]-1); + break; + case 'h': /* SM -- Set terminal mode */ + tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); + break; + case 'm': /* SGR -- Terminal attribute (color) */ + tsetattr(csiescseq.arg, csiescseq.narg); + break; + case 'n': /* DSR – Device Status Report (cursor position) */ + if (csiescseq.arg[0] == 6) { + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + } + break; + case 'r': /* DECSTBM -- Set Scrolling Region */ + if (csiescseq.priv) { + goto unknown; + } else { + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], term.row); + tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); + tmoveato(0, 0); + } + break; + case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ + tcursor(CURSOR_SAVE); + break; + case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ + tcursor(CURSOR_LOAD); + break; + case ' ': + switch (csiescseq.mode[1]) { + case 'q': /* DECSCUSR -- Set Cursor Style */ + if (xsetcursor(csiescseq.arg[0])) + goto unknown; + break; + default: + goto unknown; + } + break; + } +} + +void +csidump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC["); + for (i = 0; i < csiescseq.len; i++) { + c = csiescseq.buf[i] & 0xff; + if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + putc('\n', stderr); +} + +void +csireset(void) +{ + memset(&csiescseq, 0, sizeof(csiescseq)); +} + +void +osc4_color_response(int num) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(num, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch osc4 color %d\n", num); + return; + } + + n = snprintf(buf, sizeof buf, "\033]4;%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + num, r, r, g, g, b, b); + + ttywrite(buf, n, 1); +} + +void +osc_color_response(int index, int num) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(index, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch osc color %d\n", index); + return; + } + + n = snprintf(buf, sizeof buf, "\033]%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + num, r, r, g, g, b, b); + + ttywrite(buf, n, 1); +} + +void +strhandle(void) +{ + char *p = NULL, *dec; + int j, narg, par; + + term.esc &= ~(ESC_STR_END|ESC_STR); + strparse(); + par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; + + switch (strescseq.type) { + case ']': /* OSC -- Operating System Command */ + switch (par) { + case 0: + if (narg > 1) { + xsettitle(strescseq.args[1]); + xseticontitle(strescseq.args[1]); + } + return; + case 1: + if (narg > 1) + xseticontitle(strescseq.args[1]); + return; + case 2: + if (narg > 1) + xsettitle(strescseq.args[1]); + return; + case 52: + if (narg > 2 && allowwindowops) { + dec = base64dec(strescseq.args[2]); + if (dec) { + xsetsel(dec); + xclipcopy(); + } else { + fprintf(stderr, "erresc: invalid base64\n"); + } + } + return; + case 10: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultfg, 10); + else if (xsetcolorname(defaultfg, p)) + fprintf(stderr, "erresc: invalid foreground color: %s\n", p); + else + redraw(); + return; + case 11: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultbg, 11); + else if (xsetcolorname(defaultbg, p)) + fprintf(stderr, "erresc: invalid background color: %s\n", p); + else + redraw(); + return; + case 12: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultcs, 12); + else if (xsetcolorname(defaultcs, p)) + fprintf(stderr, "erresc: invalid cursor color: %s\n", p); + else + redraw(); + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + + if (p && !strcmp(p, "?")) + osc4_color_response(j); + else if (xsetcolorname(j, p)) { + if (par == 104 && narg <= 1) + return; /* color reset without parameter */ + fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", + j, p ? p : "(null)"); + } else { + /* + * TODO if defaultbg color is changed, borders + * are dirty + */ + redraw(); + } + return; + } + break; + case 'k': /* old title set compatibility */ + xsettitle(strescseq.args[0]); + return; + case 'P': /* DCS -- Device Control String */ + /* https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec */ + if (strstr(strescseq.buf, "=1s") == strescseq.buf) + tsync_begin(); /* BSU */ + else if (strstr(strescseq.buf, "=2s") == strescseq.buf) + tsync_end(); /* ESU */ + return; + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + return; + } + + fprintf(stderr, "erresc: unknown str "); + strdump(); +} + +void +strparse(void) +{ + int c; + char *p = strescseq.buf; + + strescseq.narg = 0; + strescseq.buf[strescseq.len] = '\0'; + + if (*p == '\0') + return; + + while (strescseq.narg < STR_ARG_SIZ) { + strescseq.args[strescseq.narg++] = p; + while ((c = *p) != ';' && c != '\0') + ++p; + if (c == '\0') + return; + *p++ = '\0'; + } +} + +void +strdump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC%c", strescseq.type); + for (i = 0; i < strescseq.len; i++) { + c = strescseq.buf[i] & 0xff; + if (c == '\0') { + putc('\n', stderr); + return; + } else if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + fprintf(stderr, "ESC\\\n"); +} + +void +strreset(void) +{ + strescseq = (STREscape){ + .buf = xrealloc(strescseq.buf, STR_BUF_SIZ), + .siz = STR_BUF_SIZ, + }; +} + +void +sendbreak(const Arg *arg) +{ + if (tcsendbreak(cmdfd, 0)) + perror("Error sending break"); +} + +void +tprinter(char *s, size_t len) +{ + if (iofd != -1 && xwrite(iofd, s, len) < 0) { + perror("Error writing to output file"); + close(iofd); + iofd = -1; + } +} + +void +toggleprinter(const Arg *arg) +{ + term.mode ^= MODE_PRINT; +} + +void +printscreen(const Arg *arg) +{ + tdump(); +} + +void +printsel(const Arg *arg) +{ + tdumpsel(); +} + +void +tdumpsel(void) +{ + char *ptr; + + if ((ptr = getsel())) { + tprinter(ptr, strlen(ptr)); + free(ptr); + } +} + +void +tdumpline(int n) +{ + char buf[UTF_SIZ]; + const Glyph *bp, *end; + + bp = &term.line[n][0]; + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if (bp != end || bp->u != ' ') { + for ( ; bp <= end; ++bp) + tprinter(buf, utf8encode(bp->u, buf)); + } + tprinter("\n", 1); +} + +void +tdump(void) +{ + int i; + + for (i = 0; i < term.row; ++i) + tdumpline(i); +} + +void +tputtab(int n) +{ + uint x = term.c.x; + + if (n > 0) { + while (x < term.col && n--) + for (++x; x < term.col && !term.tabs[x]; ++x) + /* nothing */ ; + } else if (n < 0) { + while (x > 0 && n++) + for (--x; x > 0 && !term.tabs[x]; --x) + /* nothing */ ; + } + term.c.x = LIMIT(x, 0, term.col-1); +} + +void +tdefutf8(char ascii) +{ + if (ascii == 'G') + term.mode |= MODE_UTF8; + else if (ascii == '@') + term.mode &= ~MODE_UTF8; +} + +void +tdeftran(char ascii) +{ + static char cs[] = "0B"; + static int vcs[] = {CS_GRAPHIC0, CS_USA}; + char *p; + + if ((p = strchr(cs, ascii)) == NULL) { + fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); + } else { + term.trantbl[term.icharset] = vcs[p - cs]; + } +} + +void +tdectest(char c) +{ + int x, y; + + if (c == '8') { /* DEC screen alignment test. */ + for (x = 0; x < term.col; ++x) { + for (y = 0; y < term.row; ++y) + tsetchar('E', &term.c.attr, x, y); + } + } +} + +void +tstrsequence(uchar c) +{ + switch (c) { + case 0x90: /* DCS -- Device Control String */ + c = 'P'; + break; + case 0x9f: /* APC -- Application Program Command */ + c = '_'; + break; + case 0x9e: /* PM -- Privacy Message */ + c = '^'; + break; + case 0x9d: /* OSC -- Operating System Command */ + c = ']'; + break; + } + strreset(); + strescseq.type = c; + term.esc |= ESC_STR; +} + +void +tcontrolcode(uchar ascii) +{ + switch (ascii) { + case '\t': /* HT */ + tputtab(1); + return; + case '\b': /* BS */ + tmoveto(term.c.x-1, term.c.y); + return; + case '\r': /* CR */ + tmoveto(0, term.c.y); + return; + case '\f': /* LF */ + case '\v': /* VT */ + case '\n': /* LF */ + /* go to first col if the mode is set */ + tnewline(IS_SET(MODE_CRLF)); + return; + case '\a': /* BEL */ + if (term.esc & ESC_STR_END) { + /* backwards compatibility to xterm */ + strhandle(); + } else { + xbell(); + } + break; + case '\033': /* ESC */ + csireset(); + term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); + term.esc |= ESC_START; + return; + case '\016': /* SO (LS1 -- Locking shift 1) */ + case '\017': /* SI (LS0 -- Locking shift 0) */ + term.charset = 1 - (ascii - '\016'); + return; + case '\032': /* SUB */ + tsetchar('?', &term.c.attr, term.c.x, term.c.y); + /* FALLTHROUGH */ + case '\030': /* CAN */ + csireset(); + break; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + return; + case 0x80: /* TODO: PAD */ + case 0x81: /* TODO: HOP */ + case 0x82: /* TODO: BPH */ + case 0x83: /* TODO: NBH */ + case 0x84: /* TODO: IND */ + break; + case 0x85: /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 0x86: /* TODO: SSA */ + case 0x87: /* TODO: ESA */ + break; + case 0x88: /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 0x89: /* TODO: HTJ */ + case 0x8a: /* TODO: VTS */ + case 0x8b: /* TODO: PLD */ + case 0x8c: /* TODO: PLU */ + case 0x8d: /* TODO: RI */ + case 0x8e: /* TODO: SS2 */ + case 0x8f: /* TODO: SS3 */ + case 0x91: /* TODO: PU1 */ + case 0x92: /* TODO: PU2 */ + case 0x93: /* TODO: STS */ + case 0x94: /* TODO: CCH */ + case 0x95: /* TODO: MW */ + case 0x96: /* TODO: SPA */ + case 0x97: /* TODO: EPA */ + case 0x98: /* TODO: SOS */ + case 0x99: /* TODO: SGCI */ + break; + case 0x9a: /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 0x9b: /* TODO: CSI */ + case 0x9c: /* TODO: ST */ + break; + case 0x90: /* DCS -- Device Control String */ + case 0x9d: /* OSC -- Operating System Command */ + case 0x9e: /* PM -- Privacy Message */ + case 0x9f: /* APC -- Application Program Command */ + tstrsequence(ascii); + return; + } + /* only CAN, SUB, \a and C1 chars interrupt a sequence */ + term.esc &= ~(ESC_STR_END|ESC_STR); +} + +/* + * returns 1 when the sequence is finished and it hasn't to read + * more characters for this sequence, otherwise 0 + */ +int +eschandle(uchar ascii) +{ + switch (ascii) { + case '[': + term.esc |= ESC_CSI; + return 0; + case '#': + term.esc |= ESC_TEST; + return 0; + case '%': + term.esc |= ESC_UTF8; + return 0; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + case ']': /* OSC -- Operating System Command */ + case 'k': /* old title set compatibility */ + tstrsequence(ascii); + return 0; + case 'n': /* LS2 -- Locking shift 2 */ + case 'o': /* LS3 -- Locking shift 3 */ + term.charset = 2 + (ascii - 'n'); + break; + case '(': /* GZD4 -- set primary charset G0 */ + case ')': /* G1D4 -- set secondary charset G1 */ + case '*': /* G2D4 -- set tertiary charset G2 */ + case '+': /* G3D4 -- set quaternary charset G3 */ + term.icharset = ascii - '('; + term.esc |= ESC_ALTCHARSET; + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { + tscrollup(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { + tscrolldown(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'c': /* RIS -- Reset to initial state */ + treset(); + resettitle(); + xloadcols(); + break; + case '=': /* DECPAM -- Application keypad */ + xsetmode(1, MODE_APPKEYPAD); + break; + case '>': /* DECPNM -- Normal keypad */ + xsetmode(0, MODE_APPKEYPAD); + break; + case '7': /* DECSC -- Save Cursor */ + tcursor(CURSOR_SAVE); + break; + case '8': /* DECRC -- Restore Cursor */ + tcursor(CURSOR_LOAD); + break; + case '\\': /* ST -- String Terminator */ + if (term.esc & ESC_STR_END) + strhandle(); + break; + default: + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", + (uchar) ascii, isprint(ascii)? ascii:'.'); + break; + } + return 1; +} + +void +tputc(Rune u) +{ + char c[UTF_SIZ]; + int control; + int width, len; + Glyph *gp; + + control = ISCONTROL(u); + if (u < 127 || !IS_SET(MODE_UTF8)) { + c[0] = u; + width = len = 1; + } else { + len = utf8encode(u, c); + if (!control && (width = wcwidth(u)) == -1) + width = 1; + } + + if (IS_SET(MODE_PRINT)) + tprinter(c, len); + + /* + * STR sequence must be checked before anything else + * because it uses all following characters until it + * receives a ESC, a SUB, a ST or any other C1 control + * character. + */ + if (term.esc & ESC_STR) { + if (u == '\a' || u == 030 || u == 032 || u == 033 || + ISCONTROLC1(u)) { + term.esc &= ~(ESC_START|ESC_STR); + term.esc |= ESC_STR_END; + goto check_control_code; + } + + if (strescseq.len+len >= strescseq.siz) { + /* + * Here is a bug in terminals. If the user never sends + * some code to stop the str or esc command, then st + * will stop responding. But this is better than + * silently failing with unknown characters. At least + * then users will report back. + * + * In the case users ever get fixed, here is the code: + */ + /* + * term.esc = 0; + * strhandle(); + */ + if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2) + return; + strescseq.siz *= 2; + strescseq.buf = xrealloc(strescseq.buf, strescseq.siz); + } + + memmove(&strescseq.buf[strescseq.len], c, len); + strescseq.len += len; + return; + } + +check_control_code: + /* + * Actions of control codes must be performed as soon they arrive + * because they can be embedded inside a control sequence, and + * they must not cause conflicts with sequences. + */ + if (control) { + tcontrolcode(u); + /* + * control codes are not shown ever + */ + if (!term.esc) + term.lastc = 0; + return; + } else if (term.esc & ESC_START) { + if (term.esc & ESC_CSI) { + csiescseq.buf[csiescseq.len++] = u; + if (BETWEEN(u, 0x40, 0x7E) + || csiescseq.len >= \ + sizeof(csiescseq.buf)-1) { + term.esc = 0; + csiparse(); + csihandle(); + } + return; + } else if (term.esc & ESC_UTF8) { + tdefutf8(u); + } else if (term.esc & ESC_ALTCHARSET) { + tdeftran(u); + } else if (term.esc & ESC_TEST) { + tdectest(u); + } else { + if (!eschandle(u)) + return; + /* sequence already finished */ + } + term.esc = 0; + /* + * All characters which form part of a sequence are not + * printed + */ + return; + } + if (selected(term.c.x, term.c.y)) + selclear(); + + gp = &term.line[term.c.y][term.c.x]; + if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + gp->mode |= ATTR_WRAP; + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) + memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); + + if (term.c.x+width > term.col) { + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + tsetchar(u, &term.c.attr, term.c.x, term.c.y); + term.lastc = u; + + if (width == 2) { + gp->mode |= ATTR_WIDE; + if (term.c.x+1 < term.col) { + if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) { + gp[2].u = ' '; + gp[2].mode &= ~ATTR_WDUMMY; + } + gp[1].u = '\0'; + gp[1].mode = ATTR_WDUMMY; + } + } + if (term.c.x+width < term.col) { + tmoveto(term.c.x+width, term.c.y); + } else { + term.c.state |= CURSOR_WRAPNEXT; + } +} + +int +twrite(const char *buf, int buflen, int show_ctrl) +{ + int charsize; + Rune u; + int n; + + int su0 = su; + twrite_aborted = 0; + + 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 (su0 && !su) { + twrite_aborted = 1; + break; // ESU - allow rendering before a new BSU + } + if (show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; + tputc('^'); + tputc('['); + } else if (u != '\n' && u != '\r' && u != '\t') { + u ^= 0x40; + tputc('^'); + } + } + tputc(u); + } + return n; +} + +void +tresize(int col, int row) +{ + int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; + TCursor c; + + if (col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to + * memmove because we're freeing the earlier lines + */ + for (i = 0; i <= term.c.y - row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + /* ensure that both src and dst are not NULL */ + if (i > 0) { + memmove(term.line, term.line + i, row * sizeof(Line)); + memmove(term.alt, term.alt + i, row * sizeof(Line)); + } + for (i += row; i < term.row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + for (i = 0; i < HISTSIZE; i++) { + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); + for (j = mincol; j < col; j++) { + term.hist[i][j] = term.c.attr; + term.hist[i][j].u = ' '; + } + } + + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); + } + + /* allocate any new rows */ + for (/* i = minrow */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + term.alt[i] = xmalloc(col * sizeof(Glyph)); + } + if (col > term.col) { + bp = term.tabs + term.col; + + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + while (--bp > term.tabs && !*bp) + /* nothing */ ; + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } + /* update terminal size */ + term.col = col; + term.row = row; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); + /* Clearing both screens (it makes dirty all lines) */ + c = term.c; + for (i = 0; i < 2; i++) { + if (mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if (0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + tcursor(CURSOR_LOAD); + } + term.c = c; +} + +void +resettitle(void) +{ + xsettitle(NULL); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y; + + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(TLINE(y), x1, y, x2); + } +} + +void +draw(void) +{ + int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; + + if (!xstartdraw()) + return; + + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); + if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) + term.ocx--; + if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); + if (term.scr == 0) + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); + if (ocx != term.ocx || ocy != term.ocy) + xximspot(term.ocx, term.ocy); +} + +void +redraw(void) +{ + tfulldirt(); + draw(); +} diff --git a/based-simple-term/st.h b/based-simple-term/st.h new file mode 100755 index 0000000..3c8d2d9 --- /dev/null +++ b/based-simple-term/st.h @@ -0,0 +1,135 @@ +/* See LICENSE for license details. */ + +#include +#include + +/* macros */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a) / sizeof(a)[0]) +#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) +#define DEFAULT(a, b) (a) = (a) ? (a) : (b) +#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) +#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ + (a).bg != (b).bg) +#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ + (t1.tv_nsec-t2.tv_nsec)/1E6) +#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) + +#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) +#define IS_TRUECOL(x) (1 << 24 & (x)) + +enum glyph_attribute { + ATTR_NULL = 0, + ATTR_BOLD = 1 << 0, + ATTR_FAINT = 1 << 1, + ATTR_ITALIC = 1 << 2, + ATTR_UNDERLINE = 1 << 3, + ATTR_BLINK = 1 << 4, + ATTR_REVERSE = 1 << 5, + ATTR_INVISIBLE = 1 << 6, + ATTR_STRUCK = 1 << 7, + ATTR_WRAP = 1 << 8, + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, + ATTR_DIRTYUNDERLINE = 1 << 15, +}; + +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 */ + int ustyle; /* underline style */ + int ucolor[3]; /* underline color */ +} Glyph; + +typedef Glyph *Line; + +typedef union { + int i; + uint ui; + float f; + const void *v; + const char *s; +} Arg; + +void die(const char *, ...); +void redraw(void); +void draw(void); + +void kscrolldown(const Arg *); +void kscrollup(const Arg *); +void printscreen(const Arg *); +void printsel(const Arg *); +void sendbreak(const Arg *); +void toggleprinter(const Arg *); + +int tattrset(int); +int tisaltscr(void); +void tnew(int, int); +void tresize(int, int); +void tsetdirtattr(int); +void ttyhangup(void); +int ttynew(const char *, char *, const char *, char **); +size_t ttyread(void); +void ttyresize(int, int); +void ttywrite(const char *, size_t, int); + +void resettitle(void); + +void selclear(void); +void selinit(void); +void selstart(int, int, int); +void selextend(int, int, int, int); +int selected(int, int); +char *getsel(void); + +size_t utf8encode(Rune, char *); + +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(const char *); + +int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b); + +/* config.h globals */ +extern char *utmp; +extern char *scroll; +extern char *stty_args; +extern char *vtiden; +extern wchar_t *worddelimiters; +extern int allowaltscreen; +extern int allowwindowops; +extern char *termname; +extern unsigned int tabspaces; +extern unsigned int defaultfg; +extern unsigned int defaultbg; +extern unsigned int defaultcs; +extern float alpha; diff --git a/based-simple-term/st.info b/based-simple-term/st.info new file mode 100755 index 0000000..7df13e8 --- /dev/null +++ b/based-simple-term/st.info @@ -0,0 +1,241 @@ +st-mono| simpleterm monocolor, + Su, + 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, + Sync=\EP=%p1%ds\E\\, + +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/based-simple-term/st.o b/based-simple-term/st.o new file mode 100644 index 0000000000000000000000000000000000000000..b7f08bfb1281f9124af4bf5de0e79094885b11a8 GIT binary patch literal 102048 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

Q%Sey>KR_J$ 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 literal 0 HcmV?d00001 diff --git a/based-simple-term/win.h b/based-simple-term/win.h new file mode 100755 index 0000000..e6e4369 --- /dev/null +++ b/based-simple-term/win.h @@ -0,0 +1,40 @@ +/* See LICENSE for license details. */ + +enum win_mode { + MODE_VISIBLE = 1 << 0, + MODE_FOCUSED = 1 << 1, + MODE_APPKEYPAD = 1 << 2, + MODE_MOUSEBTN = 1 << 3, + MODE_MOUSEMOTION = 1 << 4, + MODE_REVERSE = 1 << 5, + MODE_KBDLOCK = 1 << 6, + MODE_HIDE = 1 << 7, + MODE_APPCURSOR = 1 << 8, + MODE_MOUSESGR = 1 << 9, + MODE_8BIT = 1 << 10, + MODE_BLINK = 1 << 11, + MODE_FBLINK = 1 << 12, + MODE_FOCUS = 1 << 13, + MODE_MOUSEX10 = 1 << 14, + MODE_MOUSEMANY = 1 << 15, + MODE_BRCKTPASTE = 1 << 16, + MODE_NUMLOCK = 1 << 17, + MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ + |MODE_MOUSEMANY, +}; + +void xbell(void); +void xclipcopy(void); +void xdrawcursor(int, int, Glyph, int, int, Glyph); +void xdrawline(Line, int, int, int); +void xfinishdraw(void); +void xloadcols(void); +int xsetcolorname(int, const char *); +void xseticontitle(char *); +void xsettitle(char *); +int xsetcursor(int); +void xsetmode(int, unsigned int); +void xsetpointermotion(int); +void xsetsel(char *); +int xstartdraw(void); +void xximspot(int, int); diff --git a/based-simple-term/x.c b/based-simple-term/x.c new file mode 100755 index 0000000..73ae2c3 --- /dev/null +++ b/based-simple-term/x.c @@ -0,0 +1,2553 @@ +/* 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; + int altscrn; /* 0: don't care, -1: not alt screen, 1: alt screen */ +} 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; + +/* Undercurl slope types */ +enum undercurl_slope_type { + UNDERCURL_SLOPE_ASCENDING = 0, + UNDERCURL_SLOPE_TOP_CAP = 1, + UNDERCURL_SLOPE_DESCENDING = 2, + UNDERCURL_SLOPE_BOTTOM_CAP = 3 +}; + +/* X modifiers */ +#define XK_ANY_MOD UINT_MAX +#define XK_NO_MOD 0 +#define XK_SWITCH_MOD (1<<13|1<<14) + +/* function definitions used in config.h */ +static void clipcopy(const Arg *); +static void clippaste(const Arg *); +static void numlock(const Arg *); +static void selpaste(const Arg *); +static void zoom(const Arg *); +static void zoomabs(const Arg *); +static void zoomreset(const Arg *); +static void ttysend(const Arg *); + +/* config.h for applying patches and the configuration. */ +#include "config.h" + +/* XEMBED messages */ +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 + +/* macros */ +#define IS_SET(flag) ((win.mode & (flag)) != 0) +#define TRUERED(x) (((x) & 0xff0000) >> 8) +#define TRUEGREEN(x) (((x) & 0xff00)) +#define TRUEBLUE(x) (((x) & 0xff) << 8) + +typedef XftDraw *Draw; +typedef XftColor Color; +typedef XftGlyphFontSpec GlyphFontSpec; + +/* Purely graphic info */ +typedef struct { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ + int ch; /* char height */ + int cw; /* char width */ + int mode; /* window state/mode flags */ + int cursor; /* cursor style */ +} TermWindow; + +typedef struct { + Display *dpy; + Colormap cmap; + Window win; + Drawable buf; + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ + Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; + struct { + XIM xim; + XIC xic; + XPoint spot; + XVaNestedList spotlist; + } ime; + Draw draw; + Visual *vis; + XSetWindowAttributes attrs; + int scr; + int isfixed; /* is fixed geometry? */ + int depth; /* bit depth */ + int l, t; /* left and top offset */ + int gm; /* geometry mask */ +} XWindow; + +typedef struct { + Atom xtarget; + char *primary, *clipboard; + struct timespec tclick1; + struct timespec tclick2; +} XSelection; + +/* Font structure */ +#define Font Font_ +typedef struct { + int height; + int width; + int ascent; + int descent; + int badslant; + int badweight; + short lbearing; + short rbearing; + XftFont *match; + FcFontSet *set; + FcPattern *pattern; +} Font; + +/* Drawing Context */ +typedef struct { + Color *col; + size_t collen; + Font font, bfont, ifont, ibfont; + GC gc; +} DC; + +static inline ushort sixd_to_16bit(int); +static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); +static void xdrawglyph(Glyph, int, int); +static void xclear(int, int, int, int); +static int xgeommasktogravity(int); +static int ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, XPointer, XPointer); +static int xicdestroy(XIC, XPointer, XPointer); +static void xinit(int, int); +static void cresize(int, int); +static void xresize(int, int); +static void xhints(void); +static int xloadcolor(int, const char *, Color *); +static int xloadfont(Font *, FcPattern *); +static void xloadfonts(const char *, double); +static void xunloadfont(Font *); +static void xunloadfonts(void); +static void xsetenv(void); +static void xseturgency(int); +static int evcol(XEvent *); +static int evrow(XEvent *); + +static void expose(XEvent *); +static void visibility(XEvent *); +static void unmap(XEvent *); +static void kpress(XEvent *); +static void cmessage(XEvent *); +static void resize(XEvent *); +static void focus(XEvent *); +static uint buttonmask(uint); +static int mouseaction(XEvent *, uint); +static void brelease(XEvent *); +static void bpress(XEvent *); +static void bmotion(XEvent *); +static void propnotify(XEvent *); +static void selnotify(XEvent *); +static void selclear_(XEvent *); +static void selrequest(XEvent *); +static void setsel(char *, Time); +static void mousesel(XEvent *, int); +static void mousereport(XEvent *); +static char *kmap(KeySym, uint); +static int match(uint, uint); + +static void run(void); +static void usage(void); + +static void (*handler[LASTEvent])(XEvent *) = { + [KeyPress] = kpress, + [ClientMessage] = cmessage, + [ConfigureNotify] = resize, + [VisibilityNotify] = visibility, + [UnmapNotify] = unmap, + [Expose] = expose, + [FocusIn] = focus, + [FocusOut] = focus, + [MotionNotify] = bmotion, + [ButtonPress] = bpress, + [ButtonRelease] = brelease, +/* + * Uncomment if you want the selection to disappear when you select something + * different in another window. + */ +/* [SelectionClear] = selclear_, */ + [SelectionNotify] = selnotify, +/* + * PropertyNotify is only turned on when there is some INCR transfer happening + * for the selection retrieval. + */ + [PropertyNotify] = propnotify, + [SelectionRequest] = selrequest, +}; + +/* Globals */ +static DC dc; +static XWindow xw; +static XSelection xsel; +static TermWindow win; + +/* Font Ring Cache */ +enum { + FRC_NORMAL, + FRC_ITALIC, + FRC_BOLD, + FRC_ITALICBOLD +}; + +typedef struct { + XftFont *font; + int flags; + Rune unicodep; +} Fontcache; + +/* Fontcache is an array now. A new font will be appended to the array. */ +static Fontcache *frc = NULL; +static int frclen = 0; +static int frccap = 0; +static char *usedfont = NULL; +static double usedfontsize = 0; +static double defaultfontsize = 0; + +static char *opt_alpha = NULL; +static char *opt_class = NULL; +static char **opt_cmd = NULL; +static char *opt_embed = NULL; +static char *opt_font = NULL; +static char *opt_io = NULL; +static char *opt_line = NULL; +static char *opt_name = NULL; +static char *opt_title = NULL; + +static 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); + 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 && + (!ms->altscrn || (ms->altscrn == (tisaltscr() ? 1 : -1))) && + (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(); +} + +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); +} + +void +xunloadfont(Font *f) +{ + XftFontClose(xw.dpy, f->match); + FcPatternDestroy(f->pattern); + if (f->set) + FcFontSetDestroy(f->set); +} + +void +xunloadfonts(void) +{ + /* Free the loaded fonts in the font cache. */ + while (frclen > 0) + XftFontClose(xw.dpy, frc[--frclen].font); + + xunloadfont(&dc.font); + xunloadfont(&dc.bfont); + xunloadfont(&dc.ifont); + xunloadfont(&dc.ibfont); +} + +int +ximopen(Display *dpy) +{ + XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; + XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; + + xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); + if (xw.ime.xim == NULL) + return 0; + + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) + fprintf(stderr, "XSetIMValues: " + "Could not set XNDestroyCallback.\n"); + + xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, + NULL); + + if (xw.ime.xic == NULL) { + xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, + XNDestroyCallback, &icdestroy, + NULL); + } + if (xw.ime.xic == NULL) + fprintf(stderr, "XCreateIC: Could not create input context.\n"); + + return 1; +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + if (ximopen(dpy)) + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.ime.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + XFree(xw.ime.spotlist); +} + +int +xicdestroy(XIC xim, XPointer client, XPointer call) +{ + xw.ime.xic = NULL; + return 1; +} + +void +xinit(int cols, int rows) +{ + XGCValues gcvalues; + Cursor cursor; + Window parent; + pid_t thispid = getpid(); + XColor xmousefg, xmousebg; + XWindowAttributes attr; + XVisualInfo vis; + + if (!(xw.dpy = XOpenDisplay(NULL))) + die("can't open display\n"); + xw.scr = XDefaultScreen(xw.dpy); + + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { + parent = XRootWindow(xw.dpy, xw.scr); + xw.depth = 32; + } else { + XGetWindowAttributes(xw.dpy, parent, &attr); + xw.depth = attr.depth; + } + + XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); + xw.vis = vis.visual; + + /* font */ + if (!FcInit()) + die("could not init fontconfig.\n"); + + usedfont = (opt_font == NULL)? font : opt_font; + xloadfonts(usedfont, 0); + + /* colors */ + xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); + xloadcols(); + + /* adjust fixed window geometry */ + win.w = 2 * borderpx + cols * win.cw; + win.h = 2 * borderpx + rows * win.ch; + if (xw.gm & XNegative) + xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; + if (xw.gm & YNegative) + xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; + + /* Events */ + xw.attrs.background_pixel = dc.col[defaultbg].pixel; + xw.attrs.border_pixel = dc.col[defaultbg].pixel; + xw.attrs.bit_gravity = NorthWestGravity; + xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask + | ExposureMask | VisibilityChangeMask | StructureNotifyMask + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + xw.attrs.colormap = xw.cmap; + + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, + win.w, win.h, 0, xw.depth, InputOutput, + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &xw.attrs); + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); + dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + + /* font spec buffer */ + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); + + /* Xft rendering context */ + xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); + + /* input methods */ + if (!ximopen(xw.dpy)) { + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + } + + /* white cursor, black outline */ + cursor = XCreateFontCursor(xw.dpy, mouseshape); + XDefineCursor(xw.dpy, xw.win, cursor); + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { + xmousefg.red = 0xffff; + xmousefg.green = 0xffff; + xmousefg.blue = 0xffff; + } + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { + xmousebg.red = 0x0000; + xmousebg.green = 0x0000; + xmousebg.blue = 0x0000; + } + + XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); + + xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); + xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); + xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); + xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); + XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); + + xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); + XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, + PropModeReplace, (uchar *)&thispid, 1); + + win.mode = MODE_NUMLOCK; + resettitle(); + xhints(); + XMapWindow(xw.dpy, xw.win); + XSync(xw.dpy, False); + + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); + xsel.primary = NULL; + xsel.clipboard = NULL; + xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); + if (xsel.xtarget == None) + xsel.xtarget = XA_STRING; +} + +int +xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) +{ + float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; + ushort mode, prevmode = USHRT_MAX; + Font *font = &dc.font; + int frcflags = FRC_NORMAL; + float runewidth = win.cw; + Rune rune; + FT_UInt glyphidx; + FcResult fcres; + FcPattern *fcpattern, *fontpattern; + FcFontSet *fcsets[] = { NULL }; + FcCharSet *fccharset; + int i, f, numspecs = 0; + + for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { + /* Fetch rune and mode for current glyph. */ + rune = glyphs[i].u; + mode = glyphs[i].mode; + + /* Skip dummy wide-character spacing. */ + if (mode == ATTR_WDUMMY) + continue; + + /* Determine font for glyph if different from previous glyph. */ + if (prevmode != mode) { + prevmode = mode; + font = &dc.font; + frcflags = FRC_NORMAL; + runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { + font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } else if (mode & ATTR_ITALIC) { + font = &dc.ifont; + frcflags = FRC_ITALIC; + } else if (mode & ATTR_BOLD) { + font = &dc.bfont; + frcflags = FRC_BOLD; + } + yp = winy + font->ascent; + } + + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + if (glyphidx) { + specs[numspecs].font = font->match; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + continue; + } + + /* Fallback on font cache, search the font cache for match. */ + for (f = 0; f < frclen; f++) { + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); + /* Everything correct. */ + if (glyphidx && frc[f].flags == frcflags) + break; + /* We got a default font for a not found glyph. */ + if (!glyphidx && frc[f].flags == frcflags + && frc[f].unicodep == rune) { + break; + } + } + + /* Nothing was found. Use fontconfig to find matching font. */ + if (f >= frclen) { + if (!font->set) + font->set = FcFontSort(0, font->pattern, + 1, 0, &fcres); + fcsets[0] = font->set; + + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + * + * Xft and fontconfig are design failures. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, + fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + + FcConfigSubstitute(0, fcpattern, + FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + fontpattern = FcFontSetMatch(0, fcsets, 1, + fcpattern, &fcres); + + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } + + frc[frclen].font = XftFontOpenPattern(xw.dpy, + fontpattern); + if (!frc[frclen].font) + die("XftFontOpenPattern failed seeking fallback font: %s\n", + strerror(errno)); + frc[frclen].flags = frcflags; + frc[frclen].unicodep = rune; + + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); + + f = frclen; + frclen++; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + } + + specs[numspecs].font = frc[f].font; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + } + + return numspecs; +} + +static int isSlopeRising (int x, int iPoint, int waveWidth) +{ + // . . . . + // / \ / \ / \ / \ + // / \ / \ / \ / \ + // . . . . . + + // Find absolute `x` of point + x += iPoint * (waveWidth/2); + + // Find index of absolute wave + int absSlope = x / ((float)waveWidth/2); + + return (absSlope % 2); +} + +static int getSlope (int x, int iPoint, int waveWidth) +{ + // Sizes: Caps are half width of slopes + // 1_2 1_2 1_2 1_2 + // / \ / \ / \ / \ + // / \ / \ / \ / \ + // 0 3_0 3_0 3_0 3_ + // <2-> <1> <---6----> + + // Find type of first point + int firstType; + x -= (x / waveWidth) * waveWidth; + if (x < (waveWidth * (2.f/6.f))) + firstType = UNDERCURL_SLOPE_ASCENDING; + else if (x < (waveWidth * (3.f/6.f))) + firstType = UNDERCURL_SLOPE_TOP_CAP; + else if (x < (waveWidth * (5.f/6.f))) + firstType = UNDERCURL_SLOPE_DESCENDING; + else + firstType = UNDERCURL_SLOPE_BOTTOM_CAP; + + // Find type of given point + int pointType = (iPoint % 4); + pointType += firstType; + pointType %= 4; + + return pointType; +} + +void +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) +{ + int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); + int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, + width = charlen * win.cw; + Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; + XRenderColor colfg, colbg; + XRectangle r; + + /* Fallback on color display for attributes not supported by the font */ + if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { + if (dc.ibfont.badslant || dc.ibfont.badweight) + base.fg = defaultattr; + } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || + (base.mode & ATTR_BOLD && dc.bfont.badweight)) { + base.fg = defaultattr; + } + + if (IS_TRUECOL(base.fg)) { + colfg.alpha = 0xffff; + colfg.red = TRUERED(base.fg); + colfg.green = TRUEGREEN(base.fg); + colfg.blue = TRUEBLUE(base.fg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); + fg = &truefg; + } else { + fg = &dc.col[base.fg]; + } + + if (IS_TRUECOL(base.bg)) { + colbg.alpha = 0xffff; + colbg.green = TRUEGREEN(base.bg); + colbg.red = TRUERED(base.bg); + colbg.blue = TRUEBLUE(base.bg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); + bg = &truebg; + } else { + bg = &dc.col[base.bg]; + } + + /* Change basic system colors [0-7] to bright system colors [8-15] */ + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) + fg = &dc.col[base.fg + 8]; + + if (IS_SET(MODE_REVERSE)) { + if (fg == &dc.col[defaultfg]) { + fg = &dc.col[defaultbg]; + } else { + colfg.red = ~fg->color.red; + colfg.green = ~fg->color.green; + colfg.blue = ~fg->color.blue; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, + &revfg); + fg = &revfg; + } + + if (bg == &dc.col[defaultbg]) { + bg = &dc.col[defaultfg]; + } else { + colbg.red = ~bg->color.red; + colbg.green = ~bg->color.green; + colbg.blue = ~bg->color.blue; + colbg.alpha = bg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, + &revbg); + bg = &revbg; + } + } + + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { + colfg.red = fg->color.red / 2; + colfg.green = fg->color.green / 2; + colfg.blue = fg->color.blue / 2; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); + fg = &revfg; + } + + if (base.mode & ATTR_REVERSE) { + temp = fg; + fg = bg; + bg = temp; + } + + if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) + fg = bg; + + if (base.mode & ATTR_INVISIBLE) + fg = bg; + + /* Intelligent cleaning up of the borders. */ + if (x == 0) { + xclear(0, (y == 0)? 0 : winy, borderpx, + winy + win.ch + + ((winy + win.ch >= borderpx + win.th)? win.h : 0)); + } + if (winx + width >= borderpx + win.tw) { + xclear(winx + width, (y == 0)? 0 : winy, win.w, + ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); + } + if (y == 0) + xclear(winx, 0, winx + width, borderpx); + if (winy + win.ch >= borderpx + win.th) + xclear(winx, winy + win.ch, winx + width, win.h); + + /* Clean up the region we want to draw to. */ + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + + /* Set the clip region because Xft is sometimes dirty. */ + r.x = 0; + r.y = 0; + r.height = win.ch; + r.width = width; + XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + + /* Render underline and strikethrough. */ + if (base.mode & ATTR_UNDERLINE) { + // Underline Color + const int widthThreshold = 28; // +1 width every widthThreshold px of font + int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width + int linecolor; + if ((base.ucolor[0] >= 0) && + !(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) && + !(base.mode & ATTR_INVISIBLE) + ) { + // Special color for underline + // Index + if (base.ucolor[1] < 0) { + linecolor = dc.col[base.ucolor[0]].pixel; + } + // RGB + else { + XColor lcolor; + lcolor.red = base.ucolor[0] * 257; + lcolor.green = base.ucolor[1] * 257; + lcolor.blue = base.ucolor[2] * 257; + lcolor.flags = DoRed | DoGreen | DoBlue; + XAllocColor(xw.dpy, xw.cmap, &lcolor); + linecolor = lcolor.pixel; + } + } else { + // Foreground color for underline + linecolor = fg->pixel; + } + + XGCValues ugcv = { + .foreground = linecolor, + .line_width = wlw, + .line_style = LineSolid, + .cap_style = CapNotLast + }; + + GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw), + GCForeground | GCLineWidth | GCLineStyle | GCCapStyle, + &ugcv); + + // Underline Style + if (base.ustyle != 3) { + //XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1); + XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx, + winy + dc.font.ascent + 1, width, wlw); + } else if (base.ustyle == 3) { + int ww = win.cw;//width; + int wh = dc.font.descent - wlw/2 - 1;//r.height/7; + int wx = winx; + int wy = winy + win.ch - dc.font.descent; + +#if UNDERCURL_STYLE == UNDERCURL_CURLY + // Draw waves + int narcs = charlen * 2 + 1; + XArc *arcs = xmalloc(sizeof(XArc) * narcs); + + int i = 0; + for (i = 0; i < charlen-1; i++) { + arcs[i*2] = (XArc) { + .x = wx + win.cw * i + ww / 4, + .y = wy, + .width = win.cw / 2, + .height = wh, + .angle1 = 0, + .angle2 = 180 * 64 + }; + arcs[i*2+1] = (XArc) { + .x = wx + win.cw * i + ww * 0.75, + .y = wy, + .width = win.cw/2, + .height = wh, + .angle1 = 180 * 64, + .angle2 = 180 * 64 + }; + } + // Last wave + arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh, + 0, 180 * 64 }; + // Last wave tail + arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.), + wh, 180 * 64, 90 * 64}; + // First wave tail + i++; + arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64, + 90 * 64 }; + + XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs); + + free(arcs); +#elif UNDERCURL_STYLE == UNDERCURL_SPIKY + // Make the underline corridor larger + /* + wy -= wh; + */ + wh *= 2; + + // Set the angle of the slope to 45° + ww = wh; + + // Position of wave is independent of word, it's absolute + wx = (wx / (ww/2)) * (ww/2); + + int marginStart = winx - wx; + + // Calculate number of points with floating precision + float n = width; // Width of word in pixels + n = (n / ww) * 2; // Number of slopes (/ or \) + n += 2; // Add two last points + int npoints = n; // Convert to int + + // Total length of underline + float waveLength = 0; + + if (npoints >= 3) { + // We add an aditional slot in case we use a bonus point + XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); + + // First point (Starts with the word bounds) + points[0] = (XPoint) { + .x = wx + marginStart, + .y = (isSlopeRising(wx, 0, ww)) + ? (wy - marginStart + ww/2.f) + : (wy + marginStart) + }; + + // Second point (Goes back to the absolute point coordinates) + points[1] = (XPoint) { + .x = (ww/2.f) - marginStart, + .y = (isSlopeRising(wx, 1, ww)) + ? (ww/2.f - marginStart) + : (-ww/2.f + marginStart) + }; + waveLength += (ww/2.f) - marginStart; + + // The rest of the points + for (int i = 2; i < npoints-1; i++) { + points[i] = (XPoint) { + .x = ww/2, + .y = (isSlopeRising(wx, i, ww)) + ? wh/2 + : -wh/2 + }; + waveLength += ww/2; + } + + // Last point + points[npoints-1] = (XPoint) { + .x = ww/2, + .y = (isSlopeRising(wx, npoints-1, ww)) + ? wh/2 + : -wh/2 + }; + waveLength += ww/2; + + // End + if (waveLength < width) { // Add a bonus point? + int marginEnd = width - waveLength; + points[npoints] = (XPoint) { + .x = marginEnd, + .y = (isSlopeRising(wx, npoints, ww)) + ? (marginEnd) + : (-marginEnd) + }; + + npoints++; + } else if (waveLength > width) { // Is last point too far? + int marginEnd = waveLength - width; + points[npoints-1].x -= marginEnd; + if (isSlopeRising(wx, npoints-1, ww)) + points[npoints-1].y -= (marginEnd); + else + points[npoints-1].y += (marginEnd); + } + + // Draw the lines + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, + CoordModePrevious); + + // Draw a second underline with an offset of 1 pixel + if ( ((win.ch / (widthThreshold/2)) % 2)) { + points[0].x++; + + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, + npoints, CoordModePrevious); + } + + // Free resources + free(points); + } +#else // UNDERCURL_CAPPED + // Cap is half of wave width + float capRatio = 0.5f; + + // Make the underline corridor larger + wh *= 2; + + // Set the angle of the slope to 45° + ww = wh; + ww *= 1 + capRatio; // Add a bit of width for the cap + + // Position of wave is independent of word, it's absolute + wx = (wx / ww) * ww; + + float marginStart; + switch(getSlope(winx, 0, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + marginStart = winx - wx; + break; + case UNDERCURL_SLOPE_TOP_CAP: + marginStart = winx - (wx + (ww * (2.f/6.f))); + break; + case UNDERCURL_SLOPE_DESCENDING: + marginStart = winx - (wx + (ww * (3.f/6.f))); + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + marginStart = winx - (wx + (ww * (5.f/6.f))); + break; + } + + // Calculate number of points with floating precision + float n = width; // Width of word in pixels + // ._. + n = (n / ww) * 4; // Number of points (./ \.) + n += 2; // Add two last points + int npoints = n; // Convert to int + + // Position of the pen to draw the lines + float penX = 0; + float penY = 0; + + if (npoints >= 3) { + XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); + + // First point (Starts with the word bounds) + penX = winx; + switch (getSlope(winx, 0, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + penY = wy + wh/2.f - marginStart; + break; + case UNDERCURL_SLOPE_TOP_CAP: + penY = wy; + break; + case UNDERCURL_SLOPE_DESCENDING: + penY = wy + marginStart; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + penY = wy + wh/2.f; + break; + } + points[0].x = penX; + points[0].y = penY; + + // Second point (Goes back to the absolute point coordinates) + switch (getSlope(winx, 1, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + penX += ww * (1.f/6.f) - marginStart; + penY += 0; + break; + case UNDERCURL_SLOPE_TOP_CAP: + penX += ww * (2.f/6.f) - marginStart; + penY += -wh/2.f + marginStart; + break; + case UNDERCURL_SLOPE_DESCENDING: + penX += ww * (1.f/6.f) - marginStart; + penY += 0; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + penX += ww * (2.f/6.f) - marginStart; + penY += -marginStart + wh/2.f; + break; + } + points[1].x = penX; + points[1].y = penY; + + // The rest of the points + for (int i = 2; i < npoints; i++) { + switch (getSlope(winx, i, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + case UNDERCURL_SLOPE_DESCENDING: + penX += ww * (1.f/6.f); + penY += 0; + break; + case UNDERCURL_SLOPE_TOP_CAP: + penX += ww * (2.f/6.f); + penY += -wh / 2.f; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + penX += ww * (2.f/6.f); + penY += wh / 2.f; + break; + } + points[i].x = penX; + points[i].y = penY; + } + + // End + float waveLength = penX - winx; + if (waveLength < width) { // Add a bonus point? + int marginEnd = width - waveLength; + penX += marginEnd; + switch(getSlope(winx, npoints, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + case UNDERCURL_SLOPE_DESCENDING: + //penY += 0; + break; + case UNDERCURL_SLOPE_TOP_CAP: + penY += -marginEnd; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + penY += marginEnd; + break; + } + + points[npoints].x = penX; + points[npoints].y = penY; + + npoints++; + } else if (waveLength > width) { // Is last point too far? + int marginEnd = waveLength - width; + points[npoints-1].x -= marginEnd; + switch(getSlope(winx, npoints-1, ww)) { + case UNDERCURL_SLOPE_TOP_CAP: + points[npoints-1].y += marginEnd; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + points[npoints-1].y -= marginEnd; + break; + default: + break; + } + } + + // Draw the lines + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, + CoordModeOrigin); + + // Draw a second underline with an offset of 1 pixel + if ( ((win.ch / (widthThreshold/2)) % 2)) { + for (int i = 0; i < npoints; i++) + points[i].x++; + + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, + npoints, CoordModeOrigin); + } + + // Free resources + free(points); + } +#endif + } + + XFreeGC(xw.dpy, ugc); + } + + if (base.mode & ATTR_STRUCK) { + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, + width, 1); + } + + /* Reset clip to none. */ + XftDrawSetClip(xw.draw, 0); +} + +void +xdrawglyph(Glyph g, int x, int y) +{ + int numspecs; + XftGlyphFontSpec spec; + + numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); + xdrawglyphfontspecs(&spec, g, numspecs, x, y); +} + +void +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) +{ + Color drawcol; + XRenderColor colbg; + + /* 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 { + /** this is the main part of the dynamic cursor color patch */ + g.bg = g.fg; + g.fg = defaultbg; + } + + /** + * and this is the second part of the dynamic cursor color patch. + * it handles the `drawcol` variable + */ + if (IS_TRUECOL(g.bg)) { + colbg.alpha = 0xffff; + colbg.red = TRUERED(g.bg); + colbg.green = TRUEGREEN(g.bg); + colbg.blue = TRUEBLUE(g.bg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &drawcol); + } else { + 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) +{ + if (IS_SET(MODE_VISIBLE)) + XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0); + return IS_SET(MODE_VISIBLE); +} + +void +xdrawline(Line line, int x1, int y1, int x2) +{ + int i, x, ox, numspecs; + Glyph base, new; + XftGlyphFontSpec *specs = xw.specbuf; + + numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + if (new.mode == ATTR_WDUMMY) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1); +} + +void +xfinishdraw(void) +{ + XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, + win.h, 0, 0); + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(MODE_REVERSE)? + defaultfg : defaultbg].pixel); +} + +void +xximspot(int x, int y) +{ + if (xw.ime.xic == NULL) + return; + + xw.ime.spot.x = borderpx + x * win.cw; + xw.ime.spot.y = borderpx + (y + 1) * win.ch; + + XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); +} + +void +expose(XEvent *ev) +{ + redraw(); +} + +void +visibility(XEvent *ev) +{ + XVisibilityEvent *e = &ev->xvisibility; + + MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); +} + +void +unmap(XEvent *ev) +{ + win.mode &= ~MODE_VISIBLE; +} + +void +xsetpointermotion(int set) +{ + MODBIT(xw.attrs.event_mask, set, PointerMotionMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); +} + +void +xsetmode(int set, unsigned int flags) +{ + int mode = win.mode; + MODBIT(win.mode, set, flags); + if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) + redraw(); +} + +int +xsetcursor(int cursor) +{ + if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ + return 1; + win.cursor = cursor; + return 0; +} + +void +xseturgency(int add) +{ + XWMHints *h = XGetWMHints(xw.dpy, xw.win); + + MODBIT(h->flags, add, XUrgencyHint); + XSetWMHints(xw.dpy, xw.win, h); + XFree(h); +} + +void +xbell(void) +{ + if (!(IS_SET(MODE_FOCUSED))) + xseturgency(1); + if (bellvolume) + XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); +} + +void +focus(XEvent *ev) +{ + XFocusChangeEvent *e = &ev->xfocus; + + if (e->mode == NotifyGrab) + return; + + if (ev->type == FocusIn) { + if (xw.ime.xic) + XSetICFocus(xw.ime.xic); + win.mode |= MODE_FOCUSED; + xseturgency(0); + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[I", 3, 0); + } else { + if (xw.ime.xic) + XUnsetICFocus(xw.ime.xic); + win.mode &= ~MODE_FOCUSED; + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[O", 3, 0); + } +} + +int +match(uint mask, uint state) +{ + return mask == XK_ANY_MOD || mask == (state & ~ignoremod); +} + +char* +kmap(KeySym k, uint state) +{ + Key *kp; + int i; + + /* Check for mapped keys out of X11 function keys. */ + for (i = 0; i < LEN(mappedkeys); i++) { + if (mappedkeys[i] == k) + break; + } + if (i == LEN(mappedkeys)) { + if ((k & 0xFFFF) < 0xFD00) + return NULL; + } + + for (kp = key; kp < key + LEN(key); kp++) { + if (kp->k != k) + continue; + + if (!match(kp->mask, state)) + continue; + + if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) + continue; + if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) + continue; + + if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) + continue; + + return kp->s; + } + + return NULL; +} + +void +kpress(XEvent *ev) +{ + XKeyEvent *e = &ev->xkey; + KeySym ksym; + char buf[64], *customkey; + int len; + Rune c; + Status status; + Shortcut *bp; + + if (IS_SET(MODE_KBDLOCK)) + return; + + if (xw.ime.xic) + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); + else + len = XLookupString(e, buf, sizeof buf, &ksym, NULL); + /* 1. shortcuts */ + for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { + if (ksym == bp->keysym && match(bp->mod, e->state)) { + bp->func(&(bp->arg)); + return; + } + } + + /* 2. custom keys from config.h */ + if ((customkey = kmap(ksym, e->state))) { + ttywrite(customkey, strlen(customkey), 1); + return; + } + + /* 3. composed string from input method */ + if (len == 0) + return; + if (len == 1 && e->state & Mod1Mask) { + if (IS_SET(MODE_8BIT)) { + if (*buf < 0177) { + c = *buf | 0x80; + len = utf8encode(c, buf); + } + } else { + buf[1] = buf[0]; + buf[0] = '\033'; + len = 2; + } + } + ttywrite(buf, len, 1); +} + +void +cmessage(XEvent *e) +{ + /* + * See xembed specs + * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html + */ + if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { + if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { + win.mode |= MODE_FOCUSED; + xseturgency(0); + } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { + win.mode &= ~MODE_FOCUSED; + } + } else if (e->xclient.data.l[0] == xw.wmdeletewin) { + ttyhangup(); + exit(0); + } +} + +void +resize(XEvent *e) +{ + if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) + return; + + cresize(e->xconfigure.width, e->xconfigure.height); +} + +int tinsync(uint); +int ttyread_pending(); + +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) || ttyread_pending()) + 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); + + int ttyin = FD_ISSET(ttyfd, &rfd) || ttyread_pending(); + if (ttyin) + 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 (ttyin || 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 */ + } + + if (tinsync(su_timeout)) { + /* + * on synchronized-update draw-suspension: don't reset + * drawing so that we draw ASAP once we can (just after + * ESU). it won't be too soon because we already can + * draw now but we skip. we set timeout > 0 to draw on + * SU-timeout even without new content. + */ + timeout = minlatency; + continue; + } + + /* idle detected or maxlatency exhausted -> draw */ + timeout = -1; + if (blinktimeout && 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/based-simple-term/x.o b/based-simple-term/x.o new file mode 100644 index 0000000000000000000000000000000000000000..056f2830663e6f2c26a0a6e41360b2bb89b4b734 GIT binary patch literal 96144 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 XD}6_kIg0 /tmp/powerc +cat /sys/class/power_supply/hidpp_battery_*/status > /tmp/powerc +value=$( +© 2006-2008 Sander van Dijk +© 2006-2007 Michał Janeczek +© 2007 Kris Maglione +© 2009 Gottox +© 2009 Markus Schnalke +© 2009 Evan Gates +© 2010-2012 Connor Lane Smith +© 2014-2022 Hiltjo Posthuma +© 2015-2019 Quentin Rameau + +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/bdmenu-based/Makefile b/bdmenu-based/Makefile new file mode 100755 index 0000000..a03a95c --- /dev/null +++ b/bdmenu-based/Makefile @@ -0,0 +1,64 @@ +# dmenu - dynamic menu +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dmenu.c stest.c util.c +OBJ = $(SRC:.c=.o) + +all: options dmenu stest + +options: + @echo dmenu build options: + @echo "CFLAGS = $(CFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + @echo "CC = $(CC)" + +.c.o: + $(CC) -c $(CFLAGS) $< + +config.h: + cp config.def.h $@ + +$(OBJ): arg.h config.h config.mk drw.h + +dmenu: dmenu.o drw.o util.o + $(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS) + +stest: stest.o + $(CC) -o $@ stest.o $(LDFLAGS) + +clean: + rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz + +dist: clean + mkdir -p dmenu-$(VERSION) + cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\ + drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\ + dmenu-$(VERSION) + tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION) + gzip dmenu-$(VERSION).tar + rm -rf dmenu-$(VERSION) + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run + chmod 755 $(DESTDIR)$(PREFIX)/bin/stest + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 + sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1 + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\ + $(DESTDIR)$(PREFIX)/bin/dmenu_path\ + $(DESTDIR)$(PREFIX)/bin/dmenu_run\ + $(DESTDIR)$(PREFIX)/bin/stest\ + $(DESTDIR)$(MANPREFIX)/man1/dmenu.1\ + $(DESTDIR)$(MANPREFIX)/man1/stest.1 + +.PHONY: all options clean dist install uninstall diff --git a/bdmenu-based/README b/bdmenu-based/README new file mode 100755 index 0000000..a8fcdfe --- /dev/null +++ b/bdmenu-based/README @@ -0,0 +1,24 @@ +dmenu - dynamic menu +==================== +dmenu is an efficient dynamic menu for X. + + +Requirements +------------ +In order to build dmenu you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dmenu is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dmenu +(if necessary as root): + + make clean install + + +Running dmenu +------------- +See the man page for details. diff --git a/bdmenu-based/arg.h b/bdmenu-based/arg.h new file mode 100755 index 0000000..e94e02b --- /dev/null +++ b/bdmenu-based/arg.h @@ -0,0 +1,49 @@ +/* + * 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;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/bdmenu-based/config.def.h b/bdmenu-based/config.def.h new file mode 100755 index 0000000..450702f --- /dev/null +++ b/bdmenu-based/config.def.h @@ -0,0 +1,33 @@ +/* See LICENSE file for copyright and license details. */ +/* Default settings; can be overriden by command line. */ + +static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +static const unsigned int alpha = 0xf0; +static int centered = 0; /* -c option; centers dmenu on screen */ +static int min_width = 500; /* minimum width when centered */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "monospace:size=10" +}; +static const char *prompt = NULL; /* -p option; prompt to the left of input field */ +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; + +static const unsigned int alphas[SchemeLast][2] = { + [SchemeNorm] = { OPAQUE, alpha }, + [SchemeSel] = { OPAQUE, alpha }, + [SchemeOut] = { OPAQUE, alpha }, +}; + +/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ +static unsigned int lines = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; diff --git a/bdmenu-based/config.h b/bdmenu-based/config.h new file mode 100755 index 0000000..450702f --- /dev/null +++ b/bdmenu-based/config.h @@ -0,0 +1,33 @@ +/* See LICENSE file for copyright and license details. */ +/* Default settings; can be overriden by command line. */ + +static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +static const unsigned int alpha = 0xf0; +static int centered = 0; /* -c option; centers dmenu on screen */ +static int min_width = 500; /* minimum width when centered */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "monospace:size=10" +}; +static const char *prompt = NULL; /* -p option; prompt to the left of input field */ +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; + +static const unsigned int alphas[SchemeLast][2] = { + [SchemeNorm] = { OPAQUE, alpha }, + [SchemeSel] = { OPAQUE, alpha }, + [SchemeOut] = { OPAQUE, alpha }, +}; + +/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ +static unsigned int lines = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; diff --git a/bdmenu-based/config.mk b/bdmenu-based/config.mk new file mode 100755 index 0000000..57afe4d --- /dev/null +++ b/bdmenu-based/config.mk @@ -0,0 +1,31 @@ +# dmenu version +VERSION = 5.1 + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama -lXrender +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +FREETYPEINC = $(X11INC)/freetype2 + +# includes and libs +INCS = -I$(X11INC) -I$(FREETYPEINC) +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) +CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) +LDFLAGS = $(LIBS) + +# compiler and linker +CC = cc diff --git a/bdmenu-based/config.mk~ b/bdmenu-based/config.mk~ new file mode 100755 index 0000000..ebd8e96 --- /dev/null +++ b/bdmenu-based/config.mk~ @@ -0,0 +1,31 @@ +# dmenu version +VERSION = 5.1 + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama -lXrender +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = $(X11INC)/freetype2 + +# includes and libs +INCS = -I$(X11INC) -I$(FREETYPEINC) +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) +CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) +LDFLAGS = $(LIBS) + +# compiler and linker +CC = cc diff --git a/bdmenu-based/dmenu b/bdmenu-based/dmenu new file mode 100755 index 0000000000000000000000000000000000000000..86a0d6b2da400d81f3e3c92c2951ea3e56601dd0 GIT binary patch literal 42384 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 literal 0 HcmV?d00001 diff --git a/bdmenu-based/dmenu.1 b/bdmenu-based/dmenu.1 new file mode 100755 index 0000000..c036baa --- /dev/null +++ b/bdmenu-based/dmenu.1 @@ -0,0 +1,197 @@ +.TH DMENU 1 dmenu\-VERSION +.SH NAME +dmenu \- dynamic menu +.SH SYNOPSIS +.B dmenu +.RB [ \-bfiv ] +.RB [ \-l +.IR lines ] +.RB [ \-m +.IR monitor ] +.RB [ \-p +.IR prompt ] +.RB [ \-fn +.IR font ] +.RB [ \-nb +.IR color ] +.RB [ \-nf +.IR color ] +.RB [ \-sb +.IR color ] +.RB [ \-sf +.IR color ] +.RB [ \-w +.IR windowid ] +.P +.BR dmenu_run " ..." +.SH DESCRIPTION +.B dmenu +is a dynamic menu for X, which reads a list of newline\-separated items from +stdin. When the user selects an item and presses Return, their choice is printed +to stdout and dmenu terminates. Entering text will narrow the items to those +matching the tokens in the input. +.P +.B dmenu_run +is a script used by +.IR dwm (1) +which lists programs in the user's $PATH and runs the result in their $SHELL. +.SH OPTIONS +.TP +.B \-b +dmenu appears at the bottom of the screen. +.TP +.B \-c +dmenu appears centered on the screen. +.TP +.B \-f +dmenu grabs the keyboard before reading stdin if not reading from a tty. This +is faster, but will lock up X until stdin reaches end\-of\-file. +.TP +.B \-i +dmenu matches menu items case insensitively. +.TP +.BI \-l " lines" +dmenu lists items vertically, with the given number of lines. +.TP +.BI \-m " monitor" +dmenu is displayed on the monitor number supplied. Monitor numbers are starting +from 0. +.TP +.BI \-p " prompt" +defines the prompt to be displayed to the left of the input field. +.TP +.BI \-fn " font" +defines the font or font set used. +.TP +.BI \-nb " color" +defines the normal background color. +.IR #RGB , +.IR #RRGGBB , +and X color names are supported. +.TP +.BI \-nf " color" +defines the normal foreground color. +.TP +.BI \-sb " color" +defines the selected background color. +.TP +.BI \-sf " color" +defines the selected foreground color. +.TP +.B \-v +prints version information to stdout, then exits. +.TP +.BI \-w " windowid" +embed into windowid. +.SH USAGE +dmenu is completely controlled by the keyboard. Items are selected using the +arrow keys, page up, page down, home, and end. +.TP +.B Tab +Copy the selected item to the input field. +.TP +.B Return +Confirm selection. Prints the selected item to stdout and exits, returning +success. +.TP +.B Ctrl-Return +Confirm selection. Prints the selected item to stdout and continues. +.TP +.B Shift\-Return +Confirm input. Prints the input text to stdout and exits, returning success. +.TP +.B Escape +Exit without selecting an item, returning failure. +.TP +.B Ctrl-Left +Move cursor to the start of the current word +.TP +.B Ctrl-Right +Move cursor to the end of the current word +.TP +.B C\-a +Home +.TP +.B C\-b +Left +.TP +.B C\-c +Escape +.TP +.B C\-d +Delete +.TP +.B C\-e +End +.TP +.B C\-f +Right +.TP +.B C\-g +Escape +.TP +.B C\-h +Backspace +.TP +.B C\-i +Tab +.TP +.B C\-j +Return +.TP +.B C\-J +Shift-Return +.TP +.B C\-k +Delete line right +.TP +.B C\-m +Return +.TP +.B C\-M +Shift-Return +.TP +.B C\-n +Down +.TP +.B C\-p +Up +.TP +.B C\-u +Delete line left +.TP +.B C\-w +Delete word left +.TP +.B C\-y +Paste from primary X selection +.TP +.B C\-Y +Paste from X clipboard +.TP +.B M\-b +Move cursor to the start of the current word +.TP +.B M\-f +Move cursor to the end of the current word +.TP +.B M\-g +Home +.TP +.B M\-G +End +.TP +.B M\-h +Up +.TP +.B M\-j +Page down +.TP +.B M\-k +Page up +.TP +.B M\-l +Down +.SH SEE ALSO +.IR dwm (1), +.IR stest (1) diff --git a/bdmenu-based/dmenu.c b/bdmenu-based/dmenu.c new file mode 100755 index 0000000..79093de --- /dev/null +++ b/bdmenu-based/dmenu.c @@ -0,0 +1,883 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ + * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#define NUMBERSMAXDIGITS 100 +#define NUMBERSBUFSIZE (NUMBERSMAXDIGITS * 2) + 1 + +#define OPAQUE 0xffU + +/* enums */ +enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ + +struct item { + char *text; + struct item *left, *right; + int out; +}; + +static char numbers[NUMBERSBUFSIZE] = ""; +static char text[BUFSIZ] = ""; +static char *embed; +static int bh, mw, mh; +static int inputw = 0, promptw; +static int lrpad; /* sum of left and right padding */ +static size_t cursor; +static struct item *items = NULL; +static struct item *matches, *matchend; +static struct item *prev, *curr, *next, *sel; +static int mon = -1, screen; + +static Atom clip, utf8; +static Display *dpy; +static Window root, parentwin, win; +static XIC xic; + +static Drw *drw; +static Clr *scheme[SchemeLast]; + +static int useargb = 0; +static Visual *visual; +static int depth; +static Colormap cmap; + +#include "config.h" + +static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; +static char *(*fstrstr)(const char *, const char *) = strstr; +static void xinitvisual(); + +static void +appenditem(struct item *item, struct item **list, struct item **last) +{ + if (*last) + (*last)->right = item; + else + *list = item; + + item->left = *last; + item->right = NULL; + *last = item; +} + +static void +calcoffsets(void) +{ + int i, n; + + if (lines > 0) + n = lines * bh; + else + n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); + /* calculate which items will begin the next page and previous page */ + for (i = 0, next = curr; next; next = next->right) + if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) + break; + for (i = 0, prev = curr; prev && prev->left; prev = prev->left) + if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) + break; +} + +static int +max_textw(void) +{ + int len = 0; + for (struct item *item = items; item && item->text; item++) + len = MAX(TEXTW(item->text), len); + return len; +} + +static void +cleanup(void) +{ + size_t i; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < SchemeLast; i++) + free(scheme[i]); + drw_free(drw); + XSync(dpy, False); + XCloseDisplay(dpy); +} + +static char * +cistrstr(const char *h, const char *n) +{ + size_t i; + + if (!n[0]) + return (char *)h; + + for (; *h; ++h) { + for (i = 0; n[i] && tolower((unsigned char)n[i]) == + tolower((unsigned char)h[i]); ++i) + ; + if (n[i] == '\0') + return (char *)h; + } + return NULL; +} + +static int +drawitem(struct item *item, int x, int y, int w) +{ + if (item == sel) + drw_setscheme(drw, scheme[SchemeSel]); + else if (item->out) + drw_setscheme(drw, scheme[SchemeOut]); + else + drw_setscheme(drw, scheme[SchemeNorm]); + + return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); +} + +static void +recalculatenumbers() +{ + unsigned int numer = 0, denom = 0; + struct item *item; + if (matchend) { + numer++; + for (item = matchend; item && item->left; item = item->left) + numer++; + } + for (item = items; item && item->text; item++) + denom++; + snprintf(numbers, NUMBERSBUFSIZE, "%d/%d", numer, denom); +} + +static void +drawmenu(void) +{ + unsigned int curpos; + struct item *item; + int x = 0, y = 0, w; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, 0, 0, mw, mh, 1, 1); + + if (prompt && *prompt) { + drw_setscheme(drw, scheme[SchemeSel]); + x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); + } + /* draw input field */ + w = (lines > 0 || !matches) ? mw - x : inputw; + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); + + curpos = TEXTW(text) - TEXTW(&text[cursor]); + if ((curpos += lrpad / 2 - 1) < w) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); + } + + recalculatenumbers(); + if (lines > 0) { + /* draw vertical list */ + for (item = curr; item != next; item = item->right) + drawitem(item, x, y += bh, mw - x); + } else if (matches) { + /* draw horizontal list */ + x += inputw; + w = TEXTW("<"); + if (curr->left) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); + } + x += w; + for (item = curr; item != next; item = item->right) + x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">") - TEXTW(numbers))); + if (next) { + w = TEXTW(">"); + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, mw - w - TEXTW(numbers), 0, w, bh, lrpad / 2, ">", 0); + } + } + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, mw - TEXTW(numbers), 0, TEXTW(numbers), bh, lrpad / 2, numbers, 0); + drw_map(drw, win, 0, 0, mw, mh); +} + +static void +grabfocus(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; + Window focuswin; + int i, revertwin; + + for (i = 0; i < 100; ++i) { + XGetInputFocus(dpy, &focuswin, &revertwin); + if (focuswin == win) + return; + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); + nanosleep(&ts, NULL); + } + die("cannot grab focus"); +} + +static void +grabkeyboard(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; + int i; + + if (embed) + return; + /* try to grab keyboard, we may have to wait for another process to ungrab */ + for (i = 0; i < 1000; i++) { + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, + GrabModeAsync, CurrentTime) == GrabSuccess) + return; + nanosleep(&ts, NULL); + } + die("cannot grab keyboard"); +} + +static void +match(void) +{ + static char **tokv = NULL; + static int tokn = 0; + + char buf[sizeof text], *s; + int i, tokc = 0; + size_t len, textsize; + struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; + + strcpy(buf, text); + /* separate input text into tokens to be matched individually */ + for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) + if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) + die("cannot realloc %u bytes:", tokn * sizeof *tokv); + len = tokc ? strlen(tokv[0]) : 0; + + matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; + textsize = strlen(text) + 1; + for (item = items; item && item->text; item++) { + for (i = 0; i < tokc; i++) + if (!fstrstr(item->text, tokv[i])) + break; + if (i != tokc) /* not all tokens match */ + continue; + /* exact matches go first, then prefixes, then substrings */ + if (!tokc || !fstrncmp(text, item->text, textsize)) + appenditem(item, &matches, &matchend); + else if (!fstrncmp(tokv[0], item->text, len)) + appenditem(item, &lprefix, &prefixend); + else + appenditem(item, &lsubstr, &substrend); + } + if (lprefix) { + if (matches) { + matchend->right = lprefix; + lprefix->left = matchend; + } else + matches = lprefix; + matchend = prefixend; + } + if (lsubstr) { + if (matches) { + matchend->right = lsubstr; + lsubstr->left = matchend; + } else + matches = lsubstr; + matchend = substrend; + } + curr = sel = matches; + calcoffsets(); +} + +static void +insert(const char *str, ssize_t n) +{ + if (strlen(text) + n > sizeof text - 1) + return; + /* move existing text out of the way, insert new text, and update cursor */ + memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); + if (n > 0) + memcpy(&text[cursor], str, n); + cursor += n; + match(); +} + +static size_t +nextrune(int inc) +{ + ssize_t n; + + /* return location of next utf8 rune in the given direction (+1 or -1) */ + for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) + ; + return n; +} + +static void +movewordedge(int dir) +{ + if (dir < 0) { /* move cursor to the start of the word*/ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + } else { /* move cursor to the end of the word */ + while (text[cursor] && strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + while (text[cursor] && !strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + } +} + +static void +keypress(XKeyEvent *ev) +{ + char buf[32]; + int len; + KeySym ksym; + Status status; + + len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); + switch (status) { + default: /* XLookupNone, XBufferOverflow */ + return; + case XLookupChars: + goto insert; + case XLookupKeySym: + case XLookupBoth: + break; + } + + if (ev->state & ControlMask) { + switch(ksym) { + case XK_a: ksym = XK_Home; break; + case XK_b: ksym = XK_Left; break; + case XK_c: ksym = XK_Escape; break; + case XK_d: ksym = XK_Delete; break; + case XK_e: ksym = XK_End; break; + case XK_f: ksym = XK_Right; break; + case XK_g: ksym = XK_Escape; break; + case XK_h: ksym = XK_BackSpace; break; + case XK_i: ksym = XK_Tab; break; + case XK_j: /* fallthrough */ + case XK_J: /* fallthrough */ + case XK_m: /* fallthrough */ + case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; + case XK_n: ksym = XK_Down; break; + case XK_p: ksym = XK_Up; break; + + case XK_k: /* delete right */ + text[cursor] = '\0'; + match(); + break; + case XK_u: /* delete left */ + insert(NULL, 0 - cursor); + break; + case XK_w: /* delete word */ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + break; + case XK_y: /* paste selection */ + case XK_Y: + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, + utf8, utf8, win, CurrentTime); + return; + case XK_Left: + case XK_KP_Left: + movewordedge(-1); + goto draw; + case XK_Right: + case XK_KP_Right: + movewordedge(+1); + goto draw; + case XK_Return: + case XK_KP_Enter: + break; + case XK_bracketleft: + cleanup(); + exit(1); + default: + return; + } + } else if (ev->state & Mod1Mask) { + switch(ksym) { + case XK_b: + movewordedge(-1); + goto draw; + case XK_f: + movewordedge(+1); + goto draw; + case XK_g: ksym = XK_Home; break; + case XK_G: ksym = XK_End; break; + case XK_h: ksym = XK_Up; break; + case XK_j: ksym = XK_Next; break; + case XK_k: ksym = XK_Prior; break; + case XK_l: ksym = XK_Down; break; + default: + return; + } + } + + switch(ksym) { + default: +insert: + if (!iscntrl(*buf)) + insert(buf, len); + break; + case XK_Delete: + case XK_KP_Delete: + if (text[cursor] == '\0') + return; + cursor = nextrune(+1); + /* fallthrough */ + case XK_BackSpace: + if (cursor == 0) + return; + insert(NULL, nextrune(-1) - cursor); + break; + case XK_End: + case XK_KP_End: + if (text[cursor] != '\0') { + cursor = strlen(text); + break; + } + if (next) { + /* jump to end of list and position items in reverse */ + curr = matchend; + calcoffsets(); + curr = prev; + calcoffsets(); + while (next && (curr = curr->right)) + calcoffsets(); + } + sel = matchend; + break; + case XK_Escape: + cleanup(); + exit(1); + case XK_Home: + case XK_KP_Home: + if (sel == matches) { + cursor = 0; + break; + } + sel = curr = matches; + calcoffsets(); + break; + case XK_Left: + case XK_KP_Left: + if (cursor > 0 && (!sel || !sel->left || lines > 0)) { + cursor = nextrune(-1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Up: + case XK_KP_Up: + if (sel && sel->left && (sel = sel->left)->right == curr) { + curr = prev; + calcoffsets(); + } + break; + case XK_Next: + case XK_KP_Next: + if (!next) + return; + sel = curr = next; + calcoffsets(); + break; + case XK_Prior: + case XK_KP_Prior: + if (!prev) + return; + sel = curr = prev; + calcoffsets(); + break; + case XK_Return: + case XK_KP_Enter: + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + if (!(ev->state & ControlMask)) { + cleanup(); + exit(0); + } + if (sel) + sel->out = 1; + break; + case XK_Right: + case XK_KP_Right: + if (text[cursor] != '\0') { + cursor = nextrune(+1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Down: + case XK_KP_Down: + if (sel && sel->right && (sel = sel->right) == next) { + curr = next; + calcoffsets(); + } + break; + case XK_Tab: + if (!sel) + return; + strncpy(text, sel->text, sizeof text - 1); + text[sizeof text - 1] = '\0'; + cursor = strlen(text); + match(); + break; + } + +draw: + drawmenu(); +} + +static void +paste(void) +{ + char *p, *q; + int di; + unsigned long dl; + Atom da; + + /* we have been given the current selection, now insert it into input */ + if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, + utf8, &da, &di, &dl, &dl, (unsigned char **)&p) + == Success && p) { + insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); + XFree(p); + } + drawmenu(); +} + +static void +readstdin(void) +{ + char buf[sizeof text], *p; + size_t i, imax = 0, size = 0; + unsigned int tmpmax = 0; + + /* read each line from stdin and add it to the item list */ + for (i = 0; fgets(buf, sizeof buf, stdin); i++) { + if (i + 1 >= size / sizeof *items) + if (!(items = realloc(items, (size += BUFSIZ)))) + die("cannot realloc %u bytes:", size); + if ((p = strchr(buf, '\n'))) + *p = '\0'; + if (!(items[i].text = strdup(buf))) + die("cannot strdup %u bytes:", strlen(buf) + 1); + items[i].out = 0; + drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); + if (tmpmax > inputw) { + inputw = tmpmax; + imax = i; + } + } + if (items) + items[i].text = NULL; + inputw = items ? TEXTW(items[imax].text) : 0; + lines = MIN(lines, i); +} + +static void +run(void) +{ + XEvent ev; + + while (!XNextEvent(dpy, &ev)) { + if (XFilterEvent(&ev, win)) + continue; + switch(ev.type) { + case DestroyNotify: + if (ev.xdestroywindow.window != win) + break; + cleanup(); + exit(1); + case Expose: + if (ev.xexpose.count == 0) + drw_map(drw, win, 0, 0, mw, mh); + break; + case FocusIn: + /* regrab focus from parent window */ + if (ev.xfocus.window != win) + grabfocus(); + break; + case KeyPress: + keypress(&ev.xkey); + break; + case SelectionNotify: + if (ev.xselection.property == utf8) + paste(); + break; + case VisibilityNotify: + if (ev.xvisibility.state != VisibilityUnobscured) + XRaiseWindow(dpy, win); + break; + } + } +} + +static void +setup(void) +{ + int x, y, i, j; + unsigned int du; + XSetWindowAttributes swa; + XIM xim; + Window w, dw, *dws; + XWindowAttributes wa; + XClassHint ch = {"dmenu", "dmenu"}; +#ifdef XINERAMA + XineramaScreenInfo *info; + Window pw; + int a, di, n, area = 0; +#endif + /* init appearance */ + for (j = 0; j < SchemeLast; j++) + scheme[j] = drw_scm_create(drw, colors[j], alphas[i], 2); + + clip = XInternAtom(dpy, "CLIPBOARD", False); + utf8 = XInternAtom(dpy, "UTF8_STRING", False); + + /* calculate menu geometry */ + bh = drw->fonts->h + 2; + lines = MAX(lines, 0); + mh = (lines + 1) * bh; + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; +#ifdef XINERAMA + i = 0; + if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { + XGetInputFocus(dpy, &w, &di); + if (mon >= 0 && mon < n) + i = mon; + else if (w != root && w != PointerRoot && w != None) { + /* find top-level window containing current input focus */ + do { + if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) + XFree(dws); + } while (w != root && w != pw); + /* find xinerama screen with which the window intersects most */ + if (XGetWindowAttributes(dpy, pw, &wa)) + for (j = 0; j < n; j++) + if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { + area = a; + i = j; + } + } + /* no focused window is on screen, so use pointer location instead */ + if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) + for (i = 0; i < n; i++) + if (INTERSECT(x, y, 1, 1, info[i]) != 0) + break; + +if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); + x = info[i].x_org + ((info[i].width - mw) / 2); + y = info[i].y_org + ((info[i].height - mh) / 2); + } else { + x = info[i].x_org; + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); + mw = info[i].width; + } + XFree(info); + } else +#endif + { + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + + if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); + x = (wa.width - mw) / 2; + y = (wa.height - mh) / 2; + } else { + x = 0; + y = topbar ? 0 : wa.height - mh; + mw = wa.width; + } + } + inputw = MIN(inputw, mw/3); + match(); + + /* create menu window */ + swa.override_redirect = True; + swa.background_pixel = 0; + swa.border_pixel = 0; + swa.colormap = cmap; + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, CWBorderWidth, + depth, CopyFromParent, visual, + CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &swa); + XSetClassHint(dpy, win, &ch); + + + /* input methods */ + if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) + die("XOpenIM failed: could not open input device"); + + xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, win, XNFocusWindow, win, NULL); + + XMapRaised(dpy, win); + if (embed) { + XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); + if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { + for (i = 0; i < du && dws[i] != win; ++i) + XSelectInput(dpy, dws[i], FocusChangeMask); + XFree(dws); + } + grabfocus(); + } + drw_resize(drw, mw, mh); + drawmenu(); +} + +static void +usage(void) +{ + fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + XWindowAttributes wa; + int i, fast = 0; + + for (i = 1; i < argc; i++) + /* these options take no arguments */ + if (!strcmp(argv[i], "-v")) { /* prints version information */ + puts("dmenu-"VERSION); + exit(0); + } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ + topbar = 0; + else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ + fast = 1; + else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ + centered = 1; + else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; + } else if (i + 1 == argc) + usage(); + /* these options take one argument */ + else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ + lines = atoi(argv[++i]); + else if (!strcmp(argv[i], "-m")) + mon = atoi(argv[++i]); + else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ + prompt = argv[++i]; + else if (!strcmp(argv[i], "-fn")) /* font or font set */ + fonts[0] = argv[++i]; + else if (!strcmp(argv[i], "-nb")) /* normal background color */ + colors[SchemeNorm][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ + colors[SchemeNorm][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-sb")) /* selected background color */ + colors[SchemeSel][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ + colors[SchemeSel][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-w")) /* embedding window id */ + embed = argv[++i]; + else + usage(); + + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("cannot open display"); + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + if (!embed || !(parentwin = strtol(embed, NULL, 0))) + parentwin = root; + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + xinitvisual(); + drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + +#ifdef __OpenBSD__ + if (pledge("stdio rpath", NULL) == -1) + die("pledge"); +#endif + + if (fast && !isatty(0)) { + grabkeyboard(); + readstdin(); + } else { + readstdin(); + grabkeyboard(); + } + setup(); + run(); + + return 1; /* unreachable */ +} + + void +xinitvisual() +{ + XVisualInfo *infos; + XRenderPictFormat *fmt; + int nitems; + int i; + + XVisualInfo tpl = { + .screen = screen, + .depth = 32, + .class = TrueColor + }; + long masks = VisualScreenMask | VisualDepthMask | VisualClassMask; + + infos = XGetVisualInfo(dpy, masks, &tpl, &nitems); + visual = NULL; + for(i = 0; i < nitems; i ++) { + fmt = XRenderFindVisualFormat(dpy, infos[i].visual); + if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { + visual = infos[i].visual; + depth = infos[i].depth; + cmap = XCreateColormap(dpy, root, visual, AllocNone); + useargb = 1; + break; + } + } + + XFree(infos); + + if (! visual) { + visual = DefaultVisual(dpy, screen); + depth = DefaultDepth(dpy, screen); + cmap = DefaultColormap(dpy, screen); + } +} diff --git a/bdmenu-based/dmenu.o b/bdmenu-based/dmenu.o new file mode 100644 index 0000000000000000000000000000000000000000..8e83e6c7fddb253303f13411455482c07b4978ec GIT binary patch literal 38336 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 +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static long +utf8decodebyte(const char c, size_t *i) +{ + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t +utf8validate(long *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t +utf8decode(const char *c, long *u, size_t clen) +{ + size_t i, j, len, type; + long 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) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Drw * +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + drw->root = root; + drw->w = w; + drw->h = h; + drw->visual = visual; + drw->depth = depth; + drw->cmap = cmap; + drw->drawable = XCreatePixmap(dpy, root, w, h, depth); + drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + drw->w = w; + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth); +} + +void +drw_free(Drw *drw) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + drw_fontset_free(drw->fonts); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) +{ + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + /* Do not allow using color fonts. This is a workaround for a BadLength + * error from Xft with color glyphs. Modelled on the Xterm workaround. See + * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + * https://lists.suckless.org/dev/1701/30932.html + * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 + * and lots more all over the internet. + */ + FcBool iscol; + if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { + XftFontClose(drw->dpy, xfont); + return NULL; + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha) +{ + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap, + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); + + dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24); +} + +/* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ +Clr * +drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]); + return ret; +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); + if (filled) + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ + char buf[1024]; + int ty; + unsigned int ew; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + size_t i, len; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0; + + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + return 0; + + if (!render) { + w = ~w; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + while (1) { + utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + } else { + nextfont = curfont; + } + break; + } + } + + if (!charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); + /* shorten text if necessary */ + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) + drw_font_getexts(usedfont, utf8str, len, &ew, NULL); + + if (len) { + memcpy(buf, utf8str, len); + buf[len] = '\0'; + if (len < utf8strlen) + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ + + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)buf, len); + } + x += ew; + w -= ew; + } + } + + if (!*text) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur * +drw_cur_create(Drw *drw, int shape) +{ + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + cur->cursor = XCreateFontCursor(drw->dpy, shape); + + return cur; +} + +void +drw_cur_free(Drw *drw, Cur *cursor) +{ + if (!cursor) + return; + + XFreeCursor(drw->dpy, cursor->cursor); + free(cursor); +} diff --git a/bdmenu-based/drw.h b/bdmenu-based/drw.h new file mode 100755 index 0000000..f6fa5cd --- /dev/null +++ b/bdmenu-based/drw.h @@ -0,0 +1,60 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Visual *visual; + unsigned int depth; + Colormap cmap; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual*, unsigned int, Colormap); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/bdmenu-based/drw.o b/bdmenu-based/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..0619a4b1980f548c7e69d12ed0ee3d56e9416b68 GIT binary patch literal 16016 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 eDYUU8(fQk$Wcw1kgv>w*Ozgx50P- literal 0 HcmV?d00001 diff --git a/bdmenu-based/stest.1 b/bdmenu-based/stest.1 new file mode 100755 index 0000000..2667d8a --- /dev/null +++ b/bdmenu-based/stest.1 @@ -0,0 +1,90 @@ +.TH STEST 1 dmenu\-VERSION +.SH NAME +stest \- filter a list of files by properties +.SH SYNOPSIS +.B stest +.RB [ -abcdefghlpqrsuwx ] +.RB [ -n +.IR file ] +.RB [ -o +.IR file ] +.RI [ file ...] +.SH DESCRIPTION +.B stest +takes a list of files and filters by the files' properties, analogous to +.IR test (1). +Files which pass all tests are printed to stdout. If no files are given, stest +reads files from stdin. +.SH OPTIONS +.TP +.B \-a +Test hidden files. +.TP +.B \-b +Test that files are block specials. +.TP +.B \-c +Test that files are character specials. +.TP +.B \-d +Test that files are directories. +.TP +.B \-e +Test that files exist. +.TP +.B \-f +Test that files are regular files. +.TP +.B \-g +Test that files have their set-group-ID flag set. +.TP +.B \-h +Test that files are symbolic links. +.TP +.B \-l +Test the contents of a directory given as an argument. +.TP +.BI \-n " file" +Test that files are newer than +.IR file . +.TP +.BI \-o " file" +Test that files are older than +.IR file . +.TP +.B \-p +Test that files are named pipes. +.TP +.B \-q +No files are printed, only the exit status is returned. +.TP +.B \-r +Test that files are readable. +.TP +.B \-s +Test that files are not empty. +.TP +.B \-u +Test that files have their set-user-ID flag set. +.TP +.B \-v +Invert the sense of tests, only failing files pass. +.TP +.B \-w +Test that files are writable. +.TP +.B \-x +Test that files are executable. +.SH EXIT STATUS +.TP +.B 0 +At least one file passed all tests. +.TP +.B 1 +No files passed all tests. +.TP +.B 2 +An error occurred. +.SH SEE ALSO +.IR dmenu (1), +.IR test (1) diff --git a/bdmenu-based/stest.c b/bdmenu-based/stest.c new file mode 100755 index 0000000..e27d3a5 --- /dev/null +++ b/bdmenu-based/stest.c @@ -0,0 +1,109 @@ +/* See LICENSE file for copyright and license details. */ +#include + +#include +#include +#include +#include +#include +#include + +#include "arg.h" +char *argv0; + +#define FLAG(x) (flag[(x)-'a']) + +static void test(const char *, const char *); +static void usage(void); + +static int match = 0; +static int flag[26]; +static struct stat old, new; + +static void +test(const char *path, const char *name) +{ + struct stat st, ln; + + if ((!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */ + && (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */ + && (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */ + && (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */ + && (!FLAG('e') || access(path, F_OK) == 0) /* exists */ + && (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */ + && (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */ + && (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */ + && (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */ + && (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */ + && (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */ + && (!FLAG('r') || access(path, R_OK) == 0) /* readable */ + && (!FLAG('s') || st.st_size > 0) /* not empty */ + && (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */ + && (!FLAG('w') || access(path, W_OK) == 0) /* writable */ + && (!FLAG('x') || access(path, X_OK) == 0)) != FLAG('v')) { /* executable */ + if (FLAG('q')) + exit(0); + match = 1; + puts(name); + } +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-abcdefghlpqrsuvwx] " + "[-n file] [-o file] [file...]\n", argv0); + exit(2); /* like test(1) return > 1 on error */ +} + +int +main(int argc, char *argv[]) +{ + struct dirent *d; + char path[PATH_MAX], *line = NULL, *file; + size_t linesiz = 0; + ssize_t n; + DIR *dir; + int r; + + ARGBEGIN { + case 'n': /* newer than file */ + case 'o': /* older than file */ + file = EARGF(usage()); + if (!(FLAG(ARGC()) = !stat(file, (ARGC() == 'n' ? &new : &old)))) + perror(file); + break; + default: + /* miscellaneous operators */ + if (strchr("abcdefghlpqrsuvwx", ARGC())) + FLAG(ARGC()) = 1; + else + usage(); /* unknown flag */ + } ARGEND; + + if (!argc) { + /* read list from stdin */ + while ((n = getline(&line, &linesiz, stdin)) > 0) { + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + test(line, line); + } + free(line); + } else { + for (; argc; argc--, argv++) { + if (FLAG('l') && (dir = opendir(*argv))) { + /* test directory contents */ + while ((d = readdir(dir))) { + r = snprintf(path, sizeof path, "%s/%s", + *argv, d->d_name); + if (r >= 0 && (size_t)r < sizeof path) + test(path, d->d_name); + } + closedir(dir); + } else { + test(*argv, *argv); + } + } + } + return match ? 0 : 1; +} diff --git a/bdmenu-based/stest.o b/bdmenu-based/stest.o new file mode 100644 index 0000000000000000000000000000000000000000..77ae22abb14aaa5b86f2d1f2d374ce193402cb85 GIT binary patch literal 5800 zcmbtXYiu0V6~4QE2HP2%G$bW%SrfacQayN`hMGDFo?s`lWn2Pz6xB`EoAvD4V!i9m z%sM7b8b`Ye9X1<6l}hjf5hN6g^P#=N{O4oHo(^g7?0^Xi; z_g?4b;%Sk3q}em~yWe@vna3U(Ft+)9KElOEwvg*9juKL8xqjY9Py5I!U>f;P;mX+1 z+P+(N|Dx^GzM}2Xc5AzK>PP<`s3s&>)ZGC*&gl7C-K{~kUf5S(*j_gBvHHQ>J4&_> zB8J>6+VrOOP9@h3D|cW{d%VBhx2e-vku9cM<{JUjrVq9IiIS~BdI5HIx9V|{Eh^dH zUsbA#>`y|59fo03i)J2l+DQiodR!n48G z!0X9h=>=NvR=4qc>5gD=Fn>p=V6=lv#kl)gI*=_ot+Y(2A=g(fmlvA2lZA^AH$O1v z&V~wMY}Q~tPMekc9b~r90$U4>q0DS^W_HnVuen#TisvCAqmq|V_~q$q4n2Zy^ub`v-su~ zBTt)Zz=lyUVPXy!rw{d%Z%VItrN6FBaE-3>SSM!qE+omVk6grUx>8=GZO>ft=||7w z5DsZy)Anlnw6Hd`7f03TrBmxP&q;EQ+T$d%9M5(^t$G#hA-m43C1+t|2&FH75A1>) z0B@y`dpYEm^}CM0Z0;wkmE-)^v}(mV^>hg`61*hv^1Xq z6DXO@qc_G)*J{&SROe}a@ozapE)lXmhdFE}!-OcggK)b1b^y1BJh%IBdy}xe8j69- z5xmT*Q*v#>?v0_R(YOpF%tJR_&P9M-tz_o~v=jRE7OcVTlGE2<&C0;7&EypN`mtcu zqUQ%r=)JT<%JHYYb_z#?og33@Mwi3F_G-vr1}3O-rQs&aZ54JK^vvPYL^-Ys7%Qk= z$*vX9b?9elVzigQBSCLhKD^!v%7Hgikfor{QT>{(O$xh`B&jISZN7*0BQsL z{QHFR!_vb0ILn6H+I|jJbQ69e`=cXfEIyi;__}4MCl5@K4*Sjyo22bX-27so!ww8} zeuT3Zz+@mcmN4-w#m^8>Rdw%8-1##46Abeme31C2ntkgU>*`NH>YoF=mF@i&`(UhW ze$;<&Q|*J9`eSv4+T%4(ROhRzp7aBMUt?|aTBI=kov1a&htn=NMiB#<6n)))jQO#^ z%MS1o2R#6q;Qd8#E>N#v8e4~J`pgHtoctQVYVyzWa(h(-gWo7YSo%oa5{NGxlqcZu zky_G>0YP~FxJ103qJ&H{JyrtE!&3@AsaubhUYX2?F;B{z=8g!SX`zE z4j}UBwSaTlW6+0~f9yZXmWrakRp3XK7yvheA1>Tq34k^~cDf?GzBAyzMexJ%hk%AP z$g&QAgjz2HpnCBPR`USzyDuI7i!0}nh{k$#UI0GF2P{2ig6BY`Bb=?97=l3ui@4Phv zJ|OV%+H(E_9{h|4|49M=q~PZ%hNHqhI4tj<3Ai{Xe-vlyxlgx|sND8o_RCis6$;@`vg-;waogAK}a5`Hh^zaZg1snE$UBz!yL|6aoHXZYV3 zj!NvqRf)fo@f+ErllNzc;kQe8l;PbBm;G;(_;JSH~r&2{Sn|W|3$jWt(_nnUN9H`rSB8$u807Qkg zg)iyUp3Ch`1n5Oak>7iM|}Nb#(x-m zV*ax1VD>UDbAJQ`z$T>j6K?;M +#include +#include +#include + +#include "util.h" + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} + +void +die(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} diff --git a/bdmenu-based/util.h b/bdmenu-based/util.h new file mode 100755 index 0000000..f633b51 --- /dev/null +++ b/bdmenu-based/util.h @@ -0,0 +1,8 @@ +/* See LICENSE file for copyright and license details. */ + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/bdmenu-based/util.o b/bdmenu-based/util.o new file mode 100644 index 0000000000000000000000000000000000000000..1189f7059fc4ca9a8cc0ccacd2697e2443349947 GIT binary patch literal 2592 zcmb_e-;Wbj6uxbjrRu^IG-eZ)SahS7gvnM^2u8AH1xA?wDhnZGQ-*eSZEQP}=@eH2 zp+qoFu&_Lk@VI}0@zEFEHJI>1%)T0VC2X?37!nOXLOI`^d+hAc8Xr8#opaB3zVqYW zxp&(0d(C~xL_)A6#BR~G7!@Kr*qx8d>A1)c)4aSHmbraobkYd!7Ct?^4BNQ0=#Ls- z{gN)6n1auIPtttt_G-ClwBH9mzbM0GierVN#)UtY2>md4VzhtEb~+s+_#$lt$1+B6 zXxI$)XU*Wyn->@ToR+(EnA}>feNw9Hu2fSkEoaYZx!SyzYksfg&Mg}4$t5E=lQG(R zDaoF+@k5#ljL|THWMN^9)@-I^D`wC!gTG`uh1PE2?=XYErM0JZcC53j`NmHm8>x3F zRxD)cA-(yIR14U=aP~aS%;1SMR_>7jS$=^Y_vf_lmRBBNE6iYI^d?y)t6FtRyCf(2 zcu5(FGoOgWY$ma3Xl;6qYGer?QNDxF=K9Rn$%%D?6PHsT6K85@Fq0*CoCf4~JPL|) z;L5<&6aXRL^%|wqN~7_9(j>=#__Nc?pAyMu;0=T%MECHZ2Bbz~V}vi)K-e`JTawHY zf@gKYGc@8-A^>?fUapg{Z)x=6ALxU>-UpxQgAez?hx*{R2!Az+oel-@kQEwRlHDEW z^{Uq+cL-6B>0CozL=edU4eTo8TLF^b4Mh195)EmvAT5j>hFOB@LHA$l4=kZ2d@ z^O6?2XICxVw`YA}&sKb)n|j0dgll`A;|YDnbDFLoq-xiNUUz(3-+yp&Tf?_Xp9{TO zJzXnW<+9hP%!t#|u2-r1(<-I?S#jF4YPKj>Y%%RN{gSZ5Yc868rK*<%3i;M4(qGn~ zi^ax1dU&=!)3m&D@!g#}gzmU@{Zyl@dse;d)XJ7`QO6-cmjtw~7mKnL^0-d5Qn!m< zK2Lo(l22vwdOn)@?d@a#o*9JyL(WnXKFiU333U}VsrSgfm?_UVuH~wS&Z7QDBDw$XDt3Wd1@bM6HW6KvDe=?RJzWyWR=e0zsOA6#$hPC6n t$MJkF@RuZIb{tYI8eLKMhx7fMB?6$gKysFZk3Nw957?y5*B*uk~`fpl_l za8&Te;Nl{PqyK;?h@*>x`raj{Ra8WbIOM_iE|&{;$$Ou?yv$m;+DtxfUn!xR?XKYi zW4`4$cE|hnw`*;;&G+Y9yN~(XdSWsjl_%cybBWZHX=QjS z>0ZGK`da?&#iiIP=_f13`m(J%>7>4H7x~nh#i>biX;LQTD1jZvK&_w#P!k9Zdntv^ zoE$}PHmsyBa271&cA~|peoz%hPhesx90cZr2Ou1?A{>fx-wx~dfd)YvAPhy7ah+R~ zP&>~!p{DMT&QBOvSRw#OiKv=s=L{`PsTMcb3bzXyZJgT zUSr+;Vr1pVCx~X9WBfUUXl{yVhTLe5!Zza|8O=YgJUl260$Fw7b0Z)KMFI!GD~?|9 z(*TM+0fkpgd;ta2qKT3Dw-x4VP5L|Ks0T%TmcXY+KoE)^90X9H7yLAU;!r>l{y0-) SWY|mO$54FZ-=w>8z4HvT=RD{D literal 0 HcmV?d00001 diff --git a/bdwm/.dwm.c.un~ b/bdwm/.dwm.c.un~ new file mode 100755 index 0000000000000000000000000000000000000000..f1177b0cdb510b4b8e801856c006ac11dffc6f17 GIT binary patch literal 2830 zcmeH}zfOZt7{x!3nlgy1i3_uPCl}+NM^MwL7aInWf<_ozyZ8vk&9M3m#@W$9AHnEi zOtiJ?qUQiN0m3L7Cpo#43-spuedoeK5MJ#^yT?yr?wwzxM!IdiyxuMzC)P=FIlBvo z>5VB5z0a-Tn@B$vkyYE>nFM~6xEqmwmTcmb2b-_~FT)f^Z5barKNe>V8QpUh;tg7y zQZPsPh4Lf*>h%8=0FL(zhsb;lj~~HDz8N_kodm*>al}g&OL@JyH zvwnh+H&G3j;khy{E@KZg2bT@dgSFTk3U+v++ih8eHid>*)Ek0q^=`XfZHP)U6pBgf?tAOqV$V>MbApnYf1jQk@<;U5`0 N{l2V!#}1~?*ME@1i(~)* literal 0 HcmV?d00001 diff --git a/bdwm/LICENSE b/bdwm/LICENSE new file mode 100755 index 0000000..995172f --- /dev/null +++ b/bdwm/LICENSE @@ -0,0 +1,38 @@ +MIT/X Consortium License + +© 2006-2019 Anselm R Garbe +© 2006-2009 Jukka Salmi +© 2006-2007 Sander van Dijk +© 2007-2011 Peter Hartlich +© 2007-2009 Szabolcs Nagy +© 2007-2009 Christof Musik +© 2007-2009 Premysl Hruby +© 2007-2008 Enno Gottox Boland +© 2008 Martin Hurton +© 2008 Neale Pickett +© 2009 Mate Nagy +© 2010-2016 Hiltjo Posthuma +© 2010-2012 Connor Lane Smith +© 2011 Christoph Lohmann <20h@r-36.net> +© 2015-2016 Quentin Rameau +© 2015-2016 Eric Pruitt +© 2016-2017 Markus Teich +© 2020-2022 Chris Down + +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/bdwm/Makefile b/bdwm/Makefile new file mode 100755 index 0000000..77bcbc0 --- /dev/null +++ b/bdwm/Makefile @@ -0,0 +1,51 @@ +# dwm - dynamic window manager +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dwm.c util.c +OBJ = ${SRC:.c=.o} + +all: options dwm + +options: + @echo dwm build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + ${CC} -c ${CFLAGS} $< + +${OBJ}: config.h config.mk + +config.h: + cp config.def.h $@ + +dwm: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz + +dist: clean + mkdir -p dwm-${VERSION} + cp -R LICENSE Makefile README config.def.h config.mk\ + dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} + tar -cf dwm-${VERSION}.tar dwm-${VERSION} + gzip dwm-${VERSION}.tar + rm -rf dwm-${VERSION} + +install: all + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f dwm ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 + chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/dwm\ + ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +.PHONY: all options clean dist install uninstall diff --git a/bdwm/config.def.h b/bdwm/config.def.h new file mode 100755 index 0000000..cbafcf8 --- /dev/null +++ b/bdwm/config.def.h @@ -0,0 +1,125 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 0.5; /* border pixel of windows */ +static const unsigned int snap = 32; /* snap pixel */ +static const int showbar = 0; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const char *fonts[] = { "monospace:size=10" }; +static const char dmenufont[] = "C095-Roman:size=9"; +static const char col_gray1[] = "#222222"; +static const char col_gray2[] = "#444444"; +static const char col_gray3[] = "#bbbbbb"; +static const char col_gray4[] = "#eeeeee"; +static const char col_cyan[] = "#000000"; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +}; + +/* tagging */ +static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating monitor */ + { "Gimp", NULL, NULL, 0, 1, -1 }, + { "Firefox", NULL, NULL, 0, 0, -1 }, +}; + +/* layout(s) */ +static const float mfact = 0.65; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + { "t", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +}; + +/* key definitions */ +#define MODKEY Mod1Mask +#define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + +/* helper for spawning shell commands in the pre dwm-5.0 fashion */ +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + +/* commands */ +static const char *dmenucmd[] = { "dmenu_run", "-c", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; +static const char *termcmd[] = { "st", NULL }; +static const char *keyboardLayoutSwitchcmd[] = { "/home/dcc/Desktop/kek/stuff/bashs/shell\ scrips/keyboardlayoutswitcher.sh", "us", "el" }; +static const char *slok[] = { "slock", NULL }; +static const char *emacs[] = { "emacs", NULL }; +static const char *flame[] = { "flameshot","gui", NULL }; +static const char *sea[] = { "seamonkey", NULL }; +static const char *c[] = { "clipmenu", NULL }; +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_y, spawn, {.v = slok } }, + { MODKEY|ShiftMask, XK_F1, spawn, {.v = keyboardLayoutSwitchcmd } }, + { MODKEY|ShiftMask, XK_s, spawn, {.v = flame } }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY|ShiftMask, XK_x, spawn, {.v = c } }, + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_o, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_s, togglesticky, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_f, togglefloating, {0} }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) + TAGKEYS( XK_4, 3) + TAGKEYS( XK_5, 4) + TAGKEYS( XK_6, 5) + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + diff --git a/bdwm/config.def.h~ b/bdwm/config.def.h~ new file mode 100755 index 0000000..a44f3e3 --- /dev/null +++ b/bdwm/config.def.h~ @@ -0,0 +1,124 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 0.5; /* border pixel of windows */ +static const unsigned int snap = 32; /* snap pixel */ +static const int showbar = 0; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const char *fonts[] = { "monospace:size=10" }; +static const char dmenufont[] = "C095-Roman:size=9"; +static const char col_gray1[] = "#222222"; +static const char col_gray2[] = "#444444"; +static const char col_gray3[] = "#bbbbbb"; +static const char col_gray4[] = "#eeeeee"; +static const char col_cyan[] = "#000000"; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +}; + +/* tagging */ +static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating monitor */ + { "Gimp", NULL, NULL, 0, 1, -1 }, + { "Firefox", NULL, NULL, 0, 0, -1 }, +}; + +/* layout(s) */ +static const float mfact = 0.65; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + { "t", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +}; + +/* key definitions */ +#define MODKEY Mod1Mask +#define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + +/* helper for spawning shell commands in the pre dwm-5.0 fashion */ +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + +/* commands */ +static const char *dmenucmd[] = { "dmenu_run", "-c", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; +static const char *termcmd[] = { "st", NULL }; +static const char *keyboardLayoutSwitchcmd[] = { "/home/dcc/Desktop/kek/stuff/bashs/shell\ scrips/keyboardlayoutswitcher.sh", "us", "el" }; +static const char *slok[] = { "slock", NULL }; +static const char *emacs[] = { "emacs", NULL }; +static const char *flame[] = { "flameshot","gui", NULL }; +static const char *sea[] = { "seamonkey", NULL }; +static const char *c[] = { "clipmenu", NULL }; +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_y, spawn, {.v = slok } }, + { MODKEY|ShiftMask, XK_F1, spawn, {.v = keyboardLayoutSwitchcmd } }, + { MODKEY|ShiftMask, XK_s, spawn, {.v = flame } }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY|ShiftMask, XK_x, spawn, {.v = c } }, + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_o, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_s, togglesticky, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) + TAGKEYS( XK_4, 3) + TAGKEYS( XK_5, 4) + TAGKEYS( XK_6, 5) + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + diff --git a/bdwm/config.h b/bdwm/config.h new file mode 100755 index 0000000..cbafcf8 --- /dev/null +++ b/bdwm/config.h @@ -0,0 +1,125 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 0.5; /* border pixel of windows */ +static const unsigned int snap = 32; /* snap pixel */ +static const int showbar = 0; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const char *fonts[] = { "monospace:size=10" }; +static const char dmenufont[] = "C095-Roman:size=9"; +static const char col_gray1[] = "#222222"; +static const char col_gray2[] = "#444444"; +static const char col_gray3[] = "#bbbbbb"; +static const char col_gray4[] = "#eeeeee"; +static const char col_cyan[] = "#000000"; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +}; + +/* tagging */ +static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating monitor */ + { "Gimp", NULL, NULL, 0, 1, -1 }, + { "Firefox", NULL, NULL, 0, 0, -1 }, +}; + +/* layout(s) */ +static const float mfact = 0.65; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + { "t", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +}; + +/* key definitions */ +#define MODKEY Mod1Mask +#define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + +/* helper for spawning shell commands in the pre dwm-5.0 fashion */ +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + +/* commands */ +static const char *dmenucmd[] = { "dmenu_run", "-c", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; +static const char *termcmd[] = { "st", NULL }; +static const char *keyboardLayoutSwitchcmd[] = { "/home/dcc/Desktop/kek/stuff/bashs/shell\ scrips/keyboardlayoutswitcher.sh", "us", "el" }; +static const char *slok[] = { "slock", NULL }; +static const char *emacs[] = { "emacs", NULL }; +static const char *flame[] = { "flameshot","gui", NULL }; +static const char *sea[] = { "seamonkey", NULL }; +static const char *c[] = { "clipmenu", NULL }; +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_y, spawn, {.v = slok } }, + { MODKEY|ShiftMask, XK_F1, spawn, {.v = keyboardLayoutSwitchcmd } }, + { MODKEY|ShiftMask, XK_s, spawn, {.v = flame } }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY|ShiftMask, XK_x, spawn, {.v = c } }, + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_o, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_s, togglesticky, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_f, togglefloating, {0} }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) + TAGKEYS( XK_4, 3) + TAGKEYS( XK_5, 4) + TAGKEYS( XK_6, 5) + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + diff --git a/bdwm/config.mk b/bdwm/config.mk new file mode 100755 index 0000000..287ee86 --- /dev/null +++ b/bdwm/config.mk @@ -0,0 +1,39 @@ +# dwm version +VERSION = 6.3 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +FREETYPEINC = ${X11INC}/freetype2 +MANPREFIX = ${PREFIX}/man + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} +CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/bdwm/config.mk~ b/bdwm/config.mk~ new file mode 100755 index 0000000..81c493e --- /dev/null +++ b/bdwm/config.mk~ @@ -0,0 +1,39 @@ +# dwm version +VERSION = 6.3 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = ${X11INC}/freetype2 +#MANPREFIX = ${PREFIX}/man + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} +CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/bdwm/drw.c b/bdwm/drw.c new file mode 100755 index 0000000..ced7d37 --- /dev/null +++ b/bdwm/drw.c @@ -0,0 +1,464 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static long +utf8decodebyte(const char c, size_t *i) +{ + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t +utf8validate(long *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t +utf8decode(const char *c, long *u, size_t clen) +{ + size_t i, j, len, type; + long 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) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Drw * +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + drw->root = root; + drw->w = w; + drw->h = h; + drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); + drw->gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + drw->w = w; + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); +} + +void +drw_free(Drw *drw) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + drw_fontset_free(drw->fonts); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) +{ + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + /* Do not allow using color fonts. This is a workaround for a BadLength + * error from Xft with color glyphs. Modelled on the Xterm workaround. See + * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + * https://lists.suckless.org/dev/1701/30932.html + * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 + * and lots more all over the internet. + */ + FcBool iscol; + if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { + XftFontClose(drw->dpy, xfont); + return NULL; + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); +} + +/* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ +Clr * +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); + if (filled) + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ + int i, ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0, overflow = 0; + /* keep track of a couple codepoints for which we have no match. */ + enum { nomatches_len = 64 }; + static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; + static unsigned int ellipsis_width = 0; + + if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) + return 0; + + if (!render) { + w = invert ? invert : ~invert; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + d = XftDrawCreate(drw->dpy, drw->drawable, + DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen)); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + if (!ellipsis_width && render) + ellipsis_width = drw_fontset_getwidth(drw, "..."); + while (1) { + ew = ellipsis_len = utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); + if (ew + ellipsis_width <= w) { + /* keep track where the ellipsis still fits */ + ellipsis_x = x + ew; + ellipsis_w = w - ew; + ellipsis_len = utf8strlen; + } + + if (ew + tmpw > w) { + overflow = 1; + /* called from drw_fontset_getwidth_clamp(): + * it wants the width AFTER the overflow + */ + if (!render) + x += tmpw; + else + utf8strlen = ellipsis_len; + } else if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + ew += tmpw; + } else { + nextfont = curfont; + } + break; + } + } + + if (overflow || !charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); + } + x += ew; + w -= ew; + } + if (render && overflow) + drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); + + if (!*text || overflow) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + for (i = 0; i < nomatches_len; ++i) { + /* avoid calling XftFontMatch if we know we won't find a match */ + if (utf8codepoint == nomatches.codepoint[i]) + goto no_match; + } + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; +no_match: + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +unsigned int +drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) +{ + unsigned int tmp = 0; + if (drw && drw->fonts && text && n) + tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); + return MIN(n, tmp); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur * +drw_cur_create(Drw *drw, int shape) +{ + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + cur->cursor = XCreateFontCursor(drw->dpy, shape); + + return cur; +} + +void +drw_cur_free(Drw *drw, Cur *cursor) +{ + if (!cursor) + return; + + XFreeCursor(drw->dpy, cursor->cursor); + free(cursor); +} diff --git a/bdwm/drw.h b/bdwm/drw.h new file mode 100755 index 0000000..6471431 --- /dev/null +++ b/bdwm/drw.h @@ -0,0 +1,58 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/bdwm/drw.o b/bdwm/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..d5e47079d8611b1f9059403cc774d6ee111c813c GIT binary patch literal 17136 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 literal 0 HcmV?d00001 diff --git a/bdwm/dwm b/bdwm/dwm new file mode 100755 index 0000000000000000000000000000000000000000..f8ccc61fe7fc3999647774e381a96422979c7ba7 GIT binary patch literal 72344 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 literal 0 HcmV?d00001 diff --git a/bdwm/dwm-steam-6.2.diff b/bdwm/dwm-steam-6.2.diff new file mode 100755 index 0000000..6b92c2a --- /dev/null +++ b/bdwm/dwm-steam-6.2.diff @@ -0,0 +1,63 @@ +From 2550931c66e10e667ce56a6761cbadd12b331c52 Mon Sep 17 00:00:00 2001 +From: bakkeby +Date: Mon, 10 Aug 2020 16:45:00 +0200 +Subject: [PATCH] Steam patch + +Steam, and steam windows (games), trigger a ConfigureNotify request every time the window +gets focus. More so, the configure event passed along from Steam tends to have the wrong +x and y coordinates which can make the window, if floating, jump around the screen. + +This patch works around this age-old issue by ignoring the x and y co-ordinates for +ConfigureNotify requests relating to Steam windows. +--- + dwm.c | 20 +++++++++++++------- + 1 file changed, 13 insertions(+), 7 deletions(-) + +diff --git a/dwm.c b/dwm.c +index 4465af1..598d36d 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int issteam; + Client *next; + Client *snext; + Monitor *mon; +@@ -291,6 +292,9 @@ applyrules(Client *c) + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + ++ if (strstr(class, "Steam") || strstr(class, "steam_app_")) ++ c->issteam = 1; ++ + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) +@@ -588,13 +592,15 @@ configurerequest(XEvent *e) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; +- if (ev->value_mask & CWX) { +- c->oldx = c->x; +- c->x = m->mx + ev->x; +- } +- if (ev->value_mask & CWY) { +- c->oldy = c->y; +- c->y = m->my + ev->y; ++ if (!c->issteam) { ++ if (ev->value_mask & CWX) { ++ c->oldx = c->x; ++ c->x = m->mx + ev->x; ++ } ++ if (ev->value_mask & CWY) { ++ c->oldy = c->y; ++ c->y = m->my + ev->y; ++ } + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; +-- +2.19.1 + diff --git a/bdwm/dwm.1 b/bdwm/dwm.1 new file mode 100755 index 0000000..ddc8321 --- /dev/null +++ b/bdwm/dwm.1 @@ -0,0 +1,176 @@ +.TH DWM 1 dwm\-VERSION +.SH NAME +dwm \- dynamic window manager +.SH SYNOPSIS +.B dwm +.RB [ \-v ] +.SH DESCRIPTION +dwm is a dynamic window manager for X. It manages windows in tiled, monocle +and floating layouts. Either layout can be applied dynamically, optimising the +environment for the application in use and the task performed. +.P +In tiled layouts windows are managed in a master and stacking area. The master +area on the left contains one window by default, and the stacking area on the +right contains all other windows. The number of master area windows can be +adjusted from zero to an arbitrary number. In monocle layout all windows are +maximised to the screen size. In floating layout windows can be resized and +moved freely. Dialog windows are always managed floating, regardless of the +layout applied. +.P +Windows are grouped by tags. Each window can be tagged with one or multiple +tags. Selecting certain tags displays all windows with these tags. +.P +Each screen contains a small status bar which displays all available tags, the +layout, the title of the focused window, and the text read from the root window +name property, if the screen is focused. A floating window is indicated with an +empty square and a maximised floating window is indicated with a filled square +before the windows title. The selected tags are indicated with a different +color. The tags of the focused window are indicated with a filled square in the +top left corner. The tags which are applied to one or more windows are +indicated with an empty square in the top left corner. +.P +dwm draws a small border around windows to indicate the focus state. +.SH OPTIONS +.TP +.B \-v +prints version information to stderr, then exits. +.SH USAGE +.SS Status bar +.TP +.B X root window name +is read and displayed in the status text area. It can be set with the +.BR xsetroot (1) +command. +.TP +.B Button1 +click on a tag label to display all windows with that tag, click on the layout +label toggles between tiled and floating layout. +.TP +.B Button3 +click on a tag label adds/removes all windows with that tag to/from the view. +.TP +.B Mod1\-Button1 +click on a tag label applies that tag to the focused window. +.TP +.B Mod1\-Button3 +click on a tag label adds/removes that tag to/from the focused window. +.SS Keyboard commands +.TP +.B Mod1\-Shift\-Return +Start +.BR st(1). +.TP +.B Mod1\-p +Spawn +.BR dmenu(1) +for launching other programs. +.TP +.B Mod1\-, +Focus previous screen, if any. +.TP +.B Mod1\-. +Focus next screen, if any. +.TP +.B Mod1\-Shift\-, +Send focused window to previous screen, if any. +.TP +.B Mod1\-Shift\-. +Send focused window to next screen, if any. +.TP +.B Mod1\-b +Toggles bar on and off. +.TP +.B Mod1\-t +Sets tiled layout. +.TP +.B Mod1\-f +Sets floating layout. +.TP +.B Mod1\-m +Sets monocle layout. +.TP +.B Mod1\-space +Toggles between current and previous layout. +.TP +.B Mod1\-j +Focus next window. +.TP +.B Mod1\-k +Focus previous window. +.TP +.B Mod1\-i +Increase number of windows in master area. +.TP +.B Mod1\-d +Decrease number of windows in master area. +.TP +.B Mod1\-l +Increase master area size. +.TP +.B Mod1\-h +Decrease master area size. +.TP +.B Mod1\-Return +Zooms/cycles focused window to/from master area (tiled layouts only). +.TP +.B Mod1\-Shift\-c +Close focused window. +.TP +.B Mod1\-Shift\-space +Toggle focused window between tiled and floating state. +.TP +.B Mod1\-Tab +Toggles to the previously selected tags. +.TP +.B Mod1\-Shift\-[1..n] +Apply nth tag to focused window. +.TP +.B Mod1\-Shift\-0 +Apply all tags to focused window. +.TP +.B Mod1\-Control\-Shift\-[1..n] +Add/remove nth tag to/from focused window. +.TP +.B Mod1\-[1..n] +View all windows with nth tag. +.TP +.B Mod1\-0 +View all windows with any tag. +.TP +.B Mod1\-Control\-[1..n] +Add/remove all windows with nth tag to/from the view. +.TP +.B Mod1\-Shift\-q +Quit dwm. +.SS Mouse commands +.TP +.B Mod1\-Button1 +Move focused window while dragging. Tiled windows will be toggled to the floating state. +.TP +.B Mod1\-Button2 +Toggles focused window between floating and tiled state. +.TP +.B Mod1\-Button3 +Resize focused window while dragging. Tiled windows will be toggled to the floating state. +.SH CUSTOMIZATION +dwm is customized by creating a custom config.h and (re)compiling the source +code. This keeps it fast, secure and simple. +.SH SEE ALSO +.BR dmenu (1), +.BR st (1) +.SH ISSUES +Java applications which use the XToolkit/XAWT backend may draw grey windows +only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early +JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds +are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the +environment variable +.BR AWT_TOOLKIT=MToolkit +(to use the older Motif backend instead) or running +.B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D +or +.B wmname LG3D +(to pretend that a non-reparenting window manager is running that the +XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable +.BR _JAVA_AWT_WM_NONREPARENTING=1 . +.SH BUGS +Send all bug reports with a patch to hackers@suckless.org. diff --git a/bdwm/dwm.c b/bdwm/dwm.c new file mode 100755 index 0000000..39aa327 --- /dev/null +++ b/bdwm/dwm.c @@ -0,0 +1,2163 @@ +/* See LICENSE file for copyright and license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * The event handlers of dwm are organized in an array which is accessed + * whenever a new event has been fetched. This allows event dispatching + * in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a linked client + * list on each monitor, the focus history is remembered through a stack list + * on each monitor. Each client contains a bit array to indicate the tags of a + * client. + * + * Keys and tagging rules are organized as arrays and defined in config.h. + * + * To understand everything else, start reading main(). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define WIDTH(X) ((X)->w + 2 * (X)->bw) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define TAGMASK ((1 << LENGTH(tags)) - 1) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +/* enums */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { SchemeNorm, SchemeSel }; /* color schemes */ +enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + +typedef struct Monitor Monitor; +typedef struct Client Client; +struct Client { + char name[256]; + float mina, maxa; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, issticky; + int issteam; + Client *next; + Client *snext; + Monitor *mon; + Window win; +}; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +} Layout; + +struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + int showbar; + int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Window barwin; + const Layout *lt[2]; +}; + +typedef struct { + const char *class; + const char *instance; + const char *title; + unsigned int tags; + int isfloating; + int monitor; +} Rule; + +/* function declarations */ +static void applyrules(Client *c); +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +static void arrange(Monitor *m); +static void arrangemon(Monitor *m); +static void attach(Client *c); +static void attachstack(Client *c); +static void buttonpress(XEvent *e); +static void checkotherwm(void); +static void cleanup(void); +static void cleanupmon(Monitor *mon); +static void clientmessage(XEvent *e); +static void configure(Client *c); +static void configurenotify(XEvent *e); +static void configurerequest(XEvent *e); +static Monitor *createmon(void); +static void destroynotify(XEvent *e); +static void detach(Client *c); +static void detachstack(Client *c); +static Monitor *dirtomon(int dir); +static void drawbar(Monitor *m); +static void drawbars(void); +static void enternotify(XEvent *e); +static void expose(XEvent *e); +static void focus(Client *c); +static void focusin(XEvent *e); +static void focusmon(const Arg *arg); +static void focusstack(const Arg *arg); +static Atom getatomprop(Client *c, Atom prop); +static int getrootptr(int *x, int *y); +static long getstate(Window w); +static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void grabbuttons(Client *c, int focused); +static void grabkeys(void); +static void incnmaster(const Arg *arg); +static void keypress(XEvent *e); +static void killclient(const Arg *arg); +static void manage(Window w, XWindowAttributes *wa); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void monocle(Monitor *m); +static void motionnotify(XEvent *e); +static void movemouse(const Arg *arg); +static Client *nexttiled(Client *c); +static void pop(Client *c); +static void propertynotify(XEvent *e); +static void quit(const Arg *arg); +static Monitor *recttomon(int x, int y, int w, int h); +static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resizeclient(Client *c, int x, int y, int w, int h); +static void resizemouse(const Arg *arg); +static void restack(Monitor *m); +static void run(void); +static void scan(void); +static int sendevent(Client *c, Atom proto); +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setlayout(const Arg *arg); +static void setmfact(const Arg *arg); +static void setup(void); +static void seturgent(Client *c, int urg); +static void showhide(Client *c); +static void sigchld(int unused); +static void spawn(const Arg *arg); +static void tag(const Arg *arg); +static void tagmon(const Arg *arg); +static void tile(Monitor *m); +static void togglebar(const Arg *arg); +static void togglefloating(const Arg *arg); +static void togglesticky(const Arg *arg); +static void toggletag(const Arg *arg); +static void toggleview(const Arg *arg); +static void unfocus(Client *c, int setfocus); +static void unmanage(Client *c, int destroyed); +static void unmapnotify(XEvent *e); +static void updatebarpos(Monitor *m); +static void updatebars(void); +static void updateclientlist(void); +static int updategeom(void); +static void updatenumlockmask(void); +static void updatesizehints(Client *c); +static void updatestatus(void); +static void updatetitle(Client *c); +static void updatewindowtype(Client *c); +static void updatewmhints(Client *c); +static void view(const Arg *arg); +static Client *wintoclient(Window w); +static Monitor *wintomon(Window w); +static int xerror(Display *dpy, XErrorEvent *ee); +static int xerrordummy(Display *dpy, XErrorEvent *ee); +static int xerrorstart(Display *dpy, XErrorEvent *ee); +static void zoom(const Arg *arg); + +/* variables */ +static const char broken[] = "broken"; +static char stext[256]; +static int screen; +static int sw, sh; /* X display screen geometry width, height */ +static int bh; /* bar height */ +static int lrpad; /* sum of left and right padding for text */ +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int numlockmask = 0; +static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast]; +static int running = 1; +static Cur *cursor[CurLast]; +static Clr **scheme; +static Display *dpy; +static Drw *drw; +static Monitor *mons, *selmon; +static Window root, wmcheckwin; + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +/* function implementations */ +void +applyrules(Client *c) +{ + const char *class, *instance; + unsigned int i; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + c->isfloating = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + + if (strstr(class, "Steam") || strstr(class, "steam_app_")) + c->issteam = 1; + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; + } + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; +} + +int +applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) +{ + int baseismin; + Monitor *m = c->mon; + + /* set minimum possible */ + *w = MAX(1, *w); + *h = MAX(1, *h); + if (interact) { + if (*x > sw) + *x = sw - WIDTH(c); + if (*y > sh) + *y = sh - HEIGHT(c); + if (*x + *w + 2 * c->bw < 0) + *x = 0; + if (*y + *h + 2 * c->bw < 0) + *y = 0; + } else { + if (*x >= m->wx + m->ww) + *x = m->wx + m->ww - WIDTH(c); + if (*y >= m->wy + m->wh) + *y = m->wy + m->wh - HEIGHT(c); + if (*x + *w + 2 * c->bw <= m->wx) + *x = m->wx; + if (*y + *h + 2 * c->bw <= m->wy) + *y = m->wy; + } + if (*h < bh) + *h = bh; + if (*w < bh) + *w = bh; + if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + if (!c->hintsvalid) + updatesizehints(c); + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for aspect limits */ + if (c->mina > 0 && c->maxa > 0) { + if (c->maxa < (float)*w / *h) + *w = *h * c->maxa + 0.5; + else if (c->mina < (float)*h / *w) + *h = *w * c->mina + 0.5; + } + if (baseismin) { /* increment calculation requires this */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for increment value */ + if (c->incw) + *w -= *w % c->incw; + if (c->inch) + *h -= *h % c->inch; + /* restore base dimensions */ + *w = MAX(*w + c->basew, c->minw); + *h = MAX(*h + c->baseh, c->minh); + if (c->maxw) + *w = MIN(*w, c->maxw); + if (c->maxh) + *h = MIN(*h, c->maxh); + } + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; +} + +void +arrange(Monitor *m) +{ + if (m) + showhide(m->stack); + else for (m = mons; m; m = m->next) + showhide(m->stack); + if (m) { + arrangemon(m); + restack(m); + } else for (m = mons; m; m = m->next) + arrangemon(m); +} + +void +arrangemon(Monitor *m) +{ + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +} + +void +attach(Client *c) +{ + c->next = c->mon->clients; + c->mon->clients = c; +} + +void +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +buttonpress(XEvent *e) +{ + unsigned int i, x, click; + Arg arg = {0}; + Client *c; + Monitor *m; + XButtonPressedEvent *ev = &e->xbutton; + + click = ClkRootWin; + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + if (ev->window == selmon->barwin) { + i = x = 0; + do + x += TEXTW(tags[i]); + while (ev->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(selmon->ltsymbol)) + click = ClkLtSymbol; + else if (ev->x > selmon->ww - (int)TEXTW(stext)) + click = ClkStatusText; + else + click = ClkWinTitle; + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } + for (i = 0; i < LENGTH(buttons); i++) + if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); +} + +void +checkotherwm(void) +{ + xerrorxlib = XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); + XSync(dpy, False); + XSetErrorHandler(xerror); + XSync(dpy, False); +} + +void +cleanup(void) +{ + Arg a = {.ui = ~0}; + Layout foo = { "", NULL }; + Monitor *m; + size_t i; + + view(&a); + selmon->lt[selmon->sellt] = &foo; + for (m = mons; m; m = m->next) + while (m->stack) + unmanage(m->stack, 0); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) + free(scheme[i]); + free(scheme); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); + XSync(dpy, False); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +} + +void +cleanupmon(Monitor *mon) +{ + Monitor *m; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); + free(mon); +} + +void +clientmessage(XEvent *e) +{ + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] + || cme->data.l[2] == netatom[NetWMFullscreen]) + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ + || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); + } else if (cme->message_type == netatom[NetActiveWindow]) { + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); + } +} + +void +configure(Client *c) +{ + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->bw; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +void +configurenotify(XEvent *e) +{ + Monitor *m; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; + + /* TODO: updategeom handling sucks, needs to be simplified */ + if (ev->window == root) { + dirty = (sw != ev->width || sh != ev->height); + sw = ev->width; + sh = ev->height; + if (updategeom() || dirty) { + drw_resize(drw, sw, bh); + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } + focus(NULL); + arrange(NULL); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + if (!c->issteam) { + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +Monitor * +createmon(void) +{ + Monitor *m; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); +} + +void +detach(Client *c) +{ + Client **tc; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; +} + +void +detachstack(Client *c) +{ + Client **tc, *t; + + for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); + *tc = c->snext; + + if (c == c->mon->sel) { + for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); + c->mon->sel = t; + } +} + +Monitor * +dirtomon(int dir) +{ + Monitor *m = NULL; + + if (dir > 0) { + if (!(m = selmon->next)) + m = mons; + } else if (selmon == mons) + for (m = mons; m->next; m = m->next); + else + for (m = mons; m->next != selmon; m = m->next); + return m; +} + +void +drawbar(Monitor *m) +{ + int x, w, tw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; + + if (!m->showbar) + return; + + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ + drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); + } + + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, + urg & 1 << i); + x += w; + } + w = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +} + +void +drawbars(void) +{ + Monitor *m; + + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +enternotify(XEvent *e) +{ + Client *c; + Monitor *m; + XCrossingEvent *ev = &e->xcrossing; + + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) + return; + c = wintoclient(ev->window); + m = c ? c->mon : wintomon(ev->window); + if (m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + } else if (!c || c == selmon->sel) + return; + focus(c); +} + +void +expose(XEvent *e) +{ + Monitor *m; + XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && (m = wintomon(ev->window))) + drawbar(m); +} + +void +focus(Client *c) +{ + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; + drawbars(); +} + +/* there are some broken focus acquiring clients needing extra handling */ +void +focusin(XEvent *e) +{ + XFocusChangeEvent *ev = &e->xfocus; + + if (selmon->sel && ev->window != selmon->sel->win) + setfocus(selmon->sel); +} + +void +focusmon(const Arg *arg) +{ + Monitor *m; + + if (!mons->next) + return; + if ((m = dirtomon(arg->i)) == selmon) + return; + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); +} + +void +focusstack(const Arg *arg) +{ + Client *c = NULL, *i; + + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + if (!c) + for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i)) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i)) + c = i; + } + if (c) { + focus(c); + restack(selmon); + } +} + +Atom +getatomprop(Client *c, Atom prop) +{ + int di; + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } + return atom; +} + +int +getrootptr(int *x, int *y) +{ + int di; + unsigned int dui; + Window dummy; + + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); +} + +long +getstate(Window w) +{ + int format; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if (n != 0) + result = *p; + XFree(p); + return result; +} + +int +gettextprop(Window w, Atom atom, char *text, unsigned int size) +{ + char **list = NULL; + int n; + XTextProperty name; + + if (!text || size == 0) + return 0; + text[0] = '\0'; + if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) + return 0; + if (name.encoding == XA_STRING) { + strncpy(text, (char *)name.value, size - 1); + } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + text[size - 1] = '\0'; + XFree(name.value); + return 1; +} + +void +grabbuttons(Client *c, int focused) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + if (!focused) + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, + BUTTONMASK, GrabModeSync, GrabModeSync, None, None); + for (i = 0; i < LENGTH(buttons); i++) + if (buttons[i].click == ClkClientWin) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, + buttons[i].mask | modifiers[j], + c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +void +grabkeys(void) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + KeyCode code; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < LENGTH(keys); i++) + if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, + True, GrabModeAsync, GrabModeAsync); + } +} + +void +incnmaster(const Arg *arg) +{ + selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); +} + +#ifdef XINERAMA +static int +isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +{ + while (n--) + if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org + && unique[n].width == info->width && unique[n].height == info->height) + return 0; + return 1; +} +#endif /* XINERAMA */ + +void +keypress(XEvent *e) +{ + unsigned int i; + KeySym keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for (i = 0; i < LENGTH(keys); i++) + if (keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); +} + +void +killclient(const Arg *arg) +{ + if (!selmon->sel) + return; + if (!sendevent(selmon->sel, wmatom[WMDelete])) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, selmon->sel->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } +} + +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, *t = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + } else { + c->mon = selmon; + applyrules(c); + } + + if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) + c->x = c->mon->wx + c->mon->ww - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) + c->y = c->mon->wy + c->mon->wh - HEIGHT(c); + c->x = MAX(c->x, c->mon->wx); + c->y = MAX(c->y, c->mon->wy); + c->bw = borderpx; + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); + attach(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); + focus(NULL); +} + +void +mappingnotify(XEvent *e) +{ + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if (ev->request == MappingKeyboard) + grabkeys(); +} + +void +maprequest(XEvent *e) +{ + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) + return; + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +void +monocle(Monitor *m) +{ + unsigned int n = 0; + Client *c; + + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); +} + +void +motionnotify(XEvent *e) +{ + static Monitor *mon = NULL; + Monitor *m; + XMotionEvent *ev = &e->xmotion; + + if (ev->window != root) + return; + if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + mon = m; +} + +void +movemouse(const Arg *arg) +{ + int x, y, ocx, ocy, nx, ny; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + if (!getrootptr(&x, &y)) + return; + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + if (abs(selmon->wx - nx) < snap) + nx = selmon->wx; + else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) + nx = selmon->wx + selmon->ww - WIDTH(c); + if (abs(selmon->wy - ny) < snap) + ny = selmon->wy; + else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) + ny = selmon->wy + selmon->wh - HEIGHT(c); + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) + togglefloating(NULL); + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, nx, ny, c->w, c->h, 1); + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +Client * +nexttiled(Client *c) +{ + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + return c; +} + +void +pop(Client *c) +{ + detach(c); + attach(c); + focus(c); + arrange(c->mon); +} + +void +propertynotify(XEvent *e) +{ + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != NULL)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; + break; + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); + } +} + +void +quit(const Arg *arg) +{ + running = 0; +} + +Monitor * +recttomon(int x, int y, int w, int h) +{ + Monitor *m, *r = selmon; + int a, area = 0; + + for (m = mons; m; m = m->next) + if ((a = INTERSECT(x, y, w, h, m)) > area) { + area = a; + r = m; + } + return r; +} + +void +resize(Client *c, int x, int y, int w, int h, int interact) +{ + if (applysizehints(c, &x, &y, &w, &h, interact)) + resizeclient(c, x, y, w, h); +} + +void +resizeclient(Client *c, int x, int y, int w, int h) +{ + XWindowChanges wc; + + c->oldx = c->x; c->x = wc.x = x; + c->oldy = c->y; c->y = wc.y = y; + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void +resizemouse(const Arg *arg) +{ + int ocx, ocy, nw, nh; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + break; + } + } while (ev.type != ButtonRelease); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +void +restack(Monitor *m) +{ + Client *c; + XEvent ev; + XWindowChanges wc; + + drawbar(m); + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +run(void) +{ + XEvent ev; + /* main event loop */ + XSync(dpy, False); + while (running && !XNextEvent(dpy, &ev)) + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ +} + +void +scan(void) +{ + unsigned int i, num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (i = 0; i < num; i++) { + if (!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if (XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + if (wins) + XFree(wins); + } +} + +void +sendmon(Client *c, Monitor *m) +{ + if (c->mon == m) + return; + unfocus(c, 1); + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attach(c); + attachstack(c); + focus(NULL); + arrange(NULL); +} + +void +setclientstate(Client *c, long state) +{ + long data[] = { state, None }; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +int +sendevent(Client *c, Atom proto) +{ + int n; + Atom *protocols; + int exists = 0; + XEvent ev; + + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + if (exists) { + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = proto; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); + } + return exists; +} + +void +setfocus(Client *c) +{ + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } + sendevent(c, wmatom[WMTakeFocus]); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = 1; + c->oldstate = c->isfloating; + c->oldbw = c->bw; + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = 0; + c->isfloating = c->oldstate; + c->bw = c->oldbw; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + } +} + +void +setlayout(const Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt ^= 1; + if (arg && arg->v) + selmon->lt[selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); + else + drawbar(selmon); +} + +/* arg > 1.0 will set mfact absolutely */ +void +setmfact(const Arg *arg) +{ + float f; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; + selmon->mfact = f; + arrange(selmon); +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + Atom utf8string; + + /* clean up any zombies immediately */ + sigchld(0); + + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ + updatebars(); + updatestatus(); + /* supporting window for NetWMCheck */ + wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, + PropModeReplace, (unsigned char *) "dwm", 3); + XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + /* EWMH support per view */ + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + XDeleteProperty(dpy, root, netatom[NetClientList]); + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); + focus(NULL); +} + +void +seturgent(Client *c, int urg) +{ + XWMHints *wmh; + + c->isurgent = urg; + if (!(wmh = XGetWMHints(dpy, c->win))) + return; + wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); + XSetWMHints(dpy, c->win, wmh); + XFree(wmh); +} + +void +showhide(Client *c) +{ + if (!c) + return; + if (ISVISIBLE(c)) { + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); + if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + } +} + +void +sigchld(int unused) +{ + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("can't install SIGCHLD handler:"); + while (0 < waitpid(-1, NULL, WNOHANG)); +} + +void +spawn(const Arg *arg) +{ + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); + } +} + +void +tag(const Arg *arg) +{ + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); + } +} + +void +tagmon(const Arg *arg) +{ + if (!selmon->sel || !mons->next) + return; + sendmon(selmon->sel, dirtomon(arg->i)); +} + +void +tile(Monitor *m) +{ + unsigned int i, n, h, mw, my, ty; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + if (n > m->nmaster) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); + } else { + h = (m->wh - ty) / (n - i); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); + } +} + +void +togglebar(const Arg *arg) +{ + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +} + +void +togglefloating(const Arg *arg) +{ + if (!selmon->sel) + return; + if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ + return; + selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; + if (selmon->sel->isfloating) + resize(selmon->sel, selmon->sel->x, selmon->sel->y, + selmon->sel->w, selmon->sel->h, 0); + arrange(selmon); +} + +void +togglesticky(const Arg *arg) +{ + if (!selmon->sel) + return; + selmon->sel->issticky = !selmon->sel->issticky; + arrange(selmon); +} + +void +toggletag(const Arg *arg) +{ + unsigned int newtags; + + if (!selmon->sel) + return; + newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + selmon->sel->tags = newtags; + focus(NULL); + arrange(selmon); + } +} + +void +toggleview(const Arg *arg) +{ + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); + } +} + +void +unfocus(Client *c, int setfocus) +{ + if (!c) + return; + grabbuttons(c, 0); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } +} + +void +unmanage(Client *c, int destroyed) +{ + Monitor *m = c->mon; + XWindowChanges wc; + + detach(c); + detachstack(c); + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XSelectInput(dpy, c->win, NoEventMask); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + free(c); + focus(NULL); + updateclientlist(); + arrange(m); +} + +void +unmapnotify(XEvent *e) +{ + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if ((c = wintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); + } +} + +void +updatebars(void) +{ + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixmap = ParentRelative, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +} + +void +updatebarpos(Monitor *m) +{ + m->wy = m->my; + m->wh = m->mh; + if (m->showbar) { + m->wh -= bh; + m->by = m->topbar ? m->wy : m->wy + m->wh; + m->wy = m->topbar ? m->wy + bh : m->wy; + } else + m->by = -bh; +} + +void +updateclientlist() +{ + Client *c; + Monitor *m; + + XDeleteProperty(dpy, root, netatom[NetClientList]); + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +} + +int +updategeom(void) +{ + int dirty = 0; + +#ifdef XINERAMA + if (XineramaIsActive(dpy)) { + int i, j, n, nn; + Client *c; + Monitor *m; + XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); + XineramaScreenInfo *unique = NULL; + + for (n = 0, m = mons; m; m = m->next, n++); + /* only consider unique geometries as separate screens */ + unique = ecalloc(nn, sizeof(XineramaScreenInfo)); + for (i = 0, j = 0; i < nn; i++) + if (isuniquegeom(unique, j, &info[i])) + memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); + XFree(info); + nn = j; + + /* new monitors if nn > n */ + for (i = n; i < nn; i++) { + for (m = mons; m && m->next; m = m->next); + if (m) + m->next = createmon(); + else + mons = createmon(); + } + for (i = 0, m = mons; i < nn && m; m = m->next, i++) + if (i >= n + || unique[i].x_org != m->mx || unique[i].y_org != m->my + || unique[i].width != m->mw || unique[i].height != m->mh) + { + dirty = 1; + m->num = i; + m->mx = m->wx = unique[i].x_org; + m->my = m->wy = unique[i].y_org; + m->mw = m->ww = unique[i].width; + m->mh = m->wh = unique[i].height; + updatebarpos(m); + } + /* removed monitors if n > nn */ + for (i = nn; i < n; i++) { + for (m = mons; m && m->next; m = m->next); + while ((c = m->clients)) { + dirty = 1; + m->clients = c->next; + detachstack(c); + c->mon = mons; + attach(c); + attachstack(c); + } + if (m == selmon) + selmon = mons; + cleanupmon(m); + } + free(unique); + } else +#endif /* XINERAMA */ + { /* default monitor setup */ + if (!mons) + mons = createmon(); + if (mons->mw != sw || mons->mh != sh) { + dirty = 1; + mons->mw = mons->ww = sw; + mons->mh = mons->wh = sh; + updatebarpos(mons); + } + } + if (dirty) { + selmon = mons; + selmon = wintomon(root); + } + return dirty; +} + +void +updatenumlockmask(void) +{ + unsigned int i, j; + XModifierKeymap *modmap; + + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) + for (j = 0; j < modmap->max_keypermod; j++) + if (modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + XFreeModifiermap(modmap); +} + +void +updatesizehints(Client *c) +{ + long msize; + XSizeHints size; + + if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) + /* size is uninitialized, ensure that size.flags aren't used */ + size.flags = PSize; + if (size.flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else if (size.flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } else + c->basew = c->baseh = 0; + if (size.flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else + c->incw = c->inch = 0; + if (size.flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else + c->maxw = c->maxh = 0; + if (size.flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else if (size.flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } else + c->minw = c->minh = 0; + if (size.flags & PAspect) { + c->mina = (float)size.min_aspect.y / size.min_aspect.x; + c->maxa = (float)size.max_aspect.x / size.max_aspect.y; + } else + c->maxa = c->mina = 0.0; + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + c->hintsvalid = 1; +} + +void +updatestatus(void) +{ + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); +} + +void +updatetitle(Client *c) +{ + if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); + if (c->name[0] == '\0') /* hack to mark broken clients */ + strcpy(c->name, broken); +} + +void +updatewindowtype(Client *c) +{ + Atom state = getatomprop(c, netatom[NetWMState]); + Atom wtype = getatomprop(c, netatom[NetWMWindowType]); + + if (state == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + if (wtype == netatom[NetWMWindowTypeDialog]) + c->isfloating = 1; +} + +void +updatewmhints(Client *c) +{ + XWMHints *wmh; + + if ((wmh = XGetWMHints(dpy, c->win))) { + if (c == selmon->sel && wmh->flags & XUrgencyHint) { + wmh->flags &= ~XUrgencyHint; + XSetWMHints(dpy, c->win, wmh); + } else + c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; + if (wmh->flags & InputHint) + c->neverfocus = !wmh->input; + else + c->neverfocus = 0; + XFree(wmh); + } +} + +void +view(const Arg *arg) +{ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); +} + +Client * +wintoclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + if (c->win == w) + return c; + return NULL; +} + +Monitor * +wintomon(Window w) +{ + int x, y; + Client *c; + Monitor *m; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + if (w == m->barwin) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +int +xerror(Display *dpy, XErrorEvent *ee) +{ + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +xerrordummy(Display *dpy, XErrorEvent *ee) +{ + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +int +xerrorstart(Display *dpy, XErrorEvent *ee) +{ + die("dwm: another window manager is already running"); + return -1; +} + +void +zoom(const Arg *arg) +{ + Client *c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) + return; + if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) + return; + pop(c); +} + +int +main(int argc, char *argv[]) +{ + if (argc == 2 && !strcmp("-v", argv[1])) + die("dwm-"VERSION); + else if (argc != 1) + die("usage: dwm [-v]"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + checkotherwm(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + run(); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} diff --git a/bdwm/dwm.c.orig b/bdwm/dwm.c.orig new file mode 100755 index 0000000..5410104 --- /dev/null +++ b/bdwm/dwm.c.orig @@ -0,0 +1,2157 @@ +/* See LICENSE file for copyright and license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * The event handlers of dwm are organized in an array which is accessed + * whenever a new event has been fetched. This allows event dispatching + * in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a linked client + * list on each monitor, the focus history is remembered through a stack list + * on each monitor. Each client contains a bit array to indicate the tags of a + * client. + * + * Keys and tagging rules are organized as arrays and defined in config.h. + * + * To understand everything else, start reading main(). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define WIDTH(X) ((X)->w + 2 * (X)->bw) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define TAGMASK ((1 << LENGTH(tags)) - 1) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +/* enums */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { SchemeNorm, SchemeSel }; /* color schemes */ +enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + +typedef struct Monitor Monitor; +typedef struct Client Client; +struct Client { + char name[256]; + float mina, maxa; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, issticky; + Client *next; + Client *snext; + Monitor *mon; + Window win; +}; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +} Layout; + +struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + int showbar; + int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Window barwin; + const Layout *lt[2]; +}; + +typedef struct { + const char *class; + const char *instance; + const char *title; + unsigned int tags; + int isfloating; + int monitor; +} Rule; + +/* function declarations */ +static void applyrules(Client *c); +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +static void arrange(Monitor *m); +static void arrangemon(Monitor *m); +static void attach(Client *c); +static void attachstack(Client *c); +static void buttonpress(XEvent *e); +static void checkotherwm(void); +static void cleanup(void); +static void cleanupmon(Monitor *mon); +static void clientmessage(XEvent *e); +static void configure(Client *c); +static void configurenotify(XEvent *e); +static void configurerequest(XEvent *e); +static Monitor *createmon(void); +static void destroynotify(XEvent *e); +static void detach(Client *c); +static void detachstack(Client *c); +static Monitor *dirtomon(int dir); +static void drawbar(Monitor *m); +static void drawbars(void); +static void enternotify(XEvent *e); +static void expose(XEvent *e); +static void focus(Client *c); +static void focusin(XEvent *e); +static void focusmon(const Arg *arg); +static void focusstack(const Arg *arg); +static Atom getatomprop(Client *c, Atom prop); +static int getrootptr(int *x, int *y); +static long getstate(Window w); +static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void grabbuttons(Client *c, int focused); +static void grabkeys(void); +static void incnmaster(const Arg *arg); +static void keypress(XEvent *e); +static void killclient(const Arg *arg); +static void manage(Window w, XWindowAttributes *wa); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void monocle(Monitor *m); +static void motionnotify(XEvent *e); +static void movemouse(const Arg *arg); +static Client *nexttiled(Client *c); +static void pop(Client *c); +static void propertynotify(XEvent *e); +static void quit(const Arg *arg); +static Monitor *recttomon(int x, int y, int w, int h); +static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resizeclient(Client *c, int x, int y, int w, int h); +static void resizemouse(const Arg *arg); +static void restack(Monitor *m); +static void run(void); +static void scan(void); +static int sendevent(Client *c, Atom proto); +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setlayout(const Arg *arg); +static void setmfact(const Arg *arg); +static void setup(void); +static void seturgent(Client *c, int urg); +static void showhide(Client *c); +static void sigchld(int unused); +static void spawn(const Arg *arg); +static void tag(const Arg *arg); +static void tagmon(const Arg *arg); +static void tile(Monitor *m); +static void togglebar(const Arg *arg); +static void togglefloating(const Arg *arg); +static void togglesticky(const Arg *arg); +static void toggletag(const Arg *arg); +static void toggleview(const Arg *arg); +static void unfocus(Client *c, int setfocus); +static void unmanage(Client *c, int destroyed); +static void unmapnotify(XEvent *e); +static void updatebarpos(Monitor *m); +static void updatebars(void); +static void updateclientlist(void); +static int updategeom(void); +static void updatenumlockmask(void); +static void updatesizehints(Client *c); +static void updatestatus(void); +static void updatetitle(Client *c); +static void updatewindowtype(Client *c); +static void updatewmhints(Client *c); +static void view(const Arg *arg); +static Client *wintoclient(Window w); +static Monitor *wintomon(Window w); +static int xerror(Display *dpy, XErrorEvent *ee); +static int xerrordummy(Display *dpy, XErrorEvent *ee); +static int xerrorstart(Display *dpy, XErrorEvent *ee); +static void zoom(const Arg *arg); + +/* variables */ +static const char broken[] = "broken"; +static char stext[256]; +static int screen; +static int sw, sh; /* X display screen geometry width, height */ +static int bh; /* bar height */ +static int lrpad; /* sum of left and right padding for text */ +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int numlockmask = 0; +static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast]; +static int running = 1; +static Cur *cursor[CurLast]; +static Clr **scheme; +static Display *dpy; +static Drw *drw; +static Monitor *mons, *selmon; +static Window root, wmcheckwin; + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +/* function implementations */ +void +applyrules(Client *c) +{ + const char *class, *instance; + unsigned int i; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + c->isfloating = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; + } + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; +} + +int +applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) +{ + int baseismin; + Monitor *m = c->mon; + + /* set minimum possible */ + *w = MAX(1, *w); + *h = MAX(1, *h); + if (interact) { + if (*x > sw) + *x = sw - WIDTH(c); + if (*y > sh) + *y = sh - HEIGHT(c); + if (*x + *w + 2 * c->bw < 0) + *x = 0; + if (*y + *h + 2 * c->bw < 0) + *y = 0; + } else { + if (*x >= m->wx + m->ww) + *x = m->wx + m->ww - WIDTH(c); + if (*y >= m->wy + m->wh) + *y = m->wy + m->wh - HEIGHT(c); + if (*x + *w + 2 * c->bw <= m->wx) + *x = m->wx; + if (*y + *h + 2 * c->bw <= m->wy) + *y = m->wy; + } + if (*h < bh) + *h = bh; + if (*w < bh) + *w = bh; + if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + if (!c->hintsvalid) + updatesizehints(c); + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for aspect limits */ + if (c->mina > 0 && c->maxa > 0) { + if (c->maxa < (float)*w / *h) + *w = *h * c->maxa + 0.5; + else if (c->mina < (float)*h / *w) + *h = *w * c->mina + 0.5; + } + if (baseismin) { /* increment calculation requires this */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for increment value */ + if (c->incw) + *w -= *w % c->incw; + if (c->inch) + *h -= *h % c->inch; + /* restore base dimensions */ + *w = MAX(*w + c->basew, c->minw); + *h = MAX(*h + c->baseh, c->minh); + if (c->maxw) + *w = MIN(*w, c->maxw); + if (c->maxh) + *h = MIN(*h, c->maxh); + } + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; +} + +void +arrange(Monitor *m) +{ + if (m) + showhide(m->stack); + else for (m = mons; m; m = m->next) + showhide(m->stack); + if (m) { + arrangemon(m); + restack(m); + } else for (m = mons; m; m = m->next) + arrangemon(m); +} + +void +arrangemon(Monitor *m) +{ + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +} + +void +attach(Client *c) +{ + c->next = c->mon->clients; + c->mon->clients = c; +} + +void +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +buttonpress(XEvent *e) +{ + unsigned int i, x, click; + Arg arg = {0}; + Client *c; + Monitor *m; + XButtonPressedEvent *ev = &e->xbutton; + + click = ClkRootWin; + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + if (ev->window == selmon->barwin) { + i = x = 0; + do + x += TEXTW(tags[i]); + while (ev->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(selmon->ltsymbol)) + click = ClkLtSymbol; + else if (ev->x > selmon->ww - (int)TEXTW(stext)) + click = ClkStatusText; + else + click = ClkWinTitle; + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } + for (i = 0; i < LENGTH(buttons); i++) + if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); +} + +void +checkotherwm(void) +{ + xerrorxlib = XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); + XSync(dpy, False); + XSetErrorHandler(xerror); + XSync(dpy, False); +} + +void +cleanup(void) +{ + Arg a = {.ui = ~0}; + Layout foo = { "", NULL }; + Monitor *m; + size_t i; + + view(&a); + selmon->lt[selmon->sellt] = &foo; + for (m = mons; m; m = m->next) + while (m->stack) + unmanage(m->stack, 0); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) + free(scheme[i]); + free(scheme); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); + XSync(dpy, False); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +} + +void +cleanupmon(Monitor *mon) +{ + Monitor *m; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); + free(mon); +} + +void +clientmessage(XEvent *e) +{ + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] + || cme->data.l[2] == netatom[NetWMFullscreen]) + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ + || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); + } else if (cme->message_type == netatom[NetActiveWindow]) { + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); + } +} + +void +configure(Client *c) +{ + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->bw; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +void +configurenotify(XEvent *e) +{ + Monitor *m; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; + + /* TODO: updategeom handling sucks, needs to be simplified */ + if (ev->window == root) { + dirty = (sw != ev->width || sh != ev->height); + sw = ev->width; + sh = ev->height; + if (updategeom() || dirty) { + drw_resize(drw, sw, bh); + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } + focus(NULL); + arrange(NULL); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +Monitor * +createmon(void) +{ + Monitor *m; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); +} + +void +detach(Client *c) +{ + Client **tc; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; +} + +void +detachstack(Client *c) +{ + Client **tc, *t; + + for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); + *tc = c->snext; + + if (c == c->mon->sel) { + for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); + c->mon->sel = t; + } +} + +Monitor * +dirtomon(int dir) +{ + Monitor *m = NULL; + + if (dir > 0) { + if (!(m = selmon->next)) + m = mons; + } else if (selmon == mons) + for (m = mons; m->next; m = m->next); + else + for (m = mons; m->next != selmon; m = m->next); + return m; +} + +void +drawbar(Monitor *m) +{ + int x, w, tw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; + + if (!m->showbar) + return; + + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ + drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); + } + + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, + urg & 1 << i); + x += w; + } + w = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +} + +void +drawbars(void) +{ + Monitor *m; + + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +enternotify(XEvent *e) +{ + Client *c; + Monitor *m; + XCrossingEvent *ev = &e->xcrossing; + + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) + return; + c = wintoclient(ev->window); + m = c ? c->mon : wintomon(ev->window); + if (m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + } else if (!c || c == selmon->sel) + return; + focus(c); +} + +void +expose(XEvent *e) +{ + Monitor *m; + XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && (m = wintomon(ev->window))) + drawbar(m); +} + +void +focus(Client *c) +{ + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; + drawbars(); +} + +/* there are some broken focus acquiring clients needing extra handling */ +void +focusin(XEvent *e) +{ + XFocusChangeEvent *ev = &e->xfocus; + + if (selmon->sel && ev->window != selmon->sel->win) + setfocus(selmon->sel); +} + +void +focusmon(const Arg *arg) +{ + Monitor *m; + + if (!mons->next) + return; + if ((m = dirtomon(arg->i)) == selmon) + return; + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); +} + +void +focusstack(const Arg *arg) +{ + Client *c = NULL, *i; + + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + if (!c) + for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i)) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i)) + c = i; + } + if (c) { + focus(c); + restack(selmon); + } +} + +Atom +getatomprop(Client *c, Atom prop) +{ + int di; + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } + return atom; +} + +int +getrootptr(int *x, int *y) +{ + int di; + unsigned int dui; + Window dummy; + + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); +} + +long +getstate(Window w) +{ + int format; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if (n != 0) + result = *p; + XFree(p); + return result; +} + +int +gettextprop(Window w, Atom atom, char *text, unsigned int size) +{ + char **list = NULL; + int n; + XTextProperty name; + + if (!text || size == 0) + return 0; + text[0] = '\0'; + if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) + return 0; + if (name.encoding == XA_STRING) { + strncpy(text, (char *)name.value, size - 1); + } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + text[size - 1] = '\0'; + XFree(name.value); + return 1; +} + +void +grabbuttons(Client *c, int focused) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + if (!focused) + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, + BUTTONMASK, GrabModeSync, GrabModeSync, None, None); + for (i = 0; i < LENGTH(buttons); i++) + if (buttons[i].click == ClkClientWin) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, + buttons[i].mask | modifiers[j], + c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +void +grabkeys(void) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + KeyCode code; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < LENGTH(keys); i++) + if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, + True, GrabModeAsync, GrabModeAsync); + } +} + +void +incnmaster(const Arg *arg) +{ + selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); +} + +#ifdef XINERAMA +static int +isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +{ + while (n--) + if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org + && unique[n].width == info->width && unique[n].height == info->height) + return 0; + return 1; +} +#endif /* XINERAMA */ + +void +keypress(XEvent *e) +{ + unsigned int i; + KeySym keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for (i = 0; i < LENGTH(keys); i++) + if (keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); +} + +void +killclient(const Arg *arg) +{ + if (!selmon->sel) + return; + if (!sendevent(selmon->sel, wmatom[WMDelete])) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, selmon->sel->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } +} + +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, *t = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + } else { + c->mon = selmon; + applyrules(c); + } + + if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) + c->x = c->mon->wx + c->mon->ww - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) + c->y = c->mon->wy + c->mon->wh - HEIGHT(c); + c->x = MAX(c->x, c->mon->wx); + c->y = MAX(c->y, c->mon->wy); + c->bw = borderpx; + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); + attach(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); + focus(NULL); +} + +void +mappingnotify(XEvent *e) +{ + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if (ev->request == MappingKeyboard) + grabkeys(); +} + +void +maprequest(XEvent *e) +{ + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) + return; + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +void +monocle(Monitor *m) +{ + unsigned int n = 0; + Client *c; + + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); +} + +void +motionnotify(XEvent *e) +{ + static Monitor *mon = NULL; + Monitor *m; + XMotionEvent *ev = &e->xmotion; + + if (ev->window != root) + return; + if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + mon = m; +} + +void +movemouse(const Arg *arg) +{ + int x, y, ocx, ocy, nx, ny; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + if (!getrootptr(&x, &y)) + return; + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + if (abs(selmon->wx - nx) < snap) + nx = selmon->wx; + else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) + nx = selmon->wx + selmon->ww - WIDTH(c); + if (abs(selmon->wy - ny) < snap) + ny = selmon->wy; + else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) + ny = selmon->wy + selmon->wh - HEIGHT(c); + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) + togglefloating(NULL); + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, nx, ny, c->w, c->h, 1); + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +Client * +nexttiled(Client *c) +{ + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + return c; +} + +void +pop(Client *c) +{ + detach(c); + attach(c); + focus(c); + arrange(c->mon); +} + +void +propertynotify(XEvent *e) +{ + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != NULL)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; + break; + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); + } +} + +void +quit(const Arg *arg) +{ + running = 0; +} + +Monitor * +recttomon(int x, int y, int w, int h) +{ + Monitor *m, *r = selmon; + int a, area = 0; + + for (m = mons; m; m = m->next) + if ((a = INTERSECT(x, y, w, h, m)) > area) { + area = a; + r = m; + } + return r; +} + +void +resize(Client *c, int x, int y, int w, int h, int interact) +{ + if (applysizehints(c, &x, &y, &w, &h, interact)) + resizeclient(c, x, y, w, h); +} + +void +resizeclient(Client *c, int x, int y, int w, int h) +{ + XWindowChanges wc; + + c->oldx = c->x; c->x = wc.x = x; + c->oldy = c->y; c->y = wc.y = y; + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void +resizemouse(const Arg *arg) +{ + int ocx, ocy, nw, nh; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + break; + } + } while (ev.type != ButtonRelease); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +void +restack(Monitor *m) +{ + Client *c; + XEvent ev; + XWindowChanges wc; + + drawbar(m); + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +run(void) +{ + XEvent ev; + /* main event loop */ + XSync(dpy, False); + while (running && !XNextEvent(dpy, &ev)) + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ +} + +void +scan(void) +{ + unsigned int i, num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (i = 0; i < num; i++) { + if (!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if (XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + if (wins) + XFree(wins); + } +} + +void +sendmon(Client *c, Monitor *m) +{ + if (c->mon == m) + return; + unfocus(c, 1); + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attach(c); + attachstack(c); + focus(NULL); + arrange(NULL); +} + +void +setclientstate(Client *c, long state) +{ + long data[] = { state, None }; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +int +sendevent(Client *c, Atom proto) +{ + int n; + Atom *protocols; + int exists = 0; + XEvent ev; + + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + if (exists) { + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = proto; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); + } + return exists; +} + +void +setfocus(Client *c) +{ + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } + sendevent(c, wmatom[WMTakeFocus]); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = 1; + c->oldstate = c->isfloating; + c->oldbw = c->bw; + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = 0; + c->isfloating = c->oldstate; + c->bw = c->oldbw; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + } +} + +void +setlayout(const Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt ^= 1; + if (arg && arg->v) + selmon->lt[selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); + else + drawbar(selmon); +} + +/* arg > 1.0 will set mfact absolutely */ +void +setmfact(const Arg *arg) +{ + float f; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; + selmon->mfact = f; + arrange(selmon); +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + Atom utf8string; + + /* clean up any zombies immediately */ + sigchld(0); + + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ + updatebars(); + updatestatus(); + /* supporting window for NetWMCheck */ + wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, + PropModeReplace, (unsigned char *) "dwm", 3); + XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + /* EWMH support per view */ + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + XDeleteProperty(dpy, root, netatom[NetClientList]); + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); + focus(NULL); +} + +void +seturgent(Client *c, int urg) +{ + XWMHints *wmh; + + c->isurgent = urg; + if (!(wmh = XGetWMHints(dpy, c->win))) + return; + wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); + XSetWMHints(dpy, c->win, wmh); + XFree(wmh); +} + +void +showhide(Client *c) +{ + if (!c) + return; + if (ISVISIBLE(c)) { + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); + if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + } +} + +void +sigchld(int unused) +{ + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("can't install SIGCHLD handler:"); + while (0 < waitpid(-1, NULL, WNOHANG)); +} + +void +spawn(const Arg *arg) +{ + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); + } +} + +void +tag(const Arg *arg) +{ + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); + } +} + +void +tagmon(const Arg *arg) +{ + if (!selmon->sel || !mons->next) + return; + sendmon(selmon->sel, dirtomon(arg->i)); +} + +void +tile(Monitor *m) +{ + unsigned int i, n, h, mw, my, ty; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + if (n > m->nmaster) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); + } else { + h = (m->wh - ty) / (n - i); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); + } +} + +void +togglebar(const Arg *arg) +{ + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +} + +void +togglefloating(const Arg *arg) +{ + if (!selmon->sel) + return; + if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ + return; + selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; + if (selmon->sel->isfloating) + resize(selmon->sel, selmon->sel->x, selmon->sel->y, + selmon->sel->w, selmon->sel->h, 0); + arrange(selmon); +} + +void +togglesticky(const Arg *arg) +{ + if (!selmon->sel) + return; + selmon->sel->issticky = !selmon->sel->issticky; + arrange(selmon); +} + +void +toggletag(const Arg *arg) +{ + unsigned int newtags; + + if (!selmon->sel) + return; + newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + selmon->sel->tags = newtags; + focus(NULL); + arrange(selmon); + } +} + +void +toggleview(const Arg *arg) +{ + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); + } +} + +void +unfocus(Client *c, int setfocus) +{ + if (!c) + return; + grabbuttons(c, 0); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } +} + +void +unmanage(Client *c, int destroyed) +{ + Monitor *m = c->mon; + XWindowChanges wc; + + detach(c); + detachstack(c); + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XSelectInput(dpy, c->win, NoEventMask); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + free(c); + focus(NULL); + updateclientlist(); + arrange(m); +} + +void +unmapnotify(XEvent *e) +{ + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if ((c = wintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); + } +} + +void +updatebars(void) +{ + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixmap = ParentRelative, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +} + +void +updatebarpos(Monitor *m) +{ + m->wy = m->my; + m->wh = m->mh; + if (m->showbar) { + m->wh -= bh; + m->by = m->topbar ? m->wy : m->wy + m->wh; + m->wy = m->topbar ? m->wy + bh : m->wy; + } else + m->by = -bh; +} + +void +updateclientlist() +{ + Client *c; + Monitor *m; + + XDeleteProperty(dpy, root, netatom[NetClientList]); + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +} + +int +updategeom(void) +{ + int dirty = 0; + +#ifdef XINERAMA + if (XineramaIsActive(dpy)) { + int i, j, n, nn; + Client *c; + Monitor *m; + XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); + XineramaScreenInfo *unique = NULL; + + for (n = 0, m = mons; m; m = m->next, n++); + /* only consider unique geometries as separate screens */ + unique = ecalloc(nn, sizeof(XineramaScreenInfo)); + for (i = 0, j = 0; i < nn; i++) + if (isuniquegeom(unique, j, &info[i])) + memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); + XFree(info); + nn = j; + + /* new monitors if nn > n */ + for (i = n; i < nn; i++) { + for (m = mons; m && m->next; m = m->next); + if (m) + m->next = createmon(); + else + mons = createmon(); + } + for (i = 0, m = mons; i < nn && m; m = m->next, i++) + if (i >= n + || unique[i].x_org != m->mx || unique[i].y_org != m->my + || unique[i].width != m->mw || unique[i].height != m->mh) + { + dirty = 1; + m->num = i; + m->mx = m->wx = unique[i].x_org; + m->my = m->wy = unique[i].y_org; + m->mw = m->ww = unique[i].width; + m->mh = m->wh = unique[i].height; + updatebarpos(m); + } + /* removed monitors if n > nn */ + for (i = nn; i < n; i++) { + for (m = mons; m && m->next; m = m->next); + while ((c = m->clients)) { + dirty = 1; + m->clients = c->next; + detachstack(c); + c->mon = mons; + attach(c); + attachstack(c); + } + if (m == selmon) + selmon = mons; + cleanupmon(m); + } + free(unique); + } else +#endif /* XINERAMA */ + { /* default monitor setup */ + if (!mons) + mons = createmon(); + if (mons->mw != sw || mons->mh != sh) { + dirty = 1; + mons->mw = mons->ww = sw; + mons->mh = mons->wh = sh; + updatebarpos(mons); + } + } + if (dirty) { + selmon = mons; + selmon = wintomon(root); + } + return dirty; +} + +void +updatenumlockmask(void) +{ + unsigned int i, j; + XModifierKeymap *modmap; + + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) + for (j = 0; j < modmap->max_keypermod; j++) + if (modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + XFreeModifiermap(modmap); +} + +void +updatesizehints(Client *c) +{ + long msize; + XSizeHints size; + + if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) + /* size is uninitialized, ensure that size.flags aren't used */ + size.flags = PSize; + if (size.flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else if (size.flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } else + c->basew = c->baseh = 0; + if (size.flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else + c->incw = c->inch = 0; + if (size.flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else + c->maxw = c->maxh = 0; + if (size.flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else if (size.flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } else + c->minw = c->minh = 0; + if (size.flags & PAspect) { + c->mina = (float)size.min_aspect.y / size.min_aspect.x; + c->maxa = (float)size.max_aspect.x / size.max_aspect.y; + } else + c->maxa = c->mina = 0.0; + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + c->hintsvalid = 1; +} + +void +updatestatus(void) +{ + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); +} + +void +updatetitle(Client *c) +{ + if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); + if (c->name[0] == '\0') /* hack to mark broken clients */ + strcpy(c->name, broken); +} + +void +updatewindowtype(Client *c) +{ + Atom state = getatomprop(c, netatom[NetWMState]); + Atom wtype = getatomprop(c, netatom[NetWMWindowType]); + + if (state == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + if (wtype == netatom[NetWMWindowTypeDialog]) + c->isfloating = 1; +} + +void +updatewmhints(Client *c) +{ + XWMHints *wmh; + + if ((wmh = XGetWMHints(dpy, c->win))) { + if (c == selmon->sel && wmh->flags & XUrgencyHint) { + wmh->flags &= ~XUrgencyHint; + XSetWMHints(dpy, c->win, wmh); + } else + c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; + if (wmh->flags & InputHint) + c->neverfocus = !wmh->input; + else + c->neverfocus = 0; + XFree(wmh); + } +} + +void +view(const Arg *arg) +{ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); +} + +Client * +wintoclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + if (c->win == w) + return c; + return NULL; +} + +Monitor * +wintomon(Window w) +{ + int x, y; + Client *c; + Monitor *m; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + if (w == m->barwin) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +int +xerror(Display *dpy, XErrorEvent *ee) +{ + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +xerrordummy(Display *dpy, XErrorEvent *ee) +{ + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +int +xerrorstart(Display *dpy, XErrorEvent *ee) +{ + die("dwm: another window manager is already running"); + return -1; +} + +void +zoom(const Arg *arg) +{ + Client *c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) + return; + if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) + return; + pop(c); +} + +int +main(int argc, char *argv[]) +{ + if (argc == 2 && !strcmp("-v", argv[1])) + die("dwm-"VERSION); + else if (argc != 1) + die("usage: dwm [-v]"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + checkotherwm(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + run(); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} diff --git a/bdwm/dwm.c.rej b/bdwm/dwm.c.rej new file mode 100755 index 0000000..22ec0dc --- /dev/null +++ b/bdwm/dwm.c.rej @@ -0,0 +1,10 @@ +--- dwm.c ++++ dwm.c +@@ -93,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int issteam; + Client *next; + Client *snext; + Monitor *mon; diff --git a/bdwm/dwm.c~ b/bdwm/dwm.c~ new file mode 100755 index 0000000..5e64e4e --- /dev/null +++ b/bdwm/dwm.c~ @@ -0,0 +1,2162 @@ +/* See LICENSE file for copyright and license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * The event handlers of dwm are organized in an array which is accessed + * whenever a new event has been fetched. This allows event dispatching + * in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a linked client + * list on each monitor, the focus history is remembered through a stack list + * on each monitor. Each client contains a bit array to indicate the tags of a + * client. + * + * Keys and tagging rules are organized as arrays and defined in config.h. + * + * To understand everything else, start reading main(). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define WIDTH(X) ((X)->w + 2 * (X)->bw) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define TAGMASK ((1 << LENGTH(tags)) - 1) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +/* enums */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { SchemeNorm, SchemeSel }; /* color schemes */ +enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + +typedef struct Monitor Monitor; +typedef struct Client Client; +struct Client { + char name[256]; + float mina, maxa; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, issticky; + Client *next; + Client *snext; + Monitor *mon; + Window win; +}; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +} Layout; + +struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + int showbar; + int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Window barwin; + const Layout *lt[2]; +}; + +typedef struct { + const char *class; + const char *instance; + const char *title; + unsigned int tags; + int isfloating; + int monitor; +} Rule; + +/* function declarations */ +static void applyrules(Client *c); +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +static void arrange(Monitor *m); +static void arrangemon(Monitor *m); +static void attach(Client *c); +static void attachstack(Client *c); +static void buttonpress(XEvent *e); +static void checkotherwm(void); +static void cleanup(void); +static void cleanupmon(Monitor *mon); +static void clientmessage(XEvent *e); +static void configure(Client *c); +static void configurenotify(XEvent *e); +static void configurerequest(XEvent *e); +static Monitor *createmon(void); +static void destroynotify(XEvent *e); +static void detach(Client *c); +static void detachstack(Client *c); +static Monitor *dirtomon(int dir); +static void drawbar(Monitor *m); +static void drawbars(void); +static void enternotify(XEvent *e); +static void expose(XEvent *e); +static void focus(Client *c); +static void focusin(XEvent *e); +static void focusmon(const Arg *arg); +static void focusstack(const Arg *arg); +static Atom getatomprop(Client *c, Atom prop); +static int getrootptr(int *x, int *y); +static long getstate(Window w); +static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void grabbuttons(Client *c, int focused); +static void grabkeys(void); +static void incnmaster(const Arg *arg); +static void keypress(XEvent *e); +static void killclient(const Arg *arg); +static void manage(Window w, XWindowAttributes *wa); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void monocle(Monitor *m); +static void motionnotify(XEvent *e); +static void movemouse(const Arg *arg); +static Client *nexttiled(Client *c); +static void pop(Client *c); +static void propertynotify(XEvent *e); +static void quit(const Arg *arg); +static Monitor *recttomon(int x, int y, int w, int h); +static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resizeclient(Client *c, int x, int y, int w, int h); +static void resizemouse(const Arg *arg); +static void restack(Monitor *m); +static void run(void); +static void scan(void); +static int sendevent(Client *c, Atom proto); +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setlayout(const Arg *arg); +static void setmfact(const Arg *arg); +static void setup(void); +static void seturgent(Client *c, int urg); +static void showhide(Client *c); +static void sigchld(int unused); +static void spawn(const Arg *arg); +static void tag(const Arg *arg); +static void tagmon(const Arg *arg); +static void tile(Monitor *m); +static void togglebar(const Arg *arg); +static void togglefloating(const Arg *arg); +static void togglesticky(const Arg *arg); +static void toggletag(const Arg *arg); +static void toggleview(const Arg *arg); +static void unfocus(Client *c, int setfocus); +static void unmanage(Client *c, int destroyed); +static void unmapnotify(XEvent *e); +static void updatebarpos(Monitor *m); +static void updatebars(void); +static void updateclientlist(void); +static int updategeom(void); +static void updatenumlockmask(void); +static void updatesizehints(Client *c); +static void updatestatus(void); +static void updatetitle(Client *c); +static void updatewindowtype(Client *c); +static void updatewmhints(Client *c); +static void view(const Arg *arg); +static Client *wintoclient(Window w); +static Monitor *wintomon(Window w); +static int xerror(Display *dpy, XErrorEvent *ee); +static int xerrordummy(Display *dpy, XErrorEvent *ee); +static int xerrorstart(Display *dpy, XErrorEvent *ee); +static void zoom(const Arg *arg); + +/* variables */ +static const char broken[] = "broken"; +static char stext[256]; +static int screen; +static int sw, sh; /* X display screen geometry width, height */ +static int bh; /* bar height */ +static int lrpad; /* sum of left and right padding for text */ +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int numlockmask = 0; +static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast]; +static int running = 1; +static Cur *cursor[CurLast]; +static Clr **scheme; +static Display *dpy; +static Drw *drw; +static Monitor *mons, *selmon; +static Window root, wmcheckwin; + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +/* function implementations */ +void +applyrules(Client *c) +{ + const char *class, *instance; + unsigned int i; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + c->isfloating = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + + if (strstr(class, "Steam") || strstr(class, "steam_app_")) + c->issteam = 1; + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; + } + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; +} + +int +applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) +{ + int baseismin; + Monitor *m = c->mon; + + /* set minimum possible */ + *w = MAX(1, *w); + *h = MAX(1, *h); + if (interact) { + if (*x > sw) + *x = sw - WIDTH(c); + if (*y > sh) + *y = sh - HEIGHT(c); + if (*x + *w + 2 * c->bw < 0) + *x = 0; + if (*y + *h + 2 * c->bw < 0) + *y = 0; + } else { + if (*x >= m->wx + m->ww) + *x = m->wx + m->ww - WIDTH(c); + if (*y >= m->wy + m->wh) + *y = m->wy + m->wh - HEIGHT(c); + if (*x + *w + 2 * c->bw <= m->wx) + *x = m->wx; + if (*y + *h + 2 * c->bw <= m->wy) + *y = m->wy; + } + if (*h < bh) + *h = bh; + if (*w < bh) + *w = bh; + if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + if (!c->hintsvalid) + updatesizehints(c); + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for aspect limits */ + if (c->mina > 0 && c->maxa > 0) { + if (c->maxa < (float)*w / *h) + *w = *h * c->maxa + 0.5; + else if (c->mina < (float)*h / *w) + *h = *w * c->mina + 0.5; + } + if (baseismin) { /* increment calculation requires this */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for increment value */ + if (c->incw) + *w -= *w % c->incw; + if (c->inch) + *h -= *h % c->inch; + /* restore base dimensions */ + *w = MAX(*w + c->basew, c->minw); + *h = MAX(*h + c->baseh, c->minh); + if (c->maxw) + *w = MIN(*w, c->maxw); + if (c->maxh) + *h = MIN(*h, c->maxh); + } + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; +} + +void +arrange(Monitor *m) +{ + if (m) + showhide(m->stack); + else for (m = mons; m; m = m->next) + showhide(m->stack); + if (m) { + arrangemon(m); + restack(m); + } else for (m = mons; m; m = m->next) + arrangemon(m); +} + +void +arrangemon(Monitor *m) +{ + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +} + +void +attach(Client *c) +{ + c->next = c->mon->clients; + c->mon->clients = c; +} + +void +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +buttonpress(XEvent *e) +{ + unsigned int i, x, click; + Arg arg = {0}; + Client *c; + Monitor *m; + XButtonPressedEvent *ev = &e->xbutton; + + click = ClkRootWin; + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + if (ev->window == selmon->barwin) { + i = x = 0; + do + x += TEXTW(tags[i]); + while (ev->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(selmon->ltsymbol)) + click = ClkLtSymbol; + else if (ev->x > selmon->ww - (int)TEXTW(stext)) + click = ClkStatusText; + else + click = ClkWinTitle; + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } + for (i = 0; i < LENGTH(buttons); i++) + if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); +} + +void +checkotherwm(void) +{ + xerrorxlib = XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); + XSync(dpy, False); + XSetErrorHandler(xerror); + XSync(dpy, False); +} + +void +cleanup(void) +{ + Arg a = {.ui = ~0}; + Layout foo = { "", NULL }; + Monitor *m; + size_t i; + + view(&a); + selmon->lt[selmon->sellt] = &foo; + for (m = mons; m; m = m->next) + while (m->stack) + unmanage(m->stack, 0); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) + free(scheme[i]); + free(scheme); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); + XSync(dpy, False); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +} + +void +cleanupmon(Monitor *mon) +{ + Monitor *m; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); + free(mon); +} + +void +clientmessage(XEvent *e) +{ + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] + || cme->data.l[2] == netatom[NetWMFullscreen]) + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ + || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); + } else if (cme->message_type == netatom[NetActiveWindow]) { + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); + } +} + +void +configure(Client *c) +{ + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->bw; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +void +configurenotify(XEvent *e) +{ + Monitor *m; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; + + /* TODO: updategeom handling sucks, needs to be simplified */ + if (ev->window == root) { + dirty = (sw != ev->width || sh != ev->height); + sw = ev->width; + sh = ev->height; + if (updategeom() || dirty) { + drw_resize(drw, sw, bh); + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } + focus(NULL); + arrange(NULL); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + if (!c->issteam) { + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +Monitor * +createmon(void) +{ + Monitor *m; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); +} + +void +detach(Client *c) +{ + Client **tc; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; +} + +void +detachstack(Client *c) +{ + Client **tc, *t; + + for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); + *tc = c->snext; + + if (c == c->mon->sel) { + for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); + c->mon->sel = t; + } +} + +Monitor * +dirtomon(int dir) +{ + Monitor *m = NULL; + + if (dir > 0) { + if (!(m = selmon->next)) + m = mons; + } else if (selmon == mons) + for (m = mons; m->next; m = m->next); + else + for (m = mons; m->next != selmon; m = m->next); + return m; +} + +void +drawbar(Monitor *m) +{ + int x, w, tw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; + + if (!m->showbar) + return; + + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ + drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); + } + + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, + urg & 1 << i); + x += w; + } + w = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +} + +void +drawbars(void) +{ + Monitor *m; + + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +enternotify(XEvent *e) +{ + Client *c; + Monitor *m; + XCrossingEvent *ev = &e->xcrossing; + + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) + return; + c = wintoclient(ev->window); + m = c ? c->mon : wintomon(ev->window); + if (m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + } else if (!c || c == selmon->sel) + return; + focus(c); +} + +void +expose(XEvent *e) +{ + Monitor *m; + XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && (m = wintomon(ev->window))) + drawbar(m); +} + +void +focus(Client *c) +{ + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; + drawbars(); +} + +/* there are some broken focus acquiring clients needing extra handling */ +void +focusin(XEvent *e) +{ + XFocusChangeEvent *ev = &e->xfocus; + + if (selmon->sel && ev->window != selmon->sel->win) + setfocus(selmon->sel); +} + +void +focusmon(const Arg *arg) +{ + Monitor *m; + + if (!mons->next) + return; + if ((m = dirtomon(arg->i)) == selmon) + return; + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); +} + +void +focusstack(const Arg *arg) +{ + Client *c = NULL, *i; + + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + if (!c) + for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i)) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i)) + c = i; + } + if (c) { + focus(c); + restack(selmon); + } +} + +Atom +getatomprop(Client *c, Atom prop) +{ + int di; + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } + return atom; +} + +int +getrootptr(int *x, int *y) +{ + int di; + unsigned int dui; + Window dummy; + + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); +} + +long +getstate(Window w) +{ + int format; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if (n != 0) + result = *p; + XFree(p); + return result; +} + +int +gettextprop(Window w, Atom atom, char *text, unsigned int size) +{ + char **list = NULL; + int n; + XTextProperty name; + + if (!text || size == 0) + return 0; + text[0] = '\0'; + if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) + return 0; + if (name.encoding == XA_STRING) { + strncpy(text, (char *)name.value, size - 1); + } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + text[size - 1] = '\0'; + XFree(name.value); + return 1; +} + +void +grabbuttons(Client *c, int focused) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + if (!focused) + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, + BUTTONMASK, GrabModeSync, GrabModeSync, None, None); + for (i = 0; i < LENGTH(buttons); i++) + if (buttons[i].click == ClkClientWin) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, + buttons[i].mask | modifiers[j], + c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +void +grabkeys(void) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + KeyCode code; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < LENGTH(keys); i++) + if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, + True, GrabModeAsync, GrabModeAsync); + } +} + +void +incnmaster(const Arg *arg) +{ + selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); +} + +#ifdef XINERAMA +static int +isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +{ + while (n--) + if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org + && unique[n].width == info->width && unique[n].height == info->height) + return 0; + return 1; +} +#endif /* XINERAMA */ + +void +keypress(XEvent *e) +{ + unsigned int i; + KeySym keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for (i = 0; i < LENGTH(keys); i++) + if (keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); +} + +void +killclient(const Arg *arg) +{ + if (!selmon->sel) + return; + if (!sendevent(selmon->sel, wmatom[WMDelete])) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, selmon->sel->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } +} + +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, *t = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + } else { + c->mon = selmon; + applyrules(c); + } + + if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) + c->x = c->mon->wx + c->mon->ww - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) + c->y = c->mon->wy + c->mon->wh - HEIGHT(c); + c->x = MAX(c->x, c->mon->wx); + c->y = MAX(c->y, c->mon->wy); + c->bw = borderpx; + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); + attach(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); + focus(NULL); +} + +void +mappingnotify(XEvent *e) +{ + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if (ev->request == MappingKeyboard) + grabkeys(); +} + +void +maprequest(XEvent *e) +{ + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) + return; + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +void +monocle(Monitor *m) +{ + unsigned int n = 0; + Client *c; + + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); +} + +void +motionnotify(XEvent *e) +{ + static Monitor *mon = NULL; + Monitor *m; + XMotionEvent *ev = &e->xmotion; + + if (ev->window != root) + return; + if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + mon = m; +} + +void +movemouse(const Arg *arg) +{ + int x, y, ocx, ocy, nx, ny; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + if (!getrootptr(&x, &y)) + return; + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + if (abs(selmon->wx - nx) < snap) + nx = selmon->wx; + else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) + nx = selmon->wx + selmon->ww - WIDTH(c); + if (abs(selmon->wy - ny) < snap) + ny = selmon->wy; + else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) + ny = selmon->wy + selmon->wh - HEIGHT(c); + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) + togglefloating(NULL); + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, nx, ny, c->w, c->h, 1); + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +Client * +nexttiled(Client *c) +{ + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + return c; +} + +void +pop(Client *c) +{ + detach(c); + attach(c); + focus(c); + arrange(c->mon); +} + +void +propertynotify(XEvent *e) +{ + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != NULL)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; + break; + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); + } +} + +void +quit(const Arg *arg) +{ + running = 0; +} + +Monitor * +recttomon(int x, int y, int w, int h) +{ + Monitor *m, *r = selmon; + int a, area = 0; + + for (m = mons; m; m = m->next) + if ((a = INTERSECT(x, y, w, h, m)) > area) { + area = a; + r = m; + } + return r; +} + +void +resize(Client *c, int x, int y, int w, int h, int interact) +{ + if (applysizehints(c, &x, &y, &w, &h, interact)) + resizeclient(c, x, y, w, h); +} + +void +resizeclient(Client *c, int x, int y, int w, int h) +{ + XWindowChanges wc; + + c->oldx = c->x; c->x = wc.x = x; + c->oldy = c->y; c->y = wc.y = y; + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void +resizemouse(const Arg *arg) +{ + int ocx, ocy, nw, nh; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + break; + } + } while (ev.type != ButtonRelease); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +void +restack(Monitor *m) +{ + Client *c; + XEvent ev; + XWindowChanges wc; + + drawbar(m); + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +run(void) +{ + XEvent ev; + /* main event loop */ + XSync(dpy, False); + while (running && !XNextEvent(dpy, &ev)) + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ +} + +void +scan(void) +{ + unsigned int i, num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (i = 0; i < num; i++) { + if (!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if (XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + if (wins) + XFree(wins); + } +} + +void +sendmon(Client *c, Monitor *m) +{ + if (c->mon == m) + return; + unfocus(c, 1); + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attach(c); + attachstack(c); + focus(NULL); + arrange(NULL); +} + +void +setclientstate(Client *c, long state) +{ + long data[] = { state, None }; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +int +sendevent(Client *c, Atom proto) +{ + int n; + Atom *protocols; + int exists = 0; + XEvent ev; + + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + if (exists) { + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = proto; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); + } + return exists; +} + +void +setfocus(Client *c) +{ + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } + sendevent(c, wmatom[WMTakeFocus]); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = 1; + c->oldstate = c->isfloating; + c->oldbw = c->bw; + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = 0; + c->isfloating = c->oldstate; + c->bw = c->oldbw; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + } +} + +void +setlayout(const Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt ^= 1; + if (arg && arg->v) + selmon->lt[selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); + else + drawbar(selmon); +} + +/* arg > 1.0 will set mfact absolutely */ +void +setmfact(const Arg *arg) +{ + float f; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; + selmon->mfact = f; + arrange(selmon); +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + Atom utf8string; + + /* clean up any zombies immediately */ + sigchld(0); + + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ + updatebars(); + updatestatus(); + /* supporting window for NetWMCheck */ + wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, + PropModeReplace, (unsigned char *) "dwm", 3); + XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + /* EWMH support per view */ + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + XDeleteProperty(dpy, root, netatom[NetClientList]); + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); + focus(NULL); +} + +void +seturgent(Client *c, int urg) +{ + XWMHints *wmh; + + c->isurgent = urg; + if (!(wmh = XGetWMHints(dpy, c->win))) + return; + wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); + XSetWMHints(dpy, c->win, wmh); + XFree(wmh); +} + +void +showhide(Client *c) +{ + if (!c) + return; + if (ISVISIBLE(c)) { + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); + if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + } +} + +void +sigchld(int unused) +{ + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("can't install SIGCHLD handler:"); + while (0 < waitpid(-1, NULL, WNOHANG)); +} + +void +spawn(const Arg *arg) +{ + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); + } +} + +void +tag(const Arg *arg) +{ + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); + } +} + +void +tagmon(const Arg *arg) +{ + if (!selmon->sel || !mons->next) + return; + sendmon(selmon->sel, dirtomon(arg->i)); +} + +void +tile(Monitor *m) +{ + unsigned int i, n, h, mw, my, ty; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + if (n > m->nmaster) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); + } else { + h = (m->wh - ty) / (n - i); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); + } +} + +void +togglebar(const Arg *arg) +{ + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +} + +void +togglefloating(const Arg *arg) +{ + if (!selmon->sel) + return; + if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ + return; + selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; + if (selmon->sel->isfloating) + resize(selmon->sel, selmon->sel->x, selmon->sel->y, + selmon->sel->w, selmon->sel->h, 0); + arrange(selmon); +} + +void +togglesticky(const Arg *arg) +{ + if (!selmon->sel) + return; + selmon->sel->issticky = !selmon->sel->issticky; + arrange(selmon); +} + +void +toggletag(const Arg *arg) +{ + unsigned int newtags; + + if (!selmon->sel) + return; + newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + selmon->sel->tags = newtags; + focus(NULL); + arrange(selmon); + } +} + +void +toggleview(const Arg *arg) +{ + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); + } +} + +void +unfocus(Client *c, int setfocus) +{ + if (!c) + return; + grabbuttons(c, 0); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } +} + +void +unmanage(Client *c, int destroyed) +{ + Monitor *m = c->mon; + XWindowChanges wc; + + detach(c); + detachstack(c); + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XSelectInput(dpy, c->win, NoEventMask); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + free(c); + focus(NULL); + updateclientlist(); + arrange(m); +} + +void +unmapnotify(XEvent *e) +{ + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if ((c = wintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); + } +} + +void +updatebars(void) +{ + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixmap = ParentRelative, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +} + +void +updatebarpos(Monitor *m) +{ + m->wy = m->my; + m->wh = m->mh; + if (m->showbar) { + m->wh -= bh; + m->by = m->topbar ? m->wy : m->wy + m->wh; + m->wy = m->topbar ? m->wy + bh : m->wy; + } else + m->by = -bh; +} + +void +updateclientlist() +{ + Client *c; + Monitor *m; + + XDeleteProperty(dpy, root, netatom[NetClientList]); + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +} + +int +updategeom(void) +{ + int dirty = 0; + +#ifdef XINERAMA + if (XineramaIsActive(dpy)) { + int i, j, n, nn; + Client *c; + Monitor *m; + XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); + XineramaScreenInfo *unique = NULL; + + for (n = 0, m = mons; m; m = m->next, n++); + /* only consider unique geometries as separate screens */ + unique = ecalloc(nn, sizeof(XineramaScreenInfo)); + for (i = 0, j = 0; i < nn; i++) + if (isuniquegeom(unique, j, &info[i])) + memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); + XFree(info); + nn = j; + + /* new monitors if nn > n */ + for (i = n; i < nn; i++) { + for (m = mons; m && m->next; m = m->next); + if (m) + m->next = createmon(); + else + mons = createmon(); + } + for (i = 0, m = mons; i < nn && m; m = m->next, i++) + if (i >= n + || unique[i].x_org != m->mx || unique[i].y_org != m->my + || unique[i].width != m->mw || unique[i].height != m->mh) + { + dirty = 1; + m->num = i; + m->mx = m->wx = unique[i].x_org; + m->my = m->wy = unique[i].y_org; + m->mw = m->ww = unique[i].width; + m->mh = m->wh = unique[i].height; + updatebarpos(m); + } + /* removed monitors if n > nn */ + for (i = nn; i < n; i++) { + for (m = mons; m && m->next; m = m->next); + while ((c = m->clients)) { + dirty = 1; + m->clients = c->next; + detachstack(c); + c->mon = mons; + attach(c); + attachstack(c); + } + if (m == selmon) + selmon = mons; + cleanupmon(m); + } + free(unique); + } else +#endif /* XINERAMA */ + { /* default monitor setup */ + if (!mons) + mons = createmon(); + if (mons->mw != sw || mons->mh != sh) { + dirty = 1; + mons->mw = mons->ww = sw; + mons->mh = mons->wh = sh; + updatebarpos(mons); + } + } + if (dirty) { + selmon = mons; + selmon = wintomon(root); + } + return dirty; +} + +void +updatenumlockmask(void) +{ + unsigned int i, j; + XModifierKeymap *modmap; + + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) + for (j = 0; j < modmap->max_keypermod; j++) + if (modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + XFreeModifiermap(modmap); +} + +void +updatesizehints(Client *c) +{ + long msize; + XSizeHints size; + + if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) + /* size is uninitialized, ensure that size.flags aren't used */ + size.flags = PSize; + if (size.flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else if (size.flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } else + c->basew = c->baseh = 0; + if (size.flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else + c->incw = c->inch = 0; + if (size.flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else + c->maxw = c->maxh = 0; + if (size.flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else if (size.flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } else + c->minw = c->minh = 0; + if (size.flags & PAspect) { + c->mina = (float)size.min_aspect.y / size.min_aspect.x; + c->maxa = (float)size.max_aspect.x / size.max_aspect.y; + } else + c->maxa = c->mina = 0.0; + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + c->hintsvalid = 1; +} + +void +updatestatus(void) +{ + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); +} + +void +updatetitle(Client *c) +{ + if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); + if (c->name[0] == '\0') /* hack to mark broken clients */ + strcpy(c->name, broken); +} + +void +updatewindowtype(Client *c) +{ + Atom state = getatomprop(c, netatom[NetWMState]); + Atom wtype = getatomprop(c, netatom[NetWMWindowType]); + + if (state == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + if (wtype == netatom[NetWMWindowTypeDialog]) + c->isfloating = 1; +} + +void +updatewmhints(Client *c) +{ + XWMHints *wmh; + + if ((wmh = XGetWMHints(dpy, c->win))) { + if (c == selmon->sel && wmh->flags & XUrgencyHint) { + wmh->flags &= ~XUrgencyHint; + XSetWMHints(dpy, c->win, wmh); + } else + c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; + if (wmh->flags & InputHint) + c->neverfocus = !wmh->input; + else + c->neverfocus = 0; + XFree(wmh); + } +} + +void +view(const Arg *arg) +{ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); +} + +Client * +wintoclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + if (c->win == w) + return c; + return NULL; +} + +Monitor * +wintomon(Window w) +{ + int x, y; + Client *c; + Monitor *m; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + if (w == m->barwin) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +int +xerror(Display *dpy, XErrorEvent *ee) +{ + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +xerrordummy(Display *dpy, XErrorEvent *ee) +{ + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +int +xerrorstart(Display *dpy, XErrorEvent *ee) +{ + die("dwm: another window manager is already running"); + return -1; +} + +void +zoom(const Arg *arg) +{ + Client *c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) + return; + if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) + return; + pop(c); +} + +int +main(int argc, char *argv[]) +{ + if (argc == 2 && !strcmp("-v", argv[1])) + die("dwm-"VERSION); + else if (argc != 1) + die("usage: dwm [-v]"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + checkotherwm(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + run(); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} diff --git a/bdwm/dwm.o b/bdwm/dwm.o new file mode 100644 index 0000000000000000000000000000000000000000..402104872fc8a130a97090d49f0113e12db4285f GIT binary patch literal 82320 zcmeFa3w%`7^*?$h;Sms>ps3iQ4uV1iF;P?^s2MW@XLJG)0;CFskPIY}m&ptdD-s(b z9FIY4eYI7K)wcAZ($*?kgrI1kMFo5m)G9t|lLi%00UtGYt-aTptnA5={_g$U|L5M% z=eJ>U&iU@|S$plZ*M6RTW|oJe6VrS?i-XU))atro)Upas>O%7roM$}@%&2=RUPaFz zGJLx2{4+Q$I5RjSI6YWo-?YCCA{sk=@$AOVw8S9WU6f%rUxI9mu$o%p{ph-*FP-GH zs3^Pn0%UAXgFhNTGToIJyeS)WvC|btO3dlhWT!XYZ8xH5(WuH!RdTw zCaXy)>s#z*<>*o8li&u-Ioxz@df~UFkU+6U!s56YaQlbyv2P5udVbXgHlu=ss$PR8+*BVJo;T{TMK6XWQ3s;I>RRL5)W` z&E_rI9Nrme1*L_7b#XOMZ?T&THbk2Xb||q;E&dhXh4N`!w!`wTNQbj<&WF+Fr%~Z( zbF3w4^YG3q+_7y@=S#XRUfB3_d1q&*m6(*md6zhe(Tz_>fs+<)g2#(a}ITHZm)F5 zUaH8E#^*C4jo%DTd_Zd0?l_V{oy?L9DOH}^T1^SbbM8f{p#%4`RL_>PU9w1st!uoD zQRg`=_Dx&yEI2DTJNW(JoZvOHaTpREe=2}hyw?s0&!&t|3b`7!C@H!G8nbR_~bz27wPdq5$90E>GVH7V`$@-XD7~R_#z{5Dkz5XP@}6s zqd}mNk`Ye>c@1A)a^t{;FT=~vX!!Ed_=tutFOQ$mF}mT)0dW9^#CtbB;!cel1*+bj7u)$lwpoMuDkK|V?n{VSTG_hG+n zPUuD|;NAa{qq=?7p&+X=UD6A*F|JEHEHn5l5?=OL$R zo4@(6Qdt#jLDT#3x~TJBfm=4b7A_JWZN?JVvPujt+wjSQF6oy4Y%zLoDg1S+8ZE3 zrG%G|lxi6w&Whm;fEPF$hl|95{(-%QM;(=CD8P`L*a2Dh-|(-IJjHY|=hAGci!Dbbw~r)d>L*{)q2ahj18b;^q)ZnH+O zDvvmKHYl{BoCPKxRcc1ZD^n->^4JUV5C_S@&gYeaO>@vE-DNGh7QW$c`XgwZ^G5p) zrMPZjnmQAvSHS$E%PVYWl5h&R5=L%KvH_)P2Z1Z$7yf~up-N2~D|gb!gkO(R zbKc0EGCGmbaf!ZkCXI6@jdZ3=aHfoI`wa}wR6dh7R?f#Q%ja-K8F!$rZw*}I(NFYN zZI*X?1ru4NEDm?W_rPZzd{+4fHZ1}3!2yRua41G&wuV)Ouv?o7;S!3)0_dJzkoDH^ zI=tk1mjx|>?)?gyI{hoK!bY2ai8rYMPy!r&`4Y?D^kazNc2W1KL3hfKpgS`=>`odN z(q@UecViLSrP=^n_Vo?Bv(m-|i^_d4d=GZU)AC?OKCNp^ogHx|ElG;i&4W7-M&Qa5 zC?eu6USeKHor4N0aF;`?)ah@Uj=8>R>ncdz!@S&);_3h=*{u4*O(Wp0w&VP2v^3D( zP`0@0oD0XC&!tA>+3jxvBc=E1)|<55e*$D*nI4x6pwVd2uH=NbHEv6D!aF!+Z7^&} z#IPvZtSSm|qRvlZx~SEK8}q_$1dnPlQ%D! z3q>B!F33F`9|heQ4C2lhn)J?y`@|Z!gX^LTXN3XnE6}j7_#x;)soLX}o5s0Wf>h_C zn(_Wo=P(GJ2t!#oYs&?YZ*gsC=T_@>uC^RvH{l&^zX*u*AMcia9(2H@018`vA>?Lk z(hJnA=)WCuDs`hy?OZYVJ`RQEL(Ne`?hliLt?q-O3@%5r{M;$S-I=4EDPx_P6B?Hn zTH0IVBVas{wG;jT9C9ZWRo&j?ei8pJ z>H@<1K`OP;+)ol8N8HJ^aDQ#bF%fqnoaqKlH4tL zRWFk$U_WTwpAl`I{=VHD-Urj;JurW6gDLWU96h_cf>R5V3km@kcb@DnYjYF(mDlLC zjR#@=*HJ`G0A^$yWGI?jyCi-Yk4``7INf%G#W-#~KAO88T@Cic;{^~fm zBWHJO7)J^)Vt_fMQ?()JPMnbYqBFh4No=j3k+mn3`%ciGzr(jHXZPVxg6^X1pgUu% z@AaU&ISRM?Izvu-))!evpeYGDEwF&ob_OcuuEf}&d)pu+?QQNA433{Tp)SqYFG zbY^dhIvpXmYDXxmE#$nD^$832y@ldKzJEhb#!(v0Lw?}v%-So{z=orH8X;dhCz2j= z+Mu(50p|vH_O^(-S=nn8+=2x-1kC6rwst;`wsK)bW#!J!ZSbc(9Xr8q8DR{NmD8-6 zdc|DVjOE2peV~mkw4EciyGB;HHLAk(YuunJTs*CF3waS1+`uLniRUovgS4LYXzTh? zCe8d|elCLiG;Wv$cC->uA6BW1U6toXZ?~IPsf?XW8G|J)T{$qR9XT!C8WN8gy&MW; z2Avmz&MQIZa96k3v|W$d{gXRjo&~eRmY{#i9`GcXC&n+=9e61Bi)AMU-Mf@$?hcMx zoe9cj?Fu?Car`^WW>7rDqHkvH!uTB>6WzjW*BRhFo-#qqQAFQ7GUJLo{FM`cuhxiUIO@{=u0`|(ixqIQyvffU? zHg|6@_l0GBoPr&g-NsGIvi=pn0QWfB!xIn0FSH)BT_NzxOw2V|R312di1OyJA}d++#N` z$$&vF&U@SQp%Rf{Ej{3GaliE+X4_oEdO=R$?c^qRa3lue5|e-_F;$r})I zR-%gTZRqlj%}ezWSB=GEoz++s+=7j8hP9z%FwD=^W`LgR zDMF<6W{k)12VciYZo$6f9q-c}IoomO0AVx!S(g{@<4)hx2(%doU~$l{0vA9t0IPQPD@T_$C>UjP;ry0pKf?#$8qj5P=K%B z4?8!bmw`L4XjyuuKeJ%xL4RiW75_Eh-kHe&?3JmfSD>ceI3ZpZDMK40L4y9DG8(3Si}eQ~%=#1f83U;kIDw z%{Cn1KUCbUb5qKVXL>1fH7KoO7+b9kwpu6Q&3d#Oaq5m>rVta7l@%)};X4WuEU#-zU)V zg>!a?-C~$u*NzMO-U{U&4Edu6!|sx?!Q8Ll^zCrgTVcQb32-KaeP4xg_W}s*@nJZ8 z6VCby;`WC9kG>VoeL3Qv(h~MhdAR_VDj|i$I<+R;eNfj^1@7sQI@oX=`05@s08C$D z{O(`zGdPbpui4Hgw)4&NU=ux1hI+7p>%b4}#$`LLcpMHbrzhZfOk%@Rop2jgM-zW$#-z4vo6Y_C(x^V4*X9Vz--lEi?H1%_tMnU#1fds&&K= z3}PC$j_V=Q|3&%W76Kg5=Hc|nzak6Qhn+82Sy=mk=i7G0p4`>%jeF{Mzy0lRZU3V? z?JfU4!-nxEjCF&mdfK_P^jWYiF`G)mLW3|kYHUqQrUPws?YL0Z zHzDmOIlDv77x=8v3eF%Y1>3NFwzqT)#ex1NWe4!45iobldEZr|8+XlItUmpAK%s^H z+V4d%#Cgj}YzR5A$3o7sjkYs;9W+@j@W9@N6%4fyq$aAc=-|v=OFBi|3vDN%8##AR z`l;IR)3>>o=dsz_gD?Nf4gJuzUv5igL>Z!SpU-y3>IRu&C}TQ%L1#;FOZ(|T-b2u+{89-hkS$4~$1=WptS z-ksz}-2{yPlSLIuSM7f?ah0M`z}^{j-crTcRhE!;(AgSvzdsII-;i$~^wU|`S?dQ% z{?+|J^GM^3bCVTKTTQQ4=WLJWs?ze`u?390O*)yk4k70QFz()KL1cJaXYE{d7v>VJ z3v=MPG;H>-xK&j!%J1}bLEkGOcNI3vXhaZ0cD&Z=D299U{s#kwo()D~Q9J!8|5{%+ z<;dtbD~EXlFGb?>e=~VCD_08uv^8j2VXG=B?Yb&UN;GUkaWkQD(_0CkrUhSqUoGmv zVnrTQ2WKa&4`c-WkG6(6xnA2dtfZ z0;yyw^Q82dg}kib235_0nO`LowiQ1URX6&()b+x~%d8t=H3A->sW~i6!u%^9Qy!#O z3JYPSa1E^#`kQ_TF^!vRmHZbv7Q?bcv7NgmegX6|JuUn6qh+6Xy6wCKD?dxn3v;(5 zc0)3WT)e4=8D4Vg3O9jTY6S!*d3ck5#RwSvx1HRPtmu@<_S<2XmF+Ht1r#8q;XJ#i zrOxL_`NT%bZ^uhwxjCmr^&xk(z~hLv`u$yL=;~{`wM$%k2+ZO_ux*N#)U`Wl|C-%? zWrk&Gfy!@Pf6nf7dywD$t78v>+{|u28wf@bg2wSWHIG+L>P~@K&#f;tU>P(n&9LG# zV9;j4M>Wj;G20d$^QpghQYOq)S~>>c->&(})h+&++X31J<2|)r?UOc;=x#zuy8b*2&$JBV2d5Qoz{fA{MMGg{o3fI7Ro?jc?RMh7t3P(* zL7#I~-mt#$UQXzC8`e**hh({<(KWDrY>x?ZJK!#XirWcOKH3X$Cin4oW5KNpzQC{&>JqLpCUI=Yy02&xbU6D8AI@*U%r_(a6I8v^m#{XYu1O z=f$8YjG(r3^j6Ez&MI8~vB5SpR)~*s*Jl9auEBY`y9y`mA-4f%>d;u>(%zGz5hPbA zhXHnq*1;K6vT9u(O8sK!SIEC_yb4$C!4;zZ@F!5^!Lh;P!9QnXt?ZM*=KarI$_mlV_ocXH7rTA_eCRgs%+K&CU)<>)H7eftI zy1HYq^PWxJx;4_J?$xWsnCdkSFd8$OIIi8d!%f{mNi0%&4V zq}$*iNDo4x6Qi*WCq@q|pPCCIc>^ZgyGGRzl)kwEP9cHBgQOm{yRfIzQiBjZ@YL;^ zek2Yv;l9d2s`C7@x{b!a7S&FtF!HyU^UwOtNO zd;$r5b>brts~%6{Vv&A4nNs(THUUR{{3{*?>7WyJ#q@NUpB5hnLjyHp^sl%Vcw{G9 zii4GMY=K~16q_L3?(;L}?yJ9Ih>-&l)fhK10v3Yx{deL0AO_noSh~hG3@n0e*ypf! zjUH1&?)^jI=hWyz4GbDw6^;7re={sTJ=1rvlEIUAlwiI`>y5Tn)+kAB;b}2lT9Q#t@pcB@dXmbKM=(eiOv$F-FMQ2lW6| zg&iTOHKB)Qzk6qIJoGs#pB|uL4XO(&<3MFy)upWW&IH(UhEHm#{ZW(mqwWb%jkeB} znz)H;x%u>4npdqauWx1RM^$Za9)Onnzu3V7BV=r*@R@7ZzTPmjROcmKLJ??pex@^P zc!o1%II&@!Ir!uNunl_((4tUYTlV#)ZGw#xGI}MxM;Gp_j9wx4hKvlDe3SUg-J*wE z`n^tS;0pkmt)bx=Aks>1f`L7kG8srit>^rwSK=wy4jF0u`k2HmV7Kh#1l*X;{#W%u ztqd zX`R{Al&wHt+ypQTTpN@0QXTg?7%AWyS95ry-8>r>SeuVvGu{llznTlS!X|^lY2Cy> z02>^epGFQoje~_jm?i11Cfbk*EuY?o?=~T$@yH3w2Q(h(6Cc}nWI%jC$FRmDC&zm? z9vPH4g^Dlg4%xzMw0uwR#KY~^ff=H1Q#RDtsMBJ@8du|yfp!0I?;HonsOc?k^8`2v z`Q9B>uoa$;lZ`^|+-z89%6iQ?eE4lx>u&4})Mba#=fcBwe&m_{IJTZ4=e?|#;Ys4# zNc#$C^?ia_pTPqucrY7s%g2VCf5R9n3m-8!uN{61R@WQ9QKHHx$c7l(`jGQ+)=OD` zcfLW|VW6R?SJi{9W$#0t_}(80=c4BUaonkv$G=|;zMq18*Wu-Uk=4Z z1xW*mpsSwyf$4Myr@9@GjG1n`61q0pCFNh?{s$Fn#R(GZ&uheCy51}a1IrKg!o$yN zfsisH?RqAzdkspRs)&EZ@gRq*iNC2AT(zBb2&;-x3u?^&=%$6!bb>ISCRRXy}Ay%UeLk4KH%r}Hoo=SVbbpROLH zw68@kPX@ELhMj+gMpbPKr9W%~H{A5$@;KrHi1Xy6VzTUjvdJ zw|F(~p4<6&2$K5*7O;QCPe5`+Z9&od6t6gp6Qciq@e34eyLbU#jqJ%?dHU-dSa6#6n5*Yu(2L@Ch3<^U?Tp*Pa%_C*K)*i zlo<*w-Rpfys!Iy`A750Ie1Wu;Hqi#34npP&#@{gfK;mpYYdyRZrhSFM^P|JthHg2u z6N|~J=tv$iV7KuKM%Zt(yW6+W9@yhn}2q<-G)g@1{Y0U_foB-?j@TSS3`0

QDKMeJ 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 literal 0 HcmV?d00001 diff --git a/bdwm/transient.c b/bdwm/transient.c new file mode 100755 index 0000000..040adb5 --- /dev/null +++ b/bdwm/transient.c @@ -0,0 +1,42 @@ +/* cc transient.c -o transient -lX11 */ + +#include +#include +#include +#include + +int main(void) { + Display *d; + Window r, f, t = None; + XSizeHints h; + XEvent e; + + d = XOpenDisplay(NULL); + if (!d) + exit(1); + r = DefaultRootWindow(d); + + f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); + h.min_width = h.max_width = h.min_height = h.max_height = 400; + h.flags = PMinSize | PMaxSize; + XSetWMNormalHints(d, f, &h); + XStoreName(d, f, "floating"); + XMapWindow(d, f); + + XSelectInput(d, f, ExposureMask); + while (1) { + XNextEvent(d, &e); + + if (t == None) { + sleep(5); + t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); + XSetTransientForHint(d, t, f); + XStoreName(d, t, "transient"); + XMapWindow(d, t); + XSelectInput(d, t, ExposureMask); + } + } + + XCloseDisplay(d); + exit(0); +} diff --git a/bdwm/util.c b/bdwm/util.c new file mode 100755 index 0000000..96b82c9 --- /dev/null +++ b/bdwm/util.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#include "util.h" + +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} diff --git a/bdwm/util.h b/bdwm/util.h new file mode 100755 index 0000000..f633b51 --- /dev/null +++ b/bdwm/util.h @@ -0,0 +1,8 @@ +/* See LICENSE file for copyright and license details. */ + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/bdwm/util.o b/bdwm/util.o new file mode 100644 index 0000000000000000000000000000000000000000..6cc164b3947cf20c860b69f4c622d0f9f287ffb3 GIT binary patch literal 2592 zcmb_e&yN#T9Di+hm#Pa>jfYKGq>xqVfyq`>2rAh^fFVryp{|6GO&QwRwXy9?rc+!9 z#u~slS*z=T1aA8eXuQcqb_phW@URz78gIr;mXH_|34~C;pEvJIXNT5!@RPjvec#Xb z=X-r`c0b;49!Mq0i0n{zJt3D9B14{&M(}&#MBzQ-lRL{q9||5B?eDXl zPR9s7OdG+8j1e3iF@u9yGdQ~K(vqLka+i;hSOjP`y?vNvs9Pg8+$8b**TERIu`X6h}pVg?;E_LI-nU=Ac=iQ^ZcfG+z6a4``?ZDuaEG9xQK+f{|gbGuJz+QFDf4U#o+u|S_8-vy^Rwf z3C#2FIVg0`u3EZp&-%iit@uJW^@i^W*Y-Te6Z(wjG+jYV)vgP@?)bKT@W|x$hHsTV z5PG$GwpO&tWv@}05of1euTuA?RZ9E);cZ~mgW;pyGxIjtpyTy@EPhq3_9oZgJtLkzc zq9o$qxL=8d>nox8@%)*J=lX0=w2T4Qum5z9{;>z{o8<%IkTVtQAAf^@c>TEEuYRUS zeS9alF3Sbtdey)BAM0;d@g&w@LB+2jNr}@ZL(YwDUvT{CSc3Te-z7WmCF;7YNPcBF vd+eX%`Ci~(h?LoJNVI5lq23Soi+F(bP!r-3E#keyHloOu3V=XM@%sM&%pq?n literal 0 HcmV?d00001 diff --git a/devour/LICENSE b/devour/LICENSE new file mode 100755 index 0000000..d159169 --- /dev/null +++ b/devour/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/devour/Makefile b/devour/Makefile new file mode 100755 index 0000000..a8c2a1c --- /dev/null +++ b/devour/Makefile @@ -0,0 +1,26 @@ +.POSIX: + +NAME = devour +VERSION = 11.0 + +CC = cc +CFLAGS = -std=c11 -D_POSIX_C_SOURCE=200809L -Wall -Wextra -pedantic -O2 +LDLIBS = -s -lX11 + +BIN_DIR = /usr/local/bin + +SRC = devour.c +OBJ = devour.o + +all: $(NAME) +$(NAME): $(OBJ) +install: all + @mkdir -p $(BIN_DIR) + @mv $(NAME) $(BIN_DIR) + @rm -f $(OBJ) + @echo Done moving the binary to ${DESTDIR}${BIN_DIR} +uninstall: + @rm -f $(BIN_DIR)/$(NAME) + @echo Done removing the binary from $(BIN_DIR) + +.PHONY: all install uninstall diff --git a/devour/README.md b/devour/README.md new file mode 100755 index 0000000..3df3e68 --- /dev/null +++ b/devour/README.md @@ -0,0 +1,128 @@ +# Devour: X11 Window Swallower + +Devour hides your current window before launching an external program and unhides it after quitting. +Devour was inspired by +[sw](https://github.com/ronniedroid/.dotfiles/blob/master/Scripts/sw) +and is a successor to +[devour.sh](https://github.com/salman-abedin/devour.sh) + +![](https://gitlab.com/salman-abedin/assets/-/raw/master/devour.gif) + +# Demonstration done by [DistroTube](https://www.youtube.com/channel/UCVls1GmFKf6WlTraIb_IaJg) + +[![](https://gitlab.com/salman-abedin/assets/-/raw/master/devour-dt.png)](https://www.youtube.com/watch?v=mBNLzHcUtTo&t=5m22s) + +## Dependencies + +- Xlib (client-side header files) + +## Installation + +#### AUR + +```sh +yay -S --noconfirm devour +# or +yay -S --noconfirm devour-git # Nightly +``` + +#### Git + +```sh +git clone https://github.com/salman-abedin/devour.git && cd devour && sudo make install +``` + +## Usage + +```sh +devour CMD ... +``` + +## Patches + +- **Shell aliases**. (ex. `devour z FILE` instead of `devour zathura FILE`) + +```sh +cd devour +patch -s < devour-shellalias-10.0.diff # Add the feature +patch -s -R < devour-shellalias-10.0.diff # Remove the feature +sudo make install # Reinstall +``` + +## Pro Tip + +**Devour from your file explorer instead of the shell.** +Watch my demo and notice how seamless it is compared to devouring from the shell. + +**Hint:** If you are one of those unfortunate souls who uses **xdg-open** instead of +[a custom launch script](https://gist.github.com/salman-abedin/6f52c52e465d89d489f9ea8d891c7332), +then go to your **~/.local/share/applications** directory and modify the applications you launch from your file explorer like below and enjoy the true devouring experience. + +``` +[Desktop Entry] +Type=Application +Name=PDF Reader +Exec=/usr/local/bin/devour /usr/bin/zathura %U +``` + +## Update + +```sh +cd devour +git pull --no-rebase && sudo make install +``` + +## Uninstallation + +```sh +cd devour +sudo make uninstall +``` + +## Logs + +- **21/06/20**:- Added support for names with spaces + +- **07/07/20**:- Added support for shell aliases + +- **03/08/20**:- Rewrote the shellscript in C + +- **23/08/20**:- Made additional features optional using patching + +- **08/11/20**:- Added support for all unsafe characters + +## Contributors + +- keni7385 (AUR package submitter/maintainer) + +- [agnipau](https://github.com/agnipau) + +- [HawaiinPizza](https://github.com/HawaiinPizza) + +- [sbuller](https://github.com/sbuller) + +- [AriaMoradi](https://github.com/AriaMoradi) + +- [durcor](https://github.com/durcor) + +## TO-DOs + +- Authentic swallowing + +--- + +## Shameful Plug + +| Name | Description | +| ------------------------------------------------------- | ---------------------------------- | +| [faint](https://github.com/salman-abedin/faint) | Extensible TUI fuzzy file explorer | +| [bolt](https://github.com/salman-abedin/bolt) | The launcher wrapper | +| [uniblocks](https://github.com/salman-abedin/uniblocks) | The status bar wrapper | +| [tide](https://github.com/salman-abedin/tide) | Minimal Transmission CLI client | +| [puri](https://github.com/salman-abedin/puri) | Minimal URL launcher | +| [Magpie](https://github.com/salman-abedin/magpie) | The dotfiles | +| [Alfred](https://github.com/salman-abedin/alfred) | The scripts | + +## Contact + +SalmanAbedin@disroot.org diff --git a/devour/devour-shellalias-10.0.diff b/devour/devour-shellalias-10.0.diff new file mode 100755 index 0000000..f81d8f2 --- /dev/null +++ b/devour/devour-shellalias-10.0.diff @@ -0,0 +1,17 @@ ++++ devour.c 2020-08-23 20:40:49.965458610 +0600 +@@ -11,6 +11,7 @@ + int is_unsafe = 0; + char cmd[512] = {0}; + ++ strcat(cmd, "$SHELL -i -c '"); + while (*++argv) { + if (!strcmp(*argv, "--")) { + is_unsafe = 1; +@@ -19,6 +20,7 @@ + strcat(cmd, *argv); + strcat(cmd, is_unsafe && *(argv + 1) ? "\\ " : " "); + } ++ strcat(cmd, "> /dev/null 2>&1; exit'"); + system(cmd); + } + diff --git a/devour/devour.c b/devour/devour.c new file mode 100755 index 0000000..39d7907 --- /dev/null +++ b/devour/devour.c @@ -0,0 +1,42 @@ +/* + * devour + * X11 window swallower + */ + +#include +#include +#include + +#define UNSAFE_CHARS "`\"'()[]& " + +void run_command(char **argv) { + char arg_char; + char *arg; + char cmd[1024] = {0}; + + while ((arg = *++argv)) { + while ((arg_char = *arg++)) { + if (strchr(UNSAFE_CHARS, arg_char)) + strcat(cmd, "\\"); + strncat(cmd, &arg_char, 1); + } + strcat(cmd, " "); + } + system(cmd); +} + +int main(int argc, char **argv) { + int rev; + Window win; + Display *dis = XOpenDisplay(0); + + XGetInputFocus(dis, &win, &rev); + XUnmapWindow(dis, win); + XFlush(dis); + run_command(argv); + XMapWindow(dis, win); + XCloseDisplay(dis); + + (void)argc; + return 0; +} diff --git a/slock/1.diff b/slock/1.diff new file mode 100755 index 0000000..55af8c8 --- /dev/null +++ b/slock/1.diff @@ -0,0 +1,31 @@ +diff --git a/slock.c b/slock.c +index 5ae738c..bd54be2 100644 +--- a/slock.c ++++ b/slock.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + + #include "arg.h" + #include "util.h" +@@ -157,6 +158,18 @@ readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens, + IsPrivateKeypadKey(ksym)) + continue; + switch (ksym) { ++ case XF86XK_AudioPlay: ++ case XF86XK_AudioStop: ++ case XF86XK_AudioPrev: ++ case XF86XK_AudioNext: ++ case XF86XK_AudioRaiseVolume: ++ case XF86XK_AudioLowerVolume: ++ case XF86XK_AudioMute: ++ case XF86XK_AudioMicMute: ++ case XF86XK_MonBrightnessDown: ++ case XF86XK_MonBrightnessUp: ++ XSendEvent(dpy, DefaultRootWindow(dpy), True, KeyPressMask, &ev); ++ break; + case XK_Return: + passwd[len] = '\0'; + errno = 0; diff --git a/slock/LICENSE b/slock/LICENSE new file mode 100755 index 0000000..2e4419b --- /dev/null +++ b/slock/LICENSE @@ -0,0 +1,24 @@ +MIT/X Consortium License + +© 2015-2016 Markus Teich +© 2014 Dimitris Papastamos +© 2006-2014 Anselm R Garbe +© 2014-2016 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/slock/Makefile b/slock/Makefile new file mode 100755 index 0000000..f4ffbb7 --- /dev/null +++ b/slock/Makefile @@ -0,0 +1,61 @@ +# slock - simple screen locker +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = slock.c ${COMPATSRC} +OBJ = ${SRC:.c=.o} + +all: options slock + +options: + @echo slock build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + @echo CC $< + @${CC} -c ${CFLAGS} $< + +${OBJ}: config.h config.mk arg.h util.h + +config.h: + @echo creating $@ from config.def.h + @cp config.def.h $@ + +slock: ${OBJ} + @echo CC -o $@ + @${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + @echo cleaning + @rm -f slock ${OBJ} slock-${VERSION}.tar.gz + +dist: clean + @echo creating dist tarball + @mkdir -p slock-${VERSION} + @cp -R LICENSE Makefile README slock.1 config.mk \ + ${SRC} explicit_bzero.c config.def.h arg.h util.h slock-${VERSION} + @tar -cf slock-${VERSION}.tar slock-${VERSION} + @gzip slock-${VERSION}.tar + @rm -rf slock-${VERSION} + +install: all + @echo installing executable file to ${DESTDIR}${PREFIX}/bin + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f slock ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/slock + @chmod u+s ${DESTDIR}${PREFIX}/bin/slock + @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @sed "s/VERSION/${VERSION}/g" ${DESTDIR}${MANPREFIX}/man1/slock.1 + @chmod 644 ${DESTDIR}${MANPREFIX}/man1/slock.1 + +uninstall: + @echo removing executable file from ${DESTDIR}${PREFIX}/bin + @rm -f ${DESTDIR}${PREFIX}/bin/slock + @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 + @rm -f ${DESTDIR}${MANPREFIX}/man1/slock.1 + +.PHONY: all options clean dist install uninstall diff --git a/slock/README b/slock/README new file mode 100755 index 0000000..dcacd01 --- /dev/null +++ b/slock/README @@ -0,0 +1,24 @@ +slock - simple screen locker +============================ +simple screen locker utility for X. + + +Requirements +------------ +In order to build slock you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (slock is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install slock +(if necessary as root): + + make clean install + + +Running slock +------------- +Simply invoke the 'slock' command. To get out of it, enter your password. diff --git a/slock/arg.h b/slock/arg.h new file mode 100755 index 0000000..0b23c53 --- /dev/null +++ b/slock/arg.h @@ -0,0 +1,65 @@ +/* + * 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;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define LNGARG() &argv[0][0] + +#endif diff --git a/slock/config.def.h b/slock/config.def.h new file mode 100755 index 0000000..4c73bb4 --- /dev/null +++ b/slock/config.def.h @@ -0,0 +1,21 @@ +/* user and group to drop privileges to */ +static const char *user = "nobody"; +static const char *group = "nogroup"; + +static const char *colorname[NUMCOLS] = { + [INIT] = "black", /* after initialization */ + [INPUT] = "black", /* during input */ + [FAILED] = "red", /* wrong password */ +}; + +/* treat a cleared input like a wrong password (color) */ +static const int failonclear = 1; + +/* default message */ +static const char * message = "Suckless: Software that sucks less."; + +/* text color */ +static const char * text_color = "#ffffff"; + +/* text size (must be a valid size) */ +static const char * font_name = "6x10"; diff --git a/slock/config.h b/slock/config.h new file mode 100755 index 0000000..4c73bb4 --- /dev/null +++ b/slock/config.h @@ -0,0 +1,21 @@ +/* user and group to drop privileges to */ +static const char *user = "nobody"; +static const char *group = "nogroup"; + +static const char *colorname[NUMCOLS] = { + [INIT] = "black", /* after initialization */ + [INPUT] = "black", /* during input */ + [FAILED] = "red", /* wrong password */ +}; + +/* treat a cleared input like a wrong password (color) */ +static const int failonclear = 1; + +/* default message */ +static const char * message = "Suckless: Software that sucks less."; + +/* text color */ +static const char * text_color = "#ffffff"; + +/* text size (must be a valid size) */ +static const char * font_name = "6x10"; diff --git a/slock/config.mk b/slock/config.mk new file mode 100755 index 0000000..c19ebb7 --- /dev/null +++ b/slock/config.mk @@ -0,0 +1,32 @@ +# slock version +VERSION = 1.4 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# includes and libs +INCS = -I. -I/usr/include -I${X11INC} +LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr -lXinerama + +# flags +CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE +CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} +LDFLAGS = -s ${LIBS} +#COMPATSRC = explicit_bzero.c + +# On OpenBSD and Darwin remove -lcrypt from LIBS +LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -lXext -lXrandr +# On *BSD remove -DHAVE_SHADOW_H from CPPFLAGS +# On NetBSD add -D_NETBSD_SOURCE to CPPFLAGS +#CPPFLAGS = -DVERSION=\"${VERSION}\" -D_BSD_SOURCE -D_NETBSD_SOURCE +# On OpenBSD set COMPATSRC to empty +COMPATSRC = + +# compiler and linker +CC = cc diff --git a/slock/config.mk~ b/slock/config.mk~ new file mode 100755 index 0000000..d431a6e --- /dev/null +++ b/slock/config.mk~ @@ -0,0 +1,32 @@ +# slock version +VERSION = 1.4 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# includes and libs +INCS = -I. -I/usr/include -I${X11INC} +#LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr -lXinerama + +# flags +CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE +CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} +LDFLAGS = -s ${LIBS} +#COMPATSRC = explicit_bzero.c + +# On OpenBSD and Darwin remove -lcrypt from LIBS +LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -lXext -lXrandr +# On *BSD remove -DHAVE_SHADOW_H from CPPFLAGS +# On NetBSD add -D_NETBSD_SOURCE to CPPFLAGS +#CPPFLAGS = -DVERSION=\"${VERSION}\" -D_BSD_SOURCE -D_NETBSD_SOURCE +# On OpenBSD set COMPATSRC to empty +COMPATSRC = + +# compiler and linker +CC = cc diff --git a/slock/explicit_bzero.c b/slock/explicit_bzero.c new file mode 100755 index 0000000..3e33ca8 --- /dev/null +++ b/slock/explicit_bzero.c @@ -0,0 +1,19 @@ +/* $OpenBSD: explicit_bzero.c,v 1.3 2014/06/21 02:34:26 matthew Exp $ */ +/* + * Public domain. + * Written by Matthew Dempsky. + */ + +#include + +__attribute__((weak)) void +__explicit_bzero_hook(void *buf, size_t len) +{ +} + +void +explicit_bzero(void *buf, size_t len) +{ + memset(buf, 0, len); + __explicit_bzero_hook(buf, len); +} diff --git a/slock/explicit_bzero.o b/slock/explicit_bzero.o new file mode 100755 index 0000000000000000000000000000000000000000..72c3806f45d85df289809397524ade5ef2556c41 GIT binary patch literal 1408 zcmbtS%}X0m5TA{<@gt?F^&r^8;-N&SkERp~9?F8KRYXclk7e1!7ZXgfVe<;nORvq% zqesvF6@mw^9{Vr!D7|!*WAmYJ$_-hFZ5ELvu zRReeeCAg|yei+{F4DWtl>aAG3%vV8v z0eP|WzIxDWx9Tku)=qR1t2%_?C>1v2_y|ThfTuhVy~z}w`-euRsesJIt6LW|}&9E)}OXlg# zZTV~)mhSd{g+8kJulZHXrJC~pqMC~IpLBnjV;!SK=hPG$wqfaR>@NC>cUDcg}-Kf7&1AO1|uGTyN_2lRhYe3}7CScI4*I hGS6W-eB}P`awb!l&e> +Date: Wed, 2 Oct 2019 14:59:00 -0400 +Subject: [PATCH] Add a message command. Fixes old version's bugs. + +--- + config.def.h | 9 ++++ + config.mk | 2 +- + slock.1 | 7 +++ + slock.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 4 files changed, 133 insertions(+), 5 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 9855e21..c2a0ab2 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -10,3 +10,12 @@ static const char *colorname[NUMCOLS] = { + + /* treat a cleared input like a wrong password (color) */ + static const int failonclear = 1; ++ ++/* default message */ ++static const char * message = "Suckless: Software that sucks less."; ++ ++/* text color */ ++static const char * text_color = "#ffffff"; ++ ++/* text size (must be a valid size) */ ++static const char * font_name = "6x10"; +diff --git a/config.mk b/config.mk +index 74429ae..c4ccf66 100644 +--- a/config.mk ++++ b/config.mk +@@ -12,7 +12,7 @@ X11LIB = /usr/X11R6/lib + + # includes and libs + INCS = -I. -I/usr/include -I${X11INC} +-LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr ++LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr -lXinerama + + # flags + CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -DHAVE_SHADOW_H +diff --git a/slock.1 b/slock.1 +index 82cdcd6..946165f 100644 +--- a/slock.1 ++++ b/slock.1 +@@ -6,6 +6,8 @@ + .Sh SYNOPSIS + .Nm + .Op Fl v ++.Op Fl f ++.Op Fl m Ar message + .Op Ar cmd Op Ar arg ... + .Sh DESCRIPTION + .Nm +@@ -16,6 +18,11 @@ is executed after the screen has been locked. + .Bl -tag -width Ds + .It Fl v + Print version information to stdout and exit. ++.It Fl f ++List all valid X fonts and exit. ++.It Fl m Ar message ++Overrides default slock lock message. ++.TP + .El + .Sh SECURITY CONSIDERATIONS + To make sure a locked screen can not be bypassed by switching VTs +diff --git a/slock.c b/slock.c +index 5ae738c..610929b 100644 +--- a/slock.c ++++ b/slock.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -24,6 +25,9 @@ + + char *argv0; + ++/* global count to prevent repeated error messages */ ++int count_error = 0; ++ + enum { + INIT, + INPUT, +@@ -83,6 +87,98 @@ dontkillme(void) + } + #endif + ++static void ++writemessage(Display *dpy, Window win, int screen) ++{ ++ int len, line_len, width, height, s_width, s_height, i, j, k, tab_replace, tab_size; ++ XGCValues gr_values; ++ XFontStruct *fontinfo; ++ XColor color, dummy; ++ XineramaScreenInfo *xsi; ++ GC gc; ++ fontinfo = XLoadQueryFont(dpy, font_name); ++ ++ if (fontinfo == NULL) { ++ if (count_error == 0) { ++ fprintf(stderr, "slock: Unable to load font \"%s\"\n", font_name); ++ fprintf(stderr, "slock: Try listing fonts with 'slock -f'\n"); ++ count_error++; ++ } ++ return; ++ } ++ ++ tab_size = 8 * XTextWidth(fontinfo, " ", 1); ++ ++ XAllocNamedColor(dpy, DefaultColormap(dpy, screen), ++ text_color, &color, &dummy); ++ ++ gr_values.font = fontinfo->fid; ++ gr_values.foreground = color.pixel; ++ gc=XCreateGC(dpy,win,GCFont+GCForeground, &gr_values); ++ ++ /* To prevent "Uninitialized" warnings. */ ++ xsi = NULL; ++ ++ /* ++ * Start formatting and drawing text ++ */ ++ ++ len = strlen(message); ++ ++ /* Max max line length (cut at '\n') */ ++ line_len = 0; ++ k = 0; ++ for (i = j = 0; i < len; i++) { ++ if (message[i] == '\n') { ++ if (i - j > line_len) ++ line_len = i - j; ++ k++; ++ i++; ++ j = i; ++ } ++ } ++ /* If there is only one line */ ++ if (line_len == 0) ++ line_len = len; ++ ++ if (XineramaIsActive(dpy)) { ++ xsi = XineramaQueryScreens(dpy, &i); ++ s_width = xsi[0].width; ++ s_height = xsi[0].height; ++ } else { ++ s_width = DisplayWidth(dpy, screen); ++ s_height = DisplayHeight(dpy, screen); ++ } ++ ++ height = s_height*3/7 - (k*20)/3; ++ width = (s_width - XTextWidth(fontinfo, message, line_len))/2; ++ ++ /* Look for '\n' and print the text between them. */ ++ for (i = j = k = 0; i <= len; i++) { ++ /* i == len is the special case for the last line */ ++ if (i == len || message[i] == '\n') { ++ tab_replace = 0; ++ while (message[j] == '\t' && j < i) { ++ tab_replace++; ++ j++; ++ } ++ ++ XDrawString(dpy, win, gc, width + tab_size*tab_replace, height + 20*k, message + j, i - j); ++ while (i < len && message[i] == '\n') { ++ i++; ++ j = i; ++ k++; ++ } ++ } ++ } ++ ++ /* xsi should not be NULL anyway if Xinerama is active, but to be safe */ ++ if (XineramaIsActive(dpy) && xsi != NULL) ++ XFree(xsi); ++} ++ ++ ++ + static const char * + gethash(void) + { +@@ -194,6 +290,7 @@ readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens, + locks[screen]->win, + locks[screen]->colors[color]); + XClearWindow(dpy, locks[screen]->win); ++ writemessage(dpy, locks[screen]->win, screen); + } + oldc = color; + } +@@ -300,7 +397,7 @@ lockscreen(Display *dpy, struct xrandr *rr, int screen) + static void + usage(void) + { +- die("usage: slock [-v] [cmd [arg ...]]\n"); ++ die("usage: slock [-v] [-f] [-m message] [cmd [arg ...]]\n"); + } + + int +@@ -313,12 +410,25 @@ main(int argc, char **argv) { + gid_t dgid; + const char *hash; + Display *dpy; +- int s, nlocks, nscreens; ++ int i, s, nlocks, nscreens; ++ int count_fonts; ++ char **font_names; + + ARGBEGIN { + case 'v': + fprintf(stderr, "slock-"VERSION"\n"); + return 0; ++ case 'm': ++ message = EARGF(usage()); ++ break; ++ case 'f': ++ if (!(dpy = XOpenDisplay(NULL))) ++ die("slock: cannot open display\n"); ++ font_names = XListFonts(dpy, "*", 10000 /* list 10000 fonts*/, &count_fonts); ++ for (i=0; iwin, s); + nlocks++; +- else ++ } else { + break; ++ } + } + XSync(dpy, 0); + +-- +2.20.1 + diff --git a/slock/slock.1 b/slock/slock.1 new file mode 100755 index 0000000..946165f --- /dev/null +++ b/slock/slock.1 @@ -0,0 +1,46 @@ +.Dd 2016-08-23 +.Dt SLOCK 1 +.Sh NAME +.Nm slock +.Nd simple X screen locker +.Sh SYNOPSIS +.Nm +.Op Fl v +.Op Fl f +.Op Fl m Ar message +.Op Ar cmd Op Ar arg ... +.Sh DESCRIPTION +.Nm +is a simple X screen locker. If provided, +.Ar cmd Op Ar arg ... +is executed after the screen has been locked. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl v +Print version information to stdout and exit. +.It Fl f +List all valid X fonts and exit. +.It Fl m Ar message +Overrides default slock lock message. +.TP +.El +.Sh SECURITY CONSIDERATIONS +To make sure a locked screen can not be bypassed by switching VTs +or killing the X server with Ctrl+Alt+Backspace, it is recommended +to disable both in +.Xr xorg.conf 5 +for maximum security: +.Bd -literal -offset left +Section "ServerFlags" + Option "DontVTSwitch" "True" + Option "DontZap" "True" +EndSection +.Ed +.Sh EXAMPLES +$ +.Nm +/usr/sbin/s2ram +.Sh CUSTOMIZATION +.Nm +can be customized by creating a custom config.h from config.def.h and +(re)compiling the source code. This keeps it fast, secure and simple. diff --git a/slock/slock.c b/slock/slock.c new file mode 100755 index 0000000..4013180 --- /dev/null +++ b/slock/slock.c @@ -0,0 +1,520 @@ +/* See LICENSE file for license details. */ +#define _XOPEN_SOURCE 500 +#if HAVE_SHADOW_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arg.h" +#include "util.h" + +char *argv0; + +/* global count to prevent repeated error messages */ +int count_error = 0; + +enum { + INIT, + INPUT, + FAILED, + NUMCOLS +}; + +struct lock { + int screen; + Window root, win; + Pixmap pmap; + unsigned long colors[NUMCOLS]; +}; + +struct xrandr { + int active; + int evbase; + int errbase; +}; + +#include "config.h" + +static void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +#ifdef __linux__ +#include +#include + +static void +dontkillme(void) +{ + FILE *f; + const char oomfile[] = "/proc/self/oom_score_adj"; + + if (!(f = fopen(oomfile, "w"))) { + if (errno == ENOENT) + return; + die("slock: fopen %s: %s\n", oomfile, strerror(errno)); + } + fprintf(f, "%d", OOM_SCORE_ADJ_MIN); + if (fclose(f)) { + if (errno == EACCES) + die("slock: unable to disable OOM killer. " + "Make sure to suid or sgid slock.\n"); + else + die("slock: fclose %s: %s\n", oomfile, strerror(errno)); + } +} +#endif + +static void +writemessage(Display *dpy, Window win, int screen) +{ + int len, line_len, width, height, s_width, s_height, i, j, k, tab_replace, tab_size; + XGCValues gr_values; + XFontStruct *fontinfo; + XColor color, dummy; + XineramaScreenInfo *xsi; + GC gc; + fontinfo = XLoadQueryFont(dpy, font_name); + + if (fontinfo == NULL) { + if (count_error == 0) { + fprintf(stderr, "slock: Unable to load font \"%s\"\n", font_name); + fprintf(stderr, "slock: Try listing fonts with 'slock -f'\n"); + count_error++; + } + return; + } + + tab_size = 8 * XTextWidth(fontinfo, " ", 1); + + XAllocNamedColor(dpy, DefaultColormap(dpy, screen), + text_color, &color, &dummy); + + gr_values.font = fontinfo->fid; + gr_values.foreground = color.pixel; + gc=XCreateGC(dpy,win,GCFont+GCForeground, &gr_values); + + /* To prevent "Uninitialized" warnings. */ + xsi = NULL; + + /* + * Start formatting and drawing text + */ + + len = strlen(message); + + /* Max max line length (cut at '\n') */ + line_len = 0; + k = 0; + for (i = j = 0; i < len; i++) { + if (message[i] == '\n') { + if (i - j > line_len) + line_len = i - j; + k++; + i++; + j = i; + } + } + /* If there is only one line */ + if (line_len == 0) + line_len = len; + + if (XineramaIsActive(dpy)) { + xsi = XineramaQueryScreens(dpy, &i); + s_width = xsi[0].width; + s_height = xsi[0].height; + } else { + s_width = DisplayWidth(dpy, screen); + s_height = DisplayHeight(dpy, screen); + } + + height = s_height*3/7 - (k*20)/3; + width = (s_width - XTextWidth(fontinfo, message, line_len))/2; + + /* Look for '\n' and print the text between them. */ + for (i = j = k = 0; i <= len; i++) { + /* i == len is the special case for the last line */ + if (i == len || message[i] == '\n') { + tab_replace = 0; + while (message[j] == '\t' && j < i) { + tab_replace++; + j++; + } + + XDrawString(dpy, win, gc, width + tab_size*tab_replace, height + 20*k, message + j, i - j); + while (i < len && message[i] == '\n') { + i++; + j = i; + k++; + } + } + } + + /* xsi should not be NULL anyway if Xinerama is active, but to be safe */ + if (XineramaIsActive(dpy) && xsi != NULL) + XFree(xsi); +} + + + +static const char * +gethash(void) +{ + const char *hash; + struct passwd *pw; + + /* Check if the current user has a password entry */ + errno = 0; + if (!(pw = getpwuid(getuid()))) { + if (errno) + die("slock: getpwuid: %s\n", strerror(errno)); + else + die("slock: cannot retrieve password entry\n"); + } + hash = pw->pw_passwd; + +#if HAVE_SHADOW_H + if (!strcmp(hash, "x")) { + struct spwd *sp; + if (!(sp = getspnam(pw->pw_name))) + die("slock: getspnam: cannot retrieve shadow entry. " + "Make sure to suid or sgid slock.\n"); + hash = sp->sp_pwdp; + } +#else + if (!strcmp(hash, "*")) { +#ifdef __OpenBSD__ + if (!(pw = getpwuid_shadow(getuid()))) + die("slock: getpwnam_shadow: cannot retrieve shadow entry. " + "Make sure to suid or sgid slock.\n"); + hash = pw->pw_passwd; +#else + die("slock: getpwuid: cannot retrieve shadow entry. " + "Make sure to suid or sgid slock.\n"); +#endif /* __OpenBSD__ */ + } +#endif /* HAVE_SHADOW_H */ + + return hash; +} + +static void +readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens, + const char *hash) +{ + XRRScreenChangeNotifyEvent *rre; + char buf[32], passwd[256], *inputhash; + int num, screen, running, failure, oldc; + unsigned int len, color; + KeySym ksym; + XEvent ev; + + len = 0; + running = 1; + failure = 0; + oldc = INIT; + + while (running && !XNextEvent(dpy, &ev)) { + if (ev.type == KeyPress) { + explicit_bzero(&buf, sizeof(buf)); + num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0); + if (IsKeypadKey(ksym)) { + if (ksym == XK_KP_Enter) + ksym = XK_Return; + else if (ksym >= XK_KP_0 && ksym <= XK_KP_9) + ksym = (ksym - XK_KP_0) + XK_0; + } + if (IsFunctionKey(ksym) || + IsKeypadKey(ksym) || + IsMiscFunctionKey(ksym) || + IsPFKey(ksym) || + IsPrivateKeypadKey(ksym)) + continue; + switch (ksym) { + case XF86XK_AudioPlay: + case XF86XK_AudioStop: + case XF86XK_AudioPrev: + case XF86XK_AudioNext: + case XF86XK_AudioRaiseVolume: + case XF86XK_AudioLowerVolume: + case XF86XK_AudioMute: + case XF86XK_AudioMicMute: + case XF86XK_MonBrightnessDown: + case XF86XK_MonBrightnessUp: + XSendEvent(dpy, DefaultRootWindow(dpy), True, KeyPressMask, &ev); + break; + case XK_Return: + passwd[len] = '\0'; + errno = 0; + if (!(inputhash = crypt(passwd, hash))) + fprintf(stderr, "slock: crypt: %s\n", strerror(errno)); + else + running = !!strcmp(inputhash, hash); + if (running) { + XBell(dpy, 100); + failure = 1; + } + explicit_bzero(&passwd, sizeof(passwd)); + len = 0; + break; + case XK_Escape: + explicit_bzero(&passwd, sizeof(passwd)); + len = 0; + break; + case XK_BackSpace: + if (len) + passwd[--len] = '\0'; + break; + default: + if (num && !iscntrl((int)buf[0]) && + (len + num < sizeof(passwd))) { + memcpy(passwd + len, buf, num); + len += num; + } + break; + } + color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT); + if (running && oldc != color) { + for (screen = 0; screen < nscreens; screen++) { + XSetWindowBackground(dpy, + locks[screen]->win, + locks[screen]->colors[color]); + XClearWindow(dpy, locks[screen]->win); + writemessage(dpy, locks[screen]->win, screen); + } + oldc = color; + } + } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) { + rre = (XRRScreenChangeNotifyEvent*)&ev; + for (screen = 0; screen < nscreens; screen++) { + if (locks[screen]->win == rre->window) { + if (rre->rotation == RR_Rotate_90 || + rre->rotation == RR_Rotate_270) + XResizeWindow(dpy, locks[screen]->win, + rre->height, rre->width); + else + XResizeWindow(dpy, locks[screen]->win, + rre->width, rre->height); + XClearWindow(dpy, locks[screen]->win); + break; + } + } + } else { + for (screen = 0; screen < nscreens; screen++) + XRaiseWindow(dpy, locks[screen]->win); + } + } +} + +static struct lock * +lockscreen(Display *dpy, struct xrandr *rr, int screen) +{ + char curs[] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, ptgrab, kbgrab; + struct lock *lock; + XColor color, dummy; + XSetWindowAttributes wa; + Cursor invisible; + + if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock)))) + return NULL; + + lock->screen = screen; + lock->root = RootWindow(dpy, lock->screen); + + for (i = 0; i < NUMCOLS; i++) { + XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen), + colorname[i], &color, &dummy); + lock->colors[i] = color.pixel; + } + + /* init */ + wa.override_redirect = 1; + wa.background_pixel = lock->colors[INIT]; + lock->win = XCreateWindow(dpy, lock->root, 0, 0, + DisplayWidth(dpy, lock->screen), + DisplayHeight(dpy, lock->screen), + 0, DefaultDepth(dpy, lock->screen), + CopyFromParent, + DefaultVisual(dpy, lock->screen), + CWOverrideRedirect | CWBackPixel, &wa); + lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8); + invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap, + &color, &color, 0, 0); + XDefineCursor(dpy, lock->win, invisible); + + /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */ + for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) { + if (ptgrab != GrabSuccess) { + ptgrab = XGrabPointer(dpy, lock->root, False, + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask, GrabModeAsync, + GrabModeAsync, None, invisible, CurrentTime); + } + if (kbgrab != GrabSuccess) { + kbgrab = XGrabKeyboard(dpy, lock->root, True, + GrabModeAsync, GrabModeAsync, CurrentTime); + } + + /* input is grabbed: we can lock the screen */ + if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) { + XMapRaised(dpy, lock->win); + if (rr->active) + XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask); + + XSelectInput(dpy, lock->root, SubstructureNotifyMask); + return lock; + } + + /* retry on AlreadyGrabbed but fail on other errors */ + if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) || + (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess)) + break; + + usleep(100000); + } + + /* we couldn't grab all input: fail out */ + if (ptgrab != GrabSuccess) + fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n", + screen); + if (kbgrab != GrabSuccess) + fprintf(stderr, "slock: unable to grab keyboard for screen %d\n", + screen); + return NULL; +} + +static void +usage(void) +{ + die("usage: slock [-v] [-f] [-m message] [cmd [arg ...]]\n"); +} + +int +main(int argc, char **argv) { + struct xrandr rr; + struct lock **locks; + struct passwd *pwd; + struct group *grp; + uid_t duid; + gid_t dgid; + const char *hash; + Display *dpy; + int i, s, nlocks, nscreens; + int count_fonts; + char **font_names; + + ARGBEGIN { + case 'v': + fprintf(stderr, "slock-"VERSION"\n"); + return 0; + case 'm': + message = EARGF(usage()); + break; + case 'f': + if (!(dpy = XOpenDisplay(NULL))) + die("slock: cannot open display\n"); + font_names = XListFonts(dpy, "*", 10000 /* list 10000 fonts*/, &count_fonts); + for (i=0; ipw_uid; + errno = 0; + if (!(grp = getgrnam(group))) + die("slock: getgrnam %s: %s\n", group, + errno ? strerror(errno) : "group entry not found"); + dgid = grp->gr_gid; + +#ifdef __linux__ + dontkillme(); +#endif + + hash = gethash(); + errno = 0; + if (!crypt("", hash)) + die("slock: crypt: %s\n", strerror(errno)); + + if (!(dpy = XOpenDisplay(NULL))) + die("slock: cannot open display\n"); + + /* drop privileges */ + if (setgroups(0, NULL) < 0) + die("slock: setgroups: %s\n", strerror(errno)); + if (setgid(dgid) < 0) + die("slock: setgid: %s\n", strerror(errno)); + if (setuid(duid) < 0) + die("slock: setuid: %s\n", strerror(errno)); + + /* check for Xrandr support */ + rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase); + + /* get number of screens in display "dpy" and blank them */ + nscreens = ScreenCount(dpy); + if (!(locks = calloc(nscreens, sizeof(struct lock *)))) + die("slock: out of memory\n"); + for (nlocks = 0, s = 0; s < nscreens; s++) { + if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL) { + writemessage(dpy, locks[s]->win, s); + nlocks++; + } else { + break; + } + } + XSync(dpy, 0); + + /* did we manage to lock everything? */ + if (nlocks != nscreens) + return 1; + + /* run post-lock command */ + if (argc > 0) { + switch (fork()) { + case -1: + die("slock: fork failed: %s\n", strerror(errno)); + case 0: + if (close(ConnectionNumber(dpy)) < 0) + die("slock: close: %s\n", strerror(errno)); + execvp(argv[0], argv); + fprintf(stderr, "slock: execvp %s: %s\n", argv[0], strerror(errno)); + _exit(1); + } + } + + /* everything is now blank. Wait for the correct password */ + readpw(dpy, &rr, locks, nscreens, hash); + + return 0; +} diff --git a/slock/slock.c.orig b/slock/slock.c.orig new file mode 100755 index 0000000..bd54be2 --- /dev/null +++ b/slock/slock.c.orig @@ -0,0 +1,408 @@ +/* See LICENSE file for license details. */ +#define _XOPEN_SOURCE 500 +#if HAVE_SHADOW_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arg.h" +#include "util.h" + +char *argv0; + +enum { + INIT, + INPUT, + FAILED, + NUMCOLS +}; + +struct lock { + int screen; + Window root, win; + Pixmap pmap; + unsigned long colors[NUMCOLS]; +}; + +struct xrandr { + int active; + int evbase; + int errbase; +}; + +#include "config.h" + +static void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +#ifdef __linux__ +#include +#include + +static void +dontkillme(void) +{ + FILE *f; + const char oomfile[] = "/proc/self/oom_score_adj"; + + if (!(f = fopen(oomfile, "w"))) { + if (errno == ENOENT) + return; + die("slock: fopen %s: %s\n", oomfile, strerror(errno)); + } + fprintf(f, "%d", OOM_SCORE_ADJ_MIN); + if (fclose(f)) { + if (errno == EACCES) + die("slock: unable to disable OOM killer. " + "Make sure to suid or sgid slock.\n"); + else + die("slock: fclose %s: %s\n", oomfile, strerror(errno)); + } +} +#endif + +static const char * +gethash(void) +{ + const char *hash; + struct passwd *pw; + + /* Check if the current user has a password entry */ + errno = 0; + if (!(pw = getpwuid(getuid()))) { + if (errno) + die("slock: getpwuid: %s\n", strerror(errno)); + else + die("slock: cannot retrieve password entry\n"); + } + hash = pw->pw_passwd; + +#if HAVE_SHADOW_H + if (!strcmp(hash, "x")) { + struct spwd *sp; + if (!(sp = getspnam(pw->pw_name))) + die("slock: getspnam: cannot retrieve shadow entry. " + "Make sure to suid or sgid slock.\n"); + hash = sp->sp_pwdp; + } +#else + if (!strcmp(hash, "*")) { +#ifdef __OpenBSD__ + if (!(pw = getpwuid_shadow(getuid()))) + die("slock: getpwnam_shadow: cannot retrieve shadow entry. " + "Make sure to suid or sgid slock.\n"); + hash = pw->pw_passwd; +#else + die("slock: getpwuid: cannot retrieve shadow entry. " + "Make sure to suid or sgid slock.\n"); +#endif /* __OpenBSD__ */ + } +#endif /* HAVE_SHADOW_H */ + + return hash; +} + +static void +readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens, + const char *hash) +{ + XRRScreenChangeNotifyEvent *rre; + char buf[32], passwd[256], *inputhash; + int num, screen, running, failure, oldc; + unsigned int len, color; + KeySym ksym; + XEvent ev; + + len = 0; + running = 1; + failure = 0; + oldc = INIT; + + while (running && !XNextEvent(dpy, &ev)) { + if (ev.type == KeyPress) { + explicit_bzero(&buf, sizeof(buf)); + num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0); + if (IsKeypadKey(ksym)) { + if (ksym == XK_KP_Enter) + ksym = XK_Return; + else if (ksym >= XK_KP_0 && ksym <= XK_KP_9) + ksym = (ksym - XK_KP_0) + XK_0; + } + if (IsFunctionKey(ksym) || + IsKeypadKey(ksym) || + IsMiscFunctionKey(ksym) || + IsPFKey(ksym) || + IsPrivateKeypadKey(ksym)) + continue; + switch (ksym) { + case XF86XK_AudioPlay: + case XF86XK_AudioStop: + case XF86XK_AudioPrev: + case XF86XK_AudioNext: + case XF86XK_AudioRaiseVolume: + case XF86XK_AudioLowerVolume: + case XF86XK_AudioMute: + case XF86XK_AudioMicMute: + case XF86XK_MonBrightnessDown: + case XF86XK_MonBrightnessUp: + XSendEvent(dpy, DefaultRootWindow(dpy), True, KeyPressMask, &ev); + break; + case XK_Return: + passwd[len] = '\0'; + errno = 0; + if (!(inputhash = crypt(passwd, hash))) + fprintf(stderr, "slock: crypt: %s\n", strerror(errno)); + else + running = !!strcmp(inputhash, hash); + if (running) { + XBell(dpy, 100); + failure = 1; + } + explicit_bzero(&passwd, sizeof(passwd)); + len = 0; + break; + case XK_Escape: + explicit_bzero(&passwd, sizeof(passwd)); + len = 0; + break; + case XK_BackSpace: + if (len) + passwd[--len] = '\0'; + break; + default: + if (num && !iscntrl((int)buf[0]) && + (len + num < sizeof(passwd))) { + memcpy(passwd + len, buf, num); + len += num; + } + break; + } + color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT); + if (running && oldc != color) { + for (screen = 0; screen < nscreens; screen++) { + XSetWindowBackground(dpy, + locks[screen]->win, + locks[screen]->colors[color]); + XClearWindow(dpy, locks[screen]->win); + } + oldc = color; + } + } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) { + rre = (XRRScreenChangeNotifyEvent*)&ev; + for (screen = 0; screen < nscreens; screen++) { + if (locks[screen]->win == rre->window) { + if (rre->rotation == RR_Rotate_90 || + rre->rotation == RR_Rotate_270) + XResizeWindow(dpy, locks[screen]->win, + rre->height, rre->width); + else + XResizeWindow(dpy, locks[screen]->win, + rre->width, rre->height); + XClearWindow(dpy, locks[screen]->win); + break; + } + } + } else { + for (screen = 0; screen < nscreens; screen++) + XRaiseWindow(dpy, locks[screen]->win); + } + } +} + +static struct lock * +lockscreen(Display *dpy, struct xrandr *rr, int screen) +{ + char curs[] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, ptgrab, kbgrab; + struct lock *lock; + XColor color, dummy; + XSetWindowAttributes wa; + Cursor invisible; + + if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock)))) + return NULL; + + lock->screen = screen; + lock->root = RootWindow(dpy, lock->screen); + + for (i = 0; i < NUMCOLS; i++) { + XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen), + colorname[i], &color, &dummy); + lock->colors[i] = color.pixel; + } + + /* init */ + wa.override_redirect = 1; + wa.background_pixel = lock->colors[INIT]; + lock->win = XCreateWindow(dpy, lock->root, 0, 0, + DisplayWidth(dpy, lock->screen), + DisplayHeight(dpy, lock->screen), + 0, DefaultDepth(dpy, lock->screen), + CopyFromParent, + DefaultVisual(dpy, lock->screen), + CWOverrideRedirect | CWBackPixel, &wa); + lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8); + invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap, + &color, &color, 0, 0); + XDefineCursor(dpy, lock->win, invisible); + + /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */ + for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) { + if (ptgrab != GrabSuccess) { + ptgrab = XGrabPointer(dpy, lock->root, False, + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask, GrabModeAsync, + GrabModeAsync, None, invisible, CurrentTime); + } + if (kbgrab != GrabSuccess) { + kbgrab = XGrabKeyboard(dpy, lock->root, True, + GrabModeAsync, GrabModeAsync, CurrentTime); + } + + /* input is grabbed: we can lock the screen */ + if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) { + XMapRaised(dpy, lock->win); + if (rr->active) + XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask); + + XSelectInput(dpy, lock->root, SubstructureNotifyMask); + return lock; + } + + /* retry on AlreadyGrabbed but fail on other errors */ + if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) || + (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess)) + break; + + usleep(100000); + } + + /* we couldn't grab all input: fail out */ + if (ptgrab != GrabSuccess) + fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n", + screen); + if (kbgrab != GrabSuccess) + fprintf(stderr, "slock: unable to grab keyboard for screen %d\n", + screen); + return NULL; +} + +static void +usage(void) +{ + die("usage: slock [-v] [cmd [arg ...]]\n"); +} + +int +main(int argc, char **argv) { + struct xrandr rr; + struct lock **locks; + struct passwd *pwd; + struct group *grp; + uid_t duid; + gid_t dgid; + const char *hash; + Display *dpy; + int s, nlocks, nscreens; + + ARGBEGIN { + case 'v': + fprintf(stderr, "slock-"VERSION"\n"); + return 0; + default: + usage(); + } ARGEND + + /* validate drop-user and -group */ + errno = 0; + if (!(pwd = getpwnam(user))) + die("slock: getpwnam %s: %s\n", user, + errno ? strerror(errno) : "user entry not found"); + duid = pwd->pw_uid; + errno = 0; + if (!(grp = getgrnam(group))) + die("slock: getgrnam %s: %s\n", group, + errno ? strerror(errno) : "group entry not found"); + dgid = grp->gr_gid; + +#ifdef __linux__ + dontkillme(); +#endif + + hash = gethash(); + errno = 0; + if (!crypt("", hash)) + die("slock: crypt: %s\n", strerror(errno)); + + if (!(dpy = XOpenDisplay(NULL))) + die("slock: cannot open display\n"); + + /* drop privileges */ + if (setgroups(0, NULL) < 0) + die("slock: setgroups: %s\n", strerror(errno)); + if (setgid(dgid) < 0) + die("slock: setgid: %s\n", strerror(errno)); + if (setuid(duid) < 0) + die("slock: setuid: %s\n", strerror(errno)); + + /* check for Xrandr support */ + rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase); + + /* get number of screens in display "dpy" and blank them */ + nscreens = ScreenCount(dpy); + if (!(locks = calloc(nscreens, sizeof(struct lock *)))) + die("slock: out of memory\n"); + for (nlocks = 0, s = 0; s < nscreens; s++) { + if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL) + nlocks++; + else + break; + } + XSync(dpy, 0); + + /* did we manage to lock everything? */ + if (nlocks != nscreens) + return 1; + + /* run post-lock command */ + if (argc > 0) { + switch (fork()) { + case -1: + die("slock: fork failed: %s\n", strerror(errno)); + case 0: + if (close(ConnectionNumber(dpy)) < 0) + die("slock: close: %s\n", strerror(errno)); + execvp(argv[0], argv); + fprintf(stderr, "slock: execvp %s: %s\n", argv[0], strerror(errno)); + _exit(1); + } + } + + /* everything is now blank. Wait for the correct password */ + readpw(dpy, &rr, locks, nscreens, hash); + + return 0; +} diff --git a/slock/slock.o b/slock/slock.o new file mode 100644 index 0000000000000000000000000000000000000000..2452d0af2a36f4bc0ead2274478befd3e32505b1 GIT binary patch literal 14624 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