summaryrefslogtreecommitdiffstats
path: root/patches
diff options
context:
space:
mode:
Diffstat (limited to 'patches')
-rw-r--r--patches/borders.patch213
-rw-r--r--patches/gestures.patch169
-rw-r--r--patches/ipc.patch597
-rw-r--r--patches/lockedkeys.patch79
-rw-r--r--patches/main...serenevoid:uselessgaps.patch137
-rw-r--r--patches/passthrough.patch82
-rw-r--r--patches/pointer-gestures-unstable-v1.patch186
-rw-r--r--patches/regexrules.patch76
-rw-r--r--patches/swallow.patch240
-rw-r--r--patches/tearing.patch356
-rw-r--r--patches/toggle_constraints.patch86
-rw-r--r--patches/xwayland-handle-minimize.patch91
12 files changed, 2312 insertions, 0 deletions
diff --git a/patches/borders.patch b/patches/borders.patch
new file mode 100644
index 0000000..0fb91c0
--- /dev/null
+++ b/patches/borders.patch
@@ -0,0 +1,213 @@
+From b12cfff672f0705d8259cf26b3a574faa5ca43ae Mon Sep 17 00:00:00 2001
+From: wochap <gean.marroquin@gmail.com>
+Date: Tue, 4 Jun 2024 16:02:25 -0500
+Subject: [PATCH] implement borders patch
+
+tihs patch adds 2 extra borders relative to the client, they don't
+change the size of the client
+---
+ client.h | 16 +++++++++++++---
+ config.def.h | 8 ++++++++
+ dwl.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----
+ 3 files changed, 70 insertions(+), 7 deletions(-)
+
+diff --git a/client.h b/client.h
+index 800b867..33fd579 100644
+--- a/client.h
++++ b/client.h
+@@ -325,11 +325,21 @@ client_send_close(Client *c)
+ }
+
+ static inline void
+-client_set_border_color(Client *c, const float color[static 4])
++client_set_border_color(Client *c, const float color[static 4], const float colors[static 4], const float colore[static 4])
+ {
+ int i;
+- for (i = 0; i < 4; i++)
+- wlr_scene_rect_set_color(c->border[i], color);
++ for (i = 0; i < 4; i++) {
++ if (border_color_type == BrdOriginal) {
++ wlr_scene_rect_set_color(c->border[i], color);
++ } else if (border_color_type == BrdStart) {
++ wlr_scene_rect_set_color(c->borders[i], colors);
++ } else if (border_color_type == BrdEnd) {
++ wlr_scene_rect_set_color(c->bordere[i], colore);
++ } else if (border_color_type == BrdStartEnd) {
++ wlr_scene_rect_set_color(c->borders[i], colors);
++ wlr_scene_rect_set_color(c->bordere[i], colore);
++ }
++ }
+ }
+
+ static inline void
+diff --git a/config.def.h b/config.def.h
+index 8847e58..2d6bbe5 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -7,8 +7,16 @@
+ 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 = 1; /* border pixel of windows */
++static const unsigned int borderspx = 0; /* width of the border that start from outside the windows */
++static const unsigned int borderepx = 0; /* width of the border that start from inside the windows */
++static const unsigned int borderspx_offset = 0; /* offset of the border that start from outside the windows */
++static const unsigned int borderepx_negative_offset = 0; /* offset of the border that start from inside the windows */
+ static const float rootcolor[] = COLOR(0x222222ff);
+ static const float bordercolor[] = COLOR(0x444444ff);
++static const float borderscolor[] = COLOR(0x444444ff); /* color of the border that start from outside the windows */
++static const float borderecolor[] = COLOR(0x444444ff); /* color of the border that start from inside the windows */
++static const int border_color_type = BrdOriginal; /* borders to be colored (focuscolor, urgentcolor) */
++static const int borders_only_floating = 0;
+ 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 */
+diff --git a/dwl.c b/dwl.c
+index bf763df..303832a 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -86,6 +86,7 @@ enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock,
+ enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar,
+ NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */
+ #endif
++enum { BrdOriginal, BrdStart, BrdEnd, BrdStartEnd };
+
+ typedef union {
+ int i;
+@@ -109,6 +110,8 @@ typedef struct {
+ Monitor *mon;
+ struct wlr_scene_tree *scene;
+ struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
++ struct wlr_scene_rect *borders[4]; /* top, bottom, left, right */
++ struct wlr_scene_rect *bordere[4]; /* top, bottom, left, right */
+ struct wlr_scene_tree *scene_surface;
+ struct wl_list link;
+ struct wl_list flink;
+@@ -136,6 +139,8 @@ typedef struct {
+ struct wl_listener set_hints;
+ #endif
+ unsigned int bw;
++ unsigned int bws;
++ unsigned int bwe;
+ uint32_t tags;
+ int isfloating, isurgent, isfullscreen;
+ uint32_t resize; /* configure serial of a pending resize */
+@@ -973,6 +978,8 @@ createnotify(struct wl_listener *listener, void *data)
+ c = xdg_surface->data = ecalloc(1, sizeof(*c));
+ c->surface.xdg = xdg_surface;
+ c->bw = borderpx;
++ c->bws = borders_only_floating ? 0 : borderspx;
++ c->bwe = borders_only_floating ? 0 : borderepx;
+
+ wlr_xdg_toplevel_set_wm_capabilities(xdg_surface->toplevel,
+ WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
+@@ -1268,7 +1275,7 @@ focusclient(Client *c, int lift)
+ /* Don't change border color if there is an exclusive focus or we are
+ * handling a drag operation */
+ if (!exclusive_focus && !seat->drag)
+- client_set_border_color(c, focuscolor);
++ client_set_border_color(c, focuscolor, focuscolor, focuscolor);
+ }
+
+ /* Deactivate old client if focus is changing */
+@@ -1285,7 +1292,7 @@ focusclient(Client *c, int lift)
+ /* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg
+ * and probably other clients */
+ } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) {
+- client_set_border_color(old_c, bordercolor);
++ client_set_border_color(old_c, bordercolor, borderscolor, borderecolor);
+
+ client_activate_surface(old, 0);
+ }
+@@ -1597,6 +1604,12 @@ mapnotify(struct wl_listener *listener, void *data)
+ c->border[i] = wlr_scene_rect_create(c->scene, 0, 0,
+ c->isurgent ? urgentcolor : bordercolor);
+ c->border[i]->node.data = c;
++
++ c->borders[i] = wlr_scene_rect_create(c->scene, 0, 0, borderscolor);
++ c->borders[i]->node.data = c;
++
++ c->bordere[i] = wlr_scene_rect_create(c->scene, 0, 0, borderecolor);
++ c->bordere[i]->node.data = c;
+ }
+
+ /* Initialize client geometry with room for border */
+@@ -1618,6 +1631,12 @@ mapnotify(struct wl_listener *listener, void *data)
+ } else {
+ applyrules(c);
+ }
++
++ if (borders_only_floating) {
++ c->bws = c->isfloating ? borderspx : 0;
++ c->bwe = c->isfloating ? borderepx : 0;
++ }
++
+ printstatus();
+
+ unset_fullscreen:
+@@ -2051,6 +2070,24 @@ resize(Client *c, struct wlr_box geo, int interact)
+ wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw);
+ wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw);
+
++ wlr_scene_rect_set_size(c->borders[0], c->geom.width - 2 * borderspx_offset, c->bws);
++ wlr_scene_rect_set_size(c->borders[1], c->geom.width - 2 * borderspx_offset, c->bws);
++ wlr_scene_rect_set_size(c->borders[2], c->bws, c->geom.height - 2 * c->bws - 2 * borderspx_offset);
++ wlr_scene_rect_set_size(c->borders[3], c->bws, c->geom.height - 2 * c->bws - 2 * borderspx_offset);
++ wlr_scene_node_set_position(&c->borders[0]->node, borderspx_offset, borderspx_offset);
++ wlr_scene_node_set_position(&c->borders[1]->node, borderspx_offset, c->geom.height - c->bws - borderspx_offset);
++ wlr_scene_node_set_position(&c->borders[2]->node, borderspx_offset, c->bws + borderspx_offset);
++ wlr_scene_node_set_position(&c->borders[3]->node, c->geom.width - c->bws - borderspx_offset, c->bws + borderspx_offset);
++
++ wlr_scene_rect_set_size(c->bordere[0], c->geom.width - (c->bw - c->bwe) * 2 + borderepx_negative_offset * 2, c->bwe);
++ wlr_scene_rect_set_size(c->bordere[1], c->geom.width - (c->bw - c->bwe) * 2 + borderepx_negative_offset * 2, c->bwe);
++ wlr_scene_rect_set_size(c->bordere[2], c->bwe, c->geom.height - 2 * c->bw + 2 * borderepx_negative_offset);
++ wlr_scene_rect_set_size(c->bordere[3], c->bwe, c->geom.height - 2 * c->bw + 2 * borderepx_negative_offset);
++ wlr_scene_node_set_position(&c->bordere[0]->node, c->bw - c->bwe - borderepx_negative_offset, c->bw - c->bwe - borderepx_negative_offset);
++ wlr_scene_node_set_position(&c->bordere[1]->node, c->bw - c->bwe - borderepx_negative_offset, c->geom.height - c->bw + borderepx_negative_offset);
++ wlr_scene_node_set_position(&c->bordere[2]->node, c->bw - c->bwe - borderepx_negative_offset, c->bw - borderepx_negative_offset);
++ wlr_scene_node_set_position(&c->bordere[3]->node, c->geom.width - c->bw + borderepx_negative_offset, c->bw - borderepx_negative_offset);
++
+ /* this is a no-op if size hasn't changed */
+ c->resize = client_set_size(c, c->geom.width - 2 * c->bw,
+ c->geom.height - 2 * c->bw);
+@@ -2151,6 +2188,12 @@ setfloating(Client *c, int floating)
+ c->isfloating = floating;
+ if (!c->mon)
+ return;
++
++ if (borders_only_floating) {
++ c->bws = c->isfloating ? borderspx : 0;
++ c->bwe = c->isfloating ? borderepx : 0;
++ }
++
+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ||
+ (p && p->isfullscreen) ? LyrFS
+ : c->isfloating ? LyrFloat : LyrTile]);
+@@ -2165,6 +2208,8 @@ setfullscreen(Client *c, int fullscreen)
+ if (!c->mon)
+ return;
+ c->bw = fullscreen ? 0 : borderpx;
++ c->bws = fullscreen ? 0 : borderspx;
++ c->bwe = fullscreen ? 0 : borderepx;
+ client_set_fullscreen(c, fullscreen);
+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen
+ ? LyrFS : c->isfloating ? LyrFloat : LyrTile]);
+@@ -2819,7 +2864,7 @@ urgent(struct wl_listener *listener, void *data)
+ printstatus();
+
+ if (client_surface(c)->mapped)
+- client_set_border_color(c, urgentcolor);
++ client_set_border_color(c, urgentcolor, urgentcolor, urgentcolor);
+ }
+
+ void
+@@ -3023,7 +3068,7 @@ sethints(struct wl_listener *listener, void *data)
+ printstatus();
+
+ if (c->isurgent && surface && surface->mapped)
+- client_set_border_color(c, urgentcolor);
++ client_set_border_color(c, urgentcolor, urgentcolor, urgentcolor);
+ }
+
+ void
+--
+2.44.1
diff --git a/patches/gestures.patch b/patches/gestures.patch
new file mode 100644
index 0000000..4f74325
--- /dev/null
+++ b/patches/gestures.patch
@@ -0,0 +1,169 @@
+From 42f97e88bd901d81b81da61c44a790b583706308 Mon Sep 17 00:00:00 2001
+From: wochap <gean.marroquin@gmail.com>
+Date: Fri, 5 Jul 2024 11:18:49 -0500
+Subject: [PATCH] implement gestures
+
+---
+ config.def.h | 9 +++++++
+ dwl.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 77 insertions(+)
+
+diff --git a/config.def.h b/config.def.h
+index 22d2171..8b75564 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -14,6 +14,8 @@ 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.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */
+
++static const unsigned int swipe_min_threshold = 0;
++
+ /* tagging - TAGCOUNT must be no greater than 31 */
+ #define TAGCOUNT (9)
+
+@@ -174,3 +176,10 @@ static const Button buttons[] = {
+ { MODKEY, BTN_MIDDLE, togglefloating, {0} },
+ { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
+ };
++
++static const Gesture gestures[] = {
++ // { MODKEY, SWIPE_LEFT, 4, shiftview, { .i = 1 } },
++ // { MODKEY, SWIPE_RIGHT, 4, shiftview, { .i = -1 } },
++ { MODKEY, SWIPE_UP, 3, focusstack, {.i = 1} },
++ { MODKEY, SWIPE_DOWN, 3, focusstack, {.i = -1} },
++};
+diff --git a/dwl.c b/dwl.c
+index ded83e2..5d861e8 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -88,6 +88,7 @@ enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock,
+ enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar,
+ NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */
+ #endif
++enum { SWIPE_LEFT, SWIPE_RIGHT, SWIPE_DOWN, SWIPE_UP };
+
+ typedef union {
+ int i;
+@@ -103,6 +104,14 @@ typedef struct {
+ const Arg arg;
+ } Button;
+
++typedef struct {
++ unsigned int mod;
++ unsigned int motion;
++ unsigned int fingers_count;
++ void (*func)(const Arg *);
++ const Arg arg;
++} Gesture;
++
+ typedef struct Monitor Monitor;
+ typedef struct {
+ /* Must keep these three elements in this order */
+@@ -251,6 +260,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
+ static void arrangelayers(Monitor *m);
+ static void axisnotify(struct wl_listener *listener, void *data);
+ static void buttonpress(struct wl_listener *listener, void *data);
++static int ongesture(struct wlr_pointer_swipe_end_event *event);
+ static void swipe_begin(struct wl_listener *listener, void *data);
+ static void swipe_update(struct wl_listener *listener, void *data);
+ static void swipe_end(struct wl_listener *listener, void *data);
+@@ -416,6 +426,10 @@ static struct wlr_box sgeom;
+ static struct wl_list mons;
+ static Monitor *selmon;
+
++static uint32_t swipe_fingers = 0;
++static double swipe_dx = 0;
++static double swipe_dy = 0;
++
+ #ifdef XWAYLAND
+ static void activatex11(struct wl_listener *listener, void *data);
+ static void associatex11(struct wl_listener *listener, void *data);
+@@ -435,6 +449,8 @@ static xcb_atom_t netatom[NetLast];
+ /* attempt to encapsulate suck into one file */
+ #include "client.h"
+
++static const unsigned int abzsquare = swipe_min_threshold * swipe_min_threshold;
++
+ /* function implementations */
+ void
+ applybounds(Client *c, struct wlr_box *bbox)
+@@ -657,6 +673,11 @@ swipe_begin(struct wl_listener *listener, void *data)
+ {
+ struct wlr_pointer_swipe_begin_event *event = data;
+
++ swipe_fingers = event->fingers;
++ // Reset swipe distance at the beginning of a swipe
++ swipe_dx = 0;
++ swipe_dy = 0;
++
+ // Forward swipe begin event to client
+ wlr_pointer_gestures_v1_send_swipe_begin(
+ pointer_gestures,
+@@ -671,6 +692,11 @@ swipe_update(struct wl_listener *listener, void *data)
+ {
+ struct wlr_pointer_swipe_update_event *event = data;
+
++ swipe_fingers = event->fingers;
++ // Accumulate swipe distance
++ swipe_dx += event->dx;
++ swipe_dy += event->dy;
++
+ // Forward swipe update event to client
+ wlr_pointer_gestures_v1_send_swipe_update(
+ pointer_gestures,
+@@ -681,11 +707,53 @@ swipe_update(struct wl_listener *listener, void *data)
+ );
+ }
+
++int
++ongesture(struct wlr_pointer_swipe_end_event *event)
++{
++ struct wlr_keyboard *keyboard;
++ uint32_t mods;
++ const Gesture *g;
++ unsigned int motion;
++ unsigned int adx = (int)round(fabs(swipe_dx));
++ unsigned int ady = (int)round(fabs(swipe_dy));
++ int handled = 0;
++
++ if (event->cancelled) {
++ return handled;
++ }
++
++ // Require absolute distance movement beyond a small thresh-hold
++ if (adx * adx + ady * ady < abzsquare) {
++ return handled;
++ }
++
++ if (adx > ady) {
++ motion = swipe_dx < 0 ? SWIPE_LEFT : SWIPE_RIGHT;
++ } else {
++ motion = swipe_dy < 0 ? SWIPE_UP : SWIPE_DOWN;
++ }
++
++ keyboard = wlr_seat_get_keyboard(seat);
++ mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
++ for (g = gestures; g < END(gestures); g++) {
++ if (CLEANMASK(mods) == CLEANMASK(g->mod) &&
++ swipe_fingers == g->fingers_count &&
++ motion == g->motion && g->func) {
++ g->func(&g->arg);
++ handled = 1;
++ }
++ }
++ return handled;
++}
++
+ void
+ swipe_end(struct wl_listener *listener, void *data)
+ {
+ struct wlr_pointer_swipe_end_event *event = data;
+
++ // TODO: should we stop here if the event has been handled?
++ ongesture(event);
++
+ // Forward swipe end event to client
+ wlr_pointer_gestures_v1_send_swipe_end(
+ pointer_gestures,
+--
+2.45.1
diff --git a/patches/ipc.patch b/patches/ipc.patch
new file mode 100644
index 0000000..9f989e7
--- /dev/null
+++ b/patches/ipc.patch
@@ -0,0 +1,597 @@
+From 6c6d655b68770ce82a24fde9b58c4d97b672553a Mon Sep 17 00:00:00 2001
+From: choc <notchoc@proton.me>
+Date: Mon, 23 Oct 2023 10:35:17 +0800
+Subject: [PATCH] implement dwl-ipc-unstable-v2
+ https://codeberg.org/dwl/dwl-patches/wiki/ipc
+
+---
+ Makefile | 14 +-
+ config.def.h | 1 +
+ dwl.c | 257 ++++++++++++++++++++++++++----
+ protocols/dwl-ipc-unstable-v2.xml | 181 +++++++++++++++++++++
+ 4 files changed, 419 insertions(+), 34 deletions(-)
+ create mode 100644 protocols/dwl-ipc-unstable-v2.xml
+
+diff --git a/Makefile b/Makefile
+index 8db7409..a79a080 100644
+--- a/Makefile
++++ b/Makefile
+@@ -17,12 +17,14 @@ DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEV
+ LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS)
+
+ all: dwl
+-dwl: dwl.o util.o
+- $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
++dwl: dwl.o util.o dwl-ipc-unstable-v2-protocol.o
++ $(CC) dwl.o util.o dwl-ipc-unstable-v2-protocol.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
+ dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
+ pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
+- wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h
++ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \
++ dwl-ipc-unstable-v2-protocol.h
+ util.o: util.c util.h
++dwl-ipc-unstable-v2-protocol.o: dwl-ipc-unstable-v2-protocol.c dwl-ipc-unstable-v2-protocol.h
+
+ # wayland-scanner is a tool which generates C headers and rigging for Wayland
+ # protocols, which are specified in XML. wlroots requires you to rig these up
+@@ -45,6 +47,12 @@ wlr-output-power-management-unstable-v1-protocol.h:
+ xdg-shell-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
++dwl-ipc-unstable-v2-protocol.h:
++ $(WAYLAND_SCANNER) server-header \
++ protocols/dwl-ipc-unstable-v2.xml $@
++dwl-ipc-unstable-v2-protocol.c:
++ $(WAYLAND_SCANNER) private-code \
++ protocols/dwl-ipc-unstable-v2.xml $@
+
+ config.h:
+ cp config.def.h $@
+diff --git a/config.def.h b/config.def.h
+index 22d2171..1593033 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -127,6 +127,7 @@ static const Key keys[] = {
+ /* modifier key function argument */
+ { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
++ { MODKEY, XKB_KEY_b, togglebar, {0} },
+ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
+ { MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
+ { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
+diff --git a/dwl.c b/dwl.c
+index 8a587d1..7a4949b 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -68,6 +68,7 @@
+ #include <xcb/xcb_icccm.h>
+ #endif
+
++#include "dwl-ipc-unstable-v2-protocol.h"
+ #include "util.h"
+
+ /* macros */
+@@ -144,6 +145,12 @@ typedef struct {
+ uint32_t resize; /* configure serial of a pending resize */
+ } Client;
+
++typedef struct {
++ struct wl_list link;
++ struct wl_resource *resource;
++ Monitor *mon;
++} DwlIpcOutput;
++
+ typedef struct {
+ uint32_t mod;
+ xkb_keysym_t keysym;
+@@ -189,6 +196,7 @@ typedef struct {
+
+ struct Monitor {
+ struct wl_list link;
++ struct wl_list dwl_ipc_outputs;
+ struct wlr_output *wlr_output;
+ struct wlr_scene_output *scene_output;
+ struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */
+@@ -286,6 +294,17 @@ static void destroysessionlock(struct wl_listener *listener, void *data);
+ static void destroysessionmgr(struct wl_listener *listener, void *data);
+ static void destroykeyboardgroup(struct wl_listener *listener, void *data);
+ static Monitor *dirtomon(enum wlr_direction dir);
++static void dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id);
++static void dwl_ipc_manager_destroy(struct wl_resource *resource);
++static void dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output);
++static void dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource);
++static void dwl_ipc_output_destroy(struct wl_resource *resource);
++static void dwl_ipc_output_printstatus(Monitor *monitor);
++static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output);
++static void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags);
++static void dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index);
++static void dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset);
++static void dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource);
+ static void focusclient(Client *c, int lift);
+ static void focusmon(const Arg *arg);
+ static void focusstack(const Arg *arg);
+@@ -338,6 +357,7 @@ static void startdrag(struct wl_listener *listener, void *data);
+ static void tag(const Arg *arg);
+ static void tagmon(const Arg *arg);
+ static void tile(Monitor *m);
++static void togglebar(const Arg *arg);
+ static void togglefloating(const Arg *arg);
+ static void togglefullscreen(const Arg *arg);
+ static void toggletag(const Arg *arg);
+@@ -411,6 +431,9 @@ static struct wlr_box sgeom;
+ static struct wl_list mons;
+ static Monitor *selmon;
+
++static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = {.release = dwl_ipc_manager_release, .get_output = dwl_ipc_manager_get_output};
++static struct zdwl_ipc_output_v2_interface dwl_output_implementation = {.release = dwl_ipc_output_release, .set_tags = dwl_ipc_output_set_tags, .set_layout = dwl_ipc_output_set_layout, .set_client_tags = dwl_ipc_output_set_client_tags};
++
+ #ifdef XWAYLAND
+ static void activatex11(struct wl_listener *listener, void *data);
+ static void associatex11(struct wl_listener *listener, void *data);
+@@ -703,6 +726,10 @@ cleanupmon(struct wl_listener *listener, void *data)
+ LayerSurface *l, *tmp;
+ size_t i;
+
++ DwlIpcOutput *ipc_output, *ipc_output_tmp;
++ wl_list_for_each_safe(ipc_output, ipc_output_tmp, &m->dwl_ipc_outputs, link)
++ wl_resource_destroy(ipc_output->resource);
++
+ /* m->layers[i] are intentionally not unlinked */
+ for (i = 0; i < LENGTH(m->layers); i++) {
+ wl_list_for_each_safe(l, tmp, &m->layers[i], link)
+@@ -983,6 +1010,8 @@ createmon(struct wl_listener *listener, void *data)
+ m = wlr_output->data = ecalloc(1, sizeof(*m));
+ m->wlr_output = wlr_output;
+
++ wl_list_init(&m->dwl_ipc_outputs);
++
+ for (i = 0; i < LENGTH(m->layers); i++)
+ wl_list_init(&m->layers[i]);
+
+@@ -1334,6 +1363,192 @@ dirtomon(enum wlr_direction dir)
+ return selmon;
+ }
+
++void
++dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
++{
++ struct wl_resource *manager_resource = wl_resource_create(client, &zdwl_ipc_manager_v2_interface, version, id);
++ if (!manager_resource) {
++ wl_client_post_no_memory(client);
++ return;
++ }
++ wl_resource_set_implementation(manager_resource, &dwl_manager_implementation, NULL, dwl_ipc_manager_destroy);
++
++ zdwl_ipc_manager_v2_send_tags(manager_resource, TAGCOUNT);
++
++ for (unsigned int i = 0; i < LENGTH(layouts); i++)
++ zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol);
++}
++
++void
++dwl_ipc_manager_destroy(struct wl_resource *resource)
++{
++ /* No state to destroy */
++}
++
++void
++dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output)
++{
++ DwlIpcOutput *ipc_output;
++ Monitor *monitor = wlr_output_from_resource(output)->data;
++ struct wl_resource *output_resource = wl_resource_create(client, &zdwl_ipc_output_v2_interface, wl_resource_get_version(resource), id);
++ if (!output_resource)
++ return;
++
++ ipc_output = ecalloc(1, sizeof(*ipc_output));
++ ipc_output->resource = output_resource;
++ ipc_output->mon = monitor;
++ wl_resource_set_implementation(output_resource, &dwl_output_implementation, ipc_output, dwl_ipc_output_destroy);
++ wl_list_insert(&monitor->dwl_ipc_outputs, &ipc_output->link);
++ dwl_ipc_output_printstatus_to(ipc_output);
++}
++
++void
++dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource)
++{
++ wl_resource_destroy(resource);
++}
++
++static void
++dwl_ipc_output_destroy(struct wl_resource *resource)
++{
++ DwlIpcOutput *ipc_output = wl_resource_get_user_data(resource);
++ wl_list_remove(&ipc_output->link);
++ free(ipc_output);
++}
++
++void
++dwl_ipc_output_printstatus(Monitor *monitor)
++{
++ DwlIpcOutput *ipc_output;
++ wl_list_for_each(ipc_output, &monitor->dwl_ipc_outputs, link)
++ dwl_ipc_output_printstatus_to(ipc_output);
++}
++
++void
++dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output)
++{
++ Monitor *monitor = ipc_output->mon;
++ Client *c, *focused;
++ int tagmask, state, numclients, focused_client, tag;
++ const char *title, *appid;
++ focused = focustop(monitor);
++ zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon);
++
++ for (tag = 0 ; tag < TAGCOUNT; tag++) {
++ numclients = state = focused_client = 0;
++ tagmask = 1 << tag;
++ if ((tagmask & monitor->tagset[monitor->seltags]) != 0)
++ state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE;
++
++ wl_list_for_each(c, &clients, link) {
++ if (c->mon != monitor)
++ continue;
++ if (!(c->tags & tagmask))
++ continue;
++ if (c == focused)
++ focused_client = 1;
++ if (c->isurgent)
++ state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT;
++
++ numclients++;
++ }
++ zdwl_ipc_output_v2_send_tag(ipc_output->resource, tag, state, numclients, focused_client);
++ }
++ title = focused ? client_get_title(focused) : "";
++ appid = focused ? client_get_appid(focused) : "";
++
++ zdwl_ipc_output_v2_send_layout(ipc_output->resource, monitor->lt[monitor->sellt] - layouts);
++ zdwl_ipc_output_v2_send_title(ipc_output->resource, title);
++ zdwl_ipc_output_v2_send_appid(ipc_output->resource, appid);
++ zdwl_ipc_output_v2_send_layout_symbol(ipc_output->resource, monitor->ltsymbol);
++ if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FULLSCREEN_SINCE_VERSION) {
++ zdwl_ipc_output_v2_send_fullscreen(ipc_output->resource, focused ? focused->isfullscreen : 0);
++ }
++ if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FLOATING_SINCE_VERSION) {
++ zdwl_ipc_output_v2_send_floating(ipc_output->resource, focused ? focused->isfloating : 0);
++ }
++ zdwl_ipc_output_v2_send_frame(ipc_output->resource);
++}
++
++void
++dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags)
++{
++ DwlIpcOutput *ipc_output;
++ Monitor *monitor;
++ Client *selected_client;
++ unsigned int newtags = 0;
++
++ ipc_output = wl_resource_get_user_data(resource);
++ if (!ipc_output)
++ return;
++
++ monitor = ipc_output->mon;
++ selected_client = focustop(monitor);
++ if (!selected_client)
++ return;
++
++ newtags = (selected_client->tags & and_tags) ^ xor_tags;
++ if (!newtags)
++ return;
++
++ selected_client->tags = newtags;
++ if (selmon == monitor)
++ focusclient(focustop(monitor), 1);
++ arrange(selmon);
++ printstatus();
++}
++
++void
++dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index)
++{
++ DwlIpcOutput *ipc_output;
++ Monitor *monitor;
++
++ ipc_output = wl_resource_get_user_data(resource);
++ if (!ipc_output)
++ return;
++
++ monitor = ipc_output->mon;
++ if (index >= LENGTH(layouts))
++ return;
++ if (index != monitor->lt[monitor->sellt] - layouts)
++ monitor->sellt ^= 1;
++
++ monitor->lt[monitor->sellt] = &layouts[index];
++ arrange(monitor);
++ printstatus();
++}
++
++void
++dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset)
++{
++ DwlIpcOutput *ipc_output;
++ Monitor *monitor;
++ unsigned int newtags = tagmask & TAGMASK;
++
++ ipc_output = wl_resource_get_user_data(resource);
++ if (!ipc_output)
++ return;
++ monitor = ipc_output->mon;
++
++ if (!newtags || newtags == monitor->tagset[monitor->seltags])
++ return;
++ if (toggle_tagset)
++ monitor->seltags ^= 1;
++
++ monitor->tagset[monitor->seltags] = newtags;
++ if (selmon == monitor)
++ focusclient(focustop(monitor), 1);
++ arrange(monitor);
++ printstatus();
++}
++
++void
++dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource)
++{
++ wl_resource_destroy(resource);
++}
++
+ void
+ focusclient(Client *c, int lift)
+ {
+@@ -2033,38 +2248,9 @@ void
+ printstatus(void)
+ {
+ Monitor *m = NULL;
+- Client *c;
+- uint32_t occ, urg, sel;
+
+- wl_list_for_each(m, &mons, link) {
+- occ = urg = 0;
+- wl_list_for_each(c, &clients, link) {
+- if (c->mon != m)
+- continue;
+- occ |= c->tags;
+- if (c->isurgent)
+- urg |= c->tags;
+- }
+- if ((c = focustop(m))) {
+- printf("%s title %s\n", m->wlr_output->name, client_get_title(c));
+- printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c));
+- printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen);
+- printf("%s floating %d\n", m->wlr_output->name, c->isfloating);
+- sel = c->tags;
+- } else {
+- printf("%s title \n", m->wlr_output->name);
+- printf("%s appid \n", m->wlr_output->name);
+- printf("%s fullscreen \n", m->wlr_output->name);
+- printf("%s floating \n", m->wlr_output->name);
+- sel = 0;
+- }
+-
+- printf("%s selmon %u\n", m->wlr_output->name, m == selmon);
+- printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n",
+- m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg);
+- printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol);
+- }
+- fflush(stdout);
++ wl_list_for_each(m, &mons, link)
++ dwl_ipc_output_printstatus(m);
+ }
+
+ void
+@@ -2584,6 +2770,8 @@ setup(void)
+ LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply);
+ LISTEN_STATIC(&output_mgr->events.test, outputmgrtest);
+
++ wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 2, NULL, dwl_ipc_manager_bind);
++
+ /* Make sure XWayland clients don't connect to the parent X server,
+ * e.g when running in the x11 backend or the wayland backend and the
+ * compositor has Xwayland support */
+@@ -2681,6 +2869,13 @@ tile(Monitor *m)
+ }
+ }
+
++void
++togglebar(const Arg *arg) {
++ DwlIpcOutput *ipc_output;
++ wl_list_for_each(ipc_output, &selmon->dwl_ipc_outputs, link)
++ zdwl_ipc_output_v2_send_toggle_visibility(ipc_output->resource);
++}
++
+ void
+ togglefloating(const Arg *arg)
+ {
+diff --git a/protocols/dwl-ipc-unstable-v2.xml b/protocols/dwl-ipc-unstable-v2.xml
+new file mode 100644
+index 0000000..0a6e7e5
+--- /dev/null
++++ b/protocols/dwl-ipc-unstable-v2.xml
+@@ -0,0 +1,181 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++This is largely ripped from somebar's ipc patchset; just with some personal modifications.
++I would probably just submit raphi's patchset but I don't think that would be polite.
++-->
++<protocol name="dwl_ipc_unstable_v2">
++ <description summary="inter-proccess-communication about dwl's state">
++ This protocol allows clients to update and get updates from dwl.
++
++ Warning! The protocol described in this file is experimental and
++ backward incompatible changes may be made. Backward compatible
++ changes may be added together with the corresponding interface
++ version bump.
++ Backward incompatible changes are done by bumping the version
++ number in the protocol and interface names and resetting the
++ interface version. Once the protocol is to be declared stable,
++ the 'z' prefix and the version number in the protocol and
++ interface names are removed and the interface version number is
++ reset.
++ </description>
++
++ <interface name="zdwl_ipc_manager_v2" version="2">
++ <description summary="manage dwl state">
++ This interface is exposed as a global in wl_registry.
++
++ Clients can use this interface to get a dwl_ipc_output.
++ After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events.
++ The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client.
++ </description>
++
++ <request name="release" type="destructor">
++ <description summary="release dwl_ipc_manager">
++ Indicates that the client will not the dwl_ipc_manager object anymore.
++ Objects created through this instance are not affected.
++ </description>
++ </request>
++
++ <request name="get_output">
++ <description summary="get a dwl_ipc_outout for a wl_output">
++ Get a dwl_ipc_outout for the specified wl_output.
++ </description>
++ <arg name="id" type="new_id" interface="zdwl_ipc_output_v2"/>
++ <arg name="output" type="object" interface="wl_output"/>
++ </request>
++
++ <event name="tags">
++ <description summary="Announces tag amount">
++ This event is sent after binding.
++ A roundtrip after binding guarantees the client recieved all tags.
++ </description>
++ <arg name="amount" type="uint"/>
++ </event>
++
++ <event name="layout">
++ <description summary="Announces a layout">
++ This event is sent after binding.
++ A roundtrip after binding guarantees the client recieved all layouts.
++ </description>
++ <arg name="name" type="string"/>
++ </event>
++ </interface>
++
++ <interface name="zdwl_ipc_output_v2" version="2">
++ <description summary="control dwl output">
++ Observe and control a dwl output.
++
++ Events are double-buffered:
++ Clients should cache events and redraw when a dwl_ipc_output.frame event is sent.
++
++ Request are not double-buffered:
++ The compositor will update immediately upon request.
++ </description>
++
++ <enum name="tag_state">
++ <entry name="none" value="0" summary="no state"/>
++ <entry name="active" value="1" summary="tag is active"/>
++ <entry name="urgent" value="2" summary="tag has at least one urgent client"/>
++ </enum>
++
++ <request name="release" type="destructor">
++ <description summary="release dwl_ipc_outout">
++ Indicates to that the client no longer needs this dwl_ipc_output.
++ </description>
++ </request>
++
++ <event name="toggle_visibility">
++ <description summary="Toggle client visibilty">
++ Indicates the client should hide or show themselves.
++ If the client is visible then hide, if hidden then show.
++ </description>
++ </event>
++
++ <event name="active">
++ <description summary="Update the selected output.">
++ Indicates if the output is active. Zero is invalid, nonzero is valid.
++ </description>
++ <arg name="active" type="uint"/>
++ </event>
++
++ <event name="tag">
++ <description summary="Update the state of a tag.">
++ Indicates that a tag has been updated.
++ </description>
++ <arg name="tag" type="uint" summary="Index of the tag"/>
++ <arg name="state" type="uint" enum="tag_state" summary="The state of the tag."/>
++ <arg name="clients" type="uint" summary="The number of clients in the tag."/>
++ <arg name="focused" type="uint" summary="If there is a focused client. Nonzero being valid, zero being invalid."/>
++ </event>
++
++ <event name="layout">
++ <description summary="Update the layout.">
++ Indicates a new layout is selected.
++ </description>
++ <arg name="layout" type="uint" summary="Index of the layout."/>
++ </event>
++
++ <event name="title">
++ <description summary="Update the title.">
++ Indicates the title has changed.
++ </description>
++ <arg name="title" type="string" summary="The new title name."/>
++ </event>
++
++ <event name="appid" since="1">
++ <description summary="Update the appid.">
++ Indicates the appid has changed.
++ </description>
++ <arg name="appid" type="string" summary="The new appid."/>
++ </event>
++
++ <event name="layout_symbol" since="1">
++ <description summary="Update the current layout symbol">
++ Indicates the layout has changed. Since layout symbols are dynamic.
++ As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying.
++ You can ignore the zdwl_ipc_output.layout event.
++ </description>
++ <arg name="layout" type="string" summary="The new layout"/>
++ </event>
++
++ <event name="frame">
++ <description summary="The update sequence is done.">
++ Indicates that a sequence of status updates have finished and the client should redraw.
++ </description>
++ </event>
++
++ <request name="set_tags">
++ <description summary="Set the active tags of this output"/>
++ <arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/>
++ <arg name="toggle_tagset" type="uint" summary="toggle the selected tagset, zero for invalid, nonzero for valid."/>
++ </request>
++
++ <request name="set_client_tags">
++ <description summary="Set the tags of the focused client.">
++ The tags are updated as follows:
++ new_tags = (current_tags AND and_tags) XOR xor_tags
++ </description>
++ <arg name="and_tags" type="uint"/>
++ <arg name="xor_tags" type="uint"/>
++ </request>
++
++ <request name="set_layout">
++ <description summary="Set the layout of this output"/>
++ <arg name="index" type="uint" summary="index of a layout recieved by dwl_ipc_manager.layout"/>
++ </request>
++
++ <!-- Version 2 -->
++ <event name="fullscreen" since="2">
++ <description summary="Update fullscreen status">
++ Indicates if the selected client on this output is fullscreen.
++ </description>
++ <arg name="is_fullscreen" type="uint" summary="If the selected client is fullscreen. Nonzero is valid, zero invalid"/>
++ </event>
++
++ <event name="floating" since="2">
++ <description summary="Update the floating status">
++ Indicates if the selected client on this output is floating.
++ </description>
++ <arg name="is_floating" type="uint" summary="If the selected client is floating. Nonzero is valid, zero invalid"/>
++ </event>
++ </interface>
++</protocol>
+--
+2.43.0
+
diff --git a/patches/lockedkeys.patch b/patches/lockedkeys.patch
new file mode 100644
index 0000000..f683536
--- /dev/null
+++ b/patches/lockedkeys.patch
@@ -0,0 +1,79 @@
+From 70dc03a3817b8fd933244c2db1bb849d9626b12b Mon Sep 17 00:00:00 2001
+From: wochap <gean.marroquin@gmail.com>
+Date: Thu, 11 Apr 2024 13:16:40 -0500
+Subject: [PATCH] allow to add keybindings in lockscreen
+
+---
+ config.def.h | 11 +++++++++++
+ dwl.c | 20 ++++++++++++++++++++
+ 2 files changed, 31 insertions(+)
+
+diff --git a/config.def.h b/config.def.h
+index 8847e58..0d4a4f8 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -164,6 +164,17 @@ static const Key keys[] = {
+ CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
+ };
+
++static const Key lockedkeys[] = {
++ /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
++ /* modifier key function argument */
++
++ /* 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} },
++#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} },
+diff --git a/dwl.c b/dwl.c
+index bf763df..db4bb2b 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -287,6 +287,7 @@ static void handlesig(int signo);
+ static void incnmaster(const Arg *arg);
+ static void inputdevice(struct wl_listener *listener, void *data);
+ static int keybinding(uint32_t mods, xkb_keysym_t sym);
++static int lockedkeybinding(uint32_t mods, xkb_keysym_t sym);
+ static void keypress(struct wl_listener *listener, void *data);
+ static void keypressmod(struct wl_listener *listener, void *data);
+ static int keyrepeat(void *data);
+@@ -1446,6 +1447,21 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
+ return 0;
+ }
+
++int
++lockedkeybinding(uint32_t mods, xkb_keysym_t sym)
++{
++ int handled = 0;
++ const Key *k;
++ for (k = lockedkeys; k < END(lockedkeys); k++) {
++ if (CLEANMASK(mods) == CLEANMASK(k->mod) &&
++ sym == k->keysym && k->func) {
++ k->func(&k->arg);
++ handled = 1;
++ }
++ }
++ return handled;
++}
++
+ void
+ keypress(struct wl_listener *listener, void *data)
+ {
+@@ -1473,6 +1489,10 @@ keypress(struct wl_listener *listener, void *data)
+ handled = keybinding(mods, syms[i]) || handled;
+ }
+
++ if (locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED)
++ for (i = 0; i < nsyms; i++)
++ handled = lockedkeybinding(mods, syms[i]) || handled;
++
+ if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) {
+ group->mods = mods;
+ group->keysyms = syms;
+--
+2.43.2
diff --git a/patches/main...serenevoid:uselessgaps.patch b/patches/main...serenevoid:uselessgaps.patch
new file mode 100644
index 0000000..4c0944f
--- /dev/null
+++ b/patches/main...serenevoid:uselessgaps.patch
@@ -0,0 +1,137 @@
+From 366a810531ce3425ea5eca4e32eba431dc26570e Mon Sep 17 00:00:00 2001
+From: serenevoid <ajuph9224@gmail.com>
+Date: Sun, 16 Jul 2023 22:07:02 +0530
+Subject: [PATCH] feat(gaps): Useless Gaps
+
+- KISS (Keep it simple and stupid)
+- Use common gap value instead of separate inner and outer gaps
+- Set gap value in config.h and toggle gaps on or off
+- minimal and efficient
+---
+ config.def.h | 3 +++
+ dwl.c | 41 +++++++++++++++++++++++++++++++----------
+ 2 files changed, 34 insertions(+), 10 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 447ba0051..16ad12ce8 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -1,7 +1,9 @@
+ /* 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 int smartgaps = 0; /* 1 means no outer gap when there is only one window */
+ static const unsigned int borderpx = 1; /* border pixel of windows */
++static const unsigned int gappx = 10; /* horiz inner gap between windows */
+ static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0};
+ static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0};
+ /* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */
+@@ -119,6 +121,7 @@ static const Key keys[] = {
+ { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05} },
+ { MODKEY, XKB_KEY_Return, zoom, {0} },
+ { MODKEY, XKB_KEY_Tab, view, {0} },
+++ { MODKEY, XKB_KEY_g, togglegaps, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} },
+ { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
+diff --git a/dwl.c b/dwl.c
+index 93f66efe6..aea07d68c 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -185,6 +185,7 @@ struct Monitor {
+ struct wlr_box w; /* window area, layout-relative */
+ struct wl_list layers[4]; /* LayerSurface::link */
+ const Layout *lt[2];
++ int gappx; /* horizontal outer gaps */
+ unsigned int seltags;
+ unsigned int sellt;
+ uint32_t tagset[2];
+@@ -304,6 +305,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 togglegaps(const Arg *arg);
+ static void toggletag(const Arg *arg);
+ static void toggleview(const Arg *arg);
+ static void unlocksession(struct wl_listener *listener, void *data);
+@@ -367,6 +369,8 @@ static struct wlr_box sgeom;
+ static struct wl_list mons;
+ static Monitor *selmon;
+
++static int enablegaps = 1; /* enables gaps, used by togglegaps */
++
+ /* global event handlers */
+ static struct wl_listener cursor_axis = {.notify = axisnotify};
+ static struct wl_listener cursor_button = {.notify = buttonpress};
+@@ -913,6 +917,7 @@ createmon(struct wl_listener *listener, void *data)
+ /* Initialize monitor state using configured rules */
+ for (i = 0; i < LENGTH(m->layers); i++)
+ wl_list_init(&m->layers[i]);
++ m->gappx = gappx;
+ m->tagset[0] = m->tagset[1] = 1;
+ for (r = monrules; r < END(monrules); r++) {
+ if (!r->name || strstr(wlr_output->name, r->name)) {
+@@ -2361,7 +2366,7 @@ tagmon(const Arg *arg)
+ void
+ tile(Monitor *m)
+ {
+- unsigned int i, n = 0, mw, my, ty;
++ unsigned int i, n = 0, h, r, e = enablegaps, mw, my, ty;
+ Client *c;
+
+ wl_list_for_each(c, &clients, link)
+@@ -2370,22 +2375,31 @@ tile(Monitor *m)
+ if (n == 0)
+ return;
+
++ if (smartgaps == n) {
++ e = 0; // outer gaps disabled
++ }
++
+ if (n > m->nmaster)
+- mw = m->nmaster ? m->w.width * m->mfact : 0;
++ mw = m->nmaster ? (m->w.width + m->gappx*e) * m->mfact : 0;
+ else
+- mw = m->w.width;
+- i = my = ty = 0;
++ mw = m->w.width - 2*m->gappx*e + m->gappx*e;
++ i = 0;
++ my = ty = m->gappx*e;
+ wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ continue;
+ if (i < m->nmaster) {
+- resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw,
+- .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0);
+- my += c->geom.height;
++ r = MIN(n, m->nmaster) - i;
++ h = (m->w.height - my - m->gappx*e - m->gappx*e * (r - 1)) / r;
++ resize(c, (struct wlr_box){.x = m->w.x + m->gappx*e, .y = m->w.y + my,
++ .width = mw - m->gappx*e, .height = h}, 0);
++ my += c->geom.height + m->gappx*e;
+ } else {
+- resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty,
+- .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0);
+- ty += c->geom.height;
++ r = n - i;
++ h = (m->w.height - ty - m->gappx*e - m->gappx*e * (r - 1)) / r;
++ resize(c, (struct wlr_box){.x = m->w.x + mw + m->gappx*e, .y = m->w.y + ty,
++ .width = m->w.width - mw - 2*m->gappx*e, .height = h}, 0);
++ ty += c->geom.height + m->gappx*e;
+ }
+ i++;
+ }
+@@ -2408,6 +2422,13 @@ togglefullscreen(const Arg *arg)
+ setfullscreen(sel, !sel->isfullscreen);
+ }
+
++void
++togglegaps(const Arg *arg)
++{
++ enablegaps = !enablegaps;
++ arrange(selmon);
++}
++
+ void
+ toggletag(const Arg *arg)
+ {
diff --git a/patches/passthrough.patch b/patches/passthrough.patch
new file mode 100644
index 0000000..d87f00e
--- /dev/null
+++ b/patches/passthrough.patch
@@ -0,0 +1,82 @@
+From cd67c8386b0188daa15348c1d0d99187a556e461 Mon Sep 17 00:00:00 2001
+From: choc <notchoc@proton.me>
+Date: Mon, 2 Jan 2023 13:00:29 +0800
+Subject: [PATCH] passthrough: allow pausing keybind handling
+
+also allows for bitcarrying-esque control of nested instances
+---
+ config.def.h | 4 ++++
+ dwl.c | 14 ++++++++++++++
+ 2 files changed, 18 insertions(+)
+
+diff --git a/config.def.h b/config.def.h
+index 646a3d6..2d14e2a 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -20,6 +20,9 @@ static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You ca
+ /* logging */
+ static int log_level = WLR_ERROR;
+
++/* passthrough */
++static int passthrough = 0;
++
+ static const Rule rules[] = {
+ /* app_id title tags mask isfloating monitor */
+ /* examples: */
+@@ -156,6 +159,7 @@ static const Key keys[] = {
+ TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
+ TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
+ TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
++ { WLR_MODIFIER_ALT|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT, XKB_KEY_Escape, togglepassthrough, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} },
+
+ /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
+diff --git a/dwl.c b/dwl.c
+index 9fb50a7..a1c65b4 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -339,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 togglepassthrough(const Arg *arg);
+ static void toggletag(const Arg *arg);
+ static void toggleview(const Arg *arg);
+ static void unlocksession(struct wl_listener *listener, void *data);
+@@ -620,6 +621,11 @@ buttonpress(struct wl_listener *listener, void *data)
+ for (b = buttons; b < END(buttons); b++) {
+ if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
+ event->button == b->button && b->func) {
++ if (passthrough) {
++ if (b->func != togglepassthrough) continue;
++ b->func(&b->arg);
++ break;
++ }
+ b->func(&b->arg);
+ return;
+ }
+@@ -1509,6 +1515,8 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
+ for (k = keys; k < END(keys); k++) {
+ if (CLEANMASK(mods) == CLEANMASK(k->mod)
+ && sym == k->keysym && k->func) {
++ if (passthrough && k->func != togglepassthrough)
++ continue;
+ k->func(&k->arg);
+ return 1;
+ }
+@@ -2677,6 +2685,12 @@ togglefullscreen(const Arg *arg)
+ setfullscreen(sel, !sel->isfullscreen);
+ }
+
++void
++togglepassthrough(const Arg *arg)
++{
++ passthrough = !passthrough;
++}
++
+ void
+ toggletag(const Arg *arg)
+ {
+--
+2.43.0
+
diff --git a/patches/pointer-gestures-unstable-v1.patch b/patches/pointer-gestures-unstable-v1.patch
new file mode 100644
index 0000000..f76648a
--- /dev/null
+++ b/patches/pointer-gestures-unstable-v1.patch
@@ -0,0 +1,186 @@
+From be7e98d28fc59aab67026e7d5efdcaeb26029713 Mon Sep 17 00:00:00 2001
+From: wochap <gean.marroquin@gmail.com>
+Date: Fri, 12 Jul 2024 11:30:17 -0500
+Subject: [PATCH] implement pointer-gestures-unstable-v1
+
+---
+ dwl.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 136 insertions(+)
+
+diff --git a/dwl.c b/dwl.c
+index dc0437e..e5805b1 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -38,6 +38,7 @@
+ #include <wlr/types/wlr_output_power_management_v1.h>
+ #include <wlr/types/wlr_pointer.h>
+ #include <wlr/types/wlr_pointer_constraints_v1.h>
++#include <wlr/types/wlr_pointer_gestures_v1.h>
+ #include <wlr/types/wlr_presentation_time.h>
+ #include <wlr/types/wlr_primary_selection.h>
+ #include <wlr/types/wlr_primary_selection_v1.h>
+@@ -250,6 +251,14 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
+ static void arrangelayers(Monitor *m);
+ static void axisnotify(struct wl_listener *listener, void *data);
+ static void buttonpress(struct wl_listener *listener, void *data);
++static void swipe_begin(struct wl_listener *listener, void *data);
++static void swipe_update(struct wl_listener *listener, void *data);
++static void swipe_end(struct wl_listener *listener, void *data);
++static void pinch_begin(struct wl_listener *listener, void *data);
++static void pinch_update(struct wl_listener *listener, void *data);
++static void pinch_end(struct wl_listener *listener, void *data);
++static void hold_begin(struct wl_listener *listener, void *data);
++static void hold_end(struct wl_listener *listener, void *data);
+ static void chvt(const Arg *arg);
+ static void checkidleinhibitor(struct wlr_surface *exclude);
+ static void cleanup(void);
+@@ -383,6 +392,7 @@ static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr;
+ static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr;
+ static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr;
+ static struct wlr_output_power_manager_v1 *power_mgr;
++static struct wlr_pointer_gestures_v1 *pointer_gestures;
+
+ static struct wlr_pointer_constraints_v1 *pointer_constraints;
+ static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr;
+@@ -644,6 +654,122 @@ buttonpress(struct wl_listener *listener, void *data)
+ event->time_msec, event->button, event->state);
+ }
+
++void
++swipe_begin(struct wl_listener *listener, void *data)
++{
++ struct wlr_pointer_swipe_begin_event *event = data;
++
++ // Forward swipe begin event to client
++ wlr_pointer_gestures_v1_send_swipe_begin(
++ pointer_gestures,
++ seat,
++ event->time_msec,
++ event->fingers
++ );
++}
++
++void
++swipe_update(struct wl_listener *listener, void *data)
++{
++ struct wlr_pointer_swipe_update_event *event = data;
++
++ // Forward swipe update event to client
++ wlr_pointer_gestures_v1_send_swipe_update(
++ pointer_gestures,
++ seat,
++ event->time_msec,
++ event->dx,
++ event->dy
++ );
++}
++
++void
++swipe_end(struct wl_listener *listener, void *data)
++{
++ struct wlr_pointer_swipe_end_event *event = data;
++
++ // Forward swipe end event to client
++ wlr_pointer_gestures_v1_send_swipe_end(
++ pointer_gestures,
++ seat,
++ event->time_msec,
++ event->cancelled
++ );
++}
++
++void
++pinch_begin(struct wl_listener *listener, void *data)
++{
++ struct wlr_pointer_pinch_begin_event *event = data;
++
++ // Forward pinch begin event to client
++ wlr_pointer_gestures_v1_send_pinch_begin(
++ pointer_gestures,
++ seat,
++ event->time_msec,
++ event->fingers
++ );
++}
++
++void
++pinch_update(struct wl_listener *listener, void *data)
++{
++ struct wlr_pointer_pinch_update_event *event = data;
++
++ // Forward pinch update event to client
++ wlr_pointer_gestures_v1_send_pinch_update(
++ pointer_gestures,
++ seat,
++ event->time_msec,
++ event->dx,
++ event->dy,
++ event->scale,
++ event->rotation
++ );
++}
++
++void
++pinch_end(struct wl_listener *listener, void *data)
++{
++ struct wlr_pointer_pinch_end_event *event = data;
++
++ // Forward pinch end event to client
++ wlr_pointer_gestures_v1_send_pinch_end(
++ pointer_gestures,
++ seat,
++ event->time_msec,
++ event->cancelled
++ );
++}
++
++void
++hold_begin(struct wl_listener *listener, void *data)
++{
++ struct wlr_pointer_hold_begin_event *event = data;
++
++ // Forward hold begin event to client
++ wlr_pointer_gestures_v1_send_hold_begin(
++ pointer_gestures,
++ seat,
++ event->time_msec,
++ event->fingers
++ );
++}
++
++void
++hold_end(struct wl_listener *listener, void *data)
++{
++ struct wlr_pointer_hold_end_event *event = data;
++
++ // Forward hold end event to client
++ wlr_pointer_gestures_v1_send_hold_end(
++ pointer_gestures,
++ seat,
++ event->time_msec,
++ event->cancelled
++ );
++}
++
+ void
+ chvt(const Arg *arg)
+ {
+@@ -2556,6 +2682,16 @@ setup(void)
+ virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy);
+ LISTEN_STATIC(&virtual_pointer_mgr->events.new_virtual_pointer, virtualpointer);
+
++ pointer_gestures = wlr_pointer_gestures_v1_create(dpy);
++ LISTEN_STATIC(&cursor->events.swipe_begin, swipe_begin);
++ LISTEN_STATIC(&cursor->events.swipe_update, swipe_update);
++ LISTEN_STATIC(&cursor->events.swipe_end, swipe_end);
++ LISTEN_STATIC(&cursor->events.pinch_begin, pinch_begin);
++ LISTEN_STATIC(&cursor->events.pinch_update, pinch_update);
++ LISTEN_STATIC(&cursor->events.pinch_end, pinch_end);
++ LISTEN_STATIC(&cursor->events.hold_begin, hold_begin);
++ LISTEN_STATIC(&cursor->events.hold_end, hold_end);
++
+ seat = wlr_seat_create(dpy, "seat0");
+ LISTEN_STATIC(&seat->events.request_set_cursor, setcursor);
+ LISTEN_STATIC(&seat->events.request_set_selection, setsel);
+--
+2.45.1
diff --git a/patches/regexrules.patch b/patches/regexrules.patch
new file mode 100644
index 0000000..f7207d3
--- /dev/null
+++ b/patches/regexrules.patch
@@ -0,0 +1,76 @@
+From 7fed9449575b1e4f58d519d2f87b7e66e2056125 Mon Sep 17 00:00:00 2001
+From: wochap <gean.marroquin@gmail.com>
+Date: Thu, 11 Apr 2024 12:45:47 -0500
+Subject: [PATCH] implement regex support in rules for app_id and title Enables
+ the use of regular expressions for window rules "app_id" and "title"
+
+---
+ config.def.h | 1 +
+ dwl.c | 19 +++++++++++++++++--
+ 2 files changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 8847e58..89f5b60 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -25,6 +25,7 @@ static const Rule rules[] = {
+ /* examples: */
+ { "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" */
++ { "^kitty_EXAMPLE$", NULL, 0, 0, -1 },
+ };
+
+ /* layout(s) */
+diff --git a/dwl.c b/dwl.c
+index bf763df..fc185af 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -10,6 +10,7 @@
+ #include <sys/wait.h>
+ #include <time.h>
+ #include <unistd.h>
++#include <regex.h>
+ #include <wayland-server-core.h>
+ #include <wlr/backend.h>
+ #include <wlr/backend/libinput.h>
+@@ -347,6 +348,7 @@ static Monitor *xytomon(double x, double y);
+ static void xytonode(double x, double y, struct wlr_surface **psurface,
+ Client **pc, LayerSurface **pl, double *nx, double *ny);
+ static void zoom(const Arg *arg);
++static int regex_match(const char *pattern, const char *str);
+
+ /* variables */
+ static const char broken[] = "broken";
+@@ -459,8 +461,8 @@ applyrules(Client *c)
+ title = broken;
+
+ for (r = rules; r < END(rules); r++) {
+- if ((!r->title || strstr(title, r->title))
+- && (!r->id || strstr(appid, r->id))) {
++ if ((!r->title || regex_match(r->title, title))
++ && (!r->id || regex_match(r->id, appid))) {
+ c->isfloating = r->isfloating;
+ newtags |= r->tags;
+ i = 0;
+@@ -2929,6 +2931,19 @@ zoom(const Arg *arg)
+ arrange(selmon);
+ }
+
++int
++regex_match(const char *pattern, const char *str) {
++ regex_t regex;
++ int reti;
++ if (regcomp(&regex, pattern, REG_EXTENDED) != 0)
++ return 0;
++ reti = regexec(&regex, str, (size_t)0, NULL, 0);
++ regfree(&regex);
++ if (reti == 0)
++ return 1;
++ return 0;
++}
++
+ #ifdef XWAYLAND
+ void
+ activatex11(struct wl_listener *listener, void *data)
+--
+2.43.2
diff --git a/patches/swallow.patch b/patches/swallow.patch
new file mode 100644
index 0000000..b4581c4
--- /dev/null
+++ b/patches/swallow.patch
@@ -0,0 +1,240 @@
+From 4b80c425c9f414bc079a0e61f5a3ef42eea85476 Mon Sep 17 00:00:00 2001
+From: choc <notchoc@proton.me>
+Date: Fri, 15 Sep 2023 10:36:21 +0800
+Subject: [PATCH] implement swallow
+
+---
+ client.h | 12 ++++++
+ config.def.h | 7 ++--
+ dwl.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++---
+ 3 files changed, 123 insertions(+), 8 deletions(-)
+
+diff --git a/client.h b/client.h
+index 42f225f..bc9cad2 100644
+--- a/client.h
++++ b/client.h
+@@ -131,6 +131,18 @@ client_get_appid(Client *c)
+ return c->surface.xdg->toplevel->app_id;
+ }
+
++static inline int
++client_get_pid(Client *c)
++{
++ pid_t pid;
++#ifdef XWAYLAND
++ if (client_is_x11(c))
++ return c->surface.xwayland->pid;
++#endif
++ wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL);
++ return pid;
++}
++
+ static inline void
+ client_get_clip(Client *c, struct wlr_box *clip)
+ {
+diff --git a/config.def.h b/config.def.h
+index 22d2171..7e5fef1 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -22,10 +22,11 @@ static int log_level = WLR_ERROR;
+
+ /* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */
+ static const Rule rules[] = {
+- /* app_id title tags mask isfloating monitor */
++ /* app_id title tags mask isfloating isterm noswallow monitor */
+ /* examples: */
+- { "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" */
++ { "Gimp_EXAMPLE", NULL, 0, 1, 0, 0, -1 }, /* Start on currently visible tags floating, not tiled */
++ { "firefox_EXAMPLE", NULL, 1 << 8, 0, 0, 0, -1 }, /* Start on ONLY tag "9" */
++ { "foot", NULL, 0, 0, 1, 1, -1 }, /* make foot swallow clients that are not foot */
+ };
+
+ /* layout(s) */
+diff --git a/dwl.c b/dwl.c
+index dc0437e..c6a5e9d 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -103,7 +103,8 @@ typedef struct {
+ } Button;
+
+ typedef struct Monitor Monitor;
+-typedef struct {
++typedef struct Client Client;
++struct Client {
+ /* Must keep these three elements in this order */
+ unsigned int type; /* XDGShell or X11* */
+ struct wlr_box geom; /* layout-relative, includes border */
+@@ -138,9 +139,11 @@ typedef struct {
+ #endif
+ unsigned int bw;
+ uint32_t tags;
+- int isfloating, isurgent, isfullscreen;
++ int isfloating, isurgent, isfullscreen, isterm, noswallow;
+ uint32_t resize; /* configure serial of a pending resize */
+-} Client;
++ pid_t pid;
++ Client *swallowing, *swallowedby;
++};
+
+ typedef struct {
+ uint32_t mod;
+@@ -229,6 +232,8 @@ typedef struct {
+ const char *title;
+ uint32_t tags;
+ int isfloating;
++ int isterm;
++ int noswallow;
+ int monitor;
+ } Rule;
+
+@@ -351,6 +356,10 @@ static Monitor *xytomon(double x, double y);
+ static void xytonode(double x, double y, struct wlr_surface **psurface,
+ Client **pc, LayerSurface **pl, double *nx, double *ny);
+ static void zoom(const Arg *arg);
++static pid_t getparentprocess(pid_t p);
++static int isdescprocess(pid_t p, pid_t c);
++static Client *termforwin(Client *w);
++static void swallow(Client *c, Client *w);
+
+ /* variables */
+ static const char broken[] = "broken";
+@@ -461,10 +470,14 @@ applyrules(Client *c)
+ if (!(title = client_get_title(c)))
+ title = broken;
+
++ c->pid = client_get_pid(c);
++
+ for (r = rules; r < END(rules); r++) {
+ if ((!r->title || strstr(title, r->title))
+ && (!r->id || strstr(appid, r->id))) {
+ c->isfloating = r->isfloating;
++ c->isterm = r->isterm;
++ c->noswallow = r->noswallow;
+ newtags |= r->tags;
+ i = 0;
+ wl_list_for_each(m, &mons, link) {
+@@ -473,6 +486,21 @@ applyrules(Client *c)
+ }
+ }
+ }
++ if (!c->noswallow && !client_is_float_type(c)
++ && !c->surface.xdg->initial_commit) {
++ Client *p = termforwin(c);
++ if (p) {
++ c->swallowedby = p;
++ p->swallowing = c;
++ wl_list_remove(&c->link);
++ wl_list_remove(&c->flink);
++ swallow(c, p);
++ wl_list_remove(&p->link);
++ wl_list_remove(&p->flink);
++ mon = p->mon;
++ newtags = p->tags;
++ }
++ }
+ setmon(c, mon, newtags);
+ }
+
+@@ -1467,6 +1495,63 @@ handlesig(int signo)
+ }
+ }
+
++pid_t
++getparentprocess(pid_t p)
++{
++ unsigned int v = 0;
++
++ FILE *f;
++ char buf[256];
++ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p);
++
++ if (!(f = fopen(buf, "r")))
++ return 0;
++
++ fscanf(f, "%*u %*s %*c %u", &v);
++ fclose(f);
++
++ return (pid_t)v;
++}
++
++int
++isdescprocess(pid_t p, pid_t c)
++{
++ while (p != c && c != 0)
++ c = getparentprocess(c);
++
++ return (int)c;
++}
++
++Client *
++termforwin(Client *w)
++{
++ Client *c;
++
++ if (!w->pid || w->isterm || w->noswallow)
++ return NULL;
++
++ wl_list_for_each(c, &fstack, flink)
++ if (c->isterm && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid))
++ return c;
++
++ return NULL;
++}
++
++void
++swallow(Client *c, Client *w)
++{
++ c->bw = w->bw;
++ c->isfloating = w->isfloating;
++ c->isurgent = w->isurgent;
++ c->isfullscreen = w->isfullscreen;
++ c->tags = w->tags;
++ c->geom = w->geom;
++ wl_list_insert(&w->link, &c->link);
++ wl_list_insert(&w->flink, &c->flink);
++ wlr_scene_node_set_enabled(&w->scene->node, 0);
++ wlr_scene_node_set_enabled(&c->scene->node, 1);
++}
++
+ void
+ incnmaster(const Arg *arg)
+ {
+@@ -2746,15 +2831,32 @@ unmapnotify(struct wl_listener *listener, void *data)
+ grabc = NULL;
+ }
+
++ if (c->swallowedby)
++ swallow(c->swallowedby, c);
++
+ if (client_is_unmanaged(c)) {
+ if (c == exclusive_focus) {
+ exclusive_focus = NULL;
+ focusclient(focustop(selmon), 1);
+ }
+ } else {
+- wl_list_remove(&c->link);
++ if (!c->swallowing)
++ wl_list_remove(&c->link);
+ setmon(c, NULL, 0);
+- wl_list_remove(&c->flink);
++ if (!c->swallowing)
++ wl_list_remove(&c->flink);
++ }
++
++ if (c->swallowedby) {
++ c->swallowedby->prev = c->geom;
++ setfullscreen(c->swallowedby, c->isfullscreen);
++ c->swallowedby->swallowing = NULL;
++ c->swallowedby = NULL;
++ }
++
++ if (c->swallowing) {
++ c->swallowing->swallowedby = NULL;
++ c->swallowing = NULL;
+ }
+
+ wlr_scene_node_destroy(&c->scene->node);
+--
+2.43.0
+
diff --git a/patches/tearing.patch b/patches/tearing.patch
new file mode 100644
index 0000000..951d25c
--- /dev/null
+++ b/patches/tearing.patch
@@ -0,0 +1,356 @@
+From 66b2e1646bee8502a3715403c165015bd019438c Mon Sep 17 00:00:00 2001
+From: korei999 <ju7t1xe@gmail.com>
+Date: Wed, 18 Sep 2024 20:11:22 +0300
+Subject: [PATCH] implement tearing protocol
+
+---
+ Makefile | 5 +-
+ config.def.h | 8 +++
+ dwl.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++----
+ 3 files changed, 182 insertions(+), 15 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 8db7409..6edc7d7 100644
+--- a/Makefile
++++ b/Makefile
+@@ -21,7 +21,7 @@ dwl: dwl.o util.o
+ $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
+ dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
+ pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
+- wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h
++ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h tearing-control-v1-protocol.h
+ util.o: util.c util.h
+
+ # wayland-scanner is a tool which generates C headers and rigging for Wayland
+@@ -45,6 +45,9 @@ wlr-output-power-management-unstable-v1-protocol.h:
+ xdg-shell-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
++tearing-control-v1-protocol.h:
++ $(WAYLAND_SCANNER) server-header \
++ $(WAYLAND_PROTOCOLS)/staging/tearing-control/tearing-control-v1.xml $@
+
+ config.h:
+ cp config.def.h $@
+diff --git a/config.def.h b/config.def.h
+index 22d2171..52d38d3 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -28,6 +28,14 @@ static const Rule rules[] = {
+ { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */
+ };
+
++/* tearing */
++static int tearing_allowed = 1;
++static const ForceTearingRule force_tearing[] = {
++ {.title = "", .appid = "hl_linux"},
++ {.title = "Warcraft III", .appid = ""},
++ {.title = "", .appid = "gamescope"},
++};
++
+ /* layout(s) */
+ static const Layout layouts[] = {
+ /* symbol arrange function */
+diff --git a/dwl.c b/dwl.c
+index dc0c861..44be1bf 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -51,6 +51,7 @@
+ #include <wlr/types/wlr_session_lock_v1.h>
+ #include <wlr/types/wlr_single_pixel_buffer_v1.h>
+ #include <wlr/types/wlr_subcompositor.h>
++#include <wlr/types/wlr_tearing_control_v1.h>
+ #include <wlr/types/wlr_viewporter.h>
+ #include <wlr/types/wlr_virtual_keyboard_v1.h>
+ #include <wlr/types/wlr_virtual_pointer_v1.h>
+@@ -90,6 +91,11 @@ enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar,
+ NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */
+ #endif
+
++typedef struct ForceTearingRule {
++ const char* title;
++ const char* appid;
++} ForceTearingRule;
++
+ typedef union {
+ int i;
+ uint32_t ui;
+@@ -143,6 +149,7 @@ typedef struct {
+ uint32_t tags;
+ int isfloating, isurgent, isfullscreen;
+ uint32_t resize; /* configure serial of a pending resize */
++ enum wp_tearing_control_v1_presentation_hint tearing_hint;
+ } Client;
+
+ typedef struct {
+@@ -243,6 +250,19 @@ typedef struct {
+ struct wl_listener destroy;
+ } SessionLock;
+
++typedef struct TearingController {
++ struct wlr_tearing_control_v1 *tearing_control;
++ struct wl_listener set_hint;
++ struct wl_listener destroy;
++
++ struct wl_list link; /* tearing_controllers */
++} TearingController;
++
++typedef struct SendFrameDoneData {
++ struct timespec when;
++ struct Monitor *mon;
++} SendFrameDoneData;
++
+ /* function declarations */
+ static void applybounds(Client *c, struct wlr_box *bbox);
+ static void applyrules(Client *c);
+@@ -293,6 +313,9 @@ static Client *focustop(Monitor *m);
+ static void fullscreennotify(struct wl_listener *listener, void *data);
+ static void gpureset(struct wl_listener *listener, void *data);
+ static void handlesig(int signo);
++static void handletearingcontrollersethint(struct wl_listener *listener, void *data);
++static void handletearingcontrollerdestroy(struct wl_listener *listener, void *data);
++static void handlenewtearinghint(struct wl_listener *listener, void *data);
+ static void incnmaster(const Arg *arg);
+ static void inputdevice(struct wl_listener *listener, void *data);
+ static int keybinding(uint32_t mods, xkb_keysym_t sym);
+@@ -309,6 +332,7 @@ static void motionnotify(uint32_t time, struct wlr_input_device *device, double
+ double sy, double sx_unaccel, double sy_unaccel);
+ static void motionrelative(struct wl_listener *listener, void *data);
+ static void moveresize(const Arg *arg);
++static int moncantear(Monitor* m);
+ static void outputmgrapply(struct wl_listener *listener, void *data);
+ static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test);
+ static void outputmgrtest(struct wl_listener *listener, void *data);
+@@ -323,6 +347,7 @@ static void requeststartdrag(struct wl_listener *listener, void *data);
+ static void requestmonstate(struct wl_listener *listener, void *data);
+ static void resize(Client *c, struct wlr_box geo, int interact);
+ static void run(char *startup_cmd);
++static void sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data);
+ static void setcursor(struct wl_listener *listener, void *data);
+ static void setcursorshape(struct wl_listener *listener, void *data);
+ static void setfloating(Client *c, int floating);
+@@ -400,6 +425,10 @@ static struct wlr_scene_rect *locked_bg;
+ static struct wlr_session_lock_v1 *cur_lock;
+ static struct wl_listener lock_listener = {.notify = locksession};
+
++struct wlr_tearing_control_manager_v1 *tearing_control_v1;
++struct wl_listener tearing_control_new_object;
++struct wl_list tearing_controllers;
++
+ static struct wlr_seat *seat;
+ static KeyboardGroup *kb_group;
+ static unsigned int cursor_mode;
+@@ -1510,6 +1539,69 @@ handlesig(int signo)
+ }
+ }
+
++void
++handletearingcontrollersethint(struct wl_listener *listener, void *data)
++{
++ Client *c = NULL, *i = NULL;
++ struct TearingController *controller = wl_container_of(listener, controller, set_hint);
++
++ struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(controller->tearing_control->surface);
++#ifdef XWAYLAND
++ struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_try_from_wlr_surface(controller->tearing_control->surface);
++#endif
++
++ wl_list_for_each(i, &fstack, flink) {
++ if (i->surface.xdg == surface
++#ifdef XWAYLAND
++ || i->surface.xwayland == xsurface
++#endif
++ ) {
++ c = i;
++ break;
++ }
++ }
++
++ if (c) {
++ enum wp_tearing_control_v1_presentation_hint hint = controller->tearing_control->current;
++ fprintf(
++ stderr, "TEARING: found surface: %p(appid: '%s', title: '%s'), hint: %d(%s)\n",
++ (void*)c, client_get_appid(c), client_get_title(c), hint, hint ? "ASYNC" : "VSYNC"
++ );
++ c->tearing_hint = controller->tearing_control->current;
++ }
++}
++
++void
++handletearingcontrollerdestroy(struct wl_listener *listener, void *data)
++{
++ struct TearingController *controller = wl_container_of(listener, controller, destroy);
++
++ wl_list_remove(&controller->set_hint.link);
++ wl_list_remove(&controller->destroy.link);
++ wl_list_remove(&controller->link);
++ free(controller);
++}
++
++void
++handlenewtearinghint(struct wl_listener *listener, void *data)
++{
++ struct wlr_tearing_control_v1 *tearing_control = data;
++ struct TearingController *controller = calloc(1, sizeof(struct TearingController));
++
++ if (!controller)
++ return;
++
++ controller->tearing_control = tearing_control;
++ controller->set_hint.notify = handletearingcontrollersethint;
++ wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint);
++
++ controller->destroy.notify = handletearingcontrollerdestroy;
++ wl_signal_add(&tearing_control->events.destroy, &controller->destroy);
++
++ wl_list_init(&controller->link);
++ wl_list_insert(&tearing_controllers, &controller->link);
++}
++
+ void
+ incnmaster(const Arg *arg)
+ {
+@@ -1677,6 +1769,33 @@ locksession(struct wl_listener *listener, void *data)
+ wlr_session_lock_v1_send_locked(session_lock);
+ }
+
++static inline void
++forcetearingrule(Client *c)
++{
++ int success = 0;
++ const char* appid = client_get_appid(c);
++ const char* title = client_get_title(c);
++
++ for (unsigned i = 0; i < LENGTH(force_tearing); i++) {
++ if (appid)
++ if (strcmp(force_tearing[i].appid, appid) == 0) {
++ success = 1;
++ break;
++ }
++
++ if (title)
++ if (strcmp(force_tearing[i].title, title) == 0) {
++ success = 1;
++ break;
++ }
++ }
++
++ if (success) {
++ c->tearing_hint = WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC;
++ fprintf(stderr, "tearing forced for: appid: '%s', title: '%s'\n", appid, title);
++ }
++}
++
+ void
+ mapnotify(struct wl_listener *listener, void *data)
+ {
+@@ -1686,6 +1805,8 @@ mapnotify(struct wl_listener *listener, void *data)
+ Monitor *m;
+ int i;
+
++ forcetearingrule(c);
++
+ /* Create scene tree for this client and its border */
+ c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
+ /* Enabled later by a call to arrange() */
+@@ -1924,6 +2045,13 @@ moveresize(const Arg *arg)
+ }
+ }
+
++int
++moncantear(Monitor* m)
++{
++ Client *c = focustop(m);
++ return (c && c->isfullscreen && c->tearing_hint); /* 1 == ASYNC */
++}
++
+ void
+ outputmgrapply(struct wl_listener *listener, void *data)
+ {
+@@ -2093,27 +2221,40 @@ quit(const Arg *arg)
+ void
+ rendermon(struct wl_listener *listener, void *data)
+ {
+- /* This function is called every time an output is ready to display a frame,
+- * generally at the output's refresh rate (e.g. 60Hz). */
+ Monitor *m = wl_container_of(listener, m, frame);
+- Client *c;
++ struct wlr_scene_output *scene_output = m->scene_output;
+ struct wlr_output_state pending = {0};
+- struct timespec now;
++ SendFrameDoneData frame_done_data = {0};
+
+- /* Render if no XDG clients have an outstanding resize and are visible on
+- * this monitor. */
+- wl_list_for_each(c, &clients, link) {
+- if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c))
+- goto skip;
++ m->wlr_output->frame_pending = false;
++
++ if (!wlr_scene_output_needs_frame(scene_output)) {
++ goto skip;
+ }
+
+- wlr_scene_output_commit(m->scene_output, NULL);
++ wlr_output_state_init(&pending);
++ if (!wlr_scene_output_build_state(m->scene_output, &pending, NULL)) {
++ goto skip;
++ }
++
++ if (tearing_allowed && moncantear(m)) {
++ pending.tearing_page_flip = true;
++
++ if (!wlr_output_test_state(m->wlr_output, &pending)) {
++ fprintf(stderr, "Output test failed on '%s', retrying without tearing page-flip\n", m->wlr_output->name);
++ pending.tearing_page_flip = false;
++ }
++ }
++
++ if (!wlr_output_commit_state(m->wlr_output, &pending))
++ fprintf(stderr, "Page-flip failed on output %s", m->wlr_output->name);
+
+-skip:
+- /* Let clients know a frame has been rendered */
+- clock_gettime(CLOCK_MONOTONIC, &now);
+- wlr_scene_output_send_frame_done(m->scene_output, &now);
+ wlr_output_state_finish(&pending);
++
++skip:
++ clock_gettime(CLOCK_MONOTONIC, &frame_done_data.when);
++ frame_done_data.mon = m;
++ wlr_scene_output_for_each_buffer(m->scene_output, sendframedoneiterator, &frame_done_data);
+ }
+
+ void
+@@ -2237,6 +2378,16 @@ run(char *startup_cmd)
+ wl_display_run(dpy);
+ }
+
++void
++sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data)
++{
++ SendFrameDoneData *data = user_data;
++ if (buffer->primary_output != data->mon->scene_output)
++ return;
++
++ wlr_scene_buffer_send_frame_done(buffer, &data->when);
++}
++
+ void
+ setcursor(struct wl_listener *listener, void *data)
+ {
+@@ -2584,6 +2735,11 @@ setup(void)
+ LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply);
+ LISTEN_STATIC(&output_mgr->events.test, outputmgrtest);
+
++ tearing_control_v1 = wlr_tearing_control_manager_v1_create(dpy, 1);
++ tearing_control_new_object.notify = handlenewtearinghint;
++ wl_signal_add(&tearing_control_v1->events.new_object, &tearing_control_new_object);
++ wl_list_init(&tearing_controllers);
++
+ /* Make sure XWayland clients don't connect to the parent X server,
+ * e.g when running in the x11 backend or the wayland backend and the
+ * compositor has Xwayland support */
+--
+2.46.0
+
diff --git a/patches/toggle_constraints.patch b/patches/toggle_constraints.patch
new file mode 100644
index 0000000..e823377
--- /dev/null
+++ b/patches/toggle_constraints.patch
@@ -0,0 +1,86 @@
+From 13bb9d86a0da2ebc82c20ee721b3eeb28592b0a0 Mon Sep 17 00:00:00 2001
+From: David Donahue <david.donahue2996@gmail.com>
+Date: Tue, 26 Mar 2024 17:45:26 -0600
+Subject: [PATCH] Added function to toggle pointer constraints via a keybind
+
+---
+ dwl.c | 41 ++++++++++++++++++++++++++---------------
+ 1 file changed, 26 insertions(+), 15 deletions(-)
+
+diff --git a/dwl.c b/dwl.c
+index 5867b0c..04d377b 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -332,6 +332,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 togglepointerconstraints(const Arg *arg);
+ static void toggletag(const Arg *arg);
+ static void toggleview(const Arg *arg);
+ static void unlocksession(struct wl_listener *listener, void *data);
+@@ -400,6 +401,8 @@ static unsigned int cursor_mode;
+ static Client *grabc;
+ static int grabcx, grabcy; /* client-relative */
+
++static int enable_constraints = 1;
++
+ static struct wlr_output_layout *output_layout;
+ static struct wlr_box sgeom;
+ static struct wl_list mons;
+@@ -1700,22 +1703,24 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
+ relative_pointer_mgr, seat, (uint64_t)time * 1000,
+ dx, dy, dx_unaccel, dy_unaccel);
+
+- wl_list_for_each(constraint, &pointer_constraints->constraints, link)
+- cursorconstrain(constraint);
+-
+- if (active_constraint && cursor_mode != CurResize && cursor_mode != CurMove) {
+- toplevel_from_wlr_surface(active_constraint->surface, &c, NULL);
+- if (c && active_constraint->surface == seat->pointer_state.focused_surface) {
+- sx = cursor->x - c->geom.x - c->bw;
+- sy = cursor->y - c->geom.y - c->bw;
+- if (wlr_region_confine(&active_constraint->region, sx, sy,
+- sx + dx, sy + dy, &sx_confined, &sy_confined)) {
+- dx = sx_confined - sx;
+- dy = sy_confined - sy;
++ if (enable_constraints){
++ wl_list_for_each(constraint, &pointer_constraints->constraints, link)
++ cursorconstrain(constraint);
++
++ if (active_constraint && cursor_mode != CurResize && cursor_mode != CurMove) {
++ toplevel_from_wlr_surface(active_constraint->surface, &c, NULL);
++ if (c && active_constraint->surface == seat->pointer_state.focused_surface) {
++ sx = cursor->x - c->geom.x - c->bw;
++ sy = cursor->y - c->geom.y - c->bw;
++ if (wlr_region_confine(&active_constraint->region, sx, sy,
++ sx + dx, sy + dy, &sx_confined, &sy_confined)) {
++ dx = sx_confined - sx;
++ dy = sy_confined - sy;
++ }
++
++ if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED)
++ return;
+ }
+-
+- if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED)
+- return;
+ }
+ }
+
+@@ -2620,6 +2625,12 @@ togglefullscreen(const Arg *arg)
+ setfullscreen(sel, !sel->isfullscreen);
+ }
+
++void
++togglepointerconstraints(const Arg *arg)
++{
++ enable_constraints = !enable_constraints;
++}
++
+ void
+ toggletag(const Arg *arg)
+ {
+--
+2.43.2
+
diff --git a/patches/xwayland-handle-minimize.patch b/patches/xwayland-handle-minimize.patch
new file mode 100644
index 0000000..d6d1f8f
--- /dev/null
+++ b/patches/xwayland-handle-minimize.patch
@@ -0,0 +1,91 @@
+From 7277f668f19f5a7fcfbbc96e80cb2829487848ca Mon Sep 17 00:00:00 2001
+From: korei999 <ju7t1xe@gmail.com>
+Date: Mon, 1 Apr 2024 15:13:11 +0300
+Subject: [PATCH] handle minimize request for xwayland clients
+
+---
+ client.h | 9 ++++++---
+ dwl.c | 19 +++++++++++++++++++
+ 2 files changed, 25 insertions(+), 3 deletions(-)
+
+diff --git a/client.h b/client.h
+index 800b867..c46cfb2 100644
+--- a/client.h
++++ b/client.h
+@@ -94,9 +94,12 @@ client_activate_surface(struct wlr_surface *s, int activated)
+ {
+ struct wlr_xdg_toplevel *toplevel;
+ #ifdef XWAYLAND
+- struct wlr_xwayland_surface *xsurface;
+- if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) {
+- wlr_xwayland_surface_activate(xsurface, activated);
++ struct wlr_xwayland_surface *surface;
++ if ((surface = wlr_xwayland_surface_try_from_wlr_surface(s))) {
++ if (activated && surface->minimized)
++ wlr_xwayland_surface_set_minimized(surface, false);
++
++ wlr_xwayland_surface_activate(surface, activated);
+ return;
+ }
+ #endif
+diff --git a/dwl.c b/dwl.c
+index 39ce68c..b49f57b 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -131,6 +131,7 @@ typedef struct {
+ #ifdef XWAYLAND
+ struct wl_listener activate;
+ struct wl_listener associate;
++ struct wl_listener minimize;
+ struct wl_listener dissociate;
+ struct wl_listener configure;
+ struct wl_listener set_hints;
+@@ -412,6 +413,7 @@ static void configurex11(struct wl_listener *listener, void *data);
+ static void createnotifyx11(struct wl_listener *listener, void *data);
+ static void dissociatex11(struct wl_listener *listener, void *data);
+ static xcb_atom_t getatom(xcb_connection_t *xc, const char *name);
++static void minimizenotify(struct wl_listener *listener, void *data);
+ static void sethints(struct wl_listener *listener, void *data);
+ static void xwaylandready(struct wl_listener *listener, void *data);
+ static struct wlr_xwayland *xwayland;
+@@ -1177,6 +1179,7 @@ destroynotify(struct wl_listener *listener, void *data)
+ wl_list_remove(&c->activate.link);
+ wl_list_remove(&c->associate.link);
+ wl_list_remove(&c->configure.link);
++ wl_list_remove(&c->minimize.link);
+ wl_list_remove(&c->dissociate.link);
+ wl_list_remove(&c->set_hints.link);
+ } else
+@@ -2984,6 +2987,7 @@ createnotifyx11(struct wl_listener *listener, void *data)
+ LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify);
+ LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11);
+ LISTEN(&xsurface->events.request_activate, &c->activate, activatex11);
++ LISTEN(&xsurface->events.request_minimize, &c->minimize, minimizenotify);
+ LISTEN(&xsurface->events.request_configure, &c->configure, configurex11);
+ LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify);
+ LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints);
+@@ -3011,6 +3015,21 @@ getatom(xcb_connection_t *xc, const char *name)
+ return atom;
+ }
+
++void
++minimizenotify(struct wl_listener *listener, void *data)
++{
++ Client *c = wl_container_of(listener, c, minimize);
++ struct wlr_xwayland_surface *xsurface = c->surface.xwayland;
++ struct wlr_xwayland_minimize_event *e = data;
++ int focused;
++
++ if (xsurface->surface == NULL || !xsurface->surface->mapped)
++ return;
++
++ focused = seat->keyboard_state.focused_surface == xsurface->surface;
++ wlr_xwayland_surface_set_minimized(xsurface, !focused && e->minimize);
++}
++
+ void
+ sethints(struct wl_listener *listener, void *data)
+ {
+--
+2.44.0
+