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