Adding attachx, autostart, center, cyclelayouts, fancybar, flextile, focusonnetactive, losefullscreen, pertag, resizecorners, rotatestack, savefloats, single-tagset, statuspadding, switchtag, systray, tagallmon, tagmonfixfs, tagswapmon, togglefullscreen, windowrolerule and zoomswap patches

pull/19/head
bakkeby 4 years ago
parent b89b2bec54
commit 5ce73fe2b4

@ -0,0 +1,136 @@
From 6e51c02e249e08561ecd49f1ad4eb3108a1bcb07 Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:27:21 +0200
Subject: [PATCH] Adding attachx patch (combined attach
above/below/bottom/aside/master patch)
This includes the following attach modes:
0 - master (default behaviour): new windows become the new master
1 - attachabove: new window is placed above selected client
2 - attachaside: new window is placed on top of the stack
3 - attachbelow: new window is placed below selected client
4 - attachbottom: new window is placed at the bottom of the stack
Refer to:
https://dwm.suckless.org/patches/attachabove/
https://dwm.suckless.org/patches/attachaside/
https://dwm.suckless.org/patches/attachbelow/
https://dwm.suckless.org/patches/attachbottom/
---
config.def.h | 1 +
dwm.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 56 insertions(+), 3 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1c0b587..5e81b92 100644
--- a/config.def.h
+++ b/config.def.h
@@ -5,6 +5,7 @@ static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
+static const int attachmode = 0; /* 0 master (default), 1 = above, 2 = aside, 3 = below, 4 = bottom */
static const char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
diff --git a/dwm.c b/dwm.c
index 4465af1..7f14718 100644
--- a/dwm.c
+++ b/dwm.c
@@ -49,7 +49,8 @@
#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 ISVISIBLEONTAG(C, T) ((C->tags & T))
+#define ISVISIBLE(C) ISVISIBLEONTAG(C, 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)
@@ -148,6 +149,7 @@ static void arrange(Monitor *m);
static void arrangemon(Monitor *m);
static void attach(Client *c);
static void attachstack(Client *c);
+static void attachx(Client *c);
static void buttonpress(XEvent *e);
static void checkotherwm(void);
static void cleanup(void);
@@ -399,6 +401,56 @@ arrangemon(Monitor *m)
m->lt[m->sellt]->arrange(m);
}
+void
+attachx(Client *c)
+{
+ Client *at;
+ unsigned int n;
+
+ switch (attachmode) {
+ case 1: // above
+ if (c->mon->sel == NULL || c->mon->sel == c->mon->clients || c->mon->sel->isfloating)
+ break;
+
+ for (at = c->mon->clients; at->next != c->mon->sel; at = at->next);
+ c->next = at->next;
+ at->next = c;
+ return;
+
+ case 2: // aside
+ for (at = c->mon->clients, n = 0; at; at = at->next)
+ if (!at->isfloating && ISVISIBLEONTAG(at, c->tags))
+ if (++n >= c->mon->nmaster)
+ break;
+
+ if (!at || !c->mon->nmaster)
+ break;
+
+ c->next = at->next;
+ at->next = c;
+ return;
+
+ case 3: // below
+ if (c->mon->sel == NULL || c->mon->sel->isfloating)
+ break;
+
+ c->next = c->mon->sel->next;
+ c->mon->sel->next = c;
+ return;
+
+ case 4: // bottom
+ for (at = c->mon->clients; at && at->next; at = at->next);
+ if (!at)
+ break;
+
+ at->next = c;
+ return;
+ }
+
+ /* master (default) */
+ attach(c);
+}
+
void
attach(Client *c)
{
@@ -1062,7 +1114,7 @@ manage(Window w, XWindowAttributes *wa)
c->isfloating = c->oldstate = trans != None || c->isfixed;
if (c->isfloating)
XRaiseWindow(dpy, c->win);
- attach(c);
+ attachx(c);
attachstack(c);
XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
(unsigned char *) &(c->win), 1);
@@ -1417,7 +1469,7 @@ sendmon(Client *c, Monitor *m)
detachstack(c);
c->mon = m;
c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
- attach(c);
+ attachx(c);
attachstack(c);
focus(NULL);
arrange(NULL);
--
2.17.1

@ -0,0 +1,51 @@
From 0fd06555d523bd015e683d65002e59643fd5e2ca Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:28:17 +0200
Subject: [PATCH] Adding autostart patch
Refer to https://dwm.suckless.org/patches/autostart/
---
dwm.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/dwm.c b/dwm.c
index 4465af1..e634dc1 100644
--- a/dwm.c
+++ b/dwm.c
@@ -193,6 +193,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);
@@ -1380,6 +1381,17 @@ run(void)
handler[ev.type](&ev); /* call handler */
}
+void
+runAutostart(void) {
+
+ int ret;
+
+ ret = system("cd ~/.config/dwm; ./autostart_blocking.sh");
+ ret = system("cd ~/.config/dwm; ./autostart.sh &");
+
+ if (ret); // ignore, hide compilation warnings
+}
+
void
scan(void)
{
@@ -2142,6 +2154,7 @@ main(int argc, char *argv[])
die("pledge");
#endif /* __OpenBSD__ */
scan();
+ runAutostart();
run();
cleanup();
XCloseDisplay(dpy);
--
2.17.1

@ -0,0 +1,92 @@
From 1234c41e4baca6094f6e292f84791944d493cc5d Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:29:08 +0200
Subject: [PATCH] Adding 6.2 center patch with multi-monitor fix and
auto-centering of floating popup windows
Refer to https://dwm.suckless.org/patches/center/
---
config.def.h | 6 +++---
dwm.c | 13 +++++++++++--
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1c0b587..44b46e5 100644
--- a/config.def.h
+++ b/config.def.h
@@ -26,9 +26,9 @@ 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 iscentered isfloating monitor */
+ { "Gimp", NULL, NULL, 0, 0, 1, -1 },
+ { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1 },
};
/* layout(s) */
diff --git a/dwm.c b/dwm.c
index 4465af1..5b8722d 100644
--- a/dwm.c
+++ b/dwm.c
@@ -92,7 +92,7 @@ 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, iscentered, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
Client *next;
Client *snext;
Monitor *mon;
@@ -137,6 +137,7 @@ typedef struct {
const char *instance;
const char *title;
unsigned int tags;
+ int iscentered;
int isfloating;
int monitor;
} Rule;
@@ -285,6 +286,7 @@ applyrules(Client *c)
XClassHint ch = { NULL, NULL };
/* rule matching */
+ c->iscentered = 0;
c->isfloating = 0;
c->tags = 0;
XGetClassHint(dpy, c->win, &ch);
@@ -297,6 +299,7 @@ applyrules(Client *c)
&& (!r->class || strstr(class, r->class))
&& (!r->instance || strstr(instance, r->instance)))
{
+ c->iscentered = r->iscentered;
c->isfloating = r->isfloating;
c->tags |= r->tags;
for (m = mons; m && m->num != r->monitor; m = m->next);
@@ -1056,6 +1059,10 @@ manage(Window w, XWindowAttributes *wa)
updatewindowtype(c);
updatesizehints(c);
updatewmhints(c);
+ if(c->iscentered) {
+ c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2;
+ c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2;
+ }
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
grabbuttons(c, 0);
if (!c->isfloating)
@@ -2009,8 +2016,10 @@ updatewindowtype(Client *c)
if (state == netatom[NetWMFullscreen])
setfullscreen(c, 1);
- if (wtype == netatom[NetWMWindowTypeDialog])
+ if (wtype == netatom[NetWMWindowTypeDialog]) {
+ c->iscentered = 1;
c->isfloating = 1;
+ }
}
void
--
2.17.1

@ -0,0 +1,95 @@
From f9393d5689c858e65d016d15ba071464ce0ccc45 Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:30:00 +0200
Subject: [PATCH] cyclelayout, function to cycle through available layouts.
MOD-CTRL-, and MOD-CTRL-.
cycle backwards and forwards through available layouts.
Probably only useful if you have a lot of additional layouts.
The NULL, NULL layout should always be the last layout in your list,
in order to guarantee consistent behavior.
Refer to https://dwm.suckless.org/patches/cyclelayouts/
---
config.def.h | 3 +++
dwm.1 | 6 ++++++
dwm.c | 18 ++++++++++++++++++
3 files changed, 27 insertions(+)
diff --git a/config.def.h b/config.def.h
index 1c0b587..9a71a37 100644
--- a/config.def.h
+++ b/config.def.h
@@ -41,6 +41,7 @@ static const Layout layouts[] = {
{ "[]=", tile }, /* first entry is default */
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
+ { NULL, NULL },
};
/* key definitions */
@@ -84,6 +85,8 @@ static Key keys[] = {
{ MODKEY, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
+ { MODKEY|ControlMask, XK_comma, cyclelayout, {.i = -1 } },
+ { MODKEY|ControlMask, XK_period, cyclelayout, {.i = +1 } },
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
diff --git a/dwm.1 b/dwm.1
index 13b3729..165891b 100644
--- a/dwm.1
+++ b/dwm.1
@@ -92,6 +92,12 @@ Sets monocle layout.
.B Mod1\-space
Toggles between current and previous layout.
.TP
+.B Mod1\-Control\-,
+Cycles backwards in layout list.
+.TP
+.B Mod1\-Control\-.
+Cycles forwards in layout list.
+.TP
.B Mod1\-j
Focus next window.
.TP
diff --git a/dwm.c b/dwm.c
index 4465af1..26db8e3 100644
--- a/dwm.c
+++ b/dwm.c
@@ -157,6 +157,7 @@ static void configure(Client *c);
static void configurenotify(XEvent *e);
static void configurerequest(XEvent *e);
static Monitor *createmon(void);
+static void cyclelayout(const Arg *arg);
static void destroynotify(XEvent *e);
static void detach(Client *c);
static void detachstack(Client *c);
@@ -644,6 +645,23 @@ createmon(void)
return m;
}
+void
+cyclelayout(const Arg *arg) {
+ Layout *l;
+ for(l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++);
+ if(arg->i > 0) {
+ if(l->symbol && (l + 1)->symbol)
+ setlayout(&((Arg) { .v = (l + 1) }));
+ else
+ setlayout(&((Arg) { .v = layouts }));
+ } else {
+ if(l != layouts && (l - 1)->symbol)
+ setlayout(&((Arg) { .v = (l - 1) }));
+ else
+ setlayout(&((Arg) { .v = &layouts[LENGTH(layouts) - 2] }));
+ }
+}
+
void
destroynotify(XEvent *e)
{
--
2.17.1

@ -0,0 +1,96 @@
From a651c9e15d1d9191ce8a7790d352603ae356d9d5 Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:30:57 +0200
Subject: [PATCH] fancybar, shows the titles of all visible windows in the
status bar
Inspired by the decorated tabbed layout of Xmonad this patch provides a status bar that shows
the titles of all visible windows (as opposed to showing just the selected one). When the
titles don't completely fit, they're cropped. The title of the selected window is inverted.
Authors:
Mate Nagy
Jochen Sprickerhof
Refer to https://dwm.suckless.org/patches/fancybar/
---
dwm.c | 46 ++++++++++++++++++++++++++++++++++++----------
1 file changed, 36 insertions(+), 10 deletions(-)
diff --git a/dwm.c b/dwm.c
index 4465af1..eff2da6 100644
--- a/dwm.c
+++ b/dwm.c
@@ -695,10 +695,10 @@ dirtomon(int dir)
void
drawbar(Monitor *m)
{
- int x, w, sw = 0;
+ int x, w, sw = 0, tw, mw, ew = 0;
int boxs = drw->fonts->h / 9;
int boxw = drw->fonts->h / 6 + 2;
- unsigned int i, occ = 0, urg = 0;
+ unsigned int i, occ = 0, urg = 0, n = 0;
Client *c;
/* draw status first so it can be overdrawn by tags later */
@@ -709,6 +709,8 @@ drawbar(Monitor *m)
}
for (c = m->clients; c; c = c->next) {
+ if (ISVISIBLE(c))
+ n++;
occ |= c->tags;
if (c->isurgent)
urg |= c->tags;
@@ -729,15 +731,39 @@ drawbar(Monitor *m)
x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
if ((w = m->ww - sw - 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);
+ if (n > 0) {
+ tw = TEXTW(m->sel->name) + lrpad;
+ mw = (tw >= w || n == 1) ? 0 : (w - tw) / (n - 1);
+
+ i = 0;
+ for (c = m->clients; c; c = c->next) {
+ if (!ISVISIBLE(c) || c == m->sel)
+ continue;
+ tw = TEXTW(c->name);
+ if(tw < mw)
+ ew += (mw - tw);
+ else
+ i++;
+ }
+ if (i > 0)
+ mw += ew / i;
+
+ for (c = m->clients; c; c = c->next) {
+ if (!ISVISIBLE(c))
+ continue;
+ tw = MIN(m->sel == c ? w : mw, TEXTW(c->name));
+
+ drw_setscheme(drw, scheme[m->sel == c ? SchemeSel : SchemeNorm]);
+ if (tw > 0) /* trap special handling of 0 in drw_text */
+ drw_text(drw, x, 0, tw, bh, lrpad / 2, c->name, 0);
+ if (c->isfloating)
+ drw_rect(drw, x + boxs, boxs, boxw, boxw, c->isfixed, 0);
+ x += tw;
+ w -= tw;
+ }
}
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_rect(drw, x, 0, w, bh, 1, 1);
}
drw_map(drw, m->barwin, 0, 0, m->ww, bh);
}
--
2.17.1

@ -0,0 +1,244 @@
From 8dfae1aad85aa7ac03852196d3b77121dfe60aee Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:32:08 +0200
Subject: [PATCH] Adding flextile patch
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Port of 5.8.2 patch by © 2010 joten <joten@freenet.de>, not compatible with nmaster
or pertag patch as this incorporates a bespoke version of both.
Refer to https://dwm.suckless.org/patches/flextile/
---
config.def.h | 14 +++++++
dwm.c | 112 ++++++++++++++++++++++++++++-----------------------
2 files changed, 75 insertions(+), 51 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1c0b587..0a8c20f 100644
--- a/config.def.h
+++ b/config.def.h
@@ -21,6 +21,9 @@ static const char *colors[][3] = {
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+/* include(s) depending on the tags array */
+#include "flextile.h"
+
static const Rule rules[] = {
/* xprop(1):
* WM_CLASS(STRING) = instance, class
@@ -35,6 +38,11 @@ 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 layoutaxis[] = {
+ 1, /* layout axis: 1 = x, 2 = y; negative values mirror the layout, setting the master area to the right / bottom instead of left / top */
+ 2, /* master axis: 1 = x (from left to right), 2 = y (from top to bottom), 3 = z (monocle) */
+ 2, /* stack axis: 1 = x (from left to right), 2 = y (from top to bottom), 3 = z (monocle) */
+};
static const Layout layouts[] = {
/* symbol arrange function */
@@ -94,6 +102,12 @@ static Key keys[] = {
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ MODKEY|ShiftMask, XK_q, quit, {0} },
+ { MODKEY|ControlMask, XK_t, rotatelayoutaxis, {.i = 0} }, /* 0 = layout axis */
+ { MODKEY|ControlMask, XK_Tab, rotatelayoutaxis, {.i = 1} }, /* 1 = master axis */
+ { MODKEY|ControlMask|ShiftMask, XK_Tab, rotatelayoutaxis, {.i = 2} }, /* 2 = stack axis */
+ { MODKEY|ControlMask, XK_Return, mirrorlayout, {0} },
+ { MODKEY|ControlMask, XK_h, shiftmastersplit, {.i = -1} }, /* reduce the number of tiled clients in the master area */
+ { MODKEY|ControlMask, XK_l, shiftmastersplit, {.i = +1} }, /* increase the number of tiled clients in the master area */
};
/* button definitions */
diff --git a/dwm.c b/dwm.c
index 4465af1..5a31822 100644
--- a/dwm.c
+++ b/dwm.c
@@ -111,27 +111,6 @@ typedef struct {
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;
@@ -630,6 +609,7 @@ configurerequest(XEvent *e)
Monitor *
createmon(void)
{
+ unsigned int i;
Monitor *m;
m = ecalloc(1, sizeof(Monitor));
@@ -641,6 +621,21 @@ createmon(void)
m->lt[0] = &layouts[0];
m->lt[1] = &layouts[1 % LENGTH(layouts)];
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+ m->ltaxis[0] = layoutaxis[0];
+ m->ltaxis[1] = layoutaxis[1];
+ m->ltaxis[2] = layoutaxis[2];
+ m->msplit = 1;
+ /* init tags, bars, layouts, axes, msplits and mfacts */
+ m->curtag = m->prevtag = 1;
+ for (i = 0; i < LENGTH(tags) + 1; i++) {
+ m->showbars[i] = m->showbar;
+ m->lts[i] = &layouts[0];
+ m->mfacts[i] = m->mfact;
+ m->ltaxes[i][0] = m->ltaxis[0];
+ m->ltaxes[i][1] = m->ltaxis[1];
+ m->ltaxes[i][2] = m->ltaxis[2];
+ m->msplits[i] = m->msplit;
+ }
return m;
}
@@ -1503,7 +1498,7 @@ 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;
+ selmon->lt[selmon->sellt] = selmon->lts[selmon->curtag] = (Layout *)arg->v;
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
if (selmon->sel)
arrange(selmon);
@@ -1522,7 +1517,7 @@ setmfact(const Arg *arg)
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
if (f < 0.1 || f > 0.9)
return;
- selmon->mfact = f;
+ selmon->mfact = selmon->mfacts[selmon->curtag] = f;
arrange(selmon);
}
@@ -1670,36 +1665,10 @@ tagmon(const Arg *arg)
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);
- 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);
- ty += HEIGHT(c);
- }
-}
-
void
togglebar(const Arg *arg)
{
- selmon->showbar = !selmon->showbar;
+ selmon->showbar = selmon->showbars[selmon->curtag] = !selmon->showbar;
updatebarpos(selmon);
XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
arrange(selmon);
@@ -1723,12 +1692,31 @@ void
toggletag(const Arg *arg)
{
unsigned int newtags;
+ unsigned int i;
if (!selmon->sel)
return;
newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
if (newtags) {
selmon->sel->tags = newtags;
+ if (newtags == ~0) {
+ selmon->prevtag = selmon->curtag;
+ selmon->curtag = 0;
+ }
+ if (!(newtags & 1 << (selmon->curtag - 1))) {
+ selmon->prevtag = selmon->curtag;
+ for (i=0; !(newtags & 1 << i); i++);
+ selmon->curtag = i + 1;
+ }
+ selmon->sel->tags = newtags;
+ selmon->lt[selmon->sellt] = selmon->lts[selmon->curtag];
+ selmon->mfact = selmon->mfacts[selmon->curtag];
+ if (selmon->showbar != selmon->showbars[selmon->curtag])
+ togglebar(NULL);
+ selmon->ltaxis[0] = selmon->ltaxes[selmon->curtag][0];
+ selmon->ltaxis[1] = selmon->ltaxes[selmon->curtag][1];
+ selmon->ltaxis[2] = selmon->ltaxes[selmon->curtag][2];
+ selmon->msplit = selmon->msplits[selmon->curtag];
focus(NULL);
arrange(selmon);
}
@@ -2035,11 +2023,33 @@ updatewmhints(Client *c)
void
view(const Arg *arg)
{
+ unsigned int i;
+
if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
return;
selmon->seltags ^= 1; /* toggle sel tagset */
- if (arg->ui & TAGMASK)
+ if (arg->ui & TAGMASK) {
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+ selmon->prevtag = selmon->curtag;
+ if (arg->ui == ~0)
+ selmon->curtag = 0;
+ else {
+ for (i=0; !(arg->ui & 1 << i); i++);
+ selmon->curtag = i + 1;
+ }
+ } else {
+ selmon->prevtag = selmon->curtag ^ selmon->prevtag;
+ selmon->curtag ^= selmon->prevtag;
+ selmon->prevtag = selmon->curtag ^ selmon->prevtag;
+ }
+ selmon->lt[selmon->sellt] = selmon->lts[selmon->curtag];
+ selmon->mfact = selmon->mfacts[selmon->curtag];
+ if (selmon->showbar != selmon->showbars[selmon->curtag])
+ togglebar(NULL);
+ selmon->ltaxis[0] = selmon->ltaxes[selmon->curtag][0];
+ selmon->ltaxis[1] = selmon->ltaxes[selmon->curtag][1];
+ selmon->ltaxis[2] = selmon->ltaxes[selmon->curtag][2];
+ selmon->msplit = selmon->msplits[selmon->curtag];
focus(NULL);
arrange(selmon);
}
--
2.17.1

@ -0,0 +1,59 @@
From 286ca3bb1af08b452bf8140abcc23d4ef61baaa2 Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:33:04 +0200
Subject: [PATCH] Activate a window in response to _NET_ACTIVE_WINDOW
By default, dwm response to client requests to _NET_ACTIVE_WINDOW client
messages by setting the urgency bit on the named window.
This patch activates the window instead.
Both behaviours are legitimate according to
https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472702304
One should decide which of these one should perform based on the message
senders' untestable claims that it represents the end-user. Setting the
urgency bit is the conservative decision. This patch implements the more
trusting alternative.
It also allows dwm to work with `wmctrl -a` and other external window
management utilities
Refer to:
https://dwm.suckless.org/patches/focusonnetactive/
---
dwm.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/dwm.c b/dwm.c
index 4465af1..3919d47 100644
--- a/dwm.c
+++ b/dwm.c
@@ -514,6 +514,7 @@ clientmessage(XEvent *e)
{
XClientMessageEvent *cme = &e->xclient;
Client *c = wintoclient(cme->window);
+ unsigned int i;
if (!c)
return;
@@ -523,8 +524,14 @@ clientmessage(XEvent *e)
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);
+ for (i = 0; i < LENGTH(tags) && !((1 << i) & c->tags); i++);
+ if (i < LENGTH(tags)) {
+ const Arg a = {.ui = 1 << i};
+ selmon = c->mon;
+ view(&a);
+ focus(c);
+ restack(selmon);
+ }
}
}
--
2.17.1

@ -0,0 +1,25 @@
From 889e53e38ae19fae0c25046af48b0b4f3b4e4a4c Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 11:40:30 +0200
Subject: [PATCH] Lose fullscreen on focus change
---
dwm.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/dwm.c b/dwm.c
index 4465af1..520a794 100644
--- a/dwm.c
+++ b/dwm.c
@@ -1751,6 +1751,8 @@ unfocus(Client *c, int setfocus)
{
if (!c)
return;
+ if (c->isfullscreen && ISVISIBLE(c))
+ setfullscreen(c, 0);
grabbuttons(c, 0);
XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
if (setfocus) {
--
2.17.1

@ -0,0 +1,196 @@
From 6d5d6363d9841d812d762dce70bf9e8962a92ab2 Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:34:15 +0200
Subject: [PATCH] pertag patch, keeps layout, mwfact, barpos and nmaster per
tag
Refer to https://dwm.suckless.org/patches/pertag/
---
dwm.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 86 insertions(+), 7 deletions(-)
diff --git a/dwm.c b/dwm.c
index 4465af1..4578b1c 100644
--- a/dwm.c
+++ b/dwm.c
@@ -111,6 +111,7 @@ typedef struct {
void (*arrange)(Monitor *);
} Layout;
+typedef struct Pertag Pertag;
struct Monitor {
char ltsymbol[16];
float mfact;
@@ -130,6 +131,7 @@ struct Monitor {
Monitor *next;
Window barwin;
const Layout *lt[2];
+ Pertag *pertag;
};
typedef struct {
@@ -271,6 +273,16 @@ static Window root, wmcheckwin;
/* 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 */
+ Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */
+ Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */
+};
+
/* compile-time check if all tags fit into an unsigned int bit array. */
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
@@ -631,6 +643,7 @@ Monitor *
createmon(void)
{
Monitor *m;
+ int i;
m = ecalloc(1, sizeof(Monitor));
m->tagset[0] = m->tagset[1] = 1;
@@ -641,6 +654,27 @@ createmon(void)
m->lt[0] = &layouts[0];
m->lt[1] = &layouts[1 % LENGTH(layouts)];
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+ if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag))))
+ die("fatal: could not malloc() %u bytes\n", sizeof(Pertag));
+ m->pertag->curtag = m->pertag->prevtag = 1;
+ for(i=0; i <= LENGTH(tags); i++) {
+ /* init nmaster */
+ m->pertag->nmasters[i] = m->nmaster;
+
+ /* init mfacts */
+ m->pertag->mfacts[i] = m->mfact;
+
+ /* init layouts */
+ m->pertag->ltidxs[i][0] = m->lt[0];
+ m->pertag->ltidxs[i][1] = m->lt[1];
+ m->pertag->sellts[i] = m->sellt;
+
+ /* init showbar */
+ m->pertag->showbars[i] = m->showbar;
+
+ /* swap focus and zoomswap*/
+ m->pertag->prevzooms[i] = NULL;
+ }
return m;
}
@@ -966,7 +1000,7 @@ grabkeys(void)
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);
}
@@ -1500,10 +1534,13 @@ setfullscreen(Client *c, int fullscreen)
void
setlayout(const Arg *arg)
{
- if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
- selmon->sellt ^= 1;
+ if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) {
+ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
+ }
if (arg && arg->v)
- selmon->lt[selmon->sellt] = (Layout *)arg->v;
+ selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
if (selmon->sel)
arrange(selmon);
@@ -1522,7 +1559,7 @@ setmfact(const Arg *arg)
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
if (f < 0.1 || f > 0.9)
return;
- selmon->mfact = f;
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
arrange(selmon);
}
@@ -1699,7 +1736,7 @@ tile(Monitor *m)
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);
arrange(selmon);
@@ -1738,9 +1775,29 @@ void
toggleview(const Arg *arg)
{
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
+ int i;
if (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;
+ }
selmon->tagset[selmon->seltags] = newtagset;
+
+ /* 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);
focus(NULL);
arrange(selmon);
}
@@ -2035,11 +2092,33 @@ updatewmhints(Client *c)
void
view(const Arg *arg)
{
+ int i;
+ unsigned int tmptag;
+
if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
return;
selmon->seltags ^= 1; /* toggle sel tagset */
- if (arg->ui & TAGMASK)
+ if (arg->ui & TAGMASK) {
+ selmon->pertag->prevtag = selmon->pertag->curtag;
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+ if (arg->ui == ~0)
+ selmon->pertag->curtag = 0;
+ else {
+ for (i=0; !(arg->ui & 1 << i); i++) ;
+ selmon->pertag->curtag = i + 1;
+ }
+ } else {
+ tmptag = selmon->pertag->prevtag;
+ selmon->pertag->prevtag = selmon->pertag->curtag;
+ selmon->pertag->curtag = tmptag;
+ }
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
+ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
+ togglebar(NULL);
focus(NULL);
arrange(selmon);
}
--
2.17.1

@ -0,0 +1,89 @@
From 80867c7d8c8186972f16e230e2910b20b4296895 Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:36:21 +0200
Subject: [PATCH] Resize a floating window from any corner
By default, windows only from the bottom right corner.
With this patch the mouse is warped to the nearest corner and you
resize from there.
Refer to https://dwm.suckless.org/patches/resizecorners/
Authors:
dusty - dusty@teknik.io
Klemens Nanni kl3@posteo.org (6.1 version)
---
dwm.c | 27 ++++++++++++++++++++++++---
1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/dwm.c b/dwm.c
index 4465af1..9922271 100644
--- a/dwm.c
+++ b/dwm.c
@@ -1291,9 +1291,14 @@ 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))
@@ -1303,10 +1308,19 @@ 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) {
@@ -1322,6 +1336,11 @@ resizemouse(const Arg *arg)
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)
{
@@ -1330,11 +1349,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) {
--
2.17.1

@ -0,0 +1,121 @@
From ce8b86167696bf5ffd3048dfee61f8e0f7949e67 Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:35:26 +0200
Subject: [PATCH] rotatestack, allows you to scroll through the stack
Stack rotation moves a client from the bottom to the top of the stack (or the other way round). This effectively rotates the clients by one position clockwise (or CCW, respectively).
It should play well with arbitrary stack layouts and nmaster values.
One may think of it as moving the zoom through the list of clients, very much in the same way as scrolling moves the view port around a pane.
Refer to https://dwm.suckless.org/patches/rotatestack/
---
config.def.h | 2 ++
dwm.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+)
diff --git a/config.def.h b/config.def.h
index 1c0b587..bd609fe 100644
--- a/config.def.h
+++ b/config.def.h
@@ -66,6 +66,8 @@ static Key keys[] = {
{ MODKEY, XK_b, togglebar, {0} },
{ MODKEY, XK_j, focusstack, {.i = +1 } },
{ MODKEY, XK_k, focusstack, {.i = -1 } },
+ { MODKEY|ShiftMask, XK_j, rotatestack, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_k, rotatestack, {.i = -1 } },
{ MODKEY, XK_i, incnmaster, {.i = +1 } },
{ MODKEY, XK_d, incnmaster, {.i = -1 } },
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
diff --git a/dwm.c b/dwm.c
index 4465af1..cf76ad3 100644
--- a/dwm.c
+++ b/dwm.c
@@ -163,6 +163,8 @@ static void detachstack(Client *c);
static Monitor *dirtomon(int dir);
static void drawbar(Monitor *m);
static void drawbars(void);
+static void enqueue(Client *c);
+static void enqueuestack(Client *c);
static void enternotify(XEvent *e);
static void expose(XEvent *e);
static void focus(Client *c);
@@ -192,6 +194,7 @@ 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 rotatestack(const Arg *arg);
static void run(void);
static void scan(void);
static int sendevent(Client *c, Atom proto);
@@ -751,6 +754,28 @@ drawbars(void)
drawbar(m);
}
+void
+enqueue(Client *c)
+{
+ Client *l;
+ for (l = c->mon->clients; l && l->next; l = l->next);
+ if (l) {
+ l->next = c;
+ c->next = NULL;
+ }
+}
+
+void
+enqueuestack(Client *c)
+{
+ Client *l;
+ for (l = c->mon->stack; l && l->snext; l = l->snext);
+ if (l) {
+ l->snext = c;
+ c->snext = NULL;
+ }
+}
+
void
enternotify(XEvent *e)
{
@@ -1369,6 +1394,37 @@ restack(Monitor *m)
while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
}
+void
+rotatestack(const Arg *arg)
+{
+ Client *c = NULL, *f;
+
+ if (!selmon->sel)
+ return;
+ f = selmon->sel;
+ if (arg->i > 0) {
+ for (c = nexttiled(selmon->clients); c && nexttiled(c->next); c = nexttiled(c->next));
+ if (c){
+ detach(c);
+ attach(c);
+ detachstack(c);
+ attachstack(c);
+ }
+ } else {
+ if ((c = nexttiled(selmon->clients))){
+ detach(c);
+ enqueue(c);
+ detachstack(c);
+ enqueuestack(c);
+ }
+ }
+ if (c){
+ arrange(selmon);
+ focus(f);
+ restack(selmon);
+ }
+}
+
void
run(void)
{
--
2.17.1

@ -0,0 +1,55 @@
From 947edb123d4f77357ca0d72a7ed2c121075e0d86 Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 11:36:24 +0200
Subject: [PATCH] Savefloats, saves size and position of floating windows
Refer to https://dwm.suckless.org/patches/save_floats/
---
dwm.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/dwm.c b/dwm.c
index 4465af1..96b0b26 100644
--- a/dwm.c
+++ b/dwm.c
@@ -88,6 +88,7 @@ struct Client {
char name[256];
float mina, maxa;
int x, y, w, h;
+ int sfx, sfy, sfw, sfh; /* stored float geometry, used on mode revert */
int oldx, oldy, oldw, oldh;
int basew, baseh, incw, inch, maxw, maxh, minw, minh;
int bw, oldbw;
@@ -1056,6 +1057,10 @@ manage(Window w, XWindowAttributes *wa)
updatewindowtype(c);
updatesizehints(c);
updatewmhints(c);
+ c->sfx = c->x;
+ c->sfy = c->y;
+ c->sfw = c->w;
+ c->sfh = c->h;
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
grabbuttons(c, 0);
if (!c->isfloating)
@@ -1714,8 +1719,16 @@ togglefloating(const Arg *arg)
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);
+ /* restore last known float dimensions */
+ resize(selmon->sel, selmon->sel->sfx, selmon->sel->sfy,
+ selmon->sel->sfw, selmon->sel->sfh, False);
+ else {
+ /* save last known float dimensions */
+ selmon->sel->sfx = selmon->sel->x;
+ selmon->sel->sfy = selmon->sel->y;
+ selmon->sel->sfw = selmon->sel->w;
+ selmon->sel->sfh = selmon->sel->h;
+ }
arrange(selmon);
}
--
2.17.1

@ -0,0 +1,541 @@
From acd90dee6b92a98e8e681c266e055574efebf20c Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 11:34:11 +0200
Subject: [PATCH] Adding single tagset
Author: Jan Christoph Ebersbach <jceb@e-jc.de>
URL: http://dwm.suckless.org/patches/single_tagset
This patch addresses the multi-monitor setup. Instead of having separate tags
for every monitor there is just one list of tags for all monitors. Instead of
moving windows from one monitor to the other, the desired tag from the
other monitor can just be selected and all windows will be drawn on the
current monitor.
Several deep changes needed to be made:
1. Macro ISVISIBLE expects a second parameter, the monitor
2. Monitor->clients and Monitor->stack were moved to the global variable
Clientlist cl. All monitors refer to this one list.
3. A new method attachclients was added. When changing between tags this
function ensures that all clients are pointing to the right monitor.
Please be aware that this patch probably breaks any other patch!
---
dwm.c | 214 +++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 159 insertions(+), 55 deletions(-)
diff --git a/dwm.c b/dwm.c
index 4465af1..b3e8de3 100644
--- a/dwm.c
+++ b/dwm.c
@@ -49,7 +49,7 @@
#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)
@@ -82,6 +82,7 @@ typedef struct {
const Arg arg;
} Button;
+typedef struct Clientlist Clientlist;
typedef struct Monitor Monitor;
typedef struct Client Client;
struct Client {
@@ -124,14 +125,18 @@ 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];
};
+struct Clientlist {
+ Client *clients;
+ Client *stack;
+};
+
typedef struct {
const char *class;
const char *instance;
@@ -147,6 +152,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 attachclients(Monitor *m);
static void attachstack(Client *c);
static void buttonpress(XEvent *e);
static void checkotherwm(void);
@@ -183,7 +189,7 @@ 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 *);
static void propertynotify(XEvent *e);
static void quit(const Arg *arg);
@@ -267,6 +273,7 @@ static Display *dpy;
static Drw *drw;
static Monitor *mons, *selmon;
static Window root, wmcheckwin;
+static Clientlist *cl;
/* configuration, allows nested code to access above variables */
#include "config.h"
@@ -299,7 +306,7 @@ applyrules(Client *c)
{
c->isfloating = r->isfloating;
c->tags |= r->tags;
- for (m = mons; m && m->num != r->monitor; m = m->next);
+ for (m = mons; m && (m->tagset[m->seltags] & c->tags) == 0; m = m->next) ;
if (m)
c->mon = m;
}
@@ -381,9 +388,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);
@@ -402,15 +409,48 @@ 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
@@ -477,8 +517,8 @@ 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);
@@ -564,7 +604,7 @@ 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);
@@ -610,7 +650,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);
@@ -630,10 +670,32 @@ 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;
@@ -659,7 +721,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;
}
@@ -668,11 +730,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;
}
}
@@ -708,7 +770,7 @@ drawbar(Monitor *m)
drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0);
}
- for (c = m->clients; c; c = c->next) {
+ for (c = m->cl->clients; c; c = c->next) {
occ |= c->tags;
if (c->isurgent)
urg |= c->tags;
@@ -783,8 +845,8 @@ expose(XEvent *e)
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) {
@@ -837,16 +899,16 @@ focusstack(const Arg *arg)
if (!selmon->sel)
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) {
@@ -1106,12 +1168,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);
}
@@ -1193,9 +1255,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;
}
@@ -1359,8 +1421,8 @@ 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;
}
@@ -1413,11 +1475,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);
@@ -1540,6 +1600,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)))
@@ -1615,7 +1677,7 @@ showhide(Client *c)
{
if (!c)
return;
- if (ISVISIBLE(c)) {
+ if (ISVISIBLE(c, c->mon)) {
/* show clients top down */
XMoveWindow(dpy, c->win, c->x, c->y);
if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
@@ -1655,7 +1717,22 @@ 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);
@@ -1676,7 +1753,7 @@ 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;
@@ -1684,7 +1761,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);
@@ -1722,12 +1799,17 @@ togglefloating(const Arg *arg)
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);
@@ -1737,12 +1819,17 @@ toggletag(const Arg *arg)
void
toggleview(const Arg *arg)
{
+ Monitor *m;
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
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);
- arrange(selmon);
+ attachclients(selmon);
}
}
@@ -1841,7 +1928,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 +1958,10 @@ updategeom(void)
if (n <= nn) { /* new monitors available */
for (i = 0; i < (nn - n); 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 +1981,13 @@ updategeom(void)
} 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;
+ for (c = m->cl->clients; c; c = c->next) {
+ dirty = True;
+ if (c->mon == m)
+ c->mon = selmon;
+ }
cleanupmon(m);
}
}
@@ -2035,13 +2121,31 @@ updatewmhints(Client *c)
void
view(const Arg *arg)
{
+ Monitor *m;
+ unsigned int newtagset = selmon->tagset[selmon->seltags ^ 1];
if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
return;
+ /* 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);
- arrange(selmon);
+ attachclients(selmon);
}
Client *
@@ -2051,7 +2155,7 @@ 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;
@@ -2118,8 +2222,8 @@ zoom(const Arg *arg)
if (!selmon->lt[selmon->sellt]->arrange
|| (selmon->sel && selmon->sel->isfloating))
return;
- if (c == nexttiled(selmon->clients))
- if (!c || !(c = nexttiled(c->next)))
+ if (c == nexttiled(selmon->cl->clients, selmon))
+ if (!c || !(c = nexttiled(c->next, selmon)))
return;
pop(c);
}
--
2.17.1

@ -0,0 +1,54 @@
From 9ef9ccccf9cbd6341bc601ef1501a3188e9d49f5 Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:37:13 +0200
Subject: [PATCH] Adding statuspadding patch
Refer to:
https://dwm.suckless.org/patches/statuspadding/
---
config.def.h | 2 ++
dwm.c | 8 ++++----
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1c0b587..ea4c3fd 100644
--- a/config.def.h
+++ b/config.def.h
@@ -5,6 +5,8 @@ static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
+static const int horizpadbar = 2; /* horizontal padding for statusbar */
+static const int vertpadbar = 0; /* vertical padding for statusbar */
static const char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
diff --git a/dwm.c b/dwm.c
index 4465af1..35e8275 100644
--- a/dwm.c
+++ b/dwm.c
@@ -704,8 +704,8 @@ drawbar(Monitor *m)
/* 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);
+ drw_text(drw, m->ww - sw, 0, sw, bh, lrpad / 2, stext, 0);
}
for (c = m->clients; c; c = c->next) {
@@ -1544,8 +1544,8 @@ setup(void)
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;
+ lrpad = drw->fonts->h + horizpadbar;
+ bh = drw->fonts->h + vertpadbar;
updategeom();
/* init atoms */
utf8string = XInternAtom(dpy, "UTF8_STRING", False);
--
2.17.1

@ -0,0 +1,71 @@
From 7a591391e5e1b9740ab71f69e3875c120240322e Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:01:30 +0200
Subject: [PATCH] Adding switchtag option for rules allowing auto-moving
dedicated tags for specific applications
dwm allow you to set application specific rules so that you can have your browser, for example,
start up on tag 9 optionally on a given monitor.
When you open your browser it is then automatically moved to the configured tag, but you have
to manually enable the tag to see the newly opened application.
This patch adds an extra configuration option for individual rules where:
- 0 is default behaviour
- 1 automatically moves you to the tag of the newly opened application and
- 2 enables the tag of the newly opened application in addition to your existing enabled tags
---
config.def.h | 6 +++---
dwm.c | 14 ++++++++++++++
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1c0b587..4ea7156 100644
--- a/config.def.h
+++ b/config.def.h
@@ -26,9 +26,9 @@ 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 switchtag isfloating monitor */
+ { "Gimp", NULL, NULL, 0, 1, 1, -1 },
+ { "Firefox", NULL, NULL, 1 << 8, 1, 0, -1 },
};
/* layout(s) */
diff --git a/dwm.c b/dwm.c
index 4465af1..9e727fc 100644
--- a/dwm.c
+++ b/dwm.c
@@ -137,6 +137,7 @@ typedef struct {
const char *instance;
const char *title;
unsigned int tags;
+ int switchtag;
int isfloating;
int monitor;
} Rule;
@@ -302,6 +303,19 @@ applyrules(Client *c)
for (m = mons; m && m->num != r->monitor; m = m->next);
if (m)
c->mon = m;
+
+ if (r->switchtag) {
+ unsigned int newtagset;
+ if (r->switchtag == 2)
+ newtagset = c->mon->tagset[c->mon->seltags] ^ c->tags;
+ else
+ newtagset = c->tags;
+
+ if (newtagset) {
+ c->mon->tagset[c->mon->seltags] = newtagset;
+ arrange(c->mon);
+ }
+ }
}
}
if (ch.res_class)
--
2.17.1

@ -0,0 +1,742 @@
From fbfac511101aeec9d0559c2f914a13ec63a42d9f Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 11:32:02 +0200
Subject: [PATCH] Adding systray patch
Refer to https://dwm.suckless.org/patches/systray/
---
config.def.h | 4 +
dwm.c | 404 +++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 380 insertions(+), 28 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1c0b587..dd8e07a 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, 0: 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..ba7fae3 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
+#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
+
+/* XEMBED messages */
+#define XEMBED_EMBEDDED_NOTIFY 0
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_FOCUS_IN 4
+#define XEMBED_MODALITY_ON 10
+
+#define XEMBED_MAPPED (1 << 0)
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_WINDOW_DEACTIVATE 2
+
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 0
+#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
+
/* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { SchemeNorm, SchemeSel }; /* color schemes */
-enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
- NetWMFullscreen, NetActiveWindow, NetWMWindowType,
- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
+enum { NetSupported, NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetWMCheck,
+ NetSystemTrayOrientationHorz, NetWMName, NetWMState, NetWMFullscreen, NetActiveWindow,
+ NetWMWindowType, NetWMWindowTypeDialog, NetClientList, NetLast, NetWMWindowTypeDock }; /* 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,24 @@ 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 unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
static const char broken[] = "broken";
static char stext[256];
static int screen;
@@ -257,9 +293,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 +476,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 +519,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 +554,50 @@ 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));
+ c->win = cme->data.l[2];
+ c->mon = selmon;
+ c->next = systray->icons;
+ systray->icons = c;
+ XGetWindowAttributes(dpy, c->win, &wa);
+ c->x = c->oldx = c->y = c->oldy = 0;
+ c->w = c->oldw = wa.width;
+ c->h = c->oldh = wa.height;
+ c->oldbw = wa.border_width;
+ c->bw = 0;
+ c->isfloating = True;
+ /* reuse tags field as mapped status */
+ c->tags = 1;
+ updatesizehints(c);
+ updatesystrayicongeom(c, wa.width, wa.height);
+ XAddToSaveSet(dpy, c->win);
+ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
+ XReparentWindow(dpy, c->win, systray->win, 0, 0);
+ /* use parents background color */
+ swa.background_pixel = 0; //scheme[SchemeNorm].bg->pix;
+ 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 +650,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 +735,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 +783,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);
+ drw_text(drw, m->ww - sw - stw, 0, sw, bh, 0, stext, 0);
}
+ resizebarwin(m);
for (c = m->clients; c; c = c->next) {
occ |= c->tags;
if (c->isurgent)
@@ -728,7 +820,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 +831,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 +868,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 +957,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 +1001,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 +1115,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 +1203,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 +1333,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 +1393,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)
{
@@ -1272,6 +1413,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 +1493,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 +1595,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 +1638,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,13 +1727,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[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[NetWMWindowTypeDock] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", 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 +1750,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 +1883,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 +1989,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 +2011,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 +2195,123 @@ 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 dimenons 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 *)&systrayorientation, 1);
+ XChangeProperty(dpy, systray->win, netatom[NetWMWindowType], XA_ATOM, 32,
+ PropModeReplace, (unsigned char *)&netatom[NetWMWindowTypeDock], 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 +2379,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 +2442,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

@ -0,0 +1,77 @@
From 4e43b3e8d60a16ab63cfff480e073a44f4abfeec Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:02:43 +0200
Subject: [PATCH] tagallmon, move all visible windows to an adjacent monitor
---
config.def.h | 2 ++
dwm.c | 33 +++++++++++++++++++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/config.def.h b/config.def.h
index 1c0b587..4852ae2 100644
--- a/config.def.h
+++ b/config.def.h
@@ -84,6 +84,8 @@ static Key keys[] = {
{ MODKEY, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
+ { MODKEY|Mod4Mask|ShiftMask, XK_comma, tagallmon, {.i = +1 } },
+ { MODKEY|Mod4Mask|ShiftMask, XK_period, tagallmon, {.i = -1 } },
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
diff --git a/dwm.c b/dwm.c
index 4465af1..5048970 100644
--- a/dwm.c
+++ b/dwm.c
@@ -208,6 +208,7 @@ 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 tagallmon(const Arg *arg);
static void tile(Monitor *);
static void togglebar(const Arg *arg);
static void togglefloating(const Arg *arg);
@@ -1670,6 +1671,38 @@ tagmon(const Arg *arg)
sendmon(selmon->sel, dirtomon(arg->i));
}
+void
+tagallmon(const Arg *arg)
+{
+ Monitor *m;
+ Client *c;
+ Client *next;
+
+ if (!mons->next)
+ return;
+
+ m = dirtomon(arg->i);
+ for (c = selmon->clients; c; c = next) {
+ next = c->next;
+ if (!ISVISIBLE(c))
+ continue;
+ unfocus(c, 1);
+ detach(c);
+ detachstack(c);
+ c->mon = m;
+ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
+ attach(c);
+ attachstack(c);
+ if (c->isfullscreen) {
+ setfullscreen(c, 0);
+ setfullscreen(c, 1);
+ }
+ }
+
+ focus(NULL);
+ arrange(NULL);
+}
+
void
tile(Monitor *m)
{
--
2.17.1

@ -0,0 +1,34 @@
From f4c2b92f26c2c7fd99d30613b5c2db7fb26ea850 Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:04:11 +0200
Subject: [PATCH] tagmonfixfs: Allow moving a fullscreen window to another
monitor
---
dwm.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/dwm.c b/dwm.c
index 4465af1..324214f 100644
--- a/dwm.c
+++ b/dwm.c
@@ -1665,9 +1665,14 @@ tag(const Arg *arg)
void
tagmon(const Arg *arg)
{
- if (!selmon->sel || !mons->next)
+ Client *c = selmon->sel;
+ if (!c || !mons->next)
return;
- sendmon(selmon->sel, dirtomon(arg->i));
+ sendmon(c, dirtomon(arg->i));
+ if (c->isfullscreen) {
+ setfullscreen(c, 0);
+ setfullscreen(c, 1);
+ }
}
void
--
2.17.1

@ -0,0 +1,107 @@
From 4029057010e010127850fc9fb6f6c354353d871d Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:41:35 +0200
Subject: [PATCH] tagswapmon, swap all visible windows with those of one of the
adjacent monitors
---
config.def.h | 2 ++
dwm.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 64 insertions(+)
diff --git a/config.def.h b/config.def.h
index 1c0b587..09331fb 100644
--- a/config.def.h
+++ b/config.def.h
@@ -84,6 +84,8 @@ static Key keys[] = {
{ MODKEY, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
+ { MODKEY|Mod4Mask|ControlMask, XK_comma, tagswapmon, {.i = +1 } },
+ { MODKEY|Mod4Mask|ControlMask, XK_period, tagswapmon, {.i = -1 } },
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
diff --git a/dwm.c b/dwm.c
index 4465af1..862eaf7 100644
--- a/dwm.c
+++ b/dwm.c
@@ -209,6 +209,7 @@ static void spawn(const Arg *arg);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
static void tile(Monitor *);
+static void tagswapmon(const Arg *arg);
static void togglebar(const Arg *arg);
static void togglefloating(const Arg *arg);
static void toggletag(const Arg *arg);
@@ -1670,6 +1671,67 @@ tagmon(const Arg *arg)
sendmon(selmon->sel, dirtomon(arg->i));
}
+void
+tagswapmon(const Arg *arg)
+{
+ Monitor *m;
+ Client *c, *sc = NULL, *mc = NULL, *next;
+
+ if (!mons->next)
+ return;
+
+ m = dirtomon(arg->i);
+
+ for (c = selmon->clients; c; c = next) {
+ next = c->next;
+ if (!ISVISIBLE(c))
+ continue;
+ unfocus(c, 1);
+ detach(c);
+ detachstack(c);
+ c->next = sc;
+ sc = c;
+ }
+
+ for (c = m->clients; c; c = next) {
+ next = c->next;
+ if (!ISVISIBLE(c))
+ continue;
+ unfocus(c, 1);
+ detach(c);
+ detachstack(c);
+ c->next = mc;
+ mc = c;
+ }
+
+ for (c = sc; c; c = next) {
+ next = c->next;
+ c->mon = m;
+ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
+ attach(c);
+ attachstack(c);
+ if (c->isfullscreen) {
+ setfullscreen(c, 0);
+ setfullscreen(c, 1);
+ }
+ }
+
+ for (c = mc; c; c = next) {
+ next = c->next;
+ c->mon = selmon;
+ c->tags = selmon->tagset[selmon->seltags]; /* assign tags of target monitor */
+ attach(c);
+ attachstack(c);
+ if (c->isfullscreen) {
+ setfullscreen(c, 0);
+ setfullscreen(c, 1);
+ }
+ }
+
+ focus(NULL);
+ arrange(NULL);
+}
+
void
tile(Monitor *m)
{
--
2.17.1

@ -0,0 +1,53 @@
From 0c9b3c039c523a4a433f13031639d86e64e5e70c Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:25:58 +0200
Subject: [PATCH] togglefullscreen, allows you to toggle fullscreen on and off
using the same shortcut key
---
config.def.h | 1 +
dwm.c | 9 +++++++++
2 files changed, 10 insertions(+)
diff --git a/config.def.h b/config.def.h
index 1c0b587..666d0c4 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, XK_y, togglefullscreen, {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..bcc6ce2 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 togglefullscreen(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,14 @@ togglefloating(const Arg *arg)
arrange(selmon);
}
+void
+togglefullscreen(const Arg *arg) {
+ if (!selmon->sel)
+ return;
+
+ setfullscreen(selmon->sel, !selmon->sel->isfullscreen);
+}
+
void
toggletag(const Arg *arg)
{
--
2.17.1

@ -0,0 +1,85 @@
From c6bb6a4f5c8206a5cf0d1460e52c1f0776c3b483 Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 12:43:06 +0200
Subject: [PATCH] Adding window role rule
This patch adds a new rule property based on WM_WINDOW_ROLE(STRING) so that one can
differentiate between window roles, e.g. Firefox "browser" vs "Preferences".
---
config.def.h | 7 ++++---
dwm.c | 7 ++++++-
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1c0b587..f0859dc 100644
--- a/config.def.h
+++ b/config.def.h
@@ -25,10 +25,11 @@ static const Rule rules[] = {
/* xprop(1):
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title
+ * WM_WINDOW_ROLE(STRING) = role
*/
- /* class instance title tags mask isfloating monitor */
- { "Gimp", NULL, NULL, 0, 1, -1 },
- { "Firefox", NULL, NULL, 1 << 8, 0, -1 },
+ /* class role instance title tags mask isfloating monitor */
+ { "Gimp", NULL, NULL, NULL, 0, 1, -1 },
+ { "Firefox", NULL, NULL, NULL, 1 << 8, 0, -1 },
};
/* layout(s) */
diff --git a/dwm.c b/dwm.c
index 4465af1..f2cf8ed 100644
--- a/dwm.c
+++ b/dwm.c
@@ -63,7 +63,7 @@ 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 { WMProtocols, WMDelete, WMState, WMTakeFocus, WMWindowRole, WMLast }; /* default atoms */
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
@@ -134,6 +134,7 @@ struct Monitor {
typedef struct {
const char *class;
+ const char *role;
const char *instance;
const char *title;
unsigned int tags;
@@ -279,6 +280,7 @@ void
applyrules(Client *c)
{
const char *class, *instance;
+ char role[64];
unsigned int i;
const Rule *r;
Monitor *m;
@@ -290,11 +292,13 @@ applyrules(Client *c)
XGetClassHint(dpy, c->win, &ch);
class = ch.res_class ? ch.res_class : broken;
instance = ch.res_name ? ch.res_name : broken;
+ gettextprop(c->win, wmatom[WMWindowRole], role, sizeof(role));
for (i = 0; i < LENGTH(rules); i++) {
r = &rules[i];
if ((!r->title || strstr(c->name, r->title))
&& (!r->class || strstr(class, r->class))
+ && (!r->role || strstr(role, r->role))
&& (!r->instance || strstr(instance, r->instance)))
{
c->isfloating = r->isfloating;
@@ -1553,6 +1557,7 @@ setup(void)
wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+ wmatom[WMWindowRole] = XInternAtom(dpy, "WM_WINDOW_ROLE", False);
netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
--
2.17.1

@ -0,0 +1,106 @@
From 9e67b8a4fd30635d9d30d62948d458c243d7c056 Mon Sep 17 00:00:00 2001
From: bakkeby <bakkeby@gmail.com>
Date: Tue, 7 Apr 2020 11:45:42 +0200
Subject: [PATCH] zoomswap, swap positions when a window becomes the new master
The default behaviour in dwm when using zoom (i.e. moving a window to become the new master)
is to use pop to re-attach the window on top of the chain. This has the side effect of moving
every window down as well, resulting in every window on the screen changing position. The
zoomswap patch changes this behaviour so that the current master swaps position with the other
window that is to become the new master. Applying zoom on the current master will result in it
swapping back to the previous master.
This patch includes some slight alteration of code compared to the original.
Original author:
Jan Christoph Ebersbach - <jceb at e-jc dot de>
Refer to https://dwm.suckless.org/patches/zoomswap/
---
dwm.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 41 insertions(+), 5 deletions(-)
diff --git a/dwm.c b/dwm.c
index 4465af1..49dfda2 100644
--- a/dwm.c
+++ b/dwm.c
@@ -185,6 +185,7 @@ 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 quit(const Arg *arg);
static Monitor *recttomon(int x, int y, int w, int h);
@@ -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;
@@ -1208,6 +1210,15 @@ pop(Client *c)
arrange(c->mon);
}
+Client *
+prevtiled(Client *c) {
+ Client *p;
+ if (!c || c == c->mon->clients)
+ return NULL;
+ for (p = c->mon->clients; p && p->next != c; p = p->next);
+ return p;
+}
+
void
propertynotify(XEvent *e)
{
@@ -2114,14 +2125,39 @@ 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))
+ || (selmon->sel && selmon->sel->isfloating) || !c)
return;
- if (c == nexttiled(selmon->clients))
- if (!c || !(c = nexttiled(c->next)))
- return;
- pop(c);
+
+ if (c == nexttiled(selmon->clients)) {
+ at = prevtiled(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 = prevtiled(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.17.1
Loading…
Cancel
Save