From d7286b0d8462bdff20c65b1dcec5ab0fad704928 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 7 Nov 2025 10:02:51 +0800
Subject: [PATCH 01/16] fix: adjust scenefx blur node feature

---
 src/animation/client.h | 12 +++++++++++
 src/animation/common.h |  8 ++------
 src/animation/layer.h  |  4 ++++
 src/mango.c            | 46 +++++++++++++++++++++++++++---------------
 4 files changed, 48 insertions(+), 22 deletions(-)

diff --git a/src/animation/client.h b/src/animation/client.h
index 90b664b55..e9459d55b 100644
--- a/src/animation/client.h
+++ b/src/animation/client.h
@@ -245,6 +245,13 @@ void buffer_set_effect(Client *c, BufferData data) {
 		data.corner_location = CORNER_LOCATION_NONE;
 	}
 
+	if (blur && !c->noblur) {
+		wlr_scene_blur_set_size(c->blur, c->animation.current.width - 2 * c->bw,
+								c->animation.current.height - 2 * c->bw);
+
+		wlr_scene_blur_set_corner_radius(c->blur, border_radius,
+										 data.corner_location);
+	}
 	wlr_scene_node_for_each_buffer(&c->scene_surface->node,
 								   scene_buffer_apply_effect, &data);
 }
@@ -535,6 +542,7 @@ void client_apply_clip(Client *c, float factor) {
 		}
 
 		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,
 										  current_corner_location, true});
@@ -987,6 +995,10 @@ void resize(Client *c, struct wlr_box geo, int interact) {
 		apply_border(c);
 		client_get_clip(c, &clip);
 		wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
+		if (blur && !c->noblur)
+			wlr_scene_blur_set_size(c->blur,
+									c->animation.current.width - 2 * c->bw,
+									c->animation.current.height - 2 * c->bw);
 		return;
 	}
 	// 如果不是工作区切换时划出去的窗口，就让动画的结束位置，就是上面的真实位置和大小
diff --git a/src/animation/common.h b/src/animation/common.h
index 06ffe1ab1..7079df867 100644
--- a/src/animation/common.h
+++ b/src/animation/common.h
@@ -158,12 +158,6 @@ static bool scene_node_snapshot(struct wlr_scene_node *node, int lx, int ly,
 										   scene_buffer->corner_radius,
 										   scene_buffer->corners);
 
-		// wlr_scene_buffer_set_backdrop_blur_optimized(
-		// 	snapshot_buffer, scene_buffer->backdrop_blur_optimized);
-		// wlr_scene_buffer_set_backdrop_blur_ignore_transparent(
-		// 	snapshot_buffer, scene_buffer->backdrop_blur_ignore_transparent);
-		wlr_scene_buffer_set_backdrop_blur(snapshot_buffer, false);
-
 		snapshot_buffer->node.data = scene_buffer->node.data;
 
 		struct wlr_scene_surface *scene_surface =
@@ -198,6 +192,8 @@ static bool scene_node_snapshot(struct wlr_scene_node *node, int lx, int ly,
 
 		break;
 	}
+	case WLR_SCENE_NODE_BLUR:
+		break;
 	case WLR_SCENE_NODE_OPTIMIZED_BLUR:
 		return true;
 	}
diff --git a/src/animation/layer.h b/src/animation/layer.h
index 48ceb2118..80caec565 100644
--- a/src/animation/layer.h
+++ b/src/animation/layer.h
@@ -356,6 +356,10 @@ void layer_animation_next_tick(LayerSurface *l) {
 		.height = height,
 	};
 
+	if (blur && blur_layer && !l->noblur && l->blur)
+		wlr_scene_blur_set_size(l->blur, l->animation.current.width,
+								l->animation.current.height);
+
 	if (animation_passed >= 1.0) {
 		l->animation.running = false;
 		l->need_output_flush = false;
diff --git a/src/mango.c b/src/mango.c
index 36cbbbfa7..69208d635 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -276,6 +276,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_blur *blur;
 	struct wlr_scene_tree *scene_surface;
 	struct wl_list link;
 	struct wl_list flink;
@@ -405,6 +406,7 @@ typedef struct {
 	struct wlr_scene_tree *scene;
 	struct wlr_scene_tree *popups;
 	struct wlr_scene_shadow *shadow;
+	struct wlr_scene_blur *blur;
 	struct wlr_scene_layer_surface_v1 *scene_layer;
 	struct wl_list link;
 	struct wl_list fadeout_link;
@@ -2081,12 +2083,16 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, int sx,
 		return;
 	}
 
-	wlr_scene_buffer_set_backdrop_blur(buffer, true);
-	wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, true);
+	LayerSurface *l = (LayerSurface *)user_data;
+
+	wlr_scene_node_set_enabled(&l->blur->node, true);
+	wlr_scene_blur_set_transparency_mask_source(l->blur, buffer);
+	wlr_scene_blur_set_size(l->blur, l->geom.width, l->geom.height);
+
 	if (blur_optimized) {
-		wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true);
+		wlr_scene_blur_set_should_only_blur_bottom_layer(l->blur, true);
 	} else {
-		wlr_scene_buffer_set_backdrop_blur_optimized(buffer, false);
+		wlr_scene_blur_set_should_only_blur_bottom_layer(l->blur, false);
 	}
 }
 
@@ -2131,13 +2137,17 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) {
 	}
 
 	// 初始化阴影
-	if (layer_surface->current.exclusive_zone == 0 &&
-		layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM &&
+	if (layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM &&
 		layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
-		l->shadow = wlr_scene_shadow_create(l->scene, 0, 0, border_radius,
-											shadows_blur, shadowscolor);
-		wlr_scene_node_lower_to_bottom(&l->shadow->node);
-		wlr_scene_node_set_enabled(&l->shadow->node, true);
+		if (layer_surface->current.exclusive_zone == 0) {
+			l->shadow = wlr_scene_shadow_create(l->scene, 0, 0, border_radius,
+												shadows_blur, shadowscolor);
+			wlr_scene_node_lower_to_bottom(&l->shadow->node);
+			wlr_scene_node_set_enabled(&l->shadow->node, true);
+		}
+
+		l->blur = wlr_scene_blur_create(l->scene, 0, 0);
+		wlr_scene_node_lower_to_bottom(&l->blur->node);
 	}
 
 	// 初始化动画
@@ -2194,7 +2204,8 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) {
 		if (!l->noblur &&
 			layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM &&
 			layer_surface->current.layer !=
-				ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
+				ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND &&
+			l->blur) {
 
 			wlr_scene_node_for_each_buffer(&l->scene->node,
 										   iter_layer_scene_buffers, l);
@@ -3521,15 +3532,15 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int sx,
 		return;
 
 	if (blur && c && !c->noblur) {
-		wlr_scene_buffer_set_backdrop_blur(buffer, true);
-		wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, false);
+		wlr_scene_node_set_enabled(&c->blur->node, true);
+		// wlr_scene_blur_set_transparency_mask_source(c->blur, buffer);
 		if (blur_optimized) {
-			wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true);
+			wlr_scene_blur_set_should_only_blur_bottom_layer(c->blur, true);
 		} else {
-			wlr_scene_buffer_set_backdrop_blur_optimized(buffer, false);
+			wlr_scene_blur_set_should_only_blur_bottom_layer(c->blur, false);
 		}
 	} else {
-		wlr_scene_buffer_set_backdrop_blur(buffer, false);
+		wlr_scene_node_set_enabled(&c->blur->node, false);
 	}
 }
 
@@ -3642,6 +3653,9 @@ mapnotify(struct wl_listener *listener, void *data) {
 	c->shadow = wlr_scene_shadow_create(c->scene, 0, 0, border_radius,
 										shadows_blur, shadowscolor);
 
+	c->blur = wlr_scene_blur_create(c->scene_surface, 0, 0);
+	wlr_scene_node_lower_to_bottom(&c->blur->node);
+
 	wlr_scene_node_lower_to_bottom(&c->shadow->node);
 	wlr_scene_node_set_enabled(&c->shadow->node, true);
 

From 358591f16a876a5b6d252d2e44b6faa09f703e47 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Sat, 8 Nov 2025 19:59:05 +0800
Subject: [PATCH 02/16] fix: blur node size and position are wrong

---
 src/animation/client.h | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/src/animation/client.h b/src/animation/client.h
index e9459d55b..3792358d4 100644
--- a/src/animation/client.h
+++ b/src/animation/client.h
@@ -246,9 +246,6 @@ void buffer_set_effect(Client *c, BufferData data) {
 	}
 
 	if (blur && !c->noblur) {
-		wlr_scene_blur_set_size(c->blur, c->animation.current.width - 2 * c->bw,
-								c->animation.current.height - 2 * c->bw);
-
 		wlr_scene_blur_set_corner_radius(c->blur, border_radius,
 										 data.corner_location);
 	}
@@ -352,6 +349,14 @@ void client_draw_shadow(Client *c) {
 	wlr_scene_shadow_set_clipped_region(c->shadow, clipped_region);
 }
 
+void client_draw_blur(Client *c, struct wlr_box clip_box, struct ivec2 offset) {
+	if (blur && !c->noblur) {
+		wlr_scene_node_set_position(&c->blur->node, offset.x, offset.y);
+		wlr_scene_blur_set_size(c->blur, clip_box.width - c->bw,
+								clip_box.height - c->bw);
+	}
+}
+
 void apply_border(Client *c) {
 	if (!c || c->iskilling || !client_surface(c)->mapped)
 		return;
@@ -536,6 +541,7 @@ void client_apply_clip(Client *c, float factor) {
 
 		apply_border(c);
 		client_draw_shadow(c);
+		client_draw_blur(c, clip_box, offset);
 
 		if (clip_box.width <= 0 || clip_box.height <= 0) {
 			return;
@@ -574,6 +580,7 @@ void client_apply_clip(Client *c, float factor) {
 	// 应用窗口装饰
 	apply_border(c);
 	client_draw_shadow(c);
+	client_draw_blur(c, clip_box, offset);
 
 	// 如果窗口剪切区域已经剪切到0，则不渲染窗口表面
 	if (clip_box.width <= 0 || clip_box.height <= 0) {

From d8583b1224a7fbb7f6b5a97839683c54985917b4 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Wed, 19 Nov 2025 11:10:05 +0800
Subject: [PATCH 03/16] fix: Fix blur lag

---
 src/mango.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/mango.c b/src/mango.c
index 69208d635..80ad2415a 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -2086,7 +2086,8 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, int sx,
 	LayerSurface *l = (LayerSurface *)user_data;
 
 	wlr_scene_node_set_enabled(&l->blur->node, true);
-	wlr_scene_blur_set_transparency_mask_source(l->blur, buffer);
+	wlr_scene_blur_set_mask_source(l->blur, &buffer->node,
+								   BLUR_MASK_IGNORE_TRANSPARENCY);
 	wlr_scene_blur_set_size(l->blur, l->geom.width, l->geom.height);
 
 	if (blur_optimized) {
@@ -3533,7 +3534,6 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int sx,
 
 	if (blur && c && !c->noblur) {
 		wlr_scene_node_set_enabled(&c->blur->node, true);
-		// wlr_scene_blur_set_transparency_mask_source(c->blur, buffer);
 		if (blur_optimized) {
 			wlr_scene_blur_set_should_only_blur_bottom_layer(c->blur, true);
 		} else {

From fc677558f88a51d55e37f2805e291ccb30729209 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Wed, 19 Nov 2025 11:36:38 +0800
Subject: [PATCH 04/16] opt: set fadein blur to no blur_optimize

---
 src/animation/client.h | 10 ++++++++++
 src/animation/layer.h  |  7 ++++++-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/src/animation/client.h b/src/animation/client.h
index 3792358d4..56bf43287 100644
--- a/src/animation/client.h
+++ b/src/animation/client.h
@@ -1137,6 +1137,16 @@ bool client_apply_focus_opacity(Client *c) {
 			target_opacity = opacity;
 		}
 		client_set_opacity(c, target_opacity);
+		if (blur && !c->noblur && !blur_optimized) {
+			wlr_scene_blur_set_strength(
+				c->blur, MIN(percent * (1.0 - fadein_begin_opacity) +
+								 fadein_begin_opacity,
+							 1.0));
+			wlr_scene_blur_set_alpha(
+				c->blur, MIN(percent * (1.0 - fadein_begin_opacity) +
+								 fadein_begin_opacity,
+							 1.0));
+		}
 	} else if (animations && c->opacity_animation.running) {
 
 		struct timespec now;
diff --git a/src/animation/layer.h b/src/animation/layer.h
index 80caec565..aa9f0479d 100644
--- a/src/animation/layer.h
+++ b/src/animation/layer.h
@@ -326,9 +326,14 @@ void layer_animation_next_tick(LayerSurface *l) {
 							 animation_passed * (1.0 - fadein_begin_opacity),
 						 1.0f);
 
-	if (animation_fade_in)
+	if (animation_fade_in) {
+		if (blur && !l->noblur && !blur_optimized) {
+			wlr_scene_blur_set_strength(l->blur, opacity);
+			wlr_scene_blur_set_alpha(l->blur, opacity);
+		}
 		wlr_scene_node_for_each_buffer(&l->scene->node,
 									   scene_buffer_apply_opacity, &opacity);
+	}
 
 	wlr_scene_node_set_position(&l->scene->node, x, y);
 

From c934afcd4a7eb0c5669b1335cb059506d954d43c Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 13 Feb 2026 09:54:45 +0800
Subject: [PATCH 05/16] chase: chase scenefx api change

---
 src/animation/client.h | 44 +++++++++++++++++++++---------------------
 src/animation/common.h |  5 ++---
 src/animation/layer.h  |  3 +--
 src/config/preset.h    |  1 -
 src/mango.c            | 11 +++++------
 5 files changed, 30 insertions(+), 34 deletions(-)

diff --git a/src/animation/client.h b/src/animation/client.h
index 56bf43287..c1382a1b1 100644
--- a/src/animation/client.h
+++ b/src/animation/client.h
@@ -8,22 +8,27 @@ void set_rect_size(struct wlr_scene_rect *rect, int width, int height) {
 	wlr_scene_rect_set_size(rect, GEZERO(width), GEZERO(height));
 }
 
-enum corner_location set_client_corner_location(Client *c) {
-	enum corner_location current_corner_location = CORNER_LOCATION_ALL;
+struct fx_corner_radii set_client_corner_location(Client *c) {
+	struct fx_corner_radii current_corner_location =
+		corner_radii_all(border_radius);
 	struct wlr_box target_geom = animations ? c->animation.current : c->geom;
 	if (target_geom.x + border_radius <= c->mon->m.x) {
-		current_corner_location &= ~CORNER_LOCATION_LEFT; // 清除左标志位
+		current_corner_location.top_left = 0;	 // 清除左标志位
+		current_corner_location.bottom_left = 0; // 清除左标志位
 	}
 	if (target_geom.x + target_geom.width - border_radius >=
 		c->mon->m.x + c->mon->m.width) {
-		current_corner_location &= ~CORNER_LOCATION_RIGHT; // 清除右标志位
+		current_corner_location.top_right = 0;	  // 清除右标志位
+		current_corner_location.bottom_right = 0; // 清除右标志位
 	}
 	if (target_geom.y + border_radius <= c->mon->m.y) {
-		current_corner_location &= ~CORNER_LOCATION_TOP; // 清除上标志位
+		current_corner_location.top_left = 0;  // 清除上标志位
+		current_corner_location.top_right = 0; // 清除上标志位
 	}
 	if (target_geom.y + target_geom.height - border_radius >=
 		c->mon->m.y + c->mon->m.height) {
-		current_corner_location &= ~CORNER_LOCATION_BOTTOM; // 清除下标志位
+		current_corner_location.bottom_left = 0;  // 清除下标志位
+		current_corner_location.bottom_right = 0; // 清除下标志位
 	}
 	return current_corner_location;
 }
@@ -223,8 +228,7 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int sx, int sy,
 	if (wlr_xdg_popup_try_from_wlr_surface(surface) != NULL)
 		return;
 
-	wlr_scene_buffer_set_corner_radius(buffer, border_radius,
-									   buffer_data->corner_location);
+	wlr_scene_buffer_set_corner_radii(buffer, buffer_data->corner_location);
 }
 
 void buffer_set_effect(Client *c, BufferData data) {
@@ -242,12 +246,11 @@ void buffer_set_effect(Client *c, BufferData data) {
 
 	if (c->isfullscreen || (no_radius_when_single && c->mon &&
 							c->mon->visible_tiling_clients == 1)) {
-		data.corner_location = CORNER_LOCATION_NONE;
+		data.corner_location = corner_radii_none();
 	}
 
 	if (blur && !c->noblur) {
-		wlr_scene_blur_set_corner_radius(c->blur, border_radius,
-										 data.corner_location);
+		wlr_scene_blur_set_corner_radii(c->blur, data.corner_location);
 	}
 	wlr_scene_node_for_each_buffer(&c->scene_surface->node,
 								   scene_buffer_apply_effect, &data);
@@ -264,11 +267,11 @@ void client_draw_shadow(Client *c) {
 	}
 
 	bool hit_no_border = check_hit_no_border(c);
-	enum corner_location current_corner_location =
+	struct fx_corner_radii current_corner_location =
 		c->isfullscreen || (no_radius_when_single && c->mon &&
 							c->mon->visible_tiling_clients == 1)
-			? CORNER_LOCATION_NONE
-			: CORNER_LOCATION_ALL;
+			? corner_radii_none()
+			: set_client_corner_location(c);
 
 	unsigned int bwoffset = c->bw != 0 && hit_no_border ? c->bw : 0;
 
@@ -301,7 +304,6 @@ void client_draw_shadow(Client *c) {
 
 	struct clipped_region clipped_region = {
 		.area = intersection_box,
-		.corner_radius = border_radius,
 		.corners = current_corner_location,
 	};
 
@@ -362,11 +364,11 @@ void apply_border(Client *c) {
 		return;
 
 	bool hit_no_border = check_hit_no_border(c);
-	enum corner_location current_corner_location =
+	struct fx_corner_radii current_corner_location =
 		c->isfullscreen || (no_radius_when_single && c->mon &&
 							c->mon->visible_tiling_clients == 1)
-			? CORNER_LOCATION_NONE
-			: CORNER_LOCATION_ALL;
+			? corner_radii_none()
+			: set_client_corner_location(c);
 
 	// Handle no-border cases
 	if (hit_no_border && smartgaps) {
@@ -438,15 +440,13 @@ void apply_border(Client *c) {
 	struct clipped_region clipped_region = {
 		.area = {inner_surface_x, inner_surface_y, inner_surface_width,
 				 inner_surface_height},
-		.corner_radius = border_radius,
 		.corners = current_corner_location,
 	};
 
 	wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
 	wlr_scene_rect_set_size(c->border, rect_width, rect_height);
 	wlr_scene_node_set_position(&c->border->node, rect_x, rect_y);
-	wlr_scene_rect_set_corner_radius(c->border, border_radius,
-									 current_corner_location);
+	wlr_scene_rect_set_corner_radii(c->border, current_corner_location);
 	wlr_scene_rect_set_clipped_region(c->border, clipped_region);
 }
 
@@ -524,7 +524,7 @@ void client_apply_clip(Client *c, float factor) {
 	struct ivec2 offset;
 	BufferData buffer_data;
 
-	enum corner_location current_corner_location =
+	struct fx_corner_radii current_corner_location =
 		set_client_corner_location(c);
 
 	int bw = (int)c->bw;
diff --git a/src/animation/common.h b/src/animation/common.h
index 7079df867..8f15010f3 100644
--- a/src/animation/common.h
+++ b/src/animation/common.h
@@ -154,9 +154,8 @@ static bool scene_node_snapshot(struct wlr_scene_node *node, int lx, int ly,
 
 		// Effects
 		wlr_scene_buffer_set_opacity(snapshot_buffer, scene_buffer->opacity);
-		wlr_scene_buffer_set_corner_radius(snapshot_buffer,
-										   scene_buffer->corner_radius,
-										   scene_buffer->corners);
+		wlr_scene_buffer_set_corner_radii(snapshot_buffer,
+										  scene_buffer->corners);
 
 		snapshot_buffer->node.data = scene_buffer->node.data;
 
diff --git a/src/animation/layer.h b/src/animation/layer.h
index aa9f0479d..e21924bf8 100644
--- a/src/animation/layer.h
+++ b/src/animation/layer.h
@@ -191,8 +191,7 @@ void layer_draw_shadow(LayerSurface *l) {
 
 	struct clipped_region clipped_region = {
 		.area = intersection_box,
-		.corner_radius = border_radius,
-		.corners = border_radius_location_default,
+		.corners = corner_radii_all(border_radius),
 	};
 
 	wlr_scene_node_set_position(&l->shadow->node, shadow_box.x, shadow_box.y);
diff --git a/src/config/preset.h b/src/config/preset.h
index 2f994ec17..bbeaf3079 100644
--- a/src/config/preset.h
+++ b/src/config/preset.h
@@ -204,7 +204,6 @@ float focused_opacity = 1.0;
 float unfocused_opacity = 1.0;
 
 int border_radius = 0;
-int border_radius_location_default = CORNER_LOCATION_ALL;
 int blur = 0;
 int blur_layer = 0;
 int blur_optimized = 1;
diff --git a/src/mango.c b/src/mango.c
index 80ad2415a..f8021be7e 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -262,7 +262,7 @@ typedef struct {
 	float height_scale;
 	int width;
 	int height;
-	enum corner_location corner_location;
+	struct fx_corner_radii corner_location;
 	bool should_scale;
 } BufferData;
 
@@ -369,6 +369,7 @@ struct Client {
 	bool isleftstack;
 	int tearing_hint;
 	int force_tearing;
+	int border_radius;
 };
 
 typedef struct {
@@ -694,7 +695,7 @@ static double find_animation_curve_at(double t, int type);
 
 static void apply_opacity_to_rect_nodes(Client *c, struct wlr_scene_node *node,
 										double animation_passed);
-static enum corner_location set_client_corner_location(Client *c);
+static struct fx_corner_radii set_client_corner_location(Client *c);
 static double all_output_frame_duration_ms();
 static struct wlr_scene_tree *
 wlr_scene_tree_snapshot(struct wlr_scene_node *node,
@@ -2086,8 +2087,7 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer, int sx,
 	LayerSurface *l = (LayerSurface *)user_data;
 
 	wlr_scene_node_set_enabled(&l->blur->node, true);
-	wlr_scene_blur_set_mask_source(l->blur, &buffer->node,
-								   BLUR_MASK_IGNORE_TRANSPARENCY);
+	wlr_scene_blur_set_transparency_mask_source(l->blur, buffer);
 	wlr_scene_blur_set_size(l->blur, l->geom.width, l->geom.height);
 
 	if (blur_optimized) {
@@ -3646,8 +3646,7 @@ mapnotify(struct wl_listener *listener, void *data) {
 									  c->isurgent ? urgentcolor : bordercolor);
 	wlr_scene_node_lower_to_bottom(&c->border->node);
 	wlr_scene_node_set_position(&c->border->node, 0, 0);
-	wlr_scene_rect_set_corner_radius(c->border, border_radius,
-									 border_radius_location_default);
+	wlr_scene_rect_set_corner_radii(c->border, corner_radii_all(border_radius));
 	wlr_scene_node_set_enabled(&c->border->node, true);
 
 	c->shadow = wlr_scene_shadow_create(c->scene, 0, 0, border_radius,

From 6c166387538bd953095986b12166d6a127d173af Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 13 Feb 2026 11:06:12 +0800
Subject: [PATCH 06/16] opt: flush the blur background cache when unmap a
 background layer

---
 src/mango.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/src/mango.c b/src/mango.c
index d53df0c6a..d3400d583 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -2281,6 +2281,19 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer,
 	}
 }
 
+void layer_flush_blur_background(LayerSurface *l) {
+	if (!blur)
+		return;
+
+	// 如果背景层发生变化,标记优化的模糊背景缓存需要更新
+	if (l->layer_surface->current.layer ==
+		ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
+		if (l->mon) {
+			wlr_scene_optimized_blur_mark_dirty(l->mon->blur);
+		}
+	}
+}
+
 void maplayersurfacenotify(struct wl_listener *listener, void *data) {
 	LayerSurface *l = wl_container_of(listener, l, map);
 	struct wlr_layer_surface_v1 *layer_surface = l->layer_surface;
@@ -2413,15 +2426,7 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) {
 		}
 	}
 
-	if (blur) {
-		// 如果背景层发生变化,标记优化的模糊背景缓存需要更新
-		if (layer_surface->current.layer ==
-			ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
-			if (l->mon) {
-				wlr_scene_optimized_blur_mark_dirty(l->mon->blur);
-			}
-		}
-	}
+	layer_flush_blur_background(l);
 
 	if (layer_surface == exclusive_focus &&
 		layer_surface->current.keyboard_interactive !=
@@ -5655,6 +5660,7 @@ void unmaplayersurfacenotify(struct wl_listener *listener, void *data) {
 		focusclient(focustop(selmon), 1);
 	motionnotify(0, NULL, 0, 0, 0, 0);
 	l->being_unmapped = false;
+	layer_flush_blur_background(l);
 	wlr_scene_node_destroy(&l->shadow->node);
 	l->shadow = NULL;
 }

From 4f3e6545bf6a15bb684afc58e72acf83eae1e826 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 13 Feb 2026 11:11:46 +0800
Subject: [PATCH 07/16] fix: remove the useless code

---
 src/mango.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/mango.c b/src/mango.c
index d3400d583..c96c2357b 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -11,7 +11,6 @@
 #include <scenefx/render/fx_renderer/fx_renderer.h>
 #include <scenefx/types/fx/blur_data.h>
 #include <scenefx/types/fx/clipped_region.h>
-#include <scenefx/types/fx/corner_location.h>
 #include <scenefx/types/wlr_scene.h>
 #include <signal.h>
 #include <stdbool.h>

From 3b535d4005b0486d918913b3128554e56dd154a5 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 19 Jun 2026 13:16:57 +0800
Subject: [PATCH 08/16] mix: add window share and fix 
 xcursor,text-input,ext-workspace

---
 meson.build                             |   5 +-
 src/ext-protocol/ext-workspace.h        | 108 ++-
 src/ext-protocol/text-input.h           |  22 +-
 src/ext-protocol/wlr_ext_workspace_v1.c | 997 ------------------------
 src/ext-protocol/wlr_ext_workspace_v1.h | 106 ---
 src/fetch/monitor.h                     |   9 +
 src/mango.c                             |  86 +-
 7 files changed, 163 insertions(+), 1170 deletions(-)
 delete mode 100644 src/ext-protocol/wlr_ext_workspace_v1.c
 delete mode 100644 src/ext-protocol/wlr_ext_workspace_v1.h

diff --git a/meson.build b/meson.build
index 478ef0e6b..f6f6d6f91 100644
--- a/meson.build
+++ b/meson.build
@@ -33,12 +33,12 @@ libm = cc.find_library('m')
 xcb = dependency('xcb', required : get_option('xwayland'))
 xlibs = dependency('xcb-icccm', required : get_option('xwayland'))
 wayland_server_dep = dependency('wayland-server',version: '>=1.23.1')
-wlroots_dep = dependency('wlroots-0.19',version: '>=0.19.0')
+wlroots_dep = dependency('wlroots-0.20',version: '>=0.20.0')
 xkbcommon_dep = dependency('xkbcommon')
 libinput_dep = dependency('libinput',version: '>=1.27.1')
 libwayland_client_dep = dependency('wayland-client')
 pcre2_dep = dependency('libpcre2-8')
-libscenefx_dep = dependency('scenefx-0.4',version: '>=0.4.1')
+libscenefx_dep = dependency('scenefx-0.5',version: '>=0.5.0')
 
 
 # 获取版本信息
@@ -97,7 +97,6 @@ endif
 executable('mango',
   'src/mango.c',
   'src/common/util.c',
-  'src/ext-protocol/wlr_ext_workspace_v1.c',
   wayland_sources,
   dependencies : [
     libm,
diff --git a/src/ext-protocol/ext-workspace.h b/src/ext-protocol/ext-workspace.h
index 28aaeeeb7..82b7ef1f2 100644
--- a/src/ext-protocol/ext-workspace.h
+++ b/src/ext-protocol/ext-workspace.h
@@ -1,4 +1,4 @@
-#include "wlr_ext_workspace_v1.h"
+#include <wlr/types/wlr_ext_workspace_v1.h>
 
 #define EXT_WORKSPACE_ENABLE_CAPS                                              \
 	EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE |                  \
@@ -7,20 +7,20 @@
 typedef struct Monitor Monitor;
 
 struct workspace {
-	struct wl_list link; // Link in global workspaces list
-	uint32_t tag;		 // Numeric identifier (1-9, 0=overview)
-	Monitor *m;			 // Associated monitor
-	struct wlr_ext_workspace_handle_v1 *ext_workspace; // Protocol object
-	/* Event listeners */
-	struct wl_listener activate;
-	struct wl_listener deactivate;
-	struct wl_listener assign;
-	struct wl_listener remove;
+	struct wl_list link;
+	uint32_t tag;
+	Monitor *m;
+	struct wlr_ext_workspace_handle_v1 *ext_workspace;
+	struct wl_listener commit;
 };
 
 struct wlr_ext_workspace_manager_v1 *ext_manager;
 struct wl_list workspaces;
 
+static void handle_ext_commit(struct wl_listener *listener, void *data);
+static struct wl_listener ext_manager_commit_listener = {.notify =
+															 handle_ext_commit};
+
 void goto_workspace(struct workspace *target) {
 	uint32_t tag;
 	tag = 1 << (target->tag - 1);
@@ -43,30 +43,60 @@ void toggle_workspace(struct workspace *target) {
 	}
 }
 
-static void handle_ext_workspace_activate(struct wl_listener *listener,
-										  void *data) {
-	struct workspace *workspace =
-		wl_container_of(listener, workspace, activate);
+static void handle_ext_commit(struct wl_listener *listener, void *data) {
+	struct wlr_ext_workspace_v1_commit_event *event = data;
+	struct wlr_ext_workspace_v1_request *request;
 
-	if (workspace->m->isoverview) {
-		return;
-	}
+	wl_list_for_each(request, event->requests, link) {
+		switch (request->type) {
+		case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE: {
+			if (!request->activate.workspace) {
+				break;
+			}
 
-	goto_workspace(workspace);
-	wlr_log(WLR_INFO, "ext activating workspace %d", workspace->tag);
-}
+			struct workspace *workspace = NULL;
+			struct workspace *w;
+			wl_list_for_each(w, &workspaces, link) {
+				if (w->ext_workspace == request->activate.workspace) {
+					workspace = w;
+					break;
+				}
+			}
 
-static void handle_ext_workspace_deactivate(struct wl_listener *listener,
-											void *data) {
-	struct workspace *workspace =
-		wl_container_of(listener, workspace, deactivate);
+			if (!workspace || workspace->m->isoverview) {
+				break;
+			}
 
-	if (workspace->m->isoverview) {
-		return;
-	}
+			goto_workspace(workspace);
+			wlr_log(WLR_INFO, "ext activating workspace %d", workspace->tag);
+			break;
+		}
+		case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE: {
+			if (!request->deactivate.workspace) {
+				break;
+			}
 
-	toggle_workspace(workspace);
-	wlr_log(WLR_INFO, "ext deactivating workspace %d", workspace->tag);
+			struct workspace *workspace = NULL;
+			struct workspace *w;
+			wl_list_for_each(w, &workspaces, link) {
+				if (w->ext_workspace == request->deactivate.workspace) {
+					workspace = w;
+					break;
+				}
+			}
+
+			if (!workspace || workspace->m->isoverview) {
+				break;
+			}
+
+			toggle_workspace(workspace);
+			wlr_log(WLR_INFO, "ext deactivating workspace %d", workspace->tag);
+			break;
+		}
+		default:
+			break;
+		}
+	}
 }
 
 static const char *get_name_from_tag(uint32_t tag) {
@@ -76,8 +106,6 @@ static const char *get_name_from_tag(uint32_t tag) {
 }
 
 void destroy_workspace(struct workspace *workspace) {
-	wl_list_remove(&workspace->activate.link);
-	wl_list_remove(&workspace->deactivate.link);
 	wlr_ext_workspace_handle_v1_destroy(workspace->ext_workspace);
 	wl_list_remove(&workspace->link);
 	free(workspace);
@@ -112,17 +140,12 @@ static void add_workspace_by_tag(int32_t tag, Monitor *m) {
 	workspace->m = m;
 	workspace->ext_workspace = wlr_ext_workspace_handle_v1_create(
 		ext_manager, name, EXT_WORKSPACE_ENABLE_CAPS);
+
+	workspace->ext_workspace->data = workspace;
+
 	wlr_ext_workspace_handle_v1_set_group(workspace->ext_workspace,
 										  m->ext_group);
 	wlr_ext_workspace_handle_v1_set_name(workspace->ext_workspace, name);
-
-	workspace->activate.notify = handle_ext_workspace_activate;
-	wl_signal_add(&workspace->ext_workspace->events.activate,
-				  &workspace->activate);
-
-	workspace->deactivate.notify = handle_ext_workspace_deactivate;
-	wl_signal_add(&workspace->ext_workspace->events.deactivate,
-				  &workspace->deactivate);
 }
 
 void dwl_ext_workspace_printstatus(Monitor *m) {
@@ -165,10 +188,10 @@ void refresh_monitors_workspaces_status(Monitor *m) {
 	int32_t i;
 
 	if (m->isoverview) {
+		add_workspace_by_tag(0, m);
 		for (i = 1; i <= LENGTH(tags); i++) {
 			remove_workspace_by_tag(i, m);
 		}
-		add_workspace_by_tag(0, m);
 	} else {
 		remove_workspace_by_tag(0, m);
 		for (i = 1; i <= LENGTH(tags); i++) {
@@ -180,8 +203,9 @@ void refresh_monitors_workspaces_status(Monitor *m) {
 }
 
 void workspaces_init() {
-	/* Create the global workspace manager with activation capability */
 	ext_manager = wlr_ext_workspace_manager_v1_create(dpy, 1);
-	/* Initialize the global workspaces list */
+
 	wl_list_init(&workspaces);
+
+	wl_signal_add(&ext_manager->events.commit, &ext_manager_commit_listener);
 }
\ No newline at end of file
diff --git a/src/ext-protocol/text-input.h b/src/ext-protocol/text-input.h
index dbd97e11f..7b4e1a1fd 100644
--- a/src/ext-protocol/text-input.h
+++ b/src/ext-protocol/text-input.h
@@ -77,15 +77,6 @@ Monitor *output_from_wlr_output(struct wlr_output *wlr_output) {
 	return NULL;
 }
 
-Monitor *output_nearest_to(int32_t lx, int32_t ly) {
-	double closest_x, closest_y;
-	wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x,
-									&closest_y);
-
-	return output_from_wlr_output(
-		wlr_output_layout_output_at(output_layout, closest_x, closest_y));
-}
-
 bool output_is_usable(Monitor *m) { return m && m->wlr_output->enabled; }
 
 static bool
@@ -255,7 +246,7 @@ static void update_popup_position(struct dwl_input_method_popup *popup) {
 		cursor_rect = (struct wlr_box){0};
 	}
 
-	output = output_nearest_to(cursor_rect.x, cursor_rect.y);
+	output = get_monitor_nearest_to(cursor_rect.x, cursor_rect.y);
 	if (!output_is_usable(output)) {
 		return;
 	}
@@ -301,9 +292,8 @@ static void handle_input_method_commit(struct wl_listener *listener,
 									   void *data) {
 	struct dwl_input_method_relay *relay =
 		wl_container_of(listener, relay, input_method_commit);
-	struct wlr_input_method_v2 *input_method = data;
 	struct text_input *text_input;
-	assert(relay->input_method == input_method);
+	struct wlr_input_method_v2 *input_method = relay->input_method;
 
 	text_input = relay->active_text_input;
 	if (!text_input) {
@@ -333,7 +323,8 @@ static void handle_keyboard_grab_destroy(struct wl_listener *listener,
 										 void *data) {
 	struct dwl_input_method_relay *relay =
 		wl_container_of(listener, relay, keyboard_grab_destroy);
-	struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data;
+	struct wlr_input_method_keyboard_grab_v2 *keyboard_grab =
+		relay->input_method->keyboard_grab;
 	wl_list_remove(&relay->keyboard_grab_destroy.link);
 
 	if (keyboard_grab->keyboard) {
@@ -365,7 +356,6 @@ static void handle_input_method_destroy(struct wl_listener *listener,
 										void *data) {
 	struct dwl_input_method_relay *relay =
 		wl_container_of(listener, relay, input_method_destroy);
-	assert(relay->input_method == data);
 	wl_list_remove(&relay->input_method_commit.link);
 	wl_list_remove(&relay->input_method_grab_keyboard.link);
 	wl_list_remove(&relay->input_method_new_popup_surface.link);
@@ -571,11 +561,11 @@ struct dwl_input_method_relay *dwl_im_relay_create() {
 	relay->popup_tree = wlr_scene_tree_create(&scene->tree);
 
 	relay->new_text_input.notify = handle_new_text_input;
-	wl_signal_add(&text_input_manager->events.text_input,
+	wl_signal_add(&text_input_manager->events.new_text_input,
 				  &relay->new_text_input);
 
 	relay->new_input_method.notify = handle_new_input_method;
-	wl_signal_add(&input_method_manager->events.input_method,
+	wl_signal_add(&input_method_manager->events.new_input_method,
 				  &relay->new_input_method);
 
 	relay->focused_surface_destroy.notify = handle_focused_surface_destroy;
diff --git a/src/ext-protocol/wlr_ext_workspace_v1.c b/src/ext-protocol/wlr_ext_workspace_v1.c
deleted file mode 100644
index 2d781b346..000000000
--- a/src/ext-protocol/wlr_ext_workspace_v1.c
+++ /dev/null
@@ -1,997 +0,0 @@
-// bash on: https://gitlab.freedesktop.org/tokyo4j/wlroots/-/tree/ext-workspace
-// TODO: remove this file
-// refer: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5115
-
-#include "wlr_ext_workspace_v1.h"
-#include "ext-workspace-v1-protocol.h"
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wlr/types/wlr_output.h>
-
-#define EXT_WORKSPACE_V1_VERSION 1
-
-enum wlr_ext_workspace_v1_request_type {
-	WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE,
-	WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE,
-	WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE,
-	WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN,
-	WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE,
-};
-
-struct wlr_ext_workspace_v1_request {
-	enum wlr_ext_workspace_v1_request_type type;
-
-	// CREATE_WORKSPACE
-	char *name;
-	// CREATE_WORKSPACE / ASSIGN
-	struct wlr_ext_workspace_group_handle_v1 *group;
-	// ACTIVATE / DEACTIVATE / ASSIGN / REMOVE
-	struct wlr_ext_workspace_handle_v1 *workspace;
-
-	struct wl_list link; // wlr_ext_workspace_manager_v1_resource.requests
-};
-
-struct wlr_ext_workspace_v1_group_output {
-	struct wlr_output *output;
-	struct wlr_ext_workspace_group_handle_v1 *group;
-	struct wl_listener output_bind;
-	struct wl_listener output_destroy;
-	struct wl_list link;
-};
-
-// These structs wrap wl_resource of each interface to access the request queue
-// (wlr_ext_workspace_manager_v1_resource.requests) assigned per manager
-// resource
-
-struct wlr_ext_workspace_manager_v1_resource {
-	struct wl_resource *resource;
-	struct wlr_ext_workspace_manager_v1 *manager;
-	struct wl_list requests;			// wlr_ext_workspace_v1_request.link
-	struct wl_list workspace_resources; // wlr_ext_workspace_v1_resource.link
-	struct wl_list group_resources; // wlr_ext_workspace_group_v1_resource.link
-	struct wl_list link;			// wlr_ext_workspace_manager_v1.resources
-};
-
-struct wlr_ext_workspace_group_v1_resource {
-	struct wl_resource *resource;
-	struct wlr_ext_workspace_group_handle_v1 *group;
-	struct wlr_ext_workspace_manager_v1_resource *manager;
-	struct wl_list link; // wlr_ext_workspace_group_v1.resources
-	struct wl_list
-		manager_resource_link; // wlr_ext_workspace_manager_v1_resource.group_resources
-};
-
-struct wlr_ext_workspace_v1_resource {
-	struct wl_resource *resource;
-	struct wlr_ext_workspace_handle_v1 *workspace;
-	struct wlr_ext_workspace_manager_v1_resource *manager;
-	struct wl_list link; // wlr_ext_workspace_v1.resources
-	struct wl_list
-		manager_resource_link; // wlr_ext_workspace_manager_v1_resource.workspace_resources
-};
-
-static const struct ext_workspace_group_handle_v1_interface group_impl;
-
-static struct wlr_ext_workspace_group_v1_resource *
-group_resource_from_resource(struct wl_resource *resource) {
-	assert(wl_resource_instance_of(
-		resource, &ext_workspace_group_handle_v1_interface, &group_impl));
-	return wl_resource_get_user_data(resource);
-}
-
-static const struct ext_workspace_handle_v1_interface workspace_impl;
-
-static struct wlr_ext_workspace_v1_resource *
-workspace_resource_from_resource(struct wl_resource *resource) {
-	assert(wl_resource_instance_of(resource, &ext_workspace_handle_v1_interface,
-								   &workspace_impl));
-	return wl_resource_get_user_data(resource);
-}
-
-static const struct ext_workspace_manager_v1_interface manager_impl;
-
-static struct wlr_ext_workspace_manager_v1_resource *
-manager_resource_from_resource(struct wl_resource *resource) {
-	assert(wl_resource_instance_of(
-		resource, &ext_workspace_manager_v1_interface, &manager_impl));
-	return wl_resource_get_user_data(resource);
-}
-
-static void workspace_handle_destroy(struct wl_client *client,
-									 struct wl_resource *resource) {
-	wl_resource_destroy(resource);
-}
-
-static void workspace_handle_activate(struct wl_client *client,
-									  struct wl_resource *workspace_resource) {
-	struct wlr_ext_workspace_v1_resource *workspace_res =
-		workspace_resource_from_resource(workspace_resource);
-	if (!workspace_res) {
-		return;
-	}
-
-	struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
-	if (!req) {
-		wl_resource_post_no_memory(workspace_resource);
-		return;
-	}
-	req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE;
-	req->workspace = workspace_res->workspace;
-	wl_list_insert(workspace_res->manager->requests.prev, &req->link);
-}
-
-static void
-workspace_handle_deactivate(struct wl_client *client,
-							struct wl_resource *workspace_resource) {
-	struct wlr_ext_workspace_v1_resource *workspace_res =
-		workspace_resource_from_resource(workspace_resource);
-	if (!workspace_res) {
-		return;
-	}
-
-	struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
-	if (!req) {
-		wl_resource_post_no_memory(workspace_resource);
-		return;
-	}
-	req->type = WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE;
-	req->workspace = workspace_res->workspace;
-	wl_list_insert(workspace_res->manager->requests.prev, &req->link);
-}
-
-static void workspace_handle_assign(struct wl_client *client,
-									struct wl_resource *workspace_resource,
-									struct wl_resource *group_resource) {
-	struct wlr_ext_workspace_v1_resource *workspace_res =
-		workspace_resource_from_resource(workspace_resource);
-	struct wlr_ext_workspace_group_v1_resource *group_res =
-		group_resource_from_resource(group_resource);
-	if (!workspace_res || !group_res) {
-		return;
-	}
-
-	struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
-	if (!req) {
-		wl_resource_post_no_memory(workspace_resource);
-		return;
-	}
-	req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN;
-	req->group = group_res->group;
-	req->workspace = workspace_res->workspace;
-	wl_list_insert(workspace_res->manager->requests.prev, &req->link);
-}
-
-static void workspace_handle_remove(struct wl_client *client,
-									struct wl_resource *workspace_resource) {
-	struct wlr_ext_workspace_v1_resource *workspace_res =
-		workspace_resource_from_resource(workspace_resource);
-	if (!workspace_res) {
-		return;
-	}
-
-	struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
-	if (!req) {
-		wl_resource_post_no_memory(workspace_resource);
-		return;
-	}
-	req->type = WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE;
-	req->workspace = workspace_res->workspace;
-	wl_list_insert(workspace_res->manager->requests.prev, &req->link);
-}
-
-static const struct ext_workspace_handle_v1_interface workspace_impl = {
-	.destroy = workspace_handle_destroy,
-	.activate = workspace_handle_activate,
-	.deactivate = workspace_handle_deactivate,
-	.assign = workspace_handle_assign,
-	.remove = workspace_handle_remove,
-};
-
-static void group_handle_create_workspace(struct wl_client *client,
-										  struct wl_resource *group_resource,
-										  const char *name) {
-	struct wlr_ext_workspace_group_v1_resource *group_res =
-		group_resource_from_resource(group_resource);
-	if (!group_res) {
-		return;
-	}
-
-	struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req));
-	if (!req) {
-		wl_resource_post_no_memory(group_resource);
-		return;
-	}
-	req->name = strdup(name);
-	if (!req->name) {
-		free(req);
-		wl_resource_post_no_memory(group_resource);
-		return;
-	}
-	req->type = WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE;
-	req->group = group_res->group;
-	wl_list_insert(group_res->manager->requests.prev, &req->link);
-}
-
-static void group_handle_destroy(struct wl_client *client,
-								 struct wl_resource *resource) {
-	wl_resource_destroy(resource);
-}
-
-static const struct ext_workspace_group_handle_v1_interface group_impl = {
-	.create_workspace = group_handle_create_workspace,
-	.destroy = group_handle_destroy,
-};
-
-static void destroy_workspace_resource(
-	struct wlr_ext_workspace_v1_resource *workspace_res) {
-	wl_list_remove(&workspace_res->link);
-	wl_list_remove(&workspace_res->manager_resource_link);
-	wl_resource_set_user_data(workspace_res->resource, NULL);
-	free(workspace_res);
-}
-
-static void workspace_resource_destroy(struct wl_resource *resource) {
-	struct wlr_ext_workspace_v1_resource *workspace_res =
-		workspace_resource_from_resource(resource);
-	if (workspace_res) {
-		destroy_workspace_resource(workspace_res);
-	}
-}
-
-static struct wlr_ext_workspace_v1_resource *create_workspace_resource(
-	struct wlr_ext_workspace_handle_v1 *workspace,
-	struct wlr_ext_workspace_manager_v1_resource *manager_res) {
-	struct wlr_ext_workspace_v1_resource *workspace_res =
-		calloc(1, sizeof(*workspace_res));
-	if (!workspace_res) {
-		return NULL;
-	}
-
-	struct wl_client *client = wl_resource_get_client(manager_res->resource);
-	workspace_res->resource =
-		wl_resource_create(client, &ext_workspace_handle_v1_interface,
-						   wl_resource_get_version(manager_res->resource), 0);
-	if (!workspace_res->resource) {
-		free(workspace_res);
-		return NULL;
-	}
-	wl_resource_set_implementation(workspace_res->resource, &workspace_impl,
-								   workspace_res, workspace_resource_destroy);
-
-	workspace_res->workspace = workspace;
-	workspace_res->manager = manager_res;
-	wl_list_insert(&workspace->resources, &workspace_res->link);
-	wl_list_insert(&manager_res->workspace_resources,
-				   &workspace_res->manager_resource_link);
-
-	return workspace_res;
-}
-
-static void
-destroy_group_resource(struct wlr_ext_workspace_group_v1_resource *group_res) {
-	wl_list_remove(&group_res->link);
-	wl_list_remove(&group_res->manager_resource_link);
-	wl_resource_set_user_data(group_res->resource, NULL);
-	free(group_res);
-}
-
-static void group_handle_resource_destroy(struct wl_resource *resource) {
-	struct wlr_ext_workspace_group_v1_resource *group_res =
-		group_resource_from_resource(resource);
-	if (group_res) {
-		destroy_group_resource(group_res);
-	}
-}
-
-static struct wlr_ext_workspace_group_v1_resource *create_group_resource(
-	struct wlr_ext_workspace_group_handle_v1 *group,
-	struct wlr_ext_workspace_manager_v1_resource *manager_res) {
-	struct wlr_ext_workspace_group_v1_resource *group_res =
-		calloc(1, sizeof(*group_res));
-	if (!group_res) {
-		return NULL;
-	}
-
-	struct wl_client *client = wl_resource_get_client(manager_res->resource);
-	uint32_t version = wl_resource_get_version(manager_res->resource);
-	group_res->resource = wl_resource_create(
-		client, &ext_workspace_group_handle_v1_interface, version, 0);
-	if (group_res->resource == NULL) {
-		free(group_res);
-		return NULL;
-	}
-	wl_resource_set_implementation(group_res->resource, &group_impl, group_res,
-								   group_handle_resource_destroy);
-
-	group_res->group = group;
-	group_res->manager = manager_res;
-	wl_list_insert(&group->resources, &group_res->link);
-	wl_list_insert(&manager_res->group_resources,
-				   &group_res->manager_resource_link);
-
-	return group_res;
-}
-
-static void destroy_request(struct wlr_ext_workspace_v1_request *req) {
-	wl_list_remove(&req->link);
-	free(req->name);
-	free(req);
-}
-
-static void manager_handle_commit(struct wl_client *client,
-								  struct wl_resource *resource) {
-	struct wlr_ext_workspace_manager_v1_resource *manager_res =
-		manager_resource_from_resource(resource);
-	if (!manager_res) {
-		return;
-	}
-
-	struct wlr_ext_workspace_v1_request *req, *tmp;
-	wl_list_for_each_safe(req, tmp, &manager_res->requests, link) {
-		switch (req->type) {
-		case WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE:;
-			struct wlr_ext_workspace_group_handle_v1_create_workspace_event
-				event = {
-					.name = req->name,
-				};
-			wl_signal_emit_mutable(&req->group->events.create_workspace,
-								   &event);
-			break;
-		case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE:
-			wl_signal_emit_mutable(&req->workspace->events.activate, NULL);
-			break;
-		case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE:
-			wl_signal_emit_mutable(&req->workspace->events.deactivate, NULL);
-			break;
-		case WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN:
-			wl_signal_emit_mutable(&req->workspace->events.assign, req->group);
-			break;
-		case WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE:
-			wl_signal_emit_mutable(&req->workspace->events.remove, NULL);
-			break;
-		}
-		destroy_request(req);
-	}
-}
-
-static void handle_idle(void *data) {
-	struct wlr_ext_workspace_manager_v1 *manager = data;
-
-	struct wlr_ext_workspace_manager_v1_resource *manager_res;
-	wl_list_for_each(manager_res, &manager->resources, link) {
-		ext_workspace_manager_v1_send_done(manager_res->resource);
-	}
-	manager->idle_source = NULL;
-}
-
-static void
-manager_schedule_done(struct wlr_ext_workspace_manager_v1 *manager) {
-	if (!manager->idle_source) {
-		manager->idle_source =
-			wl_event_loop_add_idle(manager->event_loop, handle_idle, manager);
-	}
-}
-
-static void
-workspace_send_details(struct wlr_ext_workspace_v1_resource *workspace_res) {
-	struct wlr_ext_workspace_handle_v1 *workspace = workspace_res->workspace;
-	struct wl_resource *resource = workspace_res->resource;
-
-	ext_workspace_handle_v1_send_capabilities(resource, workspace->caps);
-	if (workspace->coordinates.size > 0) {
-		ext_workspace_handle_v1_send_coordinates(resource,
-												 &workspace->coordinates);
-	}
-	if (workspace->name) {
-		ext_workspace_handle_v1_send_name(resource, workspace->name);
-	}
-	if (workspace->id) {
-		ext_workspace_handle_v1_send_id(resource, workspace->id);
-	}
-	ext_workspace_handle_v1_send_state(resource, workspace->state);
-	manager_schedule_done(workspace->manager);
-}
-
-static void manager_handle_stop(struct wl_client *client,
-								struct wl_resource *resource) {
-	ext_workspace_manager_v1_send_finished(resource);
-	wl_resource_destroy(resource);
-}
-
-static const struct ext_workspace_manager_v1_interface manager_impl = {
-	.commit = manager_handle_commit,
-	.stop = manager_handle_stop,
-};
-
-static void destroy_manager_resource(
-	struct wlr_ext_workspace_manager_v1_resource *manager_res) {
-	struct wlr_ext_workspace_v1_request *req, *tmp;
-	wl_list_for_each_safe(req, tmp, &manager_res->requests, link) {
-		destroy_request(req);
-	}
-	struct wlr_ext_workspace_v1_resource *workspace_res, *tmp2;
-	wl_list_for_each_safe(workspace_res, tmp2,
-						  &manager_res->workspace_resources,
-						  manager_resource_link) {
-		destroy_workspace_resource(workspace_res);
-	}
-	struct wlr_ext_workspace_group_v1_resource *group_res, *tmp3;
-	wl_list_for_each_safe(group_res, tmp3, &manager_res->group_resources,
-						  manager_resource_link) {
-		destroy_group_resource(group_res);
-	}
-
-	wl_list_remove(&manager_res->link);
-	wl_resource_set_user_data(manager_res->resource, NULL);
-	free(manager_res);
-}
-
-static void manager_resource_destroy(struct wl_resource *resource) {
-	struct wlr_ext_workspace_manager_v1_resource *manager_res =
-		manager_resource_from_resource(resource);
-	if (manager_res) {
-		destroy_manager_resource(manager_res);
-	}
-}
-
-static void
-group_send_details(struct wlr_ext_workspace_group_v1_resource *group_res) {
-	struct wlr_ext_workspace_group_handle_v1 *group = group_res->group;
-	struct wl_resource *resource = group_res->resource;
-	struct wl_client *client = wl_resource_get_client(resource);
-
-	ext_workspace_group_handle_v1_send_capabilities(resource, group->caps);
-
-	struct wlr_ext_workspace_v1_group_output *group_output;
-	wl_list_for_each(group_output, &group->outputs, link) {
-		struct wl_resource *output_resource;
-		wl_resource_for_each(output_resource,
-							 &group_output->output->resources) {
-			if (wl_resource_get_client(output_resource) == client) {
-				ext_workspace_group_handle_v1_send_output_enter(
-					resource, output_resource);
-			}
-		}
-	}
-
-	manager_schedule_done(group->manager);
-}
-
-static void manager_bind(struct wl_client *client, void *data, uint32_t version,
-						 uint32_t id) {
-	struct wlr_ext_workspace_manager_v1 *manager = data;
-
-	struct wlr_ext_workspace_manager_v1_resource *manager_res =
-		calloc(1, sizeof(*manager_res));
-	if (!manager_res) {
-		wl_client_post_no_memory(client);
-		return;
-	}
-
-	manager_res->manager = manager;
-	wl_list_init(&manager_res->requests);
-	wl_list_init(&manager_res->workspace_resources);
-	wl_list_init(&manager_res->group_resources);
-
-	manager_res->resource = wl_resource_create(
-		client, &ext_workspace_manager_v1_interface, version, id);
-	if (!manager_res->resource) {
-		free(manager_res);
-		wl_client_post_no_memory(client);
-		return;
-	}
-	wl_resource_set_implementation(manager_res->resource, &manager_impl,
-								   manager_res, manager_resource_destroy);
-	wl_list_insert(&manager->resources, &manager_res->link);
-
-	struct wlr_ext_workspace_group_handle_v1 *group;
-	wl_list_for_each(group, &manager->groups, link) {
-		struct wlr_ext_workspace_group_v1_resource *group_res =
-			create_group_resource(group, manager_res);
-		if (!group_res) {
-			wl_resource_post_no_memory(manager_res->resource);
-			continue;
-		}
-		ext_workspace_manager_v1_send_workspace_group(manager_res->resource,
-													  group_res->resource);
-		group_send_details(group_res);
-	}
-
-	struct wlr_ext_workspace_handle_v1 *workspace;
-	wl_list_for_each(workspace, &manager->workspaces, link) {
-		struct wlr_ext_workspace_v1_resource *workspace_res =
-			create_workspace_resource(workspace, manager_res);
-		if (!workspace_res) {
-			wl_resource_post_no_memory(manager_res->resource);
-			continue;
-		}
-		ext_workspace_manager_v1_send_workspace(manager_res->resource,
-												workspace_res->resource);
-		workspace_send_details(workspace_res);
-
-		if (!workspace->group) {
-			continue;
-		}
-		struct wlr_ext_workspace_group_v1_resource *group_res;
-		wl_list_for_each(group_res, &workspace->group->resources, link) {
-			if (group_res->manager == manager_res) {
-				ext_workspace_group_handle_v1_send_workspace_enter(
-					group_res->resource, workspace_res->resource);
-			}
-		}
-	}
-
-	ext_workspace_manager_v1_send_done(manager_res->resource);
-}
-
-static void manager_handle_display_destroy(struct wl_listener *listener,
-										   void *data) {
-	struct wlr_ext_workspace_manager_v1 *manager =
-		wl_container_of(listener, manager, display_destroy);
-
-	wl_signal_emit_mutable(&manager->events.destroy, NULL);
-	assert(wl_list_empty(&manager->events.destroy.listener_list));
-
-	struct wlr_ext_workspace_group_handle_v1 *group, *tmp;
-	wl_list_for_each_safe(group, tmp, &manager->groups, link) {
-		wlr_ext_workspace_group_handle_v1_destroy(group);
-	}
-
-	struct wlr_ext_workspace_handle_v1 *workspace, *tmp2;
-	wl_list_for_each_safe(workspace, tmp2, &manager->workspaces, link) {
-		wlr_ext_workspace_handle_v1_destroy(workspace);
-	}
-
-	struct wlr_ext_workspace_manager_v1_resource *manager_res, *tmp3;
-	wl_list_for_each_safe(manager_res, tmp3, &manager->resources, link) {
-		destroy_manager_resource(manager_res);
-	}
-
-	if (manager->idle_source) {
-		wl_event_source_remove(manager->idle_source);
-	}
-
-	wl_list_remove(&manager->display_destroy.link);
-	wl_global_destroy(manager->global);
-	free(manager);
-}
-
-struct wlr_ext_workspace_manager_v1 *
-wlr_ext_workspace_manager_v1_create(struct wl_display *display,
-									uint32_t version) {
-	assert(version <= EXT_WORKSPACE_V1_VERSION);
-
-	struct wlr_ext_workspace_manager_v1 *manager = calloc(1, sizeof(*manager));
-	if (!manager) {
-		return NULL;
-	}
-
-	manager->global =
-		wl_global_create(display, &ext_workspace_manager_v1_interface, version,
-						 manager, manager_bind);
-	if (!manager->global) {
-		free(manager);
-		return NULL;
-	}
-
-	manager->event_loop = wl_display_get_event_loop(display);
-
-	manager->display_destroy.notify = manager_handle_display_destroy;
-	wl_display_add_destroy_listener(display, &manager->display_destroy);
-
-	wl_list_init(&manager->groups);
-	wl_list_init(&manager->workspaces);
-	wl_list_init(&manager->resources);
-	wl_signal_init(&manager->events.destroy);
-
-	return manager;
-}
-
-struct wlr_ext_workspace_group_handle_v1 *
-wlr_ext_workspace_group_handle_v1_create(
-	struct wlr_ext_workspace_manager_v1 *manager, uint32_t caps) {
-	struct wlr_ext_workspace_group_handle_v1 *group = calloc(1, sizeof(*group));
-	if (!group) {
-		return NULL;
-	}
-
-	group->manager = manager;
-	group->caps = caps;
-
-	wl_list_init(&group->outputs);
-	wl_list_init(&group->resources);
-	wl_signal_init(&group->events.create_workspace);
-	wl_signal_init(&group->events.destroy);
-
-	wl_list_insert(manager->groups.prev, &group->link);
-
-	struct wlr_ext_workspace_manager_v1_resource *manager_res;
-	wl_list_for_each(manager_res, &manager->resources, link) {
-		struct wlr_ext_workspace_group_v1_resource *group_res =
-			create_group_resource(group, manager_res);
-		if (!group_res) {
-			continue;
-		}
-		ext_workspace_manager_v1_send_workspace_group(manager_res->resource,
-													  group_res->resource);
-		group_send_details(group_res);
-	}
-
-	manager_schedule_done(manager);
-
-	return group;
-}
-
-static void
-workspace_send_group(struct wlr_ext_workspace_handle_v1 *workspace,
-					 struct wlr_ext_workspace_group_handle_v1 *group,
-					 bool enter) {
-
-	struct wlr_ext_workspace_v1_resource *workspace_res;
-	wl_list_for_each(workspace_res, &workspace->resources, link) {
-		struct wlr_ext_workspace_group_v1_resource *group_res;
-		wl_list_for_each(group_res, &group->resources, link) {
-			if (group_res->manager != workspace_res->manager) {
-				continue;
-			}
-			if (enter) {
-				ext_workspace_group_handle_v1_send_workspace_enter(
-					group_res->resource, workspace_res->resource);
-			} else {
-				ext_workspace_group_handle_v1_send_workspace_leave(
-					group_res->resource, workspace_res->resource);
-			}
-		}
-	}
-
-	manager_schedule_done(workspace->manager);
-}
-
-static void
-destroy_group_output(struct wlr_ext_workspace_v1_group_output *group_output) {
-	wl_list_remove(&group_output->output_bind.link);
-	wl_list_remove(&group_output->output_destroy.link);
-	wl_list_remove(&group_output->link);
-	free(group_output);
-}
-
-static void group_send_output(struct wlr_ext_workspace_group_handle_v1 *group,
-							  struct wlr_output *output, bool enter) {
-
-	struct wlr_ext_workspace_group_v1_resource *group_res;
-	wl_list_for_each(group_res, &group->resources, link) {
-		struct wl_client *client = wl_resource_get_client(group_res->resource);
-
-		struct wl_resource *output_resource;
-		wl_resource_for_each(output_resource, &output->resources) {
-			if (wl_resource_get_client(output_resource) != client) {
-				continue;
-			}
-			if (enter) {
-				ext_workspace_group_handle_v1_send_output_enter(
-					group_res->resource, output_resource);
-			} else {
-				ext_workspace_group_handle_v1_send_output_leave(
-					group_res->resource, output_resource);
-			}
-		}
-	}
-
-	manager_schedule_done(group->manager);
-}
-
-void wlr_ext_workspace_group_handle_v1_destroy(
-	struct wlr_ext_workspace_group_handle_v1 *group) {
-	if (!group) {
-		return;
-	}
-
-	wl_signal_emit_mutable(&group->events.destroy, NULL);
-
-	assert(wl_list_empty(&group->events.create_workspace.listener_list));
-	assert(wl_list_empty(&group->events.destroy.listener_list));
-
-	struct wlr_ext_workspace_handle_v1 *workspace;
-	wl_list_for_each(workspace, &group->manager->workspaces, link) {
-		if (workspace->group == group) {
-			workspace_send_group(workspace, group, false);
-			workspace->group = NULL;
-		}
-	}
-
-	struct wlr_ext_workspace_group_v1_resource *group_res, *tmp;
-	wl_list_for_each_safe(group_res, tmp, &group->resources, link) {
-		ext_workspace_group_handle_v1_send_removed(group_res->resource);
-		destroy_group_resource(group_res);
-	}
-
-	struct wlr_ext_workspace_manager_v1_resource *manager_res;
-	wl_list_for_each(manager_res, &group->manager->resources, link) {
-		struct wlr_ext_workspace_v1_request *req, *tmp2;
-		wl_list_for_each_safe(req, tmp2, &manager_res->requests, link) {
-			if (req->group == group) {
-				destroy_request(req);
-			}
-		}
-	}
-
-	struct wlr_ext_workspace_v1_group_output *group_output, *tmp3;
-	wl_list_for_each_safe(group_output, tmp3, &group->outputs, link) {
-		group_send_output(group, group_output->output, false);
-		destroy_group_output(group_output);
-	}
-
-	manager_schedule_done(group->manager);
-
-	wl_list_remove(&group->link);
-	free(group);
-}
-
-static void handle_output_bind(struct wl_listener *listener, void *data) {
-	struct wlr_ext_workspace_v1_group_output *group_output =
-		wl_container_of(listener, group_output, output_bind);
-	struct wlr_output_event_bind *event = data;
-	struct wl_client *client = wl_resource_get_client(event->resource);
-
-	struct wlr_ext_workspace_group_v1_resource *group_res;
-	wl_list_for_each(group_res, &group_output->group->resources, link) {
-		if (wl_resource_get_client(group_res->resource) == client) {
-			ext_workspace_group_handle_v1_send_output_enter(group_res->resource,
-															event->resource);
-		}
-	}
-
-	manager_schedule_done(group_output->group->manager);
-}
-
-static void handle_output_destroy(struct wl_listener *listener, void *data) {
-	struct wlr_ext_workspace_v1_group_output *group_output =
-		wl_container_of(listener, group_output, output_destroy);
-	group_send_output(group_output->group, group_output->output, false);
-	destroy_group_output(group_output);
-}
-
-static struct wlr_ext_workspace_v1_group_output *
-get_group_output(struct wlr_ext_workspace_group_handle_v1 *group,
-				 struct wlr_output *output) {
-	struct wlr_ext_workspace_v1_group_output *group_output;
-	wl_list_for_each(group_output, &group->outputs, link) {
-		if (group_output->output == output) {
-			return group_output;
-		}
-	}
-	return NULL;
-}
-
-void wlr_ext_workspace_group_handle_v1_output_enter(
-	struct wlr_ext_workspace_group_handle_v1 *group,
-	struct wlr_output *output) {
-	if (get_group_output(group, output)) {
-		return;
-	}
-	struct wlr_ext_workspace_v1_group_output *group_output =
-		calloc(1, sizeof(*group_output));
-	if (!group_output) {
-		return;
-	}
-	group_output->output = output;
-	group_output->group = group;
-	wl_list_insert(&group->outputs, &group_output->link);
-
-	group_output->output_bind.notify = handle_output_bind;
-	wl_signal_add(&output->events.bind, &group_output->output_bind);
-	group_output->output_destroy.notify = handle_output_destroy;
-	wl_signal_add(&output->events.destroy, &group_output->output_destroy);
-
-	group_send_output(group, output, true);
-}
-
-void wlr_ext_workspace_group_handle_v1_output_leave(
-	struct wlr_ext_workspace_group_handle_v1 *group,
-	struct wlr_output *output) {
-	struct wlr_ext_workspace_v1_group_output *group_output =
-		get_group_output(group, output);
-	if (!group_output) {
-		return;
-	}
-
-	group_send_output(group, output, false);
-	destroy_group_output(group_output);
-}
-
-struct wlr_ext_workspace_handle_v1 *
-wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager,
-								   const char *id, uint32_t caps) {
-	struct wlr_ext_workspace_handle_v1 *workspace =
-		calloc(1, sizeof(*workspace));
-	if (!workspace) {
-		return NULL;
-	}
-
-	workspace->manager = manager;
-	workspace->caps = caps;
-
-	if (id) {
-		workspace->id = strdup(id);
-		if (!workspace->id) {
-			free(workspace);
-			return NULL;
-		}
-	}
-
-	wl_list_init(&workspace->resources);
-	wl_array_init(&workspace->coordinates);
-	wl_signal_init(&workspace->events.activate);
-	wl_signal_init(&workspace->events.deactivate);
-	wl_signal_init(&workspace->events.remove);
-	wl_signal_init(&workspace->events.assign);
-	wl_signal_init(&workspace->events.destroy);
-
-	wl_list_insert(&manager->workspaces, &workspace->link);
-
-	struct wlr_ext_workspace_manager_v1_resource *manager_res;
-	wl_list_for_each(manager_res, &manager->resources, link) {
-		struct wlr_ext_workspace_v1_resource *workspace_res =
-			create_workspace_resource(workspace, manager_res);
-		if (!workspace_res) {
-			continue;
-		}
-		ext_workspace_manager_v1_send_workspace(manager_res->resource,
-												workspace_res->resource);
-		workspace_send_details(workspace_res);
-	}
-
-	manager_schedule_done(manager);
-
-	return workspace;
-}
-
-void wlr_ext_workspace_handle_v1_destroy(
-	struct wlr_ext_workspace_handle_v1 *workspace) {
-	if (!workspace) {
-		return;
-	}
-
-	wl_signal_emit_mutable(&workspace->events.destroy, NULL);
-
-	assert(wl_list_empty(&workspace->events.activate.listener_list));
-	assert(wl_list_empty(&workspace->events.deactivate.listener_list));
-	assert(wl_list_empty(&workspace->events.remove.listener_list));
-	assert(wl_list_empty(&workspace->events.assign.listener_list));
-	assert(wl_list_empty(&workspace->events.destroy.listener_list));
-
-	if (workspace->group) {
-		workspace_send_group(workspace, workspace->group, false);
-	}
-
-	struct wlr_ext_workspace_v1_resource *workspace_res, *tmp;
-	wl_list_for_each_safe(workspace_res, tmp, &workspace->resources, link) {
-		ext_workspace_handle_v1_send_removed(workspace_res->resource);
-		destroy_workspace_resource(workspace_res);
-	}
-
-	struct wlr_ext_workspace_manager_v1_resource *manager_res;
-	wl_list_for_each(manager_res, &workspace->manager->resources, link) {
-		struct wlr_ext_workspace_v1_request *req, *tmp2;
-		wl_list_for_each_safe(req, tmp2, &manager_res->requests, link) {
-			if (req->workspace == workspace) {
-				destroy_request(req);
-			}
-		}
-	}
-
-	manager_schedule_done(workspace->manager);
-
-	wl_list_remove(&workspace->link);
-	wl_array_release(&workspace->coordinates);
-	free(workspace->id);
-	free(workspace->name);
-	free(workspace);
-}
-
-void wlr_ext_workspace_handle_v1_set_group(
-	struct wlr_ext_workspace_handle_v1 *workspace,
-	struct wlr_ext_workspace_group_handle_v1 *group) {
-	if (workspace->group == group) {
-		return;
-	}
-
-	if (workspace->group) {
-		workspace_send_group(workspace, workspace->group, false);
-	}
-	workspace->group = group;
-	if (group) {
-		workspace_send_group(workspace, group, true);
-	}
-}
-
-void wlr_ext_workspace_handle_v1_set_name(
-	struct wlr_ext_workspace_handle_v1 *workspace, const char *name) {
-	assert(name);
-
-	if (workspace->name && strcmp(workspace->name, name) == 0) {
-		return;
-	}
-
-	free(workspace->name);
-	workspace->name = strdup(name);
-	if (workspace->name == NULL) {
-		return;
-	}
-
-	struct wlr_ext_workspace_v1_resource *workspace_res;
-	wl_list_for_each(workspace_res, &workspace->resources, link) {
-		ext_workspace_handle_v1_send_name(workspace_res->resource,
-										  workspace->name);
-	}
-
-	manager_schedule_done(workspace->manager);
-}
-
-static bool array_equal(struct wl_array *a, struct wl_array *b) {
-	return (a->size == b->size) &&
-		   (a->size == 0 || memcmp(a->data, b->data, a->size) == 0);
-}
-
-void wlr_ext_workspace_handle_v1_set_coordinates(
-	struct wlr_ext_workspace_handle_v1 *workspace,
-	struct wl_array *coordinates) {
-	assert(coordinates);
-
-	if (array_equal(&workspace->coordinates, coordinates)) {
-		return;
-	}
-
-	wl_array_release(&workspace->coordinates);
-	wl_array_init(&workspace->coordinates);
-	wl_array_copy(&workspace->coordinates, coordinates);
-
-	struct wlr_ext_workspace_v1_resource *workspace_res;
-	wl_list_for_each(workspace_res, &workspace->resources, link) {
-		ext_workspace_handle_v1_send_coordinates(workspace_res->resource,
-												 &workspace->coordinates);
-	}
-
-	manager_schedule_done(workspace->manager);
-}
-
-static void workspace_set_state(struct wlr_ext_workspace_handle_v1 *workspace,
-								enum ext_workspace_handle_v1_state state,
-								bool enabled) {
-	uint32_t old_state = workspace->state;
-	if (enabled) {
-		workspace->state |= state;
-	} else {
-		workspace->state &= ~state;
-	}
-	if (old_state == workspace->state) {
-		return;
-	}
-
-	struct wlr_ext_workspace_v1_resource *workspace_res;
-	wl_list_for_each(workspace_res, &workspace->resources, link) {
-		ext_workspace_handle_v1_send_state(workspace_res->resource,
-										   workspace->state);
-	}
-
-	manager_schedule_done(workspace->manager);
-}
-
-void wlr_ext_workspace_handle_v1_set_active(
-	struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) {
-	workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE,
-						enabled);
-}
-
-void wlr_ext_workspace_handle_v1_set_urgent(
-	struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) {
-	workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_URGENT,
-						enabled);
-}
-
-void wlr_ext_workspace_handle_v1_set_hidden(
-	struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) {
-	workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN,
-						enabled);
-}
diff --git a/src/ext-protocol/wlr_ext_workspace_v1.h b/src/ext-protocol/wlr_ext_workspace_v1.h
deleted file mode 100644
index a2a733b3b..000000000
--- a/src/ext-protocol/wlr_ext_workspace_v1.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// bash on: https://gitlab.freedesktop.org/tokyo4j/wlroots/-/tree/ext-workspace
-// TODO: remove this file
-// refer: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5115
-
-#include <wayland-protocols/ext-workspace-v1-enum.h>
-#include <wayland-server-core.h>
-
-struct wlr_output;
-
-struct wlr_ext_workspace_manager_v1 {
-	struct wl_global *global;
-	struct wl_list groups;	   // wlr_ext_workspace_group_handle_v1.link
-	struct wl_list workspaces; // wlr_ext_workspace_handle_v1.link
-
-	struct {
-		struct wl_signal destroy;
-	} events;
-
-	struct {
-		struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link
-		struct wl_event_source *idle_source;
-		struct wl_event_loop *event_loop;
-		struct wl_listener display_destroy;
-	};
-};
-
-struct wlr_ext_workspace_group_handle_v1_create_workspace_event {
-	const char *name;
-};
-
-struct wlr_ext_workspace_group_handle_v1 {
-	struct wlr_ext_workspace_manager_v1 *manager;
-	uint32_t caps; // ext_workspace_group_handle_v1_group_capabilities
-	struct {
-		struct wl_signal
-			create_workspace; // wlr_ext_workspace_group_handle_v1_create_workspace_event
-		struct wl_signal destroy;
-	} events;
-
-	struct wl_list link; // wlr_ext_workspace_manager_v1.groups
-
-	struct {
-		struct wl_list outputs;	  // wlr_ext_workspace_v1_group_output.link
-		struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link
-	};
-};
-
-struct wlr_ext_workspace_handle_v1 {
-	struct wlr_ext_workspace_manager_v1 *manager;
-	struct wlr_ext_workspace_group_handle_v1 *group; // May be NULL
-	char *id;
-	char *name;
-	struct wl_array coordinates;
-	uint32_t caps;	// ext_workspace_handle_v1_workspace_capabilities
-	uint32_t state; // ext_workspace_handle_v1_state
-
-	struct {
-		struct wl_signal activate;
-		struct wl_signal deactivate;
-		struct wl_signal remove;
-		struct wl_signal assign; // wlr_ext_workspace_group_handle_v1
-		struct wl_signal destroy;
-	} events;
-
-	struct wl_list link; // wlr_ext_workspace_manager_v1.workspaces
-
-	struct {
-		struct wl_list resources; // wlr_ext_workspace_v1_resource.link
-	};
-};
-
-struct wlr_ext_workspace_manager_v1 *
-wlr_ext_workspace_manager_v1_create(struct wl_display *display,
-									uint32_t version);
-
-struct wlr_ext_workspace_group_handle_v1 *
-wlr_ext_workspace_group_handle_v1_create(
-	struct wlr_ext_workspace_manager_v1 *manager, uint32_t caps);
-void wlr_ext_workspace_group_handle_v1_destroy(
-	struct wlr_ext_workspace_group_handle_v1 *group);
-
-void wlr_ext_workspace_group_handle_v1_output_enter(
-	struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output);
-void wlr_ext_workspace_group_handle_v1_output_leave(
-	struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output);
-
-struct wlr_ext_workspace_handle_v1 *
-wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager,
-								   const char *id, uint32_t caps);
-void wlr_ext_workspace_handle_v1_destroy(
-	struct wlr_ext_workspace_handle_v1 *workspace);
-
-void wlr_ext_workspace_handle_v1_set_group(
-	struct wlr_ext_workspace_handle_v1 *workspace,
-	struct wlr_ext_workspace_group_handle_v1 *group);
-void wlr_ext_workspace_handle_v1_set_name(
-	struct wlr_ext_workspace_handle_v1 *workspace, const char *name);
-void wlr_ext_workspace_handle_v1_set_coordinates(
-	struct wlr_ext_workspace_handle_v1 *workspace,
-	struct wl_array *coordinates);
-void wlr_ext_workspace_handle_v1_set_active(
-	struct wlr_ext_workspace_handle_v1 *workspace, bool enabled);
-void wlr_ext_workspace_handle_v1_set_urgent(
-	struct wlr_ext_workspace_handle_v1 *workspace, bool enabled);
-void wlr_ext_workspace_handle_v1_set_hidden(
-	struct wlr_ext_workspace_handle_v1 *workspace, bool enabled);
diff --git a/src/fetch/monitor.h b/src/fetch/monitor.h
index 7a1ca4dc6..041a170b7 100644
--- a/src/fetch/monitor.h
+++ b/src/fetch/monitor.h
@@ -96,3 +96,12 @@ Monitor *xytomon(double x, double y) {
 	struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
 	return o ? o->data : NULL;
 }
+
+Monitor *get_monitor_nearest_to(int32_t lx, int32_t ly) {
+	double closest_x, closest_y;
+	wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x,
+									&closest_y);
+
+	return output_from_wlr_output(
+		wlr_output_layout_output_at(output_layout, closest_x, closest_y));
+}
diff --git a/src/mango.c b/src/mango.c
index c96c2357b..37a856daa 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -40,8 +40,10 @@
 #include <wlr/types/wlr_drm_lease_v1.h>
 #include <wlr/types/wlr_export_dmabuf_v1.h>
 #include <wlr/types/wlr_ext_data_control_v1.h>
+#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
 #include <wlr/types/wlr_ext_image_capture_source_v1.h>
 #include <wlr/types/wlr_ext_image_copy_capture_v1.h>
+#include <wlr/types/wlr_fixes.h>
 #include <wlr/types/wlr_fractional_scale_v1.h>
 #include <wlr/types/wlr_gamma_control_v1.h>
 #include <wlr/types/wlr_idle_inhibit_v1.h>
@@ -316,6 +318,10 @@ struct Client {
 	struct wlr_scene_shadow *shadow;
 	struct wlr_scene_blur *blur;
 	struct wlr_scene_tree *scene_surface;
+	struct wlr_scene_tree *image_capture_tree;
+	struct wlr_scene *image_capture_scene;
+	struct wlr_ext_image_capture_source_v1 *image_capture_source;
+	struct wlr_scene_surface *image_capture_scene_surface;
 	struct wl_list link;
 	struct wl_list flink;
 	struct wl_list fadeout_link;
@@ -407,6 +413,7 @@ struct Client {
 	float unfocused_opacity;
 	char oldmonname[128];
 	int32_t noblur;
+	struct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel;
 	double master_mfact_per, master_inner_per, stack_inner_per;
 	double old_master_mfact_per, old_master_inner_per, old_stack_inner_per;
 	double old_scroller_pproportion;
@@ -683,6 +690,7 @@ static void virtualkeyboard(struct wl_listener *listener, void *data);
 static void virtualpointer(struct wl_listener *listener, void *data);
 static void warp_cursor(const Client *c);
 static Monitor *xytomon(double x, double y);
+static Monitor *get_monitor_nearest_to(int32_t x, int32_t y);
 static void xytonode(double x, double y, struct wlr_surface **psurface,
 					 Client **pc, LayerSurface **pl, double *nx, double *ny);
 static void clear_fullscreen_flag(Client *c);
@@ -845,7 +853,13 @@ 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 grabcx, grabcy; /* client-relative */
+
+static struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1
+	*ext_foreign_toplevel_image_capture_source_manager_v1;
+static struct wl_listener new_foreign_toplevel_capture_request;
+static struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list;
+
 static int32_t drag_begin_cursorx, drag_begin_cursory; /* client-relative */
 static bool start_drag_window = false;
 static int32_t last_apply_drap_time = 0;
@@ -2147,6 +2161,7 @@ void cleanuplisteners(void) {
 	wl_list_remove(&request_start_drag.link);
 	wl_list_remove(&start_drag.link);
 	wl_list_remove(&new_session_lock.link);
+	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);
 	if (drm_lease_manager) {
@@ -3933,6 +3948,18 @@ mapnotify(struct wl_listener *listener, void *data) {
 	c->geom.width += 2 * c->bw;
 	c->geom.height += 2 * c->bw;
 
+	struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
+		.app_id = client_get_appid(c),
+		.title = client_get_title(c),
+	};
+
+	c->image_capture_scene = wlr_scene_create();
+	c->ext_foreign_toplevel = wlr_ext_foreign_toplevel_handle_v1_create(
+		foreign_toplevel_list, &foreign_toplevel_state);
+	c->ext_foreign_toplevel->data = c;
+	c->image_capture_scene_surface = wlr_scene_surface_create(
+		&c->image_capture_scene->tree, client_surface(c));
+
 	/* Handle unmanaged clients first so we can return prior create borders
 	 */
 	if (client_is_unmanaged(c)) {
@@ -4279,6 +4306,26 @@ void outputmgrapply(struct wl_listener *listener, void *data) {
 	outputmgrapplyortest(config, 0);
 }
 
+static void
+handle_new_foreign_toplevel_capture_request(struct wl_listener *listener,
+											void *data) {
+	struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request
+		*request = data;
+	Client *c = request->toplevel_handle->data;
+
+	if (c->image_capture_source == NULL) {
+		c->image_capture_source =
+			wlr_ext_image_capture_source_v1_create_with_scene_node(
+				&c->image_capture_scene->tree.node, event_loop, alloc, drw);
+		if (c->image_capture_source == NULL) {
+			return;
+		}
+	}
+
+	wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(
+		request, c->image_capture_source);
+}
+
 void // 0.7 custom
 outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int32_t test) {
 	/*
@@ -5285,6 +5332,7 @@ void setup(void) {
 	wlr_subcompositor_create(dpy);
 	wlr_alpha_modifier_v1_create(dpy);
 	wlr_ext_data_control_manager_v1_create(dpy, 1);
+	wlr_fixes_create(dpy, 1);
 
 	// 在 setup 函数中
 	wl_signal_init(&mango_print_status);
@@ -5300,6 +5348,15 @@ void setup(void) {
 	power_mgr = wlr_output_power_manager_v1_create(dpy);
 	wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode);
 
+	foreign_toplevel_list = wlr_ext_foreign_toplevel_list_v1_create(dpy, 1);
+	ext_foreign_toplevel_image_capture_source_manager_v1 =
+		wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(dpy, 1);
+	new_foreign_toplevel_capture_request.notify =
+		handle_new_foreign_toplevel_capture_request;
+	wl_signal_add(&ext_foreign_toplevel_image_capture_source_manager_v1->events
+					   .new_request,
+				  &new_foreign_toplevel_capture_request);
+
 	tearing_control = wlr_tearing_control_manager_v1_create(dpy, 1);
 	tearing_new_object.notify = handle_tearing_new_object;
 	wl_signal_add(&tearing_control->events.new_object, &tearing_new_object);
@@ -5678,6 +5735,11 @@ void unmapnotify(struct wl_listener *listener, void *data) {
 		(!c->mon || VISIBLEON(c, c->mon)))
 		init_fadeout_client(c);
 
+	if (c->ext_foreign_toplevel) {
+		wlr_ext_foreign_toplevel_handle_v1_destroy(c->ext_foreign_toplevel);
+		c->ext_foreign_toplevel = NULL;
+	}
+
 	// If the client is in a stack, remove it from the stack
 
 	if (c->swallowedby) {
@@ -5762,6 +5824,8 @@ void unmapnotify(struct wl_listener *listener, void *data) {
 	c->next_in_stack = NULL;
 	c->prev_in_stack = NULL;
 
+	wlr_scene_node_destroy(&c->image_capture_scene_surface->buffer->node);
+	wlr_scene_node_destroy(&c->image_capture_scene->tree.node);
 	wlr_scene_node_destroy(&c->scene->node);
 	printstatus();
 	motionnotify(0, NULL, 0, 0, 0, 0);
@@ -5911,6 +5975,14 @@ void updatetitle(struct wl_listener *listener, void *data) {
 	title = client_get_title(c);
 	if (title && c->foreign_toplevel)
 		wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel, title);
+	if (title && c->ext_foreign_toplevel) {
+		wlr_ext_foreign_toplevel_handle_v1_update_state(
+			c->ext_foreign_toplevel,
+			&(struct wlr_ext_foreign_toplevel_handle_v1_state){
+				.title = title,
+				.app_id = c->ext_foreign_toplevel->app_id,
+			});
+	}
 	if (c == focustop(c->mon))
 		printstatus();
 }
@@ -6244,11 +6316,13 @@ void xwaylandready(struct wl_listener *listener, void *data) {
 	wlr_xwayland_set_seat(xwayland, seat);
 
 	/* Set the default XWayland cursor to match the rest of dwl. */
-	if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1)))
-		wlr_xwayland_set_cursor(
-			xwayland, xcursor->images[0]->buffer, xcursor->images[0]->width * 4,
-			xcursor->images[0]->width, xcursor->images[0]->height,
-			xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y);
+	if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1))) {
+		struct wlr_xcursor_image *image = xcursor->images[0];
+		struct wlr_buffer *buffer = wlr_xcursor_image_get_buffer(image);
+		wlr_xwayland_set_cursor(xwayland, buffer, xcursor->images[0]->hotspot_x,
+								xcursor->images[0]->hotspot_y);
+	}
+
 	/* xwayland can't auto sync the keymap, so we do it manually
 	  and we need to wait the xwayland completely inited
 	*/

From d11bc63802179ae0269619f305fb41afb94127dd Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 19 Jun 2026 13:47:04 +0800
Subject: [PATCH 09/16] feat: add shield_when_capture windowrule

---
 src/animation/client.h    | 23 +++++++++++++
 src/config/parse_config.h |  4 +++
 src/mango.c               | 72 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 99 insertions(+)

diff --git a/src/animation/client.h b/src/animation/client.h
index bb116cb0b..b60c07f55 100644
--- a/src/animation/client.h
+++ b/src/animation/client.h
@@ -355,6 +355,22 @@ void client_draw_shadow(Client *c) {
 	wlr_scene_shadow_set_clipped_region(c->shadow, clipped_region);
 }
 
+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, 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 {
+		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 client_draw_blur(Client *c, struct wlr_box clip_box, struct ivec2 offset) {
 	if (blur && !c->noblur) {
 		wlr_scene_node_set_position(&c->blur->node, offset.x, offset.y);
@@ -544,6 +560,7 @@ void client_apply_clip(Client *c, float factor) {
 
 		apply_border(c);
 		client_draw_shadow(c);
+		apply_shield(c, clip_box);
 		client_draw_blur(c, clip_box, offset);
 
 		if (clip_box.width <= 0 || clip_box.height <= 0) {
@@ -583,6 +600,7 @@ void client_apply_clip(Client *c, float factor) {
 	// 应用窗口装饰
 	apply_border(c);
 	client_draw_shadow(c);
+	apply_shield(c, clip_box);
 	client_draw_blur(c, clip_box, offset);
 
 	// 如果窗口剪切区域已经剪切到0，则不渲染窗口表面
@@ -777,6 +795,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 &&
@@ -1011,6 +1033,7 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
 		wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
 
 		client_draw_shadow(c);
+		apply_shield(c, clip);
 		apply_border(c);
 		client_get_clip(c, &clip);
 		wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
diff --git a/src/config/parse_config.h b/src/config/parse_config.h
index 7e6db028b..74b0cdcf1 100644
--- a/src/config/parse_config.h
+++ b/src/config/parse_config.h
@@ -73,6 +73,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;
@@ -2005,6 +2006,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;
@@ -2111,6 +2113,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 37a856daa..4a0657aa1 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -316,6 +316,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_blur *blur;
 	struct wlr_scene_tree *scene_surface;
 	struct wlr_scene_tree *image_capture_tree;
@@ -386,6 +387,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;
@@ -548,6 +550,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,
@@ -691,6 +698,8 @@ static void virtualpointer(struct wl_listener *listener, void *data);
 static void warp_cursor(const Client *c);
 static Monitor *xytomon(double x, double y);
 static Monitor *get_monitor_nearest_to(int32_t x, int32_t y);
+static void handle_iamge_copy_capture_new_session(struct wl_listener *listener,
+												  void *data);
 static void xytonode(double x, double y, struct wlr_surface **psurface,
 					 Client **pc, LayerSurface **pl, double *nx, double *ny);
 static void clear_fullscreen_flag(Client *c);
@@ -827,6 +836,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;
@@ -896,6 +906,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,
@@ -946,6 +957,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};
@@ -1319,6 +1332,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);
@@ -3997,6 +4011,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_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);
+
 	if (new_is_master && selmon && !is_scroller_layout(selmon))
 		// tile at the top
 		wl_list_insert(&clients, &c->link); // 新窗口是master,头部入栈
@@ -4425,6 +4445,53 @@ 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);
+
+	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);
+}
+
+// 新会话创建时的回调
+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++;
+
+	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);
+}
+
 void powermgrsetmode(struct wl_listener *listener, void *data) {
 	struct wlr_output_power_v1_set_mode_event *event = data;
 	struct wlr_output_state state = {0};
@@ -5338,6 +5405,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 e9b551c87dcfebd161a91a3473c09aa2a98c8c2e Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 19 Jun 2026 14:18:25 +0800
Subject: [PATCH 10/16] fix: fix scenefx next

---
 src/animation/client.h    | 56 +++++++++++++++++++++++++--------------
 src/animation/layer.h     |  4 +--
 src/config/parse_config.h |  1 -
 src/mango.c               | 26 +++++-------------
 4 files changed, 44 insertions(+), 43 deletions(-)

diff --git a/src/animation/client.h b/src/animation/client.h
index 412daaaa1..12ef5f29c 100644
--- a/src/animation/client.h
+++ b/src/animation/client.h
@@ -12,7 +12,8 @@ void set_rect_size(struct wlr_scene_rect *rect, int32_t width, int32_t height) {
 struct fx_corner_radii set_client_corner_location(Client *c) {
 	struct fx_corner_radii current_corner_location =
 		corner_radii_all(config.border_radius);
-	struct wlr_box target_geom = config.animations ? c->animation.current : c->geom;
+	struct wlr_box target_geom =
+		config.animations ? c->animation.current : c->geom;
 	if (target_geom.x + config.border_radius <= c->mon->m.x) {
 		current_corner_location.top_left = 0;	 // 清除左标志位
 		current_corner_location.bottom_left = 0; // 清除左标志位
@@ -240,7 +241,6 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx,
 		return;
 
 	wlr_scene_buffer_set_corner_radii(buffer, buffer_data->corner_location);
-
 }
 
 void scene_buffer_apply_overview_effect(struct wlr_scene_buffer *buffer,
@@ -288,7 +288,7 @@ void buffer_set_effect(Client *c, BufferData data) {
 	if (c == grabc)
 		data.should_scale = false;
 
-	if (c->isfullscreen || (no_radius_when_single && c->mon &&
+	if (c->isfullscreen || (config.no_radius_when_single && c->mon &&
 							c->mon->visible_tiling_clients == 1)) {
 		data.corner_location = corner_radii_none();
 	}
@@ -420,10 +420,21 @@ void apply_shield(Client *c, struct wlr_box clip_box) {
 }
 
 void client_draw_blur(Client *c, struct wlr_box clip_box, struct ivec2 offset) {
-	if (blur && !c->noblur) {
-		wlr_scene_node_set_position(&c->blur->node, offset.x, offset.y);
-		wlr_scene_blur_set_size(c->blur, clip_box.width - c->bw,
-								clip_box.height - c->bw);
+
+	if (c->isfullscreen) {
+		if (c->blur->node.enabled) {
+			wlr_scene_node_set_enabled(&c->blur->node, false);
+		}
+		return;
+	} else {
+		if (config.blur && !c->noblur) {
+			wlr_scene_node_set_enabled(&c->blur->node, true);
+			wlr_scene_node_set_position(&c->blur->node, offset.x, offset.y);
+			wlr_scene_blur_set_size(c->blur, clip_box.width - c->bw,
+									clip_box.height - c->bw);
+		} else {
+			wlr_scene_node_set_enabled(&c->blur->node, false);
+		}
 	}
 }
 
@@ -538,7 +549,8 @@ void apply_border(Client *c) {
 
 	if (c->isfullscreen) {
 		if (c->border->node.enabled) {
-			wlr_scene_node_set_position(&c->scene_surface->node, 0, 0);
+			wlr_scene_node_set_enabled(&c->splitindicator[0]->node, false);
+			wlr_scene_node_set_enabled(&c->splitindicator[1]->node, false);
 			wlr_scene_node_set_enabled(&c->border->node, false);
 		}
 		return;
@@ -552,7 +564,7 @@ void apply_border(Client *c) {
 	apply_split_border(c, hit_no_border);
 
 	struct fx_corner_radii current_corner_location =
-		c->isfullscreen || (no_radius_when_single && c->mon &&
+		c->isfullscreen || (config.no_radius_when_single && c->mon &&
 							c->mon->visible_tiling_clients == 1)
 			? corner_radii_none()
 			: set_client_corner_location(c);
@@ -1320,7 +1332,7 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
 		return;
 
 	struct wlr_box *bbox;
-	struct wlr_box clip;
+	struct wlr_box clip_box;
 
 	if (!c->mon)
 		return;
@@ -1411,12 +1423,16 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
 			c->geom;
 		wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
 
-		client_draw_shadow(c);
-		apply_shield(c, clip);
+		struct ivec2 offset = clip_to_hide(c, &clip_box);
+
 		apply_border(c);
-		client_get_clip(c, &clip);
-		wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
-		if (blur && !c->noblur)
+		client_draw_shadow(c);
+		apply_shield(c, clip_box);
+		client_draw_blur(c, clip_box, offset);
+
+		client_get_clip(c, &clip_box);
+		wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box);
+		if (config.blur && !c->noblur)
 			wlr_scene_blur_set_size(c->blur,
 									c->animation.current.width - 2 * c->bw,
 									c->animation.current.height - 2 * c->bw);
@@ -1549,14 +1565,14 @@ bool client_apply_focus_opacity(Client *c) {
 			   sizeof(c->opacity_animation.current_border_color));
 		c->opacity_animation.current_opacity = target_opacity;
 		client_set_opacity(c, target_opacity);
-		if (blur && !c->noblur && !blur_optimized) {
+		if (config.blur && !c->noblur && !config.blur_optimized) {
 			wlr_scene_blur_set_strength(
-				c->blur, MIN(percent * (1.0 - fadein_begin_opacity) +
-								 fadein_begin_opacity,
+				c->blur, MIN(percent * (1.0 - config.fadein_begin_opacity) +
+								 config.fadein_begin_opacity,
 							 1.0));
 			wlr_scene_blur_set_alpha(
-				c->blur, MIN(percent * (1.0 - fadein_begin_opacity) +
-								 fadein_begin_opacity,
+				c->blur, MIN(percent * (1.0 - config.fadein_begin_opacity) +
+								 config.fadein_begin_opacity,
 							 1.0));
 		}
 		client_set_border_color(c, c->opacity_animation.target_border_color);
diff --git a/src/animation/layer.h b/src/animation/layer.h
index 3d8191b13..80d7e6ca6 100644
--- a/src/animation/layer.h
+++ b/src/animation/layer.h
@@ -328,7 +328,7 @@ void layer_animation_next_tick(LayerSurface *l) {
 							   1.0f);
 
 	if (config.animation_fade_in) {
-		if (blur && !l->noblur && !blur_optimized) {
+		if (config.blur && !l->noblur && !config.blur_optimized) {
 			wlr_scene_blur_set_strength(l->blur, opacity);
 			wlr_scene_blur_set_alpha(l->blur, opacity);
 		}
@@ -362,7 +362,7 @@ void layer_animation_next_tick(LayerSurface *l) {
 		.height = height,
 	};
 
-	if (blur && blur_layer && !l->noblur && l->blur)
+	if (config.blur && config.blur_layer && !l->noblur && l->blur)
 		wlr_scene_blur_set_size(l->blur, l->animation.current.width,
 								l->animation.current.height);
 
diff --git a/src/config/parse_config.h b/src/config/parse_config.h
index d37a4ca4c..cbb629da7 100644
--- a/src/config/parse_config.h
+++ b/src/config/parse_config.h
@@ -3742,7 +3742,6 @@ void set_value_default() {
 	config.blur_layer = 0;
 	config.blur_optimized = 1;
 	config.border_radius = 0;
-	config.border_radius_location_default = CORNER_LOCATION_ALL;
 	config.blur_params.num_passes = 1;
 	config.blur_params.radius = 5;
 	config.blur_params.noise = 0.02f;
diff --git a/src/mango.c b/src/mango.c
index c01fc59d6..6ec780c2d 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -2743,19 +2743,6 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer,
 	}
 }
 
-void layer_flush_blur_background(LayerSurface *l) {
-	if (!blur)
-		return;
-
-	// 如果背景层发生变化,标记优化的模糊背景缓存需要更新
-	if (l->layer_surface->current.layer ==
-		ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
-		if (l->mon) {
-			wlr_scene_optimized_blur_mark_dirty(l->mon->blur);
-		}
-	}
-}
-
 void layer_flush_blur_background(LayerSurface *l) {
 	if (!config.blur)
 		return;
@@ -2813,8 +2800,9 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) {
 	if (layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM &&
 		layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
 		if (layer_surface->current.exclusive_zone == 0) {
-			l->shadow = wlr_scene_shadow_create(l->scene, 0, 0, border_radius,
-												shadows_blur, shadowscolor);
+			l->shadow = wlr_scene_shadow_create(
+				l->scene, 0, 0, config.border_radius, config.shadows_blur,
+				config.shadowscolor);
 			wlr_scene_node_lower_to_bottom(&l->shadow->node);
 			wlr_scene_node_set_enabled(&l->shadow->node, true);
 		}
@@ -4459,14 +4447,11 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx,
 		return;
 
 	if (config.blur && c && !c->noblur) {
-		wlr_scene_node_set_enabled(&c->blur->node, true);
-		if (blur_optimized) {
+		if (config.blur_optimized) {
 			wlr_scene_blur_set_should_only_blur_bottom_layer(c->blur, true);
 		} else {
 			wlr_scene_blur_set_should_only_blur_bottom_layer(c->blur, false);
 		}
-	} else {
-		wlr_scene_node_set_enabled(&c->blur->node, false);
 	}
 }
 
@@ -4656,7 +4641,8 @@ mapnotify(struct wl_listener *listener, void *data) {
 		c->scene, 0, 0, c->isurgent ? config.urgentcolor : config.bordercolor);
 	wlr_scene_node_lower_to_bottom(&c->border->node);
 	wlr_scene_node_set_position(&c->border->node, 0, 0);
-	wlr_scene_rect_set_corner_radii(c->border, corner_radii_all(config.border_radius));
+	wlr_scene_rect_set_corner_radii(c->border,
+									corner_radii_all(config.border_radius));
 	wlr_scene_node_set_enabled(&c->border->node, true);
 
 	c->shadow =

From 59a4df0a2b6fe089de83fd5b8ce262c747c541c4 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 19 Jun 2026 15:26:47 +0800
Subject: [PATCH 11/16] fix: window share shouldn't capture shield client

---
 src/animation/client.h | 12 ++++++++----
 src/mango.c            |  3 +++
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/src/animation/client.h b/src/animation/client.h
index 12ef5f29c..1df141216 100644
--- a/src/animation/client.h
+++ b/src/animation/client.h
@@ -404,6 +404,11 @@ void client_draw_shadow(Client *c) {
 }
 
 void apply_shield(Client *c, struct wlr_box clip_box) {
+
+	if (clip_box.width <= 0 || clip_box.height <= 0) {
+		return;
+	}
+
 	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, clip_box.y);
@@ -933,8 +938,8 @@ void client_apply_clip(Client *c, float factor) {
 
 		apply_border(c);
 		client_draw_shadow(c);
-		apply_shield(c, clip_box);
 		client_draw_blur(c, clip_box, offset);
+		apply_shield(c, clip_box);
 
 		if (clip_box.width <= 0 || clip_box.height <= 0) {
 			return;
@@ -1426,11 +1431,10 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
 		struct ivec2 offset = clip_to_hide(c, &clip_box);
 
 		apply_border(c);
-		client_draw_shadow(c);
+		client_get_clip(c, &clip_box);
 		apply_shield(c, clip_box);
+		client_draw_shadow(c);
 		client_draw_blur(c, clip_box, offset);
-
-		client_get_clip(c, &clip_box);
 		wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box);
 		if (config.blur && !c->noblur)
 			wlr_scene_blur_set_size(c->blur,
diff --git a/src/mango.c b/src/mango.c
index 6ec780c2d..4176e251b 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -5013,6 +5013,9 @@ handle_new_foreign_toplevel_capture_request(struct wl_listener *listener,
 		*request = data;
 	Client *c = request->toplevel_handle->data;
 
+	if (c->shield_when_capture)
+		return;
+
 	if (c->image_capture_source == NULL) {
 		c->image_capture_source =
 			wlr_ext_image_capture_source_v1_create_with_scene_node(

From a2b071b5fea0f8793adf3b0072768f1d56c66451 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 19 Jun 2026 15:46:30 +0800
Subject: [PATCH 12/16] update docs

---
 docs/window-management/rules.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/docs/window-management/rules.md b/docs/window-management/rules.md
index b5ae81b14..5c97961c6 100644
--- a/docs/window-management/rules.md
+++ b/docs/window-management/rules.md
@@ -35,6 +35,8 @@ windowrule=Parameter:Values,Parameter:Values,appid:Values,title:Values
 | `single_scratchpad` | integer | `0` / `1` (default 1) | Only show one out of named scratchpads or the normal scratchpad |
 | `allow_shortcuts_inhibit` | integer | `0` / `1` (default 1) | Allow shortcuts to be inhibited by clients |
 | `idleinhibit_when_focus` | integer | `0` / `1` (default 0) | Automatically keep idle inhibit active when this window is focused |
+| `shield_when_capture` | integer | `0` / `1` | Shield window when captured |
+
 
 ### Geometry & Position
 

From 6d535e2c1920d94db2fb30df7a0e88182dce66c0 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 19 Jun 2026 16:13:34 +0800
Subject: [PATCH 13/16] fix: shield and blur node in overview

---
 src/mango.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/mango.c b/src/mango.c
index 4176e251b..1bd38a068 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -6444,6 +6444,11 @@ void overview_backup_surface(Client *c) {
 		wlr_scene_tree_snapshot(&c->scene_surface->node, c->scene);
 	wlr_scene_node_set_enabled(&c->overview_scene_surface->node, false);
 	wlr_scene_node_set_enabled(&c->scene_surface->node, true);
+
+	wlr_scene_node_reparent(&c->blur->node, c->scene_surface);
+	wlr_scene_node_lower_to_bottom(&c->blur->node);
+	wlr_scene_node_reparent(&c->shield->node, c->scene_surface);
+	wlr_scene_node_raise_to_top(&c->shield->node);
 }
 
 // 普通视图切换到overview时保存窗口的旧状态
@@ -6489,6 +6494,10 @@ void overview_restore(Client *c, const Arg *arg) {
 	c->is_restoring_from_ov = (arg->ui & c->tags & TAGMASK) == 0 ? true : false;
 
 	if (c->overview_scene_surface) {
+		wlr_scene_node_reparent(&c->blur->node, c->overview_scene_surface);
+		wlr_scene_node_lower_to_bottom(&c->blur->node);
+		wlr_scene_node_reparent(&c->shield->node, c->overview_scene_surface);
+		wlr_scene_node_raise_to_top(&c->shield->node);
 		wlr_scene_node_destroy(&c->scene_surface->node);
 		c->scene_surface = c->overview_scene_surface;
 		c->overview_scene_surface = NULL;

From 902806e16782b1b57111f07cde6e0d4c2c31f1e3 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 19 Jun 2026 17:15:05 +0800
Subject: [PATCH 14/16] opt: syncobj_enable default to 1

---
 docs/configuration/miscellaneous.md | 2 +-
 src/config/parse_config.h           | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/configuration/miscellaneous.md b/docs/configuration/miscellaneous.md
index cd8d167da..6fb5dc8f0 100644
--- a/docs/configuration/miscellaneous.md
+++ b/docs/configuration/miscellaneous.md
@@ -8,7 +8,7 @@ description: Advanced settings for XWayland, focus behavior, and system integrat
 | Setting | Default | Description |
 | :--- | :--- | :--- |
 | `xwayland_persistence` | `1` | Keep XWayland running even when no X11 apps are open (reduces startup lag). |
-| `syncobj_enable` | `0` | Enable `drm_syncobj` timeline support (helps with gaming stutter/lag). **Requires restart.** |
+| `syncobj_enable` | `1` | Enable `drm_syncobj` timeline support (helps with gaming stutter/lag). **Requires restart.** |
 | `allow_lock_transparent` | `0` | Allow the lock screen to be transparent. |
 | `allow_shortcuts_inhibit` | `1` | Allow shortcuts to be inhibited by clients. |
 | `vrr` | - | Set via [monitor rule](/docs/configuration/monitors#monitor-rules). |
diff --git a/src/config/parse_config.h b/src/config/parse_config.h
index cbb629da7..61a6da697 100644
--- a/src/config/parse_config.h
+++ b/src/config/parse_config.h
@@ -3687,7 +3687,7 @@ void set_value_default() {
 	config.view_current_to_back = 0;
 	config.single_scratchpad = 1;
 	config.xwayland_persistence = 1;
-	config.syncobj_enable = 0;
+	config.syncobj_enable = 1;
 	config.tag_carousel = 0;
 	config.drag_tile_refresh_interval = 8.0f;
 	config.drag_floating_refresh_interval = 8.0f;

From 3ab6e45220337f12f330bcf807d992b4a7c6acd2 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 19 Jun 2026 18:22:58 +0800
Subject: [PATCH 15/16] opt: make sure animaiton var init

---
 src/mango.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/mango.c b/src/mango.c
index 1bd38a068..1684f9fc4 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -4547,6 +4547,11 @@ void init_client_properties(Client *c) {
 		   sizeof(c->opacity_animation.current_border_color));
 	c->opacity_animation.initial_opacity = c->unfocused_opacity;
 	c->opacity_animation.current_opacity = c->unfocused_opacity;
+	c->animation.tagining = false;
+	c->animation.running = false;
+	c->animation.overining = false;
+	c->animation.tagouting = false;
+	c->animation.tagouted = false;
 }
 
 void // old fix to 0.5

From 1ea7f88844d72b496f22453605e1a8ae898b1f18 Mon Sep 17 00:00:00 2001
From: DreamMaoMao <2523610504@qq.com>
Date: Fri, 19 Jun 2026 19:25:10 +0800
Subject: [PATCH 16/16] fix: error clip box set for grabc client

---
 src/animation/client.h | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/src/animation/client.h b/src/animation/client.h
index 1df141216..fb771da9b 100644
--- a/src/animation/client.h
+++ b/src/animation/client.h
@@ -1337,7 +1337,7 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
 		return;
 
 	struct wlr_box *bbox;
-	struct wlr_box clip_box;
+	struct wlr_box clip;
 
 	if (!c->mon)
 		return;
@@ -1428,14 +1428,11 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
 			c->geom;
 		wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
 
-		struct ivec2 offset = clip_to_hide(c, &clip_box);
-
 		apply_border(c);
-		client_get_clip(c, &clip_box);
-		apply_shield(c, clip_box);
+		client_get_clip(c, &clip);
+		apply_shield(c, clip);
 		client_draw_shadow(c);
-		client_draw_blur(c, clip_box, offset);
-		wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box);
+		wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
 		if (config.blur && !c->noblur)
 			wlr_scene_blur_set_size(c->blur,
 									c->animation.current.width - 2 * c->bw,
