mirror of
https://codeberg.org/dwl/dwl-patches.git
synced 2026-06-11 10:23:19 +00:00
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.
This commit is contained in:
@@ -1,21 +1,23 @@
|
|||||||
From f5d7fe287bd4405879a92d649243d3f646652617 Mon Sep 17 00:00:00 2001
|
From ad32bb92157d5238340eb53322254dc8127477db 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: Sun, 17 May 2026 22:49:07 +0300
|
||||||
Subject: [PATCH] Refactor btrtile-gaps to v0.8
|
Subject: [PATCH] btrtile resizing refactor
|
||||||
|
|
||||||
|
- Refactored the tiled client resizing logic to be more logical. Insted
|
||||||
|
of resizing based on clients split nodes, now btrtile handles client resizing based on the nearest edge client edge on the resize direction. Now it handles also resizing of the clientless splitnodes and should feel more intuitive.
|
||||||
---
|
---
|
||||||
btrtile.c | 584 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
btrtile.c | 582 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
config.def.h | 12 ++
|
config.def.h | 12 ++
|
||||||
dwl.c | 148 ++++++++++---
|
dwl.c | 160 +++++++++++---
|
||||||
3 files changed, 717 insertions(+), 27 deletions(-)
|
3 files changed, 727 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..c63e59f
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/btrtile.c
|
+++ b/btrtile.c
|
||||||
@@ -0,0 +1,584 @@
|
@@ -0,0 +1,582 @@
|
||||||
+/* ************************************************************************** */
|
+/* ************************************************************************** */
|
||||||
+/* @@@ @@@@@@@@ */
|
+/* @@@ @@@@@@@@ */
|
||||||
+/* @@@ @@@@@@@@@@ */
|
+/* @@@ @@@@@@@@@@ */
|
||||||
@@ -26,7 +28,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/05/17 22:00:31 by julmajustus : : : : : : */
|
||||||
+/* */
|
+/* */
|
||||||
+/* ************************************************************************** */
|
+/* ************************************************************************** */
|
||||||
+
|
+
|
||||||
@@ -49,7 +51,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);
|
||||||
@@ -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;
|
||||||
@@ -129,7 +132,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 +150,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,16 +169,16 @@ 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 non tiled clients from tree. */
|
||||||
+ 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 && !c->isfullscreen) {
|
||||||
+ } else {
|
+ } 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) */
|
||||||
+ if (!(focused = xytoclient(cursor->x, cursor->y)))
|
+ if (!(focused = xytoclient(cursor->x, cursor->y)))
|
||||||
@@ -268,30 +271,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 +425,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,43 +460,22 @@ 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,
|
+ /* Skip the arrange when called from motionnotify; that path calls
|
||||||
+ * we call arrange from motionotify */
|
+ * arrange itself after rate-limiting. */
|
||||||
+ if (!resizing_from_mouse) {
|
+ if (!resizing_from_mouse)
|
||||||
+ arrange(selmon);
|
+ 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) {
|
||||||
@@ -565,11 +564,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 +578,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 +591,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 +639,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..d37e235 100644
|
||||||
--- a/dwl.c
|
--- a/dwl.c
|
||||||
+++ b/dwl.c
|
+++ b/dwl.c
|
||||||
@@ -1,6 +1,7 @@
|
@@ -1,6 +1,7 @@
|
||||||
@@ -765,18 +765,22 @@ index 44f3ad9..f74d9a1 100644
|
|||||||
|
|
||||||
/* The xdg-protocol specifies:
|
/* The xdg-protocol specifies:
|
||||||
*
|
*
|
||||||
@@ -1332,6 +1364,10 @@ destroynotify(struct wl_listener *listener, void *data)
|
@@ -1332,6 +1364,14 @@ destroynotify(struct wl_listener *listener, void *data)
|
||||||
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)
|
+ Monitor *mon;
|
||||||
+ remove_client(selmon, c);
|
+ wl_list_for_each(mon, &mons, link) {
|
||||||
|
+ 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 +1902,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,7 +790,7 @@ 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 +1957,56 @@ 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));
|
||||||
|
|
||||||
@@ -850,7 +854,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 +2040,41 @@ moveresize(const Arg *arg)
|
||||||
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
|
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -908,6 +912,21 @@ index 44f3ad9..f74d9a1 100644
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2826,6 +2924,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,23 @@
|
|||||||
From 1b75163aac16942a715c6fc0aaf5d508e778dd6a Mon Sep 17 00:00:00 2001
|
From 7ef8c161eb3cd801f608784e7d888fe12e94b9d4 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: Sun, 17 May 2026 22:39:30 +0300
|
||||||
Subject: [PATCH] Refactor btrtile for v0.8
|
Subject: [PATCH] btrtile resizing refactor
|
||||||
|
|
||||||
|
- Refactored the tiled client resizing logic to be more logical. Insted
|
||||||
|
of resizing based on clients split nodes, now btrtile handles client resizing based on the nearest edge client edge on the resize direction. Now it handles also resizing of the clientless splitnodes and should feel more intuitive.
|
||||||
---
|
---
|
||||||
btrtile.c | 565 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
btrtile.c | 563 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
config.def.h | 12 ++
|
config.def.h | 12 ++
|
||||||
dwl.c | 148 +++++++++++---
|
dwl.c | 160 ++++++++++++---
|
||||||
3 files changed, 698 insertions(+), 27 deletions(-)
|
3 files changed, 708 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..ad810bf
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/btrtile.c
|
+++ b/btrtile.c
|
||||||
@@ -0,0 +1,565 @@
|
@@ -0,0 +1,563 @@
|
||||||
+/* ************************************************************************** */
|
+/* ************************************************************************** */
|
||||||
+/* @@@ @@@@@@@@ */
|
+/* @@@ @@@@@@@@ */
|
||||||
+/* @@@ @@@@@@@@@@ */
|
+/* @@@ @@@@@@@@@@ */
|
||||||
@@ -26,7 +28,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/05/17 22:09:15 by julmajustus : : : : : : */
|
||||||
+/* */
|
+/* */
|
||||||
+/* ************************************************************************** */
|
+/* ************************************************************************** */
|
||||||
+
|
+
|
||||||
@@ -49,7 +51,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);
|
||||||
@@ -147,16 +150,16 @@ 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 non tiled clients from tree. */
|
||||||
+ 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 && !c->isfullscreen) {
|
||||||
+ } else {
|
+ } 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) */
|
||||||
+ if (!(focused = xytoclient(cursor->x, cursor->y)))
|
+ if (!(focused = xytoclient(cursor->x, cursor->y)))
|
||||||
@@ -249,30 +252,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 +406,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,43 +441,22 @@ 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,
|
+ /* Skip the arrange when called from motionnotify; that path calls
|
||||||
+ * we call arrange from motionotify */
|
+ * arrange itself after rate-limiting. */
|
||||||
+ if (!resizing_from_mouse) {
|
+ if (!resizing_from_mouse)
|
||||||
+ arrange(selmon);
|
+ 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) {
|
||||||
@@ -546,11 +545,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 +559,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 +572,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 +620,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..d37e235 100644
|
||||||
--- a/dwl.c
|
--- a/dwl.c
|
||||||
+++ b/dwl.c
|
+++ b/dwl.c
|
||||||
@@ -1,6 +1,7 @@
|
@@ -1,6 +1,7 @@
|
||||||
@@ -746,18 +746,22 @@ index 44f3ad9..f74d9a1 100644
|
|||||||
|
|
||||||
/* The xdg-protocol specifies:
|
/* The xdg-protocol specifies:
|
||||||
*
|
*
|
||||||
@@ -1332,6 +1364,10 @@ destroynotify(struct wl_listener *listener, void *data)
|
@@ -1332,6 +1364,14 @@ destroynotify(struct wl_listener *listener, void *data)
|
||||||
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)
|
+ Monitor *mon;
|
||||||
+ remove_client(selmon, c);
|
+ wl_list_for_each(mon, &mons, link) {
|
||||||
|
+ 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 +1902,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,7 +771,7 @@ 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 +1957,56 @@ 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));
|
||||||
|
|
||||||
@@ -831,7 +835,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 +2040,41 @@ moveresize(const Arg *arg)
|
||||||
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
|
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -889,6 +893,21 @@ index 44f3ad9..f74d9a1 100644
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2826,6 +2924,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,23 @@
|
|||||||
From a9329abe48083a4c5b64eee726ceefdb4bf0bb9f Mon Sep 17 00:00:00 2001
|
From b8d84c87f13681118865b83312387a881ab10b73 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: Sun, 17 May 2026 23:10:49 +0300
|
||||||
Subject: [PATCH] Refactor btrtile-gaps to wlroots-next
|
Subject: [PATCH] btrtile resizing refactor
|
||||||
|
|
||||||
|
- Refactored the tiled client resizing logic to be more logical. Insted
|
||||||
|
of resizing based on clients split nodes, now btrtile handles client resizing based on the nearest edge client edge on the resize direction. Now it handles also resizing of the clientless splitnodes and should feel more intuitive.
|
||||||
---
|
---
|
||||||
btrtile.c | 584 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
btrtile.c | 582 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
config.def.h | 12 ++
|
config.def.h | 12 ++
|
||||||
dwl.c | 148 ++++++++++---
|
dwl.c | 161 +++++++++++---
|
||||||
3 files changed, 717 insertions(+), 27 deletions(-)
|
3 files changed, 728 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..c63e59f
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/btrtile.c
|
+++ b/btrtile.c
|
||||||
@@ -0,0 +1,584 @@
|
@@ -0,0 +1,582 @@
|
||||||
+/* ************************************************************************** */
|
+/* ************************************************************************** */
|
||||||
+/* @@@ @@@@@@@@ */
|
+/* @@@ @@@@@@@@ */
|
||||||
+/* @@@ @@@@@@@@@@ */
|
+/* @@@ @@@@@@@@@@ */
|
||||||
@@ -26,7 +28,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/05/17 22:00:31 by julmajustus : : : : : : */
|
||||||
+/* */
|
+/* */
|
||||||
+/* ************************************************************************** */
|
+/* ************************************************************************** */
|
||||||
+
|
+
|
||||||
@@ -49,7 +51,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);
|
||||||
@@ -129,10 +132,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,16 +169,16 @@ 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 non tiled clients from tree. */
|
||||||
+ 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 && !c->isfullscreen) {
|
||||||
+ } else {
|
+ } 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) */
|
||||||
+ if (!(focused = xytoclient(cursor->x, cursor->y)))
|
+ if (!(focused = xytoclient(cursor->x, cursor->y)))
|
||||||
@@ -268,30 +271,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 +425,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,43 +460,22 @@ 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,
|
+ /* Skip the arrange when called from motionnotify; that path calls
|
||||||
+ * we call arrange from motionotify */
|
+ * arrange itself after rate-limiting. */
|
||||||
+ if (!resizing_from_mouse) {
|
+ if (!resizing_from_mouse)
|
||||||
+ arrange(selmon);
|
+ 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) {
|
||||||
@@ -565,11 +564,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 +578,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 +591,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 +639,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..32a94c4 100644
|
||||||
--- a/dwl.c
|
--- a/dwl.c
|
||||||
+++ b/dwl.c
|
+++ b/dwl.c
|
||||||
@@ -1,6 +1,7 @@
|
@@ -1,6 +1,7 @@
|
||||||
@@ -765,18 +765,23 @@ index 8101ffa..e4e7074 100644
|
|||||||
|
|
||||||
/* The xdg-protocol specifies:
|
/* The xdg-protocol specifies:
|
||||||
*
|
*
|
||||||
@@ -1336,6 +1368,10 @@ destroynotify(struct wl_listener *listener, void *data)
|
@@ -1336,6 +1368,15 @@ destroynotify(struct wl_listener *listener, void *data)
|
||||||
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)
|
+ // btrtile remove clients for each monitor
|
||||||
+ remove_client(selmon, c);
|
+ Monitor *mon;
|
||||||
|
+ wl_list_for_each(mon, &mons, link) {
|
||||||
|
+ 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 +1907,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,7 +791,7 @@ 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 +1962,56 @@ 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));
|
||||||
|
|
||||||
@@ -850,7 +855,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 +2045,41 @@ moveresize(const Arg *arg)
|
||||||
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
|
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -908,6 +913,21 @@ index 8101ffa..e4e7074 100644
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2833,6 +2932,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,23 @@
|
|||||||
From 912701a41b19c013a9adcb176a7b6503aa1cf9c1 Mon Sep 17 00:00:00 2001
|
From 3c78ee7e48fb24276d1a8d722ca1e715ea07d17f 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: Sun, 17 May 2026 23:28:41 +0300
|
||||||
Subject: [PATCH] Refactor btrtile to wlroots-next
|
Subject: [PATCH] btrtile resizing refactor
|
||||||
|
|
||||||
|
- Refactored the tiled client resizing logic to be more logical. Insted
|
||||||
|
of resizing based on clients split nodes, now btrtile handles client resizing based on the nearest edge client edge on the resize direction. Now it handles also resizing of the clientless splitnodes and should feel more intuitive.
|
||||||
---
|
---
|
||||||
btrtile.c | 565 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
btrtile.c | 563 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
config.def.h | 12 ++
|
config.def.h | 12 ++
|
||||||
dwl.c | 148 +++++++++++---
|
dwl.c | 161 ++++++++++++---
|
||||||
3 files changed, 698 insertions(+), 27 deletions(-)
|
3 files changed, 709 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..7e70478
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/btrtile.c
|
+++ b/btrtile.c
|
||||||
@@ -0,0 +1,565 @@
|
@@ -0,0 +1,563 @@
|
||||||
+/* ************************************************************************** */
|
+/* ************************************************************************** */
|
||||||
+/* @@@ @@@@@@@@ */
|
+/* @@@ @@@@@@@@ */
|
||||||
+/* @@@ @@@@@@@@@@ */
|
+/* @@@ @@@@@@@@@@ */
|
||||||
@@ -26,7 +28,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/05/17 23:26:25 by julmajustus : : : : : : */
|
||||||
+/* */
|
+/* */
|
||||||
+/* ************************************************************************** */
|
+/* ************************************************************************** */
|
||||||
+
|
+
|
||||||
@@ -49,7 +51,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);
|
||||||
@@ -147,16 +150,16 @@ 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 non tiled clients from tree. */
|
||||||
+ 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 && !c->isfullscreen) {
|
||||||
+ } else {
|
+ } 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) */
|
||||||
+ if (!(focused = xytoclient(cursor->x, cursor->y)))
|
+ if (!(focused = xytoclient(cursor->x, cursor->y)))
|
||||||
@@ -249,30 +252,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 +406,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,43 +441,22 @@ 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,
|
+ /* Skip the arrange when called from motionnotify; that path calls
|
||||||
+ * we call arrange from motionotify */
|
+ * arrange itself after rate-limiting. */
|
||||||
+ if (!resizing_from_mouse) {
|
+ if (!resizing_from_mouse)
|
||||||
+ arrange(selmon);
|
+ 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) {
|
||||||
@@ -546,11 +545,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 +559,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 +572,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 +620,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..32a94c4 100644
|
||||||
--- a/dwl.c
|
--- a/dwl.c
|
||||||
+++ b/dwl.c
|
+++ b/dwl.c
|
||||||
@@ -1,6 +1,7 @@
|
@@ -1,6 +1,7 @@
|
||||||
@@ -746,18 +746,23 @@ index 8101ffa..e4e7074 100644
|
|||||||
|
|
||||||
/* The xdg-protocol specifies:
|
/* The xdg-protocol specifies:
|
||||||
*
|
*
|
||||||
@@ -1336,6 +1368,10 @@ destroynotify(struct wl_listener *listener, void *data)
|
@@ -1336,6 +1368,15 @@ destroynotify(struct wl_listener *listener, void *data)
|
||||||
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)
|
+ // btrtile remove clients for each monitor
|
||||||
+ remove_client(selmon, c);
|
+ Monitor *mon;
|
||||||
|
+ wl_list_for_each(mon, &mons, link) {
|
||||||
|
+ 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 +1907,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,7 +772,7 @@ 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 +1962,56 @@ 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));
|
||||||
|
|
||||||
@@ -831,7 +836,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 +2045,41 @@ moveresize(const Arg *arg)
|
||||||
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
|
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -889,6 +894,21 @@ index 8101ffa..e4e7074 100644
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2833,6 +2932,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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user