From f9a001dee71ca5ce23282ca7a7d7a677307d41c7 Mon Sep 17 00:00:00 2001 From: bakkeby Date: Thu, 16 Apr 2020 16:39:22 +0200 Subject: [PATCH] [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). --- README.md | 2 ++ config.def.h | 29 ++++++++++++++++++++++------- dwm.c | 38 +++++++++++++++++++++++++------------- patch/scratchpad.c | 38 ++++++++++++++++++++------------------ patch/scratchpad.h | 5 +++++ patches.def.h | 11 ++++++++++- 6 files changed, 84 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 34c31b4..2d0987a 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Refer to [https://dwm.suckless.org/](https://dwm.suckless.org/) for details on t ### 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-03-31 - Added the rounded corners patch diff --git a/config.def.h b/config.def.h index 77a4ae0..a164d5b 100644 --- a/config.def.h +++ b/config.def.h @@ -256,6 +256,18 @@ char *colors[][3] = { }; #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 */ #if EWMHTAGS_PATCH 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 */ { "Gimp", NULL, NULL, 0, 1, -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 }; @@ -679,10 +696,6 @@ static const char *statuscmds[] = { "notify-send Mouse$BUTTON" }; static char *statuscmd[] = { "/bin/sh", "-c", NULL, NULL }; #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 static const unsigned scratchpad_mask = 1u << sizeof tags / sizeof * tags; #endif // SCRATCHPAD_ALT_1_PATCH @@ -691,9 +704,6 @@ static Key keys[] = { /* modifier key function argument */ { MODKEY, XK_p, spawn, {.v = dmenucmd } }, { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, - #if SCRATCHPAD_PATCH - { MODKEY, XK_grave, togglescratch, {.v = scratchpadcmd } }, - #endif // SCRATCHPAD_PATCH { MODKEY, XK_b, togglebar, {0} }, #if STACKER_PATCH STACKKEYS(MODKEY, focus) @@ -815,6 +825,11 @@ static Key keys[] = { { MODKEY|ControlMask|ShiftMask, XK_k, toggleverticalmax, {0} }, { MODKEY|ControlMask, XK_m, togglemax, {0} }, #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 { MODKEY|Mod4Mask, XK_space, unfloatvisible, {0} }, { MODKEY|ShiftMask, XK_t, unfloatvisible, {.v = &layouts[0]} }, diff --git a/dwm.c b/dwm.c index c29f8d9..5b87c54 100644 --- a/dwm.c +++ b/dwm.c @@ -75,7 +75,14 @@ #define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define WIDTH(X) ((X)->w + 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) +#endif // SCRATCHPAD_PATCH #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) /* enums */ @@ -528,6 +535,12 @@ applyrules(Client *c) #endif // SWALLOW_PATCH c->isfloating = r->isfloating; 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); if (m) c->mon = m; @@ -559,8 +572,14 @@ applyrules(Client *c) XFree(ch.res_name); #if EMPTYVIEW_PATCH 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]; + #endif // SCRATCHPAD_PATCH else c->tags = 1; + #elif SCRATCHPAD_PATCH + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : (c->mon->tagset[c->mon->seltags] & ~SPTAGMASK); #else c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; #endif // EMPTYVIEW_PATCH @@ -2115,16 +2134,6 @@ manage(Window w, XWindowAttributes *wa) c->bw = borderpx; #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; XConfigureWindow(dpy, w, CWBorderWidth, &wc); #if FLOAT_BORDER_COLOR_PATCH @@ -3081,6 +3090,12 @@ showhide(Client *c) if (!c) return; 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 */ #if SAVEFLOATS_PATCH || EXRESIZE_PATCH if (!c->mon->lt[c->mon->sellt]->arrange && c->sfx != -9999 && !c->isfullscreen) { @@ -3149,9 +3164,6 @@ spawn(const Arg *arg) } #endif // STATUSCMD_PATCH - #if SCRATCHPAD_PATCH - selmon->tagset[selmon->seltags] &= ~scratchtag; - #endif // SCRATCHPAD_PATCH if (fork() == 0) { if (dpy) close(ConnectionNumber(dpy)); diff --git a/patch/scratchpad.c b/patch/scratchpad.c index 7ba6366..4edff52 100644 --- a/patch/scratchpad.c +++ b/patch/scratchpad.c @@ -1,23 +1,25 @@ -static unsigned int scratchtag = 1 << LENGTH(tags); - void togglescratch(const Arg *arg) { - Client *c; - unsigned int found = 0; + Client *c; + 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); - if (found) { - unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; - if (newtagset) { - selmon->tagset[selmon->seltags] = newtagset; - focus(NULL); - arrange(selmon); - } - if (ISVISIBLE(c)) { - focus(c); - restack(selmon); - } - } else - spawn(arg); + for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next); + if (found) { + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); + } + if (ISVISIBLE(c)) { + focus(c); + restack(selmon); + } + } else { + selmon->tagset[selmon->seltags] |= scratchtag; + spawn(&sparg); + } } \ No newline at end of file diff --git a/patch/scratchpad.h b/patch/scratchpad.h index be5c3a4..3142cdf 100644 --- a/patch/scratchpad.h +++ b/patch/scratchpad.h @@ -1 +1,6 @@ +typedef struct { + const char *name; + const void *cmd; +} Sp; + static void togglescratch(const Arg *arg); \ No newline at end of file diff --git a/patches.def.h b/patches.def.h index 261603a..72fabc3 100644 --- a/patches.def.h +++ b/patches.def.h @@ -397,12 +397,21 @@ */ #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. + * Upgraded to Christian Tenllado's multiple scratchpad version. + * https://lists.suckless.org/hackers/2004/17205.html * https://dwm.suckless.org/patches/scratchpad/ */ #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 * feature in i3wm. * https://github.com/GasparVardanyan/dwm-scratchpad