From 0ea1abc27523132d6c2323474f231e01bb8bf14b Mon Sep 17 00:00:00 2001 From: ernestoCruz05 Date: Sun, 5 Apr 2026 03:13:35 +0100 Subject: [PATCH] feat: working but not 100% polished and ready dwindle implementation --- src/animation/client.h | 2 +- src/dispatch/bind_define.h | 15 +- src/layout/dwindle.h | 271 +++++++++++++++++++++++++++++++++++++ src/layout/layout.h | 3 + src/mango.c | 47 +++++-- 5 files changed, 328 insertions(+), 10 deletions(-) create mode 100644 src/layout/dwindle.h diff --git a/src/animation/client.h b/src/animation/client.h index f2afa106..0aa646f9 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -1102,7 +1102,7 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) { c->mon->resizing_count_pending++; } - if (c == grabc) { + if (c == grabc || (cursor_mode == CurResize && ISTILED(c))) { c->animation.running = false; c->need_output_flush = false; diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 3f84fe9d..b05c88a6 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -112,7 +112,18 @@ int32_t exchange_client(const Arg *arg) { Client *tc = direction_select(arg); tc = get_focused_stack_client(tc); - exchange_two_client(c, tc); + + if (!tc) + return 0; + + if (c->mon && c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == DWINDLE) { + uint32_t tag = c->mon->pertag->curtag; + dwindle_move_client(&c->mon->pertag->dwindle_root[tag], c, tc, + c->mon->pertag->mfacts[tag]); + arrange(c->mon, false, false); + } else { + exchange_two_client(c, tc); + } return 0; } @@ -441,6 +452,8 @@ int32_t moveresize(const Arg *arg) { wlr_cursor_set_xcursor(cursor, cursor_mgr, cursors[rzcorner]); } else { + grabcx = (int32_t)round(cursor->x); + grabcy = (int32_t)round(cursor->y); wlr_cursor_set_xcursor(cursor, cursor_mgr, "grab"); } break; diff --git a/src/layout/dwindle.h b/src/layout/dwindle.h new file mode 100644 index 00000000..bce03c2a --- /dev/null +++ b/src/layout/dwindle.h @@ -0,0 +1,271 @@ +typedef struct DwindleNode DwindleNode; +struct DwindleNode { + bool is_split; + bool split_h; + float ratio; + int32_t container_w; + int32_t container_h; + DwindleNode *parent; + DwindleNode *first; + DwindleNode *second; + Client *client; +}; + +static DwindleNode *dwindle_new_leaf(Client *c) { + DwindleNode *n = calloc(1, sizeof(DwindleNode)); + n->client = c; + return n; +} + +static DwindleNode *dwindle_find_leaf(DwindleNode *node, Client *c) { + if (!node) + return NULL; + if (!node->is_split) + return node->client == c ? node : NULL; + DwindleNode *r = dwindle_find_leaf(node->first, c); + return r ? r : dwindle_find_leaf(node->second, c); +} + +static DwindleNode *dwindle_first_leaf(DwindleNode *node) { + if (!node) + return NULL; + while (node->is_split) + node = node->first; + return node; +} + +static void dwindle_free_tree(DwindleNode *node) { + if (!node) + return; + dwindle_free_tree(node->first); + dwindle_free_tree(node->second); + free(node); +} + +static void dwindle_insert(DwindleNode **root, Client *new_c, Client *focused, + float ratio) { + DwindleNode *new_leaf = dwindle_new_leaf(new_c); + + if (!*root) { + *root = new_leaf; + return; + } + + DwindleNode *target = focused ? dwindle_find_leaf(*root, focused) : NULL; + if (!target) + target = dwindle_first_leaf(*root); + + DwindleNode *split = calloc(1, sizeof(DwindleNode)); + split->is_split = true; + split->ratio = ratio; + split->first = target; + split->second = new_leaf; + split->parent = target->parent; + target->parent = split; + new_leaf->parent = split; + + if (!split->parent) { + *root = split; + } else { + if (split->parent->first == target) + split->parent->first = split; + else + split->parent->second = split; + } +} + +static void dwindle_remove(DwindleNode **root, Client *c) { + DwindleNode *leaf = dwindle_find_leaf(*root, c); + if (!leaf) + return; + + DwindleNode *parent = leaf->parent; + if (!parent) { + free(leaf); + *root = NULL; + return; + } + + DwindleNode *sibling = + (parent->first == leaf) ? parent->second : parent->first; + DwindleNode *grandparent = parent->parent; + sibling->parent = grandparent; + + if (!grandparent) { + *root = sibling; + } else { + if (grandparent->first == parent) + grandparent->first = sibling; + else + grandparent->second = sibling; + } + + free(leaf); + free(parent); +} + +static void dwindle_assign(DwindleNode *node, int32_t ax, int32_t ay, + int32_t aw, int32_t ah, int32_t gap_h, + int32_t gap_v) { + if (!node) + return; + + if (!node->is_split) { + if (node->client) { + struct wlr_box box = {ax, ay, MAX(1, aw), MAX(1, ah)}; + resize(node->client, box, 0); + } + return; + } + + if (node->container_w == 0 && node->container_h == 0) + node->split_h = (aw >= ah); + node->container_w = aw; + node->container_h = ah; + if (node->split_h) { + int32_t w1 = MAX(1, (int32_t)(aw * node->ratio) - gap_h / 2); + dwindle_assign(node->first, ax, ay, w1, ah, gap_h, gap_v); + dwindle_assign(node->second, ax + w1 + gap_h, ay, aw - w1 - gap_h, ah, + gap_h, gap_v); + } else { + int32_t h1 = MAX(1, (int32_t)(ah * node->ratio) - gap_v / 2); + dwindle_assign(node->first, ax, ay, aw, h1, gap_h, gap_v); + dwindle_assign(node->second, ax, ay + h1 + gap_v, aw, ah - h1 - gap_v, + gap_h, gap_v); + } +} + +static void dwindle_move_client(DwindleNode **root, Client *c, Client *target, + float ratio) { + if (!c || !target || c == target) + return; + if (!dwindle_find_leaf(*root, c) || !dwindle_find_leaf(*root, target)) + return; + dwindle_remove(root, c); + dwindle_insert(root, c, target, ratio); +} + +static void dwindle_swap_clients(DwindleNode **root, Client *a, Client *b) { + DwindleNode *la = dwindle_find_leaf(*root, a); + DwindleNode *lb = dwindle_find_leaf(*root, b); + if (!la || !lb || la == lb) + return; + la->client = b; + lb->client = a; +} + +static void dwindle_resize_client(Monitor *m, Client *c, int32_t dx, + int32_t dy) { + uint32_t tag = m->pertag->curtag; + DwindleNode *leaf = dwindle_find_leaf(m->pertag->dwindle_root[tag], c); + if (!leaf) + return; + + bool want_h = abs(dx) >= abs(dy); + + DwindleNode *node = leaf->parent; + while (node) { + if (node->split_h == want_h) { + float container_sz = want_h ? (float)MAX(1, node->container_w) + : (float)MAX(1, node->container_h); + + float delta = + want_h ? (float)dx / container_sz : (float)dy / container_sz; + + node->ratio += delta; + node->ratio = CLAMP_FLOAT(node->ratio, 0.05f, 0.95f); + + int32_t n = m->visible_tiling_clients; + int32_t gap_ih = enablegaps ? m->gappih : 0; + int32_t gap_iv = enablegaps ? m->gappiv : 0; + int32_t gap_oh = enablegaps ? m->gappoh : 0; + int32_t gap_ov = enablegaps ? m->gappov : 0; + if (config.smartgaps && n == 1) + gap_ih = gap_iv = gap_oh = gap_ov = 0; + dwindle_assign(m->pertag->dwindle_root[tag], m->w.x + gap_oh, + m->w.y + gap_ov, m->w.width - 2 * gap_oh, + m->w.height - 2 * gap_ov, gap_ih, gap_iv); + return; + } + node = node->parent; + } +} + +static void dwindle_remove_client(Client *c) { + Monitor *m; + wl_list_for_each(m, &mons, link) { + for (uint32_t t = 0; t < LENGTH(tags) + 1; t++) + dwindle_remove(&m->pertag->dwindle_root[t], c); + } +} + +void dwindle(Monitor *m) { + int32_t n = m->visible_tiling_clients; + if (n == 0) + return; + + uint32_t tag = m->pertag->curtag; + DwindleNode **root = &m->pertag->dwindle_root[tag]; + float ratio = m->pertag->mfacts[tag]; + + Client *vis[512]; + int32_t count = 0; + Client *c; + wl_list_for_each(c, &clients, link) { + if (VISIBLEON(c, m) && ISTILED(c)) + vis[count++] = c; + if (count >= 512) + break; + } + + { + DwindleNode *leaves[512]; + int32_t lc = 0; + + DwindleNode *stack[1024]; + int32_t sp = 0; + if (*root) + stack[sp++] = *root; + while (sp > 0) { + DwindleNode *nd = stack[--sp]; + if (!nd->is_split) { + leaves[lc++] = nd; + } else { + if (nd->second) + stack[sp++] = nd->second; + if (nd->first) + stack[sp++] = nd->first; + } + } + + for (int32_t i = 0; i < lc; i++) { + bool found = false; + for (int32_t j = 0; j < count; j++) + if (vis[j] == leaves[i]->client) { + found = true; + break; + } + if (!found) + dwindle_remove(root, leaves[i]->client); + } + } + + Client *focused = focustop(m); + if (focused && !dwindle_find_leaf(*root, focused)) + focused = m->sel; + for (int32_t i = 0; i < count; i++) { + if (!dwindle_find_leaf(*root, vis[i])) + dwindle_insert(root, vis[i], focused, ratio); + } + + int32_t gap_ih = enablegaps ? m->gappih : 0; + int32_t gap_iv = enablegaps ? m->gappiv : 0; + int32_t gap_oh = enablegaps ? m->gappoh : 0; + int32_t gap_ov = enablegaps ? m->gappov : 0; + if (config.smartgaps && n == 1) + gap_ih = gap_iv = gap_oh = gap_ov = 0; + + dwindle_assign(*root, m->w.x + gap_oh, m->w.y + gap_ov, + m->w.width - 2 * gap_oh, m->w.height - 2 * gap_ov, gap_ih, + gap_iv); +} diff --git a/src/layout/layout.h b/src/layout/layout.h index 15722f3c..90875340 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -13,6 +13,7 @@ static void vertical_scroller(Monitor *m); static void vertical_deck(Monitor *mon); static void tgmix(Monitor *m); static void canvas(Monitor *m); +static void dwindle(Monitor *m); /* layout(s) */ Layout overviewlayout = {"󰃇", overview, "overview"}; @@ -31,6 +32,7 @@ enum { RIGHT_TILE, TGMIX, CANVAS, + DWINDLE, }; Layout layouts[] = { @@ -50,4 +52,5 @@ Layout layouts[] = { {"VK", vertical_deck, "vertical_deck", VERTICAL_DECK}, // 垂直卡片布局 {"TG", tgmix, "tgmix", TGMIX}, // 混合布局 {"CV", canvas, "canvas", CANVAS}, // canvas layout + {"DW", dwindle, "dwindle", DWINDLE}, }; \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index ec4ec93f..0f0feacc 100644 --- a/src/mango.c +++ b/src/mango.c @@ -962,6 +962,8 @@ static struct { #include "client/client.h" #include "config/preset.h" +typedef struct DwindleNode DwindleNode; + struct Pertag { uint32_t curtag, prevtag; /* current and previous tag */ int32_t nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ @@ -974,6 +976,7 @@ struct Pertag { float canvas_pan_x[LENGTH(tags) + 1]; float canvas_pan_y[LENGTH(tags) + 1]; float canvas_zoom[LENGTH(tags) + 1]; /* visual zoom factor, 1.0 = no zoom */ + struct DwindleNode *dwindle_root[LENGTH(tags) + 1]; }; #include "config/parse_config.h" @@ -1047,6 +1050,7 @@ static void canvas_pan_to_client(Monitor *m, Client *c); #include "fetch/fetch.h" #include "layout/arrange.h" #include "layout/canvas.h" +#include "layout/dwindle.h" #include "layout/horizontal.h" #include "layout/vertical.h" @@ -2160,14 +2164,21 @@ void place_drag_tile_client(Client *c) { } } } - if (closest_client && closest_client->link.prev != &c->link) { - wl_list_remove(&c->link); - c->link.next = &closest_client->link; - c->link.prev = closest_client->link.prev; - closest_client->link.prev->next = &c->link; - closest_client->link.prev = &c->link; - } else if (closest_client) { - exchange_two_client(c, closest_client); + if (closest_client) { + if (c->mon && + c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == DWINDLE) { + uint32_t tag = c->mon->pertag->curtag; + dwindle_insert(&c->mon->pertag->dwindle_root[tag], c, + closest_client, c->mon->pertag->mfacts[tag]); + } else if (closest_client->link.prev != &c->link) { + wl_list_remove(&c->link); + c->link.next = &closest_client->link; + c->link.prev = closest_client->link.prev; + closest_client->link.prev->next = &c->link; + closest_client->link.prev = &c->link; + } else { + exchange_two_client(c, closest_client); + } } setfloating(c, 0); } @@ -4760,6 +4771,15 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx, last_apply_drap_time = time; } return; + } else if (grabc->mon && + grabc->mon->pertag->ltidxs[grabc->mon->pertag->curtag]->id == + DWINDLE) { + int32_t dx = (int32_t)round(cursor->x) - grabcx; + int32_t dy = (int32_t)round(cursor->y) - grabcy; + grabcx = (int32_t)round(cursor->x); + grabcy = (int32_t)round(cursor->y); + if (dx || dy) + dwindle_resize_client(grabc->mon, grabc, dx, dy); } else { resize_tile_client(grabc, true, 0, 0, time); } @@ -5771,10 +5791,20 @@ void exchange_two_client(Client *c1, Client *c2) { tmp_tags = c2->tags; setmon(c2, c1->mon, c1->tags, false); setmon(c1, tmp_mon, tmp_tags, false); + if (c1->mon && + c1->mon->pertag->ltidxs[c1->mon->pertag->curtag]->id == DWINDLE) + dwindle_swap_clients( + &c1->mon->pertag->dwindle_root[c1->mon->pertag->curtag], c1, + c2); arrange(c1->mon, false, false); arrange(c2->mon, false, false); focusclient(c1, 0); } else { + if (c1->mon && + c1->mon->pertag->ltidxs[c1->mon->pertag->curtag]->id == DWINDLE) + dwindle_swap_clients( + &c1->mon->pertag->dwindle_root[c1->mon->pertag->curtag], c1, + c2); arrange(c1->mon, false, false); } @@ -7017,6 +7047,7 @@ void unmapnotify(struct wl_listener *listener, void *data) { c->next_in_stack = NULL; c->prev_in_stack = NULL; + dwindle_remove_client(c); wlr_scene_node_destroy(&c->scene->node); printstatus(); motionnotify(0, NULL, 0, 0, 0, 0);