diff options
| author | Sam Chudnick <sam@chudnick.com> | 2021-11-07 06:43:28 -0500 |
|---|---|---|
| committer | Sam Chudnick <sam@chudnick.com> | 2021-11-07 06:43:28 -0500 |
| commit | 28e9605edf57dd92f278c9ac5a2757532b2cb9e0 (patch) | |
| tree | 86cb42771c52abd94848ecdd43e697b97ed0118d | |
initial commit
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | Makefile | 51 | ||||
| -rw-r--r-- | README | 48 | ||||
| -rw-r--r-- | colors.h | 51 | ||||
| -rw-r--r-- | config.h | 148 | ||||
| -rw-r--r-- | config.mk | 38 | ||||
| -rw-r--r-- | drw.c | 435 | ||||
| -rw-r--r-- | drw.h | 57 | ||||
| -rw-r--r-- | dwm.1 | 176 | ||||
| -rw-r--r-- | dwm.c | 2452 | ||||
| -rw-r--r-- | patch-list | 5 | ||||
| -rw-r--r-- | transient.c | 42 | ||||
| -rw-r--r-- | util.c | 35 | ||||
| -rw-r--r-- | util.h | 8 | ||||
| -rw-r--r-- | vanitygaps.c | 809 |
15 files changed, 4358 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eef289 --- /dev/null +++ b/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | *.o | ||
| 2 | dwm | ||
| 3 | patches/* | ||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..77bcbc0 --- /dev/null +++ b/Makefile | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | # dwm - dynamic window manager | ||
| 2 | # See LICENSE file for copyright and license details. | ||
| 3 | |||
| 4 | include config.mk | ||
| 5 | |||
| 6 | SRC = drw.c dwm.c util.c | ||
| 7 | OBJ = ${SRC:.c=.o} | ||
| 8 | |||
| 9 | all: options dwm | ||
| 10 | |||
| 11 | options: | ||
| 12 | @echo dwm build options: | ||
| 13 | @echo "CFLAGS = ${CFLAGS}" | ||
| 14 | @echo "LDFLAGS = ${LDFLAGS}" | ||
| 15 | @echo "CC = ${CC}" | ||
| 16 | |||
| 17 | .c.o: | ||
| 18 | ${CC} -c ${CFLAGS} $< | ||
| 19 | |||
| 20 | ${OBJ}: config.h config.mk | ||
| 21 | |||
| 22 | config.h: | ||
| 23 | cp config.def.h $@ | ||
| 24 | |||
| 25 | dwm: ${OBJ} | ||
| 26 | ${CC} -o $@ ${OBJ} ${LDFLAGS} | ||
| 27 | |||
| 28 | clean: | ||
| 29 | rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz | ||
| 30 | |||
| 31 | dist: clean | ||
| 32 | mkdir -p dwm-${VERSION} | ||
| 33 | cp -R LICENSE Makefile README config.def.h config.mk\ | ||
| 34 | dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} | ||
| 35 | tar -cf dwm-${VERSION}.tar dwm-${VERSION} | ||
| 36 | gzip dwm-${VERSION}.tar | ||
| 37 | rm -rf dwm-${VERSION} | ||
| 38 | |||
| 39 | install: all | ||
| 40 | mkdir -p ${DESTDIR}${PREFIX}/bin | ||
| 41 | cp -f dwm ${DESTDIR}${PREFIX}/bin | ||
| 42 | chmod 755 ${DESTDIR}${PREFIX}/bin/dwm | ||
| 43 | mkdir -p ${DESTDIR}${MANPREFIX}/man1 | ||
| 44 | sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 | ||
| 45 | chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 | ||
| 46 | |||
| 47 | uninstall: | ||
| 48 | rm -f ${DESTDIR}${PREFIX}/bin/dwm\ | ||
| 49 | ${DESTDIR}${MANPREFIX}/man1/dwm.1 | ||
| 50 | |||
| 51 | .PHONY: all options clean dist install uninstall | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | dwm - dynamic window manager | ||
| 2 | ============================ | ||
| 3 | dwm is an extremely fast, small, and dynamic window manager for X. | ||
| 4 | |||
| 5 | |||
| 6 | Requirements | ||
| 7 | ------------ | ||
| 8 | In order to build dwm you need the Xlib header files. | ||
| 9 | |||
| 10 | |||
| 11 | Installation | ||
| 12 | ------------ | ||
| 13 | Edit config.mk to match your local setup (dwm is installed into | ||
| 14 | the /usr/local namespace by default). | ||
| 15 | |||
| 16 | Afterwards enter the following command to build and install dwm (if | ||
| 17 | necessary as root): | ||
| 18 | |||
| 19 | make clean install | ||
| 20 | |||
| 21 | |||
| 22 | Running dwm | ||
| 23 | ----------- | ||
| 24 | Add the following line to your .xinitrc to start dwm using startx: | ||
| 25 | |||
| 26 | exec dwm | ||
| 27 | |||
| 28 | In order to connect dwm to a specific display, make sure that | ||
| 29 | the DISPLAY environment variable is set correctly, e.g.: | ||
| 30 | |||
| 31 | DISPLAY=foo.bar:1 exec dwm | ||
| 32 | |||
| 33 | (This will start dwm on display :1 of the host foo.bar.) | ||
| 34 | |||
| 35 | In order to display status info in the bar, you can do something | ||
| 36 | like this in your .xinitrc: | ||
| 37 | |||
| 38 | while xsetroot -name "`date` `uptime | sed 's/.*,//'`" | ||
| 39 | do | ||
| 40 | sleep 1 | ||
| 41 | done & | ||
| 42 | exec dwm | ||
| 43 | |||
| 44 | |||
| 45 | Configuration | ||
| 46 | ------------- | ||
| 47 | The configuration of dwm is done by creating a custom config.h | ||
| 48 | and (re)compiling the source code. | ||
diff --git a/colors.h b/colors.h new file mode 100644 index 0000000..24540c1 --- /dev/null +++ b/colors.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | /*Arc-Dark Variables*/ | ||
| 2 | static const char ad_base[] = "#404552"; | ||
| 3 | static const char ad_fg[] = "#d3dae3"; | ||
| 4 | static const char ad_text[] = "#d3dae3"; | ||
| 5 | static const char ad_bg[] = "#383c4a"; | ||
| 6 | static const char ad_sel_fg[] = "#ffffff"; | ||
| 7 | static const char ad_sel_bg[] = "#5294e2"; | ||
| 8 | static const char ad_hlght[] = "#5294e2"; | ||
| 9 | |||
| 10 | /* Dwm Defaults */ | ||
| 11 | static const char col_gray1[] = "#222222"; | ||
| 12 | static const char col_gray2[] = "#444444"; | ||
| 13 | static const char col_gray3[] = "#bbbbbb"; | ||
| 14 | static const char col_gray4[] = "#eeeeee"; | ||
| 15 | static const char col_cyan[] = "#005577"; | ||
| 16 | |||
| 17 | /*Dark Blood Variables*/ | ||
| 18 | static const char db_base[] = "#0A0608"; | ||
| 19 | static const char db_fg[] = "#B9B9B9"; | ||
| 20 | static const char db_text[] = "#B9B9B9"; | ||
| 21 | static const char db_bg[] = "#232323"; | ||
| 22 | static const char db_sel_fg[] = "#FF9EA7"; | ||
| 23 | static const char db_sel_bg[] = "#3D1317"; | ||
| 24 | static const char db_hlght[] = "#3D1317"; | ||
| 25 | |||
| 26 | /*Dark Cold Variables*/ | ||
| 27 | static const char dc_base[] = "#060A0F"; | ||
| 28 | static const char dc_fg[] = "#6EBEFE"; | ||
| 29 | static const char dc_text[] = "#6EBEFE"; | ||
| 30 | static const char dc_bg[] = "#232323"; | ||
| 31 | static const char dc_sel_fg[] = "#FFFFFF"; | ||
| 32 | static const char dc_sel_bg[] = "#0D377C"; | ||
| 33 | static const char dc_hlght[] = "#0D377C"; | ||
| 34 | |||
| 35 | /*Dark Mint Variables*/ | ||
| 36 | static const char dm_base[] = "#060A08"; | ||
| 37 | static const char dm_fg[] = "#78B97E"; | ||
| 38 | static const char dm_text[] = "#78B97E"; | ||
| 39 | static const char dm_bg[] = "#232323"; | ||
| 40 | static const char dm_sel_fg[] = "#9EFFA7"; | ||
| 41 | static const char dm_sel_bg[] = "#133D17"; | ||
| 42 | static const char dm_hlght[] = "#133D17"; | ||
| 43 | |||
| 44 | /*Dark Fire Variables*/ | ||
| 45 | static const char df_base[] = "#0F0606"; | ||
| 46 | static const char df_fg[] = "#FEFEFE"; | ||
| 47 | static const char df_text[] = "#FEFEFE"; | ||
| 48 | static const char df_bg[] = "#232323"; | ||
| 49 | static const char df_sel_fg[] = "#FFFFFF"; | ||
| 50 | static const char df_sel_bg[] = "#7C0D0D"; | ||
| 51 | static const char df_hlght[] = "#7C0D0D"; | ||
diff --git a/config.h b/config.h new file mode 100644 index 0000000..fab08c9 --- /dev/null +++ b/config.h | |||
| @@ -0,0 +1,148 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | #define TERMCLASS "URxvt" | ||
| 3 | #define TERM "urxvt" | ||
| 4 | |||
| 5 | /* appearance */ | ||
| 6 | static const unsigned int borderpx = 3; /* border pixel of windows */ | ||
| 7 | static const unsigned int snap = 32; /* snap pixel */ | ||
| 8 | static const unsigned int gappih = 20; /* horiz inner gap between windows */ | ||
| 9 | static const unsigned int gappiv = 10; /* vert inner gap between windows */ | ||
| 10 | static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ | ||
| 11 | static const unsigned int gappov = 30; /* vert outer gap between windows and screen edge */ | ||
| 12 | static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ | ||
| 13 | static int smartgaps = 0; /* 1 means no outer gap when there is only one window */ | ||
| 14 | static const int showbar = 1; /* 0 means no bar */ | ||
| 15 | static const int topbar = 1; /* 0 means bottom bar */ | ||
| 16 | static const char *fonts[] = { "monospace:size=10"}; | ||
| 17 | static const char dmenufont[] = "monospace:size=10"; | ||
| 18 | #include "colors.h" | ||
| 19 | static const char *colors[][3] = { | ||
| 20 | /* fg bg border */ | ||
| 21 | [SchemeNorm] = { ad_fg, ad_bg, ad_base }, | ||
| 22 | [SchemeSel] = { ad_sel_fg, ad_sel_bg, ad_hlght }, | ||
| 23 | }; | ||
| 24 | |||
| 25 | /* tagging */ | ||
| 26 | static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; | ||
| 27 | |||
| 28 | |||
| 29 | static const Rule rules[] = { | ||
| 30 | /* xprop(1): | ||
| 31 | * WM_CLASS(STRING) = instance, class | ||
| 32 | * WM_NAME(STRING) = title | ||
| 33 | */ | ||
| 34 | /* class instance title tags mask isfloating isterminal noswallow monitor */ | ||
| 35 | { "Firefox", NULL, NULL, 0, 0, 0, -1, -1 }, | ||
| 36 | { "URxvt", NULL, NULL, 0, 0, 1, 0, -1 }, | ||
| 37 | { "Steam", NULL, NULL, 1, 0, 0, 0, -1 }, | ||
| 38 | { NULL, "cmus", NULL, 1 << 8, 0, 1, 1, -1 }, | ||
| 39 | { NULL, "ranger", NULL, 0, 0, 1, 1, -1 }, | ||
| 40 | { "KeePassXC", NULL, NULL, 1 << 7, 0, 0, 0, -1 }, | ||
| 41 | { NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */ | ||
| 42 | }; | ||
| 43 | |||
| 44 | /* layout(s) */ | ||
| 45 | static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ | ||
| 46 | static const int nmaster = 1; /* number of clients in master area */ | ||
| 47 | static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ | ||
| 48 | |||
| 49 | #define FORCE_VSPLIT 1 /* nrowgrid layout: force two clients to always split vertically */ | ||
| 50 | #include "vanitygaps.c" | ||
| 51 | |||
| 52 | static const Layout layouts[] = { | ||
| 53 | /* symbol arrange function */ | ||
| 54 | { "[]=", tile }, /* first entry is default, Master on left - slaves on right */ | ||
| 55 | { "TTT", bstack }, /* Master on top - slaves on bottom */ | ||
| 56 | { "[@]", spiral }, /* Fibonacci */ | ||
| 57 | { "HHH", grid }, /* All windows in a grid view */ | ||
| 58 | { "><>", NULL }, // no layout function means floating behavior | ||
| 59 | /* Other available layouts that I do not use | ||
| 60 | { "[\\]", dwindle }, | ||
| 61 | { "[M]", monocle }, | ||
| 62 | { "H[]", deck }, | ||
| 63 | { "===", bstackhoriz }, | ||
| 64 | { "###", nrowgrid }, | ||
| 65 | { "---", horizgrid }, | ||
| 66 | { ":::", gaplessgrid }, | ||
| 67 | { "|M|", centeredmaster }, | ||
| 68 | { ">M>", centeredfloatingmaster }, | ||
| 69 | */ | ||
| 70 | { NULL, NULL }, | ||
| 71 | }; | ||
| 72 | |||
| 73 | /* key definitions */ | ||
| 74 | #define MODKEY Mod4Mask | ||
| 75 | #define TAGKEYS(KEY,TAG) \ | ||
| 76 | { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ | ||
| 77 | { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ | ||
| 78 | { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ | ||
| 79 | { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, | ||
| 80 | |||
| 81 | /* helper for spawning shell commands in the pre dwm-5.0 fashion */ | ||
| 82 | #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } | ||
| 83 | #define STATUSBAR "dwmblocks" | ||
| 84 | static const char *termcmd[] = {"urxvt"}; | ||
| 85 | |||
| 86 | static Key keys[] = { | ||
| 87 | /* modifier key function argument */ | ||
| 88 | /* Layouts */ | ||
| 89 | { MODKEY, XK_y, setlayout, {.v = &layouts[0]} }, | ||
| 90 | { MODKEY, XK_u, setlayout, {.v = &layouts[1]} }, | ||
| 91 | { MODKEY, XK_i, setlayout, {.v = &layouts[2]} }, | ||
| 92 | { MODKEY, XK_o, setlayout, {.v = &layouts[3]} }, | ||
| 93 | /* Stack Control */ | ||
| 94 | { MODKEY, XK_h, setmfact, {.f = -0.05} }, | ||
| 95 | { MODKEY, XK_j, focusstack, {.i = +1 } }, | ||
| 96 | { MODKEY, XK_k, focusstack, {.i = -1 } }, | ||
| 97 | { MODKEY, XK_l, setmfact, {.f = +0.05} }, | ||
| 98 | { MODKEY, XK_p, incnmaster, {.i = +1 } }, | ||
| 99 | { MODKEY|ShiftMask, XK_p, incnmaster, {.i = -1 } }, | ||
| 100 | { MODKEY, XK_space, zoom, {0} }, | ||
| 101 | /* Tag Control */ | ||
| 102 | TAGKEYS( XK_1, 0) | ||
| 103 | TAGKEYS( XK_2, 1) | ||
| 104 | TAGKEYS( XK_3, 2) | ||
| 105 | TAGKEYS( XK_4, 3) | ||
| 106 | TAGKEYS( XK_5, 4) | ||
| 107 | TAGKEYS( XK_6, 5) | ||
| 108 | TAGKEYS( XK_7, 6) | ||
| 109 | TAGKEYS( XK_8, 7) | ||
| 110 | TAGKEYS( XK_9, 8) | ||
| 111 | { MODKEY, XK_0, view, {.ui = ~0 } }, | ||
| 112 | { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, | ||
| 113 | { MODKEY, XK_Tab, view, {0} }, | ||
| 114 | { MODKEY, XK_Left, focusmon, {.i = -1 } }, | ||
| 115 | { MODKEY|ShiftMask, XK_Left, focusmon, {.i = -1 } }, | ||
| 116 | { MODKEY, XK_Right, focusmon, {.i = +1 } }, | ||
| 117 | { MODKEY|ShiftMask, XK_Right, tagmon, {.i = +1 } }, | ||
| 118 | /* Commands */ | ||
| 119 | { MODKEY|ShiftMask, XK_q, killclient, {0} }, | ||
| 120 | { MODKEY|ShiftMask, XK_Escape, quit, {0} }, | ||
| 121 | { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, | ||
| 122 | { MODKEY, XK_b, togglebar, {0} }, | ||
| 123 | /* Gaps Control */ | ||
| 124 | { MODKEY, XK_semicolon, incrgaps, {.i = +1 } }, | ||
| 125 | { MODKEY|ShiftMask, XK_semicolon, defaultgaps, {0} }, | ||
| 126 | { MODKEY, XK_apostrophe, incrgaps, {.i = -1 } }, | ||
| 127 | { MODKEY|ShiftMask, XK_apostrophe, togglegaps, {0} }, | ||
| 128 | }; | ||
| 129 | |||
| 130 | /* button definitions */ | ||
| 131 | /* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ | ||
| 132 | static Button buttons[] = { | ||
| 133 | /* click event mask button function argument */ | ||
| 134 | { ClkLtSymbol, 0, Button1, setlayout, {0} }, | ||
| 135 | { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[5]}}, | ||
| 136 | { ClkWinTitle, 0, Button2, zoom, {0} }, | ||
| 137 | { ClkStatusText, 0, Button1, sigstatusbar, {.i = 1} }, | ||
| 138 | { ClkStatusText, 0, Button2, sigstatusbar, {.i = 2} }, | ||
| 139 | { ClkStatusText, 0, Button3, sigstatusbar, {.i = 3} }, | ||
| 140 | { ClkClientWin, MODKEY, Button1, movemouse, {0} }, | ||
| 141 | { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, | ||
| 142 | { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, | ||
| 143 | { ClkTagBar, 0, Button1, view, {0} }, | ||
| 144 | { ClkTagBar, 0, Button3, toggleview, {0} }, | ||
| 145 | { ClkTagBar, MODKEY, Button1, tag, {0} }, | ||
| 146 | { ClkTagBar, MODKEY, Button3, toggletag, {0} }, | ||
| 147 | }; | ||
| 148 | |||
diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..0c20086 --- /dev/null +++ b/config.mk | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | # dwm version | ||
| 2 | VERSION = 6.2 | ||
| 3 | |||
| 4 | # Customize below to fit your system | ||
| 5 | |||
| 6 | # paths | ||
| 7 | PREFIX = /usr/local | ||
| 8 | MANPREFIX = /usr/share/man | ||
| 9 | |||
| 10 | X11INC = /usr/X11R6/include | ||
| 11 | X11LIB = /usr/X11R6/lib | ||
| 12 | |||
| 13 | # Xinerama, comment if you don't want it | ||
| 14 | XINERAMALIBS = -lXinerama | ||
| 15 | XINERAMAFLAGS = -DXINERAMA | ||
| 16 | |||
| 17 | # freetype | ||
| 18 | FREETYPELIBS = -lfontconfig -lXft | ||
| 19 | FREETYPEINC = /usr/include/freetype2 | ||
| 20 | # OpenBSD (uncomment) | ||
| 21 | #FREETYPEINC = ${X11INC}/freetype2 | ||
| 22 | |||
| 23 | # includes and libs | ||
| 24 | INCS = -I${X11INC} -I${FREETYPEINC} | ||
| 25 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res | ||
| 26 | |||
| 27 | # flags | ||
| 28 | CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} | ||
| 29 | #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} | ||
| 30 | CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} | ||
| 31 | LDFLAGS = ${LIBS} | ||
| 32 | |||
| 33 | # Solaris | ||
| 34 | #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" | ||
| 35 | #LDFLAGS = ${LIBS} | ||
| 36 | |||
| 37 | # compiler and linker | ||
| 38 | CC = cc | ||
| @@ -0,0 +1,435 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | #include <stdio.h> | ||
| 3 | #include <stdlib.h> | ||
| 4 | #include <string.h> | ||
| 5 | #include <X11/Xlib.h> | ||
| 6 | #include <X11/Xft/Xft.h> | ||
| 7 | |||
| 8 | #include "drw.h" | ||
| 9 | #include "util.h" | ||
| 10 | |||
| 11 | #define UTF_INVALID 0xFFFD | ||
| 12 | #define UTF_SIZ 4 | ||
| 13 | |||
| 14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; | ||
| 15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; | ||
| 16 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; | ||
| 17 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; | ||
| 18 | |||
| 19 | static long | ||
| 20 | utf8decodebyte(const char c, size_t *i) | ||
| 21 | { | ||
| 22 | for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) | ||
| 23 | if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) | ||
| 24 | return (unsigned char)c & ~utfmask[*i]; | ||
| 25 | return 0; | ||
| 26 | } | ||
| 27 | |||
| 28 | static size_t | ||
| 29 | utf8validate(long *u, size_t i) | ||
| 30 | { | ||
| 31 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) | ||
| 32 | *u = UTF_INVALID; | ||
| 33 | for (i = 1; *u > utfmax[i]; ++i) | ||
| 34 | ; | ||
| 35 | return i; | ||
| 36 | } | ||
| 37 | |||
| 38 | static size_t | ||
| 39 | utf8decode(const char *c, long *u, size_t clen) | ||
| 40 | { | ||
| 41 | size_t i, j, len, type; | ||
| 42 | long udecoded; | ||
| 43 | |||
| 44 | *u = UTF_INVALID; | ||
| 45 | if (!clen) | ||
| 46 | return 0; | ||
| 47 | udecoded = utf8decodebyte(c[0], &len); | ||
| 48 | if (!BETWEEN(len, 1, UTF_SIZ)) | ||
| 49 | return 1; | ||
| 50 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { | ||
| 51 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); | ||
| 52 | if (type) | ||
| 53 | return j; | ||
| 54 | } | ||
| 55 | if (j < len) | ||
| 56 | return 0; | ||
| 57 | *u = udecoded; | ||
| 58 | utf8validate(u, len); | ||
| 59 | |||
| 60 | return len; | ||
| 61 | } | ||
| 62 | |||
| 63 | Drw * | ||
| 64 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) | ||
| 65 | { | ||
| 66 | Drw *drw = ecalloc(1, sizeof(Drw)); | ||
| 67 | |||
| 68 | drw->dpy = dpy; | ||
| 69 | drw->screen = screen; | ||
| 70 | drw->root = root; | ||
| 71 | drw->w = w; | ||
| 72 | drw->h = h; | ||
| 73 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); | ||
| 74 | drw->gc = XCreateGC(dpy, root, 0, NULL); | ||
| 75 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); | ||
| 76 | |||
| 77 | return drw; | ||
| 78 | } | ||
| 79 | |||
| 80 | void | ||
| 81 | drw_resize(Drw *drw, unsigned int w, unsigned int h) | ||
| 82 | { | ||
| 83 | if (!drw) | ||
| 84 | return; | ||
| 85 | |||
| 86 | drw->w = w; | ||
| 87 | drw->h = h; | ||
| 88 | if (drw->drawable) | ||
| 89 | XFreePixmap(drw->dpy, drw->drawable); | ||
| 90 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); | ||
| 91 | } | ||
| 92 | |||
| 93 | void | ||
| 94 | drw_free(Drw *drw) | ||
| 95 | { | ||
| 96 | XFreePixmap(drw->dpy, drw->drawable); | ||
| 97 | XFreeGC(drw->dpy, drw->gc); | ||
| 98 | free(drw); | ||
| 99 | } | ||
| 100 | |||
| 101 | /* This function is an implementation detail. Library users should use | ||
| 102 | * drw_fontset_create instead. | ||
| 103 | */ | ||
| 104 | static Fnt * | ||
| 105 | xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) | ||
| 106 | { | ||
| 107 | Fnt *font; | ||
| 108 | XftFont *xfont = NULL; | ||
| 109 | FcPattern *pattern = NULL; | ||
| 110 | |||
| 111 | if (fontname) { | ||
| 112 | /* Using the pattern found at font->xfont->pattern does not yield the | ||
| 113 | * same substitution results as using the pattern returned by | ||
| 114 | * FcNameParse; using the latter results in the desired fallback | ||
| 115 | * behaviour whereas the former just results in missing-character | ||
| 116 | * rectangles being drawn, at least with some fonts. */ | ||
| 117 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { | ||
| 118 | fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); | ||
| 119 | return NULL; | ||
| 120 | } | ||
| 121 | if (!(pattern = FcNameParse((FcChar8 *) fontname))) { | ||
| 122 | fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); | ||
| 123 | XftFontClose(drw->dpy, xfont); | ||
| 124 | return NULL; | ||
| 125 | } | ||
| 126 | } else if (fontpattern) { | ||
| 127 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { | ||
| 128 | fprintf(stderr, "error, cannot load font from pattern.\n"); | ||
| 129 | return NULL; | ||
| 130 | } | ||
| 131 | } else { | ||
| 132 | die("no font specified."); | ||
| 133 | } | ||
| 134 | |||
| 135 | /* Do not allow using color fonts. This is a workaround for a BadLength | ||
| 136 | * error from Xft with color glyphs. Modelled on the Xterm workaround. See | ||
| 137 | * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 | ||
| 138 | * https://lists.suckless.org/dev/1701/30932.html | ||
| 139 | * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 | ||
| 140 | * and lots more all over the internet. | ||
| 141 | */ | ||
| 142 | FcBool iscol; | ||
| 143 | if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { | ||
| 144 | XftFontClose(drw->dpy, xfont); | ||
| 145 | return NULL; | ||
| 146 | } | ||
| 147 | |||
| 148 | font = ecalloc(1, sizeof(Fnt)); | ||
| 149 | font->xfont = xfont; | ||
| 150 | font->pattern = pattern; | ||
| 151 | font->h = xfont->ascent + xfont->descent; | ||
| 152 | font->dpy = drw->dpy; | ||
| 153 | |||
| 154 | return font; | ||
| 155 | } | ||
| 156 | |||
| 157 | static void | ||
| 158 | xfont_free(Fnt *font) | ||
| 159 | { | ||
| 160 | if (!font) | ||
| 161 | return; | ||
| 162 | if (font->pattern) | ||
| 163 | FcPatternDestroy(font->pattern); | ||
| 164 | XftFontClose(font->dpy, font->xfont); | ||
| 165 | free(font); | ||
| 166 | } | ||
| 167 | |||
| 168 | Fnt* | ||
| 169 | drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) | ||
| 170 | { | ||
| 171 | Fnt *cur, *ret = NULL; | ||
| 172 | size_t i; | ||
| 173 | |||
| 174 | if (!drw || !fonts) | ||
| 175 | return NULL; | ||
| 176 | |||
| 177 | for (i = 1; i <= fontcount; i++) { | ||
| 178 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { | ||
| 179 | cur->next = ret; | ||
| 180 | ret = cur; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | return (drw->fonts = ret); | ||
| 184 | } | ||
| 185 | |||
| 186 | void | ||
| 187 | drw_fontset_free(Fnt *font) | ||
| 188 | { | ||
| 189 | if (font) { | ||
| 190 | drw_fontset_free(font->next); | ||
| 191 | xfont_free(font); | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | void | ||
| 196 | drw_clr_create(Drw *drw, Clr *dest, const char *clrname) | ||
| 197 | { | ||
| 198 | if (!drw || !dest || !clrname) | ||
| 199 | return; | ||
| 200 | |||
| 201 | if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), | ||
| 202 | DefaultColormap(drw->dpy, drw->screen), | ||
| 203 | clrname, dest)) | ||
| 204 | die("error, cannot allocate color '%s'", clrname); | ||
| 205 | } | ||
| 206 | |||
| 207 | /* Wrapper to create color schemes. The caller has to call free(3) on the | ||
| 208 | * returned color scheme when done using it. */ | ||
| 209 | Clr * | ||
| 210 | drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) | ||
| 211 | { | ||
| 212 | size_t i; | ||
| 213 | Clr *ret; | ||
| 214 | |||
| 215 | /* need at least two colors for a scheme */ | ||
| 216 | if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) | ||
| 217 | return NULL; | ||
| 218 | |||
| 219 | for (i = 0; i < clrcount; i++) | ||
| 220 | drw_clr_create(drw, &ret[i], clrnames[i]); | ||
| 221 | return ret; | ||
| 222 | } | ||
| 223 | |||
| 224 | void | ||
| 225 | drw_setfontset(Drw *drw, Fnt *set) | ||
| 226 | { | ||
| 227 | if (drw) | ||
| 228 | drw->fonts = set; | ||
| 229 | } | ||
| 230 | |||
| 231 | void | ||
| 232 | drw_setscheme(Drw *drw, Clr *scm) | ||
| 233 | { | ||
| 234 | if (drw) | ||
| 235 | drw->scheme = scm; | ||
| 236 | } | ||
| 237 | |||
| 238 | void | ||
| 239 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) | ||
| 240 | { | ||
| 241 | if (!drw || !drw->scheme) | ||
| 242 | return; | ||
| 243 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); | ||
| 244 | if (filled) | ||
| 245 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); | ||
| 246 | else | ||
| 247 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); | ||
| 248 | } | ||
| 249 | |||
| 250 | int | ||
| 251 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) | ||
| 252 | { | ||
| 253 | char buf[1024]; | ||
| 254 | int ty; | ||
| 255 | unsigned int ew; | ||
| 256 | XftDraw *d = NULL; | ||
| 257 | Fnt *usedfont, *curfont, *nextfont; | ||
| 258 | size_t i, len; | ||
| 259 | int utf8strlen, utf8charlen, render = x || y || w || h; | ||
| 260 | long utf8codepoint = 0; | ||
| 261 | const char *utf8str; | ||
| 262 | FcCharSet *fccharset; | ||
| 263 | FcPattern *fcpattern; | ||
| 264 | FcPattern *match; | ||
| 265 | XftResult result; | ||
| 266 | int charexists = 0; | ||
| 267 | |||
| 268 | if (!drw || (render && !drw->scheme) || !text || !drw->fonts) | ||
| 269 | return 0; | ||
| 270 | |||
| 271 | if (!render) { | ||
| 272 | w = ~w; | ||
| 273 | } else { | ||
| 274 | XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); | ||
| 275 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); | ||
| 276 | d = XftDrawCreate(drw->dpy, drw->drawable, | ||
| 277 | DefaultVisual(drw->dpy, drw->screen), | ||
| 278 | DefaultColormap(drw->dpy, drw->screen)); | ||
| 279 | x += lpad; | ||
| 280 | w -= lpad; | ||
| 281 | } | ||
| 282 | |||
| 283 | usedfont = drw->fonts; | ||
| 284 | while (1) { | ||
| 285 | utf8strlen = 0; | ||
| 286 | utf8str = text; | ||
| 287 | nextfont = NULL; | ||
| 288 | while (*text) { | ||
| 289 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); | ||
| 290 | for (curfont = drw->fonts; curfont; curfont = curfont->next) { | ||
| 291 | charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); | ||
| 292 | if (charexists) { | ||
| 293 | if (curfont == usedfont) { | ||
| 294 | utf8strlen += utf8charlen; | ||
| 295 | text += utf8charlen; | ||
| 296 | } else { | ||
| 297 | nextfont = curfont; | ||
| 298 | } | ||
| 299 | break; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | if (!charexists || nextfont) | ||
| 304 | break; | ||
| 305 | else | ||
| 306 | charexists = 0; | ||
| 307 | } | ||
| 308 | |||
| 309 | if (utf8strlen) { | ||
| 310 | drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); | ||
| 311 | /* shorten text if necessary */ | ||
| 312 | for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) | ||
| 313 | drw_font_getexts(usedfont, utf8str, len, &ew, NULL); | ||
| 314 | |||
| 315 | if (len) { | ||
| 316 | memcpy(buf, utf8str, len); | ||
| 317 | buf[len] = '\0'; | ||
| 318 | if (len < utf8strlen) | ||
| 319 | for (i = len; i && i > len - 3; buf[--i] = '.') | ||
| 320 | ; /* NOP */ | ||
| 321 | |||
| 322 | if (render) { | ||
| 323 | ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; | ||
| 324 | XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], | ||
| 325 | usedfont->xfont, x, ty, (XftChar8 *)buf, len); | ||
| 326 | } | ||
| 327 | x += ew; | ||
| 328 | w -= ew; | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | if (!*text) { | ||
| 333 | break; | ||
| 334 | } else if (nextfont) { | ||
| 335 | charexists = 0; | ||
| 336 | usedfont = nextfont; | ||
| 337 | } else { | ||
| 338 | /* Regardless of whether or not a fallback font is found, the | ||
| 339 | * character must be drawn. */ | ||
| 340 | charexists = 1; | ||
| 341 | |||
| 342 | fccharset = FcCharSetCreate(); | ||
| 343 | FcCharSetAddChar(fccharset, utf8codepoint); | ||
| 344 | |||
| 345 | if (!drw->fonts->pattern) { | ||
| 346 | /* Refer to the comment in xfont_create for more information. */ | ||
| 347 | die("the first font in the cache must be loaded from a font string."); | ||
| 348 | } | ||
| 349 | |||
| 350 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); | ||
| 351 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); | ||
| 352 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); | ||
| 353 | FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); | ||
| 354 | |||
| 355 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); | ||
| 356 | FcDefaultSubstitute(fcpattern); | ||
| 357 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); | ||
| 358 | |||
| 359 | FcCharSetDestroy(fccharset); | ||
| 360 | FcPatternDestroy(fcpattern); | ||
| 361 | |||
| 362 | if (match) { | ||
| 363 | usedfont = xfont_create(drw, NULL, match); | ||
| 364 | if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { | ||
| 365 | for (curfont = drw->fonts; curfont->next; curfont = curfont->next) | ||
| 366 | ; /* NOP */ | ||
| 367 | curfont->next = usedfont; | ||
| 368 | } else { | ||
| 369 | xfont_free(usedfont); | ||
| 370 | usedfont = drw->fonts; | ||
| 371 | } | ||
| 372 | } | ||
| 373 | } | ||
| 374 | } | ||
| 375 | if (d) | ||
| 376 | XftDrawDestroy(d); | ||
| 377 | |||
| 378 | return x + (render ? w : 0); | ||
| 379 | } | ||
| 380 | |||
| 381 | void | ||
| 382 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) | ||
| 383 | { | ||
| 384 | if (!drw) | ||
| 385 | return; | ||
| 386 | |||
| 387 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); | ||
| 388 | XSync(drw->dpy, False); | ||
| 389 | } | ||
| 390 | |||
| 391 | unsigned int | ||
| 392 | drw_fontset_getwidth(Drw *drw, const char *text) | ||
| 393 | { | ||
| 394 | if (!drw || !drw->fonts || !text) | ||
| 395 | return 0; | ||
| 396 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0); | ||
| 397 | } | ||
| 398 | |||
| 399 | void | ||
| 400 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) | ||
| 401 | { | ||
| 402 | XGlyphInfo ext; | ||
| 403 | |||
| 404 | if (!font || !text) | ||
| 405 | return; | ||
| 406 | |||
| 407 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); | ||
| 408 | if (w) | ||
| 409 | *w = ext.xOff; | ||
| 410 | if (h) | ||
| 411 | *h = font->h; | ||
| 412 | } | ||
| 413 | |||
| 414 | Cur * | ||
| 415 | drw_cur_create(Drw *drw, int shape) | ||
| 416 | { | ||
| 417 | Cur *cur; | ||
| 418 | |||
| 419 | if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) | ||
| 420 | return NULL; | ||
| 421 | |||
| 422 | cur->cursor = XCreateFontCursor(drw->dpy, shape); | ||
| 423 | |||
| 424 | return cur; | ||
| 425 | } | ||
| 426 | |||
| 427 | void | ||
| 428 | drw_cur_free(Drw *drw, Cur *cursor) | ||
| 429 | { | ||
| 430 | if (!cursor) | ||
| 431 | return; | ||
| 432 | |||
| 433 | XFreeCursor(drw->dpy, cursor->cursor); | ||
| 434 | free(cursor); | ||
| 435 | } | ||
| @@ -0,0 +1,57 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | |||
| 3 | typedef struct { | ||
| 4 | Cursor cursor; | ||
| 5 | } Cur; | ||
| 6 | |||
| 7 | typedef struct Fnt { | ||
| 8 | Display *dpy; | ||
| 9 | unsigned int h; | ||
| 10 | XftFont *xfont; | ||
| 11 | FcPattern *pattern; | ||
| 12 | struct Fnt *next; | ||
| 13 | } Fnt; | ||
| 14 | |||
| 15 | enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ | ||
| 16 | typedef XftColor Clr; | ||
| 17 | |||
| 18 | typedef struct { | ||
| 19 | unsigned int w, h; | ||
| 20 | Display *dpy; | ||
| 21 | int screen; | ||
| 22 | Window root; | ||
| 23 | Drawable drawable; | ||
| 24 | GC gc; | ||
| 25 | Clr *scheme; | ||
| 26 | Fnt *fonts; | ||
| 27 | } Drw; | ||
| 28 | |||
| 29 | /* Drawable abstraction */ | ||
| 30 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); | ||
| 31 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); | ||
| 32 | void drw_free(Drw *drw); | ||
| 33 | |||
| 34 | /* Fnt abstraction */ | ||
| 35 | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); | ||
| 36 | void drw_fontset_free(Fnt* set); | ||
| 37 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); | ||
| 38 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); | ||
| 39 | |||
| 40 | /* Colorscheme abstraction */ | ||
| 41 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); | ||
| 42 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); | ||
| 43 | |||
| 44 | /* Cursor abstraction */ | ||
| 45 | Cur *drw_cur_create(Drw *drw, int shape); | ||
| 46 | void drw_cur_free(Drw *drw, Cur *cursor); | ||
| 47 | |||
| 48 | /* Drawing context manipulation */ | ||
| 49 | void drw_setfontset(Drw *drw, Fnt *set); | ||
| 50 | void drw_setscheme(Drw *drw, Clr *scm); | ||
| 51 | |||
| 52 | /* Drawing functions */ | ||
| 53 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); | ||
| 54 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); | ||
| 55 | |||
| 56 | /* Map functions */ | ||
| 57 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); | ||
| @@ -0,0 +1,176 @@ | |||
| 1 | .TH DWM 1 dwm\-VERSION | ||
| 2 | .SH NAME | ||
| 3 | dwm \- dynamic window manager | ||
| 4 | .SH SYNOPSIS | ||
| 5 | .B dwm | ||
| 6 | .RB [ \-v ] | ||
| 7 | .SH DESCRIPTION | ||
| 8 | dwm is a dynamic window manager for X. It manages windows in tiled, monocle | ||
| 9 | and floating layouts. Either layout can be applied dynamically, optimising the | ||
| 10 | environment for the application in use and the task performed. | ||
| 11 | .P | ||
| 12 | In tiled layouts windows are managed in a master and stacking area. The master | ||
| 13 | area on the left contains one window by default, and the stacking area on the | ||
| 14 | right contains all other windows. The number of master area windows can be | ||
| 15 | adjusted from zero to an arbitrary number. In monocle layout all windows are | ||
| 16 | maximised to the screen size. In floating layout windows can be resized and | ||
| 17 | moved freely. Dialog windows are always managed floating, regardless of the | ||
| 18 | layout applied. | ||
| 19 | .P | ||
| 20 | Windows are grouped by tags. Each window can be tagged with one or multiple | ||
| 21 | tags. Selecting certain tags displays all windows with these tags. | ||
| 22 | .P | ||
| 23 | Each screen contains a small status bar which displays all available tags, the | ||
| 24 | layout, the title of the focused window, and the text read from the root window | ||
| 25 | name property, if the screen is focused. A floating window is indicated with an | ||
| 26 | empty square and a maximised floating window is indicated with a filled square | ||
| 27 | before the windows title. The selected tags are indicated with a different | ||
| 28 | color. The tags of the focused window are indicated with a filled square in the | ||
| 29 | top left corner. The tags which are applied to one or more windows are | ||
| 30 | indicated with an empty square in the top left corner. | ||
| 31 | .P | ||
| 32 | dwm draws a small border around windows to indicate the focus state. | ||
| 33 | .SH OPTIONS | ||
| 34 | .TP | ||
| 35 | .B \-v | ||
| 36 | prints version information to standard output, then exits. | ||
| 37 | .SH USAGE | ||
| 38 | .SS Status bar | ||
| 39 | .TP | ||
| 40 | .B X root window name | ||
| 41 | is read and displayed in the status text area. It can be set with the | ||
| 42 | .BR xsetroot (1) | ||
| 43 | command. | ||
| 44 | .TP | ||
| 45 | .B Button1 | ||
| 46 | click on a tag label to display all windows with that tag, click on the layout | ||
| 47 | label toggles between tiled and floating layout. | ||
| 48 | .TP | ||
| 49 | .B Button3 | ||
| 50 | click on a tag label adds/removes all windows with that tag to/from the view. | ||
| 51 | .TP | ||
| 52 | .B Mod1\-Button1 | ||
| 53 | click on a tag label applies that tag to the focused window. | ||
| 54 | .TP | ||
| 55 | .B Mod1\-Button3 | ||
| 56 | click on a tag label adds/removes that tag to/from the focused window. | ||
| 57 | .SS Keyboard commands | ||
| 58 | .TP | ||
| 59 | .B Mod1\-Shift\-Return | ||
| 60 | Start | ||
| 61 | .BR st(1). | ||
| 62 | .TP | ||
| 63 | .B Mod1\-p | ||
| 64 | Spawn | ||
| 65 | .BR dmenu(1) | ||
| 66 | for launching other programs. | ||
| 67 | .TP | ||
| 68 | .B Mod1\-, | ||
| 69 | Focus previous screen, if any. | ||
| 70 | .TP | ||
| 71 | .B Mod1\-. | ||
| 72 | Focus next screen, if any. | ||
| 73 | .TP | ||
| 74 | .B Mod1\-Shift\-, | ||
| 75 | Send focused window to previous screen, if any. | ||
| 76 | .TP | ||
| 77 | .B Mod1\-Shift\-. | ||
| 78 | Send focused window to next screen, if any. | ||
| 79 | .TP | ||
| 80 | .B Mod1\-b | ||
| 81 | Toggles bar on and off. | ||
| 82 | .TP | ||
| 83 | .B Mod1\-t | ||
| 84 | Sets tiled layout. | ||
| 85 | .TP | ||
| 86 | .B Mod1\-f | ||
| 87 | Sets floating layout. | ||
| 88 | .TP | ||
| 89 | .B Mod1\-m | ||
| 90 | Sets monocle layout. | ||
| 91 | .TP | ||
| 92 | .B Mod1\-space | ||
| 93 | Toggles between current and previous layout. | ||
| 94 | .TP | ||
| 95 | .B Mod1\-j | ||
| 96 | Focus next window. | ||
| 97 | .TP | ||
| 98 | .B Mod1\-k | ||
| 99 | Focus previous window. | ||
| 100 | .TP | ||
| 101 | .B Mod1\-i | ||
| 102 | Increase number of windows in master area. | ||
| 103 | .TP | ||
| 104 | .B Mod1\-d | ||
| 105 | Decrease number of windows in master area. | ||
| 106 | .TP | ||
| 107 | .B Mod1\-l | ||
| 108 | Increase master area size. | ||
| 109 | .TP | ||
| 110 | .B Mod1\-h | ||
| 111 | Decrease master area size. | ||
| 112 | .TP | ||
| 113 | .B Mod1\-Return | ||
| 114 | Zooms/cycles focused window to/from master area (tiled layouts only). | ||
| 115 | .TP | ||
| 116 | .B Mod1\-Shift\-c | ||
| 117 | Close focused window. | ||
| 118 | .TP | ||
| 119 | .B Mod1\-Shift\-space | ||
| 120 | Toggle focused window between tiled and floating state. | ||
| 121 | .TP | ||
| 122 | .B Mod1\-Tab | ||
| 123 | Toggles to the previously selected tags. | ||
| 124 | .TP | ||
| 125 | .B Mod1\-Shift\-[1..n] | ||
| 126 | Apply nth tag to focused window. | ||
| 127 | .TP | ||
| 128 | .B Mod1\-Shift\-0 | ||
| 129 | Apply all tags to focused window. | ||
| 130 | .TP | ||
| 131 | .B Mod1\-Control\-Shift\-[1..n] | ||
| 132 | Add/remove nth tag to/from focused window. | ||
| 133 | .TP | ||
| 134 | .B Mod1\-[1..n] | ||
| 135 | View all windows with nth tag. | ||
| 136 | .TP | ||
| 137 | .B Mod1\-0 | ||
| 138 | View all windows with any tag. | ||
| 139 | .TP | ||
| 140 | .B Mod1\-Control\-[1..n] | ||
| 141 | Add/remove all windows with nth tag to/from the view. | ||
| 142 | .TP | ||
| 143 | .B Mod1\-Shift\-q | ||
| 144 | Quit dwm. | ||
| 145 | .SS Mouse commands | ||
| 146 | .TP | ||
| 147 | .B Mod1\-Button1 | ||
| 148 | Move focused window while dragging. Tiled windows will be toggled to the floating state. | ||
| 149 | .TP | ||
| 150 | .B Mod1\-Button2 | ||
| 151 | Toggles focused window between floating and tiled state. | ||
| 152 | .TP | ||
| 153 | .B Mod1\-Button3 | ||
| 154 | Resize focused window while dragging. Tiled windows will be toggled to the floating state. | ||
| 155 | .SH CUSTOMIZATION | ||
| 156 | dwm is customized by creating a custom config.h and (re)compiling the source | ||
| 157 | code. This keeps it fast, secure and simple. | ||
| 158 | .SH SEE ALSO | ||
| 159 | .BR dmenu (1), | ||
| 160 | .BR st (1) | ||
| 161 | .SH ISSUES | ||
| 162 | Java applications which use the XToolkit/XAWT backend may draw grey windows | ||
| 163 | only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early | ||
| 164 | JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds | ||
| 165 | are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the | ||
| 166 | environment variable | ||
| 167 | .BR AWT_TOOLKIT=MToolkit | ||
| 168 | (to use the older Motif backend instead) or running | ||
| 169 | .B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D | ||
| 170 | or | ||
| 171 | .B wmname LG3D | ||
| 172 | (to pretend that a non-reparenting window manager is running that the | ||
| 173 | XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable | ||
| 174 | .BR _JAVA_AWT_WM_NONREPARENTING=1 . | ||
| 175 | .SH BUGS | ||
| 176 | Send all bug reports with a patch to hackers@suckless.org. | ||
| @@ -0,0 +1,2452 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. | ||
| 2 | * | ||
| 3 | * dynamic window manager is designed like any other X client as well. It is | ||
| 4 | * driven through handling X events. In contrast to other X clients, a window | ||
| 5 | * manager selects for SubstructureRedirectMask on the root window, to receive | ||
| 6 | * events about window (dis-)appearance. Only one X connection at a time is | ||
| 7 | * allowed to select for this event mask. | ||
| 8 | * | ||
| 9 | * The event handlers of dwm are organized in an array which is accessed | ||
| 10 | * whenever a new event has been fetched. This allows event dispatching | ||
| 11 | * in O(1) time. | ||
| 12 | * | ||
| 13 | * Each child of the root window is called a client, except windows which have | ||
| 14 | * set the override_redirect flag. Clients are organized in a linked client | ||
| 15 | * list on each monitor, the focus history is remembered through a stack list | ||
| 16 | * on each monitor. Each client contains a bit array to indicate the tags of a | ||
| 17 | * client. | ||
| 18 | * | ||
| 19 | * Keys and tagging rules are organized as arrays and defined in config.h. | ||
| 20 | * | ||
| 21 | * To understand everything else, start reading main(). | ||
| 22 | */ | ||
| 23 | #include <errno.h> | ||
| 24 | #include <locale.h> | ||
| 25 | #include <signal.h> | ||
| 26 | #include <stdarg.h> | ||
| 27 | #include <stdio.h> | ||
| 28 | #include <stdlib.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <unistd.h> | ||
| 31 | #include <sys/types.h> | ||
| 32 | #include <sys/wait.h> | ||
| 33 | #include <X11/cursorfont.h> | ||
| 34 | #include <X11/keysym.h> | ||
| 35 | #include <X11/Xatom.h> | ||
| 36 | #include <X11/Xlib.h> | ||
| 37 | #include <X11/Xproto.h> | ||
| 38 | #include <X11/Xutil.h> | ||
| 39 | #ifdef XINERAMA | ||
| 40 | #include <X11/extensions/Xinerama.h> | ||
| 41 | #endif /* XINERAMA */ | ||
| 42 | #include <X11/Xft/Xft.h> | ||
| 43 | #include <X11/Xlib-xcb.h> | ||
| 44 | #include <xcb/res.h> | ||
| 45 | |||
| 46 | #include "drw.h" | ||
| 47 | #include "util.h" | ||
| 48 | |||
| 49 | /* macros */ | ||
| 50 | #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) | ||
| 51 | #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) | ||
| 52 | #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ | ||
| 53 | * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) | ||
| 54 | #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) | ||
| 55 | #define LENGTH(X) (sizeof X / sizeof X[0]) | ||
| 56 | #define MOUSEMASK (BUTTONMASK|PointerMotionMask) | ||
| 57 | #define WIDTH(X) ((X)->w + 2 * (X)->bw) | ||
| 58 | #define HEIGHT(X) ((X)->h + 2 * (X)->bw) | ||
| 59 | #define TAGMASK ((1 << LENGTH(tags)) - 1) | ||
| 60 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) | ||
| 61 | |||
| 62 | /* enums */ | ||
| 63 | enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ | ||
| 64 | enum { SchemeNorm, SchemeSel }; /* color schemes */ | ||
| 65 | enum { NetSupported, NetWMName, NetWMState, NetWMCheck, | ||
| 66 | NetWMFullscreen, NetActiveWindow, NetWMWindowType, | ||
| 67 | NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ | ||
| 68 | enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ | ||
| 69 | enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, | ||
| 70 | ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ | ||
| 71 | |||
| 72 | typedef union { | ||
| 73 | int i; | ||
| 74 | unsigned int ui; | ||
| 75 | float f; | ||
| 76 | const void *v; | ||
| 77 | } Arg; | ||
| 78 | |||
| 79 | typedef struct { | ||
| 80 | unsigned int click; | ||
| 81 | unsigned int mask; | ||
| 82 | unsigned int button; | ||
| 83 | void (*func)(const Arg *arg); | ||
| 84 | const Arg arg; | ||
| 85 | } Button; | ||
| 86 | |||
| 87 | typedef struct Monitor Monitor; | ||
| 88 | typedef struct Client Client; | ||
| 89 | struct Client { | ||
| 90 | char name[256]; | ||
| 91 | float mina, maxa; | ||
| 92 | int x, y, w, h; | ||
| 93 | int oldx, oldy, oldw, oldh; | ||
| 94 | int basew, baseh, incw, inch, maxw, maxh, minw, minh; | ||
| 95 | int bw, oldbw; | ||
| 96 | unsigned int tags; | ||
| 97 | int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow; | ||
| 98 | int issteam; | ||
| 99 | pid_t pid; | ||
| 100 | Client *next; | ||
| 101 | Client *snext; | ||
| 102 | Client *swallowing; | ||
| 103 | Monitor *mon; | ||
| 104 | Window win; | ||
| 105 | }; | ||
| 106 | |||
| 107 | typedef struct { | ||
| 108 | unsigned int mod; | ||
| 109 | KeySym keysym; | ||
| 110 | void (*func)(const Arg *); | ||
| 111 | const Arg arg; | ||
| 112 | } Key; | ||
| 113 | |||
| 114 | typedef struct { | ||
| 115 | const char *symbol; | ||
| 116 | void (*arrange)(Monitor *); | ||
| 117 | } Layout; | ||
| 118 | |||
| 119 | struct Monitor { | ||
| 120 | char ltsymbol[16]; | ||
| 121 | float mfact; | ||
| 122 | int nmaster; | ||
| 123 | int num; | ||
| 124 | int by; /* bar geometry */ | ||
| 125 | int mx, my, mw, mh; /* screen size */ | ||
| 126 | int wx, wy, ww, wh; /* window area */ | ||
| 127 | int gappih; /* horizontal gap between windows */ | ||
| 128 | int gappiv; /* vertical gap between windows */ | ||
| 129 | int gappoh; /* horizontal outer gaps */ | ||
| 130 | int gappov; /* vertical outer gaps */ | ||
| 131 | unsigned int seltags; | ||
| 132 | unsigned int sellt; | ||
| 133 | unsigned int tagset[2]; | ||
| 134 | int showbar; | ||
| 135 | int topbar; | ||
| 136 | Client *clients; | ||
| 137 | Client *sel; | ||
| 138 | Client *stack; | ||
| 139 | Monitor *next; | ||
| 140 | Window barwin; | ||
| 141 | const Layout *lt[2]; | ||
| 142 | }; | ||
| 143 | |||
| 144 | typedef struct { | ||
| 145 | const char *class; | ||
| 146 | const char *instance; | ||
| 147 | const char *title; | ||
| 148 | unsigned int tags; | ||
| 149 | int isfloating; | ||
| 150 | int isterminal; | ||
| 151 | int noswallow; | ||
| 152 | int monitor; | ||
| 153 | } Rule; | ||
| 154 | |||
| 155 | /* function declarations */ | ||
| 156 | static void applyrules(Client *c); | ||
| 157 | static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); | ||
| 158 | static void arrange(Monitor *m); | ||
| 159 | static void arrangemon(Monitor *m); | ||
| 160 | static void attach(Client *c); | ||
| 161 | static void attachstack(Client *c); | ||
| 162 | static void buttonpress(XEvent *e); | ||
| 163 | static void checkotherwm(void); | ||
| 164 | static void cleanup(void); | ||
| 165 | static void cleanupmon(Monitor *mon); | ||
| 166 | static void clientmessage(XEvent *e); | ||
| 167 | static void configure(Client *c); | ||
| 168 | static void configurenotify(XEvent *e); | ||
| 169 | static void configurerequest(XEvent *e); | ||
| 170 | static Monitor *createmon(void); | ||
| 171 | static void destroynotify(XEvent *e); | ||
| 172 | static void detach(Client *c); | ||
| 173 | static void detachstack(Client *c); | ||
| 174 | static Monitor *dirtomon(int dir); | ||
| 175 | static void drawbar(Monitor *m); | ||
| 176 | static void drawbars(void); | ||
| 177 | static void enternotify(XEvent *e); | ||
| 178 | static void expose(XEvent *e); | ||
| 179 | static void focus(Client *c); | ||
| 180 | static void focusin(XEvent *e); | ||
| 181 | static void focusmon(const Arg *arg); | ||
| 182 | static void focusstack(const Arg *arg); | ||
| 183 | static int getrootptr(int *x, int *y); | ||
| 184 | static long getstate(Window w); | ||
| 185 | static pid_t getstatusbarpid(); | ||
| 186 | static int gettextprop(Window w, Atom atom, char *text, unsigned int size); | ||
| 187 | static void grabbuttons(Client *c, int focused); | ||
| 188 | static void grabkeys(void); | ||
| 189 | static void incnmaster(const Arg *arg); | ||
| 190 | static void keypress(XEvent *e); | ||
| 191 | static void killclient(const Arg *arg); | ||
| 192 | static void manage(Window w, XWindowAttributes *wa); | ||
| 193 | static void mappingnotify(XEvent *e); | ||
| 194 | static void maprequest(XEvent *e); | ||
| 195 | static void monocle(Monitor *m); | ||
| 196 | static void motionnotify(XEvent *e); | ||
| 197 | static void movemouse(const Arg *arg); | ||
| 198 | static Client *nexttiled(Client *c); | ||
| 199 | static void pop(Client *); | ||
| 200 | static void propertynotify(XEvent *e); | ||
| 201 | static void quit(const Arg *arg); | ||
| 202 | static Monitor *recttomon(int x, int y, int w, int h); | ||
| 203 | static void resize(Client *c, int x, int y, int w, int h, int interact); | ||
| 204 | static void resizeclient(Client *c, int x, int y, int w, int h); | ||
| 205 | static void resizemouse(const Arg *arg); | ||
| 206 | static void restack(Monitor *m); | ||
| 207 | static void run(void); | ||
| 208 | static void scan(void); | ||
| 209 | static int sendevent(Client *c, Atom proto); | ||
| 210 | static void sendmon(Client *c, Monitor *m); | ||
| 211 | static void setclientstate(Client *c, long state); | ||
| 212 | static void setfocus(Client *c); | ||
| 213 | static void setfullscreen(Client *c, int fullscreen); | ||
| 214 | static void setlayout(const Arg *arg); | ||
| 215 | static void setmfact(const Arg *arg); | ||
| 216 | static void setup(void); | ||
| 217 | static void seturgent(Client *c, int urg); | ||
| 218 | static void showhide(Client *c); | ||
| 219 | static void sigchld(int unused); | ||
| 220 | static void sigstatusbar(const Arg *arg); | ||
| 221 | static void spawn(const Arg *arg); | ||
| 222 | static void tag(const Arg *arg); | ||
| 223 | static void tagmon(const Arg *arg); | ||
| 224 | static void togglebar(const Arg *arg); | ||
| 225 | static void togglefloating(const Arg *arg); | ||
| 226 | static void toggletag(const Arg *arg); | ||
| 227 | static void toggleview(const Arg *arg); | ||
| 228 | static void unfocus(Client *c, int setfocus); | ||
| 229 | static void unmanage(Client *c, int destroyed); | ||
| 230 | static void unmapnotify(XEvent *e); | ||
| 231 | static void updatebarpos(Monitor *m); | ||
| 232 | static void updatebars(void); | ||
| 233 | static void updateclientlist(void); | ||
| 234 | static int updategeom(void); | ||
| 235 | static void updatenumlockmask(void); | ||
| 236 | static void updatesizehints(Client *c); | ||
| 237 | static void updatestatus(void); | ||
| 238 | static void updatetitle(Client *c); | ||
| 239 | static void updatewindowtype(Client *c); | ||
| 240 | static void updatewmhints(Client *c); | ||
| 241 | static void view(const Arg *arg); | ||
| 242 | static Client *wintoclient(Window w); | ||
| 243 | static Monitor *wintomon(Window w); | ||
| 244 | static int xerror(Display *dpy, XErrorEvent *ee); | ||
| 245 | static int xerrordummy(Display *dpy, XErrorEvent *ee); | ||
| 246 | static int xerrorstart(Display *dpy, XErrorEvent *ee); | ||
| 247 | static void zoom(const Arg *arg); | ||
| 248 | |||
| 249 | static pid_t getparentprocess(pid_t p); | ||
| 250 | static int isdescprocess(pid_t p, pid_t c); | ||
| 251 | static Client *swallowingclient(Window w); | ||
| 252 | static Client *termforwin(const Client *c); | ||
| 253 | static pid_t winpid(Window w); | ||
| 254 | |||
| 255 | /* variables */ | ||
| 256 | static const char broken[] = "broken"; | ||
| 257 | static char stext[256]; | ||
| 258 | static int statusw; | ||
| 259 | static int statussig; | ||
| 260 | static pid_t statuspid = -1; | ||
| 261 | static int screen; | ||
| 262 | static int sw, sh; /* X display screen geometry width, height */ | ||
| 263 | static int bh, blw = 0; /* bar geometry */ | ||
| 264 | static int lrpad; /* sum of left and right padding for text */ | ||
| 265 | static int (*xerrorxlib)(Display *, XErrorEvent *); | ||
| 266 | static unsigned int numlockmask = 0; | ||
| 267 | static void (*handler[LASTEvent]) (XEvent *) = { | ||
| 268 | [ButtonPress] = buttonpress, | ||
| 269 | [ClientMessage] = clientmessage, | ||
| 270 | [ConfigureRequest] = configurerequest, | ||
| 271 | [ConfigureNotify] = configurenotify, | ||
| 272 | [DestroyNotify] = destroynotify, | ||
| 273 | [EnterNotify] = enternotify, | ||
| 274 | [Expose] = expose, | ||
| 275 | [FocusIn] = focusin, | ||
| 276 | [KeyPress] = keypress, | ||
| 277 | [MappingNotify] = mappingnotify, | ||
| 278 | [MapRequest] = maprequest, | ||
| 279 | [MotionNotify] = motionnotify, | ||
| 280 | [PropertyNotify] = propertynotify, | ||
| 281 | [UnmapNotify] = unmapnotify | ||
| 282 | }; | ||
| 283 | static Atom wmatom[WMLast], netatom[NetLast]; | ||
| 284 | static int running = 1; | ||
| 285 | static Cur *cursor[CurLast]; | ||
| 286 | static Clr **scheme; | ||
| 287 | static Display *dpy; | ||
| 288 | static Drw *drw; | ||
| 289 | static Monitor *mons, *selmon; | ||
| 290 | static Window root, wmcheckwin; | ||
| 291 | |||
| 292 | static xcb_connection_t *xcon; | ||
| 293 | |||
| 294 | /* configuration, allows nested code to access above variables */ | ||
| 295 | #include "config.h" | ||
| 296 | |||
| 297 | /* compile-time check if all tags fit into an unsigned int bit array. */ | ||
| 298 | struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; | ||
| 299 | |||
| 300 | /* function implementations */ | ||
| 301 | void | ||
| 302 | applyrules(Client *c) | ||
| 303 | { | ||
| 304 | const char *class, *instance; | ||
| 305 | unsigned int i; | ||
| 306 | const Rule *r; | ||
| 307 | Monitor *m; | ||
| 308 | XClassHint ch = { NULL, NULL }; | ||
| 309 | |||
| 310 | /* rule matching */ | ||
| 311 | c->isfloating = 0; | ||
| 312 | c->tags = 0; | ||
| 313 | XGetClassHint(dpy, c->win, &ch); | ||
| 314 | class = ch.res_class ? ch.res_class : broken; | ||
| 315 | instance = ch.res_name ? ch.res_name : broken; | ||
| 316 | |||
| 317 | if (strstr(class, "Steam") || strstr(class, "steam_app_")) | ||
| 318 | c->issteam = 1; | ||
| 319 | |||
| 320 | for (i = 0; i < LENGTH(rules); i++) { | ||
| 321 | r = &rules[i]; | ||
| 322 | if ((!r->title || strstr(c->name, r->title)) | ||
| 323 | && (!r->class || strstr(class, r->class)) | ||
| 324 | && (!r->instance || strstr(instance, r->instance))) | ||
| 325 | { | ||
| 326 | c->isterminal = r->isterminal; | ||
| 327 | c->noswallow = r->noswallow; | ||
| 328 | c->isfloating = r->isfloating; | ||
| 329 | c->tags |= r->tags; | ||
| 330 | for (m = mons; m && m->num != r->monitor; m = m->next); | ||
| 331 | if (m) | ||
| 332 | c->mon = m; | ||
| 333 | } | ||
| 334 | } | ||
| 335 | if (ch.res_class) | ||
| 336 | XFree(ch.res_class); | ||
| 337 | if (ch.res_name) | ||
| 338 | XFree(ch.res_name); | ||
| 339 | c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; | ||
| 340 | } | ||
| 341 | |||
| 342 | int | ||
| 343 | applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) | ||
| 344 | { | ||
| 345 | int baseismin; | ||
| 346 | Monitor *m = c->mon; | ||
| 347 | |||
| 348 | /* set minimum possible */ | ||
| 349 | *w = MAX(1, *w); | ||
| 350 | *h = MAX(1, *h); | ||
| 351 | if (interact) { | ||
| 352 | if (*x > sw) | ||
| 353 | *x = sw - WIDTH(c); | ||
| 354 | if (*y > sh) | ||
| 355 | *y = sh - HEIGHT(c); | ||
| 356 | if (*x + *w + 2 * c->bw < 0) | ||
| 357 | *x = 0; | ||
| 358 | if (*y + *h + 2 * c->bw < 0) | ||
| 359 | *y = 0; | ||
| 360 | } else { | ||
| 361 | if (*x >= m->wx + m->ww) | ||
| 362 | *x = m->wx + m->ww - WIDTH(c); | ||
| 363 | if (*y >= m->wy + m->wh) | ||
| 364 | *y = m->wy + m->wh - HEIGHT(c); | ||
| 365 | if (*x + *w + 2 * c->bw <= m->wx) | ||
| 366 | *x = m->wx; | ||
| 367 | if (*y + *h + 2 * c->bw <= m->wy) | ||
| 368 | *y = m->wy; | ||
| 369 | } | ||
| 370 | if (*h < bh) | ||
| 371 | *h = bh; | ||
| 372 | if (*w < bh) | ||
| 373 | *w = bh; | ||
| 374 | if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { | ||
| 375 | /* see last two sentences in ICCCM 4.1.2.3 */ | ||
| 376 | baseismin = c->basew == c->minw && c->baseh == c->minh; | ||
| 377 | if (!baseismin) { /* temporarily remove base dimensions */ | ||
| 378 | *w -= c->basew; | ||
| 379 | *h -= c->baseh; | ||
| 380 | } | ||
| 381 | /* adjust for aspect limits */ | ||
| 382 | if (c->mina > 0 && c->maxa > 0) { | ||
| 383 | if (c->maxa < (float)*w / *h) | ||
| 384 | *w = *h * c->maxa + 0.5; | ||
| 385 | else if (c->mina < (float)*h / *w) | ||
| 386 | *h = *w * c->mina + 0.5; | ||
| 387 | } | ||
| 388 | if (baseismin) { /* increment calculation requires this */ | ||
| 389 | *w -= c->basew; | ||
| 390 | *h -= c->baseh; | ||
| 391 | } | ||
| 392 | /* adjust for increment value */ | ||
| 393 | if (c->incw) | ||
| 394 | *w -= *w % c->incw; | ||
| 395 | if (c->inch) | ||
| 396 | *h -= *h % c->inch; | ||
| 397 | /* restore base dimensions */ | ||
| 398 | *w = MAX(*w + c->basew, c->minw); | ||
| 399 | *h = MAX(*h + c->baseh, c->minh); | ||
| 400 | if (c->maxw) | ||
| 401 | *w = MIN(*w, c->maxw); | ||
| 402 | if (c->maxh) | ||
| 403 | *h = MIN(*h, c->maxh); | ||
| 404 | } | ||
| 405 | return *x != c->x || *y != c->y || *w != c->w || *h != c->h; | ||
| 406 | } | ||
| 407 | |||
| 408 | void | ||
| 409 | arrange(Monitor *m) | ||
| 410 | { | ||
| 411 | if (m) | ||
| 412 | showhide(m->stack); | ||
| 413 | else for (m = mons; m; m = m->next) | ||
| 414 | showhide(m->stack); | ||
| 415 | if (m) { | ||
| 416 | arrangemon(m); | ||
| 417 | restack(m); | ||
| 418 | } else for (m = mons; m; m = m->next) | ||
| 419 | arrangemon(m); | ||
| 420 | } | ||
| 421 | |||
| 422 | void | ||
| 423 | arrangemon(Monitor *m) | ||
| 424 | { | ||
| 425 | strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); | ||
| 426 | if (m->lt[m->sellt]->arrange) | ||
| 427 | m->lt[m->sellt]->arrange(m); | ||
| 428 | } | ||
| 429 | |||
| 430 | void | ||
| 431 | attach(Client *c) | ||
| 432 | { | ||
| 433 | c->next = c->mon->clients; | ||
| 434 | c->mon->clients = c; | ||
| 435 | } | ||
| 436 | |||
| 437 | void | ||
| 438 | attachstack(Client *c) | ||
| 439 | { | ||
| 440 | c->snext = c->mon->stack; | ||
| 441 | c->mon->stack = c; | ||
| 442 | } | ||
| 443 | |||
| 444 | void | ||
| 445 | swallow(Client *p, Client *c) | ||
| 446 | { | ||
| 447 | |||
| 448 | if (c->noswallow || c->isterminal) | ||
| 449 | return; | ||
| 450 | if (c->noswallow && !swallowfloating && c->isfloating) | ||
| 451 | return; | ||
| 452 | |||
| 453 | detach(c); | ||
| 454 | detachstack(c); | ||
| 455 | |||
| 456 | setclientstate(c, WithdrawnState); | ||
| 457 | XUnmapWindow(dpy, p->win); | ||
| 458 | |||
| 459 | p->swallowing = c; | ||
| 460 | c->mon = p->mon; | ||
| 461 | |||
| 462 | Window w = p->win; | ||
| 463 | p->win = c->win; | ||
| 464 | c->win = w; | ||
| 465 | updatetitle(p); | ||
| 466 | XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h); | ||
| 467 | arrange(p->mon); | ||
| 468 | configure(p); | ||
| 469 | updateclientlist(); | ||
| 470 | } | ||
| 471 | |||
| 472 | void | ||
| 473 | unswallow(Client *c) | ||
| 474 | { | ||
| 475 | c->win = c->swallowing->win; | ||
| 476 | |||
| 477 | free(c->swallowing); | ||
| 478 | c->swallowing = NULL; | ||
| 479 | |||
| 480 | /* unfullscreen the client */ | ||
| 481 | setfullscreen(c, 0); | ||
| 482 | updatetitle(c); | ||
| 483 | arrange(c->mon); | ||
| 484 | XMapWindow(dpy, c->win); | ||
| 485 | XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); | ||
| 486 | setclientstate(c, NormalState); | ||
| 487 | focus(NULL); | ||
| 488 | arrange(c->mon); | ||
| 489 | } | ||
| 490 | |||
| 491 | void | ||
| 492 | buttonpress(XEvent *e) | ||
| 493 | { | ||
| 494 | unsigned int i, x, click, occ = 0; | ||
| 495 | Arg arg = {0}; | ||
| 496 | Client *c; | ||
| 497 | Monitor *m; | ||
| 498 | XButtonPressedEvent *ev = &e->xbutton; | ||
| 499 | char *text, *s, ch; | ||
| 500 | |||
| 501 | click = ClkRootWin; | ||
| 502 | /* focus monitor if necessary */ | ||
| 503 | if ((m = wintomon(ev->window)) && m != selmon) { | ||
| 504 | unfocus(selmon->sel, 1); | ||
| 505 | selmon = m; | ||
| 506 | focus(NULL); | ||
| 507 | } | ||
| 508 | if (ev->window == selmon->barwin) { | ||
| 509 | i = x = 0; | ||
| 510 | for (c = m->clients; c; c = c->next) | ||
| 511 | occ |= c->tags == 255 ? 0 : c-> tags; | ||
| 512 | do { | ||
| 513 | /* do not reserve space for vacant tags */ | ||
| 514 | if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) | ||
| 515 | continue; | ||
| 516 | x += TEXTW(tags[i]); | ||
| 517 | } while (ev->x >= x && ++i < LENGTH(tags)); | ||
| 518 | if (i < LENGTH(tags)) { | ||
| 519 | click = ClkTagBar; | ||
| 520 | arg.ui = 1 << i; | ||
| 521 | } else if (ev->x < x + blw) | ||
| 522 | click = ClkLtSymbol; | ||
| 523 | else if (ev->x > selmon->ww - statusw) { | ||
| 524 | x = selmon->ww - statusw; | ||
| 525 | click = ClkStatusText; | ||
| 526 | statussig = 0; | ||
| 527 | for (text = s = stext; *s && x <= ev->x; s++) { | ||
| 528 | if ((unsigned char)(*s) < ' ') { | ||
| 529 | ch = *s; | ||
| 530 | *s = '\0'; | ||
| 531 | x += TEXTW(text) - lrpad; | ||
| 532 | *s = ch; | ||
| 533 | text = s + 1; | ||
| 534 | if (x >= ev->x) | ||
| 535 | break; | ||
| 536 | statussig = ch; | ||
| 537 | } | ||
| 538 | } | ||
| 539 | } else | ||
| 540 | click = ClkWinTitle; | ||
| 541 | } else if ((c = wintoclient(ev->window))) { | ||
| 542 | focus(c); | ||
| 543 | restack(selmon); | ||
| 544 | XAllowEvents(dpy, ReplayPointer, CurrentTime); | ||
| 545 | click = ClkClientWin; | ||
| 546 | } | ||
| 547 | for (i = 0; i < LENGTH(buttons); i++) | ||
| 548 | if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button | ||
| 549 | && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) | ||
| 550 | buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); | ||
| 551 | } | ||
| 552 | |||
| 553 | void | ||
| 554 | checkotherwm(void) | ||
| 555 | { | ||
| 556 | xerrorxlib = XSetErrorHandler(xerrorstart); | ||
| 557 | /* this causes an error if some other window manager is running */ | ||
| 558 | XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); | ||
| 559 | XSync(dpy, False); | ||
| 560 | XSetErrorHandler(xerror); | ||
| 561 | XSync(dpy, False); | ||
| 562 | } | ||
| 563 | |||
| 564 | void | ||
| 565 | cleanup(void) | ||
| 566 | { | ||
| 567 | Arg a = {.ui = ~0}; | ||
| 568 | Layout foo = { "", NULL }; | ||
| 569 | Monitor *m; | ||
| 570 | size_t i; | ||
| 571 | |||
| 572 | view(&a); | ||
| 573 | selmon->lt[selmon->sellt] = &foo; | ||
| 574 | for (m = mons; m; m = m->next) | ||
| 575 | while (m->stack) | ||
| 576 | unmanage(m->stack, 0); | ||
| 577 | XUngrabKey(dpy, AnyKey, AnyModifier, root); | ||
| 578 | while (mons) | ||
| 579 | cleanupmon(mons); | ||
| 580 | for (i = 0; i < CurLast; i++) | ||
| 581 | drw_cur_free(drw, cursor[i]); | ||
| 582 | for (i = 0; i < LENGTH(colors); i++) | ||
| 583 | free(scheme[i]); | ||
| 584 | XDestroyWindow(dpy, wmcheckwin); | ||
| 585 | drw_free(drw); | ||
| 586 | XSync(dpy, False); | ||
| 587 | XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); | ||
| 588 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); | ||
| 589 | } | ||
| 590 | |||
| 591 | void | ||
| 592 | cleanupmon(Monitor *mon) | ||
| 593 | { | ||
| 594 | Monitor *m; | ||
| 595 | |||
| 596 | if (mon == mons) | ||
| 597 | mons = mons->next; | ||
| 598 | else { | ||
| 599 | for (m = mons; m && m->next != mon; m = m->next); | ||
| 600 | m->next = mon->next; | ||
| 601 | } | ||
| 602 | XUnmapWindow(dpy, mon->barwin); | ||
| 603 | XDestroyWindow(dpy, mon->barwin); | ||
| 604 | free(mon); | ||
| 605 | } | ||
| 606 | |||
| 607 | void | ||
| 608 | clientmessage(XEvent *e) | ||
| 609 | { | ||
| 610 | XClientMessageEvent *cme = &e->xclient; | ||
| 611 | Client *c = wintoclient(cme->window); | ||
| 612 | |||
| 613 | if (!c) | ||
| 614 | return; | ||
| 615 | if (cme->message_type == netatom[NetWMState]) { | ||
| 616 | if (cme->data.l[1] == netatom[NetWMFullscreen] | ||
| 617 | || cme->data.l[2] == netatom[NetWMFullscreen]) | ||
| 618 | setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ | ||
| 619 | || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); | ||
| 620 | } else if (cme->message_type == netatom[NetActiveWindow]) { | ||
| 621 | if (c != selmon->sel && !c->isurgent) | ||
| 622 | seturgent(c, 1); | ||
| 623 | } | ||
| 624 | } | ||
| 625 | |||
| 626 | void | ||
| 627 | configure(Client *c) | ||
| 628 | { | ||
| 629 | XConfigureEvent ce; | ||
| 630 | |||
| 631 | ce.type = ConfigureNotify; | ||
| 632 | ce.display = dpy; | ||
| 633 | ce.event = c->win; | ||
| 634 | ce.window = c->win; | ||
| 635 | ce.x = c->x; | ||
| 636 | ce.y = c->y; | ||
| 637 | ce.width = c->w; | ||
| 638 | ce.height = c->h; | ||
| 639 | ce.border_width = c->bw; | ||
| 640 | ce.above = None; | ||
| 641 | ce.override_redirect = False; | ||
| 642 | XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); | ||
| 643 | } | ||
| 644 | |||
| 645 | void | ||
| 646 | configurenotify(XEvent *e) | ||
| 647 | { | ||
| 648 | Monitor *m; | ||
| 649 | Client *c; | ||
| 650 | XConfigureEvent *ev = &e->xconfigure; | ||
| 651 | int dirty; | ||
| 652 | |||
| 653 | /* TODO: updategeom handling sucks, needs to be simplified */ | ||
| 654 | if (ev->window == root) { | ||
| 655 | dirty = (sw != ev->width || sh != ev->height); | ||
| 656 | sw = ev->width; | ||
| 657 | sh = ev->height; | ||
| 658 | if (updategeom() || dirty) { | ||
| 659 | drw_resize(drw, sw, bh); | ||
| 660 | updatebars(); | ||
| 661 | for (m = mons; m; m = m->next) { | ||
| 662 | for (c = m->clients; c; c = c->next) | ||
| 663 | if (c->isfullscreen) | ||
| 664 | resizeclient(c, m->mx, m->my, m->mw, m->mh); | ||
| 665 | XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); | ||
| 666 | } | ||
| 667 | focus(NULL); | ||
| 668 | arrange(NULL); | ||
| 669 | } | ||
| 670 | } | ||
| 671 | } | ||
| 672 | |||
| 673 | void | ||
| 674 | configurerequest(XEvent *e) | ||
| 675 | { | ||
| 676 | Client *c; | ||
| 677 | Monitor *m; | ||
| 678 | XConfigureRequestEvent *ev = &e->xconfigurerequest; | ||
| 679 | XWindowChanges wc; | ||
| 680 | |||
| 681 | if ((c = wintoclient(ev->window))) { | ||
| 682 | if (ev->value_mask & CWBorderWidth) | ||
| 683 | c->bw = ev->border_width; | ||
| 684 | else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { | ||
| 685 | m = c->mon; | ||
| 686 | if (!c->issteam) { | ||
| 687 | if (ev->value_mask & CWX) { | ||
| 688 | c->oldx = c->x; | ||
| 689 | c->x = m->mx + ev->x; | ||
| 690 | } | ||
| 691 | if (ev->value_mask & CWY) { | ||
| 692 | c->oldy = c->y; | ||
| 693 | c->y = m->my + ev->y; | ||
| 694 | } | ||
| 695 | } | ||
| 696 | if (ev->value_mask & CWWidth) { | ||
| 697 | c->oldw = c->w; | ||
| 698 | c->w = ev->width; | ||
| 699 | } | ||
| 700 | if (ev->value_mask & CWHeight) { | ||
| 701 | c->oldh = c->h; | ||
| 702 | c->h = ev->height; | ||
| 703 | } | ||
| 704 | if ((c->x + c->w) > m->mx + m->mw && c->isfloating) | ||
| 705 | c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ | ||
| 706 | if ((c->y + c->h) > m->my + m->mh && c->isfloating) | ||
| 707 | c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ | ||
| 708 | if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) | ||
| 709 | configure(c); | ||
| 710 | if (ISVISIBLE(c)) | ||
| 711 | XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); | ||
| 712 | } else | ||
| 713 | configure(c); | ||
| 714 | } else { | ||
| 715 | wc.x = ev->x; | ||
| 716 | wc.y = ev->y; | ||
| 717 | wc.width = ev->width; | ||
| 718 | wc.height = ev->height; | ||
| 719 | wc.border_width = ev->border_width; | ||
| 720 | wc.sibling = ev->above; | ||
| 721 | wc.stack_mode = ev->detail; | ||
| 722 | XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); | ||
| 723 | } | ||
| 724 | XSync(dpy, False); | ||
| 725 | } | ||
| 726 | |||
| 727 | Monitor * | ||
| 728 | createmon(void) | ||
| 729 | { | ||
| 730 | Monitor *m; | ||
| 731 | |||
| 732 | m = ecalloc(1, sizeof(Monitor)); | ||
| 733 | m->tagset[0] = m->tagset[1] = 1; | ||
| 734 | m->mfact = mfact; | ||
| 735 | m->nmaster = nmaster; | ||
| 736 | m->showbar = showbar; | ||
| 737 | m->topbar = topbar; | ||
| 738 | m->gappih = gappih; | ||
| 739 | m->gappiv = gappiv; | ||
| 740 | m->gappoh = gappoh; | ||
| 741 | m->gappov = gappov; | ||
| 742 | m->lt[0] = &layouts[0]; | ||
| 743 | m->lt[1] = &layouts[1 % LENGTH(layouts)]; | ||
| 744 | strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); | ||
| 745 | return m; | ||
| 746 | } | ||
| 747 | |||
| 748 | void | ||
| 749 | destroynotify(XEvent *e) | ||
| 750 | { | ||
| 751 | Client *c; | ||
| 752 | XDestroyWindowEvent *ev = &e->xdestroywindow; | ||
| 753 | |||
| 754 | if ((c = wintoclient(ev->window))) | ||
| 755 | unmanage(c, 1); | ||
| 756 | |||
| 757 | else if ((c = swallowingclient(ev->window))) | ||
| 758 | unmanage(c->swallowing, 1); | ||
| 759 | } | ||
| 760 | |||
| 761 | void | ||
| 762 | detach(Client *c) | ||
| 763 | { | ||
| 764 | Client **tc; | ||
| 765 | |||
| 766 | for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); | ||
| 767 | *tc = c->next; | ||
| 768 | } | ||
| 769 | |||
| 770 | void | ||
| 771 | detachstack(Client *c) | ||
| 772 | { | ||
| 773 | Client **tc, *t; | ||
| 774 | |||
| 775 | for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); | ||
| 776 | *tc = c->snext; | ||
| 777 | |||
| 778 | if (c == c->mon->sel) { | ||
| 779 | for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); | ||
| 780 | c->mon->sel = t; | ||
| 781 | } | ||
| 782 | } | ||
| 783 | |||
| 784 | Monitor * | ||
| 785 | dirtomon(int dir) | ||
| 786 | { | ||
| 787 | Monitor *m = NULL; | ||
| 788 | |||
| 789 | if (dir > 0) { | ||
| 790 | if (!(m = selmon->next)) | ||
| 791 | m = mons; | ||
| 792 | } else if (selmon == mons) | ||
| 793 | for (m = mons; m->next; m = m->next); | ||
| 794 | else | ||
| 795 | for (m = mons; m->next != selmon; m = m->next); | ||
| 796 | return m; | ||
| 797 | } | ||
| 798 | |||
| 799 | void | ||
| 800 | drawbar(Monitor *m) | ||
| 801 | { | ||
| 802 | int x, w, sw = 0; | ||
| 803 | int boxs = drw->fonts->h / 9; | ||
| 804 | int boxw = drw->fonts->h / 6 + 2; | ||
| 805 | unsigned int i, occ = 0, urg = 0; | ||
| 806 | Client *c; | ||
| 807 | |||
| 808 | /* draw status first so it can be overdrawn by tags later */ | ||
| 809 | if (m == selmon) { /* status is only drawn on selected monitor */ | ||
| 810 | char *text, *s, ch; | ||
| 811 | drw_setscheme(drw, scheme[SchemeNorm]); | ||
| 812 | x = 0; | ||
| 813 | for (text = s = stext; *s; s++) { | ||
| 814 | if ((unsigned char)(*s) < ' ') { | ||
| 815 | ch = *s; | ||
| 816 | *s = '\0'; | ||
| 817 | sw = TEXTW(text) - lrpad; | ||
| 818 | drw_text(drw, m->ww - statusw + x, 0, sw, bh, 0, text, 0); | ||
| 819 | x += sw; | ||
| 820 | *s = ch; | ||
| 821 | text = s + 1; | ||
| 822 | } | ||
| 823 | } | ||
| 824 | sw = TEXTW(text) - lrpad + 2; | ||
| 825 | drw_text(drw, m->ww - statusw + x, 0, sw, bh, 0, text, 0); | ||
| 826 | sw = statusw; | ||
| 827 | } | ||
| 828 | |||
| 829 | for (c = m->clients; c; c = c->next) { | ||
| 830 | occ |= c->tags; | ||
| 831 | if (c->isurgent) | ||
| 832 | urg |= c->tags; | ||
| 833 | } | ||
| 834 | x = 0; | ||
| 835 | for (i = 0; i < LENGTH(tags); i++) { | ||
| 836 | /* do not draw vacant tags */ | ||
| 837 | if (!(occ & 1 << i || m ->tagset[m->seltags] & 1 << i)) | ||
| 838 | continue; | ||
| 839 | |||
| 840 | w = TEXTW(tags[i]); | ||
| 841 | drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); | ||
| 842 | drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); | ||
| 843 | /*if (occ & 1 << i) | ||
| 844 | drw_rect(drw, x + boxs, boxs, boxw, boxw, | ||
| 845 | m == selmon && selmon->sel && selmon->sel->tags & 1 << i, | ||
| 846 | urg & 1 << i); | ||
| 847 | */ | ||
| 848 | x += w; | ||
| 849 | } | ||
| 850 | w = blw = TEXTW(m->ltsymbol); | ||
| 851 | drw_setscheme(drw, scheme[SchemeNorm]); | ||
| 852 | x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); | ||
| 853 | |||
| 854 | if ((w = m->ww - sw - x) > bh) { | ||
| 855 | if (m->sel) { | ||
| 856 | drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); | ||
| 857 | drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); | ||
| 858 | if (m->sel->isfloating) | ||
| 859 | drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); | ||
| 860 | } else { | ||
| 861 | drw_setscheme(drw, scheme[SchemeNorm]); | ||
| 862 | drw_rect(drw, x, 0, w, bh, 1, 1); | ||
| 863 | } | ||
| 864 | } | ||
| 865 | drw_map(drw, m->barwin, 0, 0, m->ww, bh); | ||
| 866 | } | ||
| 867 | |||
| 868 | void | ||
| 869 | drawbars(void) | ||
| 870 | { | ||
| 871 | Monitor *m; | ||
| 872 | |||
| 873 | for (m = mons; m; m = m->next) | ||
| 874 | drawbar(m); | ||
| 875 | } | ||
| 876 | |||
| 877 | void | ||
| 878 | enternotify(XEvent *e) | ||
| 879 | { | ||
| 880 | Client *c; | ||
| 881 | Monitor *m; | ||
| 882 | XCrossingEvent *ev = &e->xcrossing; | ||
| 883 | |||
| 884 | if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) | ||
| 885 | return; | ||
| 886 | c = wintoclient(ev->window); | ||
| 887 | m = c ? c->mon : wintomon(ev->window); | ||
| 888 | if (m != selmon) { | ||
| 889 | unfocus(selmon->sel, 1); | ||
| 890 | selmon = m; | ||
| 891 | } else if (!c || c == selmon->sel) | ||
| 892 | return; | ||
| 893 | focus(c); | ||
| 894 | } | ||
| 895 | |||
| 896 | void | ||
| 897 | expose(XEvent *e) | ||
| 898 | { | ||
| 899 | Monitor *m; | ||
| 900 | XExposeEvent *ev = &e->xexpose; | ||
| 901 | |||
| 902 | if (ev->count == 0 && (m = wintomon(ev->window))) | ||
| 903 | drawbar(m); | ||
| 904 | } | ||
| 905 | |||
| 906 | void | ||
| 907 | focus(Client *c) | ||
| 908 | { | ||
| 909 | if (!c || !ISVISIBLE(c)) | ||
| 910 | for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); | ||
| 911 | if (selmon->sel && selmon->sel != c) | ||
| 912 | unfocus(selmon->sel, 0); | ||
| 913 | if (c) { | ||
| 914 | if (c->mon != selmon) | ||
| 915 | selmon = c->mon; | ||
| 916 | if (c->isurgent) | ||
| 917 | seturgent(c, 0); | ||
| 918 | detachstack(c); | ||
| 919 | attachstack(c); | ||
| 920 | grabbuttons(c, 1); | ||
| 921 | XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); | ||
| 922 | setfocus(c); | ||
| 923 | } else { | ||
| 924 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); | ||
| 925 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); | ||
| 926 | } | ||
| 927 | selmon->sel = c; | ||
| 928 | drawbars(); | ||
| 929 | } | ||
| 930 | |||
| 931 | /* there are some broken focus acquiring clients needing extra handling */ | ||
| 932 | void | ||
| 933 | focusin(XEvent *e) | ||
| 934 | { | ||
| 935 | XFocusChangeEvent *ev = &e->xfocus; | ||
| 936 | |||
| 937 | if (selmon->sel && ev->window != selmon->sel->win) | ||
| 938 | setfocus(selmon->sel); | ||
| 939 | } | ||
| 940 | |||
| 941 | void | ||
| 942 | focusmon(const Arg *arg) | ||
| 943 | { | ||
| 944 | Monitor *m; | ||
| 945 | |||
| 946 | if (!mons->next) | ||
| 947 | return; | ||
| 948 | if ((m = dirtomon(arg->i)) == selmon) | ||
| 949 | return; | ||
| 950 | unfocus(selmon->sel, 0); | ||
| 951 | selmon = m; | ||
| 952 | focus(NULL); | ||
| 953 | } | ||
| 954 | |||
| 955 | void | ||
| 956 | focusstack(const Arg *arg) | ||
| 957 | { | ||
| 958 | Client *c = NULL, *i; | ||
| 959 | |||
| 960 | if (!selmon->sel) | ||
| 961 | return; | ||
| 962 | if (arg->i > 0) { | ||
| 963 | for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); | ||
| 964 | if (!c) | ||
| 965 | for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); | ||
| 966 | } else { | ||
| 967 | for (i = selmon->clients; i != selmon->sel; i = i->next) | ||
| 968 | if (ISVISIBLE(i)) | ||
| 969 | c = i; | ||
| 970 | if (!c) | ||
| 971 | for (; i; i = i->next) | ||
| 972 | if (ISVISIBLE(i)) | ||
| 973 | c = i; | ||
| 974 | } | ||
| 975 | if (c) { | ||
| 976 | focus(c); | ||
| 977 | restack(selmon); | ||
| 978 | } | ||
| 979 | } | ||
| 980 | |||
| 981 | Atom | ||
| 982 | getatomprop(Client *c, Atom prop) | ||
| 983 | { | ||
| 984 | int di; | ||
| 985 | unsigned long dl; | ||
| 986 | unsigned char *p = NULL; | ||
| 987 | Atom da, atom = None; | ||
| 988 | |||
| 989 | if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, | ||
| 990 | &da, &di, &dl, &dl, &p) == Success && p) { | ||
| 991 | atom = *(Atom *)p; | ||
| 992 | XFree(p); | ||
| 993 | } | ||
| 994 | return atom; | ||
| 995 | } | ||
| 996 | |||
| 997 | pid_t | ||
| 998 | getstatusbarpid() | ||
| 999 | { | ||
| 1000 | char buf[32], *str = buf, *c; | ||
| 1001 | FILE *fp; | ||
| 1002 | |||
| 1003 | if (statuspid > 0) { | ||
| 1004 | snprintf(buf, sizeof(buf), "/proc/%u/cmdline", statuspid); | ||
| 1005 | if ((fp = fopen(buf, "r"))) { | ||
| 1006 | fgets(buf, sizeof(buf), fp); | ||
| 1007 | while ((c = strchr(str, '/'))) | ||
| 1008 | str = c + 1; | ||
| 1009 | fclose(fp); | ||
| 1010 | if (!strcmp(str, STATUSBAR)) | ||
| 1011 | return statuspid; | ||
| 1012 | } | ||
| 1013 | } | ||
| 1014 | if (!(fp = popen("pidof -s "STATUSBAR, "r"))) | ||
| 1015 | return -1; | ||
| 1016 | fgets(buf, sizeof(buf), fp); | ||
| 1017 | pclose(fp); | ||
| 1018 | return strtol(buf, NULL, 10); | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | int | ||
| 1022 | getrootptr(int *x, int *y) | ||
| 1023 | { | ||
| 1024 | int di; | ||
| 1025 | unsigned int dui; | ||
| 1026 | Window dummy; | ||
| 1027 | |||
| 1028 | return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | long | ||
| 1032 | getstate(Window w) | ||
| 1033 | { | ||
| 1034 | int format; | ||
| 1035 | long result = -1; | ||
| 1036 | unsigned char *p = NULL; | ||
| 1037 | unsigned long n, extra; | ||
| 1038 | Atom real; | ||
| 1039 | |||
| 1040 | if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], | ||
| 1041 | &real, &format, &n, &extra, (unsigned char **)&p) != Success) | ||
| 1042 | return -1; | ||
| 1043 | if (n != 0) | ||
| 1044 | result = *p; | ||
| 1045 | XFree(p); | ||
| 1046 | return result; | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | int | ||
| 1050 | gettextprop(Window w, Atom atom, char *text, unsigned int size) | ||
| 1051 | { | ||
| 1052 | char **list = NULL; | ||
| 1053 | int n; | ||
| 1054 | XTextProperty name; | ||
| 1055 | |||
| 1056 | if (!text || size == 0) | ||
| 1057 | return 0; | ||
| 1058 | text[0] = '\0'; | ||
| 1059 | if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) | ||
| 1060 | return 0; | ||
| 1061 | if (name.encoding == XA_STRING) | ||
| 1062 | strncpy(text, (char *)name.value, size - 1); | ||
| 1063 | else { | ||
| 1064 | if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { | ||
| 1065 | strncpy(text, *list, size - 1); | ||
| 1066 | XFreeStringList(list); | ||
| 1067 | } | ||
| 1068 | } | ||
| 1069 | text[size - 1] = '\0'; | ||
| 1070 | XFree(name.value); | ||
| 1071 | return 1; | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | void | ||
| 1075 | grabbuttons(Client *c, int focused) | ||
| 1076 | { | ||
| 1077 | updatenumlockmask(); | ||
| 1078 | { | ||
| 1079 | unsigned int i, j; | ||
| 1080 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; | ||
| 1081 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); | ||
| 1082 | if (!focused) | ||
| 1083 | XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, | ||
| 1084 | BUTTONMASK, GrabModeSync, GrabModeSync, None, None); | ||
| 1085 | for (i = 0; i < LENGTH(buttons); i++) | ||
| 1086 | if (buttons[i].click == ClkClientWin) | ||
| 1087 | for (j = 0; j < LENGTH(modifiers); j++) | ||
| 1088 | XGrabButton(dpy, buttons[i].button, | ||
| 1089 | buttons[i].mask | modifiers[j], | ||
| 1090 | c->win, False, BUTTONMASK, | ||
| 1091 | GrabModeAsync, GrabModeSync, None, None); | ||
| 1092 | } | ||
| 1093 | } | ||
| 1094 | |||
| 1095 | void | ||
| 1096 | grabkeys(void) | ||
| 1097 | { | ||
| 1098 | updatenumlockmask(); | ||
| 1099 | { | ||
| 1100 | unsigned int i, j; | ||
| 1101 | unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; | ||
| 1102 | KeyCode code; | ||
| 1103 | |||
| 1104 | XUngrabKey(dpy, AnyKey, AnyModifier, root); | ||
| 1105 | for (i = 0; i < LENGTH(keys); i++) | ||
| 1106 | if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) | ||
| 1107 | for (j = 0; j < LENGTH(modifiers); j++) | ||
| 1108 | XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, | ||
| 1109 | True, GrabModeAsync, GrabModeAsync); | ||
| 1110 | } | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | void | ||
| 1114 | incnmaster(const Arg *arg) | ||
| 1115 | { | ||
| 1116 | selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); | ||
| 1117 | arrange(selmon); | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | #ifdef XINERAMA | ||
| 1121 | static int | ||
| 1122 | isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) | ||
| 1123 | { | ||
| 1124 | while (n--) | ||
| 1125 | if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org | ||
| 1126 | && unique[n].width == info->width && unique[n].height == info->height) | ||
| 1127 | return 0; | ||
| 1128 | return 1; | ||
| 1129 | } | ||
| 1130 | #endif /* XINERAMA */ | ||
| 1131 | |||
| 1132 | void | ||
| 1133 | keypress(XEvent *e) | ||
| 1134 | { | ||
| 1135 | unsigned int i; | ||
| 1136 | KeySym keysym; | ||
| 1137 | XKeyEvent *ev; | ||
| 1138 | |||
| 1139 | ev = &e->xkey; | ||
| 1140 | keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); | ||
| 1141 | for (i = 0; i < LENGTH(keys); i++) | ||
| 1142 | if (keysym == keys[i].keysym | ||
| 1143 | && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) | ||
| 1144 | && keys[i].func) | ||
| 1145 | keys[i].func(&(keys[i].arg)); | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | void | ||
| 1149 | killclient(const Arg *arg) | ||
| 1150 | { | ||
| 1151 | if (!selmon->sel) | ||
| 1152 | return; | ||
| 1153 | if (!sendevent(selmon->sel, wmatom[WMDelete])) { | ||
| 1154 | XGrabServer(dpy); | ||
| 1155 | XSetErrorHandler(xerrordummy); | ||
| 1156 | XSetCloseDownMode(dpy, DestroyAll); | ||
| 1157 | XKillClient(dpy, selmon->sel->win); | ||
| 1158 | XSync(dpy, False); | ||
| 1159 | XSetErrorHandler(xerror); | ||
| 1160 | XUngrabServer(dpy); | ||
| 1161 | } | ||
| 1162 | } | ||
| 1163 | |||
| 1164 | void | ||
| 1165 | manage(Window w, XWindowAttributes *wa) | ||
| 1166 | { | ||
| 1167 | Client *c, *t = NULL, *term = NULL; | ||
| 1168 | Window trans = None; | ||
| 1169 | XWindowChanges wc; | ||
| 1170 | |||
| 1171 | c = ecalloc(1, sizeof(Client)); | ||
| 1172 | c->win = w; | ||
| 1173 | c->pid = winpid(w); | ||
| 1174 | /* geometry */ | ||
| 1175 | c->x = c->oldx = wa->x; | ||
| 1176 | c->y = c->oldy = wa->y; | ||
| 1177 | c->w = c->oldw = wa->width; | ||
| 1178 | c->h = c->oldh = wa->height; | ||
| 1179 | c->oldbw = wa->border_width; | ||
| 1180 | |||
| 1181 | updatetitle(c); | ||
| 1182 | if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { | ||
| 1183 | c->mon = t->mon; | ||
| 1184 | c->tags = t->tags; | ||
| 1185 | } else { | ||
| 1186 | c->mon = selmon; | ||
| 1187 | applyrules(c); | ||
| 1188 | term = termforwin(c); | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) | ||
| 1192 | c->x = c->mon->mx + c->mon->mw - WIDTH(c); | ||
| 1193 | if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) | ||
| 1194 | c->y = c->mon->my + c->mon->mh - HEIGHT(c); | ||
| 1195 | c->x = MAX(c->x, c->mon->mx); | ||
| 1196 | /* only fix client y-offset, if the client center might cover the bar */ | ||
| 1197 | c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) | ||
| 1198 | && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); | ||
| 1199 | c->bw = borderpx; | ||
| 1200 | |||
| 1201 | wc.border_width = c->bw; | ||
| 1202 | XConfigureWindow(dpy, w, CWBorderWidth, &wc); | ||
| 1203 | XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); | ||
| 1204 | configure(c); /* propagates border_width, if size doesn't change */ | ||
| 1205 | updatewindowtype(c); | ||
| 1206 | updatesizehints(c); | ||
| 1207 | updatewmhints(c); | ||
| 1208 | XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); | ||
| 1209 | grabbuttons(c, 0); | ||
| 1210 | if (!c->isfloating) | ||
| 1211 | c->isfloating = c->oldstate = trans != None || c->isfixed; | ||
| 1212 | if (c->isfloating) | ||
| 1213 | XRaiseWindow(dpy, c->win); | ||
| 1214 | attach(c); | ||
| 1215 | attachstack(c); | ||
| 1216 | XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, | ||
| 1217 | (unsigned char *) &(c->win), 1); | ||
| 1218 | XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ | ||
| 1219 | setclientstate(c, NormalState); | ||
| 1220 | if (c->mon == selmon) | ||
| 1221 | unfocus(selmon->sel, 0); | ||
| 1222 | c->mon->sel = c; | ||
| 1223 | arrange(c->mon); | ||
| 1224 | XMapWindow(dpy, c->win); | ||
| 1225 | if (term) | ||
| 1226 | swallow(term, c); | ||
| 1227 | focus(NULL); | ||
| 1228 | } | ||
| 1229 | |||
| 1230 | void | ||
| 1231 | mappingnotify(XEvent *e) | ||
| 1232 | { | ||
| 1233 | XMappingEvent *ev = &e->xmapping; | ||
| 1234 | |||
| 1235 | XRefreshKeyboardMapping(ev); | ||
| 1236 | if (ev->request == MappingKeyboard) | ||
| 1237 | grabkeys(); | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | void | ||
| 1241 | maprequest(XEvent *e) | ||
| 1242 | { | ||
| 1243 | static XWindowAttributes wa; | ||
| 1244 | XMapRequestEvent *ev = &e->xmaprequest; | ||
| 1245 | |||
| 1246 | if (!XGetWindowAttributes(dpy, ev->window, &wa)) | ||
| 1247 | return; | ||
| 1248 | if (wa.override_redirect) | ||
| 1249 | return; | ||
| 1250 | if (!wintoclient(ev->window)) | ||
| 1251 | manage(ev->window, &wa); | ||
| 1252 | } | ||
| 1253 | |||
| 1254 | void | ||
| 1255 | monocle(Monitor *m) | ||
| 1256 | { | ||
| 1257 | unsigned int n = 0; | ||
| 1258 | Client *c; | ||
| 1259 | |||
| 1260 | for (c = m->clients; c; c = c->next) | ||
| 1261 | if (ISVISIBLE(c)) | ||
| 1262 | n++; | ||
| 1263 | if (n > 0) /* override layout symbol */ | ||
| 1264 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); | ||
| 1265 | for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) | ||
| 1266 | resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | void | ||
| 1270 | motionnotify(XEvent *e) | ||
| 1271 | { | ||
| 1272 | static Monitor *mon = NULL; | ||
| 1273 | Monitor *m; | ||
| 1274 | XMotionEvent *ev = &e->xmotion; | ||
| 1275 | |||
| 1276 | if (ev->window != root) | ||
| 1277 | return; | ||
| 1278 | if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { | ||
| 1279 | unfocus(selmon->sel, 1); | ||
| 1280 | selmon = m; | ||
| 1281 | focus(NULL); | ||
| 1282 | } | ||
| 1283 | mon = m; | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | void | ||
| 1287 | movemouse(const Arg *arg) | ||
| 1288 | { | ||
| 1289 | int x, y, ocx, ocy, nx, ny; | ||
| 1290 | Client *c; | ||
| 1291 | Monitor *m; | ||
| 1292 | XEvent ev; | ||
| 1293 | Time lasttime = 0; | ||
| 1294 | |||
| 1295 | if (!(c = selmon->sel)) | ||
| 1296 | return; | ||
| 1297 | if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ | ||
| 1298 | return; | ||
| 1299 | restack(selmon); | ||
| 1300 | ocx = c->x; | ||
| 1301 | ocy = c->y; | ||
| 1302 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, | ||
| 1303 | None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) | ||
| 1304 | return; | ||
| 1305 | if (!getrootptr(&x, &y)) | ||
| 1306 | return; | ||
| 1307 | do { | ||
| 1308 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); | ||
| 1309 | switch(ev.type) { | ||
| 1310 | case ConfigureRequest: | ||
| 1311 | case Expose: | ||
| 1312 | case MapRequest: | ||
| 1313 | handler[ev.type](&ev); | ||
| 1314 | break; | ||
| 1315 | case MotionNotify: | ||
| 1316 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) | ||
| 1317 | continue; | ||
| 1318 | lasttime = ev.xmotion.time; | ||
| 1319 | |||
| 1320 | nx = ocx + (ev.xmotion.x - x); | ||
| 1321 | ny = ocy + (ev.xmotion.y - y); | ||
| 1322 | if (abs(selmon->wx - nx) < snap) | ||
| 1323 | nx = selmon->wx; | ||
| 1324 | else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) | ||
| 1325 | nx = selmon->wx + selmon->ww - WIDTH(c); | ||
| 1326 | if (abs(selmon->wy - ny) < snap) | ||
| 1327 | ny = selmon->wy; | ||
| 1328 | else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) | ||
| 1329 | ny = selmon->wy + selmon->wh - HEIGHT(c); | ||
| 1330 | if (!c->isfloating && selmon->lt[selmon->sellt]->arrange | ||
| 1331 | && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) | ||
| 1332 | togglefloating(NULL); | ||
| 1333 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) | ||
| 1334 | resize(c, nx, ny, c->w, c->h, 1); | ||
| 1335 | break; | ||
| 1336 | } | ||
| 1337 | } while (ev.type != ButtonRelease); | ||
| 1338 | XUngrabPointer(dpy, CurrentTime); | ||
| 1339 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { | ||
| 1340 | sendmon(c, m); | ||
| 1341 | selmon = m; | ||
| 1342 | focus(NULL); | ||
| 1343 | } | ||
| 1344 | } | ||
| 1345 | |||
| 1346 | Client * | ||
| 1347 | nexttiled(Client *c) | ||
| 1348 | { | ||
| 1349 | for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); | ||
| 1350 | return c; | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | void | ||
| 1354 | pop(Client *c) | ||
| 1355 | { | ||
| 1356 | detach(c); | ||
| 1357 | attach(c); | ||
| 1358 | focus(c); | ||
| 1359 | arrange(c->mon); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | void | ||
| 1363 | propertynotify(XEvent *e) | ||
| 1364 | { | ||
| 1365 | Client *c; | ||
| 1366 | Window trans; | ||
| 1367 | XPropertyEvent *ev = &e->xproperty; | ||
| 1368 | |||
| 1369 | if ((ev->window == root) && (ev->atom == XA_WM_NAME)) | ||
| 1370 | updatestatus(); | ||
| 1371 | else if (ev->state == PropertyDelete) | ||
| 1372 | return; /* ignore */ | ||
| 1373 | else if ((c = wintoclient(ev->window))) { | ||
| 1374 | switch(ev->atom) { | ||
| 1375 | default: break; | ||
| 1376 | case XA_WM_TRANSIENT_FOR: | ||
| 1377 | if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && | ||
| 1378 | (c->isfloating = (wintoclient(trans)) != NULL)) | ||
| 1379 | arrange(c->mon); | ||
| 1380 | break; | ||
| 1381 | case XA_WM_NORMAL_HINTS: | ||
| 1382 | updatesizehints(c); | ||
| 1383 | break; | ||
| 1384 | case XA_WM_HINTS: | ||
| 1385 | updatewmhints(c); | ||
| 1386 | drawbars(); | ||
| 1387 | break; | ||
| 1388 | } | ||
| 1389 | if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { | ||
| 1390 | updatetitle(c); | ||
| 1391 | if (c == c->mon->sel) | ||
| 1392 | drawbar(c->mon); | ||
| 1393 | } | ||
| 1394 | if (ev->atom == netatom[NetWMWindowType]) | ||
| 1395 | updatewindowtype(c); | ||
| 1396 | } | ||
| 1397 | } | ||
| 1398 | |||
| 1399 | void | ||
| 1400 | quit(const Arg *arg) | ||
| 1401 | { | ||
| 1402 | running = 0; | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | Monitor * | ||
| 1406 | recttomon(int x, int y, int w, int h) | ||
| 1407 | { | ||
| 1408 | Monitor *m, *r = selmon; | ||
| 1409 | int a, area = 0; | ||
| 1410 | |||
| 1411 | for (m = mons; m; m = m->next) | ||
| 1412 | if ((a = INTERSECT(x, y, w, h, m)) > area) { | ||
| 1413 | area = a; | ||
| 1414 | r = m; | ||
| 1415 | } | ||
| 1416 | return r; | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | void | ||
| 1420 | resize(Client *c, int x, int y, int w, int h, int interact) | ||
| 1421 | { | ||
| 1422 | if (applysizehints(c, &x, &y, &w, &h, interact)) | ||
| 1423 | resizeclient(c, x, y, w, h); | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | void | ||
| 1427 | resizeclient(Client *c, int x, int y, int w, int h) | ||
| 1428 | { | ||
| 1429 | XWindowChanges wc; | ||
| 1430 | |||
| 1431 | c->oldx = c->x; c->x = wc.x = x; | ||
| 1432 | c->oldy = c->y; c->y = wc.y = y; | ||
| 1433 | c->oldw = c->w; c->w = wc.width = w; | ||
| 1434 | c->oldh = c->h; c->h = wc.height = h; | ||
| 1435 | wc.border_width = c->bw; | ||
| 1436 | XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); | ||
| 1437 | configure(c); | ||
| 1438 | XSync(dpy, False); | ||
| 1439 | } | ||
| 1440 | |||
| 1441 | void | ||
| 1442 | resizemouse(const Arg *arg) | ||
| 1443 | { | ||
| 1444 | int ocx, ocy, nw, nh; | ||
| 1445 | Client *c; | ||
| 1446 | Monitor *m; | ||
| 1447 | XEvent ev; | ||
| 1448 | Time lasttime = 0; | ||
| 1449 | |||
| 1450 | if (!(c = selmon->sel)) | ||
| 1451 | return; | ||
| 1452 | if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ | ||
| 1453 | return; | ||
| 1454 | restack(selmon); | ||
| 1455 | ocx = c->x; | ||
| 1456 | ocy = c->y; | ||
| 1457 | if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, | ||
| 1458 | None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) | ||
| 1459 | return; | ||
| 1460 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); | ||
| 1461 | do { | ||
| 1462 | XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); | ||
| 1463 | switch(ev.type) { | ||
| 1464 | case ConfigureRequest: | ||
| 1465 | case Expose: | ||
| 1466 | case MapRequest: | ||
| 1467 | handler[ev.type](&ev); | ||
| 1468 | break; | ||
| 1469 | case MotionNotify: | ||
| 1470 | if ((ev.xmotion.time - lasttime) <= (1000 / 60)) | ||
| 1471 | continue; | ||
| 1472 | lasttime = ev.xmotion.time; | ||
| 1473 | |||
| 1474 | nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); | ||
| 1475 | nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); | ||
| 1476 | if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww | ||
| 1477 | && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) | ||
| 1478 | { | ||
| 1479 | if (!c->isfloating && selmon->lt[selmon->sellt]->arrange | ||
| 1480 | && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) | ||
| 1481 | togglefloating(NULL); | ||
| 1482 | } | ||
| 1483 | if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) | ||
| 1484 | resize(c, c->x, c->y, nw, nh, 1); | ||
| 1485 | break; | ||
| 1486 | } | ||
| 1487 | } while (ev.type != ButtonRelease); | ||
| 1488 | XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); | ||
| 1489 | XUngrabPointer(dpy, CurrentTime); | ||
| 1490 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); | ||
| 1491 | if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { | ||
| 1492 | sendmon(c, m); | ||
| 1493 | selmon = m; | ||
| 1494 | focus(NULL); | ||
| 1495 | } | ||
| 1496 | } | ||
| 1497 | |||
| 1498 | void | ||
| 1499 | restack(Monitor *m) | ||
| 1500 | { | ||
| 1501 | Client *c; | ||
| 1502 | XEvent ev; | ||
| 1503 | XWindowChanges wc; | ||
| 1504 | |||
| 1505 | drawbar(m); | ||
| 1506 | if (!m->sel) | ||
| 1507 | return; | ||
| 1508 | if (m->sel->isfloating || !m->lt[m->sellt]->arrange) | ||
| 1509 | XRaiseWindow(dpy, m->sel->win); | ||
| 1510 | if (m->lt[m->sellt]->arrange) { | ||
| 1511 | wc.stack_mode = Below; | ||
| 1512 | wc.sibling = m->barwin; | ||
| 1513 | for (c = m->stack; c; c = c->snext) | ||
| 1514 | if (!c->isfloating && ISVISIBLE(c)) { | ||
| 1515 | XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); | ||
| 1516 | wc.sibling = c->win; | ||
| 1517 | } | ||
| 1518 | } | ||
| 1519 | XSync(dpy, False); | ||
| 1520 | while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); | ||
| 1521 | } | ||
| 1522 | |||
| 1523 | void | ||
| 1524 | run(void) | ||
| 1525 | { | ||
| 1526 | XEvent ev; | ||
| 1527 | /* main event loop */ | ||
| 1528 | XSync(dpy, False); | ||
| 1529 | while (running && !XNextEvent(dpy, &ev)) | ||
| 1530 | if (handler[ev.type]) | ||
| 1531 | handler[ev.type](&ev); /* call handler */ | ||
| 1532 | } | ||
| 1533 | |||
| 1534 | void | ||
| 1535 | scan(void) | ||
| 1536 | { | ||
| 1537 | unsigned int i, num; | ||
| 1538 | Window d1, d2, *wins = NULL; | ||
| 1539 | XWindowAttributes wa; | ||
| 1540 | |||
| 1541 | if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { | ||
| 1542 | for (i = 0; i < num; i++) { | ||
| 1543 | if (!XGetWindowAttributes(dpy, wins[i], &wa) | ||
| 1544 | || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) | ||
| 1545 | continue; | ||
| 1546 | if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) | ||
| 1547 | manage(wins[i], &wa); | ||
| 1548 | } | ||
| 1549 | for (i = 0; i < num; i++) { /* now the transients */ | ||
| 1550 | if (!XGetWindowAttributes(dpy, wins[i], &wa)) | ||
| 1551 | continue; | ||
| 1552 | if (XGetTransientForHint(dpy, wins[i], &d1) | ||
| 1553 | && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) | ||
| 1554 | manage(wins[i], &wa); | ||
| 1555 | } | ||
| 1556 | if (wins) | ||
| 1557 | XFree(wins); | ||
| 1558 | } | ||
| 1559 | } | ||
| 1560 | |||
| 1561 | void | ||
| 1562 | sendmon(Client *c, Monitor *m) | ||
| 1563 | { | ||
| 1564 | if (c->mon == m) | ||
| 1565 | return; | ||
| 1566 | unfocus(c, 1); | ||
| 1567 | detach(c); | ||
| 1568 | detachstack(c); | ||
| 1569 | c->mon = m; | ||
| 1570 | c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ | ||
| 1571 | attach(c); | ||
| 1572 | attachstack(c); | ||
| 1573 | focus(NULL); | ||
| 1574 | arrange(NULL); | ||
| 1575 | } | ||
| 1576 | |||
| 1577 | void | ||
| 1578 | setclientstate(Client *c, long state) | ||
| 1579 | { | ||
| 1580 | long data[] = { state, None }; | ||
| 1581 | |||
| 1582 | XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, | ||
| 1583 | PropModeReplace, (unsigned char *)data, 2); | ||
| 1584 | } | ||
| 1585 | |||
| 1586 | int | ||
| 1587 | sendevent(Client *c, Atom proto) | ||
| 1588 | { | ||
| 1589 | int n; | ||
| 1590 | Atom *protocols; | ||
| 1591 | int exists = 0; | ||
| 1592 | XEvent ev; | ||
| 1593 | |||
| 1594 | if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { | ||
| 1595 | while (!exists && n--) | ||
| 1596 | exists = protocols[n] == proto; | ||
| 1597 | XFree(protocols); | ||
| 1598 | } | ||
| 1599 | if (exists) { | ||
| 1600 | ev.type = ClientMessage; | ||
| 1601 | ev.xclient.window = c->win; | ||
| 1602 | ev.xclient.message_type = wmatom[WMProtocols]; | ||
| 1603 | ev.xclient.format = 32; | ||
| 1604 | ev.xclient.data.l[0] = proto; | ||
| 1605 | ev.xclient.data.l[1] = CurrentTime; | ||
| 1606 | XSendEvent(dpy, c->win, False, NoEventMask, &ev); | ||
| 1607 | } | ||
| 1608 | return exists; | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | void | ||
| 1612 | setfocus(Client *c) | ||
| 1613 | { | ||
| 1614 | if (!c->neverfocus) { | ||
| 1615 | XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); | ||
| 1616 | XChangeProperty(dpy, root, netatom[NetActiveWindow], | ||
| 1617 | XA_WINDOW, 32, PropModeReplace, | ||
| 1618 | (unsigned char *) &(c->win), 1); | ||
| 1619 | } | ||
| 1620 | sendevent(c, wmatom[WMTakeFocus]); | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | void | ||
| 1624 | setfullscreen(Client *c, int fullscreen) | ||
| 1625 | { | ||
| 1626 | if (fullscreen && !c->isfullscreen) { | ||
| 1627 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, | ||
| 1628 | PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); | ||
| 1629 | c->isfullscreen = 1; | ||
| 1630 | c->oldstate = c->isfloating; | ||
| 1631 | c->oldbw = c->bw; | ||
| 1632 | c->bw = 0; | ||
| 1633 | c->isfloating = 1; | ||
| 1634 | resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); | ||
| 1635 | XRaiseWindow(dpy, c->win); | ||
| 1636 | } else if (!fullscreen && c->isfullscreen){ | ||
| 1637 | XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, | ||
| 1638 | PropModeReplace, (unsigned char*)0, 0); | ||
| 1639 | c->isfullscreen = 0; | ||
| 1640 | c->isfloating = c->oldstate; | ||
| 1641 | c->bw = c->oldbw; | ||
| 1642 | c->x = c->oldx; | ||
| 1643 | c->y = c->oldy; | ||
| 1644 | c->w = c->oldw; | ||
| 1645 | c->h = c->oldh; | ||
| 1646 | resizeclient(c, c->x, c->y, c->w, c->h); | ||
| 1647 | arrange(c->mon); | ||
| 1648 | } | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | void | ||
| 1652 | setlayout(const Arg *arg) | ||
| 1653 | { | ||
| 1654 | if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) | ||
| 1655 | selmon->sellt ^= 1; | ||
| 1656 | if (arg && arg->v) | ||
| 1657 | selmon->lt[selmon->sellt] = (Layout *)arg->v; | ||
| 1658 | strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); | ||
| 1659 | if (selmon->sel) | ||
| 1660 | arrange(selmon); | ||
| 1661 | else | ||
| 1662 | drawbar(selmon); | ||
| 1663 | } | ||
| 1664 | |||
| 1665 | /* arg > 1.0 will set mfact absolutely */ | ||
| 1666 | void | ||
| 1667 | setmfact(const Arg *arg) | ||
| 1668 | { | ||
| 1669 | float f; | ||
| 1670 | |||
| 1671 | if (!arg || !selmon->lt[selmon->sellt]->arrange) | ||
| 1672 | return; | ||
| 1673 | f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; | ||
| 1674 | if (f < 0.1 || f > 0.9) | ||
| 1675 | return; | ||
| 1676 | selmon->mfact = f; | ||
| 1677 | arrange(selmon); | ||
| 1678 | } | ||
| 1679 | |||
| 1680 | void | ||
| 1681 | setup(void) | ||
| 1682 | { | ||
| 1683 | int i; | ||
| 1684 | XSetWindowAttributes wa; | ||
| 1685 | Atom utf8string; | ||
| 1686 | |||
| 1687 | /* clean up any zombies immediately */ | ||
| 1688 | sigchld(0); | ||
| 1689 | |||
| 1690 | /* init screen */ | ||
| 1691 | screen = DefaultScreen(dpy); | ||
| 1692 | sw = DisplayWidth(dpy, screen); | ||
| 1693 | sh = DisplayHeight(dpy, screen); | ||
| 1694 | root = RootWindow(dpy, screen); | ||
| 1695 | drw = drw_create(dpy, screen, root, sw, sh); | ||
| 1696 | if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) | ||
| 1697 | die("no fonts could be loaded."); | ||
| 1698 | lrpad = drw->fonts->h; | ||
| 1699 | bh = drw->fonts->h + 2; | ||
| 1700 | updategeom(); | ||
| 1701 | /* init atoms */ | ||
| 1702 | utf8string = XInternAtom(dpy, "UTF8_STRING", False); | ||
| 1703 | wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); | ||
| 1704 | wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); | ||
| 1705 | wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); | ||
| 1706 | wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); | ||
| 1707 | netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); | ||
| 1708 | netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); | ||
| 1709 | netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); | ||
| 1710 | netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); | ||
| 1711 | netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); | ||
| 1712 | netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); | ||
| 1713 | netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); | ||
| 1714 | netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); | ||
| 1715 | netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); | ||
| 1716 | /* init cursors */ | ||
| 1717 | cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); | ||
| 1718 | cursor[CurResize] = drw_cur_create(drw, XC_sizing); | ||
| 1719 | cursor[CurMove] = drw_cur_create(drw, XC_fleur); | ||
| 1720 | /* init appearance */ | ||
| 1721 | scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); | ||
| 1722 | for (i = 0; i < LENGTH(colors); i++) | ||
| 1723 | scheme[i] = drw_scm_create(drw, colors[i], 3); | ||
| 1724 | /* init bars */ | ||
| 1725 | updatebars(); | ||
| 1726 | updatestatus(); | ||
| 1727 | /* supporting window for NetWMCheck */ | ||
| 1728 | wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); | ||
| 1729 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, | ||
| 1730 | PropModeReplace, (unsigned char *) &wmcheckwin, 1); | ||
| 1731 | XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, | ||
| 1732 | PropModeReplace, (unsigned char *) "dwm", 3); | ||
| 1733 | XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, | ||
| 1734 | PropModeReplace, (unsigned char *) &wmcheckwin, 1); | ||
| 1735 | /* EWMH support per view */ | ||
| 1736 | XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, | ||
| 1737 | PropModeReplace, (unsigned char *) netatom, NetLast); | ||
| 1738 | XDeleteProperty(dpy, root, netatom[NetClientList]); | ||
| 1739 | /* select events */ | ||
| 1740 | wa.cursor = cursor[CurNormal]->cursor; | ||
| 1741 | wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask | ||
| 1742 | |ButtonPressMask|PointerMotionMask|EnterWindowMask | ||
| 1743 | |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; | ||
| 1744 | XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); | ||
| 1745 | XSelectInput(dpy, root, wa.event_mask); | ||
| 1746 | grabkeys(); | ||
| 1747 | focus(NULL); | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | |||
| 1751 | void | ||
| 1752 | seturgent(Client *c, int urg) | ||
| 1753 | { | ||
| 1754 | XWMHints *wmh; | ||
| 1755 | |||
| 1756 | c->isurgent = urg; | ||
| 1757 | if (!(wmh = XGetWMHints(dpy, c->win))) | ||
| 1758 | return; | ||
| 1759 | wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); | ||
| 1760 | XSetWMHints(dpy, c->win, wmh); | ||
| 1761 | XFree(wmh); | ||
| 1762 | } | ||
| 1763 | |||
| 1764 | void | ||
| 1765 | showhide(Client *c) | ||
| 1766 | { | ||
| 1767 | if (!c) | ||
| 1768 | return; | ||
| 1769 | if (ISVISIBLE(c)) { | ||
| 1770 | /* show clients top down */ | ||
| 1771 | XMoveWindow(dpy, c->win, c->x, c->y); | ||
| 1772 | if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) | ||
| 1773 | resize(c, c->x, c->y, c->w, c->h, 0); | ||
| 1774 | showhide(c->snext); | ||
| 1775 | } else { | ||
| 1776 | /* hide clients bottom up */ | ||
| 1777 | showhide(c->snext); | ||
| 1778 | XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); | ||
| 1779 | } | ||
| 1780 | } | ||
| 1781 | |||
| 1782 | void | ||
| 1783 | sigchld(int unused) | ||
| 1784 | { | ||
| 1785 | if (signal(SIGCHLD, sigchld) == SIG_ERR) | ||
| 1786 | die("can't install SIGCHLD handler:"); | ||
| 1787 | while (0 < waitpid(-1, NULL, WNOHANG)); | ||
| 1788 | } | ||
| 1789 | |||
| 1790 | void | ||
| 1791 | sigstatusbar(const Arg *arg) | ||
| 1792 | { | ||
| 1793 | union sigval sv; | ||
| 1794 | |||
| 1795 | if (!statussig) | ||
| 1796 | return; | ||
| 1797 | sv.sival_int = arg->i; | ||
| 1798 | if ((statuspid = getstatusbarpid()) <= 0) | ||
| 1799 | return; | ||
| 1800 | |||
| 1801 | sigqueue(statuspid, SIGRTMIN+statussig, sv); | ||
| 1802 | } | ||
| 1803 | |||
| 1804 | void | ||
| 1805 | spawn(const Arg *arg) | ||
| 1806 | { | ||
| 1807 | if (fork() == 0) { | ||
| 1808 | if (dpy) | ||
| 1809 | close(ConnectionNumber(dpy)); | ||
| 1810 | setsid(); | ||
| 1811 | execvp(((char **)arg->v)[0], (char **)arg->v); | ||
| 1812 | fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); | ||
| 1813 | perror(" failed"); | ||
| 1814 | exit(EXIT_SUCCESS); | ||
| 1815 | } | ||
| 1816 | } | ||
| 1817 | |||
| 1818 | void | ||
| 1819 | tag(const Arg *arg) | ||
| 1820 | { | ||
| 1821 | if (selmon->sel && arg->ui & TAGMASK) { | ||
| 1822 | selmon->sel->tags = arg->ui & TAGMASK; | ||
| 1823 | focus(NULL); | ||
| 1824 | arrange(selmon); | ||
| 1825 | } | ||
| 1826 | } | ||
| 1827 | |||
| 1828 | void | ||
| 1829 | tagmon(const Arg *arg) | ||
| 1830 | { | ||
| 1831 | if (!selmon->sel || !mons->next) | ||
| 1832 | return; | ||
| 1833 | sendmon(selmon->sel, dirtomon(arg->i)); | ||
| 1834 | } | ||
| 1835 | |||
| 1836 | void | ||
| 1837 | togglebar(const Arg *arg) | ||
| 1838 | { | ||
| 1839 | selmon->showbar = !selmon->showbar; | ||
| 1840 | updatebarpos(selmon); | ||
| 1841 | XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); | ||
| 1842 | arrange(selmon); | ||
| 1843 | } | ||
| 1844 | |||
| 1845 | void | ||
| 1846 | togglefloating(const Arg *arg) | ||
| 1847 | { | ||
| 1848 | if (!selmon->sel) | ||
| 1849 | return; | ||
| 1850 | if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ | ||
| 1851 | return; | ||
| 1852 | selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; | ||
| 1853 | if (selmon->sel->isfloating) | ||
| 1854 | resize(selmon->sel, selmon->sel->x, selmon->sel->y, | ||
| 1855 | selmon->sel->w, selmon->sel->h, 0); | ||
| 1856 | arrange(selmon); | ||
| 1857 | } | ||
| 1858 | |||
| 1859 | void | ||
| 1860 | toggletag(const Arg *arg) | ||
| 1861 | { | ||
| 1862 | unsigned int newtags; | ||
| 1863 | |||
| 1864 | if (!selmon->sel) | ||
| 1865 | return; | ||
| 1866 | newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); | ||
| 1867 | if (newtags) { | ||
| 1868 | selmon->sel->tags = newtags; | ||
| 1869 | focus(NULL); | ||
| 1870 | arrange(selmon); | ||
| 1871 | } | ||
| 1872 | } | ||
| 1873 | |||
| 1874 | void | ||
| 1875 | toggleview(const Arg *arg) | ||
| 1876 | { | ||
| 1877 | unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); | ||
| 1878 | |||
| 1879 | if (newtagset) { | ||
| 1880 | selmon->tagset[selmon->seltags] = newtagset; | ||
| 1881 | focus(NULL); | ||
| 1882 | arrange(selmon); | ||
| 1883 | } | ||
| 1884 | } | ||
| 1885 | |||
| 1886 | void | ||
| 1887 | unfocus(Client *c, int setfocus) | ||
| 1888 | { | ||
| 1889 | if (!c) | ||
| 1890 | return; | ||
| 1891 | grabbuttons(c, 0); | ||
| 1892 | XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); | ||
| 1893 | if (setfocus) { | ||
| 1894 | XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); | ||
| 1895 | XDeleteProperty(dpy, root, netatom[NetActiveWindow]); | ||
| 1896 | } | ||
| 1897 | } | ||
| 1898 | |||
| 1899 | void | ||
| 1900 | unmanage(Client *c, int destroyed) | ||
| 1901 | { | ||
| 1902 | Monitor *m = c->mon; | ||
| 1903 | XWindowChanges wc; | ||
| 1904 | |||
| 1905 | if (c->swallowing) { | ||
| 1906 | unswallow(c); | ||
| 1907 | return; | ||
| 1908 | } | ||
| 1909 | |||
| 1910 | Client *s = swallowingclient(c->win); | ||
| 1911 | if (s) { | ||
| 1912 | free(s->swallowing); | ||
| 1913 | s->swallowing = NULL; | ||
| 1914 | arrange(m); | ||
| 1915 | focus(NULL); | ||
| 1916 | return; | ||
| 1917 | } | ||
| 1918 | |||
| 1919 | detach(c); | ||
| 1920 | detachstack(c); | ||
| 1921 | if (!destroyed) { | ||
| 1922 | wc.border_width = c->oldbw; | ||
| 1923 | XGrabServer(dpy); /* avoid race conditions */ | ||
| 1924 | XSetErrorHandler(xerrordummy); | ||
| 1925 | XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ | ||
| 1926 | XUngrabButton(dpy, AnyButton, AnyModifier, c->win); | ||
| 1927 | setclientstate(c, WithdrawnState); | ||
| 1928 | XSync(dpy, False); | ||
| 1929 | XSetErrorHandler(xerror); | ||
| 1930 | XUngrabServer(dpy); | ||
| 1931 | } | ||
| 1932 | free(c); | ||
| 1933 | |||
| 1934 | if (!s) { | ||
| 1935 | arrange(m); | ||
| 1936 | focus(NULL); | ||
| 1937 | updateclientlist(); | ||
| 1938 | } | ||
| 1939 | } | ||
| 1940 | |||
| 1941 | void | ||
| 1942 | unmapnotify(XEvent *e) | ||
| 1943 | { | ||
| 1944 | Client *c; | ||
| 1945 | XUnmapEvent *ev = &e->xunmap; | ||
| 1946 | |||
| 1947 | if ((c = wintoclient(ev->window))) { | ||
| 1948 | if (ev->send_event) | ||
| 1949 | setclientstate(c, WithdrawnState); | ||
| 1950 | else | ||
| 1951 | unmanage(c, 0); | ||
| 1952 | } | ||
| 1953 | } | ||
| 1954 | |||
| 1955 | void | ||
| 1956 | updatebars(void) | ||
| 1957 | { | ||
| 1958 | Monitor *m; | ||
| 1959 | XSetWindowAttributes wa = { | ||
| 1960 | .override_redirect = True, | ||
| 1961 | .background_pixmap = ParentRelative, | ||
| 1962 | .event_mask = ButtonPressMask|ExposureMask | ||
| 1963 | }; | ||
| 1964 | XClassHint ch = {"dwm", "dwm"}; | ||
| 1965 | for (m = mons; m; m = m->next) { | ||
| 1966 | if (m->barwin) | ||
| 1967 | continue; | ||
| 1968 | m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), | ||
| 1969 | CopyFromParent, DefaultVisual(dpy, screen), | ||
| 1970 | CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); | ||
| 1971 | XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); | ||
| 1972 | XMapRaised(dpy, m->barwin); | ||
| 1973 | XSetClassHint(dpy, m->barwin, &ch); | ||
| 1974 | } | ||
| 1975 | } | ||
| 1976 | |||
| 1977 | void | ||
| 1978 | updatebarpos(Monitor *m) | ||
| 1979 | { | ||
| 1980 | m->wy = m->my; | ||
| 1981 | m->wh = m->mh; | ||
| 1982 | if (m->showbar) { | ||
| 1983 | m->wh -= bh; | ||
| 1984 | m->by = m->topbar ? m->wy : m->wy + m->wh; | ||
| 1985 | m->wy = m->topbar ? m->wy + bh : m->wy; | ||
| 1986 | } else | ||
| 1987 | m->by = -bh; | ||
| 1988 | } | ||
| 1989 | |||
| 1990 | void | ||
| 1991 | updateclientlist() | ||
| 1992 | { | ||
| 1993 | Client *c; | ||
| 1994 | Monitor *m; | ||
| 1995 | |||
| 1996 | XDeleteProperty(dpy, root, netatom[NetClientList]); | ||
| 1997 | for (m = mons; m; m = m->next) | ||
| 1998 | for (c = m->clients; c; c = c->next) | ||
| 1999 | XChangeProperty(dpy, root, netatom[NetClientList], | ||
| 2000 | XA_WINDOW, 32, PropModeAppend, | ||
| 2001 | (unsigned char *) &(c->win), 1); | ||
| 2002 | } | ||
| 2003 | |||
| 2004 | int | ||
| 2005 | updategeom(void) | ||
| 2006 | { | ||
| 2007 | int dirty = 0; | ||
| 2008 | |||
| 2009 | #ifdef XINERAMA | ||
| 2010 | if (XineramaIsActive(dpy)) { | ||
| 2011 | int i, j, n, nn; | ||
| 2012 | Client *c; | ||
| 2013 | Monitor *m; | ||
| 2014 | XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); | ||
| 2015 | XineramaScreenInfo *unique = NULL; | ||
| 2016 | |||
| 2017 | for (n = 0, m = mons; m; m = m->next, n++); | ||
| 2018 | /* only consider unique geometries as separate screens */ | ||
| 2019 | unique = ecalloc(nn, sizeof(XineramaScreenInfo)); | ||
| 2020 | for (i = 0, j = 0; i < nn; i++) | ||
| 2021 | if (isuniquegeom(unique, j, &info[i])) | ||
| 2022 | memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); | ||
| 2023 | XFree(info); | ||
| 2024 | nn = j; | ||
| 2025 | if (n <= nn) { /* new monitors available */ | ||
| 2026 | for (i = 0; i < (nn - n); i++) { | ||
| 2027 | for (m = mons; m && m->next; m = m->next); | ||
| 2028 | if (m) | ||
| 2029 | m->next = createmon(); | ||
| 2030 | else | ||
| 2031 | mons = createmon(); | ||
| 2032 | } | ||
| 2033 | for (i = 0, m = mons; i < nn && m; m = m->next, i++) | ||
| 2034 | if (i >= n | ||
| 2035 | || unique[i].x_org != m->mx || unique[i].y_org != m->my | ||
| 2036 | || unique[i].width != m->mw || unique[i].height != m->mh) | ||
| 2037 | { | ||
| 2038 | dirty = 1; | ||
| 2039 | m->num = i; | ||
| 2040 | m->mx = m->wx = unique[i].x_org; | ||
| 2041 | m->my = m->wy = unique[i].y_org; | ||
| 2042 | m->mw = m->ww = unique[i].width; | ||
| 2043 | m->mh = m->wh = unique[i].height; | ||
| 2044 | updatebarpos(m); | ||
| 2045 | } | ||
| 2046 | } else { /* less monitors available nn < n */ | ||
| 2047 | for (i = nn; i < n; i++) { | ||
| 2048 | for (m = mons; m && m->next; m = m->next); | ||
| 2049 | while ((c = m->clients)) { | ||
| 2050 | dirty = 1; | ||
| 2051 | m->clients = c->next; | ||
| 2052 | detachstack(c); | ||
| 2053 | c->mon = mons; | ||
| 2054 | attach(c); | ||
| 2055 | attachstack(c); | ||
| 2056 | } | ||
| 2057 | if (m == selmon) | ||
| 2058 | selmon = mons; | ||
| 2059 | cleanupmon(m); | ||
| 2060 | } | ||
| 2061 | } | ||
| 2062 | free(unique); | ||
| 2063 | } else | ||
| 2064 | #endif /* XINERAMA */ | ||
| 2065 | { /* default monitor setup */ | ||
| 2066 | if (!mons) | ||
| 2067 | mons = createmon(); | ||
| 2068 | if (mons->mw != sw || mons->mh != sh) { | ||
| 2069 | dirty = 1; | ||
| 2070 | mons->mw = mons->ww = sw; | ||
| 2071 | mons->mh = mons->wh = sh; | ||
| 2072 | updatebarpos(mons); | ||
| 2073 | } | ||
| 2074 | } | ||
| 2075 | if (dirty) { | ||
| 2076 | selmon = mons; | ||
| 2077 | selmon = wintomon(root); | ||
| 2078 | } | ||
| 2079 | return dirty; | ||
| 2080 | } | ||
| 2081 | |||
| 2082 | void | ||
| 2083 | updatenumlockmask(void) | ||
| 2084 | { | ||
| 2085 | unsigned int i, j; | ||
| 2086 | XModifierKeymap *modmap; | ||
| 2087 | |||
| 2088 | numlockmask = 0; | ||
| 2089 | modmap = XGetModifierMapping(dpy); | ||
| 2090 | for (i = 0; i < 8; i++) | ||
| 2091 | for (j = 0; j < modmap->max_keypermod; j++) | ||
| 2092 | if (modmap->modifiermap[i * modmap->max_keypermod + j] | ||
| 2093 | == XKeysymToKeycode(dpy, XK_Num_Lock)) | ||
| 2094 | numlockmask = (1 << i); | ||
| 2095 | XFreeModifiermap(modmap); | ||
| 2096 | } | ||
| 2097 | |||
| 2098 | void | ||
| 2099 | updatesizehints(Client *c) | ||
| 2100 | { | ||
| 2101 | long msize; | ||
| 2102 | XSizeHints size; | ||
| 2103 | |||
| 2104 | if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) | ||
| 2105 | /* size is uninitialized, ensure that size.flags aren't used */ | ||
| 2106 | size.flags = PSize; | ||
| 2107 | if (size.flags & PBaseSize) { | ||
| 2108 | c->basew = size.base_width; | ||
| 2109 | c->baseh = size.base_height; | ||
| 2110 | } else if (size.flags & PMinSize) { | ||
| 2111 | c->basew = size.min_width; | ||
| 2112 | c->baseh = size.min_height; | ||
| 2113 | } else | ||
| 2114 | c->basew = c->baseh = 0; | ||
| 2115 | if (size.flags & PResizeInc) { | ||
| 2116 | c->incw = size.width_inc; | ||
| 2117 | c->inch = size.height_inc; | ||
| 2118 | } else | ||
| 2119 | c->incw = c->inch = 0; | ||
| 2120 | if (size.flags & PMaxSize) { | ||
| 2121 | c->maxw = size.max_width; | ||
| 2122 | c->maxh = size.max_height; | ||
| 2123 | } else | ||
| 2124 | c->maxw = c->maxh = 0; | ||
| 2125 | if (size.flags & PMinSize) { | ||
| 2126 | c->minw = size.min_width; | ||
| 2127 | c->minh = size.min_height; | ||
| 2128 | } else if (size.flags & PBaseSize) { | ||
| 2129 | c->minw = size.base_width; | ||
| 2130 | c->minh = size.base_height; | ||
| 2131 | } else | ||
| 2132 | c->minw = c->minh = 0; | ||
| 2133 | if (size.flags & PAspect) { | ||
| 2134 | c->mina = (float)size.min_aspect.y / size.min_aspect.x; | ||
| 2135 | c->maxa = (float)size.max_aspect.x / size.max_aspect.y; | ||
| 2136 | } else | ||
| 2137 | c->maxa = c->mina = 0.0; | ||
| 2138 | c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); | ||
| 2139 | } | ||
| 2140 | |||
| 2141 | void | ||
| 2142 | updatestatus(void) | ||
| 2143 | { | ||
| 2144 | if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) { | ||
| 2145 | strcpy(stext, "dwm-"VERSION); | ||
| 2146 | statusw = TEXTW(stext) - lrpad + 2; | ||
| 2147 | } else { | ||
| 2148 | char *text, *s, ch; | ||
| 2149 | |||
| 2150 | statusw = 0; | ||
| 2151 | for (text = s = stext; *s; s++) { | ||
| 2152 | if ((unsigned char)(*s) < ' ') { | ||
| 2153 | ch = *s; | ||
| 2154 | *s = '\0'; | ||
| 2155 | statusw += TEXTW(text) - lrpad; | ||
| 2156 | *s = ch; | ||
| 2157 | text = s + 1; | ||
| 2158 | } | ||
| 2159 | } | ||
| 2160 | statusw += TEXTW(text) - lrpad + 2; | ||
| 2161 | |||
| 2162 | } | ||
| 2163 | drawbar(selmon); | ||
| 2164 | } | ||
| 2165 | |||
| 2166 | void | ||
| 2167 | updatetitle(Client *c) | ||
| 2168 | { | ||
| 2169 | if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) | ||
| 2170 | gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); | ||
| 2171 | if (c->name[0] == '\0') /* hack to mark broken clients */ | ||
| 2172 | strcpy(c->name, broken); | ||
| 2173 | } | ||
| 2174 | |||
| 2175 | void | ||
| 2176 | updatewindowtype(Client *c) | ||
| 2177 | { | ||
| 2178 | Atom state = getatomprop(c, netatom[NetWMState]); | ||
| 2179 | Atom wtype = getatomprop(c, netatom[NetWMWindowType]); | ||
| 2180 | |||
| 2181 | if (state == netatom[NetWMFullscreen]) | ||
| 2182 | setfullscreen(c, 1); | ||
| 2183 | if (wtype == netatom[NetWMWindowTypeDialog]) | ||
| 2184 | c->isfloating = 1; | ||
| 2185 | } | ||
| 2186 | |||
| 2187 | void | ||
| 2188 | updatewmhints(Client *c) | ||
| 2189 | { | ||
| 2190 | XWMHints *wmh; | ||
| 2191 | |||
| 2192 | if ((wmh = XGetWMHints(dpy, c->win))) { | ||
| 2193 | if (c == selmon->sel && wmh->flags & XUrgencyHint) { | ||
| 2194 | wmh->flags &= ~XUrgencyHint; | ||
| 2195 | XSetWMHints(dpy, c->win, wmh); | ||
| 2196 | } else | ||
| 2197 | c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; | ||
| 2198 | if (wmh->flags & InputHint) | ||
| 2199 | c->neverfocus = !wmh->input; | ||
| 2200 | else | ||
| 2201 | c->neverfocus = 0; | ||
| 2202 | XFree(wmh); | ||
| 2203 | } | ||
| 2204 | } | ||
| 2205 | |||
| 2206 | void | ||
| 2207 | view(const Arg *arg) | ||
| 2208 | { | ||
| 2209 | if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) | ||
| 2210 | return; | ||
| 2211 | selmon->seltags ^= 1; /* toggle sel tagset */ | ||
| 2212 | if (arg->ui & TAGMASK) | ||
| 2213 | selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; | ||
| 2214 | focus(NULL); | ||
| 2215 | arrange(selmon); | ||
| 2216 | } | ||
| 2217 | |||
| 2218 | pid_t | ||
| 2219 | winpid(Window w) | ||
| 2220 | { | ||
| 2221 | |||
| 2222 | pid_t result = 0; | ||
| 2223 | |||
| 2224 | #ifdef __linux__ | ||
| 2225 | xcb_res_client_id_spec_t spec = {0}; | ||
| 2226 | spec.client = w; | ||
| 2227 | spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; | ||
| 2228 | |||
| 2229 | xcb_generic_error_t *e = NULL; | ||
| 2230 | xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); | ||
| 2231 | xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); | ||
| 2232 | |||
| 2233 | if (!r) | ||
| 2234 | return (pid_t)0; | ||
| 2235 | |||
| 2236 | xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); | ||
| 2237 | for (; i.rem; xcb_res_client_id_value_next(&i)) { | ||
| 2238 | spec = i.data->spec; | ||
| 2239 | if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { | ||
| 2240 | uint32_t *t = xcb_res_client_id_value_value(i.data); | ||
| 2241 | result = *t; | ||
| 2242 | break; | ||
| 2243 | } | ||
| 2244 | } | ||
| 2245 | |||
| 2246 | free(r); | ||
| 2247 | |||
| 2248 | if (result == (pid_t)-1) | ||
| 2249 | result = 0; | ||
| 2250 | |||
| 2251 | #endif /* __linux__ */ | ||
| 2252 | |||
| 2253 | #ifdef __OpenBSD__ | ||
| 2254 | Atom type; | ||
| 2255 | int format; | ||
| 2256 | unsigned long len, bytes; | ||
| 2257 | unsigned char *prop; | ||
| 2258 | pid_t ret; | ||
| 2259 | |||
| 2260 | if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop) | ||
| 2261 | return 0; | ||
| 2262 | |||
| 2263 | ret = *(pid_t*)prop; | ||
| 2264 | XFree(prop); | ||
| 2265 | result = ret; | ||
| 2266 | |||
| 2267 | #endif /* __OpenBSD__ */ | ||
| 2268 | return result; | ||
| 2269 | } | ||
| 2270 | |||
| 2271 | pid_t | ||
| 2272 | getparentprocess(pid_t p) | ||
| 2273 | { | ||
| 2274 | unsigned int v = 0; | ||
| 2275 | |||
| 2276 | #ifdef __linux__ | ||
| 2277 | FILE *f; | ||
| 2278 | char buf[256]; | ||
| 2279 | snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); | ||
| 2280 | |||
| 2281 | if (!(f = fopen(buf, "r"))) | ||
| 2282 | return 0; | ||
| 2283 | |||
| 2284 | fscanf(f, "%*u %*s %*c %u", &v); | ||
| 2285 | fclose(f); | ||
| 2286 | #endif /* __linux__*/ | ||
| 2287 | |||
| 2288 | #ifdef __OpenBSD__ | ||
| 2289 | int n; | ||
| 2290 | kvm_t *kd; | ||
| 2291 | struct kinfo_proc *kp; | ||
| 2292 | |||
| 2293 | kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); | ||
| 2294 | if (!kd) | ||
| 2295 | return 0; | ||
| 2296 | |||
| 2297 | kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); | ||
| 2298 | v = kp->p_ppid; | ||
| 2299 | #endif /* __OpenBSD__ */ | ||
| 2300 | |||
| 2301 | return (pid_t)v; | ||
| 2302 | } | ||
| 2303 | |||
| 2304 | int | ||
| 2305 | isdescprocess(pid_t p, pid_t c) | ||
| 2306 | { | ||
| 2307 | while (p != c && c != 0) | ||
| 2308 | c = getparentprocess(c); | ||
| 2309 | |||
| 2310 | return (int)c; | ||
| 2311 | } | ||
| 2312 | |||
| 2313 | Client * | ||
| 2314 | termforwin(const Client *w) | ||
| 2315 | { | ||
| 2316 | Client *c; | ||
| 2317 | Monitor *m; | ||
| 2318 | |||
| 2319 | if (!w->pid || w->isterminal) | ||
| 2320 | return NULL; | ||
| 2321 | |||
| 2322 | for (m = mons; m; m = m->next) { | ||
| 2323 | for (c = m->clients; c; c = c->next) { | ||
| 2324 | if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) | ||
| 2325 | return c; | ||
| 2326 | } | ||
| 2327 | } | ||
| 2328 | |||
| 2329 | return NULL; | ||
| 2330 | } | ||
| 2331 | |||
| 2332 | Client * | ||
| 2333 | swallowingclient(Window w) | ||
| 2334 | { | ||
| 2335 | Client *c; | ||
| 2336 | Monitor *m; | ||
| 2337 | |||
| 2338 | for (m = mons; m; m = m->next) { | ||
| 2339 | for (c = m->clients; c; c = c->next) { | ||
| 2340 | if (c->swallowing && c->swallowing->win == w) | ||
| 2341 | return c; | ||
| 2342 | } | ||
| 2343 | } | ||
| 2344 | |||
| 2345 | return NULL; | ||
| 2346 | } | ||
| 2347 | |||
| 2348 | Client * | ||
| 2349 | wintoclient(Window w) | ||
| 2350 | { | ||
| 2351 | Client *c; | ||
| 2352 | Monitor *m; | ||
| 2353 | |||
| 2354 | for (m = mons; m; m = m->next) | ||
| 2355 | for (c = m->clients; c; c = c->next) | ||
| 2356 | if (c->win == w) | ||
| 2357 | return c; | ||
| 2358 | return NULL; | ||
| 2359 | } | ||
| 2360 | |||
| 2361 | Monitor * | ||
| 2362 | wintomon(Window w) | ||
| 2363 | { | ||
| 2364 | int x, y; | ||
| 2365 | Client *c; | ||
| 2366 | Monitor *m; | ||
| 2367 | |||
| 2368 | if (w == root && getrootptr(&x, &y)) | ||
| 2369 | return recttomon(x, y, 1, 1); | ||
| 2370 | for (m = mons; m; m = m->next) | ||
| 2371 | if (w == m->barwin) | ||
| 2372 | return m; | ||
| 2373 | if ((c = wintoclient(w))) | ||
| 2374 | return c->mon; | ||
| 2375 | return selmon; | ||
| 2376 | } | ||
| 2377 | |||
| 2378 | /* There's no way to check accesses to destroyed windows, thus those cases are | ||
| 2379 | * ignored (especially on UnmapNotify's). Other types of errors call Xlibs | ||
| 2380 | * default error handler, which may call exit. */ | ||
| 2381 | int | ||
| 2382 | xerror(Display *dpy, XErrorEvent *ee) | ||
| 2383 | { | ||
| 2384 | if (ee->error_code == BadWindow | ||
| 2385 | || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) | ||
| 2386 | || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) | ||
| 2387 | || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) | ||
| 2388 | || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) | ||
| 2389 | || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) | ||
| 2390 | || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) | ||
| 2391 | || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) | ||
| 2392 | || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) | ||
| 2393 | return 0; | ||
| 2394 | fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", | ||
| 2395 | ee->request_code, ee->error_code); | ||
| 2396 | return xerrorxlib(dpy, ee); /* may call exit */ | ||
| 2397 | } | ||
| 2398 | |||
| 2399 | int | ||
| 2400 | xerrordummy(Display *dpy, XErrorEvent *ee) | ||
| 2401 | { | ||
| 2402 | return 0; | ||
| 2403 | } | ||
| 2404 | |||
| 2405 | /* Startup Error handler to check if another window manager | ||
| 2406 | * is already running. */ | ||
| 2407 | int | ||
| 2408 | xerrorstart(Display *dpy, XErrorEvent *ee) | ||
| 2409 | { | ||
| 2410 | die("dwm: another window manager is already running"); | ||
| 2411 | return -1; | ||
| 2412 | } | ||
| 2413 | |||
| 2414 | void | ||
| 2415 | zoom(const Arg *arg) | ||
| 2416 | { | ||
| 2417 | Client *c = selmon->sel; | ||
| 2418 | |||
| 2419 | if (!selmon->lt[selmon->sellt]->arrange | ||
| 2420 | || (selmon->sel && selmon->sel->isfloating)) | ||
| 2421 | return; | ||
| 2422 | if (c == nexttiled(selmon->clients)) | ||
| 2423 | if (!c || !(c = nexttiled(c->next))) | ||
| 2424 | return; | ||
| 2425 | pop(c); | ||
| 2426 | } | ||
| 2427 | |||
| 2428 | int | ||
| 2429 | main(int argc, char *argv[]) | ||
| 2430 | { | ||
| 2431 | if (argc == 2 && !strcmp("-v", argv[1])) | ||
| 2432 | die("dwm-"VERSION); | ||
| 2433 | else if (argc != 1) | ||
| 2434 | die("usage: dwm [-v]"); | ||
| 2435 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) | ||
| 2436 | fputs("warning: no locale support\n", stderr); | ||
| 2437 | if (!(dpy = XOpenDisplay(NULL))) | ||
| 2438 | die("dwm: cannot open display"); | ||
| 2439 | if (!(xcon = XGetXCBConnection(dpy))) | ||
| 2440 | die("dwm: cannot get xcb connection\n"); | ||
| 2441 | checkotherwm(); | ||
| 2442 | setup(); | ||
| 2443 | #ifdef __OpenBSD__ | ||
| 2444 | if (pledge("stdio rpath proc exec ps", NULL) == -1) | ||
| 2445 | die("pledge"); | ||
| 2446 | #endif /* __OpenBSD__ */ | ||
| 2447 | scan(); | ||
| 2448 | run(); | ||
| 2449 | cleanup(); | ||
| 2450 | XCloseDisplay(dpy); | ||
| 2451 | return EXIT_SUCCESS; | ||
| 2452 | } | ||
diff --git a/patch-list b/patch-list new file mode 100644 index 0000000..6c4ac01 --- /dev/null +++ b/patch-list | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | hidevacant | ||
| 2 | statuscmd | ||
| 3 | steam | ||
| 4 | swallow | ||
| 5 | vanitygaps | ||
diff --git a/transient.c b/transient.c new file mode 100644 index 0000000..040adb5 --- /dev/null +++ b/transient.c | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | /* cc transient.c -o transient -lX11 */ | ||
| 2 | |||
| 3 | #include <stdlib.h> | ||
| 4 | #include <unistd.h> | ||
| 5 | #include <X11/Xlib.h> | ||
| 6 | #include <X11/Xutil.h> | ||
| 7 | |||
| 8 | int main(void) { | ||
| 9 | Display *d; | ||
| 10 | Window r, f, t = None; | ||
| 11 | XSizeHints h; | ||
| 12 | XEvent e; | ||
| 13 | |||
| 14 | d = XOpenDisplay(NULL); | ||
| 15 | if (!d) | ||
| 16 | exit(1); | ||
| 17 | r = DefaultRootWindow(d); | ||
| 18 | |||
| 19 | f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); | ||
| 20 | h.min_width = h.max_width = h.min_height = h.max_height = 400; | ||
| 21 | h.flags = PMinSize | PMaxSize; | ||
| 22 | XSetWMNormalHints(d, f, &h); | ||
| 23 | XStoreName(d, f, "floating"); | ||
| 24 | XMapWindow(d, f); | ||
| 25 | |||
| 26 | XSelectInput(d, f, ExposureMask); | ||
| 27 | while (1) { | ||
| 28 | XNextEvent(d, &e); | ||
| 29 | |||
| 30 | if (t == None) { | ||
| 31 | sleep(5); | ||
| 32 | t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); | ||
| 33 | XSetTransientForHint(d, t, f); | ||
| 34 | XStoreName(d, t, "transient"); | ||
| 35 | XMapWindow(d, t); | ||
| 36 | XSelectInput(d, t, ExposureMask); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | XCloseDisplay(d); | ||
| 41 | exit(0); | ||
| 42 | } | ||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | #include <stdarg.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include <stdlib.h> | ||
| 5 | #include <string.h> | ||
| 6 | |||
| 7 | #include "util.h" | ||
| 8 | |||
| 9 | void * | ||
| 10 | ecalloc(size_t nmemb, size_t size) | ||
| 11 | { | ||
| 12 | void *p; | ||
| 13 | |||
| 14 | if (!(p = calloc(nmemb, size))) | ||
| 15 | die("calloc:"); | ||
| 16 | return p; | ||
| 17 | } | ||
| 18 | |||
| 19 | void | ||
| 20 | die(const char *fmt, ...) { | ||
| 21 | va_list ap; | ||
| 22 | |||
| 23 | va_start(ap, fmt); | ||
| 24 | vfprintf(stderr, fmt, ap); | ||
| 25 | va_end(ap); | ||
| 26 | |||
| 27 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { | ||
| 28 | fputc(' ', stderr); | ||
| 29 | perror(NULL); | ||
| 30 | } else { | ||
| 31 | fputc('\n', stderr); | ||
| 32 | } | ||
| 33 | |||
| 34 | exit(1); | ||
| 35 | } | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | |||
| 3 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) | ||
| 4 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) | ||
| 5 | #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) | ||
| 6 | |||
| 7 | void die(const char *fmt, ...); | ||
| 8 | void *ecalloc(size_t nmemb, size_t size); | ||
diff --git a/vanitygaps.c b/vanitygaps.c new file mode 100644 index 0000000..c336ee9 --- /dev/null +++ b/vanitygaps.c | |||
| @@ -0,0 +1,809 @@ | |||
| 1 | /* Key binding functions */ | ||
| 2 | static void defaultgaps(const Arg *arg); | ||
| 3 | static void incrgaps(const Arg *arg); | ||
| 4 | static void incrigaps(const Arg *arg); | ||
| 5 | static void incrogaps(const Arg *arg); | ||
| 6 | static void incrohgaps(const Arg *arg); | ||
| 7 | static void incrovgaps(const Arg *arg); | ||
| 8 | static void incrihgaps(const Arg *arg); | ||
| 9 | static void incrivgaps(const Arg *arg); | ||
| 10 | static void togglegaps(const Arg *arg); | ||
| 11 | /* Layouts (delete the ones you do not need) */ | ||
| 12 | static void bstack(Monitor *m); | ||
| 13 | static void bstackhoriz(Monitor *m); | ||
| 14 | static void centeredmaster(Monitor *m); | ||
| 15 | static void centeredfloatingmaster(Monitor *m); | ||
| 16 | static void deck(Monitor *m); | ||
| 17 | static void dwindle(Monitor *m); | ||
| 18 | static void fibonacci(Monitor *m, int s); | ||
| 19 | static void grid(Monitor *m); | ||
| 20 | static void nrowgrid(Monitor *m); | ||
| 21 | static void spiral(Monitor *m); | ||
| 22 | static void tile(Monitor *m); | ||
| 23 | /* Internals */ | ||
| 24 | static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc); | ||
| 25 | static void getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr); | ||
| 26 | static void setgaps(int oh, int ov, int ih, int iv); | ||
| 27 | |||
| 28 | /* Settings */ | ||
| 29 | #if !PERTAG_PATCH | ||
| 30 | static int enablegaps = 1; | ||
| 31 | #endif // PERTAG_PATCH | ||
| 32 | |||
| 33 | void | ||
| 34 | setgaps(int oh, int ov, int ih, int iv) | ||
| 35 | { | ||
| 36 | if (oh < 0) oh = 0; | ||
| 37 | if (ov < 0) ov = 0; | ||
| 38 | if (ih < 0) ih = 0; | ||
| 39 | if (iv < 0) iv = 0; | ||
| 40 | |||
| 41 | selmon->gappoh = oh; | ||
| 42 | selmon->gappov = ov; | ||
| 43 | selmon->gappih = ih; | ||
| 44 | selmon->gappiv = iv; | ||
| 45 | arrange(selmon); | ||
| 46 | } | ||
| 47 | |||
| 48 | void | ||
| 49 | togglegaps(const Arg *arg) | ||
| 50 | { | ||
| 51 | #if PERTAG_PATCH | ||
| 52 | selmon->pertag->enablegaps[selmon->pertag->curtag] = !selmon->pertag->enablegaps[selmon->pertag->curtag]; | ||
| 53 | #else | ||
| 54 | enablegaps = !enablegaps; | ||
| 55 | #endif // PERTAG_PATCH | ||
| 56 | arrange(NULL); | ||
| 57 | } | ||
| 58 | |||
| 59 | void | ||
| 60 | defaultgaps(const Arg *arg) | ||
| 61 | { | ||
| 62 | setgaps(gappoh, gappov, gappih, gappiv); | ||
| 63 | } | ||
| 64 | |||
| 65 | void | ||
| 66 | incrgaps(const Arg *arg) | ||
| 67 | { | ||
| 68 | setgaps( | ||
| 69 | selmon->gappoh + arg->i, | ||
| 70 | selmon->gappov + arg->i, | ||
| 71 | selmon->gappih + arg->i, | ||
| 72 | selmon->gappiv + arg->i | ||
| 73 | ); | ||
| 74 | } | ||
| 75 | |||
| 76 | void | ||
| 77 | incrigaps(const Arg *arg) | ||
| 78 | { | ||
| 79 | setgaps( | ||
| 80 | selmon->gappoh, | ||
| 81 | selmon->gappov, | ||
| 82 | selmon->gappih + arg->i, | ||
| 83 | selmon->gappiv + arg->i | ||
| 84 | ); | ||
| 85 | } | ||
| 86 | |||
| 87 | void | ||
| 88 | incrogaps(const Arg *arg) | ||
| 89 | { | ||
| 90 | setgaps( | ||
| 91 | selmon->gappoh + arg->i, | ||
| 92 | selmon->gappov + arg->i, | ||
| 93 | selmon->gappih, | ||
| 94 | selmon->gappiv | ||
| 95 | ); | ||
| 96 | } | ||
| 97 | |||
| 98 | void | ||
| 99 | incrohgaps(const Arg *arg) | ||
| 100 | { | ||
| 101 | setgaps( | ||
| 102 | selmon->gappoh + arg->i, | ||
| 103 | selmon->gappov, | ||
| 104 | selmon->gappih, | ||
| 105 | selmon->gappiv | ||
| 106 | ); | ||
| 107 | } | ||
| 108 | |||
| 109 | void | ||
| 110 | incrovgaps(const Arg *arg) | ||
| 111 | { | ||
| 112 | setgaps( | ||
| 113 | selmon->gappoh, | ||
| 114 | selmon->gappov + arg->i, | ||
| 115 | selmon->gappih, | ||
| 116 | selmon->gappiv | ||
| 117 | ); | ||
| 118 | } | ||
| 119 | |||
| 120 | void | ||
| 121 | incrihgaps(const Arg *arg) | ||
| 122 | { | ||
| 123 | setgaps( | ||
| 124 | selmon->gappoh, | ||
| 125 | selmon->gappov, | ||
| 126 | selmon->gappih + arg->i, | ||
| 127 | selmon->gappiv | ||
| 128 | ); | ||
| 129 | } | ||
| 130 | |||
| 131 | void | ||
| 132 | incrivgaps(const Arg *arg) | ||
| 133 | { | ||
| 134 | setgaps( | ||
| 135 | selmon->gappoh, | ||
| 136 | selmon->gappov, | ||
| 137 | selmon->gappih, | ||
| 138 | selmon->gappiv + arg->i | ||
| 139 | ); | ||
| 140 | } | ||
| 141 | |||
| 142 | void | ||
| 143 | getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc) | ||
| 144 | { | ||
| 145 | unsigned int n, oe, ie; | ||
| 146 | #if PERTAG_PATCH | ||
| 147 | oe = ie = selmon->pertag->enablegaps[selmon->pertag->curtag]; | ||
| 148 | #else | ||
| 149 | oe = ie = enablegaps; | ||
| 150 | #endif // PERTAG_PATCH | ||
| 151 | Client *c; | ||
| 152 | |||
| 153 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); | ||
| 154 | if (smartgaps && n == 1) { | ||
| 155 | oe = 0; // outer gaps disabled when only one client | ||
| 156 | } | ||
| 157 | |||
| 158 | *oh = m->gappoh*oe; // outer horizontal gap | ||
| 159 | *ov = m->gappov*oe; // outer vertical gap | ||
| 160 | *ih = m->gappih*ie; // inner horizontal gap | ||
| 161 | *iv = m->gappiv*ie; // inner vertical gap | ||
| 162 | *nc = n; // number of clients | ||
| 163 | } | ||
| 164 | |||
| 165 | void | ||
| 166 | getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr) | ||
| 167 | { | ||
| 168 | unsigned int n; | ||
| 169 | float mfacts, sfacts; | ||
| 170 | int mtotal = 0, stotal = 0; | ||
| 171 | Client *c; | ||
| 172 | |||
| 173 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); | ||
| 174 | mfacts = MIN(n, m->nmaster); | ||
| 175 | sfacts = n - m->nmaster; | ||
| 176 | |||
| 177 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) | ||
| 178 | if (n < m->nmaster) | ||
| 179 | mtotal += msize / mfacts; | ||
| 180 | else | ||
| 181 | stotal += ssize / sfacts; | ||
| 182 | |||
| 183 | *mf = mfacts; // total factor of master area | ||
| 184 | *sf = sfacts; // total factor of stack area | ||
| 185 | *mr = msize - mtotal; // the remainder (rest) of pixels after an even master split | ||
| 186 | *sr = ssize - stotal; // the remainder (rest) of pixels after an even stack split | ||
| 187 | } | ||
| 188 | |||
| 189 | /*** | ||
| 190 | * Layouts | ||
| 191 | */ | ||
| 192 | |||
| 193 | /* | ||
| 194 | * Bottomstack layout + gaps | ||
| 195 | * https://dwm.suckless.org/patches/bottomstack/ | ||
| 196 | */ | ||
| 197 | static void | ||
| 198 | bstack(Monitor *m) | ||
| 199 | { | ||
| 200 | unsigned int i, n; | ||
| 201 | int oh, ov, ih, iv; | ||
| 202 | int mx = 0, my = 0, mh = 0, mw = 0; | ||
| 203 | int sx = 0, sy = 0, sh = 0, sw = 0; | ||
| 204 | float mfacts, sfacts; | ||
| 205 | int mrest, srest; | ||
| 206 | Client *c; | ||
| 207 | |||
| 208 | getgaps(m, &oh, &ov, &ih, &iv, &n); | ||
| 209 | if (n == 0) | ||
| 210 | return; | ||
| 211 | |||
| 212 | sx = mx = m->wx + ov; | ||
| 213 | sy = my = m->wy + oh; | ||
| 214 | sh = mh = m->wh - 2*oh; | ||
| 215 | mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); | ||
| 216 | sw = m->ww - 2*ov - iv * (n - m->nmaster - 1); | ||
| 217 | |||
| 218 | if (m->nmaster && n > m->nmaster) { | ||
| 219 | sh = (mh - ih) * (1 - m->mfact); | ||
| 220 | mh = mh - ih - sh; | ||
| 221 | sx = mx; | ||
| 222 | sy = my + mh + ih; | ||
| 223 | } | ||
| 224 | |||
| 225 | getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); | ||
| 226 | |||
| 227 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { | ||
| 228 | if (i < m->nmaster) { | ||
| 229 | resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); | ||
| 230 | mx += WIDTH(c) + iv; | ||
| 231 | } else { | ||
| 232 | resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); | ||
| 233 | sx += WIDTH(c) + iv; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | static void | ||
| 239 | bstackhoriz(Monitor *m) | ||
| 240 | { | ||
| 241 | unsigned int i, n; | ||
| 242 | int oh, ov, ih, iv; | ||
| 243 | int mx = 0, my = 0, mh = 0, mw = 0; | ||
| 244 | int sx = 0, sy = 0, sh = 0, sw = 0; | ||
| 245 | float mfacts, sfacts; | ||
| 246 | int mrest, srest; | ||
| 247 | Client *c; | ||
| 248 | |||
| 249 | getgaps(m, &oh, &ov, &ih, &iv, &n); | ||
| 250 | if (n == 0) | ||
| 251 | return; | ||
| 252 | |||
| 253 | sx = mx = m->wx + ov; | ||
| 254 | sy = my = m->wy + oh; | ||
| 255 | mh = m->wh - 2*oh; | ||
| 256 | sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); | ||
| 257 | mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); | ||
| 258 | sw = m->ww - 2*ov; | ||
| 259 | |||
| 260 | if (m->nmaster && n > m->nmaster) { | ||
| 261 | sh = (mh - ih) * (1 - m->mfact); | ||
| 262 | mh = mh - ih - sh; | ||
| 263 | sy = my + mh + ih; | ||
| 264 | sh = m->wh - mh - 2*oh - ih * (n - m->nmaster); | ||
| 265 | } | ||
| 266 | |||
| 267 | getfacts(m, mw, sh, &mfacts, &sfacts, &mrest, &srest); | ||
| 268 | |||
| 269 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { | ||
| 270 | if (i < m->nmaster) { | ||
| 271 | resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); | ||
| 272 | mx += WIDTH(c) + iv; | ||
| 273 | } else { | ||
| 274 | resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); | ||
| 275 | sy += HEIGHT(c) + ih; | ||
| 276 | } | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | /* | ||
| 281 | * Centred master layout + gaps | ||
| 282 | * https://dwm.suckless.org/patches/centeredmaster/ | ||
| 283 | */ | ||
| 284 | void | ||
| 285 | centeredmaster(Monitor *m) | ||
| 286 | { | ||
| 287 | unsigned int i, n; | ||
| 288 | int oh, ov, ih, iv; | ||
| 289 | int mx = 0, my = 0, mh = 0, mw = 0; | ||
| 290 | int lx = 0, ly = 0, lw = 0, lh = 0; | ||
| 291 | int rx = 0, ry = 0, rw = 0, rh = 0; | ||
| 292 | float mfacts = 0, lfacts = 0, rfacts = 0; | ||
| 293 | int mtotal = 0, ltotal = 0, rtotal = 0; | ||
| 294 | int mrest = 0, lrest = 0, rrest = 0; | ||
| 295 | Client *c; | ||
| 296 | |||
| 297 | getgaps(m, &oh, &ov, &ih, &iv, &n); | ||
| 298 | if (n == 0) | ||
| 299 | return; | ||
| 300 | |||
| 301 | /* initialize areas */ | ||
| 302 | mx = m->wx + ov; | ||
| 303 | my = m->wy + oh; | ||
| 304 | mh = m->wh - 2*oh - ih * ((!m->nmaster ? n : MIN(n, m->nmaster)) - 1); | ||
| 305 | mw = m->ww - 2*ov; | ||
| 306 | lh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - 1); | ||
| 307 | rh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - ((n - m->nmaster) % 2 ? 0 : 1)); | ||
| 308 | |||
| 309 | if (m->nmaster && n > m->nmaster) { | ||
| 310 | /* go mfact box in the center if more than nmaster clients */ | ||
| 311 | if (n - m->nmaster > 1) { | ||
| 312 | /* ||<-S->|<---M--->|<-S->|| */ | ||
| 313 | mw = (m->ww - 2*ov - 2*iv) * m->mfact; | ||
| 314 | lw = (m->ww - mw - 2*ov - 2*iv) / 2; | ||
| 315 | rw = (m->ww - mw - 2*ov - 2*iv) - lw; | ||
| 316 | mx += lw + iv; | ||
| 317 | } else { | ||
| 318 | /* ||<---M--->|<-S->|| */ | ||
| 319 | mw = (mw - iv) * m->mfact; | ||
| 320 | lw = 0; | ||
| 321 | rw = m->ww - mw - iv - 2*ov; | ||
| 322 | } | ||
| 323 | lx = m->wx + ov; | ||
| 324 | ly = m->wy + oh; | ||
| 325 | rx = mx + mw + iv; | ||
| 326 | ry = m->wy + oh; | ||
| 327 | } | ||
| 328 | |||
| 329 | /* calculate facts */ | ||
| 330 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { | ||
| 331 | if (!m->nmaster || n < m->nmaster) | ||
| 332 | mfacts += 1; | ||
| 333 | else if ((n - m->nmaster) % 2) | ||
| 334 | lfacts += 1; // total factor of left hand stack area | ||
| 335 | else | ||
| 336 | rfacts += 1; // total factor of right hand stack area | ||
| 337 | } | ||
| 338 | |||
| 339 | for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) | ||
| 340 | if (!m->nmaster || n < m->nmaster) | ||
| 341 | mtotal += mh / mfacts; | ||
| 342 | else if ((n - m->nmaster) % 2) | ||
| 343 | ltotal += lh / lfacts; | ||
| 344 | else | ||
| 345 | rtotal += rh / rfacts; | ||
| 346 | |||
| 347 | mrest = mh - mtotal; | ||
| 348 | lrest = lh - ltotal; | ||
| 349 | rrest = rh - rtotal; | ||
| 350 | |||
| 351 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { | ||
| 352 | if (!m->nmaster || i < m->nmaster) { | ||
| 353 | /* nmaster clients are stacked vertically, in the center of the screen */ | ||
| 354 | resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); | ||
| 355 | my += HEIGHT(c) + ih; | ||
| 356 | } else { | ||
| 357 | /* stack clients are stacked vertically */ | ||
| 358 | if ((i - m->nmaster) % 2 ) { | ||
| 359 | resize(c, lx, ly, lw - (2*c->bw), (lh / lfacts) + ((i - 2*m->nmaster) < 2*lrest ? 1 : 0) - (2*c->bw), 0); | ||
| 360 | ly += HEIGHT(c) + ih; | ||
| 361 | } else { | ||
| 362 | resize(c, rx, ry, rw - (2*c->bw), (rh / rfacts) + ((i - 2*m->nmaster) < 2*rrest ? 1 : 0) - (2*c->bw), 0); | ||
| 363 | ry += HEIGHT(c) + ih; | ||
| 364 | } | ||
| 365 | } | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | void | ||
| 370 | centeredfloatingmaster(Monitor *m) | ||
| 371 | { | ||
| 372 | unsigned int i, n; | ||
| 373 | float mfacts, sfacts; | ||
| 374 | float mivf = 1.0; // master inner vertical gap factor | ||
| 375 | int oh, ov, ih, iv, mrest, srest; | ||
| 376 | int mx = 0, my = 0, mh = 0, mw = 0; | ||
| 377 | int sx = 0, sy = 0, sh = 0, sw = 0; | ||
| 378 | Client *c; | ||
| 379 | |||
| 380 | getgaps(m, &oh, &ov, &ih, &iv, &n); | ||
| 381 | if (n == 0) | ||
| 382 | return; | ||
| 383 | |||
| 384 | sx = mx = m->wx + ov; | ||
| 385 | sy = my = m->wy + oh; | ||
| 386 | sh = mh = m->wh - 2*oh; | ||
| 387 | mw = m->ww - 2*ov - iv*(n - 1); | ||
| 388 | sw = m->ww - 2*ov - iv*(n - m->nmaster - 1); | ||
| 389 | |||
| 390 | if (m->nmaster && n > m->nmaster) { | ||
| 391 | mivf = 0.8; | ||
| 392 | /* go mfact box in the center if more than nmaster clients */ | ||
| 393 | if (m->ww > m->wh) { | ||
| 394 | mw = m->ww * m->mfact - iv*mivf*(MIN(n, m->nmaster) - 1); | ||
| 395 | mh = m->wh * 0.9; | ||
| 396 | } else { | ||
| 397 | mw = m->ww * 0.9 - iv*mivf*(MIN(n, m->nmaster) - 1); | ||
| 398 | mh = m->wh * m->mfact; | ||
| 399 | } | ||
| 400 | mx = m->wx + (m->ww - mw) / 2; | ||
| 401 | my = m->wy + (m->wh - mh - 2*oh) / 2; | ||
| 402 | |||
| 403 | sx = m->wx + ov; | ||
| 404 | sy = m->wy + oh; | ||
| 405 | sh = m->wh - 2*oh; | ||
| 406 | } | ||
| 407 | |||
| 408 | getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); | ||
| 409 | |||
| 410 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) | ||
| 411 | if (i < m->nmaster) { | ||
| 412 | /* nmaster clients are stacked horizontally, in the center of the screen */ | ||
| 413 | resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); | ||
| 414 | mx += WIDTH(c) + iv*mivf; | ||
| 415 | } else { | ||
| 416 | /* stack clients are stacked horizontally */ | ||
| 417 | resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); | ||
| 418 | sx += WIDTH(c) + iv; | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | /* | ||
| 423 | * Deck layout + gaps | ||
| 424 | * https://dwm.suckless.org/patches/deck/ | ||
| 425 | */ | ||
| 426 | void | ||
| 427 | deck(Monitor *m) | ||
| 428 | { | ||
| 429 | unsigned int i, n; | ||
| 430 | int oh, ov, ih, iv; | ||
| 431 | int mx = 0, my = 0, mh = 0, mw = 0; | ||
| 432 | int sx = 0, sy = 0, sh = 0, sw = 0; | ||
| 433 | float mfacts, sfacts; | ||
| 434 | int mrest, srest; | ||
| 435 | Client *c; | ||
| 436 | |||
| 437 | getgaps(m, &oh, &ov, &ih, &iv, &n); | ||
| 438 | if (n == 0) | ||
| 439 | return; | ||
| 440 | |||
| 441 | sx = mx = m->wx + ov; | ||
| 442 | sy = my = m->wy + oh; | ||
| 443 | sh = mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); | ||
| 444 | sw = mw = m->ww - 2*ov; | ||
| 445 | |||
| 446 | if (m->nmaster && n > m->nmaster) { | ||
| 447 | sw = (mw - iv) * (1 - m->mfact); | ||
| 448 | mw = mw - iv - sw; | ||
| 449 | sx = mx + mw + iv; | ||
| 450 | sh = m->wh - 2*oh; | ||
| 451 | } | ||
| 452 | |||
| 453 | getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); | ||
| 454 | |||
| 455 | if (n - m->nmaster > 0) /* override layout symbol */ | ||
| 456 | snprintf(m->ltsymbol, sizeof m->ltsymbol, "D %d", n - m->nmaster); | ||
| 457 | |||
| 458 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) | ||
| 459 | if (i < m->nmaster) { | ||
| 460 | resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); | ||
| 461 | my += HEIGHT(c) + ih; | ||
| 462 | } else { | ||
| 463 | resize(c, sx, sy, sw - (2*c->bw), sh - (2*c->bw), 0); | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | /* | ||
| 468 | * Fibonacci layout + gaps | ||
| 469 | * https://dwm.suckless.org/patches/fibonacci/ | ||
| 470 | */ | ||
| 471 | void | ||
| 472 | fibonacci(Monitor *m, int s) | ||
| 473 | { | ||
| 474 | unsigned int i, n; | ||
| 475 | int nx, ny, nw, nh; | ||
| 476 | int oh, ov, ih, iv; | ||
| 477 | int nv, hrest = 0, wrest = 0, r = 1; | ||
| 478 | Client *c; | ||
| 479 | |||
| 480 | getgaps(m, &oh, &ov, &ih, &iv, &n); | ||
| 481 | if (n == 0) | ||
| 482 | return; | ||
| 483 | |||
| 484 | nx = m->wx + ov; | ||
| 485 | ny = m->wy + oh; | ||
| 486 | nw = m->ww - 2*ov; | ||
| 487 | nh = m->wh - 2*oh; | ||
| 488 | |||
| 489 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { | ||
| 490 | if (r) { | ||
| 491 | if ((i % 2 && (nh - ih) / 2 <= (bh + 2*c->bw)) | ||
| 492 | || (!(i % 2) && (nw - iv) / 2 <= (bh + 2*c->bw))) { | ||
| 493 | r = 0; | ||
| 494 | } | ||
| 495 | if (r && i < n - 1) { | ||
| 496 | if (i % 2) { | ||
| 497 | nv = (nh - ih) / 2; | ||
| 498 | hrest = nh - 2*nv - ih; | ||
| 499 | nh = nv; | ||
| 500 | } else { | ||
| 501 | nv = (nw - iv) / 2; | ||
| 502 | wrest = nw - 2*nv - iv; | ||
| 503 | nw = nv; | ||
| 504 | } | ||
| 505 | |||
| 506 | if ((i % 4) == 2 && !s) | ||
| 507 | nx += nw + iv; | ||
| 508 | else if ((i % 4) == 3 && !s) | ||
| 509 | ny += nh + ih; | ||
| 510 | } | ||
| 511 | |||
| 512 | if ((i % 4) == 0) { | ||
| 513 | if (s) { | ||
| 514 | ny += nh + ih; | ||
| 515 | nh += hrest; | ||
| 516 | } | ||
| 517 | else { | ||
| 518 | nh -= hrest; | ||
| 519 | ny -= nh + ih; | ||
| 520 | } | ||
| 521 | } | ||
| 522 | else if ((i % 4) == 1) { | ||
| 523 | nx += nw + iv; | ||
| 524 | nw += wrest; | ||
| 525 | } | ||
| 526 | else if ((i % 4) == 2) { | ||
| 527 | ny += nh + ih; | ||
| 528 | nh += hrest; | ||
| 529 | if (i < n - 1) | ||
| 530 | nw += wrest; | ||
| 531 | } | ||
| 532 | else if ((i % 4) == 3) { | ||
| 533 | if (s) { | ||
| 534 | nx += nw + iv; | ||
| 535 | nw -= wrest; | ||
| 536 | } else { | ||
| 537 | nw -= wrest; | ||
| 538 | nx -= nw + iv; | ||
| 539 | nh += hrest; | ||
| 540 | } | ||
| 541 | } | ||
| 542 | if (i == 0) { | ||
| 543 | if (n != 1) { | ||
| 544 | nw = (m->ww - iv - 2*ov) - (m->ww - iv - 2*ov) * (1 - m->mfact); | ||
| 545 | wrest = 0; | ||
| 546 | } | ||
| 547 | ny = m->wy + oh; | ||
| 548 | } | ||
| 549 | else if (i == 1) | ||
| 550 | nw = m->ww - nw - iv - 2*ov; | ||
| 551 | i++; | ||
| 552 | } | ||
| 553 | |||
| 554 | resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False); | ||
| 555 | } | ||
| 556 | } | ||
| 557 | |||
| 558 | void | ||
| 559 | dwindle(Monitor *m) | ||
| 560 | { | ||
| 561 | fibonacci(m, 1); | ||
| 562 | } | ||
| 563 | |||
| 564 | void | ||
| 565 | spiral(Monitor *m) | ||
| 566 | { | ||
| 567 | fibonacci(m, 0); | ||
| 568 | } | ||
| 569 | |||
| 570 | /* | ||
| 571 | * Gappless grid layout + gaps (ironically) | ||
| 572 | * https://dwm.suckless.org/patches/gaplessgrid/ | ||
| 573 | */ | ||
| 574 | void | ||
| 575 | gaplessgrid(Monitor *m) | ||
| 576 | { | ||
| 577 | unsigned int i, n; | ||
| 578 | int x, y, cols, rows, ch, cw, cn, rn, rrest, crest; // counters | ||
| 579 | int oh, ov, ih, iv; | ||
| 580 | Client *c; | ||
| 581 | |||
| 582 | getgaps(m, &oh, &ov, &ih, &iv, &n); | ||
| 583 | if (n == 0) | ||
| 584 | return; | ||
| 585 | |||
| 586 | /* grid dimensions */ | ||
| 587 | for (cols = 0; cols <= n/2; cols++) | ||
| 588 | if (cols*cols >= n) | ||
| 589 | break; | ||
| 590 | if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ | ||
| 591 | cols = 2; | ||
| 592 | rows = n/cols; | ||
| 593 | cn = rn = 0; // reset column no, row no, client count | ||
| 594 | |||
| 595 | ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; | ||
| 596 | cw = (m->ww - 2*ov - iv * (cols - 1)) / cols; | ||
| 597 | rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; | ||
| 598 | crest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; | ||
| 599 | x = m->wx + ov; | ||
| 600 | y = m->wy + oh; | ||
| 601 | |||
| 602 | for (i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) { | ||
| 603 | if (i/rows + 1 > cols - n%cols) { | ||
| 604 | rows = n/cols + 1; | ||
| 605 | ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; | ||
| 606 | rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; | ||
| 607 | } | ||
| 608 | resize(c, | ||
| 609 | x, | ||
| 610 | y + rn*(ch + ih) + MIN(rn, rrest), | ||
| 611 | cw + (cn < crest ? 1 : 0) - 2*c->bw, | ||
| 612 | ch + (rn < rrest ? 1 : 0) - 2*c->bw, | ||
| 613 | 0); | ||
| 614 | rn++; | ||
| 615 | if (rn >= rows) { | ||
| 616 | rn = 0; | ||
| 617 | x += cw + ih + (cn < crest ? 1 : 0); | ||
| 618 | cn++; | ||
| 619 | } | ||
| 620 | } | ||
| 621 | } | ||
| 622 | |||
| 623 | /* | ||
| 624 | * Gridmode layout + gaps | ||
| 625 | * https://dwm.suckless.org/patches/gridmode/ | ||
| 626 | */ | ||
| 627 | void | ||
| 628 | grid(Monitor *m) | ||
| 629 | { | ||
| 630 | unsigned int i, n; | ||
| 631 | int cx, cy, cw, ch, cc, cr, chrest, cwrest, cols, rows; | ||
| 632 | int oh, ov, ih, iv; | ||
| 633 | Client *c; | ||
| 634 | |||
| 635 | getgaps(m, &oh, &ov, &ih, &iv, &n); | ||
| 636 | |||
| 637 | /* grid dimensions */ | ||
| 638 | for (rows = 0; rows <= n/2; rows++) | ||
| 639 | if (rows*rows >= n) | ||
| 640 | break; | ||
| 641 | cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; | ||
| 642 | |||
| 643 | /* window geoms (cell height/width) */ | ||
| 644 | ch = (m->wh - 2*oh - ih * (rows - 1)) / (rows ? rows : 1); | ||
| 645 | cw = (m->ww - 2*ov - iv * (cols - 1)) / (cols ? cols : 1); | ||
| 646 | chrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; | ||
| 647 | cwrest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; | ||
| 648 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { | ||
| 649 | cc = i / rows; | ||
| 650 | cr = i % rows; | ||
| 651 | cx = m->wx + ov + cc * (cw + iv) + MIN(cc, cwrest); | ||
| 652 | cy = m->wy + oh + cr * (ch + ih) + MIN(cr, chrest); | ||
| 653 | resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False); | ||
| 654 | } | ||
| 655 | } | ||
| 656 | |||
| 657 | /* | ||
| 658 | * Horizontal grid layout + gaps | ||
| 659 | * https://dwm.suckless.org/patches/horizgrid/ | ||
| 660 | */ | ||
| 661 | void | ||
| 662 | horizgrid(Monitor *m) { | ||
| 663 | Client *c; | ||
| 664 | unsigned int n, i; | ||
| 665 | int oh, ov, ih, iv; | ||
| 666 | int mx = 0, my = 0, mh = 0, mw = 0; | ||
| 667 | int sx = 0, sy = 0, sh = 0, sw = 0; | ||
| 668 | int ntop, nbottom = 1; | ||
| 669 | float mfacts, sfacts; | ||
| 670 | int mrest, srest; | ||
| 671 | |||
| 672 | /* Count windows */ | ||
| 673 | getgaps(m, &oh, &ov, &ih, &iv, &n); | ||
| 674 | if (n == 0) | ||
| 675 | return; | ||
| 676 | |||
| 677 | if (n <= 2) | ||
| 678 | ntop = n; | ||
| 679 | else { | ||
| 680 | ntop = n / 2; | ||
| 681 | nbottom = n - ntop; | ||
| 682 | } | ||
| 683 | sx = mx = m->wx + ov; | ||
| 684 | sy = my = m->wy + oh; | ||
| 685 | sh = mh = m->wh - 2*oh; | ||
| 686 | sw = mw = m->ww - 2*ov; | ||
| 687 | |||
| 688 | if (n > ntop) { | ||
| 689 | sh = (mh - ih) / 2; | ||
| 690 | mh = mh - ih - sh; | ||
| 691 | sy = my + mh + ih; | ||
| 692 | mw = m->ww - 2*ov - iv * (ntop - 1); | ||
| 693 | sw = m->ww - 2*ov - iv * (nbottom - 1); | ||
| 694 | } | ||
| 695 | |||
| 696 | mfacts = ntop; | ||
| 697 | sfacts = nbottom; | ||
| 698 | mrest = mw - (mw / ntop) * ntop; | ||
| 699 | srest = sw - (sw / nbottom) * nbottom; | ||
| 700 | |||
| 701 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) | ||
| 702 | if (i < ntop) { | ||
| 703 | resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); | ||
| 704 | mx += WIDTH(c) + iv; | ||
| 705 | } else { | ||
| 706 | resize(c, sx, sy, (sw / sfacts) + ((i - ntop) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); | ||
| 707 | sx += WIDTH(c) + iv; | ||
| 708 | } | ||
| 709 | } | ||
| 710 | |||
| 711 | /* | ||
| 712 | * nrowgrid layout + gaps | ||
| 713 | * https://dwm.suckless.org/patches/nrowgrid/ | ||
| 714 | */ | ||
| 715 | void | ||
| 716 | nrowgrid(Monitor *m) | ||
| 717 | { | ||
| 718 | unsigned int n; | ||
| 719 | int ri = 0, ci = 0; /* counters */ | ||
| 720 | int oh, ov, ih, iv; /* vanitygap settings */ | ||
| 721 | unsigned int cx, cy, cw, ch; /* client geometry */ | ||
| 722 | unsigned int uw = 0, uh = 0, uc = 0; /* utilization trackers */ | ||
| 723 | unsigned int cols, rows = m->nmaster + 1; | ||
| 724 | Client *c; | ||
| 725 | |||
| 726 | /* count clients */ | ||
| 727 | getgaps(m, &oh, &ov, &ih, &iv, &n); | ||
| 728 | |||
| 729 | /* nothing to do here */ | ||
| 730 | if (n == 0) | ||
| 731 | return; | ||
| 732 | |||
| 733 | /* force 2 clients to always split vertically */ | ||
| 734 | if (FORCE_VSPLIT && n == 2) | ||
| 735 | rows = 1; | ||
| 736 | |||
| 737 | /* never allow empty rows */ | ||
| 738 | if (n < rows) | ||
| 739 | rows = n; | ||
| 740 | |||
| 741 | /* define first row */ | ||
| 742 | cols = n / rows; | ||
| 743 | uc = cols; | ||
| 744 | cy = m->wy + oh; | ||
| 745 | ch = (m->wh - 2*oh - ih*(rows - 1)) / rows; | ||
| 746 | uh = ch; | ||
| 747 | |||
| 748 | for (c = nexttiled(m->clients); c; c = nexttiled(c->next), ci++) { | ||
| 749 | if (ci == cols) { | ||
| 750 | uw = 0; | ||
| 751 | ci = 0; | ||
| 752 | ri++; | ||
| 753 | |||
| 754 | /* next row */ | ||
| 755 | cols = (n - uc) / (rows - ri); | ||
| 756 | uc += cols; | ||
| 757 | cy = m->wy + oh + uh + ih; | ||
| 758 | uh += ch + ih; | ||
| 759 | } | ||
| 760 | |||
| 761 | cx = m->wx + ov + uw; | ||
| 762 | cw = (m->ww - 2*ov - uw) / (cols - ci); | ||
| 763 | uw += cw + iv; | ||
| 764 | |||
| 765 | resize(c, cx, cy, cw - (2*c->bw), ch - (2*c->bw), 0); | ||
| 766 | } | ||
| 767 | } | ||
| 768 | |||
| 769 | /* | ||
| 770 | * Default tile layout + gaps | ||
| 771 | */ | ||
| 772 | static void | ||
| 773 | tile(Monitor *m) | ||
| 774 | { | ||
| 775 | unsigned int i, n; | ||
| 776 | int oh, ov, ih, iv; | ||
| 777 | int mx = 0, my = 0, mh = 0, mw = 0; | ||
| 778 | int sx = 0, sy = 0, sh = 0, sw = 0; | ||
| 779 | float mfacts, sfacts; | ||
| 780 | int mrest, srest; | ||
| 781 | Client *c; | ||
| 782 | |||
| 783 | getgaps(m, &oh, &ov, &ih, &iv, &n); | ||
| 784 | if (n == 0) | ||
| 785 | return; | ||
| 786 | |||
| 787 | sx = mx = m->wx + ov; | ||
| 788 | sy = my = m->wy + oh; | ||
| 789 | mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); | ||
| 790 | sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); | ||
| 791 | sw = mw = m->ww - 2*ov; | ||
| 792 | |||
| 793 | if (m->nmaster && n > m->nmaster) { | ||
| 794 | sw = (mw - iv) * (1 - m->mfact); | ||
| 795 | mw = mw - iv - sw; | ||
| 796 | sx = mx + mw + iv; | ||
| 797 | } | ||
| 798 | |||
| 799 | getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); | ||
| 800 | |||
| 801 | for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) | ||
| 802 | if (i < m->nmaster) { | ||
| 803 | resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); | ||
| 804 | my += HEIGHT(c) + ih; | ||
| 805 | } else { | ||
| 806 | resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); | ||
| 807 | sy += HEIGHT(c) + ih; | ||
| 808 | } | ||
| 809 | } | ||
