aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--config.def.h3
-rw-r--r--config.h210
-rw-r--r--dwl.c34
-rw-r--r--patches/simple_scratchpad-0.8.patch211
-rw-r--r--simple_scratchpad.c68
6 files changed, 524 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index 0dde90e..5c28541 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,3 @@ dwl
*-protocol.c
*-protocol.h
.ccls-cache
-config.h
diff --git a/config.def.h b/config.def.h
index f490e24..fcb6f78 100644
--- a/config.def.h
+++ b/config.def.h
@@ -134,6 +134,9 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_Return, zoom, {0} },
{ MODKEY, XKB_KEY_Tab, view, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_c, killclient, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_z, addscratchpad, {0} },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_z, togglescratchpad, {0} },
+ { MODKEY, XKB_KEY_z, removescratchpad, {0} },
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..341c39f
--- /dev/null
+++ b/config.h
@@ -0,0 +1,210 @@
+/* Taken from https://github.com/djpohly/dwl/issues/466 */
+#define COLOR(hex) { ((hex >> 24) & 0xFF) / 255.0f, \
+ ((hex >> 16) & 0xFF) / 255.0f, \
+ ((hex >> 8) & 0xFF) / 255.0f, \
+ (hex & 0xFF) / 255.0f }
+/* appearance */
+static const int sloppyfocus = 1; /* focus follows mouse */
+static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
+static const unsigned int borderpx = 3; /* border pixel of windows */
+static const float rootcolor[] = COLOR(0x222222ff);
+static const float bordercolor[] = COLOR(0x444444ff);
+static const float focuscolor[] = COLOR(0x005577ff);
+static const float urgentcolor[] = COLOR(0xff0000ff);
+/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */
+static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */
+
+/* tagging - TAGCOUNT must be no greater than 31 */
+#define TAGCOUNT (10)
+
+/* logging */
+static int log_level = WLR_ERROR;
+
+static const Rule rules[] = {
+ /* app_id title tags mask isfloating monitor */
+ { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */
+ { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */
+ /* default/example rule: can be changed but cannot be eliminated; at least one rule must exist */
+};
+
+/* layout(s) */
+static const Layout layouts[] = {
+ /* symbol arrange function */
+ { "[]=", tile },
+ { "><>", NULL }, /* no layout function means floating behavior */
+ { "[M]", monocle },
+};
+
+/* monitors */
+/* (x=-1, y=-1) is reserved as an "autoconfigure" monitor position indicator
+ * WARNING: negative values other than (-1, -1) cause problems with Xwayland clients due to
+ * https://gitlab.freedesktop.org/xorg/xserver/-/issues/899 */
+static const MonitorRule monrules[] = {
+ /* name mfact nmaster scale layout rotate/reflect x y
+ * example of a HiDPI laptop monitor:
+ { "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, */
+ { NULL, 0.55f, 1, 1.5, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 3840, 0 },
+ /* { "DP-6", 0.55f, 0, 1.0, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 1920, 0 }, */
+ /* { "eDP-1", 0.55f, 1, 1.5, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, */
+ /* default monitor rule: can be changed but cannot be eliminated; at least one monitor rule must exist */
+};
+
+/* keyboard */
+static const struct xkb_rule_names xkb_rules = {
+ /* can specify fields: rules, model, layout, variant, options */
+ /* example:
+ .options = "ctrl:nocaps",
+ */
+ .options = "caps:escape",
+};
+
+static const int repeat_rate = 50;
+static const int repeat_delay = 300;
+
+/* Trackpad */
+static const int tap_to_click = 1;
+static const int tap_and_drag = 1;
+static const int drag_lock = 1;
+static const int natural_scrolling = 0;
+static const int disable_while_typing = 1;
+static const int left_handed = 0;
+static const int middle_button_emulation = 0;
+/* You can choose between:
+LIBINPUT_CONFIG_SCROLL_NO_SCROLL
+LIBINPUT_CONFIG_SCROLL_2FG
+LIBINPUT_CONFIG_SCROLL_EDGE
+LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
+*/
+static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
+
+/* You can choose between:
+LIBINPUT_CONFIG_CLICK_METHOD_NONE
+LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
+LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
+*/
+static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
+
+/* You can choose between:
+LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
+*/
+static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
+
+/* You can choose between:
+LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
+LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
+*/
+static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
+static const double accel_speed = 0.0;
+
+/* You can choose between:
+LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle
+LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right
+*/
+static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
+
+/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
+// #define MODKEY WLR_MODIFIER_ALT
+#define MODKEY WLR_MODIFIER_LOGO
+
+#define TAGKEYS(KEY,SKEY,TAG) \
+ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
+ { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
+ { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \
+ { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }
+
+/* helper for spawning shell commands in the pre dwm-5.0 fashion */
+#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
+
+/* commands */
+static const char *termcmd[] = { "foot", NULL };
+static const char *menucmd[] = { "wmenu-run", NULL };
+
+static const Key keys[] = {
+ /* Note that Shift changes certain key codes: 2 -> at, etc. */
+ /* modifier key function argument */
+ /* FN row */
+ { 0, XKB_KEY_XF86AudioMute, spawn, {.v = (const char *[]){"wpctl", "set-mute", "@DEFAULT_AUDIO_SINK@", "toggle", NULL} } },
+ { 0, XKB_KEY_XF86AudioLowerVolume, spawn, {.v = (const char *[]){"wpctl", "set-volume", "@DEFAULT_AUDIO_SINK@", "0.05-", NULL} } },
+ { 0, XKB_KEY_XF86AudioRaiseVolume, spawn, {.v = (const char *[]){"wpctl", "set-volume", "@DEFAULT_AUDIO_SINK@", "0.05+", NULL} } },
+ { 0, XKB_KEY_XF86AudioMicMute, spawn, {.v = (const char *[]){"wpctl", "set-mute", "@DEFAULT_AUDIO_SOURCE@", "toggle", NULL} } },
+ { 0, XKB_KEY_XF86MonBrightnessDown,spawn, {.v = (const char *[]){"brightnessctl", "set", "5%-", NULL} } },
+ { 0, XKB_KEY_XF86MonBrightnessUp, spawn, {.v = (const char *[]){"brightnessctl", "set", "+5%", NULL} } },
+ /* Num row */
+ { MODKEY, XKB_KEY_grave, spawn, {.v = (const char *[]){"swaylock", "-c", "000000", NULL} } },
+ TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
+ TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
+ TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
+ TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
+ TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
+ TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
+ TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
+ TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
+ TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
+ { MODKEY, XKB_KEY_0, view, {.ui = ~0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
+ { MODKEY, XKB_KEY_minus, togglescratchpad, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_underscore, addscratchpad, {0} },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_minus, removescratchpad, {0} },
+ /* Row one */
+ { MODKEY, XKB_KEY_Tab, view, {0} },
+ { MODKEY, XKB_KEY_q, killclient, {0} },
+ { MODKEY, XKB_KEY_e, togglefullscreen, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_e, quit, {0} },
+ /* { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, */
+ { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY, XKB_KEY_o, spawn, {.v = (const char *[]){"passmenu", "-u", "-t", NULL} } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_o, spawn, {.v = (const char *[]){"passmenu", "-u", NULL} } },
+ { MODKEY, XKB_KEY_p, spawn, {.v = (const char *[]){"passmenu", "-t", NULL} } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_p, spawn, {.v = (const char *[]){"passmenu", NULL} } },
+ { MODKEY, XKB_KEY_bracketleft, spawn, {.v = (const char *[]){"mpc", "seek", "0", NULL} } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_braceleft, spawn, {.v = (const char *[]){"mpc", "prev", NULL} } },
+ { MODKEY, XKB_KEY_bracketright,spawn, {.v = (const char *[]){"mpc", "next", NULL} } },
+ /* { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_braceright, spawn, {.v = (const char *[]){"mpc", "prev", NULL} } }, */
+ { MODKEY, XKB_KEY_backslash, spawn, {.v = (const char *[]){"mpc", "toggle", NULL} } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_bar, spawn, {.v = (const char *[]){"foot", "ncmpcpp", NULL} } },
+ /* Row two */
+ { MODKEY, XKB_KEY_d, spawn, {.v = menucmd} },
+ /* { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} }, */
+ { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
+ { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} },
+ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_j, movestack, {.i = +1} },
+ { MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_k, movestack, {.i = -1} },
+ { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} },
+ { MODKEY, XKB_KEY_Return, spawn, {.v = termcmd} },
+ /* { MODKEY, XKB_KEY_Return, zoom, {0} }, */
+ /* Row three */
+ { MODKEY, XKB_KEY_z, spawn, {.v = (const char *[]){"firefox", "-P", "insecure", NULL } } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_z, spawn, {.v = (const char *[]){"firefox", "-P", "insecure", "--private-window", NULL } } },
+ { MODKEY, XKB_KEY_x, spawn, {.v = (const char *[]){"secure_browser", NULL } } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_x, spawn, {.v = (const char *[]){"secure_browser", "--private-window", NULL } } },
+ { MODKEY, XKB_KEY_b, spawn, SHCMD("get-bookmark | wtype -") },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_b, spawn, SHCMD("get-bookmark | wl-copy") },
+ { MODKEY, XKB_KEY_n, spawn, SHCMD("wl-paste | save-bookmark") },
+ { MODKEY, XKB_KEY_m, spawn, SHCMD("wl-paste | linkmenu") },
+ /* { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, */
+ { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
+ { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
+ { MODKEY, XKB_KEY_space, setlayout, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
+
+ /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
+ { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
+ /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is
+ * do not remove them.
+ */
+#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
+ CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
+ CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
+};
+
+static const Button buttons[] = {
+ { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
+ { MODKEY, BTN_MIDDLE, togglefloating, {0} },
+ { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
+};
diff --git a/dwl.c b/dwl.c
index 67d3167..eba383d 100644
--- a/dwl.c
+++ b/dwl.c
@@ -140,6 +140,7 @@ typedef struct {
uint32_t tags;
int isfloating, isurgent, isfullscreen;
uint32_t resize; /* configure serial of a pending resize */
+ struct wl_list link_temp;
} Client;
typedef struct {
@@ -241,6 +242,7 @@ typedef struct {
} SessionLock;
/* function declarations */
+static void addscratchpad(const Arg *arg);
static void applybounds(Client *c, struct wlr_box *bbox);
static void applyrules(Client *c);
static void arrange(Monitor *m);
@@ -316,6 +318,7 @@ static void printstatus(void);
static void powermgrsetmode(struct wl_listener *listener, void *data);
static void quit(const Arg *arg);
static void rendermon(struct wl_listener *listener, void *data);
+static void removescratchpad(const Arg *arg);
static void requestdecorationmode(struct wl_listener *listener, void *data);
static void requeststartdrag(struct wl_listener *listener, void *data);
static void requestmonstate(struct wl_listener *listener, void *data);
@@ -338,6 +341,7 @@ static void tagmon(const Arg *arg);
static void tile(Monitor *m);
static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg);
+static void togglescratchpad(const Arg *arg);
static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
static void unlocksession(struct wl_listener *listener, void *data);
@@ -408,6 +412,9 @@ static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static struct wl_list scratchpad_clients;
+static int scratchpad_visible = 1;
+
/* global event handlers */
static struct wl_listener cursor_axis = {.notify = axisnotify};
static struct wl_listener cursor_button = {.notify = buttonpress};
@@ -457,6 +464,8 @@ static struct wlr_xwayland *xwayland;
/* attempt to encapsulate suck into one file */
#include "client.h"
+#include "simple_scratchpad.c"
+
/* function implementations */
void
applybounds(Client *c, struct wlr_box *bbox)
@@ -1330,10 +1339,20 @@ void
destroynotify(struct wl_listener *listener, void *data)
{
/* Called when the xdg_toplevel is destroyed. */
- Client *c = wl_container_of(listener, c, destroy);
+ Client *sc, *c = wl_container_of(listener, c, destroy);
wl_list_remove(&c->destroy.link);
wl_list_remove(&c->set_title.link);
wl_list_remove(&c->fullscreen.link);
+ /* Check if destroyed client was part of scratchpad_clients
+ * and clean it from the list if so. */
+ if (c && wl_list_length(&scratchpad_clients) > 0) {
+ wl_list_for_each(sc, &scratchpad_clients, link_temp) {
+ if (sc == c) {
+ wl_list_remove(&c->link_temp);
+ break;
+ }
+ }
+ }
#ifdef XWAYLAND
if (c->type != XDGShell) {
wl_list_remove(&c->activate.link);
@@ -2377,11 +2396,21 @@ setcursorshape(struct wl_listener *listener, void *data)
void
setfloating(Client *c, int floating)
{
- Client *p = client_get_parent(c);
+ Client *sc, *p = client_get_parent(c);
c->isfloating = floating;
/* If in floating layout do not change the client's layer */
if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange)
return;
+ /* Check if unfloated client was part of scratchpad_clients
+ * and remove it from scratchpad_clients list if so */
+ if (!floating && wl_list_length(&scratchpad_clients) > 0) {
+ wl_list_for_each(sc, &scratchpad_clients, link_temp) {
+ if (sc == c) {
+ wl_list_remove(&c->link_temp);
+ break;
+ }
+ }
+ }
wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ||
(p && p->isfullscreen) ? LyrFS
: c->isfloating ? LyrFloat : LyrTile]);
@@ -2599,6 +2628,7 @@ setup(void)
*/
wl_list_init(&clients);
wl_list_init(&fstack);
+ wl_list_init(&scratchpad_clients);
xdg_shell = wlr_xdg_shell_create(dpy, 6);
wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
diff --git a/patches/simple_scratchpad-0.8.patch b/patches/simple_scratchpad-0.8.patch
new file mode 100644
index 0000000..0d8af99
--- /dev/null
+++ b/patches/simple_scratchpad-0.8.patch
@@ -0,0 +1,211 @@
+From 0d603b179ccccbb4270fd202a14a2db18d0a5413 Mon Sep 17 00:00:00 2001
+From: A Frederick Christensen <dwl@ivories.org>
+Date: Thu, 26 Feb 2026 17:09:28 -0600
+Subject: [PATCH] Apply simple_scratchpad patch
+
+---
+ config.def.h | 3 ++
+ dwl.c | 34 +++++++++++++++++++++--
+ simple_scratchpad.c | 68 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 103 insertions(+), 2 deletions(-)
+ create mode 100644 simple_scratchpad.c
+
+diff --git a/config.def.h b/config.def.h
+index 8a6eda0..6254fc9 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -132,6 +132,9 @@ static const Key keys[] = {
+ { MODKEY, XKB_KEY_Return, zoom, {0} },
+ { MODKEY, XKB_KEY_Tab, view, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_c, killclient, {0} },
++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_z, addscratchpad, {0} },
++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_z, togglescratchpad, {0} },
++ { MODKEY, XKB_KEY_z, removescratchpad, {0} },
+ { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
+ { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
+diff --git a/dwl.c b/dwl.c
+index 44f3ad9..215f630 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -139,6 +139,7 @@ typedef struct {
+ uint32_t tags;
+ int isfloating, isurgent, isfullscreen;
+ uint32_t resize; /* configure serial of a pending resize */
++ struct wl_list link_temp;
+ } Client;
+
+ typedef struct {
+@@ -240,6 +241,7 @@ typedef struct {
+ } SessionLock;
+
+ /* function declarations */
++static void addscratchpad(const Arg *arg);
+ static void applybounds(Client *c, struct wlr_box *bbox);
+ static void applyrules(Client *c);
+ static void arrange(Monitor *m);
+@@ -314,6 +316,7 @@ static void printstatus(void);
+ static void powermgrsetmode(struct wl_listener *listener, void *data);
+ static void quit(const Arg *arg);
+ static void rendermon(struct wl_listener *listener, void *data);
++static void removescratchpad(const Arg *arg);
+ static void requestdecorationmode(struct wl_listener *listener, void *data);
+ static void requeststartdrag(struct wl_listener *listener, void *data);
+ static void requestmonstate(struct wl_listener *listener, void *data);
+@@ -336,6 +339,7 @@ static void tagmon(const Arg *arg);
+ static void tile(Monitor *m);
+ static void togglefloating(const Arg *arg);
+ static void togglefullscreen(const Arg *arg);
++static void togglescratchpad(const Arg *arg);
+ static void toggletag(const Arg *arg);
+ static void toggleview(const Arg *arg);
+ static void unlocksession(struct wl_listener *listener, void *data);
+@@ -406,6 +410,9 @@ static struct wlr_box sgeom;
+ static struct wl_list mons;
+ static Monitor *selmon;
+
++static struct wl_list scratchpad_clients;
++static int scratchpad_visible = 1;
++
+ /* global event handlers */
+ static struct wl_listener cursor_axis = {.notify = axisnotify};
+ static struct wl_listener cursor_button = {.notify = buttonpress};
+@@ -455,6 +462,8 @@ static struct wlr_xwayland *xwayland;
+ /* attempt to encapsulate suck into one file */
+ #include "client.h"
+
++#include "simple_scratchpad.c"
++
+ /* function implementations */
+ void
+ applybounds(Client *c, struct wlr_box *bbox)
+@@ -1328,10 +1337,20 @@ void
+ destroynotify(struct wl_listener *listener, void *data)
+ {
+ /* Called when the xdg_toplevel is destroyed. */
+- Client *c = wl_container_of(listener, c, destroy);
++ Client *sc, *c = wl_container_of(listener, c, destroy);
+ wl_list_remove(&c->destroy.link);
+ wl_list_remove(&c->set_title.link);
+ wl_list_remove(&c->fullscreen.link);
++ /* Check if destroyed client was part of scratchpad_clients
++ * and clean it from the list if so. */
++ if (c && wl_list_length(&scratchpad_clients) > 0) {
++ wl_list_for_each(sc, &scratchpad_clients, link_temp) {
++ if (sc == c) {
++ wl_list_remove(&c->link_temp);
++ break;
++ }
++ }
++ }
+ #ifdef XWAYLAND
+ if (c->type != XDGShell) {
+ wl_list_remove(&c->activate.link);
+@@ -2333,11 +2352,21 @@ setcursorshape(struct wl_listener *listener, void *data)
+ void
+ setfloating(Client *c, int floating)
+ {
+- Client *p = client_get_parent(c);
++ Client *sc, *p = client_get_parent(c);
+ c->isfloating = floating;
+ /* If in floating layout do not change the client's layer */
+ if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange)
+ return;
++ /* Check if unfloated client was part of scratchpad_clients
++ * and remove it from scratchpad_clients list if so */
++ if (!floating && wl_list_length(&scratchpad_clients) > 0) {
++ wl_list_for_each(sc, &scratchpad_clients, link_temp) {
++ if (sc == c) {
++ wl_list_remove(&c->link_temp);
++ break;
++ }
++ }
++ }
+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ||
+ (p && p->isfullscreen) ? LyrFS
+ : c->isfloating ? LyrFloat : LyrTile]);
+@@ -2554,6 +2583,7 @@ setup(void)
+ */
+ wl_list_init(&clients);
+ wl_list_init(&fstack);
++ wl_list_init(&scratchpad_clients);
+
+ xdg_shell = wlr_xdg_shell_create(dpy, 6);
+ wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
+diff --git a/simple_scratchpad.c b/simple_scratchpad.c
+new file mode 100644
+index 0000000..26d6b66
+--- /dev/null
++++ b/simple_scratchpad.c
+@@ -0,0 +1,68 @@
++/* ************************************************************************** */
++/* */
++/* ::: :::::::: */
++/* simple_scratchpad.c :+: :+: :+: */
++/* +:+ +:+ +:+ */
++/* By: jmakkone <jmakkone@student.hive.fi> +#+ +:+ +#+ */
++/* +#+#+#+#+#+ +#+ */
++/* Created: 2024/12/19 19:35:02 by jmakkone #+# #+# */
++/* Updated: 2025/01/04 13:35:50 by jmakkone ### ########.fr */
++/* */
++/* ************************************************************************** */
++
++void
++addscratchpad(const Arg *arg)
++{
++ Client *cc, *c = focustop(selmon);
++
++ if (!c)
++ return;
++ /* Check if the added client is already a scratchpad client */
++ wl_list_for_each(cc, &scratchpad_clients, link_temp) {
++ if (cc == c)
++ return;
++ }
++ if (!c->isfloating) {
++ setfloating(c, 1);
++ }
++ wl_list_insert(&scratchpad_clients, &c->link_temp);
++}
++
++void
++togglescratchpad(const Arg *arg)
++{
++ Client *c;
++ Monitor *m = selmon;
++
++ scratchpad_visible = !scratchpad_visible;
++ if (scratchpad_visible) {
++ wl_list_for_each(c, &scratchpad_clients, link_temp) {
++ c->mon = m;
++ c->tags = m->tagset[m->seltags];
++ arrange(m);
++ focusclient(c, 1);
++ }
++ } else {
++ wl_list_for_each(c, &scratchpad_clients, link_temp) {
++ c->tags = 0;
++ focusclient(focustop(m), 1);
++ arrange(m);
++ }
++ }
++}
++
++void
++removescratchpad(const Arg *arg)
++{
++ Client *sc, *c = focustop(selmon);
++
++ if (c && wl_list_length(&scratchpad_clients) > 0) {
++ /* Check if c is in scratchpad_clients */
++ wl_list_for_each(sc, &scratchpad_clients, link_temp) {
++ if (sc == c) {
++ wl_list_remove(&c->link_temp);
++ break;
++ }
++ }
++ }
++}
+--
+2.52.0
+
diff --git a/simple_scratchpad.c b/simple_scratchpad.c
new file mode 100644
index 0000000..26d6b66
--- /dev/null
+++ b/simple_scratchpad.c
@@ -0,0 +1,68 @@
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* simple_scratchpad.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: jmakkone <jmakkone@student.hive.fi> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/12/19 19:35:02 by jmakkone #+# #+# */
+/* Updated: 2025/01/04 13:35:50 by jmakkone ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+void
+addscratchpad(const Arg *arg)
+{
+ Client *cc, *c = focustop(selmon);
+
+ if (!c)
+ return;
+ /* Check if the added client is already a scratchpad client */
+ wl_list_for_each(cc, &scratchpad_clients, link_temp) {
+ if (cc == c)
+ return;
+ }
+ if (!c->isfloating) {
+ setfloating(c, 1);
+ }
+ wl_list_insert(&scratchpad_clients, &c->link_temp);
+}
+
+void
+togglescratchpad(const Arg *arg)
+{
+ Client *c;
+ Monitor *m = selmon;
+
+ scratchpad_visible = !scratchpad_visible;
+ if (scratchpad_visible) {
+ wl_list_for_each(c, &scratchpad_clients, link_temp) {
+ c->mon = m;
+ c->tags = m->tagset[m->seltags];
+ arrange(m);
+ focusclient(c, 1);
+ }
+ } else {
+ wl_list_for_each(c, &scratchpad_clients, link_temp) {
+ c->tags = 0;
+ focusclient(focustop(m), 1);
+ arrange(m);
+ }
+ }
+}
+
+void
+removescratchpad(const Arg *arg)
+{
+ Client *sc, *c = focustop(selmon);
+
+ if (c && wl_list_length(&scratchpad_clients) > 0) {
+ /* Check if c is in scratchpad_clients */
+ wl_list_for_each(sc, &scratchpad_clients, link_temp) {
+ if (sc == c) {
+ wl_list_remove(&c->link_temp);
+ break;
+ }
+ }
+ }
+}