opt: redo scroller and dwindle layout

This commit is contained in:
DreamMaoMao 2026-05-10 19:07:10 +08:00
parent cabafb2393
commit d0c5b1ccdc
8 changed files with 1352 additions and 1081 deletions

View file

@ -114,7 +114,9 @@
(A && !(A)->isfloating && !(A)->isminimized && !(A)->iskilling && \
!(A)->isunglobal)
#define VISIBLEON(C, M) \
((C) && (M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]))
((C) && (M) && (C)->mon == (M) && \
(((C)->tags & (M)->tagset[(M)->seltags] || (C)->isglobal || \
(C)->isunglobal)))
#define LENGTH(X) (sizeof X / sizeof X[0])
#define END(A) ((A) + LENGTH(A))
#define TAGMASK ((1 << LENGTH(tags)) - 1)
@ -421,8 +423,6 @@ struct Client {
int32_t allow_shortcuts_inhibit;
float scroller_proportion_single;
bool isfocusing;
struct Client *next_in_stack;
struct Client *prev_in_stack;
bool enable_drop_area_draw;
int32_t drop_direction;
struct wlr_box drag_tile_float_backup_geom;
@ -561,6 +561,21 @@ typedef struct {
} SessionLock;
typedef struct DwindleNode DwindleNode;
struct ScrollerStackNode {
Client *client;
float scroller_proportion;
float stack_proportion;
float scroller_proportion_single;
struct ScrollerStackNode *next_in_stack;
struct ScrollerStackNode *prev_in_stack;
struct ScrollerStackNode *all_next;
};
struct TagScrollerState {
struct ScrollerStackNode *all_first; /* 所有节点的单链表头 */
int count;
};
/* function declarations */
static void applybounds(
@ -798,7 +813,7 @@ static void request_fresh_all_monitors(void);
static Client *find_client_by_direction(Client *tc, const Arg *arg,
bool findfloating, bool ignore_align);
static void exit_scroller_stack(Client *c);
static Client *get_scroll_stack_head(Client *c);
static Client *scroll_get_stack_head_client(Client *c);
static bool client_only_in_one_tag(Client *c);
static Client *get_focused_stack_client(Client *sc);
static bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc);
@ -823,6 +838,16 @@ static void dwindle_resize_client_step(Monitor *m, Client *c, int32_t dx,
int32_t dy);
static void dwindle_resize_client(Monitor *m, Client *c);
static struct TagScrollerState *ensure_scroller_state(Monitor *m, uint32_t tag);
static struct ScrollerStackNode *find_scroller_node(struct TagScrollerState *st,
Client *c);
static void sync_scroller_state_to_clients(Monitor *m, uint32_t tag);
static void scroller_node_remove(struct TagScrollerState *st,
struct ScrollerStackNode *target);
static struct ScrollerStackNode *
scroller_node_create(struct TagScrollerState *st, Client *c);
static void update_scroller_state(Monitor *m);
#include "data/static_keymap.h"
#include "dispatch/bind_declare.h"
#include "layout/layout.h"
@ -945,19 +970,17 @@ static struct {
#include "client/client.h"
#include "config/preset.h"
struct Pertag {
uint32_t curtag, prevtag; /* current and previous tag */
int32_t nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
int32_t no_hide[LENGTH(tags) + 1]; /* no_hide per tag */
int32_t no_render_border[LENGTH(tags) + 1]; /* no_render_border per tag */
int32_t open_as_floating[LENGTH(tags) + 1]; /* open_as_floating per tag */
uint32_t curtag, prevtag;
int32_t nmasters[LENGTH(tags) + 1];
float mfacts[LENGTH(tags) + 1];
int32_t no_hide[LENGTH(tags) + 1];
int32_t no_render_border[LENGTH(tags) + 1];
int32_t open_as_floating[LENGTH(tags) + 1];
struct DwindleNode *dwindle_root[LENGTH(tags) + 1];
const Layout
*ltidxs[LENGTH(tags) + 1]; /* matrix of tags and layouts indexes */
const Layout *ltidxs[LENGTH(tags) + 1];
struct TagScrollerState *scroller_state[LENGTH(tags) + 1];
};
#include "config/parse_config.h"
static struct wl_signal mango_print_status;
@ -1028,6 +1051,7 @@ static struct wl_event_source *sync_keymap;
#include "layout/arrange.h"
#include "layout/dwindle.h"
#include "layout/horizontal.h"
#include "layout/scroll.h"
#include "layout/vertical.h"
void client_change_mon(Client *c, Monitor *m) {
@ -1156,15 +1180,12 @@ void swallow(Client *c, Client *w) {
c->master_inner_per = w->master_inner_per;
c->master_mfact_per = w->master_mfact_per;
c->scroller_proportion = w->scroller_proportion;
c->next_in_stack = w->next_in_stack;
c->prev_in_stack = w->prev_in_stack;
c->isglobal = w->isglobal;
if (w->next_in_stack)
w->next_in_stack->prev_in_stack = c;
if (w->prev_in_stack)
w->prev_in_stack->next_in_stack = c;
/* 调整 w 的邻居指针,让它们指向 c */
c->stack_proportion = w->stack_proportion;
/* 全局链表替换 */
wl_list_insert(&w->link, &c->link);
wl_list_insert(&w->flink, &c->flink);
@ -1190,16 +1211,39 @@ void swallow(Client *c, Client *w) {
client_pending_maximized_state(c, w->ismaximizescreen);
client_pending_minimized_state(c, w->isminimized);
/* ---------- 跨 tag 同步dwindle 与 scroller ---------- */
Monitor *m;
wl_list_for_each(m, &mons, link) {
for (uint32_t t = 0; t < LENGTH(tags) + 1; t++) {
/* dwindle */
DwindleNode **root = &m->pertag->dwindle_root[t];
dwindle_remove(root, c);
DwindleNode *wn = dwindle_find_leaf(*root, w);
DwindleNode *dnode = dwindle_find_leaf(*root, w);
if (dnode)
dnode->client = c;
/* scroller */
struct TagScrollerState *st = m->pertag->scroller_state[t];
if (!st)
continue;
/* 先移除 c 在任意 tag 中的旧节点 */
struct ScrollerStackNode *cn = find_scroller_node(st, c);
if (cn)
scroller_node_remove(st, cn);
/* 将 w 的节点(如果存在)转给 c */
struct ScrollerStackNode *wn = find_scroller_node(st, w);
if (wn)
wn->client = c;
}
}
/* 同步当前活动 tag 的全局客户端字段 */
if (c->mon) {
uint32_t curtag = c->mon->pertag->curtag;
sync_scroller_state_to_clients(c->mon, curtag);
}
}
bool switch_scratchpad_client_state(Client *c) {
@ -2107,100 +2151,6 @@ Client *find_closest_tiled_client(Client *c) {
return closest;
}
void scroller_insert_stack(Client *c, Client *target_client,
bool insert_before) {
Client *stack_head = NULL;
if (!target_client || target_client->mon != c->mon) {
return;
} else {
c->isglobal = target_client->isglobal = 0;
c->isunglobal = target_client->isunglobal = 0;
c->tags = target_client->tags = get_tags_first_tag(target_client->tags);
}
if (c->isfullscreen) {
setfullscreen(c, 0);
}
if (c->ismaximizescreen) {
setmaximizescreen(c, 0);
}
exit_scroller_stack(c);
stack_head = get_scroll_stack_head(target_client);
if (insert_before) {
if (target_client->prev_in_stack) {
target_client->prev_in_stack->next_in_stack = c;
}
c->prev_in_stack = target_client->prev_in_stack;
c->next_in_stack = target_client;
target_client->prev_in_stack = c;
} else {
if (target_client->next_in_stack) {
target_client->next_in_stack->prev_in_stack = c;
}
c->next_in_stack = target_client->next_in_stack;
c->prev_in_stack = target_client;
target_client->next_in_stack = c;
}
if (stack_head->ismaximizescreen) {
setmaximizescreen(stack_head, 0);
}
if (stack_head->isfullscreen) {
setfullscreen(stack_head, 0);
}
arrange(c->mon, false, false);
return;
}
void try_scroller_drop(Client *c, Client *closest, int vertical) {
Client *stack_head = get_scroll_stack_head(closest);
if (vertical) {
if (closest->drop_direction == LEFT) {
setfloating(c, 0);
scroller_insert_stack(c, closest, true);
return;
} else if (closest->drop_direction == RIGHT) {
setfloating(c, 0);
scroller_insert_stack(c, closest, false);
return;
} else if (closest->drop_direction == UP) {
wl_list_remove(&c->link);
wl_list_insert(stack_head->link.prev, &c->link);
} else if (closest->drop_direction == DOWN) {
wl_list_remove(&c->link);
wl_list_insert(&stack_head->link, &c->link);
}
} else {
if (closest->drop_direction == UP) {
setfloating(c, 0);
scroller_insert_stack(c, closest, true);
return;
} else if (closest->drop_direction == DOWN) {
setfloating(c, 0);
scroller_insert_stack(c, closest, false);
return;
} else if (closest->drop_direction == LEFT) {
wl_list_remove(&c->link);
wl_list_insert(stack_head->link.prev, &c->link);
} else if (closest->drop_direction == RIGHT) {
wl_list_remove(&c->link);
wl_list_insert(&stack_head->link, &c->link);
}
}
setfloating(c, 0);
}
void place_drag_tile_client(Client *c) {
Client *closest = find_closest_tiled_client(c);
@ -2215,11 +2165,11 @@ void place_drag_tile_client(Client *c) {
}
if (layout->id == SCROLLER) {
try_scroller_drop(c, closest, 0);
scroller_drop_tile(c, closest, 0);
return;
}
if (layout->id == VERTICAL_SCROLLER) {
try_scroller_drop(c, closest, 1);
scroller_drop_tile(c, closest, 1);
return;
}
if (layout->id == DWINDLE) {
@ -2550,6 +2500,9 @@ void cleanupmon(struct wl_listener *listener, void *data) {
for (uint32_t t = 0; t < LENGTH(tags) + 1; t++)
dwindle_free_tree(m->pertag->dwindle_root[t]);
cleanup_monitor_scroller(m);
free(m->pertag);
free(m);
}
@ -3292,7 +3245,11 @@ void createmon(struct wl_listener *listener, void *data) {
wlr_output_state_finish(&state);
wl_list_insert(&mons, &m->link);
m->pertag = calloc(1, sizeof(Pertag));
for (int i = 0; i < LENGTH(tags) + 1; i++)
m->pertag->scroller_state[i] = NULL;
if (chvt_backup_tag &&
regex_match(chvt_backup_selmon, m->wlr_output->name)) {
m->tagset[0] = m->tagset[1] = (1 << (chvt_backup_tag - 1)) & TAGMASK;
@ -4327,8 +4284,6 @@ void init_client_properties(Client *c) {
c->float_geom.x = 0;
c->float_geom.y = 0;
c->stack_proportion = 0.0f;
c->next_in_stack = NULL;
c->prev_in_stack = NULL;
memset(c->oldmonname, 0, sizeof(c->oldmonname));
memcpy(c->opacity_animation.initial_border_color, config.bordercolor,
sizeof(c->opacity_animation.initial_border_color));
@ -4423,7 +4378,7 @@ mapnotify(struct wl_listener *listener, void *data) {
if (selmon->sel && ISSCROLLTILED(selmon->sel) &&
VISIBLEON(selmon->sel, selmon)) {
at_client = get_scroll_stack_head(selmon->sel);
at_client = scroll_get_stack_head_client(selmon->sel);
} else {
at_client = center_tiled_select(selmon);
}
@ -5027,91 +4982,137 @@ void setborder_color(Client *c) {
}
void exchange_two_client(Client *c1, Client *c2) {
Monitor *tmp_mon = NULL;
uint32_t tmp_tags;
double master_inner_per = 0.0f;
double master_mfact_per = 0.0f;
double stack_inner_per = 0.0f;
float scroller_proportion = 0.0f;
float stack_proportion = 0.0f;
struct ScrollerStackNode *n1 = NULL;
struct ScrollerStackNode *n2 = NULL;
struct TagScrollerState *st1 = NULL;
struct TagScrollerState *st2 = NULL;
if (c1 == NULL || c2 == NULL ||
(!config.exchange_cross_monitor && c1->mon != c2->mon)) {
return;
}
if (c1->mon != c2->mon && (c1->prev_in_stack || c2->prev_in_stack ||
c1->next_in_stack || c2->next_in_stack))
return;
Client *c1head = get_scroll_stack_head(c1);
Client *c2head = get_scroll_stack_head(c2);
// 交换布局参数
if (c1head == c2head) {
scroller_proportion = c1->scroller_proportion;
stack_proportion = c1->stack_proportion;
c1->scroller_proportion = c2->scroller_proportion;
c1->stack_proportion = c2->stack_proportion;
c2->scroller_proportion = scroller_proportion;
c2->stack_proportion = stack_proportion;
}
master_inner_per = c1->master_inner_per;
master_mfact_per = c1->master_mfact_per;
stack_inner_per = c1->stack_inner_per;
c1->master_inner_per = c2->master_inner_per;
c1->master_mfact_per = c2->master_mfact_per;
c1->stack_inner_per = c2->stack_inner_per;
c2->master_inner_per = master_inner_per;
c2->master_mfact_per = master_mfact_per;
c2->stack_inner_per = stack_inner_per;
// 交换栈链表连接
Client *tmp1_next_in_stack = c1->next_in_stack;
Client *tmp1_prev_in_stack = c1->prev_in_stack;
Client *tmp2_next_in_stack = c2->next_in_stack;
Client *tmp2_prev_in_stack = c2->prev_in_stack;
bool c1_scroller = c1->mon && is_scroller_layout(c1->mon);
bool c2_scroller = c2->mon && is_scroller_layout(c2->mon);
Monitor *m1 = c1->mon;
Monitor *m2 = c2->mon;
uint32_t tag1 = m1->pertag->curtag;
uint32_t tag2 = m2->pertag->curtag;
// 处理相邻节点的情况
if (c1->next_in_stack == c2) {
c1->next_in_stack = tmp2_next_in_stack;
c2->next_in_stack = c1;
c1->prev_in_stack = c2;
c2->prev_in_stack = tmp1_prev_in_stack;
if (tmp1_prev_in_stack)
tmp1_prev_in_stack->next_in_stack = c2;
if (tmp2_next_in_stack)
tmp2_next_in_stack->prev_in_stack = c1;
} else if (c2->next_in_stack == c1) {
c2->next_in_stack = tmp1_next_in_stack;
c1->next_in_stack = c2;
c2->prev_in_stack = c1;
c1->prev_in_stack = tmp2_prev_in_stack;
if (tmp2_prev_in_stack)
tmp2_prev_in_stack->next_in_stack = c1;
if (tmp1_next_in_stack)
tmp1_next_in_stack->prev_in_stack = c2;
} else if (is_scroller_layout(c1->mon) &&
(c1->prev_in_stack || c2->prev_in_stack)) {
Client *c1head = get_scroll_stack_head(c1);
Client *c2head = get_scroll_stack_head(c2);
exchange_two_client(c1head, c2head);
focusclient(c1, 0);
return;
if (c1_scroller) {
st1 = ensure_scroller_state(m1, tag1);
n1 = find_scroller_node(st1, c1);
}
// 交换全局链表连接
if (c2_scroller) {
st2 = ensure_scroller_state(m2, tag2);
n2 = find_scroller_node(st2, c2);
}
if (!n1 || !n2)
goto exchange_common;
if (n1 && n2) {
/* 跨显示器且任一方有堆叠关系时不允许交换 */
if (m1 != m2 && (n1->prev_in_stack || n2->prev_in_stack ||
n1->next_in_stack || n2->next_in_stack))
return;
/* 获取各自的堆叠头节点 */
struct ScrollerStackNode *head1 = n1;
while (head1->prev_in_stack)
head1 = head1->prev_in_stack;
struct ScrollerStackNode *head2 = n2;
while (head2->prev_in_stack)
head2 = head2->prev_in_stack;
/* 同一堆叠内交换 */
if (head1 == head2) {
float tmp_scroller = n1->scroller_proportion;
float tmp_stack = n1->stack_proportion;
n1->scroller_proportion = n2->scroller_proportion;
n1->stack_proportion = n2->stack_proportion;
n2->scroller_proportion = tmp_scroller;
n2->stack_proportion = tmp_stack;
/* 交换堆叠链表指针 */
struct ScrollerStackNode *p1 = n1->prev_in_stack;
struct ScrollerStackNode *next1 = n1->next_in_stack;
struct ScrollerStackNode *p2 = n2->prev_in_stack;
struct ScrollerStackNode *next2 = n2->next_in_stack;
if (n1->next_in_stack == n2) {
n1->next_in_stack = next2;
n2->prev_in_stack = p1;
n1->prev_in_stack = n2;
n2->next_in_stack = n1;
if (p1)
p1->next_in_stack = n2;
if (next2)
next2->prev_in_stack = n1;
} else if (n2->next_in_stack == n1) {
n2->next_in_stack = next1;
n1->prev_in_stack = p2;
n2->prev_in_stack = n1;
n1->next_in_stack = n2;
if (p2)
p2->next_in_stack = n1;
if (next1)
next1->prev_in_stack = n2;
} else {
if (p1)
p1->next_in_stack = n2;
if (next1)
next1->prev_in_stack = n2;
if (p2)
p2->next_in_stack = n1;
if (next2)
next2->prev_in_stack = n1;
n1->prev_in_stack = p2;
n1->next_in_stack = next2;
n2->prev_in_stack = p1;
n2->next_in_stack = next1;
}
sync_scroller_state_to_clients(m1, tag1);
} else {
/* 不同堆叠:交换两个堆叠整体位置 */
if (n1 != head1 || n2 != head2) {
/* 当前不是头部,递归交换头部 */
exchange_two_client(head1->client, head2->client);
return;
}
}
}
exchange_common:
/* 跨显示器且任一方有堆叠关系时不允许交换 */
if (m1 != m2 && ((n1 && n1->prev_in_stack) || (n2 && n2->prev_in_stack) ||
(n1 && n1->next_in_stack) || (n2 && n2->next_in_stack)))
return;
struct wl_list *tmp1_prev = c1->link.prev;
struct wl_list *tmp2_prev = c2->link.prev;
struct wl_list *tmp1_next = c1->link.next;
struct wl_list *tmp2_next = c2->link.next;
// 处理相邻节点的情况
if (c1->link.next == &c2->link) {
c1->link.next = c2->link.next;
c1->link.prev = &c2->link;
@ -5126,39 +5127,47 @@ void exchange_two_client(Client *c1, Client *c2) {
c1->link.prev = tmp2_prev;
tmp2_prev->next = &c1->link;
tmp1_next->prev = &c2->link;
} else { // 不为相邻节点
} else {
c2->link.next = tmp1_next;
c2->link.prev = tmp1_prev;
c1->link.next = tmp2_next;
c1->link.prev = tmp2_prev;
tmp1_prev->next = &c2->link;
tmp1_next->prev = &c2->link;
tmp2_prev->next = &c1->link;
tmp2_next->prev = &c1->link;
}
// 处理跨监视器交换
if (config.exchange_cross_monitor) {
DwindleNode **c1_root = &m1->pertag->dwindle_root[m1->pertag->curtag];
DwindleNode *c1node = dwindle_find_leaf(*c1_root, c1);
DwindleNode **c2_root = &m2->pertag->dwindle_root[m2->pertag->curtag];
DwindleNode *c2node = dwindle_find_leaf(*c2_root, c2);
if (c1node)
c1node->client = c2;
if (c2node)
c2node->client = c1;
tmp_mon = c2->mon;
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);
c2->mon = c1->mon;
c1->mon = tmp_mon;
c2->tags = c1->tags;
c1->tags = tmp_tags;
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)
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(c1->mon, false, false);
}
}
// In order to facilitate repeated exchanges for get_focused_stack_client
@ -5427,24 +5436,18 @@ void reset_maximizescreen_size(Client *c) {
}
void exit_scroller_stack(Client *c) {
// If c is already in a stack, remove it.
if (c->prev_in_stack) {
c->prev_in_stack->next_in_stack = c->next_in_stack;
}
if (!c || !c->mon)
return;
if (!c->prev_in_stack && c->next_in_stack) {
c->next_in_stack->scroller_proportion = c->scroller_proportion;
wl_list_remove(&c->next_in_stack->link);
wl_list_insert(&c->link, &c->next_in_stack->link);
uint32_t tag = c->mon->pertag->curtag;
struct TagScrollerState *st = c->mon->pertag->scroller_state[tag];
if (st) {
struct ScrollerStackNode *n = find_scroller_node(st, c);
if (n) {
scroller_node_remove(st, n);
return; /* 节点已移除,客户端指针已在函数内清空 */
}
}
if (c->next_in_stack) {
c->next_in_stack->prev_in_stack = c->prev_in_stack;
}
c->prev_in_stack = NULL;
c->next_in_stack = NULL;
c->stack_proportion = 0.0f;
}
void setmaximizescreen(Client *c, int32_t maximizescreen) {
@ -6105,7 +6108,6 @@ void tag_client(const Arg *arg, Client *target_client) {
Client *fc = NULL;
if (target_client && arg->ui & TAGMASK) {
exit_scroller_stack(target_client);
target_client->tags = arg->ui & TAGMASK;
target_client->istagswitching = 1;
@ -6300,9 +6302,15 @@ void unmapnotify(struct wl_listener *listener, void *data) {
Client *c = wl_container_of(listener, c, unmap);
Monitor *m = NULL;
Client *nextfocus = NULL;
Client *next_in_stack = c->next_in_stack;
Client *prev_in_stack = c->prev_in_stack;
c->iskilling = 1;
struct ScrollerStackNode *target_node =
c->mon ? find_scroller_node(
c->mon->pertag->scroller_state[c->mon->pertag->curtag], c)
: NULL;
struct ScrollerStackNode *prev_node =
target_node ? target_node->prev_in_stack : NULL;
struct ScrollerStackNode *next_node =
target_node ? target_node->next_in_stack : NULL;
if (config.animations && !c->is_clip_to_hide && !c->isminimized &&
(!c->mon || VISIBLEON(c, c->mon)))
@ -6314,7 +6322,8 @@ void unmapnotify(struct wl_listener *listener, void *data) {
c->swallowedby->mon = c->mon;
swallow(c->swallowedby, c);
} else {
exit_scroller_stack(c);
scroller_remove_client(c);
dwindle_remove_client(c);
}
if (c == grabc) {
@ -6339,10 +6348,10 @@ void unmapnotify(struct wl_listener *listener, void *data) {
}
if (c->mon && c->mon == selmon) {
if (next_in_stack && !c->swallowedby) {
nextfocus = next_in_stack;
} else if (prev_in_stack && !c->swallowedby) {
nextfocus = prev_in_stack;
if (next_node && !c->swallowedby) {
nextfocus = next_node->client;
} else if (prev_node && !c->swallowedby) {
nextfocus = prev_node->client;
} else {
nextfocus = focustop(selmon);
}
@ -6393,10 +6402,7 @@ void unmapnotify(struct wl_listener *listener, void *data) {
}
c->stack_proportion = 0.0f;
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);