feat: working but not 100% polished and ready dwindle implementation

This commit is contained in:
ernestoCruz05 2026-04-05 03:13:35 +01:00
parent 179ae4995c
commit 0ea1abc275
5 changed files with 328 additions and 10 deletions

View file

@ -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;

View file

@ -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;

271
src/layout/dwindle.h Normal file
View file

@ -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);
}

View file

@ -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},
};

View file

@ -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);