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