dwl/other-patches/btrtile-v0.7.patch

903 lines
27 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From b9789420f166c20579f29ecd171a8956c681848d Mon Sep 17 00:00:00 2001
From: julmajustus <julmajustus@tutanota.com>
Date: Thu, 13 Feb 2025 23:23:40 +0200
Subject: [PATCH] btrtile with multi-tag support
---
btrtile.c | 563 +++++++++++++++++++++++++++++++++++++++++++++++++++
config.def.h | 12 ++
dwl.c | 152 +++++++++++---
3 files changed, 698 insertions(+), 29 deletions(-)
create mode 100644 btrtile.c
diff --git a/btrtile.c b/btrtile.c
new file mode 100644
index 0000000..03f4680
--- /dev/null
+++ b/btrtile.c
@@ -0,0 +1,563 @@
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* btrtile.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: jmakkone <jmakkone@student.hive.fi> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/12/15 00:26:07 by jmakkone #+# #+# */
+/* Updated: 2025/02/13 23:22:33 by jmakkone ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+typedef struct LayoutNode {
+ unsigned int is_client_node;
+ unsigned int is_split_vertically;
+ float split_ratio;
+ struct LayoutNode *left;
+ struct LayoutNode *right;
+ struct LayoutNode *split_node;
+ Client *client;
+} LayoutNode;
+
+static void apply_layout(Monitor *m, LayoutNode *node,
+ struct wlr_box area, unsigned int is_root);
+static void btrtile(Monitor *m);
+static LayoutNode *create_client_node(Client *c);
+static LayoutNode *create_split_node(unsigned int is_split_vertically,
+ LayoutNode *left, LayoutNode *right);
+static void destroy_node(LayoutNode *node);
+static void destroy_tree(Monitor *m);
+static LayoutNode *find_client_node(LayoutNode *node, Client *c);
+static LayoutNode *find_suitable_split(LayoutNode *start, unsigned int need_vert);
+static void init_tree(Monitor *m);
+static void insert_client(Monitor *m, Client *focused_client, Client *new_client);
+static LayoutNode *remove_client_node(LayoutNode *node, Client *c);
+static void remove_client(Monitor *m, Client *c);
+static void setratio_h(const Arg *arg);
+static void setratio_v(const Arg *arg);
+static void swapclients(const Arg *arg);
+static unsigned int visible_count(LayoutNode *node, Monitor *m);
+static Client *xytoclient(double x, double y);
+
+static int resizing_from_mouse = 0;
+static double resize_last_update_x, resize_last_update_y;
+static uint32_t last_resize_time = 0;
+
+void
+apply_layout(Monitor *m, LayoutNode *node,
+ struct wlr_box area, unsigned int is_root)
+{
+ Client *c;
+ float ratio;
+ unsigned int left_count, right_count, mid;
+ struct wlr_box left_area, right_area;
+
+ if (!node)
+ return;
+
+ /* If this node is a client node, check if it is visible. */
+ if (node->is_client_node) {
+ c = node->client;
+ if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ return;
+ resize(c, area, 0);
+ c->old_geom = area;
+ return;
+ }
+
+ /* For a split node, we see how many visible children are on each side: */
+ left_count = visible_count(node->left, m);
+ right_count = visible_count(node->right, m);
+
+ if (left_count == 0 && right_count == 0) {
+ return;
+ } else if (left_count > 0 && right_count == 0) {
+ apply_layout(m, node->left, area, 0);
+ return;
+ } else if (left_count == 0 && right_count > 0) {
+ apply_layout(m, node->right, area, 0);
+ return;
+ }
+
+ /* If were here, we have visible clients in both subtrees. */
+ ratio = node->split_ratio;
+ if (ratio < 0.05f)
+ ratio = 0.05f;
+ if (ratio > 0.95f)
+ ratio = 0.95f;
+
+ memset(&left_area, 0, sizeof(left_area));
+ memset(&right_area, 0, sizeof(right_area));
+
+ if (node->is_split_vertically) {
+ mid = (unsigned int)(area.width * ratio);
+ left_area.x = area.x;
+ left_area.y = area.y;
+ left_area.width = mid;
+ left_area.height = area.height;
+
+ right_area.x = area.x + mid;
+ right_area.y = area.y;
+ right_area.width = area.width - mid;
+ right_area.height = area.height;
+ } else {
+ /* horizontal split */
+ mid = (unsigned int)(area.height * ratio);
+ left_area.x = area.x;
+ left_area.y = area.y;
+ left_area.width = area.width;
+ left_area.height = mid;
+
+ right_area.x = area.x;
+ right_area.y = area.y + mid;
+ right_area.width = area.width;
+ right_area.height= area.height - mid;
+ }
+
+ apply_layout(m, node->left, left_area, 0);
+ apply_layout(m, node->right, right_area, 0);
+}
+
+void
+btrtile(Monitor *m)
+{
+ Client *c, *focused = NULL;
+ int n = 0;
+ LayoutNode *found;
+ struct wlr_box full_area;
+
+ if (!m || !m->root)
+ return;
+
+ /* Remove non tiled clients from tree. */
+ wl_list_for_each(c, &clients, link) {
+ if (c->mon == m && !c->isfloating && !c->isfullscreen) {
+ } else {
+ remove_client(m, c);
+ }
+ }
+
+ /* If no client is found under cursor, fallback to focustop(m) */
+ if (!(focused = xytoclient(cursor->x, cursor->y)))
+ focused = focustop(m);
+
+ /* Insert visible clients that are not part of the tree. */
+ wl_list_for_each(c, &clients, link) {
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen && c->mon == m) {
+ found = find_client_node(m->root, c);
+ if (!found) {
+ insert_client(m, focused, c);
+ }
+ n++;
+ }
+ }
+
+ if (n == 0)
+ return;
+
+ full_area = m->w;
+ apply_layout(m, m->root, full_area, 1);
+}
+
+LayoutNode *
+create_client_node(Client *c)
+{
+ LayoutNode *node = calloc(1, sizeof(LayoutNode));
+
+ if (!node)
+ return NULL;
+ node->is_client_node = 1;
+ node->split_ratio = 0.5f;
+ node->client = c;
+ return node;
+}
+
+LayoutNode *
+create_split_node(unsigned int is_split_vertically,
+ LayoutNode *left, LayoutNode *right)
+{
+ LayoutNode *node = calloc(1, sizeof(LayoutNode));
+
+ if (!node)
+ return NULL;
+ node->is_client_node = 0;
+ node->split_ratio = 0.5f;
+ node->is_split_vertically = is_split_vertically;
+ node->left = left;
+ node->right = right;
+ if (left)
+ left->split_node = node;
+ if (right)
+ right->split_node = node;
+ return node;
+}
+
+void
+destroy_node(LayoutNode *node)
+{
+ if (!node)
+ return;
+ if (!node->is_client_node) {
+ destroy_node(node->left);
+ destroy_node(node->right);
+ }
+ free(node);
+}
+
+void
+destroy_tree(Monitor *m)
+{
+ if (!m || !m->root)
+ return;
+ destroy_node(m->root);
+ m->root = NULL;
+}
+
+LayoutNode *
+find_client_node(LayoutNode *node, Client *c)
+{
+ LayoutNode *res;
+
+ if (!node || !c)
+ return NULL;
+ if (node->is_client_node) {
+ return (node->client == c) ? node : NULL;
+ }
+ res = find_client_node(node->left, c);
+ return res ? res : find_client_node(node->right, c);
+}
+
+LayoutNode *
+find_suitable_split(LayoutNode *start_node, unsigned int need_vertical)
+{
+ LayoutNode *n = start_node;
+ /* if we started from a client node, jump to its parent: */
+ if (n && n->is_client_node)
+ n = n->split_node;
+
+ while (n) {
+ if (!n->is_client_node && n->is_split_vertically == need_vertical &&
+ visible_count(n->left, selmon) > 0 && visible_count(n->right, selmon) > 0)
+ return n;
+ n = n->split_node;
+ }
+ return NULL;
+}
+
+void
+init_tree(Monitor *m)
+{
+ if (!m)
+ return;
+ m->root = calloc(1, sizeof(LayoutNode));
+ if (!m->root)
+ m->root = NULL;
+}
+
+void
+insert_client(Monitor *m, Client *focused_client, Client *new_client)
+{
+ Client *old_client;
+ LayoutNode **root = &m->root, *old_root,
+ *focused_node, *new_client_node, *old_client_node;
+ unsigned int wider, mid_x, mid_y;
+
+ /* If no root , new client becomes the root. */
+ if (!*root) {
+ *root = create_client_node(new_client);
+ return;
+ }
+
+ /* Find the focused_client node,
+ * if not found split the root. */
+ focused_node = focused_client ?
+ find_client_node(*root, focused_client) : NULL;
+ if (!focused_node) {
+ old_root = *root;
+ new_client_node = create_client_node(new_client);
+ *root = create_split_node(1, old_root, new_client_node);
+ return;
+ }
+
+ /* Turn focused node from a client node into a split node,
+ * and attach old_client + new_client. */
+ old_client = focused_node->client;
+ old_client_node = create_client_node(old_client);
+ new_client_node = create_client_node(new_client);
+
+ /* Decide split direction. */
+ wider = (focused_client->geom.width >= focused_client->geom.height);
+ focused_node->is_client_node = 0;
+ focused_node->client = NULL;
+ focused_node->is_split_vertically = (wider ? 1 : 0);
+
+ /* Pick new_client side depending on the cursor position. */
+ mid_x = focused_client->geom.x + focused_client->geom.width / 2;
+ mid_y = focused_client->geom.y + focused_client->geom.height / 2;
+
+ if (wider) {
+ /* vertical split => left vs right */
+ if (cursor->x <= mid_x) {
+ focused_node->left = new_client_node;
+ focused_node->right = old_client_node;
+ } else {
+ focused_node->left = old_client_node;
+ focused_node->right = new_client_node;
+ }
+ } else {
+ /* horizontal split => top vs bottom */
+ if (cursor->y <= mid_y) {
+ focused_node->left = new_client_node;
+ focused_node->right = old_client_node;
+ } else {
+ focused_node->left = old_client_node;
+ focused_node->right = new_client_node;
+ }
+ }
+ old_client_node->split_node = focused_node;
+ new_client_node->split_node = focused_node;
+ focused_node->split_ratio = 0.5f;
+}
+
+LayoutNode *
+remove_client_node(LayoutNode *node, Client *c)
+{
+ LayoutNode *tmp;
+ if (!node)
+ return NULL;
+ if (node->is_client_node) {
+ /* If this client_node is the client we're removing,
+ * return NULL to remove it */
+ if (node->client == c) {
+ free(node);
+ return NULL;
+ }
+ return node;
+ }
+
+ node->left = remove_client_node(node->left, c);
+ node->right = remove_client_node(node->right, c);
+
+ /* If one of the client node is NULL after removal and the other is not,
+ * we "lift" the other client node up to replace this split node. */
+ if (!node->left && node->right) {
+ tmp = node->right;
+
+ /* Save pointer to split node */
+ if (tmp)
+ tmp->split_node = node->split_node;
+
+ free(node);
+ return tmp;
+ }
+
+ if (!node->right && node->left) {
+ tmp = node->left;
+
+ /* Save pointer to split node */
+ if (tmp)
+ tmp->split_node = node->split_node;
+
+ free(node);
+ return tmp;
+ }
+
+ /* If both children exist or both are NULL (empty tree),
+ * return node as is. */
+ return node;
+}
+
+void
+remove_client(Monitor *m, Client *c)
+{
+ if (!m->root || !c)
+ return;
+ m->root = remove_client_node(m->root, c);
+}
+
+void
+setratio_h(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ 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, 1);
+ 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
+setratio_v(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ 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) {
+ Client *c, *tmp, *target = NULL, *sel = focustop(selmon);
+ LayoutNode *sel_node, *target_node;
+ int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y,
+ cand_center_x, cand_center_y;
+
+ if (!sel || sel->isfullscreen ||
+ !selmon->root || !selmon->lt[selmon->sellt]->arrange)
+ return;
+
+
+ /* Get the center coordinates of the selected client */
+ sel_center_x = sel->geom.x + sel->geom.width / 2;
+ sel_center_y = sel->geom.y + sel->geom.height / 2;
+
+ wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel)
+ continue;
+
+ /* Get the center of candidate client */
+ cand_center_x = c->geom.x + c->geom.width / 2;
+ cand_center_y = c->geom.y + c->geom.height / 2;
+
+ /* Check that the candidate lies in the requested direction. */
+ switch (arg->ui) {
+ case 0:
+ if (cand_center_x >= sel_center_x)
+ continue;
+ break;
+ case 1:
+ if (cand_center_x <= sel_center_x)
+ continue;
+ break;
+ case 2:
+ if (cand_center_y >= sel_center_y)
+ continue;
+ break;
+ case 3:
+ if (cand_center_y <= sel_center_y)
+ continue;
+ break;
+ default:
+ continue;
+ }
+
+ /* Get distance between the centers */
+ dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y);
+ if (dist < closest_dist) {
+ closest_dist = dist;
+ target = c;
+ }
+ }
+
+ /* If target is found, swap the two clients positions in the layout tree */
+ if (target) {
+ sel_node = find_client_node(selmon->root, sel);
+ target_node = find_client_node(selmon->root, target);
+ if (sel_node && target_node) {
+ tmp = sel_node->client;
+ sel_node->client = target_node->client;
+ target_node->client = tmp;
+ arrange(selmon);
+ }
+ }
+}
+
+unsigned int
+visible_count(LayoutNode *node, Monitor *m)
+{
+ Client *c;
+
+ if (!node)
+ return 0;
+ /* Check if this client is visible. */
+ if (node->is_client_node) {
+ c = node->client;
+ if (c && VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
+ return 1;
+ return 0;
+ }
+ /* Else its a split node. */
+ return visible_count(node->left, m) + visible_count(node->right, m);
+}
+
+Client *
+xytoclient(double x, double y) {
+ Client *c, *closest = NULL;
+ double dist, mindist = INT_MAX, dx, dy;
+
+ wl_list_for_each_reverse(c, &clients, link) {
+ 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, &clients, link) {
+ 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
+++ b/config.def.h
@@ -13,7 +13,10 @@ static const float focuscolor[] = COLOR(0x005577ff);
static const float urgentcolor[] = COLOR(0xff0000ff);
/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */
static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */
+static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */
+static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */
+enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN };
/* tagging - TAGCOUNT must be no greater than 31 */
#define TAGCOUNT (9)
@@ -31,6 +34,7 @@ static const Rule rules[] = {
/* layout(s) */
static const Layout layouts[] = {
/* symbol arrange function */
+ { "|w|", btrtile },
{ "[]=", tile },
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
@@ -148,6 +152,14 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Up, swapclients, {.i = DIR_UP} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, swapclients, {.i = DIR_DOWN} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, swapclients, {.i = DIR_RIGHT} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, swapclients, {.i = DIR_LEFT} },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Right, setratio_h, {.f = +0.025f} },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Left, setratio_h, {.f = -0.025f} },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Up, setratio_v, {.f = -0.025f} },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Down, setratio_v, {.f = +0.025f} },
TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
diff --git a/dwl.c b/dwl.c
index a2711f6..e49a061 100644
--- a/dwl.c
+++ b/dwl.c
@@ -1,6 +1,7 @@
/*
* See LICENSE file for copyright and license details.
*/
+#include <limits.h>
#include <getopt.h>
#include <libinput.h>
#include <linux/input-event-codes.h>
@@ -103,6 +104,7 @@ typedef struct {
const Arg arg;
} Button;
+typedef struct LayoutNode LayoutNode;
typedef struct Monitor Monitor;
typedef struct {
/* Must keep these three elements in this order */
@@ -139,8 +141,9 @@ typedef struct {
#endif
unsigned int bw;
uint32_t tags;
- int isfloating, isurgent, isfullscreen;
+ int isfloating, isurgent, isfullscreen, was_tiled;
uint32_t resize; /* configure serial of a pending resize */
+ struct wlr_box old_geom;
} Client;
typedef struct {
@@ -208,6 +211,7 @@ struct Monitor {
int nmaster;
char ltsymbol[16];
int asleep;
+ LayoutNode *root;
};
typedef struct {
@@ -250,6 +254,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
struct wlr_box *usable_area, int exclusive);
static void arrangelayers(Monitor *m);
static void axisnotify(struct wl_listener *listener, void *data);
+static void btrtile(Monitor *m);
static void buttonpress(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg);
static void checkidleinhibitor(struct wlr_surface *exclude);
@@ -333,6 +338,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags);
static void setpsel(struct wl_listener *listener, void *data);
static void setsel(struct wl_listener *listener, void *data);
static void setup(void);
+static void setratio_h(const Arg *arg);
+static void setratio_v(const Arg *arg);
+static void swapclients(const Arg *arg);
static void spawn(const Arg *arg);
static void startdrag(struct wl_listener *listener, void *data);
static void tag(const Arg *arg);
@@ -431,6 +439,7 @@ static xcb_atom_t netatom[NetLast];
/* attempt to encapsulate suck into one file */
#include "client.h"
+#include "btrtile.c"
/* function implementations */
void
@@ -601,7 +610,7 @@ buttonpress(struct wl_listener *listener, void *data)
struct wlr_pointer_button_event *event = data;
struct wlr_keyboard *keyboard;
uint32_t mods;
- Client *c;
+ Client *c, *target = NULL;
const Button *b;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
@@ -622,7 +631,7 @@ buttonpress(struct wl_listener *listener, void *data)
mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
for (b = buttons; b < END(buttons); b++) {
if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
- event->button == b->button && b->func) {
+ event->button == b->button && b->func) {
b->func(&b->arg);
return;
}
@@ -632,15 +641,36 @@ 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) {
+ c = grabc;
+ if (c && c->was_tiled && !strcmp(selmon->ltsymbol, "|w|")) {
+ if (cursor_mode == CurMove && c->isfloating) {
+ target = xytoclient(cursor->x, cursor->y);
+
+ if (target && !target->isfloating && !target->isfullscreen)
+ insert_client(selmon, target, c);
+ else
+ selmon->root = create_client_node(c);
+
+ setfloating(c, 0);
+ arrange(selmon);
+
+ } else if (cursor_mode == CurResize && !c->isfloating) {
+ resizing_from_mouse = 0;
+ }
+ } else {
+ if (cursor_mode == CurResize && resizing_from_mouse)
+ resizing_from_mouse = 0;
+ }
+ /* Default behaviour */
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
cursor_mode = CurNormal;
/* Drop the window off on its new monitor */
selmon = xytomon(cursor->x, cursor->y);
setmon(grabc, selmon, 0);
+ grabc = NULL;
return;
- } else {
- cursor_mode = CurNormal;
}
+ cursor_mode = CurNormal;
break;
}
/* If the event wasn't handled by the compositor, notify the client with
@@ -720,6 +750,7 @@ cleanupmon(struct wl_listener *listener, void *data)
wlr_output_layout_remove(output_layout, m->wlr_output);
wlr_scene_output_destroy(m->scene_output);
+ destroy_tree(m);
closemon(m);
wlr_scene_node_destroy(&m->fullscreen_bg->node);
free(m);
@@ -1024,6 +1055,7 @@ createmon(struct wl_listener *listener, void *data)
wl_list_insert(&mons, &m->link);
printstatus();
+ init_tree(m);
/* The xdg-protocol specifies:
*
@@ -1263,6 +1295,10 @@ 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);
+ /* 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 */
+ if (selmon && selmon->root)
+ remove_client(selmon, c);
#ifdef XWAYLAND
if (c->type != XDGShell) {
wl_list_remove(&c->activate.link);
@@ -1809,7 +1845,8 @@ void
motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
double dx_unaccel, double dy_unaccel)
{
- double sx = 0, sy = 0, sx_confined, sy_confined;
+ int tiled = 0;
+ double sx = 0, sy = 0, sx_confined, sy_confined, dx_total, dy_total;
Client *c = NULL, *w = NULL;
LayerSurface *l = NULL;
struct wlr_surface *surface = NULL;
@@ -1863,18 +1900,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));
- /* If we are currently grabbing the mouse, handle and return */
+ /* Skip if internal call or already resizing */
+ if (time == 0 && resizing_from_mouse)
+ goto focus;
+
+ tiled = grabc && !grabc->isfloating && !grabc->isfullscreen;
if (cursor_mode == CurMove) {
/* Move the grabbed client to the new position. */
- resize(grabc, (struct wlr_box){.x = (int)round(cursor->x) - grabcx, .y = (int)round(cursor->y) - grabcy,
- .width = grabc->geom.width, .height = grabc->geom.height}, 1);
- return;
+ if (grabc && grabc->isfloating) {
+ resize(grabc, (struct wlr_box){
+ .x = (int)round(cursor->x) - grabcx,
+ .y = (int)round(cursor->y) - grabcy,
+ .width = grabc->geom.width,
+ .height = grabc->geom.height
+ }, 1);
+ return;
+ }
} else if (cursor_mode == CurResize) {
- resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
- .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1);
- return;
+ if (tiled && resizing_from_mouse) {
+ dx_total = cursor->x - resize_last_update_x;
+ dy_total = cursor->y - resize_last_update_y;
+
+ if (time - last_resize_time >= resize_interval_ms) {
+ Arg a = {0};
+ if (fabs(dx_total) > fabs(dy_total)) {
+ a.f = (float)(dx_total * resize_factor);
+ setratio_h(&a);
+ } else {
+ a.f = (float)(dy_total * resize_factor);
+ setratio_v(&a);
+ }
+ arrange(selmon);
+
+ last_resize_time = time;
+ resize_last_update_x = cursor->x;
+ resize_last_update_y = cursor->y;
+ }
+
+ } else if (grabc && grabc->isfloating) {
+ /* Floating resize as original */
+ resize(grabc, (struct wlr_box){
+ .x = grabc->geom.x,
+ .y = grabc->geom.y,
+ .width = (int)round(cursor->x) - grabc->geom.x,
+ .height = (int)round(cursor->y) - grabc->geom.y
+ }, 1);
+ return;
+ }
}
+focus:
/* 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. */
@@ -1908,22 +1983,41 @@ moveresize(const Arg *arg)
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
return;
- /* Float the window and tell motionnotify to grab it */
- setfloating(grabc, 1);
- switch (cursor_mode = arg->ui) {
- case CurMove:
- grabcx = (int)round(cursor->x) - grabc->geom.x;
- grabcy = (int)round(cursor->y) - grabc->geom.y;
- wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur");
- break;
- case CurResize:
- /* Doesn't work for X11 output - the next absolute motion event
- * returns the cursor to where it started */
- wlr_cursor_warp_closest(cursor, NULL,
- grabc->geom.x + grabc->geom.width,
- grabc->geom.y + grabc->geom.height);
- wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
- break;
+ cursor_mode = arg->ui;
+ grabc->was_tiled = (!grabc->isfloating && !grabc->isfullscreen);
+
+ if (grabc->was_tiled) {
+ switch (cursor_mode) {
+ case CurMove:
+ setfloating(grabc, 1);
+ grabcx = (int)round(cursor->x) - grabc->geom.x;
+ grabcy = (int)round(cursor->y) - grabc->geom.y;
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur");
+ break;
+ case CurResize:
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
+ resize_last_update_x = cursor->x;
+ resize_last_update_y = cursor->y;
+ resizing_from_mouse = 1;
+ break;
+ }
+ } else {
+ /* Default floating logic */
+ /* Float the window and tell motionnotify to grab it */
+ setfloating(grabc, 1);
+ switch (cursor_mode) {
+ case CurMove:
+ grabcx = (int)round(cursor->x) - grabc->geom.x;
+ grabcy = (int)round(cursor->y) - grabc->geom.y;
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur");
+ break;
+ case CurResize:
+ wlr_cursor_warp_closest(cursor, NULL,
+ grabc->geom.x + grabc->geom.width,
+ grabc->geom.y + grabc->geom.height);
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
+ break;
+ }
}
}
--
2.45.3