summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gear/dwm.spec4
-rw-r--r--dwm-6.4_userconfig.patch196
-rw-r--r--dwm-6.4_userpatches.patch2519
3 files changed, 2716 insertions, 3 deletions
diff --git a/.gear/dwm.spec b/.gear/dwm.spec
index d2285e1..90b4faa 100644
--- a/.gear/dwm.spec
+++ b/.gear/dwm.spec
@@ -13,8 +13,8 @@ Patch1: %{name}-%{version}_userconfig.patch
BuildRequires: make
BuildRequires: gcc
-BuildRequires: libXft-devel libXinerama-devel
-Requires: libX11 libXft libXinerama libfontconfig1
+BuildRequires: libXft-devel libXinerama-devel imlib2-devel
+Requires: libX11 libXft libXinerama libfontconfig1 imlib2
%description
dwm is a dynamic window manager for X. It manages windows in tiled, monocle and
diff --git a/dwm-6.4_userconfig.patch b/dwm-6.4_userconfig.patch
index 8b13789..84ff91f 100644
--- a/dwm-6.4_userconfig.patch
+++ b/dwm-6.4_userconfig.patch
@@ -1 +1,195 @@
-
+diff --git a/config.h b/config.h
+new file mode 100644
+index 0000000..d93cacd
+--- /dev/null
++++ b/config.h
+@@ -0,0 +1,189 @@
++/* See LICENSE file for copyright and license details. */
++
++/* appearance */
++static const unsigned int borderpx = 4; /* 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 systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */
++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 */
++#define ICONSIZE (bh - 2) /* icon size */
++#define ICONSPACING (bh - 4) /* space between icon and title */
++static const char *fonts[] = { "monospace:size=9" };
++static const char dmenufont[] = "monospace: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[] = "#005577";
++static const char *colors[][3] = {
++ /* fg bg border */
++ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
++ [SchemeSel] = { col_gray4, col_cyan, col_cyan },
++};
++
++static const char *const autostart[] = {
++ "telegram", NULL,
++ "nm-applet", NULL,
++ "dwmblocks", NULL,
++ "setxkbmap", "-layout", "us,ru", "-option", "grp:caps_toggle", NULL,
++ NULL /* terminate */
++};
++
++typedef struct {
++ const char *name;
++ const void *cmd;
++} Sp;
++const char *telegram_scratchpad[] = { "telegram", NULL };
++const char *keepass_scratchpad[] = {"keepassxc", NULL };
++static Sp scratchpads[] = {
++ /* name cmd */
++ {"telegram", telegram_scratchpad},
++ {"keepassxc", keepass_scratchpad},
++};
++
++/* tagging */
++static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
++static const Rule rules[] = {
++ /* xprop(1):
++ * WM_CLASS(STRING) = instance, class
++ * WM_NAME(STRING) = title
++ */
++ /* class instance title tags mask isfloating monitor */
++ { NULL, "telegram", NULL, SPTAG(0), 1, -1 },
++ { NULL, "keepassxc", NULL, SPTAG(1), 1, -1 },
++};
++
++/* layout(s) */
++static const float mfact = 0.5; /* factor of master area size [0.05..0.95] */
++static const int nmaster = 1; /* number of clients in master area */
++static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */
++static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
++
++static const Layout layouts[] = {
++ /* symbol arrange function */
++ { "[D]", deck },
++ { "[]=", tile }, /* first entry is default */
++ { "[M]", monocle },
++ { "[][]=", tilewide },
++ { "><>", NULL }, /* no layout function means floating behavior */
++};
++
++/* key definitions */
++#define MODKEY Mod4Mask
++#define TAGKEYS(KEY,TAG) \
++ {1, {{MODKEY, KEY}}, comboview, {.ui = 1 << TAG} }, \
++ {1, {{MODKEY|ControlMask, KEY}}, toggleview, {.ui = 1 << TAG} }, \
++ {1, {{MODKEY|ShiftMask, KEY}}, combotag, {.ui = 1 << TAG} }, \
++ {1, {{MODKEY|ControlMask|ShiftMask, KEY}}, swaptags, {.ui = 1 << TAG} },
++ /* {1, {{MODKEY|ControlMask|ShiftMask, KEY}}, toggletag, {.ui = 1 << TAG} }, \ */
++
++/* helper for spawning shell commands in the pre dwm-5.0 fashion */
++#define SHCMD(cmd) { "/bin/sh", "-c", cmd, NULL }
++
++/* commands */
++static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
++static const char *termcmd[] = { "alacritty", NULL };
++static const char *browsercmd[] = { "firefox", NULL };
++static const char *brightness_dec[] = { "xbacklight", "-dec", "5", NULL };
++static const char *brightness_inc[] = { "xbacklight", "-inc", "5", NULL };
++static const char *sound_mute[] = SHCMD("pulsemixer --toggle-mute; pkill -RTMIN+9 dwmblocks");
++static const char *sound_dec[] = SHCMD("pulsemixer --change-volume -5; pkill -RTMIN+9 dwmblocks");
++static const char *sound_inc[] = SHCMD("pulsemixer --change-volume +5; pkill -RTMIN+9 dwmblocks");
++
++#include <X11/XF86keysym.h>
++#include "movestack.c"
++
++// Keychord = {n, nchords, function, argument}
++static Keychord keychords[] = {
++ { 1, { {MODKEY, XK_slash} }, togglebar, {0} },
++ { 1, { {MODKEY, XK_j} }, focusstack, {.i = +1 } },
++ { 1, { {MODKEY, XK_k} }, focusstack, {.i = -1 } },
++ { 1, { {MODKEY, XK_i} }, incnmaster, {.i = +1 } },
++ { 1, { {MODKEY, XK_d} }, incnmaster, {.i = -1 } },
++ { 1, { {MODKEY, XK_h} }, setmfact, {.f = -0.05} },
++ { 1, { {MODKEY, XK_l} }, setmfact, {.f = +0.05} },
++ { 1, { {MODKEY|ShiftMask, XK_j} }, movestack, {.i = +1 } },
++ { 1, { {MODKEY|ShiftMask, XK_k} }, movestack, {.i = -1 } },
++ { 1, { {MODKEY|ShiftMask, XK_Return} }, zoom, {0} },
++ { 1, { {MODKEY, XK_Tab} }, view, {0} },
++ { 1, { {MODKEY, XK_q} }, killclient, {0} },
++ { 1, { {MODKEY|ShiftMask, XK_space} }, togglefloating, {0} },
++ { 1, { {MODKEY, XK_0} }, view, {.ui = ~0 } },
++ { 1, { {MODKEY|ShiftMask, XK_0} }, tag, {.ui = ~0 } },
++ { 1, { {MODKEY, XK_comma} }, focusmon, {.i = -1 } },
++ { 1, { {MODKEY, XK_period} }, focusmon, {.i = +1 } },
++ { 1, { {MODKEY|ShiftMask, XK_comma} }, tagmon, {.i = -1 } },
++ { 1, { {MODKEY|ShiftMask, XK_period} }, tagmon, {.i = +1 } },
++ { 1, { {MODKEY|ShiftMask, XK_e} }, quit, {0} },
++ TAGKEYS( XK_1, 0 )
++ TAGKEYS( XK_2, 1 )
++ TAGKEYS( XK_3, 2 )
++ TAGKEYS( XK_4, 3 )
++ TAGKEYS( XK_5, 4 )
++ TAGKEYS( XK_6, 5 )
++ TAGKEYS( XK_7, 6 )
++ TAGKEYS( XK_8, 7 )
++ TAGKEYS( XK_9, 8 )
++
++ // Layouts
++ { 2, { {MODKEY, XK_space}, {0, XK_d} }, setlayout, {.v = &layouts[0]} }, // deck
++ { 2, { {MODKEY, XK_space}, {0, XK_t} }, setlayout, {.v = &layouts[1]} }, // tile
++ { 2, { {MODKEY, XK_space}, {0, XK_m} }, setlayout, {.v = &layouts[2]} }, // monocle
++ { 2, { {MODKEY, XK_space}, {0, XK_w} }, setlayout, {.v = &layouts[3]} }, // wide
++ { 2, { {MODKEY, XK_space}, {0, XK_space} }, setlayout, {.v = &layouts[4]} }, // floating
++
++ // Program runners
++ { 1, { {MODKEY, XK_p} }, spawn, {.v = dmenucmd } },
++ { 1, { {MODKEY, XK_Return} }, spawn, {.v = termcmd } },
++ { 2, { {MODKEY, XK_r}, {0, XK_b} }, spawn, {.v = browsercmd } },
++
++ // Fn Keys
++ { 1, { {0, XF86XK_MonBrightnessDown} }, spawn, { .v = brightness_dec } },
++ { 1, { {0, XF86XK_MonBrightnessUp} }, spawn, { .v = brightness_inc } },
++ { 1, { {0, XF86XK_AudioMute} }, spawn, { .v = sound_mute } },
++ { 1, { {0, XF86XK_AudioLowerVolume} }, spawn, { .v = sound_dec } },
++ { 1, { {0, XF86XK_AudioRaiseVolume} }, spawn, { .v = sound_inc } },
++ // { 0, XF86XK_TouchpadOff, spawn, SHCMD("notify-send 'Touchpad turned off'") },
++ // { 0, XF86XK_TouchpadOn, spawn, SHCMD("notify-send 'Touchpad turned on'") },
++
++ // Screenshots
++ { 2, { {MODKEY, XK_Print}, {0, XK_a} }, spawn, { .v = SHCMD("screenshot area to clip") } },
++ { 2, { {MODKEY, XK_Print}, {0|ShiftMask, XK_a} }, spawn, { .v = SHCMD("screenshot area to file") } },
++ { 2, { {MODKEY, XK_Print}, {0, XK_space} }, spawn, { .v = SHCMD("screenshot window to clip") } },
++ { 2, { {MODKEY, XK_Print}, {0|ShiftMask, XK_space} }, spawn, { .v = SHCMD("screenshot window to file") } },
++ { 2, { {MODKEY, XK_Print}, {0, XK_Return} }, spawn, { .v = SHCMD("screenshot screen to clip") } },
++ { 2, { {MODKEY, XK_Print}, {0|ShiftMask, XK_Return} }, spawn, { .v = SHCMD("screenshot screen to file") } },
++
++ // actualfullscreen
++ { 1, { {MODKEY|ShiftMask, XK_f} }, togglefullscr, {0} },
++
++ // adjacent tags
++ {1, { {MODKEY, XK_Left} }, viewtoleft, {0} },
++ {1, { {MODKEY, XK_Right} }, viewtoright, {0} },
++ {1, { {MODKEY|ShiftMask, XK_Left} }, tagtoleft, {0} },
++ {1, { {MODKEY|ShiftMask, XK_Right} }, tagtoright, {0} },
++
++ // scratchpads
++ {2, { {MODKEY, XK_s}, {0, XK_t} }, togglescratch, {.ui = 0 } },
++ {2, { {MODKEY, XK_s}, {0, XK_x} }, togglescratch, {.ui = 1 } },
++
++ // actualfullscreen
++ {1, { {MODKEY|ShiftMask, XK_f} }, togglefullscr, {0} },
++};
++
++/* button definitions */
++/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
++static const Button buttons[] = {
++ /* click event mask button function argument */
++ { ClkTagBar, MODKEY, Button1, tag, {0} },
++ { ClkTagBar, MODKEY, Button3, toggletag, {0} },
++ { ClkClientWin, MODKEY, Button1, movemouse, {0} },
++ { ClkClientWin, MODKEY|ShiftMask, Button1, resizemouse, {0} },
++ { ClkTagBar, 0, Button1, view, {0} },
++ { ClkTagBar, 0, Button3, toggleview, {0} },
++};
++
diff --git a/dwm-6.4_userpatches.patch b/dwm-6.4_userpatches.patch
index e69de29..91eded5 100644
--- a/dwm-6.4_userpatches.patch
+++ b/dwm-6.4_userpatches.patch
@@ -0,0 +1,2519 @@
+diff --git a/config.def.h b/config.def.h
+index 061ad66..59b18a0 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -3,8 +3,15 @@
+ /* 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 systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */
++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 */
++#define ICONSIZE 16 /* icon size */
++#define ICONSPACING 5 /* space between icon and title */
+ static const char *fonts[] = { "monospace:size=10" };
+ static const char dmenufont[] = "monospace:size=10";
+ static const char col_gray1[] = "#222222";
+@@ -18,17 +25,38 @@ static const char *colors[][3] = {
+ [SchemeSel] = { col_gray4, col_cyan, col_cyan },
+ };
+
++static const char *const autostart[] = {
++ "st", NULL,
++ NULL /* terminate */
++};
++
++typedef struct {
++ const char *name;
++ const void *cmd;
++} Sp;
++const char *spcmd1[] = {"st", "-n", "spterm", "-g", "120x34", NULL };
++const char *spcmd2[] = {"st", "-n", "spfm", "-g", "144x41", "-e", "ranger", NULL };
++const char *spcmd3[] = {"keepassxc", NULL };
++static Sp scratchpads[] = {
++ /* name cmd */
++ {"spterm", spcmd1},
++ {"spranger", spcmd2},
++ {"keepassxc", spcmd3},
++};
++
+ /* tagging */
+ static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+-
+ static const Rule rules[] = {
+ /* xprop(1):
+ * WM_CLASS(STRING) = instance, class
+ * WM_NAME(STRING) = title
+ */
+ /* class instance title tags mask isfloating monitor */
+- { "Gimp", NULL, NULL, 0, 1, -1 },
+- { "Firefox", NULL, NULL, 1 << 8, 0, -1 },
++ { "Gimp", NULL, NULL, 0, 1, -1 },
++ { "Firefox", NULL, NULL, 1 << 8, 0, -1 },
++ { NULL, "spterm", NULL, SPTAG(0), 1, -1 },
++ { NULL, "spfm", NULL, SPTAG(1), 1, -1 },
++ { NULL, "keepassxc", NULL, SPTAG(2), 0, -1 },
+ };
+
+ /* layout(s) */
+@@ -42,15 +70,18 @@ static const Layout layouts[] = {
+ { "[]=", tile }, /* first entry is default */
+ { "><>", NULL }, /* no layout function means floating behavior */
+ { "[M]", monocle },
++ { "[D]", deck },
++ { "[][]=", tilewide },
+ };
+
+ /* 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} },
++#define TAGKEYS(KEY,TAG) \
++ {1, {{MODKEY, KEY}}, view, {.ui = 1 << TAG} }, \
++ {1, {{MODKEY|ControlMask, KEY}}, toggleview, {.ui = 1 << TAG} }, \
++ {1, {{MODKEY|ShiftMask, KEY}}, tag, {.ui = 1 << TAG} }, \
++ {1, {{MODKEY|ControlMask|ShiftMask, KEY}}, toggletag, {.ui = 1 << TAG} }, \
++ {1, {{Mod4Mask|ShiftMask, KEY}}, swaptags, {.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 } }
+@@ -59,54 +90,77 @@ static const Layout layouts[] = {
+ static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
+ static const char *termcmd[] = { "st", NULL };
+
+-static const Key keys[] = {
+- /* modifier key function argument */
+- { MODKEY, XK_p, spawn, {.v = dmenucmd } },
+- { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
+- { MODKEY, XK_b, togglebar, {0} },
+- { MODKEY, XK_j, focusstack, {.i = +1 } },
+- { MODKEY, XK_k, focusstack, {.i = -1 } },
+- { MODKEY, XK_i, incnmaster, {.i = +1 } },
+- { MODKEY, XK_d, incnmaster, {.i = -1 } },
+- { MODKEY, XK_h, setmfact, {.f = -0.05} },
+- { MODKEY, XK_l, setmfact, {.f = +0.05} },
+- { MODKEY, XK_Return, zoom, {0} },
+- { MODKEY, XK_Tab, view, {0} },
+- { MODKEY|ShiftMask, XK_c, killclient, {0} },
+- { MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
+- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
+- { MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
+- { MODKEY, XK_space, setlayout, {0} },
+- { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
+- { MODKEY, XK_0, view, {.ui = ~0 } },
+- { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
+- { MODKEY, XK_comma, focusmon, {.i = -1 } },
+- { MODKEY, XK_period, focusmon, {.i = +1 } },
+- { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
+- { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
+- TAGKEYS( XK_1, 0)
+- TAGKEYS( XK_2, 1)
+- TAGKEYS( XK_3, 2)
+- TAGKEYS( XK_4, 3)
+- TAGKEYS( XK_5, 4)
+- TAGKEYS( XK_6, 5)
+- TAGKEYS( XK_7, 6)
+- TAGKEYS( XK_8, 7)
+- TAGKEYS( XK_9, 8)
+- { MODKEY|ShiftMask, XK_q, quit, {0} },
++#include "movestack.c"
++
++static Keychord keychords[] = {
++ // n nchords function argument
++ { 1, { {MODKEY, XK_p} }, spawn, {.v = dmenucmd } },
++ { 1, { {MODKEY|ShiftMask, XK_Return} }, spawn, {.v = termcmd } },
++ { 1, { {MODKEY, XK_slash} }, togglebar, {0} },
++ { 1, { {MODKEY, XK_j} }, focusstack, {.i = +1 } },
++ { 1, { {MODKEY, XK_k} }, focusstack, {.i = -1 } },
++ { 1, { {MODKEY, XK_i} }, incnmaster, {.i = +1 } },
++ { 1, { {MODKEY, XK_d} }, incnmaster, {.i = -1 } },
++ { 1, { {MODKEY, XK_h} }, setmfact, {.f = -0.05} },
++ { 1, { {MODKEY, XK_l} }, setmfact, {.f = +0.05} },
++ { 1, { {MODKEY|ShiftMask, XK_j} }, movestack, {.i = +1 } },
++ { 1, { {MODKEY|ShiftMask, XK_k} }, movestack, {.i = -1 } },
++ { 1, { {MODKEY, XK_Return} }, zoom, {0} },
++ { 1, { {MODKEY, XK_Tab} }, view, {0} },
++ { 1, { {MODKEY|ShiftMask, XK_c} }, killclient, {0} },
++ { 1, { {MODKEY, XK_t} }, setlayout, {.v = &layouts[0]} },
++ { 1, { {MODKEY, XK_f} }, setlayout, {.v = &layouts[1]} },
++ { 1, { {MODKEY, XK_m} }, setlayout, {.v = &layouts[2]} },
++ { 1, { {MODKEY, XK_r} }, setlayout, {.v = &layouts[3]} },
++ { 1, { {MODKEY, XK_w} }, setlayout, {.v = &layouts[4]} },
++ { 1, { {MODKEY, XK_space} }, setlayout, {0} },
++ { 1, { {MODKEY|ShiftMask, XK_space} }, togglefloating, {0} },
++ { 1, { {MODKEY, XK_0} }, view, {.ui = ~0 } },
++ { 1, { {MODKEY|ShiftMask, XK_0} }, tag, {.ui = ~0 } },
++ { 1, { {MODKEY, XK_comma} }, focusmon, {.i = -1 } },
++ { 1, { {MODKEY, XK_period} }, focusmon, {.i = +1 } },
++ { 1, { {MODKEY|ShiftMask, XK_comma} }, tagmon, {.i = -1 } },
++ { 1, { {MODKEY|ShiftMask, XK_period} }, tagmon, {.i = +1 } },
++ { 1, { {MODKEY|ShiftMask, XK_q} }, quit, {0} },
++ TAGKEYS( XK_1, 0 )
++ TAGKEYS( XK_2, 1 )
++ TAGKEYS( XK_3, 2 )
++ TAGKEYS( XK_4, 3 )
++ TAGKEYS( XK_5, 4 )
++ TAGKEYS( XK_6, 5 )
++ TAGKEYS( XK_7, 6 )
++ TAGKEYS( XK_8, 7 )
++ TAGKEYS( XK_9, 8 )
++
++ // actualfullscreen
++ { 1, { {MODKEY|ShiftMask, XK_f} }, togglefullscr, {0} },
++
++ // adjacent tags
++ {1, { {MODKEY, XK_Left} }, viewtoleft, {0} },
++ {1, { {MODKEY, XK_Right} }, viewtoright, {0} },
++ {1, { {MODKEY|ShiftMask, XK_Left} }, tagtoleft, {0} },
++ {1, { {MODKEY|ShiftMask, XK_Right} }, tagtoright, {0} },
++
++ // scratchpads
++ {1, { {MODKEY, XK_y} }, togglescratch, {.ui = 0 } },
++ {1, { {MODKEY, XK_u} }, togglescratch, {.ui = 1 } },
++ {1, { {MODKEY, XK_x} }, togglescratch, {.ui = 2 } },
++
++ // actualfullscreen
++ {1, { {MODKEY|ShiftMask, XK_f} }, togglefullscr, {0} },
+ };
+
+ /* button definitions */
+ /* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
+ static const Button buttons[] = {
+ /* click event mask button function argument */
+- { ClkLtSymbol, 0, Button1, setlayout, {0} },
+- { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
++ { ClkTagBar, MODKEY, Button1, tag, {0} },
++ { ClkTagBar, MODKEY, Button3, toggletag, {0} },
+ { ClkWinTitle, 0, Button2, zoom, {0} },
+ { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
+ { ClkClientWin, MODKEY, Button1, movemouse, {0} },
+ { ClkClientWin, MODKEY, Button2, togglefloating, {0} },
+- { ClkClientWin, MODKEY, Button3, resizemouse, {0} },
++ { ClkClientWin, MODKEY, Button1, resizemouse, {0} },
+ { ClkTagBar, 0, Button1, view, {0} },
+ { ClkTagBar, 0, Button3, toggleview, {0} },
+ { ClkTagBar, MODKEY, Button1, tag, {0} },
+diff --git a/config.mk b/config.mk
+index ef8acf7..69edc9f 100644
+--- a/config.mk
++++ b/config.mk
+@@ -23,7 +23,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} -lXrender -lImlib2
+
+ # flags
+ CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
+diff --git a/drw.c b/drw.c
+index a58a2b4..ff26025 100644
+--- a/drw.c
++++ b/drw.c
+@@ -4,6 +4,7 @@
+ #include <string.h>
+ #include <X11/Xlib.h>
+ #include <X11/Xft/Xft.h>
++#include <Imlib2.h>
+
+ #include "drw.h"
+ #include "util.h"
+@@ -71,6 +72,7 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
+ drw->w = w;
+ drw->h = h;
+ drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
++ drw->picture = XRenderCreatePicture(dpy, drw->drawable, XRenderFindVisualFormat(dpy, DefaultVisual(dpy, screen)), 0, NULL);
+ drw->gc = XCreateGC(dpy, root, 0, NULL);
+ XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
+
+@@ -85,14 +87,18 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
+
+ drw->w = w;
+ drw->h = h;
++ if (drw->picture)
++ XRenderFreePicture(drw->dpy, drw->picture);
+ if (drw->drawable)
+ XFreePixmap(drw->dpy, drw->drawable);
+ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
++ drw->picture = XRenderCreatePicture(drw->dpy, drw->drawable, XRenderFindVisualFormat(drw->dpy, DefaultVisual(drw->dpy, drw->screen)), 0, NULL);
+ }
+
+ void
+ drw_free(Drw *drw)
+ {
++ XRenderFreePicture(drw->dpy, drw->picture);
+ XFreePixmap(drw->dpy, drw->drawable);
+ XFreeGC(drw->dpy, drw->gc);
+ drw_fontset_free(drw->fonts);
+@@ -190,6 +196,8 @@ drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
+ DefaultColormap(drw->dpy, drw->screen),
+ clrname, dest))
+ die("error, cannot allocate color '%s'", clrname);
++
++ dest->pixel |= 0xff << 24;
+ }
+
+ /* Wrapper to create color schemes. The caller has to call free(3) on the
+@@ -223,6 +231,67 @@ drw_setscheme(Drw *drw, Clr *scm)
+ drw->scheme = scm;
+ }
+
++Picture
++drw_picture_create_resized(Drw *drw, char *src, unsigned int srcw, unsigned int srch, unsigned int dstw, unsigned int dsth) {
++ Pixmap pm;
++ Picture pic;
++ GC gc;
++
++ if (srcw <= (dstw << 1u) && srch <= (dsth << 1u)) {
++ XImage img = {
++ srcw, srch, 0, ZPixmap, src,
++ ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32,
++ 32, 0, 32,
++ 0, 0, 0
++ };
++ XInitImage(&img);
++
++ pm = XCreatePixmap(drw->dpy, drw->root, srcw, srch, 32);
++ gc = XCreateGC(drw->dpy, pm, 0, NULL);
++ XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, srcw, srch);
++ XFreeGC(drw->dpy, gc);
++
++ pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL);
++ XFreePixmap(drw->dpy, pm);
++
++ XRenderSetPictureFilter(drw->dpy, pic, FilterBilinear, NULL, 0);
++ XTransform xf;
++ xf.matrix[0][0] = (srcw << 16u) / dstw; xf.matrix[0][1] = 0; xf.matrix[0][2] = 0;
++ xf.matrix[1][0] = 0; xf.matrix[1][1] = (srch << 16u) / dsth; xf.matrix[1][2] = 0;
++ xf.matrix[2][0] = 0; xf.matrix[2][1] = 0; xf.matrix[2][2] = 65536;
++ XRenderSetPictureTransform(drw->dpy, pic, &xf);
++ } else {
++ Imlib_Image origin = imlib_create_image_using_data(srcw, srch, (DATA32 *)src);
++ if (!origin) return None;
++ imlib_context_set_image(origin);
++ imlib_image_set_has_alpha(1);
++ Imlib_Image scaled = imlib_create_cropped_scaled_image(0, 0, srcw, srch, dstw, dsth);
++ imlib_free_image_and_decache();
++ if (!scaled) return None;
++ imlib_context_set_image(scaled);
++ imlib_image_set_has_alpha(1);
++
++ XImage img = {
++ dstw, dsth, 0, ZPixmap, (char *)imlib_image_get_data_for_reading_only(),
++ ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32,
++ 32, 0, 32,
++ 0, 0, 0
++ };
++ XInitImage(&img);
++
++ pm = XCreatePixmap(drw->dpy, drw->root, dstw, dsth, 32);
++ gc = XCreateGC(drw->dpy, pm, 0, NULL);
++ XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, dstw, dsth);
++ imlib_free_image_and_decache();
++ XFreeGC(drw->dpy, gc);
++
++ pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL);
++ XFreePixmap(drw->dpy, pm);
++ }
++
++ return pic;
++}
++
+ void
+ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
+ {
+@@ -384,6 +453,14 @@ no_match:
+ return x + (render ? w : 0);
+ }
+
++void
++drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic)
++{
++ if (!drw)
++ return;
++ XRenderComposite(drw->dpy, PictOpOver, pic, None, drw->picture, 0, 0, 0, 0, x, y, w, h);
++}
++
+ void
+ drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
+ {
+diff --git a/drw.h b/drw.h
+index 6471431..e42eead 100644
+--- a/drw.h
++++ b/drw.h
+@@ -21,6 +21,7 @@ typedef struct {
+ int screen;
+ Window root;
+ Drawable drawable;
++ Picture picture;
+ GC gc;
+ Clr *scheme;
+ Fnt *fonts;
+@@ -50,9 +51,12 @@ void drw_cur_free(Drw *drw, Cur *cursor);
+ void drw_setfontset(Drw *drw, Fnt *set);
+ void drw_setscheme(Drw *drw, Clr *scm);
+
++Picture drw_picture_create_resized(Drw *drw, char *src, unsigned int src_w, unsigned int src_h, unsigned int dst_w, unsigned int dst_h);
++
+ /* Drawing functions */
+ void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
+ int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
++void drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic);
+
+ /* Map functions */
+ void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
+diff --git a/dwm.1 b/dwm.1
+index ddc8321..bc2c486 100644
+--- a/dwm.1
++++ b/dwm.1
+@@ -77,6 +77,18 @@ Send focused window to previous screen, if any.
+ .B Mod1\-Shift\-.
+ Send focused window to next screen, if any.
+ .TP
++.B Mod1\-Right
++Focus tag on the right, if any.
++.TP
++.B Mod1\-Left
++Focus tag on the left, if any.
++.TP
++.B Mod1\-Shift\-Right
++Send focused window to tag on the right, if any.
++.TP
++.B Mod1\-Shift\-Left
++Send focused window to tag on the left, if any.
++.TP
+ .B Mod1\-b
+ Toggles bar on and off.
+ .TP
+@@ -116,6 +128,9 @@ Zooms/cycles focused window to/from master area (tiled layouts only).
+ .B Mod1\-Shift\-c
+ Close focused window.
+ .TP
++.B Mod1\-Shift\-f
++Toggle fullscreen for focused window.
++.TP
+ .B Mod1\-Shift\-space
+ Toggle focused window between tiled and floating state.
+ .TP
+diff --git a/dwm.c b/dwm.c
+index e5efb6a..0eeb6f4 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -28,6 +28,8 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
++#include <limits.h>
++#include <stdint.h>
+ #include <sys/types.h>
+ #include <sys/wait.h>
+ #include <X11/cursorfont.h>
+@@ -49,20 +51,38 @@
+ #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 ISVISIBLE(C, M) ((C->tags & M->tagset[M->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 NUMTAGS (LENGTH(tags) + LENGTH(scratchpads))
++#define TAGMASK ((1 << NUMTAGS) - 1)
++#define SPTAG(i) ((1 << LENGTH(tags)) << (i))
++#define SPTAGMASK (((1 << LENGTH(scratchpads))-1) << LENGTH(tags))
+ #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,
++enum { NetSupported, NetWMName, NetWMIcon, 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 */
+@@ -82,6 +102,7 @@ typedef struct {
+ const Arg arg;
+ } Button;
+
++typedef struct Clientlist Clientlist;
+ typedef struct Monitor Monitor;
+ typedef struct Client Client;
+ struct Client {
+@@ -93,6 +114,7 @@ struct Client {
+ int bw, oldbw;
+ unsigned int tags;
+ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
++ unsigned int icw, ich; Picture icon;
+ Client *next;
+ Client *snext;
+ Monitor *mon;
+@@ -102,15 +124,21 @@ struct Client {
+ typedef struct {
+ unsigned int mod;
+ KeySym keysym;
++} Key;
++
++typedef struct {
++ unsigned int n;
++ const Key keys[5];
+ void (*func)(const Arg *);
+ const Arg arg;
+-} Key;
++} Keychord;
+
+ typedef struct {
+ const char *symbol;
+ void (*arrange)(Monitor *);
+ } Layout;
+
++typedef struct Pertag Pertag;
+ struct Monitor {
+ char ltsymbol[16];
+ float mfact;
+@@ -124,12 +152,12 @@ struct Monitor {
+ unsigned int tagset[2];
+ int showbar;
+ int topbar;
+- Client *clients;
++ Clientlist *cl;
+ Client *sel;
+- Client *stack;
+ Monitor *next;
+ Window barwin;
+ const Layout *lt[2];
++ Pertag *pertag;
+ };
+
+ typedef struct {
+@@ -141,12 +169,24 @@ typedef struct {
+ int monitor;
+ } Rule;
+
++struct Clientlist {
++ Client *clients;
++ Client *stack;
++};
++
++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 attachclients(Monitor *m);
+ static void attachstack(Client *c);
+ static void buttonpress(XEvent *e);
+ static void checkotherwm(void);
+@@ -157,6 +197,7 @@ static void configure(Client *c);
+ static void configurenotify(XEvent *e);
+ static void configurerequest(XEvent *e);
+ static Monitor *createmon(void);
++static void deck(Monitor *m);
+ static void destroynotify(XEvent *e);
+ static void detach(Client *c);
+ static void detachstack(Client *c);
+@@ -170,8 +211,10 @@ 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 Picture geticonprop(Window w, unsigned int *icw, unsigned int *ich);
+ 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,18 +227,21 @@ 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 Client *nexttiled(Client *c, Monitor *m);
+ static void pop(Client *c);
+ static void propertynotify(XEvent *e);
+ static void quit(const Arg *arg);
+ static Monitor *recttomon(int x, int y, int w, int h);
++static void 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);
+@@ -207,13 +253,20 @@ 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 tagtoleft(const Arg *arg);
++static void tagtoright(const Arg *arg);
+ static void tile(Monitor *m);
++static void tilewide(Monitor *m);
+ static void togglebar(const Arg *arg);
+ static void togglefloating(const Arg *arg);
++static void togglescratch(const Arg *arg);
++static void togglefullscr(const Arg *arg);
+ static void toggletag(const Arg *arg);
+ static void toggleview(const Arg *arg);
++static void freeicon(Client *c);
+ static void unfocus(Client *c, int setfocus);
+ static void unmanage(Client *c, int destroyed);
+ static void unmapnotify(XEvent *e);
+@@ -224,18 +277,34 @@ 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 updateicon(Client *c);
+ static void updatewindowtype(Client *c);
+ static void updatewmhints(Client *c);
+ static void view(const Arg *arg);
++static void warp(const Client *c);
++static void viewtoleft(const Arg *arg);
++static void viewtoright(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 void swaptags(const Arg *arg);
++static void autostart_exec(void);
++
++static void keyrelease(XEvent *e);
++static void combotag(const Arg *arg);
++static void comboview(const Arg *arg);
++
+
+ /* variables */
++static Systray *systray = NULL;
+ static const char broken[] = "broken";
+ static char stext[256];
+ static int screen;
+@@ -246,6 +315,7 @@ static int (*xerrorxlib)(Display *, XErrorEvent *);
+ static unsigned int numlockmask = 0;
+ static void (*handler[LASTEvent]) (XEvent *) = {
+ [ButtonPress] = buttonpress,
++ [ButtonRelease] = keyrelease,
+ [ClientMessage] = clientmessage,
+ [ConfigureRequest] = configurerequest,
+ [ConfigureNotify] = configurenotify,
+@@ -253,14 +323,16 @@ static void (*handler[LASTEvent]) (XEvent *) = {
+ [EnterNotify] = enternotify,
+ [Expose] = expose,
+ [FocusIn] = focusin,
++ [KeyRelease] = keyrelease,
+ [KeyPress] = keypress,
+ [MappingNotify] = mappingnotify,
+ [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;
+@@ -268,14 +340,89 @@ static Display *dpy;
+ static Drw *drw;
+ static Monitor *mons, *selmon;
+ static Window root, wmcheckwin;
++unsigned int currentkey = 0;
++static Clientlist *cl;
+
+ /* configuration, allows nested code to access above variables */
+ #include "config.h"
+
++struct Pertag {
++ unsigned int curtag, prevtag; /* current and previous tag */
++ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
++ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
++ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
++ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */
++ int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */
++};
++
+ /* compile-time check if all tags fit into an unsigned int bit array. */
+ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
+
++/* dwm will keep pid's of processes from autostart array and kill them at quit */
++static pid_t *autostart_pids;
++static size_t autostart_len;
++
++/* execute command from autostart array */
++static void
++autostart_exec() {
++ const char *const *p;
++ size_t i = 0;
++
++ /* count entries */
++ for (p = autostart; *p; autostart_len++, p++)
++ while (*++p);
++
++ autostart_pids = malloc(autostart_len * sizeof(pid_t));
++ for (p = autostart; *p; i++, p++) {
++ if ((autostart_pids[i] = fork()) == 0) {
++ setsid();
++ execvp(*p, (char *const *)p);
++ fprintf(stderr, "dwm: execvp %s\n", *p);
++ perror(" failed");
++ _exit(EXIT_FAILURE);
++ }
++ /* skip arguments */
++ while (*++p);
++ }
++}
++
+ /* function implementations */
++static int combo = 0;
++
++void
++keyrelease(XEvent *e) {
++ combo = 0;
++}
++
++void
++combotag(const Arg *arg) {
++ if(selmon->sel && arg->ui & TAGMASK) {
++ if (combo) {
++ selmon->sel->tags |= arg->ui & TAGMASK;
++ } else {
++ combo = 1;
++ selmon->sel->tags = arg->ui & TAGMASK;
++ }
++ focus(NULL);
++ arrange(selmon);
++ }
++}
++
++void
++comboview(const Arg *arg) {
++ unsigned newtags = arg->ui & TAGMASK;
++ if (combo) {
++ selmon->tagset[selmon->seltags] |= newtags;
++ } else {
++ selmon->seltags ^= 1; /*toggle tagset*/
++ combo = 1;
++ if (newtags)
++ selmon->tagset[selmon->seltags] = newtags;
++ }
++ focus(NULL);
++ arrange(selmon);
++}
++
+ void
+ applyrules(Client *c)
+ {
+@@ -300,7 +447,13 @@ applyrules(Client *c)
+ {
+ c->isfloating = r->isfloating;
+ c->tags |= r->tags;
+- for (m = mons; m && m->num != r->monitor; m = m->next);
++
++ if ((r->tags & SPTAGMASK) && r->isfloating) {
++ c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2);
++ c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2);
++ }
++
++ for (m = mons; m && (m->tagset[m->seltags] & c->tags) == 0; m = m->next);
+ if (m)
+ c->mon = m;
+ }
+@@ -309,7 +462,7 @@ applyrules(Client *c)
+ 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];
++ c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : (c->mon->tagset[c->mon->seltags] & ~SPTAGMASK);
+ }
+
+ int
+@@ -384,9 +537,9 @@ void
+ arrange(Monitor *m)
+ {
+ if (m)
+- showhide(m->stack);
++ showhide(m->cl->stack);
+ else for (m = mons; m; m = m->next)
+- showhide(m->stack);
++ showhide(m->cl->stack);
+ if (m) {
+ arrangemon(m);
+ restack(m);
+@@ -405,15 +558,49 @@ arrangemon(Monitor *m)
+ void
+ attach(Client *c)
+ {
+- c->next = c->mon->clients;
+- c->mon->clients = c;
++ c->next = c->mon->cl->clients;
++ c->mon->cl->clients = c;
++}
++
++void
++attachclients(Monitor *m) {
++ /* attach clients to the specified monitor */
++ Monitor *tm;
++ Client *c;
++ unsigned int utags = 0;
++ Bool rmons = False;
++ if(!m)
++ return;
++
++ /* collect information about the tags in use */
++ for (tm = mons; tm; tm = tm->next)
++ if(tm != m)
++ utags |= tm->tagset[tm->seltags];
++
++ for (c = m->cl->clients; c; c = c->next)
++ if(ISVISIBLE(c, m)) {
++ /* if client is also visible on other tags that are displayed on
++ * other monitors, remove these tags */
++ if(c->tags & utags) {
++ c->tags = c->tags & m->tagset[m->seltags];
++ rmons = True;
++ }
++ unfocus(c, True);
++ c->mon = m;
++ }
++
++ if (rmons)
++ for (tm = mons; tm; tm = tm->next)
++ if(tm != m)
++ arrange(tm);
++
+ }
+
+ void
+ attachstack(Client *c)
+ {
+- c->snext = c->mon->stack;
+- c->mon->stack = c;
++ c->snext = c->mon->cl->stack;
++ c->mon->cl->stack = c;
+ }
+
+ void
+@@ -434,15 +621,21 @@ buttonpress(XEvent *e)
+ }
+ if (ev->window == selmon->barwin) {
+ i = x = 0;
+- do
++ unsigned int occ = 0;
++ for(c = m->cl->clients; c; c=c->next)
++ occ |= c->tags;
++ do {
++ /* Do not reserve space for vacant tags */
++ if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i))
++ continue;
+ x += TEXTW(tags[i]);
+- while (ev->x >= x && ++i < LENGTH(tags));
++ } while (ev->x >= x && ++i < LENGTH(tags));
+ if (i < LENGTH(tags)) {
+ click = ClkTagBar;
+ arg.ui = 1 << i;
+ } else if (ev->x < x + TEXTW(selmon->ltsymbol))
+ click = ClkLtSymbol;
+- else if (ev->x > selmon->ww - (int)TEXTW(stext))
++ else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth())
+ click = ClkStatusText;
+ else
+ click = ClkWinTitle;
+@@ -480,11 +673,18 @@ cleanup(void)
+ view(&a);
+ selmon->lt[selmon->sellt] = &foo;
+ for (m = mons; m; m = m->next)
+- while (m->stack)
+- unmanage(m->stack, 0);
++ while (m->cl->stack)
++ unmanage(m->cl->stack, 0);
+ 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++)
+@@ -516,9 +716,58 @@ 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,10 +817,10 @@ configurenotify(XEvent *e)
+ drw_resize(drw, sw, bh);
+ updatebars();
+ for (m = mons; m; m = m->next) {
+- for (c = m->clients; c; c = c->next)
++ for (c = m->cl->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);
+@@ -614,7 +863,7 @@ configurerequest(XEvent *e)
+ 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))
++ if (ISVISIBLE(c, m))
+ XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
+ } else
+ configure(c);
+@@ -634,10 +883,31 @@ configurerequest(XEvent *e)
+ Monitor *
+ createmon(void)
+ {
+- Monitor *m;
++ Monitor *m, *tm;
++ int i;
+
++ /* bail out if the number of monitors exceeds the number of tags */
++ for (i=1, tm=mons; tm; i++, tm=tm->next);
++ if (i > LENGTH(tags)) {
++ fprintf(stderr, "dwm: failed to add monitor, number of tags exceeded\n");
++ return NULL;
++ }
++ /* find the first tag that isn't in use */
++ for (i=0; i < LENGTH(tags); i++) {
++ for (tm=mons; tm && !(tm->tagset[tm->seltags] & (1<<i)); tm=tm->next);
++ if (!tm)
++ break;
++ }
++ /* reassign all tags to monitors since there's currently no free tag for the
++ * new monitor */
++ if (i >= LENGTH(tags))
++ for (i=0, tm=mons; tm; tm=tm->next, i++) {
++ tm->seltags ^= 1;
++ tm->tagset[tm->seltags] = (1<<i) & TAGMASK;
++ }
+ m = ecalloc(1, sizeof(Monitor));
+- m->tagset[0] = m->tagset[1] = 1;
++ m->cl = cl;
++ m->tagset[0] = m->tagset[1] = (1<<i) & TAGMASK;
+ m->mfact = mfact;
+ m->nmaster = nmaster;
+ m->showbar = showbar;
+@@ -645,6 +915,20 @@ createmon(void)
+ m->lt[0] = &layouts[0];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
++ m->pertag = ecalloc(1, sizeof(Pertag));
++ m->pertag->curtag = m->pertag->prevtag = 1;
++
++ for (i = 0; i <= LENGTH(tags); i++) {
++ m->pertag->nmasters[i] = m->nmaster;
++ m->pertag->mfacts[i] = m->mfact;
++
++ m->pertag->ltidxs[i][0] = m->lt[0];
++ m->pertag->ltidxs[i][1] = m->lt[1];
++ m->pertag->sellts[i] = m->sellt;
++
++ m->pertag->showbars[i] = m->showbar;
++ }
++
+ return m;
+ }
+
+@@ -656,6 +940,36 @@ destroynotify(XEvent *e)
+
+ if ((c = wintoclient(ev->window)))
+ unmanage(c, 1);
++ else if ((c = wintosystrayicon(ev->window))) {
++ removesystrayicon(c);
++ resizebarwin(selmon);
++ updatesystray();
++ }
++}
++
++void
++deck(Monitor *m) {
++ unsigned int i, n, h, mw, my;
++ Client *c;
++
++ for(n = 0, c = nexttiled(m->cl->clients, selmon); c; c = nexttiled(c->next, selmon), n++);
++ if(n == 0)
++ return;
++
++ if(n > m->nmaster) {
++ mw = m->nmaster ? m->ww * m->mfact : 0;
++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n - m->nmaster);
++ }
++ else
++ mw = m->ww;
++ for(i = my = 0, c = nexttiled(m->cl->clients, selmon); c; c = nexttiled(c->next, selmon), 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), False);
++ my += HEIGHT(c);
++ }
++ else
++ resize(c, m->wx + mw, m->wy, m->ww - mw - (2*c->bw), m->wh - (2*c->bw), False);
+ }
+
+ void
+@@ -663,7 +977,7 @@ detach(Client *c)
+ {
+ Client **tc;
+
+- for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
++ for (tc = &c->mon->cl->clients; *tc && *tc != c; tc = &(*tc)->next);
+ *tc = c->next;
+ }
+
+@@ -672,11 +986,11 @@ detachstack(Client *c)
+ {
+ Client **tc, *t;
+
+- for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
++ for (tc = &c->mon->cl->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);
++ for (t = c->mon->cl->stack; t && !ISVISIBLE(t, c->mon); t = t->snext);
+ c->mon->sel = t;
+ }
+ }
+@@ -699,7 +1013,7 @@ dirtomon(int dir)
+ void
+ drawbar(Monitor *m)
+ {
+- int x, w, tw = 0;
++ 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;
+@@ -708,37 +1022,41 @@ drawbar(Monitor *m)
+ if (!m->showbar)
+ return;
+
++ if(showsystray && m == systraytomon(m) && !systrayonleft)
++ 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 extra right padding */
++ drw_text(drw, m->ww - tw - stw, 0, tw, bh, lrpad / 2 - 2, stext, 0);
+ }
+
+- for (c = m->clients; c; c = c->next) {
++ resizebarwin(m);
++ for (c = m->cl->clients; c; c = c->next) {
+ occ |= c->tags;
+ if (c->isurgent)
+ urg |= c->tags;
+ }
+ x = 0;
+ for (i = 0; i < LENGTH(tags); i++) {
++ /* Do not draw vacant tags */
++ if(!(occ & 1 << i || m->tagset[m->seltags] & 1 << i))
++ continue;
+ w = TEXTW(tags[i]);
+ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
+- if (occ & 1 << i)
+- drw_rect(drw, x + boxs, boxs, boxw, boxw,
+- m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
+- urg & 1 << i);
+ x += w;
+ }
+ w = TEXTW(m->ltsymbol);
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
+
+- if ((w = m->ww - tw - x) > bh) {
++ if ((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);
++ drw_text(drw, x, 0, w, bh, lrpad / 2 + (m->sel->icon ? m->sel->icw + ICONSPACING : 0), m->sel->name, 0);
++ if (m->sel->icon) drw_pic(drw, x + lrpad / 2, (bh - m->sel->ich) / 2, m->sel->icw, m->sel->ich, m->sel->icon);
+ if (m->sel->isfloating)
+ drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
+ } else {
+@@ -746,7 +1064,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
+@@ -783,15 +1101,18 @@ 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
+ focus(Client *c)
+ {
+- if (!c || !ISVISIBLE(c))
+- for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
++ if (!c || !ISVISIBLE(c, selmon))
++ for (c = selmon->cl->stack; c && !ISVISIBLE(c, selmon); c = c->snext);
+ if (selmon->sel && selmon->sel != c)
+ unfocus(selmon->sel, 0);
+ if (c) {
+@@ -834,6 +1155,7 @@ focusmon(const Arg *arg)
+ unfocus(selmon->sel, 0);
+ selmon = m;
+ focus(NULL);
++ warp(selmon->sel);
+ }
+
+ void
+@@ -844,16 +1166,16 @@ focusstack(const Arg *arg)
+ if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen))
+ return;
+ if (arg->i > 0) {
+- for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
++ for (c = selmon->sel->next; c && !ISVISIBLE(c, selmon); c = c->next);
+ if (!c)
+- for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
++ for (c = selmon->cl->clients; c && !ISVISIBLE(c, selmon); c = c->next);
+ } else {
+- for (i = selmon->clients; i != selmon->sel; i = i->next)
+- if (ISVISIBLE(i))
++ for (i = selmon->cl->clients; i != selmon->sel; i = i->next)
++ if (ISVISIBLE(i, selmon))
+ c = i;
+ if (!c)
+ for (; i; i = i->next)
+- if (ISVISIBLE(i))
++ if (ISVISIBLE(i, selmon))
+ c = i;
+ }
+ if (c) {
+@@ -870,14 +1192,93 @@ getatomprop(Client *c, Atom prop)
+ unsigned char *p = NULL;
+ Atom da, atom = None;
+
+- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
++ /* FIXME getatomprop should return the number of items and a pointer to
++ * the stored data instead of this workaround */
++ Atom req = XA_ATOM;
++ if (prop == xatom[XembedInfo])
++ req = xatom[XembedInfo];
++
++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
+ &da, &di, &dl, &dl, &p) == Success && p) {
+ atom = *(Atom *)p;
++ if (da == xatom[XembedInfo] && dl == 2)
++ atom = ((Atom *)p)[1];
+ XFree(p);
+ }
+ return atom;
+ }
+
++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;
++}
++
++static uint32_t prealpha(uint32_t p) {
++ uint8_t a = p >> 24u;
++ uint32_t rb = (a * (p & 0xFF00FFu)) >> 8u;
++ uint32_t g = (a * (p & 0x00FF00u)) >> 8u;
++ return (rb & 0xFF00FFu) | (g & 0x00FF00u) | (a << 24u);
++}
++
++Picture
++geticonprop(Window win, unsigned int *picw, unsigned int *pich)
++{
++ int format;
++ unsigned long n, extra, *p = NULL;
++ Atom real;
++
++ if (XGetWindowProperty(dpy, win, netatom[NetWMIcon], 0L, LONG_MAX, False, AnyPropertyType,
++ &real, &format, &n, &extra, (unsigned char **)&p) != Success)
++ return None;
++ if (n == 0 || format != 32) { XFree(p); return None; }
++
++ unsigned long *bstp = NULL;
++ uint32_t w, h, sz;
++ {
++ unsigned long *i; const unsigned long *end = p + n;
++ uint32_t bstd = UINT32_MAX, d, m;
++ for (i = p; i < end - 1; i += sz) {
++ if ((w = *i++) >= 16384 || (h = *i++) >= 16384) { XFree(p); return None; }
++ if ((sz = w * h) > end - i) break;
++ if ((m = w > h ? w : h) >= ICONSIZE && (d = m - ICONSIZE) < bstd) { bstd = d; bstp = i; }
++ }
++ if (!bstp) {
++ for (i = p; i < end - 1; i += sz) {
++ if ((w = *i++) >= 16384 || (h = *i++) >= 16384) { XFree(p); return None; }
++ if ((sz = w * h) > end - i) break;
++ if ((d = ICONSIZE - (w > h ? w : h)) < bstd) { bstd = d; bstp = i; }
++ }
++ }
++ if (!bstp) { XFree(p); return None; }
++ }
++
++ if ((w = *(bstp - 2)) == 0 || (h = *(bstp - 1)) == 0) { XFree(p); return None; }
++
++ uint32_t icw, ich;
++ if (w <= h) {
++ ich = ICONSIZE; icw = w * ICONSIZE / h;
++ if (icw == 0) icw = 1;
++ }
++ else {
++ icw = ICONSIZE; ich = h * ICONSIZE / w;
++ if (ich == 0) ich = 1;
++ }
++ *picw = icw; *pich = ich;
++
++ uint32_t i, *bstp32 = (uint32_t *)bstp;
++ for (sz = w * h, i = 0; i < sz; ++i) bstp32[i] = prealpha(bstp[i]);
++
++ Picture ret = drw_picture_create_resized(drw, (char *)bstp, w, h, icw, ich);
++ XFree(p);
++
++ return ret;
++}
++
+ int
+ getrootptr(int *x, int *y)
+ {
+@@ -955,23 +1356,23 @@ grabkeys(void)
+ {
+ updatenumlockmask();
+ {
+- unsigned int i, j;
++ unsigned int i, k;
+ 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);
++ for (i = 0; i < LENGTH(keychords); i++)
++ if ((code = XKeysymToKeycode(dpy, keychords[i].keys[currentkey].keysym)))
++ for (k = 0; k < LENGTH(modifiers); k++)
++ XGrabKey(dpy, code, keychords[i].keys[currentkey].mod | modifiers[k], root,
++ True, GrabModeAsync, GrabModeAsync);
+ }
+ }
+
+ void
+ incnmaster(const Arg *arg)
+ {
+- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
+ arrange(selmon);
+ }
+
+@@ -990,17 +1391,48 @@ isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
+ void
+ keypress(XEvent *e)
+ {
+- unsigned int i;
++ XEvent event = *e;
++ Keychord *keychord;
++ unsigned int ran = 0;
+ 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));
++ Keychord *newoptions;
++ Keychord *oldoptions = (Keychord *)malloc(sizeof(keychords));
++
++ memcpy(oldoptions, keychords, sizeof(keychords));
++ size_t numoption = 0;
++ while(!ran){
++ ev = &event.xkey;
++ keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
++ newoptions = (Keychord *)malloc(0);
++ numoption = 0;
++ for (keychord = oldoptions; keychord->n != 0 && currentkey < 5; keychord = (Keychord *)((char *)keychord + sizeof(Keychord))){
++ if(keysym == keychord->keys[currentkey].keysym
++ && CLEANMASK(keychord->keys[currentkey].mod) == CLEANMASK(ev->state)
++ && keychord->func){
++ if(keychord->n == currentkey +1){
++ keychord->func(&(keychord->arg));
++ ran = 1;
++ }else{
++ numoption++;
++ newoptions = (Keychord *)realloc(newoptions, numoption * sizeof(Keychord));
++ memcpy((char *)newoptions + (numoption -1) * sizeof(Keychord),keychord, sizeof(Keychord));
++ }
++ }
++ }
++ currentkey++;
++ if(numoption == 0)
++ break;
++ grabkeys();
++ while (running && !XNextEvent(dpy, &event) && !ran)
++ if(event.type == KeyPress)
++ break;
++ free(oldoptions);
++ oldoptions = newoptions;
++ }
++ free(newoptions);
++ currentkey = 0;
++ grabkeys();
+ }
+
+ void
+@@ -1008,7 +1440,8 @@ 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);
+@@ -1035,6 +1468,7 @@ manage(Window w, XWindowAttributes *wa)
+ c->h = c->oldh = wa->height;
+ c->oldbw = wa->border_width;
+
++ updateicon(c);
+ updatetitle(c);
+ if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
+ c->mon = t->mon;
+@@ -1095,6 +1529,13 @@ 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) || wa.override_redirect)
+ return;
+ if (!wintoclient(ev->window))
+@@ -1107,12 +1548,12 @@ monocle(Monitor *m)
+ unsigned int n = 0;
+ Client *c;
+
+- for (c = m->clients; c; c = c->next)
+- if (ISVISIBLE(c))
++ for (c = m->cl->clients; c; c = c->next)
++ if (ISVISIBLE(c, m))
+ 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))
++ for (c = nexttiled(m->cl->clients, m); c; c = nexttiled(c->next, m))
+ resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0);
+ }
+
+@@ -1194,9 +1635,9 @@ movemouse(const Arg *arg)
+ }
+
+ Client *
+-nexttiled(Client *c)
++nexttiled(Client *c, Monitor *m)
+ {
+- for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);
++ for (; c && (c->isfloating || !ISVISIBLE(c, m)); c = c->next);
+ return c;
+ }
+
+@@ -1216,6 +1657,17 @@ 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)
+@@ -1241,6 +1693,11 @@ propertynotify(XEvent *e)
+ if (c == c->mon->sel)
+ drawbar(c->mon);
+ }
++ else if (ev->atom == netatom[NetWMIcon]) {
++ updateicon(c);
++ if (c == c->mon->sel)
++ drawbar(c->mon);
++ }
+ if (ev->atom == netatom[NetWMWindowType])
+ updatewindowtype(c);
+ }
+@@ -1249,6 +1706,16 @@ propertynotify(XEvent *e)
+ void
+ quit(const Arg *arg)
+ {
++ size_t i;
++
++ /* kill child processes */
++ for (i = 0; i < autostart_len; i++) {
++ if (0 < autostart_pids[i]) {
++ kill(autostart_pids[i], SIGTERM);
++ waitpid(autostart_pids[i], NULL, 0);
++ }
++ }
++
+ running = 0;
+ }
+
+@@ -1266,6 +1733,19 @@ 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,6 +1753,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) && !systrayonleft)
++ w -= getsystraywidth();
++ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
++}
++
+ void
+ resizeclient(Client *c, int x, int y, int w, int h)
+ {
+@@ -1288,13 +1776,31 @@ resizeclient(Client *c, int x, int y, int w, int h)
+ XSync(dpy, False);
+ }
+
++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
+ resizemouse(const Arg *arg)
+ {
+ int ocx, ocy, nw, nh;
++ int ocx2, ocy2, nx, ny;
+ Client *c;
+ Monitor *m;
+ XEvent ev;
++ int horizcorner, vertcorner;
++ int di;
++ unsigned int dui;
++ Window dummy;
+ Time lasttime = 0;
+
+ if (!(c = selmon->sel))
+@@ -1304,10 +1810,18 @@ resizemouse(const Arg *arg)
+ restack(selmon);
+ ocx = c->x;
+ ocy = c->y;
++ ocx2 = c->x + c->w;
++ ocy2 = c->y + c->h;
+ 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);
++ if (!XQueryPointer (dpy, c->win, &dummy, &dummy, &di, &di, &nx, &ny, &dui))
++ return;
++ horizcorner = nx < c->w / 2;
++ vertcorner = ny < c->h / 2;
++ XWarpPointer (dpy, None, c->win, 0, 0, 0, 0,
++ horizcorner ? (-c->bw) : (c->w + c->bw -1),
++ vertcorner ? (-c->bw) : (c->h + c->bw -1));
+ do {
+ XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
+ switch(ev.type) {
+@@ -1321,8 +1835,11 @@ resizemouse(const Arg *arg)
+ 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);
++ nx = horizcorner ? ev.xmotion.x : c->x;
++ ny = vertcorner ? ev.xmotion.y : c->y;
++ nw = MAX(horizcorner ? (ocx2 - nx) : (ev.xmotion.x - ocx - 2 * c->bw + 1), 1);
++ nh = MAX(vertcorner ? (ocy2 - ny) : (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)
+ {
+@@ -1331,11 +1848,13 @@ resizemouse(const Arg *arg)
+ togglefloating(NULL);
+ }
+ if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+- resize(c, c->x, c->y, nw, nh, 1);
++ resize(c, nx, ny, 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);
++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,
++ horizcorner ? (-c->bw) : (c->w + c->bw - 1),
++ vertcorner ? (-c->bw) : (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) {
+@@ -1360,12 +1879,14 @@ restack(Monitor *m)
+ 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)) {
++ for (c = m->cl->stack; c; c = c->snext)
++ if (!c->isfloating && ISVISIBLE(c, m)) {
+ XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
+ wc.sibling = c->win;
+ }
+ }
++ if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && m->lt[m->sellt]->arrange != &monocle)
++ warp(m->sel);
+ XSync(dpy, False);
+ while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
+ }
+@@ -1414,11 +1935,9 @@ 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);
+@@ -1434,26 +1953,37 @@ 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 +1997,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
+@@ -1502,9 +2032,9 @@ void
+ setlayout(const Arg *arg)
+ {
+ if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
+- selmon->sellt ^= 1;
++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
+ if (arg && arg->v)
+- selmon->lt[selmon->sellt] = (Layout *)arg->v;
++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
+ strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
+ if (selmon->sel)
+ arrange(selmon);
+@@ -1523,7 +2053,7 @@ setmfact(const Arg *arg)
+ f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
+ if (f < 0.05 || f > 0.95)
+ return;
+- selmon->mfact = f;
++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
+ arrange(selmon);
+ }
+
+@@ -1541,6 +2071,8 @@ setup(void)
+ screen = DefaultScreen(dpy);
+ sw = DisplayWidth(dpy, screen);
+ sh = DisplayHeight(dpy, screen);
++ if(!(cl = (Clientlist *)calloc(1, sizeof(Clientlist))))
++ die("fatal: could not malloc() %u bytes\n", sizeof(Clientlist));
+ root = RootWindow(dpy, screen);
+ drw = drw_create(dpy, screen, root, sw, sh);
+ if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
+@@ -1556,13 +2088,21 @@ 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[NetWMIcon] = XInternAtom(dpy, "_NET_WM_ICON", 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);
++ 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 +2111,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();
+@@ -1615,7 +2157,12 @@ showhide(Client *c)
+ {
+ if (!c)
+ return;
+- if (ISVISIBLE(c)) {
++ if (ISVISIBLE(c, c->mon)) {
++ if ((c->tags & SPTAGMASK) && c->isfloating) {
++ c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2);
++ c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2);
++ }
++
+ /* show clients top down */
+ XMoveWindow(dpy, c->win, c->x, c->y);
+ if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
+@@ -1631,9 +2178,25 @@ showhide(Client *c)
+ void
+ sigchld(int unused)
+ {
++ pid_t pid;
++
+ if (signal(SIGCHLD, sigchld) == SIG_ERR)
+ die("can't install SIGCHLD handler:");
+- while (0 < waitpid(-1, NULL, WNOHANG));
++ while (0 < (pid = waitpid(-1, NULL, WNOHANG))) {
++ pid_t *p, *lim;
++
++ if (!(p = autostart_pids))
++ continue;
++ lim = &p[autostart_len];
++
++ for (; p < lim; p++) {
++ if (*p == pid) {
++ *p = -1;
++ break;
++ }
++ }
++
++ }
+ }
+
+ void
+@@ -1651,7 +2214,23 @@ spawn(const Arg *arg)
+ void
+ tag(const Arg *arg)
+ {
++ Monitor *m;
++ unsigned int newtags;
+ if (selmon->sel && arg->ui & TAGMASK) {
++ newtags = arg->ui & TAGMASK;
++ for (m = mons; m; m = m->next)
++ /* if tag is visible on another monitor, move client to the new monitor */
++ if (m != selmon && m->tagset[m->seltags] & newtags) {
++ /* prevent moving client to all tags (MODKEY-Shift-0) when multiple monitors are connected */
++ if(newtags & selmon->tagset[selmon->seltags])
++ return;
++ selmon->sel->tags = newtags;
++ selmon->sel->mon = m;
++ arrange(m);
++ break;
++ }
++ /* workaround in case just one monitor is connected */
++
+ selmon->sel->tags = arg->ui & TAGMASK;
+ focus(NULL);
+ arrange(selmon);
+@@ -1666,13 +2245,35 @@ tagmon(const Arg *arg)
+ sendmon(selmon->sel, dirtomon(arg->i));
+ }
+
++void
++tagtoleft(const Arg *arg) {
++ if(selmon->sel != NULL
++ && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1
++ && selmon->tagset[selmon->seltags] > 1) {
++ selmon->sel->tags >>= 1;
++ focus(NULL);
++ arrange(selmon);
++ }
++}
++
++void
++tagtoright(const Arg *arg) {
++ if(selmon->sel != NULL
++ && __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1
++ && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) {
++ selmon->sel->tags <<= 1;
++ focus(NULL);
++ arrange(selmon);
++ }
++}
++
+ void
+ 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++);
++ for (n = 0, c = nexttiled(m->cl->clients, m); c; c = nexttiled(c->next, m), n++);
+ if (n == 0)
+ return;
+
+@@ -1680,7 +2281,7 @@ tile(Monitor *m)
+ 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++)
++ for (i = my = ty = 0, c = nexttiled(m->cl->clients, m); c; c = nexttiled(c->next, m), 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);
+@@ -1694,12 +2295,51 @@ tile(Monitor *m)
+ }
+ }
+
++void
++tilewide(Monitor *m)
++{
++ unsigned int i, n, w, h, mw, mx, ty;
++ Client *c;
++
++ for (n = 0, c = nexttiled(m->cl->clients, selmon); c; c = nexttiled(c->next, selmon), n++);
++ if (n == 0)
++ return;
++
++ if (n > m->nmaster)
++ mw = m->nmaster ? m->ww * m->mfact : 0;
++ else
++ mw = m->ww;
++ for (i = mx = ty = 0, c = nexttiled(m->cl->clients, selmon); c; c = nexttiled(c->next, selmon), i++)
++ if (i < m->nmaster) {
++ w = (mw - mx) / (MIN(n, m->nmaster) - i);
++ resize(c, m->wx + mx, m->wy, w - (2*c->bw), (m->wh - ty) - (2*c->bw), 0);
++ if (mx + WIDTH(c) < m->ww)
++ mx += WIDTH(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;
++ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !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);
+ }
+
+@@ -1717,15 +2357,53 @@ togglefloating(const Arg *arg)
+ arrange(selmon);
+ }
+
++void
++togglefullscr(const Arg *arg)
++{
++ if(selmon->sel)
++ setfullscreen(selmon->sel, !selmon->sel->isfullscreen);
++}
++
++void
++togglescratch(const Arg *arg)
++{
++ Client *c;
++ unsigned int found = 0;
++ unsigned int scratchtag = SPTAG(arg->ui);
++ Arg sparg = {.v = scratchpads[arg->ui].cmd};
++
++ for (c = selmon->cl->clients; c && !(found = c->tags & scratchtag); c = c->next);
++ if (found) {
++ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag;
++ if (newtagset) {
++ selmon->tagset[selmon->seltags] = newtagset;
++ focus(NULL);
++ arrange(selmon);
++ }
++ if (ISVISIBLE(c, selmon)) {
++ focus(c);
++ restack(selmon);
++ }
++ } else {
++ selmon->tagset[selmon->seltags] |= scratchtag;
++ spawn(&sparg);
++ }
++}
++
+ void
+ toggletag(const Arg *arg)
+ {
++ Monitor *m;
+ unsigned int newtags;
+
+ if (!selmon->sel)
+ return;
+ newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
+ if (newtags) {
++ /* prevent adding tags that are in use on other monitors */
++ for (m = mons; m; m = m->next)
++ if (m != selmon && newtags & m->tagset[m->seltags])
++ return;
+ selmon->sel->tags = newtags;
+ focus(NULL);
+ arrange(selmon);
+@@ -1735,12 +2413,60 @@ toggletag(const Arg *arg)
+ void
+ toggleview(const Arg *arg)
+ {
++ Monitor *m;
+ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
++ int i;
+
+ if (newtagset) {
++ /* prevent displaying the same tags on multiple monitors */
++ for(m = mons; m; m = m->next)
++ if(m != selmon && newtagset & m->tagset[m->seltags])
++ return;
+ selmon->tagset[selmon->seltags] = newtagset;
+- focus(NULL);
++ attachclients(selmon);
+ arrange(selmon);
++ focus(NULL);
++
++ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
++
++ if (newtagset) {
++ selmon->tagset[selmon->seltags] = newtagset;
++
++ if (newtagset == ~0) {
++ selmon->pertag->prevtag = selmon->pertag->curtag;
++ selmon->pertag->curtag = 0;
++ }
++
++ /* test if the user did not select the same tag */
++ if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
++ selmon->pertag->prevtag = selmon->pertag->curtag;
++ for (i = 0; !(newtagset & 1 << i); i++) ;
++ selmon->pertag->curtag = i + 1;
++ }
++
++ /* apply settings for this view */
++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
++
++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
++ togglebar(NULL);
++
++ attachclients(selmon);
++ arrange(selmon);
++ focus(NULL);
++ }
++ }
++}
++
++void
++freeicon(Client *c)
++{
++ if (c->icon) {
++ XRenderFreePicture(dpy, c->icon);
++ c->icon = None;
+ }
+ }
+
+@@ -1765,6 +2491,7 @@ unmanage(Client *c, int destroyed)
+
+ detach(c);
+ detachstack(c);
++ freeicon(c);
+ if (!destroyed) {
+ wc.border_width = c->oldbw;
+ XGrabServer(dpy); /* avoid race conditions */
+@@ -1795,11 +2522,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,
+@@ -1810,10 +2544,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);
+ }
+@@ -1840,7 +2579,7 @@ updateclientlist()
+
+ XDeleteProperty(dpy, root, netatom[NetClientList]);
+ for (m = mons; m; m = m->next)
+- for (c = m->clients; c; c = c->next)
++ for (c = m->cl->clients; c; c = c->next)
+ XChangeProperty(dpy, root, netatom[NetClientList],
+ XA_WINDOW, 32, PropModeAppend,
+ (unsigned char *) &(c->win), 1);
+@@ -1871,8 +2610,10 @@ updategeom(void)
+ /* new monitors if nn > n */
+ for (i = n; i < nn; i++) {
+ for (m = mons; m && m->next; m = m->next);
+- if (m)
++ if (m) {
+ m->next = createmon();
++ attachclients(m->next);
++ }
+ else
+ mons = createmon();
+ }
+@@ -1892,16 +2633,13 @@ updategeom(void)
+ /* removed monitors if n > nn */
+ for (i = nn; i < n; i++) {
+ for (m = mons; m && m->next; m = m->next);
+- while ((c = m->clients)) {
+- dirty = 1;
+- m->clients = c->next;
+- detachstack(c);
+- c->mon = mons;
+- attach(c);
+- attachstack(c);
+- }
+ if (m == selmon)
+ selmon = mons;
++ for (c = m->cl->clients; c; c = c->next) {
++ dirty = True;
++ if (c->mon == m)
++ c->mon = selmon;
++ }
+ cleanupmon(m);
+ }
+ free(unique);
+@@ -1990,6 +2728,125 @@ 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 sw = TEXTW(stext) - lrpad + systrayspacing;
++ unsigned int w = 1;
++
++ if (!showsystray)
++ return;
++ if (systrayonleft)
++ x -= sw + lrpad / 2;
++ 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
+@@ -2001,6 +2858,13 @@ updatetitle(Client *c)
+ strcpy(c->name, broken);
+ }
+
++void
++updateicon(Client *c)
++{
++ freeicon(c);
++ c->icon = geticonprop(c->win, &c->icw, &c->ich);
++}
++
+ void
+ updatewindowtype(Client *c)
+ {
+@@ -2035,13 +2899,102 @@ updatewmhints(Client *c)
+ void
+ view(const Arg *arg)
+ {
++ int i;
++ unsigned int tmptag;
++ Monitor *m;
++ unsigned int newtagset = selmon->tagset[selmon->seltags ^ 1];
+ if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
+ return;
+- selmon->seltags ^= 1; /* toggle sel tagset */
++ /* swap tags when trying to display a tag from another monitor */
+ if (arg->ui & TAGMASK)
++ newtagset = arg->ui & TAGMASK;
++ for (m = mons; m; m = m->next)
++ if (m != selmon && newtagset & m->tagset[m->seltags]) {
++ /* prevent displaying all tags (MODKEY-0) when multiple monitors
++ * are connected */
++ if (newtagset & selmon->tagset[selmon->seltags])
++ return;
++ m->sel = selmon->sel;
++ m->seltags ^= 1;
++ m->tagset[m->seltags] = selmon->tagset[selmon->seltags];
++ attachclients(m);
++ arrange(m);
++ break;
++ }
++ selmon->seltags ^= 1; /* toggle sel tagset */
++
++ if (arg->ui & TAGMASK) {
+ selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+- focus(NULL);
++ selmon->pertag->prevtag = selmon->pertag->curtag;
++
++ if (arg->ui == ~0)
++ selmon->pertag->curtag = 0;
++ else {
++ for (i = 0; !(arg->ui & 1 << i); i++) ;
++ selmon->pertag->curtag = i + 1;
++ }
++ } else {
++ tmptag = selmon->pertag->prevtag;
++ selmon->pertag->prevtag = selmon->pertag->curtag;
++ selmon->pertag->curtag = tmptag;
++ }
++
++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
++
++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
++ togglebar(NULL);
++
++ attachclients(selmon);
+ arrange(selmon);
++ focus(NULL);
++}
++
++void
++viewtoleft(const Arg *arg) {
++ if(__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1
++ && selmon->tagset[selmon->seltags] > 1) {
++ selmon->seltags ^= 1; /* toggle sel tagset */
++ selmon->tagset[selmon->seltags] = selmon->tagset[selmon->seltags ^ 1] >> 1;
++ focus(NULL);
++ arrange(selmon);
++ }
++}
++
++void
++viewtoright(const Arg *arg) {
++ if(__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1
++ && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) {
++ selmon->seltags ^= 1; /* toggle sel tagset */
++ selmon->tagset[selmon->seltags] = selmon->tagset[selmon->seltags ^ 1] << 1;
++ focus(NULL);
++ arrange(selmon);
++ }
++}
++
++void
++warp(const Client *c)
++{
++ int x, y;
++
++ if (!c) {
++ XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww / 2, selmon->wy + selmon->wh / 2);
++ return;
++ }
++
++ if (!getrootptr(&x, &y) ||
++ (x > c->x - c->bw &&
++ y > c->y - c->bw &&
++ x < c->x + c->w + c->bw*2 &&
++ y < c->y + c->h + c->bw*2) ||
++ (y > c->mon->by && y < c->mon->by + bh) ||
++ (c->mon->topbar && !y))
++ return;
++
++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2);
+ }
+
+ Client *
+@@ -2051,12 +3004,22 @@ wintoclient(Window w)
+ Monitor *m;
+
+ for (m = mons; m; m = m->next)
+- for (c = m->clients; c; c = c->next)
++ for (c = m->cl->clients; c; c = c->next)
+ if (c->win == w)
+ return c;
+ 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 +3073,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)
+ {
+@@ -2117,11 +3096,33 @@ zoom(const Arg *arg)
+
+ if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating)
+ return;
+- if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next)))
++ if (c == nexttiled(selmon->cl->clients, selmon) && !(c = nexttiled(c->next, selmon)))
+ return;
+ pop(c);
+ }
+
++void
++swaptags(const Arg *arg)
++{
++ unsigned int newtag = arg->ui & TAGMASK;
++ unsigned int curtag = selmon->tagset[selmon->seltags];
++
++ if (newtag == curtag || !curtag || (curtag & (curtag-1)))
++ return;
++
++ for (Client *c = selmon->cl->clients; c != NULL; c = c->next) {
++ if((c->tags & newtag) || (c->tags & curtag))
++ c->tags ^= curtag ^ newtag;
++
++ if(!c->tags) c->tags = newtag;
++ }
++
++ selmon->tagset[selmon->seltags] = newtag;
++
++ focus(NULL);
++ arrange(selmon);
++}
++
+ int
+ main(int argc, char *argv[])
+ {
+@@ -2134,6 +3135,7 @@ main(int argc, char *argv[])
+ if (!(dpy = XOpenDisplay(NULL)))
+ die("dwm: cannot open display");
+ checkotherwm();
++ autostart_exec();
+ setup();
+ #ifdef __OpenBSD__
+ if (pledge("stdio rpath proc exec", NULL) == -1)
+diff --git a/movestack.c b/movestack.c
+new file mode 100644
+index 0000000..a50bb57
+--- /dev/null
++++ b/movestack.c
+@@ -0,0 +1,48 @@
++void
++movestack(const Arg *arg) {
++ Client *c = NULL, *p = NULL, *pc = NULL, *i;
++
++ if(arg->i > 0) {
++ /* find the client after selmon->sel */
++ for(c = selmon->sel->next; c && (!ISVISIBLE(c, selmon) || c->isfloating); c = c->next);
++ if(!c)
++ for(c = selmon->cl->clients; c && (!ISVISIBLE(c, selmon) || c->isfloating); c = c->next);
++
++ }
++ else {
++ /* find the client before selmon->sel */
++ for(i = selmon->cl->clients; i != selmon->sel; i = i->next)
++ if(ISVISIBLE(i, selmon) && !i->isfloating)
++ c = i;
++ if(!c)
++ for(; i; i = i->next)
++ if(ISVISIBLE(i, selmon) && !i->isfloating)
++ c = i;
++ }
++ /* find the client before selmon->sel and c */
++ for(i = selmon->cl->clients; i && (!p || !pc); i = i->next) {
++ if(i->next == selmon->sel)
++ p = i;
++ if(i->next == c)
++ pc = i;
++ }
++
++ /* swap c and selmon->sel selmon->clients in the selmon->clients list */
++ if(c && c != selmon->sel) {
++ Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next;
++ selmon->sel->next = c->next==selmon->sel?c:c->next;
++ c->next = temp;
++
++ if(p && p != c)
++ p->next = c;
++ if(pc && pc != selmon->sel)
++ pc->next = selmon->sel;
++
++ if(selmon->sel == selmon->cl->clients)
++ selmon->cl->clients = c;
++ else if(c == selmon->cl->clients)
++ selmon->cl->clients = selmon->sel;
++
++ arrange(selmon);
++ }
++}