btrtile: Refactor client management and insertion logic

- **Mouse-based Client Selection:**
  - If no visible client is found under the cursor, fallback to selecting the closest client.

- **Client Insertion:**
  - Handle insertion of multiple new clients.
  - When moving multiple clients to another tag, ensure the splitting logic is consistently applied.
This commit is contained in:
julmajustus 2025-02-08 06:27:25 +02:00
parent 919741ee19
commit 32a21e2261
3 changed files with 213 additions and 146 deletions

View File

@ -99,8 +99,9 @@ If mouse resizing feels sluggish, you can try compiling dwl with more aggressive
---
### Download
- [git branch](https://codeberg.org/julmajustus/dwl/src/branch/btrtile-dev)
- [0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/btrtile/btrtile-v0.7.patch)
- [0.7 WITH gapps](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/btrtile/btrtile-v0.7-gapps.patch)
- [0.7 WITH gaps](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/btrtile/btrtile-v0.7-gaps.patch)
### Authors
- [julmajustus](https://codeberg.org/julmajustus)

View File

@ -1,21 +1,21 @@
From 6d60b3cd6eb4d08f8566e80141071bd33d231c59 Mon Sep 17 00:00:00 2001
From d56f0b61d6b611a977c065d0834f76b42c0aa038 Mon Sep 17 00:00:00 2001
From: julmajustus <julmajustus@tutanota.com>
Date: Wed, 1 Jan 2025 19:08:45 +0200
Subject: [PATCH] btrtile-gapps init
Date: Sat, 8 Feb 2025 05:41:12 +0200
Subject: [PATCH] btrtile-gaps init
---
btrtile.c | 679 +++++++++++++++++++++++++++++++++++++++++++++++++++
btrtile.c | 716 +++++++++++++++++++++++++++++++++++++++++++++++++++
config.def.h | 12 +
dwl.c | 177 ++++++++++++--
3 files changed, 841 insertions(+), 27 deletions(-)
dwl.c | 173 +++++++++++--
3 files changed, 874 insertions(+), 27 deletions(-)
create mode 100644 btrtile.c
diff --git a/btrtile.c b/btrtile.c
new file mode 100644
index 0000000..dea8a8c
index 0000000..3ff3e20
--- /dev/null
+++ b/btrtile.c
@@ -0,0 +1,679 @@
@@ -0,0 +1,716 @@
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
@ -24,7 +24,7 @@ index 0000000..dea8a8c
+/* By: jmakkone <jmakkone@student.hive.fi> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/12/15 00:26:07 by jmakkone #+# #+# */
+/* Updated: 2025/01/01 17:26:52 by jmakkone ### ########.fr */
+/* Updated: 2025/02/08 04:52:00 by jmakkone ### ########.fr */
+/* */
+/* ************************************************************************** */
+
@ -77,6 +77,7 @@ index 0000000..dea8a8c
+static void setratio_h(const Arg *arg);
+static void setratio_v(const Arg *arg);
+static void swapclients(const Arg *arg);
+static Client *xytoclient(double x, double y, uint32_t tag);
+
+static int resizing_from_mouse = 0;
+static double resize_last_update_x, resize_last_update_y;
@ -166,16 +167,8 @@ index 0000000..dea8a8c
+ root_ptr = &m->tree_layout->root[curtag];
+ tiled_clients = &m->tree_layout->tiled_clients[curtag];
+ wl_list_for_each_reverse(c, &clients, link) {
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) {
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
+ n++;
+ if (!focused_client &&
+ cursor->x >= c->old_geom.x
+ && cursor->x < c->old_geom.x + c->old_geom.width
+ && cursor->y >= c->old_geom.y
+ && cursor->y < c->old_geom.y + c->old_geom.height) {
+ focused_client = c;
+ }
+ }
+ }
+
+ /* If no visible clients, clear the node tree and tiled_clients list. */
@ -186,6 +179,7 @@ index 0000000..dea8a8c
+ return;
+ }
+
+ focused_client = xytoclient(cursor->x, cursor->y, curtag);
+ /* If no focused client found, pick the first visible */
+ if (!focused_client) {
+ wl_list_for_each_reverse(c, &clients, link) {
@ -217,6 +211,11 @@ index 0000000..dea8a8c
+ }
+ if (!found) {
+ insert_client(m, focused_client, c, root_ptr, tiled_clients);
+ /* Recursion failsafe to handle inserted clients individually
+ * if a batch of new clients is added. */
+ apply_layout(m, *root_ptr, full_area, 1);
+ btrtile(m);
+ return;
+ }
+ wl_list_insert(&current_clients, &c->link_temp);
+ }
@ -226,7 +225,7 @@ index 0000000..dea8a8c
+ * clients that no longer exist on the current tag.
+ * This handles cases where user moves clients to other tags.
+ * When client is closed or killed we manage the list and tree clean up in
+ * destroynotify */
+ * destroynotify. */
+ wl_list_for_each_safe(cc, tmp, tiled_clients, link_tiled) {
+ found = 0;
+ wl_list_for_each(cur, &current_clients, link_temp) {
@ -235,9 +234,8 @@ index 0000000..dea8a8c
+ break;
+ }
+ }
+ if (!found) {
+ if (!found)
+ remove_client(m, cc, root_ptr, tiled_clients);
+ }
+ }
+
+ /* Rebuild the updated client list */
@ -272,6 +270,7 @@ index 0000000..dea8a8c
+ if (!node)
+ return NULL;
+ node->is_client_node = 0;
+ node->split_ratio = 0.5f;
+ node->split_vertically = split_vertically;
+ node->left = left;
+ node->right = right;
@ -477,52 +476,53 @@ index 0000000..dea8a8c
+ unsigned int wider;
+
+ /* If there is no root node, inserted client must be the first one.
+ * If there's no focused client or client node cannot be found for the
+ * focused client we treat them as root node.*/
+ * If there's no focused client we treat the inserted client as a new root node.*/
+ if (!*root) {
+ *root = create_client_node(new_client);
+ add_client_to_tiled_list(new_client, tiled_clients);
+ } else if (!focused || !(client_node = find_client_node(*root, focused))) {
+ return;
+ }
+ if (!focused || !(client_node = find_client_node(*root, focused))) {
+ old_root = *root;
+ *root = create_split_node(1, old_root, create_client_node(new_client));
+ add_client_to_tiled_list(new_client, tiled_clients);
+ } else {
+ /* We check the cursor location on splittable area and choose
+ return;
+ }
+ /* We check the cursor location on splittable area and choose
+ * the new client's position. On horizontal splits left node represent
+ * the upper node and vice versa.*/
+ mid_x = focused->old_geom.x + focused->old_geom.width / 2;
+ mid_y = focused->old_geom.y + focused->old_geom.height / 2;
+ old_client_node = create_client_node(client_node->client);
+ new_client_node = create_client_node(new_client);
+ mid_x = focused->old_geom.x + focused->old_geom.width / 2;
+ mid_y = focused->old_geom.y + focused->old_geom.height / 2;
+ old_client_node = create_client_node(client_node->client);
+ new_client_node = create_client_node(new_client);
+
+ wider = focused->old_geom.width >= focused->old_geom.height;
+ if (wider) {
+ /* Vertical split */
+ client_node->split_vertically = 1;
+ if (cursor->x < mid_x) {
+ client_node->left = new_client_node;
+ client_node->right = old_client_node;
+ } else {
+ client_node->left = old_client_node;
+ client_node->right = new_client_node;
+ }
+ wider = focused->old_geom.width >= focused->old_geom.height;
+ if (wider) {
+ /* Vertical split */
+ client_node->split_vertically = 1;
+ if (cursor->x <= mid_x) {
+ client_node->left = new_client_node;
+ client_node->right = old_client_node;
+ } else {
+ /* Horizontal split */
+ client_node->split_vertically = 0;
+ if (cursor->y < mid_y) {
+ client_node->left = new_client_node;
+ client_node->right = old_client_node;
+ } else {
+ client_node->left = old_client_node;
+ client_node->right = new_client_node;
+ }
+ client_node->left = old_client_node;
+ client_node->right = new_client_node;
+ }
+ } else {
+ /* Horizontal split */
+ client_node->split_vertically = 0;
+ if (cursor->y <= mid_y) {
+ client_node->left = new_client_node;
+ client_node->right = old_client_node;
+ } else {
+ client_node->left = old_client_node;
+ client_node->right = new_client_node;
+ }
+ /* The old client node becomes the splitnode for the old and new client
+ * nodes.*/
+ client_node->is_client_node = 0;
+ client_node->client = NULL;
+ add_client_to_tiled_list(new_client, tiled_clients);
+ }
+ /* The old client node becomes the splitnode for the old and new client
+ * nodes.*/
+ client_node->is_client_node = 0;
+ client_node->client = NULL;
+ add_client_to_tiled_list(new_client, tiled_clients);
+}
+
+LayoutNode *
@ -675,9 +675,8 @@ index 0000000..dea8a8c
+ * node if no suitable horizontal split node is found, default to vertical */
+ if (dir == DIR_UP || dir == DIR_DOWN) {
+ split_node = find_suitable_split_node(client_node, 0);
+ if (!split_node) {
+ if (!split_node)
+ return;
+ }
+ } else {
+ split_node = selmon->tree_layout->root[curtag];
+ }
@ -695,6 +694,44 @@ index 0000000..dea8a8c
+ arrange(selmon);
+ }
+}
+
+Client *
+xytoclient(double x, double y, uint32_t tag) {
+ Client *c, *closest = NULL;
+ double dist, mindist = INT_MAX, dx, dy;
+
+ wl_list_for_each_reverse(c, &selmon->tree_layout->tiled_clients[tag], link_tiled) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen &&
+ x >= c->geom.x && x <= (c->geom.x + c->geom.width) &&
+ y >= c->geom.y && y <= (c->geom.y + c->geom.height)){
+ return c;
+ }
+ }
+
+ /* If no client was found at cursor position fallback to closest. */
+ wl_list_for_each_reverse(c, &selmon->tree_layout->tiled_clients[tag], link_tiled) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen) {
+ dx = 0, dy = 0;
+
+ if (x < c->geom.x)
+ dx = c->geom.x - x;
+ else if (x > (c->geom.x + c->geom.width))
+ dx = x - (c->geom.x + c->geom.width);
+
+ if (y < c->geom.y)
+ dy = c->geom.y - y;
+ else if (y > (c->geom.y + c->geom.height))
+ dy = y - (c->geom.y + c->geom.height);
+
+ dist = sqrt(dx * dx + dy * dy);
+ if (dist < mindist) {
+ mindist = dist;
+ closest = c;
+ }
+ }
+ }
+ return closest;
+}
diff --git a/config.def.h b/config.def.h
index 22d2171..92f3ad6 100644
--- a/config.def.h
@ -734,7 +771,7 @@ index 22d2171..92f3ad6 100644
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 def2562..604a9a4 100644
index a2711f6..7682366 100644
--- a/dwl.c
+++ b/dwl.c
@@ -1,6 +1,7 @@
@ -800,18 +837,16 @@ index def2562..604a9a4 100644
/* function implementations */
void
@@ -600,10 +611,17 @@ buttonpress(struct wl_listener *listener, void *data)
@@ -600,10 +611,15 @@ buttonpress(struct wl_listener *listener, void *data)
{
struct wlr_pointer_button_event *event = data;
struct wlr_keyboard *keyboard;
- uint32_t mods;
- Client *c;
+ struct wl_list *tiled_clients;
+ struct wlr_surface *surface;
+ double sx, sy;
+ LayoutNode **root, *old_root;
+ uint32_t mods, curtag, active_tags = selmon->tagset[selmon->seltags];
+ Client *c, *target;
+ Client *c, *target = NULL;
const Button *b;
+ curtag = get_current_tag(selmon);
@ -820,7 +855,7 @@ index def2562..604a9a4 100644
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
switch (event->state) {
@@ -632,15 +650,49 @@ buttonpress(struct wl_listener *listener, void *data)
@@ -632,15 +648,47 @@ buttonpress(struct wl_listener *listener, void *data)
/* If you released any buttons, we exit interactive move/resize mode. */
/* TODO should reset to the pointer focus's current setcursor */
if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
@ -830,9 +865,7 @@ index def2562..604a9a4 100644
+ if (active_tags && (active_tags & (active_tags - 1)))
+ break;
+ if (cursor_mode == CurMove && c->isfloating) {
+ target = NULL;
+ surface = NULL;
+ xytonode(cursor->x, cursor->y, &surface, &target, NULL, &sx, &sy);
+ target = xytoclient(cursor->x, cursor->y, curtag);
+
+ if (target && !target->isfloating && !target->isfullscreen) {
+ insert_client(selmon, target, c, root, tiled_clients);
@ -872,7 +905,7 @@ index def2562..604a9a4 100644
break;
}
/* If the event wasn't handled by the compositor, notify the client with
@@ -720,6 +772,9 @@ cleanupmon(struct wl_listener *listener, void *data)
@@ -720,6 +768,9 @@ cleanupmon(struct wl_listener *listener, void *data)
wlr_output_layout_remove(output_layout, m->wlr_output);
wlr_scene_output_destroy(m->scene_output);
@ -882,7 +915,7 @@ index def2562..604a9a4 100644
closemon(m);
wlr_scene_node_destroy(&m->fullscreen_bg->node);
free(m);
@@ -1026,6 +1081,7 @@ createmon(struct wl_listener *listener, void *data)
@@ -1024,6 +1075,7 @@ createmon(struct wl_listener *listener, void *data)
wl_list_insert(&mons, &m->link);
printstatus();
@ -890,7 +923,7 @@ index def2562..604a9a4 100644
/* The xdg-protocol specifies:
*
@@ -1265,6 +1321,15 @@ destroynotify(struct wl_listener *listener, void *data)
@@ -1263,6 +1315,15 @@ destroynotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->destroy.link);
wl_list_remove(&c->set_title.link);
wl_list_remove(&c->fullscreen.link);
@ -906,7 +939,7 @@ index def2562..604a9a4 100644
#ifdef XWAYLAND
if (c->type != XDGShell) {
wl_list_remove(&c->activate.link);
@@ -1811,7 +1876,8 @@ void
@@ -1809,7 +1870,8 @@ void
motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
double dx_unaccel, double dy_unaccel)
{
@ -916,7 +949,7 @@ index def2562..604a9a4 100644
Client *c = NULL, *w = NULL;
LayerSurface *l = NULL;
struct wlr_surface *surface = NULL;
@@ -1865,18 +1931,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
@@ -1863,18 +1925,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
/* Update drag icon's position */
wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
@ -980,7 +1013,7 @@ index def2562..604a9a4 100644
/* 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
* off of a client or over its border. */
@@ -1910,22 +2014,41 @@ moveresize(const Arg *arg)
@@ -1908,22 +2008,41 @@ moveresize(const Arg *arg)
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
return;
@ -1037,5 +1070,5 @@ index def2562..604a9a4 100644
}
--
2.45.2
2.45.3

View File

@ -1,21 +1,21 @@
From 2b7e152bd39432cd82738a29a8815cf9264f02d8 Mon Sep 17 00:00:00 2001
From e73fdffcc2bd4af301f4cebc9f665f0675190b85 Mon Sep 17 00:00:00 2001
From: julmajustus <julmajustus@tutanota.com>
Date: Wed, 1 Jan 2025 19:06:03 +0200
Date: Sat, 8 Feb 2025 04:54:14 +0200
Subject: [PATCH] btrtile init
---
btrtile.c | 659 +++++++++++++++++++++++++++++++++++++++++++++++++++
btrtile.c | 696 +++++++++++++++++++++++++++++++++++++++++++++++++++
config.def.h | 12 +
dwl.c | 177 +++++++++++---
3 files changed, 821 insertions(+), 27 deletions(-)
dwl.c | 173 +++++++++++--
3 files changed, 854 insertions(+), 27 deletions(-)
create mode 100644 btrtile.c
diff --git a/btrtile.c b/btrtile.c
new file mode 100644
index 0000000..7fb0716
index 0000000..e2c697d
--- /dev/null
+++ b/btrtile.c
@@ -0,0 +1,659 @@
@@ -0,0 +1,696 @@
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
@ -24,7 +24,7 @@ index 0000000..7fb0716
+/* By: jmakkone <jmakkone@student.hive.fi> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/12/15 00:26:07 by jmakkone #+# #+# */
+/* Updated: 2025/01/01 18:40:14 by jmakkone ### ########.fr */
+/* Updated: 2025/02/08 04:45:46 by jmakkone ### ########.fr */
+/* */
+/* ************************************************************************** */
+
@ -77,6 +77,7 @@ index 0000000..7fb0716
+static void setratio_h(const Arg *arg);
+static void setratio_v(const Arg *arg);
+static void swapclients(const Arg *arg);
+static Client *xytoclient(double x, double y, uint32_t tag);
+
+static int resizing_from_mouse = 0;
+static double resize_last_update_x, resize_last_update_y;
@ -146,16 +147,8 @@ index 0000000..7fb0716
+ root_ptr = &m->tree_layout->root[curtag];
+ tiled_clients = &m->tree_layout->tiled_clients[curtag];
+ wl_list_for_each_reverse(c, &clients, link) {
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) {
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
+ n++;
+ if (!focused_client &&
+ cursor->x >= c->old_geom.x
+ && cursor->x < c->old_geom.x + c->old_geom.width
+ && cursor->y >= c->old_geom.y
+ && cursor->y < c->old_geom.y + c->old_geom.height) {
+ focused_client = c;
+ }
+ }
+ }
+
+ /* If no visible clients, clear the node tree and tiled_clients list. */
@ -166,6 +159,7 @@ index 0000000..7fb0716
+ return;
+ }
+
+ focused_client = xytoclient(cursor->x, cursor->y, curtag);
+ /* If no focused client found, pick the first visible */
+ if (!focused_client) {
+ wl_list_for_each_reverse(c, &clients, link) {
@ -197,6 +191,11 @@ index 0000000..7fb0716
+ }
+ if (!found) {
+ insert_client(m, focused_client, c, root_ptr, tiled_clients);
+ /* Recursion failsafe to handle inserted clients individually
+ * if a batch of new clients is added. */
+ apply_layout(m, *root_ptr, full_area, 1);
+ btrtile(m);
+ return;
+ }
+ wl_list_insert(&current_clients, &c->link_temp);
+ }
@ -206,7 +205,7 @@ index 0000000..7fb0716
+ * clients that no longer exist on the current tag.
+ * This handles cases where user moves clients to other tags.
+ * When client is closed or killed we manage the list and tree clean up in
+ * destroynotify */
+ * destroynotify. */
+ wl_list_for_each_safe(cc, tmp, tiled_clients, link_tiled) {
+ found = 0;
+ wl_list_for_each(cur, &current_clients, link_temp) {
@ -215,9 +214,8 @@ index 0000000..7fb0716
+ break;
+ }
+ }
+ if (!found) {
+ if (!found)
+ remove_client(m, cc, root_ptr, tiled_clients);
+ }
+ }
+
+ /* Rebuild the updated client list */
@ -252,6 +250,7 @@ index 0000000..7fb0716
+ if (!node)
+ return NULL;
+ node->is_client_node = 0;
+ node->split_ratio = 0.5f;
+ node->split_vertically = split_vertically;
+ node->left = left;
+ node->right = right;
@ -457,52 +456,53 @@ index 0000000..7fb0716
+ unsigned int wider;
+
+ /* If there is no root node, inserted client must be the first one.
+ * If there's no focused client or client node cannot be found for the
+ * focused client we treat them as root node.*/
+ * If there's no focused client we treat the inserted client as a new root node.*/
+ if (!*root) {
+ *root = create_client_node(new_client);
+ add_client_to_tiled_list(new_client, tiled_clients);
+ } else if (!focused || !(client_node = find_client_node(*root, focused))) {
+ return;
+ }
+ if (!focused || !(client_node = find_client_node(*root, focused))) {
+ old_root = *root;
+ *root = create_split_node(1, old_root, create_client_node(new_client));
+ add_client_to_tiled_list(new_client, tiled_clients);
+ } else {
+ /* We check the cursor location on splittable area and choose
+ return;
+ }
+ /* We check the cursor location on splittable area and choose
+ * the new client's position. On horizontal splits left node represent
+ * the upper node and vice versa.*/
+ mid_x = focused->old_geom.x + focused->old_geom.width / 2;
+ mid_y = focused->old_geom.y + focused->old_geom.height / 2;
+ old_client_node = create_client_node(client_node->client);
+ new_client_node = create_client_node(new_client);
+ mid_x = focused->old_geom.x + focused->old_geom.width / 2;
+ mid_y = focused->old_geom.y + focused->old_geom.height / 2;
+ old_client_node = create_client_node(client_node->client);
+ new_client_node = create_client_node(new_client);
+
+ wider = focused->old_geom.width >= focused->old_geom.height;
+ if (wider) {
+ /* Vertical split */
+ client_node->split_vertically = 1;
+ if (cursor->x < mid_x) {
+ client_node->left = new_client_node;
+ client_node->right = old_client_node;
+ } else {
+ client_node->left = old_client_node;
+ client_node->right = new_client_node;
+ }
+ wider = focused->old_geom.width >= focused->old_geom.height;
+ if (wider) {
+ /* Vertical split */
+ client_node->split_vertically = 1;
+ if (cursor->x <= mid_x) {
+ client_node->left = new_client_node;
+ client_node->right = old_client_node;
+ } else {
+ /* Horizontal split */
+ client_node->split_vertically = 0;
+ if (cursor->y < mid_y) {
+ client_node->left = new_client_node;
+ client_node->right = old_client_node;
+ } else {
+ client_node->left = old_client_node;
+ client_node->right = new_client_node;
+ }
+ client_node->left = old_client_node;
+ client_node->right = new_client_node;
+ }
+ } else {
+ /* Horizontal split */
+ client_node->split_vertically = 0;
+ if (cursor->y <= mid_y) {
+ client_node->left = new_client_node;
+ client_node->right = old_client_node;
+ } else {
+ client_node->left = old_client_node;
+ client_node->right = new_client_node;
+ }
+ /* The old client node becomes the splitnode for the old and new client
+ * nodes.*/
+ client_node->is_client_node = 0;
+ client_node->client = NULL;
+ add_client_to_tiled_list(new_client, tiled_clients);
+ }
+ /* The old client node becomes the splitnode for the old and new client
+ * nodes.*/
+ client_node->is_client_node = 0;
+ client_node->client = NULL;
+ add_client_to_tiled_list(new_client, tiled_clients);
+}
+
+LayoutNode *
@ -655,9 +655,8 @@ index 0000000..7fb0716
+ * node if no suitable horizontal split node is found, default to vertical */
+ if (dir == DIR_UP || dir == DIR_DOWN) {
+ split_node = find_suitable_split_node(client_node, 0);
+ if (!split_node) {
+ if (!split_node)
+ return;
+ }
+ } else {
+ split_node = selmon->tree_layout->root[curtag];
+ }
@ -675,6 +674,44 @@ index 0000000..7fb0716
+ arrange(selmon);
+ }
+}
+
+Client *
+xytoclient(double x, double y, uint32_t tag) {
+ Client *c, *closest = NULL;
+ double dist, mindist = INT_MAX, dx, dy;
+
+ wl_list_for_each_reverse(c, &selmon->tree_layout->tiled_clients[tag], link_tiled) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen &&
+ x >= c->geom.x && x <= (c->geom.x + c->geom.width) &&
+ y >= c->geom.y && y <= (c->geom.y + c->geom.height)){
+ return c;
+ }
+ }
+
+ /* If no client was found at cursor position fallback to closest. */
+ wl_list_for_each_reverse(c, &selmon->tree_layout->tiled_clients[tag], link_tiled) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen) {
+ dx = 0, dy = 0;
+
+ if (x < c->geom.x)
+ dx = c->geom.x - x;
+ else if (x > (c->geom.x + c->geom.width))
+ dx = x - (c->geom.x + c->geom.width);
+
+ if (y < c->geom.y)
+ dy = c->geom.y - y;
+ else if (y > (c->geom.y + c->geom.height))
+ dy = y - (c->geom.y + c->geom.height);
+
+ dist = sqrt(dx * dx + dy * dy);
+ if (dist < mindist) {
+ mindist = dist;
+ closest = c;
+ }
+ }
+ }
+ return closest;
+}
diff --git a/config.def.h b/config.def.h
index 22d2171..92f3ad6 100644
--- a/config.def.h
@ -714,7 +751,7 @@ index 22d2171..92f3ad6 100644
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 def2562..604a9a4 100644
index a2711f6..7682366 100644
--- a/dwl.c
+++ b/dwl.c
@@ -1,6 +1,7 @@
@ -780,18 +817,16 @@ index def2562..604a9a4 100644
/* function implementations */
void
@@ -600,10 +611,17 @@ buttonpress(struct wl_listener *listener, void *data)
@@ -600,10 +611,15 @@ buttonpress(struct wl_listener *listener, void *data)
{
struct wlr_pointer_button_event *event = data;
struct wlr_keyboard *keyboard;
- uint32_t mods;
- Client *c;
+ struct wl_list *tiled_clients;
+ struct wlr_surface *surface;
+ double sx, sy;
+ LayoutNode **root, *old_root;
+ uint32_t mods, curtag, active_tags = selmon->tagset[selmon->seltags];
+ Client *c, *target;
+ Client *c, *target = NULL;
const Button *b;
+ curtag = get_current_tag(selmon);
@ -800,7 +835,7 @@ index def2562..604a9a4 100644
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
switch (event->state) {
@@ -632,15 +650,49 @@ buttonpress(struct wl_listener *listener, void *data)
@@ -632,15 +648,47 @@ buttonpress(struct wl_listener *listener, void *data)
/* If you released any buttons, we exit interactive move/resize mode. */
/* TODO should reset to the pointer focus's current setcursor */
if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
@ -810,9 +845,7 @@ index def2562..604a9a4 100644
+ if (active_tags && (active_tags & (active_tags - 1)))
+ break;
+ if (cursor_mode == CurMove && c->isfloating) {
+ target = NULL;
+ surface = NULL;
+ xytonode(cursor->x, cursor->y, &surface, &target, NULL, &sx, &sy);
+ target = xytoclient(cursor->x, cursor->y, curtag);
+
+ if (target && !target->isfloating && !target->isfullscreen) {
+ insert_client(selmon, target, c, root, tiled_clients);
@ -852,7 +885,7 @@ index def2562..604a9a4 100644
break;
}
/* If the event wasn't handled by the compositor, notify the client with
@@ -720,6 +772,9 @@ cleanupmon(struct wl_listener *listener, void *data)
@@ -720,6 +768,9 @@ cleanupmon(struct wl_listener *listener, void *data)
wlr_output_layout_remove(output_layout, m->wlr_output);
wlr_scene_output_destroy(m->scene_output);
@ -862,7 +895,7 @@ index def2562..604a9a4 100644
closemon(m);
wlr_scene_node_destroy(&m->fullscreen_bg->node);
free(m);
@@ -1026,6 +1081,7 @@ createmon(struct wl_listener *listener, void *data)
@@ -1024,6 +1075,7 @@ createmon(struct wl_listener *listener, void *data)
wl_list_insert(&mons, &m->link);
printstatus();
@ -870,7 +903,7 @@ index def2562..604a9a4 100644
/* The xdg-protocol specifies:
*
@@ -1265,6 +1321,15 @@ destroynotify(struct wl_listener *listener, void *data)
@@ -1263,6 +1315,15 @@ destroynotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->destroy.link);
wl_list_remove(&c->set_title.link);
wl_list_remove(&c->fullscreen.link);
@ -886,7 +919,7 @@ index def2562..604a9a4 100644
#ifdef XWAYLAND
if (c->type != XDGShell) {
wl_list_remove(&c->activate.link);
@@ -1811,7 +1876,8 @@ void
@@ -1809,7 +1870,8 @@ void
motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
double dx_unaccel, double dy_unaccel)
{
@ -896,7 +929,7 @@ index def2562..604a9a4 100644
Client *c = NULL, *w = NULL;
LayerSurface *l = NULL;
struct wlr_surface *surface = NULL;
@@ -1865,18 +1931,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
@@ -1863,18 +1925,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
/* Update drag icon's position */
wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
@ -960,7 +993,7 @@ index def2562..604a9a4 100644
/* 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
* off of a client or over its border. */
@@ -1910,22 +2014,41 @@ moveresize(const Arg *arg)
@@ -1908,22 +2008,41 @@ moveresize(const Arg *arg)
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
return;
@ -1017,5 +1050,5 @@ index def2562..604a9a4 100644
}
--
2.45.2
2.45.3