commit 84c06e3578afe49f45bdcfff1beb66f69b7e7994 Author: wiebel Date: Tue Mar 12 22:36:44 2024 +0100 first commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d221f09 --- /dev/null +++ b/LICENSE @@ -0,0 +1,37 @@ +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 + +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/Makefile b/Makefile new file mode 100644 index 0000000..5ce1953 --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ +# 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} + +# FreeBSD users, prefix all ifdef, else and endif statements with a . for this to work (e.g. .ifdef) + +ifdef YAJLLIBS +all: options dwm dwm-msg +else +all: options dwm +endif + +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} + +ifdef YAJLLIBS +dwm-msg: + ${CC} -o $@ patch/ipc/dwm-msg.c ${LDFLAGS} +endif + +clean: + rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz + rm -f dwm-msg + +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 +ifdef YAJLLIBS + cp -f dwm-msg ${DESTDIR}${PREFIX}/bin +endif + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm +ifdef YAJLLIBS + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm-msg +endif + 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/README b/README new file mode 100644 index 0000000..95d4fd0 --- /dev/null +++ b/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/config.def.h b/config.def.h new file mode 100644 index 0000000..11c5ebf --- /dev/null +++ b/config.def.h @@ -0,0 +1,345 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 1; /* border pixel of windows */ +static const unsigned int snap = 32; /* snap pixel */ +static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ +static const unsigned int gappih = 20; /* horiz inner gap between windows */ +static const unsigned int gappiv = 10; /* vert inner gap between windows */ +static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ +static const unsigned int gappov = 30; /* vert outer gap between windows and screen edge */ +static const int smartgaps_fact = 1; /* gap factor when there is only one client; 0 = no gaps, 3 = 3x outer gaps */ +static const char autostartblocksh[] = "autostart_blocking.sh"; +static const char autostartsh[] = "autostart.sh"; +static const char dwmdir[] = "dwm"; +static const char localshare[] = ".local/share"; +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +/* Status is to be shown on: -1 (all monitors), 0 (a specific monitor by index), 'A' (active monitor) */ +static const int statusmon = 'A'; +static const unsigned int systrayspacing = 2; /* systray spacing */ +static const int showsystray = 1; /* 0 means no systray */ +/* Indicators: see patch/bar_indicators.h for options */ +static int tagindicatortype = INDICATOR_TOP_LEFT_SQUARE; +static int tiledindicatortype = INDICATOR_NONE; +static int floatindicatortype = INDICATOR_TOP_LEFT_SQUARE; +static int fakefsindicatortype = INDICATOR_PLUS; +static int floatfakefsindicatortype = INDICATOR_PLUS_AND_LARGER_SQUARE; +static void (*bartabmonfns[])(Monitor *) = { monocle /* , customlayoutfn */ }; +static const char *fonts[] = { "monospace:size=10" }; +static const char dmenufont[] = "monospace:size=10"; + +static char c000000[] = "#000000"; // placeholder value + +static char normfgcolor[] = "#bbbbbb"; +static char normbgcolor[] = "#222222"; +static char normbordercolor[] = "#444444"; +static char normfloatcolor[] = "#db8fd9"; + +static char selfgcolor[] = "#eeeeee"; +static char selbgcolor[] = "#005577"; +static char selbordercolor[] = "#005577"; +static char selfloatcolor[] = "#005577"; + +static char titlenormfgcolor[] = "#bbbbbb"; +static char titlenormbgcolor[] = "#222222"; +static char titlenormbordercolor[] = "#444444"; +static char titlenormfloatcolor[] = "#db8fd9"; + +static char titleselfgcolor[] = "#eeeeee"; +static char titleselbgcolor[] = "#005577"; +static char titleselbordercolor[] = "#005577"; +static char titleselfloatcolor[] = "#005577"; + +static char tagsnormfgcolor[] = "#bbbbbb"; +static char tagsnormbgcolor[] = "#222222"; +static char tagsnormbordercolor[] = "#444444"; +static char tagsnormfloatcolor[] = "#db8fd9"; + +static char tagsselfgcolor[] = "#eeeeee"; +static char tagsselbgcolor[] = "#005577"; +static char tagsselbordercolor[] = "#005577"; +static char tagsselfloatcolor[] = "#005577"; + +static char hidnormfgcolor[] = "#005577"; +static char hidselfgcolor[] = "#227799"; +static char hidnormbgcolor[] = "#222222"; +static char hidselbgcolor[] = "#222222"; + +static char urgfgcolor[] = "#bbbbbb"; +static char urgbgcolor[] = "#222222"; +static char urgbordercolor[] = "#ff0000"; +static char urgfloatcolor[] = "#db8fd9"; + + +static const unsigned int baralpha = 0xd0; +static const unsigned int borderalpha = OPAQUE; +static const unsigned int alphas[][3] = { + /* fg bg border */ + [SchemeNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeTitleNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeTitleSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeTagsNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeTagsSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeHidNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeHidSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeUrg] = { OPAQUE, baralpha, borderalpha }, +}; + +static char *colors[][ColCount] = { + /* fg bg border float */ + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor, normfloatcolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor, selfloatcolor }, + [SchemeTitleNorm] = { titlenormfgcolor, titlenormbgcolor, titlenormbordercolor, titlenormfloatcolor }, + [SchemeTitleSel] = { titleselfgcolor, titleselbgcolor, titleselbordercolor, titleselfloatcolor }, + [SchemeTagsNorm] = { tagsnormfgcolor, tagsnormbgcolor, tagsnormbordercolor, tagsnormfloatcolor }, + [SchemeTagsSel] = { tagsselfgcolor, tagsselbgcolor, tagsselbordercolor, tagsselfloatcolor }, + [SchemeHidNorm] = { hidnormfgcolor, hidnormbgcolor, c000000, c000000 }, + [SchemeHidSel] = { hidselfgcolor, hidselbgcolor, c000000, c000000 }, + [SchemeUrg] = { urgfgcolor, urgbgcolor, urgbordercolor, urgfloatcolor }, +}; + + + + + +/* Tags + * In a traditional dwm the number of tags in use can be changed simply by changing the number + * of strings in the tags array. This build does things a bit different which has some added + * benefits. If you need to change the number of tags here then change the NUMTAGS macro in dwm.c. + * + * Examples: + * + * 1) static char *tagicons[][NUMTAGS*2] = { + * [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I" }, + * } + * + * 2) static char *tagicons[][1] = { + * [DEFAULT_TAGS] = { "•" }, + * } + * + * The first example would result in the tags on the first monitor to be 1 through 9, while the + * tags for the second monitor would be named A through I. A third monitor would start again at + * 1 through 9 while the tags on a fourth monitor would also be named A through I. Note the tags + * count of NUMTAGS*2 in the array initialiser which defines how many tag text / icon exists in + * the array. This can be changed to *3 to add separate icons for a third monitor. + * + * For the second example each tag would be represented as a bullet point. Both cases work the + * same from a technical standpoint - the icon index is derived from the tag index and the monitor + * index. If the icon index is is greater than the number of tag icons then it will wrap around + * until it an icon matches. Similarly if there are two tag icons then it would alternate between + * them. This works seamlessly with alternative tags and alttagsdecoration patches. + */ +static char *tagicons[][NUMTAGS] = { + [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + [ALTERNATIVE_TAGS] = { "A", "B", "C", "D", "E", "F", "G", "H", "I" }, + [ALT_TAGS_DECORATION] = { "<1>", "<2>", "<3>", "<4>", "<5>", "<6>", "<7>", "<8>", "<9>" }, +}; + + +/* There are two options when it comes to per-client rules: + * - a typical struct table or + * - using the RULE macro + * + * A traditional struct table looks like this: + * // class instance title wintype tags mask isfloating monitor + * { "Gimp", NULL, NULL, NULL, 1 << 4, 0, -1 }, + * { "Firefox", NULL, NULL, NULL, 1 << 7, 0, -1 }, + * + * The RULE macro has the default values set for each field allowing you to only + * specify the values that are relevant for your rule, e.g. + * + * RULE(.class = "Gimp", .tags = 1 << 4) + * RULE(.class = "Firefox", .tags = 1 << 7) + * + * Refer to the Rule struct definition for the list of available fields depending on + * the patches you enable. + */ +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + * WM_WINDOW_ROLE(STRING) = role + * _NET_WM_WINDOW_TYPE(ATOM) = wintype + */ + RULE(.wintype = WTYPE "DIALOG", .isfloating = 1) + RULE(.wintype = WTYPE "UTILITY", .isfloating = 1) + RULE(.wintype = WTYPE "TOOLBAR", .isfloating = 1) + RULE(.wintype = WTYPE "SPLASH", .isfloating = 1) + RULE(.class = "Gimp", .tags = 1 << 4) + RULE(.class = "Firefox", .tags = 1 << 7) +}; + + + +/* Bar rules allow you to configure what is shown where on the bar, as well as + * introducing your own bar modules. + * + * monitor: + * -1 show on all monitors + * 0 show on monitor 0 + * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) + * bar - bar index, 0 is default, 1 is extrabar + * alignment - how the module is aligned compared to other modules + * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions + * name - does nothing, intended for visual clue and for logging / debugging + */ +static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ + { 0, 0, BAR_ALIGN_LEFT, width_pwrl_tags, draw_pwrl_tags, click_pwrl_tags, "powerline_tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { 0, 0, BAR_ALIGN_RIGHT, width_systray, draw_systray, click_systray, "systray" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, + { statusmon, 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_statuscmd, "status" }, + { -1, 0, BAR_ALIGN_NONE, width_bartabgroups, draw_bartabgroups, click_bartabgroups, "bartabgroups" }, +}; + +/* 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 = 0; /* 1 means respect size hints in tiled resizals */ +static const int decorhints = 1; /* 1 means respect decoration hints */ + +#define FORCE_VSPLIT 1 + + +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, + { "|M|", centeredmaster }, + { ">M>", centeredfloatingmaster }, + { "[\\]", dwindle }, + { "###", nrowgrid }, +}; + + +/* 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", normbgcolor, + "-nf", normfgcolor, + "-sb", selbgcolor, + "-sf", selfgcolor, + NULL +}; +static const char *termcmd[] = { "st", NULL }; + +/* This defines the name of the executable that handles the bar (used for signalling purposes) */ +#define STATUSBAR "dwmblocks" + + +static 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|Mod4Mask, XK_j, inplacerotate, {.i = +2 } }, // same as rotatestack + { MODKEY|Mod4Mask, XK_k, inplacerotate, {.i = -2 } }, // same as reotatestack + { MODKEY|Mod4Mask|ShiftMask, XK_j, inplacerotate, {.i = +1} }, + { MODKEY|Mod4Mask|ShiftMask, XK_k, inplacerotate, {.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_x, transfer, {0} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY|Mod4Mask, XK_u, incrgaps, {.i = +1 } }, + { MODKEY|Mod4Mask|ShiftMask, XK_u, incrgaps, {.i = -1 } }, + { MODKEY|Mod4Mask, XK_i, incrigaps, {.i = +1 } }, + { MODKEY|Mod4Mask|ShiftMask, XK_i, incrigaps, {.i = -1 } }, + { MODKEY|Mod4Mask, XK_o, incrogaps, {.i = +1 } }, + { MODKEY|Mod4Mask|ShiftMask, XK_o, incrogaps, {.i = -1 } }, + { MODKEY|Mod4Mask, XK_6, incrihgaps, {.i = +1 } }, + { MODKEY|Mod4Mask|ShiftMask, XK_6, incrihgaps, {.i = -1 } }, + { MODKEY|Mod4Mask, XK_7, incrivgaps, {.i = +1 } }, + { MODKEY|Mod4Mask|ShiftMask, XK_7, incrivgaps, {.i = -1 } }, + { MODKEY|Mod4Mask, XK_8, incrohgaps, {.i = +1 } }, + { MODKEY|Mod4Mask|ShiftMask, XK_8, incrohgaps, {.i = -1 } }, + { MODKEY|Mod4Mask, XK_9, incrovgaps, {.i = +1 } }, + { MODKEY|Mod4Mask|ShiftMask, XK_9, incrovgaps, {.i = -1 } }, + { MODKEY|Mod4Mask, XK_0, togglegaps, {0} }, + { MODKEY|Mod4Mask|ShiftMask, XK_0, defaultgaps, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ControlMask, XK_z, showhideclient, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY|ShiftMask, XK_q, quit, {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|ShiftMask, XK_y, togglefakefullscreen, {0} }, + { MODKEY|ShiftMask, XK_s, togglesticky, {0} }, + { MODKEY, XK_minus, scratchpad_show, {0} }, + { MODKEY|ShiftMask, XK_minus, scratchpad_hide, {0} }, + { MODKEY, XK_equal, scratchpad_remove, {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_Left, viewtoleft, {0} }, // note keybinding conflict with focusdir + { MODKEY, XK_Right, viewtoright, {0} }, // note keybinding conflict with focusdir + { MODKEY|ShiftMask, XK_Left, tagtoleft, {0} }, + { MODKEY|ShiftMask, XK_Right, tagtoright, {0} }, + { MODKEY|ControlMask, XK_Left, tagandviewtoleft, {0} }, + { MODKEY|ControlMask, XK_Right, tagandviewtoright, {0} }, + { MODKEY|Mod4Mask|ShiftMask, XK_comma, tagallmon, {.i = +1 } }, + { MODKEY|Mod4Mask|ShiftMask, XK_period, tagallmon, {.i = -1 } }, + { MODKEY|Mod4Mask|ControlMask, XK_comma, tagswapmon, {.i = +1 } }, + { MODKEY|Mod4Mask|ControlMask, XK_period, tagswapmon, {.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) +}; + + +/* 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, Button1, togglewin, {0} }, + { ClkWinTitle, 0, Button3, showhideclient, {0} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button1, sigstatusbar, {.i = 1 } }, + { ClkStatusText, 0, Button2, sigstatusbar, {.i = 2 } }, + { ClkStatusText, 0, Button3, sigstatusbar, {.i = 3 } }, + { 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/config.h b/config.h new file mode 100644 index 0000000..92c0a4e --- /dev/null +++ b/config.h @@ -0,0 +1,416 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 2; /* border pixel of windows */ +static const unsigned int snap = 16; /* snap pixel */ +static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ +static const unsigned int gappih = 10; /* horiz inner gap between windows */ +static const unsigned int gappiv = 10; /* vert inner gap between windows */ +static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ +static const unsigned int gappov = 10; /* vert outer gap between windows and screen edge */ +static const int smartgaps_fact = 0; /* 1 means no outer gap when there is only one window */ +static const char autostartblocksh[] = "autostart_blocking.sh"; +static const char autostartsh[] = "autostart.sh"; +static const char dwmdir[] = "dwm"; +static const char localshare[] = ".local/share"; +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +/* Status is to be shown on: -1 (all monitors), 0 (a specific monitor by index), 'A' (active monitor) */ +static const int statusmon = 'A'; +static const unsigned int systrayspacing = 2; /* systray spacing */ +static const int showsystray = 1; /* 0 means no systray */ +/* Indicators: see patch/bar_indicators.h for options */ +static int tagindicatortype = INDICATOR_TOP_LEFT_SQUARE; +static int tiledindicatortype = INDICATOR_NONE; +static int floatindicatortype = INDICATOR_TOP_LEFT_SQUARE; +static int fakefsindicatortype = INDICATOR_PLUS; +static int floatfakefsindicatortype = INDICATOR_PLUS_AND_LARGER_SQUARE; +static void (*bartabmonfns[])(Monitor *) = { monocle /* , customlayoutfn */ }; +static const char *fonts[] = { + "FontAwesome:size=12", + "mononoki:size=12", +}; +static const char dmenufont[] = "mononoki:size=12"; + +static char normfgcolor[] = "#bbbbbb"; +static char normbgcolor[] = "#222222"; +static char normbordercolor[] = "#444444"; +static char normfloatcolor[] = "#db8fd9"; + +static char selfgcolor[] = "#111111"; +static char selbgcolor[] = "#ffd700"; +static char selbordercolor[] = "#ffd700"; +static char selfloatcolor[] = "#ffd700"; + +static char titlenormfgcolor[] = "#bbbbbb"; +static char titlenormbgcolor[] = "#222222"; +static char titlenormbordercolor[] = "#444444"; +static char titlenormfloatcolor[] = "#db8fd9"; + +static char titleselfgcolor[] = "#111111"; +static char titleselbgcolor[] = "#ffd700"; +static char titleselbordercolor[] = "#ffd700"; +static char titleselfloatcolor[] = "#ffd700"; + +static char tagsnormfgcolor[] = "#bbbbbb"; +static char tagsnormbgcolor[] = "#222222"; +static char tagsnormbordercolor[] = "#444444"; +static char tagsnormfloatcolor[] = "#db8fd9"; + +static char tagsselfgcolor[] = "#111111"; +static char tagsselbgcolor[] = "#ffd700"; +static char tagsselbordercolor[] = "#ffd700"; +static char tagsselfloatcolor[] = "#ffd700"; + +static char hidnormfgcolor[] = "#ffd700"; +static char hidnormbgcolor[] = "#222222"; +static char hidselfgcolor[] = "#ffd700"; +static char hidselbgcolor[] = "#ffd700"; +static char hidbordercolor[] = "#ffd700"; +static char hidfloatcolor[] = "#f76e0c"; + +static char urgfgcolor[] = "#bbbbbb"; +static char urgbgcolor[] = "#222222"; +static char urgbordercolor[] = "#ff0000"; +static char urgfloatcolor[] = "#db8fd9"; + + +static const unsigned int baralpha = 0xd0; +static const unsigned int borderalpha = OPAQUE; +static const unsigned int alphas[][3] = { + /* fg bg border */ + [SchemeNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeTitleNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeTitleSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeTagsNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeTagsSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeHidNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeHidSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeUrg] = { OPAQUE, baralpha, borderalpha }, +}; + +static char *colors[][ColCount] = { + /* fg bg border float */ + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor, normfloatcolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor, selfloatcolor }, + [SchemeTitleNorm] = { titlenormfgcolor, titlenormbgcolor, titlenormbordercolor, titlenormfloatcolor }, + [SchemeTitleSel] = { titleselfgcolor, titleselbgcolor, titleselbordercolor, titleselfloatcolor }, + [SchemeTagsNorm] = { tagsnormfgcolor, tagsnormbgcolor, tagsnormbordercolor, tagsnormfloatcolor }, + [SchemeTagsSel] = { tagsselfgcolor, tagsselbgcolor, tagsselbordercolor, tagsselfloatcolor }, + [SchemeHidNorm] = { hidnormfgcolor, hidnormbgcolor, hidbordercolor, hidfloatcolor }, + [SchemeHidSel] = { hidselfgcolor, hidselbgcolor, hidbordercolor, hidfloatcolor }, + [SchemeUrg] = { urgfgcolor, urgbgcolor, urgbordercolor, urgfloatcolor }, +}; + +/* +static char *statuscolors[][ColCount] = { +// fg bg border float + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor, normfloatcolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor, selfloatcolor }, + [SchemeTitleNorm] = { titlenormfgcolor, titlenormbgcolor, titlenormbordercolor, titlenormfloatcolor }, + [SchemeTitleSel] = { titleselfgcolor, titleselbgcolor, titleselbordercolor, titleselfloatcolor }, + [SchemeTagsNorm] = { tagsnormfgcolor, tagsnormbgcolor, tagsnormbordercolor, tagsnormfloatcolor }, + [SchemeTagsSel] = { tagsselfgcolor, tagsselbgcolor, tagsselbordercolor, tagsselfloatcolor }, + [SchemeHidNorm] = { hidnormfgcolor, hidnormbgcolor, hidbordercolor, hidfloatcolor }, + [SchemeHidSel] = { hidselfgcolor, hidselbgcolor, hidbordercolor, hidfloatcolor }, + [SchemeUrg] = { urgfgcolor, urgbgcolor, urgbordercolor, urgfloatcolor }, +}; +*/ + + + +/* Tags + * In a traditional dwm the number of tags in use can be changed simply by changing the number + * of strings in the tags array. This build does things a bit different which has some added + * benefits. If you need to change the number of tags here then change the NUMTAGS macro in dwm.c. + * + * Examples: + * + * 1) static char *tagicons[][NUMTAGS*2] = { + * [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I" }, + * } + * + * 2) static char *tagicons[][1] = { + * [DEFAULT_TAGS] = { "•" }, + * } + * + * The first example would result in the tags on the first monitor to be 1 through 9, while the + * tags for the second monitor would be named A through I. A third monitor would start again at + * 1 through 9 while the tags on a fourth monitor would also be named A through I. Note the tags + * count of NUMTAGS*2 in the array initialiser which defines how many tag text / icon exists in + * the array. This can be changed to *3 to add separate icons for a third monitor. + * + * For the second example each tag would be represented as a bullet point. Both cases work the + * same from a technical standpoint - the icon index is derived from the tag index and the monitor + * index. If the icon index is is greater than the number of tag icons then it will wrap around + * until it an icon matches. Similarly if there are two tag icons then it would alternate between + * them. This works seamlessly with alternative tags and alttagsdecoration patches. + */ +static char *tagicons[][NUMTAGS] = { +// [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + [DEFAULT_TAGS] = { "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉" }, + [ALTERNATIVE_TAGS] = { "A", "B", "C", "D", "E", "F", "G", "H", "I" }, + [ALT_TAGS_DECORATION] = { "<1>", "<2>", "<3>", "<4>", "<5>", "<6>", "<7>", "<8>", "<9>" }, +}; + + + + +/* There are two options when it comes to per-client rules: + * - a typical struct table or + * - using the RULE macro + * + * A traditional struct table looks like this: + * // class instance title wintype tags mask isfloating monitor + * { "Gimp", NULL, NULL, NULL, 1 << 4, 0, -1 }, + * { "Firefox", NULL, NULL, NULL, 1 << 7, 0, -1 }, + * + * The RULE macro has the default values set for each field allowing you to only + * specify the values that are relevant for your rule, e.g. + * + * RULE(.class = "Gimp", .tags = 1 << 4) + * RULE(.class = "Firefox", .tags = 1 << 7) + * + * Refer to the Rule struct definition for the list of available fields depending on + * the patches you enable. + */ +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + * WM_WINDOW_ROLE(STRING) = role + * _NET_WM_WINDOW_TYPE(ATOM) = wintype + */ + RULE(.wintype = WTYPE "DIALOG", .isfloating = 1) + RULE(.wintype = WTYPE "UTILITY", .isfloating = 1) + RULE(.wintype = WTYPE "TOOLBAR", .isfloating = 1) + RULE(.wintype = WTYPE "SPLASH", .isfloating = 1) + RULE(.instance= "main", .class = "Psi+", .tags = 1 << 3) + RULE(.instance= "tabs", .class = "Psi+", .tags = 1 << 3) + RULE(.class = "Claws-mail", .tags = 1 << 3) + RULE(.class = "Zoiper", .tags = 1 << 3) + RULE(.class = "linphone", .tags = 1 << 3) + RULE(.class = "Skype", .tags = 1 << 3) + RULE(.class = "Mattermost", .tags = 1 << 3) + + RULE(.class = "pinentry-qt", .isfloating = 1) + RULE(.class = "pinentry-qt5", .isfloating = 1) + RULE(.class = "Nm-connection-editor", .isfloating = 1) + RULE(.class = "pavucontrol-qt", .isfloating = 0) + RULE(.class = "Firefox", .wintype = WTYPE "NORMAL", .tags = 1 << 2) + + RULE(.class = "Spotify", .tags = 1 << 8) + RULE(.class = "Kasts", .tags = 1 << 8) + + RULE(.class = "st-tilda", .isfloating = 1) + RULE(.class = "st-256color", .isterminal = 1) +}; + + + +/* Bar rules allow you to configure what is shown where on the bar, as well as + * introducing your own bar modules. + * + * monitor: + * -1 show on all monitors + * 0 show on monitor 0 + * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) + * bar - bar index, 0 is default, 1 is extrabar + * alignment - how the module is aligned compared to other modules + * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions + * name - does nothing, intended for visual clue and for logging / debugging + */ +static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ + { -1, 0, BAR_ALIGN_LEFT, width_pwrl_tags, draw_pwrl_tags, click_pwrl_tags, "powerline_tags" }, + //{ -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_systray, draw_systray, click_systray, "systray" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, +// { 'A', 0, BAR_ALIGN_RIGHT, width_pwrl_status, draw_pwrl_status, click_pwrl_status, "powerline_status" }, + { statusmon, 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_statuscmd, "status" }, + { -1, 0, BAR_ALIGN_NONE, width_bartabgroups, draw_bartabgroups, click_bartabgroups, "bartabgroups" }, +}; + +/* 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 = 0; /* 1 means respect size hints in tiled resizals */ +static const int decorhints = 1; /* 1 means respect decoration hints */ + +#define FORCE_VSPLIT 1 + + +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, + { "|M|", centeredmaster }, + { ">M>", centeredfloatingmaster }, + { "[\\]", dwindle }, + { "###", nrowgrid }, +}; + + +/* key definitions */ +#define MODKEY Mod4Mask +#define AltMask Mod1Mask +#define ALTKEY 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", normbgcolor, + "-nf", normfgcolor, + "-sb", selbgcolor, + "-sf", selfgcolor, + NULL +}; +static const char *termcmd[] = { "st", NULL }; +static const char *tmuxcmd[] = { "st", "-c", "st-tmux", "-e", "/home/wiebel/sync/bin/tmux-spawn.sh", NULL }; +// static const char scratchpadname[] = "tilda"; +/* static const char *scratchpadcmd[] = { "st", "-t", scratchpadname, "-g", "120x34", NULL }; */ +// static const char *scratchpadcmd[] = { "st", "-t", scratchpadname, "-c", "st-tilda", "-g", "180x18", "-e", "tmux", "new-session", "-A", "-s", "tilda", NULL }; + +static const char *volup[] = { "/opt/pavolume/pavolume", "volup", "10", NULL }; +static const char *voldn[] = { "/opt/pavolume/pavolume", "voldown", "10", NULL }; +static const char *volmute[] = { "/opt/pavolume/pavolume", "mutetoggle", NULL }; +static const char *playpause[] = { "/usr/bin/playerctl", "play-pause", NULL }; +#include + +/* This defines the name of the executable that handles the bar (used for signalling purposes) */ +#define STATUSBAR "dwmblocks" + + +static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = tmuxcmd } }, + { MODKEY|ControlMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY|ControlMask, XK_s, spawn, SHCMD("transset -a --dec .1") }, + { MODKEY|ControlMask, XK_d, spawn, SHCMD("transset -a --inc .1") }, + { ALTKEY, XK_w, spawn, SHCMD("pass-wrapper.sh") }, + { MODKEY, XK_n, spawn, SHCMD("networkmanager_dmenu") }, +// { MODKEY, XK_grave, spawn, SHCMD("/home/wiebel/sync/bin/st-tilda.sh") }, + { ALTKEY|ControlMask, XK_l, spawn, SHCMD("/usr/local/bin/lock.me") }, + /* Media Keys */ + { 0, XF86XK_MonBrightnessUp, spawn, SHCMD("xbacklight -inc 5") }, + { 0, XF86XK_MonBrightnessDown, spawn, SHCMD("xbacklight -dec 5") }, + { 0, XF86XK_AudioRaiseVolume, spawn, {.v = volup } }, + { 0, XF86XK_AudioLowerVolume, spawn, {.v = voldn } }, + { 0, XF86XK_AudioMute, spawn, {.v = volmute } }, + { 0, XF86XK_AudioPlay, spawn, {.v = playpause } }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY|ShiftMask, XK_j, inplacerotate, {.i = +1} }, + { MODKEY|ShiftMask, XK_k, inplacerotate, {.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_x, transfer, {0} }, + { MODKEY, XK_Return, zoom, {0} }, +// { MODKEY|AltMask, XK_u, incrgaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_u, incrgaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_i, incrigaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_i, incrigaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_o, incrogaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_o, incrogaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_6, incrihgaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_6, incrihgaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_7, incrivgaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_7, incrivgaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_8, incrohgaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_8, incrohgaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_9, incrovgaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_9, incrovgaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_0, togglegaps, {0} }, +// { MODKEY|AltMask|ShiftMask, XK_0, defaultgaps, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ControlMask, XK_z, showhideclient, {0} }, + { MODKEY, XK_q, killclient, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY|ShiftMask, XK_q, quit, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_u, setlayout, {.v = &layouts[3]} }, + { MODKEY, XK_o, setlayout, {.v = &layouts[4]} }, + { MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[5]} }, + { MODKEY, XK_g, setlayout, {.v = &layouts[6]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY|ShiftMask, XK_y, togglefakefullscreen, {0} }, + { MODKEY|ShiftMask, XK_s, togglesticky, {0} }, + { MODKEY, XK_grave, scratchpad_show, {0} }, + { MODKEY, XK_Escape, scratchpad_show, {0} }, + { MODKEY|ShiftMask, XK_grave, scratchpad_hide, {0} }, + { MODKEY|ShiftMask, XK_Escape, scratchpad_hide, {0} }, + { MODKEY|ShiftMask|ControlMask, XK_grave, scratchpad_remove, {0} }, + { MODKEY|ShiftMask|ControlMask, XK_Escape, scratchpad_remove, {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_Left, viewtoleft, {0} }, + { MODKEY, XK_Right, viewtoright, {0} }, + { MODKEY|ShiftMask, XK_Left, tagtoleft, {0} }, + { MODKEY|ShiftMask, XK_Right, tagtoright, {0} }, + { MODKEY|ControlMask, XK_Left, tagandviewtoleft, {0} }, + { MODKEY|ControlMask, XK_Right, tagandviewtoright, {0} }, + { MODKEY|ControlMask, XK_comma, tagallmon, {.i = +1 } }, + { MODKEY|ControlMask, XK_period, tagallmon, {.i = -1 } }, + { MODKEY|ShiftMask|ControlMask, XK_comma, tagswapmon, {.i = +1 } }, + { MODKEY|ShiftMask|ControlMask, XK_period, tagswapmon, {.i = -1 } }, + + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~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) +}; + + +/* 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, Button1, togglewin, {0} }, + { ClkWinTitle, 0, Button3, showhideclient, {0} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button1, sigstatusbar, {.i = 1 } }, + { ClkStatusText, 0, Button2, sigstatusbar, {.i = 2 } }, + { ClkStatusText, 0, Button3, sigstatusbar, {.i = 3 } }, + { 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/config.h.tmp b/config.h.tmp new file mode 100644 index 0000000..e2b1ea2 --- /dev/null +++ b/config.h.tmp @@ -0,0 +1,416 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 2; /* border pixel of windows */ +static const unsigned int snap = 16; /* snap pixel */ +static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ +static const unsigned int gappih = 10; /* horiz inner gap between windows */ +static const unsigned int gappiv = 10; /* vert inner gap between windows */ +static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ +static const unsigned int gappov = 10; /* vert outer gap between windows and screen edge */ +static const int smartgaps_fact = 0; /* 1 means no outer gap when there is only one window */ +static const char autostartblocksh[] = "autostart_blocking.sh"; +static const char autostartsh[] = "autostart.sh"; +static const char dwmdir[] = "dwm"; +static const char localshare[] = ".local/share"; +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +/* Status is to be shown on: -1 (all monitors), 0 (a specific monitor by index), 'A' (active monitor) */ +static const int statusmon = 'A'; +static const unsigned int systrayspacing = 2; /* systray spacing */ +static const int showsystray = 1; /* 0 means no systray */ +/* Indicators: see patch/bar_indicators.h for options */ +static int tagindicatortype = INDICATOR_TOP_LEFT_SQUARE; +static int tiledindicatortype = INDICATOR_NONE; +static int floatindicatortype = INDICATOR_TOP_LEFT_SQUARE; +static int fakefsindicatortype = INDICATOR_PLUS; +static int floatfakefsindicatortype = INDICATOR_PLUS_AND_LARGER_SQUARE; +static void (*bartabmonfns[])(Monitor *) = { monocle /* , customlayoutfn */ }; +static const char *fonts[] = { + "FontAwesome:size=12", + "mononoki:size=12", +}; +static const char dmenufont[] = "mononoki:size=12"; + +static char normfgcolor[] = "#bbbbbb"; +static char normbgcolor[] = "#222222"; +static char normbordercolor[] = "#444444"; +static char normfloatcolor[] = "#db8fd9"; + +static char selfgcolor[] = "#111111"; +static char selbgcolor[] = "#ffd700"; +static char selbordercolor[] = "#ffd700"; +static char selfloatcolor[] = "#ffd700"; + +static char titlenormfgcolor[] = "#bbbbbb"; +static char titlenormbgcolor[] = "#222222"; +static char titlenormbordercolor[] = "#444444"; +static char titlenormfloatcolor[] = "#db8fd9"; + +static char titleselfgcolor[] = "#111111"; +static char titleselbgcolor[] = "#ffd700"; +static char titleselbordercolor[] = "#ffd700"; +static char titleselfloatcolor[] = "#ffd700"; + +static char tagsnormfgcolor[] = "#bbbbbb"; +static char tagsnormbgcolor[] = "#222222"; +static char tagsnormbordercolor[] = "#444444"; +static char tagsnormfloatcolor[] = "#db8fd9"; + +static char tagsselfgcolor[] = "#111111"; +static char tagsselbgcolor[] = "#ffd700"; +static char tagsselbordercolor[] = "#ffd700"; +static char tagsselfloatcolor[] = "#ffd700"; + +static char hidnormfgcolor[] = "#ffd700"; +static char hidnormbgcolor[] = "#222222"; +static char hidselfgcolor[] = "#ffd700"; +static char hidselbgcolor[] = "#ffd700"; +static char hidbordercolor[] = "#ffd700"; +static char hidfloatcolor[] = "#f76e0c"; + +static char urgfgcolor[] = "#bbbbbb"; +static char urgbgcolor[] = "#222222"; +static char urgbordercolor[] = "#ff0000"; +static char urgfloatcolor[] = "#db8fd9"; + + +static const unsigned int baralpha = 0xd0; +static const unsigned int borderalpha = OPAQUE; +static const unsigned int alphas[][3] = { + /* fg bg border */ + [SchemeNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeTitleNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeTitleSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeTagsNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeTagsSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeHidNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeHidSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeUrg] = { OPAQUE, baralpha, borderalpha }, +}; + +static char *colors[][ColCount] = { + /* fg bg border float */ + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor, normfloatcolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor, selfloatcolor }, + [SchemeTitleNorm] = { titlenormfgcolor, titlenormbgcolor, titlenormbordercolor, titlenormfloatcolor }, + [SchemeTitleSel] = { titleselfgcolor, titleselbgcolor, titleselbordercolor, titleselfloatcolor }, + [SchemeTagsNorm] = { tagsnormfgcolor, tagsnormbgcolor, tagsnormbordercolor, tagsnormfloatcolor }, + [SchemeTagsSel] = { tagsselfgcolor, tagsselbgcolor, tagsselbordercolor, tagsselfloatcolor }, + [SchemeHidNorm] = { hidnormfgcolor, hidnormbgcolor, hidbordercolor, hidfloatcolor }, + [SchemeHidSel] = { hidselfgcolor, hidselbgcolor, hidbordercolor, hidfloatcolor }, + [SchemeUrg] = { urgfgcolor, urgbgcolor, urgbordercolor, urgfloatcolor }, +}; + +/* +static char *statuscolors[][ColCount] = { +// fg bg border float + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor, normfloatcolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor, selfloatcolor }, + [SchemeTitleNorm] = { titlenormfgcolor, titlenormbgcolor, titlenormbordercolor, titlenormfloatcolor }, + [SchemeTitleSel] = { titleselfgcolor, titleselbgcolor, titleselbordercolor, titleselfloatcolor }, + [SchemeTagsNorm] = { tagsnormfgcolor, tagsnormbgcolor, tagsnormbordercolor, tagsnormfloatcolor }, + [SchemeTagsSel] = { tagsselfgcolor, tagsselbgcolor, tagsselbordercolor, tagsselfloatcolor }, + [SchemeHidNorm] = { hidnormfgcolor, hidnormbgcolor, hidbordercolor, hidfloatcolor }, + [SchemeHidSel] = { hidselfgcolor, hidselbgcolor, hidbordercolor, hidfloatcolor }, + [SchemeUrg] = { urgfgcolor, urgbgcolor, urgbordercolor, urgfloatcolor }, +}; +*/ + + + +/* Tags + * In a traditional dwm the number of tags in use can be changed simply by changing the number + * of strings in the tags array. This build does things a bit different which has some added + * benefits. If you need to change the number of tags here then change the NUMTAGS macro in dwm.c. + * + * Examples: + * + * 1) static char *tagicons[][NUMTAGS*2] = { + * [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I" }, + * } + * + * 2) static char *tagicons[][1] = { + * [DEFAULT_TAGS] = { "•" }, + * } + * + * The first example would result in the tags on the first monitor to be 1 through 9, while the + * tags for the second monitor would be named A through I. A third monitor would start again at + * 1 through 9 while the tags on a fourth monitor would also be named A through I. Note the tags + * count of NUMTAGS*2 in the array initialiser which defines how many tag text / icon exists in + * the array. This can be changed to *3 to add separate icons for a third monitor. + * + * For the second example each tag would be represented as a bullet point. Both cases work the + * same from a technical standpoint - the icon index is derived from the tag index and the monitor + * index. If the icon index is is greater than the number of tag icons then it will wrap around + * until it an icon matches. Similarly if there are two tag icons then it would alternate between + * them. This works seamlessly with alternative tags and alttagsdecoration patches. + */ +static char *tagicons[][NUMTAGS] = { +// [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + [DEFAULT_TAGS] = { "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉" }, + [ALTERNATIVE_TAGS] = { "A", "B", "C", "D", "E", "F", "G", "H", "I" }, + [ALT_TAGS_DECORATION] = { "<1>", "<2>", "<3>", "<4>", "<5>", "<6>", "<7>", "<8>", "<9>" }, +}; + + + + +/* There are two options when it comes to per-client rules: + * - a typical struct table or + * - using the RULE macro + * + * A traditional struct table looks like this: + * // class instance title wintype tags mask isfloating monitor + * { "Gimp", NULL, NULL, NULL, 1 << 4, 0, -1 }, + * { "Firefox", NULL, NULL, NULL, 1 << 7, 0, -1 }, + * + * The RULE macro has the default values set for each field allowing you to only + * specify the values that are relevant for your rule, e.g. + * + * RULE(.class = "Gimp", .tags = 1 << 4) + * RULE(.class = "Firefox", .tags = 1 << 7) + * + * Refer to the Rule struct definition for the list of available fields depending on + * the patches you enable. + */ +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + * WM_WINDOW_ROLE(STRING) = role + * _NET_WM_WINDOW_TYPE(ATOM) = wintype + */ + RULE(.wintype = WTYPE "DIALOG", .isfloating = 1) + RULE(.wintype = WTYPE "UTILITY", .isfloating = 1) + RULE(.wintype = WTYPE "TOOLBAR", .isfloating = 1) + RULE(.wintype = WTYPE "SPLASH", .isfloating = 1) + RULE(.instance= "main", .class = "Psi+", .tags = 1 << 2) + RULE(.instance= "tabs", .class = "Psi+", .tags = 1 << 2) + RULE(.class = "Claws-mail", .tags = 1 << 2) + RULE(.class = "Zoiper", .tags = 1 << 2) + RULE(.class = "linphone", .tags = 1 << 2) + RULE(.class = "Skype", .tags = 1 << 2) + RULE(.class = "Mattermost", .tags = 1 << 2) + + RULE(.class = "pinentry-qt", .isfloating = 1) + RULE(.class = "pinentry-qt5", .isfloating = 1) + RULE(.class = "Nm-connection-editor", .isfloating = 1) + RULE(.class = "pavucontrol-qt", .isfloating = 0) + RULE(.class = "Gimp", .tags = 1 << 4) + RULE(.class = "Firefox", .wintype = WTYPE "NORMAL", .tags = 1 << 1) + + RULE(.class = "Spotify", .tags = 1 << 7) + + RULE(.class = "st-tilda", .isfloating = 1) + RULE(.class = "st-256color", .isterminal = 1) +}; + + + +/* Bar rules allow you to configure what is shown where on the bar, as well as + * introducing your own bar modules. + * + * monitor: + * -1 show on all monitors + * 0 show on monitor 0 + * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) + * bar - bar index, 0 is default, 1 is extrabar + * alignment - how the module is aligned compared to other modules + * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions + * name - does nothing, intended for visual clue and for logging / debugging + */ +static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc name */ + { -1, 0, BAR_ALIGN_LEFT, width_pwrl_tags, draw_pwrl_tags, click_pwrl_tags, "powerline_tags" }, + //{ -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_systray, draw_systray, click_systray, "systray" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, +// { 'A', 0, BAR_ALIGN_RIGHT, width_pwrl_status, draw_pwrl_status, click_pwrl_status, "powerline_status" }, + { statusmon, 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_statuscmd, "status" }, + { -1, 0, BAR_ALIGN_NONE, width_bartabgroups, draw_bartabgroups, click_bartabgroups, "bartabgroups" }, +}; + +/* 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 = 0; /* 1 means respect size hints in tiled resizals */ +static const int decorhints = 1; /* 1 means respect decoration hints */ + +#define FORCE_VSPLIT 1 + + +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, + { "|M|", centeredmaster }, + { ">M>", centeredfloatingmaster }, + { "[\\]", dwindle }, + { "###", nrowgrid }, +}; + + +/* key definitions */ +#define MODKEY Mod4Mask +#define AltMask Mod1Mask +#define ALTKEY 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", normbgcolor, + "-nf", normfgcolor, + "-sb", selbgcolor, + "-sf", selfgcolor, + NULL +}; +static const char *termcmd[] = { "st", NULL }; +static const char *tmuxcmd[] = { "st", "-c", "st-tmux", "-e", "/home/wiebel/sync/bin/tmux-spawn.sh", NULL }; +// static const char scratchpadname[] = "tilda"; +/* static const char *scratchpadcmd[] = { "st", "-t", scratchpadname, "-g", "120x34", NULL }; */ +// static const char *scratchpadcmd[] = { "st", "-t", scratchpadname, "-c", "st-tilda", "-g", "180x18", "-e", "tmux", "new-session", "-A", "-s", "tilda", NULL }; + +static const char *volup[] = { "/opt/pavolume/pavolume", "volup", "10", NULL }; +static const char *voldn[] = { "/opt/pavolume/pavolume", "voldown", "10", NULL }; +static const char *volmute[] = { "/opt/pavolume/pavolume", "mutetoggle", NULL }; +static const char *playpause[] = { "/usr/bin/playerctl", "play-pause", NULL }; +#include + +/* This defines the name of the executable that handles the bar (used for signalling purposes) */ +#define STATUSBAR "dwmblocks" + + +static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = tmuxcmd } }, + { MODKEY|ControlMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY|ControlMask, XK_s, spawn, SHCMD("transset -a --dec .1") }, + { MODKEY|ControlMask, XK_d, spawn, SHCMD("transset -a --inc .1") }, + { ALTKEY, XK_w, spawn, SHCMD("pass-wrapper.sh") }, + { MODKEY, XK_n, spawn, SHCMD("networkmanager_dmenu") }, +// { MODKEY, XK_grave, spawn, SHCMD("/home/wiebel/sync/bin/st-tilda.sh") }, + { ALTKEY|ControlMask, XK_l, spawn, SHCMD("/usr/local/bin/lock.me") }, + /* Media Keys */ + { 0, XF86XK_MonBrightnessUp, spawn, SHCMD("xbacklight -inc 5") }, + { 0, XF86XK_MonBrightnessDown, spawn, SHCMD("xbacklight -dec 5") }, + { 0, XF86XK_AudioRaiseVolume, spawn, {.v = volup } }, + { 0, XF86XK_AudioLowerVolume, spawn, {.v = voldn } }, + { 0, XF86XK_AudioMute, spawn, {.v = volmute } }, + { 0, XF86XK_AudioPlay, spawn, {.v = playpause } }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY|ShiftMask, XK_j, inplacerotate, {.i = +1} }, + { MODKEY|ShiftMask, XK_k, inplacerotate, {.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_x, transfer, {0} }, + { MODKEY, XK_Return, zoom, {0} }, +// { MODKEY|AltMask, XK_u, incrgaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_u, incrgaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_i, incrigaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_i, incrigaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_o, incrogaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_o, incrogaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_6, incrihgaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_6, incrihgaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_7, incrivgaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_7, incrivgaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_8, incrohgaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_8, incrohgaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_9, incrovgaps, {.i = +1 } }, +// { MODKEY|AltMask|ShiftMask, XK_9, incrovgaps, {.i = -1 } }, +// { MODKEY|AltMask, XK_0, togglegaps, {0} }, +// { MODKEY|AltMask|ShiftMask, XK_0, defaultgaps, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ControlMask, XK_z, showhideclient, {0} }, + { MODKEY, XK_q, killclient, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY|ShiftMask, XK_q, quit, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_u, setlayout, {.v = &layouts[3]} }, + { MODKEY, XK_o, setlayout, {.v = &layouts[4]} }, + { MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[5]} }, + { MODKEY, XK_g, setlayout, {.v = &layouts[6]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY|ShiftMask, XK_y, togglefakefullscreen, {0} }, + { MODKEY|ShiftMask, XK_s, togglesticky, {0} }, + { MODKEY, XK_grave, scratchpad_show, {0} }, + { MODKEY, XK_Escape, scratchpad_show, {0} }, + { MODKEY|ShiftMask, XK_grave, scratchpad_hide, {0} }, + { MODKEY|ShiftMask, XK_Escape, scratchpad_hide, {0} }, + { MODKEY|ShiftMask|ControlMask, XK_grave, scratchpad_remove, {0} }, + { MODKEY|ShiftMask|ControlMask, XK_Escape, scratchpad_remove, {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_Left, viewtoleft, {0} }, + { MODKEY, XK_Right, viewtoright, {0} }, + { MODKEY|ShiftMask, XK_Left, tagtoleft, {0} }, + { MODKEY|ShiftMask, XK_Right, tagtoright, {0} }, + { MODKEY|ControlMask, XK_Left, tagandviewtoleft, {0} }, + { MODKEY|ControlMask, XK_Right, tagandviewtoright, {0} }, + { MODKEY|ControlMask, XK_comma, tagallmon, {.i = +1 } }, + { MODKEY|ControlMask, XK_period, tagallmon, {.i = -1 } }, + { MODKEY|ShiftMask|ControlMask, XK_comma, tagswap:mon, {.i = +1 } }, + { MODKEY|ShiftMask|ControlMask, XK_period, tagswapmon, {.i = -1 } }, + + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~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) +}; + + +/* 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, Button1, togglewin, {0} }, + { ClkWinTitle, 0, Button3, showhideclient, {0} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button1, sigstatusbar, {.i = 1 } }, + { ClkStatusText, 0, Button2, sigstatusbar, {.i = 2 } }, + { ClkStatusText, 0, Button3, sigstatusbar, {.i = 3 } }, + { 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/config.mk b/config.mk new file mode 100644 index 0000000..0b119af --- /dev/null +++ b/config.mk @@ -0,0 +1,70 @@ +# dwm version +VERSION = 6.2 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# FreeBSD (uncomment) +#X11INC = /usr/local/include +#X11LIB = /usr/local/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# FreeBSD (uncomment) +#FREETYPEINC = /usr/local/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = ${X11INC}/freetype2 +# OpenBSD - Uncomment this for the swallow patch / SWALLOW_PATCH +#KVMLIB = -lkvm + +# Uncomment this for the alpha patch / BAR_ALPHA_PATCH +XRENDER = -lXrender + +# Uncomment this for the mdpcontrol patch / MDPCONTROL_PATCH +#MPDCLIENT = -lmpdclient + +# Uncomment for the pango patch / BAR_PANGO_PATCH +#PANGOINC = `pkg-config --cflags xft pango pangoxft` +#PANGOLIB = `pkg-config --libs xft pango pangoxft` + +# Uncomment for the ipc patch / IPC_PATCH +#YAJLLIBS = -lyajl +#YAJLINC = -I/usr/include/yajl + +# Uncomment this for the rounded corners patch / ROUNDED_CORNERS_PATCH +#XEXTLIB = -lXext + +# Uncomment this for the swallow patch / SWALLOW_PATCH +XCBLIBS = -lX11-xcb -lxcb -lxcb-res + +# This is needed for the winicon patch / BAR_WINICON_PATCH +#IMLIB2LIBS = -lImlib2 + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} ${YAJLINC} ${PANGOINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XRENDER} ${MPDCLIENT} ${XEXTLIB} ${XCBLIBS} ${KVMLIB} ${PANGOLIB} ${YAJLLIBS} ${IMLIB2LIBS} + +# 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} +CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -march=native -O3 -flto ${INCS} ${CPPFLAGS} +LDFLAGS = -flto ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/drw.c b/drw.c new file mode 100644 index 0000000..8b2065e --- /dev/null +++ b/drw.c @@ -0,0 +1,490 @@ +/* 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}; + +Clr transcheme[3]; + +static long +utf8decodebyte(const char c, size_t *i) +{ + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t +utf8validate(long *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t +utf8decode(const char *c, long *u, size_t clen) +{ + size_t i, j, len, type; + long udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Drw * +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + drw->root = root; + drw->w = w; + drw->h = h; + + drw->visual = visual; + drw->depth = depth; + drw->cmap = cmap; + drw->drawable = XCreatePixmap(dpy, root, w, h, depth); + drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + drw->w = w; + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth); +} + +void +drw_free(Drw *drw) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + drw_fontset_free(drw->fonts); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) +{ + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + /* Do not allow using color fonts. This is a workaround for a BadLength + * error from Xft with color glyphs. Modelled on the Xterm workaround. See + * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + * https://lists.suckless.org/dev/1701/30932.html + * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 + * and lots more all over the internet. + */ + FcBool iscol; + if (FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { + XftFontClose(drw->dpy, xfont); + return NULL; + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create( + Drw *drw, + Clr *dest, + const char *clrname + , unsigned int alpha +) { + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap, + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); + + dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24); +} + +/* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ +Clr * +drw_scm_create( + Drw *drw, + 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_settrans(Drw *drw, Clr *psc, Clr *nsc) +{ + if (drw) { + transcheme[0] = psc[ColBg]; transcheme[1] = nsc[ColBg]; transcheme[2] = psc[ColBorder]; + drw->scheme = transcheme; + } +} + +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, Bool ignored) +{ + char buf[1024]; + int ty; + unsigned int ew; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + size_t i, len; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0; + + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + return 0; + + if (!render) { + w = ~w; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + while (1) { + utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + } else { + nextfont = curfont; + } + break; + } + } + + if (!charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); + /* shorten text if necessary */ + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) + drw_font_getexts(usedfont, utf8str, len, &ew, NULL); + + if (len) { + memcpy(buf, utf8str, len); + buf[len] = '\0'; + if (len < utf8strlen) + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ + + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)buf, len); + } + x += ew; + w -= ew; + } + } + + if (!*text) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_arrow(Drw *drw, int x, int y, unsigned int w, unsigned int h, int direction, int slash) +{ + if (!drw || !drw->scheme) + return; + + /* direction=1 draws right arrow */ + x = direction ? x : x + w; + w = direction ? w : -w; + /* slash=1 draws slash instead of arrow */ + unsigned int hh = slash ? (direction ? 0 : h) : h/2; + + XPoint points[] = { + {x , y }, + {x + w, y + hh }, + {x , y + h }, + }; + + XPoint bg[] = { + {x , y }, + {x + w, y }, + {x + w, y + h}, + {x , y + h}, + }; + + XSetForeground(drw->dpy, drw->gc, drw->scheme[ColBg].pixel); + XFillPolygon(drw->dpy, drw->drawable, drw->gc, bg, 4, Convex, CoordModeOrigin); + XSetForeground(drw->dpy, drw->gc, drw->scheme[ColFg].pixel); + XFillPolygon(drw->dpy, drw->drawable, drw->gc, points, 3, Nonconvex, CoordModeOrigin); +} + +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, Bool markup) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0, markup); +} + +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/drw.h b/drw.h new file mode 100644 index 0000000..7a9bf84 --- /dev/null +++ b/drw.h @@ -0,0 +1,74 @@ +/* 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, ColFloat, ColCount }; /* 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_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text, Bool markup); + +/* Colorscheme abstraction */ +void drw_clr_create( + Drw *drw, + Clr *dest, + const char *clrname + , unsigned int alpha +); +Clr *drw_scm_create( + Drw *drw, + 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); +void drw_settrans(Drw *drw, Clr *psc, Clr *nsc); + +/* 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, Bool markup); +void drw_arrow(Drw *drw, int x, int y, unsigned int w, unsigned int h, int direction, int slash); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); + diff --git a/drw.o b/drw.o new file mode 100644 index 0000000..4355fba Binary files /dev/null and b/drw.o differ diff --git a/dwm b/dwm new file mode 100755 index 0000000..d76ee6e Binary files /dev/null and b/dwm differ diff --git a/dwm.1 b/dwm.1 new file mode 100644 index 0000000..ddc8321 --- /dev/null +++ b/dwm.1 @@ -0,0 +1,176 @@ +.TH DWM 1 dwm\-VERSION +.SH NAME +dwm \- dynamic window manager +.SH SYNOPSIS +.B dwm +.RB [ \-v ] +.SH DESCRIPTION +dwm is a dynamic window manager for X. It manages windows in tiled, monocle +and floating layouts. Either layout can be applied dynamically, optimising the +environment for the application in use and the task performed. +.P +In tiled layouts windows are managed in a master and stacking area. The master +area on the left contains one window by default, and the stacking area on the +right contains all other windows. The number of master area windows can be +adjusted from zero to an arbitrary number. In monocle layout all windows are +maximised to the screen size. In floating layout windows can be resized and +moved freely. Dialog windows are always managed floating, regardless of the +layout applied. +.P +Windows are grouped by tags. Each window can be tagged with one or multiple +tags. Selecting certain tags displays all windows with these tags. +.P +Each screen contains a small status bar which displays all available tags, the +layout, the title of the focused window, and the text read from the root window +name property, if the screen is focused. A floating window is indicated with an +empty square and a maximised floating window is indicated with a filled square +before the windows title. The selected tags are indicated with a different +color. The tags of the focused window are indicated with a filled square in the +top left corner. The tags which are applied to one or more windows are +indicated with an empty square in the top left corner. +.P +dwm draws a small border around windows to indicate the focus state. +.SH OPTIONS +.TP +.B \-v +prints version information to stderr, then exits. +.SH USAGE +.SS Status bar +.TP +.B X root window name +is read and displayed in the status text area. It can be set with the +.BR xsetroot (1) +command. +.TP +.B Button1 +click on a tag label to display all windows with that tag, click on the layout +label toggles between tiled and floating layout. +.TP +.B Button3 +click on a tag label adds/removes all windows with that tag to/from the view. +.TP +.B Mod1\-Button1 +click on a tag label applies that tag to the focused window. +.TP +.B Mod1\-Button3 +click on a tag label adds/removes that tag to/from the focused window. +.SS Keyboard commands +.TP +.B Mod1\-Shift\-Return +Start +.BR st(1). +.TP +.B Mod1\-p +Spawn +.BR dmenu(1) +for launching other programs. +.TP +.B Mod1\-, +Focus previous screen, if any. +.TP +.B Mod1\-. +Focus next screen, if any. +.TP +.B Mod1\-Shift\-, +Send focused window to previous screen, if any. +.TP +.B Mod1\-Shift\-. +Send focused window to next screen, if any. +.TP +.B Mod1\-b +Toggles bar on and off. +.TP +.B Mod1\-t +Sets tiled layout. +.TP +.B Mod1\-f +Sets floating layout. +.TP +.B Mod1\-m +Sets monocle layout. +.TP +.B Mod1\-space +Toggles between current and previous layout. +.TP +.B Mod1\-j +Focus next window. +.TP +.B Mod1\-k +Focus previous window. +.TP +.B Mod1\-i +Increase number of windows in master area. +.TP +.B Mod1\-d +Decrease number of windows in master area. +.TP +.B Mod1\-l +Increase master area size. +.TP +.B Mod1\-h +Decrease master area size. +.TP +.B Mod1\-Return +Zooms/cycles focused window to/from master area (tiled layouts only). +.TP +.B Mod1\-Shift\-c +Close focused window. +.TP +.B Mod1\-Shift\-space +Toggle focused window between tiled and floating state. +.TP +.B Mod1\-Tab +Toggles to the previously selected tags. +.TP +.B Mod1\-Shift\-[1..n] +Apply nth tag to focused window. +.TP +.B Mod1\-Shift\-0 +Apply all tags to focused window. +.TP +.B Mod1\-Control\-Shift\-[1..n] +Add/remove nth tag to/from focused window. +.TP +.B Mod1\-[1..n] +View all windows with nth tag. +.TP +.B Mod1\-0 +View all windows with any tag. +.TP +.B Mod1\-Control\-[1..n] +Add/remove all windows with nth tag to/from the view. +.TP +.B Mod1\-Shift\-q +Quit dwm. +.SS Mouse commands +.TP +.B Mod1\-Button1 +Move focused window while dragging. Tiled windows will be toggled to the floating state. +.TP +.B Mod1\-Button2 +Toggles focused window between floating and tiled state. +.TP +.B Mod1\-Button3 +Resize focused window while dragging. Tiled windows will be toggled to the floating state. +.SH CUSTOMIZATION +dwm is customized by creating a custom config.h and (re)compiling the source +code. This keeps it fast, secure and simple. +.SH SEE ALSO +.BR dmenu (1), +.BR st (1) +.SH ISSUES +Java applications which use the XToolkit/XAWT backend may draw grey windows +only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early +JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds +are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the +environment variable +.BR AWT_TOOLKIT=MToolkit +(to use the older Motif backend instead) or running +.B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D +or +.B wmname LG3D +(to pretend that a non-reparenting window manager is running that the +XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable +.BR _JAVA_AWT_WM_NONREPARENTING=1 . +.SH BUGS +Send all bug reports with a patch to hackers@suckless.org. diff --git a/dwm.c b/dwm.c new file mode 100644 index 0000000..2418270 --- /dev/null +++ b/dwm.c @@ -0,0 +1,2893 @@ +/* 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 Button6 6 +#define Button7 7 +#define Button8 8 +#define Button9 9 +#define NUMTAGS 9 +#define BARRULES 20 +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define WIDTH(X) ((X)->w + 2 * (X)->bw) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define WTYPE "_NET_WM_WINDOW_TYPE_" +#define TAGMASK ((1 << NUMTAGS) - 1) +#define TEXTWM(X) (drw_fontset_getwidth(drw, (X), True) + lrpad) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X), False) + lrpad) +#define HIDDEN(C) ((getstate(C->win) == IconicState)) + +/* enums */ +enum { + CurResizeBR, + CurResizeBL, + CurResizeTR, + CurResizeTL, + CurNormal, + CurResize, + CurMove, + CurLast +}; /* cursor */ + +enum { + SchemeNorm, + SchemeSel, + SchemeTitleNorm, + SchemeTitleSel, + SchemeTagsNorm, + SchemeTagsSel, + SchemeHidNorm, + SchemeHidSel, + SchemeUrg, +}; /* color schemes */ + +enum { + NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, + NetSystemTrayVisual, NetWMWindowTypeDock, NetSystemTrayOrientationHorz, + NetDesktopNames, NetDesktopViewport, NetNumberOfDesktops, NetCurrentDesktop, + NetClientList, + NetClientListStacking, + NetLast +}; /* EWMH atoms */ + +enum { + WMProtocols, + WMDelete, + WMState, + WMTakeFocus, + WMWindowRole, + WMLast +}; /* default atoms */ + +enum { + ClkTagBar, + ClkLtSymbol, + ClkStatusText, + ClkWinTitle, + ClkClientWin, + ClkRootWin, + ClkLast +}; /* clicks */ + +enum { + BAR_ALIGN_LEFT, + BAR_ALIGN_CENTER, + BAR_ALIGN_RIGHT, + BAR_ALIGN_LEFT_LEFT, + BAR_ALIGN_LEFT_RIGHT, + BAR_ALIGN_LEFT_CENTER, + BAR_ALIGN_NONE, + BAR_ALIGN_RIGHT_LEFT, + BAR_ALIGN_RIGHT_RIGHT, + BAR_ALIGN_RIGHT_CENTER, + BAR_ALIGN_LAST +}; /* bar alignment */ + + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct Monitor Monitor; +typedef struct Bar Bar; +struct Bar { + Window win; + Monitor *mon; + Bar *next; + int idx; + int showbar; + int topbar; + int external; + int borderpx; + int borderscheme; + int bx, by, bw, bh; /* bar geometry */ + int w[BARRULES]; // width, array length == barrules, then use r index for lookup purposes + int x[BARRULES]; // x position, array length == ^ +}; + +typedef struct { + int x; + int y; + int h; + int w; +} BarArg; + +typedef struct { + int monitor; + int bar; + int alignment; // see bar alignment enum + int (*widthfunc)(Bar *bar, BarArg *a); + int (*drawfunc)(Bar *bar, BarArg *a); + int (*clickfunc)(Bar *bar, Arg *arg, BarArg *a); + char *name; // for debugging + int x, w; // position, width for internal use +} BarRule; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + + +typedef struct Client Client; +struct Client { + char name[256]; + float mina, maxa; + int x, y, w, h; + int sfx, sfy, sfw, sfh; /* stored float geometry, used on mode revert */ + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; + unsigned int tags; + unsigned int switchtag; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + int fakefullscreen; + int iscentered; + int ispermanent; + int isterminal, noswallow; + pid_t pid; + int issteam; + int issticky; + Client *next; + Client *snext; + Client *swallowing; + 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; + + +typedef struct Pertag Pertag; +struct Monitor { + int index; + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + int gappih; /* horizontal gap between windows */ + int gappiv; /* vertical gap between windows */ + int gappoh; /* horizontal outer gaps */ + int gappov; /* vertical outer gaps */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + int showbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Bar *bar; + const Layout *lt[2]; + Pertag *pertag; +}; + +typedef struct { + const char *class; + const char *role; + const char *instance; + const char *title; + const char *wintype; + unsigned int tags; + int switchtag; + int iscentered; + int isfloating; + int isfakefullscreen; + int ispermanent; + int isterminal; + int noswallow; + int monitor; +} Rule; + +#define RULE(...) { .monitor = -1, __VA_ARGS__ }, + +/* Cross patch compatibility rule macro helper macros */ +#define FLOATING , .isfloating = 1 +#define CENTERED , .iscentered = 1 +#define PERMANENT , .ispermanent = 1 +#define FAKEFULLSCREEN , .isfakefullscreen = 1 +#define NOSWALLOW , .noswallow = 1 +#define TERMINAL , .isterminal = 1 +#define SWITCHTAG , .switchtag = 1 + + +/* 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 drawbarwin(Bar *bar); +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 motionnotify(XEvent *e); +static void movemouse(const Arg *arg); +static Client *nexttiled(Client *c); +static void pop(Client *); +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(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setlayout(const Arg *arg); +static void setmfact(const Arg *arg); +static void setup(void); +static void seturgent(Client *c, int urg); +static void showhide(Client *c); +static void sigchld(int unused); +static void spawn(const Arg *arg); +static void tag(const Arg *arg); +static void tagmon(const Arg *arg); +static void 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, Client *nextfocus); +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 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); + +/* bar functions */ + +#include "patch/include.h" + +/* variables */ +static const char broken[] = "broken"; +static char stext[512]; +static char rawstext[512]; + +static int screen; +static int sw, sh; /* X display screen geometry width, height */ +static int bh; /* bar geometry */ +static int lrpad; /* sum of left and right padding for text */ +/* Some clients (e.g. alacritty) helpfully send configure requests with a new size or position + * when they detect that they have been moved to another monitor. This can cause visual glitches + * when moving (or resizing) client windows from one monitor to another. This variable is used + * internally to ignore such configure requests while movemouse or resizemouse are being used. */ +static int ignoreconfigurerequests = 0; +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, + [ResizeRequest] = resizerequest, + [UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; +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" + +#include "patch/include.c" + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[NUMTAGS > 30 ? -1 : 1]; }; + +/* function implementations */ +void +applyrules(Client *c) +{ + const char *class, *instance; + Atom wintype; + char role[64]; + unsigned int i; + unsigned int newtagset; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + c->noswallow = -1; + 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; + wintype = getatomprop(c, netatom[NetWMWindowType]); + gettextprop(c->win, wmatom[WMWindowRole], role, sizeof(role)); + + if (strstr(class, "Steam") || strstr(class, "steam_app_")) + c->issteam = 1; + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + && (!r->role || strstr(role, r->role)) + && (!r->instance || strstr(instance, r->instance)) + && (!r->wintype || wintype == XInternAtom(dpy, r->wintype, False))) + { + c->iscentered = r->iscentered; + c->ispermanent = r->ispermanent; + c->fakefullscreen = r->isfakefullscreen; + c->isterminal = r->isterminal; + c->noswallow = r->noswallow; + 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 (r->switchtag && ( + c->noswallow > 0 || + !termforwin(c) || + !(c->isfloating && swallowfloating && c->noswallow < 0))) + { + selmon = c->mon; + if (r->switchtag == 2 || r->switchtag == 4) + newtagset = c->mon->tagset[c->mon->seltags] ^ c->tags; + else + newtagset = c->tags; + + /* Switch to the client's tag, but only if that tag is not already shown */ + if (newtagset && !(c->tags & c->mon->tagset[c->mon->seltags])) { + if (r->switchtag == 3 || r->switchtag == 4) + c->switchtag = c->mon->tagset[c->mon->seltags]; + if (r->switchtag == 1 || r->switchtag == 3) { + pertagview(&((Arg) { .ui = newtagset })); + arrange(c->mon); + } else { + c->mon->tagset[c->mon->seltags] = newtagset; + arrange(c->mon); + } + } + } + } + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + if (c->tags != SCRATCHPAD_MASK) + 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) { + /* 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) +{ + int click, i, r; + Arg arg = {0}; + Client *c; + Monitor *m; + Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; + const BarRule *br; + BarArg carg = { 0, 0, 0, 0 }; + click = ClkRootWin; + + + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon + ) { + unfocus(selmon->sel, 1, NULL); + selmon = m; + focus(NULL); + } + + for (bar = selmon->bar; bar; bar = bar->next) { + if (ev->window == bar->win) { + for (r = 0; r < LENGTH(barrules); r++) { + br = &barrules[r]; + if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) + continue; + if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->index) + continue; + if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { + carg.x = ev->x - bar->x[r]; + carg.y = ev->y - bar->borderpx; + carg.w = bar->w[r]; + carg.h = bar->bh - 2 * bar->borderpx; + click = br->clickfunc(bar, &arg, &carg); + if (click < 0) + return; + break; + } + } + break; + } + } + + + if (click == ClkRootWin && (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 + || click == ClkWinTitle + ) && 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); + if (showsystray && systray) { + while (systray->icons) + removesystrayicon(systray->icons); + if (systray->win) { + XUnmapWindow(dpy, systray->win); + XDestroyWindow(dpy, systray->win); + } + free(systray); + } + 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; + Bar *bar; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + for (bar = mon->bar; bar; bar = mon->bar) { + if (!bar->external) { + XUnmapWindow(dpy, bar->win); + XDestroyWindow(dpy, bar->win); + } + mon->bar = bar->next; + if (systray && bar == systray->bar) + systray->bar = NULL; + free(bar); + } + free(mon->pertag); + free(mon); +} + +void +clientmessage(XEvent *e) +{ + XWindowAttributes wa; + XSetWindowAttributes swa; + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + + if (showsystray && systray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { + /* add systray icons */ + if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { + if (!(c = (Client *)calloc(1, sizeof(Client)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Client)); + if (!(c->win = cme->data.l[2])) { + free(c); + return; + } + + c->mon = selmon; + c->next = systray->icons; + systray->icons = c; + XGetWindowAttributes(dpy, c->win, &wa); + c->x = c->oldx = c->y = c->oldy = 0; + c->w = c->oldw = wa.width; + c->h = c->oldh = wa.height; + c->oldbw = wa.border_width; + c->bw = 0; + c->isfloating = True; + /* reuse tags field as mapped status */ + c->tags = 1; + updatesizehints(c); + updatesystrayicongeom(c, wa.width, wa.height); + XAddToSaveSet(dpy, c->win); + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); + XClassHint ch = {"dwmsystray", "dwmsystray"}; + XSetClassHint(dpy, c->win, &ch); + XReparentWindow(dpy, c->win, systray->win, 0, 0); + /* use parents background color */ + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); + XSync(dpy, False); + setclientstate(c, NormalState); + } + return; + } + + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] + || cme->data.l[2] == netatom[NetWMFullscreen]) { + if (c->fakefullscreen == 2 && c->isfullscreen) + c->fakefullscreen = 3; + 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; + Bar *bar; + 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, sh); + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) + if (c->isfullscreen && c->fakefullscreen != 1) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + for (bar = m->bar; bar; bar = bar->next) + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if (ignoreconfigurerequests) + return; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + if (!c->issteam) { + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +Monitor * +createmon(void) +{ + Monitor *m, *mon; + int i, n, mi, max_bars = 2, istopbar = topbar; + + const BarRule *br; + Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; + m->gappih = gappih; + m->gappiv = gappiv; + m->gappoh = gappoh; + m->gappov = gappov; + for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->index = mi; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + + /* Derive the number of bars for this monitor based on bar rules */ + for (n = -1, i = 0; i < LENGTH(barrules); i++) { + br = &barrules[i]; + if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) + n = MAX(br->bar, n); + } + + m->bar = NULL; + for (i = 0; i <= n && i < max_bars; i++) { + bar = ecalloc(1, sizeof(Bar)); + bar->mon = m; + bar->idx = i; + bar->next = m->bar; + bar->topbar = istopbar; + m->bar = bar; + istopbar = !istopbar; + bar->showbar = 1; + bar->external = 0; + bar->borderpx = 0; + bar->bh = bh + bar->borderpx * 2; + bar->borderscheme = SchemeNorm; + } + + + if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; + for (i = 0; i <= NUMTAGS; i++) { + + /* init nmaster */ + m->pertag->nmasters[i] = m->nmaster; + + /* init mfacts */ + m->pertag->mfacts[i] = m->mfact; + + + + /* init layouts */ + m->pertag->ltidxs[i][0] = m->lt[0]; + m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + + } + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); + else if ((c = swallowingclient(ev->window))) + unmanage(c->swallowing, 1); + else if (showsystray && (c = wintosystrayicon(ev->window))) { + removesystrayicon(c); + drawbarwin(systray->bar); + } +} + +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) +{ + Bar *bar; + for (bar = m->bar; bar; bar = bar->next) + drawbarwin(bar); +} + +void +drawbars(void) +{ + Monitor *m; + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +drawbarwin(Bar *bar) +{ + if (!bar || !bar->win || bar->external) + return; + int r, w, total_drawn = 0; + int rx, lx, rw, lw; // bar size, split between left and right if a center module is added + const BarRule *br; + + if (bar->borderpx) { + XSetForeground(drw->dpy, drw->gc, scheme[bar->borderscheme][ColBorder].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, 0, 0, bar->bw, bar->bh); + } + + BarArg warg = { 0 }; + BarArg darg = { 0 }; + warg.h = bar->bh - 2 * bar->borderpx; + + rw = lw = bar->bw - 2 * bar->borderpx; + rx = lx = bar->borderpx; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, lx, bar->borderpx, lw, bar->bh - 2 * bar->borderpx, 1, 1); + for (r = 0; r < LENGTH(barrules); r++) { + br = &barrules[r]; + if (br->bar != bar->idx || !br->widthfunc || (br->monitor == 'A' && bar->mon != selmon)) + continue; + if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->index) + continue; + drw_setscheme(drw, scheme[SchemeNorm]); + warg.w = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); + + w = br->widthfunc(bar, &warg); + w = MIN(warg.w, w); + + if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa + lw = rw; + lx = rx; + } else if (rw <= 0) { + rw = lw; + rx = lx; + } + + switch(br->alignment) { + default: + case BAR_ALIGN_NONE: + case BAR_ALIGN_LEFT_LEFT: + case BAR_ALIGN_LEFT: + bar->x[r] = lx; + if (lx == rx) { + rx += w; + rw -= w; + } + lx += w; + lw -= w; + break; + case BAR_ALIGN_LEFT_RIGHT: + case BAR_ALIGN_RIGHT: + bar->x[r] = lx + lw - w; + if (lx == rx) + rw -= w; + lw -= w; + break; + case BAR_ALIGN_LEFT_CENTER: + case BAR_ALIGN_CENTER: + bar->x[r] = lx + lw / 2 - w / 2; + if (lx == rx) { + rw = rx + rw - bar->x[r] - w; + rx = bar->x[r] + w; + } + lw = bar->x[r] - lx; + break; + case BAR_ALIGN_RIGHT_LEFT: + bar->x[r] = rx; + if (lx == rx) { + lx += w; + lw -= w; + } + rx += w; + rw -= w; + break; + case BAR_ALIGN_RIGHT_RIGHT: + bar->x[r] = rx + rw - w; + if (lx == rx) + lw -= w; + rw -= w; + break; + case BAR_ALIGN_RIGHT_CENTER: + bar->x[r] = rx + rw / 2 - w / 2; + if (lx == rx) { + lw = lx + lw - bar->x[r] + w; + lx = bar->x[r] + w; + } + rw = bar->x[r] - rx; + break; + } + bar->w[r] = w; + darg.x = bar->x[r]; + darg.y = bar->borderpx; + darg.h = bar->bh - 2 * bar->borderpx; + darg.w = bar->w[r]; + if (br->drawfunc) + total_drawn += br->drawfunc(bar, &darg); + } + + if (total_drawn == 0 && bar->showbar) { + bar->showbar = 0; + updatebarpos(bar->mon); + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(bar->mon); + } + else if (total_drawn > 0 && !bar->showbar) { + bar->showbar = 1; + updatebarpos(bar->mon); + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); + arrange(bar->mon); + } else + drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); +} + +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, c); + 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, c); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel); + else + 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, NULL); + selmon = m; + focus(NULL); +} + +void +focusstack(const Arg *arg) +{ + Client *c = NULL, *i; + + if (!selmon->sel || (selmon->sel->isfullscreen && !selmon->sel->fakefullscreen)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && (!ISVISIBLE(c) || (arg->i == 1 && HIDDEN(c))); c = c->next); + if (!c) + for (c = selmon->clients; c && (!ISVISIBLE(c) || (arg->i == 1 && HIDDEN(c))); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i) && !(arg->i == -1 && HIDDEN(i))) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i) && !(arg->i == -1 && HIDDEN(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; + + /* FIXME getatomprop should return the number of items and a pointer to + * the stored data instead of this workaround */ + Atom req = XA_ATOM; + if (prop == xatom[XembedInfo]) + req = xatom[XembedInfo]; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + if (da == xatom[XembedInfo] && dl == 2) + atom = ((Atom *)p)[1]; + XFree(p); + } + return atom; +} + +int +getrootptr(int *x, int *y) +{ + int di; + unsigned int dui; + Window dummy; + + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); +} + +long +getstate(Window w) +{ + int format; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if (n != 0) + result = *p; + XFree(p); + return result; +} + +int +gettextprop(Window w, Atom atom, char *text, unsigned int size) +{ + char **list = NULL; + int n; + XTextProperty name; + + if (!text || size == 0) + return 0; + text[0] = '\0'; + if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) + return 0; + if (name.encoding == XA_STRING) + strncpy(text, (char *)name.value, size - 1); + else { + if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + } + text[size - 1] = '\0'; + XFree(name.value); + return 1; +} + +void +grabbuttons(Client *c, int focused) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + if (!focused) + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, + BUTTONMASK, GrabModeSync, GrabModeSync, None, None); + for (i = 0; i < LENGTH(buttons); i++) + if (buttons[i].click == ClkClientWin + ) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, + buttons[i].mask | modifiers[j], + c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +void +grabkeys(void) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + KeyCode code; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < LENGTH(keys); i++) + if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, + True, GrabModeAsync, GrabModeAsync); + } +} + +void +incnmaster(const Arg *arg) +{ + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = 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; + int keysyms_return; + KeySym* keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XGetKeyboardMapping(dpy, (KeyCode)ev->keycode, 1, &keysyms_return); + 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)); + XFree(keysym); +} + +void +killclient(const Arg *arg) +{ + if (!selmon->sel || selmon->sel->ispermanent) + return; + if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0, 0, 0)) + { + 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; + Client *term = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + c->pid = winpid(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; + c->bw = borderpx; + c->x = t->x + WIDTH(t) / 2 - WIDTH(c) / 2; + c->y = t->y + HEIGHT(t) / 2 - HEIGHT(c) / 2; + } else { + c->mon = selmon; + if (c->x == c->mon->wx && c->y == c->mon->wy) + c->iscentered = 1; + c->bw = borderpx; + applyrules(c); + term = termforwin(c); + if (term) + c->mon = term->mon; + } + + if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) + c->x = c->mon->mx + c->mon->mw - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ + c->y = MAX(c->y, ((!c->mon->bar || c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + if (c->isfloating) + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel); + else + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatesizehints(c); + if (getatomprop(c, netatom[NetWMState]) == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + updatewmhints(c); + updatemotifhints(c); + + c->sfx = -9999; + c->sfy = -9999; + if (c->iscentered) { + c->sfx = c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2; + c->sfy = c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2; + } + c->sfw = c->w; + c->sfh = c->h; + + 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); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel); + } + attach(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XChangeProperty(dpy, root, netatom[NetClientListStacking], XA_WINDOW, 32, PropModePrepend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + + if (!HIDDEN(c)) + setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0, c); + c->mon->sel = c; + if (!(term && swallow(term, c))) { + arrange(c->mon); + if (!HIDDEN(c)) + XMapWindow(dpy, c->win); + } + focus(NULL); + + setfloatinghint(c); +} + +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; + + Client *i; + if (showsystray && systray && (i = wintosystrayicon(ev->window))) { + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); + drawbarwin(systray->bar); + } + + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + if (wa.override_redirect) + return; + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +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, NULL); + 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 && c->fakefullscreen != 1) /* 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; + ignoreconfigurerequests = 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; + + 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)) { + c->sfx = -9999; // disable savefloats when using movemouse + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { + resize(c, nx, ny, c->w, c->h, 1); + /* save last known float coordinates */ + c->sfx = nx; + c->sfy = ny; + } + 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); + } + ignoreconfigurerequests = 0; +} + +Client * +nexttiled(Client *c) +{ + for (; c && (c->isfloating || !ISVISIBLE(c) || HIDDEN(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 (showsystray && (c = wintosystrayicon(ev->window))) { + if (ev->atom == XA_WM_NORMAL_HINTS) { + updatesizehints(c); + updatesystrayicongeom(c, c->w, c->h); + } + else + updatesystrayiconstate(c, ev); + drawbarwin(systray->bar); + } + + 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: + updatesizehints(c); + break; + case XA_WM_HINTS: + updatewmhints(c); + if (c->isurgent) + 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 == motifatom) + updatemotifhints(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; + int opx, opy, och, ocw, nx, ny; + int horizcorner, vertcorner; + unsigned int dui; + Window dummy; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen && c->fakefullscreen != 1) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + och = c->h; + ocw = c->w; + if (!XQueryPointer(dpy, c->win, &dummy, &dummy, &opx, &opy, &nx, &ny, &dui)) + return; + horizcorner = nx < c->w / 2; + vertcorner = ny < c->h / 2; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[horizcorner | (vertcorner << 1)]->cursor, CurrentTime) != GrabSuccess) + return; + ignoreconfigurerequests = 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; + + nx = horizcorner ? (ocx + ev.xmotion.x - opx) : c->x; + ny = vertcorner ? (ocy + ev.xmotion.y - opy) : c->y; + nw = MAX(horizcorner ? (ocx + ocw - nx) : (ocw + (ev.xmotion.x - opx)), 1); + nh = MAX(vertcorner ? (ocy + och - ny) : (och + (ev.xmotion.y - opy)), 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)) { + c->sfx = -9999; // disable savefloats when using resizemouse + togglefloating(NULL); + } + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { + resizeclient(c, nx, ny, nw, nh); + /* save last known float dimensions */ + c->sfx = nx; + c->sfy = ny; + c->sfw = nw; + c->sfh = nh; + } + break; + } + } while (ev.type != ButtonRelease); + 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); + } + ignoreconfigurerequests = 0; +} + +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 && m->bar) { + wc.stack_mode = Below; + wc.sibling = m->bar->win; + 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) +{ + scanner = 1; + char swin[256]; + 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); + else if (gettextprop(wins[i], netatom[NetClientList], swin, sizeof swin)) + 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); + } + XFree(wins); + } + scanner = 0; +} + +void +sendmon(Client *c, Monitor *m) +{ + if (c->mon == m) + return; + unfocus(c, 1, NULL); + 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); + if (c->switchtag) + c->switchtag = 0; +} + +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(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) +{ + int n; + Atom *protocols; + Atom mt; + int exists = 0; + XEvent ev; + + if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { + mt = wmatom[WMProtocols]; + if (XGetWMProtocols(dpy, w, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + } else { + exists = True; + mt = proto; + } + + if (exists) { + ev.type = ClientMessage; + ev.xclient.window = w; + ev.xclient.message_type = mt; + ev.xclient.format = 32; + ev.xclient.data.l[0] = d0; + ev.xclient.data.l[1] = d1; + ev.xclient.data.l[2] = d2; + ev.xclient.data.l[3] = d3; + ev.xclient.data.l[4] = d4; + XSendEvent(dpy, w, False, mask, &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->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + XEvent ev; + int savestate = 0, restorestate = 0; + + if ((c->fakefullscreen == 0 && fullscreen && !c->isfullscreen) // normal fullscreen + || (c->fakefullscreen == 2 && fullscreen)) // fake fullscreen --> actual fullscreen + savestate = 1; // go actual fullscreen + else if ((c->fakefullscreen == 0 && !fullscreen && c->isfullscreen) // normal fullscreen exit + || (c->fakefullscreen >= 2 && !fullscreen)) // fullscreen exit --> fake fullscreen + restorestate = 1; // go back into tiled + + /* If leaving fullscreen and the window was previously fake fullscreen (2), then restore + * that while staying in fullscreen. The exception to this is if we are in said state, but + * the client itself disables fullscreen (3) then we let the client go out of fullscreen + * while keeping fake fullscreen enabled (as otherwise there will be a mismatch between the + * client and the window manager's perception of the client's fullscreen state). */ + if (c->fakefullscreen == 2 && !fullscreen && c->isfullscreen) { + c->fakefullscreen = 1; + c->isfullscreen = 1; + fullscreen = 1; + } else if (c->fakefullscreen == 3) // client exiting actual fullscreen + c->fakefullscreen = 1; + + if (fullscreen != c->isfullscreen) { // only send property change if necessary + if (fullscreen) + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + else + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + } + + c->isfullscreen = fullscreen; + + /* Some clients, e.g. firefox, will send a client message informing the window manager + * that it is going into fullscreen after receiving the above signal. This has the side + * effect of this function (setfullscreen) sometimes being called twice when toggling + * fullscreen on and off via the window manager as opposed to the application itself. + * To protect against obscure issues where the client settings are stored or restored + * when they are not supposed to we add an additional bit-lock on the old state so that + * settings can only be stored and restored in that precise order. */ + if (savestate && !(c->oldstate & (1 << 1))) { + c->oldbw = c->bw; + c->oldstate = c->isfloating | (1 << 1); + 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 (restorestate && (c->oldstate & (1 << 1))) { + c->bw = c->oldbw; + c->isfloating = c->oldstate = c->oldstate & 1; + 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); + restack(c->mon); + } else + resizeclient(c, c->x, c->y, c->w, c->h); + + /* Exception: if the client was in actual fullscreen and we exit out to fake fullscreen + * mode, then the focus would sometimes drift to whichever window is under the mouse cursor + * at the time. To avoid this we ask X for all EnterNotify events and just ignore them. + */ + if (!c->isfullscreen) + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +setlayout(const Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) { + selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + } + if (arg && arg->v) + selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + + 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 = selmon->pertag->mfacts[selmon->pertag->curtag] = 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); + wmatom[WMWindowRole] = XInternAtom(dpy, "WM_WINDOW_ROLE", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); + netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); + netatom[NetSystemTrayVisual] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_VISUAL", False); + netatom[NetWMWindowTypeDock] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); + xatom[Manager] = XInternAtom(dpy, "MANAGER", False); + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + netatom[NetDesktopViewport] = XInternAtom(dpy, "_NET_DESKTOP_VIEWPORT", False); + netatom[NetNumberOfDesktops] = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False); + netatom[NetCurrentDesktop] = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); + netatom[NetDesktopNames] = XInternAtom(dpy, "_NET_DESKTOP_NAMES", 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[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + netatom[NetClientListStacking] = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False); + motifatom = XInternAtom(dpy, "_MOTIF_WM_HINTS", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurResizeBR] = drw_cur_create(drw, XC_bottom_right_corner); + cursor[CurResizeBL] = drw_cur_create(drw, XC_bottom_left_corner); + cursor[CurResizeTR] = drw_cur_create(drw, XC_top_right_corner); + cursor[CurResizeTL] = drw_cur_create(drw, XC_top_left_corner); + 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], ColCount); + + 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); + setnumdesktops(); + setcurrentdesktop(); + setdesktopnames(); + setviewport(); + XDeleteProperty(dpy, root, netatom[NetClientList]); + XDeleteProperty(dpy, root, netatom[NetClientListStacking]); + /* 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 */ + if (!c->mon->lt[c->mon->sellt]->arrange && c->sfx != -9999 && !c->isfullscreen) { + XMoveWindow(dpy, c->win, c->sfx, c->sfy); + resize(c, c->sfx, c->sfy, c->sfw, c->sfh, 0); + showhide(c->snext); + return; + } + 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); + fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); + perror(" failed"); + exit(EXIT_SUCCESS); + } +} + +void +tag(const Arg *arg) +{ + + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + if (selmon->sel->switchtag) + selmon->sel->switchtag = 0; + focus(NULL); + arrange(selmon); + if ((arg->ui & TAGMASK) != selmon->tagset[selmon->seltags]) + view(arg); + } +} + +void +tagmon(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c || !mons->next) + return; + if (c->isfullscreen) { + c->isfullscreen = 0; + sendmon(c, dirtomon(arg->i)); + c->isfullscreen = 1; + if (c->fakefullscreen != 1) { + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } + } else + sendmon(c, dirtomon(arg->i)); +} + +void +togglebar(const Arg *arg) +{ + Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + for (bar = selmon->bar; bar; bar = bar->next) + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); +} + +void +togglefloating(const Arg *arg) +{ + Client *c = selmon->sel; + if (arg && arg->v) + c = (Client*)arg->v; + if (!c) + return; + if (c->isfullscreen && c->fakefullscreen != 1) /* no support for fullscreen windows */ + return; + c->isfloating = !c->isfloating || c->isfixed; + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel); + else + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + if (c->isfloating) { + if (c->sfx != -9999) { + /* restore last known float dimensions */ + resize(c, c->sfx, c->sfy, c->sfw, c->sfh, 0); + } else + resize(c, c->x, c->y, c->w, c->h, 0); + } else { + /* save last known float dimensions */ + c->sfx = c->x; + c->sfy = c->y; + c->sfw = c->w; + c->sfh = c->h; + } + arrange(c->mon); + + setfloatinghint(c); +} + +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); + } + updatecurrentdesktop(); +} + +void +toggleview(const Arg *arg) +{ + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + int i; + + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + + if (newtagset == ~0) + { + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = 0; + } + /* test if the user did not select the same tag */ + if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { + selmon->pertag->prevtag = selmon->pertag->curtag; + for (i = 0; !(newtagset & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + + /* apply settings for this view */ + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + focus(NULL); + arrange(selmon); + } + updatecurrentdesktop(); +} + +void +unfocus(Client *c, int setfocus, Client *nextfocus) +{ + if (!c) + return; + grabbuttons(c, 0); + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColFloat].pixel); + else + 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; + unsigned int switchtag = c->switchtag; + XWindowChanges wc; + + if (c->swallowing) { + unswallow(c); + return; + } + + Client *s = swallowingclient(c->win); + if (s) { + free(s->swallowing); + s->swallowing = NULL; + arrange(m); + focus(NULL); + return; + } + + detach(c); + detachstack(c); + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + + if (scratchpad_last_showed == c) + scratchpad_last_showed = NULL; + + free(c); + if (s) + return; + focus(NULL); + updateclientlist(); + arrange(m); + if (switchtag && ((switchtag & TAGMASK) != selmon->tagset[selmon->seltags])) + view(&((Arg) { .ui = switchtag })); +} + +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); + } else if (showsystray && (c = wintosystrayicon(ev->window))) { + /* KLUDGE! sometimes icons occasionally unmap their windows, but do + * _not_ destroy them. We map those windows back */ + XMapRaised(dpy, c->win); + removesystrayicon(c); + drawbarwin(systray->bar); + } +} + +void +updatebars(void) +{ + Bar *bar; + 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) { + for (bar = m->bar; bar; bar = bar->next) { + if (bar->external) + continue; + if (!bar->win) { + bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, + InputOutput, visual, + CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); + XMapRaised(dpy, bar->win); + XSetClassHint(dpy, bar->win, &ch); + } + } + } +} + +void +updatebarpos(Monitor *m) +{ + + m->wx = m->mx; + m->wy = m->my; + m->ww = m->mw; + m->wh = m->mh; + Bar *bar; + int y_pad = 0; + int x_pad = 0; + + + for (bar = m->bar; bar; bar = bar->next) { + bar->bx = m->wx + x_pad; + bar->bw = m->ww - 2 * x_pad; + } + + for (bar = m->bar; bar; bar = bar->next) + if (!m->showbar || !bar->showbar) + bar->by = -bar->bh - y_pad; + + + if (!m->showbar) + return; + for (bar = m->bar; bar; bar = bar->next) { + if (!bar->showbar) + continue; + if (bar->topbar) + m->wy = m->wy + bar->bh + y_pad; + m->wh -= y_pad + bar->bh; + bar->by = (bar->topbar ? m->wy - bar->bh : m->wy + m->wh); + } +} + +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); + + XDeleteProperty(dpy, root, netatom[NetClientListStacking]); + for (m = mons; m; m = m->next) + for (c = m->stack; c; c = c->snext) + XChangeProperty(dpy, root, netatom[NetClientListStacking], + 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; + if (n <= nn) { /* new monitors available */ + for (i = 0; i < (nn - n); 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); + } + } + } else { /* less monitors available nn < n */ + 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); + } + } + for (i = 0, m = mons; m; m = m->next, i++) + m->index = i; + 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); +} + +void +updatestatus(void) +{ + Monitor *m; + if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) + strcpy(stext, "dwm-"VERSION); + else + copyvalidchars(stext, rawstext); + for (m = mons; m; m = m->next) + drawbar(m); +} + +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 +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 (c->isurgent) { + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeUrg][ColFloat].pixel); + else + XSetWindowBorder(dpy, c->win, scheme[SchemeUrg][ColBorder].pixel); + } + 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 */ + pertagview(arg); + focus(NULL); + arrange(selmon); + updatecurrentdesktop(); +} + +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; + Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + for (bar = m->bar; bar; bar = bar->next) + if (w == bar->win) + 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 (arg && arg->v) + c = (Client*)arg->v; + if (!c) + return; + + + + if (!c->mon->lt[c->mon->sellt]->arrange + || (c && c->isfloating) + ) + return; + + if (c == nexttiled(c->mon->clients)) + if (!c || !(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"); + if (!(xcon = XGetXCBConnection(dpy))) + die("dwm: cannot get xcb connection\n"); + checkotherwm(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec ps", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + runautostart(); + run(); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} + diff --git a/dwm.o b/dwm.o new file mode 100644 index 0000000..121f1aa Binary files /dev/null and b/dwm.o differ diff --git a/dwm.png b/dwm.png new file mode 100644 index 0000000..b1f9ba7 Binary files /dev/null and b/dwm.png differ diff --git a/patch/autostart.c b/patch/autostart.c new file mode 100644 index 0000000..03477bb --- /dev/null +++ b/patch/autostart.c @@ -0,0 +1,84 @@ +void +runautostart(void) +{ + char *pathpfx; + char *path; + char *xdgdatahome; + char *home; + + if ((home = getenv("HOME")) == NULL) + /* this is almost impossible */ + return; + + /* if $XDG_DATA_HOME is defined, use $XDG_DATA_HOME/dwm, + * otherwise use ~/.local/share/dwm as autostart script directory + */ + if ((xdgdatahome = getenv("XDG_DATA_HOME")) != NULL) { + /* space for path segments, separators and nul */ + if ((pathpfx = malloc(strlen(xdgdatahome) + strlen(dwmdir) + 2)) == NULL) + return; + + if (sprintf(pathpfx, "%s/%s", xdgdatahome, dwmdir) <= 0) { + free(pathpfx); + return; + } + } else { + /* space for path segments, separators and nul */ + if ((pathpfx = malloc(strlen(home) + strlen(localshare) + strlen(dwmdir) + 3)) == NULL) + return; + + if (sprintf(pathpfx, "%s/%s/%s", home, localshare, dwmdir) < 0) { + free(pathpfx); + return; + } + } + + /* check if the autostart script directory exists */ + struct stat sb; + + if (! (stat(pathpfx, &sb) == 0 && S_ISDIR(sb.st_mode))) { + /* the XDG conformant path does not exist or are not directories + * so we try ~/.dwm instead + */ + if (realloc(pathpfx, strlen(home) + strlen(dwmdir) + 3) == NULL) { + free(pathpfx); + return; + } + + if (sprintf(pathpfx, "%s/.%s", home, dwmdir) <= 0) { + free(pathpfx); + return; + } + } + + /* try the blocking script first */ + if ((path = malloc(strlen(pathpfx) + strlen(autostartblocksh) + 2)) == NULL) { + free(pathpfx); + return; + } else + if (sprintf(path, "%s/%s", pathpfx, autostartblocksh) <= 0) { + free(path); + free(pathpfx); + } + + if (access(path, X_OK) == 0) + system(path); + + /* now the non-blocking script */ + if ((path = realloc(path, strlen(pathpfx) + strlen(autostartsh) + 4)) == NULL) { + free(pathpfx); + free(path); + return; + } else + if (sprintf(path, "%s/%s", pathpfx, autostartsh) <= 0) { + free(path); + free(pathpfx); + } + + if (access(path, X_OK) == 0) { + system(strcat(path, " &")); + free(pathpfx); + free(path); + } +} + diff --git a/patch/autostart.h b/patch/autostart.h new file mode 100644 index 0000000..db29377 --- /dev/null +++ b/patch/autostart.h @@ -0,0 +1,2 @@ +static void runautostart(void); + diff --git a/patch/bar_alpha.c b/patch/bar_alpha.c new file mode 100644 index 0000000..465f6f2 --- /dev/null +++ b/patch/bar_alpha.c @@ -0,0 +1,43 @@ + +static int useargb = 0; +static Visual *visual; +static int depth; +static Colormap cmap; + +void +xinitvisual() +{ + XVisualInfo *infos; + XRenderPictFormat *fmt; + int nitems; + int i; + + XVisualInfo tpl = { + .screen = screen, + .depth = 32, + .class = TrueColor + }; + long masks = VisualScreenMask | VisualDepthMask | VisualClassMask; + + infos = XGetVisualInfo(dpy, masks, &tpl, &nitems); + visual = NULL; + for (i = 0; i < nitems; i ++) { + fmt = XRenderFindVisualFormat(dpy, infos[i].visual); + if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { + visual = infos[i].visual; + depth = infos[i].depth; + cmap = XCreateColormap(dpy, root, visual, AllocNone); + useargb = 1; + break; + } + } + + XFree(infos); + + if (!visual) { + visual = DefaultVisual(dpy, screen); + depth = DefaultDepth(dpy, screen); + cmap = DefaultColormap(dpy, screen); + } +} + diff --git a/patch/bar_alpha.h b/patch/bar_alpha.h new file mode 100644 index 0000000..1c2a012 --- /dev/null +++ b/patch/bar_alpha.h @@ -0,0 +1,4 @@ +#define OPAQUE 0xffU + +static void xinitvisual(); + diff --git a/patch/bar_dwmblocks.c b/patch/bar_dwmblocks.c new file mode 100644 index 0000000..0a4b097 --- /dev/null +++ b/patch/bar_dwmblocks.c @@ -0,0 +1,41 @@ +static int statussig; +pid_t statuspid = -1; + +pid_t +getstatusbarpid() +{ + char buf[32], *str = buf, *c; + FILE *fp; + + if (statuspid > 0) { + snprintf(buf, sizeof(buf), "/proc/%u/cmdline", statuspid); + if ((fp = fopen(buf, "r"))) { + fgets(buf, sizeof(buf), fp); + while ((c = strchr(str, '/'))) + str = c + 1; + fclose(fp); + if (!strcmp(str, STATUSBAR)) + return statuspid; + } + } + if (!(fp = popen("pgrep -o "STATUSBAR, "r"))) + return -1; + fgets(buf, sizeof(buf), fp); + pclose(fp); + return strtol(buf, NULL, 10); +} + +void +sigstatusbar(const Arg *arg) +{ + union sigval sv; + + if (!statussig) + return; + if ((statuspid = getstatusbarpid()) <= 0) + return; + + sv.sival_int = arg->i; + sigqueue(statuspid, SIGRTMIN+statussig, sv); +} + diff --git a/patch/bar_dwmblocks.h b/patch/bar_dwmblocks.h new file mode 100644 index 0000000..4db9467 --- /dev/null +++ b/patch/bar_dwmblocks.h @@ -0,0 +1,3 @@ +static int getstatusbarpid(); +static void sigstatusbar(const Arg *arg); + diff --git a/patch/bar_ewmhtags.c b/patch/bar_ewmhtags.c new file mode 100644 index 0000000..d86fd88 --- /dev/null +++ b/patch/bar_ewmhtags.c @@ -0,0 +1,53 @@ +void +setcurrentdesktop(void) +{ + long data[] = { 0 }; + XChangeProperty(dpy, root, netatom[NetCurrentDesktop], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +void +setdesktopnames(void) +{ + int i; + XTextProperty text; + char *tags[NUMTAGS]; + for (i = 0; i < NUMTAGS; i++) + tags[i] = tagicon(selmon, i); + Xutf8TextListToTextProperty(dpy, tags, NUMTAGS, XUTF8StringStyle, &text); + XSetTextProperty(dpy, root, &text, netatom[NetDesktopNames]); +} + +void +setfloatinghint(Client *c) +{ + Atom target = XInternAtom(dpy, "_IS_FLOATING", 0); + unsigned int floating[1] = {c->isfloating}; + XChangeProperty(dpy, c->win, target, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)floating, 1); +} + +void +setnumdesktops(void) +{ + long data[] = { NUMTAGS }; + XChangeProperty(dpy, root, netatom[NetNumberOfDesktops], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +void +setviewport(void) +{ + long data[] = { 0, 0 }; + XChangeProperty(dpy, root, netatom[NetDesktopViewport], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 2); +} + +void +updatecurrentdesktop(void) +{ + long rawdata[] = { selmon->tagset[selmon->seltags] }; + int i = 0; + while (*rawdata >> (i + 1)) { + i++; + } + long data[] = { i }; + XChangeProperty(dpy, root, netatom[NetCurrentDesktop], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + diff --git a/patch/bar_ewmhtags.h b/patch/bar_ewmhtags.h new file mode 100644 index 0000000..4d6a74b --- /dev/null +++ b/patch/bar_ewmhtags.h @@ -0,0 +1,7 @@ +static void setcurrentdesktop(void); +static void setdesktopnames(void); +static void setfloatinghint(Client *c); +static void setnumdesktops(void); +static void setviewport(void); +static void updatecurrentdesktop(void); + diff --git a/patch/bar_indicators.c b/patch/bar_indicators.c new file mode 100644 index 0000000..ad17a39 --- /dev/null +++ b/patch/bar_indicators.c @@ -0,0 +1,109 @@ +/* Indicator properties, you can override these in your config.h if you want. */ +#ifndef TAGSINDICATOR +#define TAGSINDICATOR 1 // 0 = off, 1 = on if >1 client/view tag, 2 = always on +#endif +#ifndef TAGSPX +#define TAGSPX 5 // # pixels for tag grid boxes +#endif +#ifndef TAGSROWS +#define TAGSROWS 3 // # rows in tag grid (9 tags, e.g. 3x3) +#endif + +void +drawindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert, int type) +{ + int i, boxw, boxs, indn = 0; + if (!(occ & 1 << tag) || type == INDICATOR_NONE) + return; + + boxs = drw->fonts->h / 9; + boxw = drw->fonts->h / 6 + 2; + if (filled == -1) + filled = m == selmon && m->sel && m->sel->tags & 1 << tag; + + switch (type) { + default: + case INDICATOR_TOP_LEFT_SQUARE: + drw_rect(drw, x + boxs, y + boxs, boxw, boxw, filled, invert); + break; + case INDICATOR_TOP_LEFT_LARGER_SQUARE: + drw_rect(drw, x + boxs + 2, y + boxs+1, boxw+1, boxw+1, filled, invert); + break; + case INDICATOR_TOP_BAR: + drw_rect(drw, x + boxw, y, w - ( 2 * boxw + 1), boxw/2, filled, invert); + break; + case INDICATOR_TOP_BAR_SLIM: + drw_rect(drw, x + boxw, y, w - ( 2 * boxw + 1), 1, 0, invert); + break; + case INDICATOR_BOTTOM_BAR: + drw_rect(drw, x + boxw, y + h - boxw/2, w - ( 2 * boxw + 1), boxw/2, filled, invert); + break; + case INDICATOR_BOTTOM_BAR_SLIM: + drw_rect(drw, x + boxw, y + h - 1, w - ( 2 * boxw + 1), 1, 0, invert); + break; + case INDICATOR_BOX: + drw_rect(drw, x + boxw, y, w - 2 * boxw, h, 0, invert); + break; + case INDICATOR_BOX_WIDER: + drw_rect(drw, x + boxw/2, y, w - boxw, h, 0, invert); + break; + case INDICATOR_BOX_FULL: + drw_rect(drw, x, y, w - 2, h, 0, invert); + break; + case INDICATOR_CLIENT_DOTS: + for (c = m->clients; c; c = c->next) { + if (c->tags & (1 << tag)) { + drw_rect(drw, x, 1 + (indn * 2), m->sel == c ? 6 : 1, 1, 1, invert); + indn++; + } + if (h <= 1 + (indn * 2)) { + indn = 0; + x += 2; + } + } + break; + case INDICATOR_RIGHT_TAGS: + if (!c) + break; + for (i = 0; i < NUMTAGS; i++) { + drw_rect(drw, + ( x + w - 2 - ((NUMTAGS / TAGSROWS) * TAGSPX) + - (i % (NUMTAGS/TAGSROWS)) + ((i % (NUMTAGS / TAGSROWS)) * TAGSPX) + ), + ( y + 2 + ((i / (NUMTAGS/TAGSROWS)) * TAGSPX) + - ((i / (NUMTAGS/TAGSROWS))) + ), + TAGSPX, TAGSPX, (c->tags >> i) & 1, 0 + ); + } + break; + case INDICATOR_PLUS_AND_LARGER_SQUARE: + boxs += 2; + boxw += 2; + /* falls through */ + case INDICATOR_PLUS_AND_SQUARE: + drw_rect(drw, x + boxs, y + boxs, boxw % 2 ? boxw : boxw + 1, boxw % 2 ? boxw : boxw + 1, filled, invert); + /* falls through */ + case INDICATOR_PLUS: + if (!(boxw % 2)) + boxw += 1; + drw_rect(drw, x + boxs + boxw / 2, y + boxs, 1, boxw, filled, invert); // | + drw_rect(drw, x + boxs, y + boxs + boxw / 2, boxw + 1, 1, filled, invert); // ‒ + break; + } +} + +void +drawstateindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert) +{ + if (c->fakefullscreen && c->isfloating) + drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, floatfakefsindicatortype); + else if (c->fakefullscreen) + drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, fakefsindicatortype); + else + if (c->isfloating) + drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, floatindicatortype); + else + drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, tiledindicatortype); +} + diff --git a/patch/bar_indicators.h b/patch/bar_indicators.h new file mode 100644 index 0000000..c66e4f0 --- /dev/null +++ b/patch/bar_indicators.h @@ -0,0 +1,21 @@ +enum { + INDICATOR_NONE, + INDICATOR_TOP_LEFT_SQUARE, + INDICATOR_TOP_LEFT_LARGER_SQUARE, + INDICATOR_TOP_BAR, + INDICATOR_TOP_BAR_SLIM, + INDICATOR_BOTTOM_BAR, + INDICATOR_BOTTOM_BAR_SLIM, + INDICATOR_BOX, + INDICATOR_BOX_WIDER, + INDICATOR_BOX_FULL, + INDICATOR_CLIENT_DOTS, + INDICATOR_RIGHT_TAGS, + INDICATOR_PLUS, + INDICATOR_PLUS_AND_SQUARE, + INDICATOR_PLUS_AND_LARGER_SQUARE, +}; + +static void drawindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert, int type); +static void drawstateindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert); + diff --git a/patch/bar_ltsymbol.c b/patch/bar_ltsymbol.c new file mode 100644 index 0000000..1fbd1b8 --- /dev/null +++ b/patch/bar_ltsymbol.c @@ -0,0 +1,18 @@ +int +width_ltsymbol(Bar *bar, BarArg *a) +{ + return TEXTW(bar->mon->ltsymbol); +} + +int +draw_ltsymbol(Bar *bar, BarArg *a) +{ + return drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, bar->mon->ltsymbol, 0, False); +} + +int +click_ltsymbol(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkLtSymbol; +} + diff --git a/patch/bar_ltsymbol.h b/patch/bar_ltsymbol.h new file mode 100644 index 0000000..4de5720 --- /dev/null +++ b/patch/bar_ltsymbol.h @@ -0,0 +1,4 @@ +static int width_ltsymbol(Bar *bar, BarArg *a); +static int draw_ltsymbol(Bar *bar, BarArg *a); +static int click_ltsymbol(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_powerline_tags.c b/patch/bar_powerline_tags.c new file mode 100644 index 0000000..86e96c0 --- /dev/null +++ b/patch/bar_powerline_tags.c @@ -0,0 +1,85 @@ +int +width_pwrl_tags(Bar *bar, BarArg *a) +{ + int w, i; + int plw = drw->fonts->h / 2 + 1; + Client *c; + unsigned int occ = 0; + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + + for (w = 0, i = 0; i < NUMTAGS; i++) { + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + w += TEXTW(tagicon(bar->mon, i)) + plw; + } + return w + lrpad; +} + +int +draw_pwrl_tags(Bar *bar, BarArg *a) +{ + int x, w; + int invert; + int plw = drw->fonts->h / 2 + 1; + unsigned int i, occ = 0, urg = 0; + char *icon; + Client *c; + Clr *prevscheme, *nxtscheme; + + for (c = bar->mon->clients; c; c = c->next) { + occ |= c->tags == 255 ? 0 : c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = a->x; + prevscheme = scheme[SchemeNorm]; + for (i = 0; i < NUMTAGS; i++) { + /* do not draw vacant tags */ + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + + icon = tagicon(bar->mon, i); + invert = 0; + w = TEXTW(icon); + if (urg & 1 << i) { + drw_settrans(drw, prevscheme, (nxtscheme = scheme[bar->mon->tagset[bar->mon->seltags] & 1 << i ? SchemeSel : SchemeUrg])); + } else { + drw_settrans(drw, prevscheme, (nxtscheme = scheme[bar->mon->tagset[bar->mon->seltags] & 1 << i ? SchemeSel : SchemeNorm])); + } + drw_arrow(drw, x, a->y, plw, a->h, 1, 1); + x += plw; + drw_setscheme(drw, nxtscheme); + drw_text(drw, x, a->y, w, a->h, lrpad / 2, icon, invert, False); + drawindicator(bar->mon, NULL, occ, x, a->y, w, a->h, i, -1, invert, tagindicatortype); + x += w; + prevscheme = nxtscheme; + } + nxtscheme = scheme[SchemeNorm]; + + drw_settrans(drw, prevscheme, nxtscheme); + drw_arrow(drw, x, a->y, plw, a->h, 1, 1); + return 1; +} + +int +click_pwrl_tags(Bar *bar, Arg *arg, BarArg *a) +{ + int i = 0, x = lrpad / 2; + int plw = drw->fonts->h / 2 + 1; + Client *c; + unsigned int occ = 0; + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + + do { + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + x += TEXTW(tagicon(bar->mon, i)) + plw; + } while (a->x >= x && ++i < NUMTAGS); + if (i < NUMTAGS) { + arg->ui = 1 << i; + } + return ClkTagBar; +} + diff --git a/patch/bar_powerline_tags.h b/patch/bar_powerline_tags.h new file mode 100644 index 0000000..a51dcc7 --- /dev/null +++ b/patch/bar_powerline_tags.h @@ -0,0 +1,4 @@ +static int width_pwrl_tags(Bar *bar, BarArg *a); +static int draw_pwrl_tags(Bar *bar, BarArg *a); +static int click_pwrl_tags(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_status.c b/patch/bar_status.c new file mode 100644 index 0000000..65595e0 --- /dev/null +++ b/patch/bar_status.c @@ -0,0 +1,20 @@ +int +width_status(Bar *bar, BarArg *a) +{ + return TEXTWM(stext); +} + + +int +draw_status(Bar *bar, BarArg *a) +{ + return drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, stext, 0, True); +} + + +int +click_status(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkStatusText; +} + diff --git a/patch/bar_status.h b/patch/bar_status.h new file mode 100644 index 0000000..c580597 --- /dev/null +++ b/patch/bar_status.h @@ -0,0 +1,4 @@ +static int width_status(Bar *bar, BarArg *a); +static int draw_status(Bar *bar, BarArg *a); +static int click_status(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_statuscmd.c b/patch/bar_statuscmd.c new file mode 100644 index 0000000..8cd5981 --- /dev/null +++ b/patch/bar_statuscmd.c @@ -0,0 +1,46 @@ + +int +click_statuscmd(Bar *bar, Arg *arg, BarArg *a) +{ + return click_statuscmd_text(arg, a->x, rawstext); +} + + +int +click_statuscmd_text(Arg *arg, int rel_x, char *text) +{ + int i = -1; + int x = 0; + char ch; + statussig = -1; + while (text[++i]) { + if ((unsigned char)text[i] < ' ') { + ch = text[i]; + text[i] = '\0'; + x += TEXTWM(text) - lrpad; + text[i] = ch; + text += i+1; + i = -1; + if (x >= rel_x && statussig != -1) + break; + statussig = ch; + } + } + if (statussig == -1) + statussig = 0; + return ClkStatusText; +} + +void +copyvalidchars(char *text, char *rawtext) +{ + int i = -1, j = 0; + + while (rawtext[++i]) { + if ((unsigned char)rawtext[i] >= ' ') { + text[j++] = rawtext[i]; + } + } + text[j] = '\0'; +} + diff --git a/patch/bar_statuscmd.h b/patch/bar_statuscmd.h new file mode 100644 index 0000000..1ec24a6 --- /dev/null +++ b/patch/bar_statuscmd.h @@ -0,0 +1,9 @@ +static int click_statuscmd(Bar *bar, Arg *arg, BarArg *a); +static int click_statuscmd_text(Arg *arg, int rel_x, char *text); +static void copyvalidchars(char *text, char *rawtext); + +typedef struct { + const char *cmd; + int id; +} StatusCmd; + diff --git a/patch/bar_systray.c b/patch/bar_systray.c new file mode 100644 index 0000000..34f7066 --- /dev/null +++ b/patch/bar_systray.c @@ -0,0 +1,186 @@ +static Systray *systray = NULL; +static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; + +int +width_systray(Bar *bar, BarArg *a) +{ + unsigned int w = 0; + Client *i; + if (!systray) + return 1; + if (showsystray) + for (i = systray->icons; i; w += i->w + systrayspacing, i = i->next); + return w ? w + lrpad - systrayspacing : 0; +} + +int +draw_systray(Bar *bar, BarArg *a) +{ + if (!showsystray) + return 0; + + XSetWindowAttributes wa; + XWindowChanges wc; + Client *i; + unsigned int w; + + if (!systray) { + /* init systray */ + if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); + + wa.override_redirect = True; + wa.event_mask = ButtonPressMask|ExposureMask; + wa.border_pixel = 0; + systray->h = MIN(a->h, drw->fonts->h); + wa.background_pixel = 0; + wa.colormap = cmap; + systray->win = XCreateWindow(dpy, root, bar->bx + a->x + lrpad / 2, bar->by + a->y + (a->h - systray->h) / 2, MAX(a->w + 40, 1), systray->h, 0, depth, + InputOutput, visual, + CWOverrideRedirect|CWBorderPixel|CWBackPixel|CWColormap|CWEventMask, &wa); // CWBackPixmap + + XSelectInput(dpy, systray->win, SubstructureNotifyMask); + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&systrayorientation, 1); + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayVisual], XA_VISUALID, 32, + PropModeReplace, (unsigned char *)&visual->visualid, 1); + XChangeProperty(dpy, systray->win, netatom[NetWMWindowType], XA_ATOM, 32, + PropModeReplace, (unsigned char *)&netatom[NetWMWindowTypeDock], 1); + XMapRaised(dpy, systray->win); + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); + if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); + XSync(dpy, False); + } else { + fprintf(stderr, "dwm: unable to obtain system tray.\n"); + free(systray); + systray = NULL; + return 0; + } + } + + systray->bar = bar; + + wc.stack_mode = Above; + wc.sibling = bar->win; + XConfigureWindow(dpy, systray->win, CWSibling|CWStackMode, &wc); + + drw_setscheme(drw, scheme[SchemeNorm]); + for (w = 0, i = systray->icons; i; i = i->next) { + wa.background_pixel = 0; + XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); + XMapRaised(dpy, i->win); + i->x = w; + XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); + w += i->w; + if (i->next) + w += systrayspacing; + if (i->mon != bar->mon) + i->mon = bar->mon; + } + + XMoveResizeWindow(dpy, systray->win, bar->bx + a->x + lrpad / 2, (w ? bar->by + a->y + (a->h - systray->h) / 2: -bar->by - a->y), MAX(w, 1), systray->h); + return w; +} + +int +click_systray(Bar *bar, Arg *arg, BarArg *a) +{ + return -1; +} + +void +removesystrayicon(Client *i) +{ + Client **ii; + + if (!showsystray || !i) + return; + for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); + if (ii) + *ii = i->next; + free(i); + drawbarwin(systray->bar); +} + +void +resizerequest(XEvent *e) +{ + XResizeRequestEvent *ev = &e->xresizerequest; + Client *i; + + if ((i = wintosystrayicon(ev->window))) { + updatesystrayicongeom(i, ev->width, ev->height); + drawbarwin(systray->bar); + } +} + +void +updatesystrayicongeom(Client *i, int w, int h) +{ + if (!systray) + return; + + int icon_height = systray->h; + if (i) { + i->h = icon_height; + if (w == h) + i->w = icon_height; + else if (h == icon_height) + i->w = w; + else + i->w = (int) ((float)icon_height * ((float)w / (float)h)); + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); + /* force icons into the systray dimensions if they don't want to */ + if (i->h > icon_height) { + if (i->w == i->h) + i->w = icon_height; + else + i->w = (int) ((float)icon_height * ((float)i->w / (float)i->h)); + i->h = icon_height; + } + if (i->w > 2 * icon_height) + i->w = icon_height; + } +} + +void +updatesystrayiconstate(Client *i, XPropertyEvent *ev) +{ + long flags; + int code = 0; + + if (!showsystray || !systray || !i || ev->atom != xatom[XembedInfo] || + !(flags = getatomprop(i, xatom[XembedInfo]))) + return; + + if (flags & XEMBED_MAPPED && !i->tags) { + i->tags = 1; + code = XEMBED_WINDOW_ACTIVATE; + XMapRaised(dpy, i->win); + setclientstate(i, NormalState); + } + else if (!(flags & XEMBED_MAPPED) && i->tags) { + i->tags = 0; + code = XEMBED_WINDOW_DEACTIVATE; + XUnmapWindow(dpy, i->win); + setclientstate(i, WithdrawnState); + } + else + return; + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, + systray->win, XEMBED_EMBEDDED_VERSION); +} + +Client * +wintosystrayicon(Window w) +{ + if (!systray) + return NULL; + Client *i = NULL; + if (!showsystray || !w) + return i; + for (i = systray->icons; i && i->win != w; i = i->next); + return i; +} + diff --git a/patch/bar_systray.h b/patch/bar_systray.h new file mode 100644 index 0000000..a64b971 --- /dev/null +++ b/patch/bar_systray.h @@ -0,0 +1,42 @@ +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 + +/* XEMBED messages */ +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_FOCUS_IN 4 +#define XEMBED_MODALITY_ON 10 + +#define XEMBED_MAPPED (1 << 0) +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_WINDOW_DEACTIVATE 2 + +#define VERSION_MAJOR 0 +#define VERSION_MINOR 0 +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR + +/* enums */ +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ + +typedef struct Systray Systray; +struct Systray { + Window win; + Client *icons; + Bar *bar; + int h; +}; + +/* bar integration */ +static int width_systray(Bar *bar, BarArg *a); +static int draw_systray(Bar *bar, BarArg *a); +static int click_systray(Bar *bar, Arg *arg, BarArg *a); + +/* function declarations */ +static Atom getatomprop(Client *c, Atom prop); +static void removesystrayicon(Client *i); +static void resizerequest(XEvent *e); +static void updatesystrayicongeom(Client *i, int w, int h); +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); +static Client *wintosystrayicon(Window w); + + diff --git a/patch/bar_tabgroups.c b/patch/bar_tabgroups.c new file mode 100644 index 0000000..f99e736 --- /dev/null +++ b/patch/bar_tabgroups.c @@ -0,0 +1,223 @@ +/* Bartabgroups properties, you can override these in your config.h if you want. */ +#ifndef BARTAB_BORDERS +#define BARTAB_BORDERS 1 // 0 = off, 1 = on +#endif +#ifndef BARTAB_SHOWFLOATING +#define BARTAB_SHOWFLOATING 0 // whether to show titles for floating windows, hidden clients are always shown +#endif +#ifndef BARTAB_STACKWEIGHT +#define BARTAB_STACKWEIGHT 1 // stack weight compared to hidden and floating window titles +#endif +#ifndef BARTAB_HIDDENWEIGHT +#define BARTAB_HIDDENWEIGHT 1 // hidden window title weight +#endif +#ifndef BARTAB_FLOATWEIGHT +#define BARTAB_FLOATWEIGHT 1 // floating window title weight, set to 0 to not show floating windows +#endif + +int +width_bartabgroups(Bar *bar, BarArg *a) +{ + return a->w; +} + +int +draw_bartabgroups(Bar *bar, BarArg *a) +{ + drw_rect(drw, a->x, a->y, a->w, a->h, 1, 1); + return bartabcalculate(bar->mon, a->x, a->w, -1, bartabdraw, NULL, a); +} + +int +click_bartabgroups(Bar *bar, Arg *arg, BarArg *a) +{ + bartabcalculate(bar->mon, 0, a->w, a->x, bartabclick, arg, a); + return ClkWinTitle; +} + +void +bartabdraw(Monitor *m, Client *c, int unused, int x, int w, int groupactive, Arg *arg, BarArg *barg) +{ + if (!c) + return; + int i, nclienttags = 0, nviewtags = 0, pad = lrpad / 2; + drw_setscheme(drw, scheme[ + m->sel == c + #ifdef HIDDEN + && HIDDEN(c) + ? SchemeHidSel + : HIDDEN(c) + ? SchemeHidNorm + : m->sel == c + #endif + ? SchemeSel + : groupactive + ? SchemeTitleSel + : SchemeTitleNorm + ]); + if (w <= TEXTW("A") - lrpad + pad) // reduce text padding if wintitle is too small + pad = (w - TEXTW("A") + lrpad < 0 ? 0 : (w - TEXTW("A") + lrpad) / 2); + else if (TEXTW(c->name) < w) + pad = (w - TEXTW(c->name) + lrpad) / 2; + + drw_text(drw, x, barg->y, w, barg->h, pad, c->name, 0, False); + + drawstateindicator(m, c, 1, x, barg->y, w, barg->h, 0, 0, c->isfixed); + + if (BARTAB_BORDERS) { + XSetForeground(drw->dpy, drw->gc, scheme[SchemeSel][ColBorder].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, barg->y, 1, barg->h); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x + w - (x + w >= barg->w ? 1 : 0), barg->y, 1, barg->h); + } + /* Optional tags icons */ + for (i = 0; i < NUMTAGS; i++) { + if ((m->tagset[m->seltags] >> i) & 1) + nviewtags++; + if ((c->tags >> i) & 1) + nclienttags++; + } + + if (TAGSINDICATOR == 2 || nclienttags > 1 || nviewtags > 1) + drawindicator(m, c, 1, x, barg->y, w, barg->h, 0, 0, 0, INDICATOR_RIGHT_TAGS); +} + +#ifndef HIDDEN +#define HIDDEN(C) 0 +#endif + +void +bartabclick(Monitor *m, Client *c, int passx, int x, int w, int unused, Arg *arg, BarArg *barg) +{ + if (passx >= x && passx <= x + w) + arg->v = c; +} + +int +bartabcalculate( + Monitor *m, int offx, int tabw, int passx, + void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg, BarArg *barg), + Arg *arg, BarArg *barg +) { + Client *c; + int + i, clientsnmaster = 0, clientsnstack = 0, clientsnfloating = 0, clientsnhidden = 0, + masteractive = 0, fulllayout = 0, + x = offx, w, r, num = 0, den, tgactive; + + for (i = 0; i < LENGTH(bartabmonfns); i++) + if (m ->lt[m->sellt]->arrange == bartabmonfns[i]) { + fulllayout = 1; + break; + } + + for (i = 0, c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c)) + continue; + if (HIDDEN(c)) { + clientsnhidden++; + continue; + } + if (c->isfloating) { + clientsnfloating++; + continue; + } + if (m->sel == c) + masteractive = i < m->nmaster; + if (i < m->nmaster) + clientsnmaster++; + else + clientsnstack++; + i++; + } + + if (clientsnmaster + clientsnstack + clientsnfloating + clientsnhidden == 0) + return 0; + + tgactive = 1; + num = tabw; + /* floating mode */ + if ((fulllayout && BARTAB_FLOATWEIGHT) || clientsnmaster + clientsnstack == 0 || !m->lt[m->sellt]->arrange) { + den = clientsnmaster + clientsnstack + clientsnfloating + clientsnhidden; + r = num % den; + w = num / den; + for (c = m->clients, i = 0; c; c = c->next) { + if (!ISVISIBLE(c)) + continue; + tabfn(m, c, passx, x, w + (i < r ? 1 : 0), tgactive, arg, barg); + x += w + (i < r ? 1 : 0); + i++; + } + /* no master and stack mode, e.g. monocole, grid layouts, fibonacci */ + } else if (fulllayout) { + den = clientsnmaster + clientsnstack + clientsnhidden; + r = num % den; + w = num / den; + for (c = m->clients, i = 0; c; c = c->next) { + if (!ISVISIBLE(c) || (c->isfloating && !HIDDEN(c))) + continue; + tabfn(m, c, passx, x, w + (i < r ? 1 : 0), tgactive, arg, barg); + x += w + (i < r ? 1 : 0); + i++; + } + /* tiled mode */ + } else { + den = clientsnmaster; + c = m->clients; + i = 0; + if (den) { + if (clientsnstack + clientsnfloating * BARTAB_FLOATWEIGHT + clientsnhidden) { + tgactive = masteractive; + num = tabw * m->mfact; + } + r = num % den; + w = num / den; + for (; c && i < m->nmaster; c = c->next) { // tiled master + if (!ISVISIBLE(c) || c->isfloating || HIDDEN(c)) + continue; + tabfn(m, c, passx, x, w + (i < r ? 1 : 0), tgactive, arg, barg); + x += w + (i < r ? 1 : 0); + i++; + } + tgactive = !tgactive; + num = tabw - num; + } + + den = clientsnstack * BARTAB_STACKWEIGHT + clientsnfloating * BARTAB_FLOATWEIGHT + clientsnhidden * BARTAB_HIDDENWEIGHT; + if (!den) + return 1; + + r = num % den; + w = num / den; + #if BARTAB_STACKWEIGHT + for (; c; c = c->next) { // tiled stack + if (!ISVISIBLE(c) || HIDDEN(c) || c->isfloating) + continue; + tabfn(m, c, passx, x, w * BARTAB_STACKWEIGHT + (i - m->nmaster < r ? 1 : 0), tgactive, arg, barg); + x += w * BARTAB_STACKWEIGHT + (i - m->nmaster < r ? 1 : 0); + i++; + } + #endif // BARTAB_STACKWEIGHT + + #if BARTAB_HIDDENWEIGHT + for (c = m->clients; c; c = c->next) { // hidden windows + if (!ISVISIBLE(c) || !HIDDEN(c)) + continue; + tabfn(m, c, passx, x, w * BARTAB_HIDDENWEIGHT + (i - m->nmaster < r ? 1 : 0), tgactive, arg, barg); + x += w * BARTAB_HIDDENWEIGHT + (i - m->nmaster < r ? 1 : 0); + i++; + } + #endif // BARTAB_HIDDENWEIGHT + + #if BARTAB_FLOATWEIGHT + for (c = m->clients; c; c = c->next) { // floating windows + if (!ISVISIBLE(c) || HIDDEN(c) || !c->isfloating) + continue; + tabfn(m, c, passx, x, w * BARTAB_FLOATWEIGHT + (i - m->nmaster < r ? 1 : 0), tgactive, arg, barg); + x += w * BARTAB_FLOATWEIGHT + (i - m->nmaster < r ? 1 : 0); + i++; + } + #endif // BARTAB_FLOATWEIGHT + } + return 1; +} + diff --git a/patch/bar_tabgroups.h b/patch/bar_tabgroups.h new file mode 100644 index 0000000..75a8512 --- /dev/null +++ b/patch/bar_tabgroups.h @@ -0,0 +1,8 @@ +static int width_bartabgroups(Bar *bar, BarArg *a); +static int draw_bartabgroups(Bar *bar, BarArg *a); +static int click_bartabgroups(Bar *bar, Arg *arg, BarArg *a); + +static void bartabdraw(Monitor *m, Client *c, int unused, int x, int w, int groupactive, Arg *arg, BarArg *barg); +static void bartabclick(Monitor *m, Client *c, int passx, int x, int w, int unused, Arg *arg, BarArg *barg); +static int bartabcalculate(Monitor *m, int offx, int w, int passx, void(*tabfn)(Monitor *, Client *, int, int, int, int, Arg *arg, BarArg *barg), Arg *arg, BarArg *barg); + diff --git a/patch/bar_tagicons.c b/patch/bar_tagicons.c new file mode 100644 index 0000000..2ed71c9 --- /dev/null +++ b/patch/bar_tagicons.c @@ -0,0 +1,9 @@ +char * +tagicon(Monitor *m, int tag) +{ + int tagindex = tag + NUMTAGS * m->index; + if (tagindex >= LENGTH(tagicons[DEFAULT_TAGS])) + tagindex = tagindex % LENGTH(tagicons[DEFAULT_TAGS]); + return tagicons[DEFAULT_TAGS][tagindex]; +} + diff --git a/patch/bar_tagicons.h b/patch/bar_tagicons.h new file mode 100644 index 0000000..16fad2a --- /dev/null +++ b/patch/bar_tagicons.h @@ -0,0 +1,8 @@ +enum { + DEFAULT_TAGS, + ALTERNATIVE_TAGS, + ALT_TAGS_DECORATION, +}; + +static char * tagicon(Monitor *m, int tag); + diff --git a/patch/bar_tags.c b/patch/bar_tags.c new file mode 100644 index 0000000..8e0883d --- /dev/null +++ b/patch/bar_tags.c @@ -0,0 +1,75 @@ +int +width_tags(Bar *bar, BarArg *a) +{ + int w, i; + Client *c; + unsigned int occ = 0; + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + + for (w = 0, i = 0; i < NUMTAGS; i++) { + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + w += TEXTW(tagicon(bar->mon, i)); + } + return w; +} + +int +draw_tags(Bar *bar, BarArg *a) +{ + int invert; + int w, x = a->x; + unsigned int i, occ = 0, urg = 0; + char *icon; + Client *c; + Monitor *m = bar->mon; + + for (c = m->clients; c; c = c->next) { + occ |= c->tags == 255 ? 0 : c->tags; + if (c->isurgent) + urg |= c->tags; + } + for (i = 0; i < NUMTAGS; i++) { + /* do not draw vacant tags */ + if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) + continue; + + icon = tagicon(bar->mon, i); + invert = 0; + w = TEXTW(icon); + drw_setscheme(drw, scheme[ + m->tagset[m->seltags] & 1 << i + ? SchemeTagsSel + : urg & 1 << i + ? SchemeUrg + : SchemeTagsNorm + ]); + drw_text(drw, x, a->y, w, a->h, lrpad / 2, icon, invert, False); + drawindicator(m, NULL, occ, x, a->y, w, a->h, i, -1, invert, tagindicatortype); + x += w; + } + + return 1; +} + +int +click_tags(Bar *bar, Arg *arg, BarArg *a) +{ + int i = 0, x = lrpad / 2; + Client *c; + unsigned int occ = 0; + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + + do { + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + x += TEXTW(tagicon(bar->mon, i)); + } while (a->x >= x && ++i < NUMTAGS); + if (i < NUMTAGS) { + arg->ui = 1 << i; + } + return ClkTagBar; +} + diff --git a/patch/bar_tags.h b/patch/bar_tags.h new file mode 100644 index 0000000..9261261 --- /dev/null +++ b/patch/bar_tags.h @@ -0,0 +1,4 @@ +static int width_tags(Bar *bar, BarArg *a); +static int draw_tags(Bar *bar, BarArg *a); +static int click_tags(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_wintitle.c b/patch/bar_wintitle.c new file mode 100644 index 0000000..bc8eebf --- /dev/null +++ b/patch/bar_wintitle.c @@ -0,0 +1,39 @@ +int +width_wintitle(Bar *bar, BarArg *a) +{ + return a->w; +} + +int +draw_wintitle(Bar *bar, BarArg *a) +{ + int x = a->x + lrpad / 2, w = a->w - lrpad / 2; + Monitor *m = bar->mon; + Client *c = m->sel; + int pad = lrpad / 2; + + if (!c) { + drw_setscheme(drw, scheme[SchemeTitleNorm]); + drw_rect(drw, x, a->y, w, a->h, 1, 1); + return 0; + } + + drw_setscheme(drw, scheme[m == selmon ? SchemeTitleSel : SchemeTitleNorm]); + XSetErrorHandler(xerrordummy); + if (TEXTW(c->name) < w) + pad = (w - TEXTW(c->name) + lrpad) / 2; + + drw_text(drw, x, a->y, w, a->h, pad, c->name, 0, False); + + XSync(dpy, False); + XSetErrorHandler(xerror); + drawstateindicator(m, c, 1, x, a->y, w, a->h, 0, 0, c->isfixed); + return 1; +} + +int +click_wintitle(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkWinTitle; +} + diff --git a/patch/bar_wintitle.h b/patch/bar_wintitle.h new file mode 100644 index 0000000..7e8cce5 --- /dev/null +++ b/patch/bar_wintitle.h @@ -0,0 +1,4 @@ +static int width_wintitle(Bar *bar, BarArg *a); +static int draw_wintitle(Bar *bar, BarArg *a); +static int click_wintitle(Bar *bar, Arg *arg, BarArg *a); + diff --git a/patch/bar_wintitleactions.c b/patch/bar_wintitleactions.c new file mode 100644 index 0000000..9a10412 --- /dev/null +++ b/patch/bar_wintitleactions.c @@ -0,0 +1,91 @@ +void +hide(Client *c) { + + Client *n; + if (!c || HIDDEN(c)) + return; + + Window w = c->win; + static XWindowAttributes ra, ca; + + // more or less taken directly from blackbox's hide() function + XGrabServer(dpy); + XGetWindowAttributes(dpy, root, &ra); + XGetWindowAttributes(dpy, w, &ca); + // prevent UnmapNotify events + XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask); + XSelectInput(dpy, w, ca.your_event_mask & ~StructureNotifyMask); + XUnmapWindow(dpy, w); + setclientstate(c, IconicState); + XSelectInput(dpy, root, ra.your_event_mask); + XSelectInput(dpy, w, ca.your_event_mask); + XUngrabServer(dpy); + + if (c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + for (n = c->snext; n && (!ISVISIBLE(n) || HIDDEN(n)); n = n->snext); + if (!n) + for (n = c->mon->stack; n && (!ISVISIBLE(n) || HIDDEN(n)); n = n->snext); + } else { + n = nexttiled(c); + if (!n) + n = prevtiled(c); + } + focus(n); + arrange(c->mon); +} + +void +show(Client *c) +{ + if (!c || !HIDDEN(c)) + return; + + XMapWindow(dpy, c->win); + setclientstate(c, NormalState); + arrange(c->mon); +} + +void +togglewin(const Arg *arg) +{ + Client *c = (Client*)arg->v; + if (!c) + return; + if (c == selmon->sel) + hide(c); + else { + if (HIDDEN(c)) + show(c); + focus(c); + restack(c->mon); + } +} + +Client * +prevtiled(Client *c) +{ + Client *p, *i; + for (p = NULL, i = c->mon->clients; c && i != c; i = i->next) + if (ISVISIBLE(i) && !HIDDEN(i)) + p = i; + return p; +} + +void +showhideclient(const Arg *arg) +{ + Client *c = (Client*)arg->v; + if (!c) + c = selmon->sel; + if (!c) + return; + + if (HIDDEN(c)) { + show(c); + focus(c); + restack(c->mon); + } else { + hide(c); + } +} + diff --git a/patch/bar_wintitleactions.h b/patch/bar_wintitleactions.h new file mode 100644 index 0000000..0c8d5f0 --- /dev/null +++ b/patch/bar_wintitleactions.h @@ -0,0 +1,6 @@ +static void hide(Client *c); +static void show(Client *c); +static void togglewin(const Arg *arg); +static Client * prevtiled(Client *c); +static void showhideclient(const Arg *arg); + diff --git a/patch/decorationhints.c b/patch/decorationhints.c new file mode 100644 index 0000000..91bf3ed --- /dev/null +++ b/patch/decorationhints.c @@ -0,0 +1,35 @@ +static Atom motifatom; + +void +updatemotifhints(Client *c) +{ + Atom real; + int format; + unsigned char *p = NULL; + unsigned long n, extra; + unsigned long *motif; + int width, height; + + if (!decorhints) + return; + + if (XGetWindowProperty(dpy, c->win, motifatom, 0L, 5L, False, motifatom, + &real, &format, &n, &extra, &p) == Success && p != NULL) { + motif = (unsigned long*)p; + if (motif[MWM_HINTS_FLAGS_FIELD] & MWM_HINTS_DECORATIONS) { + width = WIDTH(c); + height = HEIGHT(c); + + if (motif[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_ALL || + motif[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_BORDER || + motif[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_TITLE) + c->bw = c->oldbw = borderpx; + else + c->bw = c->oldbw = 0; + + resize(c, c->x, c->y, width - (2*c->bw), height - (2*c->bw), 0); + } + XFree(p); + } +} + diff --git a/patch/decorationhints.h b/patch/decorationhints.h new file mode 100644 index 0000000..e28f507 --- /dev/null +++ b/patch/decorationhints.h @@ -0,0 +1,9 @@ +#define MWM_HINTS_FLAGS_FIELD 0 +#define MWM_HINTS_DECORATIONS_FIELD 2 +#define MWM_HINTS_DECORATIONS (1 << 1) +#define MWM_DECOR_ALL (1 << 0) +#define MWM_DECOR_BORDER (1 << 1) +#define MWM_DECOR_TITLE (1 << 3) + +static void updatemotifhints(Client *c); + diff --git a/patch/fakefullscreenclient.c b/patch/fakefullscreenclient.c new file mode 100644 index 0000000..509211f --- /dev/null +++ b/patch/fakefullscreenclient.c @@ -0,0 +1,19 @@ +void +togglefakefullscreen(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c) + return; + + if (c->fakefullscreen != 1 && c->isfullscreen) { // exit fullscreen --> fake fullscreen + c->fakefullscreen = 2; + setfullscreen(c, 0); + } else if (c->fakefullscreen == 1) { + setfullscreen(c, 0); + c->fakefullscreen = 0; + } else { + c->fakefullscreen = 1; + setfullscreen(c, 1); + } +} + diff --git a/patch/fakefullscreenclient.h b/patch/fakefullscreenclient.h new file mode 100644 index 0000000..889eed6 --- /dev/null +++ b/patch/fakefullscreenclient.h @@ -0,0 +1,2 @@ +static void togglefakefullscreen(const Arg *arg); + diff --git a/patch/focusadjacenttag.c b/patch/focusadjacenttag.c new file mode 100644 index 0000000..990ee29 --- /dev/null +++ b/patch/focusadjacenttag.c @@ -0,0 +1,80 @@ +void +tagtoleft(const Arg *arg) +{ + if (selmon->sel != NULL + && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] > 1) { + selmon->sel->tags >>= 1; + focus(NULL); + arrange(selmon); + } +} + +void +tagtoright(const Arg *arg) +{ + if (selmon->sel != NULL + && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) { + selmon->sel->tags <<= 1; + focus(NULL); + arrange(selmon); + } +} + +void +viewtoleft(const Arg *arg) +{ + if (__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] > 1) { + selmon->seltags ^= 1; /* toggle sel tagset */ + pertagview(&((Arg) { .ui = selmon->tagset[selmon->seltags ^ 1] >> 1 })); + focus(NULL); + arrange(selmon); + updatecurrentdesktop(); + } +} + +void +viewtoright(const Arg *arg) +{ + if (__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) { + selmon->seltags ^= 1; /* toggle sel tagset */ + pertagview(&((Arg) { .ui = selmon->tagset[selmon->seltags ^ 1] << 1 })); + focus(NULL); + arrange(selmon); + updatecurrentdesktop(); + } +} + +void +tagandviewtoleft(const Arg *arg) +{ + if (selmon->sel != NULL + && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] > 1) { + selmon->sel->tags >>= 1; + selmon->seltags ^= 1; /* toggle sel tagset */ + pertagview(&((Arg) { .ui = selmon->tagset[selmon->seltags ^ 1] >> 1 })); + focus(selmon->sel); + arrange(selmon); + updatecurrentdesktop(); + } +} + +void +tagandviewtoright(const Arg *arg) +{ + if (selmon->sel != NULL + && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) { + selmon->sel->tags <<= 1; + selmon->seltags ^= 1; /* toggle sel tagset */ + pertagview(&((Arg) { .ui = selmon->tagset[selmon->seltags ^ 1] << 1 })); + focus(selmon->sel); + arrange(selmon); + updatecurrentdesktop(); + } +} + diff --git a/patch/focusadjacenttag.h b/patch/focusadjacenttag.h new file mode 100644 index 0000000..abd7439 --- /dev/null +++ b/patch/focusadjacenttag.h @@ -0,0 +1,7 @@ +static void tagtoleft(const Arg *arg); +static void tagtoright(const Arg *arg); +static void viewtoleft(const Arg *arg); +static void viewtoright(const Arg *arg); +static void tagandviewtoleft(const Arg *arg); +static void tagandviewtoright(const Arg *arg); + diff --git a/patch/include.c b/patch/include.c new file mode 100644 index 0000000..25f4017 --- /dev/null +++ b/patch/include.c @@ -0,0 +1,40 @@ +/* Bar functionality */ +#include "bar_indicators.c" +#include "bar_tagicons.c" + +#include "bar_alpha.c" +#include "bar_dwmblocks.c" +#include "bar_ewmhtags.c" +#include "bar_ltsymbol.c" +#include "bar_powerline_tags.c" +#include "bar_status.c" +#include "bar_statuscmd.c" +#include "bar_tabgroups.c" +#include "bar_tags.c" +#include "bar_wintitle.c" +#include "bar_systray.c" +#include "bar_wintitleactions.c" + +/* Other patches */ +#include "autostart.c" +#include "decorationhints.c" +#include "fakefullscreenclient.c" +#include "focusadjacenttag.c" +#include "inplacerotate.c" +#include "pertag.c" +#include "scratchpad_alt_1.c" +#include "sticky.c" +#include "swallow.c" +#include "tagallmon.c" +#include "tagswapmon.c" +#include "transfer.c" +#include "vanitygaps.c" +/* Layouts */ +#include "layout_facts.c" +#include "layout_centeredmaster.c" +#include "layout_centeredfloatingmaster.c" +#include "layout_fibonacci.c" +#include "layout_monocle.c" +#include "layout_nrowgrid.c" +#include "layout_tile.c" + diff --git a/patch/include.h b/patch/include.h new file mode 100644 index 0000000..7a2d97a --- /dev/null +++ b/patch/include.h @@ -0,0 +1,39 @@ +/* Bar functionality */ +#include "bar_indicators.h" +#include "bar_tagicons.h" + +#include "bar_alpha.h" +#include "bar_dwmblocks.h" +#include "bar_ewmhtags.h" +#include "bar_ltsymbol.h" +#include "bar_powerline_tags.h" +#include "bar_status.h" +#include "bar_statuscmd.h" +#include "bar_tabgroups.h" +#include "bar_tags.h" +#include "bar_wintitle.h" +#include "bar_systray.h" +#include "bar_wintitleactions.h" + +/* Other patches */ +#include "autostart.h" +#include "decorationhints.h" +#include "fakefullscreenclient.h" +#include "focusadjacenttag.h" +#include "inplacerotate.h" +#include "pertag.h" +#include "scratchpad_alt_1.h" +#include "sticky.h" +#include "swallow.h" +#include "tagallmon.h" +#include "tagswapmon.h" +#include "transfer.h" +#include "vanitygaps.h" +/* Layouts */ +#include "layout_centeredmaster.h" +#include "layout_centeredfloatingmaster.h" +#include "layout_fibonacci.h" +#include "layout_monocle.h" +#include "layout_nrowgrid.h" +#include "layout_tile.h" + diff --git a/patch/inplacerotate.c b/patch/inplacerotate.c new file mode 100644 index 0000000..b700e04 --- /dev/null +++ b/patch/inplacerotate.c @@ -0,0 +1,84 @@ +void +insertclient(Client *item, Client *insertItem, int after) +{ + Client *c; + if (item == NULL || insertItem == NULL || item == insertItem) + return; + detach(insertItem); + if (!after && selmon->clients == item) { + attach(insertItem); + return; + } + if (after) { + c = item; + } else { + for (c = selmon->clients; c; c = c->next) { + if (c->next == item) + break; + } + } + insertItem->next = c->next; + c->next = insertItem; +} + +void +inplacerotate(const Arg *arg) +{ + if (!selmon->sel || (selmon->sel->isfloating && !arg->f)) + return; + + unsigned int selidx = 0, i = 0; + Client *c = NULL, *stail = NULL, *mhead = NULL, *mtail = NULL, *shead = NULL; + + // Determine positionings for insertclient + for (c = selmon->clients; c; c = c->next) { + if (ISVISIBLE(c) && !(c->isfloating)) { + if (selmon->sel == c) + selidx = i; + if (i == selmon->nmaster - 1) + mtail = c; + if (i == selmon->nmaster) + shead = c; + if (mhead == NULL) + mhead = c; + stail = c; + i++; + } + } + + switch(arg->i) { + case 1: + if (selidx >= selmon->nmaster) + insertclient(shead, stail, 0); + else + insertclient(mhead, mtail, 0); + break; + case -1: + if (selidx >= selmon->nmaster) + insertclient(stail, shead, 1); + else + insertclient(mtail, mhead, 1); + break; + case 2: + insertclient(selmon->clients, stail, 0); + break; + case -2: + insertclient(stail, selmon->clients, 1); + break; + } + + // Restore focus position + i = 0; + for (c = selmon->clients; c; c = c->next) { + if (!ISVISIBLE(c) || (c->isfloating)) + continue; + if (i == selidx) { + focus(c); + break; + } + i++; + } + arrange(selmon); + focus(c); +} + diff --git a/patch/inplacerotate.h b/patch/inplacerotate.h new file mode 100644 index 0000000..2669375 --- /dev/null +++ b/patch/inplacerotate.h @@ -0,0 +1,2 @@ +static void inplacerotate(const Arg *arg); + diff --git a/patch/ipc/IPCClient.h b/patch/ipc/IPCClient.h new file mode 100644 index 0000000..ee93030 --- /dev/null +++ b/patch/ipc/IPCClient.h @@ -0,0 +1,62 @@ +#ifndef IPC_CLIENT_H_ +#define IPC_CLIENT_H_ + +#include +#include +#include + +typedef struct IPCClient IPCClient; +/** + * This structure contains the details of an IPC Client and pointers for a + * linked list + */ +struct IPCClient { + int fd; + int subscriptions; + + char *buffer; + uint32_t buffer_size; + + struct epoll_event event; + IPCClient *next; + IPCClient *prev; +}; + +typedef IPCClient *IPCClientList; + +/** + * Allocate memory for new IPCClient with the specified file descriptor and + * initialize struct. + * + * @param fd File descriptor of IPC client + * + * @return Address to allocated IPCClient struct + */ +IPCClient *ipc_client_new(int fd); + +/** + * Add an IPC Client to the specified list + * + * @param list Address of the list to add the client to + * @param nc Address of the IPCClient + */ +void ipc_list_add_client(IPCClientList *list, IPCClient *nc); + +/** + * Remove an IPCClient from the specified list + * + * @param list Address of the list to remove the client from + * @param c Address of the IPCClient + */ +void ipc_list_remove_client(IPCClientList *list, IPCClient *c); + +/** + * Get an IPCClient from the specified IPCClient list + * + * @param list List to remove the client from + * @param fd File descriptor of the IPCClient + */ +IPCClient *ipc_list_get_client(IPCClientList list, int fd); + +#endif // IPC_CLIENT_H_ + diff --git a/patch/ipc/dwm-msg.c b/patch/ipc/dwm-msg.c new file mode 100644 index 0000000..ca1e1a4 --- /dev/null +++ b/patch/ipc/dwm-msg.c @@ -0,0 +1,549 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPC_MAGIC "DWM-IPC" +// clang-format off +#define IPC_MAGIC_ARR { 'D', 'W', 'M', '-', 'I', 'P', 'C' } +// clang-format on +#define IPC_MAGIC_LEN 7 // Not including null char + +#define IPC_EVENT_TAG_CHANGE "tag_change_event" +#define IPC_EVENT_CLIENT_FOCUS_CHANGE "client_focus_change_event" +#define IPC_EVENT_LAYOUT_CHANGE "layout_change_event" +#define IPC_EVENT_MONITOR_FOCUS_CHANGE "monitor_focus_change_event" +#define IPC_EVENT_FOCUSED_TITLE_CHANGE "focused_title_change_event" +#define IPC_EVENT_FOCUSED_STATE_CHANGE "focused_state_change_event" + +#define YSTR(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str)) +#define YINT(num) yajl_gen_integer(gen, num) +#define YDOUBLE(num) yajl_gen_double(gen, num) +#define YBOOL(v) yajl_gen_bool(gen, v) +#define YNULL() yajl_gen_null(gen) +#define YARR(body) \ + { \ + yajl_gen_array_open(gen); \ + body; \ + yajl_gen_array_close(gen); \ + } +#define YMAP(body) \ + { \ + yajl_gen_map_open(gen); \ + body; \ + yajl_gen_map_close(gen); \ + } + +typedef unsigned long Window; + +const char *DEFAULT_SOCKET_PATH = "/tmp/dwm.sock"; +static int sock_fd = -1; +static unsigned int ignore_reply = 0; + +typedef enum IPCMessageType { + IPC_TYPE_RUN_COMMAND = 0, + IPC_TYPE_GET_MONITORS = 1, + IPC_TYPE_GET_TAGS = 2, + IPC_TYPE_GET_LAYOUTS = 3, + IPC_TYPE_GET_DWM_CLIENT = 4, + IPC_TYPE_SUBSCRIBE = 5, + IPC_TYPE_EVENT = 6 +} IPCMessageType; + +// Every IPC message must begin with this +typedef struct dwm_ipc_header { + uint8_t magic[IPC_MAGIC_LEN]; + uint32_t size; + uint8_t type; +} __attribute((packed)) dwm_ipc_header_t; + +static int +recv_message(uint8_t *msg_type, uint32_t *reply_size, uint8_t **reply) +{ + uint32_t read_bytes = 0; + const int32_t to_read = sizeof(dwm_ipc_header_t); + char header[to_read]; + char *walk = header; + + // Try to read header + while (read_bytes < to_read) { + ssize_t n = read(sock_fd, header + read_bytes, to_read - read_bytes); + + if (n == 0) { + if (read_bytes == 0) { + fprintf(stderr, "Unexpectedly reached EOF while reading header."); + fprintf(stderr, + "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n", + read_bytes, to_read); + return -2; + } else { + fprintf(stderr, "Unexpectedly reached EOF while reading header."); + fprintf(stderr, + "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n", + read_bytes, to_read); + return -3; + } + } else if (n == -1) { + return -1; + } + + read_bytes += n; + } + + // Check if magic string in header matches + if (memcmp(walk, IPC_MAGIC, IPC_MAGIC_LEN) != 0) { + fprintf(stderr, "Invalid magic string. Got '%.*s', expected '%s'\n", + IPC_MAGIC_LEN, walk, IPC_MAGIC); + return -3; + } + + walk += IPC_MAGIC_LEN; + + // Extract reply size + memcpy(reply_size, walk, sizeof(uint32_t)); + walk += sizeof(uint32_t); + + // Extract message type + memcpy(msg_type, walk, sizeof(uint8_t)); + walk += sizeof(uint8_t); + + (*reply) = malloc(*reply_size); + + // Extract payload + read_bytes = 0; + while (read_bytes < *reply_size) { + ssize_t n = read(sock_fd, *reply + read_bytes, *reply_size - read_bytes); + + if (n == 0) { + fprintf(stderr, "Unexpectedly reached EOF while reading payload."); + fprintf(stderr, "Read %" PRIu32 " bytes, expected %" PRIu32 " bytes.\n", + read_bytes, *reply_size); + free(*reply); + return -2; + } else if (n == -1) { + if (errno == EINTR || errno == EAGAIN) continue; + free(*reply); + return -1; + } + + read_bytes += n; + } + + return 0; +} + +static int +read_socket(IPCMessageType *msg_type, uint32_t *msg_size, char **msg) +{ + int ret = -1; + + while (ret != 0) { + ret = recv_message((uint8_t *)msg_type, msg_size, (uint8_t **)msg); + + if (ret < 0) { + // Try again (non-fatal error) + if (ret == -1 && (errno == EINTR || errno == EAGAIN)) continue; + + fprintf(stderr, "Error receiving response from socket. "); + fprintf(stderr, "The connection might have been lost.\n"); + exit(2); + } + } + + return 0; +} + +static ssize_t +write_socket(const void *buf, size_t count) +{ + size_t written = 0; + + while (written < count) { + const ssize_t n = + write(sock_fd, ((uint8_t *)buf) + written, count - written); + + if (n == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + continue; + else + return n; + } + written += n; + } + return written; +} + +static void +connect_to_socket() +{ + struct sockaddr_un addr; + + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + + // Initialize struct to 0 + memset(&addr, 0, sizeof(struct sockaddr_un)); + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, DEFAULT_SOCKET_PATH); + + connect(sock, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)); + + sock_fd = sock; +} + +static int +send_message(IPCMessageType msg_type, uint32_t msg_size, uint8_t *msg) +{ + dwm_ipc_header_t header = { + .magic = IPC_MAGIC_ARR, .size = msg_size, .type = msg_type}; + + size_t header_size = sizeof(dwm_ipc_header_t); + size_t total_size = header_size + msg_size; + + uint8_t buffer[total_size]; + + // Copy header to buffer + memcpy(buffer, &header, header_size); + // Copy message to buffer + memcpy(buffer + header_size, msg, header.size); + + write_socket(buffer, total_size); + + return 0; +} + +static int +is_float(const char *s) +{ + size_t len = strlen(s); + int is_dot_used = 0; + int is_minus_used = 0; + + // Floats can only have one decimal point in between or digits + // Optionally, floats can also be below zero (negative) + for (int i = 0; i < len; i++) { + if (isdigit(s[i])) + continue; + else if (!is_dot_used && s[i] == '.' && i != 0 && i != len - 1) { + is_dot_used = 1; + continue; + } else if (!is_minus_used && s[i] == '-' && i == 0) { + is_minus_used = 1; + continue; + } else + return 0; + } + + return 1; +} + +static int +is_unsigned_int(const char *s) +{ + size_t len = strlen(s); + + // Unsigned int can only have digits + for (int i = 0; i < len; i++) { + if (isdigit(s[i])) + continue; + else + return 0; + } + + return 1; +} + +static int +is_signed_int(const char *s) +{ + size_t len = strlen(s); + + // Signed int can only have digits and a negative sign at the start + for (int i = 0; i < len; i++) { + if (isdigit(s[i])) + continue; + else if (i == 0 && s[i] == '-') { + continue; + } else + return 0; + } + + return 1; +} + +static void +flush_socket_reply() +{ + IPCMessageType reply_type; + uint32_t reply_size; + char *reply; + + read_socket(&reply_type, &reply_size, &reply); + + free(reply); +} + +static void +print_socket_reply() +{ + IPCMessageType reply_type; + uint32_t reply_size; + char *reply; + + read_socket(&reply_type, &reply_size, &reply); + + printf("%.*s\n", reply_size, reply); + fflush(stdout); + free(reply); +} + +static int +run_command(const char *name, char *args[], int argc) +{ + const unsigned char *msg; + size_t msg_size; + + yajl_gen gen = yajl_gen_alloc(NULL); + + // Message format: + // { + // "command": "", + // "args": [ ... ] + // } + // clang-format off + YMAP( + YSTR("command"); YSTR(name); + YSTR("args"); YARR( + for (int i = 0; i < argc; i++) { + if (is_signed_int(args[i])) { + long long num = atoll(args[i]); + YINT(num); + } else if (is_float(args[i])) { + float num = atof(args[i]); + YDOUBLE(num); + } else { + YSTR(args[i]); + } + } + ) + ) + // clang-format on + + yajl_gen_get_buf(gen, &msg, &msg_size); + + send_message(IPC_TYPE_RUN_COMMAND, msg_size, (uint8_t *)msg); + + if (!ignore_reply) + print_socket_reply(); + else + flush_socket_reply(); + + yajl_gen_free(gen); + + return 0; +} + +static int +get_monitors() +{ + send_message(IPC_TYPE_GET_MONITORS, 1, (uint8_t *)""); + print_socket_reply(); + return 0; +} + +static int +get_tags() +{ + send_message(IPC_TYPE_GET_TAGS, 1, (uint8_t *)""); + print_socket_reply(); + + return 0; +} + +static int +get_layouts() +{ + send_message(IPC_TYPE_GET_LAYOUTS, 1, (uint8_t *)""); + print_socket_reply(); + + return 0; +} + +static int +get_dwm_client(Window win) +{ + const unsigned char *msg; + size_t msg_size; + + yajl_gen gen = yajl_gen_alloc(NULL); + + // Message format: + // { + // "client_window_id": "" + // } + // clang-format off + YMAP( + YSTR("client_window_id"); YINT(win); + ) + // clang-format on + + yajl_gen_get_buf(gen, &msg, &msg_size); + + send_message(IPC_TYPE_GET_DWM_CLIENT, msg_size, (uint8_t *)msg); + + print_socket_reply(); + + yajl_gen_free(gen); + + return 0; +} + +static int +subscribe(const char *event) +{ + const unsigned char *msg; + size_t msg_size; + + yajl_gen gen = yajl_gen_alloc(NULL); + + // Message format: + // { + // "event": "", + // "action": "subscribe" + // } + // clang-format off + YMAP( + YSTR("event"); YSTR(event); + YSTR("action"); YSTR("subscribe"); + ) + // clang-format on + + yajl_gen_get_buf(gen, &msg, &msg_size); + + send_message(IPC_TYPE_SUBSCRIBE, msg_size, (uint8_t *)msg); + + if (!ignore_reply) + print_socket_reply(); + else + flush_socket_reply(); + + yajl_gen_free(gen); + + return 0; +} + +static void +usage_error(const char *prog_name, const char *format, ...) +{ + va_list args; + va_start(args, format); + + fprintf(stderr, "Error: "); + vfprintf(stderr, format, args); + fprintf(stderr, "\nusage: %s [...]\n", prog_name); + fprintf(stderr, "Try '%s help'\n", prog_name); + + va_end(args); + exit(1); +} + +static void +print_usage(const char *name) +{ + printf("usage: %s [options] [...]\n", name); + puts(""); + puts("Commands:"); + puts(" run_command [args...] Run an IPC command"); + puts(""); + puts(" get_monitors Get monitor properties"); + puts(""); + puts(" get_tags Get list of tags"); + puts(""); + puts(" get_layouts Get list of layouts"); + puts(""); + puts(" get_dwm_client Get dwm client proprties"); + puts(""); + puts(" subscribe [events...] Subscribe to specified events"); + puts(" Options: " IPC_EVENT_TAG_CHANGE ","); + puts(" " IPC_EVENT_LAYOUT_CHANGE ","); + puts(" " IPC_EVENT_CLIENT_FOCUS_CHANGE ","); + puts(" " IPC_EVENT_MONITOR_FOCUS_CHANGE ","); + puts(" " IPC_EVENT_FOCUSED_TITLE_CHANGE ","); + puts(" " IPC_EVENT_FOCUSED_STATE_CHANGE); + puts(""); + puts(" help Display this message"); + puts(""); + puts("Options:"); + puts(" --ignore-reply Don't print reply messages from"); + puts(" run_command and subscribe."); + puts(""); +} + +int +main(int argc, char *argv[]) +{ + const char *prog_name = argv[0]; + + connect_to_socket(); + if (sock_fd == -1) { + fprintf(stderr, "Failed to connect to socket\n"); + return 1; + } + + int i = 1; + if (i < argc && strcmp(argv[i], "--ignore-reply") == 0) { + ignore_reply = 1; + i++; + } + + if (i >= argc) usage_error(prog_name, "Expected an argument, got none"); + + if (!argc || strcmp(argv[i], "help") == 0) + print_usage(prog_name); + else if (strcmp(argv[i], "run_command") == 0) { + if (++i >= argc) usage_error(prog_name, "No command specified"); + // Command name + char *command = argv[i]; + // Command arguments are everything after command name + char **command_args = argv + ++i; + // Number of command arguments + int command_argc = argc - i; + run_command(command, command_args, command_argc); + } else if (strcmp(argv[i], "get_monitors") == 0) { + get_monitors(); + } else if (strcmp(argv[i], "get_tags") == 0) { + get_tags(); + } else if (strcmp(argv[i], "get_layouts") == 0) { + get_layouts(); + } else if (strcmp(argv[i], "get_dwm_client") == 0) { + if (++i < argc) { + if (is_unsigned_int(argv[i])) { + Window win = atol(argv[i]); + get_dwm_client(win); + } else + usage_error(prog_name, "Expected unsigned integer argument"); + } else + usage_error(prog_name, "Expected the window id"); + } else if (strcmp(argv[i], "subscribe") == 0) { + if (++i < argc) { + for (int j = i; j < argc; j++) subscribe(argv[j]); + } else + usage_error(prog_name, "Expected event name"); + // Keep listening for events forever + while (1) { + print_socket_reply(); + } + } else + usage_error(prog_name, "Invalid argument '%s'", argv[i]); + + return 0; +} + diff --git a/patch/ipc/yajl_dumps.h b/patch/ipc/yajl_dumps.h new file mode 100644 index 0000000..bb57a17 --- /dev/null +++ b/patch/ipc/yajl_dumps.h @@ -0,0 +1,66 @@ +#ifndef YAJL_DUMPS_H_ +#define YAJL_DUMPS_H_ + +#include +#include + +#define YSTR(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str)) +#define YINT(num) yajl_gen_integer(gen, num) +#define YDOUBLE(num) yajl_gen_double(gen, num) +#define YBOOL(v) yajl_gen_bool(gen, v) +#define YNULL() yajl_gen_null(gen) +#define YARR(body) \ + { \ + yajl_gen_array_open(gen); \ + body; \ + yajl_gen_array_close(gen); \ + } +#define YMAP(body) \ + { \ + yajl_gen_map_open(gen); \ + body; \ + yajl_gen_map_close(gen); \ + } + +int dump_tag(yajl_gen gen, const char *name, const int tag_mask); + +int dump_tags(yajl_gen gen, int tags_len); + +int dump_client(yajl_gen gen, Client *c); + +int dump_monitor(yajl_gen gen, Monitor *mon, int is_selected); + +int dump_monitors(yajl_gen gen, Monitor *mons, Monitor *selmon); + +int dump_layouts(yajl_gen gen, const Layout layouts[], const int layouts_len); + +int dump_tag_state(yajl_gen gen, TagState state); + +int dump_tag_event(yajl_gen gen, int mon_num, TagState old_state, + TagState new_state); + +int dump_client_focus_change_event(yajl_gen gen, Client *old_client, + Client *new_client, int mon_num); + +int dump_layout_change_event(yajl_gen gen, const int mon_num, + const char *old_symbol, const Layout *old_layout, + const char *new_symbol, const Layout *new_layout); + +int dump_monitor_focus_change_event(yajl_gen gen, const int last_mon_num, + const int new_mon_num); + +int dump_focused_title_change_event(yajl_gen gen, const int mon_num, + const Window client_id, + const char *old_name, const char *new_name); + +int dump_client_state(yajl_gen gen, const ClientState *state); + +int dump_focused_state_change_event(yajl_gen gen, const int mon_num, + const Window client_id, + const ClientState *old_state, + const ClientState *new_state); + +int dump_error_message(yajl_gen gen, const char *reason); + +#endif // YAJL_DUMPS_H_ + diff --git a/patch/layout_centeredfloatingmaster.c b/patch/layout_centeredfloatingmaster.c new file mode 100644 index 0000000..af3c192 --- /dev/null +++ b/patch/layout_centeredfloatingmaster.c @@ -0,0 +1,55 @@ +void +centeredfloatingmaster(Monitor *m) +{ + unsigned int i, n; + float mfacts, sfacts; + int mrest, srest; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + Client *c; + + float mivf = 1.0; // master inner vertical gap factor + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + + if (n == 0) + return; + + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + sh = mh = m->wh - 2*oh; + mw = m->ww - 2*ov - iv*(n - 1); + sw = m->ww - 2*ov - iv*(n - m->nmaster - 1); + + if (m->nmaster && n > m->nmaster) { + mivf = 0.8; + /* go mfact box in the center if more than nmaster clients */ + if (m->ww > m->wh) { + mw = m->ww * m->mfact - iv*mivf*(MIN(n, m->nmaster) - 1); + mh = m->wh * 0.9; + } else { + mw = m->ww * 0.9 - iv*mivf*(MIN(n, m->nmaster) - 1); + mh = m->wh * m->mfact; + } + mx = m->wx + (m->ww - mw) / 2; + my = m->wy + (m->wh - mh - 2*oh) / 2; + + sx = m->wx + ov; + sy = m->wy + oh; + sh = m->wh - 2*oh; + } + + getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + /* nmaster clients are stacked horizontally, in the center of the screen */ + resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + mx += WIDTH(c) + iv*mivf; + } else { + /* stack clients are stacked horizontally */ + resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + sx += WIDTH(c) + iv; + } +} + diff --git a/patch/layout_centeredfloatingmaster.h b/patch/layout_centeredfloatingmaster.h new file mode 100644 index 0000000..66b0b0e --- /dev/null +++ b/patch/layout_centeredfloatingmaster.h @@ -0,0 +1,2 @@ +static void centeredfloatingmaster(Monitor *m); + diff --git a/patch/layout_centeredmaster.c b/patch/layout_centeredmaster.c new file mode 100644 index 0000000..4b40a30 --- /dev/null +++ b/patch/layout_centeredmaster.c @@ -0,0 +1,85 @@ +void +centeredmaster(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int lx = 0, ly = 0, lw = 0, lh = 0; + int rx = 0, ry = 0, rw = 0, rh = 0; + float mfacts = 0, lfacts = 0, rfacts = 0; + int mtotal = 0, ltotal = 0, rtotal = 0; + int mrest = 0, lrest = 0, rrest = 0; + Client *c; + + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + + if (n == 0) + return; + + /* initialize areas */ + mx = m->wx + ov; + my = m->wy + oh; + mh = m->wh - 2*oh - ih * ((!m->nmaster ? n : MIN(n, m->nmaster)) - 1); + mw = m->ww - 2*ov; + lh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - 1); + rh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - ((n - m->nmaster) % 2 ? 0 : 1)); + + if (m->nmaster && n > m->nmaster) { + /* go mfact box in the center if more than nmaster clients */ + if (n - m->nmaster > 1) { + /* ||<-S->|<---M--->|<-S->|| */ + mw = (m->ww - 2*ov - 2*iv) * m->mfact; + lw = (m->ww - mw - 2*ov - 2*iv) / 2; + mx += lw + iv; + } else { + /* ||<---M--->|<-S->|| */ + mw = (mw - iv) * m->mfact; + lw = m->ww - mw - iv - 2*ov; + } + rw = lw; + lx = m->wx + ov; + ly = m->wy + oh; + rx = mx + mw + iv; + ry = m->wy + oh; + } + + /* calculate facts */ + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { + if (!m->nmaster || n < m->nmaster) + mfacts += 1; + else if ((n - m->nmaster) % 2) + lfacts += 1; // total factor of left hand stack area + else + rfacts += 1; // total factor of right hand stack area + } + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) + if (!m->nmaster || n < m->nmaster) + mtotal += mh / mfacts; + else if ((n - m->nmaster) % 2) + ltotal += lh / lfacts; + else + rtotal += rh / rfacts; + + mrest = mh - mtotal; + lrest = lh - ltotal; + rrest = rh - rtotal; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + if (!m->nmaster || i < m->nmaster) { + /* nmaster clients are stacked vertically, in the center of the screen */ + resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); + my += HEIGHT(c) + ih; + } else { + /* stack clients are stacked vertically */ + if ((i - m->nmaster) % 2 ) { + resize(c, lx, ly, lw - (2*c->bw), (lh / lfacts) + ((i - 2*m->nmaster) < 2*lrest ? 1 : 0) - (2*c->bw), 0); + ly += HEIGHT(c) + ih; + } else { + resize(c, rx, ry, rw - (2*c->bw), (rh / rfacts) + ((i - 2*m->nmaster) < 2*rrest ? 1 : 0) - (2*c->bw), 0); + ry += HEIGHT(c) + ih; + } + } + } +} + diff --git a/patch/layout_centeredmaster.h b/patch/layout_centeredmaster.h new file mode 100644 index 0000000..703f8b8 --- /dev/null +++ b/patch/layout_centeredmaster.h @@ -0,0 +1,2 @@ +static void centeredmaster(Monitor *m); + diff --git a/patch/layout_facts.c b/patch/layout_facts.c new file mode 100644 index 0000000..241d344 --- /dev/null +++ b/patch/layout_facts.c @@ -0,0 +1,24 @@ +void +getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr) +{ + unsigned int n; + float mfacts, sfacts; + int mtotal = 0, stotal = 0; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + mfacts = MIN(n, m->nmaster); + sfacts = n - m->nmaster; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) + if (n < m->nmaster) + mtotal += msize / mfacts; + else + stotal += ssize / sfacts; + + *mf = mfacts; // total factor of master area + *sf = sfacts; // total factor of stack area + *mr = msize - mtotal; // the remainder (rest) of pixels after an even master split + *sr = ssize - stotal; // the remainder (rest) of pixels after an even stack split +} + diff --git a/patch/layout_fibonacci.c b/patch/layout_fibonacci.c new file mode 100644 index 0000000..58f0f3e --- /dev/null +++ b/patch/layout_fibonacci.c @@ -0,0 +1,94 @@ +void +fibonacci(Monitor *m, int s) +{ + unsigned int i, n; + int nx, ny, nw, nh; + int oh, ov, ih, iv; + int nv, hrest = 0, wrest = 0, r = 1; + Client *c; + + getgaps(m, &oh, &ov, &ih, &iv, &n); + if (n == 0) + return; + + nx = m->wx + ov; + ny = oh; + nw = m->ww - 2*ov; + nh = m->wh - 2*oh; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (r) { + if ((i % 2 && (nh - ih) / 2 <= (bh + 2*c->bw)) + || (!(i % 2) && (nw - iv) / 2 <= (bh + 2*c->bw))) { + r = 0; + } + if (r && i < n - 1) { + if (i % 2) { + nv = (nh - ih) / 2; + hrest = nh - 2*nv - ih; + nh = nv; + } else { + nv = (nw - iv) / 2; + wrest = nw - 2*nv - iv; + nw = nv; + } + + if ((i % 4) == 2 && !s) + nx += nw + iv; + else if ((i % 4) == 3 && !s) + ny += nh + ih; + } + + if ((i % 4) == 0) { + if (s) { + ny += nh + ih; + nh += hrest; + } + else { + nh -= hrest; + ny -= nh + ih; + } + } + else if ((i % 4) == 1) { + nx += nw + iv; + nw += wrest; + } + else if ((i % 4) == 2) { + ny += nh + ih; + nh += hrest; + if (i < n - 1) + nw += wrest; + } + else if ((i % 4) == 3) { + if (s) { + nx += nw + iv; + nw -= wrest; + } else { + nw -= wrest; + nx -= nw + iv; + nh += hrest; + } + } + if (i == 0) { + if (n != 1) { + nw = (m->ww - iv - 2*ov) - (m->ww - iv - 2*ov) * (1 - m->mfact); + wrest = 0; + } + ny = m->wy + oh; + } + else if (i == 1) + nw = m->ww - nw - iv - 2*ov; + i++; + } + + resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False); + } +} + +static void +dwindle(Monitor *m) +{ + fibonacci(m, 1); +} + + diff --git a/patch/layout_fibonacci.h b/patch/layout_fibonacci.h new file mode 100644 index 0000000..f13f77f --- /dev/null +++ b/patch/layout_fibonacci.h @@ -0,0 +1,3 @@ +static void dwindle(Monitor *m); +static void fibonacci(Monitor *m, int s); + diff --git a/patch/layout_monocle.c b/patch/layout_monocle.c new file mode 100644 index 0000000..4b3516c --- /dev/null +++ b/patch/layout_monocle.c @@ -0,0 +1,15 @@ +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); +} + diff --git a/patch/layout_monocle.h b/patch/layout_monocle.h new file mode 100644 index 0000000..f32e49f --- /dev/null +++ b/patch/layout_monocle.h @@ -0,0 +1,2 @@ +static void monocle(Monitor *m); + diff --git a/patch/layout_nrowgrid.c b/patch/layout_nrowgrid.c new file mode 100644 index 0000000..5ea7e3d --- /dev/null +++ b/patch/layout_nrowgrid.c @@ -0,0 +1,53 @@ +void +nrowgrid(Monitor *m) +{ + unsigned int n = 0, i = 0, ri = 0, ci = 0; /* counters */ + int oh, ov, ih, iv; /* vanitygap settings */ + unsigned int cx, cy, cw, ch; /* client geometry */ + unsigned int uw = 0, uh = 0, uc = 0; /* utilization trackers */ + unsigned int cols, rows = m->nmaster + 1; + Client *c; + + /* count clients */ + getgaps(m, &oh, &ov, &ih, &iv, &n); + + /* nothing to do here */ + if (n == 0) + return; + + /* force 2 clients to always split vertically */ + if (FORCE_VSPLIT && n == 2) + rows = 1; + + /* never allow empty rows */ + if (n < rows) + rows = n; + + /* define first row */ + cols = n / rows; + uc = cols; + cy = m->wy + oh; + ch = (m->wh - 2*oh - ih*(rows - 1)) / rows; + uh = ch; + + for (c = nexttiled(m->clients); c; c = nexttiled(c->next), i++, ci++) { + if (ci == cols) { + uw = 0; + ci = 0; + ri++; + + /* next row */ + cols = (n - uc) / (rows - ri); + uc += cols; + cy = m->wy + oh + uh + ih; + uh += ch + ih; + } + + cx = m->wx + ov + uw; + cw = (m->ww - 2*ov - uw) / (cols - ci); + uw += cw + iv; + + resize(c, cx, cy, cw - (2*c->bw), ch - (2*c->bw), 0); + } +} + diff --git a/patch/layout_nrowgrid.h b/patch/layout_nrowgrid.h new file mode 100644 index 0000000..9cce593 --- /dev/null +++ b/patch/layout_nrowgrid.h @@ -0,0 +1,2 @@ +static void nrowgrid(Monitor *m); + diff --git a/patch/layout_tile.c b/patch/layout_tile.c new file mode 100644 index 0000000..8d41d2a --- /dev/null +++ b/patch/layout_tile.c @@ -0,0 +1,41 @@ +static void +tile(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + float mfacts, sfacts; + int mrest, srest; + Client *c; + + + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + + if (n == 0) + return; + + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); + sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); + sw = mw = m->ww - 2*ov; + + if (m->nmaster && n > m->nmaster) { + sw = (mw - iv) * (1 - m->mfact); + mw = (mw - iv) * m->mfact; + sx = mx + mw + iv; + } + + getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); + my += HEIGHT(c) + ih; + } else { + resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); + sy += HEIGHT(c) + ih; + } +} + diff --git a/patch/layout_tile.h b/patch/layout_tile.h new file mode 100644 index 0000000..78cafc8 --- /dev/null +++ b/patch/layout_tile.h @@ -0,0 +1,2 @@ +static void tile(Monitor *); + diff --git a/patch/pertag.c b/patch/pertag.c new file mode 100644 index 0000000..ca9e47a --- /dev/null +++ b/patch/pertag.c @@ -0,0 +1,36 @@ +struct Pertag { + unsigned int curtag, prevtag; /* current and previous tag */ + int nmasters[NUMTAGS + 1]; /* number of windows in master area */ + const Layout *ltidxs[NUMTAGS + 1][2]; /* matrix of tags and layouts indexes */ + float mfacts[NUMTAGS + 1]; /* mfacts per tag */ + unsigned int sellts[NUMTAGS + 1]; /* selected layouts */ +}; + +void +pertagview(const Arg *arg) +{ + int i; + unsigned int tmptag; + if (arg->ui & TAGMASK) { + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + if (arg->ui == ~0) + selmon->pertag->curtag = 0; + else { + for (i = 0; !(arg->ui & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + +} + diff --git a/patch/pertag.h b/patch/pertag.h new file mode 100644 index 0000000..5c53ac9 --- /dev/null +++ b/patch/pertag.h @@ -0,0 +1,2 @@ +static void pertagview(const Arg *arg); + diff --git a/patch/scratchpad_alt_1.c b/patch/scratchpad_alt_1.c new file mode 100644 index 0000000..6724d5c --- /dev/null +++ b/patch/scratchpad_alt_1.c @@ -0,0 +1,72 @@ +static Client * scratchpad_last_showed = NULL; + +void +scratchpad_hide() +{ + if (selmon->sel) { + selmon->sel->tags = SCRATCHPAD_MASK; + selmon->sel->isfloating = 1; + focus(NULL); + arrange(selmon); + } +} + +_Bool +scratchpad_last_showed_is_killed(void) +{ + Client *c; + for (c = selmon->clients; c && c != scratchpad_last_showed; c = c->next); + return (c == NULL); +} + +void +scratchpad_remove() +{ + if (selmon->sel && scratchpad_last_showed != NULL && selmon->sel == scratchpad_last_showed) + scratchpad_last_showed = NULL; +} + +void +scratchpad_show() +{ + if (scratchpad_last_showed == NULL || scratchpad_last_showed_is_killed()) { + scratchpad_show_first(); + return; + } + + if (scratchpad_last_showed->tags != SCRATCHPAD_MASK) { + scratchpad_last_showed->tags = SCRATCHPAD_MASK; + focus(NULL); + arrange(selmon); + return; + } + + Client *c; + + for (c = selmon->clients; c && c != scratchpad_last_showed; c = c->next); + for (c = (c ? c->next : NULL); c && c->tags != SCRATCHPAD_MASK; c = c->next); + + if (c) + scratchpad_show_client(c); + else + scratchpad_show_first(); +} + +void +scratchpad_show_client(Client* c) +{ + scratchpad_last_showed = c; + c->tags = selmon->tagset[selmon->seltags]; + focus(c); + arrange(selmon); +} + +void +scratchpad_show_first(void) +{ + Client *c; + for (c = selmon->clients; c && c->tags != SCRATCHPAD_MASK; c = c->next); + if (c) + scratchpad_show_client(c); +} + diff --git a/patch/scratchpad_alt_1.h b/patch/scratchpad_alt_1.h new file mode 100644 index 0000000..4e25f11 --- /dev/null +++ b/patch/scratchpad_alt_1.h @@ -0,0 +1,9 @@ +#define SCRATCHPAD_MASK (1u << NUMTAGS) + +static void scratchpad_hide(); +static _Bool scratchpad_last_showed_is_killed(void); +static void scratchpad_remove(); +static void scratchpad_show(); +static void scratchpad_show_client(Client *c); +static void scratchpad_show_first(void); + diff --git a/patch/sticky.c b/patch/sticky.c new file mode 100644 index 0000000..420c3e0 --- /dev/null +++ b/patch/sticky.c @@ -0,0 +1,9 @@ +void +togglesticky(const Arg *arg) +{ + if (!selmon->sel) + return; + selmon->sel->issticky = !selmon->sel->issticky; + arrange(selmon); +} + diff --git a/patch/sticky.h b/patch/sticky.h new file mode 100644 index 0000000..277bb34 --- /dev/null +++ b/patch/sticky.h @@ -0,0 +1,2 @@ +static void togglesticky(const Arg *arg); + diff --git a/patch/swallow.c b/patch/swallow.c new file mode 100644 index 0000000..8a84134 --- /dev/null +++ b/patch/swallow.c @@ -0,0 +1,214 @@ +#include +#include +#ifdef __OpenBSD__ +#include +#include +#endif /* __OpenBSD__ */ + +static int scanner; +static xcb_connection_t *xcon; + +int +swallow(Client *p, Client *c) +{ + Client *s; + XWindowChanges wc; + + if (c->noswallow > 0 || c->isterminal) + return 0; + if (c->noswallow < 0 && !swallowfloating && c->isfloating) + return 0; + + XMapWindow(dpy, c->win); + + detach(c); + detachstack(c); + + setclientstate(c, WithdrawnState); + XUnmapWindow(dpy, p->win); + + p->swallowing = c; + c->mon = p->mon; + + Window w = p->win; + p->win = c->win; + c->win = w; + + XChangeProperty(dpy, c->win, netatom[NetClientList], XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(p->win), 1); + + updatetitle(p); + s = scanner ? c : p; + setfloatinghint(s); + + wc.border_width = p->bw; + XConfigureWindow(dpy, p->win, CWBorderWidth, &wc); + XMoveResizeWindow(dpy, p->win, s->x, s->y, s->w, s->h); + XSetWindowBorder(dpy, p->win, scheme[SchemeNorm][ColBorder].pixel); + + arrange(p->mon); + configure(p); + updateclientlist(); + + return 1; +} + +void +unswallow(Client *c) +{ + XWindowChanges wc; + c->win = c->swallowing->win; + + free(c->swallowing); + c->swallowing = NULL; + + XDeleteProperty(dpy, c->win, netatom[NetClientList]); + + /* unfullscreen the client */ + setfullscreen(c, 0); + updatetitle(c); + arrange(c->mon); + XMapWindow(dpy, c->win); + + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + + setfloatinghint(c); + setclientstate(c, NormalState); + focus(NULL); + arrange(c->mon); +} + +pid_t +winpid(Window w) +{ + pid_t result = 0; + + #ifdef __linux__ + xcb_res_client_id_spec_t spec = {0}; + spec.client = w; + spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; + + xcb_generic_error_t *e = NULL; + xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); + xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); + + if (!r) + return (pid_t)0; + + xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); + for (; i.rem; xcb_res_client_id_value_next(&i)) { + spec = i.data->spec; + if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { + uint32_t *t = xcb_res_client_id_value_value(i.data); + result = *t; + break; + } + } + + free(r); + + if (result == (pid_t)-1) + result = 0; + + #endif /* __linux__ */ + #ifdef __OpenBSD__ + Atom type; + int format; + unsigned long len, bytes; + unsigned char *prop; + pid_t ret; + + if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 1), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop) + return 0; + + ret = *(pid_t*)prop; + XFree(prop); + result = ret; + #endif /* __OpenBSD__ */ + + return result; +} + +pid_t +getparentprocess(pid_t p) +{ + unsigned int v = 0; + +#ifdef __linux__ + FILE *f; + char buf[256]; + snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); + + if (!(f = fopen(buf, "r"))) + return (pid_t)0; + + if (fscanf(f, "%*u %*s %*c %u", (unsigned *)&v) != 1) + v = (pid_t)0; + fclose(f); +#endif /* __linux__ */ +#ifdef __OpenBSD__ + int n; + kvm_t *kd; + struct kinfo_proc *kp; + + kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); + if (!kd) + return 0; + + kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); + v = kp->p_ppid; +#endif /* __OpenBSD__ */ + return (pid_t)v; +} + +int +isdescprocess(pid_t p, pid_t c) +{ + while (p != c && c != 0) + c = getparentprocess(c); + + return (int)c; +} + +Client * +termforwin(const Client *w) +{ + Client *c; + Monitor *m; + + if (!w->pid || w->isterminal) + return NULL; + + c = selmon->sel; + if (c && c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) + return c; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) + return c; + } + } + + return NULL; +} + +Client * +swallowingclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->swallowing && c->swallowing->win == w) + return c; + } + } + + return NULL; +} + diff --git a/patch/swallow.h b/patch/swallow.h new file mode 100644 index 0000000..529fea9 --- /dev/null +++ b/patch/swallow.h @@ -0,0 +1,8 @@ +static pid_t getparentprocess(pid_t p); +static int isdescprocess(pid_t p, pid_t c); +static int swallow(Client *p, Client *c); +static Client *swallowingclient(Window w); +static Client *termforwin(const Client *c); +static void unswallow(Client *c); +static pid_t winpid(Window w); + diff --git a/patch/tagallmon.c b/patch/tagallmon.c new file mode 100644 index 0000000..163d0ad --- /dev/null +++ b/patch/tagallmon.c @@ -0,0 +1,44 @@ +void +tagallmon(const Arg *arg) +{ + Monitor *m; + Client *c, *last, *slast, *next; + + if (!mons->next) + return; + + m = dirtomon(arg->i); + for (last = m->clients; last && last->next; last = last->next); + for (slast = m->stack; slast && slast->snext; slast = slast->snext); + + for (c = selmon->clients; c; c = next) { + next = c->next; + if (!ISVISIBLE(c)) + continue; + unfocus(c, 1, NULL); + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + c->next = NULL; + c->snext = NULL; + if (last) + last = last->next = c; + else + m->clients = last = c; + if (slast) + slast = slast->snext = c; + else + m->stack = slast = c; + if (c->isfullscreen) { + if (c->fakefullscreen != 1) { + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } + } + } + + focus(NULL); + arrange(NULL); +} + diff --git a/patch/tagallmon.h b/patch/tagallmon.h new file mode 100644 index 0000000..9abf535 --- /dev/null +++ b/patch/tagallmon.h @@ -0,0 +1,2 @@ +static void tagallmon(const Arg *arg); + diff --git a/patch/tagswapmon.c b/patch/tagswapmon.c new file mode 100644 index 0000000..e8b9719 --- /dev/null +++ b/patch/tagswapmon.c @@ -0,0 +1,65 @@ +void +tagswapmon(const Arg *arg) +{ + Monitor *m; + Client *c, *sc = NULL, *mc = NULL, *next; + + if (!mons->next) + return; + + m = dirtomon(arg->i); + + for (c = selmon->clients; c; c = next) { + next = c->next; + if (!ISVISIBLE(c)) + continue; + unfocus(c, 1, NULL); + detach(c); + detachstack(c); + c->next = sc; + sc = c; + } + + for (c = m->clients; c; c = next) { + next = c->next; + if (!ISVISIBLE(c)) + continue; + unfocus(c, 1, NULL); + detach(c); + detachstack(c); + c->next = mc; + mc = c; + } + + for (c = sc; c; c = next) { + next = c->next; + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attach(c); + attachstack(c); + if (c->isfullscreen) { + if (c->fakefullscreen != 1) { + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } + } + } + + for (c = mc; c; c = next) { + next = c->next; + c->mon = selmon; + c->tags = selmon->tagset[selmon->seltags]; /* assign tags of target monitor */ + attach(c); + attachstack(c); + if (c->isfullscreen) { + if (c->fakefullscreen != 1) { + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } + } + } + + focus(NULL); + arrange(NULL); +} + diff --git a/patch/tagswapmon.h b/patch/tagswapmon.h new file mode 100644 index 0000000..949f591 --- /dev/null +++ b/patch/tagswapmon.h @@ -0,0 +1,2 @@ +static void tagswapmon(const Arg *arg); + diff --git a/patch/transfer.c b/patch/transfer.c new file mode 100644 index 0000000..ce64319 --- /dev/null +++ b/patch/transfer.c @@ -0,0 +1,34 @@ +void +transfer(const Arg *arg) +{ + Client *c, *mtail = selmon->clients, *stail = NULL, *insertafter; + int transfertostack = 0, i, nmasterclients; + + for (i = 0, c = selmon->clients; c; c = c->next) { + if (!ISVISIBLE(c) || c->isfloating) continue; + if (selmon->sel == c) { transfertostack = i < selmon->nmaster && selmon->nmaster != 0; } + if (i < selmon->nmaster) { nmasterclients++; mtail = c; } + stail = c; + i++; + } + if (!selmon->sel || selmon->sel->isfloating || i == 0) { + return; + } else if (transfertostack) { + selmon->nmaster = MIN(i, selmon->nmaster) - 1; + insertafter = stail; + } else { + selmon->nmaster = selmon->nmaster + 1; + insertafter = mtail; + } + if (insertafter != selmon->sel) { + detach(selmon->sel); + if (selmon->nmaster == 1 && !transfertostack) { + attach(selmon->sel); // Head prepend case + } else { + selmon->sel->next = insertafter->next; + insertafter->next = selmon->sel; + } + } + arrange(selmon); +} + diff --git a/patch/transfer.h b/patch/transfer.h new file mode 100644 index 0000000..e887dae --- /dev/null +++ b/patch/transfer.h @@ -0,0 +1,2 @@ +static void transfer(const Arg *arg); + diff --git a/patch/vanitygaps.c b/patch/vanitygaps.c new file mode 100644 index 0000000..f3ecc12 --- /dev/null +++ b/patch/vanitygaps.c @@ -0,0 +1,130 @@ +/* Settings */ +static int enablegaps = 1; + +static void +setgaps(int oh, int ov, int ih, int iv) +{ + if (oh < 0) oh = 0; + if (ov < 0) ov = 0; + if (ih < 0) ih = 0; + if (iv < 0) iv = 0; + + selmon->gappoh = oh; + selmon->gappov = ov; + selmon->gappih = ih; + selmon->gappiv = iv; + + + arrange(selmon); +} + + +static void +togglegaps(const Arg *arg) +{ + enablegaps = !enablegaps; + arrange(NULL); +} + +static void +defaultgaps(const Arg *arg) +{ + setgaps(gappoh, gappov, gappih, gappiv); +} + +static void +incrgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +static void +incrigaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +static void +incrogaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +static void +incrohgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov, + selmon->gappih, + selmon->gappiv + ); +} + +static void +incrovgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +static void +incrihgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + ); +} + +static void +incrivgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih, + selmon->gappiv + arg->i + ); +} + +static void +getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc) +{ + unsigned int n, oe, ie; + oe = ie = enablegaps; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 1) { + oe *= smartgaps_fact; // outer gaps disabled or multiplied when only one client + } + + *oh = m->gappoh*oe; // outer horizontal gap + *ov = m->gappov*oe; // outer vertical gap + *ih = m->gappih*ie; // inner horizontal gap + *iv = m->gappiv*ie; // inner vertical gap + *nc = n; // number of clients +} + diff --git a/patch/vanitygaps.h b/patch/vanitygaps.h new file mode 100644 index 0000000..81045ad --- /dev/null +++ b/patch/vanitygaps.h @@ -0,0 +1,15 @@ +/* Key binding functions */ +static void defaultgaps(const Arg *arg); +static void incrgaps(const Arg *arg); +static void incrigaps(const Arg *arg); +static void incrogaps(const Arg *arg); +static void incrohgaps(const Arg *arg); +static void incrovgaps(const Arg *arg); +static void incrihgaps(const Arg *arg); +static void incrivgaps(const Arg *arg); +static void togglegaps(const Arg *arg); + +/* Internals */ +static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc); +static void setgaps(int oh, int ov, int ih, int iv); + diff --git a/transient.c b/transient.c new file mode 100644 index 0000000..158460f --- /dev/null +++ b/transient.c @@ -0,0 +1,43 @@ +/* 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/util.c b/util.c new file mode 100644 index 0000000..bcecb12 --- /dev/null +++ b/util.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#include "util.h" + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} + +void +die(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + diff --git a/util.h b/util.h new file mode 100644 index 0000000..1e3cf9a --- /dev/null +++ b/util.h @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ + +#ifndef MAX +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#endif +#ifndef MIN +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#endif +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +#ifdef _DEBUG +#define DEBUG(...) fprintf(stderr, __VA_ARGS__) +#else +#define DEBUG(...) +#endif + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); + diff --git a/util.o b/util.o new file mode 100644 index 0000000..a11e0c1 Binary files /dev/null and b/util.o differ