diff --git a/assets/config.conf b/assets/config.conf index e0bcdadd..938a1f2f 100644 --- a/assets/config.conf +++ b/assets/config.conf @@ -121,6 +121,7 @@ borderpx=4 rootcolor=0x201b14ff bordercolor=0x444444ff dropcolor=0x8FBA7C55 +splitcolor=0xEB441EFF focuscolor=0xc9b890ff maximizescreencolor=0x89aa61ff urgentcolor=0xad401fff diff --git a/src/animation/client.h b/src/animation/client.h index 8ce81cd2..2b8b7b55 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -1,3 +1,4 @@ +#include "wlr/util/log.h" void client_actual_size(Client *c, int32_t *width, int32_t *height) { *width = c->animation.current.width - 2 * (int32_t)c->bw; @@ -284,8 +285,8 @@ void client_draw_shadow(Client *c) { struct wlr_box client_box = { .x = bwoffset, .y = bwoffset, - .width = width + (int32_t)c->bw - bwoffset, - .height = height + (int32_t)c->bw - bwoffset, + .width = width + 2 * (int32_t)c->bw - 2 * bwoffset, + .height = height + 2 * (int32_t)c->bw - 2 * bwoffset, }; struct wlr_box shadow_box = { @@ -350,11 +351,102 @@ void client_draw_shadow(Client *c) { wlr_scene_shadow_set_clipped_region(c->shadow, clipped_region); } +void apply_split_border(Client *c, bool hit_no_border) { + + if (c->iskilling || !c->mon || !client_surface(c)->mapped) + return; + + const Layout *layout = c->mon->pertag->ltidxs[c->mon->pertag->curtag]; + + if (hit_no_border || layout->id != DWINDLE || + !config.dwindle_manual_split) { + if (c->splitindicator[0]->node.enabled) { + wlr_scene_node_set_enabled(&c->splitindicator[0]->node, false); + } + if (c->splitindicator[1]->node.enabled) { + wlr_scene_node_set_enabled(&c->splitindicator[1]->node, false); + } + return; + } else { + + DwindleNode **root = + &c->mon->pertag->dwindle_root[c->mon->pertag->curtag]; + DwindleNode *dnode = dwindle_find_leaf(*root, c); + + if (!dnode) { + wlr_scene_node_set_enabled(&c->splitindicator[0]->node, false); + wlr_scene_node_set_enabled(&c->splitindicator[1]->node, false); + return; + } else { + if (dnode->custom_leaf_split_h) { + wlr_scene_node_set_enabled(&c->splitindicator[0]->node, false); + wlr_scene_node_set_enabled(&c->splitindicator[1]->node, true); + } else { + wlr_scene_node_set_enabled(&c->splitindicator[0]->node, true); + wlr_scene_node_set_enabled(&c->splitindicator[1]->node, false); + } + } + } + + struct wlr_box fullgeom = c->animation.current; + // 一但在GEZERO如果使用无符号,那么其他数据也会转换为无符号导致没有负数出错 + int32_t bw = (int32_t)c->bw; + + int32_t right_offset, bottom_offset, left_offset, top_offset; + + if (c == grabc) { + right_offset = 0; + bottom_offset = 0; + left_offset = 0; + top_offset = 0; + } else { + right_offset = + GEZERO(c->animation.current.x + c->animation.current.width - + c->mon->m.x - c->mon->m.width); + bottom_offset = + GEZERO(c->animation.current.y + c->animation.current.height - + c->mon->m.y - c->mon->m.height); + + left_offset = GEZERO(c->mon->m.x - c->animation.current.x); + top_offset = GEZERO(c->mon->m.y - c->animation.current.y); + } + + int32_t border_down_width = GEZERO(fullgeom.width - left_offset - + right_offset - 2 * config.border_radius); + int32_t border_down_height = + GEZERO(bw - bottom_offset - GEZERO(top_offset + bw - fullgeom.height)); + + int32_t border_right_width = + GEZERO(bw - right_offset - GEZERO(left_offset + bw - fullgeom.width)); + int32_t border_right_height = + GEZERO(fullgeom.height - top_offset - bottom_offset - + 2 * config.border_radius); + + int32_t border_down_x = GEZERO(config.border_radius - left_offset); + int32_t border_down_y = GEZERO(fullgeom.height - bw) + + GEZERO(top_offset + bw - fullgeom.height); + + int32_t border_right_x = + GEZERO(fullgeom.width - bw) + GEZERO(left_offset + bw - fullgeom.width); + int32_t border_right_y = GEZERO(config.border_radius - top_offset); + + set_rect_size(c->splitindicator[0], border_down_width, border_down_height); + set_rect_size(c->splitindicator[1], border_right_width, + border_right_height); + wlr_scene_node_set_position(&c->splitindicator[0]->node, border_down_x, + border_down_y); + wlr_scene_node_set_position(&c->splitindicator[1]->node, border_right_x, + border_right_y); +} + void apply_border(Client *c) { if (!c || c->iskilling || !client_surface(c)->mapped) return; bool hit_no_border = check_hit_no_border(c); + + apply_split_border(c, hit_no_border); + enum corner_location current_corner_location; if (c->isfullscreen || (config.no_radius_when_single && c->mon && c->mon->visible_tiling_clients == 1)) { @@ -1203,6 +1295,7 @@ bool client_draw_fadeout_frame(Client *c) { void client_set_focused_opacity_animation(Client *c) { float *border_color = get_border_color(c); + wlr_scene_node_lower_to_bottom(&c->border->node); if (!config.animations) { setborder_color(c); @@ -1224,7 +1317,7 @@ void client_set_focused_opacity_animation(Client *c) { void client_set_unfocused_opacity_animation(Client *c) { float *border_color = get_border_color(c); - + wlr_scene_node_raise_to_top(&c->border->node); if (!config.animations) { setborder_color(c); return; diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 9548f13d..c9426c6a 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -249,6 +249,7 @@ typedef struct { int32_t dwindle_smart_split; int32_t dwindle_smart_resize; int32_t dwindle_drop_simple_split; + int32_t dwindle_manual_split; float dwindle_split_ratio; uint32_t hotarea_size; @@ -326,6 +327,7 @@ typedef struct { float rootcolor[4]; float bordercolor[4]; float dropcolor[4]; + float splitcolor[4]; float focuscolor[4]; float maximizescreencolor[4]; float urgentcolor[4]; @@ -1222,6 +1224,8 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, (*arg).i = parse_direction(arg_value); } else if (strcmp(func_name, "toggle_all_floating") == 0) { func = toggle_all_floating; + } else if (strcmp(func_name, "dwindle_toggle_split_direction") == 0) { + func = dwindle_toggle_split_direction; } else { return NULL; } @@ -1635,6 +1639,8 @@ bool parse_option(Config *config, char *key, char *value) { config->dwindle_smart_resize = atoi(value); } else if (strcmp(key, "dwindle_drop_simple_split") == 0) { config->dwindle_drop_simple_split = atoi(value); + } else if (strcmp(key, "dwindle_manual_split") == 0) { + config->dwindle_manual_split = atoi(value); } else if (strcmp(key, "dwindle_split_ratio") == 0) { config->dwindle_split_ratio = atof(value); } else if (strcmp(key, "hotarea_size") == 0) { @@ -1777,6 +1783,17 @@ bool parse_option(Config *config, char *key, char *value) { } else { convert_hex_to_rgba(config->dropcolor, color); } + } else if (strcmp(key, "splitcolor") == 0) { + int64_t color = parse_color(value); + if (color == -1) { + fprintf(stderr, + "\033[1m\033[31m[ERROR]:\033[33m Invalid splitcolor " + "format: %s\n", + value); + return false; + } else { + convert_hex_to_rgba(config->splitcolor, color); + } } else if (strcmp(key, "focuscolor") == 0) { int64_t color = parse_color(value); if (color == -1) { @@ -3209,6 +3226,7 @@ void override_config(void) { config.dwindle_smart_resize = CLAMP_INT(config.dwindle_smart_resize, 0, 1); config.dwindle_drop_simple_split = CLAMP_INT(config.dwindle_drop_simple_split, 0, 1); + config.dwindle_manual_split = CLAMP_INT(config.dwindle_manual_split, 0, 1); config.dwindle_split_ratio = CLAMP_FLOAT(config.dwindle_split_ratio, 0.05f, 0.95f); config.hotarea_size = CLAMP_INT(config.hotarea_size, 1, 1000); @@ -3356,6 +3374,7 @@ void set_value_default() { config.dwindle_smart_split = 0; config.dwindle_smart_resize = 0; config.dwindle_drop_simple_split = 1; + config.dwindle_manual_split = 0; config.dwindle_split_ratio = 0.5f; config.log_level = WLR_ERROR; @@ -3503,10 +3522,14 @@ void set_value_default() { config.bordercolor[1] = 0x44 / 255.0f; config.bordercolor[2] = 0x44 / 255.0f; config.bordercolor[3] = 1.0f; - config.dropcolor[0] = 0x8f / 255.0f; - config.dropcolor[1] = 0xba / 255.0f; - config.dropcolor[2] = 0x7c / 255.0f; + config.dropcolor[0] = 0xd5 / 255.0f; + config.dropcolor[1] = 0x89 / 255.0f; + config.dropcolor[2] = 0x9d / 255.0f; config.dropcolor[3] = 0.5f; + config.splitcolor[0] = 0xeb / 255.0f; + config.splitcolor[1] = 0x44 / 255.0f; + config.splitcolor[2] = 0x1e / 255.0f; + config.splitcolor[3] = 1.0f; config.focuscolor[0] = 0xc6 / 255.0f; config.focuscolor[1] = 0x6b / 255.0f; config.focuscolor[2] = 0x25 / 255.0f; @@ -3767,6 +3790,8 @@ void reapply_property(void) { } wlr_scene_rect_set_color(c->droparea, config.dropcolor); + wlr_scene_rect_set_color(c->splitindicator[0], config.splitcolor); + wlr_scene_rect_set_color(c->splitindicator[1], config.splitcolor); } } } diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h index dbeebd33..41945dda 100644 --- a/src/dispatch/bind_declare.h +++ b/src/dispatch/bind_declare.h @@ -70,4 +70,5 @@ int32_t disable_monitor(const Arg *arg); int32_t enable_monitor(const Arg *arg); int32_t toggle_monitor(const Arg *arg); int32_t scroller_stack(const Arg *arg); -int32_t toggle_all_floating(const Arg *arg); \ No newline at end of file +int32_t toggle_all_floating(const Arg *arg); +int32_t dwindle_toggle_split_direction(const Arg *arg); \ No newline at end of file diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index ffeff6f0..22fc16a3 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -1917,3 +1917,28 @@ int32_t toggle_all_floating(const Arg *arg) { } return 0; } + +int32_t dwindle_toggle_split_direction(const Arg *arg) { + if (!selmon || !selmon->sel) + return 0; + + Client *c = selmon->sel; + if (!c || !c->mon || c->isfloating) + return 0; + + const Layout *layout = c->mon->pertag->ltidxs[c->mon->pertag->curtag]; + + if (layout->id != DWINDLE) + return 0; + + DwindleNode **root = &selmon->pertag->dwindle_root[selmon->pertag->curtag]; + DwindleNode *leaf = dwindle_find_leaf(*root, c); + + if (!leaf) + return 0; + + leaf->custom_leaf_split_h = !leaf->custom_leaf_split_h; + bool hit_no_border = check_hit_no_border(c); + apply_split_border(c, hit_no_border); + return 0; +} \ No newline at end of file diff --git a/src/layout/dwindle.h b/src/layout/dwindle.h index 94d65be6..bf56d4dc 100644 --- a/src/layout/dwindle.h +++ b/src/layout/dwindle.h @@ -1,20 +1,3 @@ -typedef struct DwindleNode DwindleNode; -struct DwindleNode { - bool is_split; - bool split_h; - bool split_locked; - float ratio; - float drag_init_ratio; - int32_t container_x; - int32_t container_y; - int32_t container_w; - int32_t container_h; - DwindleNode *parent; - DwindleNode *first; - DwindleNode *second; - Client *client; -}; - static DwindleNode *dwindle_locked_h_node = NULL; static DwindleNode *dwindle_locked_v_node = NULL; @@ -24,6 +7,39 @@ static DwindleNode *dwindle_new_leaf(Client *c) { return n; } +// 统计同方向上的节点总和 (N_old) +static int count_block_items(DwindleNode *node, bool split_h) { + if (!node) + return 0; + if (!node->is_split || node->split_h != split_h) + return 1; + return count_block_items(node->first, split_h) + + count_block_items(node->second, split_h); +} + +// 向上查找方向块路径,并计算每个祖先节点的绝对占比 +static int get_block_path_and_ratios(DwindleNode *target, bool split_h, + DwindleNode **path, float *p) { + int path_len = 0; + path[path_len++] = target; + DwindleNode *curr = target->parent; + while (curr && curr->split_h == split_h) { + path[path_len++] = curr; + curr = curr->parent; + } + + p[path_len - 1] = 1.0f; // 方向块根节点占比为 100% + for (int i = path_len - 1; i > 0; i--) { + DwindleNode *S = path[i]; + DwindleNode *child = path[i - 1]; + if (S->first == child) + p[i - 1] = p[i] * S->ratio; + else + p[i - 1] = p[i] * (1.0f - S->ratio); + } + return path_len; +} + static DwindleNode *dwindle_find_leaf(DwindleNode *node, Client *c) { if (!node) return NULL; @@ -55,6 +71,7 @@ static void dwindle_insert(DwindleNode **root, Client *new_c, Client *focused, DwindleNode *new_leaf = dwindle_new_leaf(new_c); if (!*root) { + new_leaf->custom_leaf_split_h = true; *root = new_leaf; return; } @@ -63,11 +80,45 @@ static void dwindle_insert(DwindleNode **root, Client *new_c, Client *focused, if (!target) target = dwindle_first_leaf(*root); + // ================= 保持其他窗口比例缩减逻辑 ================= + DwindleNode *path[512]; + float p[512]; + int path_len = get_block_path_and_ratios(target, split_h, path, p); + + int n_old = 1; + if (path_len > 1) { + n_old = count_block_items(path[path_len - 1], split_h); + } + float N = (float)(n_old + 1); + + for (int i = path_len - 1; i > 0; i--) { + DwindleNode *S = path[i]; + DwindleNode *child = path[i - 1]; + float p_S = p[i]; + float p_first = p_S * S->ratio; + + if (S->first == child) { + float p_first_new = p_first * (N - 1.0f) / N + 1.0f / N; + float p_S_new = p_S * (N - 1.0f) / N + 1.0f / N; + S->ratio = p_first_new / p_S_new; + } else { + float p_first_new = p_first * (N - 1.0f) / N; + float p_S_new = p_S * (N - 1.0f) / N + 1.0f / N; + S->ratio = p_first_new / p_S_new; + } + if (S->ratio < 0.001f) + S->ratio = 0.001f; + if (S->ratio > 0.999f) + S->ratio = 0.999f; + } + // ============================================================ + DwindleNode *split = calloc(1, sizeof(DwindleNode)); split->is_split = true; - split->ratio = ratio; split->split_h = split_h; split->split_locked = lock; + split->custom_leaf_split_h = target->custom_leaf_split_h; + new_leaf->custom_leaf_split_h = target->custom_leaf_split_h; if (as_first) { split->first = new_leaf; @@ -76,6 +127,10 @@ static void dwindle_insert(DwindleNode **root, Client *new_c, Client *focused, split->first = target; split->second = new_leaf; } + + // 通用逻辑 + split->ratio = ratio; + split->parent = target->parent; target->parent = split; new_leaf->parent = split; @@ -102,12 +157,69 @@ static void dwindle_remove(DwindleNode **root, Client *c) { return; } + // 开始删除空间的比例回退逻辑 + + // 查找连续的同方向块路径 + bool split_h = parent->split_h; + DwindleNode *path[128]; + int path_len = 0; + path[path_len++] = parent; + DwindleNode *curr = parent->parent; + while (curr && curr->split_h == split_h) { + path[path_len++] = curr; + curr = curr->parent; + } + + // 计算各祖先的旧绝对占比 + float p[128]; + p[path_len - 1] = 1.0f; + for (int i = path_len - 1; i > 0; i--) { + DwindleNode *S = path[i]; + DwindleNode *child = path[i - 1]; + if (S->first == child) + p[i - 1] = p[i] * S->ratio; + else + p[i - 1] = p[i] * (1.0f - S->ratio); + } + + // 计算即将被删除的叶子节点,在该方向块中所占的绝对面积比例 (P_del) + float p_del = + p[0] * (parent->first == leaf ? parent->ratio : (1.0f - parent->ratio)); + if (p_del > 0.999f) + p_del = 0.999f; // 兜底 + + // 重算祖先比例:将 P_del 空出来的空间,按原定比例无缝分配给其他窗口 + for (int i = path_len - 1; i > 0; i--) { + DwindleNode *S = path[i]; + DwindleNode *child = path[i - 1]; + float p_S = p[i]; + float p_first = p_S * S->ratio; + + float denom = p_S - p_del; + if (denom < 0.0001f) + denom = 0.0001f; + + if (S->first == child) { + S->ratio = (p_first - p_del) / denom; + } else { + S->ratio = p_first / denom; + } + + if (S->ratio < 0.001f) + S->ratio = 0.001f; + if (S->ratio > 0.999f) + S->ratio = 0.999f; + } + + // 比例重算结束 + + // 基础的二叉树摘除节点逻辑 DwindleNode *sibling = (parent->first == leaf) ? parent->second : parent->first; DwindleNode *grandparent = parent->parent; + sibling->parent = grandparent; - /* Preserve split direction on sibling split-nodes when requested. */ if (!sibling->is_split || (!config.dwindle_preserve_split && !config.dwindle_smart_split)) { sibling->container_w = 0; @@ -319,45 +431,86 @@ static void dwindle_remove_client(Client *c) { * dwindle_smart_split config options. */ static void dwindle_insert_with_config(DwindleNode **root, Client *new_c, Client *focused, float ratio) { + + if (!new_c || !focused) + return; + bool as_first = false; bool split_h = false; bool lock = false; - if (focused) { - struct wlr_box *fg = &focused->geom; - double fcx = fg->x + fg->width * 0.5; - double fcy = fg->y + fg->height * 0.5; + struct wlr_box *fg = &focused->geom; + double fcx = fg->x + fg->width * 0.5; + double fcy = fg->y + fg->height * 0.5; - if (config.dwindle_smart_split) { - double nx = (cursor->x - fcx) / (fg->width * 0.5); - double ny = (cursor->y - fcy) / (fg->height * 0.5); + if (config.dwindle_smart_split) { + double nx = (cursor->x - fcx) / (fg->width * 0.5); + double ny = (cursor->y - fcy) / (fg->height * 0.5); - if (fabs(ny) > fabs(nx)) { - split_h = false; // vertical split - as_first = (ny < 0); // top → new window on top - } else { - split_h = true; // horizontal split - as_first = (nx < 0); // left → new window on left - } - lock = true; // lock split direction + if (fabs(ny) > fabs(nx)) { + split_h = false; // vertical split + as_first = (ny < 0); // top → new window on top } else { - // normal mode, auto split - bool likely_h = (fg->width >= fg->height); - if (likely_h) { - if (config.dwindle_hsplit == 0) - as_first = (cursor->x < fcx); - else - as_first = (config.dwindle_hsplit == 2); - } else { - if (config.dwindle_vsplit == 0) - as_first = (cursor->y < fcy); - else - as_first = (config.dwindle_vsplit == 2); - } - // split_h and lock are false, decided by width/height ratio + split_h = true; // horizontal split + as_first = (nx < 0); // left → new window on left + } + lock = true; // lock split direction + } else { + // normal mode, auto split + bool likely_h = (fg->width >= fg->height); + split_h = likely_h; + + if (likely_h) { + if (config.dwindle_hsplit == 0) + as_first = (cursor->x < fcx); + else + as_first = (config.dwindle_hsplit == 2); + } else { + if (config.dwindle_vsplit == 0) + as_first = (cursor->y < fcy); + else + as_first = (config.dwindle_vsplit == 2); } } + DwindleNode *target = focused ? dwindle_find_leaf(*root, focused) : NULL; + if (!target && *root) + target = dwindle_first_leaf(*root); + + // 当且仅当 manual_split=1 时,计算精确的 1/N 新节点比例 + if (config.dwindle_manual_split && target) { + split_h = target->custom_leaf_split_h; + lock = true; + as_first = false; + + // ================= 计算新节点的 1/N 比例 ================= + DwindleNode *path[128]; + float p[128]; + int path_len = get_block_path_and_ratios(target, split_h, path, p); + + int n_old = 1; + if (path_len > 1) { + n_old = count_block_items(path[path_len - 1], split_h); + } + float N = (float)(n_old + 1); + + float p_target_old = p[0]; + float p_split_new = p_target_old * (N - 1.0f) / N + 1.0f / N; + + if (as_first) { + ratio = (1.0f / N) / p_split_new; + } else { + ratio = (p_target_old * (N - 1.0f) / N) / p_split_new; + } + + if (ratio < 0.001f) + ratio = 0.001f; + if (ratio > 0.999f) + ratio = 0.999f; + // ========================================================= + } + + // 调用通用插入函数 dwindle_insert(root, new_c, focused, ratio, as_first, split_h, lock); } @@ -431,3 +584,8 @@ void dwindle(Monitor *m) { m->w.width - 2 * gap_oh, m->w.height - 2 * gap_ov, gap_ih, gap_iv); } + +void cleanup_monitor_dwindle(Monitor *m) { + for (uint32_t t = 0; t < LENGTH(tags) + 1; t++) + dwindle_free_tree(m->pertag->dwindle_root[t]); +} \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index f12d8e1c..8c11c318 100644 --- a/src/mango.c +++ b/src/mango.c @@ -317,6 +317,7 @@ struct Client { struct wlr_scene_tree *scene; struct wlr_scene_rect *border; /* top, bottom, left, right */ struct wlr_scene_rect *droparea; + struct wlr_scene_rect *splitindicator[4]; struct wlr_scene_shadow *shadow; struct wlr_scene_tree *scene_surface; struct wl_list link; @@ -561,6 +562,23 @@ typedef struct { } SessionLock; typedef struct DwindleNode DwindleNode; +struct DwindleNode { + bool is_split; + bool split_h; + bool split_locked; + bool custom_leaf_split_h; + float ratio; + float drag_init_ratio; + int32_t container_x; + int32_t container_y; + int32_t container_w; + int32_t container_h; + DwindleNode *parent; + DwindleNode *first; + DwindleNode *second; + Client *client; +}; + struct ScrollerStackNode { Client *client; float scroller_proportion; @@ -848,6 +866,7 @@ static struct ScrollerStackNode * scroller_node_create(struct TagScrollerState *st, Client *c); static void update_scroller_state(Monitor *m); Client *scroll_get_stack_tail_client(Client *c); +static DwindleNode *dwindle_find_leaf(DwindleNode *node, Client *c); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" @@ -1212,38 +1231,47 @@ 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) { + if (!w->mon) + return; + + const Layout *layout = w->mon->pertag->ltidxs[w->mon->pertag->curtag]; + + if (layout->id == DWINDLE || layout->id == SCROLLER || + layout->id == VERTICAL_SCROLLER) { + for (uint32_t t = 0; t < LENGTH(tags) + 1; t++) { /* dwindle */ - DwindleNode **root = &m->pertag->dwindle_root[t]; - dwindle_remove(root, c); - DwindleNode *dnode = dwindle_find_leaf(*root, w); - if (dnode) - dnode->client = c; - /* scroller */ - struct TagScrollerState *st = m->pertag->scroller_state[t]; - if (!st) - continue; + if (layout->id == DWINDLE) { - /* 先移除 c 在任意 tag 中的旧节点 */ - struct ScrollerStackNode *cn = find_scroller_node(st, c); - if (cn) - scroller_node_remove(st, cn); + DwindleNode **root = &w->mon->pertag->dwindle_root[t]; + dwindle_remove(root, c); + DwindleNode *dnode = dwindle_find_leaf(*root, w); + if (dnode) + dnode->client = c; + } - /* 将 w 的节点(如果存在)转给 c */ - struct ScrollerStackNode *wn = find_scroller_node(st, w); - if (wn) - wn->client = c; + // scroller + if (layout->id == SCROLLER || layout->id == VERTICAL_SCROLLER) { + struct TagScrollerState *st = w->mon->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); + if (layout->id == SCROLLER || layout->id == VERTICAL_SCROLLER) { + sync_scroller_state_to_clients(w->mon, w->mon->pertag->curtag); } } @@ -2503,9 +2531,7 @@ void cleanupmon(struct wl_listener *listener, void *data) { } m->wlr_output->data = NULL; - for (uint32_t t = 0; t < LENGTH(tags) + 1; t++) - dwindle_free_tree(m->pertag->dwindle_root[t]); - + cleanup_monitor_dwindle(m); cleanup_monitor_scroller(m); free(m->pertag); @@ -4334,6 +4360,7 @@ mapnotify(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ Client *at_client = NULL; Client *c = wl_container_of(listener, c, map); + int32_t i = 0; /* Create scene tree for this client and its border */ c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]); wlr_scene_node_set_enabled(&c->scene->node, c->type != XDGShell); @@ -4386,6 +4413,15 @@ mapnotify(struct wl_listener *listener, void *data) { // extra node + for (i = 0; i < 2; i++) { + c->splitindicator[i] = wlr_scene_rect_create( + c->scene, 0, 0, + c->isurgent ? config.urgentcolor : config.splitcolor); + c->splitindicator[i]->node.data = c; + wlr_scene_node_lower_to_bottom(&c->splitindicator[i]->node); + wlr_scene_node_set_enabled(&c->splitindicator[i]->node, false); + } + c->droparea = wlr_scene_rect_create(c->scene, 0, 0, config.dropcolor); wlr_scene_node_lower_to_bottom(&c->droparea->node); wlr_scene_node_set_position(&c->droparea->node, 0, 0); @@ -5168,18 +5204,27 @@ exchange_common: tmp2_next->prev = &c1->link; } - if (config.exchange_cross_monitor && c1->mon != c2->mon) { - DwindleNode **c1_root = &m1->pertag->dwindle_root[m1->pertag->curtag]; - DwindleNode *c1node = dwindle_find_leaf(*c1_root, c1); + const Layout *layout1 = c1->mon->pertag->ltidxs[c1->mon->pertag->curtag]; - DwindleNode **c2_root = &m2->pertag->dwindle_root[m2->pertag->curtag]; - DwindleNode *c2node = dwindle_find_leaf(*c2_root, c2); + const Layout *layout2 = c2->mon->pertag->ltidxs[c2->mon->pertag->curtag]; - if (c1node) - c1node->client = c2; + if (c1->mon != c2->mon) { - if (c2node) - c2node->client = c1; + if (layout1->id == DWINDLE && layout2->id == DWINDLE) { + 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; @@ -5191,8 +5236,7 @@ exchange_common: arrange(c1->mon, false, false); arrange(c2->mon, false, false); } else { - if (c1->mon && - c1->mon->pertag->ltidxs[c1->mon->pertag->curtag]->id == DWINDLE) { + if (layout1->id == DWINDLE && layout2->id == DWINDLE) { dwindle_swap_clients( &c1->mon->pertag->dwindle_root[c1->mon->pertag->curtag], c1, c2);