diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..095e840 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +dwm diff --git a/Makefile b/Makefile index 77bcbc0..c6bc24b 100644 --- a/Makefile +++ b/Makefile @@ -37,8 +37,9 @@ dist: clean rm -rf dwm-${VERSION} install: all - mkdir -p ${DESTDIR}${PREFIX}/bin - cp -f dwm ${DESTDIR}${PREFIX}/bin + mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDIR}${XSESSIONPREFIX} + cp -f dwm dwmc volsv ${DESTDIR}${PREFIX}/bin + cp -f dwm.desktop ${DESTDIR}${XSESSIONPREFIX} chmod 755 ${DESTDIR}${PREFIX}/bin/dwm mkdir -p ${DESTDIR}${MANPREFIX}/man1 sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 @@ -46,6 +47,8 @@ install: all uninstall: rm -f ${DESTDIR}${PREFIX}/bin/dwm\ + ${DESTDIR}${PREFIX}/bin/dwmc\ + ${DESTDIR}${PREFIX}/bin/volsv\ ${DESTDIR}${MANPREFIX}/man1/dwm.1 .PHONY: all options clean dist install uninstall diff --git a/config.def.h b/config.def.h index 1c0b587..8bfef3e 100644 --- a/config.def.h +++ b/config.def.h @@ -2,7 +2,13 @@ /* appearance */ static const unsigned int borderpx = 1; /* border pixel of windows */ +static const unsigned int gappx = 6; /* gaps between windows */ static const unsigned int snap = 32; /* snap pixel */ +static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ +static const unsigned int systrayspacing = 2; /* systray spacing */ +static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor */ +static const int showsystray = 1; /* 0 means no systray */ +static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ static const int showbar = 1; /* 0 means no bar */ static const int topbar = 1; /* 0 means bottom bar */ static const char *fonts[] = { "monospace:size=10" }; @@ -26,15 +32,18 @@ static const Rule rules[] = { * WM_CLASS(STRING) = instance, class * WM_NAME(STRING) = title */ - /* class instance title tags mask isfloating monitor */ - { "Gimp", NULL, NULL, 0, 1, -1 }, - { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, + /* class instance title tags mask isfloating isterminal noswallow monitor */ + { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 }, + { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1, -1 }, + { "st", NULL, NULL, 0, 0, 1, -1, -1 }, + { NULL, NULL, "Event Tester", 0, 1, 0, 1, -1 }, /* xev */ }; /* layout(s) */ static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ static const int nmaster = 1; /* number of clients in master area */ static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +static int attachbelow = 1; /* 1 means attach after the currently active window */ static const Layout layouts[] = { /* symbol arrange function */ @@ -70,6 +79,9 @@ static Key keys[] = { { MODKEY, XK_d, incnmaster, {.i = -1 } }, { MODKEY, XK_h, setmfact, {.f = -0.05} }, { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_h, setcfact, {.f = +0.25} }, + { MODKEY|ShiftMask, XK_l, setcfact, {.f = -0.25} }, + { MODKEY|ShiftMask, XK_o, setcfact, {.f = 0.00} }, { MODKEY, XK_Return, zoom, {0} }, { MODKEY, XK_Tab, view, {0} }, { MODKEY|ShiftMask, XK_c, killclient, {0} }, @@ -78,6 +90,7 @@ static Key keys[] = { { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, { MODKEY, XK_space, setlayout, {0} }, { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY|ShiftMask, XK_f, togglefullscr, {0} }, { MODKEY, XK_0, view, {.ui = ~0 } }, { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, { MODKEY, XK_comma, focusmon, {.i = -1 } }, @@ -94,6 +107,7 @@ static Key keys[] = { TAGKEYS( XK_8, 7) TAGKEYS( XK_9, 8) { MODKEY|ShiftMask, XK_q, quit, {0} }, + { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} }, }; /* button definitions */ @@ -103,7 +117,9 @@ static Button buttons[] = { { ClkLtSymbol, 0, Button1, setlayout, {0} }, { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, { ClkWinTitle, 0, Button2, zoom, {0} }, - { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkStatusText, 0, Button1, sigdwmblocks, {.i = 1} }, + { ClkStatusText, 0, Button2, sigdwmblocks, {.i = 2} }, + { ClkStatusText, 0, Button3, sigdwmblocks, {.i = 3} }, { ClkClientWin, MODKEY, Button1, movemouse, {0} }, { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, @@ -113,3 +129,73 @@ static Button buttons[] = { { ClkTagBar, MODKEY, Button3, toggletag, {0} }, }; +void +setlayoutex(const Arg *arg) +{ + setlayout(&((Arg) { .v = &layouts[arg->i] })); +} + +void +viewex(const Arg *arg) +{ + view(&((Arg) { .ui = 1 << arg->ui })); +} + +void +viewall(const Arg *arg) +{ + view(&((Arg){.ui = ~0})); +} + +void +toggleviewex(const Arg *arg) +{ + toggleview(&((Arg) { .ui = 1 << arg->ui })); +} + +void +tagex(const Arg *arg) +{ + tag(&((Arg) { .ui = 1 << arg->ui })); +} + +void +toggletagex(const Arg *arg) +{ + toggletag(&((Arg) { .ui = 1 << arg->ui })); +} + +void +tagall(const Arg *arg) +{ + tag(&((Arg){.ui = ~0})); +} + +/* signal definitions */ +/* signum must be greater than 0 */ +/* trigger signals using `xsetroot -name "fsignal: [ ]"` */ +static Signal signals[] = { + /* signum function */ + { "focusstack", focusstack }, + { "setmfact", setmfact }, + { "togglebar", togglebar }, + { "incnmaster", incnmaster }, + { "togglefloating", togglefloating }, + { "focusmon", focusmon }, + { "tagmon", tagmon }, + { "zoom", zoom }, + { "view", view }, + { "viewall", viewall }, + { "viewex", viewex }, + { "toggleview", view }, + { "toggleviewex", toggleviewex }, + { "tag", tag }, + { "tagall", tagall }, + { "tagex", tagex }, + { "toggletag", tag }, + { "toggletagex", toggletagex }, + { "killclient", killclient }, + { "quit", quit }, + { "setlayout", setlayout }, + { "setlayoutex", setlayoutex }, +}; diff --git a/config.h b/config.h new file mode 100644 index 0000000..607e67f --- /dev/null +++ b/config.h @@ -0,0 +1,260 @@ +/* See LICENSE file for copyright and license details. */ + +#include + +/* appearance */ +static const unsigned int borderpx = 1; /* border pixel of windows */ +static const unsigned int gappx = 6; /* gaps between windows */ +static const unsigned int snap = 32; /* snap pixel */ +static const int swallowfloating = 0; +static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ +static const unsigned int systrayspacing = 2; /* systray spacing */ +static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ +static const int showsystray = 1; /* 0 means no systray */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const char *fonts[] = { "mononoki Nerd Font:size=9", "JoyPixels:size=9" }; +static const char dmenufont[] = "mononoki Nerd Font:size=9"; +static const char col_gray1[] = "#222222"; +static const char col_gray2[] = "#444444"; +static const char col_gray3[] = "#bbbbbb"; +static const char col_gray4[] = "#eeeeee"; +static const char col_cyan[] = "#750000"; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +}; + +/* tagging */ +static const char *tags[] = { " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9" }; + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating isterminal noswallow monitor */ + { NULL, NULL,"Picture in picture", 511, 1, 0, 0, -1 }, + { "ardour-6.2.0",NULL, NULL, 0, 1, 0, 0, -1 }, + { "urxvt", NULL, NULL, 0, 0, 1, 0, -1 }, + { "URxvt", NULL, NULL, 0, 0, 1, 0, -1 }, + { "Ardour-6.2.0",NULL, NULL, 0, 1, 0, 0, -1 }, + { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 }, + { "Firefox", NULL, NULL, 1 << 8, 0, 0, 1, -1 }, + { "st-256color",NULL, NULL, 0, 0, 1, 1, -1 }, + { "st", NULL, NULL, 0, 0, 1, 1, -1 }, + { "St", NULL, NULL, 0, 0, 1, 1, -1 }, + { "tabbed", NULL, NULL, 0, 0, 1, 0, -1 }, + { NULL, NULL, "abduco", 0, 0, 1, 0, -1 }, + { "Alacritty", NULL, NULL, 0, 0, 1, 0, -1 }, + { "Blueman", NULL, NULL, 0, 1, 0, 0, -1 }, + { "QjackCtl", NULL, NULL, 0, 1, 0, 0, -1 }, + { "qjackctl", NULL, NULL, 0, 1, 0, 0, -1 }, + { "catia.py", NULL, NULL, 0, 1, 0, 0, -1 }, + { "Catia", NULL, NULL, 0, 1, 0, 0, -1 }, + { NULL, NULL, "Event Tester", 0, 1, 0, 1, -1 }, + { "Steam", NULL, NULL, 4, 0, 0, 0, -1 }, + { "steam", NULL, NULL, 4, 0, 0, 0, -1 }, + { NULL, NULL, "steam", 4, 0, 0, 0, -1 }, + { "Lutris", NULL, NULL, 2, 0, 0, 0, -1 }, + { "lutris", NULL, NULL, 2, 0, 0, 0, -1 }, +}; + +/* layout(s) */ +static const float mfact = 0.50; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +static int attachbelow = 1; /* 1 means attach after the currently active window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +}; + +/* key definitions */ +#define MODKEY Mod1Mask +#define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + +/* helper for spawning shell commands in the pre dwm-5.0 fashion */ +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + +/* commands */ +static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ +static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; +static const char *passmenu[] = { "passmenu", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; +static const char *termcmd[] = { "tabbed", "-c", "st", "-w", NULL }; +static const char *mpdtoggle[] = { "playerctl", "--player=mpd,mpv,%any", "play-pause", NULL }; +static const char *mpdnext[] = { "playerctl", "--player=mpd,mpv,%any", "next", NULL }; +static const char *mpdprev[] = { "playerctl", "--player=mpd,mpv,%any", "previous", NULL }; +static const char *plytoggle[] = { "playerctl", "--player=%any,mpd", "play-pause", NULL }; +static const char *plyfwd[] = { "playerctl", "--player=%any,mpd", "position 5+", NULL }; +static const char *plybck[] = { "playerctl", "--player=%any,mpd", "position 5-", NULL }; +static const char *blightup[] = { "light", "-A", "1", NULL }; +static const char *blightdown[] = { "light", "-U", "1", NULL }; +static const char *audioup[] = { "volsv", "-i", NULL }; +static const char *audiodown[] = { "volsv", "-d", NULL }; +static const char *audiomute[] = { "volsv", "-t", NULL }; +static const char *micmute[] = { "pamixer", "--source", "1", "-t", NULL }; +static const char *lockscr[] = { "xscreensaver-command", "-lock", NULL }; +static const char *xidletog[] = { "xidletog", NULL }; +static const char *xkillcmd[] = { "xkill", NULL }; + +static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_d, spawn, {.v = dmenucmd } }, + { MODKEY, XK_p, spawn, {.v = passmenu } }, + { MODKEY, XK_c, spawn, {.v = xidletog } }, + { MODKEY, XK_x, spawn, {.v = xkillcmd } }, + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + { 0, XF86XK_AudioPlay, spawn, {.v = mpdtoggle } }, + { 0, XF86XK_AudioNext, spawn, {.v = mpdnext } }, + { 0, XF86XK_AudioPrev, spawn, {.v = mpdprev } }, + { ShiftMask, XF86XK_AudioPlay, spawn, {.v = plytoggle } }, + { ShiftMask, XF86XK_AudioNext, spawn, {.v = plyfwd } }, + { ShiftMask, XF86XK_AudioPrev, spawn, {.v = plybck } }, + { 0, XF86XK_MonBrightnessUp, spawn, {.v = blightup } }, + { 0, XF86XK_MonBrightnessDown, spawn, {.v = blightdown } }, + { 0, XF86XK_AudioLowerVolume, spawn, {.v = audiodown } }, + { 0, XF86XK_AudioRaiseVolume, spawn, {.v = audioup } }, + { 0, XF86XK_AudioMute, spawn, {.v = audiomute } }, + { 0, XF86XK_AudioMicMute, spawn, {.v = micmute } }, + { Mod4Mask, XK_l, spawn, {.v = lockscr } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY|ShiftMask, XK_j, pushdown, {.i = +1 } }, + { MODKEY|ShiftMask, XK_k, pushup, {.i = -1 } }, + { MODKEY|ControlMask, XK_k, setcfact, {.f = +0.25} }, + { MODKEY|ControlMask, XK_j, setcfact, {.f = -0.25} }, + { MODKEY|ControlMask, XK_o, setcfact, {.f = 0.00} }, + { MODKEY|ShiftMask, XK_h, incnmaster, {.i = +1 } }, + { MODKEY|ShiftMask, XK_l, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_q, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_s, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY|ShiftMask, XK_space, setlayout, {-1} }, + { MODKEY, XK_space, togglefloating, {0} }, + { MODKEY, XK_f, togglefullscr, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY|ControlMask, XK_comma, focusmon, {.i = -1 } }, + { MODKEY|ControlMask, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ControlMask|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ControlMask|ShiftMask, XK_period, tagmon, {.i = +1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) + TAGKEYS( XK_4, 3) + TAGKEYS( XK_5, 4) + TAGKEYS( XK_6, 5) + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_e, quit, {0} }, + { MODKEY|ShiftMask, XK_r, quit, {1} }, + { MODKEY|ShiftMask, XK_Tab, toggleAttachBelow, {0} }, +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button1, sigdwmblocks, {.i = 1} }, + { ClkStatusText, 0, Button2, sigdwmblocks, {.i = 2} }, + { ClkStatusText, 0, Button3, sigdwmblocks, {.i = 3} }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + +void +setlayoutex(const Arg *arg) +{ + setlayout(&((Arg) { .v = &layouts[arg->i] })); +} + +void +viewex(const Arg *arg) +{ + view(&((Arg) { .ui = 1 << arg->ui })); +} + +void +viewall(const Arg *arg) +{ + view(&((Arg){.ui = ~0})); +} + +void +toggleviewex(const Arg *arg) +{ + toggleview(&((Arg) { .ui = 1 << arg->ui })); +} + +void +tagex(const Arg *arg) +{ + tag(&((Arg) { .ui = 1 << arg->ui })); +} + +void +toggletagex(const Arg *arg) +{ + toggletag(&((Arg) { .ui = 1 << arg->ui })); +} + +void +tagall(const Arg *arg) +{ + tag(&((Arg){.ui = ~0})); +} + +/* signal definitions */ +/* signum must be greater than 0 */ +/* trigger signals using `xsetroot -name "fsignal: [ ]"` */ +static Signal signals[] = { + /* signum function */ + { "focusstack", focusstack }, + { "setmfact", setmfact }, + { "togglebar", togglebar }, + { "incnmaster", incnmaster }, + { "togglefloating", togglefloating }, + { "togglefullscr", togglefullscr }, + { "focusmon", focusmon }, + { "tagmon", tagmon }, + { "zoom", zoom }, + { "view", view }, + { "viewall", viewall }, + { "viewex", viewex }, + { "toggleview", view }, + { "toggleviewex", toggleviewex }, + { "tag", tag }, + { "tagall", tagall }, + { "tagex", tagex }, + { "toggletag", tag }, + { "toggletagex", toggletagex }, + { "killclient", killclient }, + { "quit", quit }, + { "setlayout", setlayout }, + { "setlayoutex", setlayoutex }, +}; + diff --git a/config.mk b/config.mk index 7084c33..dbbc526 100644 --- a/config.mk +++ b/config.mk @@ -6,6 +6,7 @@ VERSION = 6.2 # paths PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man +XSESSIONPREFIX = /usr/share/xsessions X11INC = /usr/X11R6/include X11LIB = /usr/X11R6/lib @@ -22,10 +23,10 @@ FREETYPEINC = /usr/include/freetype2 # includes and libs INCS = -I${X11INC} -I${FREETYPEINC} -LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res # flags -CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} LDFLAGS = ${LIBS} diff --git a/drw.c b/drw.c index 8fd1ca4..4cdbcbe 100644 --- a/drw.c +++ b/drw.c @@ -95,6 +95,7 @@ drw_free(Drw *drw) { XFreePixmap(drw->dpy, drw->drawable); XFreeGC(drw->dpy, drw->gc); + drw_fontset_free(drw->fonts); free(drw); } diff --git a/dwm.1 b/dwm.1 index 13b3729..0ec6c60 100644 --- a/dwm.1 +++ b/dwm.1 @@ -12,10 +12,11 @@ environment for the application in use and the task performed. In tiled layouts windows are managed in a master and stacking area. The master area on the left contains one window by default, and the stacking area on the right contains all other windows. The number of master area windows can be -adjusted from zero to an arbitrary number. In monocle layout all windows are -maximised to the screen size. In floating layout windows can be resized and -moved freely. Dialog windows are always managed floating, regardless of the -layout applied. +adjusted from zero to an arbitrary number. Windows in both the master and stack +can be resized vertically, as well as resizing the master area. In monocle +layout all windows are maximised to the screen size. In floating layout windows +can be resized and moved freely. Dialog windows are always managed floating, +regardless of the layout applied. .P Windows are grouped by tags. Each window can be tagged with one or multiple tags. Selecting certain tags displays all windows with these tags. @@ -24,16 +25,19 @@ Each screen contains a small status bar which displays all available tags, the layout, the title of the focused window, and the text read from the root window name property, if the screen is focused. A floating window is indicated with an empty square and a maximised floating window is indicated with a filled square -before the windows title. The selected tags are indicated with a different -color. The tags of the focused window are indicated with a filled square in the -top left corner. The tags which are applied to one or more windows are -indicated with an empty square in the top left corner. +before the window's title. The selected tags are indicated with a different +color. The focused window is represented by a long line before the tags which +are applied to it, and all other windows are represented by dots for all their +corresponding tags. +.P +The attach below patch makes newly spawned windows attach after the currently +selected window .P dwm draws a small border around windows to indicate the focus state. .SH OPTIONS .TP .B \-v -prints version information to standard output, then exits. +prints version information to stderr, then exits. .SH USAGE .SS Status bar .TP @@ -56,41 +60,28 @@ click on a tag label applies that tag to the focused window. click on a tag label adds/removes that tag to/from the focused window. .SS Keyboard commands .TP -.B Mod1\-Shift\-Return +.B Mod1\-Return Start +.BR tabbed(1) +Running .BR st(1). .TP .B Mod1\-p +Spawn passmenu. +.TP +.B Mod1\-d Spawn .BR dmenu(1) for launching other programs. .TP -.B Mod1\-, -Focus previous screen, if any. -.TP -.B Mod1\-. -Focus next screen, if any. -.TP -.B Mod1\-Shift\-, -Send focused window to previous screen, if any. -.TP -.B Mod1\-Shift\-. -Send focused window to next screen, if any. -.TP .B Mod1\-b Toggles bar on and off. .TP -.B Mod1\-t -Sets tiled layout. +.B Mod1\-Shift\-j +Push the selected client window down the stack .TP -.B Mod1\-f -Sets floating layout. -.TP -.B Mod1\-m -Sets monocle layout. -.TP -.B Mod1\-space -Toggles between current and previous layout. +.B Mod1\-Shift\-k +Push the selected client window up the stack .TP .B Mod1\-j Focus next window. @@ -98,50 +89,91 @@ Focus next window. .B Mod1\-k Focus previous window. .TP -.B Mod1\-i -Increase number of windows in master area. +.B Mod1\-, +Increase the number of master windows. .TP -.B Mod1\-d -Decrease number of windows in master area. +.B Mod1\-. +Decrease the number of master windows. +.TP +.B Mod1\-h +Decrease master area size. .TP .B Mod1\-l Increase master area size. .TP -.B Mod1\-h -Decrease master area size. +.B Mod1\-Shift\-h +Increase the size ratio of the selected client .TP -.B Mod1\-Return +.B Mod1\-Shift\-l +Decrease the size ratio of the selected client +.B Mod1\-Shift\-o +Reset the size ratio of the selected client +.TP +.B Mod1\-Shift\-Return Zooms/cycles focused window to/from master area (tiled layouts only). .TP -.B Mod1\-Shift\-c +.B Mod1\-Tab +Toggles to the previously selected tags. +.TP +.B Mod1\-Shift\-q Close focused window. .TP +.B Mod1\-t +Sets tiled layout. +.TP +.B Mod1\-s +Sets floating layout. +.TP +.B Mod1\-m +Sets monocle layout. +.TP .B Mod1\-Shift\-space -Toggle focused window between tiled and floating state. +Toggles between current and previous layout. .TP -.B Mod1\-Tab -Toggles to the previously selected tags. +.B Mod1\-space +Toggles the selected window into the floating state. .TP -.B Mod1\-Shift\-[1..n] -Apply nth tag to focused window. +.B Mod1\-f +Toggle the selected window into the fullscreen state. +.TP +.B Mod1\-0 +View all windows with any tag. .TP .B Mod1\-Shift\-0 Apply all tags to focused window. .TP +.B Mod1\-Shift\-, +Change focus to previous screen, if any. +.TP +.B Mod1\-Shift\-. +Change focus to next screen, if any. +.TP +.B Mod1\-Control\-Shift\-, +Send focused window to previous screen, if any. +.TP +.B Mod1\-Control\-Shift\-. +Send focused window to next screen, if any. +.TP +.B Mod1\-Shift\-[1..n] +Apply nth tag to focused window. +.TP .B Mod1\-Control\-Shift\-[1..n] Add/remove nth tag to/from focused window. .TP .B Mod1\-[1..n] View all windows with nth tag. .TP -.B Mod1\-0 -View all windows with any tag. -.TP .B Mod1\-Control\-[1..n] Add/remove all windows with nth tag to/from the view. .TP -.B Mod1\-Shift\-q +.B Mod1\-Shift\-e Quit dwm. +.TP +.B Mod1\-Shift\-r +Restart dwm. +.TP +.B Mod1\-Shift\-Tab +Toggle AttachBelow patch .SS Mouse commands .TP .B Mod1\-Button1 @@ -155,6 +187,41 @@ Resize focused window while dragging. Tiled windows will be toggled to the float .SH CUSTOMIZATION dwm is customized by creating a custom config.h and (re)compiling the source code. This keeps it fast, secure and simple. +.SH SIGNALS +.TP +.B SIGHUP - 1 +Restart the dwm process. +.TP +.B SIGTERM - 15 +Cleanly terminate the dwm process. +.SH PATCHES +This version of dwm was compiled using the: +.TP +.B dwm-actualfullscreen-20191112-cb3f58a.diff +.TP +.B dwm-attachbelow-toggleable-6.2.diff +.TP +.B dwm-autostart-20161205-bb3bd6f.diff +.TP +.B dwm-cfacts-20200913-61bb8b2.diff +.TP +.B dwm-clientindicators-6.2.diff +.TP +.B dwm-dwmc-6.2.diff +.TP +.B dwm-push_no_master-6.2.diff +.TP +.B dwm-restartsig-20180523-6.2.diff +.TP +.B dwm-statuscmd-signal-6.2.diff +.TP +.B dwm-swallow-20200522-7accbcf.diff +.TP +.B dwm-systray-6.2.diff +.TP +.B dwm-uselessgap-6.2.diff +.TP +.B dwm-zoomswap-6.2.diff .SH SEE ALSO .BR dmenu (1), .BR st (1) diff --git a/dwm.c b/dwm.c index 41c6767..cecbe53 100644 --- a/dwm.c +++ b/dwm.c @@ -40,6 +40,8 @@ #include #endif /* XINERAMA */ #include +#include +#include #include "drw.h" #include "util.h" @@ -52,17 +54,35 @@ #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) #define MOUSEMASK (BUTTONMASK|PointerMotionMask) -#define WIDTH(X) ((X)->w + 2 * (X)->bw) -#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define WIDTH(X) ((X)->w + 2 * (X)->bw + gappx) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw + gappx) #define TAGMASK ((1 << LENGTH(tags)) - 1) #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#define SYSTEM_TRAY_REQUEST_DOCK 0 + +/* XEMBED messages */ +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_FOCUS_IN 4 +#define XEMBED_MODALITY_ON 10 + +#define XEMBED_MAPPED (1 << 0) +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_WINDOW_DEACTIVATE 2 + +#define VERSION_MAJOR 0 +#define VERSION_MINOR 0 +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR + /* enums */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ enum { SchemeNorm, SchemeSel }; /* color schemes */ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ @@ -87,14 +107,17 @@ typedef struct Client Client; struct Client { char name[256]; float mina, maxa; + float cfact; int x, y, w, h; int oldx, oldy, oldw, oldh; int basew, baseh, incw, inch, maxw, maxh, minw, minh; int bw, oldbw; unsigned int tags; - int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow; + pid_t pid; Client *next; Client *snext; + Client *swallowing; Monitor *mon; Window win; }; @@ -106,6 +129,11 @@ typedef struct { const Arg arg; } Key; +typedef struct { + const char * sig; + void (*func)(const Arg *); +} Signal; + typedef struct { const char *symbol; void (*arrange)(Monitor *); @@ -138,16 +166,27 @@ typedef struct { const char *title; unsigned int tags; int isfloating; + int isterminal; + int noswallow; int monitor; } Rule; +typedef struct Systray Systray; +struct Systray { + Window win; + Client *icons; +}; + /* function declarations */ static void applyrules(Client *c); static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); static void arrange(Monitor *m); static void arrangemon(Monitor *m); static void attach(Client *c); +static void attachBelow(Client *c); +static void toggleAttachBelow(); static void attachstack(Client *c); +static int fake_signal(void); static void buttonpress(XEvent *e); static void checkotherwm(void); static void cleanup(void); @@ -156,6 +195,7 @@ static void clientmessage(XEvent *e); static void configure(Client *c); static void configurenotify(XEvent *e); static void configurerequest(XEvent *e); +static void copyvalidchars(char *text, char *rawtext); static Monitor *createmon(void); static void destroynotify(XEvent *e); static void detach(Client *c); @@ -165,12 +205,16 @@ static void drawbar(Monitor *m); static void drawbars(void); static void enternotify(XEvent *e); static void expose(XEvent *e); +static Client *findbefore(Client *c); static void focus(Client *c); static void focusin(XEvent *e); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); +static int getdwmblockspid(); +static Atom getatomprop(Client *c, Atom prop); static int getrootptr(int *x, int *y); static long getstate(Window w); +static unsigned int getsystraywidth(); static int gettextprop(Window w, Atom atom, char *text, unsigned int size); static void grabbuttons(Client *c, int focused); static void grabkeys(void); @@ -184,33 +228,46 @@ static void monocle(Monitor *m); static void motionnotify(XEvent *e); static void movemouse(const Arg *arg); static Client *nexttiled(Client *c); -static void pop(Client *); +//static void pop(Client *); +static Client *prevtiled(Client *c); static void propertynotify(XEvent *e); +static void pushdown(const Arg *arg); +static void pushup(const Arg *arg); static void quit(const Arg *arg); static Monitor *recttomon(int x, int y, int w, int h); +static void removesystrayicon(Client *i); static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resizebarwin(Monitor *m); static void resizeclient(Client *c, int x, int y, int w, int h); static void resizemouse(const Arg *arg); +static void resizerequest(XEvent *e); static void restack(Monitor *m); static void run(void); +static void runAutostart(void); static void scan(void); -static int sendevent(Client *c, Atom proto); +static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); static void sendmon(Client *c, Monitor *m); static void setclientstate(Client *c, long state); static void setfocus(Client *c); static void setfullscreen(Client *c, int fullscreen); static void setlayout(const Arg *arg); +static void setcfact(const Arg *arg); static void setmfact(const Arg *arg); static void setup(void); static void seturgent(Client *c, int urg); static void showhide(Client *c); static void sigchld(int unused); +static void sigdwmblocks(const Arg *arg); +static void sighup(int unused); +static void sigterm(int unused); static void spawn(const Arg *arg); +static Monitor *systraytomon(Monitor *m); static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *); static void togglebar(const Arg *arg); static void togglefloating(const Arg *arg); +static void togglefullscr(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unfocus(Client *c, int setfocus); @@ -223,20 +280,36 @@ static int updategeom(void); static void updatenumlockmask(void); static void updatesizehints(Client *c); static void updatestatus(void); +static void updatesystray(void); +static void updatesystrayicongeom(Client *i, int w, int h); +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); static void updatetitle(Client *c); static void updatewindowtype(Client *c); static void updatewmhints(Client *c); static void view(const Arg *arg); static Client *wintoclient(Window w); static Monitor *wintomon(Window w); +static Client *wintosystrayicon(Window w); static int xerror(Display *dpy, XErrorEvent *ee); static int xerrordummy(Display *dpy, XErrorEvent *ee); static int xerrorstart(Display *dpy, XErrorEvent *ee); static void zoom(const Arg *arg); +static pid_t getparentprocess(pid_t p); +static int isdescprocess(pid_t p, pid_t c); +static Client *swallowingclient(Window w); +static Client *termforwin(const Client *c); +static pid_t winpid(Window w); + /* variables */ +static Client *prevzoom = NULL; +static Systray *systray = NULL; static const char broken[] = "broken"; static char stext[256]; +static char rawstext[256]; +static int dwmblockssig; +pid_t dwmblockspid = 0; +static int scanner; static int screen; static int sw, sh; /* X display screen geometry width, height */ static int bh, blw = 0; /* bar geometry */ @@ -257,9 +330,11 @@ static void (*handler[LASTEvent]) (XEvent *) = { [MapRequest] = maprequest, [MotionNotify] = motionnotify, [PropertyNotify] = propertynotify, + [ResizeRequest] = resizerequest, [UnmapNotify] = unmapnotify }; -static Atom wmatom[WMLast], netatom[NetLast]; +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; +static int restart = 0; static int running = 1; static Cur *cursor[CurLast]; static Clr **scheme; @@ -268,6 +343,8 @@ static Drw *drw; static Monitor *mons, *selmon; static Window root, wmcheckwin; +static xcb_connection_t *xcon; + /* configuration, allows nested code to access above variables */ #include "config.h" @@ -285,6 +362,7 @@ applyrules(Client *c) XClassHint ch = { NULL, NULL }; /* rule matching */ + c->noswallow = -1; c->isfloating = 0; c->tags = 0; XGetClassHint(dpy, c->win, &ch); @@ -297,6 +375,8 @@ applyrules(Client *c) && (!r->class || strstr(class, r->class)) && (!r->instance || strstr(instance, r->instance))) { + c->isterminal = r->isterminal; + c->noswallow = r->noswallow; c->isfloating = r->isfloating; c->tags |= r->tags; for (m = mons; m && m->num != r->monitor; m = m->next); @@ -405,6 +485,26 @@ attach(Client *c) c->next = c->mon->clients; c->mon->clients = c; } +void +attachBelow(Client *c) +{ + //If there is nothing on the monitor or the selected client is floating, attach as normal + if(c->mon->sel == NULL || c->mon->sel->isfloating) { + attach(c); + return; + } + + //Set the new client's next property to the same as the currently selected clients next + c->next = c->mon->sel->next; + //Set the currently selected clients next property to the new client + c->mon->sel->next = c; + +} + +void toggleAttachBelow() +{ + attachbelow = !attachbelow; +} void attachstack(Client *c) @@ -413,6 +513,61 @@ attachstack(Client *c) c->mon->stack = c; } +void +swallow(Client *p, Client *c) +{ + Client *s; + + if (c->noswallow > 0 || c->isterminal) + return; + if (c->noswallow < 0 && !swallowfloating && c->isfloating) + return; + + detach(c); + detachstack(c); + + setclientstate(c, WithdrawnState); + XUnmapWindow(dpy, p->win); + + p->swallowing = c; + c->mon = p->mon; + + Window w = p->win; + p->win = c->win; + c->win = w; + + XChangeProperty(dpy, c->win, netatom[NetClientList], XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(p->win), 1); + + updatetitle(p); + s = scanner ? c : p; + XMoveResizeWindow(dpy, p->win, s->x, s->y, s->w, s->h); + arrange(p->mon); + configure(p); + updateclientlist(); +} + +void +unswallow(Client *c) +{ + c->win = c->swallowing->win; + + free(c->swallowing); + c->swallowing = NULL; + + XDeleteProperty(dpy, c->win, netatom[NetClientList]); + + /* unfullscreen the client */ + setfullscreen(c, 0); + updatetitle(c); + arrange(c->mon); + XMapWindow(dpy, c->win); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + setclientstate(c, NormalState); + focus(NULL); + arrange(c->mon); +} + void buttonpress(XEvent *e) { @@ -439,9 +594,25 @@ buttonpress(XEvent *e) arg.ui = 1 << i; } else if (ev->x < x + blw) click = ClkLtSymbol; - else if (ev->x > selmon->ww - TEXTW(stext)) + else if (ev->x > (x = selmon->ww - (int)TEXTW(stext) + lrpad - getsystraywidth())) { click = ClkStatusText; - else + char *text = rawstext; + int i = -1; + char ch; + dwmblockssig = 0; + while (text[++i]) { + if ((unsigned char)text[i] < ' ') { + ch = text[i]; + text[i] = '\0'; + x += TEXTW(text) - lrpad; + text[i] = ch; + text += i+1; + i = -1; + if (x >= ev->x) break; + dwmblockssig = ch; + } + } + } else click = ClkWinTitle; } else if ((c = wintoclient(ev->window))) { focus(c); @@ -482,6 +653,11 @@ cleanup(void) XUngrabKey(dpy, AnyKey, AnyModifier, root); while (mons) cleanupmon(mons); + if (showsystray) { + XUnmapWindow(dpy, systray->win); + XDestroyWindow(dpy, systray->win); + free(systray); + } for (i = 0; i < CurLast; i++) drw_cur_free(drw, cursor[i]); for (i = 0; i < LENGTH(colors); i++) @@ -512,9 +688,57 @@ cleanupmon(Monitor *mon) void clientmessage(XEvent *e) { + XWindowAttributes wa; + XSetWindowAttributes swa; XClientMessageEvent *cme = &e->xclient; Client *c = wintoclient(cme->window); + if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { + /* add systray icons */ + if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { + if (!(c = (Client *)calloc(1, sizeof(Client)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Client)); + if (!(c->win = cme->data.l[2])) { + free(c); + return; + } + c->mon = selmon; + c->next = systray->icons; + systray->icons = c; + if (!XGetWindowAttributes(dpy, c->win, &wa)) { + /* use sane defaults */ + wa.width = bh; + wa.height = bh; + wa.border_width = 0; + } + c->x = c->oldx = c->y = c->oldy = 0; + c->w = c->oldw = wa.width; + c->h = c->oldh = wa.height; + c->oldbw = wa.border_width; + c->bw = 0; + c->isfloating = True; + /* reuse tags field as mapped status */ + c->tags = 1; + updatesizehints(c); + updatesystrayicongeom(c, wa.width, wa.height); + XAddToSaveSet(dpy, c->win); + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); + XReparentWindow(dpy, c->win, systray->win, 0, 0); + /* use parents background color */ + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); + /* FIXME not sure if I have to send these events, too */ + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); + XSync(dpy, False); + resizebarwin(selmon); + updatesystray(); + setclientstate(c, NormalState); + } + return; + } if (!c) return; if (cme->message_type == netatom[NetWMState]) { @@ -567,7 +791,7 @@ configurenotify(XEvent *e) for (c = m->clients; c; c = c->next) if (c->isfullscreen) resizeclient(c, m->mx, m->my, m->mw, m->mh); - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + resizebarwin(m); } focus(NULL); arrange(NULL); @@ -627,6 +851,19 @@ configurerequest(XEvent *e) XSync(dpy, False); } +void +copyvalidchars(char *text, char *rawtext) +{ + int i = -1, j = 0; + + while(rawtext[++i]) { + if ((unsigned char)rawtext[i] >= ' ') { + text[j++] = rawtext[i]; + } + } + text[j] = '\0'; +} + Monitor * createmon(void) { @@ -652,6 +889,15 @@ destroynotify(XEvent *e) if ((c = wintoclient(ev->window))) unmanage(c, 1); + + else if ((c = wintosystrayicon(ev->window))) { + removesystrayicon(c); + resizebarwin(selmon); + updatesystray(); + } + + else if ((c = swallowingclient(ev->window))) + unmanage(c->swallowing, 1); } void @@ -695,18 +941,24 @@ dirtomon(int dir) void drawbar(Monitor *m) { - int x, w, sw = 0; + int indn; + int x, w, tw = 0, stw = 0; int boxs = drw->fonts->h / 9; int boxw = drw->fonts->h / 6 + 2; unsigned int i, occ = 0, urg = 0; Client *c; + if(showsystray && m == systraytomon(m)) + stw = getsystraywidth(); + /* draw status first so it can be overdrawn by tags later */ if (m == selmon) { /* status is only drawn on selected monitor */ drw_setscheme(drw, scheme[SchemeNorm]); - sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ - drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0); - } + tw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */ + drw_text(drw, m->ww - tw - stw, 0, tw, bh, lrpad / 2 - 2, stext, 0); + } + + resizebarwin(m); for (c = m->clients; c; c = c->next) { occ |= c->tags; @@ -715,20 +967,24 @@ drawbar(Monitor *m) } x = 0; for (i = 0; i < LENGTH(tags); i++) { + indn = 0; w = TEXTW(tags[i]); drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); - if (occ & 1 << i) - drw_rect(drw, x + boxs, boxs, boxw, boxw, - m == selmon && selmon->sel && selmon->sel->tags & 1 << i, - urg & 1 << i); + for (c = m->clients; c; c = c->next) { + if (c->tags & (1 << i)) { + drw_rect(drw, x, 1 + (indn * 2), selmon->sel == c ? 6 : 1, 1, 1, urg & 1 << i); + indn++; + } + } + x += w; } w = blw = TEXTW(m->ltsymbol); drw_setscheme(drw, scheme[SchemeNorm]); x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); - if ((w = m->ww - sw - x) > bh) { + if ((w = m->ww - tw - stw - x) > bh) { if (m->sel) { drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); @@ -739,7 +995,7 @@ drawbar(Monitor *m) drw_rect(drw, x, 0, w, bh, 1, 1); } } - drw_map(drw, m->barwin, 0, 0, m->ww, bh); + drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); } void @@ -776,8 +1032,21 @@ expose(XEvent *e) Monitor *m; XExposeEvent *ev = &e->xexpose; - if (ev->count == 0 && (m = wintomon(ev->window))) + if (ev->count == 0 && (m = wintomon(ev->window))) { drawbar(m); + if (m == selmon) + updatesystray(); + } +} + +Client * +findbefore(Client *c) +{ + Client *tmp; + if (c == selmon->clients) + return NULL; + for (tmp = selmon->clients; tmp && tmp->next != c; tmp = tmp->next); + return tmp; } void @@ -862,15 +1131,34 @@ getatomprop(Client *c, Atom prop) unsigned long dl; unsigned char *p = NULL; Atom da, atom = None; + /* FIXME getatomprop should return the number of items and a pointer to + * the stored data instead of this workaround */ + Atom req = XA_ATOM; + if (prop == xatom[XembedInfo]) + req = xatom[XembedInfo]; - if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, &da, &di, &dl, &dl, &p) == Success && p) { atom = *(Atom *)p; + if (da == xatom[XembedInfo] && dl == 2) + atom = ((Atom *)p)[1]; XFree(p); } return atom; } +int +getdwmblockspid() +{ + char buf[16]; + FILE *fp = popen("pidof -s dwmblocks", "r"); + fgets(buf, sizeof(buf), fp); + pid_t pid = strtoul(buf, NULL, 10); + pclose(fp); + dwmblockspid = pid; + return pid != 0 ? 0 : -1; +} + int getrootptr(int *x, int *y) { @@ -899,6 +1187,16 @@ getstate(Window w) return result; } +unsigned int +getsystraywidth() +{ + unsigned int w = 0; + Client *i; + if(showsystray) + for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; + return w ? w + systrayspacing : 1; +} + int gettextprop(Window w, Atom atom, char *text, unsigned int size) { @@ -998,12 +1296,55 @@ keypress(XEvent *e) keys[i].func(&(keys[i].arg)); } +int +fake_signal(void) +{ + char fsignal[256]; + char indicator[9] = "fsignal:"; + char str_sig[50]; + char param[16]; + int i, len_str_sig, n, paramn; + size_t len_fsignal, len_indicator = strlen(indicator); + Arg arg; + + // Get root name property + if (gettextprop(root, XA_WM_NAME, fsignal, sizeof(fsignal))) { + len_fsignal = strlen(fsignal); + + // Check if this is indeed a fake signal + if (len_indicator > len_fsignal ? 0 : strncmp(indicator, fsignal, len_indicator) == 0) { + paramn = sscanf(fsignal+len_indicator, "%s%n%s%n", str_sig, &len_str_sig, param, &n); + + if (paramn == 1) arg = (Arg) {0}; + else if (paramn > 2) return 1; + else if (strncmp(param, "i", n - len_str_sig) == 0) + sscanf(fsignal + len_indicator + n, "%i", &(arg.i)); + else if (strncmp(param, "ui", n - len_str_sig) == 0) + sscanf(fsignal + len_indicator + n, "%u", &(arg.ui)); + else if (strncmp(param, "f", n - len_str_sig) == 0) + sscanf(fsignal + len_indicator + n, "%f", &(arg.f)); + else return 1; + + // Check if a signal was found, and if so handle it + for (i = 0; i < LENGTH(signals); i++) + if (strncmp(str_sig, signals[i].sig, len_str_sig) == 0 && signals[i].func) + signals[i].func(&(arg)); + + // A fake signal was sent + return 1; + } + } + + // No fake signal was sent, so proceed with update + return 0; +} + void killclient(const Arg *arg) { if (!selmon->sel) return; - if (!sendevent(selmon->sel, wmatom[WMDelete])) { + if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { XGrabServer(dpy); XSetErrorHandler(xerrordummy); XSetCloseDownMode(dpy, DestroyAll); @@ -1017,18 +1358,20 @@ killclient(const Arg *arg) void manage(Window w, XWindowAttributes *wa) { - Client *c, *t = NULL; + Client *c, *t = NULL, *term = NULL; Window trans = None; XWindowChanges wc; c = ecalloc(1, sizeof(Client)); c->win = w; + c->pid = winpid(w); /* geometry */ c->x = c->oldx = wa->x; c->y = c->oldy = wa->y; c->w = c->oldw = wa->width; c->h = c->oldh = wa->height; c->oldbw = wa->border_width; + c->cfact = 1.0; updatetitle(c); if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { @@ -1037,6 +1380,7 @@ manage(Window w, XWindowAttributes *wa) } else { c->mon = selmon; applyrules(c); + term = termforwin(c); } if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) @@ -1062,7 +1406,10 @@ manage(Window w, XWindowAttributes *wa) c->isfloating = c->oldstate = trans != None || c->isfixed; if (c->isfloating) XRaiseWindow(dpy, c->win); - attach(c); + if( attachbelow ) + attachBelow(c); + else + attach(c); attachstack(c); XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, (unsigned char *) &(c->win), 1); @@ -1073,6 +1420,8 @@ manage(Window w, XWindowAttributes *wa) c->mon->sel = c; arrange(c->mon); XMapWindow(dpy, c->win); + if (term) + swallow(term, c); focus(NULL); } @@ -1091,6 +1440,12 @@ maprequest(XEvent *e) { static XWindowAttributes wa; XMapRequestEvent *ev = &e->xmaprequest; + Client *i; + if ((i = wintosystrayicon(ev->window))) { + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); + resizebarwin(selmon); + updatesystray(); + } if (!XGetWindowAttributes(dpy, ev->window, &wa)) return; @@ -1199,6 +1554,7 @@ nexttiled(Client *c) return c; } +/* void pop(Client *c) { @@ -1207,6 +1563,17 @@ pop(Client *c) focus(c); arrange(c->mon); } +*/ + +Client * +prevtiled(Client *c) { + Client *p, *r; + + for(p = selmon->clients, r = NULL; p && p != c; p = p->next) + if(!p->isfloating && ISVISIBLE(p)) + r = p; + return r; +} void propertynotify(XEvent *e) @@ -1215,8 +1582,20 @@ propertynotify(XEvent *e) Window trans; XPropertyEvent *ev = &e->xproperty; - if ((ev->window == root) && (ev->atom == XA_WM_NAME)) - updatestatus(); + if ((c = wintosystrayicon(ev->window))) { + if (ev->atom == XA_WM_NORMAL_HINTS) { + updatesizehints(c); + updatesystrayicongeom(c, c->w, c->h); + } else + updatesystrayiconstate(c, ev); + resizebarwin(selmon); + updatesystray(); + + } + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) { + if (!fake_signal()) + updatestatus(); + } else if (ev->state == PropertyDelete) return; /* ignore */ else if ((c = wintoclient(ev->window))) { @@ -1245,9 +1624,41 @@ propertynotify(XEvent *e) } } +void +pushdown(const Arg *arg) { + Client *sel = selmon->sel, *c; + + if(!sel || sel->isfloating || sel == nexttiled(selmon->clients)) + return; + if((c = nexttiled(sel->next))) { + detach(sel); + sel->next = c->next; + c->next = sel; + } + focus(sel); + arrange(selmon); +} + +void +pushup(const Arg *arg) { + Client *sel = selmon->sel, *c; + + if(!sel || sel->isfloating) + return; + if((c = prevtiled(sel)) && c != nexttiled(selmon->clients)) { + detach(sel); + sel->next = c; + for(c = selmon->clients; c->next != sel->next; c = c->next); + c->next = sel; + } + focus(sel); + arrange(selmon); +} + void quit(const Arg *arg) { + if(arg->i) restart = 1; running = 0; } @@ -1265,6 +1676,20 @@ recttomon(int x, int y, int w, int h) return r; } +void +removesystrayicon(Client *i) +{ + Client **ii; + + if (!showsystray || !i) + return; + for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); + if (ii) + *ii = i->next; + free(i); +} + + void resize(Client *c, int x, int y, int w, int h, int interact) { @@ -1272,16 +1697,48 @@ resize(Client *c, int x, int y, int w, int h, int interact) resizeclient(c, x, y, w, h); } +void +resizebarwin(Monitor *m) { + unsigned int w = m->ww; + if (showsystray && m == systraytomon(m)) + w -= getsystraywidth(); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); +} + void resizeclient(Client *c, int x, int y, int w, int h) { XWindowChanges wc; + unsigned int n; + unsigned int gapoffset; + unsigned int gapincr; + Client *nbc; - c->oldx = c->x; c->x = wc.x = x; - c->oldy = c->y; c->y = wc.y = y; - c->oldw = c->w; c->w = wc.width = w; - c->oldh = c->h; c->h = wc.height = h; wc.border_width = c->bw; + + /* Get number of clients for the selected monitor */ + for (n = 0, nbc = nexttiled(selmon->clients); nbc; nbc = nexttiled(nbc->next), n++); + + /* Do nothing if layout is floating */ + if (c->isfloating || selmon->lt[selmon->sellt]->arrange == NULL) { + gapincr = gapoffset = 0; + } else { + /* Remove border and gap if layout is monocle or only one client */ + if (selmon->lt[selmon->sellt]->arrange == monocle || n == 1) { + gapoffset = 0; + gapincr = -2 * borderpx; + wc.border_width = 0; + } else { + gapoffset = gappx; + gapincr = 2 * gappx; + } + } + + c->oldx = c->x; c->x = wc.x = x + gapoffset; + c->oldy = c->y; c->y = wc.y = y + gapoffset; + c->oldw = c->w; c->w = wc.width = w - gapincr; + c->oldh = c->h; c->h = wc.height = h - gapincr; + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); configure(c); XSync(dpy, False); @@ -1344,6 +1801,19 @@ resizemouse(const Arg *arg) } } +void +resizerequest(XEvent *e) +{ + XResizeRequestEvent *ev = &e->xresizerequest; + Client *i; + + if ((i = wintosystrayicon(ev->window))) { + updatesystrayicongeom(i, ev->width, ev->height); + resizebarwin(selmon); + updatesystray(); + } +} + void restack(Monitor *m) { @@ -1380,10 +1850,18 @@ run(void) handler[ev.type](&ev); /* call handler */ } +void +runAutostart(void) { + system("cd ~; ./.config/dwm/autostart_blocking.sh"); + system("cd ~; ./.config/dwm/autostart.sh &"); +} + void scan(void) { + scanner = 1; unsigned int i, num; + char swin[256]; Window d1, d2, *wins = NULL; XWindowAttributes wa; @@ -1394,6 +1872,8 @@ scan(void) continue; if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) manage(wins[i], &wa); + else if (gettextprop(wins[i], netatom[NetClientList], swin, sizeof swin)) + manage(wins[i], &wa); } for (i = 0; i < num; i++) { /* now the transients */ if (!XGetWindowAttributes(dpy, wins[i], &wa)) @@ -1405,6 +1885,7 @@ scan(void) if (wins) XFree(wins); } + scanner = 0; } void @@ -1417,7 +1898,10 @@ sendmon(Client *c, Monitor *m) detachstack(c); c->mon = m; c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ - attach(c); + if( attachbelow ) + attachBelow(c); + else + attach(c); attachstack(c); focus(NULL); arrange(NULL); @@ -1433,26 +1917,36 @@ setclientstate(Client *c, long state) } int -sendevent(Client *c, Atom proto) +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) { int n; - Atom *protocols; + Atom *protocols, mt; int exists = 0; XEvent ev; - if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { - while (!exists && n--) - exists = protocols[n] == proto; - XFree(protocols); + if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { + mt = wmatom[WMProtocols]; + if (XGetWMProtocols(dpy, w, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + } + else { + exists = True; + mt = proto; } if (exists) { ev.type = ClientMessage; - ev.xclient.window = c->win; - ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.window = w; + ev.xclient.message_type = mt; ev.xclient.format = 32; - ev.xclient.data.l[0] = proto; - ev.xclient.data.l[1] = CurrentTime; - XSendEvent(dpy, c->win, False, NoEventMask, &ev); + ev.xclient.data.l[0] = d0; + ev.xclient.data.l[1] = d1; + ev.xclient.data.l[2] = d2; + ev.xclient.data.l[3] = d3; + ev.xclient.data.l[4] = d4; + XSendEvent(dpy, w, False, mask, &ev); } return exists; } @@ -1466,7 +1960,7 @@ setfocus(Client *c) XA_WINDOW, 32, PropModeReplace, (unsigned char *) &(c->win), 1); } - sendevent(c, wmatom[WMTakeFocus]); + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); } void @@ -1511,6 +2005,23 @@ setlayout(const Arg *arg) drawbar(selmon); } +void setcfact(const Arg *arg) { + float f; + Client *c; + + c = selmon->sel; + + if(!arg || !c || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f + c->cfact; + if(arg->f == 0.0) + f = 1.0; + else if(f < 0.25 || f > 4.0) + return; + c->cfact = f; + arrange(selmon); +} + /* arg > 1.0 will set mfact absolutely */ void setmfact(const Arg *arg) @@ -1520,7 +2031,7 @@ setmfact(const Arg *arg) if (!arg || !selmon->lt[selmon->sellt]->arrange) return; f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; - if (f < 0.05 || f > 0.95) + if (f < 0.1 || f > 0.9) return; selmon->mfact = f; arrange(selmon); @@ -1536,6 +2047,9 @@ setup(void) /* clean up any zombies immediately */ sigchld(0); + signal(SIGHUP, sighup); + signal(SIGTERM, sigterm); + /* init screen */ screen = DefaultScreen(dpy); sw = DisplayWidth(dpy, screen); @@ -1555,6 +2069,10 @@ setup(void) wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); + netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); @@ -1562,6 +2080,9 @@ setup(void) netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + xatom[Manager] = XInternAtom(dpy, "MANAGER", False); + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); /* init cursors */ cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); cursor[CurResize] = drw_cur_create(drw, XC_sizing); @@ -1570,6 +2091,8 @@ setup(void) scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); for (i = 0; i < LENGTH(colors); i++) scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init system tray */ + updatesystray(); /* init bars */ updatebars(); updatestatus(); @@ -1636,6 +2159,37 @@ sigchld(int unused) while (0 < waitpid(-1, NULL, WNOHANG)); } +void +sighup(int unused) +{ + Arg a = {.i = 1}; + quit(&a); +} + +void +sigterm(int unused) +{ + Arg a = {.i = 0}; + quit(&a); +} + +void +sigdwmblocks(const Arg *arg) +{ + union sigval sv; + sv.sival_int = (dwmblockssig << 8) | arg->i; + if (!dwmblockspid) + if (getdwmblockspid() == -1) + return; + + if (sigqueue(dwmblockspid, SIGUSR1, sv) == -1) { + if (errno == ESRCH) { + if (!getdwmblockspid()) + sigqueue(dwmblockspid, SIGUSR1, sv); + } + } +} + void spawn(const Arg *arg) { @@ -1674,9 +2228,15 @@ void tile(Monitor *m) { unsigned int i, n, h, mw, my, ty; + float mfacts = 0, sfacts = 0; Client *c; - for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { + if (n < m->nmaster) + mfacts += c->cfact; + else + sfacts += c->cfact; + } if (n == 0) return; @@ -1686,13 +2246,19 @@ tile(Monitor *m) mw = m->ww; for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) if (i < m->nmaster) { - h = (m->wh - my) / (MIN(n, m->nmaster) - i); + h = (m->wh - my) * (c->cfact / mfacts); resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); - my += HEIGHT(c); + if (my + HEIGHT(c) < m->wh) { + my += HEIGHT(c); + mfacts -= c->cfact; + } } else { - h = (m->wh - ty) / (n - i); + h = (m->wh - ty) * (c->cfact / sfacts); resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); - ty += HEIGHT(c); + if (ty + HEIGHT(c) < m->wh) { + ty += HEIGHT(c); + sfacts -= c->cfact; + } } } @@ -1701,7 +2267,18 @@ togglebar(const Arg *arg) { selmon->showbar = !selmon->showbar; updatebarpos(selmon); - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + resizebarwin(selmon); + if (showsystray) { + XWindowChanges wc; + if (!selmon->showbar) + wc.y = -bh; + else if (selmon->showbar) { + wc.y = 0; + if (!selmon->topbar) + wc.y = selmon->mh - bh; + } + XConfigureWindow(dpy, systray->win, CWY, &wc); + } arrange(selmon); } @@ -1719,6 +2296,13 @@ togglefloating(const Arg *arg) arrange(selmon); } +void +togglefullscr(const Arg *arg) +{ + if(selmon->sel) + setfullscreen(selmon->sel, !selmon->sel->isfullscreen); +} + void toggletag(const Arg *arg) { @@ -1765,6 +2349,20 @@ unmanage(Client *c, int destroyed) Monitor *m = c->mon; XWindowChanges wc; + if (c->swallowing) { + unswallow(c); + return; + } + + Client *s = swallowingclient(c->win); + if (s) { + free(s->swallowing); + s->swallowing = NULL; + arrange(m); + focus(NULL); + return; + } + detach(c); detachstack(c); if (!destroyed) { @@ -1779,9 +2377,12 @@ unmanage(Client *c, int destroyed) XUngrabServer(dpy); } free(c); - focus(NULL); - updateclientlist(); - arrange(m); + + if (!s) { + arrange(m); + focus(NULL); + updateclientlist(); + } } void @@ -1796,11 +2397,18 @@ unmapnotify(XEvent *e) else unmanage(c, 0); } + else if ((c = wintosystrayicon(ev->window))) { + /* KLUDGE! sometimes icons occasionally unmap their windows, but do + * _not_ destroy them. We map those windows back */ + XMapRaised(dpy, c->win); + updatesystray(); + } } void updatebars(void) { + unsigned int w; Monitor *m; XSetWindowAttributes wa = { .override_redirect = True, @@ -1811,10 +2419,15 @@ updatebars(void) for (m = mons; m; m = m->next) { if (m->barwin) continue; - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), + w = m->ww; + if (showsystray && m == systraytomon(m)) + w -= getsystraywidth(); + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + if (showsystray && m == systraytomon(m)) + XMapRaised(dpy, systray->win); XMapRaised(dpy, m->barwin); XSetClassHint(dpy, m->barwin, &ch); } @@ -1897,7 +2510,10 @@ updategeom(void) m->clients = c->next; detachstack(c); c->mon = mons; - attach(c); + if( attachbelow ) + attachBelow(c); + else + attach(c); attachstack(c); } if (m == selmon) @@ -1987,9 +2603,126 @@ updatesizehints(Client *c) void updatestatus(void) { - if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) strcpy(stext, "dwm-"VERSION); + else + copyvalidchars(stext, rawstext); drawbar(selmon); + updatesystray(); +} + +void +updatesystrayicongeom(Client *i, int w, int h) +{ + if (i) { + i->h = bh; + if (w == h) + i->w = bh; + else if (h == bh) + i->w = w; + else + i->w = (int) ((float)bh * ((float)w / (float)h)); + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); + /* force icons into the systray dimensions if they don't want to */ + if (i->h > bh) { + if (i->w == i->h) + i->w = bh; + else + i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); + i->h = bh; + } + } +} + +void +updatesystrayiconstate(Client *i, XPropertyEvent *ev) +{ + long flags; + int code = 0; + + if (!showsystray || !i || ev->atom != xatom[XembedInfo] || + !(flags = getatomprop(i, xatom[XembedInfo]))) + return; + + if (flags & XEMBED_MAPPED && !i->tags) { + i->tags = 1; + code = XEMBED_WINDOW_ACTIVATE; + XMapRaised(dpy, i->win); + setclientstate(i, NormalState); + } + else if (!(flags & XEMBED_MAPPED) && i->tags) { + i->tags = 0; + code = XEMBED_WINDOW_DEACTIVATE; + XUnmapWindow(dpy, i->win); + setclientstate(i, WithdrawnState); + } + else + return; + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, + systray->win, XEMBED_EMBEDDED_VERSION); +} + +void +updatesystray(void) +{ + XSetWindowAttributes wa; + XWindowChanges wc; + Client *i; + Monitor *m = systraytomon(NULL); + unsigned int x = m->mx + m->mw; + unsigned int w = 1; + + if (!showsystray) + return; + if (!systray) { + /* init systray */ + if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); + systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); + wa.event_mask = ButtonPressMask | ExposureMask; + wa.override_redirect = True; + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + XSelectInput(dpy, systray->win, SubstructureNotifyMask); + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); + XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); + XMapRaised(dpy, systray->win); + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); + if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); + XSync(dpy, False); + } + else { + fprintf(stderr, "dwm: unable to obtain system tray.\n"); + free(systray); + systray = NULL; + return; + } + } + for (w = 0, i = systray->icons; i; i = i->next) { + /* make sure the background color stays the same */ + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); + XMapRaised(dpy, i->win); + w += systrayspacing; + i->x = w; + XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); + w += i->w; + if (i->mon != m) + i->mon = m; + } + w = w ? w + systrayspacing : 1; + x -= w; + XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); + wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; + wc.stack_mode = Above; wc.sibling = m->barwin; + XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); + XMapWindow(dpy, systray->win); + XMapSubwindows(dpy, systray->win); + /* redraw background */ + XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); + XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); + XSync(dpy, False); } void @@ -2044,6 +2777,110 @@ view(const Arg *arg) arrange(selmon); } +pid_t +winpid(Window w) +{ + pid_t result = 0; + + xcb_res_client_id_spec_t spec = {0}; + spec.client = w; + spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; + + xcb_generic_error_t *e = NULL; + xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); + xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); + + if (!r) + return (pid_t)0; + + xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); + for (; i.rem; xcb_res_client_id_value_next(&i)) { + spec = i.data->spec; + if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { + uint32_t *t = xcb_res_client_id_value_value(i.data); + result = *t; + break; + } + } + + free(r); + + if (result == (pid_t)-1) + result = 0; + return result; +} + +pid_t +getparentprocess(pid_t p) +{ + unsigned int v = 0; + +#if defined(__linux__) + FILE *f; + char buf[256]; + snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); + + if (!(f = fopen(buf, "r"))) + return (pid_t)0; + + if (fscanf(f, "%*u %*s %*c %u", (unsigned *)&v) != 1) + v = (pid_t)0; + fclose(f); +#elif defined(__FreeBSD__) + struct kinfo_proc *proc = kinfo_getproc(p); + if (!proc) + return (pid_t)0; + + v = proc->ki_ppid; + free(proc); +#endif + return (pid_t)v; +} + +int +isdescprocess(pid_t p, pid_t c) +{ + while (p != c && c != 0) + c = getparentprocess(c); + + return (int)c; +} + +Client * +termforwin(const Client *w) +{ + Client *c; + Monitor *m; + + if (!w->pid || w->isterminal) + return NULL; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) + return c; + } + } + + return NULL; +} + +Client * +swallowingclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->swallowing && c->swallowing->win == w) + return c; + } + } + + return NULL; +} + Client * wintoclient(Window w) { @@ -2057,6 +2894,16 @@ wintoclient(Window w) return NULL; } +Client * +wintosystrayicon(Window w) { + Client *i = NULL; + + if (!showsystray || !w) + return i; + for (i = systray->icons; i && i->win != w; i = i->next) ; + return i; +} + Monitor * wintomon(Window w) { @@ -2110,18 +2957,58 @@ xerrorstart(Display *dpy, XErrorEvent *ee) return -1; } +Monitor * +systraytomon(Monitor *m) { + Monitor *t; + int i, n; + if(!systraypinning) { + if(!m) + return selmon; + return m == selmon ? m : NULL; + } + for(n = 1, t = mons; t && t->next; n++, t = t->next) ; + for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; + if(systraypinningfailfirst && n < systraypinning) + return mons; + return t; +} + void zoom(const Arg *arg) { Client *c = selmon->sel; + Client *at = NULL, *cold, *cprevious = NULL; if (!selmon->lt[selmon->sellt]->arrange || (selmon->sel && selmon->sel->isfloating)) return; - if (c == nexttiled(selmon->clients)) - if (!c || !(c = nexttiled(c->next))) - return; - pop(c); + if (c == nexttiled(selmon->clients)) { + at = findbefore(prevzoom); + if (at) + cprevious = nexttiled(at->next); + if (!cprevious || cprevious != prevzoom) { + prevzoom = NULL; + if (!c || !(c = nexttiled(c->next))) + return; + } else + c = cprevious; + } + cold = nexttiled(selmon->clients); + if (c != cold && !at) + at = findbefore(c); + detach(c); + attach(c); + /* swap windows instead of pushing the previous one down */ + if (c != cold && at) { + prevzoom = cold; + if (cold && at != cold) { + detach(cold); + cold->next = at->next; + at->next = cold; + } + } + focus(c); + arrange(c->mon); } int @@ -2135,6 +3022,8 @@ main(int argc, char *argv[]) fputs("warning: no locale support\n", stderr); if (!(dpy = XOpenDisplay(NULL))) die("dwm: cannot open display"); + if (!(xcon = XGetXCBConnection(dpy))) + die("dwm: cannot get xcb connection\n"); checkotherwm(); setup(); #ifdef __OpenBSD__ @@ -2142,7 +3031,9 @@ main(int argc, char *argv[]) die("pledge"); #endif /* __OpenBSD__ */ scan(); - run(); + runAutostart(); +run(); + if(restart) execvp(argv[0], argv); cleanup(); XCloseDisplay(dpy); return EXIT_SUCCESS; diff --git a/dwm.desktop b/dwm.desktop new file mode 100644 index 0000000..5a6021f --- /dev/null +++ b/dwm.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=6.3 +Type=Application +Name=dwm +Comment=Suckless' dynamic window manager +Exec=dwm +Icon=dwm +Terminal=false +StartupNotify=false +Categories=Application; diff --git a/dwmc b/dwmc new file mode 100755 index 0000000..5ff8dbc --- /dev/null +++ b/dwmc @@ -0,0 +1,40 @@ +#!/usr/bin/env sh + +signal() { + xsetroot -name "fsignal:$*" +} + +case $# in +1) + case $1 in + setlayout | view | viewall | togglebar | togglefloating | zoom | killclient | quit) + signal $1 + ;; + *) + echo "Unknown command or missing one argument." + exit 1 + ;; + esac + ;; +2) + case $1 in + view) + signal $1 ui $2 + ;; + viewex | toggleviewex | tagex | toggletagex | setlayoutex | focusstack | incnmaster | focusmon | tagmon) + signal $1 i $2 + ;; + setmfact) + signal $1 f $2 + ;; + *) + echo "Unknown command or one too many arguments." + exit 1 + ;; + esac + ;; +*) + echo "Too many arguments." + exit 1 + ;; +esac diff --git a/patch.diff b/patch.diff new file mode 100644 index 0000000..2ce43a2 --- /dev/null +++ b/patch.diff @@ -0,0 +1,4773 @@ +diff --git a/Makefile b/Makefile +index 77bcbc0..c6bc24b 100644 +--- a/Makefile ++++ b/Makefile +@@ -37,8 +37,9 @@ dist: clean + rm -rf dwm-${VERSION} + + install: all +- mkdir -p ${DESTDIR}${PREFIX}/bin +- cp -f dwm ${DESTDIR}${PREFIX}/bin ++ mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDIR}${XSESSIONPREFIX} ++ cp -f dwm dwmc volsv ${DESTDIR}${PREFIX}/bin ++ cp -f dwm.desktop ${DESTDIR}${XSESSIONPREFIX} + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 +@@ -46,6 +47,8 @@ install: all + + uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/dwm\ ++ ${DESTDIR}${PREFIX}/bin/dwmc\ ++ ${DESTDIR}${PREFIX}/bin/volsv\ + ${DESTDIR}${MANPREFIX}/man1/dwm.1 + + .PHONY: all options clean dist install uninstall +diff --git a/config.def.h b/config.def.h +index 1c0b587..8bfef3e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -2,7 +2,13 @@ + + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ ++static const unsigned int gappx = 6; /* gaps between windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ ++static const unsigned int systrayspacing = 2; /* systray spacing */ ++static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor */ ++static const int showsystray = 1; /* 0 means no systray */ ++static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +@@ -26,15 +32,18 @@ static const Rule rules[] = { + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ +- /* class instance title tags mask isfloating monitor */ +- { "Gimp", NULL, NULL, 0, 1, -1 }, +- { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, ++ /* class instance title tags mask isfloating isterminal noswallow monitor */ ++ { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 }, ++ { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1, -1 }, ++ { "st", NULL, NULL, 0, 0, 1, -1, -1 }, ++ { NULL, NULL, "Event Tester", 0, 1, 0, 1, -1 }, /* xev */ + }; + + /* layout(s) */ + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ ++static int attachbelow = 1; /* 1 means attach after the currently active window */ + + static const Layout layouts[] = { + /* symbol arrange function */ +@@ -70,6 +79,9 @@ static Key keys[] = { + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, ++ { MODKEY|ShiftMask, XK_h, setcfact, {.f = +0.25} }, ++ { MODKEY|ShiftMask, XK_l, setcfact, {.f = -0.25} }, ++ { MODKEY|ShiftMask, XK_o, setcfact, {.f = 0.00} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, +@@ -78,6 +90,7 @@ static Key keys[] = { + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { MODKEY|ShiftMask, XK_f, togglefullscr, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, +@@ -94,6 +107,7 @@ static Key keys[] = { + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, ++ { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} }, + }; + + /* button definitions */ +@@ -103,7 +117,9 @@ static Button buttons[] = { + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, +- { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, ++ { ClkStatusText, 0, Button1, sigdwmblocks, {.i = 1} }, ++ { ClkStatusText, 0, Button2, sigdwmblocks, {.i = 2} }, ++ { ClkStatusText, 0, Button3, sigdwmblocks, {.i = 3} }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, +@@ -113,3 +129,73 @@ static Button buttons[] = { + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, + }; + ++void ++setlayoutex(const Arg *arg) ++{ ++ setlayout(&((Arg) { .v = &layouts[arg->i] })); ++} ++ ++void ++viewex(const Arg *arg) ++{ ++ view(&((Arg) { .ui = 1 << arg->ui })); ++} ++ ++void ++viewall(const Arg *arg) ++{ ++ view(&((Arg){.ui = ~0})); ++} ++ ++void ++toggleviewex(const Arg *arg) ++{ ++ toggleview(&((Arg) { .ui = 1 << arg->ui })); ++} ++ ++void ++tagex(const Arg *arg) ++{ ++ tag(&((Arg) { .ui = 1 << arg->ui })); ++} ++ ++void ++toggletagex(const Arg *arg) ++{ ++ toggletag(&((Arg) { .ui = 1 << arg->ui })); ++} ++ ++void ++tagall(const Arg *arg) ++{ ++ tag(&((Arg){.ui = ~0})); ++} ++ ++/* signal definitions */ ++/* signum must be greater than 0 */ ++/* trigger signals using `xsetroot -name "fsignal: [ ]"` */ ++static Signal signals[] = { ++ /* signum function */ ++ { "focusstack", focusstack }, ++ { "setmfact", setmfact }, ++ { "togglebar", togglebar }, ++ { "incnmaster", incnmaster }, ++ { "togglefloating", togglefloating }, ++ { "focusmon", focusmon }, ++ { "tagmon", tagmon }, ++ { "zoom", zoom }, ++ { "view", view }, ++ { "viewall", viewall }, ++ { "viewex", viewex }, ++ { "toggleview", view }, ++ { "toggleviewex", toggleviewex }, ++ { "tag", tag }, ++ { "tagall", tagall }, ++ { "tagex", tagex }, ++ { "toggletag", tag }, ++ { "toggletagex", toggletagex }, ++ { "killclient", killclient }, ++ { "quit", quit }, ++ { "setlayout", setlayout }, ++ { "setlayoutex", setlayoutex }, ++}; +diff --git a/config.h b/config.h +new file mode 100644 +index 0000000..607e67f +--- /dev/null ++++ b/config.h +@@ -0,0 +1,260 @@ ++/* See LICENSE file for copyright and license details. */ ++ ++#include ++ ++/* appearance */ ++static const unsigned int borderpx = 1; /* border pixel of windows */ ++static const unsigned int gappx = 6; /* gaps between windows */ ++static const unsigned int snap = 32; /* snap pixel */ ++static const int swallowfloating = 0; ++static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ ++static const unsigned int systrayspacing = 2; /* systray spacing */ ++static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ ++static const int showsystray = 1; /* 0 means no systray */ ++static const int showbar = 1; /* 0 means no bar */ ++static const int topbar = 1; /* 0 means bottom bar */ ++static const char *fonts[] = { "mononoki Nerd Font:size=9", "JoyPixels:size=9" }; ++static const char dmenufont[] = "mononoki Nerd Font:size=9"; ++static const char col_gray1[] = "#222222"; ++static const char col_gray2[] = "#444444"; ++static const char col_gray3[] = "#bbbbbb"; ++static const char col_gray4[] = "#eeeeee"; ++static const char col_cyan[] = "#750000"; ++static const char *colors[][3] = { ++ /* fg bg border */ ++ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, ++ [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++}; ++ ++/* tagging */ ++static const char *tags[] = { " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9" }; ++ ++static const Rule rules[] = { ++ /* xprop(1): ++ * WM_CLASS(STRING) = instance, class ++ * WM_NAME(STRING) = title ++ */ ++ /* class instance title tags mask isfloating isterminal noswallow monitor */ ++ { NULL, NULL,"Picture in picture", 511, 1, 0, 0, -1 }, ++ { "ardour-6.2.0",NULL, NULL, 0, 1, 0, 0, -1 }, ++ { "urxvt", NULL, NULL, 0, 0, 1, 0, -1 }, ++ { "URxvt", NULL, NULL, 0, 0, 1, 0, -1 }, ++ { "Ardour-6.2.0",NULL, NULL, 0, 1, 0, 0, -1 }, ++ { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 }, ++ { "Firefox", NULL, NULL, 1 << 8, 0, 0, 1, -1 }, ++ { "st-256color",NULL, NULL, 0, 0, 1, 1, -1 }, ++ { "st", NULL, NULL, 0, 0, 1, 1, -1 }, ++ { "St", NULL, NULL, 0, 0, 1, 1, -1 }, ++ { "tabbed", NULL, NULL, 0, 0, 1, 0, -1 }, ++ { NULL, NULL, "abduco", 0, 0, 1, 0, -1 }, ++ { "Alacritty", NULL, NULL, 0, 0, 1, 0, -1 }, ++ { "Blueman", NULL, NULL, 0, 1, 0, 0, -1 }, ++ { "QjackCtl", NULL, NULL, 0, 1, 0, 0, -1 }, ++ { "qjackctl", NULL, NULL, 0, 1, 0, 0, -1 }, ++ { "catia.py", NULL, NULL, 0, 1, 0, 0, -1 }, ++ { "Catia", NULL, NULL, 0, 1, 0, 0, -1 }, ++ { NULL, NULL, "Event Tester", 0, 1, 0, 1, -1 }, ++ { "Steam", NULL, NULL, 4, 0, 0, 0, -1 }, ++ { "steam", NULL, NULL, 4, 0, 0, 0, -1 }, ++ { NULL, NULL, "steam", 4, 0, 0, 0, -1 }, ++ { "Lutris", NULL, NULL, 2, 0, 0, 0, -1 }, ++ { "lutris", NULL, NULL, 2, 0, 0, 0, -1 }, ++}; ++ ++/* layout(s) */ ++static const float mfact = 0.50; /* factor of master area size [0.05..0.95] */ ++static const int nmaster = 1; /* number of clients in master area */ ++static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ ++static int attachbelow = 1; /* 1 means attach after the currently active window */ ++ ++static const Layout layouts[] = { ++ /* symbol arrange function */ ++ { "[]=", tile }, /* first entry is default */ ++ { "><>", NULL }, /* no layout function means floating behavior */ ++ { "[M]", monocle }, ++}; ++ ++/* key definitions */ ++#define MODKEY Mod1Mask ++#define TAGKEYS(KEY,TAG) \ ++ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ ++ { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ ++ { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ ++ { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, ++ ++/* helper for spawning shell commands in the pre dwm-5.0 fashion */ ++#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } ++ ++/* commands */ ++static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ ++static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; ++static const char *passmenu[] = { "passmenu", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; ++static const char *termcmd[] = { "tabbed", "-c", "st", "-w", NULL }; ++static const char *mpdtoggle[] = { "playerctl", "--player=mpd,mpv,%any", "play-pause", NULL }; ++static const char *mpdnext[] = { "playerctl", "--player=mpd,mpv,%any", "next", NULL }; ++static const char *mpdprev[] = { "playerctl", "--player=mpd,mpv,%any", "previous", NULL }; ++static const char *plytoggle[] = { "playerctl", "--player=%any,mpd", "play-pause", NULL }; ++static const char *plyfwd[] = { "playerctl", "--player=%any,mpd", "position 5+", NULL }; ++static const char *plybck[] = { "playerctl", "--player=%any,mpd", "position 5-", NULL }; ++static const char *blightup[] = { "light", "-A", "1", NULL }; ++static const char *blightdown[] = { "light", "-U", "1", NULL }; ++static const char *audioup[] = { "volsv", "-i", NULL }; ++static const char *audiodown[] = { "volsv", "-d", NULL }; ++static const char *audiomute[] = { "volsv", "-t", NULL }; ++static const char *micmute[] = { "pamixer", "--source", "1", "-t", NULL }; ++static const char *lockscr[] = { "xscreensaver-command", "-lock", NULL }; ++static const char *xidletog[] = { "xidletog", NULL }; ++static const char *xkillcmd[] = { "xkill", NULL }; ++ ++static Key keys[] = { ++ /* modifier key function argument */ ++ { MODKEY, XK_d, spawn, {.v = dmenucmd } }, ++ { MODKEY, XK_p, spawn, {.v = passmenu } }, ++ { MODKEY, XK_c, spawn, {.v = xidletog } }, ++ { MODKEY, XK_x, spawn, {.v = xkillcmd } }, ++ { MODKEY, XK_Return, spawn, {.v = termcmd } }, ++ { 0, XF86XK_AudioPlay, spawn, {.v = mpdtoggle } }, ++ { 0, XF86XK_AudioNext, spawn, {.v = mpdnext } }, ++ { 0, XF86XK_AudioPrev, spawn, {.v = mpdprev } }, ++ { ShiftMask, XF86XK_AudioPlay, spawn, {.v = plytoggle } }, ++ { ShiftMask, XF86XK_AudioNext, spawn, {.v = plyfwd } }, ++ { ShiftMask, XF86XK_AudioPrev, spawn, {.v = plybck } }, ++ { 0, XF86XK_MonBrightnessUp, spawn, {.v = blightup } }, ++ { 0, XF86XK_MonBrightnessDown, spawn, {.v = blightdown } }, ++ { 0, XF86XK_AudioLowerVolume, spawn, {.v = audiodown } }, ++ { 0, XF86XK_AudioRaiseVolume, spawn, {.v = audioup } }, ++ { 0, XF86XK_AudioMute, spawn, {.v = audiomute } }, ++ { 0, XF86XK_AudioMicMute, spawn, {.v = micmute } }, ++ { Mod4Mask, XK_l, spawn, {.v = lockscr } }, ++ { MODKEY, XK_b, togglebar, {0} }, ++ { MODKEY, XK_j, focusstack, {.i = +1 } }, ++ { MODKEY, XK_k, focusstack, {.i = -1 } }, ++ { MODKEY|ShiftMask, XK_j, pushdown, {.i = +1 } }, ++ { MODKEY|ShiftMask, XK_k, pushup, {.i = -1 } }, ++ { MODKEY|ControlMask, XK_k, setcfact, {.f = +0.25} }, ++ { MODKEY|ControlMask, XK_j, setcfact, {.f = -0.25} }, ++ { MODKEY|ControlMask, XK_o, setcfact, {.f = 0.00} }, ++ { MODKEY|ShiftMask, XK_h, incnmaster, {.i = +1 } }, ++ { MODKEY|ShiftMask, XK_l, incnmaster, {.i = -1 } }, ++ { MODKEY, XK_h, setmfact, {.f = -0.05} }, ++ { MODKEY, XK_l, setmfact, {.f = +0.05} }, ++ { MODKEY|ShiftMask, XK_Return, zoom, {0} }, ++ { MODKEY, XK_Tab, view, {0} }, ++ { MODKEY|ShiftMask, XK_q, killclient, {0} }, ++ { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, ++ { MODKEY, XK_s, setlayout, {.v = &layouts[1]} }, ++ { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, ++ { MODKEY|ShiftMask, XK_space, setlayout, {-1} }, ++ { MODKEY, XK_space, togglefloating, {0} }, ++ { MODKEY, XK_f, togglefullscr, {0} }, ++ { MODKEY, XK_0, view, {.ui = ~0 } }, ++ { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, ++ { MODKEY|ControlMask, XK_comma, focusmon, {.i = -1 } }, ++ { MODKEY|ControlMask, XK_period, focusmon, {.i = +1 } }, ++ { MODKEY|ControlMask|ShiftMask, XK_comma, tagmon, {.i = -1 } }, ++ { MODKEY|ControlMask|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ TAGKEYS( XK_1, 0) ++ TAGKEYS( XK_2, 1) ++ TAGKEYS( XK_3, 2) ++ TAGKEYS( XK_4, 3) ++ TAGKEYS( XK_5, 4) ++ TAGKEYS( XK_6, 5) ++ TAGKEYS( XK_7, 6) ++ TAGKEYS( XK_8, 7) ++ TAGKEYS( XK_9, 8) ++ { MODKEY|ShiftMask, XK_e, quit, {0} }, ++ { MODKEY|ShiftMask, XK_r, quit, {1} }, ++ { MODKEY|ShiftMask, XK_Tab, toggleAttachBelow, {0} }, ++}; ++ ++/* button definitions */ ++/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ ++static Button buttons[] = { ++ /* click event mask button function argument */ ++ { ClkLtSymbol, 0, Button1, setlayout, {0} }, ++ { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, ++ { ClkWinTitle, 0, Button2, zoom, {0} }, ++ { ClkStatusText, 0, Button1, sigdwmblocks, {.i = 1} }, ++ { ClkStatusText, 0, Button2, sigdwmblocks, {.i = 2} }, ++ { ClkStatusText, 0, Button3, sigdwmblocks, {.i = 3} }, ++ { ClkClientWin, MODKEY, Button1, movemouse, {0} }, ++ { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, ++ { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, ++ { ClkTagBar, 0, Button1, view, {0} }, ++ { ClkTagBar, 0, Button3, toggleview, {0} }, ++ { ClkTagBar, MODKEY, Button1, tag, {0} }, ++ { ClkTagBar, MODKEY, Button3, toggletag, {0} }, ++}; ++ ++void ++setlayoutex(const Arg *arg) ++{ ++ setlayout(&((Arg) { .v = &layouts[arg->i] })); ++} ++ ++void ++viewex(const Arg *arg) ++{ ++ view(&((Arg) { .ui = 1 << arg->ui })); ++} ++ ++void ++viewall(const Arg *arg) ++{ ++ view(&((Arg){.ui = ~0})); ++} ++ ++void ++toggleviewex(const Arg *arg) ++{ ++ toggleview(&((Arg) { .ui = 1 << arg->ui })); ++} ++ ++void ++tagex(const Arg *arg) ++{ ++ tag(&((Arg) { .ui = 1 << arg->ui })); ++} ++ ++void ++toggletagex(const Arg *arg) ++{ ++ toggletag(&((Arg) { .ui = 1 << arg->ui })); ++} ++ ++void ++tagall(const Arg *arg) ++{ ++ tag(&((Arg){.ui = ~0})); ++} ++ ++/* signal definitions */ ++/* signum must be greater than 0 */ ++/* trigger signals using `xsetroot -name "fsignal: [ ]"` */ ++static Signal signals[] = { ++ /* signum function */ ++ { "focusstack", focusstack }, ++ { "setmfact", setmfact }, ++ { "togglebar", togglebar }, ++ { "incnmaster", incnmaster }, ++ { "togglefloating", togglefloating }, ++ { "togglefullscr", togglefullscr }, ++ { "focusmon", focusmon }, ++ { "tagmon", tagmon }, ++ { "zoom", zoom }, ++ { "view", view }, ++ { "viewall", viewall }, ++ { "viewex", viewex }, ++ { "toggleview", view }, ++ { "toggleviewex", toggleviewex }, ++ { "tag", tag }, ++ { "tagall", tagall }, ++ { "tagex", tagex }, ++ { "toggletag", tag }, ++ { "toggletagex", toggletagex }, ++ { "killclient", killclient }, ++ { "quit", quit }, ++ { "setlayout", setlayout }, ++ { "setlayoutex", setlayoutex }, ++}; ++ +diff --git a/config.mk b/config.mk +index 7084c33..dbbc526 100644 +--- a/config.mk ++++ b/config.mk +@@ -6,6 +6,7 @@ VERSION = 6.2 + # paths + PREFIX = /usr/local + MANPREFIX = ${PREFIX}/share/man ++XSESSIONPREFIX = /usr/share/xsessions + + X11INC = /usr/X11R6/include + X11LIB = /usr/X11R6/lib +@@ -22,10 +23,10 @@ FREETYPEINC = /usr/include/freetype2 + + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res + + # flags +-CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} ++CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} + #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} + CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} + LDFLAGS = ${LIBS} +diff --git a/dwm.1 b/dwm.1 +index ddc8321..0ec6c60 100644 +--- a/dwm.1 ++++ b/dwm.1 +@@ -12,10 +12,11 @@ environment for the application in use and the task performed. + In tiled layouts windows are managed in a master and stacking area. The master + area on the left contains one window by default, and the stacking area on the + right contains all other windows. The number of master area windows can be +-adjusted from zero to an arbitrary number. In monocle layout all windows are +-maximised to the screen size. In floating layout windows can be resized and +-moved freely. Dialog windows are always managed floating, regardless of the +-layout applied. ++adjusted from zero to an arbitrary number. Windows in both the master and stack ++can be resized vertically, as well as resizing the master area. In monocle ++layout all windows are maximised to the screen size. In floating layout windows ++can be resized and moved freely. Dialog windows are always managed floating, ++regardless of the layout applied. + .P + Windows are grouped by tags. Each window can be tagged with one or multiple + tags. Selecting certain tags displays all windows with these tags. +@@ -24,10 +25,13 @@ Each screen contains a small status bar which displays all available tags, the + layout, the title of the focused window, and the text read from the root window + name property, if the screen is focused. A floating window is indicated with an + empty square and a maximised floating window is indicated with a filled square +-before the windows title. The selected tags are indicated with a different +-color. The tags of the focused window are indicated with a filled square in the +-top left corner. The tags which are applied to one or more windows are +-indicated with an empty square in the top left corner. ++before the window's title. The selected tags are indicated with a different ++color. The focused window is represented by a long line before the tags which ++are applied to it, and all other windows are represented by dots for all their ++corresponding tags. ++.P ++The attach below patch makes newly spawned windows attach after the currently ++selected window + .P + dwm draws a small border around windows to indicate the focus state. + .SH OPTIONS +@@ -56,41 +60,28 @@ click on a tag label applies that tag to the focused window. + click on a tag label adds/removes that tag to/from the focused window. + .SS Keyboard commands + .TP +-.B Mod1\-Shift\-Return ++.B Mod1\-Return + Start ++.BR tabbed(1) ++Running + .BR st(1). + .TP + .B Mod1\-p ++Spawn passmenu. ++.TP ++.B Mod1\-d + Spawn + .BR dmenu(1) + for launching other programs. + .TP +-.B Mod1\-, +-Focus previous screen, if any. +-.TP +-.B Mod1\-. +-Focus next screen, if any. +-.TP +-.B Mod1\-Shift\-, +-Send focused window to previous screen, if any. +-.TP +-.B Mod1\-Shift\-. +-Send focused window to next screen, if any. +-.TP + .B Mod1\-b + Toggles bar on and off. + .TP +-.B Mod1\-t +-Sets tiled layout. ++.B Mod1\-Shift\-j ++Push the selected client window down the stack + .TP +-.B Mod1\-f +-Sets floating layout. +-.TP +-.B Mod1\-m +-Sets monocle layout. +-.TP +-.B Mod1\-space +-Toggles between current and previous layout. ++.B Mod1\-Shift\-k ++Push the selected client window up the stack + .TP + .B Mod1\-j + Focus next window. +@@ -98,50 +89,91 @@ Focus next window. + .B Mod1\-k + Focus previous window. + .TP +-.B Mod1\-i +-Increase number of windows in master area. ++.B Mod1\-, ++Increase the number of master windows. + .TP +-.B Mod1\-d +-Decrease number of windows in master area. ++.B Mod1\-. ++Decrease the number of master windows. ++.TP ++.B Mod1\-h ++Decrease master area size. + .TP + .B Mod1\-l + Increase master area size. + .TP +-.B Mod1\-h +-Decrease master area size. ++.B Mod1\-Shift\-h ++Increase the size ratio of the selected client + .TP +-.B Mod1\-Return ++.B Mod1\-Shift\-l ++Decrease the size ratio of the selected client ++.B Mod1\-Shift\-o ++Reset the size ratio of the selected client ++.TP ++.B Mod1\-Shift\-Return + Zooms/cycles focused window to/from master area (tiled layouts only). + .TP +-.B Mod1\-Shift\-c ++.B Mod1\-Tab ++Toggles to the previously selected tags. ++.TP ++.B Mod1\-Shift\-q + Close focused window. + .TP ++.B Mod1\-t ++Sets tiled layout. ++.TP ++.B Mod1\-s ++Sets floating layout. ++.TP ++.B Mod1\-m ++Sets monocle layout. ++.TP + .B Mod1\-Shift\-space +-Toggle focused window between tiled and floating state. ++Toggles between current and previous layout. + .TP +-.B Mod1\-Tab +-Toggles to the previously selected tags. ++.B Mod1\-space ++Toggles the selected window into the floating state. + .TP +-.B Mod1\-Shift\-[1..n] +-Apply nth tag to focused window. ++.B Mod1\-f ++Toggle the selected window into the fullscreen state. ++.TP ++.B Mod1\-0 ++View all windows with any tag. + .TP + .B Mod1\-Shift\-0 + Apply all tags to focused window. + .TP ++.B Mod1\-Shift\-, ++Change focus to previous screen, if any. ++.TP ++.B Mod1\-Shift\-. ++Change focus to next screen, if any. ++.TP ++.B Mod1\-Control\-Shift\-, ++Send focused window to previous screen, if any. ++.TP ++.B Mod1\-Control\-Shift\-. ++Send focused window to next screen, if any. ++.TP ++.B Mod1\-Shift\-[1..n] ++Apply nth tag to focused window. ++.TP + .B Mod1\-Control\-Shift\-[1..n] + Add/remove nth tag to/from focused window. + .TP + .B Mod1\-[1..n] + View all windows with nth tag. + .TP +-.B Mod1\-0 +-View all windows with any tag. +-.TP + .B Mod1\-Control\-[1..n] + Add/remove all windows with nth tag to/from the view. + .TP +-.B Mod1\-Shift\-q ++.B Mod1\-Shift\-e + Quit dwm. ++.TP ++.B Mod1\-Shift\-r ++Restart dwm. ++.TP ++.B Mod1\-Shift\-Tab ++Toggle AttachBelow patch + .SS Mouse commands + .TP + .B Mod1\-Button1 +@@ -155,6 +187,41 @@ Resize focused window while dragging. Tiled windows will be toggled to the float + .SH CUSTOMIZATION + dwm is customized by creating a custom config.h and (re)compiling the source + code. This keeps it fast, secure and simple. ++.SH SIGNALS ++.TP ++.B SIGHUP - 1 ++Restart the dwm process. ++.TP ++.B SIGTERM - 15 ++Cleanly terminate the dwm process. ++.SH PATCHES ++This version of dwm was compiled using the: ++.TP ++.B dwm-actualfullscreen-20191112-cb3f58a.diff ++.TP ++.B dwm-attachbelow-toggleable-6.2.diff ++.TP ++.B dwm-autostart-20161205-bb3bd6f.diff ++.TP ++.B dwm-cfacts-20200913-61bb8b2.diff ++.TP ++.B dwm-clientindicators-6.2.diff ++.TP ++.B dwm-dwmc-6.2.diff ++.TP ++.B dwm-push_no_master-6.2.diff ++.TP ++.B dwm-restartsig-20180523-6.2.diff ++.TP ++.B dwm-statuscmd-signal-6.2.diff ++.TP ++.B dwm-swallow-20200522-7accbcf.diff ++.TP ++.B dwm-systray-6.2.diff ++.TP ++.B dwm-uselessgap-6.2.diff ++.TP ++.B dwm-zoomswap-6.2.diff + .SH SEE ALSO + .BR dmenu (1), + .BR st (1) +diff --git a/dwm.c b/dwm.c +index 664c527..cecbe53 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -40,6 +40,8 @@ + #include + #endif /* XINERAMA */ + #include ++#include ++#include + + #include "drw.h" + #include "util.h" +@@ -52,17 +54,35 @@ + #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) + #define LENGTH(X) (sizeof X / sizeof X[0]) + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) +-#define WIDTH(X) ((X)->w + 2 * (X)->bw) +-#define HEIGHT(X) ((X)->h + 2 * (X)->bw) ++#define WIDTH(X) ((X)->w + 2 * (X)->bw + gappx) ++#define HEIGHT(X) ((X)->h + 2 * (X)->bw + gappx) + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + ++#define SYSTEM_TRAY_REQUEST_DOCK 0 ++ ++/* XEMBED messages */ ++#define XEMBED_EMBEDDED_NOTIFY 0 ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_FOCUS_IN 4 ++#define XEMBED_MODALITY_ON 10 ++ ++#define XEMBED_MAPPED (1 << 0) ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_WINDOW_DEACTIVATE 2 ++ ++#define VERSION_MAJOR 0 ++#define VERSION_MINOR 0 ++#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR ++ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, ++ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ +@@ -87,14 +107,17 @@ typedef struct Client Client; + struct Client { + char name[256]; + float mina, maxa; ++ float cfact; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; + unsigned int tags; +- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow; ++ pid_t pid; + Client *next; + Client *snext; ++ Client *swallowing; + Monitor *mon; + Window win; + }; +@@ -106,6 +129,11 @@ typedef struct { + const Arg arg; + } Key; + ++typedef struct { ++ const char * sig; ++ void (*func)(const Arg *); ++} Signal; ++ + typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +@@ -138,16 +166,27 @@ typedef struct { + const char *title; + unsigned int tags; + int isfloating; ++ int isterminal; ++ int noswallow; + int monitor; + } Rule; + ++typedef struct Systray Systray; ++struct Systray { ++ Window win; ++ Client *icons; ++}; ++ + /* function declarations */ + static void applyrules(Client *c); + static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); + static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); ++static void attachBelow(Client *c); ++static void toggleAttachBelow(); + static void attachstack(Client *c); ++static int fake_signal(void); + static void buttonpress(XEvent *e); + static void checkotherwm(void); + static void cleanup(void); +@@ -156,6 +195,7 @@ static void clientmessage(XEvent *e); + static void configure(Client *c); + static void configurenotify(XEvent *e); + static void configurerequest(XEvent *e); ++static void copyvalidchars(char *text, char *rawtext); + static Monitor *createmon(void); + static void destroynotify(XEvent *e); + static void detach(Client *c); +@@ -165,13 +205,16 @@ static void drawbar(Monitor *m); + static void drawbars(void); + static void enternotify(XEvent *e); + static void expose(XEvent *e); ++static Client *findbefore(Client *c); + static void focus(Client *c); + static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); ++static int getdwmblockspid(); + static Atom getatomprop(Client *c, Atom prop); + static int getrootptr(int *x, int *y); + static long getstate(Window w); ++static unsigned int getsystraywidth(); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); +@@ -185,33 +228,46 @@ static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); + static Client *nexttiled(Client *c); +-static void pop(Client *); ++//static void pop(Client *); ++static Client *prevtiled(Client *c); + static void propertynotify(XEvent *e); ++static void pushdown(const Arg *arg); ++static void pushup(const Arg *arg); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); ++static void removesystrayicon(Client *i); + static void resize(Client *c, int x, int y, int w, int h, int interact); ++static void resizebarwin(Monitor *m); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); ++static void resizerequest(XEvent *e); + static void restack(Monitor *m); + static void run(void); ++static void runAutostart(void); + static void scan(void); +-static int sendevent(Client *c, Atom proto); ++static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); + static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); + static void setlayout(const Arg *arg); ++static void setcfact(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); + static void seturgent(Client *c, int urg); + static void showhide(Client *c); + static void sigchld(int unused); ++static void sigdwmblocks(const Arg *arg); ++static void sighup(int unused); ++static void sigterm(int unused); + static void spawn(const Arg *arg); ++static Monitor *systraytomon(Monitor *m); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglefullscr(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -224,20 +280,36 @@ static int updategeom(void); + static void updatenumlockmask(void); + static void updatesizehints(Client *c); + static void updatestatus(void); ++static void updatesystray(void); ++static void updatesystrayicongeom(Client *i, int w, int h); ++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); + static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); ++static Client *wintosystrayicon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); + static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++static pid_t getparentprocess(pid_t p); ++static int isdescprocess(pid_t p, pid_t c); ++static Client *swallowingclient(Window w); ++static Client *termforwin(const Client *c); ++static pid_t winpid(Window w); ++ + /* variables */ ++static Client *prevzoom = NULL; ++static Systray *systray = NULL; + static const char broken[] = "broken"; + static char stext[256]; ++static char rawstext[256]; ++static int dwmblockssig; ++pid_t dwmblockspid = 0; ++static int scanner; + static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh, blw = 0; /* bar geometry */ +@@ -258,9 +330,11 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, ++ [ResizeRequest] = resizerequest, + [UnmapNotify] = unmapnotify + }; +-static Atom wmatom[WMLast], netatom[NetLast]; ++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; ++static int restart = 0; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -269,6 +343,8 @@ static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; + ++static xcb_connection_t *xcon; ++ + /* configuration, allows nested code to access above variables */ + #include "config.h" + +@@ -286,6 +362,7 @@ applyrules(Client *c) + XClassHint ch = { NULL, NULL }; + + /* rule matching */ ++ c->noswallow = -1; + c->isfloating = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); +@@ -298,6 +375,8 @@ applyrules(Client *c) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { ++ c->isterminal = r->isterminal; ++ c->noswallow = r->noswallow; + c->isfloating = r->isfloating; + c->tags |= r->tags; + for (m = mons; m && m->num != r->monitor; m = m->next); +@@ -406,6 +485,26 @@ attach(Client *c) + c->next = c->mon->clients; + c->mon->clients = c; + } ++void ++attachBelow(Client *c) ++{ ++ //If there is nothing on the monitor or the selected client is floating, attach as normal ++ if(c->mon->sel == NULL || c->mon->sel->isfloating) { ++ attach(c); ++ return; ++ } ++ ++ //Set the new client's next property to the same as the currently selected clients next ++ c->next = c->mon->sel->next; ++ //Set the currently selected clients next property to the new client ++ c->mon->sel->next = c; ++ ++} ++ ++void toggleAttachBelow() ++{ ++ attachbelow = !attachbelow; ++} + + void + attachstack(Client *c) +@@ -414,6 +513,61 @@ attachstack(Client *c) + c->mon->stack = c; + } + ++void ++swallow(Client *p, Client *c) ++{ ++ Client *s; ++ ++ if (c->noswallow > 0 || c->isterminal) ++ return; ++ if (c->noswallow < 0 && !swallowfloating && c->isfloating) ++ return; ++ ++ detach(c); ++ detachstack(c); ++ ++ setclientstate(c, WithdrawnState); ++ XUnmapWindow(dpy, p->win); ++ ++ p->swallowing = c; ++ c->mon = p->mon; ++ ++ Window w = p->win; ++ p->win = c->win; ++ c->win = w; ++ ++ XChangeProperty(dpy, c->win, netatom[NetClientList], XA_WINDOW, 32, PropModeReplace, ++ (unsigned char *) &(p->win), 1); ++ ++ updatetitle(p); ++ s = scanner ? c : p; ++ XMoveResizeWindow(dpy, p->win, s->x, s->y, s->w, s->h); ++ arrange(p->mon); ++ configure(p); ++ updateclientlist(); ++} ++ ++void ++unswallow(Client *c) ++{ ++ c->win = c->swallowing->win; ++ ++ free(c->swallowing); ++ c->swallowing = NULL; ++ ++ XDeleteProperty(dpy, c->win, netatom[NetClientList]); ++ ++ /* unfullscreen the client */ ++ setfullscreen(c, 0); ++ updatetitle(c); ++ arrange(c->mon); ++ XMapWindow(dpy, c->win); ++ XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); ++ setclientstate(c, NormalState); ++ focus(NULL); ++ arrange(c->mon); ++} ++ + void + buttonpress(XEvent *e) + { +@@ -440,9 +594,25 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + blw) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - (int)TEXTW(stext)) ++ else if (ev->x > (x = selmon->ww - (int)TEXTW(stext) + lrpad - getsystraywidth())) { + click = ClkStatusText; +- else ++ char *text = rawstext; ++ int i = -1; ++ char ch; ++ dwmblockssig = 0; ++ while (text[++i]) { ++ if ((unsigned char)text[i] < ' ') { ++ ch = text[i]; ++ text[i] = '\0'; ++ x += TEXTW(text) - lrpad; ++ text[i] = ch; ++ text += i+1; ++ i = -1; ++ if (x >= ev->x) break; ++ dwmblockssig = ch; ++ } ++ } ++ } else + click = ClkWinTitle; + } else if ((c = wintoclient(ev->window))) { + focus(c); +@@ -483,6 +653,11 @@ cleanup(void) + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); ++ if (showsystray) { ++ XUnmapWindow(dpy, systray->win); ++ XDestroyWindow(dpy, systray->win); ++ free(systray); ++ } + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) +@@ -513,9 +688,57 @@ cleanupmon(Monitor *mon) + void + clientmessage(XEvent *e) + { ++ XWindowAttributes wa; ++ XSetWindowAttributes swa; + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + ++ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { ++ /* add systray icons */ ++ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { ++ if (!(c = (Client *)calloc(1, sizeof(Client)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Client)); ++ if (!(c->win = cme->data.l[2])) { ++ free(c); ++ return; ++ } ++ c->mon = selmon; ++ c->next = systray->icons; ++ systray->icons = c; ++ if (!XGetWindowAttributes(dpy, c->win, &wa)) { ++ /* use sane defaults */ ++ wa.width = bh; ++ wa.height = bh; ++ wa.border_width = 0; ++ } ++ c->x = c->oldx = c->y = c->oldy = 0; ++ c->w = c->oldw = wa.width; ++ c->h = c->oldh = wa.height; ++ c->oldbw = wa.border_width; ++ c->bw = 0; ++ c->isfloating = True; ++ /* reuse tags field as mapped status */ ++ c->tags = 1; ++ updatesizehints(c); ++ updatesystrayicongeom(c, wa.width, wa.height); ++ XAddToSaveSet(dpy, c->win); ++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); ++ XReparentWindow(dpy, c->win, systray->win, 0, 0); ++ /* use parents background color */ ++ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ /* FIXME not sure if I have to send these events, too */ ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ XSync(dpy, False); ++ resizebarwin(selmon); ++ updatesystray(); ++ setclientstate(c, NormalState); ++ } ++ return; ++ } + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { +@@ -568,7 +791,7 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ resizebarwin(m); + } + focus(NULL); + arrange(NULL); +@@ -628,6 +851,19 @@ configurerequest(XEvent *e) + XSync(dpy, False); + } + ++void ++copyvalidchars(char *text, char *rawtext) ++{ ++ int i = -1, j = 0; ++ ++ while(rawtext[++i]) { ++ if ((unsigned char)rawtext[i] >= ' ') { ++ text[j++] = rawtext[i]; ++ } ++ } ++ text[j] = '\0'; ++} ++ + Monitor * + createmon(void) + { +@@ -653,6 +889,15 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ ++ else if ((c = wintosystrayicon(ev->window))) { ++ removesystrayicon(c); ++ resizebarwin(selmon); ++ updatesystray(); ++ } ++ ++ else if ((c = swallowingclient(ev->window))) ++ unmanage(c->swallowing, 1); + } + + void +@@ -696,18 +941,24 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; ++ int indn; ++ int x, w, tw = 0, stw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; + ++ if(showsystray && m == systraytomon(m)) ++ stw = getsystraywidth(); ++ + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); +- } ++ tw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */ ++ drw_text(drw, m->ww - tw - stw, 0, tw, bh, lrpad / 2 - 2, stext, 0); ++ } ++ ++ resizebarwin(m); + + for (c = m->clients; c; c = c->next) { + occ |= c->tags; +@@ -716,20 +967,24 @@ drawbar(Monitor *m) + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { ++ indn = 0; + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); +- if (occ & 1 << i) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); ++ for (c = m->clients; c; c = c->next) { ++ if (c->tags & (1 << i)) { ++ drw_rect(drw, x, 1 + (indn * 2), selmon->sel == c ? 6 : 1, 1, 1, urg & 1 << i); ++ indn++; ++ } ++ } ++ + x += w; + } + w = blw = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + +- if ((w = m->ww - tw - x) > bh) { ++ if ((w = m->ww - tw - stw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +@@ -740,7 +995,7 @@ drawbar(Monitor *m) + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); + } + + void +@@ -777,8 +1032,21 @@ expose(XEvent *e) + Monitor *m; + XExposeEvent *ev = &e->xexpose; + +- if (ev->count == 0 && (m = wintomon(ev->window))) ++ if (ev->count == 0 && (m = wintomon(ev->window))) { + drawbar(m); ++ if (m == selmon) ++ updatesystray(); ++ } ++} ++ ++Client * ++findbefore(Client *c) ++{ ++ Client *tmp; ++ if (c == selmon->clients) ++ return NULL; ++ for (tmp = selmon->clients; tmp && tmp->next != c; tmp = tmp->next); ++ return tmp; + } + + void +@@ -863,15 +1131,34 @@ getatomprop(Client *c, Atom prop) + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; ++ /* FIXME getatomprop should return the number of items and a pointer to ++ * the stored data instead of this workaround */ ++ Atom req = XA_ATOM; ++ if (prop == xatom[XembedInfo]) ++ req = xatom[XembedInfo]; + +- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, ++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; ++ if (da == xatom[XembedInfo] && dl == 2) ++ atom = ((Atom *)p)[1]; + XFree(p); + } + return atom; + } + ++int ++getdwmblockspid() ++{ ++ char buf[16]; ++ FILE *fp = popen("pidof -s dwmblocks", "r"); ++ fgets(buf, sizeof(buf), fp); ++ pid_t pid = strtoul(buf, NULL, 10); ++ pclose(fp); ++ dwmblockspid = pid; ++ return pid != 0 ? 0 : -1; ++} ++ + int + getrootptr(int *x, int *y) + { +@@ -900,6 +1187,16 @@ getstate(Window w) + return result; + } + ++unsigned int ++getsystraywidth() ++{ ++ unsigned int w = 0; ++ Client *i; ++ if(showsystray) ++ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; ++ return w ? w + systrayspacing : 1; ++} ++ + int + gettextprop(Window w, Atom atom, char *text, unsigned int size) + { +@@ -999,12 +1296,55 @@ keypress(XEvent *e) + keys[i].func(&(keys[i].arg)); + } + ++int ++fake_signal(void) ++{ ++ char fsignal[256]; ++ char indicator[9] = "fsignal:"; ++ char str_sig[50]; ++ char param[16]; ++ int i, len_str_sig, n, paramn; ++ size_t len_fsignal, len_indicator = strlen(indicator); ++ Arg arg; ++ ++ // Get root name property ++ if (gettextprop(root, XA_WM_NAME, fsignal, sizeof(fsignal))) { ++ len_fsignal = strlen(fsignal); ++ ++ // Check if this is indeed a fake signal ++ if (len_indicator > len_fsignal ? 0 : strncmp(indicator, fsignal, len_indicator) == 0) { ++ paramn = sscanf(fsignal+len_indicator, "%s%n%s%n", str_sig, &len_str_sig, param, &n); ++ ++ if (paramn == 1) arg = (Arg) {0}; ++ else if (paramn > 2) return 1; ++ else if (strncmp(param, "i", n - len_str_sig) == 0) ++ sscanf(fsignal + len_indicator + n, "%i", &(arg.i)); ++ else if (strncmp(param, "ui", n - len_str_sig) == 0) ++ sscanf(fsignal + len_indicator + n, "%u", &(arg.ui)); ++ else if (strncmp(param, "f", n - len_str_sig) == 0) ++ sscanf(fsignal + len_indicator + n, "%f", &(arg.f)); ++ else return 1; ++ ++ // Check if a signal was found, and if so handle it ++ for (i = 0; i < LENGTH(signals); i++) ++ if (strncmp(str_sig, signals[i].sig, len_str_sig) == 0 && signals[i].func) ++ signals[i].func(&(arg)); ++ ++ // A fake signal was sent ++ return 1; ++ } ++ } ++ ++ // No fake signal was sent, so proceed with update ++ return 0; ++} ++ + void + killclient(const Arg *arg) + { + if (!selmon->sel) + return; +- if (!sendevent(selmon->sel, wmatom[WMDelete])) { ++ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); +@@ -1018,18 +1358,20 @@ killclient(const Arg *arg) + void + manage(Window w, XWindowAttributes *wa) + { +- Client *c, *t = NULL; ++ Client *c, *t = NULL, *term = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; ++ c->pid = winpid(w); + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; ++ c->cfact = 1.0; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { +@@ -1038,6 +1380,7 @@ manage(Window w, XWindowAttributes *wa) + } else { + c->mon = selmon; + applyrules(c); ++ term = termforwin(c); + } + + if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) +@@ -1063,7 +1406,10 @@ manage(Window w, XWindowAttributes *wa) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +- attach(c); ++ if( attachbelow ) ++ attachBelow(c); ++ else ++ attach(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +@@ -1074,6 +1420,8 @@ manage(Window w, XWindowAttributes *wa) + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); ++ if (term) ++ swallow(term, c); + focus(NULL); + } + +@@ -1092,6 +1440,12 @@ maprequest(XEvent *e) + { + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; ++ Client *i; ++ if ((i = wintosystrayicon(ev->window))) { ++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); ++ resizebarwin(selmon); ++ updatesystray(); ++ } + + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; +@@ -1200,6 +1554,7 @@ nexttiled(Client *c) + return c; + } + ++/* + void + pop(Client *c) + { +@@ -1208,6 +1563,17 @@ pop(Client *c) + focus(c); + arrange(c->mon); + } ++*/ ++ ++Client * ++prevtiled(Client *c) { ++ Client *p, *r; ++ ++ for(p = selmon->clients, r = NULL; p && p != c; p = p->next) ++ if(!p->isfloating && ISVISIBLE(p)) ++ r = p; ++ return r; ++} + + void + propertynotify(XEvent *e) +@@ -1216,8 +1582,20 @@ propertynotify(XEvent *e) + Window trans; + XPropertyEvent *ev = &e->xproperty; + +- if ((ev->window == root) && (ev->atom == XA_WM_NAME)) +- updatestatus(); ++ if ((c = wintosystrayicon(ev->window))) { ++ if (ev->atom == XA_WM_NORMAL_HINTS) { ++ updatesizehints(c); ++ updatesystrayicongeom(c, c->w, c->h); ++ } else ++ updatesystrayiconstate(c, ev); ++ resizebarwin(selmon); ++ updatesystray(); ++ ++ } ++ if ((ev->window == root) && (ev->atom == XA_WM_NAME)) { ++ if (!fake_signal()) ++ updatestatus(); ++ } + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { +@@ -1246,9 +1624,41 @@ propertynotify(XEvent *e) + } + } + ++void ++pushdown(const Arg *arg) { ++ Client *sel = selmon->sel, *c; ++ ++ if(!sel || sel->isfloating || sel == nexttiled(selmon->clients)) ++ return; ++ if((c = nexttiled(sel->next))) { ++ detach(sel); ++ sel->next = c->next; ++ c->next = sel; ++ } ++ focus(sel); ++ arrange(selmon); ++} ++ ++void ++pushup(const Arg *arg) { ++ Client *sel = selmon->sel, *c; ++ ++ if(!sel || sel->isfloating) ++ return; ++ if((c = prevtiled(sel)) && c != nexttiled(selmon->clients)) { ++ detach(sel); ++ sel->next = c; ++ for(c = selmon->clients; c->next != sel->next; c = c->next); ++ c->next = sel; ++ } ++ focus(sel); ++ arrange(selmon); ++} ++ + void + quit(const Arg *arg) + { ++ if(arg->i) restart = 1; + running = 0; + } + +@@ -1266,6 +1676,20 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++void ++removesystrayicon(Client *i) ++{ ++ Client **ii; ++ ++ if (!showsystray || !i) ++ return; ++ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); ++ if (ii) ++ *ii = i->next; ++ free(i); ++} ++ ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1273,16 +1697,48 @@ resize(Client *c, int x, int y, int w, int h, int interact) + resizeclient(c, x, y, w, h); + } + ++void ++resizebarwin(Monitor *m) { ++ unsigned int w = m->ww; ++ if (showsystray && m == systraytomon(m)) ++ w -= getsystraywidth(); ++ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); ++} ++ + void + resizeclient(Client *c, int x, int y, int w, int h) + { + XWindowChanges wc; ++ unsigned int n; ++ unsigned int gapoffset; ++ unsigned int gapincr; ++ Client *nbc; + +- c->oldx = c->x; c->x = wc.x = x; +- c->oldy = c->y; c->y = wc.y = y; +- c->oldw = c->w; c->w = wc.width = w; +- c->oldh = c->h; c->h = wc.height = h; + wc.border_width = c->bw; ++ ++ /* Get number of clients for the selected monitor */ ++ for (n = 0, nbc = nexttiled(selmon->clients); nbc; nbc = nexttiled(nbc->next), n++); ++ ++ /* Do nothing if layout is floating */ ++ if (c->isfloating || selmon->lt[selmon->sellt]->arrange == NULL) { ++ gapincr = gapoffset = 0; ++ } else { ++ /* Remove border and gap if layout is monocle or only one client */ ++ if (selmon->lt[selmon->sellt]->arrange == monocle || n == 1) { ++ gapoffset = 0; ++ gapincr = -2 * borderpx; ++ wc.border_width = 0; ++ } else { ++ gapoffset = gappx; ++ gapincr = 2 * gappx; ++ } ++ } ++ ++ c->oldx = c->x; c->x = wc.x = x + gapoffset; ++ c->oldy = c->y; c->y = wc.y = y + gapoffset; ++ c->oldw = c->w; c->w = wc.width = w - gapincr; ++ c->oldh = c->h; c->h = wc.height = h - gapincr; ++ + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +@@ -1345,6 +1801,19 @@ resizemouse(const Arg *arg) + } + } + ++void ++resizerequest(XEvent *e) ++{ ++ XResizeRequestEvent *ev = &e->xresizerequest; ++ Client *i; ++ ++ if ((i = wintosystrayicon(ev->window))) { ++ updatesystrayicongeom(i, ev->width, ev->height); ++ resizebarwin(selmon); ++ updatesystray(); ++ } ++} ++ + void + restack(Monitor *m) + { +@@ -1381,10 +1850,18 @@ run(void) + handler[ev.type](&ev); /* call handler */ + } + ++void ++runAutostart(void) { ++ system("cd ~; ./.config/dwm/autostart_blocking.sh"); ++ system("cd ~; ./.config/dwm/autostart.sh &"); ++} ++ + void + scan(void) + { ++ scanner = 1; + unsigned int i, num; ++ char swin[256]; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + +@@ -1395,6 +1872,8 @@ scan(void) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); ++ else if (gettextprop(wins[i], netatom[NetClientList], swin, sizeof swin)) ++ manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) +@@ -1406,6 +1885,7 @@ scan(void) + if (wins) + XFree(wins); + } ++ scanner = 0; + } + + void +@@ -1418,7 +1898,10 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ if( attachbelow ) ++ attachBelow(c); ++ else ++ attach(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1434,26 +1917,36 @@ setclientstate(Client *c, long state) + } + + int +-sendevent(Client *c, Atom proto) ++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) + { + int n; +- Atom *protocols; ++ Atom *protocols, mt; + int exists = 0; + XEvent ev; + +- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { +- while (!exists && n--) +- exists = protocols[n] == proto; +- XFree(protocols); ++ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { ++ mt = wmatom[WMProtocols]; ++ if (XGetWMProtocols(dpy, w, &protocols, &n)) { ++ while (!exists && n--) ++ exists = protocols[n] == proto; ++ XFree(protocols); ++ } ++ } ++ else { ++ exists = True; ++ mt = proto; + } + if (exists) { + ev.type = ClientMessage; +- ev.xclient.window = c->win; +- ev.xclient.message_type = wmatom[WMProtocols]; ++ ev.xclient.window = w; ++ ev.xclient.message_type = mt; + ev.xclient.format = 32; +- ev.xclient.data.l[0] = proto; +- ev.xclient.data.l[1] = CurrentTime; +- XSendEvent(dpy, c->win, False, NoEventMask, &ev); ++ ev.xclient.data.l[0] = d0; ++ ev.xclient.data.l[1] = d1; ++ ev.xclient.data.l[2] = d2; ++ ev.xclient.data.l[3] = d3; ++ ev.xclient.data.l[4] = d4; ++ XSendEvent(dpy, w, False, mask, &ev); + } + return exists; + } +@@ -1467,7 +1960,7 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } +- sendevent(c, wmatom[WMTakeFocus]); ++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + } + + void +@@ -1512,6 +2005,23 @@ setlayout(const Arg *arg) + drawbar(selmon); + } + ++void setcfact(const Arg *arg) { ++ float f; ++ Client *c; ++ ++ c = selmon->sel; ++ ++ if(!arg || !c || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ f = arg->f + c->cfact; ++ if(arg->f == 0.0) ++ f = 1.0; ++ else if(f < 0.25 || f > 4.0) ++ return; ++ c->cfact = f; ++ arrange(selmon); ++} ++ + /* arg > 1.0 will set mfact absolutely */ + void + setmfact(const Arg *arg) +@@ -1521,7 +2031,7 @@ setmfact(const Arg *arg) + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; +- if (f < 0.05 || f > 0.95) ++ if (f < 0.1 || f > 0.9) + return; + selmon->mfact = f; + arrange(selmon); +@@ -1537,6 +2047,9 @@ setup(void) + /* clean up any zombies immediately */ + sigchld(0); + ++ signal(SIGHUP, sighup); ++ signal(SIGTERM, sigterm); ++ + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); +@@ -1556,6 +2069,10 @@ setup(void) + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); ++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); ++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); ++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); ++ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); +@@ -1563,6 +2080,9 @@ setup(void) + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); ++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False); ++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); ++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); +@@ -1571,6 +2091,8 @@ setup(void) + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); ++ /* init system tray */ ++ updatesystray(); + /* init bars */ + updatebars(); + updatestatus(); +@@ -1637,6 +2159,37 @@ sigchld(int unused) + while (0 < waitpid(-1, NULL, WNOHANG)); + } + ++void ++sighup(int unused) ++{ ++ Arg a = {.i = 1}; ++ quit(&a); ++} ++ ++void ++sigterm(int unused) ++{ ++ Arg a = {.i = 0}; ++ quit(&a); ++} ++ ++void ++sigdwmblocks(const Arg *arg) ++{ ++ union sigval sv; ++ sv.sival_int = (dwmblockssig << 8) | arg->i; ++ if (!dwmblockspid) ++ if (getdwmblockspid() == -1) ++ return; ++ ++ if (sigqueue(dwmblockspid, SIGUSR1, sv) == -1) { ++ if (errno == ESRCH) { ++ if (!getdwmblockspid()) ++ sigqueue(dwmblockspid, SIGUSR1, sv); ++ } ++ } ++} ++ + void + spawn(const Arg *arg) + { +@@ -1675,9 +2228,15 @@ void + tile(Monitor *m) + { + unsigned int i, n, h, mw, my, ty; ++ float mfacts = 0, sfacts = 0; + Client *c; + +- for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ if (n < m->nmaster) ++ mfacts += c->cfact; ++ else ++ sfacts += c->cfact; ++ } + if (n == 0) + return; + +@@ -1687,15 +2246,19 @@ tile(Monitor *m) + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { +- h = (m->wh - my) / (MIN(n, m->nmaster) - i); ++ h = (m->wh - my) * (c->cfact / mfacts); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); +- if (my + HEIGHT(c) < m->wh) ++ if (my + HEIGHT(c) < m->wh) { + my += HEIGHT(c); ++ mfacts -= c->cfact; ++ } + } else { +- h = (m->wh - ty) / (n - i); ++ h = (m->wh - ty) * (c->cfact / sfacts); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); +- if (ty + HEIGHT(c) < m->wh) ++ if (ty + HEIGHT(c) < m->wh) { + ty += HEIGHT(c); ++ sfacts -= c->cfact; ++ } + } + } + +@@ -1704,7 +2267,18 @@ togglebar(const Arg *arg) + { + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ resizebarwin(selmon); ++ if (showsystray) { ++ XWindowChanges wc; ++ if (!selmon->showbar) ++ wc.y = -bh; ++ else if (selmon->showbar) { ++ wc.y = 0; ++ if (!selmon->topbar) ++ wc.y = selmon->mh - bh; ++ } ++ XConfigureWindow(dpy, systray->win, CWY, &wc); ++ } + arrange(selmon); + } + +@@ -1722,6 +2296,13 @@ togglefloating(const Arg *arg) + arrange(selmon); + } + ++void ++togglefullscr(const Arg *arg) ++{ ++ if(selmon->sel) ++ setfullscreen(selmon->sel, !selmon->sel->isfullscreen); ++} ++ + void + toggletag(const Arg *arg) + { +@@ -1768,6 +2349,20 @@ unmanage(Client *c, int destroyed) + Monitor *m = c->mon; + XWindowChanges wc; + ++ if (c->swallowing) { ++ unswallow(c); ++ return; ++ } ++ ++ Client *s = swallowingclient(c->win); ++ if (s) { ++ free(s->swallowing); ++ s->swallowing = NULL; ++ arrange(m); ++ focus(NULL); ++ return; ++ } ++ + detach(c); + detachstack(c); + if (!destroyed) { +@@ -1782,9 +2377,12 @@ unmanage(Client *c, int destroyed) + XUngrabServer(dpy); + } + free(c); +- focus(NULL); +- updateclientlist(); +- arrange(m); ++ ++ if (!s) { ++ arrange(m); ++ focus(NULL); ++ updateclientlist(); ++ } + } + + void +@@ -1799,11 +2397,18 @@ unmapnotify(XEvent *e) + else + unmanage(c, 0); + } ++ else if ((c = wintosystrayicon(ev->window))) { ++ /* KLUDGE! sometimes icons occasionally unmap their windows, but do ++ * _not_ destroy them. We map those windows back */ ++ XMapRaised(dpy, c->win); ++ updatesystray(); ++ } + } + + void + updatebars(void) + { ++ unsigned int w; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, +@@ -1814,10 +2419,15 @@ updatebars(void) + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), ++ w = m->ww; ++ if (showsystray && m == systraytomon(m)) ++ w -= getsystraywidth(); ++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); ++ if (showsystray && m == systraytomon(m)) ++ XMapRaised(dpy, systray->win); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +@@ -1900,7 +2510,10 @@ updategeom(void) + m->clients = c->next; + detachstack(c); + c->mon = mons; +- attach(c); ++ if( attachbelow ) ++ attachBelow(c); ++ else ++ attach(c); + attachstack(c); + } + if (m == selmon) +@@ -1990,9 +2603,126 @@ updatesizehints(Client *c) + void + updatestatus(void) + { +- if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) ++ if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) + strcpy(stext, "dwm-"VERSION); ++ else ++ copyvalidchars(stext, rawstext); + drawbar(selmon); ++ updatesystray(); ++} ++ ++void ++updatesystrayicongeom(Client *i, int w, int h) ++{ ++ if (i) { ++ i->h = bh; ++ if (w == h) ++ i->w = bh; ++ else if (h == bh) ++ i->w = w; ++ else ++ i->w = (int) ((float)bh * ((float)w / (float)h)); ++ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); ++ /* force icons into the systray dimensions if they don't want to */ ++ if (i->h > bh) { ++ if (i->w == i->h) ++ i->w = bh; ++ else ++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); ++ i->h = bh; ++ } ++ } ++} ++ ++void ++updatesystrayiconstate(Client *i, XPropertyEvent *ev) ++{ ++ long flags; ++ int code = 0; ++ ++ if (!showsystray || !i || ev->atom != xatom[XembedInfo] || ++ !(flags = getatomprop(i, xatom[XembedInfo]))) ++ return; ++ ++ if (flags & XEMBED_MAPPED && !i->tags) { ++ i->tags = 1; ++ code = XEMBED_WINDOW_ACTIVATE; ++ XMapRaised(dpy, i->win); ++ setclientstate(i, NormalState); ++ } ++ else if (!(flags & XEMBED_MAPPED) && i->tags) { ++ i->tags = 0; ++ code = XEMBED_WINDOW_DEACTIVATE; ++ XUnmapWindow(dpy, i->win); ++ setclientstate(i, WithdrawnState); ++ } ++ else ++ return; ++ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, ++ systray->win, XEMBED_EMBEDDED_VERSION); ++} ++ ++void ++updatesystray(void) ++{ ++ XSetWindowAttributes wa; ++ XWindowChanges wc; ++ Client *i; ++ Monitor *m = systraytomon(NULL); ++ unsigned int x = m->mx + m->mw; ++ unsigned int w = 1; ++ ++ if (!showsystray) ++ return; ++ if (!systray) { ++ /* init systray */ ++ if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); ++ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); ++ wa.event_mask = ButtonPressMask | ExposureMask; ++ wa.override_redirect = True; ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XSelectInput(dpy, systray->win, SubstructureNotifyMask); ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, ++ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); ++ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); ++ XMapRaised(dpy, systray->win); ++ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); ++ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { ++ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); ++ XSync(dpy, False); ++ } ++ else { ++ fprintf(stderr, "dwm: unable to obtain system tray.\n"); ++ free(systray); ++ systray = NULL; ++ return; ++ } ++ } ++ for (w = 0, i = systray->icons; i; i = i->next) { ++ /* make sure the background color stays the same */ ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); ++ XMapRaised(dpy, i->win); ++ w += systrayspacing; ++ i->x = w; ++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); ++ w += i->w; ++ if (i->mon != m) ++ i->mon = m; ++ } ++ w = w ? w + systrayspacing : 1; ++ x -= w; ++ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); ++ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; ++ wc.stack_mode = Above; wc.sibling = m->barwin; ++ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); ++ XMapWindow(dpy, systray->win); ++ XMapSubwindows(dpy, systray->win); ++ /* redraw background */ ++ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); ++ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); ++ XSync(dpy, False); + } + + void +@@ -2047,6 +2777,110 @@ view(const Arg *arg) + arrange(selmon); + } + ++pid_t ++winpid(Window w) ++{ ++ pid_t result = 0; ++ ++ xcb_res_client_id_spec_t spec = {0}; ++ spec.client = w; ++ spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; ++ ++ xcb_generic_error_t *e = NULL; ++ xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); ++ xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); ++ ++ if (!r) ++ return (pid_t)0; ++ ++ xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); ++ for (; i.rem; xcb_res_client_id_value_next(&i)) { ++ spec = i.data->spec; ++ if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { ++ uint32_t *t = xcb_res_client_id_value_value(i.data); ++ result = *t; ++ break; ++ } ++ } ++ ++ free(r); ++ ++ if (result == (pid_t)-1) ++ result = 0; ++ return result; ++} ++ ++pid_t ++getparentprocess(pid_t p) ++{ ++ unsigned int v = 0; ++ ++#if defined(__linux__) ++ FILE *f; ++ char buf[256]; ++ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); ++ ++ if (!(f = fopen(buf, "r"))) ++ return (pid_t)0; ++ ++ if (fscanf(f, "%*u %*s %*c %u", (unsigned *)&v) != 1) ++ v = (pid_t)0; ++ fclose(f); ++#elif defined(__FreeBSD__) ++ struct kinfo_proc *proc = kinfo_getproc(p); ++ if (!proc) ++ return (pid_t)0; ++ ++ v = proc->ki_ppid; ++ free(proc); ++#endif ++ return (pid_t)v; ++} ++ ++int ++isdescprocess(pid_t p, pid_t c) ++{ ++ while (p != c && c != 0) ++ c = getparentprocess(c); ++ ++ return (int)c; ++} ++ ++Client * ++termforwin(const Client *w) ++{ ++ Client *c; ++ Monitor *m; ++ ++ if (!w->pid || w->isterminal) ++ return NULL; ++ ++ for (m = mons; m; m = m->next) { ++ for (c = m->clients; c; c = c->next) { ++ if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) ++ return c; ++ } ++ } ++ ++ return NULL; ++} ++ ++Client * ++swallowingclient(Window w) ++{ ++ Client *c; ++ Monitor *m; ++ ++ for (m = mons; m; m = m->next) { ++ for (c = m->clients; c; c = c->next) { ++ if (c->swallowing && c->swallowing->win == w) ++ return c; ++ } ++ } ++ ++ return NULL; ++} ++ + Client * + wintoclient(Window w) + { +@@ -2060,6 +2894,16 @@ wintoclient(Window w) + return NULL; + } + ++Client * ++wintosystrayicon(Window w) { ++ Client *i = NULL; ++ ++ if (!showsystray || !w) ++ return i; ++ for (i = systray->icons; i && i->win != w; i = i->next) ; ++ return i; ++} ++ + Monitor * + wintomon(Window w) + { +@@ -2113,18 +2957,58 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + return -1; + } + ++Monitor * ++systraytomon(Monitor *m) { ++ Monitor *t; ++ int i, n; ++ if(!systraypinning) { ++ if(!m) ++ return selmon; ++ return m == selmon ? m : NULL; ++ } ++ for(n = 1, t = mons; t && t->next; n++, t = t->next) ; ++ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; ++ if(systraypinningfailfirst && n < systraypinning) ++ return mons; ++ return t; ++} ++ + void + zoom(const Arg *arg) + { + Client *c = selmon->sel; ++ Client *at = NULL, *cold, *cprevious = NULL; + + if (!selmon->lt[selmon->sellt]->arrange + || (selmon->sel && selmon->sel->isfloating)) + return; +- if (c == nexttiled(selmon->clients)) +- if (!c || !(c = nexttiled(c->next))) +- return; +- pop(c); ++ if (c == nexttiled(selmon->clients)) { ++ at = findbefore(prevzoom); ++ if (at) ++ cprevious = nexttiled(at->next); ++ if (!cprevious || cprevious != prevzoom) { ++ prevzoom = NULL; ++ if (!c || !(c = nexttiled(c->next))) ++ return; ++ } else ++ c = cprevious; ++ } ++ cold = nexttiled(selmon->clients); ++ if (c != cold && !at) ++ at = findbefore(c); ++ detach(c); ++ attach(c); ++ /* swap windows instead of pushing the previous one down */ ++ if (c != cold && at) { ++ prevzoom = cold; ++ if (cold && at != cold) { ++ detach(cold); ++ cold->next = at->next; ++ at->next = cold; ++ } ++ } ++ focus(c); ++ arrange(c->mon); + } + + int +@@ -2138,6 +3022,8 @@ main(int argc, char *argv[]) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); ++ if (!(xcon = XGetXCBConnection(dpy))) ++ die("dwm: cannot get xcb connection\n"); + checkotherwm(); + setup(); + #ifdef __OpenBSD__ +@@ -2145,7 +3031,9 @@ main(int argc, char *argv[]) + die("pledge"); + #endif /* __OpenBSD__ */ + scan(); +- run(); ++ runAutostart(); ++run(); ++ if(restart) execvp(argv[0], argv); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +diff --git a/dwm.c.orig b/dwm.c.orig +new file mode 100644 +index 0000000..664c527 +--- /dev/null ++++ b/dwm.c.orig +@@ -0,0 +1,2152 @@ ++/* See LICENSE file for copyright and license details. ++ * ++ * dynamic window manager is designed like any other X client as well. It is ++ * driven through handling X events. In contrast to other X clients, a window ++ * manager selects for SubstructureRedirectMask on the root window, to receive ++ * events about window (dis-)appearance. Only one X connection at a time is ++ * allowed to select for this event mask. ++ * ++ * The event handlers of dwm are organized in an array which is accessed ++ * whenever a new event has been fetched. This allows event dispatching ++ * in O(1) time. ++ * ++ * Each child of the root window is called a client, except windows which have ++ * set the override_redirect flag. Clients are organized in a linked client ++ * list on each monitor, the focus history is remembered through a stack list ++ * on each monitor. Each client contains a bit array to indicate the tags of a ++ * client. ++ * ++ * Keys and tagging rules are organized as arrays and defined in config.h. ++ * ++ * To understand everything else, start reading main(). ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef XINERAMA ++#include ++#endif /* XINERAMA */ ++#include ++ ++#include "drw.h" ++#include "util.h" ++ ++/* macros */ ++#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) ++#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) ++#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ ++ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) ++#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) ++#define LENGTH(X) (sizeof X / sizeof X[0]) ++#define MOUSEMASK (BUTTONMASK|PointerMotionMask) ++#define WIDTH(X) ((X)->w + 2 * (X)->bw) ++#define HEIGHT(X) ((X)->h + 2 * (X)->bw) ++#define TAGMASK ((1 << LENGTH(tags)) - 1) ++#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) ++ ++/* enums */ ++enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ ++enum { SchemeNorm, SchemeSel }; /* color schemes */ ++enum { NetSupported, NetWMName, NetWMState, NetWMCheck, ++ NetWMFullscreen, NetActiveWindow, NetWMWindowType, ++ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ ++enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ++ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ++ ++typedef union { ++ int i; ++ unsigned int ui; ++ float f; ++ const void *v; ++} Arg; ++ ++typedef struct { ++ unsigned int click; ++ unsigned int mask; ++ unsigned int button; ++ void (*func)(const Arg *arg); ++ const Arg arg; ++} Button; ++ ++typedef struct Monitor Monitor; ++typedef struct Client Client; ++struct Client { ++ char name[256]; ++ float mina, maxa; ++ int x, y, w, h; ++ int oldx, oldy, oldw, oldh; ++ int basew, baseh, incw, inch, maxw, maxh, minw, minh; ++ int bw, oldbw; ++ unsigned int tags; ++ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ Client *next; ++ Client *snext; ++ Monitor *mon; ++ Window win; ++}; ++ ++typedef struct { ++ unsigned int mod; ++ KeySym keysym; ++ void (*func)(const Arg *); ++ const Arg arg; ++} Key; ++ ++typedef struct { ++ const char *symbol; ++ void (*arrange)(Monitor *); ++} Layout; ++ ++struct Monitor { ++ char ltsymbol[16]; ++ float mfact; ++ int nmaster; ++ int num; ++ int by; /* bar geometry */ ++ int mx, my, mw, mh; /* screen size */ ++ int wx, wy, ww, wh; /* window area */ ++ unsigned int seltags; ++ unsigned int sellt; ++ unsigned int tagset[2]; ++ int showbar; ++ int topbar; ++ Client *clients; ++ Client *sel; ++ Client *stack; ++ Monitor *next; ++ Window barwin; ++ const Layout *lt[2]; ++}; ++ ++typedef struct { ++ const char *class; ++ const char *instance; ++ const char *title; ++ unsigned int tags; ++ int isfloating; ++ int monitor; ++} Rule; ++ ++/* function declarations */ ++static void applyrules(Client *c); ++static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); ++static void arrange(Monitor *m); ++static void arrangemon(Monitor *m); ++static void attach(Client *c); ++static void attachstack(Client *c); ++static void buttonpress(XEvent *e); ++static void checkotherwm(void); ++static void cleanup(void); ++static void cleanupmon(Monitor *mon); ++static void clientmessage(XEvent *e); ++static void configure(Client *c); ++static void configurenotify(XEvent *e); ++static void configurerequest(XEvent *e); ++static Monitor *createmon(void); ++static void destroynotify(XEvent *e); ++static void detach(Client *c); ++static void detachstack(Client *c); ++static Monitor *dirtomon(int dir); ++static void drawbar(Monitor *m); ++static void drawbars(void); ++static void enternotify(XEvent *e); ++static void expose(XEvent *e); ++static void focus(Client *c); ++static void focusin(XEvent *e); ++static void focusmon(const Arg *arg); ++static void focusstack(const Arg *arg); ++static Atom getatomprop(Client *c, Atom prop); ++static int getrootptr(int *x, int *y); ++static long getstate(Window w); ++static int gettextprop(Window w, Atom atom, char *text, unsigned int size); ++static void grabbuttons(Client *c, int focused); ++static void grabkeys(void); ++static void incnmaster(const Arg *arg); ++static void keypress(XEvent *e); ++static void killclient(const Arg *arg); ++static void manage(Window w, XWindowAttributes *wa); ++static void mappingnotify(XEvent *e); ++static void maprequest(XEvent *e); ++static void monocle(Monitor *m); ++static void motionnotify(XEvent *e); ++static void movemouse(const Arg *arg); ++static Client *nexttiled(Client *c); ++static void pop(Client *); ++static void propertynotify(XEvent *e); ++static void quit(const Arg *arg); ++static Monitor *recttomon(int x, int y, int w, int h); ++static void resize(Client *c, int x, int y, int w, int h, int interact); ++static void resizeclient(Client *c, int x, int y, int w, int h); ++static void resizemouse(const Arg *arg); ++static void restack(Monitor *m); ++static void run(void); ++static void scan(void); ++static int sendevent(Client *c, Atom proto); ++static void sendmon(Client *c, Monitor *m); ++static void setclientstate(Client *c, long state); ++static void setfocus(Client *c); ++static void setfullscreen(Client *c, int fullscreen); ++static void setlayout(const Arg *arg); ++static void setmfact(const Arg *arg); ++static void setup(void); ++static void seturgent(Client *c, int urg); ++static void showhide(Client *c); ++static void sigchld(int unused); ++static void spawn(const Arg *arg); ++static void tag(const Arg *arg); ++static void tagmon(const Arg *arg); ++static void tile(Monitor *); ++static void togglebar(const Arg *arg); ++static void togglefloating(const Arg *arg); ++static void toggletag(const Arg *arg); ++static void toggleview(const Arg *arg); ++static void unfocus(Client *c, int setfocus); ++static void unmanage(Client *c, int destroyed); ++static void unmapnotify(XEvent *e); ++static void updatebarpos(Monitor *m); ++static void updatebars(void); ++static void updateclientlist(void); ++static int updategeom(void); ++static void updatenumlockmask(void); ++static void updatesizehints(Client *c); ++static void updatestatus(void); ++static void updatetitle(Client *c); ++static void updatewindowtype(Client *c); ++static void updatewmhints(Client *c); ++static void view(const Arg *arg); ++static Client *wintoclient(Window w); ++static Monitor *wintomon(Window w); ++static int xerror(Display *dpy, XErrorEvent *ee); ++static int xerrordummy(Display *dpy, XErrorEvent *ee); ++static int xerrorstart(Display *dpy, XErrorEvent *ee); ++static void zoom(const Arg *arg); ++ ++/* variables */ ++static const char broken[] = "broken"; ++static char stext[256]; ++static int screen; ++static int sw, sh; /* X display screen geometry width, height */ ++static int bh, blw = 0; /* bar geometry */ ++static int lrpad; /* sum of left and right padding for text */ ++static int (*xerrorxlib)(Display *, XErrorEvent *); ++static unsigned int numlockmask = 0; ++static void (*handler[LASTEvent]) (XEvent *) = { ++ [ButtonPress] = buttonpress, ++ [ClientMessage] = clientmessage, ++ [ConfigureRequest] = configurerequest, ++ [ConfigureNotify] = configurenotify, ++ [DestroyNotify] = destroynotify, ++ [EnterNotify] = enternotify, ++ [Expose] = expose, ++ [FocusIn] = focusin, ++ [KeyPress] = keypress, ++ [MappingNotify] = mappingnotify, ++ [MapRequest] = maprequest, ++ [MotionNotify] = motionnotify, ++ [PropertyNotify] = propertynotify, ++ [UnmapNotify] = unmapnotify ++}; ++static Atom wmatom[WMLast], netatom[NetLast]; ++static int running = 1; ++static Cur *cursor[CurLast]; ++static Clr **scheme; ++static Display *dpy; ++static Drw *drw; ++static Monitor *mons, *selmon; ++static Window root, wmcheckwin; ++ ++/* configuration, allows nested code to access above variables */ ++#include "config.h" ++ ++/* compile-time check if all tags fit into an unsigned int bit array. */ ++struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; ++ ++/* function implementations */ ++void ++applyrules(Client *c) ++{ ++ const char *class, *instance; ++ unsigned int i; ++ const Rule *r; ++ Monitor *m; ++ XClassHint ch = { NULL, NULL }; ++ ++ /* rule matching */ ++ c->isfloating = 0; ++ c->tags = 0; ++ XGetClassHint(dpy, c->win, &ch); ++ class = ch.res_class ? ch.res_class : broken; ++ instance = ch.res_name ? ch.res_name : broken; ++ ++ for (i = 0; i < LENGTH(rules); i++) { ++ r = &rules[i]; ++ if ((!r->title || strstr(c->name, r->title)) ++ && (!r->class || strstr(class, r->class)) ++ && (!r->instance || strstr(instance, r->instance))) ++ { ++ c->isfloating = r->isfloating; ++ c->tags |= r->tags; ++ for (m = mons; m && m->num != r->monitor; m = m->next); ++ if (m) ++ c->mon = m; ++ } ++ } ++ if (ch.res_class) ++ XFree(ch.res_class); ++ if (ch.res_name) ++ XFree(ch.res_name); ++ c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; ++} ++ ++int ++applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) ++{ ++ int baseismin; ++ Monitor *m = c->mon; ++ ++ /* set minimum possible */ ++ *w = MAX(1, *w); ++ *h = MAX(1, *h); ++ if (interact) { ++ if (*x > sw) ++ *x = sw - WIDTH(c); ++ if (*y > sh) ++ *y = sh - HEIGHT(c); ++ if (*x + *w + 2 * c->bw < 0) ++ *x = 0; ++ if (*y + *h + 2 * c->bw < 0) ++ *y = 0; ++ } else { ++ if (*x >= m->wx + m->ww) ++ *x = m->wx + m->ww - WIDTH(c); ++ if (*y >= m->wy + m->wh) ++ *y = m->wy + m->wh - HEIGHT(c); ++ if (*x + *w + 2 * c->bw <= m->wx) ++ *x = m->wx; ++ if (*y + *h + 2 * c->bw <= m->wy) ++ *y = m->wy; ++ } ++ if (*h < bh) ++ *h = bh; ++ if (*w < bh) ++ *w = bh; ++ if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { ++ /* see last two sentences in ICCCM 4.1.2.3 */ ++ baseismin = c->basew == c->minw && c->baseh == c->minh; ++ if (!baseismin) { /* temporarily remove base dimensions */ ++ *w -= c->basew; ++ *h -= c->baseh; ++ } ++ /* adjust for aspect limits */ ++ if (c->mina > 0 && c->maxa > 0) { ++ if (c->maxa < (float)*w / *h) ++ *w = *h * c->maxa + 0.5; ++ else if (c->mina < (float)*h / *w) ++ *h = *w * c->mina + 0.5; ++ } ++ if (baseismin) { /* increment calculation requires this */ ++ *w -= c->basew; ++ *h -= c->baseh; ++ } ++ /* adjust for increment value */ ++ if (c->incw) ++ *w -= *w % c->incw; ++ if (c->inch) ++ *h -= *h % c->inch; ++ /* restore base dimensions */ ++ *w = MAX(*w + c->basew, c->minw); ++ *h = MAX(*h + c->baseh, c->minh); ++ if (c->maxw) ++ *w = MIN(*w, c->maxw); ++ if (c->maxh) ++ *h = MIN(*h, c->maxh); ++ } ++ return *x != c->x || *y != c->y || *w != c->w || *h != c->h; ++} ++ ++void ++arrange(Monitor *m) ++{ ++ if (m) ++ showhide(m->stack); ++ else for (m = mons; m; m = m->next) ++ showhide(m->stack); ++ if (m) { ++ arrangemon(m); ++ restack(m); ++ } else for (m = mons; m; m = m->next) ++ arrangemon(m); ++} ++ ++void ++arrangemon(Monitor *m) ++{ ++ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); ++ if (m->lt[m->sellt]->arrange) ++ m->lt[m->sellt]->arrange(m); ++} ++ ++void ++attach(Client *c) ++{ ++ c->next = c->mon->clients; ++ c->mon->clients = c; ++} ++ ++void ++attachstack(Client *c) ++{ ++ c->snext = c->mon->stack; ++ c->mon->stack = c; ++} ++ ++void ++buttonpress(XEvent *e) ++{ ++ unsigned int i, x, click; ++ Arg arg = {0}; ++ Client *c; ++ Monitor *m; ++ XButtonPressedEvent *ev = &e->xbutton; ++ ++ click = ClkRootWin; ++ /* focus monitor if necessary */ ++ if ((m = wintomon(ev->window)) && m != selmon) { ++ unfocus(selmon->sel, 1); ++ selmon = m; ++ focus(NULL); ++ } ++ if (ev->window == selmon->barwin) { ++ i = x = 0; ++ do ++ x += TEXTW(tags[i]); ++ while (ev->x >= x && ++i < LENGTH(tags)); ++ if (i < LENGTH(tags)) { ++ click = ClkTagBar; ++ arg.ui = 1 << i; ++ } else if (ev->x < x + blw) ++ click = ClkLtSymbol; ++ else if (ev->x > selmon->ww - (int)TEXTW(stext)) ++ click = ClkStatusText; ++ else ++ click = ClkWinTitle; ++ } else if ((c = wintoclient(ev->window))) { ++ focus(c); ++ restack(selmon); ++ XAllowEvents(dpy, ReplayPointer, CurrentTime); ++ click = ClkClientWin; ++ } ++ for (i = 0; i < LENGTH(buttons); i++) ++ if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button ++ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) ++ buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); ++} ++ ++void ++checkotherwm(void) ++{ ++ xerrorxlib = XSetErrorHandler(xerrorstart); ++ /* this causes an error if some other window manager is running */ ++ XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); ++ XSync(dpy, False); ++ XSetErrorHandler(xerror); ++ XSync(dpy, False); ++} ++ ++void ++cleanup(void) ++{ ++ Arg a = {.ui = ~0}; ++ Layout foo = { "", NULL }; ++ Monitor *m; ++ size_t i; ++ ++ view(&a); ++ selmon->lt[selmon->sellt] = &foo; ++ for (m = mons; m; m = m->next) ++ while (m->stack) ++ unmanage(m->stack, 0); ++ XUngrabKey(dpy, AnyKey, AnyModifier, root); ++ while (mons) ++ cleanupmon(mons); ++ for (i = 0; i < CurLast; i++) ++ drw_cur_free(drw, cursor[i]); ++ for (i = 0; i < LENGTH(colors); i++) ++ free(scheme[i]); ++ XDestroyWindow(dpy, wmcheckwin); ++ drw_free(drw); ++ XSync(dpy, False); ++ XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); ++ XDeleteProperty(dpy, root, netatom[NetActiveWindow]); ++} ++ ++void ++cleanupmon(Monitor *mon) ++{ ++ Monitor *m; ++ ++ if (mon == mons) ++ mons = mons->next; ++ else { ++ for (m = mons; m && m->next != mon; m = m->next); ++ m->next = mon->next; ++ } ++ XUnmapWindow(dpy, mon->barwin); ++ XDestroyWindow(dpy, mon->barwin); ++ free(mon); ++} ++ ++void ++clientmessage(XEvent *e) ++{ ++ XClientMessageEvent *cme = &e->xclient; ++ Client *c = wintoclient(cme->window); ++ ++ if (!c) ++ return; ++ if (cme->message_type == netatom[NetWMState]) { ++ if (cme->data.l[1] == netatom[NetWMFullscreen] ++ || cme->data.l[2] == netatom[NetWMFullscreen]) ++ setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ ++ || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); ++ } else if (cme->message_type == netatom[NetActiveWindow]) { ++ if (c != selmon->sel && !c->isurgent) ++ seturgent(c, 1); ++ } ++} ++ ++void ++configure(Client *c) ++{ ++ XConfigureEvent ce; ++ ++ ce.type = ConfigureNotify; ++ ce.display = dpy; ++ ce.event = c->win; ++ ce.window = c->win; ++ ce.x = c->x; ++ ce.y = c->y; ++ ce.width = c->w; ++ ce.height = c->h; ++ ce.border_width = c->bw; ++ ce.above = None; ++ ce.override_redirect = False; ++ XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); ++} ++ ++void ++configurenotify(XEvent *e) ++{ ++ Monitor *m; ++ Client *c; ++ XConfigureEvent *ev = &e->xconfigure; ++ int dirty; ++ ++ /* TODO: updategeom handling sucks, needs to be simplified */ ++ if (ev->window == root) { ++ dirty = (sw != ev->width || sh != ev->height); ++ sw = ev->width; ++ sh = ev->height; ++ if (updategeom() || dirty) { ++ drw_resize(drw, sw, bh); ++ updatebars(); ++ for (m = mons; m; m = m->next) { ++ for (c = m->clients; c; c = c->next) ++ if (c->isfullscreen) ++ resizeclient(c, m->mx, m->my, m->mw, m->mh); ++ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ } ++ focus(NULL); ++ arrange(NULL); ++ } ++ } ++} ++ ++void ++configurerequest(XEvent *e) ++{ ++ Client *c; ++ Monitor *m; ++ XConfigureRequestEvent *ev = &e->xconfigurerequest; ++ XWindowChanges wc; ++ ++ if ((c = wintoclient(ev->window))) { ++ if (ev->value_mask & CWBorderWidth) ++ c->bw = ev->border_width; ++ else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { ++ m = c->mon; ++ if (ev->value_mask & CWX) { ++ c->oldx = c->x; ++ c->x = m->mx + ev->x; ++ } ++ if (ev->value_mask & CWY) { ++ c->oldy = c->y; ++ c->y = m->my + ev->y; ++ } ++ if (ev->value_mask & CWWidth) { ++ c->oldw = c->w; ++ c->w = ev->width; ++ } ++ if (ev->value_mask & CWHeight) { ++ c->oldh = c->h; ++ c->h = ev->height; ++ } ++ if ((c->x + c->w) > m->mx + m->mw && c->isfloating) ++ c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ ++ if ((c->y + c->h) > m->my + m->mh && c->isfloating) ++ c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ ++ if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) ++ configure(c); ++ if (ISVISIBLE(c)) ++ XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); ++ } else ++ configure(c); ++ } else { ++ wc.x = ev->x; ++ wc.y = ev->y; ++ wc.width = ev->width; ++ wc.height = ev->height; ++ wc.border_width = ev->border_width; ++ wc.sibling = ev->above; ++ wc.stack_mode = ev->detail; ++ XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); ++ } ++ XSync(dpy, False); ++} ++ ++Monitor * ++createmon(void) ++{ ++ Monitor *m; ++ ++ m = ecalloc(1, sizeof(Monitor)); ++ m->tagset[0] = m->tagset[1] = 1; ++ m->mfact = mfact; ++ m->nmaster = nmaster; ++ m->showbar = showbar; ++ m->topbar = topbar; ++ m->lt[0] = &layouts[0]; ++ m->lt[1] = &layouts[1 % LENGTH(layouts)]; ++ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ return m; ++} ++ ++void ++destroynotify(XEvent *e) ++{ ++ Client *c; ++ XDestroyWindowEvent *ev = &e->xdestroywindow; ++ ++ if ((c = wintoclient(ev->window))) ++ unmanage(c, 1); ++} ++ ++void ++detach(Client *c) ++{ ++ Client **tc; ++ ++ for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); ++ *tc = c->next; ++} ++ ++void ++detachstack(Client *c) ++{ ++ Client **tc, *t; ++ ++ for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); ++ *tc = c->snext; ++ ++ if (c == c->mon->sel) { ++ for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); ++ c->mon->sel = t; ++ } ++} ++ ++Monitor * ++dirtomon(int dir) ++{ ++ Monitor *m = NULL; ++ ++ if (dir > 0) { ++ if (!(m = selmon->next)) ++ m = mons; ++ } else if (selmon == mons) ++ for (m = mons; m->next; m = m->next); ++ else ++ for (m = mons; m->next != selmon; m = m->next); ++ return m; ++} ++ ++void ++drawbar(Monitor *m) ++{ ++ int x, w, tw = 0; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ ++ /* draw status first so it can be overdrawn by tags later */ ++ if (m == selmon) { /* status is only drawn on selected monitor */ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ ++ drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ } ++ ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++ urg |= c->tags; ++ } ++ x = 0; ++ for (i = 0; i < LENGTH(tags); i++) { ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); ++ if (occ & 1 << i) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, ++ m == selmon && selmon->sel && selmon->sel->tags & 1 << i, ++ urg & 1 << i); ++ x += w; ++ } ++ w = blw = TEXTW(m->ltsymbol); ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); ++ ++ if ((w = m->ww - tw - x) > bh) { ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++ if (m->sel->isfloating) ++ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); ++ } else { ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ } ++ } ++ drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++} ++ ++void ++drawbars(void) ++{ ++ Monitor *m; ++ ++ for (m = mons; m; m = m->next) ++ drawbar(m); ++} ++ ++void ++enternotify(XEvent *e) ++{ ++ Client *c; ++ Monitor *m; ++ XCrossingEvent *ev = &e->xcrossing; ++ ++ if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) ++ return; ++ c = wintoclient(ev->window); ++ m = c ? c->mon : wintomon(ev->window); ++ if (m != selmon) { ++ unfocus(selmon->sel, 1); ++ selmon = m; ++ } else if (!c || c == selmon->sel) ++ return; ++ focus(c); ++} ++ ++void ++expose(XEvent *e) ++{ ++ Monitor *m; ++ XExposeEvent *ev = &e->xexpose; ++ ++ if (ev->count == 0 && (m = wintomon(ev->window))) ++ drawbar(m); ++} ++ ++void ++focus(Client *c) ++{ ++ if (!c || !ISVISIBLE(c)) ++ for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); ++ if (selmon->sel && selmon->sel != c) ++ unfocus(selmon->sel, 0); ++ if (c) { ++ if (c->mon != selmon) ++ selmon = c->mon; ++ if (c->isurgent) ++ seturgent(c, 0); ++ detachstack(c); ++ attachstack(c); ++ grabbuttons(c, 1); ++ XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); ++ setfocus(c); ++ } else { ++ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); ++ XDeleteProperty(dpy, root, netatom[NetActiveWindow]); ++ } ++ selmon->sel = c; ++ drawbars(); ++} ++ ++/* there are some broken focus acquiring clients needing extra handling */ ++void ++focusin(XEvent *e) ++{ ++ XFocusChangeEvent *ev = &e->xfocus; ++ ++ if (selmon->sel && ev->window != selmon->sel->win) ++ setfocus(selmon->sel); ++} ++ ++void ++focusmon(const Arg *arg) ++{ ++ Monitor *m; ++ ++ if (!mons->next) ++ return; ++ if ((m = dirtomon(arg->i)) == selmon) ++ return; ++ unfocus(selmon->sel, 0); ++ selmon = m; ++ focus(NULL); ++} ++ ++void ++focusstack(const Arg *arg) ++{ ++ Client *c = NULL, *i; ++ ++ if (!selmon->sel) ++ return; ++ if (arg->i > 0) { ++ for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); ++ if (!c) ++ for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); ++ } else { ++ for (i = selmon->clients; i != selmon->sel; i = i->next) ++ if (ISVISIBLE(i)) ++ c = i; ++ if (!c) ++ for (; i; i = i->next) ++ if (ISVISIBLE(i)) ++ c = i; ++ } ++ if (c) { ++ focus(c); ++ restack(selmon); ++ } ++} ++ ++Atom ++getatomprop(Client *c, Atom prop) ++{ ++ int di; ++ unsigned long dl; ++ unsigned char *p = NULL; ++ Atom da, atom = None; ++ ++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, ++ &da, &di, &dl, &dl, &p) == Success && p) { ++ atom = *(Atom *)p; ++ XFree(p); ++ } ++ return atom; ++} ++ ++int ++getrootptr(int *x, int *y) ++{ ++ int di; ++ unsigned int dui; ++ Window dummy; ++ ++ return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); ++} ++ ++long ++getstate(Window w) ++{ ++ int format; ++ long result = -1; ++ unsigned char *p = NULL; ++ unsigned long n, extra; ++ Atom real; ++ ++ if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], ++ &real, &format, &n, &extra, (unsigned char **)&p) != Success) ++ return -1; ++ if (n != 0) ++ result = *p; ++ XFree(p); ++ return result; ++} ++ ++int ++gettextprop(Window w, Atom atom, char *text, unsigned int size) ++{ ++ char **list = NULL; ++ int n; ++ XTextProperty name; ++ ++ if (!text || size == 0) ++ return 0; ++ text[0] = '\0'; ++ if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) ++ return 0; ++ if (name.encoding == XA_STRING) ++ strncpy(text, (char *)name.value, size - 1); ++ else { ++ if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { ++ strncpy(text, *list, size - 1); ++ XFreeStringList(list); ++ } ++ } ++ text[size - 1] = '\0'; ++ XFree(name.value); ++ return 1; ++} ++ ++void ++grabbuttons(Client *c, int focused) ++{ ++ updatenumlockmask(); ++ { ++ unsigned int i, j; ++ unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; ++ XUngrabButton(dpy, AnyButton, AnyModifier, c->win); ++ if (!focused) ++ XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, ++ BUTTONMASK, GrabModeSync, GrabModeSync, None, None); ++ for (i = 0; i < LENGTH(buttons); i++) ++ if (buttons[i].click == ClkClientWin) ++ for (j = 0; j < LENGTH(modifiers); j++) ++ XGrabButton(dpy, buttons[i].button, ++ buttons[i].mask | modifiers[j], ++ c->win, False, BUTTONMASK, ++ GrabModeAsync, GrabModeSync, None, None); ++ } ++} ++ ++void ++grabkeys(void) ++{ ++ updatenumlockmask(); ++ { ++ unsigned int i, j; ++ unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; ++ KeyCode code; ++ ++ XUngrabKey(dpy, AnyKey, AnyModifier, root); ++ for (i = 0; i < LENGTH(keys); i++) ++ if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) ++ for (j = 0; j < LENGTH(modifiers); j++) ++ XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, ++ True, GrabModeAsync, GrabModeAsync); ++ } ++} ++ ++void ++incnmaster(const Arg *arg) ++{ ++ selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); ++ arrange(selmon); ++} ++ ++#ifdef XINERAMA ++static int ++isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) ++{ ++ while (n--) ++ if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org ++ && unique[n].width == info->width && unique[n].height == info->height) ++ return 0; ++ return 1; ++} ++#endif /* XINERAMA */ ++ ++void ++keypress(XEvent *e) ++{ ++ unsigned int i; ++ KeySym keysym; ++ XKeyEvent *ev; ++ ++ ev = &e->xkey; ++ keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); ++ for (i = 0; i < LENGTH(keys); i++) ++ if (keysym == keys[i].keysym ++ && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) ++ && keys[i].func) ++ keys[i].func(&(keys[i].arg)); ++} ++ ++void ++killclient(const Arg *arg) ++{ ++ if (!selmon->sel) ++ return; ++ if (!sendevent(selmon->sel, wmatom[WMDelete])) { ++ XGrabServer(dpy); ++ XSetErrorHandler(xerrordummy); ++ XSetCloseDownMode(dpy, DestroyAll); ++ XKillClient(dpy, selmon->sel->win); ++ XSync(dpy, False); ++ XSetErrorHandler(xerror); ++ XUngrabServer(dpy); ++ } ++} ++ ++void ++manage(Window w, XWindowAttributes *wa) ++{ ++ Client *c, *t = NULL; ++ Window trans = None; ++ XWindowChanges wc; ++ ++ c = ecalloc(1, sizeof(Client)); ++ c->win = w; ++ /* geometry */ ++ c->x = c->oldx = wa->x; ++ c->y = c->oldy = wa->y; ++ c->w = c->oldw = wa->width; ++ c->h = c->oldh = wa->height; ++ c->oldbw = wa->border_width; ++ ++ updatetitle(c); ++ if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { ++ c->mon = t->mon; ++ c->tags = t->tags; ++ } else { ++ c->mon = selmon; ++ applyrules(c); ++ } ++ ++ if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) ++ c->x = c->mon->mx + c->mon->mw - WIDTH(c); ++ if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) ++ c->y = c->mon->my + c->mon->mh - HEIGHT(c); ++ c->x = MAX(c->x, c->mon->mx); ++ /* only fix client y-offset, if the client center might cover the bar */ ++ c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) ++ && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); ++ c->bw = borderpx; ++ ++ wc.border_width = c->bw; ++ XConfigureWindow(dpy, w, CWBorderWidth, &wc); ++ XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); ++ configure(c); /* propagates border_width, if size doesn't change */ ++ updatewindowtype(c); ++ updatesizehints(c); ++ updatewmhints(c); ++ XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); ++ grabbuttons(c, 0); ++ if (!c->isfloating) ++ c->isfloating = c->oldstate = trans != None || c->isfixed; ++ if (c->isfloating) ++ XRaiseWindow(dpy, c->win); ++ attach(c); ++ attachstack(c); ++ XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, ++ (unsigned char *) &(c->win), 1); ++ XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ ++ setclientstate(c, NormalState); ++ if (c->mon == selmon) ++ unfocus(selmon->sel, 0); ++ c->mon->sel = c; ++ arrange(c->mon); ++ XMapWindow(dpy, c->win); ++ focus(NULL); ++} ++ ++void ++mappingnotify(XEvent *e) ++{ ++ XMappingEvent *ev = &e->xmapping; ++ ++ XRefreshKeyboardMapping(ev); ++ if (ev->request == MappingKeyboard) ++ grabkeys(); ++} ++ ++void ++maprequest(XEvent *e) ++{ ++ static XWindowAttributes wa; ++ XMapRequestEvent *ev = &e->xmaprequest; ++ ++ if (!XGetWindowAttributes(dpy, ev->window, &wa)) ++ return; ++ if (wa.override_redirect) ++ return; ++ if (!wintoclient(ev->window)) ++ manage(ev->window, &wa); ++} ++ ++void ++monocle(Monitor *m) ++{ ++ unsigned int n = 0; ++ Client *c; ++ ++ for (c = m->clients; c; c = c->next) ++ if (ISVISIBLE(c)) ++ n++; ++ if (n > 0) /* override layout symbol */ ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); ++ for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) ++ resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); ++} ++ ++void ++motionnotify(XEvent *e) ++{ ++ static Monitor *mon = NULL; ++ Monitor *m; ++ XMotionEvent *ev = &e->xmotion; ++ ++ if (ev->window != root) ++ return; ++ if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { ++ unfocus(selmon->sel, 1); ++ selmon = m; ++ focus(NULL); ++ } ++ mon = m; ++} ++ ++void ++movemouse(const Arg *arg) ++{ ++ int x, y, ocx, ocy, nx, ny; ++ Client *c; ++ Monitor *m; ++ XEvent ev; ++ Time lasttime = 0; ++ ++ if (!(c = selmon->sel)) ++ return; ++ if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ ++ return; ++ restack(selmon); ++ ocx = c->x; ++ ocy = c->y; ++ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, ++ None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) ++ return; ++ if (!getrootptr(&x, &y)) ++ return; ++ do { ++ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); ++ switch(ev.type) { ++ case ConfigureRequest: ++ case Expose: ++ case MapRequest: ++ handler[ev.type](&ev); ++ break; ++ case MotionNotify: ++ if ((ev.xmotion.time - lasttime) <= (1000 / 60)) ++ continue; ++ lasttime = ev.xmotion.time; ++ ++ nx = ocx + (ev.xmotion.x - x); ++ ny = ocy + (ev.xmotion.y - y); ++ if (abs(selmon->wx - nx) < snap) ++ nx = selmon->wx; ++ else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) ++ nx = selmon->wx + selmon->ww - WIDTH(c); ++ if (abs(selmon->wy - ny) < snap) ++ ny = selmon->wy; ++ else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) ++ ny = selmon->wy + selmon->wh - HEIGHT(c); ++ if (!c->isfloating && selmon->lt[selmon->sellt]->arrange ++ && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) ++ togglefloating(NULL); ++ if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) ++ resize(c, nx, ny, c->w, c->h, 1); ++ break; ++ } ++ } while (ev.type != ButtonRelease); ++ XUngrabPointer(dpy, CurrentTime); ++ if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { ++ sendmon(c, m); ++ selmon = m; ++ focus(NULL); ++ } ++} ++ ++Client * ++nexttiled(Client *c) ++{ ++ for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); ++ return c; ++} ++ ++void ++pop(Client *c) ++{ ++ detach(c); ++ attach(c); ++ focus(c); ++ arrange(c->mon); ++} ++ ++void ++propertynotify(XEvent *e) ++{ ++ Client *c; ++ Window trans; ++ XPropertyEvent *ev = &e->xproperty; ++ ++ if ((ev->window == root) && (ev->atom == XA_WM_NAME)) ++ updatestatus(); ++ else if (ev->state == PropertyDelete) ++ return; /* ignore */ ++ else if ((c = wintoclient(ev->window))) { ++ switch(ev->atom) { ++ default: break; ++ case XA_WM_TRANSIENT_FOR: ++ if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && ++ (c->isfloating = (wintoclient(trans)) != NULL)) ++ arrange(c->mon); ++ break; ++ case XA_WM_NORMAL_HINTS: ++ updatesizehints(c); ++ break; ++ case XA_WM_HINTS: ++ updatewmhints(c); ++ drawbars(); ++ break; ++ } ++ if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { ++ updatetitle(c); ++ if (c == c->mon->sel) ++ drawbar(c->mon); ++ } ++ if (ev->atom == netatom[NetWMWindowType]) ++ updatewindowtype(c); ++ } ++} ++ ++void ++quit(const Arg *arg) ++{ ++ running = 0; ++} ++ ++Monitor * ++recttomon(int x, int y, int w, int h) ++{ ++ Monitor *m, *r = selmon; ++ int a, area = 0; ++ ++ for (m = mons; m; m = m->next) ++ if ((a = INTERSECT(x, y, w, h, m)) > area) { ++ area = a; ++ r = m; ++ } ++ return r; ++} ++ ++void ++resize(Client *c, int x, int y, int w, int h, int interact) ++{ ++ if (applysizehints(c, &x, &y, &w, &h, interact)) ++ resizeclient(c, x, y, w, h); ++} ++ ++void ++resizeclient(Client *c, int x, int y, int w, int h) ++{ ++ XWindowChanges wc; ++ ++ c->oldx = c->x; c->x = wc.x = x; ++ c->oldy = c->y; c->y = wc.y = y; ++ c->oldw = c->w; c->w = wc.width = w; ++ c->oldh = c->h; c->h = wc.height = h; ++ wc.border_width = c->bw; ++ XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); ++ configure(c); ++ XSync(dpy, False); ++} ++ ++void ++resizemouse(const Arg *arg) ++{ ++ int ocx, ocy, nw, nh; ++ Client *c; ++ Monitor *m; ++ XEvent ev; ++ Time lasttime = 0; ++ ++ if (!(c = selmon->sel)) ++ return; ++ if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ ++ return; ++ restack(selmon); ++ ocx = c->x; ++ ocy = c->y; ++ if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, ++ None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) ++ return; ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); ++ do { ++ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); ++ switch(ev.type) { ++ case ConfigureRequest: ++ case Expose: ++ case MapRequest: ++ handler[ev.type](&ev); ++ break; ++ case MotionNotify: ++ if ((ev.xmotion.time - lasttime) <= (1000 / 60)) ++ continue; ++ lasttime = ev.xmotion.time; ++ ++ nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); ++ nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); ++ if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww ++ && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) ++ { ++ if (!c->isfloating && selmon->lt[selmon->sellt]->arrange ++ && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) ++ togglefloating(NULL); ++ } ++ if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) ++ resize(c, c->x, c->y, nw, nh, 1); ++ break; ++ } ++ } while (ev.type != ButtonRelease); ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); ++ XUngrabPointer(dpy, CurrentTime); ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); ++ if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { ++ sendmon(c, m); ++ selmon = m; ++ focus(NULL); ++ } ++} ++ ++void ++restack(Monitor *m) ++{ ++ Client *c; ++ XEvent ev; ++ XWindowChanges wc; ++ ++ drawbar(m); ++ if (!m->sel) ++ return; ++ if (m->sel->isfloating || !m->lt[m->sellt]->arrange) ++ XRaiseWindow(dpy, m->sel->win); ++ if (m->lt[m->sellt]->arrange) { ++ wc.stack_mode = Below; ++ wc.sibling = m->barwin; ++ for (c = m->stack; c; c = c->snext) ++ if (!c->isfloating && ISVISIBLE(c)) { ++ XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); ++ wc.sibling = c->win; ++ } ++ } ++ XSync(dpy, False); ++ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); ++} ++ ++void ++run(void) ++{ ++ XEvent ev; ++ /* main event loop */ ++ XSync(dpy, False); ++ while (running && !XNextEvent(dpy, &ev)) ++ if (handler[ev.type]) ++ handler[ev.type](&ev); /* call handler */ ++} ++ ++void ++scan(void) ++{ ++ unsigned int i, num; ++ Window d1, d2, *wins = NULL; ++ XWindowAttributes wa; ++ ++ if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { ++ for (i = 0; i < num; i++) { ++ if (!XGetWindowAttributes(dpy, wins[i], &wa) ++ || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) ++ continue; ++ if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) ++ manage(wins[i], &wa); ++ } ++ for (i = 0; i < num; i++) { /* now the transients */ ++ if (!XGetWindowAttributes(dpy, wins[i], &wa)) ++ continue; ++ if (XGetTransientForHint(dpy, wins[i], &d1) ++ && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) ++ manage(wins[i], &wa); ++ } ++ if (wins) ++ XFree(wins); ++ } ++} ++ ++void ++sendmon(Client *c, Monitor *m) ++{ ++ if (c->mon == m) ++ return; ++ unfocus(c, 1); ++ detach(c); ++ detachstack(c); ++ c->mon = m; ++ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ ++ attach(c); ++ attachstack(c); ++ focus(NULL); ++ arrange(NULL); ++} ++ ++void ++setclientstate(Client *c, long state) ++{ ++ long data[] = { state, None }; ++ ++ XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, ++ PropModeReplace, (unsigned char *)data, 2); ++} ++ ++int ++sendevent(Client *c, Atom proto) ++{ ++ int n; ++ Atom *protocols; ++ int exists = 0; ++ XEvent ev; ++ ++ if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { ++ while (!exists && n--) ++ exists = protocols[n] == proto; ++ XFree(protocols); ++ } ++ if (exists) { ++ ev.type = ClientMessage; ++ ev.xclient.window = c->win; ++ ev.xclient.message_type = wmatom[WMProtocols]; ++ ev.xclient.format = 32; ++ ev.xclient.data.l[0] = proto; ++ ev.xclient.data.l[1] = CurrentTime; ++ XSendEvent(dpy, c->win, False, NoEventMask, &ev); ++ } ++ return exists; ++} ++ ++void ++setfocus(Client *c) ++{ ++ if (!c->neverfocus) { ++ XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); ++ XChangeProperty(dpy, root, netatom[NetActiveWindow], ++ XA_WINDOW, 32, PropModeReplace, ++ (unsigned char *) &(c->win), 1); ++ } ++ sendevent(c, wmatom[WMTakeFocus]); ++} ++ ++void ++setfullscreen(Client *c, int fullscreen) ++{ ++ if (fullscreen && !c->isfullscreen) { ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); ++ c->isfullscreen = 1; ++ c->oldstate = c->isfloating; ++ c->oldbw = c->bw; ++ c->bw = 0; ++ c->isfloating = 1; ++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); ++ XRaiseWindow(dpy, c->win); ++ } else if (!fullscreen && c->isfullscreen){ ++ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, ++ PropModeReplace, (unsigned char*)0, 0); ++ c->isfullscreen = 0; ++ c->isfloating = c->oldstate; ++ c->bw = c->oldbw; ++ c->x = c->oldx; ++ c->y = c->oldy; ++ c->w = c->oldw; ++ c->h = c->oldh; ++ resizeclient(c, c->x, c->y, c->w, c->h); ++ arrange(c->mon); ++ } ++} ++ ++void ++setlayout(const Arg *arg) ++{ ++ if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) ++ selmon->sellt ^= 1; ++ if (arg && arg->v) ++ selmon->lt[selmon->sellt] = (Layout *)arg->v; ++ strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); ++ if (selmon->sel) ++ arrange(selmon); ++ else ++ drawbar(selmon); ++} ++ ++/* arg > 1.0 will set mfact absolutely */ ++void ++setmfact(const Arg *arg) ++{ ++ float f; ++ ++ if (!arg || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; ++ if (f < 0.05 || f > 0.95) ++ return; ++ selmon->mfact = f; ++ arrange(selmon); ++} ++ ++void ++setup(void) ++{ ++ int i; ++ XSetWindowAttributes wa; ++ Atom utf8string; ++ ++ /* clean up any zombies immediately */ ++ sigchld(0); ++ ++ /* init screen */ ++ screen = DefaultScreen(dpy); ++ sw = DisplayWidth(dpy, screen); ++ sh = DisplayHeight(dpy, screen); ++ root = RootWindow(dpy, screen); ++ drw = drw_create(dpy, screen, root, sw, sh); ++ if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) ++ die("no fonts could be loaded."); ++ lrpad = drw->fonts->h; ++ bh = drw->fonts->h + 2; ++ updategeom(); ++ /* init atoms */ ++ utf8string = XInternAtom(dpy, "UTF8_STRING", False); ++ wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); ++ wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); ++ wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); ++ wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); ++ netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); ++ netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); ++ netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); ++ netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); ++ netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); ++ netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); ++ netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); ++ netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); ++ netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); ++ /* init cursors */ ++ cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); ++ cursor[CurResize] = drw_cur_create(drw, XC_sizing); ++ cursor[CurMove] = drw_cur_create(drw, XC_fleur); ++ /* init appearance */ ++ scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); ++ for (i = 0; i < LENGTH(colors); i++) ++ scheme[i] = drw_scm_create(drw, colors[i], 3); ++ /* init bars */ ++ updatebars(); ++ updatestatus(); ++ /* supporting window for NetWMCheck */ ++ wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); ++ XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, ++ PropModeReplace, (unsigned char *) &wmcheckwin, 1); ++ XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, ++ PropModeReplace, (unsigned char *) "dwm", 3); ++ XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, ++ PropModeReplace, (unsigned char *) &wmcheckwin, 1); ++ /* EWMH support per view */ ++ XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, ++ PropModeReplace, (unsigned char *) netatom, NetLast); ++ XDeleteProperty(dpy, root, netatom[NetClientList]); ++ /* select events */ ++ wa.cursor = cursor[CurNormal]->cursor; ++ wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask ++ |ButtonPressMask|PointerMotionMask|EnterWindowMask ++ |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; ++ XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); ++ XSelectInput(dpy, root, wa.event_mask); ++ grabkeys(); ++ focus(NULL); ++} ++ ++ ++void ++seturgent(Client *c, int urg) ++{ ++ XWMHints *wmh; ++ ++ c->isurgent = urg; ++ if (!(wmh = XGetWMHints(dpy, c->win))) ++ return; ++ wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); ++ XSetWMHints(dpy, c->win, wmh); ++ XFree(wmh); ++} ++ ++void ++showhide(Client *c) ++{ ++ if (!c) ++ return; ++ if (ISVISIBLE(c)) { ++ /* show clients top down */ ++ XMoveWindow(dpy, c->win, c->x, c->y); ++ if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) ++ resize(c, c->x, c->y, c->w, c->h, 0); ++ showhide(c->snext); ++ } else { ++ /* hide clients bottom up */ ++ showhide(c->snext); ++ XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); ++ } ++} ++ ++void ++sigchld(int unused) ++{ ++ if (signal(SIGCHLD, sigchld) == SIG_ERR) ++ die("can't install SIGCHLD handler:"); ++ while (0 < waitpid(-1, NULL, WNOHANG)); ++} ++ ++void ++spawn(const Arg *arg) ++{ ++ if (arg->v == dmenucmd) ++ dmenumon[0] = '0' + selmon->num; ++ if (fork() == 0) { ++ if (dpy) ++ close(ConnectionNumber(dpy)); ++ setsid(); ++ execvp(((char **)arg->v)[0], (char **)arg->v); ++ fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); ++ perror(" failed"); ++ exit(EXIT_SUCCESS); ++ } ++} ++ ++void ++tag(const Arg *arg) ++{ ++ if (selmon->sel && arg->ui & TAGMASK) { ++ selmon->sel->tags = arg->ui & TAGMASK; ++ focus(NULL); ++ arrange(selmon); ++ } ++} ++ ++void ++tagmon(const Arg *arg) ++{ ++ if (!selmon->sel || !mons->next) ++ return; ++ sendmon(selmon->sel, dirtomon(arg->i)); ++} ++ ++void ++tile(Monitor *m) ++{ ++ unsigned int i, n, h, mw, my, ty; ++ Client *c; ++ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ if (n == 0) ++ return; ++ ++ if (n > m->nmaster) ++ mw = m->nmaster ? m->ww * m->mfact : 0; ++ else ++ mw = m->ww; ++ for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ h = (m->wh - my) / (MIN(n, m->nmaster) - i); ++ resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); ++ if (my + HEIGHT(c) < m->wh) ++ my += HEIGHT(c); ++ } else { ++ h = (m->wh - ty) / (n - i); ++ resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); ++ if (ty + HEIGHT(c) < m->wh) ++ ty += HEIGHT(c); ++ } ++} ++ ++void ++togglebar(const Arg *arg) ++{ ++ selmon->showbar = !selmon->showbar; ++ updatebarpos(selmon); ++ XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ arrange(selmon); ++} ++ ++void ++togglefloating(const Arg *arg) ++{ ++ if (!selmon->sel) ++ return; ++ if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ ++ return; ++ selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; ++ if (selmon->sel->isfloating) ++ resize(selmon->sel, selmon->sel->x, selmon->sel->y, ++ selmon->sel->w, selmon->sel->h, 0); ++ arrange(selmon); ++} ++ ++void ++toggletag(const Arg *arg) ++{ ++ unsigned int newtags; ++ ++ if (!selmon->sel) ++ return; ++ newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); ++ if (newtags) { ++ selmon->sel->tags = newtags; ++ focus(NULL); ++ arrange(selmon); ++ } ++} ++ ++void ++toggleview(const Arg *arg) ++{ ++ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); ++ ++ if (newtagset) { ++ selmon->tagset[selmon->seltags] = newtagset; ++ focus(NULL); ++ arrange(selmon); ++ } ++} ++ ++void ++unfocus(Client *c, int setfocus) ++{ ++ if (!c) ++ return; ++ grabbuttons(c, 0); ++ XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); ++ if (setfocus) { ++ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); ++ XDeleteProperty(dpy, root, netatom[NetActiveWindow]); ++ } ++} ++ ++void ++unmanage(Client *c, int destroyed) ++{ ++ Monitor *m = c->mon; ++ XWindowChanges wc; ++ ++ detach(c); ++ detachstack(c); ++ if (!destroyed) { ++ wc.border_width = c->oldbw; ++ XGrabServer(dpy); /* avoid race conditions */ ++ XSetErrorHandler(xerrordummy); ++ XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ ++ XUngrabButton(dpy, AnyButton, AnyModifier, c->win); ++ setclientstate(c, WithdrawnState); ++ XSync(dpy, False); ++ XSetErrorHandler(xerror); ++ XUngrabServer(dpy); ++ } ++ free(c); ++ focus(NULL); ++ updateclientlist(); ++ arrange(m); ++} ++ ++void ++unmapnotify(XEvent *e) ++{ ++ Client *c; ++ XUnmapEvent *ev = &e->xunmap; ++ ++ if ((c = wintoclient(ev->window))) { ++ if (ev->send_event) ++ setclientstate(c, WithdrawnState); ++ else ++ unmanage(c, 0); ++ } ++} ++ ++void ++updatebars(void) ++{ ++ Monitor *m; ++ XSetWindowAttributes wa = { ++ .override_redirect = True, ++ .background_pixmap = ParentRelative, ++ .event_mask = ButtonPressMask|ExposureMask ++ }; ++ XClassHint ch = {"dwm", "dwm"}; ++ for (m = mons; m; m = m->next) { ++ if (m->barwin) ++ continue; ++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), ++ CopyFromParent, DefaultVisual(dpy, screen), ++ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); ++ XMapRaised(dpy, m->barwin); ++ XSetClassHint(dpy, m->barwin, &ch); ++ } ++} ++ ++void ++updatebarpos(Monitor *m) ++{ ++ m->wy = m->my; ++ m->wh = m->mh; ++ if (m->showbar) { ++ m->wh -= bh; ++ m->by = m->topbar ? m->wy : m->wy + m->wh; ++ m->wy = m->topbar ? m->wy + bh : m->wy; ++ } else ++ m->by = -bh; ++} ++ ++void ++updateclientlist() ++{ ++ Client *c; ++ Monitor *m; ++ ++ XDeleteProperty(dpy, root, netatom[NetClientList]); ++ for (m = mons; m; m = m->next) ++ for (c = m->clients; c; c = c->next) ++ XChangeProperty(dpy, root, netatom[NetClientList], ++ XA_WINDOW, 32, PropModeAppend, ++ (unsigned char *) &(c->win), 1); ++} ++ ++int ++updategeom(void) ++{ ++ int dirty = 0; ++ ++#ifdef XINERAMA ++ if (XineramaIsActive(dpy)) { ++ int i, j, n, nn; ++ Client *c; ++ Monitor *m; ++ XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); ++ XineramaScreenInfo *unique = NULL; ++ ++ for (n = 0, m = mons; m; m = m->next, n++); ++ /* only consider unique geometries as separate screens */ ++ unique = ecalloc(nn, sizeof(XineramaScreenInfo)); ++ for (i = 0, j = 0; i < nn; i++) ++ if (isuniquegeom(unique, j, &info[i])) ++ memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); ++ XFree(info); ++ nn = j; ++ if (n <= nn) { /* new monitors available */ ++ for (i = 0; i < (nn - n); i++) { ++ for (m = mons; m && m->next; m = m->next); ++ if (m) ++ m->next = createmon(); ++ else ++ mons = createmon(); ++ } ++ for (i = 0, m = mons; i < nn && m; m = m->next, i++) ++ if (i >= n ++ || unique[i].x_org != m->mx || unique[i].y_org != m->my ++ || unique[i].width != m->mw || unique[i].height != m->mh) ++ { ++ dirty = 1; ++ m->num = i; ++ m->mx = m->wx = unique[i].x_org; ++ m->my = m->wy = unique[i].y_org; ++ m->mw = m->ww = unique[i].width; ++ m->mh = m->wh = unique[i].height; ++ updatebarpos(m); ++ } ++ } else { /* less monitors available nn < n */ ++ for (i = nn; i < n; i++) { ++ for (m = mons; m && m->next; m = m->next); ++ while ((c = m->clients)) { ++ dirty = 1; ++ m->clients = c->next; ++ detachstack(c); ++ c->mon = mons; ++ attach(c); ++ attachstack(c); ++ } ++ if (m == selmon) ++ selmon = mons; ++ cleanupmon(m); ++ } ++ } ++ free(unique); ++ } else ++#endif /* XINERAMA */ ++ { /* default monitor setup */ ++ if (!mons) ++ mons = createmon(); ++ if (mons->mw != sw || mons->mh != sh) { ++ dirty = 1; ++ mons->mw = mons->ww = sw; ++ mons->mh = mons->wh = sh; ++ updatebarpos(mons); ++ } ++ } ++ if (dirty) { ++ selmon = mons; ++ selmon = wintomon(root); ++ } ++ return dirty; ++} ++ ++void ++updatenumlockmask(void) ++{ ++ unsigned int i, j; ++ XModifierKeymap *modmap; ++ ++ numlockmask = 0; ++ modmap = XGetModifierMapping(dpy); ++ for (i = 0; i < 8; i++) ++ for (j = 0; j < modmap->max_keypermod; j++) ++ if (modmap->modifiermap[i * modmap->max_keypermod + j] ++ == XKeysymToKeycode(dpy, XK_Num_Lock)) ++ numlockmask = (1 << i); ++ XFreeModifiermap(modmap); ++} ++ ++void ++updatesizehints(Client *c) ++{ ++ long msize; ++ XSizeHints size; ++ ++ if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) ++ /* size is uninitialized, ensure that size.flags aren't used */ ++ size.flags = PSize; ++ if (size.flags & PBaseSize) { ++ c->basew = size.base_width; ++ c->baseh = size.base_height; ++ } else if (size.flags & PMinSize) { ++ c->basew = size.min_width; ++ c->baseh = size.min_height; ++ } else ++ c->basew = c->baseh = 0; ++ if (size.flags & PResizeInc) { ++ c->incw = size.width_inc; ++ c->inch = size.height_inc; ++ } else ++ c->incw = c->inch = 0; ++ if (size.flags & PMaxSize) { ++ c->maxw = size.max_width; ++ c->maxh = size.max_height; ++ } else ++ c->maxw = c->maxh = 0; ++ if (size.flags & PMinSize) { ++ c->minw = size.min_width; ++ c->minh = size.min_height; ++ } else if (size.flags & PBaseSize) { ++ c->minw = size.base_width; ++ c->minh = size.base_height; ++ } else ++ c->minw = c->minh = 0; ++ if (size.flags & PAspect) { ++ c->mina = (float)size.min_aspect.y / size.min_aspect.x; ++ c->maxa = (float)size.max_aspect.x / size.max_aspect.y; ++ } else ++ c->maxa = c->mina = 0.0; ++ c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); ++} ++ ++void ++updatestatus(void) ++{ ++ if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) ++ strcpy(stext, "dwm-"VERSION); ++ drawbar(selmon); ++} ++ ++void ++updatetitle(Client *c) ++{ ++ if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) ++ gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); ++ if (c->name[0] == '\0') /* hack to mark broken clients */ ++ strcpy(c->name, broken); ++} ++ ++void ++updatewindowtype(Client *c) ++{ ++ Atom state = getatomprop(c, netatom[NetWMState]); ++ Atom wtype = getatomprop(c, netatom[NetWMWindowType]); ++ ++ if (state == netatom[NetWMFullscreen]) ++ setfullscreen(c, 1); ++ if (wtype == netatom[NetWMWindowTypeDialog]) ++ c->isfloating = 1; ++} ++ ++void ++updatewmhints(Client *c) ++{ ++ XWMHints *wmh; ++ ++ if ((wmh = XGetWMHints(dpy, c->win))) { ++ if (c == selmon->sel && wmh->flags & XUrgencyHint) { ++ wmh->flags &= ~XUrgencyHint; ++ XSetWMHints(dpy, c->win, wmh); ++ } else ++ c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; ++ if (wmh->flags & InputHint) ++ c->neverfocus = !wmh->input; ++ else ++ c->neverfocus = 0; ++ XFree(wmh); ++ } ++} ++ ++void ++view(const Arg *arg) ++{ ++ if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) ++ return; ++ selmon->seltags ^= 1; /* toggle sel tagset */ ++ if (arg->ui & TAGMASK) ++ selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; ++ focus(NULL); ++ arrange(selmon); ++} ++ ++Client * ++wintoclient(Window w) ++{ ++ Client *c; ++ Monitor *m; ++ ++ for (m = mons; m; m = m->next) ++ for (c = m->clients; c; c = c->next) ++ if (c->win == w) ++ return c; ++ return NULL; ++} ++ ++Monitor * ++wintomon(Window w) ++{ ++ int x, y; ++ Client *c; ++ Monitor *m; ++ ++ if (w == root && getrootptr(&x, &y)) ++ return recttomon(x, y, 1, 1); ++ for (m = mons; m; m = m->next) ++ if (w == m->barwin) ++ return m; ++ if ((c = wintoclient(w))) ++ return c->mon; ++ return selmon; ++} ++ ++/* There's no way to check accesses to destroyed windows, thus those cases are ++ * ignored (especially on UnmapNotify's). Other types of errors call Xlibs ++ * default error handler, which may call exit. */ ++int ++xerror(Display *dpy, XErrorEvent *ee) ++{ ++ if (ee->error_code == BadWindow ++ || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) ++ || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) ++ || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) ++ || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) ++ || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) ++ || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) ++ || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) ++ || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) ++ return 0; ++ fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", ++ ee->request_code, ee->error_code); ++ return xerrorxlib(dpy, ee); /* may call exit */ ++} ++ ++int ++xerrordummy(Display *dpy, XErrorEvent *ee) ++{ ++ return 0; ++} ++ ++/* Startup Error handler to check if another window manager ++ * is already running. */ ++int ++xerrorstart(Display *dpy, XErrorEvent *ee) ++{ ++ die("dwm: another window manager is already running"); ++ return -1; ++} ++ ++void ++zoom(const Arg *arg) ++{ ++ Client *c = selmon->sel; ++ ++ if (!selmon->lt[selmon->sellt]->arrange ++ || (selmon->sel && selmon->sel->isfloating)) ++ return; ++ if (c == nexttiled(selmon->clients)) ++ if (!c || !(c = nexttiled(c->next))) ++ return; ++ pop(c); ++} ++ ++int ++main(int argc, char *argv[]) ++{ ++ if (argc == 2 && !strcmp("-v", argv[1])) ++ die("dwm-"VERSION); ++ else if (argc != 1) ++ die("usage: dwm [-v]"); ++ if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) ++ fputs("warning: no locale support\n", stderr); ++ if (!(dpy = XOpenDisplay(NULL))) ++ die("dwm: cannot open display"); ++ checkotherwm(); ++ setup(); ++#ifdef __OpenBSD__ ++ if (pledge("stdio rpath proc exec", NULL) == -1) ++ die("pledge"); ++#endif /* __OpenBSD__ */ ++ scan(); ++ run(); ++ cleanup(); ++ XCloseDisplay(dpy); ++ return EXIT_SUCCESS; ++} +diff --git a/dwm.c.rej b/dwm.c.rej +new file mode 100644 +index 0000000..079df04 +--- /dev/null ++++ b/dwm.c.rej +@@ -0,0 +1,124 @@ ++--- dwm.c +++++ dwm.c ++@@ -205,12 +245,16 @@ static void drawbar(Monitor *m); ++ static void drawbars(void); ++ static void enternotify(XEvent *e); ++ static void expose(XEvent *e); +++static Client *findbefore(Client *c); ++ static void focus(Client *c); ++ static void focusin(XEvent *e); ++ static void focusmon(const Arg *arg); ++ static void focusstack(const Arg *arg); +++static int getdwmblockspid(); +++static Atom getatomprop(Client *c, Atom prop); ++ static int getrootptr(int *x, int *y); ++ static long getstate(Window w); +++static unsigned int getsystraywidth(); ++ static int gettextprop(Window w, Atom atom, char *text, unsigned int size); ++ static void grabbuttons(Client *c, int focused); ++ static void grabkeys(void); ++@@ -590,9 +745,25 @@ buttonpress(XEvent *e) ++ arg.ui = 1 << i; ++ } else if (ev->x < x + blw) ++ click = ClkLtSymbol; ++- else if (ev->x > selmon->ww - TEXTW(stext)) +++ else if (ev->x > (x = selmon->ww - TEXTW(stext) + lrpad - getsystraywidth())) { ++ click = ClkStatusText; ++- else +++ char *text = rawstext; +++ int i = -1; +++ char ch; +++ dwmblockssig = 0; +++ while (text[++i]) { +++ if ((unsigned char)text[i] < ' ') { +++ ch = text[i]; +++ text[i] = '\0'; +++ x += TEXTW(text) - lrpad; +++ text[i] = ch; +++ text += i+1; +++ i = -1; +++ if (x >= ev->x) break; +++ dwmblockssig = ch; +++ } +++ } +++ } else ++ click = ClkWinTitle; ++ } else if ((c = wintoclient(ev->window))) { ++ focus(c); ++@@ -921,19 +1167,24 @@ dirtomon(int dir) ++ void ++ drawbar(Monitor *m) ++ { ++- int x, w, sw = 0; +++ int indn; +++ int x, w, sw = 0, stw = 0; ++ int boxs = drw->fonts->h / 9; ++ int boxw = drw->fonts->h / 6 + 2; ++ unsigned int i, occ = 0, urg = 0; ++ Client *c; ++ +++ if(showsystray && m == systraytomon(m)) +++ stw = getsystraywidth(); +++ ++ /* draw status first so it can be overdrawn by tags later */ ++ if (m == selmon) { /* status is only drawn on selected monitor */ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++- sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ ++- drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0); +++ sw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */ +++ drw_text(drw, m->ww - sw - stw, 0, sw, bh, lrpad / 2 - 2, stext, 0); ++ } ++ +++ resizebarwin(m); ++ for (c = m->clients; c; c = c->next) { ++ occ |= c->tags; ++ if (c->isurgent) ++@@ -941,20 +1192,25 @@ drawbar(Monitor *m) ++ } ++ x = 0; ++ for (i = 0; i < LENGTH(tags); i++) { +++ indn = 0; ++ w = TEXTW(tags[i]); ++ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); ++- if (occ & 1 << i) ++- drw_rect(drw, x + boxs, boxs, boxw, boxw, ++- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, ++- urg & 1 << i); +++ +++ for (c = m->clients; c; c = c->next) { +++ if (c->tags & (1 << i)) { +++ drw_rect(drw, x, 1 + (indn * 2), selmon->sel == c ? 6 : 1, 1, 1, urg & 1 << i); +++ indn++; +++ } +++ } +++ ++ x += w; ++ } ++ w = blw = TEXTW(m->ltsymbol); ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); ++ ++- if ((w = m->ww - sw - x) > bh) { +++ if ((w = m->ww - sw - stw - x) > bh) { ++ if (m->sel) { ++ drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); ++@@ -2216,13 +2776,15 @@ tile(Monitor *m) ++ mw = m->ww; ++ for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++- h = (m->wh - my) / (MIN(n, m->nmaster) - i); +++ h = (m->wh - my) * (c->cfact / mfacts); ++ resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); ++ my += HEIGHT(c); +++ mfacts -= c->cfact; ++ } else { ++- h = (m->wh - ty) / (n - i); +++ h = (m->wh - ty) * (c->cfact / sfacts); ++ resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); ++ ty += HEIGHT(c); +++ sfacts -= c->cfact; ++ } ++ } ++ +diff --git a/dwm.desktop b/dwm.desktop +new file mode 100644 +index 0000000..5a6021f +--- /dev/null ++++ b/dwm.desktop +@@ -0,0 +1,10 @@ ++[Desktop Entry] ++Version=6.3 ++Type=Application ++Name=dwm ++Comment=Suckless' dynamic window manager ++Exec=dwm ++Icon=dwm ++Terminal=false ++StartupNotify=false ++Categories=Application; +diff --git a/dwmc b/dwmc +new file mode 100755 +index 0000000..5ff8dbc +--- /dev/null ++++ b/dwmc +@@ -0,0 +1,40 @@ ++#!/usr/bin/env sh ++ ++signal() { ++ xsetroot -name "fsignal:$*" ++} ++ ++case $# in ++1) ++ case $1 in ++ setlayout | view | viewall | togglebar | togglefloating | zoom | killclient | quit) ++ signal $1 ++ ;; ++ *) ++ echo "Unknown command or missing one argument." ++ exit 1 ++ ;; ++ esac ++ ;; ++2) ++ case $1 in ++ view) ++ signal $1 ui $2 ++ ;; ++ viewex | toggleviewex | tagex | toggletagex | setlayoutex | focusstack | incnmaster | focusmon | tagmon) ++ signal $1 i $2 ++ ;; ++ setmfact) ++ signal $1 f $2 ++ ;; ++ *) ++ echo "Unknown command or one too many arguments." ++ exit 1 ++ ;; ++ esac ++ ;; ++*) ++ echo "Too many arguments." ++ exit 1 ++ ;; ++esac +diff --git a/volsv b/volsv +new file mode 100755 +index 0000000..f79662b +--- /dev/null ++++ b/volsv +@@ -0,0 +1,52 @@ ++#!/bin/sh ++# calculate average of two integers (for ALSA) ++average () { ++ echo "$(( $(( $1 + $2 )) / 2 ))$" ++} ++# print error message ++printerror () { ++ echo "$1 is not a recognized command or flag" ++} ++# if pulseaudio ++pulsesv () { ++ case "$1" in ++ "up" | "-i") pamixer -i 5 ;; ++ "down" | "-d") pamixer -d 5 ;; ++ "toggle" | "-t") pamixer -t ;; ++ "mic" | "-m") pamixer --source 1 -t ;; ++ "getv" | "-v") printf "%s%%\n" "$(pamixer --get-volume)" ;; ++ "getm" | "-g") pamixer --get-mute | sed 's/[Ff]alse/\[on\]/;s/[Tt]rue/\[off\]/' ;; ++ *) printerror "$1" ;; ++ esac ++} ++# if alsa ++alsasv () { ++ case "$1" in ++ "up" | "-i") amixer sset Master 5%+ ;; ++ "down" | "-d") amixer sset Master 5%- ;; ++ "toggle" | "-t") amixer sset Master toggle ;; ++ "mic" | "-m") amixer sset Capture toggle ;; ++ "getv" | "-v") amixer sget Master | grep '\[[0-9]*\%\]' | cut -d' ' -f6 | sed 's/\[//;s/\]//g' ;; ++ "getm" | "-g") amixer sget Master | grep '\[o[fn]' | cut -d' ' -f8 | head -1 ;; ++ *) printerror "$1" ;; ++ esac ++} ++#Search input for ++echo "$@" | grep -q ' *-h *' && echo \ ++"Volsv is Free software. You can use it for any purpose, but I make no guerantee about its usability or fitness "\ ++"for any particular purpose. You are also free to redistribute, modify, and distribute your modifications to "\ ++"volsv. Volsv is distributed under the BSD 3-Clause license to ensure full license compatibility with GNU, "\ ++"Linux, and BSD operating systems. A copy of this license is included in the repository.\n\n"\ ++"Volsv is a script designed to control the volume of a *NIX machine. The commands/flags are as follows: ++increase volume: 'up' or '-i' ++decrease volume: 'down' or '-d' ++toggle volume mute: 'toggle' or '-t' ++toggle mic mute: 'mic' or '-m' ++get volume level: 'getv' or '-v' ++get mute state: 'getm' or '-g'" && exit ++ ++for i in $@; do ++ pgrep -x pulseaudio >/dev/null && pulsesv $1 || alsasv $1 ++ pgrep -x dwmblocks >/dev/null && pkill -RTMIN+10 dwmblocks ++ pgrep -x dwmbar >/dev/null && dwmbar-signal volume ++done diff --git a/patches/dwm-actualfullscreen-20191112-cb3f58a.diff b/patches/dwm-actualfullscreen-20191112-cb3f58a.diff new file mode 100644 index 0000000..21eea19 --- /dev/null +++ b/patches/dwm-actualfullscreen-20191112-cb3f58a.diff @@ -0,0 +1,53 @@ +From 3a16816a6f5d38014c2a06ce395873c545c8789a Mon Sep 17 00:00:00 2001 +From: Soenke Lambert +Date: Tue, 12 Nov 2019 10:44:02 +0100 +Subject: [PATCH] Fullscreen current window with [Alt]+[Shift]+[f] + +This actually fullscreens a window, instead of just hiding the statusbar +and applying the monocle layout. +--- + config.def.h | 1 + + dwm.c | 8 ++++++++ + 2 files changed, 9 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..8cd3204 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -78,6 +78,7 @@ static Key keys[] = { + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { MODKEY|ShiftMask, XK_f, togglefullscr, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, +diff --git a/dwm.c b/dwm.c +index 4465af1..c1b899a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -211,6 +211,7 @@ static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglefullscr(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -1719,6 +1720,13 @@ togglefloating(const Arg *arg) + arrange(selmon); + } + ++void ++togglefullscr(const Arg *arg) ++{ ++ if(selmon->sel) ++ setfullscreen(selmon->sel, !selmon->sel->isfullscreen); ++} ++ + void + toggletag(const Arg *arg) + { +-- +2.17.1 + diff --git a/patches/dwm-attachbelow-toggleable-6.2.diff b/patches/dwm-attachbelow-toggleable-6.2.diff new file mode 100644 index 0000000..e5ff9fd --- /dev/null +++ b/patches/dwm-attachbelow-toggleable-6.2.diff @@ -0,0 +1,199 @@ +From ee036687ed9e1bb973b9e34694a57cf5dd67652d Mon Sep 17 00:00:00 2001 +From: Jonathan Hodgson +Date: Mon, 6 May 2019 18:34:40 +0100 +Subject: [PATCH 1/4] Adds attach below option + +--- + config.def.h | 1 + + dwm.c | 31 ++++++++++++++++++++++++++++--- + 2 files changed, 29 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..51ad933 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -35,6 +35,7 @@ static const Rule rules[] = { + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ ++static const int attachbelow = 1; /* 1 means attach at the end */ + + static const Layout layouts[] = { + /* symbol arrange function */ +diff --git a/dwm.c b/dwm.c +index 4465af1..bd715a2 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -147,6 +147,7 @@ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interac + static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); ++static void attachBelow(Client *c); + static void attachstack(Client *c); + static void buttonpress(XEvent *e); + static void checkotherwm(void); +@@ -405,6 +406,21 @@ attach(Client *c) + c->next = c->mon->clients; + c->mon->clients = c; + } ++void ++attachBelow(Client *c) ++{ ++ //If there is nothing on the monitor or the selected client is floating, attach as normal ++ if(c->mon->sel == NULL || c->mon->sel->isfloating) { ++ attach(c); ++ return; ++ } ++ ++ //Set the new client's next property to the same as the currently selected clients next ++ c->next = c->mon->sel->next; ++ //Set the currently selected clients next property to the new client ++ c->mon->sel->next = c; ++ ++} + + void + attachstack(Client *c) +@@ -1062,7 +1078,10 @@ manage(Window w, XWindowAttributes *wa) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +- attach(c); ++ if( attachbelow ) ++ attachBelow(c); ++ else ++ attach(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +@@ -1417,7 +1436,10 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ if( attachbelow ) ++ attachBelow(c); ++ else ++ attach(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1897,7 +1919,10 @@ updategeom(void) + m->clients = c->next; + detachstack(c); + c->mon = mons; +- attach(c); ++ if( attachbelow ) ++ attachBelow(c); ++ else ++ attach(c); + attachstack(c); + } + if (m == selmon) +-- +2.21.0 + + +From e212c1d8cbdcc56c33c717131dfa7c1689e27e9f Mon Sep 17 00:00:00 2001 +From: Jonathan Hodgson +Date: Mon, 6 May 2019 19:27:57 +0100 +Subject: [PATCH 2/4] fixes comment + +--- + config.def.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index 51ad933..cb8053a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -35,7 +35,7 @@ static const Rule rules[] = { + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +-static const int attachbelow = 1; /* 1 means attach at the end */ ++static const int attachbelow = 1; /* 1 means attach after the currently active window */ + + static const Layout layouts[] = { + /* symbol arrange function */ +-- +2.21.0 + + +From 7568ea3f8756e7e82b30c4943556ae646a445d1c Mon Sep 17 00:00:00 2001 +From: Jonathan Hodgson +Date: Mon, 6 May 2019 20:00:30 +0100 +Subject: [PATCH 3/4] Makes changes to man page to reflect attach below patch + +--- + dwm.1 | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/dwm.1 b/dwm.1 +index 13b3729..fb6e76c 100644 +--- a/dwm.1 ++++ b/dwm.1 +@@ -29,6 +29,9 @@ color. The tags of the focused window are indicated with a filled square in the + top left corner. The tags which are applied to one or more windows are + indicated with an empty square in the top left corner. + .P ++The attach below patch makes newly spawned windows attach after the currently ++selected window ++.P + dwm draws a small border around windows to indicate the focus state. + .SH OPTIONS + .TP +-- +2.21.0 + + +From 362b95a5b9f91673f27f3e3343b5738df3c9d6e9 Mon Sep 17 00:00:00 2001 +From: Jonathan Hodgson +Date: Sun, 2 Jun 2019 15:11:57 +0100 +Subject: [PATCH 4/4] Allows attach below to be toggled + +--- + config.def.h | 2 +- + dwm.c | 6 ++++++ + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index cb8053a..b4d35aa 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -35,7 +35,7 @@ static const Rule rules[] = { + static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ + static const int nmaster = 1; /* number of clients in master area */ + static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +-static const int attachbelow = 1; /* 1 means attach after the currently active window */ ++static int attachbelow = 1; /* 1 means attach after the currently active window */ + + static const Layout layouts[] = { + /* symbol arrange function */ +diff --git a/dwm.c b/dwm.c +index bd715a2..5d88653 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -148,6 +148,7 @@ static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); + static void attachBelow(Client *c); ++static void toggleAttachBelow(); + static void attachstack(Client *c); + static void buttonpress(XEvent *e); + static void checkotherwm(void); +@@ -422,6 +423,11 @@ attachBelow(Client *c) + + } + ++void toggleAttachBelow() ++{ ++ attachbelow = !attachbelow; ++} ++ + void + attachstack(Client *c) + { +-- +2.21.0 + diff --git a/patches/dwm-autostart-20161205-bb3bd6f.diff b/patches/dwm-autostart-20161205-bb3bd6f.diff new file mode 100644 index 0000000..6f11eaf --- /dev/null +++ b/patches/dwm-autostart-20161205-bb3bd6f.diff @@ -0,0 +1,39 @@ +commit 5918623c5bd7fda155bf9dc3d33890c4ae1722d0 +Author: Simon Bremer +Date: Thu Dec 22 17:31:07 2016 +0100 + + Applied and fixed autostart patch for previous version; + +diff --git a/dwm.c b/dwm.c +index d27cb67..066ed71 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -194,6 +194,7 @@ static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); + static void restack(Monitor *m); + static void run(void); ++static void runAutostart(void); + static void scan(void); + static int sendevent(Client *c, Atom proto); + static void sendmon(Client *c, Monitor *m); +@@ -1386,6 +1387,12 @@ run(void) + } + + void ++runAutostart(void) { ++ system("cd ~/.dwm; ./autostart_blocking.sh"); ++ system("cd ~/.dwm; ./autostart.sh &"); ++} ++ ++void + scan(void) + { + unsigned int i, num; +@@ -2145,6 +2152,7 @@ main(int argc, char *argv[]) + checkotherwm(); + setup(); + scan(); ++ runAutostart(); + run(); + cleanup(); + XCloseDisplay(dpy); diff --git a/patches/dwm-cfacts-20200913-61bb8b2.diff b/patches/dwm-cfacts-20200913-61bb8b2.diff new file mode 100644 index 0000000..bb70e13 --- /dev/null +++ b/patches/dwm-cfacts-20200913-61bb8b2.diff @@ -0,0 +1,117 @@ +From c32a879432573d71dec7fcb4bf68927d2f4cdf10 Mon Sep 17 00:00:00 2001 +From: iofq +Date: Sat, 12 Sep 2020 22:28:09 -0500 +Subject: [PATCH] Fixed 'cfacts' patch failure due to upstream commit + 'f09418bbb...' + +--- + config.def.h | 3 +++ + dwm.c | 34 +++++++++++++++++++++++++++++++--- + 2 files changed, 34 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..83910c1 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -70,6 +70,9 @@ static Key keys[] = { + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, ++ { MODKEY|ShiftMask, XK_h, setcfact, {.f = +0.25} }, ++ { MODKEY|ShiftMask, XK_l, setcfact, {.f = -0.25} }, ++ { MODKEY|ShiftMask, XK_o, setcfact, {.f = 0.00} }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, +diff --git a/dwm.c b/dwm.c +index 664c527..5233229 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -87,6 +87,7 @@ typedef struct Client Client; + struct Client { + char name[256]; + float mina, maxa; ++ float cfact; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh; +@@ -201,6 +202,7 @@ static void setclientstate(Client *c, long state); + static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); + static void setlayout(const Arg *arg); ++static void setcfact(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); + static void seturgent(Client *c, int urg); +@@ -1030,6 +1032,7 @@ manage(Window w, XWindowAttributes *wa) + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; ++ c->cfact = 1.0; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { +@@ -1512,6 +1515,23 @@ setlayout(const Arg *arg) + drawbar(selmon); + } + ++void setcfact(const Arg *arg) { ++ float f; ++ Client *c; ++ ++ c = selmon->sel; ++ ++ if(!arg || !c || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ f = arg->f + c->cfact; ++ if(arg->f == 0.0) ++ f = 1.0; ++ else if(f < 0.25 || f > 4.0) ++ return; ++ c->cfact = f; ++ arrange(selmon); ++} ++ + /* arg > 1.0 will set mfact absolutely */ + void + setmfact(const Arg *arg) +@@ -1675,9 +1695,15 @@ void + tile(Monitor *m) + { + unsigned int i, n, h, mw, my, ty; ++ float mfacts = 0, sfacts = 0; + Client *c; + +- for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ if (n < m->nmaster) ++ mfacts += c->cfact; ++ else ++ sfacts += c->cfact; ++ } + if (n == 0) + return; + +@@ -1687,15 +1713,17 @@ tile(Monitor *m) + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { +- h = (m->wh - my) / (MIN(n, m->nmaster) - i); ++ h = (m->wh - my) * (c->cfact / mfacts); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); ++ mfacts -= c->cfact; + } else { +- h = (m->wh - ty) / (n - i); ++ h = (m->wh - ty) * (c->cfact / sfacts); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); ++ sfacts -= c->cfact; + } + } + +-- +2.28.0 + diff --git a/patches/dwm-clientindicators-6.2.diff b/patches/dwm-clientindicators-6.2.diff new file mode 100644 index 0000000..f2e9afb --- /dev/null +++ b/patches/dwm-clientindicators-6.2.diff @@ -0,0 +1,48 @@ +From 8c72f9ea7c9cd8d254b52a4f7059113c41483597 Mon Sep 17 00:00:00 2001 +From: Miles Alan +Date: Mon, 17 Aug 2020 20:33:45 -0500 +Subject: [PATCH] Draws a dot indicator overlayed on each tag icon for each + client. The selected client is drawn as a larger horizontal line. + +--- + dwm.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/dwm.c b/dwm.c +index 4465af1..e0ca438 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -695,6 +695,7 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { ++ int indn; + int x, w, sw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; +@@ -715,13 +716,18 @@ drawbar(Monitor *m) + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { ++ indn = 0; + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); +- if (occ & 1 << i) +- drw_rect(drw, x + boxs, boxs, boxw, boxw, +- m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +- urg & 1 << i); ++ ++ for (c = m->clients; c; c = c->next) { ++ if (c->tags & (1 << i)) { ++ drw_rect(drw, x, 1 + (indn * 2), selmon->sel == c ? 6 : 1, 1, 1, urg & 1 << i); ++ indn++; ++ } ++ } ++ + x += w; + } + w = blw = TEXTW(m->ltsymbol); +-- +2.25.4 + diff --git a/patches/dwm-dwmc-6.2.diff b/patches/dwm-dwmc-6.2.diff new file mode 100644 index 0000000..bf606d5 --- /dev/null +++ b/patches/dwm-dwmc-6.2.diff @@ -0,0 +1,240 @@ +From d94cb6f1a553d19127f44dbdc96e8bb5041956c2 Mon Sep 17 00:00:00 2001 +From: Nihal Jere +Date: Sat, 21 Mar 2020 15:16:49 -0500 +Subject: [PATCH] dwm-client + +--- + Makefile | 2 +- + config.def.h | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + dwm.c | 55 +++++++++++++++++++++++++++++++++++++++-- + dwmc | 40 ++++++++++++++++++++++++++++++ + 4 files changed, 164 insertions(+), 3 deletions(-) + create mode 100755 dwmc + +diff --git a/Makefile b/Makefile +index 77bcbc0..f837f5c 100644 +--- a/Makefile ++++ b/Makefile +@@ -38,7 +38,7 @@ dist: clean + + install: all + mkdir -p ${DESTDIR}${PREFIX}/bin +- cp -f dwm ${DESTDIR}${PREFIX}/bin ++ cp -f dwm dwmc ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 +diff --git a/config.def.h b/config.def.h +index 1c0b587..efbae79 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -113,3 +113,73 @@ static Button buttons[] = { + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, + }; + ++void ++setlayoutex(const Arg *arg) ++{ ++ setlayout(&((Arg) { .v = &layouts[arg->i] })); ++} ++ ++void ++viewex(const Arg *arg) ++{ ++ view(&((Arg) { .ui = 1 << arg->ui })); ++} ++ ++void ++viewall(const Arg *arg) ++{ ++ view(&((Arg){.ui = ~0})); ++} ++ ++void ++toggleviewex(const Arg *arg) ++{ ++ toggleview(&((Arg) { .ui = 1 << arg->ui })); ++} ++ ++void ++tagex(const Arg *arg) ++{ ++ tag(&((Arg) { .ui = 1 << arg->ui })); ++} ++ ++void ++toggletagex(const Arg *arg) ++{ ++ toggletag(&((Arg) { .ui = 1 << arg->ui })); ++} ++ ++void ++tagall(const Arg *arg) ++{ ++ tag(&((Arg){.ui = ~0})); ++} ++ ++/* signal definitions */ ++/* signum must be greater than 0 */ ++/* trigger signals using `xsetroot -name "fsignal: [ ]"` */ ++static Signal signals[] = { ++ /* signum function */ ++ { "focusstack", focusstack }, ++ { "setmfact", setmfact }, ++ { "togglebar", togglebar }, ++ { "incnmaster", incnmaster }, ++ { "togglefloating", togglefloating }, ++ { "focusmon", focusmon }, ++ { "tagmon", tagmon }, ++ { "zoom", zoom }, ++ { "view", view }, ++ { "viewall", viewall }, ++ { "viewex", viewex }, ++ { "toggleview", view }, ++ { "toggleviewex", toggleviewex }, ++ { "tag", tag }, ++ { "tagall", tagall }, ++ { "tagex", tagex }, ++ { "toggletag", tag }, ++ { "toggletagex", toggletagex }, ++ { "killclient", killclient }, ++ { "quit", quit }, ++ { "setlayout", setlayout }, ++ { "setlayoutex", setlayoutex }, ++}; +diff --git a/dwm.c b/dwm.c +index 4465af1..aa53706 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -106,6 +106,11 @@ typedef struct { + const Arg arg; + } Key; + ++typedef struct { ++ const char * sig; ++ void (*func)(const Arg *); ++} Signal; ++ + typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +@@ -148,6 +153,7 @@ static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); + static void attachstack(Client *c); ++static int fake_signal(void); + static void buttonpress(XEvent *e); + static void checkotherwm(void); + static void cleanup(void); +@@ -998,6 +1004,49 @@ keypress(XEvent *e) + keys[i].func(&(keys[i].arg)); + } + ++int ++fake_signal(void) ++{ ++ char fsignal[256]; ++ char indicator[9] = "fsignal:"; ++ char str_sig[50]; ++ char param[16]; ++ int i, len_str_sig, n, paramn; ++ size_t len_fsignal, len_indicator = strlen(indicator); ++ Arg arg; ++ ++ // Get root name property ++ if (gettextprop(root, XA_WM_NAME, fsignal, sizeof(fsignal))) { ++ len_fsignal = strlen(fsignal); ++ ++ // Check if this is indeed a fake signal ++ if (len_indicator > len_fsignal ? 0 : strncmp(indicator, fsignal, len_indicator) == 0) { ++ paramn = sscanf(fsignal+len_indicator, "%s%n%s%n", str_sig, &len_str_sig, param, &n); ++ ++ if (paramn == 1) arg = (Arg) {0}; ++ else if (paramn > 2) return 1; ++ else if (strncmp(param, "i", n - len_str_sig) == 0) ++ sscanf(fsignal + len_indicator + n, "%i", &(arg.i)); ++ else if (strncmp(param, "ui", n - len_str_sig) == 0) ++ sscanf(fsignal + len_indicator + n, "%u", &(arg.ui)); ++ else if (strncmp(param, "f", n - len_str_sig) == 0) ++ sscanf(fsignal + len_indicator + n, "%f", &(arg.f)); ++ else return 1; ++ ++ // Check if a signal was found, and if so handle it ++ for (i = 0; i < LENGTH(signals); i++) ++ if (strncmp(str_sig, signals[i].sig, len_str_sig) == 0 && signals[i].func) ++ signals[i].func(&(arg)); ++ ++ // A fake signal was sent ++ return 1; ++ } ++ } ++ ++ // No fake signal was sent, so proceed with update ++ return 0; ++} ++ + void + killclient(const Arg *arg) + { +@@ -1215,8 +1264,10 @@ propertynotify(XEvent *e) + Window trans; + XPropertyEvent *ev = &e->xproperty; + +- if ((ev->window == root) && (ev->atom == XA_WM_NAME)) +- updatestatus(); ++ if ((ev->window == root) && (ev->atom == XA_WM_NAME)) { ++ if (!fake_signal()) ++ updatestatus(); ++ } + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { +diff --git a/dwmc b/dwmc +new file mode 100755 +index 0000000..5ff8dbc +--- /dev/null ++++ b/dwmc +@@ -0,0 +1,40 @@ ++#!/usr/bin/env sh ++ ++signal() { ++ xsetroot -name "fsignal:$*" ++} ++ ++case $# in ++1) ++ case $1 in ++ setlayout | view | viewall | togglebar | togglefloating | zoom | killclient | quit) ++ signal $1 ++ ;; ++ *) ++ echo "Unknown command or missing one argument." ++ exit 1 ++ ;; ++ esac ++ ;; ++2) ++ case $1 in ++ view) ++ signal $1 ui $2 ++ ;; ++ viewex | toggleviewex | tagex | toggletagex | setlayoutex | focusstack | incnmaster | focusmon | tagmon) ++ signal $1 i $2 ++ ;; ++ setmfact) ++ signal $1 f $2 ++ ;; ++ *) ++ echo "Unknown command or one too many arguments." ++ exit 1 ++ ;; ++ esac ++ ;; ++*) ++ echo "Too many arguments." ++ exit 1 ++ ;; ++esac +-- +2.25.1 + diff --git a/patches/dwm-push_no_master-6.2.diff b/patches/dwm-push_no_master-6.2.diff new file mode 100644 index 0000000..873e438 --- /dev/null +++ b/patches/dwm-push_no_master-6.2.diff @@ -0,0 +1,70 @@ +diff --git a/dwm.c b/dwm.c +index 4465af1..49e63f0 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -185,7 +185,10 @@ static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); + static Client *nexttiled(Client *c); + static void pop(Client *); ++static Client *prevtiled(Client *c); + static void propertynotify(XEvent *e); ++static void pushdown(const Arg *arg); ++static void pushup(const Arg *arg); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); + static void resize(Client *c, int x, int y, int w, int h, int interact); +@@ -1208,6 +1211,16 @@ pop(Client *c) + arrange(c->mon); + } + ++Client * ++prevtiled(Client *c) { ++ Client *p, *r; ++ ++ for(p = selmon->clients, r = NULL; p && p != c; p = p->next) ++ if(!p->isfloating && ISVISIBLE(p)) ++ r = p; ++ return r; ++} ++ + void + propertynotify(XEvent *e) + { +@@ -1245,6 +1258,37 @@ propertynotify(XEvent *e) + } + } + ++void ++pushdown(const Arg *arg) { ++ Client *sel = selmon->sel, *c; ++ ++ if(!sel || sel->isfloating || sel == nexttiled(selmon->clients)) ++ return; ++ if((c = nexttiled(sel->next))) { ++ detach(sel); ++ sel->next = c->next; ++ c->next = sel; ++ } ++ focus(sel); ++ arrange(selmon); ++} ++ ++void ++pushup(const Arg *arg) { ++ Client *sel = selmon->sel, *c; ++ ++ if(!sel || sel->isfloating) ++ return; ++ if((c = prevtiled(sel)) && c != nexttiled(selmon->clients)) { ++ detach(sel); ++ sel->next = c; ++ for(c = selmon->clients; c->next != sel->next; c = c->next); ++ c->next = sel; ++ } ++ focus(sel); ++ arrange(selmon); ++} ++ + void + quit(const Arg *arg) + { diff --git a/patches/dwm-restartsig-20180523-6.2.diff b/patches/dwm-restartsig-20180523-6.2.diff new file mode 100644 index 0000000..f1f8680 --- /dev/null +++ b/patches/dwm-restartsig-20180523-6.2.diff @@ -0,0 +1,139 @@ +From 2991f37f0aaf44b9f9b11e7893ff0af8eb88f649 Mon Sep 17 00:00:00 2001 +From: Christopher Drelich +Date: Wed, 23 May 2018 22:50:38 -0400 +Subject: [PATCH] Modifies quit to handle restarts and adds SIGHUP and SIGTERM + handlers. + +Modified quit() to restart if it receives arg .i = 1 +MOD+CTRL+SHIFT+Q was added to confid.def.h to do just that. + +Signal handlers were handled for SIGHUP and SIGTERM. +If dwm receives these signals it calls quit() with +arg .i = to 1 or 0, respectively. + +To restart dwm: +MOD+CTRL+SHIFT+Q +or +kill -HUP dwmpid + +To quit dwm cleanly: +MOD+SHIFT+Q +or +kill -TERM dwmpid +--- + config.def.h | 1 + + dwm.1 | 10 ++++++++++ + dwm.c | 22 ++++++++++++++++++++++ + 3 files changed, 33 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a9ac303..e559429 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -94,6 +94,7 @@ static Key keys[] = { + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, ++ { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} }, + }; + + /* button definitions */ +diff --git a/dwm.1 b/dwm.1 +index 13b3729..36a331c 100644 +--- a/dwm.1 ++++ b/dwm.1 +@@ -142,6 +142,9 @@ Add/remove all windows with nth tag to/from the view. + .TP + .B Mod1\-Shift\-q + Quit dwm. ++.TP ++.B Mod1\-Control\-Shift\-q ++Restart dwm. + .SS Mouse commands + .TP + .B Mod1\-Button1 +@@ -155,6 +158,13 @@ Resize focused window while dragging. Tiled windows will be toggled to the float + .SH CUSTOMIZATION + dwm is customized by creating a custom config.h and (re)compiling the source + code. This keeps it fast, secure and simple. ++.SH SIGNALS ++.TP ++.B SIGHUP - 1 ++Restart the dwm process. ++.TP ++.B SIGTERM - 15 ++Cleanly terminate the dwm process. + .SH SEE ALSO + .BR dmenu (1), + .BR st (1) +diff --git a/dwm.c b/dwm.c +index bb95e26..286eecd 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -205,6 +205,8 @@ static void setup(void); + static void seturgent(Client *c, int urg); + static void showhide(Client *c); + static void sigchld(int unused); ++static void sighup(int unused); ++static void sigterm(int unused); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); +@@ -260,6 +262,7 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [UnmapNotify] = unmapnotify + }; + static Atom wmatom[WMLast], netatom[NetLast]; ++static int restart = 0; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -1248,6 +1251,7 @@ propertynotify(XEvent *e) + void + quit(const Arg *arg) + { ++ if(arg->i) restart = 1; + running = 0; + } + +@@ -1536,6 +1540,9 @@ setup(void) + /* clean up any zombies immediately */ + sigchld(0); + ++ signal(SIGHUP, sighup); ++ signal(SIGTERM, sigterm); ++ + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); +@@ -1637,6 +1644,20 @@ sigchld(int unused) + } + + void ++sighup(int unused) ++{ ++ Arg a = {.i = 1}; ++ quit(&a); ++} ++ ++void ++sigterm(int unused) ++{ ++ Arg a = {.i = 0}; ++ quit(&a); ++} ++ ++void + spawn(const Arg *arg) + { + if (arg->v == dmenucmd) +@@ -2139,6 +2160,7 @@ main(int argc, char *argv[]) + setup(); + scan(); + run(); ++ if(restart) execvp(argv[0], argv); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +-- +2.7.4 + diff --git a/patches/dwm-statuscmd-signal-6.2.diff b/patches/dwm-statuscmd-signal-6.2.diff new file mode 100644 index 0000000..97fda1b --- /dev/null +++ b/patches/dwm-statuscmd-signal-6.2.diff @@ -0,0 +1,157 @@ +diff --git a/config.def.h b/config.def.h +index 1c0b587..b67825e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -103,7 +103,9 @@ static Button buttons[] = { + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, +- { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, ++ { ClkStatusText, 0, Button1, sigdwmblocks, {.i = 1} }, ++ { ClkStatusText, 0, Button2, sigdwmblocks, {.i = 2} }, ++ { ClkStatusText, 0, Button3, sigdwmblocks, {.i = 3} }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, +diff --git a/dwm.c b/dwm.c +index 4465af1..c600131 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -156,6 +156,7 @@ static void clientmessage(XEvent *e); + static void configure(Client *c); + static void configurenotify(XEvent *e); + static void configurerequest(XEvent *e); ++static void copyvalidchars(char *text, char *rawtext); + static Monitor *createmon(void); + static void destroynotify(XEvent *e); + static void detach(Client *c); +@@ -169,6 +170,7 @@ static void focus(Client *c); + static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); ++static int getdwmblockspid(); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +@@ -205,6 +207,7 @@ static void setup(void); + static void seturgent(Client *c, int urg); + static void showhide(Client *c); + static void sigchld(int unused); ++static void sigdwmblocks(const Arg *arg); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); +@@ -237,6 +240,9 @@ static void zoom(const Arg *arg); + /* variables */ + static const char broken[] = "broken"; + static char stext[256]; ++static char rawstext[256]; ++static int dwmblockssig; ++pid_t dwmblockspid = 0; + static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh, blw = 0; /* bar geometry */ +@@ -439,9 +445,26 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + blw) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - TEXTW(stext)) ++ else if (ev->x > (x = selmon->ww - TEXTW(stext) + lrpad)) { + click = ClkStatusText; +- else ++ ++ char *text = rawstext; ++ int i = -1; ++ char ch; ++ dwmblockssig = 0; ++ while (text[++i]) { ++ if ((unsigned char)text[i] < ' ') { ++ ch = text[i]; ++ text[i] = '\0'; ++ x += TEXTW(text) - lrpad; ++ text[i] = ch; ++ text += i+1; ++ i = -1; ++ if (x >= ev->x) break; ++ dwmblockssig = ch; ++ } ++ } ++ } else + click = ClkWinTitle; + } else if ((c = wintoclient(ev->window))) { + focus(c); +@@ -627,6 +650,19 @@ configurerequest(XEvent *e) + XSync(dpy, False); + } + ++void ++copyvalidchars(char *text, char *rawtext) ++{ ++ int i = -1, j = 0; ++ ++ while(rawtext[++i]) { ++ if ((unsigned char)rawtext[i] >= ' ') { ++ text[j++] = rawtext[i]; ++ } ++ } ++ text[j] = '\0'; ++} ++ + Monitor * + createmon(void) + { +@@ -871,6 +907,18 @@ getatomprop(Client *c, Atom prop) + return atom; + } + ++int ++getdwmblockspid() ++{ ++ char buf[16]; ++ FILE *fp = popen("pidof -s dwmblocks", "r"); ++ fgets(buf, sizeof(buf), fp); ++ pid_t pid = strtoul(buf, NULL, 10); ++ pclose(fp); ++ dwmblockspid = pid; ++ return pid != 0 ? 0 : -1; ++} ++ + int + getrootptr(int *x, int *y) + { +@@ -1636,6 +1684,23 @@ sigchld(int unused) + while (0 < waitpid(-1, NULL, WNOHANG)); + } + ++void ++sigdwmblocks(const Arg *arg) ++{ ++ union sigval sv; ++ sv.sival_int = (dwmblockssig << 8) | arg->i; ++ if (!dwmblockspid) ++ if (getdwmblockspid() == -1) ++ return; ++ ++ if (sigqueue(dwmblockspid, SIGUSR1, sv) == -1) { ++ if (errno == ESRCH) { ++ if (!getdwmblockspid()) ++ sigqueue(dwmblockspid, SIGUSR1, sv); ++ } ++ } ++} ++ + void + spawn(const Arg *arg) + { +@@ -1987,8 +2052,10 @@ updatesizehints(Client *c) + void + updatestatus(void) + { +- if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) ++ if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) + strcpy(stext, "dwm-"VERSION); ++ else ++ copyvalidchars(stext, rawstext); + drawbar(selmon); + } + diff --git a/patches/dwm-swallow-20200522-7accbcf.diff b/patches/dwm-swallow-20200522-7accbcf.diff new file mode 100644 index 0000000..1cb144f --- /dev/null +++ b/patches/dwm-swallow-20200522-7accbcf.diff @@ -0,0 +1,420 @@ +From 7accbcf7db35995d4c26c5cd69338aafa6feb89a Mon Sep 17 00:00:00 2001 +From: wtl +Date: Fri, 22 May 2020 22:38:38 +0300 +Subject: [PATCH] swallow X windows from the terminal + +--- + config.def.h | 9 ++- + config.mk | 2 +- + dwm.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 220 insertions(+), 9 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..4c0b25c 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,6 +3,7 @@ + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +@@ -26,9 +27,11 @@ static const Rule rules[] = { + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ +- /* class instance title tags mask isfloating monitor */ +- { "Gimp", NULL, NULL, 0, 1, -1 }, +- { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, ++ /* class instance title tags mask isfloating isterminal noswallow monitor */ ++ { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 }, ++ { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1, -1 }, ++ { "st", NULL, NULL, 0, 0, 1, -1, -1 }, ++ { NULL, NULL, "Event Tester", 0, 1, 0, 1, -1 }, /* xev */ + }; + + /* layout(s) */ +diff --git a/config.mk b/config.mk +index 7084c33..b77641d 100644 +--- a/config.mk ++++ b/config.mk +@@ -22,7 +22,7 @@ FREETYPEINC = /usr/include/freetype2 + + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/dwm.c b/dwm.c +index 9fd0286..1befee4 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -40,6 +40,8 @@ + #include + #endif /* XINERAMA */ + #include ++#include ++#include + + #include "drw.h" + #include "util.h" +@@ -92,9 +94,11 @@ struct Client { + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; + unsigned int tags; +- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow; ++ pid_t pid; + Client *next; + Client *snext; ++ Client *swallowing; + Monitor *mon; + Window win; + }; +@@ -138,6 +142,8 @@ typedef struct { + const char *title; + unsigned int tags; + int isfloating; ++ int isterminal; ++ int noswallow; + int monitor; + } Rule; + +@@ -235,9 +241,16 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++static pid_t getparentprocess(pid_t p); ++static int isdescprocess(pid_t p, pid_t c); ++static Client *swallowingclient(Window w); ++static Client *termforwin(const Client *c); ++static pid_t winpid(Window w); ++ + /* variables */ + static const char broken[] = "broken"; + static char stext[256]; ++static int scanner; + static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh, blw = 0; /* bar geometry */ +@@ -269,6 +282,8 @@ static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; + ++static xcb_connection_t *xcon; ++ + /* configuration, allows nested code to access above variables */ + #include "config.h" + +@@ -286,6 +301,7 @@ applyrules(Client *c) + XClassHint ch = { NULL, NULL }; + + /* rule matching */ ++ c->noswallow = -1; + c->isfloating = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); +@@ -298,6 +314,8 @@ applyrules(Client *c) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { ++ c->isterminal = r->isterminal; ++ c->noswallow = r->noswallow; + c->isfloating = r->isfloating; + c->tags |= r->tags; + for (m = mons; m && m->num != r->monitor; m = m->next); +@@ -414,6 +432,61 @@ attachstack(Client *c) + c->mon->stack = c; + } + ++void ++swallow(Client *p, Client *c) ++{ ++ Client *s; ++ ++ if (c->noswallow > 0 || c->isterminal) ++ return; ++ if (c->noswallow < 0 && !swallowfloating && c->isfloating) ++ return; ++ ++ detach(c); ++ detachstack(c); ++ ++ setclientstate(c, WithdrawnState); ++ XUnmapWindow(dpy, p->win); ++ ++ p->swallowing = c; ++ c->mon = p->mon; ++ ++ Window w = p->win; ++ p->win = c->win; ++ c->win = w; ++ ++ XChangeProperty(dpy, c->win, netatom[NetClientList], XA_WINDOW, 32, PropModeReplace, ++ (unsigned char *) &(p->win), 1); ++ ++ updatetitle(p); ++ s = scanner ? c : p; ++ XMoveResizeWindow(dpy, p->win, s->x, s->y, s->w, s->h); ++ arrange(p->mon); ++ configure(p); ++ updateclientlist(); ++} ++ ++void ++unswallow(Client *c) ++{ ++ c->win = c->swallowing->win; ++ ++ free(c->swallowing); ++ c->swallowing = NULL; ++ ++ XDeleteProperty(dpy, c->win, netatom[NetClientList]); ++ ++ /* unfullscreen the client */ ++ setfullscreen(c, 0); ++ updatetitle(c); ++ arrange(c->mon); ++ XMapWindow(dpy, c->win); ++ XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); ++ setclientstate(c, NormalState); ++ focus(NULL); ++ arrange(c->mon); ++} ++ + void + buttonpress(XEvent *e) + { +@@ -653,6 +726,9 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ ++ else if ((c = swallowingclient(ev->window))) ++ unmanage(c->swallowing, 1); + } + + void +@@ -1018,12 +1094,13 @@ killclient(const Arg *arg) + void + manage(Window w, XWindowAttributes *wa) + { +- Client *c, *t = NULL; ++ Client *c, *t = NULL, *term = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; ++ c->pid = winpid(w); + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; +@@ -1038,6 +1115,7 @@ manage(Window w, XWindowAttributes *wa) + } else { + c->mon = selmon; + applyrules(c); ++ term = termforwin(c); + } + + if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) +@@ -1074,6 +1152,8 @@ manage(Window w, XWindowAttributes *wa) + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); ++ if (term) ++ swallow(term, c); + focus(NULL); + } + +@@ -1384,7 +1464,9 @@ run(void) + void + scan(void) + { ++ scanner = 1; + unsigned int i, num; ++ char swin[256]; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + +@@ -1395,6 +1477,8 @@ scan(void) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); ++ else if (gettextprop(wins[i], netatom[NetClientList], swin, sizeof swin)) ++ manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) +@@ -1406,6 +1490,7 @@ scan(void) + if (wins) + XFree(wins); + } ++ scanner = 0; + } + + void +@@ -1768,6 +1853,20 @@ unmanage(Client *c, int destroyed) + Monitor *m = c->mon; + XWindowChanges wc; + ++ if (c->swallowing) { ++ unswallow(c); ++ return; ++ } ++ ++ Client *s = swallowingclient(c->win); ++ if (s) { ++ free(s->swallowing); ++ s->swallowing = NULL; ++ arrange(m); ++ focus(NULL); ++ return; ++ } ++ + detach(c); + detachstack(c); + if (!destroyed) { +@@ -1782,9 +1881,12 @@ unmanage(Client *c, int destroyed) + XUngrabServer(dpy); + } + free(c); +- focus(NULL); +- updateclientlist(); +- arrange(m); ++ ++ if (!s) { ++ arrange(m); ++ focus(NULL); ++ updateclientlist(); ++ } + } + + void +@@ -2047,6 +2149,110 @@ view(const Arg *arg) + arrange(selmon); + } + ++pid_t ++winpid(Window w) ++{ ++ pid_t result = 0; ++ ++ xcb_res_client_id_spec_t spec = {0}; ++ spec.client = w; ++ spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; ++ ++ xcb_generic_error_t *e = NULL; ++ xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); ++ xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); ++ ++ if (!r) ++ return (pid_t)0; ++ ++ xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); ++ for (; i.rem; xcb_res_client_id_value_next(&i)) { ++ spec = i.data->spec; ++ if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { ++ uint32_t *t = xcb_res_client_id_value_value(i.data); ++ result = *t; ++ break; ++ } ++ } ++ ++ free(r); ++ ++ if (result == (pid_t)-1) ++ result = 0; ++ return result; ++} ++ ++pid_t ++getparentprocess(pid_t p) ++{ ++ unsigned int v = 0; ++ ++#if defined(__linux__) ++ FILE *f; ++ char buf[256]; ++ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); ++ ++ if (!(f = fopen(buf, "r"))) ++ return (pid_t)0; ++ ++ if (fscanf(f, "%*u %*s %*c %u", (unsigned *)&v) != 1) ++ v = (pid_t)0; ++ fclose(f); ++#elif defined(__FreeBSD__) ++ struct kinfo_proc *proc = kinfo_getproc(p); ++ if (!proc) ++ return (pid_t)0; ++ ++ v = proc->ki_ppid; ++ free(proc); ++#endif ++ return (pid_t)v; ++} ++ ++int ++isdescprocess(pid_t p, pid_t c) ++{ ++ while (p != c && c != 0) ++ c = getparentprocess(c); ++ ++ return (int)c; ++} ++ ++Client * ++termforwin(const Client *w) ++{ ++ Client *c; ++ Monitor *m; ++ ++ if (!w->pid || w->isterminal) ++ return NULL; ++ ++ for (m = mons; m; m = m->next) { ++ for (c = m->clients; c; c = c->next) { ++ if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) ++ return c; ++ } ++ } ++ ++ return NULL; ++} ++ ++Client * ++swallowingclient(Window w) ++{ ++ Client *c; ++ Monitor *m; ++ ++ for (m = mons; m; m = m->next) { ++ for (c = m->clients; c; c = c->next) { ++ if (c->swallowing && c->swallowing->win == w) ++ return c; ++ } ++ } ++ ++ return NULL; ++} ++ + Client * + wintoclient(Window w) + { +@@ -2138,6 +2344,8 @@ main(int argc, char *argv[]) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); ++ if (!(xcon = XGetXCBConnection(dpy))) ++ die("dwm: cannot get xcb connection\n"); + checkotherwm(); + setup(); + #ifdef __OpenBSD__ +-- +2.26.2 + diff --git a/patches/dwm-systray-6.2.diff b/patches/dwm-systray-6.2.diff new file mode 100644 index 0000000..27187ac --- /dev/null +++ b/patches/dwm-systray-6.2.diff @@ -0,0 +1,746 @@ +From 4001ccae7b1a41bdcb247b0cf095a51af7b68c28 Mon Sep 17 00:00:00 2001 +From: Igor Gevka +Date: Sun, 16 Feb 2020 15:03:10 -0800 +Subject: [PATCH] [PATCH] Implements a system tray for dwm. + +Original author: Jan Christoph Ebersbach , inspired by http://code.google.com/p/dwm-plus +URL: http://dwm.suckless.org/patches/systray +dwm 6.2 port by Igor Gevka +--- + config.def.h | 4 + + dwm.c | 404 +++++++++++++++++++++++++++++++++++++++++++++++---- + 2 files changed, 382 insertions(+), 26 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..2d824d1 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,6 +3,10 @@ + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ ++static const unsigned int systrayspacing = 2; /* systray spacing */ ++static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ ++static const int showsystray = 1; /* 0 means no systray */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +diff --git a/dwm.c b/dwm.c +index 4465af1..3e361fa 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -57,12 +57,30 @@ + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + ++#define SYSTEM_TRAY_REQUEST_DOCK 0 ++ ++/* XEMBED messages */ ++#define XEMBED_EMBEDDED_NOTIFY 0 ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_FOCUS_IN 4 ++#define XEMBED_MODALITY_ON 10 ++ ++#define XEMBED_MAPPED (1 << 0) ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_WINDOW_DEACTIVATE 2 ++ ++#define VERSION_MAJOR 0 ++#define VERSION_MINOR 0 ++#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR ++ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, ++ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ +@@ -141,6 +159,12 @@ typedef struct { + int monitor; + } Rule; + ++typedef struct Systray Systray; ++struct Systray { ++ Window win; ++ Client *icons; ++}; ++ + /* function declarations */ + static void applyrules(Client *c); + static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +@@ -169,8 +193,10 @@ static void focus(Client *c); + static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); ++static Atom getatomprop(Client *c, Atom prop); + static int getrootptr(int *x, int *y); + static long getstate(Window w); ++static unsigned int getsystraywidth(); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); +@@ -188,13 +214,16 @@ static void pop(Client *); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); ++static void removesystrayicon(Client *i); + static void resize(Client *c, int x, int y, int w, int h, int interact); ++static void resizebarwin(Monitor *m); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); ++static void resizerequest(XEvent *e); + static void restack(Monitor *m); + static void run(void); + static void scan(void); +-static int sendevent(Client *c, Atom proto); ++static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); + static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); +@@ -206,6 +235,7 @@ static void seturgent(Client *c, int urg); + static void showhide(Client *c); + static void sigchld(int unused); + static void spawn(const Arg *arg); ++static Monitor *systraytomon(Monitor *m); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); +@@ -223,18 +253,23 @@ static int updategeom(void); + static void updatenumlockmask(void); + static void updatesizehints(Client *c); + static void updatestatus(void); ++static void updatesystray(void); ++static void updatesystrayicongeom(Client *i, int w, int h); ++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); + static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); ++static Client *wintosystrayicon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); + static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + + /* variables */ ++static Systray *systray = NULL; + static const char broken[] = "broken"; + static char stext[256]; + static int screen; +@@ -257,9 +292,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, ++ [ResizeRequest] = resizerequest, + [UnmapNotify] = unmapnotify + }; +-static Atom wmatom[WMLast], netatom[NetLast]; ++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -439,7 +475,7 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + blw) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - TEXTW(stext)) ++ else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth()) + click = ClkStatusText; + else + click = ClkWinTitle; +@@ -482,6 +518,11 @@ cleanup(void) + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); ++ if (showsystray) { ++ XUnmapWindow(dpy, systray->win); ++ XDestroyWindow(dpy, systray->win); ++ free(systray); ++ } + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) +@@ -512,9 +553,57 @@ cleanupmon(Monitor *mon) + void + clientmessage(XEvent *e) + { ++ XWindowAttributes wa; ++ XSetWindowAttributes swa; + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + ++ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { ++ /* add systray icons */ ++ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { ++ if (!(c = (Client *)calloc(1, sizeof(Client)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Client)); ++ if (!(c->win = cme->data.l[2])) { ++ free(c); ++ return; ++ } ++ c->mon = selmon; ++ c->next = systray->icons; ++ systray->icons = c; ++ if (!XGetWindowAttributes(dpy, c->win, &wa)) { ++ /* use sane defaults */ ++ wa.width = bh; ++ wa.height = bh; ++ wa.border_width = 0; ++ } ++ c->x = c->oldx = c->y = c->oldy = 0; ++ c->w = c->oldw = wa.width; ++ c->h = c->oldh = wa.height; ++ c->oldbw = wa.border_width; ++ c->bw = 0; ++ c->isfloating = True; ++ /* reuse tags field as mapped status */ ++ c->tags = 1; ++ updatesizehints(c); ++ updatesystrayicongeom(c, wa.width, wa.height); ++ XAddToSaveSet(dpy, c->win); ++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); ++ XReparentWindow(dpy, c->win, systray->win, 0, 0); ++ /* use parents background color */ ++ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ /* FIXME not sure if I have to send these events, too */ ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ XSync(dpy, False); ++ resizebarwin(selmon); ++ updatesystray(); ++ setclientstate(c, NormalState); ++ } ++ return; ++ } + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { +@@ -567,7 +656,7 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ resizebarwin(m); + } + focus(NULL); + arrange(NULL); +@@ -652,6 +741,11 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ else if ((c = wintosystrayicon(ev->window))) { ++ removesystrayicon(c); ++ resizebarwin(selmon); ++ updatesystray(); ++ } + } + + void +@@ -695,19 +789,23 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, sw = 0; ++ int x, w, sw = 0, stw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; + ++ if(showsystray && m == systraytomon(m)) ++ stw = getsystraywidth(); ++ + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); +- sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0); ++ sw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */ ++ drw_text(drw, m->ww - sw - stw, 0, sw, bh, lrpad / 2 - 2, stext, 0); + } + ++ resizebarwin(m); + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) +@@ -728,7 +826,7 @@ drawbar(Monitor *m) + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + +- if ((w = m->ww - sw - x) > bh) { ++ if ((w = m->ww - sw - stw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +@@ -739,7 +837,7 @@ drawbar(Monitor *m) + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); + } + + void +@@ -776,8 +874,11 @@ expose(XEvent *e) + Monitor *m; + XExposeEvent *ev = &e->xexpose; + +- if (ev->count == 0 && (m = wintomon(ev->window))) ++ if (ev->count == 0 && (m = wintomon(ev->window))) { + drawbar(m); ++ if (m == selmon) ++ updatesystray(); ++ } + } + + void +@@ -862,10 +963,17 @@ getatomprop(Client *c, Atom prop) + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; ++ /* FIXME getatomprop should return the number of items and a pointer to ++ * the stored data instead of this workaround */ ++ Atom req = XA_ATOM; ++ if (prop == xatom[XembedInfo]) ++ req = xatom[XembedInfo]; + +- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, ++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; ++ if (da == xatom[XembedInfo] && dl == 2) ++ atom = ((Atom *)p)[1]; + XFree(p); + } + return atom; +@@ -899,6 +1007,16 @@ getstate(Window w) + return result; + } + ++unsigned int ++getsystraywidth() ++{ ++ unsigned int w = 0; ++ Client *i; ++ if(showsystray) ++ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; ++ return w ? w + systrayspacing : 1; ++} ++ + int + gettextprop(Window w, Atom atom, char *text, unsigned int size) + { +@@ -1003,7 +1121,7 @@ killclient(const Arg *arg) + { + if (!selmon->sel) + return; +- if (!sendevent(selmon->sel, wmatom[WMDelete])) { ++ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); +@@ -1091,6 +1209,12 @@ maprequest(XEvent *e) + { + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; ++ Client *i; ++ if ((i = wintosystrayicon(ev->window))) { ++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); ++ resizebarwin(selmon); ++ updatesystray(); ++ } + + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; +@@ -1215,6 +1339,16 @@ propertynotify(XEvent *e) + Window trans; + XPropertyEvent *ev = &e->xproperty; + ++ if ((c = wintosystrayicon(ev->window))) { ++ if (ev->atom == XA_WM_NORMAL_HINTS) { ++ updatesizehints(c); ++ updatesystrayicongeom(c, c->w, c->h); ++ } ++ else ++ updatesystrayiconstate(c, ev); ++ resizebarwin(selmon); ++ updatesystray(); ++ } + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) +@@ -1265,6 +1399,20 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++void ++removesystrayicon(Client *i) ++{ ++ Client **ii; ++ ++ if (!showsystray || !i) ++ return; ++ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); ++ if (ii) ++ *ii = i->next; ++ free(i); ++} ++ ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1272,6 +1420,14 @@ resize(Client *c, int x, int y, int w, int h, int interact) + resizeclient(c, x, y, w, h); + } + ++void ++resizebarwin(Monitor *m) { ++ unsigned int w = m->ww; ++ if (showsystray && m == systraytomon(m)) ++ w -= getsystraywidth(); ++ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); ++} ++ + void + resizeclient(Client *c, int x, int y, int w, int h) + { +@@ -1344,6 +1500,19 @@ resizemouse(const Arg *arg) + } + } + ++void ++resizerequest(XEvent *e) ++{ ++ XResizeRequestEvent *ev = &e->xresizerequest; ++ Client *i; ++ ++ if ((i = wintosystrayicon(ev->window))) { ++ updatesystrayicongeom(i, ev->width, ev->height); ++ resizebarwin(selmon); ++ updatesystray(); ++ } ++} ++ + void + restack(Monitor *m) + { +@@ -1433,26 +1602,36 @@ setclientstate(Client *c, long state) + } + + int +-sendevent(Client *c, Atom proto) ++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) + { + int n; +- Atom *protocols; ++ Atom *protocols, mt; + int exists = 0; + XEvent ev; + +- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { +- while (!exists && n--) +- exists = protocols[n] == proto; +- XFree(protocols); ++ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { ++ mt = wmatom[WMProtocols]; ++ if (XGetWMProtocols(dpy, w, &protocols, &n)) { ++ while (!exists && n--) ++ exists = protocols[n] == proto; ++ XFree(protocols); ++ } ++ } ++ else { ++ exists = True; ++ mt = proto; + } + if (exists) { + ev.type = ClientMessage; +- ev.xclient.window = c->win; +- ev.xclient.message_type = wmatom[WMProtocols]; ++ ev.xclient.window = w; ++ ev.xclient.message_type = mt; + ev.xclient.format = 32; +- ev.xclient.data.l[0] = proto; +- ev.xclient.data.l[1] = CurrentTime; +- XSendEvent(dpy, c->win, False, NoEventMask, &ev); ++ ev.xclient.data.l[0] = d0; ++ ev.xclient.data.l[1] = d1; ++ ev.xclient.data.l[2] = d2; ++ ev.xclient.data.l[3] = d3; ++ ev.xclient.data.l[4] = d4; ++ XSendEvent(dpy, w, False, mask, &ev); + } + return exists; + } +@@ -1466,7 +1645,7 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } +- sendevent(c, wmatom[WMTakeFocus]); ++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + } + + void +@@ -1555,6 +1734,10 @@ setup(void) + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); ++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); ++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); ++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); ++ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); +@@ -1562,6 +1745,9 @@ setup(void) + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); ++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False); ++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); ++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); +@@ -1570,6 +1756,8 @@ setup(void) + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); ++ /* init system tray */ ++ updatesystray(); + /* init bars */ + updatebars(); + updatestatus(); +@@ -1701,7 +1889,18 @@ togglebar(const Arg *arg) + { + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ resizebarwin(selmon); ++ if (showsystray) { ++ XWindowChanges wc; ++ if (!selmon->showbar) ++ wc.y = -bh; ++ else if (selmon->showbar) { ++ wc.y = 0; ++ if (!selmon->topbar) ++ wc.y = selmon->mh - bh; ++ } ++ XConfigureWindow(dpy, systray->win, CWY, &wc); ++ } + arrange(selmon); + } + +@@ -1796,11 +1995,18 @@ unmapnotify(XEvent *e) + else + unmanage(c, 0); + } ++ else if ((c = wintosystrayicon(ev->window))) { ++ /* KLUDGE! sometimes icons occasionally unmap their windows, but do ++ * _not_ destroy them. We map those windows back */ ++ XMapRaised(dpy, c->win); ++ updatesystray(); ++ } + } + + void + updatebars(void) + { ++ unsigned int w; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, +@@ -1811,10 +2017,15 @@ updatebars(void) + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), ++ w = m->ww; ++ if (showsystray && m == systraytomon(m)) ++ w -= getsystraywidth(); ++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); ++ if (showsystray && m == systraytomon(m)) ++ XMapRaised(dpy, systray->win); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +@@ -1990,6 +2201,121 @@ updatestatus(void) + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); ++ updatesystray(); ++} ++ ++void ++updatesystrayicongeom(Client *i, int w, int h) ++{ ++ if (i) { ++ i->h = bh; ++ if (w == h) ++ i->w = bh; ++ else if (h == bh) ++ i->w = w; ++ else ++ i->w = (int) ((float)bh * ((float)w / (float)h)); ++ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); ++ /* force icons into the systray dimensions if they don't want to */ ++ if (i->h > bh) { ++ if (i->w == i->h) ++ i->w = bh; ++ else ++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); ++ i->h = bh; ++ } ++ } ++} ++ ++void ++updatesystrayiconstate(Client *i, XPropertyEvent *ev) ++{ ++ long flags; ++ int code = 0; ++ ++ if (!showsystray || !i || ev->atom != xatom[XembedInfo] || ++ !(flags = getatomprop(i, xatom[XembedInfo]))) ++ return; ++ ++ if (flags & XEMBED_MAPPED && !i->tags) { ++ i->tags = 1; ++ code = XEMBED_WINDOW_ACTIVATE; ++ XMapRaised(dpy, i->win); ++ setclientstate(i, NormalState); ++ } ++ else if (!(flags & XEMBED_MAPPED) && i->tags) { ++ i->tags = 0; ++ code = XEMBED_WINDOW_DEACTIVATE; ++ XUnmapWindow(dpy, i->win); ++ setclientstate(i, WithdrawnState); ++ } ++ else ++ return; ++ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, ++ systray->win, XEMBED_EMBEDDED_VERSION); ++} ++ ++void ++updatesystray(void) ++{ ++ XSetWindowAttributes wa; ++ XWindowChanges wc; ++ Client *i; ++ Monitor *m = systraytomon(NULL); ++ unsigned int x = m->mx + m->mw; ++ unsigned int w = 1; ++ ++ if (!showsystray) ++ return; ++ if (!systray) { ++ /* init systray */ ++ if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); ++ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); ++ wa.event_mask = ButtonPressMask | ExposureMask; ++ wa.override_redirect = True; ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XSelectInput(dpy, systray->win, SubstructureNotifyMask); ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, ++ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); ++ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); ++ XMapRaised(dpy, systray->win); ++ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); ++ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { ++ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); ++ XSync(dpy, False); ++ } ++ else { ++ fprintf(stderr, "dwm: unable to obtain system tray.\n"); ++ free(systray); ++ systray = NULL; ++ return; ++ } ++ } ++ for (w = 0, i = systray->icons; i; i = i->next) { ++ /* make sure the background color stays the same */ ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); ++ XMapRaised(dpy, i->win); ++ w += systrayspacing; ++ i->x = w; ++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); ++ w += i->w; ++ if (i->mon != m) ++ i->mon = m; ++ } ++ w = w ? w + systrayspacing : 1; ++ x -= w; ++ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); ++ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; ++ wc.stack_mode = Above; wc.sibling = m->barwin; ++ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); ++ XMapWindow(dpy, systray->win); ++ XMapSubwindows(dpy, systray->win); ++ /* redraw background */ ++ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); ++ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); ++ XSync(dpy, False); + } + + void +@@ -2057,6 +2383,16 @@ wintoclient(Window w) + return NULL; + } + ++Client * ++wintosystrayicon(Window w) { ++ Client *i = NULL; ++ ++ if (!showsystray || !w) ++ return i; ++ for (i = systray->icons; i && i->win != w; i = i->next) ; ++ return i; ++} ++ + Monitor * + wintomon(Window w) + { +@@ -2110,6 +2446,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + return -1; + } + ++Monitor * ++systraytomon(Monitor *m) { ++ Monitor *t; ++ int i, n; ++ if(!systraypinning) { ++ if(!m) ++ return selmon; ++ return m == selmon ? m : NULL; ++ } ++ for(n = 1, t = mons; t && t->next; n++, t = t->next) ; ++ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; ++ if(systraypinningfailfirst && n < systraypinning) ++ return mons; ++ return t; ++} ++ + void + zoom(const Arg *arg) + { +-- +2.17.1 + diff --git a/patches/dwm-uselessgap-6.2.diff b/patches/dwm-uselessgap-6.2.diff new file mode 100644 index 0000000..d9cacbe --- /dev/null +++ b/patches/dwm-uselessgap-6.2.diff @@ -0,0 +1,81 @@ +From 58a5ece9406ca6c90dc362617c065e4aac19417f Mon Sep 17 00:00:00 2001 +From: Cyril Cressent +Date: Wed, 3 Jul 2019 21:33:45 -0700 +Subject: [PATCH] Port the uselessgap patch to 6.2 + +--- + config.def.h | 1 + + dwm.c | 36 ++++++++++++++++++++++++++++++------ + 2 files changed, 31 insertions(+), 6 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..b11471d 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -2,6 +2,7 @@ + + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ ++static const unsigned int gappx = 6; /* gaps between windows */ + static const unsigned int snap = 32; /* snap pixel */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ +diff --git a/dwm.c b/dwm.c +index 4465af1..4545e05 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -52,8 +52,8 @@ + #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) + #define LENGTH(X) (sizeof X / sizeof X[0]) + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) +-#define WIDTH(X) ((X)->w + 2 * (X)->bw) +-#define HEIGHT(X) ((X)->h + 2 * (X)->bw) ++#define WIDTH(X) ((X)->w + 2 * (X)->bw + gappx) ++#define HEIGHT(X) ((X)->h + 2 * (X)->bw + gappx) + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +@@ -1276,12 +1276,36 @@ void + resizeclient(Client *c, int x, int y, int w, int h) + { + XWindowChanges wc; ++ unsigned int n; ++ unsigned int gapoffset; ++ unsigned int gapincr; ++ Client *nbc; + +- c->oldx = c->x; c->x = wc.x = x; +- c->oldy = c->y; c->y = wc.y = y; +- c->oldw = c->w; c->w = wc.width = w; +- c->oldh = c->h; c->h = wc.height = h; + wc.border_width = c->bw; ++ ++ /* Get number of clients for the selected monitor */ ++ for (n = 0, nbc = nexttiled(selmon->clients); nbc; nbc = nexttiled(nbc->next), n++); ++ ++ /* Do nothing if layout is floating */ ++ if (c->isfloating || selmon->lt[selmon->sellt]->arrange == NULL) { ++ gapincr = gapoffset = 0; ++ } else { ++ /* Remove border and gap if layout is monocle or only one client */ ++ if (selmon->lt[selmon->sellt]->arrange == monocle || n == 1) { ++ gapoffset = 0; ++ gapincr = -2 * borderpx; ++ wc.border_width = 0; ++ } else { ++ gapoffset = gappx; ++ gapincr = 2 * gappx; ++ } ++ } ++ ++ c->oldx = c->x; c->x = wc.x = x + gapoffset; ++ c->oldy = c->y; c->y = wc.y = y + gapoffset; ++ c->oldw = c->w; c->w = wc.width = w - gapincr; ++ c->oldh = c->h; c->h = wc.height = h - gapincr; ++ + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +-- +2.22.0 + diff --git a/patches/dwm-zoomswap-6.2.diff b/patches/dwm-zoomswap-6.2.diff new file mode 100644 index 0000000..3c658e6 --- /dev/null +++ b/patches/dwm-zoomswap-6.2.diff @@ -0,0 +1,95 @@ +From 3867ef5a68e15a4faff377ddbc8371853de4a800 Mon Sep 17 00:00:00 2001 +From: aleks +Date: Sat, 19 Oct 2019 00:56:21 +0200 +Subject: [PATCH] Put master to exact position of zoomed client + +The default behaviour when zooming a client is to put the previous +master on top of the client-stack. This patch puts the master to the +exact position of the zoomed client in the stack. +--- + dwm.c | 44 ++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 40 insertions(+), 4 deletions(-) + +diff --git a/dwm.c b/dwm.c +index 4465af1..1719b36 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -165,6 +165,7 @@ static void drawbar(Monitor *m); + static void drawbars(void); + static void enternotify(XEvent *e); + static void expose(XEvent *e); ++static Client *findbefore(Client *c); + static void focus(Client *c); + static void focusin(XEvent *e); + static void focusmon(const Arg *arg); +@@ -235,6 +236,7 @@ static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + + /* variables */ ++static Client *prevzoom = NULL; + static const char broken[] = "broken"; + static char stext[256]; + static int screen; +@@ -780,6 +782,16 @@ expose(XEvent *e) + drawbar(m); + } + ++Client * ++findbefore(Client *c) ++{ ++ Client *tmp; ++ if (c == selmon->clients) ++ return NULL; ++ for (tmp = selmon->clients; tmp && tmp->next != c; tmp = tmp->next); ++ return tmp; ++} ++ + void + focus(Client *c) + { +@@ -2114,14 +2126,38 @@ void + zoom(const Arg *arg) + { + Client *c = selmon->sel; ++ Client *at = NULL, *cold, *cprevious = NULL; + + if (!selmon->lt[selmon->sellt]->arrange + || (selmon->sel && selmon->sel->isfloating)) + return; +- if (c == nexttiled(selmon->clients)) +- if (!c || !(c = nexttiled(c->next))) +- return; +- pop(c); ++ if (c == nexttiled(selmon->clients)) { ++ at = findbefore(prevzoom); ++ if (at) ++ cprevious = nexttiled(at->next); ++ if (!cprevious || cprevious != prevzoom) { ++ prevzoom = NULL; ++ if (!c || !(c = nexttiled(c->next))) ++ return; ++ } else ++ c = cprevious; ++ } ++ cold = nexttiled(selmon->clients); ++ if (c != cold && !at) ++ at = findbefore(c); ++ detach(c); ++ attach(c); ++ /* swap windows instead of pushing the previous one down */ ++ if (c != cold && at) { ++ prevzoom = cold; ++ if (cold && at != cold) { ++ detach(cold); ++ cold->next = at->next; ++ at->next = cold; ++ } ++ } ++ focus(c); ++ arrange(c->mon); + } + + int +-- +2.23.0 + diff --git a/volsv b/volsv new file mode 100755 index 0000000..f79662b --- /dev/null +++ b/volsv @@ -0,0 +1,52 @@ +#!/bin/sh +# calculate average of two integers (for ALSA) +average () { + echo "$(( $(( $1 + $2 )) / 2 ))$" +} +# print error message +printerror () { + echo "$1 is not a recognized command or flag" +} +# if pulseaudio +pulsesv () { + case "$1" in + "up" | "-i") pamixer -i 5 ;; + "down" | "-d") pamixer -d 5 ;; + "toggle" | "-t") pamixer -t ;; + "mic" | "-m") pamixer --source 1 -t ;; + "getv" | "-v") printf "%s%%\n" "$(pamixer --get-volume)" ;; + "getm" | "-g") pamixer --get-mute | sed 's/[Ff]alse/\[on\]/;s/[Tt]rue/\[off\]/' ;; + *) printerror "$1" ;; + esac +} +# if alsa +alsasv () { + case "$1" in + "up" | "-i") amixer sset Master 5%+ ;; + "down" | "-d") amixer sset Master 5%- ;; + "toggle" | "-t") amixer sset Master toggle ;; + "mic" | "-m") amixer sset Capture toggle ;; + "getv" | "-v") amixer sget Master | grep '\[[0-9]*\%\]' | cut -d' ' -f6 | sed 's/\[//;s/\]//g' ;; + "getm" | "-g") amixer sget Master | grep '\[o[fn]' | cut -d' ' -f8 | head -1 ;; + *) printerror "$1" ;; + esac +} +#Search input for +echo "$@" | grep -q ' *-h *' && echo \ +"Volsv is Free software. You can use it for any purpose, but I make no guerantee about its usability or fitness "\ +"for any particular purpose. You are also free to redistribute, modify, and distribute your modifications to "\ +"volsv. Volsv is distributed under the BSD 3-Clause license to ensure full license compatibility with GNU, "\ +"Linux, and BSD operating systems. A copy of this license is included in the repository.\n\n"\ +"Volsv is a script designed to control the volume of a *NIX machine. The commands/flags are as follows: +increase volume: 'up' or '-i' +decrease volume: 'down' or '-d' +toggle volume mute: 'toggle' or '-t' +toggle mic mute: 'mic' or '-m' +get volume level: 'getv' or '-v' +get mute state: 'getm' or '-g'" && exit + +for i in $@; do + pgrep -x pulseaudio >/dev/null && pulsesv $1 || alsasv $1 + pgrep -x dwmblocks >/dev/null && pkill -RTMIN+10 dwmblocks + pgrep -x dwmbar >/dev/null && dwmbar-signal volume +done