window: create sub-surfaces
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.
Add a client settable default commit mode, and toggle the mode when
resizing to guarantee in-sync updates of a window and its sub-surfaces.
Changes in v3:
- replaced set_commit_mode with set_sync and set_desync
Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
diff --git a/clients/window.c b/clients/window.c
index 695cf5d..30bb71e 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -85,6 +85,7 @@
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 window *window;
struct wl_surface *surface;
+ struct wl_subsurface *subsurface;
+ int synchronized;
+ int synchronized_default;
struct toysurface *toysurface;
struct widget *widget;
@@ -202,6 +206,8 @@
enum wl_output_transform buffer_transform;
cairo_surface_t *cairo_surface;
+
+ struct wl_list link;
};
struct window {
@@ -240,6 +246,9 @@
struct frame *frame;
+ /* struct surface::link, contains also main_surface */
+ struct wl_list subsurface_list;
+
void *user_data;
struct wl_list link;
};
@@ -1197,12 +1206,21 @@
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);
}
@@ -1299,11 +1317,15 @@
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);
}
@@ -1373,7 +1395,16 @@
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 *
@@ -1423,8 +1454,13 @@
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;
@@ -1498,12 +1534,15 @@
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;
}
@@ -3220,6 +3259,36 @@
}
static void
+surface_set_synchronized(struct surface *surface)
+{
+ if (!surface->subsurface)
+ return;
+
+ if (surface->synchronized)
+ return;
+
+ wl_subsurface_set_sync(surface->subsurface);
+ surface->synchronized = 1;
+}
+
+static void
+surface_set_synchronized_default(struct surface *surface)
+{
+ if (!surface->subsurface)
+ return;
+
+ if (surface->synchronized == surface->synchronized_default)
+ return;
+
+ if (surface->synchronized_default)
+ wl_subsurface_set_sync(surface->subsurface);
+ else
+ wl_subsurface_set_desync(surface->subsurface);
+
+ surface->synchronized = surface->synchronized_default;
+}
+
+static void
surface_resize(struct surface *surface)
{
struct widget *widget = surface->widget;
@@ -3241,11 +3310,18 @@
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,
@@ -3256,6 +3332,8 @@
static void
idle_resize(struct window *window)
{
+ struct surface *surface;
+
window->resize_needed = 0;
widget_set_allocation(window->main_surface->widget,
@@ -3265,6 +3343,19 @@
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_synchronized(surface);
+ surface_resize(surface);
+ }
}
void
@@ -3377,17 +3468,23 @@
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_synchronized_default(surface);
}
void
@@ -3687,6 +3784,8 @@
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;
}
@@ -3702,6 +3801,7 @@
return NULL;
memset(window, 0, sizeof *window);
+ wl_list_init(&window->subsurface_list);
window->display = display;
window->parent = parent;
@@ -3952,6 +4052,42 @@
window->main_surface->buffer_type = type;
}
+struct widget *
+window_add_subsurface(struct window *window, void *data,
+ enum subsurface_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->synchronized = 1;
+
+ switch (default_mode) {
+ case SUBSURFACE_SYNCHRONIZED:
+ surface->synchronized_default = 1;
+ break;
+ case SUBSURFACE_DESYNCHRONIZED:
+ surface->synchronized_default = 0;
+ break;
+ default:
+ assert(!"bad enum subsurface_mode");
+ }
+
+ return widget;
+}
static void
display_handle_geometry(void *data,
@@ -4223,6 +4359,10 @@
&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)
@@ -4516,6 +4656,9 @@
fini_egl(display);
#endif
+ if (display->subcompositor)
+ wl_subcompositor_destroy(display->subcompositor);
+
if (display->shell)
wl_shell_destroy(display->shell);