fix: fix the scrolling stack error

This commit is contained in:
DreamMaoMao 2026-01-17 11:52:40 +08:00
parent 535f41e76b
commit 810933cbf8
7 changed files with 193 additions and 225 deletions

View file

@ -205,7 +205,6 @@ typedef struct {
int32_t scroller_ignore_proportion_single;
int32_t scroller_focus_center;
int32_t scroller_prefer_center;
int32_t stacker_loop;
int32_t edge_scroller_pointer_focus;
int32_t focus_cross_monitor;
int32_t exchange_cross_monitor;
@ -1081,26 +1080,11 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
} else if (strcmp(func_name, "toggle_monitor") == 0) {
func = toggle_monitor;
(*arg).v = strdup(arg_value);
} else if (strcmp(func_name, "expand_client_left") == 0) {
func = expand_client_left;
if (arg_value && *arg_value) {
(*arg).f = atof(arg_value);
} else {
(*arg).f = 0.05;
}
} else if (strcmp(func_name, "collapse_client_right") == 0) {
func = collapse_client_right;
if (arg_value && *arg_value) {
(*arg).f = atof(arg_value);
} else {
(*arg).f = -0.05;
}
} else if (strcmp(func_name, "stack_with_left") == 0) {
func = stack_with_left;
} else if (strcmp(func_name, "unstack") == 0) {
func = unstack;
} else if (strcmp(func_name, "revert_size") == 0) {
func = revert_size;
} else if (strcmp(func_name, "scroller_stack") == 0) {
func = scroller_stack;
(*arg).i = parse_direction(arg_value);
} else if (strcmp(func_name, "scroller_unstack") == 0) {
func = scroller_unstack;
} else {
return NULL;
}
@ -1242,8 +1226,6 @@ void parse_option(Config *config, char *key, char *value) {
config->scroller_focus_center = atoi(value);
} else if (strcmp(key, "scroller_prefer_center") == 0) {
config->scroller_prefer_center = atoi(value);
} else if (strcmp(key, "stacker_loop") == 0) {
config->stacker_loop = atoi(value);
} else if (strcmp(key, "edge_scroller_pointer_focus") == 0) {
config->edge_scroller_pointer_focus = atoi(value);
} else if (strcmp(key, "focus_cross_monitor") == 0) {

View file

@ -65,7 +65,6 @@ float scroller_default_proportion_single = 1.0;
int32_t scroller_ignore_proportion_single = 0;
int32_t scroller_focus_center = 0;
int32_t scroller_prefer_center = 0;
int32_t stacker_loop = 1;
int32_t focus_cross_monitor = 0;
int32_t focus_cross_tag = 0;
int32_t exchange_cross_monitor = 0;

View file

@ -69,8 +69,5 @@ int32_t setoption(const Arg *arg);
int32_t disable_monitor(const Arg *arg);
int32_t enable_monitor(const Arg *arg);
int32_t toggle_monitor(const Arg *arg);
int32_t expand_client_left(const Arg *arg);
int32_t collapse_client_right(const Arg *arg);
int32_t stack_with_left(const Arg *arg);
int32_t unstack(const Arg *arg);
int32_t revert_size(const Arg *arg);
int32_t scroller_stack(const Arg *arg);
int32_t scroller_unstack(const Arg *arg);

View file

@ -107,7 +107,7 @@ int32_t exchange_client(const Arg *arg) {
if ((c->isfullscreen || c->ismaximizescreen) && !is_scroller_layout(c->mon))
return 0;
exchange_two_client(c, direction_select(arg));
exchange_two_client(c, direction_select(arg), false);
return 0;
}
@ -122,7 +122,7 @@ int32_t exchange_stack_client(const Arg *arg) {
tc = get_next_stack_client(c, true);
}
if (tc)
exchange_two_client(c, tc);
exchange_two_client(c, tc, false);
return 0;
}
@ -427,7 +427,8 @@ int32_t resizewin(const Arg *arg) {
if (ISTILED(c)) {
Client *target_client = c;
if (is_scroller_layout(c->mon) && (c->prev_in_stack || c->next_in_stack)) {
if (is_scroller_layout(c->mon) &&
(c->prev_in_stack || c->next_in_stack)) {
while (target_client->prev_in_stack) {
target_client = target_client->prev_in_stack;
}
@ -1592,143 +1593,69 @@ int32_t toggle_monitor(const Arg *arg) {
return 0;
}
int32_t expand_client_left(const Arg *arg) {
int32_t scroller_stack(const Arg *arg) {
Client *c = selmon->sel;
if (selmon && c && is_scroller_layout(selmon)) {
c->scroller_proportion += arg->f;
if (c->scroller_proportion > 1.0)
c->scroller_proportion = 1.0;
arrange(selmon, false, false);
} else {
setmfact(arg);
if (!c || c->isfloating || !is_scroller_layout(selmon))
return 0;
Client *left_c = find_client_by_direction(c, arg, false, true);
if (!left_c)
return 0;
if (c->isfullscreen) {
setfullscreen(c, 0);
}
if (c->ismaximizescreen) {
setmaximizescreen(c, 0);
}
exit_scroller_stack(c);
// Find the tail of left_c's stack
Client *stack_tail = left_c;
while (stack_tail->next_in_stack) {
stack_tail = stack_tail->next_in_stack;
}
// Add c to the stack
stack_tail->next_in_stack = c;
c->prev_in_stack = stack_tail;
c->next_in_stack = NULL;
arrange(selmon, false, false);
return 0;
}
int32_t collapse_client_right(const Arg *arg) {
int32_t scroller_unstack(const Arg *arg) {
Client *c = selmon->sel;
if (selmon && c && is_scroller_layout(selmon)) {
c->scroller_proportion += arg->f;
if (c->scroller_proportion < 0.1)
c->scroller_proportion = 0.1;
arrange(selmon, false, false);
} else {
setmfact(arg);
if (!c || !c->prev_in_stack) {
// Not in a stack or is the head of a stack, do nothing.
return 0;
}
Client *scroller_stack_head = c;
while (scroller_stack_head->prev_in_stack) {
scroller_stack_head = scroller_stack_head->prev_in_stack;
}
// Remove c from its current stack
if (c->prev_in_stack) {
c->prev_in_stack->next_in_stack = c->next_in_stack;
}
if (c->next_in_stack) {
c->next_in_stack->prev_in_stack = c->prev_in_stack;
}
c->next_in_stack = NULL;
c->prev_in_stack = NULL;
// Insert c after the stack it was in
wl_list_remove(&c->link);
wl_list_insert(&scroller_stack_head->link, &c->link);
focusclient(c, 1);
arrange(selmon, false, false);
return 0;
}
int32_t stack_with_left(const Arg *arg) {
Client *c = selmon->sel;
if (!c || c->isfloating || !is_scroller_layout(selmon))
return 0;
if (!config.stacker_loop) {
Client *first_tiled = NULL;
Client *iter_c = NULL;
wl_list_for_each(iter_c, &clients, link) {
if (ISTILED(iter_c) && VISIBLEON(iter_c, selmon)) {
first_tiled = iter_c;
break;
}
}
if (c == first_tiled) {
return 0; // It's the first client and loop is disabled, so do nothing.
}
}
Client *left_c = get_next_stack_client(c, true);
if (!left_c)
return 0;
// 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->next_in_stack) {
c->next_in_stack->prev_in_stack = c->prev_in_stack;
}
// If c was a stack head, its next client becomes the new head.
if (c->next_in_stack) {
c->next_in_stack->prev_in_stack = NULL;
}
// Find the tail of left_c's stack
Client *stack_tail = left_c;
while (stack_tail->next_in_stack) {
stack_tail = stack_tail->next_in_stack;
}
// Add c to the stack
stack_tail->next_in_stack = c;
c->prev_in_stack = stack_tail;
c->next_in_stack = NULL;
arrange(selmon, false, false);
return 0;
}
int32_t unstack(const Arg *arg) {
Client *c = selmon->sel;
if (!c || !c->prev_in_stack) {
// Not in a stack or is the head of a stack, do nothing.
return 0;
}
Client *stack_head = c;
while(stack_head->prev_in_stack) {
stack_head = stack_head->prev_in_stack;
}
// Remove c from its current stack
if (c->prev_in_stack) {
c->prev_in_stack->next_in_stack = c->next_in_stack;
}
if (c->next_in_stack) {
c->next_in_stack->prev_in_stack = c->prev_in_stack;
}
c->next_in_stack = NULL;
c->prev_in_stack = NULL;
// Insert c after the stack it was in
wl_list_remove(&c->link);
wl_list_insert(&stack_head->link, &c->link);
focusclient(c, 1);
arrange(selmon, false, false);
return 0;
}
int32_t revert_size(const Arg *arg) {
Client *c = selmon->sel;
if (!c) {
return 0;
}
// Ensure the client is not floating and its size is managed by the layout
if (c->isfloating) {
setfloating(c, false);
}
c->iscustomsize = 0; // Let the layout manage its size
// Explicitly remove the client from any stack it might be in
if (c->prev_in_stack) {
c->prev_in_stack->next_in_stack = c->next_in_stack;
}
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;
// Explicitly reset float_geom to ensure arrange recalculates geometry
c->float_geom = (struct wlr_box){0};
// The arrange function will now correctly size and position the window
// within the scroller layout, giving it full vertical size and preventing overlaps.
arrange(selmon, false, false);
return 0;
}
}

View file

@ -447,4 +447,12 @@ bool client_only_in_one_tag(Client *c) {
} else {
return false;
}
}
Client *get_scroll_stack_head(Client *c) {
Client *scroller_stack_head = c;
while (scroller_stack_head->prev_in_stack) {
scroller_stack_head = scroller_stack_head->prev_in_stack;
}
return scroller_stack_head;
}

View file

@ -212,31 +212,32 @@ void horizontal_scroll_adjust_fullandmax(Client *c,
target_geom->y = m->w.y + (m->w.height - target_geom->height) / 2;
}
void arrange_stack(Client *stack_head, struct wlr_box geometry, int32_t gappiv) {
int32_t stack_size = 0;
Client *iter = stack_head;
while (iter) {
stack_size++;
iter = iter->next_in_stack;
}
void arrange_stack(Client *scroller_stack_head, struct wlr_box geometry,
int32_t gappiv) {
int32_t stack_size = 0;
Client *iter = scroller_stack_head;
while (iter) {
stack_size++;
iter = iter->next_in_stack;
}
if (stack_size == 0) return;
if (stack_size == 0)
return;
int32_t client_height = (geometry.height - (stack_size - 1) * gappiv) / stack_size;
int32_t current_y = geometry.y;
int32_t client_height =
(geometry.height - (stack_size - 1) * gappiv) / stack_size;
int32_t current_y = geometry.y;
iter = stack_head;
while (iter) {
struct wlr_box client_geom = {
.x = geometry.x,
.y = current_y,
.width = geometry.width,
.height = client_height
};
resize(iter, client_geom, 0);
current_y += client_height + gappiv;
iter = iter->next_in_stack;
}
iter = scroller_stack_head;
while (iter) {
struct wlr_box client_geom = {.x = geometry.x,
.y = current_y,
.width = geometry.width,
.height = client_height};
resize(iter, client_geom, 0);
current_y += client_height + gappiv;
iter = iter->next_in_stack;
}
}
// 滚动布局
@ -252,7 +253,7 @@ void scroller(Monitor *m) {
int32_t cur_gappih = enablegaps ? m->gappih : 0;
int32_t cur_gappoh = enablegaps ? m->gappoh : 0;
int32_t cur_gappov = enablegaps ? m->gappov : 0;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
int32_t cur_gappiv = enablegaps ? m->gappiv : 0;
cur_gappih =
smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappih;
@ -311,13 +312,12 @@ void scroller(Monitor *m) {
root_client = center_tiled_select(m);
}
// root_client might be in a stack, find the stack head
if (root_client) {
while(root_client->prev_in_stack) {
root_client = root_client->prev_in_stack;
}
}
// root_client might be in a stack, find the stack head
if (root_client) {
while (root_client->prev_in_stack) {
root_client = root_client->prev_in_stack;
}
}
if (!root_client) {
free(tempClients); // 释放内存

View file

@ -627,7 +627,7 @@ static void motionrelative(struct wl_listener *listener, void *data);
static void reset_foreign_tolevel(Client *c);
static void remove_foreign_topleve(Client *c);
static void add_foreign_topleve(Client *c);
static void exchange_two_client(Client *c1, Client *c2);
static void exchange_two_client(Client *c1, Client *c2, bool samemon);
static void outputmgrapply(struct wl_listener *listener, void *data);
static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config,
int32_t test);
@ -764,6 +764,9 @@ static void init_client_properties(Client *c);
static float *get_border_color(Client *c);
static void clear_fullscreen_and_maximized_state(Monitor *m);
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);
#include "data/static_keymap.h"
#include "dispatch/bind_declare.h"
@ -1840,7 +1843,7 @@ void place_drag_tile_client(Client *c) {
closest_client->link.prev->next = &c->link;
closest_client->link.prev = &c->link;
} else if (closest_client) {
exchange_two_client(c, closest_client);
exchange_two_client(c, closest_client, false);
}
setfloating(c, 0);
}
@ -3640,25 +3643,6 @@ void keypressmod(struct wl_listener *listener, void *data) {
void pending_kill_client(Client *c) {
if (!c || c->iskilling)
return;
// If the client is in a stack, remove it from the stack
if (c->prev_in_stack || c->next_in_stack) {
if (c->prev_in_stack) {
c->prev_in_stack->next_in_stack = c->next_in_stack;
}
if (c->next_in_stack) {
c->next_in_stack->prev_in_stack = c->prev_in_stack;
if (!c->prev_in_stack) { // c was the head of the stack
// The next client becomes the new head, so we need to ensure it's treated as such.
// This is implicitly handled by setting its prev_in_stack to NULL.
}
}
c->prev_in_stack = NULL;
c->next_in_stack = NULL;
arrange(c->mon, false, false);
}
c->iskilling = 1;
client_send_close(c);
}
@ -3845,7 +3829,7 @@ mapnotify(struct wl_listener *listener, void *data) {
if (selmon->sel && ISSCROLLTILED(selmon->sel) &&
VISIBLEON(selmon->sel, selmon)) {
at_client = selmon->sel;
at_client = get_scroll_stack_head(selmon->sel);
} else {
at_client = center_tiled_select(selmon);
}
@ -4368,7 +4352,7 @@ void setborder_color(Client *c) {
client_set_border_color(c, border_color);
}
void exchange_two_client(Client *c1, Client *c2) {
void exchange_two_client(Client *c1, Client *c2, bool samemon) {
Monitor *tmp_mon = NULL;
uint32_t tmp_tags;
@ -4381,6 +4365,10 @@ void exchange_two_client(Client *c1, Client *c2) {
return;
}
if (samemon && c1->mon != c2->mon)
return;
// 交换布局参数
master_inner_per = c1->master_inner_per;
master_mfact_per = c1->master_mfact_per;
stack_inner_per = c1->stack_inner_per;
@ -4393,17 +4381,46 @@ void exchange_two_client(Client *c1, Client *c2) {
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;
// 处理相邻节点的情况
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 (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, true);
focusclient(c1, 0);
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;
// wl_list
// 是双向链表,其中clients是头部节点,它的下一个节点是第一个客户端的链表节点
// 最后一个客户端的链表节点的下一个节点也指向clients,但clients本身不是客户端的链表节点
// 客户端遍历从clients的下一个节点开始,到检测到客户端节点的下一个是clients结束
// 当c1和c2为相邻节点时
// 处理相邻节点的情况
if (c1->link.next == &c2->link) {
c1->link.next = c2->link.next;
c1->link.prev = &c2->link;
@ -4430,6 +4447,7 @@ void exchange_two_client(Client *c1, Client *c2) {
tmp2_next->prev = &c1->link;
}
// 处理跨监视器交换
if (exchange_cross_monitor) {
tmp_mon = c2->mon;
tmp_tags = c2->tags;
@ -4440,7 +4458,6 @@ void exchange_two_client(Client *c1, Client *c2) {
focusclient(c1, 0);
} else {
arrange(c1->mon, false, false);
focusclient(c1, 0);
}
}
@ -4631,6 +4648,19 @@ void reset_maximizescreen_size(Client *c) {
resize(c, c->geom, 0);
}
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->next_in_stack) {
c->next_in_stack->prev_in_stack = c->prev_in_stack;
}
c->prev_in_stack = NULL;
c->next_in_stack = NULL;
}
void setmaximizescreen(Client *c, int32_t maximizescreen) {
struct wlr_box maximizescreen_box;
if (!c || !c->mon || !client_surface(c)->mapped || c->iskilling)
@ -4646,6 +4676,8 @@ void setmaximizescreen(Client *c, int32_t maximizescreen) {
if (c->isfullscreen)
setfullscreen(c, 0);
exit_scroller_stack(c);
if (c->isfloating)
c->float_geom = c->geom;
@ -4706,6 +4738,8 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自
if (c->ismaximizescreen)
setmaximizescreen(c, 0);
exit_scroller_stack(c);
if (c->isfloating)
c->float_geom = c->geom;
@ -5426,12 +5460,27 @@ 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;
if (animations && !c->is_clip_to_hide && !c->isminimized &&
(!c->mon || VISIBLEON(c, c->mon)))
init_fadeout_client(c);
// If the client is in a stack, remove it from the stack
if (c->prev_in_stack || c->next_in_stack) {
if (c->prev_in_stack) {
c->prev_in_stack->next_in_stack = c->next_in_stack;
}
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;
}
if (c->swallowedby) {
c->swallowedby->mon = c->mon;
swallow(c->swallowedby, c);
@ -5455,7 +5504,13 @@ void unmapnotify(struct wl_listener *listener, void *data) {
}
if (c->mon && c->mon == selmon) {
Client *nextfocus = focustop(selmon);
if (next_in_stack) {
nextfocus = next_in_stack;
} else if (prev_in_stack) {
nextfocus = prev_in_stack;
} else {
nextfocus = focustop(selmon);
}
if (nextfocus) {
focusclient(nextfocus, 0);