wayland: Make subsurface desynchronized after first parent surface frame

Initially the subsurface will be in synchronized mode and we will leave
it like this until the first time the parent surface has been committed.
The reason for this is because the subsurface position will be applied
as part of the parent surface state, and we need to synchronize the
initial position with the initial frame, so that we don't accidentally
draw the subsurface at the default position (0, 0) which would happen in
desynchronized mode if the subsurface content is committed before the
next parent surface commit.

https://bugzilla.gnome.org/show_bug.cgi?id=754839
This commit is contained in:
Jonas Ådahl 2015-09-10 17:42:21 +08:00
parent 07a574dddd
commit 7c4c8b90f0

View File

@ -37,6 +37,14 @@
#include <string.h>
#include <errno.h>
enum {
COMMITTED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
#define WINDOW_IS_TOPLEVEL_OR_FOREIGN(window) \
(GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD && \
GDK_WINDOW_TYPE (window) != GDK_WINDOW_OFFSCREEN)
@ -431,6 +439,8 @@ on_frame_clock_after_paint (GdkFrameClock *clock,
wl_surface_commit (impl->surface);
if (_gdk_wayland_is_shm_surface (impl->cairo_surface))
_gdk_wayland_shm_surface_set_busy (impl->cairo_surface);
g_signal_emit (impl, signals[COMMITTED], 0);
}
static void
@ -859,6 +869,19 @@ static const struct wl_surface_listener surface_listener = {
surface_leave
};
static void
on_parent_surface_committed (GdkWindowImplWayland *parent_impl,
GdkWindow *window)
{
GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
g_signal_handlers_disconnect_by_func (parent_impl,
(gpointer) on_parent_surface_committed,
window);
wl_subsurface_set_desync (impl->subsurface);
}
static void
gdk_wayland_window_create_subsurface (GdkWindow *window)
{
@ -886,7 +909,14 @@ gdk_wayland_window_create_subsurface (GdkWindow *window)
wl_subcompositor_get_subsurface (display_wayland->subcompositor,
impl->surface, parent_impl->surface);
wl_subsurface_set_position (impl->subsurface, window->x, window->y);
wl_subsurface_set_desync (impl->subsurface);
/* In order to synchronize the initial position with the initial frame
* content, wait with making the subsurface desynchronized until after
* the parent was committed.
*/
g_signal_connect_object (parent_impl, "committed",
G_CALLBACK (on_parent_surface_committed),
window, 0);
gdk_window_request_transient_parent_commit (window);
}
}
@ -1294,6 +1324,23 @@ gdk_wayland_window_show (GdkWindow *window,
gdk_wayland_window_attach_image (window);
}
static void
unmap_subsurface (GdkWindow *window)
{
GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
GdkWindowImplWayland *parent_impl;
g_return_if_fail (impl->subsurface);
g_return_if_fail (impl->transient_for);
parent_impl = GDK_WINDOW_IMPL_WAYLAND (impl->transient_for->impl);
wl_subsurface_destroy (impl->subsurface);
g_signal_handlers_disconnect_by_func (parent_impl,
(gpointer) on_parent_surface_committed,
window);
impl->subsurface = NULL;
}
static void
gdk_wayland_window_hide_surface (GdkWindow *window)
{
@ -1338,10 +1385,7 @@ gdk_wayland_window_hide_surface (GdkWindow *window)
}
if (impl->subsurface)
{
wl_subsurface_destroy (impl->subsurface);
impl->subsurface = NULL;
}
unmap_subsurface (window);
if (impl->awaiting_frame)
{
@ -1793,24 +1837,18 @@ static void
gdk_wayland_window_set_transient_for (GdkWindow *window,
GdkWindow *parent)
{
GdkWindowImplWayland *impl;
GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
if (impl->subsurface)
unmap_subsurface (window);
impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
impl->transient_for = parent;
gdk_wayland_window_sync_parent (window);
if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_SUBSURFACE)
{
if (impl->subsurface)
{
wl_subsurface_destroy (impl->subsurface);
impl->subsurface = NULL;
}
if (parent && gdk_window_is_visible (window))
gdk_wayland_window_create_subsurface (window);
}
if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_SUBSURFACE &&
parent && gdk_window_is_visible (window))
gdk_wayland_window_create_subsurface (window);
}
static void
@ -2392,6 +2430,13 @@ _gdk_window_impl_wayland_class_init (GdkWindowImplWaylandClass *klass)
impl_class->show_window_menu = gdk_wayland_window_show_window_menu;
impl_class->create_gl_context = gdk_wayland_window_create_gl_context;
impl_class->invalidate_for_new_frame = gdk_wayland_window_invalidate_for_new_frame;
signals[COMMITTED] = g_signal_new ("committed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
void