mirror of
https://codeberg.org/dwl/dwl-patches.git
synced 2025-09-08 12:14:50 +00:00
1124 lines
34 KiB
Diff
1124 lines
34 KiB
Diff
From 95a7437ccb456b37fc469f237da181b4a174d8b3 Mon Sep 17 00:00:00 2001
|
|
From: julmajustus <julmajustus@tutanota.com>
|
|
Date: Sat, 8 Feb 2025 05:41:12 +0200
|
|
Subject: [PATCH] btrtile-gaps init
|
|
|
|
---
|
|
btrtile.c | 716 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
config.def.h | 12 +
|
|
dwl.c | 223 ++++++++++++----
|
|
3 files changed, 896 insertions(+), 55 deletions(-)
|
|
create mode 100644 btrtile.c
|
|
|
|
diff --git a/btrtile.c b/btrtile.c
|
|
new file mode 100644
|
|
index 0000000..3ff3e20
|
|
--- /dev/null
|
|
+++ b/btrtile.c
|
|
@@ -0,0 +1,716 @@
|
|
+/* ************************************************************************** */
|
|
+/* */
|
|
+/* ::: :::::::: */
|
|
+/* btrtile.c :+: :+: :+: */
|
|
+/* +:+ +:+ +:+ */
|
|
+/* By: jmakkone <jmakkone@student.hive.fi> +#+ +:+ +#+ */
|
|
+/* +#+#+#+#+#+ +#+ */
|
|
+/* Created: 2024/12/15 00:26:07 by jmakkone #+# #+# */
|
|
+/* Updated: 2025/02/08 04:52:00 by jmakkone ### ########.fr */
|
|
+/* */
|
|
+/* ************************************************************************** */
|
|
+
|
|
+typedef enum {
|
|
+ COORD_X,
|
|
+ COORD_Y
|
|
+} CoordType;
|
|
+
|
|
+typedef struct LayoutNode {
|
|
+ unsigned int is_client_node;
|
|
+ unsigned int split_vertically;
|
|
+ float split_ratio;
|
|
+ struct LayoutNode *left;
|
|
+ struct LayoutNode *right;
|
|
+ struct LayoutNode *split_node;
|
|
+ Client *client;
|
|
+} LayoutNode;
|
|
+
|
|
+struct TreeLayout {
|
|
+ LayoutNode *root[TAGCOUNT + 1];
|
|
+ struct wl_list tiled_clients[TAGCOUNT + 1];
|
|
+};
|
|
+
|
|
+static void add_client_to_tiled_list(Client *c, struct wl_list *tiled_clients);
|
|
+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 split_vertically,
|
|
+ LayoutNode *left, LayoutNode *right);
|
|
+static void destroy_node_tree(LayoutNode *node);
|
|
+static void destroy_tree_layout(Monitor *m);
|
|
+static LayoutNode *find_client_node(LayoutNode *node, Client *c);
|
|
+static LayoutNode *find_split_node(LayoutNode *root, LayoutNode *child);
|
|
+static LayoutNode *find_suitable_split_node(LayoutNode *client_node,
|
|
+ unsigned int need_vertical);
|
|
+static LayoutNode *find_closest_client_node(LayoutNode *split_node,
|
|
+ enum Direction dir, int current_x,
|
|
+ int current_y, LayoutNode **closest,
|
|
+ int *closest_dist);
|
|
+static unsigned int get_client_center(LayoutNode *node, CoordType type);
|
|
+static unsigned int get_current_tag(Monitor *m);
|
|
+static void init_tree_layout(Monitor *m);
|
|
+static void insert_client(Monitor *m, Client *focused, Client *new_client,
|
|
+ LayoutNode **root, struct wl_list *tiled_clients);
|
|
+static unsigned int is_client_tiled(Client *c, struct wl_list *tiled_clients);
|
|
+static LayoutNode *remove_client_node(LayoutNode *node, Client *c);
|
|
+static void remove_client(Monitor *m, Client *c,
|
|
+ LayoutNode **root, struct wl_list *tiled_clients);
|
|
+static void setratio_h(const Arg *arg);
|
|
+static void setratio_v(const Arg *arg);
|
|
+static void swapclients(const Arg *arg);
|
|
+static Client *xytoclient(double x, double y, uint32_t tag);
|
|
+
|
|
+static int resizing_from_mouse = 0;
|
|
+static double resize_last_update_x, resize_last_update_y;
|
|
+static uint32_t last_resize_time = 0;
|
|
+
|
|
+void
|
|
+add_client_to_tiled_list(Client *c, struct wl_list *tiled_clients)
|
|
+{
|
|
+ if (!is_client_tiled(c, tiled_clients))
|
|
+ wl_list_insert(tiled_clients, &c->link_tiled);
|
|
+}
|
|
+
|
|
+void
|
|
+apply_layout(Monitor *m, LayoutNode *node,
|
|
+ struct wlr_box area, unsigned int is_root)
|
|
+{
|
|
+ float ratio;
|
|
+ int mid;
|
|
+ unsigned int 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 (node->is_client_node) {
|
|
+ resize(node->client, area, 0);
|
|
+ node->client->old_geom = area;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ratio = node->split_ratio;
|
|
+ if (ratio == 0.0f)
|
|
+ ratio = 0.5f;
|
|
+ if (ratio < 0.05f)
|
|
+ ratio = 0.05f;
|
|
+ if (ratio > 0.95f)
|
|
+ ratio = 0.95f;
|
|
+
|
|
+ if (node->split_vertically) {
|
|
+ mid = (int)(area.width * ratio);
|
|
+ left_area = (struct wlr_box){ area.x, area.y, mid, area.height};
|
|
+ right_area = (struct wlr_box){ area.x + mid, area.y,
|
|
+ area.width - mid, area.height};
|
|
+
|
|
+ if (e) {
|
|
+ left_area.width -= gappx / 2;
|
|
+ right_area.x += gappx / 2;
|
|
+ right_area.width -= gappx / 2;
|
|
+ }
|
|
+ } else {
|
|
+ mid = (int)(area.height * ratio);
|
|
+ left_area = (struct wlr_box){ area.x, area.y, area.width, mid };
|
|
+ right_area = (struct wlr_box){ area.x, area.y + mid,
|
|
+ area.width, 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)
|
|
+{
|
|
+ uint32_t active_tags = m->tagset[m->seltags];
|
|
+ unsigned int n = 0, found = 0, curtag;
|
|
+ Client *c, *cc, *tmp, *cur, *focused_client = NULL;
|
|
+ LayoutNode **root_ptr;
|
|
+ struct wl_list current_clients, *tiled_clients;
|
|
+ struct wlr_box full_area = m->w;
|
|
+
|
|
+ /* We skip handling clients in btrtile if multiple tags are selected */
|
|
+ if (!m->tree_layout || (active_tags && (active_tags & (active_tags - 1))))
|
|
+ return;
|
|
+ curtag = get_current_tag(m);
|
|
+ root_ptr = &m->tree_layout->root[curtag];
|
|
+ tiled_clients = &m->tree_layout->tiled_clients[curtag];
|
|
+ wl_list_for_each_reverse(c, &clients, link) {
|
|
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
|
|
+ n++;
|
|
+ }
|
|
+
|
|
+ /* If no visible clients, clear the node tree and tiled_clients list. */
|
|
+ if (n == 0) {
|
|
+ destroy_node_tree(*root_ptr);
|
|
+ *root_ptr = NULL;
|
|
+ wl_list_init(tiled_clients);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ focused_client = xytoclient(cursor->x, cursor->y, curtag);
|
|
+ /* If no focused client found, pick the first visible */
|
|
+ if (!focused_client) {
|
|
+ wl_list_for_each_reverse(c, &clients, link) {
|
|
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) {
|
|
+ focused_client = c;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Build a temporary list of currently visible tiled clients.
|
|
+ * If new clients are found add them to tree.*/
|
|
+ wl_list_init(¤t_clients);
|
|
+ wl_list_for_each_reverse(c, &clients, link) {
|
|
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) {
|
|
+ found = 0;
|
|
+ /* If client has multiple tags set, we might create infinite
|
|
+ * point to self loops by adding the same client to multiple
|
|
+ * tiled_clients lists, so hacky way to prevent that is to revert
|
|
+ * clients active tags to current active tag if multiple
|
|
+ * tags are selected. */
|
|
+ if (c->tags != 1u << curtag)
|
|
+ c->tags = 1u << curtag;
|
|
+ wl_list_for_each(cc, tiled_clients, link_tiled) {
|
|
+ if (cc == c) {
|
|
+ found = 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (!found) {
|
|
+ insert_client(m, focused_client, c, root_ptr, tiled_clients);
|
|
+ /* Recursion failsafe to handle inserted clients individually
|
|
+ * if a batch of new clients is added. */
|
|
+ apply_layout(m, *root_ptr, full_area, 1);
|
|
+ btrtile(m);
|
|
+ return;
|
|
+ }
|
|
+ wl_list_insert(¤t_clients, &c->link_temp);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Compare the current list of clients to the previous one and remove
|
|
+ * clients that no longer exist on the current tag.
|
|
+ * This handles cases where user moves clients to other tags.
|
|
+ * When client is closed or killed we manage the list and tree clean up in
|
|
+ * destroynotify. */
|
|
+ wl_list_for_each_safe(cc, tmp, tiled_clients, link_tiled) {
|
|
+ found = 0;
|
|
+ wl_list_for_each(cur, ¤t_clients, link_temp) {
|
|
+ if (cur == cc) {
|
|
+ found = 1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (!found)
|
|
+ remove_client(m, cc, root_ptr, tiled_clients);
|
|
+ }
|
|
+
|
|
+ /* Rebuild the updated client list */
|
|
+ wl_list_init(tiled_clients);
|
|
+ wl_list_for_each(cur, ¤t_clients, link_temp) {
|
|
+ wl_list_insert(tiled_clients, &cur->link_tiled);
|
|
+ }
|
|
+
|
|
+ /* Tile visible clients. */
|
|
+ apply_layout(m, *root_ptr, 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 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->split_vertically = split_vertically;
|
|
+ node->left = left;
|
|
+ node->right = right;
|
|
+ if (left)
|
|
+ left->split_node = node;
|
|
+ if (right)
|
|
+ right->split_node = node;
|
|
+ return node;
|
|
+}
|
|
+
|
|
+unsigned int
|
|
+is_client_tiled(Client *c, struct wl_list *tiled_clients)
|
|
+{
|
|
+ Client *cc;
|
|
+ wl_list_for_each(cc, tiled_clients, link_tiled) {
|
|
+ if (cc == c)
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+destroy_node_tree(LayoutNode *node)
|
|
+{
|
|
+ if (!node)
|
|
+ return;
|
|
+ if (!node->is_client_node) {
|
|
+ destroy_node_tree(node->left);
|
|
+ destroy_node_tree(node->right);
|
|
+ }
|
|
+ free(node);
|
|
+}
|
|
+
|
|
+void
|
|
+destroy_tree_layout(Monitor *m)
|
|
+{
|
|
+ if (!m)
|
|
+ return;
|
|
+ for (int i = 0; i <= TAGCOUNT; i++) {
|
|
+ destroy_node_tree(m->tree_layout->root[i]);
|
|
+ }
|
|
+}
|
|
+
|
|
+LayoutNode *
|
|
+find_client_node(LayoutNode *node, Client *c)
|
|
+{
|
|
+ LayoutNode *res;
|
|
+
|
|
+ if (!node)
|
|
+ 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_split_node(LayoutNode *root, LayoutNode *child)
|
|
+{
|
|
+ LayoutNode *res;
|
|
+
|
|
+ if (!root || root->is_client_node)
|
|
+ return NULL;
|
|
+ if (root->left == child || root->right == child)
|
|
+ return root;
|
|
+ res = find_split_node(root->left, child);
|
|
+ return res ? res : find_split_node(root->right, child);
|
|
+}
|
|
+
|
|
+LayoutNode *
|
|
+find_suitable_split_node(LayoutNode *client_node, unsigned int need_vertical)
|
|
+{
|
|
+ LayoutNode *node = client_node;
|
|
+ unsigned int curtag;
|
|
+
|
|
+ curtag = get_current_tag(selmon);
|
|
+ /* If we're starting from a client_node, go up one level first */
|
|
+ if (node->is_client_node) {
|
|
+ node = node->split_node ? node->split_node :
|
|
+ find_split_node(selmon->tree_layout->root[curtag], node);
|
|
+ }
|
|
+
|
|
+ /* Climb the tree until we find a node that is not client_node and
|
|
+ * match needed orientation. */
|
|
+ while (node && (node->is_client_node ||
|
|
+ node->split_vertically != need_vertical)) {
|
|
+ node = node->split_node ? node->split_node :
|
|
+ find_split_node(selmon->tree_layout->root[curtag], node);
|
|
+ }
|
|
+
|
|
+ return node;
|
|
+}
|
|
+
|
|
+LayoutNode *
|
|
+find_closest_client_node(LayoutNode *split_node, enum Direction dir,
|
|
+ int current_x, int current_y, LayoutNode **closest,
|
|
+ int *closest_dist)
|
|
+{
|
|
+ int client_center_x, client_center_y, dist, is_candidate;
|
|
+ if (!split_node)
|
|
+ return NULL;
|
|
+
|
|
+ if (split_node->is_client_node && split_node->client) {
|
|
+ client_center_x = get_client_center(split_node, COORD_X);
|
|
+ client_center_y = get_client_center(split_node, COORD_Y);
|
|
+ dist = 0;
|
|
+ is_candidate = 0;
|
|
+
|
|
+ switch (dir) {
|
|
+ case DIR_LEFT:
|
|
+ if (client_center_x < current_x) {
|
|
+ dist = current_x - client_center_x;
|
|
+ is_candidate = 1;
|
|
+ }
|
|
+ break;
|
|
+ case DIR_RIGHT:
|
|
+ if (client_center_x > current_x) {
|
|
+ dist = client_center_x - current_x;
|
|
+ is_candidate = 1;
|
|
+ }
|
|
+ break;
|
|
+ case DIR_UP:
|
|
+ if (client_center_y < current_y) {
|
|
+ dist = current_y - client_center_y;
|
|
+ is_candidate = 1;
|
|
+ }
|
|
+ break;
|
|
+ case DIR_DOWN:
|
|
+ if (client_center_y > current_y) {
|
|
+ dist = client_center_y - current_y;
|
|
+ is_candidate = 1;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (is_candidate && dist < *closest_dist) {
|
|
+ *closest_dist = dist;
|
|
+ *closest = split_node;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Recursively search in left and right split_nodes */
|
|
+ find_closest_client_node(split_node->left, dir, current_x, current_y,
|
|
+ closest, closest_dist);
|
|
+ find_closest_client_node(split_node->right, dir, current_x, current_y,
|
|
+ closest, closest_dist);
|
|
+
|
|
+ return *closest;
|
|
+}
|
|
+
|
|
+unsigned int
|
|
+get_client_center(LayoutNode *node, CoordType type)
|
|
+{
|
|
+ if (!node || !node->is_client_node || !node->client)
|
|
+ return 0;
|
|
+
|
|
+ switch (type) {
|
|
+ case COORD_X:
|
|
+ return node->client->old_geom.x + node->client->old_geom.width / 2;
|
|
+ case COORD_Y:
|
|
+ return node->client->old_geom.y + node->client->old_geom.height / 2;
|
|
+ default:
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+unsigned int
|
|
+get_current_tag(Monitor *m)
|
|
+{
|
|
+ uint32_t active;
|
|
+
|
|
+ if (!m)
|
|
+ return 0;
|
|
+
|
|
+ active = m->tagset[m->seltags];
|
|
+ for (int i = 0; i < TAGCOUNT; i++) {
|
|
+ if (active & (1u << i))
|
|
+ return i;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+init_tree_layout(Monitor *m)
|
|
+{
|
|
+ if (!m)
|
|
+ return;
|
|
+ m->tree_layout = calloc(1, sizeof(TreeLayout));
|
|
+ for (int i = 0; i <= TAGCOUNT; i++) {
|
|
+ m->tree_layout->root[i] = NULL;
|
|
+ wl_list_init(&m->tree_layout->tiled_clients[i]);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+insert_client(Monitor *m, Client *focused, Client *new_client,
|
|
+ LayoutNode **root, struct wl_list *tiled_clients)
|
|
+{
|
|
+ int mid_x, mid_y;
|
|
+ LayoutNode *old_root, *client_node, *old_client_node, *new_client_node;
|
|
+ unsigned int wider;
|
|
+
|
|
+ /* If there is no root node, inserted client must be the first one.
|
|
+ * If there's no focused client we treat the inserted client as a new root node.*/
|
|
+ if (!*root) {
|
|
+ *root = create_client_node(new_client);
|
|
+ add_client_to_tiled_list(new_client, tiled_clients);
|
|
+ return;
|
|
+ }
|
|
+ if (!focused || !(client_node = find_client_node(*root, focused))) {
|
|
+ old_root = *root;
|
|
+ *root = create_split_node(1, old_root, create_client_node(new_client));
|
|
+ add_client_to_tiled_list(new_client, tiled_clients);
|
|
+ return;
|
|
+ }
|
|
+ /* We check the cursor location on splittable area and choose
|
|
+ * the new client's position. On horizontal splits left node represent
|
|
+ * the upper node and vice versa.*/
|
|
+ mid_x = focused->old_geom.x + focused->old_geom.width / 2;
|
|
+ mid_y = focused->old_geom.y + focused->old_geom.height / 2;
|
|
+ old_client_node = create_client_node(client_node->client);
|
|
+ new_client_node = create_client_node(new_client);
|
|
+
|
|
+ wider = focused->old_geom.width >= focused->old_geom.height;
|
|
+ if (wider) {
|
|
+ /* Vertical split */
|
|
+ client_node->split_vertically = 1;
|
|
+ if (cursor->x <= mid_x) {
|
|
+ client_node->left = new_client_node;
|
|
+ client_node->right = old_client_node;
|
|
+ } else {
|
|
+ client_node->left = old_client_node;
|
|
+ client_node->right = new_client_node;
|
|
+ }
|
|
+ } else {
|
|
+ /* Horizontal split */
|
|
+ client_node->split_vertically = 0;
|
|
+ if (cursor->y <= mid_y) {
|
|
+ client_node->left = new_client_node;
|
|
+ client_node->right = old_client_node;
|
|
+ } else {
|
|
+ client_node->left = old_client_node;
|
|
+ client_node->right = new_client_node;
|
|
+ }
|
|
+ }
|
|
+ /* The old client node becomes the splitnode for the old and new client
|
|
+ * nodes.*/
|
|
+ client_node->is_client_node = 0;
|
|
+ client_node->client = NULL;
|
|
+ add_client_to_tiled_list(new_client, tiled_clients);
|
|
+}
|
|
+
|
|
+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, LayoutNode **root,
|
|
+ struct wl_list *tiled_clients)
|
|
+{
|
|
+ Client *cc, *tmp;
|
|
+
|
|
+ *root = remove_client_node(*root, c);
|
|
+ wl_list_for_each_safe(cc, tmp, tiled_clients, link_tiled) {
|
|
+ if (cc == c) {
|
|
+ wl_list_remove(&cc->link_tiled);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+setratio_h(const Arg *arg)
|
|
+{
|
|
+ Client *sel = focustop(selmon);
|
|
+ LayoutNode *client_node, *node;
|
|
+ float new_ratio;
|
|
+
|
|
+ if (!arg || !sel || !selmon->lt[selmon->sellt]->arrange)
|
|
+ return;
|
|
+
|
|
+ client_node = find_client_node(selmon->tree_layout->root[get_current_tag(selmon)], sel);
|
|
+ if (!client_node)
|
|
+ return;
|
|
+
|
|
+ /* Find a suitable vertical node */
|
|
+ node = find_suitable_split_node(client_node, 1);
|
|
+ if (!node)
|
|
+ return;
|
|
+
|
|
+ new_ratio = arg->f ? (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;
|
|
+
|
|
+ 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)
|
|
+{
|
|
+ float new_ratio;
|
|
+ Client *sel = focustop(selmon);
|
|
+ LayoutNode *client_node, *node;
|
|
+
|
|
+ if (!arg || !sel || !selmon->lt[selmon->sellt]->arrange)
|
|
+ return;
|
|
+
|
|
+ client_node = find_client_node(selmon->tree_layout->root[get_current_tag(selmon)], sel);
|
|
+ if (!client_node)
|
|
+ return;
|
|
+
|
|
+ /* Find a suitable horizontal node */
|
|
+ node = find_suitable_split_node(client_node, 0);
|
|
+ if (!node)
|
|
+ return;
|
|
+
|
|
+ new_ratio = arg->f ? (node->split_ratio + arg->f) : 0.5f;
|
|
+ if (new_ratio < 0.05f) new_ratio = 0.5f;
|
|
+ if (new_ratio > 0.95f) new_ratio = 0.95f;
|
|
+
|
|
+ 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 *tmp, *sel = focustop(selmon);
|
|
+ enum Direction dir = (enum Direction)arg->i;
|
|
+ LayoutNode *client_node, *target = NULL, *split_node = NULL;
|
|
+ unsigned int current_x, current_y, curtag = get_current_tag(selmon);
|
|
+ int closest_dist = INT_MAX;
|
|
+
|
|
+ if (!arg || !sel || !selmon->lt[selmon->sellt]->arrange)
|
|
+ return;
|
|
+
|
|
+ client_node = find_client_node(selmon->tree_layout->root[curtag], sel);
|
|
+ if (!client_node)
|
|
+ return;
|
|
+
|
|
+ current_x = get_client_center(client_node, COORD_X);
|
|
+ current_y = get_client_center(client_node, COORD_Y);
|
|
+
|
|
+ /* For up/down swaps, restrict search within the current horizontal split
|
|
+ * node if no suitable horizontal split node is found, default to vertical */
|
|
+ if (dir == DIR_UP || dir == DIR_DOWN) {
|
|
+ split_node = find_suitable_split_node(client_node, 0);
|
|
+ if (!split_node)
|
|
+ return;
|
|
+ } else {
|
|
+ split_node = selmon->tree_layout->root[curtag];
|
|
+ }
|
|
+
|
|
+ /* Find the closest client node in the specified direction and swap
|
|
+ * the clients */
|
|
+ find_closest_client_node(split_node, dir, current_x, current_y,
|
|
+ &target, &closest_dist);
|
|
+
|
|
+ if (target && target->is_client_node && target->client) {
|
|
+ tmp = client_node->client;
|
|
+ client_node->client = target->client;
|
|
+ target->client = tmp;
|
|
+
|
|
+ arrange(selmon);
|
|
+ }
|
|
+}
|
|
+
|
|
+Client *
|
|
+xytoclient(double x, double y, uint32_t tag) {
|
|
+ Client *c, *closest = NULL;
|
|
+ double dist, mindist = INT_MAX, dx, dy;
|
|
+
|
|
+ wl_list_for_each_reverse(c, &selmon->tree_layout->tiled_clients[tag], link_tiled) {
|
|
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen &&
|
|
+ x >= c->geom.x && x <= (c->geom.x + c->geom.width) &&
|
|
+ y >= c->geom.y && y <= (c->geom.y + c->geom.height)){
|
|
+ return c;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* If no client was found at cursor position fallback to closest. */
|
|
+ wl_list_for_each_reverse(c, &selmon->tree_layout->tiled_clients[tag], link_tiled) {
|
|
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen) {
|
|
+ dx = 0, dy = 0;
|
|
+
|
|
+ if (x < c->geom.x)
|
|
+ dx = c->geom.x - x;
|
|
+ else if (x > (c->geom.x + c->geom.width))
|
|
+ dx = x - (c->geom.x + c->geom.width);
|
|
+
|
|
+ if (y < c->geom.y)
|
|
+ dy = c->geom.y - y;
|
|
+ else if (y > (c->geom.y + c->geom.height))
|
|
+ dy = y - (c->geom.y + c->geom.height);
|
|
+
|
|
+ dist = sqrt(dx * dx + dy * dy);
|
|
+ if (dist < mindist) {
|
|
+ mindist = dist;
|
|
+ closest = c;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return closest;
|
|
+}
|
|
diff --git a/config.def.h b/config.def.h
|
|
index 22d2171..92f3ad6 100644
|
|
--- a/config.def.h
|
|
+++ 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..c7f097d 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 TreeLayout TreeLayout;
|
|
typedef struct Monitor Monitor;
|
|
typedef struct {
|
|
/* Must keep these three elements in this order */
|
|
@@ -139,8 +141,11 @@ 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;
|
|
+ struct wl_list link_tiled;
|
|
+ struct wl_list link_temp;
|
|
} Client;
|
|
|
|
typedef struct {
|
|
@@ -208,6 +213,7 @@ struct Monitor {
|
|
int nmaster;
|
|
char ltsymbol[16];
|
|
int asleep;
|
|
+ TreeLayout *tree_layout;
|
|
};
|
|
|
|
typedef struct {
|
|
@@ -250,6 +256,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 +340,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 +441,7 @@ static xcb_atom_t netatom[NetLast];
|
|
|
|
/* attempt to encapsulate suck into one file */
|
|
#include "client.h"
|
|
+#include "btrtile.c"
|
|
|
|
/* function implementations */
|
|
void
|
|
@@ -600,53 +611,84 @@ buttonpress(struct wl_listener *listener, void *data)
|
|
{
|
|
struct wlr_pointer_button_event *event = data;
|
|
struct wlr_keyboard *keyboard;
|
|
- uint32_t mods;
|
|
- Client *c;
|
|
+ struct wl_list *tiled_clients;
|
|
+ LayoutNode **root;
|
|
+ uint32_t mods, curtag, active_tags = selmon->tagset[selmon->seltags];
|
|
+ Client *c, *target = NULL;
|
|
const Button *b;
|
|
|
|
+ curtag = get_current_tag(selmon);
|
|
+ root = &selmon->tree_layout->root[curtag];
|
|
+ tiled_clients = &selmon->tree_layout->tiled_clients[curtag];
|
|
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
|
|
|
|
switch (event->state) {
|
|
- case WL_POINTER_BUTTON_STATE_PRESSED:
|
|
- cursor_mode = CurPressed;
|
|
- selmon = xytomon(cursor->x, cursor->y);
|
|
- if (locked)
|
|
- break;
|
|
+ case WL_POINTER_BUTTON_STATE_PRESSED:
|
|
+ cursor_mode = CurPressed;
|
|
+ selmon = xytomon(cursor->x, cursor->y);
|
|
+ if (locked)
|
|
+ break;
|
|
|
|
- /* Change focus if the button was _pressed_ over a client */
|
|
- xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
|
|
- if (c && (!client_is_unmanaged(c) || client_wants_focus(c)))
|
|
- focusclient(c, 1);
|
|
+ /* Change focus if the button was _pressed_ over a client */
|
|
+ xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
|
|
+ if (c && (!client_is_unmanaged(c) || client_wants_focus(c)))
|
|
+ focusclient(c, 1);
|
|
|
|
- keyboard = wlr_seat_get_keyboard(seat);
|
|
- mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
|
|
- for (b = buttons; b < END(buttons); b++) {
|
|
- if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
|
|
+ keyboard = wlr_seat_get_keyboard(seat);
|
|
+ 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) {
|
|
- b->func(&b->arg);
|
|
+ b->func(&b->arg);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case WL_POINTER_BUTTON_STATE_RELEASED:
|
|
+ /* 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|")) {
|
|
+ /* Check if more than one tag is active, if so we escape */
|
|
+ if (active_tags && (active_tags & (active_tags - 1)))
|
|
+ break;
|
|
+ if (cursor_mode == CurMove && c->isfloating) {
|
|
+ target = xytoclient(cursor->x, cursor->y, curtag);
|
|
+
|
|
+ if (target && !target->isfloating && !target->isfullscreen) {
|
|
+ insert_client(selmon, target, c, root, tiled_clients);
|
|
+ } else {
|
|
+ *root = create_client_node(c);
|
|
+ add_client_to_tiled_list(c, tiled_clients);
|
|
+ }
|
|
+
|
|
+ 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;
|
|
}
|
|
- }
|
|
- break;
|
|
- case WL_POINTER_BUTTON_STATE_RELEASED:
|
|
- /* 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) {
|
|
- 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);
|
|
- return;
|
|
- } else {
|
|
cursor_mode = CurNormal;
|
|
- }
|
|
- break;
|
|
+ break;
|
|
}
|
|
/* If the event wasn't handled by the compositor, notify the client with
|
|
* pointer focus that a button press has occurred */
|
|
wlr_seat_pointer_notify_button(seat,
|
|
- event->time_msec, event->button, event->state);
|
|
+ event->time_msec, event->button, event->state);
|
|
}
|
|
|
|
void
|
|
@@ -720,6 +762,9 @@ 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_layout(m);
|
|
+ free(m->tree_layout);
|
|
+ m->tree_layout = NULL;
|
|
closemon(m);
|
|
wlr_scene_node_destroy(&m->fullscreen_bg->node);
|
|
free(m);
|
|
@@ -1024,6 +1069,7 @@ createmon(struct wl_listener *listener, void *data)
|
|
|
|
wl_list_insert(&mons, &m->link);
|
|
printstatus();
|
|
+ init_tree_layout(m);
|
|
|
|
/* The xdg-protocol specifies:
|
|
*
|
|
@@ -1263,6 +1309,15 @@ destroynotify(struct wl_listener *listener, void *data)
|
|
wl_list_remove(&c->destroy.link);
|
|
wl_list_remove(&c->set_title.link);
|
|
wl_list_remove(&c->fullscreen.link);
|
|
+ /* 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->tree_layout) {
|
|
+ for (int i = 0; i < TAGCOUNT; i++) {
|
|
+ remove_client(selmon, c,
|
|
+ &selmon->tree_layout->root[i],
|
|
+ &selmon->tree_layout->tiled_clients[i]);
|
|
+ }
|
|
+ }
|
|
#ifdef XWAYLAND
|
|
if (c->type != XDGShell) {
|
|
wl_list_remove(&c->activate.link);
|
|
@@ -1809,7 +1864,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 +1919,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 +2002,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,
|
|
+ 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;
|
|
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
|
|
+ break;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
--
|
|
2.45.3
|
|
|