From ec71fa84818d8b5a521295067142e619bda792af Mon Sep 17 00:00:00 2001
From: Reza Jelveh <git@jelveh.me>
Date: Sun, 12 Apr 2026 19:46:30 +0800
Subject: [PATCH 1/2] update to scenefx 0.5 for mwlroots 0.20

---
 meson.build                   |  4 +--
 src/animation/client.h        | 64 ++++++++++++++++++++---------------
 src/animation/common.h        |  8 ++---
 src/animation/layer.h         |  3 +-
 src/config/parse_config.h     |  3 +-
 src/ext-protocol/text-input.h |  4 +--
 src/mango.c                   | 53 +++++++++++++++--------------
 7 files changed, 76 insertions(+), 63 deletions(-)

diff --git a/meson.build b/meson.build
index 64394cd9a..303240330 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')
 
 
 # 获取版本信息
diff --git a/src/animation/client.h b/src/animation/client.h
index e94f872a3..af20b3ef5 100644
--- a/src/animation/client.h
+++ b/src/animation/client.h
@@ -1,3 +1,5 @@
+#include <scenefx/types/fx/clipped_region.h>
+
 void client_actual_size(Client *c, int32_t *width, int32_t *height) {
 	*width = c->animation.current.width - 2 * (int32_t)c->bw;
 
@@ -8,25 +10,38 @@ void set_rect_size(struct wlr_scene_rect *rect, int32_t width, int32_t 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_radii(Client *c) {
+	struct fx_corner_radii corners = corner_radii_all(config.border_radius);
 	struct wlr_box target_geom =
 		config.animations ? c->animation.current : c->geom;
+
+	// If window is touching left edge, disable left corners
 	if (target_geom.x + config.border_radius <= c->mon->m.x) {
-		current_corner_location &= ~CORNER_LOCATION_LEFT;
+		corners.top_left = 0;
+		corners.bottom_left = 0;
 	}
+
+	// If window is touching right edge, disable right corners
 	if (target_geom.x + target_geom.width - config.border_radius >=
 		c->mon->m.x + c->mon->m.width) {
-		current_corner_location &= ~CORNER_LOCATION_RIGHT;
+		corners.top_right = 0;
+		corners.bottom_right = 0;
 	}
+
+	// If window is touching top edge, disable top corners
 	if (target_geom.y + config.border_radius <= c->mon->m.y) {
-		current_corner_location &= ~CORNER_LOCATION_TOP;
+		corners.top_left = 0;
+		corners.top_right = 0;
 	}
+
+	// If window is touching bottom edge, disable bottom corners
 	if (target_geom.y + target_geom.height - config.border_radius >=
 		c->mon->m.y + c->mon->m.height) {
-		current_corner_location &= ~CORNER_LOCATION_BOTTOM;
+		corners.bottom_left = 0;
+		corners.bottom_right = 0;
 	}
-	return current_corner_location;
+
+	return corners;
 }
 
 bool is_horizontal_stack_layout(Monitor *m) {
@@ -226,8 +241,7 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx,
 	if (wlr_xdg_popup_try_from_wlr_surface(surface) != NULL)
 		return;
 
-	wlr_scene_buffer_set_corner_radius(buffer, config.border_radius,
-									   buffer_data->corner_location);
+	wlr_scene_buffer_set_corner_radii(buffer, buffer_data->corner_radii);
 }
 
 void buffer_set_effect(Client *c, BufferData data) {
@@ -246,7 +260,7 @@ void buffer_set_effect(Client *c, BufferData data) {
 	if (c->isnoradius || c->isfullscreen ||
 		(config.no_radius_when_single && c->mon &&
 		 c->mon->visible_tiling_clients == 1)) {
-		data.corner_location = CORNER_LOCATION_NONE;
+		data.corner_radii = corner_radii_none();
 	}
 
 	wlr_scene_node_for_each_buffer(&c->scene_surface->node,
@@ -268,11 +282,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_radii =
 		c->isfullscreen || (config.no_radius_when_single && c->mon &&
 							c->mon->visible_tiling_clients == 1)
-			? CORNER_LOCATION_NONE
-			: CORNER_LOCATION_ALL;
+			? corner_radii_none()
+			: corner_radii_all(config.border_radius);
 
 	int32_t bwoffset = c->bw != 0 && hit_no_border ? (int32_t)c->bw : 0;
 
@@ -302,8 +316,7 @@ void client_draw_shadow(Client *c) {
 
 	struct clipped_region clipped_region = {
 		.area = intersection_box,
-		.corner_radius = config.border_radius,
-		.corners = current_corner_location,
+		.corners = current_corner_radii,
 	};
 
 	struct wlr_box absolute_shadow_box = {
@@ -355,12 +368,12 @@ 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_radii;
 	if (c->isfullscreen || (config.no_radius_when_single && c->mon &&
 							c->mon->visible_tiling_clients == 1)) {
-		current_corner_location = CORNER_LOCATION_NONE;
+		current_corner_radii = corner_radii_none();
 	} else {
-		current_corner_location = set_client_corner_location(c);
+		current_corner_radii = set_client_corner_radii(c);
 	}
 
 	if (hit_no_border && config.smartgaps) {
@@ -434,15 +447,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 = config.border_radius,
-		.corners = current_corner_location,
+		.corners = current_corner_radii,
 	};
 
 	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, config.border_radius,
-									 current_corner_location);
+	wlr_scene_rect_set_corner_radius(c->border, config.border_radius);
 	wlr_scene_rect_set_clipped_region(c->border, clipped_region);
 }
 
@@ -520,8 +531,7 @@ void client_apply_clip(Client *c, float factor) {
 	struct ivec2 offset;
 	BufferData buffer_data;
 
-	enum corner_location current_corner_location =
-		set_client_corner_location(c);
+	struct fx_corner_radii current_corner_radii = set_client_corner_radii(c);
 
 	if (!config.animations) {
 		c->animation.running = false;
@@ -542,8 +552,8 @@ 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});
+										  clip_box.height, current_corner_radii,
+										  true});
 		return;
 	}
 
@@ -600,7 +610,7 @@ void client_apply_clip(Client *c, float factor) {
 	buffer_data.should_scale = true;
 	buffer_data.width = clip_box.width;
 	buffer_data.height = clip_box.height;
-	buffer_data.corner_location = current_corner_location;
+	buffer_data.corner_radii = current_corner_radii;
 
 	if (factor == 1.0) {
 		buffer_data.width_scale = 1.0;
diff --git a/src/animation/common.h b/src/animation/common.h
index 10b66b918..4b8f215f5 100644
--- a/src/animation/common.h
+++ b/src/animation/common.h
@@ -175,15 +175,15 @@ static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx,
 
 		// 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);
 
+		// Blur functions removed in scenefx 0.5
 		// 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);
+		// wlr_scene_buffer_set_backdrop_blur(snapshot_buffer, false);
 
 		snapshot_buffer->node.data = scene_buffer->node.data;
 
diff --git a/src/animation/layer.h b/src/animation/layer.h
index 90f1b4ee1..27696c5d4 100644
--- a/src/animation/layer.h
+++ b/src/animation/layer.h
@@ -187,8 +187,7 @@ void layer_draw_shadow(LayerSurface *l) {
 
 	struct clipped_region clipped_region = {
 		.area = intersection_box,
-		.corner_radius = config.border_radius,
-		.corners = config.border_radius_location_default,
+		.corners = corner_radii_all(config.border_radius),
 	};
 
 	wlr_scene_node_set_position(&l->shadow->node, shadow_box.x, shadow_box.y);
diff --git a/src/config/parse_config.h b/src/config/parse_config.h
index e02b50179..45b662702 100644
--- a/src/config/parse_config.h
+++ b/src/config/parse_config.h
@@ -3363,7 +3363,8 @@ 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.border_radius_location_default =
+		0; // CORNER_LOCATION_ALL no longer exists
 	config.blur_params.num_passes = 1;
 	config.blur_params.radius = 5;
 	config.blur_params.noise = 0.02f;
diff --git a/src/ext-protocol/text-input.h b/src/ext-protocol/text-input.h
index e9f221a47..93b8d1b3c 100644
--- a/src/ext-protocol/text-input.h
+++ b/src/ext-protocol/text-input.h
@@ -562,11 +562,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/mango.c b/src/mango.c
index 8fdff709a..f050eedd8 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -11,7 +11,7 @@
 #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>
@@ -301,7 +301,7 @@ typedef struct {
 	float height_scale;
 	int32_t width;
 	int32_t height;
-	enum corner_location corner_location;
+	struct fx_corner_radii corner_radii;
 	bool should_scale;
 } BufferData;
 
@@ -763,7 +763,7 @@ static double find_animation_curve_at(double t, int32_t 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_radii(Client *c);
 static double all_output_frame_duration_ms();
 static struct wlr_scene_tree *
 wlr_scene_tree_snapshot(struct wlr_scene_node *node,
@@ -2399,13 +2399,14 @@ static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer,
 		return;
 	}
 
-	wlr_scene_buffer_set_backdrop_blur(buffer, true);
-	wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, true);
-	if (config.blur_optimized) {
-		wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true);
-	} else {
-		wlr_scene_buffer_set_backdrop_blur_optimized(buffer, false);
-	}
+	// Blur functions removed in scenefx 0.5
+	// wlr_scene_buffer_set_backdrop_blur(buffer, true);
+	// wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, true);
+	// if (config.blur_optimized) {
+	// 	wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true);
+	// } else {
+	// 	wlr_scene_buffer_set_backdrop_blur_optimized(buffer, false);
+	// }
 }
 
 void layer_flush_blur_background(LayerSurface *l) {
@@ -4019,15 +4020,16 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx,
 		return;
 
 	if (config.blur && c && !c->noblur) {
-		wlr_scene_buffer_set_backdrop_blur(buffer, true);
-		wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, false);
-		if (config.blur_optimized) {
-			wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true);
-		} else {
-			wlr_scene_buffer_set_backdrop_blur_optimized(buffer, false);
-		}
+		// Blur functions removed in scenefx 0.5
+		// wlr_scene_buffer_set_backdrop_blur(buffer, true);
+		// wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, false);
+		// if (config.blur_optimized) {
+		// 	wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true);
+		// } else {
+		// 	wlr_scene_buffer_set_backdrop_blur_optimized(buffer, false);
+		// }
 	} else {
-		wlr_scene_buffer_set_backdrop_blur(buffer, false);
+		// wlr_scene_buffer_set_backdrop_blur(buffer, false);
 	}
 }
 
@@ -4177,8 +4179,7 @@ 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_radius(c->border, config.border_radius,
-									 config.border_radius_location_default);
+	wlr_scene_rect_set_corner_radius(c->border, config.border_radius);
 	wlr_scene_node_set_enabled(&c->border->node, true);
 
 	c->shadow =
@@ -6627,11 +6628,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);
+	/* XWayland cursor API changed in wlroots 0.20.0 - needs wlr_buffer instead
+	 * of raw pixels */
+	/* 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); */
 	/* xwayland can't auto sync the keymap, so we do it manually
 	  and we need to wait the xwayland completely inited
 	*/

From 0a8f92959d9a4349a82bf4fc96b5db20ba4e4bf5 Mon Sep 17 00:00:00 2001
From: Reza Jelveh <git@jelveh.me>
Date: Sat, 2 May 2026 22:19:31 +0800
Subject: [PATCH 2/2] feat: add wlr_scene_blur support and fix wlroots 0.20 API
 compatibility

- Implement blur effect using `wlr_scene_blur` for both clients and layer surfaces
- Add blur node creation and sizing in client/layer mapping and resize handlers
- Fix XWayland cursor initialization for wlroots 0.20 (use `wlr_xcursor_image_get_buffer`)
- Replace `wlr_scene_buffer` backdrop blur calls with `wlr_scene_blur_set_transparency_mask_source`
---
 src/animation/client.h |  6 ++++
 src/animation/common.h | 11 ++-----
 src/mango.c            | 69 ++++++++++++++++++++++++++++--------------
 3 files changed, 55 insertions(+), 31 deletions(-)

diff --git a/src/animation/client.h b/src/animation/client.h
index af20b3ef5..0e056e2b0 100644
--- a/src/animation/client.h
+++ b/src/animation/client.h
@@ -995,6 +995,12 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
 		c->fake_no_border = true;
 	}
 
+	if (config.blur && c->blur && !c->noblur) {
+		wlr_scene_blur_set_size(c->blur,
+			c->geom.width - 2 * c->bw,
+			c->geom.height - 2 * c->bw);
+	}
+
 	// c->geom 是真实的窗口大小和位置，跟过度的动画无关，用于计算布局
 	c->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw,
 										  c->geom.height - 2 * c->bw);
diff --git a/src/animation/common.h b/src/animation/common.h
index 4b8f215f5..3e8a520dc 100644
--- a/src/animation/common.h
+++ b/src/animation/common.h
@@ -178,13 +178,6 @@ static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx,
 		wlr_scene_buffer_set_corner_radii(snapshot_buffer,
 										  scene_buffer->corners);
 
-		// Blur functions removed in scenefx 0.5
-		// 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 =
@@ -221,6 +214,8 @@ static bool scene_node_snapshot(struct wlr_scene_node *node, int32_t lx,
 	}
 	case WLR_SCENE_NODE_OPTIMIZED_BLUR:
 		return true;
+	case WLR_SCENE_NODE_BLUR:
+		return true;
 	}
 
 	if (snapshot_node != NULL) {
@@ -259,4 +254,4 @@ void request_fresh_all_monitors(void) {
 		}
 		wlr_output_schedule_frame(m->wlr_output);
 	}
-}
\ No newline at end of file
+}
diff --git a/src/mango.c b/src/mango.c
index f050eedd8..3544048ed 100644
--- a/src/mango.c
+++ b/src/mango.c
@@ -92,6 +92,7 @@
 #include <wlr/xwayland.h>
 #include <xcb/xcb_icccm.h>
 #endif
+#include "common/xdg-shell-protocol.h"
 #include "common/util.h"
 
 /* macros */
@@ -315,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_blur *blur;
 	struct wlr_scene_tree *scene_surface;
 	struct wl_list link;
 	struct wl_list flink;
@@ -465,6 +467,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;
@@ -2393,20 +2396,21 @@ void closemon(Monitor *m) {
 
 static void iter_layer_scene_buffers(struct wlr_scene_buffer *buffer,
 									 int32_t sx, int32_t sy, void *user_data) {
+	LayerSurface *l = user_data;
+
 	struct wlr_scene_surface *scene_surface =
 		wlr_scene_surface_try_from_buffer(buffer);
 	if (!scene_surface) {
 		return;
 	}
 
-	// Blur functions removed in scenefx 0.5
-	// wlr_scene_buffer_set_backdrop_blur(buffer, true);
-	// wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, true);
-	// if (config.blur_optimized) {
-	// 	wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true);
-	// } else {
-	// 	wlr_scene_buffer_set_backdrop_blur_optimized(buffer, false);
-	// }
+	struct wlr_surface *surface = scene_surface->surface;
+	if (wlr_subsurface_try_from_wlr_surface(surface) != NULL)
+		return;
+
+	if (config.blur && l && !l->noblur) {
+		wlr_scene_blur_set_transparency_mask_source(l->blur, buffer);
+	}
 }
 
 void layer_flush_blur_background(LayerSurface *l) {
@@ -2473,6 +2477,15 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) {
 		wlr_scene_node_set_enabled(&l->shadow->node, true);
 	}
 
+	if (config.blur && config.blur_layer &&
+		layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM &&
+		layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
+		l->blur = wlr_scene_blur_create(l->scene, 0, 0);
+		wlr_scene_blur_set_should_only_blur_bottom_layer(l->blur,
+			config.blur_optimized);
+		wlr_scene_node_lower_to_bottom(&l->blur->node);
+	}
+
 	// 初始化动画
 	if (config.animations && config.layer_animations && !l->noanim) {
 		l->animation.duration = config.animation_duration_open;
@@ -2535,6 +2548,8 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) {
 		l->animation.duration = config.animation_duration_move;
 		l->need_output_flush = true;
 		layer_set_pending_state(l);
+	} else {
+		l->geom = box;
 	}
 
 	if (config.blur && config.blur_layer) {
@@ -2544,6 +2559,9 @@ void commitlayersurfacenotify(struct wl_listener *listener, void *data) {
 			layer_surface->current.layer !=
 				ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
 
+			if (l->blur) {
+				wlr_scene_blur_set_size(l->blur, l->geom.width, l->geom.height);
+			}
 			wlr_scene_node_for_each_buffer(&l->scene->node,
 										   iter_layer_scene_buffers, l);
 		}
@@ -4020,16 +4038,7 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int32_t sx,
 		return;
 
 	if (config.blur && c && !c->noblur) {
-		// Blur functions removed in scenefx 0.5
-		// wlr_scene_buffer_set_backdrop_blur(buffer, true);
-		// wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, false);
-		// if (config.blur_optimized) {
-		// 	wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true);
-		// } else {
-		// 	wlr_scene_buffer_set_backdrop_blur_optimized(buffer, false);
-		// }
-	} else {
-		// wlr_scene_buffer_set_backdrop_blur(buffer, false);
+		wlr_scene_blur_set_transparency_mask_source(c->blur, buffer);
 	}
 }
 
@@ -4133,6 +4142,13 @@ mapnotify(struct wl_listener *listener, void *data) {
 			: wlr_scene_subsurface_tree_create(c->scene, client_surface(c));
 	c->scene->node.data = c->scene_surface->node.data = c;
 
+	if (config.blur) {
+		c->blur = wlr_scene_blur_create(c->scene, 0, 0);
+		wlr_scene_blur_set_should_only_blur_bottom_layer(c->blur,
+			config.blur_optimized);
+		wlr_scene_node_lower_to_bottom(&c->blur->node);
+	}
+
 	client_get_geometry(c, &c->geom);
 
 	if (client_is_x11(c))
@@ -4222,6 +4238,9 @@ mapnotify(struct wl_listener *listener, void *data) {
 	}
 
 	// apply buffer effects of client
+	if (config.blur && c && !c->noblur && c->blur) {
+		wlr_scene_blur_set_size(c->blur, c->geom.width, c->geom.height);
+	}
 	wlr_scene_node_for_each_buffer(&c->scene_surface->node,
 								   iter_xdg_scene_buffers, c);
 
@@ -5599,6 +5618,7 @@ void setup(void) {
 	wlr_ext_image_copy_capture_manager_v1_create(dpy, 1);
 	wlr_ext_output_image_capture_source_manager_v1_create(dpy, 1);
 	wlr_data_control_manager_v1_create(dpy);
+	wlr_ext_data_control_manager_v1_create(dpy, 1);
 	wlr_data_device_manager_create(dpy);
 	wlr_primary_selection_v1_device_manager_create(dpy);
 	wlr_viewporter_create(dpy);
@@ -6026,6 +6046,10 @@ void unmaplayersurfacenotify(struct wl_listener *listener, void *data) {
 	layer_flush_blur_background(l);
 	wlr_scene_node_destroy(&l->shadow->node);
 	l->shadow = NULL;
+	if (l->blur) {
+		wlr_scene_node_destroy(&l->blur->node);
+		l->blur = NULL;
+	}
 	l->being_unmapped = false;
 }
 
@@ -6630,11 +6654,10 @@ void xwaylandready(struct wl_listener *listener, void *data) {
 	/* Set the default XWayland cursor to match the rest of dwl. */
 	/* XWayland cursor API changed in wlroots 0.20.0 - needs wlr_buffer instead
 	 * of raw pixels */
-	/* 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)))
+		wlr_xwayland_set_cursor(
+			xwayland, wlr_xcursor_image_get_buffer(xcursor->images[0]),
+			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
 	*/
