[dwm][PATCH] Multiple scratchpads

This patch enables multiple scratchpads, each with one assigned window.
This enables the same scratchpad workflow that you have in i3.

Scratchpads are implemented as special tags, whose mask does not
apply to new spawned windows. To assign a window to a scratchpad you
have to set up a rule, as you do with regular tags.

Windows tagged with scratchpad tags can be set floating or not in the
rules array. Most users would probably want them floating (i3 style),
but having them tiled does also perfectly work and might fit better the
DWM approach. In case they are set floating, the patch moves them to the
center of the screen whenever the are shown. The patch can easily be
modified to make this last feature configurable in the rules array (see
the center patch).

The togglescratch function, borrowed from the previous scratchpad patch
and slightly modified, can be used to spawn a registered scratchpad
process or toggle its view. This function looks for a window tagged on
the selected scratchpad. If it is found its view is toggled. If it is
not found the corresponding registered command is spawned. The
config.def.h shows three examples of its use to spawn a terminal in the
first scratchpad tag, a second terminal running ranger on the second
scratchpad tag and the keepassxc application to manage passwords on a
third scratchpad tag.

If you prefer to spawn your scratchpad applications from the startup
script, you might opt for binding keys to toggleview instead, as
scratchpads are just special tags (you may even extend the TAGKEYS macro
to generalize the key bindings).
This commit is contained in:
bakkeby 2020-04-16 16:39:22 +02:00
parent 525dc0d107
commit f9a001dee7
6 changed files with 84 additions and 39 deletions

View File

@ -15,6 +15,8 @@ Refer to [https://dwm.suckless.org/](https://dwm.suckless.org/) for details on t
### Changelog: ### Changelog:
2020-04-16 - Upgraded the scratchpad patch to the multiple scratchpads patch \[[ref](https://lists.suckless.org/hackers/2004/17205.html)\]
2020-04-13 - Added statuscmd patch 2020-04-13 - Added statuscmd patch
2020-03-31 - Added the rounded corners patch 2020-03-31 - Added the rounded corners patch

View File

@ -256,6 +256,18 @@ char *colors[][3] = {
}; };
#endif // VTCOLORS_PATCH / FLOAT_BORDER_COLOR_PATCH #endif // VTCOLORS_PATCH / FLOAT_BORDER_COLOR_PATCH
#if SCRATCHPAD_PATCH
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},
};
#endif // SCRATCHPAD_PATCH
/* tagging */ /* tagging */
#if EWMHTAGS_PATCH #if EWMHTAGS_PATCH
static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
@ -418,6 +430,11 @@ static const Rule rules[] = {
/* class instance title tags mask isfloating monitor */ /* class instance title tags mask isfloating monitor */
{ "Gimp", NULL, NULL, 0, 1, -1 }, { "Gimp", NULL, NULL, 0, 1, -1 },
{ "Firefox", NULL, NULL, 1 << 8, 0, -1 }, { "Firefox", NULL, NULL, 1 << 8, 0, -1 },
#if SCRATCHPAD_PATCH
{ NULL, "spterm", NULL, SPTAG(0), 1, -1 },
{ NULL, "spfm", NULL, SPTAG(1), 1, -1 },
{ NULL, "keepassxc",NULL, SPTAG(2), 0, -1 },
#endif // SCRATCHPAD_PATCH
#endif #endif
}; };
@ -679,10 +696,6 @@ static const char *statuscmds[] = { "notify-send Mouse$BUTTON" };
static char *statuscmd[] = { "/bin/sh", "-c", NULL, NULL }; static char *statuscmd[] = { "/bin/sh", "-c", NULL, NULL };
#endif // STATUSCMD_PATCH #endif // STATUSCMD_PATCH
#if SCRATCHPAD_PATCH
static const char scratchpadname[] = "scratchpad";
static const char *scratchpadcmd[] = { "st", "-t", scratchpadname, "-g", "120x34", NULL };
#endif // SCRATCHPAD_PATCH
#if SCRATCHPAD_ALT_1_PATCH #if SCRATCHPAD_ALT_1_PATCH
static const unsigned scratchpad_mask = 1u << sizeof tags / sizeof * tags; static const unsigned scratchpad_mask = 1u << sizeof tags / sizeof * tags;
#endif // SCRATCHPAD_ALT_1_PATCH #endif // SCRATCHPAD_ALT_1_PATCH
@ -691,9 +704,6 @@ static Key keys[] = {
/* modifier key function argument */ /* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } }, { MODKEY, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
#if SCRATCHPAD_PATCH
{ MODKEY, XK_grave, togglescratch, {.v = scratchpadcmd } },
#endif // SCRATCHPAD_PATCH
{ MODKEY, XK_b, togglebar, {0} }, { MODKEY, XK_b, togglebar, {0} },
#if STACKER_PATCH #if STACKER_PATCH
STACKKEYS(MODKEY, focus) STACKKEYS(MODKEY, focus)
@ -815,6 +825,11 @@ static Key keys[] = {
{ MODKEY|ControlMask|ShiftMask, XK_k, toggleverticalmax, {0} }, { MODKEY|ControlMask|ShiftMask, XK_k, toggleverticalmax, {0} },
{ MODKEY|ControlMask, XK_m, togglemax, {0} }, { MODKEY|ControlMask, XK_m, togglemax, {0} },
#endif // MAXIMIZE_PATCH #endif // MAXIMIZE_PATCH
#if SCRATCHPAD_PATCH
{ MODKEY, XK_grave, togglescratch, {.ui = 0 } },
{ MODKEY|ControlMask, XK_grave, togglescratch, {.ui = 1 } },
{ MODKEY|ShiftMask, XK_grave, togglescratch, {.ui = 2 } },
#endif // SCRATCHPAD_PATCH
#if UNFLOATVISIBLE_PATCH #if UNFLOATVISIBLE_PATCH
{ MODKEY|Mod4Mask, XK_space, unfloatvisible, {0} }, { MODKEY|Mod4Mask, XK_space, unfloatvisible, {0} },
{ MODKEY|ShiftMask, XK_t, unfloatvisible, {.v = &layouts[0]} }, { MODKEY|ShiftMask, XK_t, unfloatvisible, {.v = &layouts[0]} },

38
dwm.c
View File

@ -75,7 +75,14 @@
#define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define WIDTH(X) ((X)->w + 2 * (X)->bw) #define WIDTH(X) ((X)->w + 2 * (X)->bw)
#define HEIGHT(X) ((X)->h + 2 * (X)->bw) #define HEIGHT(X) ((X)->h + 2 * (X)->bw)
#if SCRATCHPAD_PATCH
#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))
#else
#define TAGMASK ((1 << LENGTH(tags)) - 1) #define TAGMASK ((1 << LENGTH(tags)) - 1)
#endif // SCRATCHPAD_PATCH
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
/* enums */ /* enums */
@ -528,6 +535,12 @@ applyrules(Client *c)
#endif // SWALLOW_PATCH #endif // SWALLOW_PATCH
c->isfloating = r->isfloating; c->isfloating = r->isfloating;
c->tags |= r->tags; c->tags |= r->tags;
#if SCRATCHPAD_PATCH && !SCRATCHPAD_KEEP_POSITION_AND_SIZE_PATCH
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);
}
#endif // SCRATCHPAD_PATCH | SCRATCHPAD_KEEP_POSITION_AND_SIZE_PATCH
for (m = mons; m && m->num != r->monitor; m = m->next); for (m = mons; m && m->num != r->monitor; m = m->next);
if (m) if (m)
c->mon = m; c->mon = m;
@ -559,8 +572,14 @@ applyrules(Client *c)
XFree(ch.res_name); XFree(ch.res_name);
#if EMPTYVIEW_PATCH #if EMPTYVIEW_PATCH
if(c->tags & TAGMASK) c->tags = c->tags & TAGMASK; if(c->tags & TAGMASK) c->tags = c->tags & TAGMASK;
#if SCRATCHPAD_PATCH
else if(c->mon->tagset[c->mon->seltags]) c->tags = c->mon->tagset[c->mon->seltags] & ~SPTAGMASK;
#else
else if(c->mon->tagset[c->mon->seltags]) c->tags = c->mon->tagset[c->mon->seltags]; else if(c->mon->tagset[c->mon->seltags]) c->tags = c->mon->tagset[c->mon->seltags];
#endif // SCRATCHPAD_PATCH
else c->tags = 1; else c->tags = 1;
#elif SCRATCHPAD_PATCH
c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : (c->mon->tagset[c->mon->seltags] & ~SPTAGMASK);
#else #else
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];
#endif // EMPTYVIEW_PATCH #endif // EMPTYVIEW_PATCH
@ -2115,16 +2134,6 @@ manage(Window w, XWindowAttributes *wa)
c->bw = borderpx; c->bw = borderpx;
#endif // SETBORDERPX_PATCH #endif // SETBORDERPX_PATCH
#if SCRATCHPAD_PATCH
selmon->tagset[selmon->seltags] &= ~scratchtag;
if (!strcmp(c->name, scratchpadname)) {
c->mon->tagset[c->mon->seltags] |= c->tags = scratchtag;
c->isfloating = True;
c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2);
c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2);
}
#endif // SCRATCHPAD_PATCH
wc.border_width = c->bw; wc.border_width = c->bw;
XConfigureWindow(dpy, w, CWBorderWidth, &wc); XConfigureWindow(dpy, w, CWBorderWidth, &wc);
#if FLOAT_BORDER_COLOR_PATCH #if FLOAT_BORDER_COLOR_PATCH
@ -3081,6 +3090,12 @@ showhide(Client *c)
if (!c) if (!c)
return; return;
if (ISVISIBLE(c)) { if (ISVISIBLE(c)) {
#if SCRATCHPAD_PATCH && !SCRATCHPAD_KEEP_POSITION_AND_SIZE_PATCH
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);
}
#endif // SCRATCHPAD_PATCH | SCRATCHPAD_KEEP_POSITION_AND_SIZE_PATCH
/* show clients top down */ /* show clients top down */
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH #if SAVEFLOATS_PATCH || EXRESIZE_PATCH
if (!c->mon->lt[c->mon->sellt]->arrange && c->sfx != -9999 && !c->isfullscreen) { if (!c->mon->lt[c->mon->sellt]->arrange && c->sfx != -9999 && !c->isfullscreen) {
@ -3149,9 +3164,6 @@ spawn(const Arg *arg)
} }
#endif // STATUSCMD_PATCH #endif // STATUSCMD_PATCH
#if SCRATCHPAD_PATCH
selmon->tagset[selmon->seltags] &= ~scratchtag;
#endif // SCRATCHPAD_PATCH
if (fork() == 0) { if (fork() == 0) {
if (dpy) if (dpy)
close(ConnectionNumber(dpy)); close(ConnectionNumber(dpy));

View File

@ -1,23 +1,25 @@
static unsigned int scratchtag = 1 << LENGTH(tags);
void void
togglescratch(const Arg *arg) togglescratch(const Arg *arg)
{ {
Client *c; Client *c;
unsigned int found = 0; unsigned int found = 0;
unsigned int scratchtag = SPTAG(arg->ui);
Arg sparg = {.v = scratchpads[arg->ui].cmd};
for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next); for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next);
if (found) { if (found) {
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag;
if (newtagset) { if (newtagset) {
selmon->tagset[selmon->seltags] = newtagset; selmon->tagset[selmon->seltags] = newtagset;
focus(NULL); focus(NULL);
arrange(selmon); arrange(selmon);
} }
if (ISVISIBLE(c)) { if (ISVISIBLE(c)) {
focus(c); focus(c);
restack(selmon); restack(selmon);
} }
} else } else {
spawn(arg); selmon->tagset[selmon->seltags] |= scratchtag;
spawn(&sparg);
}
} }

View File

@ -1 +1,6 @@
typedef struct {
const char *name;
const void *cmd;
} Sp;
static void togglescratch(const Arg *arg); static void togglescratch(const Arg *arg);

View File

@ -397,12 +397,21 @@
*/ */
#define SAVEFLOATS_PATCH 0 #define SAVEFLOATS_PATCH 0
/* The scratchpad patch allows you to spawn or restore a floating terminal window. /* The scratchpad patch allows you to spawn or restore floating terminal windows.
* It is typically useful when one need to do some short typing. * It is typically useful when one need to do some short typing.
* Upgraded to Christian Tenllado's multiple scratchpad version.
* https://lists.suckless.org/hackers/2004/17205.html
* https://dwm.suckless.org/patches/scratchpad/ * https://dwm.suckless.org/patches/scratchpad/
*/ */
#define SCRATCHPAD_PATCH 0 #define SCRATCHPAD_PATCH 0
/* The scratchpad patch above automatically resizes and centers the scratchpad window every
* time you spawn it. This alteration of the patch disables that so that the size and position
* of the scratchpad window is retained when you respawn it. If you enable this then you may
* want to also take the centered patch and enable the iscentered flag for floating scratchpads.
*/
#define SCRATCHPAD_KEEP_POSITION_AND_SIZE_PATCH 0
/* This alternative patch enables a scratchpad feature in dwm similar to the scratchpad /* This alternative patch enables a scratchpad feature in dwm similar to the scratchpad
* feature in i3wm. * feature in i3wm.
* https://github.com/GasparVardanyan/dwm-scratchpad * https://github.com/GasparVardanyan/dwm-scratchpad