+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ julmajustus
+
+
+
+
+
+
+
+
+
+
+
+
+ 03de6e2ada
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Simplified the resizing logic to avoid full arrange calls from +motionnotify +- Minor intend fixes+ + + + +
+ 2026-05-21 00:54:00 +03:00
+
+
+
+
+
+
+
+
+
+
+ 929 lines
+
+
+
+
+
+ 27 KiB
+
+
+
+
+
+ Diff
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 929 lines
+
+
+
+
+
+ 27 KiB
+
+
+
+
+
+ Diff
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | + + | From 1520d1f200ef0fb381683c1bcd58e553b52ac289 Mon Sep 17 00:00:00 2001
+ |
+
| + + | From: julmajustus <julmajustus@tutanota.com>
+ |
+
| + + | Date: Thu, 21 May 2026 00:42:07 +0300
+ |
+
| + + | Subject: [PATCH] btrtile: Spring update pt2
+ |
+
| + + |
+ |
+
| + + | - Simplified the resizing logic to avoid full arrange calls from
+ |
+
| + + | motionnotify
+ |
+
| + + | - Minor intend fixes
+ |
+
| + + | ---
+ |
+
| + + | btrtile.c | 583 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ |
+
| + + | config.def.h | 12 ++
+ |
+
| + + | dwl.c | 152 +++++++++++---
+ |
+
| + + | 3 files changed, 720 insertions(+), 27 deletions(-)
+ |
+
| + + | create mode 100644 btrtile.c
+ |
+
| + + |
+ |
+
| + + | diff --git a/btrtile.c b/btrtile.c
+ |
+
| + + | new file mode 100644
+ |
+
| + + | index 0000000..f05a30f
+ |
+
| + + | --- /dev/null
+ |
+
| + + | +++ b/btrtile.c
+ |
+
| + + | @@ -0,0 +1,583 @@
+ |
+
| + + | +/* ************************************************************************** */
+ |
+
| + + | +/* @@@ @@@@@@@@ */
+ |
+
| + + | +/* @@@ @@@@@@@@@@ */
+ |
+
| + + | +/* @@! @@! @@@@ */
+ |
+
| + + | +/* !@! !@! @!@!@ */
+ |
+
| + + | +/* btrtile.c @!! @!@ @! !@! */
+ |
+
| + + | +/* !!! !@!!! !!! */
+ |
+
| + + | +/* By: julmajustus <julmajustus@tutanota.com> !!: !!:! !!! */
+ |
+
| + + | +/* ::! :!: !:! */
+ |
+
| + + | +/* Created: 2024/12/15 00:26:07 by julmajustus :: ::::::: :: */
+ |
+
| + + | +/* Updated: 2026/05/20 22:38:02 by julmajustus : : : : : : */
+ |
+
| + + | +/* */
+ |
+
| + + | +/* ************************************************************************** */
+ |
+
| + + | +
+ |
+
| + + | +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(Monitor *m, LayoutNode *start,
+ |
+
| + + | + unsigned int need_vertical, int focused_on_left);
+ |
+
| + + | +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 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, e = m->gaps;
+ |
+
| + + | + struct wlr_box left_area, right_area;
+ |
+
| + + | +
+ |
+
| + + | + if (!node)
+ |
+
| + + | + return;
+ |
+
| + + | +
+ |
+
| + + | + if (is_root && e) {
+ |
+
| + + | + area.x += gappx;
+ |
+
| + + | + area.y += gappx;
+ |
+
| + + | + area.width -= 2 * gappx;
+ |
+
| + + | + area.height -= 2 * gappx;
+ |
+
| + + | + }
+ |
+
| + + | +
+ |
+
| + + | + /* 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;
+ |
+
| + + | + if (area.x == c->old_geom.x && area.y == c->old_geom.y &&
+ |
+
| + + | + area.width == c->old_geom.width && area.height == c->old_geom.height)
+ |
+
| + + | + 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 we’re 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;
+ |
+
| + + | +
+ |
+
| + + | + if (e) {
+ |
+
| + + | + left_area.width -= gappx / 2;
+ |
+
| + + | + right_area.x += gappx / 2;
+ |
+
| + + | + right_area.width -= gappx / 2;
+ |
+
| + + | + }
+ |
+
| + + | + } 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;
+ |
+
| + + | +
+ |
+
| + + | + if (e) {
+ |
+
| + + | + left_area.height -= gappx / 2;
+ |
+
| + + | + right_area.y += gappx / 2;
+ |
+
| + + | + right_area.height -= gappx / 2;
+ |
+
| + + | + }
+ |
+
| + + | + }
+ |
+
| + + | +
+ |
+
| + + | + 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)
+ |
+
| + + | + 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(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;
+ |
+
| + + | +}
+ |
+
| + + | +void
+ |
+
| + + | +init_tree(Monitor *m)
+ |
+
| + + | +{
+ |
+
| + + | + if (m)
+ |
+
| + + | + 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);
+ |
+
| + + | +}
+ |
+
| + + | +
+ |
+
| + + | +static void
+ |
+
| + + | +setratio(unsigned int need_vertical, const Arg *arg)
+ |
+
| + + | +{
+ |
+
| + + | + Client *sel;
+ |
+
| + + | + LayoutNode *client_node, *split_node;
+ |
+
| + + | + float new_ratio;
+ |
+
| + + | + int focused_on_left;
+ |
+
| + + | +
+ |
+
| + + | + if (!selmon || !selmon->lt[selmon->sellt]->arrange)
+ |
+
| + + | + return;
+ |
+
| + + | +
+ |
+
| + + | + sel = focustop(selmon);
+ |
+
| + + | + if (!sel)
+ |
+
| + + | + return;
+ |
+
| + + | +
+ |
+
| + + | + client_node = find_client_node(selmon->root, sel);
+ |
+
| + + | + if (!client_node)
+ |
+
| + + | + return;
+ |
+
| + + | +
+ |
+
| + + | + 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)
+ |
+
| + + | + 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;
+ |
+
| + + | +
+ |
+
| + + | + apply_layout(selmon, selmon->root, selmon->w, 1);
+ |
+
| + + | + /* Skip the arrange when called from motionnotify; that path calls
+ |
+
| + + | + * arrange itself after rate-limiting. */
+ |
+
| + + | +}
+ |
+
| + + | +
+ |
+
| + + | +void
+ |
+
| + + | +setratio_h(const Arg *arg)
+ |
+
| + + | +{
+ |
+
| + + | + setratio(1, arg);
+ |
+
| + + | +}
+ |
+
| + + | +
+ |
+
| + + | +void
+ |
+
| + + | +setratio_v(const Arg *arg)
+ |
+
| + + | +{
+ |
+
| + + | + setratio(0, arg);
+ |
+
| + + | +}
+ |
+
| + + | +
+ |
+
| + + | +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 it’s a split node. */
+ |
+
| + + | + return visible_count(node->left, m) + visible_count(node->right, m);
+ |
+
| + + | +}
+ |
+
| + + | +
+ |
+
| + + | +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 &&
+ |
+
| + + | + 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) {
+ |
+
| + + | + 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 8a6eda0..bc04e3f 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.0f, 0.0f, 0.0f, 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)
+ |
+
| + + |
+ |
+
| + + | @@ -30,6 +33,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 +148,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 44f3ad9..a121efc 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>
+ |
+
| + + | @@ -100,6 +101,7 @@ typedef struct {
+ |
+
| + + | const Arg arg;
+ |
+
| + + | } Button;
+ |
+
| + + |
+ |
+
| + + | +typedef struct LayoutNode LayoutNode;
+ |
+
| + + | typedef struct Monitor Monitor;
+ |
+
| + + | typedef struct {
+ |
+
| + + | /* Must keep this field first */
+ |
+
| + + | @@ -137,8 +139,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 {
+ |
+
| + + | @@ -205,6 +208,7 @@ struct Monitor {
+ |
+
| + + | int nmaster;
+ |
+
| + + | char ltsymbol[16];
+ |
+
| + + | int asleep;
+ |
+
| + + | + LayoutNode *root;
+ |
+
| + + | };
+ |
+
| + + |
+ |
+
| + + | typedef struct {
+ |
+
| + + | @@ -247,6 +251,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);
+ |
+
| + + | @@ -329,6 +334,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);
+ |
+
| + + | @@ -454,6 +462,7 @@ static struct wlr_xwayland *xwayland;
+ |
+
| + + |
+ |
+
| + + | /* attempt to encapsulate suck into one file */
+ |
+
| + + | #include "client.h"
+ |
+
| + + | +#include "btrtile.c"
+ |
+
| + + |
+ |
+
| + + | /* function implementations */
+ |
+
| + + | void
+ |
+
| + + | @@ -624,7 +633,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);
+ |
+
| + + | @@ -645,7 +654,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;
+ |
+
| + + | }
+ |
+
| + + | @@ -655,6 +664,21 @@ 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);
+ |
+
| + + | + apply_layout(selmon, selmon->root, selmon->w, 1);
+ |
+
| + + | + }
+ |
+
| + + | + }
+ |
+
| + + | + /* Default behaviour */
+ |
+
| + + | wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
+ |
+
| + + | cursor_mode = CurNormal;
+ |
+
| + + | /* Drop the window off on its new monitor */
+ |
+
| + + | @@ -746,6 +770,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);
+ |
+
| + + | @@ -1090,6 +1115,7 @@ createmon(struct wl_listener *listener, void *data)
+ |
+
| + + |
+ |
+
| + + | wl_list_insert(&mons, &m->link);
+ |
+
| + + | printstatus();
+ |
+
| + + | + init_tree(m);
+ |
+
| + + |
+ |
+
| + + | /* The xdg-protocol specifies:
+ |
+
| + + | *
+ |
+
| + + | @@ -1329,9 +1355,17 @@ destroynotify(struct wl_listener *listener, void *data)
+ |
+
| + + | {
+ |
+
| + + | /* Called when the xdg_toplevel is destroyed. */
+ |
+
| + + | Client *c = wl_container_of(listener, c, destroy);
+ |
+
| + + | + Monitor *mon;
+ |
+
| + + | wl_list_remove(&c->destroy.link);
+ |
+
| + + | wl_list_remove(&c->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 */
+ |
+
| + + | + wl_list_for_each(mon, &mons, link) {
+ |
+
| + + | + if (mon->root) {
+ |
+
| + + | + remove_client(mon, c);
+ |
+
| + + | + }
+ |
+
| + + | + }
+ |
+
| + + | #ifdef XWAYLAND
+ |
+
| + + | if (c->type != XDGShell) {
+ |
+
| + + | wl_list_remove(&c->activate.link);
+ |
+
| + + | @@ -1862,7 +1896,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;
+ |
+
| + + | @@ -1916,18 +1951,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));
+ |
+
| + + |
+ |
+
| + + | - /* If we are currently grabbing the mouse, handle and return */
+ |
+
| + + | + /* Skip if internal call */
+ |
+
| + + | + if (time == 0)
+ |
+
| + + | + 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) {
+ |
+
| + + | + 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);
+ |
+
| + + | + }
+ |
+
| + + | +
+ |
+
| + + | + 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. */
+ |
+
| + + | @@ -1961,22 +2033,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, "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;
+ |
+
| + + | + 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;
+ |
+
| + + | + }
+ |
+
| + + | }
+ |
+
| + + | }
+ |
+
| + + |
+ |
+
| + + | @@ -2826,6 +2916,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.53.0
+ |
+
| + + |
+ |
+