From d9aaf4df3f3fd97e080d6eb7b9d5025fc42aecca Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 19 Feb 2026 14:39:37 +0800 Subject: [PATCH 1/6] feat: add shield_when_capture windowrule to disable capture --- src/animation/client.h | 21 +++++++++++++++ src/config/parse_config.h | 4 +++ src/mango.c | 57 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/animation/client.h b/src/animation/client.h index 2588fb1c..448054c5 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -350,6 +350,24 @@ void client_draw_shadow(Client *c) { wlr_scene_shadow_set_clipped_region(c->shadow, clipped_region); } +void apply_shield(Client *c) { + if (active_capture_count > 0 && c->shield_when_capture) { + wlr_scene_node_raise_to_top(&c->shield->node); + wlr_scene_node_set_position(&c->shield->node, c->bw, c->bw); + wlr_scene_rect_set_size(c->shield, + c->animation.current.width - 2 * c->bw, + c->animation.current.height - 2 * c->bw); + wlr_scene_node_set_enabled(&c->shield->node, true); + } else { + if (c->shield->node.enabled) { + wlr_scene_node_lower_to_bottom(&c->shield->node); + wlr_scene_rect_set_size(c->shield, c->animation.current.width, + c->animation.current.height); + wlr_scene_node_set_enabled(&c->shield->node, false); + } + } +} + void apply_border(Client *c) { if (!c || c->iskilling || !client_surface(c)->mapped) return; @@ -535,6 +553,7 @@ void client_apply_clip(Client *c, float factor) { apply_border(c); client_draw_shadow(c); + apply_shield(c); if (clip_box.width <= 0 || clip_box.height <= 0) { return; @@ -572,6 +591,7 @@ void client_apply_clip(Client *c, float factor) { // 应用窗口装饰 apply_border(c); client_draw_shadow(c); + apply_shield(c); // 如果窗口剪切区域已经剪切到0,则不渲染窗口表面 if (clip_box.width <= 0 || clip_box.height <= 0) { @@ -1000,6 +1020,7 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) { client_draw_shadow(c); apply_border(c); + apply_shield(c); client_get_clip(c, &clip); wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); return; diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 830d22ba..c28d879b 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -74,6 +74,7 @@ typedef struct { int32_t isunglobal; int32_t isglobal; int32_t isoverlay; + int32_t shield_when_capture; int32_t allow_shortcuts_inhibit; int32_t ignore_maximize; int32_t ignore_minimize; @@ -2011,6 +2012,7 @@ bool parse_option(Config *config, char *key, char *value) { rule->isunglobal = -1; rule->isglobal = -1; rule->isoverlay = -1; + rule->shield_when_capture = -1; rule->allow_shortcuts_inhibit = -1; rule->ignore_maximize = -1; rule->ignore_minimize = -1; @@ -2117,6 +2119,8 @@ bool parse_option(Config *config, char *key, char *value) { rule->focused_opacity = atof(val); } else if (strcmp(key, "isoverlay") == 0) { rule->isoverlay = atoi(val); + } else if (strcmp(key, "shield_when_capture") == 0) { + rule->shield_when_capture = atoi(val); } else if (strcmp(key, "allow_shortcuts_inhibit") == 0) { rule->allow_shortcuts_inhibit = atoi(val); } else if (strcmp(key, "ignore_maximize") == 0) { diff --git a/src/mango.c b/src/mango.c index 540395ae..0985bdfe 100644 --- a/src/mango.c +++ b/src/mango.c @@ -315,6 +315,7 @@ struct Client { struct wlr_scene_tree *scene; struct wlr_scene_rect *border; /* top, bottom, left, right */ struct wlr_scene_shadow *shadow; + struct wlr_scene_rect *shield; struct wlr_scene_tree *scene_surface; struct wl_list link; struct wl_list flink; @@ -381,6 +382,7 @@ struct Client { int32_t iskilling; int32_t istagswitching; int32_t isnamedscratchpad; + int32_t shield_when_capture; bool is_pending_open_animation; bool is_restoring_from_ov; float scroller_proportion; @@ -545,6 +547,11 @@ typedef struct { struct wl_listener destroy; } SessionLock; +struct capture_session_tracker { + struct wl_listener session_destroy; + struct wlr_ext_image_copy_capture_session_v1 *session; +}; + /* function declarations */ static void applybounds( Client *c, @@ -788,6 +795,8 @@ static Client *get_focused_stack_client(Client *sc); static bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc); static void monitor_stop_skip_timer(Monitor *m); static int monitor_skip_frame_timeout_callback(void *data); +static void handle_iamge_copy_capture_new_session(struct wl_listener *listener, + void *data); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" @@ -825,6 +834,7 @@ static struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; static struct wlr_output_power_manager_v1 *power_mgr; +static struct wlr_ext_image_copy_capture_manager_v1 *ext_image_copy_capture_mgr; static struct wlr_pointer_gestures_v1 *pointer_gestures; static struct wlr_drm_lease_v1_manager *drm_lease_manager; struct mango_print_status_manager *print_status_manager; @@ -888,6 +898,7 @@ static struct wl_event_source *hide_source; static bool cursor_hidden = false; static bool tag_combo = false; static const char *cli_config_path = NULL; +static int active_capture_count = 0; static KeyMode keymode = { .mode = {'d', 'e', 'f', 'a', 'u', 'l', 't', '\0'}, .isdefault = true, @@ -938,6 +949,8 @@ static struct wl_listener output_mgr_apply = {.notify = outputmgrapply}; static struct wl_listener output_mgr_test = {.notify = outputmgrtest}; static struct wl_listener output_power_mgr_set_mode = {.notify = powermgrsetmode}; +static struct wl_listener ext_image_copy_capture_mgr_new_session = { + .notify = handle_iamge_copy_capture_new_session}; static struct wl_listener request_activate = {.notify = urgent}; static struct wl_listener request_cursor = {.notify = setcursor}; static struct wl_listener request_set_psel = {.notify = setpsel}; @@ -1313,6 +1326,7 @@ static void apply_rule_properties(Client *c, const ConfigWinRule *r) { APPLY_INT_PROP(c, r, isnamedscratchpad); APPLY_INT_PROP(c, r, isglobal); APPLY_INT_PROP(c, r, isoverlay); + APPLY_INT_PROP(c, r, shield_when_capture); APPLY_INT_PROP(c, r, ignore_maximize); APPLY_INT_PROP(c, r, ignore_minimize); APPLY_INT_PROP(c, r, isnosizehint); @@ -4001,6 +4015,12 @@ mapnotify(struct wl_listener *listener, void *data) { wlr_scene_node_lower_to_bottom(&c->shadow->node); wlr_scene_node_set_enabled(&c->shadow->node, true); + c->shield = + wlr_scene_rect_create(c->scene, 0, 0, (float[4]){0, 0, 0, 0xff}); + c->shield->node.data = c; + wlr_scene_node_lower_to_bottom(&c->shield->node); + wlr_scene_node_set_enabled(&c->shield->node, false); + if (new_is_master && selmon && !is_scroller_layout(selmon)) // tile at the top wl_list_insert(&clients, &c->link); // 新窗口是master,头部入栈 @@ -4409,6 +4429,37 @@ void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, // 修改printstatus函数,接受掩码参数 void printstatus(void) { wl_signal_emit(&mango_print_status, NULL); } +// 会话销毁时的回调 +void handle_session_destroy(struct wl_listener *listener, void *data) { + struct capture_session_tracker *tracker = + wl_container_of(listener, tracker, session_destroy); + active_capture_count--; + wl_list_remove(&tracker->session_destroy.link); + wlr_log(WLR_DEBUG, "Capture session ended, active count: %d", + active_capture_count); + free(tracker); +} + +// 新会话创建时的回调 +void handle_iamge_copy_capture_new_session(struct wl_listener *listener, + void *data) { + struct wlr_ext_image_copy_capture_session_v1 *session = data; + + struct capture_session_tracker *tracker = calloc(1, sizeof(*tracker)); + if (!tracker) { + wlr_log(WLR_ERROR, "Failed to allocate capture session tracker"); + return; + } + tracker->session = session; + tracker->session_destroy.notify = handle_session_destroy; + // 监听会话的 destroy 信号,以便在会话结束时减少计数 + wl_signal_add(&session->events.destroy, &tracker->session_destroy); + + active_capture_count++; + wlr_log(WLR_DEBUG, "New capture session started, active count: %d", + active_capture_count); +} + void powermgrsetmode(struct wl_listener *listener, void *data) { struct wlr_output_power_v1_set_mode_event *event = data; struct wlr_output_state state = {0}; @@ -5344,7 +5395,6 @@ void setup(void) { compositor = wlr_compositor_create(dpy, 6, drw); wlr_export_dmabuf_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy); - wlr_ext_image_copy_capture_manager_v1_create(dpy, 1); wlr_ext_output_image_capture_source_manager_v1_create(dpy, 1); wlr_data_control_manager_v1_create(dpy); wlr_data_device_manager_create(dpy); @@ -5361,6 +5411,11 @@ void setup(void) { wl_signal_init(&mango_print_status); wl_signal_add(&mango_print_status, &print_status_listener); + ext_image_copy_capture_mgr = + wlr_ext_image_copy_capture_manager_v1_create(dpy, 1); + wl_signal_add(&ext_image_copy_capture_mgr->events.new_session, + &ext_image_copy_capture_mgr_new_session); + /* Initializes the interface used to implement urgency hints */ activation = wlr_xdg_activation_v1_create(dpy); wl_signal_add(&activation->events.request_activate, &request_activate); From a7803bd2a56cd8da581ecba6b92fd01a3e17a2cb Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 19 Feb 2026 15:03:52 +0800 Subject: [PATCH 2/6] opt: shield clip when over screen --- src/animation/client.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index 448054c5..b4cd5566 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -350,13 +350,12 @@ void client_draw_shadow(Client *c) { wlr_scene_shadow_set_clipped_region(c->shadow, clipped_region); } -void apply_shield(Client *c) { +void apply_shield(Client *c, struct wlr_box clip_box) { if (active_capture_count > 0 && c->shield_when_capture) { wlr_scene_node_raise_to_top(&c->shield->node); - wlr_scene_node_set_position(&c->shield->node, c->bw, c->bw); - wlr_scene_rect_set_size(c->shield, - c->animation.current.width - 2 * c->bw, - c->animation.current.height - 2 * c->bw); + wlr_scene_node_set_position(&c->shield->node, clip_box.x + c->bw, + clip_box.y + c->bw); + wlr_scene_rect_set_size(c->shield, clip_box.width, clip_box.height); wlr_scene_node_set_enabled(&c->shield->node, true); } else { if (c->shield->node.enabled) { @@ -553,12 +552,12 @@ void client_apply_clip(Client *c, float factor) { apply_border(c); client_draw_shadow(c); - apply_shield(c); if (clip_box.width <= 0 || clip_box.height <= 0) { return; } + apply_shield(c, clip_box); wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box); buffer_set_effect(c, (BufferData){1.0f, 1.0f, clip_box.width, clip_box.height, @@ -591,7 +590,6 @@ void client_apply_clip(Client *c, float factor) { // 应用窗口装饰 apply_border(c); client_draw_shadow(c); - apply_shield(c); // 如果窗口剪切区域已经剪切到0,则不渲染窗口表面 if (clip_box.width <= 0 || clip_box.height <= 0) { @@ -608,6 +606,7 @@ void client_apply_clip(Client *c, float factor) { } // 应用窗口表面剪切 + apply_shield(c, clip_box); wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box); // 获取剪切后的表面的实际大小用于计算缩放 @@ -1020,8 +1019,8 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) { client_draw_shadow(c); apply_border(c); - apply_shield(c); client_get_clip(c, &clip); + apply_shield(c, clip); wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); return; } From b400fd5aad734fc0708fae084429d9f45e97a6c5 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 19 Feb 2026 15:27:06 +0800 Subject: [PATCH 3/6] opt: Ensure timely shield --- src/mango.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/mango.c b/src/mango.c index 0985bdfe..855639c6 100644 --- a/src/mango.c +++ b/src/mango.c @@ -4435,6 +4435,14 @@ void handle_session_destroy(struct wl_listener *listener, void *data) { wl_container_of(listener, tracker, session_destroy); active_capture_count--; wl_list_remove(&tracker->session_destroy.link); + + Client *c = NULL; + wl_list_for_each(c, &clients, link) { + if (c->shield_when_capture && !c->iskilling && VISIBLEON(c, c->mon)) { + arrange(c->mon, false, false); + } + } + wlr_log(WLR_DEBUG, "Capture session ended, active count: %d", active_capture_count); free(tracker); @@ -4456,6 +4464,14 @@ void handle_iamge_copy_capture_new_session(struct wl_listener *listener, wl_signal_add(&session->events.destroy, &tracker->session_destroy); active_capture_count++; + + Client *c = NULL; + wl_list_for_each(c, &clients, link) { + if (c->shield_when_capture && !c->iskilling && VISIBLEON(c, c->mon)) { + arrange(c->mon, false, false); + } + } + wlr_log(WLR_DEBUG, "New capture session started, active count: %d", active_capture_count); } From 593084b03680671908bd11d4473381a7dd93840b Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 19 Feb 2026 15:36:54 +0800 Subject: [PATCH 4/6] opt: make surface node as shield parent node --- src/animation/client.h | 3 +-- src/mango.c | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/animation/client.h b/src/animation/client.h index b4cd5566..d3a932d4 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -353,8 +353,7 @@ void client_draw_shadow(Client *c) { void apply_shield(Client *c, struct wlr_box clip_box) { if (active_capture_count > 0 && c->shield_when_capture) { wlr_scene_node_raise_to_top(&c->shield->node); - wlr_scene_node_set_position(&c->shield->node, clip_box.x + c->bw, - clip_box.y + c->bw); + wlr_scene_node_set_position(&c->shield->node, clip_box.x, clip_box.y); wlr_scene_rect_set_size(c->shield, clip_box.width, clip_box.height); wlr_scene_node_set_enabled(&c->shield->node, true); } else { diff --git a/src/mango.c b/src/mango.c index 855639c6..f4052dc0 100644 --- a/src/mango.c +++ b/src/mango.c @@ -4015,8 +4015,8 @@ mapnotify(struct wl_listener *listener, void *data) { wlr_scene_node_lower_to_bottom(&c->shadow->node); wlr_scene_node_set_enabled(&c->shadow->node, true); - c->shield = - wlr_scene_rect_create(c->scene, 0, 0, (float[4]){0, 0, 0, 0xff}); + c->shield = wlr_scene_rect_create(c->scene_surface, 0, 0, + (float[4]){0, 0, 0, 0xff}); c->shield->node.data = c; wlr_scene_node_lower_to_bottom(&c->shield->node); wlr_scene_node_set_enabled(&c->shield->node, false); From 7aa79a430b604326ec2a032f61d29902fc0f27d1 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 19 Feb 2026 19:07:36 +0800 Subject: [PATCH 5/6] opt: use base surface of client when xytonode in rect node --- src/fetch/common.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/fetch/common.h b/src/fetch/common.h index 58e69dc1..57a1a8e6 100644 --- a/src/fetch/common.h +++ b/src/fetch/common.h @@ -100,10 +100,6 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, surface = wlr_scene_surface_try_from_buffer( wlr_scene_buffer_from_node(node)) ->surface; - else if (node->type == WLR_SCENE_NODE_RECT) { - surface = NULL; - break; - } /* start from the topmost layer, find a sureface that can be focused by pointer, @@ -119,6 +115,13 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, l = pnode->data; } } + + if (node->type == WLR_SCENE_NODE_RECT) { + if (c) { + surface = client_surface(c); + } + break; + } } if (psurface) From a411924331346ce0c87c8e15a8075e3117ac6199 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 19 Feb 2026 19:12:01 +0800 Subject: [PATCH 6/6] opt: dont fade out shield client when capture --- src/animation/client.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/animation/client.h b/src/animation/client.h index d3a932d4..1e43cca0 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -783,6 +783,10 @@ void init_fadeout_client(Client *c) { return; } + if (c->shield_when_capture && active_capture_count > 0) { + return; + } + if ((c->animation_type_close && strcmp(c->animation_type_close, "none") == 0) || (!c->animation_type_close &&