diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 3b5f08a1..8ad166a4 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -672,7 +672,6 @@ uint32_t parse_mod(const char *mod_str) { } } } else { - // 完整的 modifier 检查(保留原始所有检查项) if (!strcmp(token, "super") || !strcmp(token, "super_l") || !strcmp(token, "super_r")) { mod |= WLR_MODIFIER_LOGO; diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 2563786d..22ac3e78 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -630,15 +630,106 @@ int32_t set_proportion(const Arg *arg) { return 0; Client *tc = selmon->sel; + if (!tc) + return 0; - if (tc) { - tc = get_scroll_stack_head(tc); - uint32_t max_client_width = - selmon->w.width - 2 * config.scroller_structs - config.gappih; - tc->scroller_proportion = arg->f; - tc->geom.width = max_client_width * arg->f; - arrange(selmon, false, false); + /* 获取堆叠头部客户端 */ + tc = scroll_get_stack_head_client(tc); + if (!tc) + return 0; + + Monitor *m = tc->mon; + uint32_t tag = m->pertag->curtag; + struct TagScrollerState *st = m->pertag->scroller_state[tag]; + struct ScrollerStackNode *node = NULL; + + if (st) + node = find_scroller_node(st, tc); + + /* 同时更新节点和客户端字段 */ + if (node) + node->scroller_proportion = arg->f; + tc->scroller_proportion = arg->f; + + /* 可选的即时几何更新,arrange 时会重新计算 */ + uint32_t max_client_width = + m->w.width - 2 * config.scroller_structs - config.gappih; + tc->geom.width = max_client_width * arg->f; + + arrange(m, false, false); + return 0; +} + +int32_t switch_proportion_preset(const Arg *arg) { + float target_proportion = 0; + if (!selmon) + return 0; + + if (config.scroller_proportion_preset_count == 0) + return 0; + + if (selmon->isoverview || !is_scroller_layout(selmon)) + return 0; + + if (selmon->visible_tiling_clients == 1 && + !config.scroller_ignore_proportion_single) + return 0; + + Client *tc = selmon->sel; + if (!tc) + return 0; + + tc = scroll_get_stack_head_client(tc); + if (!tc) + return 0; + + Monitor *m = tc->mon; + uint32_t tag = m->pertag->curtag; + struct TagScrollerState *st = m->pertag->scroller_state[tag]; + struct ScrollerStackNode *node = NULL; + + if (st) + node = find_scroller_node(st, tc); + + /* 优先从节点读取当前比例,以确保切换基于正确的值 */ + float current_proportion = + node ? node->scroller_proportion : tc->scroller_proportion; + + /* 查找预设目标 */ + for (int32_t i = 0; i < config.scroller_proportion_preset_count; i++) { + if (config.scroller_proportion_preset[i] == current_proportion) { + if (arg->i == NEXT) { + if (i == config.scroller_proportion_preset_count - 1) + target_proportion = config.scroller_proportion_preset[0]; + else + target_proportion = + config.scroller_proportion_preset[i + 1]; + } else { + if (i == 0) + target_proportion = + config.scroller_proportion_preset + [config.scroller_proportion_preset_count - 1]; + else + target_proportion = + config.scroller_proportion_preset[i - 1]; + } + break; + } } + + if (target_proportion == 0.0f) + target_proportion = config.scroller_proportion_preset[0]; + + /* 更新节点和客户端 */ + if (node) + node->scroller_proportion = target_proportion; + tc->scroller_proportion = target_proportion; + + uint32_t max_client_width = + m->w.width - 2 * config.scroller_structs - config.gappih; + tc->geom.width = max_client_width * target_proportion; + + arrange(m, false, false); return 0; } @@ -836,7 +927,7 @@ int32_t centerwin(const Arg *arg) { if (!is_scroller_layout(selmon)) return 0; - Client *stack_head = get_scroll_stack_head(c); + Client *stack_head = scroll_get_stack_head_client(c); if (selmon->pertag->ltidxs[selmon->pertag->curtag]->id == SCROLLER) { stack_head->geom.x = selmon->w.x + (selmon->w.width - stack_head->geom.width) / 2; @@ -1043,68 +1134,6 @@ int32_t switch_layout(const Arg *arg) { return 0; } -int32_t switch_proportion_preset(const Arg *arg) { - float target_proportion = 0; - if (!selmon) - return 0; - - if (config.scroller_proportion_preset_count == 0) { - return 0; - } - - if (selmon->isoverview || !is_scroller_layout(selmon)) - return 0; - - if (selmon->visible_tiling_clients == 1 && - !config.scroller_ignore_proportion_single) - return 0; - - Client *tc = selmon->sel; - - if (tc) { - tc = get_scroll_stack_head(tc); - for (int32_t i = 0; i < config.scroller_proportion_preset_count; i++) { - if (config.scroller_proportion_preset[i] == - tc->scroller_proportion) { - - if (arg->i == NEXT) { - if (i == config.scroller_proportion_preset_count - 1) { - target_proportion = - config.scroller_proportion_preset[0]; - break; - } else { - target_proportion = - config.scroller_proportion_preset[i + 1]; - break; - } - } else { - if (i == 0) { - target_proportion = - config.scroller_proportion_preset - [config.scroller_proportion_preset_count - 1]; - break; - } else { - target_proportion = - config.scroller_proportion_preset[i - 1]; - break; - } - } - } - } - - if (target_proportion == 0) { - target_proportion = config.scroller_proportion_preset[0]; - } - - uint32_t max_client_width = - selmon->w.width - 2 * config.scroller_structs - config.gappih; - tc->scroller_proportion = target_proportion; - tc->geom.width = max_client_width * target_proportion; - arrange(selmon, false, false); - } - return 0; -} - int32_t tag(const Arg *arg) { if (!selmon) return 0; @@ -1203,7 +1232,6 @@ int32_t tagsilent(const Arg *arg) { clear_fullscreen_flag(fc); } } - exit_scroller_stack(target_client); focusclient(focustop(selmon), 1); arrange(target_client->mon, false, false); return 0; @@ -1354,11 +1382,6 @@ int32_t toggleglobal(const Arg *arg) { selmon->sel->isnamedscratchpad = 0; } selmon->sel->isglobal ^= 1; - if (selmon->sel->isglobal && - (selmon->sel->prev_in_stack || selmon->sel->next_in_stack)) { - exit_scroller_stack(selmon->sel); - arrange(selmon, false, false); - } setborder_color(selmon->sel); return 0; } @@ -1781,76 +1804,69 @@ int32_t toggle_monitor(const Arg *arg) { int32_t scroller_apply_stack(Client *c, Client *target_client, int32_t direction) { + if (!c || !c->mon || c->isfloating || !is_scroller_layout(c->mon)) + return 0; - Client *source_stack_head = NULL; - Client *stack_head = NULL; - bool is_horizontal_layout = - c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == SCROLLER ? true - : false; + Monitor *m = c->mon; + uint32_t tag = m->pertag->curtag; + struct TagScrollerState *st = ensure_scroller_state(m, tag); - if (target_client) { - stack_head = get_scroll_stack_head(target_client); - } + /* 获取当前节点 */ + struct ScrollerStackNode *cnode = find_scroller_node(st, c); + struct ScrollerStackNode *tnode = + target_client ? find_scroller_node(st, target_client) : NULL; - source_stack_head = get_scroll_stack_head(c); + bool is_horizontal = (m->pertag->ltidxs[tag]->id == SCROLLER); - if (source_stack_head == stack_head) { + /* 若方向为 UNDIR 且有目标,直接插入到目标尾部 */ + if (direction == UNDIR && target_client && target_client->mon == c->mon) { + scroller_insert_stack(c, target_client, false); return 0; } - if (c->isfullscreen) { - setfullscreen(c, 0); - } + /* 处理从堆叠中移出的情况(方向 LEFT/UP 或 RIGHT/DOWN) */ + if (cnode && (cnode->prev_in_stack || cnode->next_in_stack)) { + bool to_left_or_up = (is_horizontal && direction == LEFT) || + (!is_horizontal && direction == UP); + bool to_right_or_down = (is_horizontal && direction == RIGHT) || + (!is_horizontal && direction == DOWN); - if (c->ismaximizescreen) { - setmaximizescreen(c, 0); - } + if (to_left_or_up || to_right_or_down) { + /* 找到当前堆叠的头节点,以便移动全局链表位置 */ + struct ScrollerStackNode *head = cnode; + while (head->prev_in_stack) + head = head->prev_in_stack; + Client *source_stack_head = head->client; - if (c->prev_in_stack && direction != UNDIR) { - if ((is_horizontal_layout && direction == LEFT) || - (!is_horizontal_layout && direction == UP)) { - exit_scroller_stack(c); + /* 从 tag 状态中移除该客户端对应的节点 */ + scroller_node_remove(st, cnode); + /* 重新创建一个独立的节点(无堆叠关系) */ + scroller_node_create(st, c); + + /* 调整全局客户端链表顺序:移到源堆叠头的前面或后面 */ wl_list_remove(&c->link); - wl_list_insert(source_stack_head->link.prev, &c->link); - arrange(selmon, false, false); + if (to_left_or_up) + wl_list_insert(source_stack_head->link.prev, &c->link); + else + wl_list_insert(&source_stack_head->link, &c->link); - } else if ((is_horizontal_layout && direction == RIGHT) || - (!is_horizontal_layout && direction == DOWN)) { - exit_scroller_stack(c); - wl_list_remove(&c->link); - wl_list_insert(&source_stack_head->link, &c->link); - arrange(selmon, false, false); + /* 同步到客户端字段并重排 */ + sync_scroller_state_to_clients(m, tag); + arrange(m, false, false); + return 0; } - return 0; - } else if (c->next_in_stack && direction != UNDIR) { - Client *next_in_stack = c->next_in_stack; - if ((is_horizontal_layout && direction == LEFT) || - (!is_horizontal_layout && direction == UP)) { - exit_scroller_stack(c); - wl_list_remove(&c->link); - wl_list_insert(next_in_stack->link.prev, &c->link); - arrange(selmon, false, false); - } else if ((is_horizontal_layout && direction == RIGHT) || - (!is_horizontal_layout && direction == DOWN)) { - exit_scroller_stack(c); - wl_list_remove(&c->link); - wl_list_insert(&next_in_stack->link, &c->link); - arrange(selmon, false, false); - } - return 0; } - if (!target_client || target_client->mon != c->mon) { + if (!target_client || target_client->mon != c->mon) return 0; - } - // Find the tail of target_client's stack - Client *stack_tail = target_client; - while (stack_tail->next_in_stack) { - stack_tail = stack_tail->next_in_stack; - } + /* 找到目标堆叠的尾部节点 */ + struct ScrollerStackNode *tail = tnode; + while (tail->next_in_stack) + tail = tail->next_in_stack; - scroller_insert_stack(c, stack_tail, false); + /* 通过封装好的插入函数实现(尾部插入) */ + scroller_insert_stack(c, tail->client, false); return 0; } diff --git a/src/fetch/client.h b/src/fetch/client.h index be9be420..524efa2a 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -538,18 +538,6 @@ bool client_only_in_one_tag(Client *c) { } } -Client *get_scroll_stack_head(Client *c) { - Client *scroller_stack_head = c; - - if (!scroller_stack_head) - return NULL; - - while (scroller_stack_head->prev_in_stack) { - scroller_stack_head = scroller_stack_head->prev_in_stack; - } - return scroller_stack_head; -} - bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) { if (!sc || !tc) return false; @@ -562,10 +550,11 @@ bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) { return false; if (id == SCROLLER || id == VERTICAL_SCROLLER) { - Client *source_stack_head = get_scroll_stack_head(sc); - Client *target_stack_head = get_scroll_stack_head(tc); - Client *fc_head = fc ? get_scroll_stack_head(fc) : NULL; - if (fc && fc->prev_in_stack && fc_head == source_stack_head) + Client *source_stack_head = scroll_get_stack_head_client(sc); + Client *target_stack_head = scroll_get_stack_head_client(tc); + Client *fc_head = fc ? scroll_get_stack_head_client(fc) : NULL; + + if (fc && fc_head == source_stack_head) return false; if (source_stack_head == target_stack_head) return true; diff --git a/src/layout/arrange.h b/src/layout/arrange.h index b822a729..56fda657 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -505,36 +505,51 @@ void resize_tile_dwindle(Client *grabc, bool isdrag, int32_t offsetx, void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx, int32_t offsety, uint32_t time, bool isvertical) { - Client *tc = NULL; + if (!grabc || grabc->isfullscreen || grabc->ismaximizescreen) + return; + if (grabc->mon->isoverview) + return; + + Monitor *m = grabc->mon; + uint32_t tag = m->pertag->curtag; + struct TagScrollerState *st = m->pertag->scroller_state[tag]; + if (!st) + return; + + struct ScrollerStackNode *curnode = find_scroller_node(st, grabc); + if (!curnode) + return; + + struct ScrollerStackNode *headnode = curnode; + while (headnode->prev_in_stack) + headnode = headnode->prev_in_stack; + + Client *stack_head_client = headnode->client; + + if (m->visible_tiling_clients == 1 && + !config.scroller_ignore_proportion_single) + return; + float delta_x, delta_y; float new_scroller_proportion; float new_stack_proportion; - Client *stack_head = get_scroll_stack_head(grabc); - - if (grabc && grabc->mon->visible_tiling_clients == 1 && - !config.scroller_ignore_proportion_single) - return; if (!start_drag_window && isdrag) { drag_begin_cursorx = cursor->x; drag_begin_cursory = cursor->y; start_drag_window = true; - // 记录初始状态 - stack_head->old_scroller_pproportion = stack_head->scroller_proportion; - grabc->old_stack_proportion = grabc->stack_proportion; + headnode->client->old_scroller_pproportion = + headnode->scroller_proportion; + grabc->old_stack_proportion = curnode->stack_proportion; grabc->cursor_in_left_half = cursor->x < grabc->geom.x + grabc->geom.width / 2; grabc->cursor_in_upper_half = cursor->y < grabc->geom.y + grabc->geom.height / 2; - // 记录初始几何信息 grabc->drag_begin_geom = grabc->geom; } else { - // 计算相对于屏幕尺寸的比例变化 - // 计算相对于屏幕尺寸的比例变化 if (isdrag) { - offsetx = cursor->x - drag_begin_cursorx; offsety = cursor->y - drag_begin_cursory; } else { @@ -542,37 +557,33 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx, grabc->old_master_inner_per = grabc->master_inner_per; grabc->old_stack_inner_per = grabc->stack_inner_per; grabc->drag_begin_geom = grabc->geom; - stack_head->old_scroller_pproportion = - stack_head->scroller_proportion; - grabc->old_stack_proportion = grabc->stack_proportion; + stack_head_client->old_scroller_pproportion = + headnode->scroller_proportion; + grabc->old_stack_proportion = curnode->stack_proportion; grabc->cursor_in_upper_half = false; grabc->cursor_in_left_half = false; } if (isvertical) { delta_y = (float)(offsety) * - (stack_head->old_scroller_pproportion) / + (headnode->client->old_scroller_pproportion) / grabc->drag_begin_geom.height; delta_x = (float)(offsetx) * (grabc->old_stack_proportion) / grabc->drag_begin_geom.width; } else { delta_x = (float)(offsetx) * - (stack_head->old_scroller_pproportion) / + (headnode->client->old_scroller_pproportion) / grabc->drag_begin_geom.width; delta_y = (float)(offsety) * (grabc->old_stack_proportion) / grabc->drag_begin_geom.height; } - bool moving_up; - bool moving_down; - bool moving_left; - bool moving_right; - + bool moving_up, moving_down, moving_left, moving_right; if (!isdrag) { - moving_up = offsety < 0 ? true : false; - moving_down = offsety > 0 ? true : false; - moving_left = offsetx < 0 ? true : false; - moving_right = offsetx > 0 ? true : false; + moving_up = offsety < 0; + moving_down = offsety > 0; + moving_left = offsetx < 0; + moving_right = offsetx > 0; } else { moving_up = cursor->y < drag_begin_cursory; moving_down = cursor->y > drag_begin_cursory; @@ -582,10 +593,8 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx, if ((grabc->cursor_in_upper_half && moving_up) || (!grabc->cursor_in_upper_half && moving_down)) { - // 光标在窗口上方且向上移动,或在窗口下方且向下移动 → 增加高度 delta_y = fabsf(delta_y); } else { - // 其他情况 → 减小高度 delta_y = -fabsf(delta_y); } @@ -597,99 +606,93 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx, } if (isvertical) { - if (!grabc->next_in_stack && grabc->prev_in_stack && !isdrag) { + if (!curnode->next_in_stack && curnode->prev_in_stack && !isdrag) { delta_x = delta_x * -1.0f; } - if (!grabc->next_in_stack && grabc->prev_in_stack && isdrag) { - if (moving_right) { + if (!curnode->next_in_stack && curnode->prev_in_stack && isdrag) { + if (moving_right) delta_x = -fabsf(delta_x); - } else { + else delta_x = fabsf(delta_x); - } } - if (!grabc->prev_in_stack && grabc->next_in_stack && isdrag) { - if (moving_left) { + if (!curnode->prev_in_stack && curnode->next_in_stack && isdrag) { + if (moving_left) delta_x = -fabsf(delta_x); - } else { + else delta_x = fabsf(delta_x); - } } - if (isdrag) { - if (moving_up) { + if (moving_up) delta_y = -fabsf(delta_y); - } else { + else delta_y = fabsf(delta_y); - } } - } else { - if (!grabc->next_in_stack && grabc->prev_in_stack && !isdrag) { + if (!curnode->next_in_stack && curnode->prev_in_stack && !isdrag) { delta_y = delta_y * -1.0f; } - if (!grabc->next_in_stack && grabc->prev_in_stack && isdrag) { - if (moving_down) { + if (!curnode->next_in_stack && curnode->prev_in_stack && isdrag) { + if (moving_down) delta_y = -fabsf(delta_y); - } else { + else delta_y = fabsf(delta_y); - } } - if (!grabc->prev_in_stack && grabc->next_in_stack && isdrag) { - if (moving_up) { + if (!curnode->prev_in_stack && curnode->next_in_stack && isdrag) { + if (moving_up) delta_y = -fabsf(delta_y); - } else { + else delta_y = fabsf(delta_y); - } } - if (isdrag) { - if (moving_left) { + if (moving_left) delta_x = -fabsf(delta_x); - } else { + else delta_x = fabsf(delta_x); - } } } - // 直接设置新的比例,基于初始值 + 变化量 if (isvertical) { new_scroller_proportion = - stack_head->old_scroller_pproportion + delta_y; + headnode->client->old_scroller_pproportion + delta_y; new_stack_proportion = grabc->old_stack_proportion + delta_x; - } else { new_scroller_proportion = - stack_head->old_scroller_pproportion + delta_x; + headnode->client->old_scroller_pproportion + delta_x; new_stack_proportion = grabc->old_stack_proportion + delta_y; } - // 应用限制,确保比例在合理范围内 new_scroller_proportion = fmaxf(0.1f, fminf(1.0f, new_scroller_proportion)); new_stack_proportion = fmaxf(0.1f, fminf(0.9f, new_stack_proportion)); - grabc->stack_proportion = new_stack_proportion; + curnode->stack_proportion = new_stack_proportion; + headnode->scroller_proportion = new_scroller_proportion; - stack_head->scroller_proportion = new_scroller_proportion; - - wl_list_for_each(tc, &clients, link) { - if (!isdrag && new_stack_proportion != 1.0f && - grabc->old_stack_proportion != 1.0f && tc != grabc && - ISTILED(tc) && get_scroll_stack_head(tc) == stack_head) { - tc->stack_proportion = (1.0f - new_stack_proportion) / - (1.0f - grabc->old_stack_proportion) * - tc->stack_proportion; + /* 调整同一堆叠内其他节点的 stack_proportion */ + /* 调整整个堆叠内除当前窗口外的所有节点 */ + if (!isdrag && grabc->old_stack_proportion != 1.0f) { + for (struct ScrollerStackNode *tc = headnode; tc; + tc = tc->next_in_stack) { + if (tc != curnode) { + tc->stack_proportion = + (1.0f - new_stack_proportion) / + (1.0f - grabc->old_stack_proportion) * + tc->stack_proportion; + } } } + /* 同步回全局字段*/ + sync_scroller_state_to_clients(m, tag); + if (!isdrag) { - arrange(grabc->mon, false, false); + arrange(m, false, false); return; } if (last_apply_drap_time == 0 || time - last_apply_drap_time > config.drag_tile_refresh_interval) { - arrange(grabc->mon, false, false); + arrange(m, false, false); last_apply_drap_time = time; } } @@ -834,11 +837,15 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation, m->visible_tiling_clients = 0; m->visible_scroll_tiling_clients = 0; - wl_list_for_each(c, &clients, link) { + uint32_t tag = m->pertag->curtag; + struct TagScrollerState *st = m->pertag->scroller_state[tag]; - if (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal) { - exit_scroller_stack(c); - } + const Layout *cur_layout = m->pertag->ltidxs[m->pertag->curtag]; + if (cur_layout->id == SCROLLER || cur_layout->id == VERTICAL_SCROLLER) { + update_scroller_state(m); + } + + wl_list_for_each(c, &clients, link) { if (from_view && (c->isglobal || c->isunglobal)) { set_size_per(m, c); @@ -862,10 +869,15 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation, if (ISTILED(c)) { m->visible_tiling_clients++; - } - if (ISSCROLLTILED(c) && !c->prev_in_stack) { - m->visible_scroll_tiling_clients++; + /* 更新可见滚动客户端计数 */ + if (st) { + struct ScrollerStackNode *n = find_scroller_node(st, c); + if (n && !n->prev_in_stack) /* 是堆叠头部 */ + m->visible_scroll_tiling_clients++; + } else if (ISSCROLLTILED(c)) { + m->visible_scroll_tiling_clients++; + } } } } @@ -879,7 +891,6 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation, if (c->mon == m) { if (VISIBLEON(c, m)) { if (ISTILED(c)) { - if (i < nmasters) { master_num++; total_master_inner_percent += c->master_inner_per; @@ -898,7 +909,6 @@ void pre_caculate_before_arrange(Monitor *m, bool want_animation, c->stack_inner_per; } } - i++; } diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index e69b4cdd..37c8793e 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -184,325 +184,6 @@ void deck(Monitor *m) { } } -void horizontal_scroll_adjust_fullandmax(Client *c, - struct wlr_box *target_geom) { - Monitor *m = c->mon; - int32_t cur_gappih = enablegaps ? m->gappih : 0; - int32_t cur_gappoh = enablegaps ? m->gappoh : 0; - int32_t cur_gappov = enablegaps ? m->gappov : 0; - - cur_gappih = config.smartgaps && m->visible_scroll_tiling_clients == 1 - ? 0 - : cur_gappih; - cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1 - ? 0 - : cur_gappoh; - cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1 - ? 0 - : cur_gappov; - - if (c->isfullscreen) { - target_geom->height = m->m.height; - target_geom->width = m->m.width; - target_geom->y = m->m.y; - return; - } - - if (c->ismaximizescreen) { - target_geom->height = m->w.height - 2 * cur_gappov; - target_geom->width = m->w.width - 2 * cur_gappoh; - target_geom->y = m->w.y + cur_gappov; - return; - } - - target_geom->height = m->w.height - 2 * cur_gappov; - target_geom->y = m->w.y + (m->w.height - target_geom->height) / 2; -} - -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; - - float total_proportion = 0.0f; - iter = scroller_stack_head; - while (iter) { - if (iter->stack_proportion <= 0.0f || iter->stack_proportion >= 1.0f) { - iter->stack_proportion = - stack_size == 1 ? 1.0f : 1.0f / (stack_size - 1); - } - total_proportion += iter->stack_proportion; - iter = iter->next_in_stack; - } - - iter = scroller_stack_head; - while (iter) { - iter->stack_proportion = iter->stack_proportion / total_proportion; - iter = iter->next_in_stack; - } - - int32_t client_height; - int32_t current_y = geometry.y; - int32_t remain_client_height = geometry.height - (stack_size - 1) * gappiv; - float remain_proportion = 1.0f; - - iter = scroller_stack_head; - while (iter) { - - client_height = - remain_client_height * (iter->stack_proportion / remain_proportion); - - struct wlr_box client_geom = {.x = geometry.x, - .y = current_y, - .width = geometry.width, - .height = client_height}; - resize(iter, client_geom, 0); - remain_proportion -= iter->stack_proportion; - remain_client_height -= client_height; - current_y += client_height + gappiv; - iter = iter->next_in_stack; - } -} - -void horizontal_check_scroller_root_inside_mon(Client *c, - struct wlr_box *geometry) { - if (!GEOMINSIDEMON(geometry, c->mon)) { - geometry->x = c->mon->w.x + (c->mon->w.width - geometry->width) / 2; - } -} - -// 滚动布局 -void scroller(Monitor *m) { - int32_t i, n, j; - float single_proportion = 1.0; - - Client *c = NULL, *root_client = NULL; - Client **tempClients = NULL; // 初始化为 NULL - struct wlr_box target_geom; - int32_t focus_client_index = 0; - bool need_scroller = false; - bool over_overspread_to_left = false; - 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; - - cur_gappih = config.smartgaps && m->visible_scroll_tiling_clients == 1 - ? 0 - : cur_gappih; - cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1 - ? 0 - : cur_gappoh; - cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1 - ? 0 - : cur_gappov; - - int32_t max_client_width = - m->w.width - 2 * config.scroller_structs - cur_gappih; - - n = m->visible_scroll_tiling_clients; - - if (n == 0) { - return; // 没有需要处理的客户端,直接返回 - } - - // 动态分配内存 - tempClients = malloc(n * sizeof(Client *)); - if (!tempClients) { - // 处理内存分配失败的情况 - return; - } - - // 第二次遍历,填充 tempClients - j = 0; - wl_list_for_each(c, &clients, link) { - if (VISIBLEON(c, m) && ISSCROLLTILED(c) && !c->prev_in_stack) { - tempClients[j] = c; - j++; - } - } - - if (n == 1 && !config.scroller_ignore_proportion_single && - !tempClients[0]->isfullscreen && !tempClients[0]->ismaximizescreen) { - c = tempClients[0]; - - single_proportion = c->scroller_proportion_single > 0.0f - ? c->scroller_proportion_single - : config.scroller_default_proportion_single; - - target_geom.height = m->w.height - 2 * cur_gappov; - target_geom.width = (m->w.width - 2 * cur_gappoh) * single_proportion; - target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; - target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; - horizontal_check_scroller_root_inside_mon(c, &target_geom); - arrange_stack(c, target_geom, cur_gappiv); - free(tempClients); // 释放内存 - return; - } - - if (m->sel && !client_is_unmanaged(m->sel) && ISSCROLLTILED(m->sel)) { - root_client = m->sel; - } else if (m->prevsel && ISSCROLLTILED(m->prevsel) && - VISIBLEON(m->prevsel, m) && !client_is_unmanaged(m->prevsel)) { - root_client = m->prevsel; - } else { - root_client = center_tiled_select(m); - } - - // root_client might be in a stack, find the stack head - if (root_client) { - root_client = get_scroll_stack_head(root_client); - } - - if (!root_client) { - free(tempClients); // 释放内存 - return; - } - - for (i = 0; i < n; i++) { - c = tempClients[i]; - if (root_client == c) { - if (c->geom.x >= m->w.x + config.scroller_structs && - c->geom.x + c->geom.width <= - m->w.x + m->w.width - config.scroller_structs) { - need_scroller = false; - } else { - need_scroller = true; - } - focus_client_index = i; - break; - } - } - - bool need_apply_overspread = - config.scroller_prefer_overspread && - m->visible_scroll_tiling_clients > 1 && - (focus_client_index == 0 || focus_client_index == n - 1) && - tempClients[focus_client_index]->scroller_proportion < 1.0f; - - if (need_apply_overspread) { - - if (focus_client_index == 0) { - over_overspread_to_left = true; - } else { - over_overspread_to_left = false; - } - - if (over_overspread_to_left && - (!INSIDEMON(tempClients[1]) || - (tempClients[1]->scroller_proportion + - tempClients[focus_client_index]->scroller_proportion >= - 1.0f))) { - need_scroller = true; - } else if (!over_overspread_to_left && - (!INSIDEMON(tempClients[focus_client_index - 1]) || - (tempClients[focus_client_index - 1]->scroller_proportion + - tempClients[focus_client_index]->scroller_proportion >= - 1.0f))) { - need_scroller = true; - } else { - need_apply_overspread = false; - } - } - - bool need_apply_center = - config.scroller_focus_center || m->visible_scroll_tiling_clients == 1 || - (config.scroller_prefer_center && !need_apply_overspread && - (!m->prevsel || - (ISSCROLLTILED(m->prevsel) && - (m->prevsel->scroller_proportion * max_client_width) + - (tempClients[focus_client_index]->scroller_proportion * - max_client_width) > - m->w.width - 2 * config.scroller_structs - cur_gappih))); - - if (n == 1 && config.scroller_ignore_proportion_single) { - need_scroller = true; - } - - if (start_drag_window) - need_scroller = false; - - target_geom.height = m->w.height - 2 * cur_gappov; - target_geom.width = max_client_width * c->scroller_proportion; - target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; - horizontal_scroll_adjust_fullandmax(tempClients[focus_client_index], - &target_geom); - if (tempClients[focus_client_index]->isfullscreen) { - target_geom.x = m->m.x; - horizontal_check_scroller_root_inside_mon( - tempClients[focus_client_index], &target_geom); - arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv); - } else if (tempClients[focus_client_index]->ismaximizescreen) { - target_geom.x = m->w.x + cur_gappoh; - horizontal_check_scroller_root_inside_mon( - tempClients[focus_client_index], &target_geom); - arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv); - } else if (need_scroller) { - if (need_apply_center) { - target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; - } else if (need_apply_overspread) { - if (over_overspread_to_left) { - target_geom.x = m->w.x + config.scroller_structs; - } else { - target_geom.x = - m->w.x + - (m->w.width - - tempClients[focus_client_index]->scroller_proportion * - max_client_width - - config.scroller_structs); - } - - } else { - target_geom.x = tempClients[focus_client_index]->geom.x > - m->w.x + (m->w.width) / 2 - ? m->w.x + (m->w.width - - tempClients[focus_client_index] - ->scroller_proportion * - max_client_width - - config.scroller_structs) - : m->w.x + config.scroller_structs; - } - horizontal_check_scroller_root_inside_mon( - tempClients[focus_client_index], &target_geom); - arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv); - } else { - target_geom.x = c->geom.x; - horizontal_check_scroller_root_inside_mon( - tempClients[focus_client_index], &target_geom); - arrange_stack(tempClients[focus_client_index], target_geom, cur_gappiv); - } - - for (i = 1; i <= focus_client_index; i++) { - c = tempClients[focus_client_index - i]; - target_geom.width = max_client_width * c->scroller_proportion; - horizontal_scroll_adjust_fullandmax(c, &target_geom); - target_geom.x = tempClients[focus_client_index - i + 1]->geom.x - - cur_gappih - target_geom.width; - - arrange_stack(c, target_geom, cur_gappiv); - } - - for (i = 1; i < n - focus_client_index; i++) { - c = tempClients[focus_client_index + i]; - target_geom.width = max_client_width * c->scroller_proportion; - horizontal_scroll_adjust_fullandmax(c, &target_geom); - target_geom.x = tempClients[focus_client_index + i - 1]->geom.x + - cur_gappih + - tempClients[focus_client_index + i - 1]->geom.width; - arrange_stack(c, target_geom, cur_gappiv); - } - - free(tempClients); // 最后释放内存 -} - void center_tile(Monitor *m) { int32_t i, n = 0, h, r, ie = enablegaps, mw, mx, my, oty, ety, tw; Client *c = NULL; diff --git a/src/layout/scroll.h b/src/layout/scroll.h new file mode 100644 index 00000000..84e84e2c --- /dev/null +++ b/src/layout/scroll.h @@ -0,0 +1,888 @@ +/* 获取或创建指定 monitor 某个 tag 的 scroller 状态 */ +static struct TagScrollerState *ensure_scroller_state(Monitor *m, + uint32_t tag) { + if (!m->pertag->scroller_state[tag]) { + struct TagScrollerState *st = + calloc(1, sizeof(struct TagScrollerState)); + m->pertag->scroller_state[tag] = st; + } + return m->pertag->scroller_state[tag]; +} + +/* 在 tag 状态中查找客户端对应的节点(无则返回 NULL) */ +static struct ScrollerStackNode *find_scroller_node(struct TagScrollerState *st, + Client *c) { + if (!st) + return NULL; + for (struct ScrollerStackNode *n = st->all_first; n; n = n->all_next) + if (n->client == c) + return n; + return NULL; +} + +/* 创建一个新节点并插入到 tag 状态的 all 链表中 */ +static struct ScrollerStackNode * +scroller_node_create(struct TagScrollerState *st, Client *c) { + struct ScrollerStackNode *n = calloc(1, sizeof(*n)); + n->client = c; + n->scroller_proportion = c->scroller_proportion; + n->stack_proportion = c->stack_proportion; + n->scroller_proportion_single = c->scroller_proportion_single; + n->next_in_stack = NULL; + n->prev_in_stack = NULL; + n->all_next = st->all_first; + st->all_first = n; + st->count++; + return n; +} + +/* 从 tag 状态中移除一个节点并释放 */ +static void scroller_node_remove(struct TagScrollerState *st, + struct ScrollerStackNode *target) { + if (!st || !target) + return; + + /* 保存邻居 */ + struct ScrollerStackNode *prev = target->prev_in_stack; + struct ScrollerStackNode *next = target->next_in_stack; + + /* 从堆叠链表中摘除 */ + if (prev) + prev->next_in_stack = next; + if (next) + next->prev_in_stack = prev; + + /* 从 all 链表摘除 */ + struct ScrollerStackNode **indirect = &st->all_first; + while (*indirect && *indirect != target) + indirect = &(*indirect)->all_next; + if (*indirect == target) { + *indirect = target->all_next; + st->count--; + } + free(target); +} + +/* 清空一个 tag 的全部 scroller 状态 */ +static void clear_scroller_state(struct TagScrollerState *st) { + if (!st) + return; + struct ScrollerStackNode *n = st->all_first; + while (n) { + struct ScrollerStackNode *next = n->all_next; + free(n); + n = next; + } + free(st); +} + +/* 在 Monitor 销毁时清理所有 tag 的 scroller 状态 */ +static void cleanup_monitor_scroller(Monitor *m) { + for (int t = 0; t < LENGTH(tags) + 1; t++) { + if (m->pertag->scroller_state[t]) { + clear_scroller_state(m->pertag->scroller_state[t]); + m->pertag->scroller_state[t] = NULL; + } + } +} + +/* 将某个 tag 的状态同步回所有客户端的全局字段 */ +static void sync_scroller_state_to_clients(Monitor *m, uint32_t tag) { + struct TagScrollerState *st = m->pertag->scroller_state[tag]; + if (!st) + return; + for (struct ScrollerStackNode *n = st->all_first; n; n = n->all_next) { + Client *c = n->client; + c->scroller_proportion = n->scroller_proportion; + c->stack_proportion = n->stack_proportion; + c->scroller_proportion_single = n->scroller_proportion_single; + } +} + +void vertical_scroll_adjust_fullandmax(Client *c, struct wlr_box *target_geom) { + Monitor *m = c->mon; + int32_t cur_gappiv = enablegaps ? m->gappiv : 0; + int32_t cur_gappov = enablegaps ? m->gappov : 0; + int32_t cur_gappoh = enablegaps ? m->gappoh : 0; + + cur_gappiv = config.smartgaps && m->visible_scroll_tiling_clients == 1 + ? 0 + : cur_gappiv; + cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1 + ? 0 + : cur_gappov; + cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1 + ? 0 + : cur_gappoh; + + if (c->isfullscreen) { + target_geom->width = m->m.width; + target_geom->height = m->m.height; + target_geom->x = m->m.x; + return; + } + + if (c->ismaximizescreen) { + target_geom->width = m->w.width - 2 * cur_gappoh; + target_geom->height = m->w.height - 2 * cur_gappov; + target_geom->x = m->w.x + cur_gappoh; + return; + } + + target_geom->width = m->w.width - 2 * cur_gappoh; + target_geom->x = m->w.x + (m->w.width - target_geom->width) / 2; +} + +void vertical_check_scroller_root_inside_mon(Client *c, + struct wlr_box *geometry) { + if (!GEOMINSIDEMON(geometry, c->mon)) { + geometry->y = c->mon->w.y + (c->mon->w.height - geometry->height) / 2; + } +} + +void horizontal_scroll_adjust_fullandmax(Client *c, + struct wlr_box *target_geom) { + Monitor *m = c->mon; + int32_t cur_gappih = enablegaps ? m->gappih : 0; + int32_t cur_gappoh = enablegaps ? m->gappoh : 0; + int32_t cur_gappov = enablegaps ? m->gappov : 0; + + cur_gappih = config.smartgaps && m->visible_scroll_tiling_clients == 1 + ? 0 + : cur_gappih; + cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1 + ? 0 + : cur_gappoh; + cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1 + ? 0 + : cur_gappov; + + if (c->isfullscreen) { + target_geom->height = m->m.height; + target_geom->width = m->m.width; + target_geom->y = m->m.y; + return; + } + + if (c->ismaximizescreen) { + target_geom->height = m->w.height - 2 * cur_gappov; + target_geom->width = m->w.width - 2 * cur_gappoh; + target_geom->y = m->w.y + cur_gappov; + return; + } + + target_geom->height = m->w.height - 2 * cur_gappov; + target_geom->y = m->w.y + (m->w.height - target_geom->height) / 2; +} + +void horizontal_check_scroller_root_inside_mon(Client *c, + struct wlr_box *geometry) { + if (!GEOMINSIDEMON(geometry, c->mon)) { + geometry->x = c->mon->w.x + (c->mon->w.width - geometry->width) / 2; + } +} + +void arrange_stack_node(struct ScrollerStackNode *head, struct wlr_box geometry, + int32_t gappiv) { + int32_t stack_size = 0; + struct ScrollerStackNode *iter = head; + while (iter) { + stack_size++; + iter = iter->next_in_stack; + } + if (stack_size == 0) + return; + + /* 归一化比例 */ + float total_proportion = 0.0f; + iter = head; + while (iter) { + if (iter->stack_proportion <= 0.0f || iter->stack_proportion >= 1.0f) + iter->stack_proportion = + stack_size == 1 ? 1.0f : 1.0f / (stack_size - 1); + total_proportion += iter->stack_proportion; + iter = iter->next_in_stack; + } + iter = head; + while (iter) { + iter->stack_proportion /= total_proportion; + iter = iter->next_in_stack; + } + + /* 竖向排列(水平堆叠) */ + int32_t client_height; + int32_t current_y = geometry.y; + int32_t remain_client_height = geometry.height - (stack_size - 1) * gappiv; + float remain_proportion = 1.0f; + + iter = head; + while (iter) { + client_height = + remain_client_height * (iter->stack_proportion / remain_proportion); + struct wlr_box client_geom = {.x = geometry.x, + .y = current_y, + .width = geometry.width, + .height = client_height}; + resize(iter->client, client_geom, 0); + remain_proportion -= iter->stack_proportion; + remain_client_height -= client_height; + current_y += client_height + gappiv; + iter = iter->next_in_stack; + } +} + +void arrange_stack_vertical_node(struct ScrollerStackNode *head, + struct wlr_box geometry, int32_t gappih) { + int32_t stack_size = 0; + struct ScrollerStackNode *iter = head; + while (iter) { + stack_size++; + iter = iter->next_in_stack; + } + if (stack_size == 0) + return; + + /* 归一化比例 */ + float total_proportion = 0.0f; + iter = head; + while (iter) { + if (iter->stack_proportion <= 0.0f || iter->stack_proportion >= 1.0f) + iter->stack_proportion = + stack_size == 1 ? 1.0f : 1.0f / (stack_size - 1); + total_proportion += iter->stack_proportion; + iter = iter->next_in_stack; + } + iter = head; + while (iter) { + iter->stack_proportion /= total_proportion; + iter = iter->next_in_stack; + } + + /* 横向排列(垂直堆叠) */ + int32_t client_width; + int32_t current_x = geometry.x; + int32_t remain_client_width = geometry.width - (stack_size - 1) * gappih; + float remain_proportion = 1.0f; + + iter = head; + while (iter) { + client_width = + remain_client_width * (iter->stack_proportion / remain_proportion); + struct wlr_box client_geom = {.y = geometry.y, + .x = current_x, + .height = geometry.height, + .width = client_width}; + resize(iter->client, client_geom, 0); + remain_proportion -= iter->stack_proportion; + remain_client_width -= client_width; + current_x += client_width + gappih; + iter = iter->next_in_stack; + } +} + +void scroller(Monitor *m) { + uint32_t tag = m->pertag->curtag; + struct TagScrollerState *st = ensure_scroller_state(m, tag); + Client *c = NULL; + + /* 按全局客户端链表顺序收集所有堆叠头,确保视觉顺序正确 */ + struct ScrollerStackNode *heads[64]; + int32_t n_heads = 0; + wl_list_for_each(c, &clients, link) { + if (VISIBLEON(c, m) && ISSCROLLTILED(c)) { + struct ScrollerStackNode *node = find_scroller_node(st, c); + if (node && !node->prev_in_stack) { + bool already = false; + for (int k = 0; k < n_heads; k++) { + if (heads[k] == node) { + already = true; + break; + } + } + if (!already) + heads[n_heads++] = node; + } + } + } + + if (n_heads == 0) { + sync_scroller_state_to_clients(m, tag); + return; + } + + m->visible_scroll_tiling_clients = n_heads; + + 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; + if (config.smartgaps && n_heads == 1) { + cur_gappih = cur_gappoh = cur_gappov = 0; + } + int32_t max_client_width = + m->w.width - 2 * config.scroller_structs - cur_gappih; + + /* 单客户端特例 */ + if (n_heads == 1 && !config.scroller_ignore_proportion_single && + !heads[0]->client->isfullscreen && + !heads[0]->client->ismaximizescreen) { + struct ScrollerStackNode *head = heads[0]; + float single_proportion = + head->scroller_proportion_single > 0.0f + ? head->scroller_proportion_single + : config.scroller_default_proportion_single; + struct wlr_box target_geom; + target_geom.height = m->w.height - 2 * cur_gappov; + target_geom.width = (m->w.width - 2 * cur_gappoh) * single_proportion; + target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; + target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; + horizontal_check_scroller_root_inside_mon(head->client, &target_geom); + arrange_stack_node(head, target_geom, cur_gappiv); + sync_scroller_state_to_clients(m, tag); + return; + } + + struct ScrollerStackNode *root_node = NULL; + if (m->sel && ISSCROLLTILED(m->sel)) { + root_node = find_scroller_node(st, m->sel); + if (root_node) { + while (root_node->prev_in_stack) + root_node = root_node->prev_in_stack; + } + } + if (!root_node && m->prevsel && ISSCROLLTILED(m->prevsel)) { + root_node = find_scroller_node(st, m->prevsel); + if (root_node) { + while (root_node->prev_in_stack) + root_node = root_node->prev_in_stack; + } + } + if (!root_node) + root_node = heads[n_heads / 2]; /* 简单回退 */ + + int32_t focus_index = -1; + for (int i = 0; i < n_heads; i++) { + if (heads[i] == root_node) { + focus_index = i; + break; + } + } + if (focus_index < 0) + focus_index = n_heads / 2; + + /* 判断是否需要滚动、overspread、center */ + bool need_scroller = false; + bool over_overspread_to_left = false; + Client *root_client = root_node->client; + + if (root_client->geom.x >= m->w.x + config.scroller_structs && + root_client->geom.x + root_client->geom.width <= + m->w.x + m->w.width - config.scroller_structs) { + need_scroller = false; + } else { + need_scroller = true; + } + + bool need_apply_overspread = + config.scroller_prefer_overspread && n_heads > 1 && + (focus_index == 0 || focus_index == n_heads - 1) && + heads[focus_index]->scroller_proportion < 1.0f; + + if (need_apply_overspread) { + if (focus_index == 0) { + over_overspread_to_left = true; + } else { + over_overspread_to_left = false; + } + if (over_overspread_to_left && + (!INSIDEMON(heads[1]->client) || + (heads[1]->scroller_proportion + heads[0]->scroller_proportion >= + 1.0f))) { + need_scroller = true; + } else if (!over_overspread_to_left && + (!INSIDEMON(heads[focus_index - 1]->client) || + (heads[focus_index - 1]->scroller_proportion + + heads[focus_index]->scroller_proportion >= + 1.0f))) { + need_scroller = true; + } else { + need_apply_overspread = false; + } + } + + bool need_apply_center = + config.scroller_focus_center || n_heads == 1 || + (config.scroller_prefer_center && !need_apply_overspread && + (!m->prevsel || + (ISSCROLLTILED(m->prevsel) && + (m->prevsel->scroller_proportion * max_client_width) + + (heads[focus_index]->scroller_proportion * + max_client_width) > + m->w.width - 2 * config.scroller_structs - cur_gappih))); + + if (n_heads == 1 && config.scroller_ignore_proportion_single) { + need_scroller = true; + } + if (start_drag_window) + need_scroller = false; + + struct wlr_box target_geom; + target_geom.height = m->w.height - 2 * cur_gappov; + target_geom.width = + max_client_width * heads[focus_index]->scroller_proportion; + target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; + horizontal_scroll_adjust_fullandmax(heads[focus_index]->client, + &target_geom); + + if (heads[focus_index]->client->isfullscreen) { + target_geom.x = m->m.x; + horizontal_check_scroller_root_inside_mon(heads[focus_index]->client, + &target_geom); + arrange_stack_node(heads[focus_index], target_geom, cur_gappiv); + } else if (heads[focus_index]->client->ismaximizescreen) { + target_geom.x = m->w.x + cur_gappoh; + horizontal_check_scroller_root_inside_mon(heads[focus_index]->client, + &target_geom); + arrange_stack_node(heads[focus_index], target_geom, cur_gappiv); + } else if (need_scroller) { + if (need_apply_center) { + target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; + } else if (need_apply_overspread) { + if (over_overspread_to_left) { + target_geom.x = m->w.x + config.scroller_structs; + } else { + target_geom.x = + m->w.x + (m->w.width - + heads[focus_index]->scroller_proportion * + max_client_width - + config.scroller_structs); + } + } else { + target_geom.x = + root_client->geom.x > m->w.x + (m->w.width) / 2 + ? m->w.x + (m->w.width - + heads[focus_index]->scroller_proportion * + max_client_width - + config.scroller_structs) + : m->w.x + config.scroller_structs; + } + horizontal_check_scroller_root_inside_mon(heads[focus_index]->client, + &target_geom); + arrange_stack_node(heads[focus_index], target_geom, cur_gappiv); + } else { + target_geom.x = root_client->geom.x; + horizontal_check_scroller_root_inside_mon(heads[focus_index]->client, + &target_geom); + arrange_stack_node(heads[focus_index], target_geom, cur_gappiv); + } + + /* 排列左侧的堆叠 */ + for (int i = 1; i <= focus_index; i++) { + struct ScrollerStackNode *cur = heads[focus_index - i]; + struct wlr_box left_geom; + left_geom.height = m->w.height - 2 * cur_gappov; + left_geom.width = max_client_width * cur->scroller_proportion; + horizontal_scroll_adjust_fullandmax(cur->client, &left_geom); + left_geom.x = heads[focus_index - i + 1]->client->geom.x - cur_gappih - + left_geom.width; + arrange_stack_node(cur, left_geom, cur_gappiv); + } + + /* 排列右侧的堆叠 */ + for (int i = 1; i < n_heads - focus_index; i++) { + struct ScrollerStackNode *cur = heads[focus_index + i]; + struct wlr_box right_geom; + right_geom.height = m->w.height - 2 * cur_gappov; + right_geom.width = max_client_width * cur->scroller_proportion; + horizontal_scroll_adjust_fullandmax(cur->client, &right_geom); + right_geom.x = heads[focus_index + i - 1]->client->geom.x + cur_gappih + + heads[focus_index + i - 1]->client->geom.width; + arrange_stack_node(cur, right_geom, cur_gappiv); + } + + sync_scroller_state_to_clients(m, tag); +} + +void vertical_scroller(Monitor *m) { + uint32_t tag = m->pertag->curtag; + struct TagScrollerState *st = ensure_scroller_state(m, tag); + Client *c = NULL; + + /* 按全局顺序收集堆叠头 */ + struct ScrollerStackNode *heads[64]; + int32_t n_heads = 0; + wl_list_for_each(c, &clients, link) { + if (VISIBLEON(c, m) && ISSCROLLTILED(c)) { + struct ScrollerStackNode *node = find_scroller_node(st, c); + if (node && !node->prev_in_stack) { + bool already = false; + for (int k = 0; k < n_heads; k++) + if (heads[k] == node) + already = true; + if (!already) + heads[n_heads++] = node; + } + } + } + + if (n_heads == 0) { + sync_scroller_state_to_clients(m, tag); + return; + } + + m->visible_scroll_tiling_clients = n_heads; + + int32_t cur_gappiv = enablegaps ? m->gappiv : 0; + int32_t cur_gappov = enablegaps ? m->gappov : 0; + int32_t cur_gappoh = enablegaps ? m->gappoh : 0; + int32_t cur_gappih = enablegaps ? m->gappih : 0; + if (config.smartgaps && n_heads == 1) { + cur_gappiv = cur_gappov = cur_gappoh = 0; + } + int32_t max_client_height = + m->w.height - 2 * config.scroller_structs - cur_gappiv; + + if (n_heads == 1 && !config.scroller_ignore_proportion_single && + !heads[0]->client->isfullscreen && + !heads[0]->client->ismaximizescreen) { + struct ScrollerStackNode *head = heads[0]; + float single_proportion = + head->scroller_proportion_single > 0.0f + ? head->scroller_proportion_single + : config.scroller_default_proportion_single; + struct wlr_box target_geom; + target_geom.width = m->w.width - 2 * cur_gappoh; + target_geom.height = (m->w.height - 2 * cur_gappov) * single_proportion; + target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; + target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; + vertical_check_scroller_root_inside_mon(head->client, &target_geom); + arrange_stack_vertical_node(head, target_geom, cur_gappih); + sync_scroller_state_to_clients(m, tag); + return; + } + + struct ScrollerStackNode *root_node = NULL; + if (m->sel && ISSCROLLTILED(m->sel)) { + root_node = find_scroller_node(st, m->sel); + if (root_node) { + while (root_node->prev_in_stack) + root_node = root_node->prev_in_stack; + } + } + if (!root_node && m->prevsel && ISSCROLLTILED(m->prevsel)) { + root_node = find_scroller_node(st, m->prevsel); + if (root_node) { + while (root_node->prev_in_stack) + root_node = root_node->prev_in_stack; + } + } + if (!root_node) + root_node = heads[n_heads / 2]; + + int32_t focus_index = -1; + for (int i = 0; i < n_heads; i++) { + if (heads[i] == root_node) { + focus_index = i; + break; + } + } + if (focus_index < 0) + focus_index = n_heads / 2; + + bool need_scroller = false; + bool over_overspread_to_up = false; + Client *root_client = root_node->client; + + if (root_client->geom.y >= m->w.y + config.scroller_structs && + root_client->geom.y + root_client->geom.height <= + m->w.y + m->w.height - config.scroller_structs) { + need_scroller = false; + } else { + need_scroller = true; + } + + bool need_apply_overspread = + config.scroller_prefer_overspread && n_heads > 1 && + (focus_index == 0 || focus_index == n_heads - 1) && + heads[focus_index]->scroller_proportion < 1.0f; + + if (need_apply_overspread) { + if (focus_index == 0) { + over_overspread_to_up = true; + } else { + over_overspread_to_up = false; + } + if (over_overspread_to_up && + (!INSIDEMON(heads[1]->client) || + (heads[1]->scroller_proportion + heads[0]->scroller_proportion >= + 1.0f))) { + need_scroller = true; + } else if (!over_overspread_to_up && + (!INSIDEMON(heads[focus_index - 1]->client) || + (heads[focus_index - 1]->scroller_proportion + + heads[focus_index]->scroller_proportion >= + 1.0f))) { + need_scroller = true; + } else { + need_apply_overspread = false; + } + } + + bool need_apply_center = + config.scroller_focus_center || n_heads == 1 || + (config.scroller_prefer_center && !need_apply_overspread && + (!m->prevsel || + (ISSCROLLTILED(m->prevsel) && + (m->prevsel->scroller_proportion * max_client_height) + + (heads[focus_index]->scroller_proportion * + max_client_height) > + m->w.height - 2 * config.scroller_structs - cur_gappiv))); + + if (n_heads == 1 && config.scroller_ignore_proportion_single) { + need_scroller = true; + } + if (start_drag_window) + need_scroller = false; + + struct wlr_box target_geom; + target_geom.width = m->w.width - 2 * cur_gappoh; + target_geom.height = + max_client_height * heads[focus_index]->scroller_proportion; + target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; + vertical_scroll_adjust_fullandmax(heads[focus_index]->client, &target_geom); + + if (heads[focus_index]->client->isfullscreen) { + target_geom.y = m->m.y; + vertical_check_scroller_root_inside_mon(heads[focus_index]->client, + &target_geom); + arrange_stack_vertical_node(heads[focus_index], target_geom, + cur_gappih); + } else if (heads[focus_index]->client->ismaximizescreen) { + target_geom.y = m->w.y + cur_gappov; + vertical_check_scroller_root_inside_mon(heads[focus_index]->client, + &target_geom); + arrange_stack_vertical_node(heads[focus_index], target_geom, + cur_gappih); + } else if (need_scroller) { + if (need_apply_center) { + target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; + } else if (need_apply_overspread) { + if (over_overspread_to_up) { + target_geom.y = m->w.y + config.scroller_structs; + } else { + target_geom.y = + m->w.y + (m->w.height - + heads[focus_index]->scroller_proportion * + max_client_height - + config.scroller_structs); + } + } else { + target_geom.y = + root_client->geom.y > m->w.y + (m->w.height) / 2 + ? m->w.y + (m->w.height - + heads[focus_index]->scroller_proportion * + max_client_height - + config.scroller_structs) + : m->w.y + config.scroller_structs; + } + vertical_check_scroller_root_inside_mon(heads[focus_index]->client, + &target_geom); + arrange_stack_vertical_node(heads[focus_index], target_geom, + cur_gappih); + } else { + target_geom.y = root_client->geom.y; + vertical_check_scroller_root_inside_mon(heads[focus_index]->client, + &target_geom); + arrange_stack_vertical_node(heads[focus_index], target_geom, + cur_gappih); + } + + for (int i = 1; i <= focus_index; i++) { + struct ScrollerStackNode *cur = heads[focus_index - i]; + struct wlr_box up_geom; + up_geom.width = m->w.width - 2 * cur_gappoh; + up_geom.height = max_client_height * cur->scroller_proportion; + vertical_scroll_adjust_fullandmax(cur->client, &up_geom); + up_geom.y = heads[focus_index - i + 1]->client->geom.y - cur_gappiv - + up_geom.height; + arrange_stack_vertical_node(cur, up_geom, cur_gappih); + } + + for (int i = 1; i < n_heads - focus_index; i++) { + struct ScrollerStackNode *cur = heads[focus_index + i]; + struct wlr_box down_geom; + down_geom.width = m->w.width - 2 * cur_gappoh; + down_geom.height = max_client_height * cur->scroller_proportion; + vertical_scroll_adjust_fullandmax(cur->client, &down_geom); + down_geom.y = heads[focus_index + i - 1]->client->geom.y + cur_gappiv + + heads[focus_index + i - 1]->client->geom.height; + arrange_stack_vertical_node(cur, down_geom, cur_gappih); + } + + sync_scroller_state_to_clients(m, tag); +} + +void scroller_remove_client(Client *c) { + Monitor *m; + wl_list_for_each(m, &mons, link) { + for (uint32_t t = 0; t < LENGTH(tags) + 1; t++) { + struct TagScrollerState *st = m->pertag->scroller_state[t]; + if (!st) + continue; + struct ScrollerStackNode *node = find_scroller_node(st, c); + if (node) { + scroller_node_remove(st, node); + } + } + } +} + +void scroller_insert_stack(Client *c, Client *target_client, + bool insert_before) { + if (!target_client || target_client->mon != c->mon) + return; + + if (c->isfullscreen) + setfullscreen(c, 0); + if (c->ismaximizescreen) + setmaximizescreen(c, 0); + + Monitor *m = c->mon; + uint32_t tag = m->pertag->curtag; + struct TagScrollerState *st = ensure_scroller_state(m, tag); + + struct ScrollerStackNode *cnode = find_scroller_node(st, c); + if (cnode) + scroller_node_remove(st, cnode); + + struct ScrollerStackNode *tnode = find_scroller_node(st, target_client); + if (!tnode) + tnode = scroller_node_create(st, target_client); + + struct ScrollerStackNode *newnode = scroller_node_create(st, c); + /* 将新节点插入到 tnode 的前面或后面 */ + if (insert_before) { + newnode->next_in_stack = tnode; + newnode->prev_in_stack = tnode->prev_in_stack; + if (tnode->prev_in_stack) + tnode->prev_in_stack->next_in_stack = newnode; + tnode->prev_in_stack = newnode; + } else { + newnode->prev_in_stack = tnode; + newnode->next_in_stack = tnode->next_in_stack; + if (tnode->next_in_stack) + tnode->next_in_stack->prev_in_stack = newnode; + tnode->next_in_stack = newnode; + } + + /* 处理堆叠头部的全屏/最大化状态*/ + struct ScrollerStackNode *head = tnode; + while (head->prev_in_stack) + head = head->prev_in_stack; + Client *stack_head = head->client; + if (stack_head->ismaximizescreen) + setmaximizescreen(stack_head, 0); + if (stack_head->isfullscreen) + setfullscreen(stack_head, 0); + + /* 同步到 Client 字段 */ + sync_scroller_state_to_clients(m, tag); + + arrange(m, false, false); +} + +void scroller_drop_tile(Client *c, Client *closest, int vertical) { + + Client *stack_head = scroll_get_stack_head_client(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); +} + +Client *scroll_get_stack_head_client(Client *c) { + if (!c || !c->mon) + return c; + 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) { + while (n->prev_in_stack) + n = n->prev_in_stack; + return n->client; + } + } + return c; +} + +static void update_scroller_state(Monitor *m) { + uint32_t tag = m->pertag->curtag; + struct TagScrollerState *st = ensure_scroller_state(m, tag); + + /* 收集当前可见的所有 scroller 平铺窗口 */ + Client *vis[512]; + int32_t count = 0; + Client *c; + wl_list_for_each(c, &clients, link) { + if (VISIBLEON(c, m) && ISSCROLLTILED(c)) + vis[count++] = c; + if (count == 512) + break; + } + + /* 移除不再可见的节点 */ + struct ScrollerStackNode *n = st->all_first; + while (n) { + bool found = false; + for (int i = 0; i < count; i++) { + if (vis[i] == n->client) { + found = true; + break; + } + } + struct ScrollerStackNode *next = n->all_next; + if (!found) + scroller_node_remove(st, n); + n = next; + } + + /* 为新的可见窗口创建节点 */ + for (int i = 0; i < count; i++) { + if (!find_scroller_node(st, vis[i])) { + scroller_node_create(st, vis[i]); + } + } +} \ No newline at end of file diff --git a/src/layout/vertical.h b/src/layout/vertical.h index 3d8863af..36114dde 100644 --- a/src/layout/vertical.h +++ b/src/layout/vertical.h @@ -176,324 +176,6 @@ void vertical_deck(Monitor *m) { } } -void vertical_scroll_adjust_fullandmax(Client *c, struct wlr_box *target_geom) { - Monitor *m = c->mon; - int32_t cur_gappiv = enablegaps ? m->gappiv : 0; - int32_t cur_gappov = enablegaps ? m->gappov : 0; - int32_t cur_gappoh = enablegaps ? m->gappoh : 0; - - cur_gappiv = config.smartgaps && m->visible_scroll_tiling_clients == 1 - ? 0 - : cur_gappiv; - cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1 - ? 0 - : cur_gappov; - cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1 - ? 0 - : cur_gappoh; - - if (c->isfullscreen) { - target_geom->width = m->m.width; - target_geom->height = m->m.height; - target_geom->x = m->m.x; - return; - } - - if (c->ismaximizescreen) { - target_geom->width = m->w.width - 2 * cur_gappoh; - target_geom->height = m->w.height - 2 * cur_gappov; - target_geom->x = m->w.x + cur_gappoh; - return; - } - - target_geom->width = m->w.width - 2 * cur_gappoh; - target_geom->x = m->w.x + (m->w.width - target_geom->width) / 2; -} - -void arrange_stack_vertical(Client *scroller_stack_head, - struct wlr_box geometry, int32_t gappih) { - int32_t stack_size = 0; - Client *iter = scroller_stack_head; - - while (iter) { - stack_size++; - iter = iter->next_in_stack; - } - - if (stack_size == 0) - return; - - float total_proportion = 0.0f; - iter = scroller_stack_head; - while (iter) { - if (iter->stack_proportion <= 0.0f || iter->stack_proportion >= 1.0f) { - iter->stack_proportion = - stack_size == 1 ? 1.0f : 1.0f / (stack_size - 1); - } - total_proportion += iter->stack_proportion; - iter = iter->next_in_stack; - } - - iter = scroller_stack_head; - while (iter) { - iter->stack_proportion = iter->stack_proportion / total_proportion; - iter = iter->next_in_stack; - } - - int32_t client_width; - int32_t current_x = geometry.x; - int32_t remain_client_width = geometry.width - (stack_size - 1) * gappih; - float remain_proportion = 1.0f; - - iter = scroller_stack_head; - while (iter) { - - client_width = - remain_client_width * (iter->stack_proportion / remain_proportion); - - struct wlr_box client_geom = {.y = geometry.y, - .x = current_x, - .height = geometry.height, - .width = client_width}; - resize(iter, client_geom, 0); - remain_proportion -= iter->stack_proportion; - remain_client_width -= client_width; - current_x += client_width + gappih; - iter = iter->next_in_stack; - } -} - -void vertical_check_scroller_root_inside_mon(Client *c, - struct wlr_box *geometry) { - if (!GEOMINSIDEMON(geometry, c->mon)) { - geometry->y = c->mon->w.y + (c->mon->w.height - geometry->height) / 2; - } -} - -// 竖屏滚动布局 -void vertical_scroller(Monitor *m) { - int32_t i, n, j; - float single_proportion = 1.0; - - Client *c = NULL, *root_client = NULL; - Client **tempClients = NULL; - struct wlr_box target_geom; - int32_t focus_client_index = 0; - bool need_scroller = false; - bool over_overspread_to_up = false; - int32_t cur_gappiv = enablegaps ? m->gappiv : 0; - int32_t cur_gappov = enablegaps ? m->gappov : 0; - int32_t cur_gappoh = enablegaps ? m->gappoh : 0; - int32_t cur_gappih = enablegaps ? m->gappih : 0; - - cur_gappiv = config.smartgaps && m->visible_scroll_tiling_clients == 1 - ? 0 - : cur_gappiv; - cur_gappov = config.smartgaps && m->visible_scroll_tiling_clients == 1 - ? 0 - : cur_gappov; - cur_gappoh = config.smartgaps && m->visible_scroll_tiling_clients == 1 - ? 0 - : cur_gappoh; - - int32_t max_client_height = - m->w.height - 2 * config.scroller_structs - cur_gappiv; - - n = m->visible_scroll_tiling_clients; - - if (n == 0) { - return; - } - - tempClients = malloc(n * sizeof(Client *)); - if (!tempClients) { - return; - } - - j = 0; - wl_list_for_each(c, &clients, link) { - if (VISIBLEON(c, m) && ISSCROLLTILED(c) && !c->prev_in_stack) { - tempClients[j] = c; - j++; - } - } - - if (n == 1 && !config.scroller_ignore_proportion_single && - !tempClients[0]->isfullscreen && !tempClients[0]->ismaximizescreen) { - c = tempClients[0]; - - single_proportion = c->scroller_proportion_single > 0.0f - ? c->scroller_proportion_single - : config.scroller_default_proportion_single; - - target_geom.width = m->w.width - 2 * cur_gappoh; - target_geom.height = (m->w.height - 2 * cur_gappov) * single_proportion; - target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; - target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; - vertical_check_scroller_root_inside_mon(c, &target_geom); - arrange_stack_vertical(c, target_geom, cur_gappih); - free(tempClients); - return; - } - - if (m->sel && !client_is_unmanaged(m->sel) && ISSCROLLTILED(m->sel)) { - root_client = m->sel; - } else if (m->prevsel && ISSCROLLTILED(m->prevsel) && - VISIBLEON(m->prevsel, m) && !client_is_unmanaged(m->prevsel)) { - root_client = m->prevsel; - } else { - root_client = center_tiled_select(m); - } - - // root_client might be in a stack, find the stack head - if (root_client) { - root_client = get_scroll_stack_head(root_client); - } - - if (!root_client) { - free(tempClients); - return; - } - - for (i = 0; i < n; i++) { - c = tempClients[i]; - if (root_client == c) { - if (c->geom.y >= m->w.y + config.scroller_structs && - c->geom.y + c->geom.height <= - m->w.y + m->w.height - config.scroller_structs) { - need_scroller = false; - } else { - need_scroller = true; - } - focus_client_index = i; - break; - } - } - - bool need_apply_overspread = - config.scroller_prefer_overspread && - m->visible_scroll_tiling_clients > 1 && - (focus_client_index == 0 || focus_client_index == n - 1) && - tempClients[focus_client_index]->scroller_proportion < 1.0f; - - if (need_apply_overspread) { - - if (focus_client_index == 0) { - over_overspread_to_up = true; - } else { - over_overspread_to_up = false; - } - - if (over_overspread_to_up && - (!INSIDEMON(tempClients[1]) || - (tempClients[1]->scroller_proportion + - tempClients[focus_client_index]->scroller_proportion >= - 1.0f))) { - need_scroller = true; - } else if (!over_overspread_to_up && - (!INSIDEMON(tempClients[focus_client_index - 1]) || - (tempClients[focus_client_index - 1]->scroller_proportion + - tempClients[focus_client_index]->scroller_proportion >= - 1.0f))) { - need_scroller = true; - } else { - need_apply_overspread = false; - } - } - - bool need_apply_center = - config.scroller_focus_center || m->visible_scroll_tiling_clients == 1 || - (config.scroller_prefer_center && !need_apply_overspread && - (!m->prevsel || - (ISSCROLLTILED(m->prevsel) && - (m->prevsel->scroller_proportion * max_client_height) + - (tempClients[focus_client_index]->scroller_proportion * - max_client_height) > - m->w.height - 2 * config.scroller_structs - cur_gappiv))); - - if (n == 1 && config.scroller_ignore_proportion_single) { - need_scroller = true; - } - - if (start_drag_window) - need_scroller = false; - - target_geom.width = m->w.width - 2 * cur_gappoh; - target_geom.height = max_client_height * c->scroller_proportion; - target_geom.x = m->w.x + (m->w.width - target_geom.width) / 2; - vertical_scroll_adjust_fullandmax(tempClients[focus_client_index], - &target_geom); - - if (tempClients[focus_client_index]->isfullscreen) { - target_geom.y = m->m.y; - vertical_check_scroller_root_inside_mon(tempClients[focus_client_index], - &target_geom); - arrange_stack_vertical(tempClients[focus_client_index], target_geom, - cur_gappih); - } else if (tempClients[focus_client_index]->ismaximizescreen) { - target_geom.y = m->w.y + cur_gappov; - vertical_check_scroller_root_inside_mon(tempClients[focus_client_index], - &target_geom); - arrange_stack_vertical(tempClients[focus_client_index], target_geom, - cur_gappih); - } else if (need_scroller) { - if (need_apply_center) { - target_geom.y = m->w.y + (m->w.height - target_geom.height) / 2; - } else if (need_apply_overspread) { - if (over_overspread_to_up) { - target_geom.y = m->w.y + config.scroller_structs; - } else { - target_geom.y = - m->w.y + - (m->w.height - - tempClients[focus_client_index]->scroller_proportion * - max_client_height - - config.scroller_structs); - } - } else { - target_geom.y = root_client->geom.y > m->w.y + (m->w.height) / 2 - ? m->w.y + (m->w.height - - tempClients[focus_client_index] - ->scroller_proportion * - max_client_height - - config.scroller_structs) - : m->w.y + config.scroller_structs; - } - vertical_check_scroller_root_inside_mon(tempClients[focus_client_index], - &target_geom); - arrange_stack_vertical(tempClients[focus_client_index], target_geom, - cur_gappih); - } else { - target_geom.y = c->geom.y; - vertical_check_scroller_root_inside_mon(tempClients[focus_client_index], - &target_geom); - arrange_stack_vertical(tempClients[focus_client_index], target_geom, - cur_gappih); - } - - for (i = 1; i <= focus_client_index; i++) { - c = tempClients[focus_client_index - i]; - target_geom.height = max_client_height * c->scroller_proportion; - vertical_scroll_adjust_fullandmax(c, &target_geom); - target_geom.y = tempClients[focus_client_index - i + 1]->geom.y - - cur_gappiv - target_geom.height; - - arrange_stack_vertical(c, target_geom, cur_gappih); - } - - for (i = 1; i < n - focus_client_index; i++) { - c = tempClients[focus_client_index + i]; - target_geom.height = max_client_height * c->scroller_proportion; - vertical_scroll_adjust_fullandmax(c, &target_geom); - target_geom.y = tempClients[focus_client_index + i - 1]->geom.y + - cur_gappiv + - tempClients[focus_client_index + i - 1]->geom.height; - arrange_stack_vertical(c, target_geom, cur_gappih); - } - - free(tempClients); -} - void vertical_grid(Monitor *m) { int32_t i, n; int32_t cx, cy, cw, ch; diff --git a/src/mango.c b/src/mango.c index b86fe0ef..a0ff755f 100644 --- a/src/mango.c +++ b/src/mango.c @@ -112,7 +112,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; @@ -571,6 +571,21 @@ struct capture_session_tracker { struct wlr_ext_image_copy_capture_session_v1 *session; }; 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( @@ -807,7 +822,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); @@ -834,6 +849,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" @@ -963,19 +988,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; @@ -1048,6 +1071,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) { @@ -1176,15 +1200,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); @@ -1210,16 +1231,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) { @@ -2125,100 +2169,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); @@ -2233,11 +2183,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) { @@ -2567,6 +2517,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); } @@ -3250,7 +3203,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; @@ -4248,8 +4205,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)); @@ -4359,7 +4314,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); } @@ -5026,91 +4981,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; @@ -5125,39 +5126,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 @@ -5426,24 +5435,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) { @@ -6113,7 +6116,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; @@ -6306,9 +6308,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))) @@ -6320,7 +6328,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) { @@ -6345,10 +6354,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); } @@ -6414,10 +6423,7 @@ void unmapnotify(struct wl_listener *listener, void *data) { c->image_capture_source = NULL; 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);