[RFC Weston 07/10] window: create sub-surfaces

Pekka Paalanen ppaalanen at gmail.com
Fri Feb 22 07:07:51 PST 2013


This patch depends on the wl_subsurface protocol.

The new application API window_add_subsurface() will create a plain
widget that is on a new sub-surface.

The sub-surface position is taken from the surface's root widget
allocation. This way widget allocations are always in the main surface
(i.e. window) coordinates. However, Cairo drawing coordinates will now
be different to widget coordinates for sub-surfaces. Cairo coordinates
are fixed by applying a translation in widget_cairo_create(), so that
widget drawing code can simply use the widget allocation as before.

Sub-surfaces are hooked up into resize, window flush, redraw, and
find_widget. Window maintains a list of sub-surfaces in top-first order.

window: move wl_subsurface to struct surface

window: use sub-surface commit mode

Add a client settable default mode, and toggle the mode when resizing to
guarantee in-sync updates of a window and its sub-surfaces.
---
 clients/window.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 clients/window.h |   4 ++
 2 files changed, 138 insertions(+), 3 deletions(-)

diff --git a/clients/window.c b/clients/window.c
index d17fc2f..e538da9 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -85,6 +85,7 @@ struct display {
 	struct wl_display *display;
 	struct wl_registry *registry;
 	struct wl_compositor *compositor;
+	struct wl_subcompositor *subcompositor;
 	struct wl_shell *shell;
 	struct wl_shm *shm;
 	struct wl_data_device_manager *data_device_manager;
@@ -189,6 +190,9 @@ struct surface {
 	struct window *window;
 
 	struct wl_surface *surface;
+	struct wl_subsurface *subsurface;
+	enum wl_subsurface_commit_mode subsurface_mode;
+	enum wl_subsurface_commit_mode default_mode;
 	struct toysurface *toysurface;
 	struct widget *widget;
 
@@ -202,6 +206,8 @@ struct surface {
 	enum wl_output_transform buffer_transform;
 
 	cairo_surface_t *cairo_surface;
+
+	struct wl_list link;
 };
 
 struct window {
@@ -238,6 +244,9 @@ struct window {
 
 	struct frame *frame;
 
+	/* struct surface::link, contains also main_surface */
+	struct wl_list subsurface_list;
+
 	void *user_data;
 	struct wl_list link;
 };
@@ -1193,12 +1202,21 @@ window_has_focus(struct window *window)
 static void
 window_flush(struct window *window)
 {
+	struct surface *surface;
+
 	if (window->type == TYPE_NONE) {
 		window->type = TYPE_TOPLEVEL;
 		if (window->shell_surface)
 			wl_shell_surface_set_toplevel(window->shell_surface);
 	}
 
+	wl_list_for_each(surface, &window->subsurface_list, link) {
+		if (surface == window->main_surface)
+			continue;
+
+		surface_flush(surface);
+	}
+
 	surface_flush(window->main_surface);
 }
 
@@ -1295,11 +1313,15 @@ surface_destroy(struct surface *surface)
 	if (surface->opaque_region)
 		wl_region_destroy(surface->opaque_region);
 
+	if (surface->subsurface)
+		wl_subsurface_destroy(surface->subsurface);
+
 	wl_surface_destroy(surface->surface);
 
 	if (surface->toysurface)
 		surface->toysurface->destroy(surface->toysurface);
 
+	wl_list_remove(&surface->link);
 	free(surface);
 }
 
@@ -1369,7 +1391,16 @@ widget_find_widget(struct widget *widget, int32_t x, int32_t y)
 static struct widget *
 window_find_widget(struct window *window, int32_t x, int32_t y)
 {
-	return widget_find_widget(window->main_surface->widget, x, y);
+	struct surface *surface;
+	struct widget *widget;
+
+	wl_list_for_each(surface, &window->subsurface_list, link) {
+		widget = widget_find_widget(surface->widget, x, y);
+		if (widget)
+			return widget;
+	}
+
+	return NULL;
 }
 
 static struct widget *
@@ -1419,8 +1450,13 @@ void
 widget_destroy(struct widget *widget)
 {
 	struct display *display = widget->window->display;
+	struct surface *surface = widget->surface;
 	struct input *input;
 
+	/* Destroy the sub-surface along with the root widget */
+	if (surface->widget == widget && surface->subsurface)
+		surface_destroy(widget->surface);
+
 	if (widget->tooltip) {
 		free(widget->tooltip);
 		widget->tooltip = NULL;
@@ -1494,12 +1530,15 @@ widget_get_cairo_surface(struct widget *widget)
 cairo_t *
 widget_cairo_create(struct widget *widget)
 {
+	struct surface *surface = widget->surface;
 	cairo_surface_t *cairo_surface;
 	cairo_t *cr;
 
 	cairo_surface = widget_get_cairo_surface(widget);
 	cr = cairo_create(cairo_surface);
 
+	cairo_translate(cr, -surface->allocation.x, -surface->allocation.y);
+
 	return cr;
 }
 
@@ -3216,6 +3255,34 @@ window_move(struct window *window, struct input *input, uint32_t serial)
 }
 
 static void
+surface_set_commit_mode_cached(struct surface *surface)
+{
+	if (!surface->subsurface)
+		return;
+
+	if (surface->subsurface_mode == WL_SUBSURFACE_COMMIT_MODE_PARENT_CACHED)
+		return;
+
+	wl_subsurface_set_commit_mode(surface->subsurface,
+		WL_SUBSURFACE_COMMIT_MODE_PARENT_CACHED);
+	surface->subsurface_mode = WL_SUBSURFACE_COMMIT_MODE_PARENT_CACHED;
+}
+
+static void
+surface_set_commit_mode_default(struct surface *surface)
+{
+	if (!surface->subsurface)
+		return;
+
+	if (surface->subsurface_mode == surface->default_mode)
+		return;
+
+	wl_subsurface_set_commit_mode(surface->subsurface,
+				      surface->default_mode);
+	surface->subsurface_mode = surface->default_mode;
+}
+
+static void
 surface_resize(struct surface *surface)
 {
 	struct widget *widget = surface->widget;
@@ -3237,11 +3304,18 @@ surface_resize(struct surface *surface)
 				       widget->allocation.height,
 				       widget->user_data);
 
+	if (surface->subsurface &&
+	    (surface->allocation.x != widget->allocation.x ||
+	     surface->allocation.y != widget->allocation.y)) {
+		wl_subsurface_set_position(surface->subsurface,
+					   widget->allocation.x,
+					   widget->allocation.y);
+	}
 	if (surface->allocation.width != widget->allocation.width ||
 	    surface->allocation.height != widget->allocation.height) {
-		surface->allocation = widget->allocation;
 		window_schedule_redraw(widget->window);
 	}
+	surface->allocation = widget->allocation;
 
 	if (widget->opaque)
 		wl_region_add(surface->opaque_region, 0, 0,
@@ -3252,6 +3326,8 @@ surface_resize(struct surface *surface)
 static void
 idle_resize(struct window *window)
 {
+	struct surface *surface;
+
 	window->resize_needed = 0;
 
 	widget_set_allocation(window->main_surface->widget,
@@ -3261,6 +3337,19 @@ idle_resize(struct window *window)
 			      window->pending_allocation.height);
 
 	surface_resize(window->main_surface);
+
+	/* The main surface is in the list, too. Main surface's
+	 * resize_handler is responsible for calling widget_set_allocation()
+	 * on all sub-surface root widgets, so they will be resized
+	 * properly.
+	 */
+	wl_list_for_each(surface, &window->subsurface_list, link) {
+		if (surface == window->main_surface)
+			continue;
+
+		surface_set_commit_mode_cached(surface);
+		surface_resize(surface);
+	}
 }
 
 void
@@ -3374,17 +3463,23 @@ static void
 idle_redraw(struct task *task, uint32_t events)
 {
 	struct window *window = container_of(task, struct window, redraw_task);
+	struct surface *surface;
 
 	if (window->resize_needed)
 		idle_resize(window);
 
-	widget_redraw(window->main_surface->widget);
+	wl_list_for_each(surface, &window->subsurface_list, link)
+		widget_redraw(surface->widget);
+
 	window->redraw_needed = 0;
 	wl_list_init(&window->redraw_task.link);
 
 	window->frame_cb = wl_surface_frame(window->main_surface->surface);
 	wl_callback_add_listener(window->frame_cb, &listener, window);
 	window_flush(window);
+
+	wl_list_for_each(surface, &window->subsurface_list, link)
+		surface_set_commit_mode_default(surface);
 }
 
 void
@@ -3633,6 +3728,8 @@ surface_create(struct window *window)
 	surface->surface = wl_compositor_create_surface(display->compositor);
 	wl_surface_add_listener(surface->surface, &surface_listener, window);
 
+	wl_list_insert(&window->subsurface_list, &surface->link);
+
 	return surface;
 }
 
@@ -3648,6 +3745,7 @@ window_create_internal(struct display *display,
 		return NULL;
 
 	memset(window, 0, sizeof *window);
+	wl_list_init(&window->subsurface_list);
 	window->display = display;
 	window->parent = parent;
 
@@ -3897,6 +3995,32 @@ window_set_buffer_type(struct window *window, enum window_buffer_type type)
 	window->main_surface->buffer_type = type;
 }
 
+struct widget *
+window_add_subsurface(struct window *window, void *data,
+		      enum wl_subsurface_commit_mode default_mode)
+{
+	struct widget *widget;
+	struct surface *surface;
+	struct wl_surface *parent;
+	struct wl_subcompositor *subcompo = window->display->subcompositor;
+
+	if (!subcompo)
+		return NULL;
+
+	surface = surface_create(window);
+	widget = widget_create(window, surface, data);
+	wl_list_init(&widget->link);
+	surface->widget = widget;
+
+	parent = window->main_surface->surface;
+	surface->subsurface = wl_subcompositor_get_subsurface(subcompo,
+							      surface->surface,
+							      parent);
+	surface->subsurface_mode = WL_SUBSURFACE_COMMIT_MODE_PARENT_CACHED;
+	surface->default_mode = default_mode;
+
+	return widget;
+}
 
 static void
 display_handle_geometry(void *data,
@@ -4168,6 +4292,10 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
 					 &text_cursor_position_interface, 1);
 	} else if (strcmp(interface, "workspace_manager") == 0) {
 		init_workspace_manager(d, id);
+	} else if (strcmp(interface, "wl_subcompositor") == 0) {
+		d->subcompositor =
+			wl_registry_bind(registry, id,
+					 &wl_subcompositor_interface, 1);
 	}
 
 	if (d->global_handler)
@@ -4453,6 +4581,9 @@ display_destroy(struct display *display)
 		fini_egl(display);
 #endif
 
+	if (display->subcompositor)
+		wl_subcompositor_destroy(display->subcompositor);
+
 	if (display->shell)
 		wl_shell_destroy(display->shell);
 
diff --git a/clients/window.h b/clients/window.h
index 815b3f1..30466c0 100644
--- a/clients/window.h
+++ b/clients/window.h
@@ -259,6 +259,10 @@ window_destroy(struct window *window);
 struct widget *
 window_add_widget(struct window *window, void *data);
 
+struct widget *
+window_add_subsurface(struct window *window, void *data,
+		      enum wl_subsurface_commit_mode default_mode);
+
 typedef void (*data_func_t)(void *data, size_t len,
 			    int32_t x, int32_t y, void *user_data);
 
-- 
1.7.12.4



More information about the wayland-devel mailing list