feat: support group

This commit is contained in:
DreamMaoMao 2026-06-20 18:44:28 +08:00
parent 0fc7559c3c
commit d1cab2c4b7
14 changed files with 483 additions and 233 deletions

View file

@ -414,7 +414,6 @@ struct Client {
int32_t istagswitching;
int32_t isnamedscratchpad;
int32_t shield_when_capture;
bool is_monocle_hide;
bool is_pending_open_animation;
bool is_restoring_from_ov;
float scroller_proportion;
@ -466,6 +465,9 @@ struct Client {
int32_t grid_col_idx;
int32_t grid_row_idx;
uint32_t id;
Client *group_prev;
Client *group_next;
bool isgroupfocusing;
};
typedef struct {
@ -804,7 +806,7 @@ static void clear_fullscreen_flag(Client *c);
static pid_t getparentprocess(pid_t p);
static int32_t isdescprocess(pid_t p, pid_t c);
static Client *termforwin(Client *w);
static void swallow(Client *c, Client *w);
static void client_replace(Client *c, Client *w, bool isgroupaction);
static void warp_cursor_to_selmon(Monitor *m);
uint32_t want_restore_fullscreen(Client *target_client);
@ -936,6 +938,8 @@ static void overview_backup_surface(Client *c);
static void create_jump_hints(Monitor *m);
static void finish_jump_mode(Monitor *m);
static void begin_jump_mode(Monitor *m);
static void global_draw_tab_bar(Client *c, int32_t x, int32_t y, int32_t width,
int32_t height);
#include "data/static_keymap.h"
#include "dispatch/bind_declare.h"
@ -1273,7 +1277,7 @@ void client_update_oldmonname_record(Client *c, Monitor *m) {
c->oldmonname[sizeof(c->oldmonname) - 1] = '\0';
}
void swallow(Client *c, Client *w) {
void client_replace(Client *c, Client *w, bool isgroupaction) {
c->bw = w->bw;
c->isfloating = w->isfloating;
c->isurgent = w->isurgent;
@ -1288,10 +1292,43 @@ void swallow(Client *c, Client *w) {
c->scroller_proportion = w->scroller_proportion;
c->isglobal = w->isglobal;
c->overview_backup_geom = w->overview_backup_geom;
/* 调整 w 的邻居指针,让它们指向 c */
c->animation.current = w->animation.current;
c->stack_proportion = w->stack_proportion;
if (!isgroupaction) {
if (w->group_prev == c) {
c->group_next = w->group_next;
if (w->group_next) {
w->group_next->group_prev = c;
}
} else if (w->group_next == c) {
c->group_prev = w->group_prev;
if (w->group_prev) {
w->group_prev->group_next = c;
}
} else {
c->group_prev = w->group_prev;
c->group_next = w->group_next;
if (w->group_prev) {
w->group_prev->group_next = c;
}
if (w->group_next) {
w->group_next->group_prev = c;
}
}
if (!c->group_prev && !c->group_next) {
c->isgroupfocusing = false;
} else {
c->isgroupfocusing = w->isgroupfocusing;
}
if (c->isgroupfocusing)
mango_tab_bar_node_set_focus(c->tab_bar_node, true);
}
if (w->overview_scene_surface) {
wlr_scene_node_destroy(&w->scene_surface->node);
w->scene_surface = w->overview_scene_surface;
@ -1302,13 +1339,36 @@ void swallow(Client *c, Client *w) {
overview_backup_surface(c);
}
if (w->tab_bar_node) {
if (w->tab_bar_node && !isgroupaction) {
wlr_scene_node_set_enabled(&w->tab_bar_node->scene_buffer->node, false);
}
/* 全局链表替换 */
wl_list_insert(&w->link, &c->link);
wl_list_insert(&w->flink, &c->flink);
if (c->link.prev && c->link.next && c->link.prev != &c->link) {
wl_list_remove(&c->link);
}
wl_list_init(&c->link);
if (c->flink.prev && c->flink.next && c->flink.prev != &c->flink) {
wl_list_remove(&c->flink);
}
wl_list_init(&c->flink);
if (w->link.prev && w->link.next && w->link.prev != &w->link) {
wl_list_insert(w->link.prev, &c->link);
wl_list_remove(&w->link);
wl_list_init(&w->link);
}
if (w->flink.prev && w->flink.next && w->flink.prev != &w->flink) {
if (selmon && c == selmon->sel) {
wl_list_insert(&fstack, &c->flink);
} else {
wl_list_insert(w->flink.prev, &c->flink);
}
wl_list_remove(&w->flink);
wl_list_init(&w->flink);
}
/* --------------------------------------------------------------------- */
if (w->foreign_toplevel) {
wlr_foreign_toplevel_handle_v1_output_leave(w->foreign_toplevel,
@ -1357,12 +1417,10 @@ void swallow(Client *c, Client *w) {
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;
@ -1780,11 +1838,9 @@ void applyrules(Client *c) {
if (p && !p->isminimized) {
c->swallowedby = p;
p->swallowing = c;
wl_list_remove(&c->link);
wl_list_remove(&c->flink);
swallow(c, p);
wl_list_remove(&p->link);
wl_list_remove(&p->flink);
client_replace(c, p, false);
mon = p->mon;
newtags = p->tags;
}
@ -1799,7 +1855,10 @@ void applyrules(Client *c) {
(!c->istagsilent || !newtags || newtags & mon->tagset[mon->seltags]);
if (!should_init_get_focus) {
wl_list_remove(&c->flink);
if (c->flink.prev && c->flink.next && c->flink.prev != &c->flink) {
wl_list_remove(&c->flink);
wl_list_init(&c->flink);
}
wl_list_insert(fstack.prev, &c->flink);
}
@ -1836,7 +1895,10 @@ void applyrules(Client *c) {
}
if (c->isfloating && !c->iscustompos && !c->isnamedscratchpad) {
wl_list_remove(&c->link);
if (c->link.prev && c->link.next && c->link.prev != &c->link) {
wl_list_remove(&c->link);
wl_list_init(&c->link);
}
wl_list_insert(clients.prev, &c->link);
set_float_malposition(c);
}
@ -2425,7 +2487,7 @@ bool handle_buttonpress(struct wlr_pointer_button_event *event) {
MangoNodeData *mangonodedata = (MangoNodeData *)node->data;
if (mangonodedata->type == MANGO_TITLE_NODE) {
Client *c = mangonodedata->node_data;
focusclient(c, 1);
client_focus_group_member(c);
}
}
@ -2592,6 +2654,7 @@ void cleanuplisteners(void) {
wl_list_remove(&new_foreign_toplevel_capture_request.link);
wl_list_remove(&tearing_new_object.link);
wl_list_remove(&keyboard_shortcuts_inhibit_new_inhibitor.link);
wl_list_remove(&ext_image_copy_capture_mgr_new_session.link);
if (drm_lease_manager) {
wl_list_remove(&drm_lease_request.link);
}
@ -2602,6 +2665,8 @@ void cleanuplisteners(void) {
}
void cleanup(void) {
allow_frame_scheduling = false;
ipc_cleanup();
cleanuplisteners();
#ifdef XWAYLAND
@ -3519,30 +3584,22 @@ void destroyinputdevice(struct wl_listener *listener, void *data) {
InputDevice *input_dev =
wl_container_of(listener, input_dev, destroy_listener);
// 清理设备特定数据
if (input_dev->device_data) {
// 根据设备类型进行特定清理
switch (input_dev->wlr_device->type) {
case WLR_INPUT_DEVICE_SWITCH: {
Switch *sw = (Switch *)input_dev->device_data;
// 移除 toggle 监听器
wl_list_remove(&sw->toggle.link);
// 释放 Switch 内存
free(sw);
break;
}
// 可以添加其他设备类型的清理代码
default:
break;
}
input_dev->device_data = NULL;
}
// 从设备列表中移除
wl_list_remove(&input_dev->link);
// 移除 destroy 监听器
wl_list_remove(&input_dev->destroy_listener.link);
// 释放内存
free(input_dev);
}
@ -4456,9 +4513,11 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx,
}
void init_client_properties(Client *c) {
c->isgroupfocusing = false;
c->group_prev = NULL;
c->group_next = NULL;
c->grid_col_per = 1.0f;
c->grid_row_per = 1.0f;
c->is_monocle_hide = false;
c->jump_label_node = NULL;
c->tab_bar_node = NULL;
c->overview_scene_surface = NULL;
@ -4637,6 +4696,8 @@ mapnotify(struct wl_listener *listener, void *data) {
wlr_scene_node_set_enabled(&c->splitindicator[i]->node, false);
}
client_add_tab_bar_node(c);
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);
@ -5641,11 +5702,18 @@ setfloating(Client *c, int32_t floating) {
}
void reset_maximizescreen_size(Client *c) {
c->geom.x = c->mon->w.x + config.gappoh;
c->geom.y = c->mon->w.y + config.gappov;
c->geom.width = c->mon->w.width - 2 * config.gappoh;
c->geom.height = c->mon->w.height - 2 * config.gappov;
resize(c, c->geom, 0);
struct wlr_box geom;
geom.x = c->mon->w.x + config.gappoh;
geom.y = c->mon->w.y + config.gappov;
geom.width = c->mon->w.width - 2 * config.gappoh;
geom.height = c->mon->w.height - 2 * config.gappov;
if ((c->group_next || c->group_prev) && c->tab_bar_node) {
geom.height -= config.tab_bar_height;
geom.y += config.tab_bar_height;
}
resize(c, geom, 0);
}
void exit_scroller_stack(Client *c) {
@ -5658,7 +5726,7 @@ void exit_scroller_stack(Client *c) {
struct ScrollerStackNode *n = find_scroller_node(st, c);
if (n) {
scroller_node_remove(st, n);
return; /* 节点已移除,客户端指针已在函数内清空 */
return;
}
}
}
@ -5686,6 +5754,12 @@ void setmaximizescreen(Client *c, int32_t maximizescreen, bool rearrange) {
maximizescreen_box.y = c->mon->w.y + config.gappov;
maximizescreen_box.width = c->mon->w.width - 2 * config.gappoh;
maximizescreen_box.height = c->mon->w.height - 2 * config.gappov;
if ((c->group_next || c->group_prev) && c->tab_bar_node) {
maximizescreen_box.height -= config.tab_bar_height;
maximizescreen_box.y += config.tab_bar_height;
}
wlr_scene_node_raise_to_top(&c->scene->node);
if (!is_scroller_layout(c->mon) || c->isfloating)
resize(c, maximizescreen_box, 0);
@ -6650,7 +6724,12 @@ void unmapnotify(struct wl_listener *listener, void *data) {
if (c->swallowedby) {
c->swallowedby->mon = c->mon;
swallow(c->swallowedby, c);
client_replace(c->swallowedby, c, false);
} else if ((c->group_next || c->group_prev) && c->isgroupfocusing) {
Client *group_replacement =
c->group_next ? c->group_next : c->group_prev;
group_replacement->mon = c->mon;
client_replace(group_replacement, c, false);
} else {
scroller_remove_client(c);
dwindle_remove_client(c);
@ -6699,7 +6778,11 @@ void unmapnotify(struct wl_listener *listener, void *data) {
if (client_is_unmanaged(c)) {
#ifdef XWAYLAND
if (client_is_x11(c)) {
wl_list_remove(&c->set_geometry.link);
if (c->set_geometry.link.prev && c->set_geometry.link.next &&
c->set_geometry.link.prev != &c->set_geometry.link) {
wl_list_remove(&c->set_geometry.link);
wl_list_init(&c->set_geometry.link);
}
}
#endif
if (c == exclusive_focus)
@ -6707,11 +6790,33 @@ void unmapnotify(struct wl_listener *listener, void *data) {
if (client_surface(c) == seat->keyboard_state.focused_surface)
focusclient(focustop(selmon), 1);
} else {
if (!c->swallowing)
wl_list_remove(&c->link);
bool is_in_group = c->group_next || c->group_prev;
if (c->group_next && !c->isgroupfocusing) {
c->group_next->group_prev = c->group_prev;
}
if (c->group_prev && !c->isgroupfocusing) {
c->group_prev->group_next = c->group_next;
}
c->group_next = NULL;
c->group_prev = NULL;
if (!c->swallowing && (!is_in_group || c->isgroupfocusing)) {
if (c->link.prev && c->link.next && c->link.prev != &c->link) {
wl_list_remove(&c->link);
wl_list_init(&c->link);
}
}
setmon(c, NULL, 0, true);
if (!c->swallowing)
wl_list_remove(&c->flink);
if (!c->swallowing && (!is_in_group || c->isgroupfocusing)) {
if (c->flink.prev && c->flink.next && c->flink.prev != &c->flink) {
wl_list_remove(&c->flink);
wl_list_init(&c->flink);
}
}
}
if (c->foreign_toplevel) {
@ -6731,8 +6836,6 @@ void unmapnotify(struct wl_listener *listener, void *data) {
c->swallowing = NULL;
}
c->stack_proportion = 0.0f;
if (c->jump_label_node) {
mango_jump_label_node_destroy(c->jump_label_node);
c->jump_label_node = NULL;
@ -6744,6 +6847,9 @@ void unmapnotify(struct wl_listener *listener, void *data) {
wlr_scene_node_destroy(&c->image_capture_scene_surface->buffer->node);
wlr_scene_node_destroy(&c->image_capture_scene->tree.node);
init_client_properties(c);
wlr_scene_node_destroy(&c->scene->node);
printstatus(IPC_WATCH_ARRANGGE);
motionnotify(0, NULL, 0, 0, 0, 0);