commit 736088f

shrub  ·  2026-02-10 21:34:58 +0000 UTC
parent 0bede0e
fix subsurfaces, so they can actually work
fix subsurface buffer rendering and do damage calculations properly,
calculate pointer position and translate it properly, and do better
subusrfcae comittis and restacking.
8 files changed,  +383, -80
+97, -51
  1@@ -41,6 +41,7 @@
  2 #include "seat.h"
  3 #include "shm.h"
  4 #include "surface.h"
  5+#include "subsurface.h"
  6 #include "util.h"
  7 #include "view.h"
  8 #include "window.h"
  9@@ -217,38 +218,50 @@ error0:
 10 static void
 11 repaint_view(struct target *target, struct compositor_view *view, pixman_region32_t *damage)
 12 {
 13-	pixman_region32_t view_region, view_damage, border_damage;
 14+	pixman_region32_t geom_region, buffer_region, border_region, view_damage, buffer_damage, border_damage;
 15 	const struct swc_rectangle *geom = &view->base.geometry, *target_geom = &target->view->geometry;
 16-	bool has_parent = view->parent && view->parent != view;
 17-	pixman_region32_t parent_region;
 18+	int32_t buf_x, buf_y;
 19+	uint32_t buf_w, buf_h;
 20+	int64_t total_border;
 21 
 22 	if (!view->base.buffer)
 23 		return;
 24 
 25-	pixman_region32_init_rect(&view_region, geom->x, geom->y, geom->width, geom->height);
 26-	if (has_parent) {
 27-		const struct swc_rectangle *parent_geom = &view->parent->base.geometry;
 28-		pixman_region32_init_rect(&parent_region, parent_geom->x, parent_geom->y,
 29-					  parent_geom->width, parent_geom->height);
 30+	buf_w = view->base.buffer->width;
 31+	buf_h = view->base.buffer->height;
 32+	buf_x = geom->x - view->buffer_offset_x;
 33+	buf_y = geom->y - view->buffer_offset_y;
 34+
 35+	total_border = (int64_t)view->border.outwidth + (int64_t)view->border.inwidth;
 36+	pixman_region32_init_rect(&geom_region, geom->x, geom->y, geom->width, geom->height);
 37+	if (view->window) {
 38+		pixman_region32_init_rect(&buffer_region, geom->x, geom->y, geom->width, geom->height);
 39+	} else {
 40+		pixman_region32_init_rect(&buffer_region, buf_x, buf_y, buf_w, buf_h);
 41 	}
 42+	pixman_region32_init_rect(&border_region,
 43+		geom->x - (int32_t)total_border,
 44+		geom->y - (int32_t)total_border,
 45+		geom->width + (uint32_t)(2 * total_border),
 46+		geom->height + (uint32_t)(2 * total_border));
 47+	pixman_region32_subtract(&border_region, &border_region, &geom_region);
 48 	pixman_region32_init_with_extents(&view_damage, &view->extents);
 49+	pixman_region32_init(&buffer_damage);
 50 	pixman_region32_init(&border_damage);
 51 
 52 	pixman_region32_intersect(&view_damage, &view_damage, damage);
 53 	pixman_region32_subtract(&view_damage, &view_damage, &view->clip);
 54-	pixman_region32_subtract(&border_damage, &view_damage, &view_region);
 55-	pixman_region32_intersect(&view_damage, &view_damage, &view_region);
 56-	if (has_parent) {
 57-		pixman_region32_intersect(&view_damage, &view_damage, &parent_region);
 58-		pixman_region32_intersect(&border_damage, &border_damage, &parent_region);
 59-	}
 60+	pixman_region32_intersect(&border_damage, &view_damage, &border_region);
 61+	pixman_region32_intersect(&buffer_damage, &view_damage, &buffer_region);
 62 
 63-	if (pixman_region32_not_empty(&view_damage)) {
 64-		pixman_region32_translate(&view_damage, -geom->x + view->buffer_offset_x, -geom->y + view->buffer_offset_y);
 65-		wld_copy_region(swc.drm->renderer, view->buffer, geom->x - target_geom->x, geom->y - target_geom->y, &view_damage);
 66+	if (pixman_region32_not_empty(&buffer_damage)) {
 67+		pixman_region32_translate(&buffer_damage, -geom->x + view->buffer_offset_x, -geom->y + view->buffer_offset_y);
 68+		wld_copy_region(swc.drm->renderer, view->buffer,
 69+			buf_x - target_geom->x, buf_y - target_geom->y, &buffer_damage);
 70 	}
 71 
 72 	pixman_region32_fini(&view_damage);
 73+	pixman_region32_fini(&buffer_damage);
 74 
 75 	pixman_region32_t in_rect;
 76 	pixman_region32_init_rect(&in_rect, 
 77@@ -263,10 +276,12 @@ repaint_view(struct target *target, struct compositor_view *view, pixman_region3
 78 
 79 	pixman_region32_t in_border;
 80 	pixman_region32_init(&in_border);
 81-	pixman_region32_subtract(&in_border, &in_rect, &view_region);
 82+	pixman_region32_subtract(&in_border, &in_rect, &geom_region);
 83 	pixman_region32_intersect(&in_border, &in_border, &border_damage);
 84 		
 85-	pixman_region32_fini(&view_region);
 86+	pixman_region32_fini(&geom_region);
 87+	pixman_region32_fini(&buffer_region);
 88+	pixman_region32_fini(&border_region);
 89 
 90 	/* Draw border */
 91 	if (view->border.outwidth > 0 && pixman_region32_not_empty(&out_border)) {
 92@@ -283,8 +298,6 @@ repaint_view(struct target *target, struct compositor_view *view, pixman_region3
 93 	pixman_region32_fini(&in_rect);
 94 	pixman_region32_fini(&out_border);
 95 	pixman_region32_fini(&in_border);
 96-	if (has_parent)
 97-		pixman_region32_fini(&parent_region);
 98 
 99 }
100 
101@@ -409,8 +422,7 @@ renderer_flush_view(struct compositor_view *view)
102 /* Surface Views {{{ */
103 
104 /**
105- * Adds the region below a view to the compositor's damaged region,
106- * taking into account its clip region.
107+ * Adds the region below a view to the compositor's damaged region.
108  */
109 static void
110 damage_below_view(struct compositor_view *view)
111@@ -418,7 +430,6 @@ damage_below_view(struct compositor_view *view)
112 	pixman_region32_t damage_below;
113 
114 	pixman_region32_init_with_extents(&damage_below, &view->extents);
115-	pixman_region32_subtract(&damage_below, &damage_below, &view->clip);
116 	pixman_region32_union(&compositor.damage, &compositor.damage, &damage_below);
117 	pixman_region32_fini(&damage_below);
118 }
119@@ -438,15 +449,25 @@ static void
120 update_extents(struct compositor_view *view)
121 {
122 	int64_t total_border = (int64_t)view->border.outwidth + (int64_t)view->border.inwidth;
123-	int64_t x = view->base.geometry.x;
124-	int64_t y = view->base.geometry.y;
125-	int64_t w = view->base.geometry.width;
126-	int64_t h = view->base.geometry.height;
127-
128-	view->extents.x1 = clamp_i32(x - total_border);
129-	view->extents.y1 = clamp_i32(y - total_border);
130-	view->extents.x2 = clamp_i32(x + w + total_border);
131-	view->extents.y2 = clamp_i32(y + h + total_border);
132+	int64_t geom_x = view->base.geometry.x;
133+	int64_t geom_y = view->base.geometry.y;
134+	int64_t geom_w = view->base.geometry.width;
135+	int64_t geom_h = view->base.geometry.height;
136+
137+	int64_t border_x1 = geom_x - total_border;
138+	int64_t border_y1 = geom_y - total_border;
139+	int64_t border_x2 = geom_x + geom_w + total_border;
140+	int64_t border_y2 = geom_y + geom_h + total_border;
141+
142+	int64_t buffer_x1 = geom_x - view->buffer_offset_x;
143+	int64_t buffer_y1 = geom_y - view->buffer_offset_y;
144+	int64_t buffer_x2 = buffer_x1 + (view->base.buffer ? view->base.buffer->width : (uint32_t)geom_w);
145+	int64_t buffer_y2 = buffer_y1 + (view->base.buffer ? view->base.buffer->height : (uint32_t)geom_h);
146+
147+	view->extents.x1 = clamp_i32(MIN(border_x1, buffer_x1));
148+	view->extents.y1 = clamp_i32(MIN(border_y1, buffer_y1));
149+	view->extents.x2 = clamp_i32(MAX(border_x2, buffer_x2));
150+	view->extents.y2 = clamp_i32(MAX(border_y2, buffer_y2));
151 
152 	if (view->extents.x2 < view->extents.x1)
153 		view->extents.x2 = view->extents.x1;
154@@ -775,15 +796,13 @@ attach(struct view *base, struct wld_buffer *buffer)
155 		update_extents(view);
156 
157 		if (view->visible) {
158-			/* Damage the region that was newly uncovered
159-			 * or covered, minus the clip region. */
160+			/* Damage the region that was newly uncovered or covered. */
161 			pixman_region32_init_with_extents(&old, &old_extents);
162 			pixman_region32_init_with_extents(&new, &view->extents);
163 			pixman_region32_init(&both);
164 			pixman_region32_intersect(&both, &old, &new);
165 			pixman_region32_union(&new, &old, &new);
166 			pixman_region32_subtract(&new, &new, &both);
167-			pixman_region32_subtract(&new, &new, &view->clip);
168 			pixman_region32_union(&compositor.damage, &compositor.damage, &new);
169 			pixman_region32_fini(&old);
170 			pixman_region32_fini(&new);
171@@ -835,14 +854,26 @@ view_at(int32_t x, int32_t y)
172 {
173 	struct compositor_view *view;
174 	struct swc_rectangle *geom;
175+	struct swc_rectangle buffer_geom;
176 
177 	wl_list_for_each (view, &compositor.views, link) {
178 		if (!view->visible)
179 			continue;
180 
181 		geom = &view->base.geometry;
182-		if (!rectangle_contains_point(geom, x, y))
183+		if (view->window) {
184+			if (!rectangle_contains_point(geom, x, y))
185+				continue;
186+		} else if (view->base.buffer) {
187+			buffer_geom.x = geom->x - view->buffer_offset_x;
188+			buffer_geom.y = geom->y - view->buffer_offset_y;
189+			buffer_geom.width = view->base.buffer->width;
190+			buffer_geom.height = view->base.buffer->height;
191+			if (!rectangle_contains_point(&buffer_geom, x, y))
192+				continue;
193+		} else if (!rectangle_contains_point(geom, x, y)) {
194 			continue;
195+		}
196 
197 		if (pixman_region32_contains_point(&view->surface->state.input,
198 		                                   x - geom->x + view->buffer_offset_x,
199@@ -1070,14 +1101,42 @@ compositor_view_set_parent(struct compositor_view *view, struct compositor_view
200 		compositor_view_hide(view);
201 }
202 
203+void
204+compositor_view_restack(struct compositor_view *view, struct compositor_view *sibling, bool above)
205+{
206+	if (!view || !sibling || view == sibling)
207+		return;
208+
209+	if (above) {
210+		if (view->link.next == &sibling->link)
211+			return;
212+		wl_list_remove(&view->link);
213+		wl_list_insert(sibling->link.prev, &view->link);
214+	} else {
215+		if (view->link.prev == &sibling->link)
216+			return;
217+		wl_list_remove(&view->link);
218+		wl_list_insert(&sibling->link, &view->link);
219+	}
220+
221+	damage_views(view, sibling);
222+}
223+
224 void
225 compositor_view_show(struct compositor_view *view)
226 {
227 	struct compositor_view *other;
228+	struct subsurface *subsurface;
229 
230 	if (view->visible)
231 		return;
232 
233+	subsurface = view->surface ? view->surface->subsurface : NULL;
234+	if (subsurface) {
235+		if (!subsurface->added || !view->surface->state.buffer)
236+			return;
237+	}
238+
239 	view->visible = true;
240 	view_update_screens(&view->base);
241 
242@@ -1168,16 +1227,9 @@ calculate_damage(void)
243 			continue;
244 
245 		geom = &view->base.geometry;
246-		bool has_parent = view->parent && view->parent != view;
247 		pixman_region32_t view_region;
248-		pixman_region32_t parent_region;
249 
250 		pixman_region32_init_rect(&view_region, geom->x, geom->y, geom->width, geom->height);
251-		if (has_parent) {
252-			const struct swc_rectangle *parent_geom = &view->parent->base.geometry;
253-			pixman_region32_init_rect(&parent_region, parent_geom->x, parent_geom->y,
254-			                          parent_geom->width, parent_geom->height);
255-		}
256 
257 		/* Clip the surface by the opaque region covering it. */
258 		pixman_region32_copy(&view->clip, &compositor.opaque);
259@@ -1188,8 +1240,6 @@ calculate_damage(void)
260 		                          geom->x - view->buffer_offset_x,
261 		                          geom->y - view->buffer_offset_y);
262 		pixman_region32_intersect(&surface_opaque, &surface_opaque, &view_region);
263-		if (has_parent)
264-			pixman_region32_intersect(&surface_opaque, &surface_opaque, &parent_region);
265 
266 		/* Add the surface's opaque region to the accumulated opaque region. */
267 		pixman_region32_union(&compositor.opaque, &compositor.opaque, &surface_opaque);
268@@ -1203,8 +1253,6 @@ calculate_damage(void)
269 			pixman_region32_translate(surface_damage,
270 			                          geom->x - view->buffer_offset_x,
271 			                          geom->y - view->buffer_offset_y);
272-			if (has_parent)
273-				pixman_region32_intersect(surface_damage, surface_damage, &parent_region);
274 
275 			/* Add the surface damage to the compositor damage. */
276 			pixman_region32_union(&compositor.damage, &compositor.damage, surface_damage);
277@@ -1227,9 +1275,7 @@ calculate_damage(void)
278 				view->border.damaged_border2 = false;
279 			}
280 
281-		pixman_region32_fini(&view_region);
282-		if (has_parent)
283-			pixman_region32_fini(&parent_region);
284+			pixman_region32_fini(&view_region);
285 	}
286 
287 	pixman_region32_fini(&surface_opaque);
+1, -0
1@@ -95,6 +95,7 @@ void compositor_view_destroy(struct compositor_view *view);
2 struct compositor_view *compositor_view(struct view *view);
3 
4 void compositor_view_set_parent(struct compositor_view *view, struct compositor_view *parent);
5+void compositor_view_restack(struct compositor_view *view, struct compositor_view *sibling, bool above);
6 
7 void compositor_view_show(struct compositor_view *view);
8 void compositor_view_hide(struct compositor_view *view);
+11, -4
 1@@ -112,14 +112,18 @@ enter(struct input_focus_handler *handler, struct wl_list *resources, struct com
 2 	struct wl_resource *resource;
 3 	uint32_t serial;
 4 	wl_fixed_t surface_x, surface_y;
 5+	int32_t origin_x, origin_y;
 6 
 7 	if (wl_list_empty(resources)) {
 8 		pointer_set_cursor(pointer, cursor_left_ptr);
 9 		return;
10 	}
11 	serial = wl_display_next_serial(swc.display);
12-	surface_x = pointer->x - wl_fixed_from_int(view->base.geometry.x);
13-	surface_y = pointer->y - wl_fixed_from_int(view->base.geometry.y);
14+	/* do based on buffer origin, holy fuck */
15+	origin_x = view->base.geometry.x - view->buffer_offset_x;
16+	origin_y = view->base.geometry.y - view->buffer_offset_y;
17+	surface_x = pointer->x - wl_fixed_from_int(origin_x);
18+	surface_y = pointer->y - wl_fixed_from_int(origin_y);
19 	wl_resource_for_each (resource, resources)
20 		wl_pointer_send_enter(resource, serial, view->surface->resource, surface_x, surface_y);
21 }
22@@ -349,12 +353,15 @@ client_handle_motion(struct pointer_handler *handler, uint32_t time, wl_fixed_t
23 	struct pointer *pointer = wl_container_of(handler, pointer, client_handler);
24 	struct wl_resource *resource;
25 	wl_fixed_t sx, sy;
26+	int32_t origin_x, origin_y;
27 
28 	if (wl_list_empty(&pointer->focus.active))
29 		return false;
30 
31-	sx = x - wl_fixed_from_int(pointer->focus.view->base.geometry.x);
32-	sy = y - wl_fixed_from_int(pointer->focus.view->base.geometry.y);
33+	origin_x = pointer->focus.view->base.geometry.x - pointer->focus.view->buffer_offset_x;
34+	origin_y = pointer->focus.view->base.geometry.y - pointer->focus.view->buffer_offset_y;
35+	sx = x - wl_fixed_from_int(origin_x);
36+	sy = y - wl_fixed_from_int(origin_y);
37 	wl_resource_for_each (resource, &pointer->focus.active)
38 		wl_pointer_send_motion(resource, time, sx, sy);
39 	return true;
+23, -2
 1@@ -28,6 +28,18 @@
 2 #include "surface.h"
 3 #include "util.h"
 4 
 5+static bool
 6+is_descendant_of(struct surface *ancestor, struct surface *surface)
 7+{
 8+	while (surface && surface->subsurface) {
 9+		surface = surface->subsurface->parent;
10+		if (surface == ancestor)
11+			return true;
12+	}
13+
14+	return false;
15+}
16+
17 static void
18 get_subsurface(struct wl_client *client, struct wl_resource *resource,
19                uint32_t id, struct wl_resource *surface_resource, struct wl_resource *parent_resource)
20@@ -36,11 +48,20 @@ get_subsurface(struct wl_client *client, struct wl_resource *resource,
21 	struct surface *surface = wl_resource_get_user_data(surface_resource);
22 	struct surface *parent = wl_resource_get_user_data(parent_resource);
23 
24-	if (!surface || !parent || surface == parent)
25+	if (!surface || !parent) {
26+		wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "invalid surface");
27 		return;
28+	}
29 
30-	if (surface->subsurface)
31+	if (surface == parent || is_descendant_of(surface, parent)) {
32+		wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_PARENT, "invalid parent surface");
33 		return;
34+	}
35+
36+	if (surface->subsurface) {
37+		wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "surface already has a subsurface role");
38+		return;
39+	}
40 
41 	subsurface = subsurface_new(client, wl_resource_get_version(resource), id, surface, parent);
42 
+222, -21
  1@@ -30,6 +30,20 @@
  2 #include <stdlib.h>
  3 #include <wayland-server.h>
  4 
  5+bool
  6+subsurface_is_synchronized(const struct subsurface *subsurface)
  7+{
  8+	while (subsurface) {
  9+		if (subsurface->sync)
 10+			return true;
 11+		if (!subsurface->parent)
 12+			return false;
 13+		subsurface = subsurface->parent->subsurface;
 14+	}
 15+
 16+	return false;
 17+}
 18+
 19 static void
 20 subsurface_update_position(struct subsurface *subsurface)
 21 {
 22@@ -50,16 +64,99 @@ subsurface_update_position(struct subsurface *subsurface)
 23 }
 24 
 25 static void
 26-handle_parent_view_move(struct view_handler *handler)
 27+list_remove_if_linked(struct wl_list *link)
 28+{
 29+	if (!wl_list_empty(link)) {
 30+		wl_list_remove(link);
 31+		wl_list_init(link);
 32+	}
 33+}
 34+
 35+void
 36+subsurface_update_visibility(struct subsurface *subsurface)
 37+{
 38+	struct compositor_view *view;
 39+	struct compositor_view *parent_view;
 40+
 41+	if (!subsurface || !subsurface->surface || !subsurface->parent)
 42+		return;
 43+
 44+	view = compositor_view(subsurface->surface->view);
 45+	parent_view = compositor_view(subsurface->parent->view);
 46+	if (!view || !parent_view)
 47+		return;
 48+
 49+	if (subsurface->added && parent_view->visible && subsurface->surface->state.buffer)
 50+		compositor_view_show(view);
 51+	else
 52+		compositor_view_hide(view);
 53+}
 54+
 55+static void
 56+handle_parent_view_change(struct view_handler *handler)
 57 {
 58 	struct subsurface *subsurface = wl_container_of(handler, subsurface, parent_view_handler);
 59 	subsurface_update_position(subsurface);
 60 }
 61 
 62+static void
 63+handle_parent_view_resize(struct view_handler *handler, uint32_t old_width, uint32_t old_height)
 64+{
 65+	(void)old_width;
 66+	(void)old_height;
 67+	handle_parent_view_change(handler);
 68+}
 69+
 70 static const struct view_handler_impl parent_view_handler_impl = {
 71-	.move = handle_parent_view_move,
 72+	.attach = handle_parent_view_change,
 73+	.move = handle_parent_view_change,
 74+	.resize = handle_parent_view_resize,
 75 };
 76 
 77+static struct subsurface *
 78+subsurface_find_sibling(struct subsurface *subsurface, struct surface *surface)
 79+{
 80+	struct surface *parent = subsurface->parent;
 81+	struct subsurface *sibling;
 82+
 83+	if (!parent)
 84+		return NULL;
 85+
 86+	wl_list_for_each (sibling, &parent->pending.state.subsurfaces_below, pending_link) {
 87+		if (sibling->surface == surface && sibling != subsurface)
 88+			return sibling;
 89+	}
 90+
 91+	wl_list_for_each (sibling, &parent->pending.state.subsurfaces_above, pending_link) {
 92+		if (sibling->surface == surface && sibling != subsurface)
 93+			return sibling;
 94+	}
 95+
 96+	return NULL;
 97+}
 98+
 99+static bool
100+is_valid_sibling(struct subsurface *subsurface, struct surface *sibling_surface,
101+                 struct subsurface **sibling_subsurface)
102+{
103+	struct subsurface *sibling;
104+
105+	if (!subsurface->parent || !sibling_surface || sibling_surface == subsurface->surface)
106+		return false;
107+
108+	if (sibling_surface == subsurface->parent) {
109+		*sibling_subsurface = NULL;
110+		return true;
111+	}
112+
113+	sibling = subsurface_find_sibling(subsurface, sibling_surface);
114+	if (!sibling)
115+		return false;
116+
117+	*sibling_subsurface = sibling;
118+	return true;
119+}
120+
121 static void
122 handle_surface_destroy(struct wl_listener *listener, void *data)
123 {
124@@ -94,6 +191,9 @@ handle_parent_destroy(struct wl_listener *listener, void *data)
125 		wl_list_init(&subsurface->link);
126 	}
127 
128+	list_remove_if_linked(&subsurface->pending_link);
129+	list_remove_if_linked(&subsurface->current_link);
130+
131 	subsurface->parent = NULL;
132 }
133 
134@@ -103,9 +203,9 @@ set_position(struct wl_client *client, struct wl_resource *resource, int32_t x,
135 	(void)client;
136 	struct subsurface *subsurface = wl_resource_get_user_data(resource);
137 
138-	subsurface->x = x;
139-	subsurface->y = y;
140-	subsurface_update_position(subsurface);
141+	subsurface->pending_x = x;
142+	subsurface->pending_y = y;
143+	subsurface->pending_position = true;
144 }
145 
146 static void
147@@ -114,17 +214,20 @@ place_above(struct wl_client *client, struct wl_resource *resource, struct wl_re
148 	(void)client;
149 	struct subsurface *subsurface = wl_resource_get_user_data(resource);
150 	struct surface *sibling_surface = wl_resource_get_user_data(sibling_resource);
151-	struct compositor_view *view = compositor_view(subsurface->surface->view);
152-	struct compositor_view *sibling_view = compositor_view(sibling_surface->view);
153+	struct subsurface *sibling_subsurface;
154 
155-	if (!view || !sibling_view || view == sibling_view)
156-		return;
157-
158-	if (view->parent != sibling_view->parent)
159+	if (!is_valid_sibling(subsurface, sibling_surface, &sibling_subsurface)) {
160+		wl_resource_post_error(resource, WL_SUBSURFACE_ERROR_BAD_SURFACE, "invalid sibling surface");
161 		return;
162+	}
163 
164-	wl_list_remove(&view->link);
165-	wl_list_insert(sibling_view->link.prev, &view->link);
166+	if (!sibling_subsurface) {
167+		wl_list_remove(&subsurface->pending_link);
168+		wl_list_insert(&subsurface->parent->pending.state.subsurfaces_above, &subsurface->pending_link);
169+	} else {
170+		wl_list_remove(&subsurface->pending_link);
171+		wl_list_insert(&sibling_subsurface->pending_link, &subsurface->pending_link);
172+	}
173 }
174 
175 static void
176@@ -133,17 +236,20 @@ place_below(struct wl_client *client, struct wl_resource *resource, struct wl_re
177 	(void)client;
178 	struct subsurface *subsurface = wl_resource_get_user_data(resource);
179 	struct surface *sibling_surface = wl_resource_get_user_data(sibling_resource);
180-	struct compositor_view *view = compositor_view(subsurface->surface->view);
181-	struct compositor_view *sibling_view = compositor_view(sibling_surface->view);
182+	struct subsurface *sibling_subsurface;
183 
184-	if (!view || !sibling_view || view == sibling_view)
185-		return;
186-
187-	if (view->parent != sibling_view->parent)
188+	if (!is_valid_sibling(subsurface, sibling_surface, &sibling_subsurface)) {
189+		wl_resource_post_error(resource, WL_SUBSURFACE_ERROR_BAD_SURFACE, "invalid sibling surface");
190 		return;
191+	}
192 
193-	wl_list_remove(&view->link);
194-	wl_list_insert(&sibling_view->link, &view->link);
195+	if (!sibling_subsurface) {
196+		wl_list_remove(&subsurface->pending_link);
197+		wl_list_insert(subsurface->parent->pending.state.subsurfaces_below.prev, &subsurface->pending_link);
198+	} else {
199+		wl_list_remove(&subsurface->pending_link);
200+		wl_list_insert(sibling_subsurface->pending_link.prev, &subsurface->pending_link);
201+	}
202 }
203 
204 static void
205@@ -159,7 +265,91 @@ set_desync(struct wl_client *client, struct wl_resource *resource)
206 {
207 	(void)client;
208 	struct subsurface *subsurface = wl_resource_get_user_data(resource);
209+	bool synchronized = subsurface_is_synchronized(subsurface);
210+
211 	subsurface->sync = false;
212+
213+	if (synchronized
214+	 && !subsurface_is_synchronized(subsurface)
215+	 && subsurface->pending
216+	 && subsurface->surface)
217+	{
218+		surface_commit_pending(subsurface->surface);
219+	}
220+}
221+
222+void
223+subsurface_parent_commit(struct surface *parent)
224+{
225+	struct subsurface *child;
226+	struct compositor_view *parent_view;
227+	struct compositor_view *reference;
228+	struct compositor_view *child_view;
229+
230+	if (!parent)
231+		return;
232+
233+	wl_list_for_each (child, &parent->subsurfaces, link)
234+		list_remove_if_linked(&child->current_link);
235+
236+	wl_list_init(&parent->state.subsurfaces_below);
237+	wl_list_init(&parent->state.subsurfaces_above);
238+
239+	wl_list_for_each (child, &parent->pending.state.subsurfaces_below, pending_link)
240+		wl_list_insert(parent->state.subsurfaces_below.prev, &child->current_link);
241+
242+	wl_list_for_each (child, &parent->pending.state.subsurfaces_above, pending_link)
243+		wl_list_insert(parent->state.subsurfaces_above.prev, &child->current_link);
244+
245+	parent_view = parent->view ? compositor_view(parent->view) : NULL;
246+	if (parent_view) {
247+		reference = parent_view;
248+		wl_list_for_each_reverse (child, &parent->state.subsurfaces_below, current_link) {
249+			if (!child->surface || !child->surface->view)
250+				continue;
251+
252+			child_view = compositor_view(child->surface->view);
253+			if (!child_view)
254+				continue;
255+
256+			compositor_view_restack(child_view, reference, false);
257+			reference = child_view;
258+		}
259+
260+		reference = parent_view;
261+		wl_list_for_each (child, &parent->state.subsurfaces_above, current_link) {
262+			if (!child->surface || !child->surface->view)
263+				continue;
264+
265+			child_view = compositor_view(child->surface->view);
266+			if (!child_view)
267+				continue;
268+
269+			compositor_view_restack(child_view, reference, true);
270+			reference = child_view;
271+		}
272+	}
273+
274+	wl_list_for_each (child, &parent->subsurfaces, link) {
275+		if (!child->pending_position)
276+			continue;
277+
278+		child->x = child->pending_x;
279+		child->y = child->pending_y;
280+		child->pending_position = false;
281+		subsurface_update_position(child);
282+	}
283+
284+	wl_list_for_each (child, &parent->state.subsurfaces_below, current_link) {
285+		if (!child->added)
286+			child->added = true;
287+		subsurface_update_visibility(child);
288+	}
289+	wl_list_for_each (child, &parent->state.subsurfaces_above, current_link) {
290+		if (!child->added)
291+			child->added = true;
292+		subsurface_update_visibility(child);
293+	}
294 }
295 
296 static const struct wl_subsurface_interface subsurface_impl = {
297@@ -200,6 +390,9 @@ subsurface_destroy(struct wl_resource *resource)
298 		wl_list_init(&subsurface->link);
299 	}
300 
301+	list_remove_if_linked(&subsurface->pending_link);
302+	list_remove_if_linked(&subsurface->current_link);
303+
304 	if (subsurface->surface && subsurface->surface->view) {
305 		struct compositor_view *view = compositor_view(subsurface->surface->view);
306 		if (view && !view->window)
307@@ -231,14 +424,20 @@ subsurface_new(struct wl_client *client, uint32_t version, uint32_t id,
308 	subsurface->parent = parent;
309 	subsurface->x = 0;
310 	subsurface->y = 0;
311+	subsurface->pending_x = 0;
312+	subsurface->pending_y = 0;
313+	subsurface->pending_position = false;
314 	subsurface->sync = true;
315 	subsurface->pending = false;
316+	subsurface->added = false;
317 
318 	subsurface->parent_view_handler.impl = &parent_view_handler_impl;
319 	wl_list_init(&subsurface->parent_view_handler.link);
320 	wl_list_init(&subsurface->surface_destroy_listener.link);
321 	wl_list_init(&subsurface->parent_destroy_listener.link);
322 	wl_list_init(&subsurface->link);
323+	wl_list_init(&subsurface->pending_link);
324+	wl_list_init(&subsurface->current_link);
325 
326 	if (!surface->view)
327 		compositor_create_view(surface);
328@@ -257,6 +456,8 @@ subsurface_new(struct wl_client *client, uint32_t version, uint32_t id,
329 	wl_list_insert(&parent_view->base.handlers, &subsurface->parent_view_handler.link);
330 	subsurface_update_position(subsurface);
331 	wl_list_insert(&parent->subsurfaces, &subsurface->link);
332+	wl_list_insert(parent->pending.state.subsurfaces_above.prev, &subsurface->pending_link);
333+	subsurface_update_visibility(subsurface);
334 
335 	subsurface->surface_destroy_listener.notify = handle_surface_destroy;
336 	wl_resource_add_destroy_listener(surface->resource, &subsurface->surface_destroy_listener);
+9, -0
 1@@ -41,11 +41,20 @@ struct subsurface {
 2 	struct wl_listener surface_destroy_listener;
 3 	struct wl_listener parent_destroy_listener;
 4 	struct wl_list link;
 5+	struct wl_list pending_link;
 6+	struct wl_list current_link;
 7 	int32_t x, y;
 8+	int32_t pending_x, pending_y;
 9+	bool pending_position;
10 	bool sync;
11 	bool pending;
12+	bool added;
13 };
14 
15+bool subsurface_is_synchronized(const struct subsurface *subsurface);
16+void subsurface_update_visibility(struct subsurface *subsurface);
17+void subsurface_parent_commit(struct surface *parent);
18+
19 struct subsurface *subsurface_new(struct wl_client *client, uint32_t version, uint32_t id,
20                                   struct surface *surface, struct surface *parent);
21 
+15, -2
 1@@ -59,6 +59,8 @@ state_initialize(struct surface_state *state)
 2 	pixman_region32_init_with_extents(&state->input, &infinite_extents);
 3 
 4 	wl_list_init(&state->frame_callbacks);
 5+	wl_list_init(&state->subsurfaces_below);
 6+	wl_list_init(&state->subsurfaces_above);
 7 }
 8 
 9 static void
10@@ -267,10 +269,15 @@ surface_apply_pending(struct surface *surface, bool flush_children)
11 	if (surface->subsurface)
12 		surface->subsurface->pending = false;
13 
14+	if (surface->subsurface)
15+		subsurface_update_visibility(surface->subsurface);
16+
17+	subsurface_parent_commit(surface);
18+
19 	if (flush_children) {
20 		struct subsurface *child;
21 		wl_list_for_each (child, &surface->subsurfaces, link) {
22-			if (!child->sync || !child->pending)
23+			if (!child->pending || !subsurface_is_synchronized(child))
24 				continue;
25 			if (child->surface)
26 				surface_apply_pending(child->surface, true);
27@@ -283,7 +290,7 @@ commit(struct wl_client *client, struct wl_resource *resource)
28 {
29 	struct surface *surface = wl_resource_get_user_data(resource);
30 
31-	if (surface->subsurface && surface->subsurface->sync) {
32+	if (surface->subsurface && subsurface_is_synchronized(surface->subsurface)) {
33 		surface->subsurface->pending = true;
34 		return;
35 	}
36@@ -402,3 +409,9 @@ surface_set_view(struct surface *surface, struct view *view)
37 		view_update(view);
38 	}
39 }
40+
41+void
42+surface_commit_pending(struct surface *surface)
43+{
44+	surface_apply_pending(surface, true);
45+}
+5, -0
 1@@ -54,6 +54,10 @@ struct surface_state {
 2 	pixman_region32_t input;
 3 
 4 	struct wl_list frame_callbacks;
 5+
 6+	/* subsurface order; double-buffered with surface state. */
 7+	struct wl_list subsurfaces_below;
 8+	struct wl_list subsurfaces_above;
 9 };
10 
11 struct surface {
12@@ -80,5 +84,6 @@ struct surface {
13 
14 struct surface *surface_new(struct wl_client *client, uint32_t version, uint32_t id);
15 void surface_set_view(struct surface *surface, struct view *view);
16+void surface_commit_pending(struct surface *surface);
17 
18 #endif