diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c index 76596da719..dc8c52f305 100644 --- a/gdk/wayland/gdkdisplay-wayland.c +++ b/gdk/wayland/gdkdisplay-wayland.c @@ -431,6 +431,7 @@ gdk_registry_handle_global (void *data, else if (strcmp (interface, "xdg_wm_base") == 0) { display_wayland->xdg_wm_base_id = id; + display_wayland->xdg_wm_base_version = version; } else if (strcmp (interface, "zxdg_shell_v6") == 0) { @@ -659,7 +660,8 @@ _gdk_wayland_display_open (const gchar *display_name) display_wayland->xdg_wm_base = wl_registry_bind (display_wayland->wl_registry, display_wayland->xdg_wm_base_id, - &xdg_wm_base_interface, 1); + &xdg_wm_base_interface, + MIN (display_wayland->xdg_wm_base_version, 3)); xdg_wm_base_add_listener (display_wayland->xdg_wm_base, &xdg_wm_base_listener, display_wayland); diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h index 4313ac3b17..db4b3efe41 100644 --- a/gdk/wayland/gdkdisplay-wayland.h +++ b/gdk/wayland/gdkdisplay-wayland.h @@ -86,6 +86,7 @@ struct _GdkWaylandDisplay guint32 serial; uint32_t xdg_wm_base_id; + int xdg_wm_base_version; uint32_t zxdg_shell_v6_id; GdkWaylandShellVariant shell_variant; diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c index 6947fe0b9e..5e734e6ecd 100644 --- a/gdk/wayland/gdksurface-wayland.c +++ b/gdk/wayland/gdksurface-wayland.c @@ -59,6 +59,7 @@ static guint signals[LAST_SIGNAL]; typedef enum _PopupState { POPUP_STATE_IDLE, + POPUP_STATE_WAITING_FOR_REPOSITIONED, POPUP_STATE_WAITING_FOR_CONFIGURE, POPUP_STATE_WAITING_FOR_FRAME, } PopupState; @@ -94,6 +95,9 @@ struct _GdkWaylandSurface EGLSurface egl_surface; EGLSurface dummy_egl_surface; + uint32_t reposition_token; + uint32_t received_reposition_token; + PopupState popup_state; unsigned int initial_configure_received : 1; @@ -167,12 +171,18 @@ struct _GdkWaylandSurface int y; int width; int height; + uint32_t repositioned_token; + gboolean has_repositioned_token; } popup; + gboolean is_initial_configure; + uint32_t serial; gboolean is_dirty; } pending; + uint32_t last_configure_serial; + int state_freeze_count; struct { @@ -438,6 +448,7 @@ frame_callback (void *data, switch (impl->popup_state) { case POPUP_STATE_IDLE: + case POPUP_STATE_WAITING_FOR_REPOSITIONED: case POPUP_STATE_WAITING_FOR_CONFIGURE: break; case POPUP_STATE_WAITING_FOR_FRAME: @@ -1282,8 +1293,17 @@ gdk_wayland_surface_configure_popup (GdkSurface *surface) impl->pending.serial); } + if (impl->pending.popup.has_repositioned_token) + impl->received_reposition_token = impl->pending.popup.repositioned_token; + switch (impl->popup_state) { + case POPUP_STATE_WAITING_FOR_REPOSITIONED: + if (impl->received_reposition_token != impl->reposition_token) + return; + else + gdk_surface_thaw_updates (surface); + G_GNUC_FALLTHROUGH; case POPUP_STATE_WAITING_FOR_CONFIGURE: impl->popup_state = POPUP_STATE_WAITING_FOR_FRAME; break; @@ -1306,6 +1326,10 @@ gdk_wayland_surface_configure_popup (GdkSurface *surface) width, height, impl->popup.layout); + if (!impl->pending.popup.has_repositioned_token && + !impl->pending.is_initial_configure) + g_signal_emit_by_name (surface, "popup-layout-changed"); + gdk_surface_invalidate_rect (surface, NULL); } @@ -1318,6 +1342,7 @@ gdk_wayland_surface_configure (GdkSurface *surface) { gdk_surface_thaw_updates (surface); impl->initial_configure_received = TRUE; + impl->pending.is_initial_configure = TRUE; } if (is_realized_popup (surface)) @@ -1327,6 +1352,8 @@ gdk_wayland_surface_configure (GdkSurface *surface) else g_warn_if_reached (); + impl->last_configure_serial = impl->pending.serial; + memset (&impl->pending, 0, sizeof (impl->pending)); } @@ -1663,9 +1690,31 @@ xdg_popup_done (void *data, gdk_surface_hide (surface); } +static void +xdg_popup_repositioned (void *data, + struct xdg_popup *xdg_popup, + uint32_t token) +{ + GdkSurface *surface = GDK_SURFACE (data); + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + + GDK_DISPLAY_NOTE (gdk_surface_get_display (surface), EVENTS, + g_message ("repositioned %p", surface)); + + if (impl->popup_state != POPUP_STATE_WAITING_FOR_REPOSITIONED) + { + g_warning ("Unexpected xdg_popup.repositioned event, probably buggy compositor"); + return; + } + + impl->pending.popup.repositioned_token = token; + impl->pending.popup.has_repositioned_token = TRUE; +} + static const struct xdg_popup_listener xdg_popup_listener = { xdg_popup_configure, xdg_popup_done, + xdg_popup_repositioned, }; static void @@ -2052,7 +2101,8 @@ static gpointer create_dynamic_positioner (GdkSurface *surface, int width, int height, - GdkPopupLayout *layout) + GdkPopupLayout *layout, + gboolean ack_parent_configure) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); GdkSurface *parent = surface->parent; @@ -2129,6 +2179,30 @@ create_dynamic_positioner (GdkSurface *surface, xdg_positioner_set_constraint_adjustment (positioner, constraint_adjustment); + if (xdg_positioner_get_version (positioner) >= + XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION) + xdg_positioner_set_reactive (positioner); + + if (ack_parent_configure && + xdg_positioner_get_version (positioner) >= + XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION) + { + GdkWaylandSurface *parent_impl = GDK_WAYLAND_SURFACE (parent); + int parent_width; + int parent_height; + + parent_width = parent->width - (parent_impl->margin_left + + parent_impl->margin_right); + parent_height = parent->height - (parent_impl->margin_top + + parent_impl->margin_bottom); + + xdg_positioner_set_parent_size (positioner, + parent_width, + parent_height); + xdg_positioner_set_parent_configure (positioner, + parent_impl->last_configure_serial); + } + return positioner; } case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: @@ -2233,7 +2307,7 @@ gdk_wayland_surface_create_xdg_popup (GdkSurface *surface, gdk_surface_freeze_updates (surface); - positioner = create_dynamic_positioner (surface, width, height, layout); + positioner = create_dynamic_positioner (surface, width, height, layout, FALSE); switch (display->shell_variant) { @@ -2501,6 +2575,9 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface) { switch (impl->popup_state) { + case POPUP_STATE_WAITING_FOR_REPOSITIONED: + gdk_surface_thaw_updates (surface); + G_GNUC_FALLTHROUGH; case POPUP_STATE_WAITING_FOR_CONFIGURE: case POPUP_STATE_WAITING_FOR_FRAME: thaw_popup_toplevel_state (surface); @@ -2602,6 +2679,7 @@ do_queue_relayout (GdkSurface *surface, GdkPopupLayout *layout) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + struct xdg_positioner *positioner; g_assert (is_realized_popup (surface)); g_assert (impl->popup_state == POPUP_STATE_IDLE || @@ -2612,7 +2690,41 @@ do_queue_relayout (GdkSurface *surface, impl->popup.unconstrained_width = width; impl->popup.unconstrained_height = height; - queue_relayout_fallback (surface, layout); + if (!impl->display_server.xdg_popup || + xdg_popup_get_version (impl->display_server.xdg_popup) < + XDG_POPUP_REPOSITION_SINCE_VERSION) + { + g_warning_once ("Compositor doesn't support moving popups, " + "relying on remapping"); + queue_relayout_fallback (surface, layout); + + return; + } + + positioner = create_dynamic_positioner (surface, + width, height, layout, + TRUE); + xdg_popup_reposition (impl->display_server.xdg_popup, + positioner, + ++impl->reposition_token); + xdg_positioner_destroy (positioner); + + gdk_surface_freeze_updates (surface); + + switch (impl->popup_state) + { + case POPUP_STATE_IDLE: + freeze_popup_toplevel_state (surface); + break; + case POPUP_STATE_WAITING_FOR_FRAME: + break; + case POPUP_STATE_WAITING_FOR_CONFIGURE: + case POPUP_STATE_WAITING_FOR_REPOSITIONED: + default: + g_assert_not_reached (); + } + + impl->popup_state = POPUP_STATE_WAITING_FOR_REPOSITIONED; } static gboolean @@ -2623,6 +2735,9 @@ is_relayout_finished (GdkSurface *surface) if (!impl->initial_configure_received) return FALSE; + if (impl->reposition_token != impl->received_reposition_token) + return FALSE; + return TRUE; } @@ -2707,6 +2822,7 @@ reposition_popup (GdkSurface *surface, case POPUP_STATE_WAITING_FOR_FRAME: do_queue_relayout (surface, width, height, layout); break; + case POPUP_STATE_WAITING_FOR_REPOSITIONED: case POPUP_STATE_WAITING_FOR_CONFIGURE: g_warn_if_reached (); break; diff --git a/gdk/wayland/meson.build b/gdk/wayland/meson.build index b6592064fb..14267f4ccc 100644 --- a/gdk/wayland/meson.build +++ b/gdk/wayland/meson.build @@ -37,10 +37,6 @@ gdk_wayland_deps = [ wlegldep, ] -# wayland protocols -proto_dir = dependency('wayland-protocols').get_pkgconfig_variable('pkgdatadir') -assert(proto_dir != '', 'Could not get pkgdatadir from wayland-protocols.pc') - wayland_scanner = find_program('wayland-scanner') # Format: @@ -68,14 +64,14 @@ foreach p: proto_sources if proto_stability == 'stable' output_base = proto_name - input = join_paths(proto_dir, '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base)) + input = files(join_paths(wlproto_dir, '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base))) elif proto_stability == 'private' output_base = proto_name - input = 'protocol/@0@.xml'.format(proto_name) + input = files('protocol/@0@.xml'.format(proto_name)) else proto_version = p.get(2) output_base = '@0@-@1@-@2@'.format(proto_name, proto_stability, proto_version) - input = join_paths(proto_dir, '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base)) + input = files(join_paths(wlproto_dir, '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base))) endif gdk_wayland_gen_headers += custom_target('@0@ client header'.format(output_base), diff --git a/gtk/meson.build b/gtk/meson.build index c7627af5f0..28e23b6781 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -644,11 +644,11 @@ foreach p: proto_sources if wayland_enabled if proto_stability == 'stable' output_base = proto_name - input = '@0@.xml'.format(proto_name) + input = files('@0@.xml'.format(proto_name)) else proto_version = p.get(2) output_base = '@0@-@1@-@2@'.format(proto_name, proto_stability, proto_version) - input = join_paths(proto_dir, '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base)) + input = files(join_paths(wlproto_dir, '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base))) endif # wayland_scanner is defined in gdk/wayland/meson.build diff --git a/meson.build b/meson.build index d3b94574ab..ecf4228b40 100644 --- a/meson.build +++ b/meson.build @@ -11,8 +11,8 @@ project('gtk', 'c', license: 'LGPLv2.1+') glib_major_req = 2 -glib_minor_req = 59 -glib_micro_req = 0 +glib_minor_req = 63 +glib_micro_req = 1 if glib_minor_req.is_odd() glib_min_required = 'GLIB_VERSION_@0@_@1@'.format(glib_major_req, glib_minor_req - 1) @@ -33,7 +33,7 @@ atk_req = '>= 2.15.1' cairo_req = '>= 1.14.0' gdk_pixbuf_req = '>= 2.30.0' introspection_req = '>= 1.39.0' -wayland_proto_req = '>= 1.14' +wayland_proto_req = '>= 1.20' wayland_req = '>= 1.14.91' graphene_req = '>= 1.9.1' epoxy_req = '>= 1.4' @@ -469,10 +469,16 @@ atk_pkgs = ['atk'] wayland_pkgs = [] if wayland_enabled wlclientdep = dependency('wayland-client', version: wayland_req) - wlprotocolsdep = dependency('wayland-protocols', version: wayland_proto_req) + wlprotocolsdep = dependency('wayland-protocols', version: wayland_proto_req, required: false) wlegldep = dependency('wayland-egl') backend_immodules += ['wayland'] + if not wlprotocolsdep.found() + wlproto_dir = subproject('wayland-protocols').get_variable('wayland_protocols_srcdir') + else + wlproto_dir = wlprotocolsdep.get_pkgconfig_variable('pkgdatadir') + endif + wayland_pkgs = [ 'wayland-client', wayland_req, 'wayland-protocols', wayland_proto_req, diff --git a/subprojects/wayland-protocols.wrap b/subprojects/wayland-protocols.wrap new file mode 100644 index 0000000000..bb9619cbc9 --- /dev/null +++ b/subprojects/wayland-protocols.wrap @@ -0,0 +1,4 @@ +[wrap-git] +directory=wayland-protocols +url=https://gitlab.freedesktop.org/jadahl/wayland-protocols.git +revision=wip/meson-meson-0.53