mirror of
https://codeberg.org/dwl/dwl-patches.git
synced 2026-09-20 14:22:59 +00:00
- Updated to current main - Added more user toggles to control the behaviour of btrtile into config.h - Simplified the btrtile gapps logic - Simplified the changes to dwl.c - Added two keybind functions to swap and toggle splits - Minor code cleanups - Added functionality to respect the client size hints
1244 lines
32 KiB
Diff
1244 lines
32 KiB
Diff
From fd672091bce3ab7db7ab89725db94df8a7bb5a6d Mon Sep 17 00:00:00 2001
|
|
From: julmajustus <julmajustus@tutanota.com>
|
|
Date: Sun, 20 Sep 2026 01:30:50 +0300
|
|
Subject: [PATCH] btrtile: refactor and few features and code simplification
|
|
|
|
- Refactor to current upstream main
|
|
- Added more user toggles to control the behaviour of btrtile into
|
|
config.h
|
|
- Simplified the btrtile gapps logic
|
|
- Simplified the changes to dwl.c
|
|
- Added two keybind functions to swap and toggle splits
|
|
- Minor code cleanups
|
|
---
|
|
Makefile | 3 +-
|
|
btrtile.h | 884 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
config.def.h | 20 +-
|
|
dwl.c | 149 +++++++--
|
|
4 files changed, 1019 insertions(+), 37 deletions(-)
|
|
create mode 100644 btrtile.h
|
|
|
|
diff --git a/Makefile b/Makefile
|
|
index 8304245..063ba0a 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -20,7 +20,8 @@ all: dwl
|
|
dwl: dwl.c client.h config.h ime.h util.h config.mk cursor-shape-v1-protocol.h \
|
|
ext-image-copy-capture-v1-protocol.h \
|
|
pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
|
|
- wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h
|
|
+ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \
|
|
+ btrtile.h
|
|
$(CC) dwl.c $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
|
|
|
|
# wayland-scanner is a tool which generates C headers and rigging for Wayland
|
|
diff --git a/btrtile.h b/btrtile.h
|
|
new file mode 100644
|
|
index 0000000..9f91be2
|
|
--- /dev/null
|
|
+++ b/btrtile.h
|
|
@@ -0,0 +1,884 @@
|
|
+/* ************************************************************************** */
|
|
+/* @@@ @@@@@@@@ */
|
|
+/* @@@ @@@@@@@@@@ */
|
|
+/* @@! @@! @@@@ */
|
|
+/* !@! !@! @!@!@ */
|
|
+/* btrtile.h @!! @!@ @! !@! */
|
|
+/* !!! !@!!! !!! */
|
|
+/* By: julmajustus <julmajustus@tutanota.com> !!: !!:! !!! */
|
|
+/* ::! :!: !:! */
|
|
+/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */
|
|
+/* Updated: 2026/09/20 01:07:46 by julmajustus : : : : : : */
|
|
+/* */
|
|
+/* ************************************************************************** */
|
|
+
|
|
+typedef struct LayoutNode {
|
|
+ unsigned int is_client_node;
|
|
+ unsigned int is_split_vertically;
|
|
+ unsigned int keep_dir;
|
|
+ float split_ratio;
|
|
+ struct wlr_box box;
|
|
+ struct LayoutNode *left; /* left or top */
|
|
+ struct LayoutNode *right; /* right or bottom */
|
|
+ struct LayoutNode *split_node; /* parent */
|
|
+ Client *client;
|
|
+} LayoutNode;
|
|
+
|
|
+static void apply_boxes(Monitor *m, LayoutNode *node);
|
|
+static void apply_layout(Monitor *m, LayoutNode *node,
|
|
+ struct wlr_box area, unsigned int is_root);
|
|
+static void btrtile(Monitor *m);
|
|
+static int clamp_split(int total, int mid, int lmin, int rmin, int g);
|
|
+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_suitable_split(Monitor *m, LayoutNode *start,
|
|
+ unsigned int need_vertical, int focused_on_left);
|
|
+static int half_gap(Monitor *m);
|
|
+static int in_tree(Monitor *m, Client *c);
|
|
+static void init_tree(Monitor *m);
|
|
+static void insert_client(Monitor *m, Client *target, Client *new_client);
|
|
+static void layout_boxes(Monitor *m, LayoutNode *node,
|
|
+ struct wlr_box area, unsigned int is_root);
|
|
+static void layout_node(Monitor *m, LayoutNode *node,
|
|
+ struct wlr_box area, unsigned int is_root);
|
|
+static void node_min_size(Monitor *m, LayoutNode *node, int *w, int *h);
|
|
+static int outer_gap(Monitor *m);
|
|
+static void remove_client(Monitor *m, Client *c);
|
|
+static void remove_leaf(Monitor *m, Client *c);
|
|
+static void resize_split(unsigned int need_vertical, double delta, int in_pixels);
|
|
+static void resize_split_px(unsigned int need_vertical, double pixels);
|
|
+static void setratio_h(const Arg *arg);
|
|
+static void setratio_v(const Arg *arg);
|
|
+static Client *split_target(Monitor *m);
|
|
+static void swapclients(const Arg *arg);
|
|
+static void swapsplit(const Arg *arg);
|
|
+static void togglesplit(const Arg *arg);
|
|
+static LayoutNode *tree_root(LayoutNode *n);
|
|
+static void unlink_client(Client *c);
|
|
+static unsigned int visible_count(LayoutNode *node, Monitor *m);
|
|
+static LayoutNode *visible_parent(Monitor *m, LayoutNode *n);
|
|
+static int wants_vertical(int width, int height);
|
|
+static Client *xytoclient(double x, double y);
|
|
+
|
|
+static double resize_last_update_x, resize_last_update_y;
|
|
+static uint32_t last_resize_time = 0;
|
|
+static int dir_changed;
|
|
+
|
|
+/* give every visible client the box of its leaf */
|
|
+void
|
|
+apply_boxes(Monitor *m, LayoutNode *node)
|
|
+{
|
|
+ Client *c;
|
|
+
|
|
+ if (!node) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (node->is_client_node) {
|
|
+ c = node->client;
|
|
+ if (c && VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) {
|
|
+ resize(c, node->box, 0);
|
|
+ }
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ apply_boxes(m, node->left);
|
|
+ apply_boxes(m, node->right);
|
|
+}
|
|
+
|
|
+void
|
|
+apply_layout(Monitor *m, LayoutNode *node,
|
|
+ struct wlr_box area, unsigned int is_root)
|
|
+{
|
|
+ layout_boxes(m, node, area, is_root);
|
|
+ apply_boxes(m, node);
|
|
+}
|
|
+
|
|
+void
|
|
+btrtile(Monitor *m)
|
|
+{
|
|
+ Client *c, *target;
|
|
+ int n = 0;
|
|
+
|
|
+ if (!m) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Remove floating and moved clients */
|
|
+ wl_list_for_each(c, &clients, link) {
|
|
+ if (c->mon != m || c->isfloating) {
|
|
+ remove_client(m, c);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ layout_boxes(m, m->root, m->w, 1);
|
|
+
|
|
+ /* Insert visible clients that are not part of the tree */
|
|
+ target = split_target(m);
|
|
+ wl_list_for_each(c, &clients, link) {
|
|
+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (c->node && !in_tree(m, c)) {
|
|
+ unlink_client(c);
|
|
+ }
|
|
+
|
|
+ if (!c->node) {
|
|
+ insert_client(m, target, c);
|
|
+ layout_boxes(m, m->root, m->w, 1);
|
|
+ target = c;
|
|
+ }
|
|
+ n++;
|
|
+ }
|
|
+
|
|
+ if (n == 0) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ apply_boxes(m, m->root);
|
|
+}
|
|
+
|
|
+/* Keep both sides of a split at least as large as min hint size */
|
|
+int
|
|
+clamp_split(int total, int mid, int lmin, int rmin, int g)
|
|
+{
|
|
+ int lo = lmin + g;
|
|
+ int hi = total - rmin - g;
|
|
+
|
|
+ if (lo <= hi) {
|
|
+ return MAX(lo, MIN(mid, hi));
|
|
+ }
|
|
+
|
|
+ if (lmin + rmin <= 0) {
|
|
+ return mid;
|
|
+ }
|
|
+
|
|
+ return (int)((double)total * lmin / (lmin + rmin));
|
|
+}
|
|
+
|
|
+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;
|
|
+ c->node = node;
|
|
+
|
|
+ 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) {
|
|
+ if (node->client) {
|
|
+ node->client->node = NULL;
|
|
+ }
|
|
+ }
|
|
+ else {
|
|
+ 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_suitable_split(Monitor *m, LayoutNode *start_node,
|
|
+ unsigned int need_vertical, int focused_on_left)
|
|
+{
|
|
+ LayoutNode *n = start_node, *child = NULL;
|
|
+
|
|
+ if (!m) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (n && n->is_client_node) {
|
|
+ child = n;
|
|
+ n = n->split_node;
|
|
+ }
|
|
+
|
|
+ while (n) {
|
|
+ if (!n->is_client_node && n->is_split_vertically == need_vertical
|
|
+ && visible_count(n->left, m) > 0
|
|
+ && 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;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+#ifdef BTRTILE_GAPS
|
|
+int
|
|
+half_gap(Monitor *m)
|
|
+{
|
|
+ return m->gaps ? (int)gappx / 2 : 0;
|
|
+}
|
|
+#else
|
|
+int
|
|
+half_gap(Monitor *m)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
+int
|
|
+in_tree(Monitor *m, Client *c)
|
|
+{
|
|
+ return m && m->root && c && c->node && tree_root(c->node) == m->root;
|
|
+}
|
|
+
|
|
+void
|
|
+init_tree(Monitor *m)
|
|
+{
|
|
+ if (m) {
|
|
+ m->root = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+void
|
|
+insert_client(Monitor *m, Client *target, Client *new_client)
|
|
+{
|
|
+ LayoutNode *leaf, *tnode, *split, *parent;
|
|
+ struct wlr_box b;
|
|
+ int vertical, new_first;
|
|
+
|
|
+ if (new_client->node) {
|
|
+ unlink_client(new_client);
|
|
+ }
|
|
+
|
|
+ if (!(leaf = create_client_node(new_client))) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* If no root, new client becomes the root. */
|
|
+ if (!m->root) {
|
|
+ m->root = leaf;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!in_tree(m, target)) {
|
|
+ vertical = wants_vertical(m->w.width, m->w.height);
|
|
+ if (!(split = create_split_node(vertical, m->root, leaf))) {
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ m->root = split;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ tnode = target->node;
|
|
+ b = tnode->box;
|
|
+ vertical = wants_vertical(b.width, b.height);
|
|
+
|
|
+ /* Pick the side of the new client */
|
|
+ if (force_split == 1) {
|
|
+ new_first = 1;
|
|
+ }
|
|
+ else if (force_split == 2) {
|
|
+ new_first = 0;
|
|
+ }
|
|
+ else if (vertical) {
|
|
+ new_first = cursor->x <= b.x + b.width / 2.0;
|
|
+ }
|
|
+ else {
|
|
+ new_first = cursor->y <= b.y + b.height / 2.0;
|
|
+ }
|
|
+
|
|
+ parent = tnode->split_node;
|
|
+ split = new_first ? create_split_node(vertical, leaf, tnode)
|
|
+ : create_split_node(vertical, tnode, leaf);
|
|
+ if (!split) {
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ split->split_node = parent;
|
|
+ if (!parent) {
|
|
+ m->root = split;
|
|
+ }
|
|
+ else if (parent->left == tnode) {
|
|
+ parent->left = split;
|
|
+ }
|
|
+ else {
|
|
+ parent->right = split;
|
|
+ }
|
|
+
|
|
+ return;
|
|
+
|
|
+fail:
|
|
+ new_client->node = NULL;
|
|
+ free(leaf);
|
|
+}
|
|
+
|
|
+/* compute the box of every node */
|
|
+void
|
|
+layout_boxes(Monitor *m, LayoutNode *node, struct wlr_box area, unsigned int is_root)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < 4; i++) {
|
|
+ dir_changed = 0;
|
|
+ layout_node(m, node, area, is_root);
|
|
+
|
|
+ if (!dir_changed) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+layout_node(Monitor *m, LayoutNode *node, struct wlr_box area, unsigned int is_root)
|
|
+{
|
|
+ unsigned int left_count, right_count;
|
|
+ int mid, lw, lh, rw, rh, vertical, og, g = half_gap(m);
|
|
+ float ratio;
|
|
+ struct wlr_box left_area, right_area;
|
|
+
|
|
+ if (!node) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (is_root) {
|
|
+ og = outer_gap(m);
|
|
+ area.x += og;
|
|
+ area.y += og;
|
|
+ area.width -= 2 * og;
|
|
+ area.height -= 2 * og;
|
|
+ }
|
|
+ node->box = area;
|
|
+
|
|
+ if (node->is_client_node) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* For a split node, see how many visible clients 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;
|
|
+ }
|
|
+
|
|
+ if (right_count == 0) {
|
|
+ layout_node(m, node->left, area, 0);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (left_count == 0) {
|
|
+ layout_node(m, node->right, area, 0);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Visible clients on both sides */
|
|
+ if (!preserve_split && !node->keep_dir) {
|
|
+ vertical = wants_vertical(area.width, area.height);
|
|
+ if ((unsigned int)vertical != node->is_split_vertically) {
|
|
+ dir_changed = 1;
|
|
+ }
|
|
+ node->is_split_vertically = vertical;
|
|
+ }
|
|
+
|
|
+ ratio = node->split_ratio;
|
|
+ if (ratio < 0.05f) {
|
|
+ ratio = 0.05f;
|
|
+ }
|
|
+
|
|
+ if (ratio > 0.95f) {
|
|
+ ratio = 0.95f;
|
|
+ }
|
|
+
|
|
+ node_min_size(m, node->left, &lw, &lh);
|
|
+ node_min_size(m, node->right, &rw, &rh);
|
|
+ left_area = right_area = area;
|
|
+
|
|
+ if (node->is_split_vertically) {
|
|
+ mid = clamp_split(area.width, (int)(area.width * ratio), lw, rw, g);
|
|
+ left_area.width = mid - g;
|
|
+ right_area.x = area.x + mid + g;
|
|
+ right_area.width = area.width - mid - g;
|
|
+ } else {
|
|
+ mid = clamp_split(area.height, (int)(area.height * ratio), lh, rh, g);
|
|
+ left_area.height = mid - g;
|
|
+ right_area.y = area.y + mid + g;
|
|
+ right_area.height = area.height - mid - g;
|
|
+ }
|
|
+
|
|
+ layout_node(m, node->left, left_area, 0);
|
|
+ layout_node(m, node->right, right_area, 0);
|
|
+}
|
|
+
|
|
+/* Smallest size a subtree can be given without violating size hints */
|
|
+void
|
|
+node_min_size(Monitor *m, LayoutNode *node, int *w, int *h)
|
|
+{
|
|
+ Client *c;
|
|
+ struct wlr_box min = {0}, max = {0};
|
|
+ int lw, lh, rw, rh, g;
|
|
+
|
|
+ *w = *h = 0;
|
|
+ if (!node) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (node->is_client_node) {
|
|
+ c = node->client;
|
|
+
|
|
+ if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ client_get_size_hints(c, &max, &min);
|
|
+ *w = MAX(min.width, 1) + 2 * (int)c->bw;
|
|
+ *h = MAX(min.height, 1) + 2 * (int)c->bw;
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ node_min_size(m, node->left, &lw, &lh);
|
|
+ node_min_size(m, node->right, &rw, &rh);
|
|
+ if (!lw) {
|
|
+ *w = rw;
|
|
+ *h = rh;
|
|
+ return;
|
|
+ }
|
|
+ if (!rw) {
|
|
+ *w = lw;
|
|
+ *h = lh;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ g = 2 * half_gap(m);
|
|
+ if (node->is_split_vertically) {
|
|
+ *w = lw + rw + g;
|
|
+ *h = MAX(lh, rh);
|
|
+ }
|
|
+ else {
|
|
+ *w = MAX(lw, rw);
|
|
+ *h = lh + rh + g;
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifdef BTRTILE_GAPS
|
|
+int
|
|
+outer_gap(Monitor *m)
|
|
+{
|
|
+ return m->gaps ? (int)gappx : 0;
|
|
+}
|
|
+
|
|
+#else
|
|
+int
|
|
+outer_gap(Monitor *m)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/* Remove c if in the tree */
|
|
+void
|
|
+remove_client(Monitor *m, Client *c)
|
|
+{
|
|
+ if (in_tree(m, c)) {
|
|
+ remove_leaf(m, c);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+remove_leaf(Monitor *m, Client *c)
|
|
+{
|
|
+ LayoutNode *n = c->node, *p, *sib, *gp;
|
|
+
|
|
+ if (!n) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ c->node = NULL;
|
|
+
|
|
+ if (!(p = n->split_node)) {
|
|
+ m->root = NULL;
|
|
+ free(n);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ sib = (p->left == n) ? p->right : p->left;
|
|
+ gp = p->split_node;
|
|
+ sib->split_node = gp;
|
|
+ if (!gp) {
|
|
+ m->root = sib;
|
|
+ }
|
|
+ else if (gp->left == p) {
|
|
+ gp->left = sib;
|
|
+ }
|
|
+ else {
|
|
+ gp->right = sib;
|
|
+ }
|
|
+
|
|
+ free(n);
|
|
+ free(p);
|
|
+}
|
|
+
|
|
+/* In pixel mode delta is a pointer movement, otherwise it is a change of the split ratio. */
|
|
+void
|
|
+resize_split(unsigned int need_vertical, double delta, int in_pixels)
|
|
+{
|
|
+ Client *sel;
|
|
+ LayoutNode *split;
|
|
+ double extent, ratio;
|
|
+
|
|
+ if (!selmon || !selmon->lt[selmon->sellt]->arrange) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ sel = focustop(selmon);
|
|
+ if (!in_tree(selmon, sel)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (in_pixels && delta == 0.0) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ split = find_suitable_split(selmon, sel->node, need_vertical, delta >= 0.0);
|
|
+ if (!split) {
|
|
+ split = find_suitable_split(selmon, sel->node, need_vertical, delta < 0.0);
|
|
+ }
|
|
+
|
|
+ if (!split) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (in_pixels) {
|
|
+ extent = need_vertical ? split->box.width : split->box.height;
|
|
+ if (extent <= 0.0) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ delta /= extent;
|
|
+ }
|
|
+
|
|
+ ratio = (delta != 0.0) ? split->split_ratio + delta : 0.5;
|
|
+ ratio = MAX(0.05, MIN(ratio, 0.95));
|
|
+ split->split_ratio = (float)ratio;
|
|
+
|
|
+ apply_layout(selmon, selmon->root, selmon->w, 1);
|
|
+}
|
|
+
|
|
+void
|
|
+resize_split_px(unsigned int need_vertical, double pixels)
|
|
+{
|
|
+ resize_split(need_vertical, pixels, 1);
|
|
+}
|
|
+
|
|
+void
|
|
+setratio_h(const Arg *arg)
|
|
+{
|
|
+ resize_split(1, arg->f, 0);
|
|
+}
|
|
+
|
|
+void
|
|
+setratio_v(const Arg *arg)
|
|
+{
|
|
+ resize_split(0, arg->f, 0);
|
|
+}
|
|
+
|
|
+Client *
|
|
+split_target(Monitor *m)
|
|
+{
|
|
+ Client *c;
|
|
+
|
|
+ if (!use_active_for_splits && (c = xytoclient(cursor->x, cursor->y))
|
|
+ && in_tree(m, c)) {
|
|
+ return c;
|
|
+ }
|
|
+
|
|
+ wl_list_for_each(c, &fstack, flink) {
|
|
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen && in_tree(m, c)) {
|
|
+ return c;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+void
|
|
+swapclients(const Arg *arg)
|
|
+{
|
|
+ Client *c, *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 || !in_tree(selmon, sel)
|
|
+ || !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' leaves in the layout tree */
|
|
+ if (target && in_tree(selmon, target)) {
|
|
+ sel_node = sel->node;
|
|
+ target_node = target->node;
|
|
+ sel_node->client = target;
|
|
+ target_node->client = sel;
|
|
+ sel->node = target_node;
|
|
+ target->node = sel_node;
|
|
+ arrange(selmon);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Mirror the split */
|
|
+void
|
|
+swapsplit(const Arg *arg)
|
|
+{
|
|
+ Client *sel = focustop(selmon);
|
|
+ LayoutNode *p, *tmp;
|
|
+
|
|
+ if (!in_tree(selmon, sel) || !(p = visible_parent(selmon, sel->node))) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ tmp = p->left;
|
|
+ p->left = p->right;
|
|
+ p->right = tmp;
|
|
+ p->split_ratio = 1.0f - p->split_ratio;
|
|
+ arrange(selmon);
|
|
+}
|
|
+
|
|
+/* Flip the split direction of the split node */
|
|
+void
|
|
+togglesplit(const Arg *arg)
|
|
+{
|
|
+ Client *sel = focustop(selmon);
|
|
+ LayoutNode *p;
|
|
+
|
|
+ if (!in_tree(selmon, sel) || !(p = visible_parent(selmon, sel->node))) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ p->is_split_vertically = !p->is_split_vertically;
|
|
+ p->keep_dir = 1;
|
|
+ arrange(selmon);
|
|
+}
|
|
+
|
|
+LayoutNode *
|
|
+tree_root(LayoutNode *n)
|
|
+{
|
|
+ while (n && n->split_node) {
|
|
+ n = n->split_node;
|
|
+ }
|
|
+
|
|
+ return n;
|
|
+}
|
|
+
|
|
+/* Remove c */
|
|
+void
|
|
+unlink_client(Client *c)
|
|
+{
|
|
+ LayoutNode *root;
|
|
+ Monitor *m;
|
|
+
|
|
+ if (!c || !c->node) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ root = tree_root(c->node);
|
|
+ wl_list_for_each(m, &mons, link) {
|
|
+ if (m->root == root) {
|
|
+ remove_leaf(m, c);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ c->node->client = NULL;
|
|
+ c->node = NULL;
|
|
+}
|
|
+
|
|
+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 it's a split node. */
|
|
+ return visible_count(node->left, m) + visible_count(node->right, m);
|
|
+}
|
|
+
|
|
+/* get nearest ancestor of node that splits visible clients on both sides */
|
|
+LayoutNode *
|
|
+visible_parent(Monitor *m, LayoutNode *n)
|
|
+{
|
|
+ for (n = n->split_node; n; n = n->split_node) {
|
|
+ if (visible_count(n->left, m) > 0 && visible_count(n->right, m) > 0) {
|
|
+ return n;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+int
|
|
+wants_vertical(int width, int height)
|
|
+{
|
|
+ return (double)width >= (double)height * split_width_multiplier;
|
|
+}
|
|
+
|
|
+/* get tiled client under the point, or the closest one */
|
|
+Client *
|
|
+xytoclient(double x, double y) {
|
|
+ Monitor *m = xytomon(x, y);
|
|
+ Client *c, *closest = NULL;
|
|
+ double dist, mindist = INT_MAX, dx, dy;
|
|
+
|
|
+ wl_list_for_each_reverse(c, &clients, link) {
|
|
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen && c->node &&
|
|
+ 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, m) && !c->isfloating && !c->isfullscreen && c->node) {
|
|
+ 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 = dx * dx + dy * dy;
|
|
+ if (dist < mindist) {
|
|
+ mindist = dist;
|
|
+ closest = c;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return closest;
|
|
+}
|
|
diff --git a/config.def.h b/config.def.h
|
|
index 13c5322..6233934 100644
|
|
--- a/config.def.h
|
|
+++ b/config.def.h
|
|
@@ -14,7 +14,14 @@ 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.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */
|
|
-
|
|
+/* btrtile */
|
|
+static const int use_active_for_splits = 1; /* 0: split the client under the cursor */
|
|
+static const int force_split = 0; /* 0: new window on the cursor's side, 1: left/top, 2: right/bottom */
|
|
+static const int preserve_split = 1; /* 1: splits keep the direction they were created with */
|
|
+static const float split_width_multiplier = 1.0f; /* side by side when width >= height * multiplier */
|
|
+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 +38,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 },
|
|
@@ -144,6 +152,16 @@ static const Key keys[] = {
|
|
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
|
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_comma, tagmon, {.i = WLR_DIRECTION_LEFT} },
|
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_period, 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} },
|
|
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_t, togglesplit, {0} },
|
|
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_y, swapsplit, {0} },
|
|
TAGKEYS( XKB_KEY_1, 0),
|
|
TAGKEYS( XKB_KEY_2, 1),
|
|
TAGKEYS( XKB_KEY_3, 2),
|
|
diff --git a/dwl.c b/dwl.c
|
|
index 4cfd0fd..8f59bef 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 <limits.h>
|
|
@@ -113,6 +114,7 @@ typedef struct {
|
|
const Arg arg;
|
|
} Button;
|
|
|
|
+typedef struct LayoutNode LayoutNode;
|
|
typedef struct Monitor Monitor;
|
|
typedef struct {
|
|
/* Must keep this field first */
|
|
@@ -158,7 +160,8 @@ typedef struct {
|
|
#endif
|
|
unsigned int bw;
|
|
uint32_t tags;
|
|
- int isfloating, isurgent, isfullscreen;
|
|
+ int isfloating, isurgent, isfullscreen, was_tiled;
|
|
+ LayoutNode *node;
|
|
} Client;
|
|
|
|
typedef struct {
|
|
@@ -225,6 +228,7 @@ struct Monitor {
|
|
int nmaster;
|
|
char ltsymbol[16];
|
|
int asleep;
|
|
+ LayoutNode *root;
|
|
};
|
|
|
|
typedef struct {
|
|
@@ -267,6 +271,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 capturerequest(struct wl_listener *listener, void *data);
|
|
static void chvt(const Arg *arg);
|
|
@@ -350,6 +355,10 @@ 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 swapsplit(const Arg *arg);
|
|
static void spawn(const Arg *arg);
|
|
static void startdrag(struct wl_listener *listener, void *data);
|
|
static void tag(const Arg *arg);
|
|
@@ -357,6 +366,7 @@ static void tagmon(const Arg *arg);
|
|
static void tile(Monitor *m);
|
|
static void togglefloating(const Arg *arg);
|
|
static void togglefullscreen(const Arg *arg);
|
|
+static void togglesplit(const Arg *arg);
|
|
static void toggletag(const Arg *arg);
|
|
static void toggleview(const Arg *arg);
|
|
static void unlocksession(struct wl_listener *listener, void *data);
|
|
@@ -481,6 +491,7 @@ static struct wlr_xwayland *xwayland;
|
|
|
|
/* attempt to encapsulate suck into one file */
|
|
#include "client.h"
|
|
+#include "btrtile.h"
|
|
|
|
#include "ime.h"
|
|
|
|
@@ -724,6 +735,17 @@ buttonpress(struct wl_listener *listener, void *data)
|
|
case WL_POINTER_BUTTON_STATE_RELEASED:
|
|
/* If you released any buttons, we exit interactive move/resize mode. */
|
|
if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
|
|
+ /* btrtile */
|
|
+ c = grabc;
|
|
+ if (c && c->was_tiled && !strcmp(selmon->ltsymbol, "|w|")) {
|
|
+ if (cursor_mode == CurMove && c->isfloating) {
|
|
+ setmon(c, selmon, 0);
|
|
+ insert_client(selmon, xytoclient(cursor->x, cursor->y), c);
|
|
+ setfloating(c, 0);
|
|
+ apply_layout(selmon, selmon->root, selmon->w, 1);
|
|
+ }
|
|
+ }
|
|
+ /* Default behaviour */
|
|
cursor_mode = CurNormal;
|
|
/* Update the cursor */
|
|
wlr_seat_pointer_clear_focus(seat);
|
|
@@ -840,6 +862,8 @@ cleanupmon(struct wl_listener *listener, void *data)
|
|
wlr_scene_output_destroy(m->scene_output);
|
|
|
|
closemon(m);
|
|
+ /* btrtile */
|
|
+ destroy_tree(m);
|
|
wlr_scene_node_destroy(&m->fullscreen_bg->node);
|
|
free(m);
|
|
}
|
|
@@ -1188,6 +1212,8 @@ createmon(struct wl_listener *listener, void *data)
|
|
|
|
wl_list_insert(&mons, &m->link);
|
|
printstatus();
|
|
+ /* btrtile */
|
|
+ init_tree(m);
|
|
|
|
/* The xdg-protocol specifies:
|
|
*
|
|
@@ -1431,6 +1457,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);
|
|
+
|
|
+ // btrtile remove clients for each monitor
|
|
+ unlink_client(c);
|
|
+
|
|
#ifdef XWAYLAND
|
|
if (c->type != XDGShell) {
|
|
wl_list_remove(&c->activate.link);
|
|
@@ -2002,8 +2032,9 @@ void
|
|
motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
|
|
double dx_unaccel, double dy_unaccel)
|
|
{
|
|
+ int tiled = 0;
|
|
int nx, ny;
|
|
- double sx = 0, sy = 0, sx_confined, sy_confined;
|
|
+ double sx = 0, sy = 0, sx_confined, sy_confined, dx_total, dy_total;
|
|
Client *c = NULL, *w = NULL;
|
|
LayerSurface *l = NULL, *focusedl = NULL;
|
|
struct wlr_surface *surface = NULL;
|
|
@@ -2057,28 +2088,55 @@ 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));
|
|
|
|
+ /* Skip if internal call */
|
|
+ if (time == 0)
|
|
+ goto focus;
|
|
+
|
|
+ tiled = grabc && !grabc->isfloating && !grabc->isfullscreen;
|
|
/* If we are currently grabbing the mouse, handle and return */
|
|
if (cursor_mode == CurMove) {
|
|
/* Move the grabbed client to the new position. */
|
|
- nx = (int)round(cursor->x) - grabcx;
|
|
- ny = (int)round(cursor->y) - grabcy;
|
|
- if (abs(selmon->w.x - nx) < (int)snap)
|
|
- nx = selmon->w.x;
|
|
- else if (abs((selmon->w.x + selmon->w.width) - (nx + grabc->geom.width)) < (int)snap)
|
|
- nx = selmon->w.x + selmon->w.width - grabc->geom.width;
|
|
- if (abs(selmon->w.y - ny) < (int)snap)
|
|
- ny = selmon->w.y;
|
|
- else if (abs((selmon->w.y + selmon->w.height) - (ny + grabc->geom.height)) < (int)snap)
|
|
- ny = selmon->w.y + selmon->w.height - grabc->geom.height;
|
|
- resize(grabc, (struct wlr_box){.x = nx, .y = ny,
|
|
- .width = grabc->geom.width, .height = grabc->geom.height}, 1);
|
|
- return;
|
|
+ if (grabc && grabc->isfloating) {
|
|
+ nx = (int)round(cursor->x) - grabcx;
|
|
+ ny = (int)round(cursor->y) - grabcy;
|
|
+ if (abs(selmon->w.x - nx) < (int)snap)
|
|
+ nx = selmon->w.x;
|
|
+ else if (abs((selmon->w.x + selmon->w.width) - (nx + grabc->geom.width)) < (int)snap)
|
|
+ nx = selmon->w.x + selmon->w.width - grabc->geom.width;
|
|
+ if (abs(selmon->w.y - ny) < (int)snap)
|
|
+ ny = selmon->w.y;
|
|
+ else if (abs((selmon->w.y + selmon->w.height) - (ny + grabc->geom.height)) < (int)snap)
|
|
+ ny = selmon->w.y + selmon->w.height - grabc->geom.height;
|
|
+ resize(grabc, (struct wlr_box){.x = nx, .y = ny,
|
|
+ .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;
|
|
- }
|
|
+ /* btrtile */
|
|
+ if (tiled) {
|
|
+ dx_total = cursor->x - resize_last_update_x;
|
|
+ dy_total = cursor->y - resize_last_update_y;
|
|
+
|
|
+ if (time - last_resize_time >= resize_interval_ms) {
|
|
+ if (fabs(dx_total) > fabs(dy_total))
|
|
+ resize_split_px(1, dx_total);
|
|
+ else
|
|
+ resize_split_px(0, dy_total);
|
|
+
|
|
+ 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. */
|
|
@@ -2112,22 +2170,40 @@ 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, "all-scroll");
|
|
- 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, "all-scroll");
|
|
+ break;
|
|
+ case CurResize:
|
|
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
|
|
+ resize_last_update_x = cursor->x;
|
|
+ resize_last_update_y = cursor->y;
|
|
+ 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, "all-scroll");
|
|
+ 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;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -2998,6 +3074,9 @@ unmapnotify(struct wl_listener *listener, void *data)
|
|
focusclient(focustop(selmon), 1);
|
|
}
|
|
} else {
|
|
+ // btrtile remove clients for each monitor
|
|
+ unlink_client(c);
|
|
+
|
|
wl_list_remove(&c->link);
|
|
setmon(c, NULL, 0);
|
|
wl_list_remove(&c->flink);
|
|
--
|
|
2.55.0
|
|
|