Compare commits

..

17 Commits

Author SHA1 Message Date
julmajustus ba9c29477d fullscreen_adaptive_sync: Minor bug fixes
- Fixed missing (void) function type in the implementation
- Minor indentation fixes
2026-06-22 23:24:09 +03:00
julmajustus 706d6350cb btrtile: bug fixes and readability improvements
- Removed client old_geom caching for simplicity; fixes: https://codeberg.org/dwl/dwl-patches/issues/647
- Improved readability of the client sanitization loop inside btrtile()
- Removed fullscreened clients from the sanitization loop to retain their geometry
2026-06-22 23:17:56 +03:00
skeetamine 41fdaddfb0 per-app-cast: per-window screencast patch 2026-06-20 20:40:39 +02:00
nshan651 ee22414b44 vanitygaps: update for v0.8 dwl. 2026-06-19 17:38:40 -05:00
C4FE1 d8046ab6ec moveresizekb: support floating layout 2026-06-19 19:25:44 -03:00
6z7y 0935f95e01 restore floating window position on reattach 2026-06-20 01:10:20 +03:00
6z7y e2ba7017ea touch-input: Support osu! and fix touch motion issue 2026-06-19 23:02:39 +02:00
6z7y 493dc4c408 fix link to correct branch 2026-05-28 16:12:14 +03:00
André Desgualdo Pereira 33acb045a8 fix swapfocus README 2026-05-21 10:04:19 -03:00
André Desgualdo Pereira bbe1199e40 adds an alternative way to handle swapfocus, not allowing changing tags 2026-05-21 09:59:17 -03:00
julmajustus 1f0e82bf5d simple_scratchpad:
- Fixed broken media links
2026-05-21 01:13:24 +03:00
julmajustus 5483e1d437 btrtile:
- Fixed broken media links
2026-05-21 01:12:03 +03:00
julmajustus 30c33ed26c simple_scratchpad: Spring update scratchpad V2
- Added support for multiple scratchpads
2026-05-21 01:05:17 +03:00
julmajustus 03de6e2ada btrtile: Spring update pt2
- Simplified the resizing logic to avoid full arrange calls from
motionnotify
- Minor intend fixes
2026-05-21 00:54:00 +03:00
julmajustus 7f06b4c1a3 fullscreenadpativesync: update & minor bug fixes
- Fixed logic error inside unmapnotify
- Added no-op guards inside set_adaptive_sync
- Added extra check for the config commit success
2026-05-18 00:29:39 +03:00
julmajustus d7471ef29e btrtile: Refactored the tiled client resizing logic
Improved detection of the node to resize. btrtile now handles client resizing based on the nearest client edge in the resize direction. It also handles resizing of clientless split nodes, making the behavior feel more intuitive.
2026-05-17 23:44:33 +03:00
6z7y 5d2f27a9f9 Add: make tag 0 a regular tag 2026-05-06 04:50:41 +02:00
28 changed files with 2492 additions and 1190 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
It provides a focus-driven, mouse- and keyboard-friendly tiling layout that grants you granular control over how clients are placed and resized. It provides a focus-driven, mouse- and keyboard-friendly tiling layout that grants you granular control over how clients are placed and resized.
![btrtile in action](https://github.com/julmajustus/dwl-patches/blob/main/demos/btrtiledemo.gif?raw=true) ![btrtile in action](https://codeberg.org/julmajustus/my-dwl-patches/raw/branch/main/demos/btrtiledemo.gif?raw=true)
--- ---
+164 -158
View File
@@ -1,21 +1,24 @@
From f5d7fe287bd4405879a92d649243d3f646652617 Mon Sep 17 00:00:00 2001 From 42e35a6a8a93f0e6d886ad69686c85c1d58536d6 Mon Sep 17 00:00:00 2001
From: julmajustus <julmajustus@tutanota.com> From: julmajustus <julmajustus@tutanota.com>
Date: Thu, 19 Mar 2026 00:08:10 +0200 Date: Mon, 22 Jun 2026 22:54:11 +0300
Subject: [PATCH] Refactor btrtile-gaps to v0.8 Subject: [PATCH] btrtile: bug fixes and readability improvement
- Removed client old_geom caching for simplicity fixes: https://codeberg.org/dwl/dwl-patches/issues/647
- Improved readability of the client sanitization loop inside btrtile function
- Removed fullscreened client's from the sanitization loop to retain their geometry
--- ---
btrtile.c | 584 +++++++++++++++++++++++++++++++++++++++++++++++++++ btrtile.c | 575 +++++++++++++++++++++++++++++++++++++++++++++++++++
config.def.h | 12 ++ config.def.h | 12 ++
dwl.c | 148 ++++++++++--- dwl.c | 151 +++++++++++---
3 files changed, 717 insertions(+), 27 deletions(-) 3 files changed, 711 insertions(+), 27 deletions(-)
create mode 100644 btrtile.c create mode 100644 btrtile.c
diff --git a/btrtile.c b/btrtile.c diff --git a/btrtile.c b/btrtile.c
new file mode 100644 new file mode 100644
index 0000000..afe3c8f index 0000000..24bf4d9
--- /dev/null --- /dev/null
+++ b/btrtile.c +++ b/btrtile.c
@@ -0,0 +1,584 @@ @@ -0,0 +1,575 @@
+/* ************************************************************************** */ +/* ************************************************************************** */
+/* @@@ @@@@@@@@ */ +/* @@@ @@@@@@@@ */
+/* @@@ @@@@@@@@@@ */ +/* @@@ @@@@@@@@@@ */
@@ -26,7 +29,7 @@ index 0000000..afe3c8f
+/* By: julmajustus <julmajustus@tutanota.com> !!: !!:! !!! */ +/* By: julmajustus <julmajustus@tutanota.com> !!: !!:! !!! */
+/* ::! :!: !:! */ +/* ::! :!: !:! */
+/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */ +/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */
+/* Updated: 2026/03/19 00:07:19 by julmajustus : : : : : : */ +/* Updated: 2026/06/22 22:54:06 by julmajustus : : : : : : */
+/* */ +/* */
+/* ************************************************************************** */ +/* ************************************************************************** */
+ +
@@ -49,7 +52,8 @@ index 0000000..afe3c8f
+static void destroy_node(LayoutNode *node); +static void destroy_node(LayoutNode *node);
+static void destroy_tree(Monitor *m); +static void destroy_tree(Monitor *m);
+static LayoutNode *find_client_node(LayoutNode *node, Client *c); +static LayoutNode *find_client_node(LayoutNode *node, Client *c);
+static LayoutNode *find_suitable_split(LayoutNode *start, unsigned int need_vert); +static LayoutNode *find_suitable_split(Monitor *m, LayoutNode *start,
+ unsigned int need_vertical, int focused_on_left);
+static void init_tree(Monitor *m); +static void init_tree(Monitor *m);
+static void insert_client(Monitor *m, Client *focused_client, Client *new_client); +static void insert_client(Monitor *m, Client *focused_client, Client *new_client);
+static LayoutNode *remove_client_node(LayoutNode *node, Client *c); +static LayoutNode *remove_client_node(LayoutNode *node, Client *c);
@@ -60,7 +64,6 @@ index 0000000..afe3c8f
+static unsigned int visible_count(LayoutNode *node, Monitor *m); +static unsigned int visible_count(LayoutNode *node, Monitor *m);
+static Client *xytoclient(double x, double y); +static Client *xytoclient(double x, double y);
+ +
+static int resizing_from_mouse = 0;
+static double resize_last_update_x, resize_last_update_y; +static double resize_last_update_x, resize_last_update_y;
+static uint32_t last_resize_time = 0; +static uint32_t last_resize_time = 0;
+ +
@@ -76,7 +79,7 @@ index 0000000..afe3c8f
+ if (!node) + if (!node)
+ return; + return;
+ +
+ if (is_root && e) { + if (is_root && e) {
+ area.x += gappx; + area.x += gappx;
+ area.y += gappx; + area.y += gappx;
+ area.width -= 2 * gappx; + area.width -= 2 * gappx;
@@ -89,7 +92,6 @@ index 0000000..afe3c8f
+ if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen) + if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ return; + return;
+ resize(c, area, 0); + resize(c, area, 0);
+ c->old_geom = area;
+ return; + return;
+ } + }
+ +
@@ -129,7 +131,7 @@ index 0000000..afe3c8f
+ right_area.width = area.width - mid; + right_area.width = area.width - mid;
+ right_area.height = area.height; + right_area.height = area.height;
+ +
+ if (e) { + if (e) {
+ left_area.width -= gappx / 2; + left_area.width -= gappx / 2;
+ right_area.x += gappx / 2; + right_area.x += gappx / 2;
+ right_area.width -= gappx / 2; + right_area.width -= gappx / 2;
@@ -147,7 +149,7 @@ index 0000000..afe3c8f
+ right_area.width = area.width; + right_area.width = area.width;
+ right_area.height= area.height - mid; + right_area.height= area.height - mid;
+ +
+ if (e) { + if (e) {
+ left_area.height -= gappx / 2; + left_area.height -= gappx / 2;
+ right_area.y += gappx / 2; + right_area.y += gappx / 2;
+ right_area.height -= gappx / 2; + right_area.height -= gappx / 2;
@@ -166,15 +168,13 @@ index 0000000..afe3c8f
+ LayoutNode *found; + LayoutNode *found;
+ struct wlr_box full_area; + struct wlr_box full_area;
+ +
+ if (!m || !m->root) + if (!m)
+ return; + return;
+ +
+ /* Remove non tiled clients from tree. */ + /* Remove floating and moved clients */
+ wl_list_for_each(c, &clients, link) { + wl_list_for_each(c, &clients, link) {
+ if (c->mon == m && !c->isfloating && !c->isfullscreen) { + if (c->mon != m || c->isfloating)
+ } else {
+ remove_client(m, c); + remove_client(m, c);
+ }
+ } + }
+ +
+ /* If no client is found under cursor, fallback to focustop(m) */ + /* If no client is found under cursor, fallback to focustop(m) */
@@ -268,30 +268,37 @@ index 0000000..afe3c8f
+} +}
+ +
+LayoutNode * +LayoutNode *
+find_suitable_split(LayoutNode *start_node, unsigned int need_vertical) +find_suitable_split(Monitor *m, LayoutNode *start_node,
+ unsigned int need_vertical, int focused_on_left)
+{ +{
+ LayoutNode *n = start_node; + LayoutNode *n = start_node, *child = NULL;
+ /* if we started from a client node, jump to its parent: */ +
+ if (n && n->is_client_node) + if (!m)
+ return NULL;
+
+ if (n && n->is_client_node) {
+ child = n;
+ n = n->split_node; + n = n->split_node;
+ }
+ +
+ while (n) { + while (n) {
+ if (!n->is_client_node && n->is_split_vertically == need_vertical && + if (!n->is_client_node && n->is_split_vertically == need_vertical
+ visible_count(n->left, selmon) > 0 && visible_count(n->right, selmon) > 0) + && visible_count(n->left, m) > 0
+ return n; + && visible_count(n->right, m) > 0) {
+ if ((focused_on_left && n->left == child) ||
+ (!focused_on_left && n->right == child))
+ return n;
+ }
+ child = n;
+ n = n->split_node; + n = n->split_node;
+ } + }
+ return NULL; + return NULL;
+} +}
+
+void +void
+init_tree(Monitor *m) +init_tree(Monitor *m)
+{ +{
+ if (!m) + if (m)
+ return; + m->root = NULL;
+ m->root = calloc(1, sizeof(LayoutNode));
+ if (!m->root)
+ m->root = NULL;
+} +}
+ +
+void +void
@@ -415,21 +422,31 @@ index 0000000..afe3c8f
+ m->root = remove_client_node(m->root, c); + m->root = remove_client_node(m->root, c);
+} +}
+ +
+void +static void
+setratio_h(const Arg *arg) +setratio(unsigned int need_vertical, const Arg *arg)
+{ +{
+ Client *sel = focustop(selmon); + Client *sel;
+ LayoutNode *client_node, *split_node; + LayoutNode *client_node, *split_node;
+ float new_ratio; + float new_ratio;
+ int focused_on_left;
+ +
+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange) + if (!selmon || !selmon->lt[selmon->sellt]->arrange)
+ return;
+
+ sel = focustop(selmon);
+ if (!sel)
+ return; + return;
+ +
+ client_node = find_client_node(selmon->root, sel); + client_node = find_client_node(selmon->root, sel);
+ if (!client_node) + if (!client_node)
+ return; + return;
+ +
+ split_node = find_suitable_split(client_node, 1); + focused_on_left = (arg->f >= 0.0f);
+
+ split_node = find_suitable_split(selmon, client_node, need_vertical, focused_on_left);
+
+ if (!split_node)
+ split_node = find_suitable_split(selmon, client_node, need_vertical, !focused_on_left);
+ if (!split_node) + if (!split_node)
+ return; + return;
+ +
@@ -440,109 +457,85 @@ index 0000000..afe3c8f
+ new_ratio = 0.95f; + new_ratio = 0.95f;
+ split_node->split_ratio = new_ratio; + split_node->split_ratio = new_ratio;
+ +
+ /* Skip the arrange if done resizing by mouse, + apply_layout(selmon, selmon->root, selmon->w, 1);
+ * we call arrange from motionotify */ +}
+ if (!resizing_from_mouse) { +
+ arrange(selmon); +void
+ } +setratio_h(const Arg *arg)
+{
+ setratio(1, arg);
+} +}
+ +
+void +void
+setratio_v(const Arg *arg) +setratio_v(const Arg *arg)
+{ +{
+ Client *sel = focustop(selmon); + setratio(0, arg);
+ LayoutNode *client_node, *split_node;
+ float new_ratio;
+
+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange)
+ return;
+
+ client_node = find_client_node(selmon->root, sel);
+ if (!client_node)
+ return;
+
+ split_node = find_suitable_split(client_node, 0);
+ if (!split_node)
+ return;
+
+ new_ratio = (arg->f != 0.0f) ? (split_node->split_ratio + arg->f) : 0.5f;
+ if (new_ratio < 0.05f)
+ new_ratio = 0.05f;
+ if (new_ratio > 0.95f)
+ new_ratio = 0.95f;
+ split_node->split_ratio = new_ratio;
+
+ /* Skip the arrange if done resizing by mouse,
+ * we call arrange from motionotify */
+ if (!resizing_from_mouse) {
+ arrange(selmon);
+ }
+} +}
+ +
+void swapclients(const Arg *arg) { +void swapclients(const Arg *arg) {
+ Client *c, *tmp, *target = NULL, *sel = focustop(selmon); + Client *c, *tmp, *target = NULL, *sel = focustop(selmon);
+ LayoutNode *sel_node, *target_node; + LayoutNode *sel_node, *target_node;
+ int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y, + int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y,
+ cand_center_x, cand_center_y; + cand_center_x, cand_center_y;
+ +
+ if (!sel || sel->isfullscreen || + if (!sel || sel->isfullscreen ||
+ !selmon->root || !selmon->lt[selmon->sellt]->arrange) + !selmon->root || !selmon->lt[selmon->sellt]->arrange)
+ return; + return;
+ +
+ +
+ /* Get the center coordinates of the selected client */ + /* Get the center coordinates of the selected client */
+ sel_center_x = sel->geom.x + sel->geom.width / 2; + sel_center_x = sel->geom.x + sel->geom.width / 2;
+ sel_center_y = sel->geom.y + sel->geom.height / 2; + sel_center_y = sel->geom.y + sel->geom.height / 2;
+ +
+ wl_list_for_each(c, &clients, link) { + wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel) + if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel)
+ continue; + continue;
+ +
+ /* Get the center of candidate client */ + /* Get the center of candidate client */
+ cand_center_x = c->geom.x + c->geom.width / 2; + cand_center_x = c->geom.x + c->geom.width / 2;
+ cand_center_y = c->geom.y + c->geom.height / 2; + cand_center_y = c->geom.y + c->geom.height / 2;
+ +
+ /* Check that the candidate lies in the requested direction. */ + /* Check that the candidate lies in the requested direction. */
+ switch (arg->ui) { + switch (arg->ui) {
+ case 0: + case 0:
+ if (cand_center_x >= sel_center_x) + if (cand_center_x >= sel_center_x)
+ continue; + continue;
+ break; + break;
+ case 1: + case 1:
+ if (cand_center_x <= sel_center_x) + if (cand_center_x <= sel_center_x)
+ continue; + continue;
+ break; + break;
+ case 2: + case 2:
+ if (cand_center_y >= sel_center_y) + if (cand_center_y >= sel_center_y)
+ continue; + continue;
+ break; + break;
+ case 3: + case 3:
+ if (cand_center_y <= sel_center_y) + if (cand_center_y <= sel_center_y)
+ continue; + continue;
+ break; + break;
+ default: + default:
+ continue; + continue;
+ } + }
+ +
+ /* Get distance between the centers */ + /* Get distance between the centers */
+ dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y); + dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y);
+ if (dist < closest_dist) { + if (dist < closest_dist) {
+ closest_dist = dist; + closest_dist = dist;
+ target = c; + target = c;
+ } + }
+ } + }
+ +
+ /* If target is found, swap the two clients positions in the layout tree */ + /* If target is found, swap the two clients positions in the layout tree */
+ if (target) { + if (target) {
+ sel_node = find_client_node(selmon->root, sel); + sel_node = find_client_node(selmon->root, sel);
+ target_node = find_client_node(selmon->root, target); + target_node = find_client_node(selmon->root, target);
+ if (sel_node && target_node) { + if (sel_node && target_node) {
+ tmp = sel_node->client; + tmp = sel_node->client;
+ sel_node->client = target_node->client; + sel_node->client = target_node->client;
+ target_node->client = tmp; + target_node->client = tmp;
+ arrange(selmon); + arrange(selmon);
+ } + }
+ } + }
+} +}
+ +
+unsigned int +unsigned int
@@ -565,11 +558,12 @@ index 0000000..afe3c8f
+ +
+Client * +Client *
+xytoclient(double x, double y) { +xytoclient(double x, double y) {
+ Monitor *m = xytomon(x, y);
+ Client *c, *closest = NULL; + Client *c, *closest = NULL;
+ double dist, mindist = INT_MAX, dx, dy; + double dist, mindist = INT_MAX, dx, dy;
+ +
+ wl_list_for_each_reverse(c, &clients, link) { + wl_list_for_each_reverse(c, &clients, link) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen && + if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen &&
+ x >= c->geom.x && x <= (c->geom.x + c->geom.width) && + x >= c->geom.x && x <= (c->geom.x + c->geom.width) &&
+ y >= c->geom.y && y <= (c->geom.y + c->geom.height)){ + y >= c->geom.y && y <= (c->geom.y + c->geom.height)){
+ return c; + return c;
@@ -578,7 +572,7 @@ index 0000000..afe3c8f
+ +
+ /* If no client was found at cursor position fallback to closest. */ + /* If no client was found at cursor position fallback to closest. */
+ wl_list_for_each_reverse(c, &clients, link) { + wl_list_for_each_reverse(c, &clients, link) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen) { + if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) {
+ dx = 0, dy = 0; + dx = 0, dy = 0;
+ +
+ if (x < c->geom.x) + if (x < c->geom.x)
@@ -591,7 +585,7 @@ index 0000000..afe3c8f
+ else if (y > (c->geom.y + c->geom.height)) + else if (y > (c->geom.y + c->geom.height))
+ dy = y - (c->geom.y + c->geom.height); + dy = y - (c->geom.y + c->geom.height);
+ +
+ dist = sqrt(dx * dx + dy * dy); + dist = dx * dx + dy * dy;
+ if (dist < mindist) { + if (dist < mindist) {
+ mindist = dist; + mindist = dist;
+ closest = c; + closest = c;
@@ -639,7 +633,7 @@ index 8a6eda0..bc04e3f 100644
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
diff --git a/dwl.c b/dwl.c diff --git a/dwl.c b/dwl.c
index 44f3ad9..f74d9a1 100644 index 44f3ad9..2529e1f 100644
--- a/dwl.c --- a/dwl.c
+++ b/dwl.c +++ b/dwl.c
@@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
@@ -658,18 +652,16 @@ index 44f3ad9..f74d9a1 100644
typedef struct Monitor Monitor; typedef struct Monitor Monitor;
typedef struct { typedef struct {
/* Must keep this field first */ /* Must keep this field first */
@@ -137,8 +139,9 @@ typedef struct { @@ -137,7 +139,7 @@ typedef struct {
#endif #endif
unsigned int bw; unsigned int bw;
uint32_t tags; uint32_t tags;
- int isfloating, isurgent, isfullscreen; - int isfloating, isurgent, isfullscreen;
+ int isfloating, isurgent, isfullscreen, was_tiled; + int isfloating, isurgent, isfullscreen, was_tiled;
uint32_t resize; /* configure serial of a pending resize */ uint32_t resize; /* configure serial of a pending resize */
+ struct wlr_box old_geom;
} Client; } Client;
typedef struct { @@ -205,6 +207,7 @@ struct Monitor {
@@ -205,6 +208,7 @@ struct Monitor {
int nmaster; int nmaster;
char ltsymbol[16]; char ltsymbol[16];
int asleep; int asleep;
@@ -677,7 +669,7 @@ index 44f3ad9..f74d9a1 100644
}; };
typedef struct { typedef struct {
@@ -247,6 +251,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list, @@ -247,6 +250,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
struct wlr_box *usable_area, int exclusive); struct wlr_box *usable_area, int exclusive);
static void arrangelayers(Monitor *m); static void arrangelayers(Monitor *m);
static void axisnotify(struct wl_listener *listener, void *data); static void axisnotify(struct wl_listener *listener, void *data);
@@ -685,7 +677,7 @@ index 44f3ad9..f74d9a1 100644
static void buttonpress(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg); static void chvt(const Arg *arg);
static void checkidleinhibitor(struct wlr_surface *exclude); static void checkidleinhibitor(struct wlr_surface *exclude);
@@ -329,6 +334,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags); @@ -329,6 +333,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags);
static void setpsel(struct wl_listener *listener, void *data); static void setpsel(struct wl_listener *listener, void *data);
static void setsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data);
static void setup(void); static void setup(void);
@@ -695,7 +687,7 @@ index 44f3ad9..f74d9a1 100644
static void spawn(const Arg *arg); static void spawn(const Arg *arg);
static void startdrag(struct wl_listener *listener, void *data); static void startdrag(struct wl_listener *listener, void *data);
static void tag(const Arg *arg); static void tag(const Arg *arg);
@@ -454,6 +462,7 @@ static struct wlr_xwayland *xwayland; @@ -454,6 +461,7 @@ static struct wlr_xwayland *xwayland;
/* attempt to encapsulate suck into one file */ /* attempt to encapsulate suck into one file */
#include "client.h" #include "client.h"
@@ -703,7 +695,7 @@ index 44f3ad9..f74d9a1 100644
/* function implementations */ /* function implementations */
void void
@@ -624,7 +633,7 @@ buttonpress(struct wl_listener *listener, void *data) @@ -624,7 +632,7 @@ buttonpress(struct wl_listener *listener, void *data)
struct wlr_pointer_button_event *event = data; struct wlr_pointer_button_event *event = data;
struct wlr_keyboard *keyboard; struct wlr_keyboard *keyboard;
uint32_t mods; uint32_t mods;
@@ -712,7 +704,7 @@ index 44f3ad9..f74d9a1 100644
const Button *b; const Button *b;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
@@ -645,7 +654,7 @@ buttonpress(struct wl_listener *listener, void *data) @@ -645,7 +653,7 @@ buttonpress(struct wl_listener *listener, void *data)
mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
for (b = buttons; b < END(buttons); b++) { for (b = buttons; b < END(buttons); b++) {
if (CLEANMASK(mods) == CLEANMASK(b->mod) && if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
@@ -721,7 +713,7 @@ index 44f3ad9..f74d9a1 100644
b->func(&b->arg); b->func(&b->arg);
return; return;
} }
@@ -655,6 +664,27 @@ buttonpress(struct wl_listener *listener, void *data) @@ -655,6 +663,21 @@ buttonpress(struct wl_listener *listener, void *data)
/* If you released any buttons, we exit interactive move/resize mode. */ /* If you released any buttons, we exit interactive move/resize mode. */
/* TODO: should reset to the pointer focus's current setcursor */ /* TODO: should reset to the pointer focus's current setcursor */
if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
@@ -736,20 +728,14 @@ index 44f3ad9..f74d9a1 100644
+ selmon->root = create_client_node(c); + selmon->root = create_client_node(c);
+ +
+ setfloating(c, 0); + setfloating(c, 0);
+ arrange(selmon); + apply_layout(selmon, selmon->root, selmon->w, 1);
+
+ } else if (cursor_mode == CurResize && !c->isfloating) {
+ resizing_from_mouse = 0;
+ } + }
+ } else {
+ if (cursor_mode == CurResize && resizing_from_mouse)
+ resizing_from_mouse = 0;
+ } + }
+ /* Default behaviour */ + /* Default behaviour */
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
cursor_mode = CurNormal; cursor_mode = CurNormal;
/* Drop the window off on its new monitor */ /* Drop the window off on its new monitor */
@@ -746,6 +776,7 @@ cleanupmon(struct wl_listener *listener, void *data) @@ -746,6 +769,7 @@ cleanupmon(struct wl_listener *listener, void *data)
wlr_output_layout_remove(output_layout, m->wlr_output); wlr_output_layout_remove(output_layout, m->wlr_output);
wlr_scene_output_destroy(m->scene_output); wlr_scene_output_destroy(m->scene_output);
@@ -757,7 +743,7 @@ index 44f3ad9..f74d9a1 100644
closemon(m); closemon(m);
wlr_scene_node_destroy(&m->fullscreen_bg->node); wlr_scene_node_destroy(&m->fullscreen_bg->node);
free(m); free(m);
@@ -1090,6 +1121,7 @@ createmon(struct wl_listener *listener, void *data) @@ -1090,6 +1114,7 @@ createmon(struct wl_listener *listener, void *data)
wl_list_insert(&mons, &m->link); wl_list_insert(&mons, &m->link);
printstatus(); printstatus();
@@ -765,18 +751,25 @@ index 44f3ad9..f74d9a1 100644
/* The xdg-protocol specifies: /* The xdg-protocol specifies:
* *
@@ -1332,6 +1364,10 @@ destroynotify(struct wl_listener *listener, void *data) @@ -1329,9 +1354,17 @@ destroynotify(struct wl_listener *listener, void *data)
{
/* Called when the xdg_toplevel is destroyed. */
Client *c = wl_container_of(listener, c, destroy);
+ Monitor *mon;
wl_list_remove(&c->destroy.link); wl_list_remove(&c->destroy.link);
wl_list_remove(&c->set_title.link); wl_list_remove(&c->set_title.link);
wl_list_remove(&c->fullscreen.link); wl_list_remove(&c->fullscreen.link);
+ /* We check if the destroyed client was part of any tiled_list, to catch + /* We check if the destroyed client was part of any tiled_list, to catch
+ * client removals even if they would not be currently managed by btrtile */ + * client removals even if they would not be currently managed by btrtile */
+ if (selmon && selmon->root) + wl_list_for_each(mon, &mons, link) {
+ remove_client(selmon, c); + if (mon->root) {
+ remove_client(mon, c);
+ }
+ }
#ifdef XWAYLAND #ifdef XWAYLAND
if (c->type != XDGShell) { if (c->type != XDGShell) {
wl_list_remove(&c->activate.link); wl_list_remove(&c->activate.link);
@@ -1862,7 +1898,8 @@ void @@ -1862,7 +1895,8 @@ void
motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
double dx_unaccel, double dy_unaccel) double dx_unaccel, double dy_unaccel)
{ {
@@ -786,13 +779,13 @@ index 44f3ad9..f74d9a1 100644
Client *c = NULL, *w = NULL; Client *c = NULL, *w = NULL;
LayerSurface *l = NULL; LayerSurface *l = NULL;
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
@@ -1916,18 +1953,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d @@ -1916,18 +1950,55 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
/* Update drag icon's position */ /* Update drag icon's position */
wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
- /* If we are currently grabbing the mouse, handle and return */ - /* If we are currently grabbing the mouse, handle and return */
+ /* Skip if internal call or already resizing */ + /* Skip if internal call */
+ if (time == 0 && resizing_from_mouse) + if (time == 0)
+ goto focus; + goto focus;
+ +
+ tiled = grabc && !grabc->isfloating && !grabc->isfullscreen; + tiled = grabc && !grabc->isfloating && !grabc->isfullscreen;
@@ -814,7 +807,7 @@ index 44f3ad9..f74d9a1 100644
- resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, - resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
- .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1); - .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1);
- return; - return;
+ if (tiled && resizing_from_mouse) { + if (tiled) {
+ dx_total = cursor->x - resize_last_update_x; + dx_total = cursor->x - resize_last_update_x;
+ dy_total = cursor->y - resize_last_update_y; + dy_total = cursor->y - resize_last_update_y;
+ +
@@ -827,7 +820,6 @@ index 44f3ad9..f74d9a1 100644
+ a.f = (float)(dy_total * resize_factor); + a.f = (float)(dy_total * resize_factor);
+ setratio_v(&a); + setratio_v(&a);
+ } + }
+ arrange(selmon);
+ +
+ last_resize_time = time; + last_resize_time = time;
+ resize_last_update_x = cursor->x; + resize_last_update_x = cursor->x;
@@ -850,7 +842,7 @@ index 44f3ad9..f74d9a1 100644
/* If there's no client surface under the cursor, set the cursor image to a /* If there's no client surface under the cursor, set the cursor image to a
* default. This is what makes the cursor image appear when you move it * default. This is what makes the cursor image appear when you move it
* off of a client or over its border. */ * off of a client or over its border. */
@@ -1961,22 +2036,41 @@ moveresize(const Arg *arg) @@ -1961,22 +2032,40 @@ moveresize(const Arg *arg)
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
return; return;
@@ -885,7 +877,6 @@ index 44f3ad9..f74d9a1 100644
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); + wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
+ resize_last_update_x = cursor->x; + resize_last_update_x = cursor->x;
+ resize_last_update_y = cursor->y; + resize_last_update_y = cursor->y;
+ resizing_from_mouse = 1;
+ break; + break;
+ } + }
+ } else { + } else {
@@ -908,6 +899,21 @@ index 44f3ad9..f74d9a1 100644
} }
} }
@@ -2826,6 +2915,14 @@ unmapnotify(struct wl_listener *listener, void *data)
focusclient(focustop(selmon), 1);
}
} else {
+ /* btrtile remove clients for each monitor */
+ Monitor *mon;
+ wl_list_for_each(mon, &mons, link) {
+ if (mon->root) {
+ remove_client(mon, c);
+ }
+ }
+
wl_list_remove(&c->link);
setmon(c, NULL, 0);
wl_list_remove(&c->flink);
-- --
2.52.0 2.53.0
+161 -155
View File
@@ -1,21 +1,24 @@
From 1b75163aac16942a715c6fc0aaf5d508e778dd6a Mon Sep 17 00:00:00 2001 From 04d7c04a0a88e381df788e1ec60398d956543f8b Mon Sep 17 00:00:00 2001
From: julmajustus <julmajustus@tutanota.com> From: julmajustus <julmajustus@tutanota.com>
Date: Wed, 18 Mar 2026 20:23:00 +0200 Date: Mon, 22 Jun 2026 22:52:57 +0300
Subject: [PATCH] Refactor btrtile for v0.8 Subject: [PATCH] btrtile: bug fixes and readability improvement
- Removed client old_geom caching for simplicity fixes: https://codeberg.org/dwl/dwl-patches/issues/647
- Improved readability of the client sanitization loop inside btrtile function
- Removed fullscreened client's from the sanitization loop to retain their geometry
--- ---
btrtile.c | 565 +++++++++++++++++++++++++++++++++++++++++++++++++++ btrtile.c | 556 +++++++++++++++++++++++++++++++++++++++++++++++++++
config.def.h | 12 ++ config.def.h | 12 ++
dwl.c | 148 +++++++++++--- dwl.c | 151 +++++++++++---
3 files changed, 698 insertions(+), 27 deletions(-) 3 files changed, 692 insertions(+), 27 deletions(-)
create mode 100644 btrtile.c create mode 100644 btrtile.c
diff --git a/btrtile.c b/btrtile.c diff --git a/btrtile.c b/btrtile.c
new file mode 100644 new file mode 100644
index 0000000..d706c3b index 0000000..6055871
--- /dev/null --- /dev/null
+++ b/btrtile.c +++ b/btrtile.c
@@ -0,0 +1,565 @@ @@ -0,0 +1,556 @@
+/* ************************************************************************** */ +/* ************************************************************************** */
+/* @@@ @@@@@@@@ */ +/* @@@ @@@@@@@@ */
+/* @@@ @@@@@@@@@@ */ +/* @@@ @@@@@@@@@@ */
@@ -26,7 +29,7 @@ index 0000000..d706c3b
+/* By: julmajustus <julmajustus@tutanota.com> !!: !!:! !!! */ +/* By: julmajustus <julmajustus@tutanota.com> !!: !!:! !!! */
+/* ::! :!: !:! */ +/* ::! :!: !:! */
+/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */ +/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */
+/* Updated: 2026/03/18 20:26:17 by julmajustus : : : : : : */ +/* Updated: 2026/06/22 22:52:52 by julmajustus : : : : : : */
+/* */ +/* */
+/* ************************************************************************** */ +/* ************************************************************************** */
+ +
@@ -49,7 +52,8 @@ index 0000000..d706c3b
+static void destroy_node(LayoutNode *node); +static void destroy_node(LayoutNode *node);
+static void destroy_tree(Monitor *m); +static void destroy_tree(Monitor *m);
+static LayoutNode *find_client_node(LayoutNode *node, Client *c); +static LayoutNode *find_client_node(LayoutNode *node, Client *c);
+static LayoutNode *find_suitable_split(LayoutNode *start, unsigned int need_vert); +static LayoutNode *find_suitable_split(Monitor *m, LayoutNode *start,
+ unsigned int need_vertical, int focused_on_left);
+static void init_tree(Monitor *m); +static void init_tree(Monitor *m);
+static void insert_client(Monitor *m, Client *focused_client, Client *new_client); +static void insert_client(Monitor *m, Client *focused_client, Client *new_client);
+static LayoutNode *remove_client_node(LayoutNode *node, Client *c); +static LayoutNode *remove_client_node(LayoutNode *node, Client *c);
@@ -60,7 +64,6 @@ index 0000000..d706c3b
+static unsigned int visible_count(LayoutNode *node, Monitor *m); +static unsigned int visible_count(LayoutNode *node, Monitor *m);
+static Client *xytoclient(double x, double y); +static Client *xytoclient(double x, double y);
+ +
+static int resizing_from_mouse = 0;
+static double resize_last_update_x, resize_last_update_y; +static double resize_last_update_x, resize_last_update_y;
+static uint32_t last_resize_time = 0; +static uint32_t last_resize_time = 0;
+ +
@@ -82,7 +85,6 @@ index 0000000..d706c3b
+ if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen) + if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ return; + return;
+ resize(c, area, 0); + resize(c, area, 0);
+ c->old_geom = area;
+ return; + return;
+ } + }
+ +
@@ -147,15 +149,13 @@ index 0000000..d706c3b
+ LayoutNode *found; + LayoutNode *found;
+ struct wlr_box full_area; + struct wlr_box full_area;
+ +
+ if (!m || !m->root) + if (!m)
+ return; + return;
+ +
+ /* Remove non tiled clients from tree. */ + /* Remove floating and moved clients */
+ wl_list_for_each(c, &clients, link) { + wl_list_for_each(c, &clients, link) {
+ if (c->mon == m && !c->isfloating && !c->isfullscreen) { + if (c->mon != m || c->isfloating)
+ } else {
+ remove_client(m, c); + remove_client(m, c);
+ }
+ } + }
+ +
+ /* If no client is found under cursor, fallback to focustop(m) */ + /* If no client is found under cursor, fallback to focustop(m) */
@@ -249,30 +249,37 @@ index 0000000..d706c3b
+} +}
+ +
+LayoutNode * +LayoutNode *
+find_suitable_split(LayoutNode *start_node, unsigned int need_vertical) +find_suitable_split(Monitor *m, LayoutNode *start_node,
+ unsigned int need_vertical, int focused_on_left)
+{ +{
+ LayoutNode *n = start_node; + LayoutNode *n = start_node, *child = NULL;
+ /* if we started from a client node, jump to its parent: */ +
+ if (n && n->is_client_node) + if (!m)
+ return NULL;
+
+ if (n && n->is_client_node) {
+ child = n;
+ n = n->split_node; + n = n->split_node;
+ }
+ +
+ while (n) { + while (n) {
+ if (!n->is_client_node && n->is_split_vertically == need_vertical && + if (!n->is_client_node && n->is_split_vertically == need_vertical
+ visible_count(n->left, selmon) > 0 && visible_count(n->right, selmon) > 0) + && visible_count(n->left, m) > 0
+ return n; + && visible_count(n->right, m) > 0) {
+ if ((focused_on_left && n->left == child) ||
+ (!focused_on_left && n->right == child))
+ return n;
+ }
+ child = n;
+ n = n->split_node; + n = n->split_node;
+ } + }
+ return NULL; + return NULL;
+} +}
+
+void +void
+init_tree(Monitor *m) +init_tree(Monitor *m)
+{ +{
+ if (!m) + if (m)
+ return; + m->root = NULL;
+ m->root = calloc(1, sizeof(LayoutNode));
+ if (!m->root)
+ m->root = NULL;
+} +}
+ +
+void +void
@@ -396,21 +403,31 @@ index 0000000..d706c3b
+ m->root = remove_client_node(m->root, c); + m->root = remove_client_node(m->root, c);
+} +}
+ +
+void +static void
+setratio_h(const Arg *arg) +setratio(unsigned int need_vertical, const Arg *arg)
+{ +{
+ Client *sel = focustop(selmon); + Client *sel;
+ LayoutNode *client_node, *split_node; + LayoutNode *client_node, *split_node;
+ float new_ratio; + float new_ratio;
+ int focused_on_left;
+ +
+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange) + if (!selmon || !selmon->lt[selmon->sellt]->arrange)
+ return;
+
+ sel = focustop(selmon);
+ if (!sel)
+ return; + return;
+ +
+ client_node = find_client_node(selmon->root, sel); + client_node = find_client_node(selmon->root, sel);
+ if (!client_node) + if (!client_node)
+ return; + return;
+ +
+ split_node = find_suitable_split(client_node, 1); + focused_on_left = (arg->f >= 0.0f);
+
+ split_node = find_suitable_split(selmon, client_node, need_vertical, focused_on_left);
+
+ if (!split_node)
+ split_node = find_suitable_split(selmon, client_node, need_vertical, !focused_on_left);
+ if (!split_node) + if (!split_node)
+ return; + return;
+ +
@@ -421,109 +438,85 @@ index 0000000..d706c3b
+ new_ratio = 0.95f; + new_ratio = 0.95f;
+ split_node->split_ratio = new_ratio; + split_node->split_ratio = new_ratio;
+ +
+ /* Skip the arrange if done resizing by mouse, + apply_layout(selmon, selmon->root, selmon->w, 1);
+ * we call arrange from motionotify */ +}
+ if (!resizing_from_mouse) { +
+ arrange(selmon); +void
+ } +setratio_h(const Arg *arg)
+{
+ setratio(1, arg);
+} +}
+ +
+void +void
+setratio_v(const Arg *arg) +setratio_v(const Arg *arg)
+{ +{
+ Client *sel = focustop(selmon); + setratio(0, arg);
+ LayoutNode *client_node, *split_node;
+ float new_ratio;
+
+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange)
+ return;
+
+ client_node = find_client_node(selmon->root, sel);
+ if (!client_node)
+ return;
+
+ split_node = find_suitable_split(client_node, 0);
+ if (!split_node)
+ return;
+
+ new_ratio = (arg->f != 0.0f) ? (split_node->split_ratio + arg->f) : 0.5f;
+ if (new_ratio < 0.05f)
+ new_ratio = 0.05f;
+ if (new_ratio > 0.95f)
+ new_ratio = 0.95f;
+ split_node->split_ratio = new_ratio;
+
+ /* Skip the arrange if done resizing by mouse,
+ * we call arrange from motionotify */
+ if (!resizing_from_mouse) {
+ arrange(selmon);
+ }
+} +}
+ +
+void swapclients(const Arg *arg) { +void swapclients(const Arg *arg) {
+ Client *c, *tmp, *target = NULL, *sel = focustop(selmon); + Client *c, *tmp, *target = NULL, *sel = focustop(selmon);
+ LayoutNode *sel_node, *target_node; + LayoutNode *sel_node, *target_node;
+ int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y, + int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y,
+ cand_center_x, cand_center_y; + cand_center_x, cand_center_y;
+ +
+ if (!sel || sel->isfullscreen || + if (!sel || sel->isfullscreen ||
+ !selmon->root || !selmon->lt[selmon->sellt]->arrange) + !selmon->root || !selmon->lt[selmon->sellt]->arrange)
+ return; + return;
+ +
+ +
+ /* Get the center coordinates of the selected client */ + /* Get the center coordinates of the selected client */
+ sel_center_x = sel->geom.x + sel->geom.width / 2; + sel_center_x = sel->geom.x + sel->geom.width / 2;
+ sel_center_y = sel->geom.y + sel->geom.height / 2; + sel_center_y = sel->geom.y + sel->geom.height / 2;
+ +
+ wl_list_for_each(c, &clients, link) { + wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel) + if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel)
+ continue; + continue;
+ +
+ /* Get the center of candidate client */ + /* Get the center of candidate client */
+ cand_center_x = c->geom.x + c->geom.width / 2; + cand_center_x = c->geom.x + c->geom.width / 2;
+ cand_center_y = c->geom.y + c->geom.height / 2; + cand_center_y = c->geom.y + c->geom.height / 2;
+ +
+ /* Check that the candidate lies in the requested direction. */ + /* Check that the candidate lies in the requested direction. */
+ switch (arg->ui) { + switch (arg->ui) {
+ case 0: + case 0:
+ if (cand_center_x >= sel_center_x) + if (cand_center_x >= sel_center_x)
+ continue; + continue;
+ break; + break;
+ case 1: + case 1:
+ if (cand_center_x <= sel_center_x) + if (cand_center_x <= sel_center_x)
+ continue; + continue;
+ break; + break;
+ case 2: + case 2:
+ if (cand_center_y >= sel_center_y) + if (cand_center_y >= sel_center_y)
+ continue; + continue;
+ break; + break;
+ case 3: + case 3:
+ if (cand_center_y <= sel_center_y) + if (cand_center_y <= sel_center_y)
+ continue; + continue;
+ break; + break;
+ default: + default:
+ continue; + continue;
+ } + }
+ +
+ /* Get distance between the centers */ + /* Get distance between the centers */
+ dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y); + dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y);
+ if (dist < closest_dist) { + if (dist < closest_dist) {
+ closest_dist = dist; + closest_dist = dist;
+ target = c; + target = c;
+ } + }
+ } + }
+ +
+ /* If target is found, swap the two clients positions in the layout tree */ + /* If target is found, swap the two clients positions in the layout tree */
+ if (target) { + if (target) {
+ sel_node = find_client_node(selmon->root, sel); + sel_node = find_client_node(selmon->root, sel);
+ target_node = find_client_node(selmon->root, target); + target_node = find_client_node(selmon->root, target);
+ if (sel_node && target_node) { + if (sel_node && target_node) {
+ tmp = sel_node->client; + tmp = sel_node->client;
+ sel_node->client = target_node->client; + sel_node->client = target_node->client;
+ target_node->client = tmp; + target_node->client = tmp;
+ arrange(selmon); + arrange(selmon);
+ } + }
+ } + }
+} +}
+ +
+unsigned int +unsigned int
@@ -546,11 +539,12 @@ index 0000000..d706c3b
+ +
+Client * +Client *
+xytoclient(double x, double y) { +xytoclient(double x, double y) {
+ Monitor *m = xytomon(x, y);
+ Client *c, *closest = NULL; + Client *c, *closest = NULL;
+ double dist, mindist = INT_MAX, dx, dy; + double dist, mindist = INT_MAX, dx, dy;
+ +
+ wl_list_for_each_reverse(c, &clients, link) { + wl_list_for_each_reverse(c, &clients, link) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen && + if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen &&
+ x >= c->geom.x && x <= (c->geom.x + c->geom.width) && + x >= c->geom.x && x <= (c->geom.x + c->geom.width) &&
+ y >= c->geom.y && y <= (c->geom.y + c->geom.height)){ + y >= c->geom.y && y <= (c->geom.y + c->geom.height)){
+ return c; + return c;
@@ -559,7 +553,7 @@ index 0000000..d706c3b
+ +
+ /* If no client was found at cursor position fallback to closest. */ + /* If no client was found at cursor position fallback to closest. */
+ wl_list_for_each_reverse(c, &clients, link) { + wl_list_for_each_reverse(c, &clients, link) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen) { + if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) {
+ dx = 0, dy = 0; + dx = 0, dy = 0;
+ +
+ if (x < c->geom.x) + if (x < c->geom.x)
@@ -572,7 +566,7 @@ index 0000000..d706c3b
+ else if (y > (c->geom.y + c->geom.height)) + else if (y > (c->geom.y + c->geom.height))
+ dy = y - (c->geom.y + c->geom.height); + dy = y - (c->geom.y + c->geom.height);
+ +
+ dist = sqrt(dx * dx + dy * dy); + dist = dx * dx + dy * dy;
+ if (dist < mindist) { + if (dist < mindist) {
+ mindist = dist; + mindist = dist;
+ closest = c; + closest = c;
@@ -620,7 +614,7 @@ index 8a6eda0..bc04e3f 100644
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
diff --git a/dwl.c b/dwl.c diff --git a/dwl.c b/dwl.c
index 44f3ad9..f74d9a1 100644 index 44f3ad9..2529e1f 100644
--- a/dwl.c --- a/dwl.c
+++ b/dwl.c +++ b/dwl.c
@@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
@@ -639,18 +633,16 @@ index 44f3ad9..f74d9a1 100644
typedef struct Monitor Monitor; typedef struct Monitor Monitor;
typedef struct { typedef struct {
/* Must keep this field first */ /* Must keep this field first */
@@ -137,8 +139,9 @@ typedef struct { @@ -137,7 +139,7 @@ typedef struct {
#endif #endif
unsigned int bw; unsigned int bw;
uint32_t tags; uint32_t tags;
- int isfloating, isurgent, isfullscreen; - int isfloating, isurgent, isfullscreen;
+ int isfloating, isurgent, isfullscreen, was_tiled; + int isfloating, isurgent, isfullscreen, was_tiled;
uint32_t resize; /* configure serial of a pending resize */ uint32_t resize; /* configure serial of a pending resize */
+ struct wlr_box old_geom;
} Client; } Client;
typedef struct { @@ -205,6 +207,7 @@ struct Monitor {
@@ -205,6 +208,7 @@ struct Monitor {
int nmaster; int nmaster;
char ltsymbol[16]; char ltsymbol[16];
int asleep; int asleep;
@@ -658,7 +650,7 @@ index 44f3ad9..f74d9a1 100644
}; };
typedef struct { typedef struct {
@@ -247,6 +251,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list, @@ -247,6 +250,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
struct wlr_box *usable_area, int exclusive); struct wlr_box *usable_area, int exclusive);
static void arrangelayers(Monitor *m); static void arrangelayers(Monitor *m);
static void axisnotify(struct wl_listener *listener, void *data); static void axisnotify(struct wl_listener *listener, void *data);
@@ -666,7 +658,7 @@ index 44f3ad9..f74d9a1 100644
static void buttonpress(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg); static void chvt(const Arg *arg);
static void checkidleinhibitor(struct wlr_surface *exclude); static void checkidleinhibitor(struct wlr_surface *exclude);
@@ -329,6 +334,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags); @@ -329,6 +333,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags);
static void setpsel(struct wl_listener *listener, void *data); static void setpsel(struct wl_listener *listener, void *data);
static void setsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data);
static void setup(void); static void setup(void);
@@ -676,7 +668,7 @@ index 44f3ad9..f74d9a1 100644
static void spawn(const Arg *arg); static void spawn(const Arg *arg);
static void startdrag(struct wl_listener *listener, void *data); static void startdrag(struct wl_listener *listener, void *data);
static void tag(const Arg *arg); static void tag(const Arg *arg);
@@ -454,6 +462,7 @@ static struct wlr_xwayland *xwayland; @@ -454,6 +461,7 @@ static struct wlr_xwayland *xwayland;
/* attempt to encapsulate suck into one file */ /* attempt to encapsulate suck into one file */
#include "client.h" #include "client.h"
@@ -684,7 +676,7 @@ index 44f3ad9..f74d9a1 100644
/* function implementations */ /* function implementations */
void void
@@ -624,7 +633,7 @@ buttonpress(struct wl_listener *listener, void *data) @@ -624,7 +632,7 @@ buttonpress(struct wl_listener *listener, void *data)
struct wlr_pointer_button_event *event = data; struct wlr_pointer_button_event *event = data;
struct wlr_keyboard *keyboard; struct wlr_keyboard *keyboard;
uint32_t mods; uint32_t mods;
@@ -693,7 +685,7 @@ index 44f3ad9..f74d9a1 100644
const Button *b; const Button *b;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
@@ -645,7 +654,7 @@ buttonpress(struct wl_listener *listener, void *data) @@ -645,7 +653,7 @@ buttonpress(struct wl_listener *listener, void *data)
mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
for (b = buttons; b < END(buttons); b++) { for (b = buttons; b < END(buttons); b++) {
if (CLEANMASK(mods) == CLEANMASK(b->mod) && if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
@@ -702,7 +694,7 @@ index 44f3ad9..f74d9a1 100644
b->func(&b->arg); b->func(&b->arg);
return; return;
} }
@@ -655,6 +664,27 @@ buttonpress(struct wl_listener *listener, void *data) @@ -655,6 +663,21 @@ buttonpress(struct wl_listener *listener, void *data)
/* If you released any buttons, we exit interactive move/resize mode. */ /* If you released any buttons, we exit interactive move/resize mode. */
/* TODO: should reset to the pointer focus's current setcursor */ /* TODO: should reset to the pointer focus's current setcursor */
if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
@@ -717,20 +709,14 @@ index 44f3ad9..f74d9a1 100644
+ selmon->root = create_client_node(c); + selmon->root = create_client_node(c);
+ +
+ setfloating(c, 0); + setfloating(c, 0);
+ arrange(selmon); + apply_layout(selmon, selmon->root, selmon->w, 1);
+
+ } else if (cursor_mode == CurResize && !c->isfloating) {
+ resizing_from_mouse = 0;
+ } + }
+ } else {
+ if (cursor_mode == CurResize && resizing_from_mouse)
+ resizing_from_mouse = 0;
+ } + }
+ /* Default behaviour */ + /* Default behaviour */
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
cursor_mode = CurNormal; cursor_mode = CurNormal;
/* Drop the window off on its new monitor */ /* Drop the window off on its new monitor */
@@ -746,6 +776,7 @@ cleanupmon(struct wl_listener *listener, void *data) @@ -746,6 +769,7 @@ cleanupmon(struct wl_listener *listener, void *data)
wlr_output_layout_remove(output_layout, m->wlr_output); wlr_output_layout_remove(output_layout, m->wlr_output);
wlr_scene_output_destroy(m->scene_output); wlr_scene_output_destroy(m->scene_output);
@@ -738,7 +724,7 @@ index 44f3ad9..f74d9a1 100644
closemon(m); closemon(m);
wlr_scene_node_destroy(&m->fullscreen_bg->node); wlr_scene_node_destroy(&m->fullscreen_bg->node);
free(m); free(m);
@@ -1090,6 +1121,7 @@ createmon(struct wl_listener *listener, void *data) @@ -1090,6 +1114,7 @@ createmon(struct wl_listener *listener, void *data)
wl_list_insert(&mons, &m->link); wl_list_insert(&mons, &m->link);
printstatus(); printstatus();
@@ -746,18 +732,25 @@ index 44f3ad9..f74d9a1 100644
/* The xdg-protocol specifies: /* The xdg-protocol specifies:
* *
@@ -1332,6 +1364,10 @@ destroynotify(struct wl_listener *listener, void *data) @@ -1329,9 +1354,17 @@ destroynotify(struct wl_listener *listener, void *data)
{
/* Called when the xdg_toplevel is destroyed. */
Client *c = wl_container_of(listener, c, destroy);
+ Monitor *mon;
wl_list_remove(&c->destroy.link); wl_list_remove(&c->destroy.link);
wl_list_remove(&c->set_title.link); wl_list_remove(&c->set_title.link);
wl_list_remove(&c->fullscreen.link); wl_list_remove(&c->fullscreen.link);
+ /* We check if the destroyed client was part of any tiled_list, to catch + /* We check if the destroyed client was part of any tiled_list, to catch
+ * client removals even if they would not be currently managed by btrtile */ + * client removals even if they would not be currently managed by btrtile */
+ if (selmon && selmon->root) + wl_list_for_each(mon, &mons, link) {
+ remove_client(selmon, c); + if (mon->root) {
+ remove_client(mon, c);
+ }
+ }
#ifdef XWAYLAND #ifdef XWAYLAND
if (c->type != XDGShell) { if (c->type != XDGShell) {
wl_list_remove(&c->activate.link); wl_list_remove(&c->activate.link);
@@ -1862,7 +1898,8 @@ void @@ -1862,7 +1895,8 @@ void
motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
double dx_unaccel, double dy_unaccel) double dx_unaccel, double dy_unaccel)
{ {
@@ -767,13 +760,13 @@ index 44f3ad9..f74d9a1 100644
Client *c = NULL, *w = NULL; Client *c = NULL, *w = NULL;
LayerSurface *l = NULL; LayerSurface *l = NULL;
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
@@ -1916,18 +1953,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d @@ -1916,18 +1950,55 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
/* Update drag icon's position */ /* Update drag icon's position */
wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
- /* If we are currently grabbing the mouse, handle and return */ - /* If we are currently grabbing the mouse, handle and return */
+ /* Skip if internal call or already resizing */ + /* Skip if internal call */
+ if (time == 0 && resizing_from_mouse) + if (time == 0)
+ goto focus; + goto focus;
+ +
+ tiled = grabc && !grabc->isfloating && !grabc->isfullscreen; + tiled = grabc && !grabc->isfloating && !grabc->isfullscreen;
@@ -795,7 +788,7 @@ index 44f3ad9..f74d9a1 100644
- resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, - resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
- .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1); - .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1);
- return; - return;
+ if (tiled && resizing_from_mouse) { + if (tiled) {
+ dx_total = cursor->x - resize_last_update_x; + dx_total = cursor->x - resize_last_update_x;
+ dy_total = cursor->y - resize_last_update_y; + dy_total = cursor->y - resize_last_update_y;
+ +
@@ -808,7 +801,6 @@ index 44f3ad9..f74d9a1 100644
+ a.f = (float)(dy_total * resize_factor); + a.f = (float)(dy_total * resize_factor);
+ setratio_v(&a); + setratio_v(&a);
+ } + }
+ arrange(selmon);
+ +
+ last_resize_time = time; + last_resize_time = time;
+ resize_last_update_x = cursor->x; + resize_last_update_x = cursor->x;
@@ -831,7 +823,7 @@ index 44f3ad9..f74d9a1 100644
/* If there's no client surface under the cursor, set the cursor image to a /* If there's no client surface under the cursor, set the cursor image to a
* default. This is what makes the cursor image appear when you move it * default. This is what makes the cursor image appear when you move it
* off of a client or over its border. */ * off of a client or over its border. */
@@ -1961,22 +2036,41 @@ moveresize(const Arg *arg) @@ -1961,22 +2032,40 @@ moveresize(const Arg *arg)
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
return; return;
@@ -866,7 +858,6 @@ index 44f3ad9..f74d9a1 100644
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); + wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
+ resize_last_update_x = cursor->x; + resize_last_update_x = cursor->x;
+ resize_last_update_y = cursor->y; + resize_last_update_y = cursor->y;
+ resizing_from_mouse = 1;
+ break; + break;
+ } + }
+ } else { + } else {
@@ -889,6 +880,21 @@ index 44f3ad9..f74d9a1 100644
} }
} }
@@ -2826,6 +2915,14 @@ unmapnotify(struct wl_listener *listener, void *data)
focusclient(focustop(selmon), 1);
}
} else {
+ /* btrtile remove clients for each monitor */
+ Monitor *mon;
+ wl_list_for_each(mon, &mons, link) {
+ if (mon->root) {
+ remove_client(mon, c);
+ }
+ }
+
wl_list_remove(&c->link);
setmon(c, NULL, 0);
wl_list_remove(&c->flink);
-- --
2.52.0 2.53.0
@@ -1,21 +1,26 @@
From a9329abe48083a4c5b64eee726ceefdb4bf0bb9f Mon Sep 17 00:00:00 2001 From efb796ce92e7212aa392141705c7322fedc45dba Mon Sep 17 00:00:00 2001
From: julmajustus <julmajustus@tutanota.com> From: julmajustus <julmajustus@tutanota.com>
Date: Thu, 19 Mar 2026 22:59:49 +0200 Date: Mon, 22 Jun 2026 22:46:19 +0300
Subject: [PATCH] Refactor btrtile-gaps to wlroots-next Subject: [PATCH] btrtile: bug fixes and readability improvement
- Removed client old_geom caching for simplicity fixes: https://codeberg.org/dwl/dwl-patches/issues/647
- Improved readability of the client sanitization loop inside btrtile function
- Removed fullscreened client's from the sanitization loop to retain simple_scratchpad: Spring update scratchpad V2
- Added support for multiple scratchpads their geometry
--- ---
btrtile.c | 584 +++++++++++++++++++++++++++++++++++++++++++++++++++ btrtile.c | 575 +++++++++++++++++++++++++++++++++++++++++++++++++++
config.def.h | 12 ++ config.def.h | 12 ++
dwl.c | 148 ++++++++++--- dwl.c | 151 +++++++++++---
3 files changed, 717 insertions(+), 27 deletions(-) 3 files changed, 711 insertions(+), 27 deletions(-)
create mode 100644 btrtile.c create mode 100644 btrtile.c
diff --git a/btrtile.c b/btrtile.c diff --git a/btrtile.c b/btrtile.c
new file mode 100644 new file mode 100644
index 0000000..8703617 index 0000000..645b741
--- /dev/null --- /dev/null
+++ b/btrtile.c +++ b/btrtile.c
@@ -0,0 +1,584 @@ @@ -0,0 +1,575 @@
+/* ************************************************************************** */ +/* ************************************************************************** */
+/* @@@ @@@@@@@@ */ +/* @@@ @@@@@@@@ */
+/* @@@ @@@@@@@@@@ */ +/* @@@ @@@@@@@@@@ */
@@ -26,7 +31,7 @@ index 0000000..8703617
+/* By: julmajustus <julmajustus@tutanota.com> !!: !!:! !!! */ +/* By: julmajustus <julmajustus@tutanota.com> !!: !!:! !!! */
+/* ::! :!: !:! */ +/* ::! :!: !:! */
+/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */ +/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */
+/* Updated: 2026/03/19 22:59:12 by julmajustus : : : : : : */ +/* Updated: 2026/06/22 22:34:11 by julmajustus : : : : : : */
+/* */ +/* */
+/* ************************************************************************** */ +/* ************************************************************************** */
+ +
@@ -49,7 +54,8 @@ index 0000000..8703617
+static void destroy_node(LayoutNode *node); +static void destroy_node(LayoutNode *node);
+static void destroy_tree(Monitor *m); +static void destroy_tree(Monitor *m);
+static LayoutNode *find_client_node(LayoutNode *node, Client *c); +static LayoutNode *find_client_node(LayoutNode *node, Client *c);
+static LayoutNode *find_suitable_split(LayoutNode *start, unsigned int need_vert); +static LayoutNode *find_suitable_split(Monitor *m, LayoutNode *start,
+ unsigned int need_vertical, int focused_on_left);
+static void init_tree(Monitor *m); +static void init_tree(Monitor *m);
+static void insert_client(Monitor *m, Client *focused_client, Client *new_client); +static void insert_client(Monitor *m, Client *focused_client, Client *new_client);
+static LayoutNode *remove_client_node(LayoutNode *node, Client *c); +static LayoutNode *remove_client_node(LayoutNode *node, Client *c);
@@ -60,7 +66,6 @@ index 0000000..8703617
+static unsigned int visible_count(LayoutNode *node, Monitor *m); +static unsigned int visible_count(LayoutNode *node, Monitor *m);
+static Client *xytoclient(double x, double y); +static Client *xytoclient(double x, double y);
+ +
+static int resizing_from_mouse = 0;
+static double resize_last_update_x, resize_last_update_y; +static double resize_last_update_x, resize_last_update_y;
+static uint32_t last_resize_time = 0; +static uint32_t last_resize_time = 0;
+ +
@@ -89,7 +94,6 @@ index 0000000..8703617
+ if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen) + if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ return; + return;
+ resize(c, area, 0); + resize(c, area, 0);
+ c->old_geom = area;
+ return; + return;
+ } + }
+ +
@@ -129,10 +133,10 @@ index 0000000..8703617
+ right_area.width = area.width - mid; + right_area.width = area.width - mid;
+ right_area.height = area.height; + right_area.height = area.height;
+ +
+ if (e) { + if (e) {
+ left_area.width -= gappx / 2; + left_area.width -= gappx / 2;
+ right_area.x += gappx / 2; + right_area.x += gappx / 2;
+ right_area.width -= gappx / 2; + right_area.width -= gappx / 2;
+ } + }
+ } else { + } else {
+ /* horizontal split */ + /* horizontal split */
@@ -166,15 +170,13 @@ index 0000000..8703617
+ LayoutNode *found; + LayoutNode *found;
+ struct wlr_box full_area; + struct wlr_box full_area;
+ +
+ if (!m || !m->root) + if (!m)
+ return; + return;
+ +
+ /* Remove non tiled clients from tree. */ + /* Remove floating and moved clients */
+ wl_list_for_each(c, &clients, link) { + wl_list_for_each(c, &clients, link) {
+ if (c->mon == m && !c->isfloating && !c->isfullscreen) { + if (c->mon != m || c->isfloating)
+ } else {
+ remove_client(m, c); + remove_client(m, c);
+ }
+ } + }
+ +
+ /* If no client is found under cursor, fallback to focustop(m) */ + /* If no client is found under cursor, fallback to focustop(m) */
@@ -268,30 +270,37 @@ index 0000000..8703617
+} +}
+ +
+LayoutNode * +LayoutNode *
+find_suitable_split(LayoutNode *start_node, unsigned int need_vertical) +find_suitable_split(Monitor *m, LayoutNode *start_node,
+ unsigned int need_vertical, int focused_on_left)
+{ +{
+ LayoutNode *n = start_node; + LayoutNode *n = start_node, *child = NULL;
+ /* if we started from a client node, jump to its parent: */ +
+ if (n && n->is_client_node) + if (!m)
+ return NULL;
+
+ if (n && n->is_client_node) {
+ child = n;
+ n = n->split_node; + n = n->split_node;
+ }
+ +
+ while (n) { + while (n) {
+ if (!n->is_client_node && n->is_split_vertically == need_vertical && + if (!n->is_client_node && n->is_split_vertically == need_vertical
+ visible_count(n->left, selmon) > 0 && visible_count(n->right, selmon) > 0) + && visible_count(n->left, m) > 0
+ return n; + && visible_count(n->right, m) > 0) {
+ if ((focused_on_left && n->left == child) ||
+ (!focused_on_left && n->right == child))
+ return n;
+ }
+ child = n;
+ n = n->split_node; + n = n->split_node;
+ } + }
+ return NULL; + return NULL;
+} +}
+
+void +void
+init_tree(Monitor *m) +init_tree(Monitor *m)
+{ +{
+ if (!m) + if (m)
+ return; + m->root = NULL;
+ m->root = calloc(1, sizeof(LayoutNode));
+ if (!m->root)
+ m->root = NULL;
+} +}
+ +
+void +void
@@ -415,21 +424,31 @@ index 0000000..8703617
+ m->root = remove_client_node(m->root, c); + m->root = remove_client_node(m->root, c);
+} +}
+ +
+void +static void
+setratio_h(const Arg *arg) +setratio(unsigned int need_vertical, const Arg *arg)
+{ +{
+ Client *sel = focustop(selmon); + Client *sel;
+ LayoutNode *client_node, *split_node; + LayoutNode *client_node, *split_node;
+ float new_ratio; + float new_ratio;
+ int focused_on_left;
+ +
+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange) + if (!selmon || !selmon->lt[selmon->sellt]->arrange)
+ return;
+
+ sel = focustop(selmon);
+ if (!sel)
+ return; + return;
+ +
+ client_node = find_client_node(selmon->root, sel); + client_node = find_client_node(selmon->root, sel);
+ if (!client_node) + if (!client_node)
+ return; + return;
+ +
+ split_node = find_suitable_split(client_node, 1); + focused_on_left = (arg->f >= 0.0f);
+
+ split_node = find_suitable_split(selmon, client_node, need_vertical, focused_on_left);
+
+ if (!split_node)
+ split_node = find_suitable_split(selmon, client_node, need_vertical, !focused_on_left);
+ if (!split_node) + if (!split_node)
+ return; + return;
+ +
@@ -440,109 +459,85 @@ index 0000000..8703617
+ new_ratio = 0.95f; + new_ratio = 0.95f;
+ split_node->split_ratio = new_ratio; + split_node->split_ratio = new_ratio;
+ +
+ /* Skip the arrange if done resizing by mouse, + apply_layout(selmon, selmon->root, selmon->w, 1);
+ * we call arrange from motionotify */ +}
+ if (!resizing_from_mouse) { +
+ arrange(selmon); +void
+ } +setratio_h(const Arg *arg)
+{
+ setratio(1, arg);
+} +}
+ +
+void +void
+setratio_v(const Arg *arg) +setratio_v(const Arg *arg)
+{ +{
+ Client *sel = focustop(selmon); + setratio(0, arg);
+ LayoutNode *client_node, *split_node;
+ float new_ratio;
+
+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange)
+ return;
+
+ client_node = find_client_node(selmon->root, sel);
+ if (!client_node)
+ return;
+
+ split_node = find_suitable_split(client_node, 0);
+ if (!split_node)
+ return;
+
+ new_ratio = (arg->f != 0.0f) ? (split_node->split_ratio + arg->f) : 0.5f;
+ if (new_ratio < 0.05f)
+ new_ratio = 0.05f;
+ if (new_ratio > 0.95f)
+ new_ratio = 0.95f;
+ split_node->split_ratio = new_ratio;
+
+ /* Skip the arrange if done resizing by mouse,
+ * we call arrange from motionotify */
+ if (!resizing_from_mouse) {
+ arrange(selmon);
+ }
+} +}
+ +
+void swapclients(const Arg *arg) { +void swapclients(const Arg *arg) {
+ Client *c, *tmp, *target = NULL, *sel = focustop(selmon); + Client *c, *tmp, *target = NULL, *sel = focustop(selmon);
+ LayoutNode *sel_node, *target_node; + LayoutNode *sel_node, *target_node;
+ int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y, + int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y,
+ cand_center_x, cand_center_y; + cand_center_x, cand_center_y;
+ +
+ if (!sel || sel->isfullscreen || + if (!sel || sel->isfullscreen ||
+ !selmon->root || !selmon->lt[selmon->sellt]->arrange) + !selmon->root || !selmon->lt[selmon->sellt]->arrange)
+ return; + return;
+ +
+ +
+ /* Get the center coordinates of the selected client */ + /* Get the center coordinates of the selected client */
+ sel_center_x = sel->geom.x + sel->geom.width / 2; + sel_center_x = sel->geom.x + sel->geom.width / 2;
+ sel_center_y = sel->geom.y + sel->geom.height / 2; + sel_center_y = sel->geom.y + sel->geom.height / 2;
+ +
+ wl_list_for_each(c, &clients, link) { + wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel) + if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel)
+ continue; + continue;
+ +
+ /* Get the center of candidate client */ + /* Get the center of candidate client */
+ cand_center_x = c->geom.x + c->geom.width / 2; + cand_center_x = c->geom.x + c->geom.width / 2;
+ cand_center_y = c->geom.y + c->geom.height / 2; + cand_center_y = c->geom.y + c->geom.height / 2;
+ +
+ /* Check that the candidate lies in the requested direction. */ + /* Check that the candidate lies in the requested direction. */
+ switch (arg->ui) { + switch (arg->ui) {
+ case 0: + case 0:
+ if (cand_center_x >= sel_center_x) + if (cand_center_x >= sel_center_x)
+ continue; + continue;
+ break; + break;
+ case 1: + case 1:
+ if (cand_center_x <= sel_center_x) + if (cand_center_x <= sel_center_x)
+ continue; + continue;
+ break; + break;
+ case 2: + case 2:
+ if (cand_center_y >= sel_center_y) + if (cand_center_y >= sel_center_y)
+ continue; + continue;
+ break; + break;
+ case 3: + case 3:
+ if (cand_center_y <= sel_center_y) + if (cand_center_y <= sel_center_y)
+ continue; + continue;
+ break; + break;
+ default: + default:
+ continue; + continue;
+ } + }
+ +
+ /* Get distance between the centers */ + /* Get distance between the centers */
+ dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y); + dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y);
+ if (dist < closest_dist) { + if (dist < closest_dist) {
+ closest_dist = dist; + closest_dist = dist;
+ target = c; + target = c;
+ } + }
+ } + }
+ +
+ /* If target is found, swap the two clients positions in the layout tree */ + /* If target is found, swap the two clients positions in the layout tree */
+ if (target) { + if (target) {
+ sel_node = find_client_node(selmon->root, sel); + sel_node = find_client_node(selmon->root, sel);
+ target_node = find_client_node(selmon->root, target); + target_node = find_client_node(selmon->root, target);
+ if (sel_node && target_node) { + if (sel_node && target_node) {
+ tmp = sel_node->client; + tmp = sel_node->client;
+ sel_node->client = target_node->client; + sel_node->client = target_node->client;
+ target_node->client = tmp; + target_node->client = tmp;
+ arrange(selmon); + arrange(selmon);
+ } + }
+ } + }
+} +}
+ +
+unsigned int +unsigned int
@@ -565,11 +560,12 @@ index 0000000..8703617
+ +
+Client * +Client *
+xytoclient(double x, double y) { +xytoclient(double x, double y) {
+ Monitor *m = xytomon(x, y);
+ Client *c, *closest = NULL; + Client *c, *closest = NULL;
+ double dist, mindist = INT_MAX, dx, dy; + double dist, mindist = INT_MAX, dx, dy;
+ +
+ wl_list_for_each_reverse(c, &clients, link) { + wl_list_for_each_reverse(c, &clients, link) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen && + if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen &&
+ x >= c->geom.x && x <= (c->geom.x + c->geom.width) && + x >= c->geom.x && x <= (c->geom.x + c->geom.width) &&
+ y >= c->geom.y && y <= (c->geom.y + c->geom.height)){ + y >= c->geom.y && y <= (c->geom.y + c->geom.height)){
+ return c; + return c;
@@ -578,7 +574,7 @@ index 0000000..8703617
+ +
+ /* If no client was found at cursor position fallback to closest. */ + /* If no client was found at cursor position fallback to closest. */
+ wl_list_for_each_reverse(c, &clients, link) { + wl_list_for_each_reverse(c, &clients, link) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen) { + if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) {
+ dx = 0, dy = 0; + dx = 0, dy = 0;
+ +
+ if (x < c->geom.x) + if (x < c->geom.x)
@@ -591,7 +587,7 @@ index 0000000..8703617
+ else if (y > (c->geom.y + c->geom.height)) + else if (y > (c->geom.y + c->geom.height))
+ dy = y - (c->geom.y + c->geom.height); + dy = y - (c->geom.y + c->geom.height);
+ +
+ dist = sqrt(dx * dx + dy * dy); + dist = dx * dx + dy * dy;
+ if (dist < mindist) { + if (dist < mindist) {
+ mindist = dist; + mindist = dist;
+ closest = c; + closest = c;
@@ -639,7 +635,7 @@ index 8a6eda0..bc04e3f 100644
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
diff --git a/dwl.c b/dwl.c diff --git a/dwl.c b/dwl.c
index 8101ffa..e4e7074 100644 index 8101ffa..7abf390 100644
--- a/dwl.c --- a/dwl.c
+++ b/dwl.c +++ b/dwl.c
@@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
@@ -658,18 +654,16 @@ index 8101ffa..e4e7074 100644
typedef struct Monitor Monitor; typedef struct Monitor Monitor;
typedef struct { typedef struct {
/* Must keep this field first */ /* Must keep this field first */
@@ -141,8 +143,9 @@ typedef struct { @@ -141,7 +143,7 @@ typedef struct {
#endif #endif
unsigned int bw; unsigned int bw;
uint32_t tags; uint32_t tags;
- int isfloating, isurgent, isfullscreen; - int isfloating, isurgent, isfullscreen;
+ int isfloating, isurgent, isfullscreen, was_tiled; + int isfloating, isurgent, isfullscreen, was_tiled;
uint32_t resize; /* configure serial of a pending resize */ uint32_t resize; /* configure serial of a pending resize */
+ struct wlr_box old_geom;
} Client; } Client;
typedef struct { @@ -209,6 +211,7 @@ struct Monitor {
@@ -209,6 +212,7 @@ struct Monitor {
int nmaster; int nmaster;
char ltsymbol[16]; char ltsymbol[16];
int asleep; int asleep;
@@ -677,7 +671,7 @@ index 8101ffa..e4e7074 100644
}; };
typedef struct { typedef struct {
@@ -251,6 +255,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list, @@ -251,6 +254,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
struct wlr_box *usable_area, int exclusive); struct wlr_box *usable_area, int exclusive);
static void arrangelayers(Monitor *m); static void arrangelayers(Monitor *m);
static void axisnotify(struct wl_listener *listener, void *data); static void axisnotify(struct wl_listener *listener, void *data);
@@ -685,7 +679,7 @@ index 8101ffa..e4e7074 100644
static void buttonpress(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg); static void chvt(const Arg *arg);
static void checkidleinhibitor(struct wlr_surface *exclude); static void checkidleinhibitor(struct wlr_surface *exclude);
@@ -333,6 +338,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags); @@ -333,6 +337,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags);
static void setpsel(struct wl_listener *listener, void *data); static void setpsel(struct wl_listener *listener, void *data);
static void setsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data);
static void setup(void); static void setup(void);
@@ -695,7 +689,7 @@ index 8101ffa..e4e7074 100644
static void spawn(const Arg *arg); static void spawn(const Arg *arg);
static void startdrag(struct wl_listener *listener, void *data); static void startdrag(struct wl_listener *listener, void *data);
static void tag(const Arg *arg); static void tag(const Arg *arg);
@@ -458,6 +466,7 @@ static struct wlr_xwayland *xwayland; @@ -458,6 +465,7 @@ static struct wlr_xwayland *xwayland;
/* attempt to encapsulate suck into one file */ /* attempt to encapsulate suck into one file */
#include "client.h" #include "client.h"
@@ -703,7 +697,7 @@ index 8101ffa..e4e7074 100644
/* function implementations */ /* function implementations */
void void
@@ -628,7 +637,7 @@ buttonpress(struct wl_listener *listener, void *data) @@ -628,7 +636,7 @@ buttonpress(struct wl_listener *listener, void *data)
struct wlr_pointer_button_event *event = data; struct wlr_pointer_button_event *event = data;
struct wlr_keyboard *keyboard; struct wlr_keyboard *keyboard;
uint32_t mods; uint32_t mods;
@@ -712,7 +706,7 @@ index 8101ffa..e4e7074 100644
const Button *b; const Button *b;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
@@ -649,7 +658,7 @@ buttonpress(struct wl_listener *listener, void *data) @@ -649,7 +657,7 @@ buttonpress(struct wl_listener *listener, void *data)
mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
for (b = buttons; b < END(buttons); b++) { for (b = buttons; b < END(buttons); b++) {
if (CLEANMASK(mods) == CLEANMASK(b->mod) && if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
@@ -721,7 +715,7 @@ index 8101ffa..e4e7074 100644
b->func(&b->arg); b->func(&b->arg);
return; return;
} }
@@ -659,6 +668,27 @@ buttonpress(struct wl_listener *listener, void *data) @@ -659,6 +667,21 @@ buttonpress(struct wl_listener *listener, void *data)
/* If you released any buttons, we exit interactive move/resize mode. */ /* If you released any buttons, we exit interactive move/resize mode. */
/* TODO: should reset to the pointer focus's current setcursor */ /* TODO: should reset to the pointer focus's current setcursor */
if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
@@ -736,20 +730,14 @@ index 8101ffa..e4e7074 100644
+ selmon->root = create_client_node(c); + selmon->root = create_client_node(c);
+ +
+ setfloating(c, 0); + setfloating(c, 0);
+ arrange(selmon); + apply_layout(selmon, selmon->root, selmon->w, 1);
+
+ } else if (cursor_mode == CurResize && !c->isfloating) {
+ resizing_from_mouse = 0;
+ } + }
+ } else {
+ if (cursor_mode == CurResize && resizing_from_mouse)
+ resizing_from_mouse = 0;
+ } + }
+ /* Default behaviour */ + /* Default behaviour */
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
cursor_mode = CurNormal; cursor_mode = CurNormal;
/* Drop the window off on its new monitor */ /* Drop the window off on its new monitor */
@@ -750,6 +780,7 @@ cleanupmon(struct wl_listener *listener, void *data) @@ -750,6 +773,7 @@ cleanupmon(struct wl_listener *listener, void *data)
wlr_output_layout_remove(output_layout, m->wlr_output); wlr_output_layout_remove(output_layout, m->wlr_output);
wlr_scene_output_destroy(m->scene_output); wlr_scene_output_destroy(m->scene_output);
@@ -757,7 +745,7 @@ index 8101ffa..e4e7074 100644
closemon(m); closemon(m);
wlr_scene_node_destroy(&m->fullscreen_bg->node); wlr_scene_node_destroy(&m->fullscreen_bg->node);
free(m); free(m);
@@ -1094,6 +1125,7 @@ createmon(struct wl_listener *listener, void *data) @@ -1094,6 +1118,7 @@ createmon(struct wl_listener *listener, void *data)
wl_list_insert(&mons, &m->link); wl_list_insert(&mons, &m->link);
printstatus(); printstatus();
@@ -765,18 +753,26 @@ index 8101ffa..e4e7074 100644
/* The xdg-protocol specifies: /* The xdg-protocol specifies:
* *
@@ -1336,6 +1368,10 @@ destroynotify(struct wl_listener *listener, void *data) @@ -1332,10 +1357,18 @@ void
destroynotify(struct wl_listener *listener, void *data)
{
/* Called when the xdg_toplevel is destroyed. */
+ Monitor *mon;
Client *c = wl_container_of(listener, c, destroy);
wl_list_remove(&c->destroy.link); wl_list_remove(&c->destroy.link);
wl_list_remove(&c->set_title.link); wl_list_remove(&c->set_title.link);
wl_list_remove(&c->fullscreen.link); wl_list_remove(&c->fullscreen.link);
+ /* We check if the destroyed client was part of any tiled_list, to catch + /* We check if the destroyed client was part of any tiled_list, to catch
+ * client removals even if they would not be currently managed by btrtile */ + * client removals even if they would not be currently managed by btrtile */
+ if (selmon && selmon->root) + wl_list_for_each(mon, &mons, link) {
+ remove_client(selmon, c); + if (mon->root) {
+ remove_client(mon, c);
+ }
+ }
#ifdef XWAYLAND #ifdef XWAYLAND
if (c->type != XDGShell) { if (c->type != XDGShell) {
wl_list_remove(&c->activate.link); wl_list_remove(&c->activate.link);
@@ -1866,7 +1902,8 @@ void @@ -1866,7 +1899,8 @@ void
motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
double dx_unaccel, double dy_unaccel) double dx_unaccel, double dy_unaccel)
{ {
@@ -786,13 +782,13 @@ index 8101ffa..e4e7074 100644
Client *c = NULL, *w = NULL; Client *c = NULL, *w = NULL;
LayerSurface *l = NULL; LayerSurface *l = NULL;
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
@@ -1920,18 +1957,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d @@ -1920,18 +1954,55 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
/* Update drag icon's position */ /* Update drag icon's position */
wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
- /* If we are currently grabbing the mouse, handle and return */ - /* If we are currently grabbing the mouse, handle and return */
+ /* Skip if internal call or already resizing */ + /* Skip if internal call */
+ if (time == 0 && resizing_from_mouse) + if (time == 0)
+ goto focus; + goto focus;
+ +
+ tiled = grabc && !grabc->isfloating && !grabc->isfullscreen; + tiled = grabc && !grabc->isfloating && !grabc->isfullscreen;
@@ -814,7 +810,7 @@ index 8101ffa..e4e7074 100644
- resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, - resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
- .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1); - .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1);
- return; - return;
+ if (tiled && resizing_from_mouse) { + if (tiled) {
+ dx_total = cursor->x - resize_last_update_x; + dx_total = cursor->x - resize_last_update_x;
+ dy_total = cursor->y - resize_last_update_y; + dy_total = cursor->y - resize_last_update_y;
+ +
@@ -827,7 +823,6 @@ index 8101ffa..e4e7074 100644
+ a.f = (float)(dy_total * resize_factor); + a.f = (float)(dy_total * resize_factor);
+ setratio_v(&a); + setratio_v(&a);
+ } + }
+ arrange(selmon);
+ +
+ last_resize_time = time; + last_resize_time = time;
+ resize_last_update_x = cursor->x; + resize_last_update_x = cursor->x;
@@ -850,7 +845,7 @@ index 8101ffa..e4e7074 100644
/* If there's no client surface under the cursor, set the cursor image to a /* If there's no client surface under the cursor, set the cursor image to a
* default. This is what makes the cursor image appear when you move it * default. This is what makes the cursor image appear when you move it
* off of a client or over its border. */ * off of a client or over its border. */
@@ -1965,22 +2040,41 @@ moveresize(const Arg *arg) @@ -1965,22 +2036,40 @@ moveresize(const Arg *arg)
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
return; return;
@@ -885,7 +880,6 @@ index 8101ffa..e4e7074 100644
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); + wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
+ resize_last_update_x = cursor->x; + resize_last_update_x = cursor->x;
+ resize_last_update_y = cursor->y; + resize_last_update_y = cursor->y;
+ resizing_from_mouse = 1;
+ break; + break;
+ } + }
+ } else { + } else {
@@ -908,6 +902,21 @@ index 8101ffa..e4e7074 100644
} }
} }
@@ -2833,6 +2922,14 @@ unmapnotify(struct wl_listener *listener, void *data)
focusclient(focustop(selmon), 1);
}
} else {
+ /* btrtile remove clients for each monitor */
+ Monitor *mon;
+ wl_list_for_each(mon, &mons, link) {
+ if (mon->root) {
+ remove_client(mon, c);
+ }
+ }
+
wl_list_remove(&c->link);
setmon(c, NULL, 0);
wl_list_remove(&c->flink);
-- --
2.52.0 2.53.0
+161 -155
View File
@@ -1,21 +1,24 @@
From 912701a41b19c013a9adcb176a7b6503aa1cf9c1 Mon Sep 17 00:00:00 2001 From 3500e732fc34aa849cd3babeff7d2545a0230fa9 Mon Sep 17 00:00:00 2001
From: julmajustus <julmajustus@tutanota.com> From: julmajustus <julmajustus@tutanota.com>
Date: Thu, 19 Mar 2026 22:56:45 +0200 Date: Mon, 22 Jun 2026 22:33:17 +0300
Subject: [PATCH] Refactor btrtile to wlroots-next Subject: [PATCH] btrtile: bug fixes and readability improvement
- Removed client old_geom caching for simplicity fixes: https://codeberg.org/dwl/dwl-patches/issues/647
- Improved readability of the client sanitization loop inside btrtile function
- Removed fullscreened client's from the sanitization loop to retain their geometry
--- ---
btrtile.c | 565 +++++++++++++++++++++++++++++++++++++++++++++++++++ btrtile.c | 556 +++++++++++++++++++++++++++++++++++++++++++++++++++
config.def.h | 12 ++ config.def.h | 12 ++
dwl.c | 148 +++++++++++--- dwl.c | 151 +++++++++++---
3 files changed, 698 insertions(+), 27 deletions(-) 3 files changed, 692 insertions(+), 27 deletions(-)
create mode 100644 btrtile.c create mode 100644 btrtile.c
diff --git a/btrtile.c b/btrtile.c diff --git a/btrtile.c b/btrtile.c
new file mode 100644 new file mode 100644
index 0000000..d706c3b index 0000000..b4f6f43
--- /dev/null --- /dev/null
+++ b/btrtile.c +++ b/btrtile.c
@@ -0,0 +1,565 @@ @@ -0,0 +1,556 @@
+/* ************************************************************************** */ +/* ************************************************************************** */
+/* @@@ @@@@@@@@ */ +/* @@@ @@@@@@@@ */
+/* @@@ @@@@@@@@@@ */ +/* @@@ @@@@@@@@@@ */
@@ -26,7 +29,7 @@ index 0000000..d706c3b
+/* By: julmajustus <julmajustus@tutanota.com> !!: !!:! !!! */ +/* By: julmajustus <julmajustus@tutanota.com> !!: !!:! !!! */
+/* ::! :!: !:! */ +/* ::! :!: !:! */
+/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */ +/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */
+/* Updated: 2026/03/18 20:26:17 by julmajustus : : : : : : */ +/* Updated: 2026/06/22 22:30:19 by julmajustus : : : : : : */
+/* */ +/* */
+/* ************************************************************************** */ +/* ************************************************************************** */
+ +
@@ -49,7 +52,8 @@ index 0000000..d706c3b
+static void destroy_node(LayoutNode *node); +static void destroy_node(LayoutNode *node);
+static void destroy_tree(Monitor *m); +static void destroy_tree(Monitor *m);
+static LayoutNode *find_client_node(LayoutNode *node, Client *c); +static LayoutNode *find_client_node(LayoutNode *node, Client *c);
+static LayoutNode *find_suitable_split(LayoutNode *start, unsigned int need_vert); +static LayoutNode *find_suitable_split(Monitor *m, LayoutNode *start,
+ unsigned int need_vertical, int focused_on_left);
+static void init_tree(Monitor *m); +static void init_tree(Monitor *m);
+static void insert_client(Monitor *m, Client *focused_client, Client *new_client); +static void insert_client(Monitor *m, Client *focused_client, Client *new_client);
+static LayoutNode *remove_client_node(LayoutNode *node, Client *c); +static LayoutNode *remove_client_node(LayoutNode *node, Client *c);
@@ -60,7 +64,6 @@ index 0000000..d706c3b
+static unsigned int visible_count(LayoutNode *node, Monitor *m); +static unsigned int visible_count(LayoutNode *node, Monitor *m);
+static Client *xytoclient(double x, double y); +static Client *xytoclient(double x, double y);
+ +
+static int resizing_from_mouse = 0;
+static double resize_last_update_x, resize_last_update_y; +static double resize_last_update_x, resize_last_update_y;
+static uint32_t last_resize_time = 0; +static uint32_t last_resize_time = 0;
+ +
@@ -82,7 +85,6 @@ index 0000000..d706c3b
+ if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen) + if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ return; + return;
+ resize(c, area, 0); + resize(c, area, 0);
+ c->old_geom = area;
+ return; + return;
+ } + }
+ +
@@ -147,15 +149,13 @@ index 0000000..d706c3b
+ LayoutNode *found; + LayoutNode *found;
+ struct wlr_box full_area; + struct wlr_box full_area;
+ +
+ if (!m || !m->root) + if (!m)
+ return; + return;
+ +
+ /* Remove non tiled clients from tree. */ + /* Remove floating and moved clients */
+ wl_list_for_each(c, &clients, link) { + wl_list_for_each(c, &clients, link) {
+ if (c->mon == m && !c->isfloating && !c->isfullscreen) { + if (c->mon != m || c->isfloating)
+ } else {
+ remove_client(m, c); + remove_client(m, c);
+ }
+ } + }
+ +
+ /* If no client is found under cursor, fallback to focustop(m) */ + /* If no client is found under cursor, fallback to focustop(m) */
@@ -249,30 +249,37 @@ index 0000000..d706c3b
+} +}
+ +
+LayoutNode * +LayoutNode *
+find_suitable_split(LayoutNode *start_node, unsigned int need_vertical) +find_suitable_split(Monitor *m, LayoutNode *start_node,
+ unsigned int need_vertical, int focused_on_left)
+{ +{
+ LayoutNode *n = start_node; + LayoutNode *n = start_node, *child = NULL;
+ /* if we started from a client node, jump to its parent: */ +
+ if (n && n->is_client_node) + if (!m)
+ return NULL;
+
+ if (n && n->is_client_node) {
+ child = n;
+ n = n->split_node; + n = n->split_node;
+ }
+ +
+ while (n) { + while (n) {
+ if (!n->is_client_node && n->is_split_vertically == need_vertical && + if (!n->is_client_node && n->is_split_vertically == need_vertical
+ visible_count(n->left, selmon) > 0 && visible_count(n->right, selmon) > 0) + && visible_count(n->left, m) > 0
+ return n; + && visible_count(n->right, m) > 0) {
+ if ((focused_on_left && n->left == child) ||
+ (!focused_on_left && n->right == child))
+ return n;
+ }
+ child = n;
+ n = n->split_node; + n = n->split_node;
+ } + }
+ return NULL; + return NULL;
+} +}
+
+void +void
+init_tree(Monitor *m) +init_tree(Monitor *m)
+{ +{
+ if (!m) + if (m)
+ return; + m->root = NULL;
+ m->root = calloc(1, sizeof(LayoutNode));
+ if (!m->root)
+ m->root = NULL;
+} +}
+ +
+void +void
@@ -396,21 +403,31 @@ index 0000000..d706c3b
+ m->root = remove_client_node(m->root, c); + m->root = remove_client_node(m->root, c);
+} +}
+ +
+void +static void
+setratio_h(const Arg *arg) +setratio(unsigned int need_vertical, const Arg *arg)
+{ +{
+ Client *sel = focustop(selmon); + Client *sel;
+ LayoutNode *client_node, *split_node; + LayoutNode *client_node, *split_node;
+ float new_ratio; + float new_ratio;
+ int focused_on_left;
+ +
+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange) + if (!selmon || !selmon->lt[selmon->sellt]->arrange)
+ return;
+
+ sel = focustop(selmon);
+ if (!sel)
+ return; + return;
+ +
+ client_node = find_client_node(selmon->root, sel); + client_node = find_client_node(selmon->root, sel);
+ if (!client_node) + if (!client_node)
+ return; + return;
+ +
+ split_node = find_suitable_split(client_node, 1); + focused_on_left = (arg->f >= 0.0f);
+
+ split_node = find_suitable_split(selmon, client_node, need_vertical, focused_on_left);
+
+ if (!split_node)
+ split_node = find_suitable_split(selmon, client_node, need_vertical, !focused_on_left);
+ if (!split_node) + if (!split_node)
+ return; + return;
+ +
@@ -421,109 +438,85 @@ index 0000000..d706c3b
+ new_ratio = 0.95f; + new_ratio = 0.95f;
+ split_node->split_ratio = new_ratio; + split_node->split_ratio = new_ratio;
+ +
+ /* Skip the arrange if done resizing by mouse, + apply_layout(selmon, selmon->root, selmon->w, 1);
+ * we call arrange from motionotify */ +}
+ if (!resizing_from_mouse) { +
+ arrange(selmon); +void
+ } +setratio_h(const Arg *arg)
+{
+ setratio(1, arg);
+} +}
+ +
+void +void
+setratio_v(const Arg *arg) +setratio_v(const Arg *arg)
+{ +{
+ Client *sel = focustop(selmon); + setratio(0, arg);
+ LayoutNode *client_node, *split_node;
+ float new_ratio;
+
+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange)
+ return;
+
+ client_node = find_client_node(selmon->root, sel);
+ if (!client_node)
+ return;
+
+ split_node = find_suitable_split(client_node, 0);
+ if (!split_node)
+ return;
+
+ new_ratio = (arg->f != 0.0f) ? (split_node->split_ratio + arg->f) : 0.5f;
+ if (new_ratio < 0.05f)
+ new_ratio = 0.05f;
+ if (new_ratio > 0.95f)
+ new_ratio = 0.95f;
+ split_node->split_ratio = new_ratio;
+
+ /* Skip the arrange if done resizing by mouse,
+ * we call arrange from motionotify */
+ if (!resizing_from_mouse) {
+ arrange(selmon);
+ }
+} +}
+ +
+void swapclients(const Arg *arg) { +void swapclients(const Arg *arg) {
+ Client *c, *tmp, *target = NULL, *sel = focustop(selmon); + Client *c, *tmp, *target = NULL, *sel = focustop(selmon);
+ LayoutNode *sel_node, *target_node; + LayoutNode *sel_node, *target_node;
+ int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y, + int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y,
+ cand_center_x, cand_center_y; + cand_center_x, cand_center_y;
+ +
+ if (!sel || sel->isfullscreen || + if (!sel || sel->isfullscreen ||
+ !selmon->root || !selmon->lt[selmon->sellt]->arrange) + !selmon->root || !selmon->lt[selmon->sellt]->arrange)
+ return; + return;
+ +
+ +
+ /* Get the center coordinates of the selected client */ + /* Get the center coordinates of the selected client */
+ sel_center_x = sel->geom.x + sel->geom.width / 2; + sel_center_x = sel->geom.x + sel->geom.width / 2;
+ sel_center_y = sel->geom.y + sel->geom.height / 2; + sel_center_y = sel->geom.y + sel->geom.height / 2;
+ +
+ wl_list_for_each(c, &clients, link) { + wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel) + if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel)
+ continue; + continue;
+ +
+ /* Get the center of candidate client */ + /* Get the center of candidate client */
+ cand_center_x = c->geom.x + c->geom.width / 2; + cand_center_x = c->geom.x + c->geom.width / 2;
+ cand_center_y = c->geom.y + c->geom.height / 2; + cand_center_y = c->geom.y + c->geom.height / 2;
+ +
+ /* Check that the candidate lies in the requested direction. */ + /* Check that the candidate lies in the requested direction. */
+ switch (arg->ui) { + switch (arg->ui) {
+ case 0: + case 0:
+ if (cand_center_x >= sel_center_x) + if (cand_center_x >= sel_center_x)
+ continue; + continue;
+ break; + break;
+ case 1: + case 1:
+ if (cand_center_x <= sel_center_x) + if (cand_center_x <= sel_center_x)
+ continue; + continue;
+ break; + break;
+ case 2: + case 2:
+ if (cand_center_y >= sel_center_y) + if (cand_center_y >= sel_center_y)
+ continue; + continue;
+ break; + break;
+ case 3: + case 3:
+ if (cand_center_y <= sel_center_y) + if (cand_center_y <= sel_center_y)
+ continue; + continue;
+ break; + break;
+ default: + default:
+ continue; + continue;
+ } + }
+ +
+ /* Get distance between the centers */ + /* Get distance between the centers */
+ dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y); + dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y);
+ if (dist < closest_dist) { + if (dist < closest_dist) {
+ closest_dist = dist; + closest_dist = dist;
+ target = c; + target = c;
+ } + }
+ } + }
+ +
+ /* If target is found, swap the two clients positions in the layout tree */ + /* If target is found, swap the two clients positions in the layout tree */
+ if (target) { + if (target) {
+ sel_node = find_client_node(selmon->root, sel); + sel_node = find_client_node(selmon->root, sel);
+ target_node = find_client_node(selmon->root, target); + target_node = find_client_node(selmon->root, target);
+ if (sel_node && target_node) { + if (sel_node && target_node) {
+ tmp = sel_node->client; + tmp = sel_node->client;
+ sel_node->client = target_node->client; + sel_node->client = target_node->client;
+ target_node->client = tmp; + target_node->client = tmp;
+ arrange(selmon); + arrange(selmon);
+ } + }
+ } + }
+} +}
+ +
+unsigned int +unsigned int
@@ -546,11 +539,12 @@ index 0000000..d706c3b
+ +
+Client * +Client *
+xytoclient(double x, double y) { +xytoclient(double x, double y) {
+ Monitor *m = xytomon(x, y);
+ Client *c, *closest = NULL; + Client *c, *closest = NULL;
+ double dist, mindist = INT_MAX, dx, dy; + double dist, mindist = INT_MAX, dx, dy;
+ +
+ wl_list_for_each_reverse(c, &clients, link) { + wl_list_for_each_reverse(c, &clients, link) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen && + if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen &&
+ x >= c->geom.x && x <= (c->geom.x + c->geom.width) && + x >= c->geom.x && x <= (c->geom.x + c->geom.width) &&
+ y >= c->geom.y && y <= (c->geom.y + c->geom.height)){ + y >= c->geom.y && y <= (c->geom.y + c->geom.height)){
+ return c; + return c;
@@ -559,7 +553,7 @@ index 0000000..d706c3b
+ +
+ /* If no client was found at cursor position fallback to closest. */ + /* If no client was found at cursor position fallback to closest. */
+ wl_list_for_each_reverse(c, &clients, link) { + wl_list_for_each_reverse(c, &clients, link) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen) { + if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) {
+ dx = 0, dy = 0; + dx = 0, dy = 0;
+ +
+ if (x < c->geom.x) + if (x < c->geom.x)
@@ -572,7 +566,7 @@ index 0000000..d706c3b
+ else if (y > (c->geom.y + c->geom.height)) + else if (y > (c->geom.y + c->geom.height))
+ dy = y - (c->geom.y + c->geom.height); + dy = y - (c->geom.y + c->geom.height);
+ +
+ dist = sqrt(dx * dx + dy * dy); + dist = dx * dx + dy * dy;
+ if (dist < mindist) { + if (dist < mindist) {
+ mindist = dist; + mindist = dist;
+ closest = c; + closest = c;
@@ -620,7 +614,7 @@ index 8a6eda0..bc04e3f 100644
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
diff --git a/dwl.c b/dwl.c diff --git a/dwl.c b/dwl.c
index 8101ffa..e4e7074 100644 index 8101ffa..31446ed 100644
--- a/dwl.c --- a/dwl.c
+++ b/dwl.c +++ b/dwl.c
@@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
@@ -639,18 +633,16 @@ index 8101ffa..e4e7074 100644
typedef struct Monitor Monitor; typedef struct Monitor Monitor;
typedef struct { typedef struct {
/* Must keep this field first */ /* Must keep this field first */
@@ -141,8 +143,9 @@ typedef struct { @@ -141,7 +143,7 @@ typedef struct {
#endif #endif
unsigned int bw; unsigned int bw;
uint32_t tags; uint32_t tags;
- int isfloating, isurgent, isfullscreen; - int isfloating, isurgent, isfullscreen;
+ int isfloating, isurgent, isfullscreen, was_tiled; + int isfloating, isurgent, isfullscreen, was_tiled;
uint32_t resize; /* configure serial of a pending resize */ uint32_t resize; /* configure serial of a pending resize */
+ struct wlr_box old_geom;
} Client; } Client;
typedef struct { @@ -209,6 +211,7 @@ struct Monitor {
@@ -209,6 +212,7 @@ struct Monitor {
int nmaster; int nmaster;
char ltsymbol[16]; char ltsymbol[16];
int asleep; int asleep;
@@ -658,7 +650,7 @@ index 8101ffa..e4e7074 100644
}; };
typedef struct { typedef struct {
@@ -251,6 +255,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list, @@ -251,6 +254,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
struct wlr_box *usable_area, int exclusive); struct wlr_box *usable_area, int exclusive);
static void arrangelayers(Monitor *m); static void arrangelayers(Monitor *m);
static void axisnotify(struct wl_listener *listener, void *data); static void axisnotify(struct wl_listener *listener, void *data);
@@ -666,7 +658,7 @@ index 8101ffa..e4e7074 100644
static void buttonpress(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg); static void chvt(const Arg *arg);
static void checkidleinhibitor(struct wlr_surface *exclude); static void checkidleinhibitor(struct wlr_surface *exclude);
@@ -333,6 +338,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags); @@ -333,6 +337,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags);
static void setpsel(struct wl_listener *listener, void *data); static void setpsel(struct wl_listener *listener, void *data);
static void setsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data);
static void setup(void); static void setup(void);
@@ -676,7 +668,7 @@ index 8101ffa..e4e7074 100644
static void spawn(const Arg *arg); static void spawn(const Arg *arg);
static void startdrag(struct wl_listener *listener, void *data); static void startdrag(struct wl_listener *listener, void *data);
static void tag(const Arg *arg); static void tag(const Arg *arg);
@@ -458,6 +466,7 @@ static struct wlr_xwayland *xwayland; @@ -458,6 +465,7 @@ static struct wlr_xwayland *xwayland;
/* attempt to encapsulate suck into one file */ /* attempt to encapsulate suck into one file */
#include "client.h" #include "client.h"
@@ -684,7 +676,7 @@ index 8101ffa..e4e7074 100644
/* function implementations */ /* function implementations */
void void
@@ -628,7 +637,7 @@ buttonpress(struct wl_listener *listener, void *data) @@ -628,7 +636,7 @@ buttonpress(struct wl_listener *listener, void *data)
struct wlr_pointer_button_event *event = data; struct wlr_pointer_button_event *event = data;
struct wlr_keyboard *keyboard; struct wlr_keyboard *keyboard;
uint32_t mods; uint32_t mods;
@@ -693,7 +685,7 @@ index 8101ffa..e4e7074 100644
const Button *b; const Button *b;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
@@ -649,7 +658,7 @@ buttonpress(struct wl_listener *listener, void *data) @@ -649,7 +657,7 @@ buttonpress(struct wl_listener *listener, void *data)
mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
for (b = buttons; b < END(buttons); b++) { for (b = buttons; b < END(buttons); b++) {
if (CLEANMASK(mods) == CLEANMASK(b->mod) && if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
@@ -702,7 +694,7 @@ index 8101ffa..e4e7074 100644
b->func(&b->arg); b->func(&b->arg);
return; return;
} }
@@ -659,6 +668,27 @@ buttonpress(struct wl_listener *listener, void *data) @@ -659,6 +667,21 @@ buttonpress(struct wl_listener *listener, void *data)
/* If you released any buttons, we exit interactive move/resize mode. */ /* If you released any buttons, we exit interactive move/resize mode. */
/* TODO: should reset to the pointer focus's current setcursor */ /* TODO: should reset to the pointer focus's current setcursor */
if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
@@ -717,20 +709,14 @@ index 8101ffa..e4e7074 100644
+ selmon->root = create_client_node(c); + selmon->root = create_client_node(c);
+ +
+ setfloating(c, 0); + setfloating(c, 0);
+ arrange(selmon); + apply_layout(selmon, selmon->root, selmon->w, 1);
+
+ } else if (cursor_mode == CurResize && !c->isfloating) {
+ resizing_from_mouse = 0;
+ } + }
+ } else {
+ if (cursor_mode == CurResize && resizing_from_mouse)
+ resizing_from_mouse = 0;
+ } + }
+ /* Default behaviour */ + /* Default behaviour */
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
cursor_mode = CurNormal; cursor_mode = CurNormal;
/* Drop the window off on its new monitor */ /* Drop the window off on its new monitor */
@@ -750,6 +780,7 @@ cleanupmon(struct wl_listener *listener, void *data) @@ -750,6 +773,7 @@ cleanupmon(struct wl_listener *listener, void *data)
wlr_output_layout_remove(output_layout, m->wlr_output); wlr_output_layout_remove(output_layout, m->wlr_output);
wlr_scene_output_destroy(m->scene_output); wlr_scene_output_destroy(m->scene_output);
@@ -738,7 +724,7 @@ index 8101ffa..e4e7074 100644
closemon(m); closemon(m);
wlr_scene_node_destroy(&m->fullscreen_bg->node); wlr_scene_node_destroy(&m->fullscreen_bg->node);
free(m); free(m);
@@ -1094,6 +1125,7 @@ createmon(struct wl_listener *listener, void *data) @@ -1094,6 +1118,7 @@ createmon(struct wl_listener *listener, void *data)
wl_list_insert(&mons, &m->link); wl_list_insert(&mons, &m->link);
printstatus(); printstatus();
@@ -746,18 +732,25 @@ index 8101ffa..e4e7074 100644
/* The xdg-protocol specifies: /* The xdg-protocol specifies:
* *
@@ -1336,6 +1368,10 @@ destroynotify(struct wl_listener *listener, void *data) @@ -1333,9 +1358,17 @@ destroynotify(struct wl_listener *listener, void *data)
{
/* Called when the xdg_toplevel is destroyed. */
Client *c = wl_container_of(listener, c, destroy);
+ Monitor *mon;
wl_list_remove(&c->destroy.link); wl_list_remove(&c->destroy.link);
wl_list_remove(&c->set_title.link); wl_list_remove(&c->set_title.link);
wl_list_remove(&c->fullscreen.link); wl_list_remove(&c->fullscreen.link);
+ /* We check if the destroyed client was part of any tiled_list, to catch + /* We check if the destroyed client was part of any tiled_list, to catch
+ * client removals even if they would not be currently managed by btrtile */ + * client removals even if they would not be currently managed by btrtile */
+ if (selmon && selmon->root) + wl_list_for_each(mon, &mons, link) {
+ remove_client(selmon, c); + if (mon->root) {
+ remove_client(mon, c);
+ }
+ }
#ifdef XWAYLAND #ifdef XWAYLAND
if (c->type != XDGShell) { if (c->type != XDGShell) {
wl_list_remove(&c->activate.link); wl_list_remove(&c->activate.link);
@@ -1866,7 +1902,8 @@ void @@ -1866,7 +1899,8 @@ void
motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
double dx_unaccel, double dy_unaccel) double dx_unaccel, double dy_unaccel)
{ {
@@ -767,13 +760,13 @@ index 8101ffa..e4e7074 100644
Client *c = NULL, *w = NULL; Client *c = NULL, *w = NULL;
LayerSurface *l = NULL; LayerSurface *l = NULL;
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
@@ -1920,18 +1957,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d @@ -1920,18 +1954,55 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
/* Update drag icon's position */ /* Update drag icon's position */
wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
- /* If we are currently grabbing the mouse, handle and return */ - /* If we are currently grabbing the mouse, handle and return */
+ /* Skip if internal call or already resizing */ + /* Skip if internal call */
+ if (time == 0 && resizing_from_mouse) + if (time == 0)
+ goto focus; + goto focus;
+ +
+ tiled = grabc && !grabc->isfloating && !grabc->isfullscreen; + tiled = grabc && !grabc->isfloating && !grabc->isfullscreen;
@@ -795,7 +788,7 @@ index 8101ffa..e4e7074 100644
- resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, - resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
- .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1); - .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1);
- return; - return;
+ if (tiled && resizing_from_mouse) { + if (tiled) {
+ dx_total = cursor->x - resize_last_update_x; + dx_total = cursor->x - resize_last_update_x;
+ dy_total = cursor->y - resize_last_update_y; + dy_total = cursor->y - resize_last_update_y;
+ +
@@ -808,7 +801,6 @@ index 8101ffa..e4e7074 100644
+ a.f = (float)(dy_total * resize_factor); + a.f = (float)(dy_total * resize_factor);
+ setratio_v(&a); + setratio_v(&a);
+ } + }
+ arrange(selmon);
+ +
+ last_resize_time = time; + last_resize_time = time;
+ resize_last_update_x = cursor->x; + resize_last_update_x = cursor->x;
@@ -831,7 +823,7 @@ index 8101ffa..e4e7074 100644
/* If there's no client surface under the cursor, set the cursor image to a /* If there's no client surface under the cursor, set the cursor image to a
* default. This is what makes the cursor image appear when you move it * default. This is what makes the cursor image appear when you move it
* off of a client or over its border. */ * off of a client or over its border. */
@@ -1965,22 +2040,41 @@ moveresize(const Arg *arg) @@ -1965,22 +2036,40 @@ moveresize(const Arg *arg)
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
return; return;
@@ -866,7 +858,6 @@ index 8101ffa..e4e7074 100644
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); + wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
+ resize_last_update_x = cursor->x; + resize_last_update_x = cursor->x;
+ resize_last_update_y = cursor->y; + resize_last_update_y = cursor->y;
+ resizing_from_mouse = 1;
+ break; + break;
+ } + }
+ } else { + } else {
@@ -889,6 +880,21 @@ index 8101ffa..e4e7074 100644
} }
} }
@@ -2833,6 +2922,14 @@ unmapnotify(struct wl_listener *listener, void *data)
focusclient(focustop(selmon), 1);
}
} else {
+ /* btrtile remove clients for each monitor */
+ Monitor *mon;
+ wl_list_for_each(mon, &mons, link) {
+ if (mon->root) {
+ remove_client(mon, c);
+ }
+ }
+
wl_list_remove(&c->link);
setmon(c, NULL, 0);
wl_list_remove(&c->flink);
-- --
2.52.0 2.53.0
+3 -2
View File
@@ -22,8 +22,9 @@ Adds a function that automatically enables adaptive sync/VRR when a fullscreen c
- Enabled by default. - Enabled by default.
### Download ### Download
- [git branch](https://codeberg.org/julmajustus/dwl/src/branch/fullscreenadaptivesync) - [git branch](https://codeberg.org/julmajustus/dwl/src/branch/fullscreenadaptivesync-wlroots-next)
- [0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/fullscreenadaptivesync/fullscreenadaptivesync-v0.7.patch) - [0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/fullscreenadaptivesync/fullscreenadaptivesync-v0.8.patch)
- [wlroots-next-d41ecb7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/fullscreenadaptivesync/fullscreenadaptivesync-wlroots-next-d41ecb7.patch)
### Authors ### Authors
- [julmajustus](https://codeberg.org/julmajustus) - [julmajustus](https://codeberg.org/julmajustus)
@@ -1,30 +1,33 @@
From c003f450c197a0c960bbb355511f8dca7a35e3c3 Mon Sep 17 00:00:00 2001 From b9bb8d48e1be3008d72da414f6f418d77f3ca16b Mon Sep 17 00:00:00 2001
From: julmajustus <julmajustus@tutanota.com> From: julmajustus <julmajustus@tutanota.com>
Date: Sat, 4 Jan 2025 14:24:59 +0200 Date: Mon, 22 Jun 2026 23:01:00 +0300
Subject: [PATCH] add fullscreenadaptivesync Subject: [PATCH] fullscreen_adaptive_sync: Minor bug fixes
- Fixed logic error inside unmapnotify
- Added no-op guards inside set_adaptive_sync
- Added extra check for the config commit success
--- ---
config.def.h | 1 + config.def.h | 1 +
dwl.c | 40 ++++++++++++++++++++++++++++++++++++++++ dwl.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 41 insertions(+) 2 files changed, 54 insertions(+)
diff --git a/config.def.h b/config.def.h diff --git a/config.def.h b/config.def.h
index 22d2171..886f1ab 100644 index 8a6eda0..06b3153 100644
--- a/config.def.h --- a/config.def.h
+++ b/config.def.h +++ b/config.def.h
@@ -142,6 +142,7 @@ static const Key keys[] = { @@ -138,6 +138,7 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_space, setlayout, {0} }, { MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} }, { MODKEY, XKB_KEY_e, togglefullscreen, {0} },
+ { MODKEY, XKB_KEY_F5, togglefullscreenadaptivesync, {0} }, + { MODKEY, XKB_KEY_F5, togglefullscreenadaptivesync, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} }, { MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
diff --git a/dwl.c b/dwl.c diff --git a/dwl.c b/dwl.c
index a2711f6..7be05ef 100644 index 44f3ad9..e4f6a9d 100644
--- a/dwl.c --- a/dwl.c
+++ b/dwl.c +++ b/dwl.c
@@ -322,6 +322,7 @@ static void requeststartdrag(struct wl_listener *listener, void *data); @@ -319,6 +319,7 @@ static void requeststartdrag(struct wl_listener *listener, void *data);
static void requestmonstate(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 resize(Client *c, struct wlr_box geo, int interact);
static void run(char *startup_cmd); static void run(char *startup_cmd);
@@ -32,7 +35,7 @@ index a2711f6..7be05ef 100644
static void setcursor(struct wl_listener *listener, void *data); static void setcursor(struct wl_listener *listener, void *data);
static void setcursorshape(struct wl_listener *listener, void *data); static void setcursorshape(struct wl_listener *listener, void *data);
static void setfloating(Client *c, int floating); static void setfloating(Client *c, int floating);
@@ -340,6 +341,7 @@ static void tagmon(const Arg *arg); @@ -336,6 +337,7 @@ static void tagmon(const Arg *arg);
static void tile(Monitor *m); static void tile(Monitor *m);
static void togglefloating(const Arg *arg); static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg); static void togglefullscreen(const Arg *arg);
@@ -40,16 +43,16 @@ index a2711f6..7be05ef 100644
static void toggletag(const Arg *arg); static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg); static void toggleview(const Arg *arg);
static void unlocksession(struct wl_listener *listener, void *data); static void unlocksession(struct wl_listener *listener, void *data);
@@ -413,6 +415,8 @@ static struct wlr_box sgeom; @@ -436,6 +438,8 @@ static struct wl_listener request_start_drag = {.notify = requeststartdrag};
static struct wl_list mons; static struct wl_listener start_drag = {.notify = startdrag};
static Monitor *selmon; static struct wl_listener new_session_lock = {.notify = locksession};
+static int fullscreen_adaptive_sync_enabled = 1; +static int fullscreen_adaptive_sync_enabled = 1;
+ +
#ifdef XWAYLAND #ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data); static void activatex11(struct wl_listener *listener, void *data);
static void associatex11(struct wl_listener *listener, void *data); static void associatex11(struct wl_listener *listener, void *data);
@@ -2269,6 +2273,31 @@ run(char *startup_cmd) @@ -2296,6 +2300,43 @@ run(char *startup_cmd)
wl_display_run(dpy); wl_display_run(dpy);
} }
@@ -64,13 +67,25 @@ index a2711f6..7be05ef 100644
+ || !fullscreen_adaptive_sync_enabled) + || !fullscreen_adaptive_sync_enabled)
+ return; + return;
+ +
+ if (enable && m->wlr_output->adaptive_sync_status
+ == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED)
+ return;
+
+ if (!enable && m->wlr_output->adaptive_sync_status
+ == WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED)
+ return;
+
+ config = wlr_output_configuration_v1_create(); + config = wlr_output_configuration_v1_create();
+ config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output); + config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
+ +
+ /* Set and commit the adaptive sync state change */ + /* Set and commit the adaptive sync state change */
+ wlr_output_state_init(&state); + wlr_output_state_init(&state);
+ wlr_output_state_set_adaptive_sync_enabled(&state, enable); + wlr_output_state_set_adaptive_sync_enabled(&state, enable);
+ wlr_output_commit_state(m->wlr_output, &state); + if (!wlr_output_commit_state(m->wlr_output, &state)) {
+ wlr_output_state_finish(&state);
+ wlr_output_configuration_v1_destroy(config);
+ return;
+ }
+ wlr_output_state_finish(&state); + wlr_output_state_finish(&state);
+ +
+ /* Broadcast the adaptive sync state change to output_mgr */ + /* Broadcast the adaptive sync state change to output_mgr */
@@ -81,7 +96,7 @@ index a2711f6..7be05ef 100644
void void
setcursor(struct wl_listener *listener, void *data) setcursor(struct wl_listener *listener, void *data)
{ {
@@ -2332,10 +2361,12 @@ setfullscreen(Client *c, int fullscreen) @@ -2359,10 +2400,12 @@ setfullscreen(Client *c, int fullscreen)
if (fullscreen) { if (fullscreen) {
c->prev = c->geom; c->prev = c->geom;
resize(c, c->mon->m, 0); resize(c, c->mon->m, 0);
@@ -94,7 +109,7 @@ index a2711f6..7be05ef 100644
} }
arrange(c->mon); arrange(c->mon);
printstatus(); printstatus();
@@ -2739,6 +2770,12 @@ togglefullscreen(const Arg *arg) @@ -2760,6 +2803,12 @@ togglefullscreen(const Arg *arg)
setfullscreen(sel, !sel->isfullscreen); setfullscreen(sel, !sel->isfullscreen);
} }
@@ -107,16 +122,24 @@ index a2711f6..7be05ef 100644
void void
toggletag(const Arg *arg) toggletag(const Arg *arg)
{ {
@@ -2809,6 +2846,9 @@ unmapnotify(struct wl_listener *listener, void *data) @@ -2815,6 +2864,7 @@ unmapnotify(struct wl_listener *listener, void *data)
{
/* Called when the surface is unmapped, and should no longer be shown. */
Client *c = wl_container_of(listener, c, unmap);
+ Monitor *lastmon = c->mon; // fullscreen_adaptive_sync
if (c == grabc) {
cursor_mode = CurNormal;
grabc = NULL;
@@ -2830,6 +2880,9 @@ unmapnotify(struct wl_listener *listener, void *data)
setmon(c, NULL, 0); setmon(c, NULL, 0);
wl_list_remove(&c->flink); wl_list_remove(&c->flink);
} }
+ /* Toggle adaptive sync off when fullscreen client is unmapped */ + /* Toggle adaptive sync off when fullscreen client is unmapped */
+ if (c->isfullscreen) + if (c->isfullscreen)
+ set_adaptive_sync(selmon, 0); + set_adaptive_sync(lastmon, 0);
wlr_scene_node_destroy(&c->scene->node); wlr_scene_node_destroy(&c->scene->node);
printstatus(); printstatus();
-- --
2.45.2 2.53.0
@@ -0,0 +1,165 @@
From 5d475f7afe7ba7cee389a5c5ed4fa676b408fee5 Mon Sep 17 00:00:00 2001
From: julmajustus <julmajustus@tutanota.com>
Date: Mon, 22 Jun 2026 23:02:25 +0300
Subject: [PATCH] fullscreen_adaptive_sync: Minor bug fixes
- Fixed logic error inside unmapnotify
- Added no-op guards inside set_adaptive_sync
- Added extra check for the config commit success
---
config.def.h | 1 +
dwl.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 57 insertions(+), 3 deletions(-)
diff --git a/config.def.h b/config.def.h
index 8a6eda0..06b3153 100644
--- a/config.def.h
+++ b/config.def.h
@@ -138,6 +138,7 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
+ { MODKEY, XKB_KEY_F5, togglefullscreenadaptivesync, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
diff --git a/dwl.c b/dwl.c
index 8101ffa..aabd882 100644
--- a/dwl.c
+++ b/dwl.c
@@ -323,6 +323,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 set_adaptive_sync(Monitor *m, int enabled);
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);
@@ -340,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 togglefullscreenadaptivesync(const Arg *arg);
static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
static void unlocksession(struct wl_listener *listener, void *data);
@@ -440,6 +442,8 @@ static struct wl_listener request_start_drag = {.notify = requeststartdrag};
static struct wl_listener start_drag = {.notify = startdrag};
static struct wl_listener new_session_lock = {.notify = locksession};
+static int fullscreen_adaptive_sync_enabled = 1;
+
#ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data);
static void associatex11(struct wl_listener *listener, void *data);
@@ -2300,6 +2304,43 @@ run(char *startup_cmd)
wl_display_run(dpy);
}
+void
+set_adaptive_sync(Monitor *m, int enable)
+{
+ struct wlr_output_state state;
+ struct wlr_output_configuration_v1 *config;
+ struct wlr_output_configuration_head_v1 *config_head;
+
+ if (!m || !m->wlr_output || !m->wlr_output->enabled
+ || !fullscreen_adaptive_sync_enabled)
+ return;
+
+ if (enable && m->wlr_output->adaptive_sync_status
+ == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED)
+ return;
+
+ if (!enable && m->wlr_output->adaptive_sync_status
+ == WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED)
+ return;
+
+ config = wlr_output_configuration_v1_create();
+ config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
+
+ /* Set and commit the adaptive sync state change */
+ wlr_output_state_init(&state);
+ wlr_output_state_set_adaptive_sync_enabled(&state, enable);
+ if (!wlr_output_commit_state(m->wlr_output, &state)) {
+ wlr_output_state_finish(&state);
+ wlr_output_configuration_v1_destroy(config);
+ return;
+ }
+ wlr_output_state_finish(&state);
+
+ /* Broadcast the adaptive sync state change to output_mgr */
+ config_head->state.adaptive_sync_enabled = enable;
+ wlr_output_manager_v1_set_configuration(output_mgr, config);
+}
+
void
setcursor(struct wl_listener *listener, void *data)
{
@@ -2363,10 +2404,12 @@ setfullscreen(Client *c, int fullscreen)
if (fullscreen) {
c->prev = c->geom;
resize(c, c->mon->m, 0);
+ set_adaptive_sync(c->mon, 1);
} else {
/* restore previous size instead of arrange for floating windows since
* client positions are set by the user and cannot be recalculated */
resize(c, c->prev, 0);
+ set_adaptive_sync(c->mon, 0);
}
arrange(c->mon);
printstatus();
@@ -2546,7 +2589,7 @@ setup(void)
output_layout = wlr_output_layout_create(dpy);
wl_signal_add(&output_layout->events.change, &layout_change);
- wlr_xdg_output_manager_v1_create(dpy, output_layout);
+ wlr_xdg_output_manager_v1_create(dpy, output_layout);
/* Configure a listener to be notified when new outputs are available on the
* backend. */
@@ -2636,8 +2679,8 @@ setup(void)
wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard,
&new_virtual_keyboard);
virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy);
- wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer,
- &new_virtual_pointer);
+ wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer,
+ &new_virtual_pointer);
seat = wlr_seat_create(dpy, "seat0");
wl_signal_add(&seat->events.request_set_cursor, &request_cursor);
@@ -2767,6 +2810,12 @@ togglefullscreen(const Arg *arg)
setfullscreen(sel, !sel->isfullscreen);
}
+void
+togglefullscreenadaptivesync(const Arg *arg)
+{
+ fullscreen_adaptive_sync_enabled = !fullscreen_adaptive_sync_enabled;
+}
+
void
toggletag(const Arg *arg)
{
@@ -2822,6 +2871,7 @@ unmapnotify(struct wl_listener *listener, void *data)
{
/* Called when the surface is unmapped, and should no longer be shown. */
Client *c = wl_container_of(listener, c, unmap);
+ Monitor *lastmon = c->mon; // fullscreen_adaptive_sync
if (c == grabc) {
cursor_mode = CurNormal;
grabc = NULL;
@@ -2837,6 +2887,9 @@ unmapnotify(struct wl_listener *listener, void *data)
setmon(c, NULL, 0);
wl_list_remove(&c->flink);
}
+ /* Toggle adaptive sync off when fullscreen client is unmapped */
+ if (c->isfullscreen)
+ set_adaptive_sync(lastmon, 0);
wlr_scene_node_destroy(&c->scene->node);
printstatus();
--
2.53.0
+2 -2
View File
@@ -13,8 +13,8 @@ This allows the user to change size and placement of floating windows using only
| <kbd>MODKEY</kbd> + <kbd>Shift</kbd> + <kbd>Right</kbd> | grow width 40px | | <kbd>MODKEY</kbd> + <kbd>Shift</kbd> + <kbd>Right</kbd> | grow width 40px |
### Download ### Download
- [git branch](https://codeberg.org/wochap/dwl/src/branch/v0.5/moveresizekb) - [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/moveresizekb/moveresizekb.patch)
- [v0.5](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/moveresizekb/moveresizekb.patch)
### Authors ### Authors
- [cana](https://codeberg.org/cana)
- [wochap](https://codeberg.org/wochap) - [wochap](https://codeberg.org/wochap)
+32 -28
View File
@@ -1,38 +1,38 @@
From c8af428f964679089599e4ffbe7d28d08a4e875f Mon Sep 17 00:00:00 2001 From f19c162ba64c2c4860e5d16e48e08cebd7a7e46c Mon Sep 17 00:00:00 2001
From: wochap <gean.marroquin@gmail.com> From: C4FE1 <heitorcdesousa13@gmail.com>
Date: Tue, 5 Mar 2024 23:42:55 -0500 Date: Wed, 10 Jun 2026 19:50:26 -0300
Subject: [PATCH] implement keybindings to move and resize focused floating Subject: [PATCH] Change moveresizekb logic to allow it's use in floating
window (NULL) Layout
--- ---
config.def.h | 8 ++++++++ config.def.h | 8 ++++++++
dwl.c | 19 +++++++++++++++++++ dwl.c | 23 +++++++++++++++++++++++
2 files changed, 27 insertions(+) 2 files changed, 31 insertions(+)
diff --git a/config.def.h b/config.def.h diff --git a/config.def.h b/config.def.h
index db0babc..d0570b8 100644 index 8a6eda0..b8398f9 100644
--- a/config.def.h --- a/config.def.h
+++ b/config.def.h +++ b/config.def.h
@@ -135,6 +135,14 @@ static const Key keys[] = { @@ -138,6 +138,14 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_space, setlayout, {0} }, { MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} }, { MODKEY, XKB_KEY_e, togglefullscreen, {0} },
+ { MODKEY, XKB_KEY_Down, moveresizekb, {.v = (int []){ 0, 40, 0, 0 }}}, + { MODKEY, XKB_KEY_Down, moveresizekb, {.v = (int []){ 0, 40, 0, 0 }}},
+ { MODKEY, XKB_KEY_Up, moveresizekb, {.v = (int []){ 0, -40, 0, 0 }}}, + { MODKEY, XKB_KEY_Up, moveresizekb, {.v = (int []){ 0, -40, 0, 0 }}},
+ { MODKEY, XKB_KEY_Right, moveresizekb, {.v = (int []){ 40, 0, 0, 0 }}}, + { MODKEY, XKB_KEY_Right, moveresizekb, {.v = (int []){ 40, 0, 0, 0 }}},
+ { MODKEY, XKB_KEY_Left, moveresizekb, {.v = (int []){ -40, 0, 0, 0 }}}, + { MODKEY, XKB_KEY_Left, moveresizekb, {.v = (int []){ -40, 0, 0, 0 }}},
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, moveresizekb, {.v = (int []){ 0, 0, 0, 40 }}}, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, moveresizekb, {.v = (int []){ 0, 0, 0, 40 }}},
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Up, moveresizekb, {.v = (int []){ 0, 0, 0, -40 }}}, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Up, moveresizekb, {.v = (int []){ 0, 0, 0, -40 }}},
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, moveresizekb, {.v = (int []){ 0, 0, 40, 0 }}}, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, moveresizekb, {.v = (int []){ 0, 0, 40, 0 }}},
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, moveresizekb, {.v = (int []){ 0, 0, -40, 0 }}}, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, moveresizekb, {.v = (int []){ 0, 0, -40, 0 }}},
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} }, { MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
diff --git a/dwl.c b/dwl.c diff --git a/dwl.c b/dwl.c
index ef27a1d..251472b 100644 index 44f3ad9..a6450f5 100644
--- a/dwl.c --- a/dwl.c
+++ b/dwl.c +++ b/dwl.c
@@ -313,6 +313,7 @@ static void tagmon(const Arg *arg); @@ -336,6 +336,7 @@ static void tagmon(const Arg *arg);
static void tile(Monitor *m); static void tile(Monitor *m);
static void togglefloating(const Arg *arg); static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg); static void togglefullscreen(const Arg *arg);
@@ -40,7 +40,7 @@ index ef27a1d..251472b 100644
static void toggletag(const Arg *arg); static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg); static void toggleview(const Arg *arg);
static void unlocksession(struct wl_listener *listener, void *data); static void unlocksession(struct wl_listener *listener, void *data);
@@ -2454,6 +2455,24 @@ togglefullscreen(const Arg *arg) @@ -2760,6 +2761,28 @@ togglefullscreen(const Arg *arg)
setfullscreen(sel, !sel->isfullscreen); setfullscreen(sel, !sel->isfullscreen);
} }
@@ -50,7 +50,11 @@ index ef27a1d..251472b 100644
+ Client *c = focustop(selmon); + Client *c = focustop(selmon);
+ Monitor *m = selmon; + Monitor *m = selmon;
+ +
+ if(!(m && arg && arg->v && c && c->isfloating)) { + if(!(m && arg && arg->v && c)){
+ return;
+ }
+
+ if(!(c->isfloating || m->lt[m->sellt]->arrange == NULL)){
+ return; + return;
+ } + }
+ +
@@ -66,5 +70,5 @@ index ef27a1d..251472b 100644
toggletag(const Arg *arg) toggletag(const Arg *arg)
{ {
-- --
2.42.0 2.54.0
+18
View File
@@ -0,0 +1,18 @@
### Description
Adds per-window screen sharing aka toplevel capture via `ext-foreign-toplevel-image-capture-source-v1` based on the sway implementation <br>
XWayland clients work in basic testing but it should be considered rather experimental. There is some possible restacking edge-cases but I was not able to reproduce them yet <br>
Note that the captured surface is rendered a second time into its own scene, so there's a small GPU cost while a capture is active
Targets the dwl **`wlroots-next`** branch (base commit `d41ecb745c`).
### Requirements
| Requirement | Notes |
| --- | --- |
| `wlroots` ≥ 0.20 | mandatory |
| `xdg-desktop-portal-wlr` ≥ 0.8 | `chooser_type` has to be configured as `dmenu` |
### Download
- [per-app-share-wlroots-next-d41ecb745c](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/per-app-cast/per-app-share-wlroots-next-d41ecb745c.patch)
### Authors
- [skeetamine](https://codeberg.org/skeetamine)
@@ -0,0 +1,194 @@
From e0fe17a27625440a1a4ac8cfe91639a938589925 Mon Sep 17 00:00:00 2001
From: skeetamine <lesrallidenud@gmail.com>
Date: Fri, 19 Jun 2026 18:37:41 +0300
Subject: [PATCH] per-app-cast: capture individual toplevels
Implement ext-foreign-toplevel-image-capture-source-v1 so portals can
offer per-window screen sharing alongside per-output. Each toplevel is
rendered into a dedicated offscreen scene so capture isolates the window
regardless of tag/position.
---
dwl.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 83 insertions(+)
diff --git a/dwl.c b/dwl.c
index 8101ffa..7f4d059 100644
--- a/dwl.c
+++ b/dwl.c
@@ -25,6 +25,7 @@
#include <wlr/types/wlr_drm.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_ext_data_control_v1.h>
+#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
#include <wlr/types/wlr_ext_image_capture_source_v1.h>
#include <wlr/types/wlr_ext_image_copy_capture_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
@@ -123,6 +124,7 @@ typedef struct {
struct wlr_xwayland_surface *xwayland;
} surface;
struct wlr_xdg_toplevel_decoration_v1 *decoration;
+ struct wlr_ext_foreign_toplevel_handle_v1 *foreign_toplevel;
struct wl_listener commit;
struct wl_listener map;
struct wl_listener maximize;
@@ -243,6 +245,11 @@ typedef struct {
struct wl_listener destroy;
} SessionLock;
+typedef struct {
+ struct wlr_scene *scene;
+ struct wl_listener source_destroy;
+} CaptureScene;
+
/* function declarations */
static void applybounds(Client *c, struct wlr_box *bbox);
static void applyrules(Client *c);
@@ -252,6 +259,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 void capturescenedestroy(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg);
static void checkidleinhibitor(struct wlr_surface *exclude);
static void cleanup(void);
@@ -292,6 +300,7 @@ static void focusstack(const Arg *arg);
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 handlecapturerequest(struct wl_listener *listener, void *data);
static void handlesig(int signo);
static void incnmaster(const Arg *arg);
static void inputdevice(struct wl_listener *listener, void *data);
@@ -399,6 +408,10 @@ static struct wlr_session_lock_manager_v1 *session_lock_mgr;
static struct wlr_scene_rect *locked_bg;
static struct wlr_session_lock_v1 *cur_lock;
+static struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list;
+static struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *toplevel_capture_mgr;
+static struct wl_listener foreign_toplevel_capture_request = {.notify = handlecapturerequest};
+
static struct wlr_seat *seat;
static KeyboardGroup *kb_group;
static unsigned int cursor_mode;
@@ -676,6 +689,15 @@ buttonpress(struct wl_listener *listener, void *data)
event->time_msec, event->button, event->state);
}
+void
+capturescenedestroy(struct wl_listener *listener, void *data)
+{
+ CaptureScene *cs = wl_container_of(listener, cs, source_destroy);
+ wl_list_remove(&cs->source_destroy.link);
+ wlr_scene_node_destroy(&cs->scene->tree.node);
+ free(cs);
+}
+
void
chvt(const Arg *arg)
{
@@ -786,6 +808,7 @@ cleanuplisteners(void)
wl_list_remove(&request_start_drag.link);
wl_list_remove(&start_drag.link);
wl_list_remove(&new_session_lock.link);
+ wl_list_remove(&foreign_toplevel_capture_request.link);
#ifdef XWAYLAND
wl_list_remove(&new_xwayland_surface.link);
wl_list_remove(&xwayland_ready.link);
@@ -1561,6 +1584,41 @@ gpureset(struct wl_listener *listener, void *data)
wlr_renderer_destroy(old_drw);
}
+void
+handlecapturerequest(struct wl_listener *listener, void *data)
+{
+ struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *req = data;
+ Client *c;
+ CaptureScene *cs;
+ struct wlr_ext_image_capture_source_v1 *source;
+
+ wl_list_for_each(c, &clients, link) {
+ if (c->foreign_toplevel != req->toplevel_handle)
+ continue;
+
+ cs = ecalloc(1, sizeof(*cs));
+ cs->scene = wlr_scene_create();
+ if (c->type == XDGShell)
+ wlr_scene_xdg_surface_create(&cs->scene->tree, c->surface.xdg);
+ else
+ wlr_scene_subsurface_tree_create(&cs->scene->tree, client_surface(c));
+
+ source =
+ wlr_ext_image_capture_source_v1_create_with_scene_node(
+ &cs->scene->tree.node, event_loop, alloc, drw);
+
+ if (source) {
+ cs->source_destroy.notify = capturescenedestroy;
+ wl_signal_add(&source->events.destroy, &cs->source_destroy);
+ wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(req, source);
+ } else {
+ wlr_scene_node_destroy(&cs->scene->tree.node);
+ free(cs);
+ }
+ return;
+ }
+}
+
void
handlesig(int signo)
{
@@ -1786,6 +1844,14 @@ mapnotify(struct wl_listener *listener, void *data)
wl_list_insert(&clients, &c->link);
wl_list_insert(&fstack, &c->flink);
+ {
+ struct wlr_ext_foreign_toplevel_handle_v1_state ft_state = {
+ .title = client_get_title(c),
+ .app_id = client_get_appid(c),
+ };
+ c->foreign_toplevel = wlr_ext_foreign_toplevel_handle_v1_create(foreign_toplevel_list, &ft_state);
+ }
+
/* Set initial monitor, tags, floating status, and focus:
* we always consider floating, clients that have parent and thus
* we set the same tags and monitor as its parent.
@@ -2532,6 +2598,10 @@ setup(void)
wlr_presentation_create(dpy, backend, 2);
wlr_alpha_modifier_v1_create(dpy);
+ foreign_toplevel_list = wlr_ext_foreign_toplevel_list_v1_create(dpy, 1);
+ toplevel_capture_mgr = wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(dpy, 1);
+ wl_signal_add(&toplevel_capture_mgr->events.new_request, &foreign_toplevel_capture_request);
+
/* Initializes the interface used to implement urgency hints */
activation = wlr_xdg_activation_v1_create(dpy);
wl_signal_add(&activation->events.request_activate, &request_activate);
@@ -2838,6 +2908,11 @@ unmapnotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->flink);
}
+ if (c->foreign_toplevel) {
+ wlr_ext_foreign_toplevel_handle_v1_destroy(c->foreign_toplevel);
+ c->foreign_toplevel = NULL;
+ }
+
wlr_scene_node_destroy(&c->scene->node);
printstatus();
motionnotify(0, NULL, 0, 0, 0, 0);
@@ -2955,6 +3030,14 @@ updatetitle(struct wl_listener *listener, void *data)
Client *c = wl_container_of(listener, c, set_title);
if (c == focustop(c->mon))
printstatus();
+
+ if (c->foreign_toplevel) {
+ struct wlr_ext_foreign_toplevel_handle_v1_state ft_state = {
+ .title = client_get_title(c),
+ .app_id = client_get_appid(c),
+ };
+ wlr_ext_foreign_toplevel_handle_v1_update_state(c->foreign_toplevel, &ft_state);
+ }
}
void
--
2.54.0
+6 -2
View File
@@ -2,7 +2,11 @@
Moves clients to their old output when it is reattached. Moves clients to their old output when it is reattached.
### Download ### Download
- [git branch](https://codeberg.org/eyusupov/dwl/src/branch/restore-monitor) - [git branch](https://codeberg.org/6z7y/dwl-patches/src/branch/restore-monitor)
- [2024-04-07](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/restore-monitor/restore-monitor.patch) - [2026-06-20](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/restore-monitor/restore-monitor.patch)
### Authors ### Authors
#### Current
- [6z7y](https://codeberg.org/6z7y)
#### Historic
- [eyusupov](https://codeberg.org/eyusupov) - [eyusupov](https://codeberg.org/eyusupov)
+50 -34
View File
@@ -1,79 +1,95 @@
From e42ca1c539437d3098d80983cfe2ad6f938d7a08 Mon Sep 17 00:00:00 2001 From e42ca1c539437d3098d80983cfe2ad6f938d7a08 Mon Sep 17 00:00:00 2001
From: Eldar Yusupov <eyusupov@gmail.com> From: Eldar Yusupov <eyusupov@gmail.com>
Date: Sun, 17 Mar 2024 19:12:29 +0300 Date: Sun, 17 Mar 2024 19:12:29 +0300
Subject: [PATCH] Restore correct monitor for client when it is reattached Subject: [PATCH] Restore correct monitor and floating position when reattached
--- ---
dwl.c | 24 ++++++++++++++++++++++-- dwl.c | 33 ++++++++++++++++++++++++++++++---
1 file changed, 22 insertions(+), 2 deletions(-) 1 file changed, 30 insertions(+), 3 deletions(-)
diff --git a/dwl.c b/dwl.c diff --git a/dwl.c b/dwl.c
index bf763df..d8d8139 100644 index 44f3ad9..41548cd 100644
--- a/dwl.c --- a/dwl.c
+++ b/dwl.c +++ b/dwl.c
@@ -107,6 +107,7 @@ typedef struct { @@ -106,6 +106,8 @@ typedef struct {
unsigned int type; /* XDGShell or X11* */ unsigned int type; /* XDGShell or X11* */
struct wlr_box geom; /* layout-relative, includes border */
Monitor *mon; Monitor *mon;
+ char *output; + char *output;
+ struct wlr_box floatgeom; /* saved geom for floating restore after monitor reconnect */
struct wlr_scene_tree *scene; struct wlr_scene_tree *scene;
struct wlr_scene_rect *border[4]; /* top, bottom, left, right */ struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
struct wlr_scene_tree *scene_surface; struct wlr_scene_tree *scene_surface;
@@ -869,6 +870,7 @@ createmon(struct wl_listener *listener, void *data) @@ -807,6 +809,13 @@ closemon(Monitor *m)
}
wl_list_for_each(c, &clients, link) {
+
+ /* Save floating geom now, before destroymon modifies it below.
+ * destroymon subtracts m->w.width from c->geom.x for floating
+ * windows, which corrupts the layout-absolute position we need
+ * to restore on reconnect. */
+ if (c->isfloating && c->mon == m) c->floatgeom = c->geom;
+
if (c->isfloating && c->geom.x > m->m.width)
resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y,
.width = c->geom.width, .height = c->geom.height}, 0);
@@ -1045,6 +1054,7 @@ createmon(struct wl_listener *listener, void *data)
size_t i; size_t i;
struct wlr_output_state state; struct wlr_output_state state;
Monitor *m; Monitor *m;
+ Client *c; + Client *c;
if (!wlr_output_init_render(wlr_output, alloc, drw)) if (!wlr_output_init_render(wlr_output, alloc, drw))
return; return;
@@ -938,6 +940,13 @@ createmon(struct wl_listener *listener, void *data) @@ -1114,6 +1124,15 @@ createmon(struct wl_listener *listener, void *data)
wlr_output_layout_add_auto(output_layout, wlr_output); wlr_output_layout_add_auto(output_layout, wlr_output);
else else
wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y); wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y);
+ +
+ wl_list_for_each(c, &clients, link) { + wl_list_for_each(c, &clients, link) {
+ if (strcmp(wlr_output->name, c->output) == 0) { + if (!c->output || strcmp(wlr_output->name, c->output) != 0)
+ c->mon = m; + continue;
+ } + c->mon = m;
+ } + if (c->isfloating)
+ updatemons(NULL, NULL); + resize(c, c->floatgeom, 0);
+ }
+ updatemons(NULL, NULL);
} }
void void
@@ -1186,6 +1195,7 @@ destroynotify(struct wl_listener *listener, void *data) @@ -1347,7 +1366,8 @@ destroynotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->map.link);
wl_list_remove(&c->unmap.link); wl_list_remove(&c->unmap.link);
wl_list_remove(&c->maximize.link);
} }
+ free(c->output); - free(c);
free(c); + free(c->output);
+ free(c);
} }
@@ -1618,6 +1628,10 @@ mapnotify(struct wl_listener *listener, void *data) void
@@ -1792,6 +1812,9 @@ mapnotify(struct wl_listener *listener, void *data)
} else { } else {
applyrules(c); applyrules(c);
} }
+ c->output = strdup(c->mon->wlr_output->name); + c->output = strdup(c->mon->wlr_output->name);
+ if (c->output == NULL) { + if (c->output == NULL) die("oom");
+ die("oom"); +
+ }
printstatus(); printstatus();
unset_fullscreen: unset_fullscreen:
@@ -2565,8 +2579,14 @@ void @@ -2705,8 +2728,12 @@ void
tagmon(const Arg *arg) tagmon(const Arg *arg)
{ {
Client *sel = focustop(selmon); Client *sel = focustop(selmon);
- if (sel) - if (sel)
- setmon(sel, dirtomon(arg->i), 0); - setmon(sel, dirtomon(arg->i), 0);
+ if (!sel) + if (!sel) return;
+ return; +
+ setmon(sel, dirtomon(arg->i), 0); + setmon(sel, dirtomon(arg->i), 0);
+ free(sel->output); + free(sel->output);
+ sel->output = strdup(sel->mon->wlr_output->name); + sel->output = strdup(sel->mon->wlr_output->name);
+ if (sel->output == NULL) { + if (sel->output == NULL) die("oom");
+ die("oom");
+ }
} }
void void
+7 -4
View File
@@ -2,8 +2,8 @@
# simple_scratchpad — A very simple scratchpad utility. # simple_scratchpad — A very simple scratchpad utility.
Adds functions to add, toggle and remove clients to/from scratchpad client list. Adds functions to add, toggle and remove clients to/from scratchpad client lists.
![scratchpad in action](https://github.com/julmajustus/dwl-patches/blob/main/demos/simple_scratchpad_demo.gif?raw=true) ![scratchpad in action](https://codeberg.org/julmajustus/my-dwl-patches/raw/branch/main/demos/simple_scratchpad_demo.gif?raw=true)
--- ---
@@ -16,11 +16,14 @@ Adds functions to add, toggle and remove clients to/from scratchpad client list.
3. **removescratchpad** 3. **removescratchpad**
- Removes client from scratchpad client list. - Removes client from scratchpad client list.
4. **setscratchpad**
- Sets current scratchpad number
### Download ### Download
- [git branch](https://codeberg.org/julmajustus/dwl/src/branch/simple_scratchpad) - [git branch](https://codeberg.org/julmajustus/dwl/src/branch/simple_scratchpad)
- [scratchpad-wlroots-next-f4249db.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/simple_scratchpad/simple_scratchpad-f4249db.patch) - [scratchpad-wlroots-next-d41ecb7.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/simple_scratchpad/simple_scratchpad-wlroots-next-d41ecb7.patch)
- [scratchpad-0.8.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/simple_scratchpad/simple_scratchpad-0.8.patch) - [scratchpad-v0.8.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/simple_scratchpad/simple_scratchpad-v0.8.patch)
### Authors ### Authors
- [julmajustus](https://codeberg.org/julmajustus) - [julmajustus](https://codeberg.org/julmajustus)
@@ -1,211 +0,0 @@
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
@@ -0,0 +1,256 @@
From 09c708876afe3675f68f7b35c3f17263b134ceee Mon Sep 17 00:00:00 2001
From: julmajustus <julmajustus@tutanota.com>
Date: Thu, 21 May 2026 00:28:15 +0300
Subject: [PATCH] simple_scratchpad: Spring update scratchpad V2
- Added support for multiple scratchpads
---
config.def.h | 7 ++++
dwl.c | 43 +++++++++++++++++++++--
simple_scratchpad.c | 85 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 133 insertions(+), 2 deletions(-)
create mode 100644 simple_scratchpad.c
diff --git a/config.def.h b/config.def.h
index 8a6eda0..a527e09 100644
--- a/config.def.h
+++ b/config.def.h
@@ -14,6 +14,7 @@ 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 */
+#define SCRATCHPAD_COUNT 3
/* tagging - TAGCOUNT must be no greater than 31 */
#define TAGCOUNT (9)
@@ -132,6 +133,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]} },
@@ -144,6 +148,9 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_1, setscratchpad, {.i = 0} },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_2, setscratchpad, {.i = 1} },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_3, setscratchpad, {.i = 2} },
TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
diff --git a/dwl.c b/dwl.c
index 44f3ad9..f54bbda 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);
@@ -327,6 +330,7 @@ static void setlayout(const Arg *arg);
static void setmfact(const Arg *arg);
static void setmon(Client *c, Monitor *m, uint32_t newtags);
static void setpsel(struct wl_listener *listener, void *data);
+static void setscratchpad(const Arg *arg);
static void setsel(struct wl_listener *listener, void *data);
static void setup(void);
static void spawn(const Arg *arg);
@@ -336,6 +340,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);
@@ -455,6 +460,11 @@ static struct wlr_xwayland *xwayland;
/* attempt to encapsulate suck into one file */
#include "client.h"
+static struct wl_list scratchpad_clients[SCRATCHPAD_COUNT];
+static int scratchpad_visible[SCRATCHPAD_COUNT];
+static int scratchpad_sel = 0;
+#include "simple_scratchpad.c"
+
/* function implementations */
void
applybounds(Client *c, struct wlr_box *bbox)
@@ -1328,10 +1338,21 @@ 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 any scratchpad slot
+ * and clean it from the list if so. */
+ for (int i = 0; i < SCRATCHPAD_COUNT; i++) {
+ wl_list_for_each(sc, &scratchpad_clients[i], link_temp) {
+ if (sc == c) {
+ wl_list_remove(&c->link_temp);
+ goto scratchpad_destroy_done;
+ }
+ }
+ }
+scratchpad_destroy_done:;
#ifdef XWAYLAND
if (c->type != XDGShell) {
wl_list_remove(&c->activate.link);
@@ -2333,11 +2354,24 @@ 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 any scratchpad slot
+ * and remove it from that slot if so */
+ if (!floating) {
+ for (int i = 0; i < SCRATCHPAD_COUNT; i++) {
+ wl_list_for_each(sc, &scratchpad_clients[i], link_temp) {
+ if (sc == c) {
+ wl_list_remove(&c->link_temp);
+ goto scratchpad_float_done;
+ }
+ }
+ }
+scratchpad_float_done:;
+ }
wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ||
(p && p->isfullscreen) ? LyrFS
: c->isfloating ? LyrFloat : LyrTile]);
@@ -2554,6 +2588,11 @@ setup(void)
*/
wl_list_init(&clients);
wl_list_init(&fstack);
+ wl_list_init(&fstack);
+ for (int j = 0; j < SCRATCHPAD_COUNT; j++) {
+ wl_list_init(&scratchpad_clients[j]);
+ scratchpad_visible[j] = 1;
+ }
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..cd62d83
--- /dev/null
+++ b/simple_scratchpad.c
@@ -0,0 +1,85 @@
+/* ************************************************************************** */
+/* @@@ @@@@@@@@ */
+/* @@@ @@@@@@@@@@ */
+/* @@! @@! @@@@ */
+/* !@! !@! @!@!@ */
+/* simple_scratchpad.c @!! @!@ @! !@! */
+/* !!! !@!!! !!! */
+/* By: julmajustus <julmajustus@tutanota.com> !!: !!:! !!! */
+/* ::! :!: !:! */
+/* Created: 2024/12/19 19:35:02 by jmakkone :: ::::::: :: */
+/* Updated: 2026/05/20 23:58:16 by julmajustus : : : : : : */
+/* */
+/* ************************************************************************** */
+
+void
+addscratchpad(const Arg *arg)
+{
+ Client *cc, *c = focustop(selmon);
+
+ if (!c)
+ return;
+ /* Check if the added client is already a scratchpad client */
+ for (int i = 0; i < SCRATCHPAD_COUNT; i++) {
+ wl_list_for_each(cc, &scratchpad_clients[i], link_temp) {
+ if (cc == c)
+ return;
+ }
+ }
+ if (!c->isfloating) {
+ setfloating(c, 1);
+ }
+ wl_list_insert(&scratchpad_clients[scratchpad_sel], &c->link_temp);
+}
+
+void
+togglescratchpad(const Arg *arg)
+{
+ Client *c;
+ Monitor *m = selmon;
+
+ scratchpad_visible[scratchpad_sel] = !scratchpad_visible[scratchpad_sel];
+ if (scratchpad_visible[scratchpad_sel]) {
+ wl_list_for_each(c, &scratchpad_clients[scratchpad_sel], link_temp) {
+ c->mon = m;
+ c->tags = m->tagset[m->seltags];
+ arrange(m);
+ focusclient(c, 1);
+ }
+ } else {
+ wl_list_for_each(c, &scratchpad_clients[scratchpad_sel], link_temp) {
+ c->tags = 0;
+ focusclient(focustop(m), 1);
+ arrange(m);
+ }
+ }
+}
+
+void
+removescratchpad(const Arg *arg)
+{
+ Client *sc, *c = focustop(selmon);
+ if (!c)
+ return;
+ for (int i = 0; i < SCRATCHPAD_COUNT; i++) {
+ /* Check if c is in scratchpad_clients */
+ wl_list_for_each(sc, &scratchpad_clients[i], link_temp) {
+ if (sc == c) {
+ wl_list_remove(&c->link_temp);
+ return;
+ }
+ }
+ }
+}
+
+void
+setscratchpad(const Arg *arg)
+{
+ int idx = arg->i;
+
+ if (idx < 0)
+ idx = 0;
+ else if (idx >= SCRATCHPAD_COUNT)
+ idx = SCRATCHPAD_COUNT - 1;
+ scratchpad_sel = idx;
+}
--
2.53.0
@@ -0,0 +1,256 @@
From be4f7488eac6ea502e0e9d1cc5595e01d97cb345 Mon Sep 17 00:00:00 2001
From: julmajustus <julmajustus@tutanota.com>
Date: Thu, 21 May 2026 00:28:41 +0300
Subject: [PATCH] simple_scratchpad: Spring update scratchpad V2
- Added support for multiple scratchpads
---
config.def.h | 7 ++++
dwl.c | 43 +++++++++++++++++++++--
simple_scratchpad.c | 85 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 133 insertions(+), 2 deletions(-)
create mode 100644 simple_scratchpad.c
diff --git a/config.def.h b/config.def.h
index 8a6eda0..a527e09 100644
--- a/config.def.h
+++ b/config.def.h
@@ -14,6 +14,7 @@ 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 */
+#define SCRATCHPAD_COUNT 3
/* tagging - TAGCOUNT must be no greater than 31 */
#define TAGCOUNT (9)
@@ -132,6 +133,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]} },
@@ -144,6 +148,9 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_1, setscratchpad, {.i = 0} },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_2, setscratchpad, {.i = 1} },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_3, setscratchpad, {.i = 2} },
TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
diff --git a/dwl.c b/dwl.c
index 8101ffa..1c858a8 100644
--- a/dwl.c
+++ b/dwl.c
@@ -143,6 +143,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 {
@@ -244,6 +245,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);
@@ -318,6 +320,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);
@@ -331,6 +334,7 @@ static void setlayout(const Arg *arg);
static void setmfact(const Arg *arg);
static void setmon(Client *c, Monitor *m, uint32_t newtags);
static void setpsel(struct wl_listener *listener, void *data);
+static void setscratchpad(const Arg *arg);
static void setsel(struct wl_listener *listener, void *data);
static void setup(void);
static void spawn(const Arg *arg);
@@ -340,6 +344,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);
@@ -459,6 +464,11 @@ static struct wlr_xwayland *xwayland;
/* attempt to encapsulate suck into one file */
#include "client.h"
+static struct wl_list scratchpad_clients[SCRATCHPAD_COUNT];
+static int scratchpad_visible[SCRATCHPAD_COUNT];
+static int scratchpad_sel = 0;
+#include "simple_scratchpad.c"
+
/* function implementations */
void
applybounds(Client *c, struct wlr_box *bbox)
@@ -1332,10 +1342,21 @@ 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 any scratchpad slot
+ * and clean it from the list if so. */
+ for (int i = 0; i < SCRATCHPAD_COUNT; i++) {
+ wl_list_for_each(sc, &scratchpad_clients[i], link_temp) {
+ if (sc == c) {
+ wl_list_remove(&c->link_temp);
+ goto scratchpad_destroy_done;
+ }
+ }
+ }
+scratchpad_destroy_done:;
#ifdef XWAYLAND
if (c->type != XDGShell) {
wl_list_remove(&c->activate.link);
@@ -2337,11 +2358,24 @@ 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 any scratchpad slot
+ * and remove it from that slot if so */
+ if (!floating) {
+ for (int i = 0; i < SCRATCHPAD_COUNT; i++) {
+ wl_list_for_each(sc, &scratchpad_clients[i], link_temp) {
+ if (sc == c) {
+ wl_list_remove(&c->link_temp);
+ goto scratchpad_float_done;
+ }
+ }
+ }
+scratchpad_float_done:;
+ }
wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ||
(p && p->isfullscreen) ? LyrFS
: c->isfloating ? LyrFloat : LyrTile]);
@@ -2561,6 +2595,11 @@ setup(void)
*/
wl_list_init(&clients);
wl_list_init(&fstack);
+ wl_list_init(&fstack);
+ for (int j = 0; j < SCRATCHPAD_COUNT; j++) {
+ wl_list_init(&scratchpad_clients[j]);
+ scratchpad_visible[j] = 1;
+ }
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..0e23ddd
--- /dev/null
+++ b/simple_scratchpad.c
@@ -0,0 +1,85 @@
+/* ************************************************************************** */
+/* @@@ @@@@@@@@ */
+/* @@@ @@@@@@@@@@ */
+/* @@! @@! @@@@ */
+/* !@! !@! @!@!@ */
+/* simple_scratchpad.c @!! @!@ @! !@! */
+/* !!! !@!!! !!! */
+/* By: julmajustus <julmajustus@tutanota.com> !!: !!:! !!! */
+/* ::! :!: !:! */
+/* Created: 2024/12/19 19:35:02 by jmakkone :: ::::::: :: */
+/* Updated: 2026/05/20 23:56:35 by julmajustus : : : : : : */
+/* */
+/* ************************************************************************** */
+
+void
+addscratchpad(const Arg *arg)
+{
+ Client *cc, *c = focustop(selmon);
+
+ if (!c)
+ return;
+ /* Check if the added client is already a scratchpad client */
+ for (int i = 0; i < SCRATCHPAD_COUNT; i++) {
+ wl_list_for_each(cc, &scratchpad_clients[i], link_temp) {
+ if (cc == c)
+ return;
+ }
+ }
+ if (!c->isfloating) {
+ setfloating(c, 1);
+ }
+ wl_list_insert(&scratchpad_clients[scratchpad_sel], &c->link_temp);
+}
+
+void
+togglescratchpad(const Arg *arg)
+{
+ Client *c;
+ Monitor *m = selmon;
+
+ scratchpad_visible[scratchpad_sel] = !scratchpad_visible[scratchpad_sel];
+ if (scratchpad_visible[scratchpad_sel]) {
+ wl_list_for_each(c, &scratchpad_clients[scratchpad_sel], link_temp) {
+ c->mon = m;
+ c->tags = m->tagset[m->seltags];
+ arrange(m);
+ focusclient(c, 1);
+ }
+ } else {
+ wl_list_for_each(c, &scratchpad_clients[scratchpad_sel], link_temp) {
+ c->tags = 0;
+ focusclient(focustop(m), 1);
+ arrange(m);
+ }
+ }
+}
+
+void
+removescratchpad(const Arg *arg)
+{
+ Client *sc, *c = focustop(selmon);
+ if (!c)
+ return;
+ for (int i = 0; i < SCRATCHPAD_COUNT; i++) {
+ /* Check if c is in scratchpad_clients */
+ wl_list_for_each(sc, &scratchpad_clients[i], link_temp) {
+ if (sc == c) {
+ wl_list_remove(&c->link_temp);
+ return;
+ }
+ }
+ }
+}
+
+void
+setscratchpad(const Arg *arg)
+{
+ int idx = arg->i;
+
+ if (idx < 0)
+ idx = 0;
+ else if (idx >= SCRATCHPAD_COUNT)
+ idx = SCRATCHPAD_COUNT - 1;
+ scratchpad_sel = idx;
+}
--
2.53.0
@@ -1,219 +0,0 @@
From 94e649317328c7af091516fe3441ea80a814d1e4 Mon Sep 17 00:00:00 2001
From: A Frederick Christensen <dwl@ivories.org>
Date: Thu, 26 Feb 2026 17:18:17 -0600
Subject: [PATCH] Apply simple_scratchpad patch
---
config.def.h | 4 ++-
dwl.c | 34 +++++++++++++++++++++--
simple_scratchpad.c | 68 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 103 insertions(+), 3 deletions(-)
create mode 100644 simple_scratchpad.c
diff --git a/config.def.h b/config.def.h
index 8a6eda0..bd6cb40 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]} },
@@ -154,7 +157,6 @@ static const Key keys[] = {
TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {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
diff --git a/dwl.c b/dwl.c
index 8a9715d..6ae7707 100644
--- a/dwl.c
+++ b/dwl.c
@@ -141,6 +141,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 {
@@ -242,6 +243,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);
@@ -2335,11 +2354,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]);
@@ -2557,6 +2586,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
+3 -1
View File
@@ -1,5 +1,7 @@
### Description ### Description
Swapfocus adds a new function on dwl: a shortcut to change the focus to the last focused window. If the last focused window is in another tag, then the focus will change to that tag. Swapfocus adds a new function on dwl: a shortcut to change the focus to the last focused window.
- If the last focused window is in another tag, then the focus will change to that tag.
- Alternatively: edit the patch and uncomment and comment out the lines instructed to keep the swapfocus shortcut from changing to another tag.
### Download ### Download
- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/swapfocus/swapfocus.patch) - [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/swapfocus/swapfocus.patch)
+50 -7
View File
@@ -1,12 +1,12 @@
From c4a9254ef63bbe1bae9ed525250e0b187668397f Mon Sep 17 00:00:00 2001 From 364b9f20b830886b9c0e6539ddba2cc206a286eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= <desgua@gmail.com> From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= <desgua@gmail.com>
Date: Fri, 1 May 2026 08:17:28 -0300 Date: Thu, 21 May 2026 09:50:03 -0300
Subject: [PATCH] swapfocus patch improvement Subject: [PATCH] swapfocus patch
--- ---
config.def.h | 1 + config.def.h | 1 +
dwl.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ dwl.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 48 insertions(+) 2 files changed, 91 insertions(+)
diff --git a/config.def.h b/config.def.h diff --git a/config.def.h b/config.def.h
index 8a6eda0..23e502d 100644 index 8a6eda0..23e502d 100644
@@ -21,7 +21,7 @@ index 8a6eda0..23e502d 100644
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
diff --git a/dwl.c b/dwl.c diff --git a/dwl.c b/dwl.c
index 101a45f..5e78719 100644 index 101a45f..c512323 100644
--- a/dwl.c --- a/dwl.c
+++ b/dwl.c +++ b/dwl.c
@@ -332,6 +332,7 @@ static void setsel(struct wl_listener *listener, void *data); @@ -332,6 +332,7 @@ static void setsel(struct wl_listener *listener, void *data);
@@ -61,7 +61,7 @@ index 101a45f..5e78719 100644
/* Put the new client atop the focus stack and select its monitor */ /* Put the new client atop the focus stack and select its monitor */
if (c && !client_is_unmanaged(c)) { if (c && !client_is_unmanaged(c)) {
wl_list_remove(&c->flink); wl_list_remove(&c->flink);
@@ -2679,6 +2688,44 @@ spawn(const Arg *arg) @@ -2679,6 +2688,87 @@ spawn(const Arg *arg)
} }
} }
@@ -93,13 +93,56 @@ index 101a45f..5e78719 100644
+ Arg a = {.ui = prevclient->tags}; + Arg a = {.ui = prevclient->tags};
+ selmon = prevclient->mon; + selmon = prevclient->mon;
+ view(&a); + view(&a);
+
+ /* Comment out the 3 lines above and
+ * uncommment the following lines
+ * if changing tags isn't desired */
+
+// int current_tag_clients = 0;
+// Client *tmp;
+// wl_list_for_each(tmp, &clients, link) {
+// /* Make sure it's on the current monitor, visible on the current tag, and mapped */
+// if (tmp->mon == selmon && (tmp->tags & selmon->tagset[selmon->seltags]) && !client_is_unmanaged(tmp)) {
+// current_tag_clients++;
+// }
+// }
+//
+// /* If there's more than 1 window here, mimic Mod+k instead of switching tags */
+// if (current_tag_clients > 1) {
+// Arg arg_focus = {.i = -1};
+// focusstack(&arg_focus);
+// }
+
+ /* End of not changing tags logic */
+ } else { + } else {
+ /* Tag IS visible: Just swap focus within the same view */ + /* Tag IS visible: Just swap focus within the same view */
+ focusclient(prevclient, 1); + focusclient(prevclient, 1);
+ } + }
+ } else { + } else {
+ Arg a = {.ui = 0}; + Arg a = {.ui = 0};
+ selmon = prevclient->mon;
+ view(&a); + view(&a);
+
+ /* Comment out the 3 lines above and
+ * uncommment the following lines
+ * if changing tags isn't desired */
+
+ /* use the following if changing tags isn't desired */
+// int current_tag_clients = 0;
+// Client *tmp;
+// wl_list_for_each(tmp, &clients, link) {
+// /* Make sure it's on the current monitor, visible on the current tag, and mapped */
+// if (tmp->mon == selmon && (tmp->tags & selmon->tagset[selmon->seltags]) && !client_is_unmanaged(tmp)) {
+// current_tag_clients++;
+// }
+// }
+//
+// /* If there's more than 1 window here, mimic Mod+k instead of switching tags */
+// if (current_tag_clients > 1) {
+// Arg arg_focus = {.i = -1};
+// focusstack(&arg_focus);
+// }
+ /* end of not changing tags logic */
+ } + }
+} +}
+ +
+6
View File
@@ -8,13 +8,19 @@ KNOWN BUGS:
- [git branch](https://codeberg.org/fauxmight/dwl/src/branch/touch-input) - [git branch](https://codeberg.org/fauxmight/dwl/src/branch/touch-input)
- [touch-input-wlroots-next-f4249db.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/touch-input/touch-input-wlroots-next-f4249db.patch) - [touch-input-wlroots-next-f4249db.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/touch-input/touch-input-wlroots-next-f4249db.patch)
- [touch-input-0.8.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/touch-input/touch-input-0.8.patch) - [touch-input-0.8.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/touch-input/touch-input-0.8.patch)
- [touch-input-0.8-osu-ver.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/touch-input/touch-input-0.8-osu-ver.patch)
### Authors ### Authors
#### Current
- [fauxmight](https://codeberg.org/fauxmight) - [fauxmight](https://codeberg.org/fauxmight)
- [6z7y](https://codeberg.org/6z7y) -- maintaining `osu!` version of the `touch-input` patch
#### Historic
- [minego](https://codeberg.org/minego) - [minego](https://codeberg.org/minego)
- [Unprex](https://github.com/Unprex) - [Unprex](https://github.com/Unprex)
### Changelog ### Changelog
- 2026-06-15 Add support for osu! fullscreen touch input (fix cursor stuck issue)
- 2026-02-26 Update patch for dwl v0.8 and dwl wlroots-next branch commit f4249db - 2026-02-26 Update patch for dwl v0.8 and dwl wlroots-next branch commit f4249db
- 2025-01-01 @fauxmight took over maintenance. Previous maintainer @minego notes [lack of available time](https://codeberg.org/dwl/dwl-patches/pulls/102#issuecomment-2557944) - 2025-01-01 @fauxmight took over maintenance. Previous maintainer @minego notes [lack of available time](https://codeberg.org/dwl/dwl-patches/pulls/102#issuecomment-2557944)
- 2024-02-11 Corrected issue where motion events where not sending notifications for unfocused clients such as an on screen keyboard - 2024-02-11 Corrected issue where motion events where not sending notifications for unfocused clients such as an on screen keyboard
+279
View File
@@ -0,0 +1,279 @@
diff --git a/dwl.c b/dwl.c
index 44f3ad9..977f2c8 100644
--- a/dwl.c
+++ b/dwl.c
@@ -24,6 +24,7 @@
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_drm.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
+#include <wlr/types/wlr_ext_data_control_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
@@ -51,6 +52,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_touch.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
@@ -161,6 +163,12 @@ typedef struct {
struct wl_listener destroy;
} KeyboardGroup;
+typedef struct TouchGroup {
+ struct wl_list link;
+ struct wlr_touch *touch;
+ Monitor *m;
+} TouchGroup;
+
typedef struct {
/* Must keep this field first */
unsigned int type; /* LayerShell */
@@ -269,6 +277,7 @@ static void createpointer(struct wlr_pointer *pointer);
static void createpointerconstraint(struct wl_listener *listener, void *data);
static void createpopup(struct wl_listener *listener, void *data);
static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint);
+static void createtouch(struct wlr_touch *touch);
static void cursorframe(struct wl_listener *listener, void *data);
static void cursorwarptohint(void);
static void destroydecoration(struct wl_listener *listener, void *data);
@@ -338,6 +347,10 @@ static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg);
static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
+static void touchdown(struct wl_listener *listener, void *data);
+static void touchup(struct wl_listener *listener, void *data);
+static void touchframe(struct wl_listener *listener, void *data);
+static void touchmotion(struct wl_listener *listener, void *data);
static void unlocksession(struct wl_listener *listener, void *data);
static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
static void unmapnotify(struct wl_listener *listener, void *data);
@@ -405,6 +418,7 @@ static struct wlr_output_layout *output_layout;
static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static struct wl_list touches;
/* global event handlers */
static struct wl_listener cursor_axis = {.notify = axisnotify};
@@ -434,6 +448,10 @@ static struct wl_listener request_set_sel = {.notify = setsel};
static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape};
static struct wl_listener request_start_drag = {.notify = requeststartdrag};
static struct wl_listener start_drag = {.notify = startdrag};
+static struct wl_listener touch_down = {.notify = touchdown};
+static struct wl_listener touch_frame = {.notify = touchframe};
+static struct wl_listener touch_motion = {.notify = touchmotion};
+static struct wl_listener touch_up = {.notify = touchup};
static struct wl_listener new_session_lock = {.notify = locksession};
#ifdef XWAYLAND
@@ -585,7 +603,7 @@ arrangelayers(Monitor *m)
arrange(m);
}
- /* Arrange non-exlusive surfaces from top->bottom */
+ /* Arrange non-exclusive surfaces from top->bottom */
for (i = 3; i >= 0; i--)
arrangelayer(m, &m->layers[i], &usable_area, 0);
@@ -781,6 +799,10 @@ cleanuplisteners(void)
wl_list_remove(&request_set_cursor_shape.link);
wl_list_remove(&request_start_drag.link);
wl_list_remove(&start_drag.link);
+ wl_list_remove(&touch_down.link);
+ wl_list_remove(&touch_frame.link);
+ wl_list_remove(&touch_motion.link);
+ wl_list_remove(&touch_up.link);
wl_list_remove(&new_session_lock.link);
#ifdef XWAYLAND
wl_list_remove(&new_xwayland_surface.link);
@@ -1199,6 +1221,16 @@ createpopup(struct wl_listener *listener, void *data)
LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup);
}
+void
+createtouch(struct wlr_touch *wlr_touch)
+{
+ TouchGroup *touch = ecalloc(1, sizeof(TouchGroup));
+
+ touch->touch = wlr_touch;
+ wl_list_insert(&touches, &touch->link);
+ wlr_cursor_attach_input_device(cursor, &wlr_touch->base);
+}
+
void
cursorconstrain(struct wlr_pointer_constraint_v1 *constraint)
{
@@ -1590,6 +1622,9 @@ inputdevice(struct wl_listener *listener, void *data)
case WLR_INPUT_DEVICE_POINTER:
createpointer(wlr_pointer_from_input_device(device));
break;
+ case WLR_INPUT_DEVICE_TOUCH:
+ createtouch(wlr_touch_from_input_device(device));
+ break;
default:
/* TODO handle other input device types */
break;
@@ -1602,6 +1637,8 @@ inputdevice(struct wl_listener *listener, void *data)
caps = WL_SEAT_CAPABILITY_POINTER;
if (!wl_list_empty(&kb_group->wlr_group->devices))
caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ if (!wl_list_empty(&touches))
+ caps |= WL_SEAT_CAPABILITY_TOUCH;
wlr_seat_set_capabilities(seat, caps);
}
@@ -2133,7 +2170,7 @@ powermgrsetmode(struct wl_listener *listener, void *data)
if (!m)
return;
- m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the ouput */
+ m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the output */
wlr_output_state_set_enabled(&state, event->mode);
wlr_output_commit_state(m->wlr_output, &state);
@@ -2455,7 +2492,7 @@ setup(void)
wlr_log_init(log_level, NULL);
/* The Wayland display is managed by libwayland. It handles accepting
- * clients from the Unix socket, manging Wayland globals, and so on. */
+ * clients from the Unix socket, managing Wayland globals, and so on. */
dpy = wl_display_create();
event_loop = wl_display_get_event_loop(dpy);
@@ -2518,6 +2555,7 @@ setup(void)
wlr_export_dmabuf_manager_v1_create(dpy);
wlr_screencopy_manager_v1_create(dpy);
wlr_data_control_manager_v1_create(dpy);
+ wlr_ext_data_control_manager_v1_create(dpy, 1);
wlr_primary_selection_v1_device_manager_create(dpy);
wlr_viewporter_create(dpy);
wlr_single_pixel_buffer_manager_v1_create(dpy);
@@ -2615,6 +2653,13 @@ setup(void)
wl_signal_add(&cursor->events.axis, &cursor_axis);
wl_signal_add(&cursor->events.frame, &cursor_frame);
+ wl_list_init(&touches);
+
+ wl_signal_add(&cursor->events.touch_down, &touch_down);
+ wl_signal_add(&cursor->events.touch_frame, &touch_frame);
+ wl_signal_add(&cursor->events.touch_motion, &touch_motion);
+ wl_signal_add(&cursor->events.touch_up, &touch_up);
+
cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape);
@@ -2787,6 +2832,111 @@ toggleview(const Arg *arg)
printstatus();
}
+void
+touchdown(struct wl_listener *listener, void *data)
+{
+ struct wlr_touch_down_event *event = data;
+ double lx, ly;
+ double sx, sy;
+ struct wlr_surface *surface;
+ Client *c = NULL;
+ Monitor *m;
+
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+
+ // Map the input to the appropriate output
+ wl_list_for_each(m, &mons, link) {
+ if (m == NULL || m->wlr_output == NULL)
+ continue;
+ if (event->touch->output_name != NULL &&
+ 0 != strcmp(event->touch->output_name, m->wlr_output->name))
+ continue;
+ wlr_cursor_map_input_to_output(cursor, &event->touch->base, m->wlr_output);
+ }
+
+ wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base,
+ event->x, event->y, &lx, &ly);
+
+ /* Find the client under the touch point */
+ xytonode(lx, ly, &surface, &c, NULL, &sx, &sy);
+ if (sloppyfocus && c)
+ focusclient(c, 0);
+
+ // Send touch event to client (if any)
+ if (surface != NULL) {
+ wlr_seat_touch_notify_down(seat, surface, event->time_msec,
+ event->touch_id, sx, sy);
+ }
+
+ // ALWAYS move cursor and send click - no conditions!
+ wlr_cursor_warp_closest(cursor, NULL, lx, ly);
+ motionnotify(0, NULL, 0, 0, 0, 0);
+
+ wlr_seat_pointer_notify_button(seat, event->time_msec,
+ BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
+ wlr_seat_pointer_notify_frame(seat);
+}
+
+void
+touchup(struct wl_listener *listener, void *data)
+{
+ struct wlr_touch_up_event *event = data;
+
+ if (!wlr_seat_touch_get_point(seat, event->touch_id))
+ return;
+
+ wlr_seat_touch_notify_up(seat, event->time_msec, event->touch_id);
+
+ // Always send click release
+ wlr_seat_pointer_notify_button(seat, event->time_msec,
+ BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
+ wlr_seat_pointer_notify_frame(seat);
+
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+}
+
+void
+touchframe(struct wl_listener *listener, void *data)
+{
+ wlr_seat_touch_notify_frame(seat);
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+}
+
+void
+touchmotion(struct wl_listener *listener, void *data)
+{
+ struct wlr_touch_motion_event *event = data;
+ double lx, ly;
+ double sx, sy;
+ struct wlr_surface *surface;
+ Client *c = NULL;
+
+ if (!wlr_seat_touch_get_point(seat, event->touch_id)) {
+ return;
+ }
+
+ wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, event->y, &lx, &ly);
+ wlr_cursor_warp_closest(cursor, NULL, lx, ly);
+ motionnotify(0, NULL, 0, 0, 0, 0);
+ wlr_seat_pointer_notify_frame(seat);
+
+ xytonode(lx, ly, &surface, &c, NULL, &sx, &sy);
+
+ if (c != NULL && surface != NULL) {
+ if (sloppyfocus)
+ focusclient(c, 0);
+ wlr_seat_touch_point_focus(seat, surface, event->time_msec, event->touch_id, sx, sy);
+ } else {
+ if (sloppyfocus)
+ focusclient(NULL, 0);
+ wlr_seat_touch_point_clear_focus(seat, event->time_msec, event->touch_id);
+ }
+ wlr_seat_touch_notify_motion(seat, event->time_msec, event->touch_id, sx, sy);
+
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+}
+
+
void
unlocksession(struct wl_listener *listener, void *data)
{
+2 -1
View File
@@ -3,10 +3,11 @@ Adds (inner) gaps between client windows and (outer) gaps between windows and
the screen edge in a flexible manner. the screen edge in a flexible manner.
### Download ### Download
- [git branch](https://codeberg.org/sevz/dwl/src/branch/vanitygaps)
- [main 2025-01-20](/dwl/dwl-patches/raw/branch/main/patches/vanitygaps/vanitygaps.patch) - [main 2025-01-20](/dwl/dwl-patches/raw/branch/main/patches/vanitygaps/vanitygaps.patch)
- [vanitygaps-0.7.patch](/dwl/dwl-patches/raw/branch/main/patches/vanitygaps/vanitygaps-0.7.patch) - [vanitygaps-0.7.patch](/dwl/dwl-patches/raw/branch/main/patches/vanitygaps/vanitygaps-0.7.patch)
- [vanitygaps-0.8.patch](/dwl/dwl-patches/raw/branch/main/patches/vanitygaps/vanitygaps-0.8.patch)
### Authors ### Authors
- [nshan651](https://codeberg.org/nshan651)
- [sevz](https://codeberg.org/sevz) - [sevz](https://codeberg.org/sevz)
- [Bonicgamer](https://github.com/Bonicgamer) - [Bonicgamer](https://github.com/Bonicgamer)
+354
View File
@@ -0,0 +1,354 @@
From 826adff70b94a402847d6d374a3c4ddca74d7175 Mon Sep 17 00:00:00 2001
From: nshan651 <nshan651@proton.me>
Date: Thu, 18 Jun 2026 22:17:54 -0500
Subject: [PATCH 1/2] vanitygaps v0.8 patch
---
config.def.h | 21 ++++++++
dwl.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 161 insertions(+), 10 deletions(-)
diff --git a/config.def.h b/config.def.h
index 8a6eda0..980c147 100644
--- a/config.def.h
+++ b/config.def.h
@@ -6,7 +6,12 @@
/* 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 gappih = 10; /* horiz inner gap between windows */
+static const unsigned int gappiv = 10; /* vert inner gap between windows */
+static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */
+static const unsigned int gappov = 10; /* vert outer gap between windows and screen edge */
static const float rootcolor[] = COLOR(0x222222ff);
static const float bordercolor[] = COLOR(0x444444ff);
static const float focuscolor[] = COLOR(0x005577ff);
@@ -129,6 +134,22 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_d, incnmaster, {.i = -1} },
{ MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} },
{ MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_h, incgaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_l, incgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_H, incogaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_L, incogaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_h, incigaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_l, incigaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_0, togglegaps, {0} },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_parenright,defaultgaps, {0} },
+ { MODKEY, XKB_KEY_y, incihgaps, {.i = +1 } },
+ { MODKEY, XKB_KEY_o, incihgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_y, incivgaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_o, incivgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_y, incohgaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_o, incohgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Y, incovgaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, incovgaps, {.i = -1 } },
{ MODKEY, XKB_KEY_Return, zoom, {0} },
{ MODKEY, XKB_KEY_Tab, view, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_c, killclient, {0} },
diff --git a/dwl.c b/dwl.c
index 44f3ad9..6af921a 100644
--- a/dwl.c
+++ b/dwl.c
@@ -197,6 +197,10 @@ struct Monitor {
struct wlr_box w; /* window area, layout-relative */
struct wl_list layers[4]; /* LayerSurface.link */
const Layout *lt[2];
+ int gappih; /* horizontal gap between windows */
+ int gappiv; /* vertical gap between windows */
+ int gappoh; /* horizontal outer gaps */
+ int gappov; /* vertical outer gaps */
unsigned int seltags;
unsigned int sellt;
uint32_t tagset[2];
@@ -271,6 +275,7 @@ static void createpopup(struct wl_listener *listener, void *data);
static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint);
static void cursorframe(struct wl_listener *listener, void *data);
static void cursorwarptohint(void);
+static void defaultgaps(const Arg *arg);
static void destroydecoration(struct wl_listener *listener, void *data);
static void destroydragicon(struct wl_listener *listener, void *data);
static void destroyidleinhibitor(struct wl_listener *listener, void *data);
@@ -290,6 +295,13 @@ 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 incnmaster(const Arg *arg);
+static void incgaps(const Arg *arg);
+static void incigaps(const Arg *arg);
+static void incihgaps(const Arg *arg);
+static void incivgaps(const Arg *arg);
+static void incogaps(const Arg *arg);
+static void incohgaps(const Arg *arg);
+static void incovgaps(const Arg *arg);
static void inputdevice(struct wl_listener *listener, void *data);
static int keybinding(uint32_t mods, xkb_keysym_t sym);
static void keypress(struct wl_listener *listener, void *data);
@@ -323,6 +335,7 @@ 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);
static void setfullscreen(Client *c, int fullscreen);
+static void setgaps(int oh, int ov, int ih, int iv);
static void setlayout(const Arg *arg);
static void setmfact(const Arg *arg);
static void setmon(Client *c, Monitor *m, uint32_t newtags);
@@ -336,6 +349,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);
@@ -406,6 +420,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};
@@ -1055,6 +1071,11 @@ createmon(struct wl_listener *listener, void *data)
for (i = 0; i < LENGTH(m->layers); i++)
wl_list_init(&m->layers[i]);
+ m->gappih = gappih;
+ m->gappiv = gappiv;
+ m->gappoh = gappoh;
+ m->gappov = gappov;
+
wlr_output_state_init(&state);
/* Initialize monitor state using configured rules */
m->tagset[0] = m->tagset[1] = 1;
@@ -1237,6 +1258,12 @@ cursorwarptohint(void)
}
}
+void
+defaultgaps(const Arg *arg)
+{
+ setgaps(gappoh, gappov, gappih, gappiv);
+}
+
void
destroydecoration(struct wl_listener *listener, void *data)
{
@@ -1575,6 +1602,83 @@ incnmaster(const Arg *arg)
arrange(selmon);
}
+void
+incgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh + arg->i,
+ selmon->gappov + arg->i,
+ selmon->gappih + arg->i,
+ selmon->gappiv + arg->i
+ );
+}
+
+void
+incigaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov,
+ selmon->gappih + arg->i,
+ selmon->gappiv + arg->i
+ );
+}
+
+void
+incihgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov,
+ selmon->gappih + arg->i,
+ selmon->gappiv
+ );
+}
+
+void
+incivgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov,
+ selmon->gappih,
+ selmon->gappiv + arg->i
+ );
+}
+
+void
+incogaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh + arg->i,
+ selmon->gappov + arg->i,
+ selmon->gappih,
+ selmon->gappiv
+ );
+}
+
+void
+incohgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh + arg->i,
+ selmon->gappov,
+ selmon->gappih,
+ selmon->gappiv
+ );
+}
+
+void
+incovgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov + arg->i,
+ selmon->gappih,
+ selmon->gappiv
+ );
+}
+
void
inputdevice(struct wl_listener *listener, void *data)
{
@@ -2368,6 +2472,16 @@ setfullscreen(Client *c, int fullscreen)
printstatus();
}
+void
+setgaps(int oh, int ov, int ih, int iv)
+{
+ selmon->gappoh = MAX(oh, 0);
+ selmon->gappov = MAX(ov, 0);
+ selmon->gappih = MAX(ih, 0);
+ selmon->gappiv = MAX(iv, 0);
+ arrange(selmon);
+}
+
void
setlayout(const Arg *arg)
{
@@ -2712,7 +2826,7 @@ tagmon(const Arg *arg)
void
tile(Monitor *m)
{
- unsigned int mw, my, ty;
+ unsigned int mw, my, ty, h, r, oe = enablegaps, ie = enablegaps;
int i, n = 0;
Client *c;
@@ -2722,22 +2836,31 @@ tile(Monitor *m)
if (n == 0)
return;
+ if (smartgaps == n) {
+ oe = 0; // outer gaps disabled
+ }
+
if (n > m->nmaster)
- mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0;
+ mw = m->nmaster ? (int)roundf((m->w.width + m->gappiv*ie) * m->mfact) : 0;
else
- mw = m->w.width;
- i = my = ty = 0;
+ mw = m->w.width - 2*m->gappov*oe + m->gappiv*ie;
+ i = 0;
+ my = ty = m->gappoh*oe;
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->gappoh*oe - m->gappih*ie * (r - 1)) / r;
+ resize(c, (struct wlr_box){.x = m->w.x + m->gappov*oe, .y = m->w.y + my,
+ .width = mw - m->gappiv*ie, .height = h}, 0);
+ my += c->geom.height + m->gappih*ie;
} 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->gappoh*oe - m->gappih*ie * (r - 1)) / r;
+ resize(c, (struct wlr_box){.x = m->w.x + mw + m->gappov*oe, .y = m->w.y + ty,
+ .width = m->w.width - mw - 2*m->gappov*oe, .height = h}, 0);
+ ty += c->geom.height + m->gappih*ie;
}
i++;
}
@@ -2760,6 +2883,13 @@ togglefullscreen(const Arg *arg)
setfullscreen(sel, !sel->isfullscreen);
}
+void
+togglegaps(const Arg *arg)
+{
+ enablegaps = !enablegaps;
+ arrange(selmon);
+}
+
void
toggletag(const Arg *arg)
{
--
2.52.0
From 8b9db986eddeade22d92fb15a8c836912869be29 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?=
<leohdz172@protonmail.com>
Date: Wed, 20 Jul 2022 00:15:32 -0500
Subject: [PATCH 2/2] allow gaps in monocle layout if requested
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Leonardo Hernández Hernández <leohdz172@proton.me>
---
config.def.h | 1 +
dwl.c | 6 +++++-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/config.def.h b/config.def.h
index 980c147..10b9054 100644
--- a/config.def.h
+++ b/config.def.h
@@ -7,6 +7,7 @@
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 int monoclegaps = 0; /* 1 means outer gaps in monocle layout */
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int gappih = 10; /* horiz inner gap between windows */
static const unsigned int gappiv = 10; /* vert inner gap between windows */
diff --git a/dwl.c b/dwl.c
index 6af921a..144dd78 100644
--- a/dwl.c
+++ b/dwl.c
@@ -1933,8 +1933,12 @@ monocle(Monitor *m)
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
continue;
- resize(c, m->w, 0);
n++;
+ if (!monoclegaps)
+ resize(c, m->w, 0);
+ else
+ resize(c, (struct wlr_box){.x = m->w.x + gappoh, .y = m->w.y + gappov,
+ .width = m->w.width - 2 * gappoh, .height = m->w.height - 2 * gappov}, 0);
}
if (n)
snprintf(m->ltsymbol, LENGTH(m->ltsymbol), "[%d]", n);
--
2.52.0
+17
View File
@@ -0,0 +1,17 @@
# Description
Make tag 0 a regular tag
### Dependencies
This patch comes in **two versions**:
- **`bar_zerotag.patch`** - Depends on the [bar](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/bar/) patch.
- **`zerotag.patch`** - No dependencies.
### Download
- [git branch](https://codeberg.org/6z7y/dwl-patches/raw/branch/zerotag/patches/zerotag/zerotag.patch)
- [main 2026-02-25](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/zerotag/zerotag.patch)
### Authors
- [6z7y](https://codeberg.org/6z7y)
+33
View File
@@ -0,0 +1,33 @@
diff --git a/config.def.h b/config.def.h
index 7da50d2..f058a17 100644
--- a/config.def.h
+++ b/config.def.h
@@ -21,7 +21,7 @@ static uint32_t colors[][3] = {
};
/* tagging */
-static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" };
/* logging */
static int log_level = WLR_ERROR;
@@ -145,8 +145,6 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
- { MODKEY, XKB_KEY_0, view, {.ui = ~0} },
- { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
@@ -160,8 +158,10 @@ 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),
+ TAGKEYS( XKB_KEY_0, XKB_KEY_parenright, 9),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {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
+30
View File
@@ -0,0 +1,30 @@
diff --git a/config.def.h b/config.def.h
index 8a6eda0..b0fdfe8 100644
--- a/config.def.h
+++ b/config.def.h
@@ -15,7 +15,7 @@ static const float urgentcolor[] = COLOR(0xff0000ff);
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 (9)
+#define TAGCOUNT (10)
/* logging */
static int log_level = WLR_ERROR;
@@ -138,8 +138,6 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
- { MODKEY, XKB_KEY_0, view, {.ui = ~0} },
- { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
@@ -153,6 +151,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),
+ TAGKEYS( XKB_KEY_0, XKB_KEY_parenright, 9),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {0} },
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */