diff --git a/meson.build b/meson.build index 83b9513..d71eb59 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('mango', ['c', 'cpp'], - version : '0.10.10', + version : '0.11.0', ) subdir('protocols') diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 9c27872..15857ae 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -243,6 +243,8 @@ typedef struct { int32_t idleinhibit_ignore_visible; int32_t sloppyfocus; int32_t warpcursor; + int32_t drag_corner; + int32_t drag_warp_cursor; /* keyboard */ int32_t repeat_rate; @@ -1080,6 +1082,9 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "toggle_monitor") == 0) { func = toggle_monitor; (*arg).v = strdup(arg_value); + } else if (strcmp(func_name, "scroller_stack") == 0) { + func = scroller_stack; + (*arg).i = parse_direction(arg_value); } else { return NULL; } @@ -1472,6 +1477,10 @@ void parse_option(Config *config, char *key, char *value) { config->sloppyfocus = atoi(value); } else if (strcmp(key, "warpcursor") == 0) { config->warpcursor = atoi(value); + } else if (strcmp(key, "drag_corner") == 0) { + config->drag_corner = atoi(value); + } else if (strcmp(key, "drag_warp_cursor") == 0) { + config->drag_warp_cursor = atoi(value); } else if (strcmp(key, "smartgaps") == 0) { config->smartgaps = atoi(value); } else if (strcmp(key, "repeat_rate") == 0) { @@ -2754,6 +2763,8 @@ void override_config(void) { CLAMP_INT(config.idleinhibit_ignore_visible, 0, 1); sloppyfocus = CLAMP_INT(config.sloppyfocus, 0, 1); warpcursor = CLAMP_INT(config.warpcursor, 0, 1); + drag_corner = CLAMP_INT(config.drag_corner, 0, 4); + drag_warp_cursor = CLAMP_INT(config.drag_warp_cursor, 0, 1); focus_cross_monitor = CLAMP_INT(config.focus_cross_monitor, 0, 1); exchange_cross_monitor = CLAMP_INT(config.exchange_cross_monitor, 0, 1); scratchpad_cross_monitor = CLAMP_INT(config.scratchpad_cross_monitor, 0, 1); @@ -2951,6 +2962,8 @@ void set_value_default() { config.cursor_hide_timeout = cursor_hide_timeout; config.warpcursor = warpcursor; /* Warp cursor to focused client */ + config.drag_corner = drag_corner; + config.drag_warp_cursor = drag_warp_cursor; config.repeat_rate = repeat_rate; config.repeat_delay = repeat_delay; diff --git a/src/config/preset.h b/src/config/preset.h index 6f3cd89..935ea0c 100644 --- a/src/config/preset.h +++ b/src/config/preset.h @@ -101,7 +101,9 @@ int32_t overviewgappo = 30; /* overview时 窗口与窗口 缝隙大小 */ * behavior */ float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; -int32_t warpcursor = 1; /* Warp cursor to focused client */ +int32_t warpcursor = 1; +int32_t drag_corner = 3; +int32_t drag_warp_cursor = 1; int32_t xwayland_persistence = 1; /* xwayland persistence */ int32_t syncobj_enable = 0; int32_t adaptive_sync = 0; diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h index b197778..22ef612 100644 --- a/src/dispatch/bind_declare.h +++ b/src/dispatch/bind_declare.h @@ -68,4 +68,5 @@ int32_t toggle_trackpad_enable(const Arg *arg); int32_t setoption(const Arg *arg); int32_t disable_monitor(const Arg *arg); int32_t enable_monitor(const Arg *arg); -int32_t toggle_monitor(const Arg *arg); \ No newline at end of file +int32_t toggle_monitor(const Arg *arg); +int32_t scroller_stack(const Arg *arg); \ No newline at end of file diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 19d743e..f27ed67 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -129,6 +129,7 @@ int32_t exchange_stack_client(const Arg *arg) { int32_t focusdir(const Arg *arg) { Client *c = NULL; c = direction_select(arg); + c = get_focused_stack_client(c); if (c) { focusclient(c, 1); if (warpcursor) @@ -337,6 +338,9 @@ int32_t killclient(const Arg *arg) { } int32_t moveresize(const Arg *arg) { + const char *cursors[] = {"nw-resize", "ne-resize", "sw-resize", + "se-resize"}; + if (cursor_mode != CurNormal && cursor_mode != CurPressed) return 0; xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); @@ -362,10 +366,29 @@ int32_t moveresize(const Arg *arg) { /* Doesn't work for X11 output - the next absolute motion event * returns the cursor to where it started */ if (grabc->isfloating) { - wlr_cursor_warp_closest(cursor, NULL, - grabc->geom.x + grabc->geom.width, - grabc->geom.y + grabc->geom.height); - wlr_cursor_set_xcursor(cursor, cursor_mgr, "bottom_right_corner"); + rzcorner = drag_corner; + grabcx = (int)round(cursor->x); + grabcy = (int)round(cursor->y); + if (rzcorner == 4) + /* identify the closest corner index */ + rzcorner = (grabcx - grabc->geom.x < + grabc->geom.x + grabc->geom.width - grabcx + ? 0 + : 1) + + (grabcy - grabc->geom.y < + grabc->geom.y + grabc->geom.height - grabcy + ? 0 + : 2); + + if (drag_warp_cursor) { + grabcx = rzcorner & 1 ? grabc->geom.x + grabc->geom.width + : grabc->geom.x; + grabcy = rzcorner & 2 ? grabc->geom.y + grabc->geom.height + : grabc->geom.y; + wlr_cursor_warp_closest(cursor, NULL, grabcx, grabcy); + } + + wlr_cursor_set_xcursor(cursor, cursor_mgr, cursors[rzcorner]); } else { wlr_cursor_set_xcursor(cursor, cursor_mgr, "grab"); } @@ -425,6 +448,10 @@ int32_t resizewin(const Arg *arg) { if (!c || c->isfullscreen || c->ismaximizescreen) return 0; + int32_t animations_state_backup = animations; + if (!c->isfloating) + animations = 0; + if (ISTILED(c)) { switch (arg->ui) { case NUM_TYPE_MINUS: @@ -450,6 +477,7 @@ int32_t resizewin(const Arg *arg) { break; } resize_tile_client(c, false, offsetx, offsety, 0); + animations = animations_state_backup; return 0; } @@ -480,6 +508,7 @@ int32_t resizewin(const Arg *arg) { c->iscustomsize = 1; c->float_geom = c->geom; resize(c, c->geom, 0); + animations = animations_state_backup; return 0; } @@ -550,12 +579,14 @@ int32_t set_proportion(const Arg *arg) { !scroller_ignore_proportion_single) return 0; - if (selmon->sel) { + Client *tc = selmon->sel; + + if (tc) { + tc = get_scroll_stack_head(tc); uint32_t max_client_width = selmon->w.width - 2 * scroller_structs - gappih; - selmon->sel->scroller_proportion = arg->f; - selmon->sel->geom.width = max_client_width * arg->f; - // resize(selmon->sel, selmon->sel->geom, 0); + tc->scroller_proportion = arg->f; + tc->geom.width = max_client_width * arg->f; arrange(selmon, false, false); } return 0; @@ -749,10 +780,13 @@ int32_t centerwin(const Arg *arg) { if (!is_scroller_layout(selmon)) return 0; + Client *stack_head = get_scroll_stack_head(c); if (selmon->pertag->ltidxs[selmon->pertag->curtag]->id == SCROLLER) { - c->geom.x = selmon->w.x + (selmon->w.width - c->geom.width) / 2; + stack_head->geom.x = + selmon->w.x + (selmon->w.width - stack_head->geom.width) / 2; } else { - c->geom.y = selmon->w.y + (selmon->w.height - c->geom.height) / 2; + stack_head->geom.y = + selmon->w.y + (selmon->w.height - stack_head->geom.height) / 2; } arrange(selmon, false, false); @@ -971,11 +1005,13 @@ int32_t switch_proportion_preset(const Arg *arg) { !scroller_ignore_proportion_single) return 0; - if (selmon->sel) { + 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] == - selmon->sel->scroller_proportion) { + tc->scroller_proportion) { if (i == config.scroller_proportion_preset_count - 1) { target_proportion = config.scroller_proportion_preset[0]; break; @@ -993,9 +1029,8 @@ int32_t switch_proportion_preset(const Arg *arg) { uint32_t max_client_width = selmon->w.width - 2 * scroller_structs - gappih; - selmon->sel->scroller_proportion = target_proportion; - selmon->sel->geom.width = max_client_width * target_proportion; - // resize(selmon->sel, selmon->sel->geom, 0); + tc->scroller_proportion = target_proportion; + tc->geom.width = max_client_width * target_proportion; arrange(selmon, false, false); } return 0; @@ -1093,6 +1128,7 @@ 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; @@ -1221,9 +1257,11 @@ int32_t toggleglobal(const Arg *arg) { selmon->sel->isnamedscratchpad = 0; } selmon->sel->isglobal ^= 1; - // selmon->sel->tags = - // selmon->sel->isglobal ? TAGMASK : selmon->tagset[selmon->seltags]; - // focustop(selmon); + 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; } @@ -1585,3 +1623,106 @@ int32_t toggle_monitor(const Arg *arg) { } return 0; } + +int32_t scroller_stack(const Arg *arg) { + Client *c = selmon->sel; + Client *stack_head = NULL; + Client *source_stack_head = NULL; + if (!c || !c->mon || c->isfloating || !is_scroller_layout(selmon)) + return 0; + + if (c && (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal)) + return 0; + + bool is_horizontal_layout = + c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == SCROLLER ? true + : false; + + Client *target_client = find_client_by_direction(c, arg, false, true); + + if (target_client && (!client_only_in_one_tag(target_client) || + target_client->isglobal || target_client->isunglobal)) + return 0; + + if (target_client) { + stack_head = get_scroll_stack_head(target_client); + } + + if (c) { + source_stack_head = get_scroll_stack_head(c); + } + + if (stack_head == source_stack_head) { + return 0; + } + + if (c->isfullscreen) { + setfullscreen(c, 0); + } + + if (c->ismaximizescreen) { + setmaximizescreen(c, 0); + } + + if (c->prev_in_stack) { + if ((is_horizontal_layout && arg->i == LEFT) || + (!is_horizontal_layout && arg->i == UP)) { + exit_scroller_stack(c); + wl_list_remove(&c->link); + wl_list_insert(source_stack_head->link.prev, &c->link); + arrange(selmon, false, false); + + } else if ((is_horizontal_layout && arg->i == RIGHT) || + (!is_horizontal_layout && arg->i == DOWN)) { + exit_scroller_stack(c); + wl_list_remove(&c->link); + wl_list_insert(&source_stack_head->link, &c->link); + arrange(selmon, false, false); + } + return 0; + } else if (c->next_in_stack) { + Client *next_in_stack = c->next_in_stack; + if ((is_horizontal_layout && arg->i == LEFT) || + (!is_horizontal_layout && arg->i == 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 && arg->i == RIGHT) || + (!is_horizontal_layout && arg->i == 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) { + return 0; + } + + exit_scroller_stack(c); + + // 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; + } + + // Add c to the stack + stack_tail->next_in_stack = c; + c->prev_in_stack = stack_tail; + c->next_in_stack = NULL; + + if (stack_head->ismaximizescreen) { + setmaximizescreen(stack_head, 0); + } + + if (stack_head->isfullscreen) { + setfullscreen(stack_head, 0); + } + + arrange(selmon, false, false); + return 0; +} \ No newline at end of file diff --git a/src/fetch/client.h b/src/fetch/client.h index 0a8f342..9802340 100644 --- a/src/fetch/client.h +++ b/src/fetch/client.h @@ -214,6 +214,27 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating, } } } + if (!tempFocusClients) { + for (int32_t _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.y < sel_y && + tempClients[_i]->mon == tc->mon && + client_is_in_same_stack(tc, tempClients[_i], NULL)) { + int32_t dis_x = tempClients[_i]->geom.x - sel_x; + int32_t dis_y = tempClients[_i]->geom.y - sel_y; + int64_t tmp_distance = + dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + if (tempClients[_i]->mon == tc->mon && + tmp_distance < same_monitor_distance) { + same_monitor_distance = tmp_distance; + tempSameMonitorFocusClients = tempClients[_i]; + } + } + } + } if (!tempFocusClients) { for (int32_t _i = 0; _i <= last; _i++) { if (tempClients[_i]->geom.y < sel_y) { @@ -251,6 +272,27 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating, } } } + if (!tempFocusClients) { + for (int32_t _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.y > sel_y && + tempClients[_i]->mon == tc->mon && + client_is_in_same_stack(tc, tempClients[_i], NULL)) { + int32_t dis_x = tempClients[_i]->geom.x - sel_x; + int32_t dis_y = tempClients[_i]->geom.y - sel_y; + int64_t tmp_distance = + dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + if (tempClients[_i]->mon == tc->mon && + tmp_distance < same_monitor_distance) { + same_monitor_distance = tmp_distance; + tempSameMonitorFocusClients = tempClients[_i]; + } + } + } + } if (!tempFocusClients) { for (int32_t _i = 0; _i <= last; _i++) { if (tempClients[_i]->geom.y > sel_y) { @@ -288,6 +330,27 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating, } } } + if (!tempFocusClients) { + for (int32_t _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.x < sel_x && + tempClients[_i]->mon == tc->mon && + client_is_in_same_stack(tc, tempClients[_i], NULL)) { + int32_t dis_x = tempClients[_i]->geom.x - sel_x; + int32_t dis_y = tempClients[_i]->geom.y - sel_y; + int64_t tmp_distance = + dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + if (tempClients[_i]->mon == tc->mon && + tmp_distance < same_monitor_distance) { + same_monitor_distance = tmp_distance; + tempSameMonitorFocusClients = tempClients[_i]; + } + } + } + } if (!tempFocusClients) { for (int32_t _i = 0; _i <= last; _i++) { if (tempClients[_i]->geom.x < sel_x) { @@ -325,6 +388,27 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating, } } } + if (!tempFocusClients) { + for (int32_t _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.x > sel_x && + tempClients[_i]->mon == tc->mon && + client_is_in_same_stack(tc, tempClients[_i], NULL)) { + int32_t dis_x = tempClients[_i]->geom.x - sel_x; + int32_t dis_y = tempClients[_i]->geom.y - sel_y; + int64_t tmp_distance = + dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + if (tempClients[_i]->mon == tc->mon && + tmp_distance < same_monitor_distance) { + same_monitor_distance = tmp_distance; + tempSameMonitorFocusClients = tempClients[_i]; + } + } + } + } if (!tempFocusClients) { for (int32_t _i = 0; _i <= last; _i++) { if (tempClients[_i]->geom.x > sel_x) { @@ -368,7 +452,9 @@ Client *direction_select(const Arg *arg) { } return find_client_by_direction( - tc, arg, true, is_scroller_layout(selmon) && !selmon->isoverview); + tc, arg, true, + (is_scroller_layout(selmon) || is_centertile_layout(selmon)) && + !selmon->isoverview); } /* We probably should change the name of this, it sounds like @@ -447,3 +533,90 @@ bool client_only_in_one_tag(Client *c) { return false; } } + +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; + + uint32_t id = sc->mon->pertag->ltidxs[sc->mon->pertag->curtag]->id; + + if (id != SCROLLER && id != VERTICAL_SCROLLER && id != TILE && + id != VERTICAL_TILE && id != DECK && id != VERTICAL_DECK && + id != CENTER_TILE && id != RIGHT_TILE && id != TGMIX) + 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) + return false; + if (source_stack_head == target_stack_head) + return true; + else + return false; + } + + if (id == TILE || id == VERTICAL_TILE || id == DECK || + id == VERTICAL_DECK || id == RIGHT_TILE) { + if (fc && !fc->ismaster) + return false; + else if (!sc->ismaster) + return true; + } + + if (id == TGMIX) { + if (fc && !fc->ismaster) + return false; + if (!sc->ismaster && sc->mon->visible_tiling_clients <= 3) + return true; + } + + if (id == CENTER_TILE) { + if (fc && !fc->ismaster) + return false; + if (!sc->ismaster && sc->geom.x == tc->geom.x) + return true; + else + return false; + } + + return false; +} + +Client *get_focused_stack_client(Client *sc) { + if (!sc || sc->isfloating) + return sc; + + Client *tc = NULL; + Client *fc = focustop(sc->mon); + + if (fc->isfloating || sc->isfloating) + return sc; + + wl_list_for_each(tc, &fstack, flink) { + if (tc->iskilling || tc->isunglobal) + continue; + if (!VISIBLEON(tc, sc->mon)) + continue; + if (tc == fc) + continue; + + if (client_is_in_same_stack(sc, tc, fc)) { + return tc; + } + } + return sc; +} diff --git a/src/fetch/monitor.h b/src/fetch/monitor.h index 47a5b82..7a1ca4d 100644 --- a/src/fetch/monitor.h +++ b/src/fetch/monitor.h @@ -26,6 +26,14 @@ bool is_scroller_layout(Monitor *m) { return false; } +bool is_centertile_layout(Monitor *m) { + + if (m->pertag->ltidxs[m->pertag->curtag]->id == CENTER_TILE) + return true; + + return false; +} + uint32_t get_tag_status(uint32_t tag, Monitor *m) { Client *c = NULL; uint32_t status = 0; diff --git a/src/layout/arrange.h b/src/layout/arrange.h index d668f30..6ff1dd2 100644 --- a/src/layout/arrange.h +++ b/src/layout/arrange.h @@ -378,6 +378,8 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx, int32_t offsety, uint32_t time, bool isvertical) { 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 && !scroller_ignore_proportion_single) @@ -389,7 +391,8 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx, start_drag_window = true; // 记录初始状态 - grabc->old_scroller_pproportion = grabc->scroller_proportion; + stack_head->old_scroller_pproportion = stack_head->scroller_proportion; + grabc->old_stack_proportion = grabc->stack_proportion; grabc->cursor_in_left_half = cursor->x < grabc->geom.x + grabc->geom.width / 2; @@ -409,15 +412,26 @@ 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; - grabc->old_scroller_pproportion = grabc->scroller_proportion; + stack_head->old_scroller_pproportion = + stack_head->scroller_proportion; + grabc->old_stack_proportion = grabc->stack_proportion; grabc->cursor_in_upper_half = false; grabc->cursor_in_left_half = false; } - delta_x = (float)(offsetx) * (grabc->old_scroller_pproportion) / - grabc->drag_begin_geom.width; - delta_y = (float)(offsety) * (grabc->old_scroller_pproportion) / - grabc->drag_begin_geom.height; + if (isvertical) { + delta_y = (float)(offsety) * + (stack_head->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) / + grabc->drag_begin_geom.width; + delta_y = (float)(offsety) * (grabc->old_stack_proportion) / + grabc->drag_begin_geom.height; + } bool moving_up; bool moving_down; @@ -452,18 +466,81 @@ void resize_tile_scroller(Client *grabc, bool isdrag, int32_t offsetx, delta_x = -fabsf(delta_x); } + if (isvertical) { + if (!grabc->next_in_stack && grabc->prev_in_stack && !isdrag) { + delta_x = delta_x * -1.0f; + } + if (!grabc->next_in_stack && grabc->prev_in_stack && isdrag) { + if (moving_right) { + delta_x = -fabsf(delta_x); + } else { + delta_x = fabsf(delta_x); + } + } + if (!grabc->prev_in_stack && grabc->next_in_stack && isdrag) { + if (moving_left) { + delta_x = -fabsf(delta_x); + } else { + delta_x = fabsf(delta_x); + } + } + + if (isdrag) { + if (moving_up) { + delta_y = -fabsf(delta_y); + } else { + delta_y = fabsf(delta_y); + } + } + + } else { + if (!grabc->next_in_stack && grabc->prev_in_stack && !isdrag) { + delta_y = delta_y * -1.0f; + } + if (!grabc->next_in_stack && grabc->prev_in_stack && isdrag) { + if (moving_down) { + delta_y = -fabsf(delta_y); + } else { + delta_y = fabsf(delta_y); + } + } + if (!grabc->prev_in_stack && grabc->next_in_stack && isdrag) { + if (moving_up) { + delta_y = -fabsf(delta_y); + } else { + delta_y = fabsf(delta_y); + } + } + + if (isdrag) { + if (moving_left) { + delta_x = -fabsf(delta_x); + } else { + delta_x = fabsf(delta_x); + } + } + } + // 直接设置新的比例,基于初始值 + 变化量 if (isvertical) { - new_scroller_proportion = grabc->old_scroller_pproportion + delta_y; + new_scroller_proportion = + stack_head->old_scroller_pproportion + delta_y; + new_stack_proportion = grabc->old_stack_proportion + delta_x; + } else { - new_scroller_proportion = grabc->old_scroller_pproportion + delta_x; + new_scroller_proportion = + stack_head->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(1.0f, new_stack_proportion)); - grabc->scroller_proportion = new_scroller_proportion; + grabc->stack_proportion = new_stack_proportion; + + stack_head->scroller_proportion = new_scroller_proportion; if (!isdrag) { arrange(grabc->mon, false, false); @@ -605,14 +682,20 @@ arrange(Monitor *m, bool want_animation, bool from_view) { wl_list_for_each(c, &clients, link) { + if (!client_only_in_one_tag(c) || c->isglobal || c->isunglobal) { + exit_scroller_stack(c); + } + if (from_view && (c->isglobal || c->isunglobal)) { set_size_per(m, c); } if (c->mon == m && (c->isglobal || c->isunglobal)) { c->tags = m->tagset[m->seltags]; - if (c->mon->sel == NULL) - focusclient(c, 0); + } + + if (from_view && m->sel == NULL && c->isglobal && VISIBLEON(c, m)) { + focusclient(c, 1); } if (VISIBLEON(c, m)) { @@ -627,7 +710,7 @@ arrange(Monitor *m, bool want_animation, bool from_view) { m->visible_tiling_clients++; } - if (ISSCROLLTILED(c)) { + if (ISSCROLLTILED(c) && !c->prev_in_stack) { m->visible_scroll_tiling_clients++; } } diff --git a/src/layout/horizontal.h b/src/layout/horizontal.h index a2f5e5c..87b6033 100644 --- a/src/layout/horizontal.h +++ b/src/layout/horizontal.h @@ -212,6 +212,66 @@ void horizontal_scroll_adjust_fullandmax(Client *c, 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; @@ -225,6 +285,7 @@ void scroller(Monitor *m) { int32_t cur_gappih = enablegaps ? m->gappih : 0; int32_t cur_gappoh = enablegaps ? m->gappoh : 0; int32_t cur_gappov = enablegaps ? m->gappov : 0; + int32_t cur_gappiv = enablegaps ? m->gappiv : 0; cur_gappih = smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappih; @@ -251,7 +312,7 @@ void scroller(Monitor *m) { // 第二次遍历,填充 tempClients j = 0; wl_list_for_each(c, &clients, link) { - if (VISIBLEON(c, m) && ISSCROLLTILED(c)) { + if (VISIBLEON(c, m) && ISSCROLLTILED(c) && !c->prev_in_stack) { tempClients[j] = c; j++; } @@ -269,7 +330,8 @@ void scroller(Monitor *m) { 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; - resize(c, target_geom, 0); + horizontal_check_scroller_root_inside_mon(c, &target_geom); + arrange_stack(c, target_geom, cur_gappiv); free(tempClients); // 释放内存 return; } @@ -283,6 +345,11 @@ void scroller(Monitor *m) { 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; @@ -317,10 +384,14 @@ void scroller(Monitor *m) { &target_geom); if (tempClients[focus_client_index]->isfullscreen) { target_geom.x = m->m.x; - resize(tempClients[focus_client_index], target_geom, 0); + 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; - resize(tempClients[focus_client_index], target_geom, 0); + 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 (scroller_focus_center || ((!m->prevsel || @@ -338,10 +409,14 @@ void scroller(Monitor *m) { scroller_structs) : m->w.x + scroller_structs; } - resize(tempClients[focus_client_index], target_geom, 0); + 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; - resize(tempClients[focus_client_index], target_geom, 0); + 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++) { @@ -351,7 +426,7 @@ void scroller(Monitor *m) { target_geom.x = tempClients[focus_client_index - i + 1]->geom.x - cur_gappih - target_geom.width; - resize(c, target_geom, 0); + arrange_stack(c, target_geom, cur_gappiv); } for (i = 1; i < n - focus_client_index; i++) { @@ -361,7 +436,7 @@ void scroller(Monitor *m) { target_geom.x = tempClients[focus_client_index + i - 1]->geom.x + cur_gappih + tempClients[focus_client_index + i - 1]->geom.width; - resize(c, target_geom, 0); + arrange_stack(c, target_geom, cur_gappiv); } free(tempClients); // 最后释放内存 @@ -853,4 +928,4 @@ void tgmix(Monitor *m) { grid(m); return; } -} \ No newline at end of file +} diff --git a/src/layout/vertical.h b/src/layout/vertical.h index 9513824..2c2dc66 100644 --- a/src/layout/vertical.h +++ b/src/layout/vertical.h @@ -199,6 +199,66 @@ void vertical_scroll_adjust_fullandmax(Client *c, struct wlr_box *target_geom) { 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; @@ -212,6 +272,7 @@ void vertical_scroller(Monitor *m) { 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 = smartgaps && m->visible_scroll_tiling_clients == 1 ? 0 : cur_gappiv; @@ -235,7 +296,7 @@ void vertical_scroller(Monitor *m) { j = 0; wl_list_for_each(c, &clients, link) { - if (VISIBLEON(c, m) && ISSCROLLTILED(c)) { + if (VISIBLEON(c, m) && ISSCROLLTILED(c) && !c->prev_in_stack) { tempClients[j] = c; j++; } @@ -253,7 +314,8 @@ void vertical_scroller(Monitor *m) { 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; - resize(c, target_geom, 0); + vertical_check_scroller_root_inside_mon(c, &target_geom); + arrange_stack_vertical(c, target_geom, cur_gappih); free(tempClients); return; } @@ -267,6 +329,11 @@ void vertical_scroller(Monitor *m) { 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; @@ -302,10 +369,16 @@ void vertical_scroller(Monitor *m) { if (tempClients[focus_client_index]->isfullscreen) { target_geom.y = m->m.y; - resize(tempClients[focus_client_index], target_geom, 0); + 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; - resize(tempClients[focus_client_index], target_geom, 0); + 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 (scroller_focus_center || ((!m->prevsel || @@ -323,10 +396,16 @@ void vertical_scroller(Monitor *m) { scroller_structs) : m->w.y + scroller_structs; } - resize(tempClients[focus_client_index], target_geom, 0); + 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; - resize(tempClients[focus_client_index], target_geom, 0); + 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++) { @@ -336,7 +415,7 @@ void vertical_scroller(Monitor *m) { target_geom.y = tempClients[focus_client_index - i + 1]->geom.y - cur_gappiv - target_geom.height; - resize(c, target_geom, 0); + arrange_stack_vertical(c, target_geom, cur_gappih); } for (i = 1; i < n - focus_client_index; i++) { @@ -346,7 +425,7 @@ void vertical_scroller(Monitor *m) { target_geom.y = tempClients[focus_client_index + i - 1]->geom.y + cur_gappiv + tempClients[focus_client_index + i - 1]->geom.height; - resize(c, target_geom, 0); + arrange_stack_vertical(c, target_geom, cur_gappih); } free(tempClients); diff --git a/src/mango.c b/src/mango.c index ac5edf9..f61e27f 100644 --- a/src/mango.c +++ b/src/mango.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -101,6 +102,10 @@ (A->geom.x >= A->mon->m.x && A->geom.y >= A->mon->m.y && \ A->geom.x + A->geom.width <= A->mon->m.x + A->mon->m.width && \ A->geom.y + A->geom.height <= A->mon->m.y + A->mon->m.height) +#define GEOMINSIDEMON(A, M) \ + (A->x >= M->m.x && A->y >= M->m.y && \ + A->x + A->width <= M->m.x + M->m.width && \ + A->y + A->height <= M->m.y + M->m.height) #define ISTILED(A) \ (A && !(A)->isfloating && !(A)->isminimized && !(A)->iskilling && \ !(A)->ismaximizescreen && !(A)->isfullscreen && !(A)->isunglobal) @@ -375,6 +380,8 @@ struct Client { bool is_pending_open_animation; bool is_restoring_from_ov; float scroller_proportion; + float stack_proportion; + float old_stack_proportion; bool need_output_flush; struct dwl_animation animation; struct dwl_opacity_animation opacity_animation; @@ -407,6 +414,8 @@ struct Client { int32_t allow_shortcuts_inhibit; float scroller_proportion_single; bool isfocusing; + struct Client *next_in_stack; + struct Client *prev_in_stack; }; typedef struct { @@ -743,6 +752,7 @@ static struct wlr_scene_tree * wlr_scene_tree_snapshot(struct wlr_scene_node *node, struct wlr_scene_tree *parent); static bool is_scroller_layout(Monitor *m); +static bool is_centertile_layout(Monitor *m); static void create_output(struct wlr_backend *backend, void *data); static void get_layout_abbr(char *abbr, const char *full_name); static void apply_named_scratchpad(Client *target_client); @@ -762,6 +772,13 @@ static void init_client_properties(Client *c); static float *get_border_color(Client *c); static void clear_fullscreen_and_maximized_state(Monitor *m); static void request_fresh_all_monitors(void); +static Client *find_client_by_direction(Client *tc, const Arg *arg, + bool findfloating, bool ignore_align); +static void exit_scroller_stack(Client *c); +static Client *get_scroll_stack_head(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); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" @@ -824,6 +841,7 @@ static struct wl_list inputdevices; static struct wl_list keyboard_shortcut_inhibitors; static uint32_t cursor_mode; static Client *grabc; +static int32_t rzcorner; static int32_t grabcx, grabcy; /* client-relative */ static int32_t drag_begin_cursorx, drag_begin_cursory; /* client-relative */ static bool start_drag_window = false; @@ -1053,6 +1071,13 @@ void swallow(Client *c, Client *w) { c->geom = w->geom; c->float_geom = w->float_geom; c->scroller_proportion = w->scroller_proportion; + c->next_in_stack = w->next_in_stack; + c->prev_in_stack = w->prev_in_stack; + 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; + c->stack_proportion = w->stack_proportion; wl_list_insert(&w->link, &c->link); wl_list_insert(&w->flink, &c->flink); @@ -3636,7 +3661,8 @@ void keypressmod(struct wl_listener *listener, void *data) { } void pending_kill_client(Client *c) { - // c->iskilling = 1; //不可以提前标记已经杀掉,因为有些客户端可能拒绝 + if (!c || c->iskilling) + return; client_send_close(c); } @@ -3744,6 +3770,9 @@ void init_client_properties(Client *c) { c->float_geom.height = 0; 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; } void // old fix to 0.5 @@ -3823,7 +3852,7 @@ mapnotify(struct wl_listener *listener, void *data) { if (selmon->sel && ISSCROLLTILED(selmon->sel) && VISIBLEON(selmon->sel, selmon)) { - at_client = selmon->sel; + at_client = get_scroll_stack_head(selmon->sel); } else { at_client = center_tiled_select(selmon); } @@ -3960,6 +3989,30 @@ void motionabsolute(struct wl_listener *listener, void *data) { motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy); } +void resize_floating_window(Client *grabc) { + int cdx = (int)round(cursor->x) - grabcx; + int cdy = (int)round(cursor->y) - grabcy; + + cdx = !(rzcorner & 1) && grabc->geom.width - 2 * (int)grabc->bw - cdx < 1 + ? 0 + : cdx; + cdy = !(rzcorner & 2) && grabc->geom.height - 2 * (int)grabc->bw - cdy < 1 + ? 0 + : cdy; + + const struct wlr_box box = { + .x = grabc->geom.x + (rzcorner & 1 ? 0 : cdx), + .y = grabc->geom.y + (rzcorner & 2 ? 0 : cdy), + .width = grabc->geom.width + (rzcorner & 1 ? cdx : -cdx), + .height = grabc->geom.height + (rzcorner & 2 ? cdy : -cdy)}; + + grabc->float_geom = box; + + resize(grabc, box, 1); + grabcx += cdx; + grabcy += cdy; +} + void motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, double dx_unaccel, double dy_unaccel) { double sx = 0, sy = 0, sx_confined, sy_confined; @@ -4037,14 +4090,9 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx, } else if (cursor_mode == CurResize) { if (grabc->isfloating) { grabc->iscustomsize = 1; - grabc->float_geom = (struct wlr_box){ - .x = grabc->geom.x, - .y = grabc->geom.y, - .width = (int32_t)round(cursor->x) - grabc->geom.x, - .height = (int32_t)round(cursor->y) - grabc->geom.y}; if (last_apply_drap_time == 0 || time - last_apply_drap_time > drag_refresh_interval) { - resize(grabc, grabc->float_geom, 1); + resize_floating_window(grabc); last_apply_drap_time = time; } return; @@ -4175,8 +4223,8 @@ void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time) { struct timespec now; - if (sloppyfocus && c && time && c->scene->node.enabled && - !c->animation.tagining && + if (sloppyfocus && !start_drag_window && c && time && + c->scene->node.enabled && !c->animation.tagining && (surface != seat->pointer_state.focused_surface) && !client_is_unmanaged(c) && VISIBLEON(c, c->mon)) focusclient(c, 0); @@ -4353,12 +4401,32 @@ void exchange_two_client(Client *c1, Client *c2) { 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; if (c1 == NULL || c2 == NULL || (!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; @@ -4371,17 +4439,47 @@ void exchange_two_client(Client *c1, Client *c2) { c2->master_mfact_per = master_mfact_per; c2->stack_inner_per = stack_inner_per; + // 交换栈链表连接 + Client *tmp1_next_in_stack = c1->next_in_stack; + Client *tmp1_prev_in_stack = c1->prev_in_stack; + Client *tmp2_next_in_stack = c2->next_in_stack; + Client *tmp2_prev_in_stack = c2->prev_in_stack; + + // 处理相邻节点的情况 + if (c1->next_in_stack == c2) { + c1->next_in_stack = tmp2_next_in_stack; + c2->next_in_stack = c1; + c1->prev_in_stack = c2; + c2->prev_in_stack = tmp1_prev_in_stack; + if (tmp1_prev_in_stack) + tmp1_prev_in_stack->next_in_stack = c2; + if (tmp2_next_in_stack) + tmp2_next_in_stack->prev_in_stack = c1; + } else if (c2->next_in_stack == c1) { + c2->next_in_stack = tmp1_next_in_stack; + c1->next_in_stack = c2; + c2->prev_in_stack = c1; + c1->prev_in_stack = tmp2_prev_in_stack; + if (tmp2_prev_in_stack) + tmp2_prev_in_stack->next_in_stack = c1; + if (tmp1_next_in_stack) + tmp1_next_in_stack->prev_in_stack = c2; + } else if (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; + } + + // 交换全局链表连接 struct wl_list *tmp1_prev = c1->link.prev; struct wl_list *tmp2_prev = c2->link.prev; struct wl_list *tmp1_next = c1->link.next; struct wl_list *tmp2_next = c2->link.next; - // wl_list - // 是双向链表,其中clients是头部节点,它的下一个节点是第一个客户端的链表节点 - // 最后一个客户端的链表节点的下一个节点也指向clients,但clients本身不是客户端的链表节点 - // 客户端遍历从clients的下一个节点开始,到检测到客户端节点的下一个是clients结束 - - // 当c1和c2为相邻节点时 + // 处理相邻节点的情况 if (c1->link.next == &c2->link) { c1->link.next = c2->link.next; c1->link.prev = &c2->link; @@ -4408,6 +4506,7 @@ void exchange_two_client(Client *c1, Client *c2) { tmp2_next->prev = &c1->link; } + // 处理跨监视器交换 if (exchange_cross_monitor) { tmp_mon = c2->mon; tmp_tags = c2->tags; @@ -4418,7 +4517,6 @@ void exchange_two_client(Client *c1, Client *c2) { focusclient(c1, 0); } else { arrange(c1->mon, false, false); - focusclient(c1, 0); } } @@ -4538,6 +4636,8 @@ setfloating(Client *c, int32_t floating) { c->bw = c->isnoborder ? 0 : borderpx; } + exit_scroller_stack(c); + // 重新计算居中的坐标 if (!client_is_x11(c) && !c->iscustompos) target_box = @@ -4609,6 +4709,27 @@ void reset_maximizescreen_size(Client *c) { resize(c, c->geom, 0); } +void exit_scroller_stack(Client *c) { + // If c is already in a stack, remove it. + if (c->prev_in_stack) { + c->prev_in_stack->next_in_stack = c->next_in_stack; + } + + if (!c->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); + } + + 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) { struct wlr_box maximizescreen_box; if (!c || !c->mon || !client_surface(c)->mapped || c->iskilling) @@ -4624,6 +4745,8 @@ void setmaximizescreen(Client *c, int32_t maximizescreen) { if (c->isfullscreen) setfullscreen(c, 0); + exit_scroller_stack(c); + if (c->isfloating) c->float_geom = c->geom; @@ -4661,10 +4784,11 @@ void setfakefullscreen(Client *c, int32_t fakefullscreen) { c->isfakefullscreen = fakefullscreen; if (!c->mon) return; + if (c->isfullscreen) setfullscreen(c, 0); - else - client_set_fullscreen(c, fakefullscreen); + + client_set_fullscreen(c, fakefullscreen); } void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自带全屏 @@ -4684,9 +4808,13 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自 if (c->ismaximizescreen) setmaximizescreen(c, 0); + exit_scroller_stack(c); + if (c->isfloating) c->float_geom = c->geom; + c->isfakefullscreen = 0; + c->bw = 0; wlr_scene_node_raise_to_top(&c->scene->node); // 将视图提升到顶层 if (!is_scroller_layout(c->mon) || c->isfloating) @@ -4695,7 +4823,6 @@ void setfullscreen(Client *c, int32_t fullscreen) // 用自定义全屏代理自 } else { c->bw = c->isnoborder ? 0 : borderpx; c->isfullscreen = 0; - c->isfakefullscreen = 0; if (c->isfloating) setfloating(c, 1); } @@ -5257,6 +5384,7 @@ 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; @@ -5404,15 +5532,22 @@ void unmapnotify(struct wl_listener *listener, void *data) { */ Client *c = wl_container_of(listener, c, unmap); Monitor *m = NULL; + Client *nextfocus = NULL; + Client *next_in_stack = c->next_in_stack; + Client *prev_in_stack = c->prev_in_stack; c->iskilling = 1; if (animations && !c->is_clip_to_hide && !c->isminimized && (!c->mon || VISIBLEON(c, c->mon))) init_fadeout_client(c); + // If the client is in a stack, remove it from the stack + if (c->swallowedby) { c->swallowedby->mon = c->mon; swallow(c->swallowedby, c); + } else { + exit_scroller_stack(c); } if (c == grabc) { @@ -5433,7 +5568,13 @@ void unmapnotify(struct wl_listener *listener, void *data) { } if (c->mon && c->mon == selmon) { - Client *nextfocus = focustop(selmon); + if (next_in_stack && !c->swallowedby) { + nextfocus = next_in_stack; + } else if (prev_in_stack && !c->swallowedby) { + nextfocus = prev_in_stack; + } else { + nextfocus = focustop(selmon); + } if (nextfocus) { focusclient(nextfocus, 0); @@ -5480,6 +5621,10 @@ void unmapnotify(struct wl_listener *listener, void *data) { c->swallowing = NULL; } + c->stack_proportion = 0.0f; + c->next_in_stack = NULL; + c->prev_in_stack = NULL; + wlr_scene_node_destroy(&c->scene->node); printstatus(); motionnotify(0, NULL, 0, 0, 0, 0);