From 2a463baed08a37d137f4b49e4ab4cdff2b07593a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 6 Jan 2023 13:44:18 -0500 Subject: [PATCH] wayland: Rearrange the surface code --- gdk/wayland/gdksurface-wayland.c | 5577 +++++++++++++++--------------- 1 file changed, 2783 insertions(+), 2794 deletions(-) diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c index 081087752a..725404c327 100644 --- a/gdk/wayland/gdksurface-wayland.c +++ b/gdk/wayland/gdksurface-wayland.c @@ -57,160 +57,10 @@ * [method@GdkWayland.WaylandSurface.get_wl_surface]. */ -/** - * GdkWaylandToplevel: - * - * The Wayland implementation of `GdkToplevel`. - * - * Beyond the [iface@Gdk.Toplevel] API, the Wayland implementation - * has API to set up cross-process parent-child relationships between - * surfaces with [method@GdkWayland.WaylandToplevel.export_handle] and - * [method@GdkWayland.WaylandToplevel.set_transient_for_exported]. - */ - -/** - * GdkWaylandPopup: - * - * The Wayland implementation of `GdkPopup`. - */ - #define MAX_WL_BUFFER_SIZE (4083) /* 4096 minus header, string argument length and NUL byte */ G_DEFINE_TYPE (GdkWaylandSurface, gdk_wayland_surface, GDK_TYPE_SURFACE) -struct _GdkWaylandToplevel -{ - GdkWaylandSurface parent_instance; - - struct { - struct gtk_surface1 *gtk_surface; - struct xdg_toplevel *xdg_toplevel; - struct zxdg_toplevel_v6 *zxdg_toplevel_v6; - } display_server; - - GdkWaylandToplevel *transient_for; - - struct org_kde_kwin_server_decoration *server_decoration; - struct zxdg_exported_v1 *xdg_exported; - struct zxdg_exported_v2 *xdg_exported_v2; - - struct { - int width; - int height; - GdkToplevelState state; - gboolean is_resizing; - - int bounds_width; - int bounds_height; - gboolean has_bounds; - } pending; - - struct { - gboolean should_constrain; - gboolean size_is_fixed; - } next_layout; - - struct { - GdkWaylandToplevelExported callback; - gpointer user_data; - GDestroyNotify destroy_func; - } exported; - - struct { - gboolean was_set; - - char *application_id; - char *app_menu_path; - char *menubar_path; - char *window_object_path; - char *application_object_path; - char *unique_bus_name; - } application; - - struct zwp_idle_inhibitor_v1 *idle_inhibitor; - size_t idle_inhibitor_refcount; - - struct wl_output *initial_fullscreen_output; - - struct { - GdkToplevelState unset_flags; - GdkToplevelState set_flags; - } initial_state; - - GdkToplevelLayout *layout; - int bounds_width; - int bounds_height; - gboolean has_bounds; - - char *title; - - GdkGeometry geometry_hints; - GdkSurfaceHints geometry_mask; - GdkGeometry last_sent_geometry_hints; - - struct zxdg_imported_v1 *imported_transient_for; - struct zxdg_imported_v2 *imported_transient_for_v2; - - GHashTable *shortcuts_inhibitors; -}; - -typedef struct -{ - GdkWaylandSurfaceClass parent_class; -} GdkWaylandToplevelClass; - -static void gdk_wayland_toplevel_iface_init (GdkToplevelInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (GdkWaylandToplevel, gdk_wayland_toplevel, GDK_TYPE_WAYLAND_SURFACE, - G_IMPLEMENT_INTERFACE (GDK_TYPE_TOPLEVEL, - gdk_wayland_toplevel_iface_init)) - -struct _GdkWaylandPopup -{ - GdkWaylandSurface parent_instance; - - struct { - struct xdg_popup *xdg_popup; - struct zxdg_popup_v6 *zxdg_popup_v6; - } display_server; - - PopupState state; - unsigned int thaw_upon_show : 1; - GdkPopupLayout *layout; - int unconstrained_width; - int unconstrained_height; - - struct { - int x; - int y; - int width; - int height; - uint32_t repositioned_token; - gboolean has_repositioned_token; - } pending; - - struct { - int x; - int y; - } next_layout; - - uint32_t reposition_token; - uint32_t received_reposition_token; - - GdkSeat *grab_input_seat; -}; - -typedef struct -{ - GdkWaylandSurfaceClass parent_class; -} GdkWaylandPopupClass; - -static void gdk_wayland_popup_iface_init (GdkPopupInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (GdkWaylandPopup, gdk_wayland_popup, GDK_TYPE_WAYLAND_SURFACE, - G_IMPLEMENT_INTERFACE (GDK_TYPE_POPUP, - gdk_wayland_popup_iface_init)) - static void gdk_wayland_surface_maybe_resize (GdkSurface *surface, int width, int height, @@ -218,9 +68,6 @@ static void gdk_wayland_surface_maybe_resize (GdkSurface *surface, static void gdk_wayland_surface_configure (GdkSurface *surface); -static void maybe_set_gtk_surface_dbus_properties (GdkWaylandToplevel *wayland_toplevel); -static void maybe_set_gtk_surface_modal (GdkWaylandToplevel *wayland_toplevel); - static void gdk_wayland_surface_show (GdkSurface *surface); static void gdk_wayland_surface_hide (GdkSurface *surface); @@ -245,59 +92,313 @@ static void update_popup_layout_state (GdkWaylandPopup *wayland_popup, static gboolean gdk_wayland_toplevel_is_exported (GdkWaylandToplevel *wayland_toplevel); -static void configure_toplevel_geometry (GdkWaylandToplevel *wayland_toplevel); +static void configure_toplevel_geometry (GdkWaylandToplevel *toplevel); +static void gdk_wayland_surface_create_xdg_toplevel (GdkWaylandToplevel *toplevel); +static void gdk_wayland_surface_configure_toplevel (GdkWaylandToplevel *toplevel); +static void gdk_wayland_toplevel_sync_parent (GdkWaylandToplevel *toplevel); +static void gdk_wayland_toplevel_sync_parent_of_imported (GdkWaylandToplevel *toplevel); +static void gdk_wayland_toplevel_sync_title (GdkWaylandToplevel *toplevel); +static void gdk_wayland_toplevel_set_geometry_hints (GdkWaylandToplevel *toplevel, + const GdkGeometry *geometry, + GdkSurfaceHints geom_mask); +static void gdk_wayland_toplevel_handle_configure (GdkWaylandToplevel *toplevel, + int32_t width, + int32_t height, + GdkToplevelState state); +static void gdk_wayland_toplevel_hide_surface (GdkWaylandToplevel *toplevel); + +static void configure_popup_geometry (GdkWaylandPopup *popup); +static void gdk_wayland_surface_configure_popup (GdkWaylandPopup *popup); +static void frame_callback_popup (GdkWaylandPopup *popup); +static void gdk_wayland_popup_hide_surface (GdkWaylandPopup *popup); + +/* {{{ Utilities */ static void -gdk_wayland_surface_init (GdkWaylandSurface *impl) +fill_presentation_time_from_frame_time (GdkFrameTimings *timings, + guint32 frame_time) { - impl->scale = 1; - impl->saved_width = -1; - impl->saved_height = -1; + /* The timestamp in a wayland frame is a msec time value that in some + * way reflects the time at which the server started drawing the frame. + * This is not useful from our perspective. + * + * However, for the DRM backend of Weston, on reasonably recent + * Linux, we know that the time is the + * clock_gettime (CLOCK_MONOTONIC) value at the vblank, and that + * backend starts drawing immediately after receiving the vblank + * notification. If we detect this, and make the assumption that the + * compositor will finish drawing before the next vblank, we can + * then determine the presentation time as the frame time we + * received plus one refresh interval. + * + * If a backend is using clock_gettime(CLOCK_MONOTONIC), but not + * picking values right at the vblank, then the presentation times + * we compute won't be accurate, but not really worse than then + * the alternative of not providing presentation times at all. + * + * The complexity here is dealing with the fact that we receive + * only the low 32 bits of the CLOCK_MONOTONIC value in milliseconds. + */ + gint64 now_monotonic = g_get_monotonic_time (); + gint64 now_monotonic_msec = now_monotonic / 1000; + uint32_t now_monotonic_low = (uint32_t)now_monotonic_msec; + + if (frame_time - now_monotonic_low < 1000 || + frame_time - now_monotonic_low > (uint32_t)-1000) + { + /* Timestamp we received is within one second of the current time. + */ + gint64 last_frame_time = now_monotonic + (gint64)1000 * (gint32)(frame_time - now_monotonic_low); + if ((gint32)now_monotonic_low < 0 && (gint32)frame_time > 0) + last_frame_time += (gint64)1000 * G_GINT64_CONSTANT(0x100000000); + else if ((gint32)now_monotonic_low > 0 && (gint32)frame_time < 0) + last_frame_time -= (gint64)1000 * G_GINT64_CONSTANT(0x100000000); + + timings->presentation_time = last_frame_time + timings->refresh_interval; + } } static void -gdk_wayland_surface_freeze_state (GdkSurface *surface) +gdk_wayland_surface_get_window_geometry (GdkSurface *surface, + GdkRectangle *geometry) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - impl->state_freeze_count++; + *geometry = (GdkRectangle) { + .x = impl->shadow_left, + .y = impl->shadow_top, + .width = surface->width - (impl->shadow_left + impl->shadow_right), + .height = surface->height - (impl->shadow_top + impl->shadow_bottom) + }; } -static void -gdk_wayland_surface_thaw_state (GdkSurface *surface) +static struct wl_region * +wl_region_from_cairo_region (GdkWaylandDisplay *display, + cairo_region_t *region) { - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - - g_assert (impl->state_freeze_count > 0); + struct wl_region *wl_region; + int i, n_rects; - impl->state_freeze_count--; + wl_region = wl_compositor_create_region (display->compositor); + if (wl_region == NULL) + return NULL; - if (impl->state_freeze_count > 0) - return; + n_rects = cairo_region_num_rectangles (region); + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; + cairo_region_get_rectangle (region, i, &rect); + wl_region_add (wl_region, rect.x, rect.y, rect.width, rect.height); + } - if (impl->pending.is_dirty) - gdk_wayland_surface_configure (surface); + return wl_region; } -static void -_gdk_wayland_surface_save_size (GdkSurface *surface) +static const char * +get_default_title (void) { - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - - if (surface->state & (GDK_TOPLEVEL_STATE_FULLSCREEN | - GDK_TOPLEVEL_STATE_MAXIMIZED | - GDK_TOPLEVEL_STATE_TILED)) - return; + const char *title; - if (surface->width <= 1 || surface->height <= 1) - return; + title = g_get_application_name (); + if (!title) + title = g_get_prgname (); + if (!title) + title = ""; - impl->saved_width = surface->width - impl->shadow_left - impl->shadow_right; - impl->saved_height = surface->height - impl->shadow_top - impl->shadow_bottom; + return title; } -static void -_gdk_wayland_surface_clear_saved_size (GdkSurface *surface) +static gboolean +is_realized_shell_surface (GdkWaylandSurface *impl) +{ + return (impl->display_server.xdg_surface || + impl->display_server.zxdg_surface_v6); +} + +static enum xdg_positioner_anchor +rect_anchor_to_anchor (GdkGravity rect_anchor) +{ + switch (rect_anchor) + { + case GDK_GRAVITY_NORTH_WEST: + case GDK_GRAVITY_STATIC: + return XDG_POSITIONER_ANCHOR_TOP_LEFT; + case GDK_GRAVITY_NORTH: + return XDG_POSITIONER_ANCHOR_TOP; + case GDK_GRAVITY_NORTH_EAST: + return XDG_POSITIONER_ANCHOR_TOP_RIGHT; + case GDK_GRAVITY_WEST: + return XDG_POSITIONER_ANCHOR_LEFT; + case GDK_GRAVITY_CENTER: + return XDG_POSITIONER_ANCHOR_NONE; + case GDK_GRAVITY_EAST: + return XDG_POSITIONER_ANCHOR_RIGHT; + case GDK_GRAVITY_SOUTH_WEST: + return XDG_POSITIONER_ANCHOR_BOTTOM_LEFT; + case GDK_GRAVITY_SOUTH: + return XDG_POSITIONER_ANCHOR_BOTTOM; + case GDK_GRAVITY_SOUTH_EAST: + return XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT; + default: + g_assert_not_reached (); + } +} + +static enum xdg_positioner_gravity +surface_anchor_to_gravity (GdkGravity rect_anchor) +{ + switch (rect_anchor) + { + case GDK_GRAVITY_NORTH_WEST: + case GDK_GRAVITY_STATIC: + return XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT; + case GDK_GRAVITY_NORTH: + return XDG_POSITIONER_GRAVITY_BOTTOM; + case GDK_GRAVITY_NORTH_EAST: + return XDG_POSITIONER_GRAVITY_BOTTOM_LEFT; + case GDK_GRAVITY_WEST: + return XDG_POSITIONER_GRAVITY_RIGHT; + case GDK_GRAVITY_CENTER: + return XDG_POSITIONER_GRAVITY_NONE; + case GDK_GRAVITY_EAST: + return XDG_POSITIONER_GRAVITY_LEFT; + case GDK_GRAVITY_SOUTH_WEST: + return XDG_POSITIONER_GRAVITY_TOP_RIGHT; + case GDK_GRAVITY_SOUTH: + return XDG_POSITIONER_GRAVITY_TOP; + case GDK_GRAVITY_SOUTH_EAST: + return XDG_POSITIONER_GRAVITY_TOP_LEFT; + default: + g_assert_not_reached (); + } +} + +static enum zxdg_positioner_v6_anchor +rect_anchor_to_anchor_legacy (GdkGravity rect_anchor) +{ + switch (rect_anchor) + { + case GDK_GRAVITY_NORTH_WEST: + case GDK_GRAVITY_STATIC: + return (ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_LEFT); + case GDK_GRAVITY_NORTH: + return ZXDG_POSITIONER_V6_ANCHOR_TOP; + case GDK_GRAVITY_NORTH_EAST: + return (ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT); + case GDK_GRAVITY_WEST: + return ZXDG_POSITIONER_V6_ANCHOR_LEFT; + case GDK_GRAVITY_CENTER: + return ZXDG_POSITIONER_V6_ANCHOR_NONE; + case GDK_GRAVITY_EAST: + return ZXDG_POSITIONER_V6_ANCHOR_RIGHT; + case GDK_GRAVITY_SOUTH_WEST: + return (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_LEFT); + case GDK_GRAVITY_SOUTH: + return ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; + case GDK_GRAVITY_SOUTH_EAST: + return (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT); + default: + g_assert_not_reached (); + } + + return (ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_LEFT); +} + +static enum zxdg_positioner_v6_gravity +surface_anchor_to_gravity_legacy (GdkGravity rect_anchor) +{ + switch (rect_anchor) + { + case GDK_GRAVITY_NORTH_WEST: + case GDK_GRAVITY_STATIC: + return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT); + case GDK_GRAVITY_NORTH: + return ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; + case GDK_GRAVITY_NORTH_EAST: + return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_LEFT); + case GDK_GRAVITY_WEST: + return ZXDG_POSITIONER_V6_GRAVITY_RIGHT; + case GDK_GRAVITY_CENTER: + return ZXDG_POSITIONER_V6_GRAVITY_NONE; + case GDK_GRAVITY_EAST: + return ZXDG_POSITIONER_V6_GRAVITY_LEFT; + case GDK_GRAVITY_SOUTH_WEST: + return (ZXDG_POSITIONER_V6_GRAVITY_TOP | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT); + case GDK_GRAVITY_SOUTH: + return ZXDG_POSITIONER_V6_GRAVITY_TOP; + case GDK_GRAVITY_SOUTH_EAST: + return (ZXDG_POSITIONER_V6_GRAVITY_TOP | + ZXDG_POSITIONER_V6_GRAVITY_LEFT); + default: + g_assert_not_reached (); + } + + return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT); +} + +/* }}} */ +/* {{{ Surface implementation */ + +static void +gdk_wayland_surface_init (GdkWaylandSurface *impl) +{ + impl->scale = 1; + impl->saved_width = -1; + impl->saved_height = -1; +} + +static void +gdk_wayland_surface_freeze_state (GdkSurface *surface) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + + impl->state_freeze_count++; +} + +static void +gdk_wayland_surface_thaw_state (GdkSurface *surface) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + + g_assert (impl->state_freeze_count > 0); + + impl->state_freeze_count--; + + if (impl->state_freeze_count > 0) + return; + + if (impl->pending.is_dirty) + gdk_wayland_surface_configure (surface); +} + +static void +_gdk_wayland_surface_save_size (GdkSurface *surface) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + + if (surface->state & (GDK_TOPLEVEL_STATE_FULLSCREEN | + GDK_TOPLEVEL_STATE_MAXIMIZED | + GDK_TOPLEVEL_STATE_TILED)) + return; + + if (surface->width <= 1 || surface->height <= 1) + return; + + impl->saved_width = surface->width - impl->shadow_left - impl->shadow_right; + impl->saved_height = surface->height - impl->shadow_top - impl->shadow_bottom; +} + +static void +_gdk_wayland_surface_clear_saved_size (GdkSurface *surface) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); @@ -345,64 +446,6 @@ gdk_wayland_surface_update_size (GdkSurface *surface, _gdk_surface_update_size (surface); } -static const char * -get_default_title (void) -{ - const char *title; - - title = g_get_application_name (); - if (!title) - title = g_get_prgname (); - if (!title) - title = ""; - - return title; -} - -static void -fill_presentation_time_from_frame_time (GdkFrameTimings *timings, - guint32 frame_time) -{ - /* The timestamp in a wayland frame is a msec time value that in some - * way reflects the time at which the server started drawing the frame. - * This is not useful from our perspective. - * - * However, for the DRM backend of Weston, on reasonably recent - * Linux, we know that the time is the - * clock_gettime (CLOCK_MONOTONIC) value at the vblank, and that - * backend starts drawing immediately after receiving the vblank - * notification. If we detect this, and make the assumption that the - * compositor will finish drawing before the next vblank, we can - * then determine the presentation time as the frame time we - * received plus one refresh interval. - * - * If a backend is using clock_gettime(CLOCK_MONOTONIC), but not - * picking values right at the vblank, then the presentation times - * we compute won't be accurate, but not really worse than then - * the alternative of not providing presentation times at all. - * - * The complexity here is dealing with the fact that we receive - * only the low 32 bits of the CLOCK_MONOTONIC value in milliseconds. - */ - gint64 now_monotonic = g_get_monotonic_time (); - gint64 now_monotonic_msec = now_monotonic / 1000; - uint32_t now_monotonic_low = (uint32_t)now_monotonic_msec; - - if (frame_time - now_monotonic_low < 1000 || - frame_time - now_monotonic_low > (uint32_t)-1000) - { - /* Timestamp we received is within one second of the current time. - */ - gint64 last_frame_time = now_monotonic + (gint64)1000 * (gint32)(frame_time - now_monotonic_low); - if ((gint32)now_monotonic_low < 0 && (gint32)frame_time > 0) - last_frame_time += (gint64)1000 * G_GINT64_CONSTANT(0x100000000); - else if ((gint32)now_monotonic_low > 0 && (gint32)frame_time < 0) - last_frame_time -= (gint64)1000 * G_GINT64_CONSTANT(0x100000000); - - timings->presentation_time = last_frame_time + timings->refresh_interval; - } -} - static GdkSurface * get_popup_toplevel (GdkSurface *surface) { @@ -430,15 +473,6 @@ thaw_popup_toplevel_state (GdkWaylandPopup *wayland_popup) gdk_wayland_surface_thaw_state (toplevel); } -static void -finish_pending_relayout (GdkWaylandPopup *wayland_popup) -{ - g_assert (wayland_popup->state == POPUP_STATE_WAITING_FOR_FRAME); - wayland_popup->state = POPUP_STATE_IDLE; - - thaw_popup_toplevel_state (wayland_popup); -} - static void frame_callback (void *data, struct wl_callback *callback, @@ -463,22 +497,7 @@ frame_callback (void *data, return; if (GDK_IS_WAYLAND_POPUP (surface)) - { - GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (surface); - - switch (wayland_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: - finish_pending_relayout (wayland_popup); - break; - default: - g_assert_not_reached (); - } - } + frame_callback_popup (GDK_WAYLAND_POPUP (surface)); impl->awaiting_frame = FALSE; if (impl->awaiting_frame_frozen) @@ -557,25 +576,6 @@ on_frame_clock_before_paint (GdkFrameClock *clock, gdk_surface_apply_state_change (surface); } -static void -configure_popup_geometry (GdkWaylandPopup *wayland_popup) -{ - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); - int x, y; - int width, height; - - x = wayland_popup->next_layout.x - wayland_surface->shadow_left; - y = wayland_popup->next_layout.y - wayland_surface->shadow_top; - width = - wayland_surface->next_layout.configured_width + - (wayland_surface->shadow_left + wayland_surface->shadow_right); - height = - wayland_surface->next_layout.configured_height + - (wayland_surface->shadow_top + wayland_surface->shadow_bottom); - - gdk_wayland_surface_move_resize (GDK_SURFACE (wayland_popup), x, y, width, height); -} - static void configure_drag_surface_geometry (GdkSurface *surface) { @@ -727,7 +727,6 @@ _gdk_wayland_display_create_surface (GdkDisplay *display, { GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); GdkSurface *surface; - GdkWaylandSurface *impl; GdkFrameClock *frame_clock; if (parent) @@ -738,16 +737,16 @@ _gdk_wayland_display_create_surface (GdkDisplay *display, switch (surface_type) { case GDK_SURFACE_TOPLEVEL: + g_warn_if_fail (parent == NULL); surface = g_object_new (GDK_TYPE_WAYLAND_TOPLEVEL, "display", display, "frame-clock", frame_clock, "title", get_default_title (), NULL); - display_wayland->toplevels = g_list_prepend (display_wayland->toplevels, - surface); - g_warn_if_fail (!parent); + display_wayland->toplevels = g_list_prepend (display_wayland->toplevels, surface); break; case GDK_SURFACE_POPUP: + g_warn_if_fail (parent != NULL); surface = g_object_new (GDK_TYPE_WAYLAND_POPUP, "parent", parent, "display", display, @@ -755,6 +754,7 @@ _gdk_wayland_display_create_surface (GdkDisplay *display, NULL); break; case GDK_SURFACE_DRAG: + g_warn_if_fail (parent == NULL); surface = g_object_new (GDK_TYPE_WAYLAND_DRAG_SURFACE, "display", display, "frame-clock", frame_clock, @@ -765,8 +765,6 @@ _gdk_wayland_display_create_surface (GdkDisplay *display, break; } - impl = GDK_WAYLAND_SURFACE (surface); - if (width > 65535) { g_warning ("Native Surfaces wider than 65535 pixels are not supported"); @@ -791,7 +789,7 @@ _gdk_wayland_display_create_surface (GdkDisplay *display, GdkMonitor *monitor = g_list_model_get_item (gdk_display_get_monitors (display), 0); if (monitor) { - impl->scale = gdk_monitor_get_scale_factor (monitor); + GDK_WAYLAND_SURFACE (surface)->scale = gdk_monitor_get_scale_factor (monitor); g_object_unref (monitor); } } @@ -874,8 +872,7 @@ gdk_wayland_surface_sync (GdkSurface *surface) static gboolean gdk_wayland_surface_beep (GdkSurface *surface) { - gdk_wayland_display_system_bell (gdk_surface_get_display (surface), - surface); + gdk_wayland_display_system_bell (gdk_surface_get_display (surface), surface); return TRUE; } @@ -933,41 +930,6 @@ gdk_wayland_surface_finalize (GObject *object) G_OBJECT_CLASS (gdk_wayland_surface_parent_class)->finalize (object); } -static gboolean -is_realized_shell_surface (GdkWaylandSurface *impl) -{ - return (impl->display_server.xdg_surface || - impl->display_server.zxdg_surface_v6); -} - -static gboolean -is_realized_toplevel (GdkWaylandSurface *impl) -{ - GdkWaylandToplevel *toplevel; - - if (!GDK_IS_WAYLAND_TOPLEVEL (impl)) - return FALSE; - - toplevel = GDK_WAYLAND_TOPLEVEL (impl); - - return (toplevel->display_server.xdg_toplevel || - toplevel->display_server.zxdg_toplevel_v6); -} - -static gboolean -is_realized_popup (GdkWaylandSurface *impl) -{ - GdkWaylandPopup *popup; - - if (!GDK_IS_WAYLAND_POPUP (impl)) - return FALSE; - - popup = GDK_WAYLAND_POPUP (impl); - - return (popup->display_server.xdg_popup || - popup->display_server.zxdg_popup_v6); -} - static void gdk_wayland_surface_maybe_resize (GdkSurface *surface, int width, @@ -989,7 +951,7 @@ gdk_wayland_surface_maybe_resize (GdkSurface *surface, * force the new size onto the compositor. See bug #772505. */ - is_xdg_popup = is_realized_popup (impl); + is_xdg_popup = GDK_IS_WAYLAND_POPUP (surface); is_visible = gdk_surface_get_mapped (surface); if (is_xdg_popup && is_visible && !impl->initial_configure_received) @@ -1001,132 +963,11 @@ gdk_wayland_surface_maybe_resize (GdkSurface *surface, gdk_wayland_surface_show (surface); } -static void -gdk_wayland_surface_sync_parent (GdkSurface *surface, - GdkSurface *parent) -{ - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (impl); - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - GdkWaylandSurface *impl_parent = NULL; - - g_assert (parent == NULL || - gdk_surface_get_display (surface) == gdk_surface_get_display (parent)); - - if (!is_realized_toplevel (impl)) - return; - - if (toplevel->transient_for) - impl_parent = GDK_WAYLAND_SURFACE (toplevel->transient_for); - else if (parent) - impl_parent = GDK_WAYLAND_SURFACE (parent); - - /* XXX: Is this correct? */ - if (impl_parent && !impl_parent->display_server.wl_surface) - return; - - switch (display_wayland->shell_variant) - { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - { - struct xdg_toplevel *parent_toplevel; - - if (impl_parent) - parent_toplevel = GDK_WAYLAND_TOPLEVEL (impl_parent)->display_server.xdg_toplevel; - else - parent_toplevel = NULL; - - xdg_toplevel_set_parent (GDK_WAYLAND_TOPLEVEL (impl)->display_server.xdg_toplevel, parent_toplevel); - break; - } - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - { - struct zxdg_toplevel_v6 *parent_toplevel; - - if (impl_parent) - parent_toplevel = GDK_WAYLAND_TOPLEVEL (impl_parent)->display_server.zxdg_toplevel_v6; - else - parent_toplevel = NULL; - - zxdg_toplevel_v6_set_parent (GDK_WAYLAND_TOPLEVEL (impl)->display_server.zxdg_toplevel_v6, parent_toplevel); - break; - } - default: - g_assert_not_reached (); - } -} - -static void -gdk_wayland_toplevel_sync_parent_of_imported (GdkWaylandToplevel *toplevel) -{ - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); - - if (!toplevel->imported_transient_for && !toplevel->imported_transient_for_v2) - return; - - if (!impl->display_server.wl_surface) - return; - - if (toplevel->imported_transient_for) - zxdg_imported_v1_set_parent_of (toplevel->imported_transient_for, - impl->display_server.wl_surface); - else - zxdg_imported_v2_set_parent_of (toplevel->imported_transient_for_v2, - impl->display_server.wl_surface); -} - -static void -gdk_wayland_toplevel_sync_title (GdkWaylandToplevel *toplevel) -{ - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); - - if (!is_realized_toplevel (impl)) - return; - - if (!toplevel->title) - return; - - switch (display_wayland->shell_variant) - { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_toplevel_set_title (toplevel->display_server.xdg_toplevel, toplevel->title); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_set_title (toplevel->display_server.zxdg_toplevel_v6, toplevel->title); - break; - default: - g_assert_not_reached (); - } -} - -static void -gdk_wayland_surface_get_window_geometry (GdkSurface *surface, - GdkRectangle *geometry) -{ - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - - *geometry = (GdkRectangle) { - .x = impl->shadow_left, - .y = impl->shadow_top, - .width = surface->width - (impl->shadow_left + impl->shadow_right), - .height = surface->height - (impl->shadow_top + impl->shadow_bottom) - }; -} - -static void gdk_wayland_toplevel_set_geometry_hints (GdkWaylandToplevel *toplevel, - const GdkGeometry *geometry, - GdkSurfaceHints geom_mask); - static void gdk_wayland_surface_sync_shadow (GdkSurface *surface) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); GdkRectangle geometry; if (!is_realized_shell_surface (impl)) @@ -1136,9 +977,7 @@ gdk_wayland_surface_sync_shadow (GdkSurface *surface) if (GDK_IS_WAYLAND_TOPLEVEL (impl)) { GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (impl); - gdk_wayland_toplevel_set_geometry_hints (toplevel, - &toplevel->geometry_hints, - toplevel->geometry_mask); + gdk_wayland_toplevel_set_geometry_hints (toplevel, NULL, 0); } if (gdk_rectangle_equal (&geometry, &impl->last_sent_window_geometry)) @@ -1167,28 +1006,6 @@ gdk_wayland_surface_sync_shadow (GdkSurface *surface) impl->last_sent_window_geometry = geometry; } -static struct wl_region * -wl_region_from_cairo_region (GdkWaylandDisplay *display, - cairo_region_t *region) -{ - struct wl_region *wl_region; - int i, n_rects; - - wl_region = wl_compositor_create_region (display->compositor); - if (wl_region == NULL) - return NULL; - - n_rects = cairo_region_num_rectangles (region); - for (i = 0; i < n_rects; i++) - { - cairo_rectangle_int_t rect; - cairo_region_get_rectangle (region, i, &rect); - wl_region_add (wl_region, rect.x, rect.y, rect.width, rect.height); - } - - return wl_region; -} - static void gdk_wayland_surface_sync_opaque_region (GdkSurface *surface) { @@ -1300,722 +1117,730 @@ gdk_wayland_surface_create_wl_surface (GdkSurface *surface) } static void -configure_toplevel_geometry (GdkWaylandToplevel *wayland_toplevel) +maybe_notify_mapped (GdkSurface *surface) { - GdkSurface *surface = GDK_SURFACE (wayland_toplevel); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); - GdkDisplay *display = gdk_surface_get_display (surface); - int bounds_width, bounds_height; - GdkToplevelSize size; - GdkToplevelLayout *layout; - GdkGeometry geometry; - GdkSurfaceHints mask; + if (surface->destroyed) + return; - if (wayland_toplevel->has_bounds) + if (!GDK_SURFACE_IS_MAPPED (surface)) + gdk_surface_set_is_mapped (surface, TRUE); +} + +static void +gdk_wayland_surface_configure (GdkSurface *surface) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + + if (!impl->initial_configure_received) { - bounds_width = wayland_toplevel->bounds_width; - bounds_height = wayland_toplevel->bounds_height; + gdk_surface_thaw_updates (surface); + impl->initial_configure_received = TRUE; + impl->pending.is_initial_configure = TRUE; + maybe_notify_mapped (surface); } - else - { - GdkMonitor *monitor; - GListModel *monitors; - GdkRectangle monitor_geometry, display_geometry = { 0 }; - guint i; - monitors = gdk_display_get_monitors (display); + impl->has_uncommitted_ack_configure = TRUE; - for (i = 0; i < g_list_model_get_n_items (monitors); i++) - { - monitor = g_list_model_get_item (monitors, i); - gdk_monitor_get_geometry (monitor, &monitor_geometry); - gdk_rectangle_union (&display_geometry, &monitor_geometry, &display_geometry); - g_object_unref (monitor); - } + if (GDK_IS_WAYLAND_POPUP (surface)) + gdk_wayland_surface_configure_popup (GDK_WAYLAND_POPUP (surface)); + else if (GDK_IS_WAYLAND_TOPLEVEL (surface)) + gdk_wayland_surface_configure_toplevel (GDK_WAYLAND_TOPLEVEL (surface)); + else + g_warn_if_reached (); - bounds_width = display_geometry.width; - bounds_height = display_geometry.height; - } + impl->last_configure_serial = impl->pending.serial; - gdk_toplevel_size_init (&size, bounds_width, bounds_height); - gdk_toplevel_notify_compute_size (GDK_TOPLEVEL (surface), &size); - g_warn_if_fail (size.width > 0); - g_warn_if_fail (size.height > 0); + memset (&impl->pending, 0, sizeof (impl->pending)); +} - layout = wayland_toplevel->layout; - if (gdk_toplevel_layout_get_resizable (layout)) - { - geometry.min_width = size.min_width; - geometry.min_height = size.min_height; - mask = GDK_HINT_MIN_SIZE; - } - else - { - geometry.max_width = geometry.min_width = size.width; - geometry.max_height = geometry.min_height = size.height; - mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE; - } +static void +gdk_wayland_surface_handle_configure (GdkWaylandSurface *impl, + uint32_t serial) +{ + impl->pending.is_dirty = TRUE; + impl->pending.serial = serial; - gdk_wayland_toplevel_set_geometry_hints (wayland_toplevel, &geometry, mask); + if (impl->state_freeze_count > 0) + return; - if (size.shadow.is_valid) - { - wayland_surface->shadow_left = size.shadow.left; - wayland_surface->shadow_right = size.shadow.right; - wayland_surface->shadow_top = size.shadow.top; - wayland_surface->shadow_bottom = size.shadow.bottom; - } + gdk_wayland_surface_configure (GDK_SURFACE (impl)); +} - if (wayland_surface->next_layout.configured_width > 0 && - wayland_surface->next_layout.configured_height > 0) - { - int width, height; +static void +gdk_wayland_surface_handle_close (GdkSurface *surface) +{ + GdkDisplay *display; + GdkEvent *event; - width = wayland_surface->next_layout.configured_width + - wayland_surface->shadow_left + wayland_surface->shadow_right; - height = wayland_surface->next_layout.configured_height + - wayland_surface->shadow_top + wayland_surface->shadow_bottom; + display = gdk_surface_get_display (surface); - if (wayland_toplevel->next_layout.should_constrain) - { - gdk_surface_constrain_size (&wayland_toplevel->geometry_hints, - wayland_toplevel->geometry_mask, - width, height, - &width, &height); - } - gdk_wayland_surface_update_size (surface, width, height, wayland_surface->scale); + GDK_DISPLAY_DEBUG (display, EVENTS, "close %p", surface); - if (!wayland_toplevel->next_layout.size_is_fixed) - { - wayland_toplevel->next_layout.should_constrain = FALSE; - wayland_surface->next_layout.configured_width = 0; - wayland_surface->next_layout.configured_height = 0; - } - } - else - { - int width, height; + event = gdk_delete_event_new (surface); - width = size.width; - height = size.height; - gdk_surface_constrain_size (&geometry, mask, - width, height, - &width, &height); - gdk_wayland_surface_update_size (surface, width, height, wayland_surface->scale); - } + _gdk_wayland_display_deliver_event (display, event); } static void -synthesize_initial_surface_state (GdkWaylandToplevel *wayland_toplevel, - GdkToplevelState unset_flags, - GdkToplevelState set_flags) +xdg_surface_configure (void *data, + struct xdg_surface *xdg_surface, + uint32_t serial) { - wayland_toplevel->initial_state.unset_flags |= unset_flags; - wayland_toplevel->initial_state.set_flags &= ~unset_flags; - - wayland_toplevel->initial_state.set_flags |= set_flags; - wayland_toplevel->initial_state.unset_flags &= ~set_flags; + gdk_wayland_surface_handle_configure (GDK_WAYLAND_SURFACE (data), serial); } +static const struct xdg_surface_listener xdg_surface_listener = { + xdg_surface_configure, +}; + static void -gdk_wayland_surface_configure_toplevel (GdkWaylandToplevel *wayland_toplevel) +zxdg_surface_v6_configure (void *data, + struct zxdg_surface_v6 *xdg_surface, + uint32_t serial) { - GdkSurface *surface = GDK_SURFACE (wayland_toplevel); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - GdkToplevelState new_state; - int width, height; - gboolean is_resizing; - gboolean fixed_size; - gboolean was_fixed_size; - gboolean saved_size; + gdk_wayland_surface_handle_configure (GDK_WAYLAND_SURFACE (data), serial); +} - new_state = wayland_toplevel->pending.state; - wayland_toplevel->pending.state = 0; +static const struct zxdg_surface_v6_listener zxdg_surface_v6_listener = { + zxdg_surface_v6_configure, +}; - is_resizing = wayland_toplevel->pending.is_resizing; - wayland_toplevel->pending.is_resizing = FALSE; +static void +gdk_wayland_surface_create_xdg_surface_resources (GdkSurface *surface) +{ + GdkWaylandDisplay *display = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - if (wayland_toplevel->pending.has_bounds) + switch (display->shell_variant) { - wayland_toplevel->bounds_width = wayland_toplevel->pending.bounds_width; - wayland_toplevel->bounds_height = wayland_toplevel->pending.bounds_height; - wayland_toplevel->has_bounds = TRUE; + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + impl->display_server.xdg_surface = + xdg_wm_base_get_xdg_surface (display->xdg_wm_base, + impl->display_server.wl_surface); + wl_proxy_set_queue ((struct wl_proxy *) impl->display_server.xdg_surface, + impl->event_queue); + xdg_surface_add_listener (impl->display_server.xdg_surface, + &xdg_surface_listener, + surface); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + impl->display_server.zxdg_surface_v6 = + zxdg_shell_v6_get_xdg_surface (display->zxdg_shell_v6, + impl->display_server.wl_surface); + zxdg_surface_v6_add_listener (impl->display_server.zxdg_surface_v6, + &zxdg_surface_v6_listener, + surface); + break; + default: + g_assert_not_reached (); } +} - fixed_size = - new_state & (GDK_TOPLEVEL_STATE_MAXIMIZED | - GDK_TOPLEVEL_STATE_FULLSCREEN | - GDK_TOPLEVEL_STATE_TILED) || - is_resizing; - - was_fixed_size = - surface->state & (GDK_TOPLEVEL_STATE_MAXIMIZED | - GDK_TOPLEVEL_STATE_FULLSCREEN | - GDK_TOPLEVEL_STATE_TILED); +static void +gdk_wayland_surface_map_toplevel (GdkSurface *surface) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - width = wayland_toplevel->pending.width; - height = wayland_toplevel->pending.height; + if (!GDK_IS_WAYLAND_TOPLEVEL (surface)) + return; - saved_size = (width == 0 && height == 0); - /* According to xdg_shell, an xdg_surface.configure with size 0x0 - * should be interpreted as that it is up to the client to set a - * size. - * - * When transitioning from maximize or fullscreen state, this means - * the client should configure its size back to what it was before - * being maximize or fullscreen. - */ - if (saved_size && !fixed_size && was_fixed_size) - { - width = wayland_surface->saved_width; - height = wayland_surface->saved_height; - } + if (impl->mapped) + return; - if (width > 0 && height > 0) - { - if (!saved_size) - { - wayland_toplevel->next_layout.should_constrain = TRUE; + gdk_wayland_surface_create_xdg_toplevel (GDK_WAYLAND_TOPLEVEL (surface)); - /* Save size for next time we get 0x0 */ - _gdk_wayland_surface_save_size (surface); - } - else if (is_resizing) - { - wayland_toplevel->next_layout.should_constrain = TRUE; - } - else - { - wayland_toplevel->next_layout.should_constrain = FALSE; - } + impl->mapped = TRUE; +} - wayland_toplevel->next_layout.size_is_fixed = fixed_size; - wayland_surface->next_layout.configured_width = width; - wayland_surface->next_layout.configured_height = height; - } - else - { - wayland_toplevel->next_layout.should_constrain = FALSE; - wayland_toplevel->next_layout.size_is_fixed = FALSE; - wayland_surface->next_layout.configured_width = 0; - wayland_surface->next_layout.configured_height = 0; - } +static void +gdk_wayland_surface_show (GdkSurface *surface) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - wayland_surface->next_layout.surface_geometry_dirty = TRUE; - gdk_surface_request_layout (surface); + if (!impl->display_server.wl_surface) + gdk_wayland_surface_create_wl_surface (surface); - GDK_DISPLAY_DEBUG (gdk_surface_get_display (surface), EVENTS, - "configure, surface %p %dx%d,%s%s%s%s", - surface, width, height, - (new_state & GDK_TOPLEVEL_STATE_FULLSCREEN) ? " fullscreen" : "", - (new_state & GDK_TOPLEVEL_STATE_MAXIMIZED) ? " maximized" : "", - (new_state & GDK_TOPLEVEL_STATE_FOCUSED) ? " focused" : "", - (new_state & GDK_TOPLEVEL_STATE_TILED) ? " tiled" : ""); + gdk_wayland_surface_map_toplevel (surface); +} - gdk_surface_queue_state_change (surface, ~0 & ~new_state, new_state); +static void +unmap_popups_for_surface (GdkSurface *surface) +{ + GdkWaylandDisplay *display_wayland; + GList *l; - switch (display_wayland->shell_variant) + display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + for (l = display_wayland->current_popups; l; l = l->next) { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_surface_ack_configure (wayland_surface->display_server.xdg_surface, - wayland_surface->pending.serial); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_surface_v6_ack_configure (wayland_surface->display_server.zxdg_surface_v6, - wayland_surface->pending.serial); - break; - default: - g_assert_not_reached (); + GdkSurface *popup = l->data; + + if (popup->parent == surface) + { + g_warning ("Tried to unmap the parent of a popup"); + gdk_surface_hide (popup); + + return; + } } } static void -gdk_wayland_surface_configure_popup (GdkWaylandPopup *wayland_popup) +gdk_wayland_surface_hide_surface (GdkSurface *surface) { - GdkSurface *surface = GDK_SURFACE (wayland_popup); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); - GdkRectangle parent_geometry; - int x, y, width, height; + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - if (wayland_popup->display_server.xdg_popup) - { - xdg_surface_ack_configure (wayland_surface->display_server.xdg_surface, - wayland_surface->pending.serial); - } - else if (wayland_popup->display_server.zxdg_popup_v6) + unmap_popups_for_surface (surface); + + if (impl->display_server.wl_surface) { - zxdg_surface_v6_ack_configure (wayland_surface->display_server.zxdg_surface_v6, - wayland_surface->pending.serial); - } + if (impl->display_server.egl_window) + { + gdk_surface_set_egl_native_window (surface, NULL); + wl_egl_window_destroy (impl->display_server.egl_window); + impl->display_server.egl_window = NULL; + } - if (wayland_popup->pending.has_repositioned_token) - wayland_popup->received_reposition_token = wayland_popup->pending.repositioned_token; + if (impl->display_server.xdg_surface) + { + xdg_surface_destroy (impl->display_server.xdg_surface); + impl->display_server.xdg_surface = NULL; + if (!impl->initial_configure_received) + gdk_surface_thaw_updates (surface); + else + impl->initial_configure_received = FALSE; + } + if (impl->display_server.zxdg_surface_v6) + { + g_clear_pointer (&impl->display_server.zxdg_surface_v6, zxdg_surface_v6_destroy); + if (!impl->initial_configure_received) + gdk_surface_thaw_updates (surface); + else + impl->initial_configure_received = FALSE; + } - switch (wayland_popup->state) - { - case POPUP_STATE_WAITING_FOR_REPOSITIONED: - if (wayland_popup->received_reposition_token != wayland_popup->reposition_token) - return; - else - gdk_surface_thaw_updates (surface); - G_GNUC_FALLTHROUGH; - case POPUP_STATE_WAITING_FOR_CONFIGURE: - wayland_popup->state = POPUP_STATE_WAITING_FOR_FRAME; - break; - case POPUP_STATE_IDLE: - case POPUP_STATE_WAITING_FOR_FRAME: - break; - default: - g_assert_not_reached (); + impl->awaiting_frame = FALSE; + if (impl->awaiting_frame_frozen) + { + impl->awaiting_frame_frozen = FALSE; + gdk_surface_thaw_updates (surface); + } + + if (GDK_IS_WAYLAND_TOPLEVEL (surface)) + gdk_wayland_toplevel_hide_surface (GDK_WAYLAND_TOPLEVEL (surface)); + + if (GDK_IS_WAYLAND_POPUP (surface)) + gdk_wayland_popup_hide_surface (GDK_WAYLAND_POPUP (surface)); + + g_clear_pointer (&impl->display_server.wl_surface, wl_surface_destroy); + + g_slist_free (impl->display_server.outputs); + impl->display_server.outputs = NULL; } - x = wayland_popup->pending.x; - y = wayland_popup->pending.y; - width = wayland_popup->pending.width; - height = wayland_popup->pending.height; + impl->has_uncommitted_ack_configure = FALSE; + impl->input_region_dirty = TRUE; + impl->opaque_region_dirty = TRUE; - gdk_wayland_surface_get_window_geometry (surface->parent, &parent_geometry); - x += parent_geometry.x; - y += parent_geometry.y; + unset_transient_for_exported (surface); - update_popup_layout_state (wayland_popup, - x, y, - width, height, - wayland_popup->layout); + impl->last_sent_window_geometry = (GdkRectangle) { 0 }; - wayland_popup->next_layout.x = x; - wayland_popup->next_layout.y = y; - wayland_surface->next_layout.configured_width = width; - wayland_surface->next_layout.configured_height = height; - wayland_surface->next_layout.surface_geometry_dirty = TRUE; - gdk_surface_request_layout (surface); + _gdk_wayland_surface_clear_saved_size (surface); + impl->mapped = FALSE; } static void -maybe_notify_mapped (GdkSurface *surface) +gdk_wayland_surface_hide (GdkSurface *surface) { - if (surface->destroyed) - return; + GdkSeat *seat; - if (!GDK_SURFACE_IS_MAPPED (surface)) - gdk_surface_set_is_mapped (surface, TRUE); + seat = gdk_display_get_default_seat (surface->display); + if (seat) + { + if (surface->autohide) + gdk_seat_ungrab (seat); + + gdk_wayland_seat_clear_touchpoints (GDK_WAYLAND_SEAT (seat), surface); + } + gdk_wayland_surface_hide_surface (surface); + _gdk_surface_clear_update_area (surface); } static void -gdk_wayland_surface_configure (GdkSurface *surface) +gdk_wayland_surface_move_resize (GdkSurface *surface, + int x, + int y, + int width, + int height) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - if (!impl->initial_configure_received) - { - gdk_surface_thaw_updates (surface); - impl->initial_configure_received = TRUE; - impl->pending.is_initial_configure = TRUE; - maybe_notify_mapped (surface); - } - - impl->has_uncommitted_ack_configure = TRUE; + surface->x = x; + surface->y = y; + gdk_wayland_surface_maybe_resize (surface, width, height, impl->scale); +} - if (is_realized_popup (impl)) - { - g_assert (GDK_IS_WAYLAND_POPUP (surface)); - GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (surface); - gdk_wayland_surface_configure_popup (wayland_popup); - } - else if (is_realized_toplevel (impl)) +static void +gdk_wayland_surface_get_geometry (GdkSurface *surface, + int *x, + int *y, + int *width, + int *height) +{ + if (!GDK_SURFACE_DESTROYED (surface)) { - g_assert (GDK_IS_WAYLAND_TOPLEVEL (surface)); - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (surface); - gdk_wayland_surface_configure_toplevel (wayland_toplevel); + if (x) + *x = surface->x; + if (y) + *y = surface->y; + if (width) + *width = surface->width; + if (height) + *height = surface->height; } - else - g_warn_if_reached (); - - impl->last_configure_serial = impl->pending.serial; - - memset (&impl->pending, 0, sizeof (impl->pending)); } static void -gdk_wayland_surface_handle_configure (GdkSurface *surface, - uint32_t serial) +gdk_wayland_surface_get_root_coords (GdkSurface *surface, + int x, + int y, + int *root_x, + int *root_y) { - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - - impl->pending.is_dirty = TRUE; - impl->pending.serial = serial; + /* + * Wayland does not have a global coordinate space shared between surfaces. In + * fact, for regular toplevels, we have no idea where our surfaces are + * positioned, relatively. + * + * However, there are some cases like popups and subsurfaces where we do have + * some amount of control over the placement of our surface, and we can + * semi-accurately control the x/y position of these surfaces, if they are + * relative to another surface. + * + * To pretend we have something called a root coordinate space, assume all + * parent-less surfaces are positioned in (0, 0), and all relative positioned + * popups and subsurfaces are placed within this fake root coordinate space. + * + * For example a 200x200 large toplevel surface will have the position (0, 0). + * If a popup positioned in the middle of the toplevel will have the fake + * position (100,100). Furthermore, if a positioned is placed in the middle + * that popup, will have the fake position (150,150), even though it has the + * relative position (50,50). These three surfaces would make up one single + * fake root coordinate space. + */ - if (impl->state_freeze_count > 0) - return; + if (root_x) + *root_x = surface->x + x; - gdk_wayland_surface_configure (surface); + if (root_y) + *root_y = surface->y + y; } -static void -gdk_wayland_surface_handle_configure_toplevel (GdkSurface *surface, - int32_t width, - int32_t height, - GdkToplevelState state) +static gboolean +gdk_wayland_surface_get_device_state (GdkSurface *surface, + GdkDevice *device, + double *x, + double *y, + GdkModifierType *mask) { - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); + if (GDK_SURFACE_DESTROYED (surface)) + return FALSE; - toplevel->pending.state |= state; - toplevel->pending.width = width; - toplevel->pending.height = height; + gdk_wayland_device_query_state (device, surface, x, y, mask); + + return *x >= 0 && *y >= 0 && *x < surface->width && *y < surface->height; } static void -gdk_wayland_surface_handle_close (GdkSurface *surface) +gdk_wayland_surface_set_input_region (GdkSurface *surface, + cairo_region_t *input_region) { - GdkDisplay *display; - GdkEvent *event; + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - display = gdk_surface_get_display (surface); + if (GDK_SURFACE_DESTROYED (surface)) + return; - GDK_DISPLAY_DEBUG (display, EVENTS, "close %p", surface); + g_clear_pointer (&impl->input_region, cairo_region_destroy); - event = gdk_delete_event_new (surface); + if (input_region) + impl->input_region = cairo_region_copy (input_region); - _gdk_wayland_display_deliver_event (display, event); + impl->input_region_dirty = TRUE; } static void -xdg_surface_configure (void *data, - struct xdg_surface *xdg_surface, - uint32_t serial) +gdk_wayland_surface_destroy (GdkSurface *surface, + gboolean foreign_destroy) { - GdkSurface *surface = GDK_SURFACE (data); + GdkWaylandDisplay *display; + GdkFrameClock *frame_clock; - gdk_wayland_surface_handle_configure (surface, serial); -} + g_return_if_fail (GDK_IS_SURFACE (surface)); -static const struct xdg_surface_listener xdg_surface_listener = { - xdg_surface_configure, -}; + /* Wayland surfaces can't be externally destroyed; we may possibly + * eventually want to use this path at display close-down + */ + g_return_if_fail (!foreign_destroy); -static void -zxdg_surface_v6_configure (void *data, - struct zxdg_surface_v6 *xdg_surface, - uint32_t serial) -{ - GdkSurface *surface = GDK_SURFACE (data); + gdk_wayland_surface_hide_surface (surface); - gdk_wayland_surface_handle_configure (surface, serial); -} + frame_clock = gdk_surface_get_frame_clock (surface); + g_signal_handlers_disconnect_by_func (frame_clock, on_frame_clock_before_paint, surface); + g_signal_handlers_disconnect_by_func (frame_clock, on_frame_clock_after_paint, surface); -static const struct zxdg_surface_v6_listener zxdg_surface_v6_listener = { - zxdg_surface_v6_configure, -}; + display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + display->toplevels = g_list_remove (display->toplevels, surface); +} static void -gdk_wayland_surface_create_xdg_surface_resources (GdkSurface *surface) +gdk_wayland_surface_destroy_notify (GdkSurface *surface) { - GdkWaylandDisplay *display = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - - switch (display->shell_variant) + if (!GDK_SURFACE_DESTROYED (surface)) { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - impl->display_server.xdg_surface = - xdg_wm_base_get_xdg_surface (display->xdg_wm_base, - impl->display_server.wl_surface); - wl_proxy_set_queue ((struct wl_proxy *) impl->display_server.xdg_surface, - impl->event_queue); - xdg_surface_add_listener (impl->display_server.xdg_surface, - &xdg_surface_listener, - surface); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - impl->display_server.zxdg_surface_v6 = - zxdg_shell_v6_get_xdg_surface (display->zxdg_shell_v6, - impl->display_server.wl_surface); - zxdg_surface_v6_add_listener (impl->display_server.zxdg_surface_v6, - &zxdg_surface_v6_listener, - surface); - break; - default: - g_assert_not_reached (); + g_warning ("GdkSurface %p unexpectedly destroyed", surface); + _gdk_surface_destroy (surface, TRUE); } + + g_object_unref (surface); } -static void -xdg_toplevel_configure (void *data, - struct xdg_toplevel *xdg_toplevel, - int32_t width, - int32_t height, - struct wl_array *states) +static int +gdk_wayland_surface_get_scale_factor (GdkSurface *surface) { - GdkSurface *surface = GDK_SURFACE (data); - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - uint32_t *p; - GdkToplevelState pending_state = 0; - - toplevel->pending.is_resizing = FALSE; - - wl_array_for_each (p, states) - { - uint32_t state = *p; + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - switch (state) - { - case XDG_TOPLEVEL_STATE_FULLSCREEN: - pending_state |= GDK_TOPLEVEL_STATE_FULLSCREEN; - break; - case XDG_TOPLEVEL_STATE_MAXIMIZED: - pending_state |= GDK_TOPLEVEL_STATE_MAXIMIZED; - break; - case XDG_TOPLEVEL_STATE_ACTIVATED: - pending_state |= GDK_TOPLEVEL_STATE_FOCUSED; - break; - case XDG_TOPLEVEL_STATE_RESIZING: - toplevel->pending.is_resizing = TRUE; - break; - case XDG_TOPLEVEL_STATE_TILED_TOP: - pending_state |= (GDK_TOPLEVEL_STATE_TILED | - GDK_TOPLEVEL_STATE_TOP_TILED); - break; - case XDG_TOPLEVEL_STATE_TILED_RIGHT: - pending_state |= (GDK_TOPLEVEL_STATE_TILED | - GDK_TOPLEVEL_STATE_RIGHT_TILED); - break; - case XDG_TOPLEVEL_STATE_TILED_BOTTOM: - pending_state |= (GDK_TOPLEVEL_STATE_TILED | - GDK_TOPLEVEL_STATE_BOTTOM_TILED); - break; - case XDG_TOPLEVEL_STATE_TILED_LEFT: - pending_state |= (GDK_TOPLEVEL_STATE_TILED | - GDK_TOPLEVEL_STATE_LEFT_TILED); - break; - default: - /* Unknown state */ - break; - } - } + if (GDK_SURFACE_DESTROYED (surface)) + return 1; - gdk_wayland_surface_handle_configure_toplevel (surface, width, height, pending_state); + return impl->scale; } static void -xdg_toplevel_close (void *data, - struct xdg_toplevel *xdg_toplevel) +gdk_wayland_surface_set_opaque_region (GdkSurface *surface, + cairo_region_t *region) { - GdkSurface *surface = GDK_SURFACE (data); + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - gdk_wayland_surface_handle_close (surface); + if (GDK_SURFACE_DESTROYED (surface)) + return; + + g_clear_pointer (&impl->opaque_region, cairo_region_destroy); + impl->opaque_region = cairo_region_reference (region); + impl->opaque_region_dirty = TRUE; } static void -xdg_toplevel_configure_bounds (void *data, - struct xdg_toplevel *xdg_toplevel, - int32_t width, - int32_t height) +gdk_wayland_surface_class_init (GdkWaylandSurfaceClass *klass) { - GdkSurface *surface = GDK_SURFACE (data); - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkSurfaceClass *impl_class = GDK_SURFACE_CLASS (klass); - toplevel->pending.bounds_width = width; - toplevel->pending.bounds_height = height; - toplevel->pending.has_bounds = TRUE; + object_class->constructed = gdk_wayland_surface_constructed; + object_class->dispose = gdk_wayland_surface_dispose; + object_class->finalize = gdk_wayland_surface_finalize; + + impl_class->hide = gdk_wayland_surface_hide; + impl_class->get_geometry = gdk_wayland_surface_get_geometry; + impl_class->get_root_coords = gdk_wayland_surface_get_root_coords; + impl_class->get_device_state = gdk_wayland_surface_get_device_state; + impl_class->set_input_region = gdk_wayland_surface_set_input_region; + impl_class->destroy = gdk_wayland_surface_destroy; + impl_class->beep = gdk_wayland_surface_beep; + + impl_class->destroy_notify = gdk_wayland_surface_destroy_notify; + impl_class->drag_begin = _gdk_wayland_surface_drag_begin; + impl_class->get_scale_factor = gdk_wayland_surface_get_scale_factor; + impl_class->set_opaque_region = gdk_wayland_surface_set_opaque_region; + impl_class->request_layout = gdk_wayland_surface_request_layout; + impl_class->compute_size = gdk_wayland_surface_compute_size; } -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - xdg_toplevel_configure, - xdg_toplevel_close, - xdg_toplevel_configure_bounds, -}; +/* }}} */ +/* {{{ Private Surface API */ -static void -create_xdg_toplevel_resources (GdkWaylandToplevel *toplevel) +struct wl_output * +gdk_wayland_surface_get_wl_output (GdkSurface *surface) { - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); + GdkWaylandSurface *impl; - toplevel->display_server.xdg_toplevel = - xdg_surface_get_toplevel (impl->display_server.xdg_surface); - xdg_toplevel_add_listener (toplevel->display_server.xdg_toplevel, - &xdg_toplevel_listener, - toplevel); + g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (surface), NULL); + + impl = GDK_WAYLAND_SURFACE (surface); + /* We pick the head of the list as this is the last entered output */ + if (impl->display_server.outputs) + return (struct wl_output *) impl->display_server.outputs->data; + + return NULL; } -static void -zxdg_toplevel_v6_configure (void *data, - struct zxdg_toplevel_v6 *xdg_toplevel, - int32_t width, - int32_t height, - struct wl_array *states) +void +_gdk_wayland_surface_offset_next_wl_buffer (GdkSurface *surface, + int x, + int y) { - GdkSurface *surface = GDK_SURFACE (data); - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - uint32_t *p; - GdkToplevelState pending_state = 0; - - toplevel->pending.is_resizing = FALSE; + GdkWaylandSurface *impl; - wl_array_for_each (p, states) - { - uint32_t state = *p; + g_return_if_fail (GDK_IS_WAYLAND_SURFACE (surface)); - switch (state) - { - case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN: - pending_state |= GDK_TOPLEVEL_STATE_FULLSCREEN; - break; - case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED: - pending_state |= GDK_TOPLEVEL_STATE_MAXIMIZED; - break; - case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED: - pending_state |= GDK_TOPLEVEL_STATE_FOCUSED; - break; - case ZXDG_TOPLEVEL_V6_STATE_RESIZING: - toplevel->pending.is_resizing = TRUE; - break; - default: - /* Unknown state */ - break; - } - } + impl = GDK_WAYLAND_SURFACE (surface); - gdk_wayland_surface_handle_configure_toplevel (surface, width, height, pending_state); + impl->pending_buffer_offset_x = x; + impl->pending_buffer_offset_y = y; } -static void -zxdg_toplevel_v6_close (void *data, - struct zxdg_toplevel_v6 *xdg_toplevel) +void +gdk_wayland_surface_ensure_wl_egl_window (GdkSurface *surface) { - GdkSurface *surface = GDK_SURFACE (data); + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - gdk_wayland_surface_handle_close (surface); + if (impl->display_server.egl_window == NULL) + { + impl->display_server.egl_window = + wl_egl_window_create (impl->display_server.wl_surface, + surface->width * impl->scale, + surface->height * impl->scale); + wl_surface_set_buffer_scale (impl->display_server.wl_surface, impl->scale); + + gdk_surface_set_egl_native_window (surface, impl->display_server.egl_window); + } } -static const struct zxdg_toplevel_v6_listener zxdg_toplevel_v6_listener = { - zxdg_toplevel_v6_configure, - zxdg_toplevel_v6_close, -}; +/* }}} */ +/* {{{ Surface API */ -static void -create_zxdg_toplevel_v6_resources (GdkWaylandToplevel *toplevel) +/** + * gdk_wayland_surface_get_wl_surface: (skip) + * @surface: (type GdkWaylandSurface): a `GdkSurface` + * + * Returns the Wayland `wl_surface` of a `GdkSurface`. + * + * Returns: (transfer none): a Wayland `wl_surface` + */ +struct wl_surface * +gdk_wayland_surface_get_wl_surface (GdkSurface *surface) { - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); + g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (surface), NULL); - toplevel->display_server.zxdg_toplevel_v6 = - zxdg_surface_v6_get_toplevel (impl->display_server.zxdg_surface_v6); - zxdg_toplevel_v6_add_listener (toplevel->display_server.zxdg_toplevel_v6, - &zxdg_toplevel_v6_listener, - toplevel); + return GDK_WAYLAND_SURFACE (surface)->display_server.wl_surface; } +/* }}}} */ +/* {{{ GdkWaylandPopup definition */ + /** - * gdk_wayland_toplevel_set_application_id: - * @toplevel: (type GdkWaylandToplevel): a `GdkToplevel` - * @application_id: the application id for the @toplevel + * GdkWaylandPopup: * - * Sets the application id on a `GdkToplevel`. + * The Wayland implementation of `GdkPopup`. */ -void -gdk_wayland_toplevel_set_application_id (GdkToplevel *toplevel, - const char *application_id) + +struct _GdkWaylandPopup { - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - GdkWaylandSurface *impl; - GdkWaylandDisplay *display_wayland; + GdkWaylandSurface parent_instance; - g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); + struct { + struct xdg_popup *xdg_popup; + struct zxdg_popup_v6 *zxdg_popup_v6; + } display_server; - g_return_if_fail (application_id != NULL); + PopupState state; + unsigned int thaw_upon_show : 1; + GdkPopupLayout *layout; + int unconstrained_width; + int unconstrained_height; - if (GDK_SURFACE_DESTROYED (toplevel)) - return; + struct { + int x; + int y; + int width; + int height; + uint32_t repositioned_token; + gboolean has_repositioned_token; + } pending; - impl = GDK_WAYLAND_SURFACE (toplevel); + struct { + int x; + int y; + } next_layout; - if (!is_realized_toplevel (impl)) - return; + uint32_t reposition_token; + uint32_t received_reposition_token; - wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); + GdkSeat *grab_input_seat; +}; - switch (display_wayland->shell_variant) +typedef struct +{ + GdkWaylandSurfaceClass parent_class; +} GdkWaylandPopupClass; + +static void gdk_wayland_popup_iface_init (GdkPopupInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (GdkWaylandPopup, gdk_wayland_popup, GDK_TYPE_WAYLAND_SURFACE, + G_IMPLEMENT_INTERFACE (GDK_TYPE_POPUP, + gdk_wayland_popup_iface_init)) + +/* }}} */ +/* {{{ Popup implementation */ + +static void +gdk_wayland_popup_hide_surface (GdkWaylandPopup *popup) +{ + GdkSurface *surface = GDK_SURFACE (popup); + GdkDisplay *display = gdk_surface_get_display (surface); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); + + g_clear_pointer (&popup->display_server.xdg_popup, xdg_popup_destroy); + g_clear_pointer (&popup->display_server.zxdg_popup_v6, zxdg_popup_v6_destroy); + display_wayland->current_popups = + g_list_remove (display_wayland->current_popups, surface); + display_wayland->current_grabbing_popups = + g_list_remove (display_wayland->current_grabbing_popups, surface); + + popup->thaw_upon_show = TRUE; + gdk_surface_freeze_updates (surface); + + switch (popup->state) { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_toplevel_set_app_id (wayland_toplevel->display_server.xdg_toplevel, application_id); + 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 (popup); break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_set_app_id (wayland_toplevel->display_server.zxdg_toplevel_v6, application_id); + case POPUP_STATE_IDLE: break; default: g_assert_not_reached (); } + + popup->state = POPUP_STATE_IDLE; + + g_clear_pointer (&popup->layout, gdk_popup_layout_unref); +} + +static gboolean +is_realized_popup (GdkWaylandSurface *impl) +{ + GdkWaylandPopup *popup; + + if (!GDK_IS_WAYLAND_POPUP (impl)) + return FALSE; + + popup = GDK_WAYLAND_POPUP (impl); + + return (popup->display_server.xdg_popup || + popup->display_server.zxdg_popup_v6); } static void -gdk_wayland_surface_create_xdg_toplevel (GdkWaylandToplevel *wayland_toplevel) +finish_pending_relayout (GdkWaylandPopup *wayland_popup) { - GdkSurface *surface = GDK_SURFACE (wayland_toplevel); - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); - const char *app_id; + g_assert (wayland_popup->state == POPUP_STATE_WAITING_FOR_FRAME); + wayland_popup->state = POPUP_STATE_IDLE; - gdk_surface_freeze_updates (surface); - gdk_wayland_surface_create_xdg_surface_resources (surface); + thaw_popup_toplevel_state (wayland_popup); +} - switch (display_wayland->shell_variant) +static void +frame_callback_popup (GdkWaylandPopup *wayland_popup) +{ + switch (wayland_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: + finish_pending_relayout (wayland_popup); + break; + default: + g_assert_not_reached (); + } +} + +static void +configure_popup_geometry (GdkWaylandPopup *wayland_popup) +{ + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); + int x, y; + int width, height; + + x = wayland_popup->next_layout.x - wayland_surface->shadow_left; + y = wayland_popup->next_layout.y - wayland_surface->shadow_top; + width = + wayland_surface->next_layout.configured_width + + (wayland_surface->shadow_left + wayland_surface->shadow_right); + height = + wayland_surface->next_layout.configured_height + + (wayland_surface->shadow_top + wayland_surface->shadow_bottom); + + gdk_wayland_surface_move_resize (GDK_SURFACE (wayland_popup), x, y, width, height); +} + +static void +gdk_wayland_surface_configure_popup (GdkWaylandPopup *wayland_popup) +{ + GdkSurface *surface = GDK_SURFACE (wayland_popup); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); + GdkRectangle parent_geometry; + int x, y, width, height; + + if (wayland_popup->display_server.xdg_popup) { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - create_xdg_toplevel_resources (wayland_toplevel); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - create_zxdg_toplevel_v6_resources (wayland_toplevel); - break; - default: - g_assert_not_reached (); + xdg_surface_ack_configure (wayland_surface->display_server.xdg_surface, + wayland_surface->pending.serial); + } + else if (wayland_popup->display_server.zxdg_popup_v6) + { + zxdg_surface_v6_ack_configure (wayland_surface->display_server.zxdg_surface_v6, + wayland_surface->pending.serial); } + else + g_warn_if_reached (); - gdk_wayland_surface_sync_parent (surface, NULL); - gdk_wayland_toplevel_sync_parent_of_imported (wayland_toplevel); - gdk_wayland_toplevel_sync_title (wayland_toplevel); + if (wayland_popup->pending.has_repositioned_token) + wayland_popup->received_reposition_token = wayland_popup->pending.repositioned_token; - switch (display_wayland->shell_variant) + switch (wayland_popup->state) { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - if (wayland_toplevel->initial_state.set_flags & GDK_TOPLEVEL_STATE_MAXIMIZED) - xdg_toplevel_set_maximized (wayland_toplevel->display_server.xdg_toplevel); - if (wayland_toplevel->initial_state.set_flags & GDK_TOPLEVEL_STATE_MINIMIZED) - xdg_toplevel_set_minimized (wayland_toplevel->display_server.xdg_toplevel); - if (wayland_toplevel->initial_state.set_flags & GDK_TOPLEVEL_STATE_FULLSCREEN) - xdg_toplevel_set_fullscreen (wayland_toplevel->display_server.xdg_toplevel, - wayland_toplevel->initial_fullscreen_output); + case POPUP_STATE_WAITING_FOR_REPOSITIONED: + if (wayland_popup->received_reposition_token != wayland_popup->reposition_token) + return; + else + gdk_surface_thaw_updates (surface); + G_GNUC_FALLTHROUGH; + case POPUP_STATE_WAITING_FOR_CONFIGURE: + wayland_popup->state = POPUP_STATE_WAITING_FOR_FRAME; break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - if (wayland_toplevel->initial_state.set_flags & GDK_TOPLEVEL_STATE_MAXIMIZED) - zxdg_toplevel_v6_set_maximized (wayland_toplevel->display_server.zxdg_toplevel_v6); - if (wayland_toplevel->initial_state.set_flags & GDK_TOPLEVEL_STATE_MINIMIZED) - zxdg_toplevel_v6_set_minimized (wayland_toplevel->display_server.zxdg_toplevel_v6); - if (wayland_toplevel->initial_state.set_flags & GDK_TOPLEVEL_STATE_FULLSCREEN) - zxdg_toplevel_v6_set_fullscreen (wayland_toplevel->display_server.zxdg_toplevel_v6, - wayland_toplevel->initial_fullscreen_output); + case POPUP_STATE_IDLE: + case POPUP_STATE_WAITING_FOR_FRAME: break; default: g_assert_not_reached (); } - wayland_toplevel->initial_fullscreen_output = NULL; - - app_id = wayland_toplevel->application.application_id; - if (app_id == NULL) - app_id = g_get_prgname (); - - if (app_id == NULL) - app_id = "GTK Application"; + x = wayland_popup->pending.x; + y = wayland_popup->pending.y; + width = wayland_popup->pending.width; + height = wayland_popup->pending.height; - gdk_wayland_toplevel_set_application_id (GDK_TOPLEVEL (wayland_toplevel), app_id); + gdk_wayland_surface_get_window_geometry (surface->parent, &parent_geometry); + x += parent_geometry.x; + y += parent_geometry.y; - maybe_set_gtk_surface_dbus_properties (wayland_toplevel); - maybe_set_gtk_surface_modal (wayland_toplevel); + update_popup_layout_state (wayland_popup, + x, y, + width, height, + wayland_popup->layout); - gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "wayland", "surface commit"); - wl_surface_commit (wayland_surface->display_server.wl_surface); + wayland_popup->next_layout.x = x; + wayland_popup->next_layout.y = y; + wayland_surface->next_layout.configured_width = width; + wayland_surface->next_layout.configured_height = height; + wayland_surface->next_layout.surface_geometry_dirty = TRUE; + gdk_surface_request_layout (surface); } static void @@ -2112,258 +1937,47 @@ static const struct zxdg_popup_v6_listener zxdg_popup_v6_listener = { zxdg_popup_v6_done, }; -static enum xdg_positioner_anchor -rect_anchor_to_anchor (GdkGravity rect_anchor) +static void +calculate_popup_rect (GdkWaylandPopup *wayland_popup, + GdkPopupLayout *layout, + GdkRectangle *out_rect) { - switch (rect_anchor) + int width, height; + GdkRectangle anchor_rect; + int dx, dy; + int shadow_left, shadow_right, shadow_top, shadow_bottom; + int x = 0, y = 0; + + gdk_popup_layout_get_shadow_width (layout, + &shadow_left, + &shadow_right, + &shadow_top, + &shadow_bottom); + + width = (wayland_popup->unconstrained_width - (shadow_left + shadow_right)); + height = (wayland_popup->unconstrained_height - (shadow_top + shadow_bottom)); + + anchor_rect = *gdk_popup_layout_get_anchor_rect (layout); + gdk_popup_layout_get_offset (layout, &dx, &dy); + anchor_rect.x += dx; + anchor_rect.y += dy; + + switch (gdk_popup_layout_get_rect_anchor (layout)) { - case GDK_GRAVITY_NORTH_WEST: + default: case GDK_GRAVITY_STATIC: - return XDG_POSITIONER_ANCHOR_TOP_LEFT; + case GDK_GRAVITY_NORTH_WEST: + x = anchor_rect.x; + y = anchor_rect.y; + break; case GDK_GRAVITY_NORTH: - return XDG_POSITIONER_ANCHOR_TOP; + x = anchor_rect.x + (anchor_rect.width / 2); + y = anchor_rect.y; + break; case GDK_GRAVITY_NORTH_EAST: - return XDG_POSITIONER_ANCHOR_TOP_RIGHT; - case GDK_GRAVITY_WEST: - return XDG_POSITIONER_ANCHOR_LEFT; - case GDK_GRAVITY_CENTER: - return XDG_POSITIONER_ANCHOR_NONE; - case GDK_GRAVITY_EAST: - return XDG_POSITIONER_ANCHOR_RIGHT; - case GDK_GRAVITY_SOUTH_WEST: - return XDG_POSITIONER_ANCHOR_BOTTOM_LEFT; - case GDK_GRAVITY_SOUTH: - return XDG_POSITIONER_ANCHOR_BOTTOM; - case GDK_GRAVITY_SOUTH_EAST: - return XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT; - default: - g_assert_not_reached (); - } -} - -static enum xdg_positioner_gravity -surface_anchor_to_gravity (GdkGravity rect_anchor) -{ - switch (rect_anchor) - { - case GDK_GRAVITY_NORTH_WEST: - case GDK_GRAVITY_STATIC: - return XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT; - case GDK_GRAVITY_NORTH: - return XDG_POSITIONER_GRAVITY_BOTTOM; - case GDK_GRAVITY_NORTH_EAST: - return XDG_POSITIONER_GRAVITY_BOTTOM_LEFT; - case GDK_GRAVITY_WEST: - return XDG_POSITIONER_GRAVITY_RIGHT; - case GDK_GRAVITY_CENTER: - return XDG_POSITIONER_GRAVITY_NONE; - case GDK_GRAVITY_EAST: - return XDG_POSITIONER_GRAVITY_LEFT; - case GDK_GRAVITY_SOUTH_WEST: - return XDG_POSITIONER_GRAVITY_TOP_RIGHT; - case GDK_GRAVITY_SOUTH: - return XDG_POSITIONER_GRAVITY_TOP; - case GDK_GRAVITY_SOUTH_EAST: - return XDG_POSITIONER_GRAVITY_TOP_LEFT; - default: - g_assert_not_reached (); - } -} - -static enum zxdg_positioner_v6_anchor -rect_anchor_to_anchor_legacy (GdkGravity rect_anchor) -{ - switch (rect_anchor) - { - case GDK_GRAVITY_NORTH_WEST: - case GDK_GRAVITY_STATIC: - return (ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_LEFT); - case GDK_GRAVITY_NORTH: - return ZXDG_POSITIONER_V6_ANCHOR_TOP; - case GDK_GRAVITY_NORTH_EAST: - return (ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_RIGHT); - case GDK_GRAVITY_WEST: - return ZXDG_POSITIONER_V6_ANCHOR_LEFT; - case GDK_GRAVITY_CENTER: - return ZXDG_POSITIONER_V6_ANCHOR_NONE; - case GDK_GRAVITY_EAST: - return ZXDG_POSITIONER_V6_ANCHOR_RIGHT; - case GDK_GRAVITY_SOUTH_WEST: - return (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | - ZXDG_POSITIONER_V6_ANCHOR_LEFT); - case GDK_GRAVITY_SOUTH: - return ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; - case GDK_GRAVITY_SOUTH_EAST: - return (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | - ZXDG_POSITIONER_V6_ANCHOR_RIGHT); - default: - g_assert_not_reached (); - } - - return (ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_LEFT); -} - -static enum zxdg_positioner_v6_gravity -surface_anchor_to_gravity_legacy (GdkGravity rect_anchor) -{ - switch (rect_anchor) - { - case GDK_GRAVITY_NORTH_WEST: - case GDK_GRAVITY_STATIC: - return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | - ZXDG_POSITIONER_V6_GRAVITY_RIGHT); - case GDK_GRAVITY_NORTH: - return ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; - case GDK_GRAVITY_NORTH_EAST: - return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | - ZXDG_POSITIONER_V6_GRAVITY_LEFT); - case GDK_GRAVITY_WEST: - return ZXDG_POSITIONER_V6_GRAVITY_RIGHT; - case GDK_GRAVITY_CENTER: - return ZXDG_POSITIONER_V6_GRAVITY_NONE; - case GDK_GRAVITY_EAST: - return ZXDG_POSITIONER_V6_GRAVITY_LEFT; - case GDK_GRAVITY_SOUTH_WEST: - return (ZXDG_POSITIONER_V6_GRAVITY_TOP | - ZXDG_POSITIONER_V6_GRAVITY_RIGHT); - case GDK_GRAVITY_SOUTH: - return ZXDG_POSITIONER_V6_GRAVITY_TOP; - case GDK_GRAVITY_SOUTH_EAST: - return (ZXDG_POSITIONER_V6_GRAVITY_TOP | - ZXDG_POSITIONER_V6_GRAVITY_LEFT); - default: - g_assert_not_reached (); - } - - return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | - ZXDG_POSITIONER_V6_GRAVITY_RIGHT); -} - -void -gdk_wayland_toplevel_announce_csd (GdkToplevel *toplevel) -{ - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); - GdkWaylandToplevel *toplevel_wayland; - - g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); - toplevel_wayland = GDK_WAYLAND_TOPLEVEL (toplevel); - - if (!display_wayland->server_decoration_manager) - return; - toplevel_wayland->server_decoration = - org_kde_kwin_server_decoration_manager_create (display_wayland->server_decoration_manager, - gdk_wayland_surface_get_wl_surface (GDK_SURFACE (toplevel_wayland))); - if (toplevel_wayland->server_decoration) - org_kde_kwin_server_decoration_request_mode (toplevel_wayland->server_decoration, - ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT); -} - -void -gdk_wayland_toplevel_announce_ssd (GdkToplevel *toplevel) -{ - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); - GdkWaylandToplevel *toplevel_wayland; - - g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); - toplevel_wayland = GDK_WAYLAND_TOPLEVEL (toplevel); - - if (!display_wayland->server_decoration_manager) - return; - toplevel_wayland->server_decoration = - org_kde_kwin_server_decoration_manager_create (display_wayland->server_decoration_manager, - gdk_wayland_surface_get_wl_surface (GDK_SURFACE (toplevel_wayland))); - if (toplevel_wayland->server_decoration) - org_kde_kwin_server_decoration_request_mode (toplevel_wayland->server_decoration, - ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER); -} - -gboolean -gdk_wayland_toplevel_inhibit_idle (GdkToplevel *toplevel) -{ - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); - GdkWaylandToplevel *wayland_toplevel; - - g_return_val_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel), FALSE); - wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - - if (!display_wayland->idle_inhibit_manager) - return FALSE; - - if (!wayland_toplevel->idle_inhibitor) - { - g_assert (wayland_toplevel->idle_inhibitor_refcount == 0); - - wayland_toplevel->idle_inhibitor = - zwp_idle_inhibit_manager_v1_create_inhibitor (display_wayland->idle_inhibit_manager, - gdk_wayland_surface_get_wl_surface (GDK_SURFACE (wayland_toplevel))); - } - ++wayland_toplevel->idle_inhibitor_refcount; - - return TRUE; -} - -void -gdk_wayland_toplevel_uninhibit_idle (GdkToplevel *toplevel) -{ - GdkWaylandToplevel *wayland_toplevel; - - g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); - wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - - g_assert (wayland_toplevel->idle_inhibitor && - wayland_toplevel->idle_inhibitor_refcount > 0); - - if (--wayland_toplevel->idle_inhibitor_refcount == 0) - { - g_clear_pointer (&wayland_toplevel->idle_inhibitor, - zwp_idle_inhibitor_v1_destroy); - } -} - -static void -calculate_popup_rect (GdkWaylandPopup *wayland_popup, - GdkPopupLayout *layout, - GdkRectangle *out_rect) -{ - int width, height; - GdkRectangle anchor_rect; - int dx, dy; - int shadow_left, shadow_right, shadow_top, shadow_bottom; - int x = 0, y = 0; - - gdk_popup_layout_get_shadow_width (layout, - &shadow_left, - &shadow_right, - &shadow_top, - &shadow_bottom); - - width = (wayland_popup->unconstrained_width - (shadow_left + shadow_right)); - height = (wayland_popup->unconstrained_height - (shadow_top + shadow_bottom)); - - anchor_rect = *gdk_popup_layout_get_anchor_rect (layout); - gdk_popup_layout_get_offset (layout, &dx, &dy); - anchor_rect.x += dx; - anchor_rect.y += dy; - - switch (gdk_popup_layout_get_rect_anchor (layout)) - { - default: - case GDK_GRAVITY_STATIC: - case GDK_GRAVITY_NORTH_WEST: - x = anchor_rect.x; - y = anchor_rect.y; - break; - case GDK_GRAVITY_NORTH: - x = anchor_rect.x + (anchor_rect.width / 2); - y = anchor_rect.y; - break; - case GDK_GRAVITY_NORTH_EAST: - x = anchor_rect.x + anchor_rect.width; - y = anchor_rect.y; - break; + x = anchor_rect.x + anchor_rect.width; + y = anchor_rect.y; + break; case GDK_GRAVITY_WEST: x = anchor_rect.x; y = anchor_rect.y + (anchor_rect.height / 2); @@ -2713,11 +2327,6 @@ gdk_wayland_surface_create_xdg_popup (GdkWaylandPopup *wayland_popup, if (!is_realized_shell_surface (parent_impl)) return FALSE; - if (is_realized_toplevel (impl)) - { - g_warning ("Can't map popup, already mapped as toplevel"); - return FALSE; - } if (is_realized_popup (impl)) { g_warning ("Can't map popup, already mapped"); @@ -2809,274 +2418,73 @@ gdk_wayland_surface_create_xdg_popup (GdkWaylandPopup *wayland_popup, return TRUE; } -static GdkWaylandSeat * -find_grab_input_seat (GdkSurface *surface, - GdkSurface *parent) -{ - GdkWaylandPopup *popup = GDK_WAYLAND_POPUP (surface); - GdkWaylandPopup *tmp_popup; - /* Use the device that was used for the grab as the device for - * the popup surface setup - so this relies on GTK taking the - * grab before showing the popup surface. - */ - if (popup->grab_input_seat) - return GDK_WAYLAND_SEAT (popup->grab_input_seat); - - while (parent) - { - if (!GDK_IS_WAYLAND_POPUP (parent)) - break; - - tmp_popup = GDK_WAYLAND_POPUP (parent); - - if (tmp_popup->grab_input_seat) - return GDK_WAYLAND_SEAT (tmp_popup->grab_input_seat); - - parent = parent->parent; - } - - return NULL; -} - -static void -gdk_wayland_surface_map_toplevel (GdkSurface *surface) -{ - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - - if (!GDK_IS_WAYLAND_TOPLEVEL (surface)) - return; - - if (impl->mapped) - return; - - gdk_wayland_surface_create_xdg_toplevel (GDK_WAYLAND_TOPLEVEL (surface)); - - impl->mapped = TRUE; -} +#define LAST_PROP 1 static void -gdk_wayland_surface_show (GdkSurface *surface) +gdk_wayland_popup_init (GdkWaylandPopup *popup) { - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - - if (!impl->display_server.wl_surface) - gdk_wayland_surface_create_wl_surface (surface); - - gdk_wayland_surface_map_toplevel (surface); } static void -unmap_popups_for_surface (GdkSurface *surface) +gdk_wayland_popup_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) { - GdkWaylandDisplay *display_wayland; - GList *l; + GdkSurface *surface = GDK_SURFACE (object); - display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - for (l = display_wayland->current_popups; l; l = l->next) + switch (prop_id) { - GdkSurface *popup = l->data; + case LAST_PROP + GDK_POPUP_PROP_PARENT: + g_value_set_object (value, surface->parent); + break; - if (popup->parent == surface) - { - g_warning ("Tried to unmap the parent of a popup"); - gdk_surface_hide (popup); + case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE: + g_value_set_boolean (value, surface->autohide); + break; - return; - } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; } } static void -gdk_wayland_surface_hide_surface (GdkSurface *surface) +gdk_wayland_popup_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - GdkDisplay *display = gdk_surface_get_display (surface); - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - - unmap_popups_for_surface (surface); + GdkSurface *surface = GDK_SURFACE (object); - if (impl->display_server.wl_surface) + switch (prop_id) { - if (impl->display_server.egl_window) - { - gdk_surface_set_egl_native_window (surface, NULL); - wl_egl_window_destroy (impl->display_server.egl_window); - impl->display_server.egl_window = NULL; - } - - if (GDK_IS_WAYLAND_TOPLEVEL (surface)) - { - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - - if (toplevel->display_server.xdg_toplevel) - { - xdg_toplevel_destroy (toplevel->display_server.xdg_toplevel); - toplevel->display_server.xdg_toplevel = NULL; - } - if (toplevel->display_server.zxdg_toplevel_v6) - { - zxdg_toplevel_v6_destroy (toplevel->display_server.zxdg_toplevel_v6); - toplevel->display_server.zxdg_toplevel_v6 = NULL; - } - } - - if (GDK_IS_WAYLAND_POPUP (surface)) - { - GdkWaylandPopup *popup = GDK_WAYLAND_POPUP (surface); - - if (popup->display_server.xdg_popup) - { - xdg_popup_destroy (popup->display_server.xdg_popup); - popup->display_server.xdg_popup = NULL; - display_wayland->current_popups = - g_list_remove (display_wayland->current_popups, surface); - display_wayland->current_grabbing_popups = - g_list_remove (display_wayland->current_grabbing_popups, surface); - } - if (popup->display_server.zxdg_popup_v6) - { - zxdg_popup_v6_destroy (popup->display_server.zxdg_popup_v6); - popup->display_server.zxdg_popup_v6 = NULL; - display_wayland->current_popups = - g_list_remove (display_wayland->current_popups, surface); - display_wayland->current_grabbing_popups = - g_list_remove (display_wayland->current_grabbing_popups, surface); - } - } - - if (impl->display_server.xdg_surface) - { - xdg_surface_destroy (impl->display_server.xdg_surface); - impl->display_server.xdg_surface = NULL; - if (!impl->initial_configure_received) - gdk_surface_thaw_updates (surface); - else - impl->initial_configure_received = FALSE; - } - if (impl->display_server.zxdg_surface_v6) - { - zxdg_surface_v6_destroy (impl->display_server.zxdg_surface_v6); - impl->display_server.zxdg_surface_v6 = NULL; - if (!impl->initial_configure_received) - gdk_surface_thaw_updates (surface); - else - impl->initial_configure_received = FALSE; - } - - impl->awaiting_frame = FALSE; - if (impl->awaiting_frame_frozen) - { - impl->awaiting_frame_frozen = FALSE; - gdk_surface_thaw_updates (surface); - } - - if (GDK_IS_WAYLAND_POPUP (surface)) - { - GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (surface); - - wayland_popup->thaw_upon_show = TRUE; - gdk_surface_freeze_updates (surface); - - switch (wayland_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 (wayland_popup); - break; - case POPUP_STATE_IDLE: - break; - default: - g_assert_not_reached (); - } - - wayland_popup->state = POPUP_STATE_IDLE; - } - - if (GDK_IS_WAYLAND_TOPLEVEL (surface)) - { - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (surface); - - if (wayland_toplevel->display_server.gtk_surface) - { - if (display_wayland->gtk_shell_version >= - GTK_SURFACE1_RELEASE_SINCE_VERSION) - gtk_surface1_release (wayland_toplevel->display_server.gtk_surface); - else - gtk_surface1_destroy (wayland_toplevel->display_server.gtk_surface); - wayland_toplevel->display_server.gtk_surface = NULL; - wayland_toplevel->application.was_set = FALSE; - } - - g_clear_pointer (&wayland_toplevel->layout, gdk_toplevel_layout_unref); - } - - wl_surface_destroy (impl->display_server.wl_surface); - impl->display_server.wl_surface = NULL; - - g_slist_free (impl->display_server.outputs); - impl->display_server.outputs = NULL; - - if (GDK_IS_WAYLAND_POPUP (surface)) - { - GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (surface); - g_clear_pointer (&wayland_popup->layout, gdk_popup_layout_unref); - } - } - - impl->has_uncommitted_ack_configure = FALSE; - impl->input_region_dirty = TRUE; - impl->opaque_region_dirty = TRUE; - - unset_transient_for_exported (surface); + case LAST_PROP + GDK_POPUP_PROP_PARENT: + surface->parent = g_value_dup_object (value); + if (surface->parent != NULL) + surface->parent->children = g_list_prepend (surface->parent->children, surface); + break; - impl->last_sent_window_geometry = (GdkRectangle) { 0 }; + case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE: + surface->autohide = g_value_get_boolean (value); + break; - if (GDK_IS_WAYLAND_TOPLEVEL (impl)) - { - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (impl); - toplevel->last_sent_geometry_hints.min_width = 0; - toplevel->last_sent_geometry_hints.min_height = 0; - toplevel->last_sent_geometry_hints.max_width = 0; - toplevel->last_sent_geometry_hints.max_height = 0; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; } - - _gdk_wayland_surface_clear_saved_size (surface); - impl->mapped = FALSE; } static void -gdk_wayland_surface_hide (GdkSurface *surface) +gdk_wayland_popup_class_init (GdkWaylandPopupClass *class) { - GdkSeat *seat; - - seat = gdk_display_get_default_seat (surface->display); - if (seat) - { - if (surface->autohide) - gdk_seat_ungrab (seat); - - gdk_wayland_seat_clear_touchpoints (GDK_WAYLAND_SEAT (seat), surface); - } - gdk_wayland_surface_hide_surface (surface); - _gdk_surface_clear_update_area (surface); -} + GObjectClass *object_class = G_OBJECT_CLASS (class); -static void -gdk_wayland_surface_move_resize (GdkSurface *surface, - int x, - int y, - int width, - int height) -{ - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + object_class->get_property = gdk_wayland_popup_get_property; + object_class->set_property = gdk_wayland_popup_set_property; - surface->x = x; - surface->y = y; - gdk_wayland_surface_maybe_resize (surface, width, height, impl->scale); + gdk_popup_install_properties (object_class, 1); } static gboolean @@ -3187,6 +2595,36 @@ is_relayout_finished (GdkSurface *surface) return TRUE; } +static GdkWaylandSeat * +find_grab_input_seat (GdkSurface *surface, + GdkSurface *parent) +{ + GdkWaylandPopup *popup = GDK_WAYLAND_POPUP (surface); + GdkWaylandPopup *tmp_popup; + + /* Use the device that was used for the grab as the device for + * the popup surface setup - so this relies on GTK taking the + * grab before showing the popup surface. + */ + if (popup->grab_input_seat) + return GDK_WAYLAND_SEAT (popup->grab_input_seat); + + while (parent) + { + if (!GDK_IS_WAYLAND_POPUP (parent)) + break; + + tmp_popup = GDK_WAYLAND_POPUP (parent); + + if (tmp_popup->grab_input_seat) + return GDK_WAYLAND_SEAT (tmp_popup->grab_input_seat); + + parent = parent->parent; + } + + return NULL; +} + static void gdk_wayland_surface_map_popup (GdkWaylandPopup *wayland_popup, int width, @@ -3335,225 +2773,801 @@ gdk_wayland_surface_present_popup (GdkWaylandPopup *wayland_popup, show_popup (wayland_popup, width, height, layout); } } - else - { - if (wayland_popup->unconstrained_width == width && - wayland_popup->unconstrained_height == height && - gdk_popup_layout_equal (wayland_popup->layout, layout)) - return TRUE; + else + { + if (wayland_popup->unconstrained_width == width && + wayland_popup->unconstrained_height == height && + gdk_popup_layout_equal (wayland_popup->layout, layout)) + return TRUE; + + reposition_popup (wayland_popup, width, height, layout); + } + + while (wayland_popup->display_server.xdg_popup && !is_relayout_finished (surface)) + wl_display_dispatch_queue (display_wayland->wl_display, wayland_surface->event_queue); + + if (wayland_popup->display_server.xdg_popup) + { + gdk_surface_invalidate_rect (surface, NULL); + return TRUE; + } + else + { + return FALSE; + } +} + +static gboolean +gdk_wayland_popup_present (GdkPopup *popup, + int width, + int height, + GdkPopupLayout *layout) +{ + return gdk_wayland_surface_present_popup (GDK_WAYLAND_POPUP (popup), width, height, layout); +} + +static GdkGravity +gdk_wayland_popup_get_surface_anchor (GdkPopup *popup) +{ + return GDK_SURFACE (popup)->popup.surface_anchor; +} + +static GdkGravity +gdk_wayland_popup_get_rect_anchor (GdkPopup *popup) +{ + return GDK_SURFACE (popup)->popup.rect_anchor; +} + +static int +gdk_wayland_popup_get_position_x (GdkPopup *popup) +{ + return GDK_SURFACE (popup)->x; +} + +static int +gdk_wayland_popup_get_position_y (GdkPopup *popup) +{ + return GDK_SURFACE (popup)->y; +} + +static void +gdk_wayland_popup_iface_init (GdkPopupInterface *iface) +{ + iface->present = gdk_wayland_popup_present; + iface->get_surface_anchor = gdk_wayland_popup_get_surface_anchor; + iface->get_rect_anchor = gdk_wayland_popup_get_rect_anchor; + iface->get_position_x = gdk_wayland_popup_get_position_x; + iface->get_position_y = gdk_wayland_popup_get_position_y; +} + +/* }}} */ +/* {{{ Private Popup API */ + +void +_gdk_wayland_surface_set_grab_seat (GdkSurface *surface, + GdkSeat *seat) +{ + GdkWaylandPopup *popup; + + g_return_if_fail (surface != NULL); + + popup = GDK_WAYLAND_POPUP (surface); + popup->grab_input_seat = seat; +} + +/* }}} */ +/* {{{ GdkWaylandToplevel definition */ + +/** + * GdkWaylandToplevel: + * + * The Wayland implementation of `GdkToplevel`. + * + * Beyond the [iface@Gdk.Toplevel] API, the Wayland implementation + * has API to set up cross-process parent-child relationships between + * surfaces with [method@GdkWayland.WaylandToplevel.export_handle] and + * [method@GdkWayland.WaylandToplevel.set_transient_for_exported]. + */ + +struct _GdkWaylandToplevel +{ + GdkWaylandSurface parent_instance; + + struct { + struct gtk_surface1 *gtk_surface; + struct xdg_toplevel *xdg_toplevel; + struct zxdg_toplevel_v6 *zxdg_toplevel_v6; + } display_server; + + GdkWaylandToplevel *transient_for; + + struct org_kde_kwin_server_decoration *server_decoration; + struct zxdg_exported_v1 *xdg_exported; + struct zxdg_exported_v2 *xdg_exported_v2; + + struct { + int width; + int height; + GdkToplevelState state; + gboolean is_resizing; + + int bounds_width; + int bounds_height; + gboolean has_bounds; + } pending; + + struct { + gboolean should_constrain; + gboolean size_is_fixed; + } next_layout; + + struct { + GdkWaylandToplevelExported callback; + gpointer user_data; + GDestroyNotify destroy_func; + } exported; + + struct { + gboolean was_set; + + char *application_id; + char *app_menu_path; + char *menubar_path; + char *window_object_path; + char *application_object_path; + char *unique_bus_name; + } application; + + struct zwp_idle_inhibitor_v1 *idle_inhibitor; + size_t idle_inhibitor_refcount; + + struct wl_output *initial_fullscreen_output; + + struct { + GdkToplevelState unset_flags; + GdkToplevelState set_flags; + } initial_state; + + GdkToplevelLayout *layout; + int bounds_width; + int bounds_height; + gboolean has_bounds; + + char *title; + + GdkGeometry geometry_hints; + GdkSurfaceHints geometry_mask; + GdkGeometry last_sent_geometry_hints; + + struct zxdg_imported_v1 *imported_transient_for; + struct zxdg_imported_v2 *imported_transient_for_v2; + GHashTable *shortcuts_inhibitors; +}; + +typedef struct +{ + GdkWaylandSurfaceClass parent_class; +} GdkWaylandToplevelClass; + +static void gdk_wayland_toplevel_iface_init (GdkToplevelInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (GdkWaylandToplevel, gdk_wayland_toplevel, GDK_TYPE_WAYLAND_SURFACE, + G_IMPLEMENT_INTERFACE (GDK_TYPE_TOPLEVEL, + gdk_wayland_toplevel_iface_init)) + +/* }}} */ +/* {{{ Toplevel implementation */ + +static void maybe_set_gtk_surface_dbus_properties (GdkWaylandToplevel *wayland_toplevel); +static void maybe_set_gtk_surface_modal (GdkWaylandToplevel *wayland_toplevel); + +static void +gdk_wayland_toplevel_hide_surface (GdkWaylandToplevel *toplevel) +{ + GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (toplevel)); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); + + g_clear_pointer (&toplevel->display_server.xdg_toplevel, xdg_toplevel_destroy); + g_clear_pointer (&toplevel->display_server.zxdg_toplevel_v6, zxdg_toplevel_v6_destroy); + + if (toplevel->display_server.gtk_surface) + { + if (display_wayland->gtk_shell_version >= GTK_SURFACE1_RELEASE_SINCE_VERSION) + gtk_surface1_release (toplevel->display_server.gtk_surface); + else + gtk_surface1_destroy (toplevel->display_server.gtk_surface); + toplevel->display_server.gtk_surface = NULL; + toplevel->application.was_set = FALSE; + } + + g_clear_pointer (&toplevel->layout, gdk_toplevel_layout_unref); + + toplevel->last_sent_geometry_hints.min_width = 0; + toplevel->last_sent_geometry_hints.min_height = 0; + toplevel->last_sent_geometry_hints.max_width = 0; + toplevel->last_sent_geometry_hints.max_height = 0; +} + +static gboolean +is_realized_toplevel (GdkWaylandSurface *impl) +{ + GdkWaylandToplevel *toplevel; + + if (!GDK_IS_WAYLAND_TOPLEVEL (impl)) + return FALSE; + + toplevel = GDK_WAYLAND_TOPLEVEL (impl); + + return (toplevel->display_server.xdg_toplevel || + toplevel->display_server.zxdg_toplevel_v6); +} + +static void +gdk_wayland_toplevel_sync_parent (GdkWaylandToplevel *toplevel) +{ + GdkSurface *surface = GDK_SURFACE (toplevel); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + GdkWaylandToplevel *parent; + + if (!is_realized_toplevel (GDK_WAYLAND_SURFACE (toplevel))) + return; + + if (toplevel->transient_for) + parent = toplevel->transient_for; + else + parent = NULL; + + /* XXX: Is this correct? */ + if (parent && !is_realized_shell_surface (GDK_WAYLAND_SURFACE (parent))) + return; + + switch (display_wayland->shell_variant) + { + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + { + struct xdg_toplevel *parent_toplevel; + + if (parent) + parent_toplevel = parent->display_server.xdg_toplevel; + else + parent_toplevel = NULL; + + xdg_toplevel_set_parent (toplevel->display_server.xdg_toplevel, parent_toplevel); + break; + } + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + { + struct zxdg_toplevel_v6 *parent_toplevel; + + if (parent) + parent_toplevel = parent->display_server.zxdg_toplevel_v6; + else + parent_toplevel = NULL; + + zxdg_toplevel_v6_set_parent (toplevel->display_server.zxdg_toplevel_v6, parent_toplevel); + break; + } + default: + g_assert_not_reached (); + } +} + +static void +gdk_wayland_toplevel_sync_parent_of_imported (GdkWaylandToplevel *toplevel) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); + + if (!impl->display_server.wl_surface) + return; + + if (!is_realized_toplevel (impl)) + return; + + if (toplevel->imported_transient_for) + zxdg_imported_v1_set_parent_of (toplevel->imported_transient_for, + impl->display_server.wl_surface); + else if (toplevel->imported_transient_for_v2) + zxdg_imported_v2_set_parent_of (toplevel->imported_transient_for_v2, + impl->display_server.wl_surface); +} + +static void +gdk_wayland_toplevel_sync_title (GdkWaylandToplevel *toplevel) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); + GdkWaylandDisplay *display_wayland = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); + + if (!is_realized_toplevel (impl)) + return; + + if (!toplevel->title) + return; + + switch (display_wayland->shell_variant) + { + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + xdg_toplevel_set_title (toplevel->display_server.xdg_toplevel, toplevel->title); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + zxdg_toplevel_v6_set_title (toplevel->display_server.zxdg_toplevel_v6, toplevel->title); + break; + default: + g_assert_not_reached (); + } +} + +static void +configure_toplevel_geometry (GdkWaylandToplevel *wayland_toplevel) +{ + GdkSurface *surface = GDK_SURFACE (wayland_toplevel); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); + GdkDisplay *display = gdk_surface_get_display (surface); + int bounds_width, bounds_height; + GdkToplevelSize size; + GdkToplevelLayout *layout; + GdkGeometry geometry; + GdkSurfaceHints mask; + + if (wayland_toplevel->has_bounds) + { + bounds_width = wayland_toplevel->bounds_width; + bounds_height = wayland_toplevel->bounds_height; + } + else + { + GdkMonitor *monitor; + GListModel *monitors; + GdkRectangle monitor_geometry, display_geometry = { 0 }; + guint i; + + monitors = gdk_display_get_monitors (display); + + for (i = 0; i < g_list_model_get_n_items (monitors); i++) + { + monitor = g_list_model_get_item (monitors, i); + gdk_monitor_get_geometry (monitor, &monitor_geometry); + gdk_rectangle_union (&display_geometry, &monitor_geometry, &display_geometry); + g_object_unref (monitor); + } + + bounds_width = display_geometry.width; + bounds_height = display_geometry.height; + } + + gdk_toplevel_size_init (&size, bounds_width, bounds_height); + gdk_toplevel_notify_compute_size (GDK_TOPLEVEL (surface), &size); + g_warn_if_fail (size.width > 0); + g_warn_if_fail (size.height > 0); + + layout = wayland_toplevel->layout; + if (gdk_toplevel_layout_get_resizable (layout)) + { + geometry.min_width = size.min_width; + geometry.min_height = size.min_height; + mask = GDK_HINT_MIN_SIZE; + } + else + { + geometry.max_width = geometry.min_width = size.width; + geometry.max_height = geometry.min_height = size.height; + mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE; + } + + gdk_wayland_toplevel_set_geometry_hints (wayland_toplevel, &geometry, mask); + + if (size.shadow.is_valid) + { + wayland_surface->shadow_left = size.shadow.left; + wayland_surface->shadow_right = size.shadow.right; + wayland_surface->shadow_top = size.shadow.top; + wayland_surface->shadow_bottom = size.shadow.bottom; + } + + if (wayland_surface->next_layout.configured_width > 0 && + wayland_surface->next_layout.configured_height > 0) + { + int width, height; + + width = wayland_surface->next_layout.configured_width + + wayland_surface->shadow_left + wayland_surface->shadow_right; + height = wayland_surface->next_layout.configured_height + + wayland_surface->shadow_top + wayland_surface->shadow_bottom; + + if (wayland_toplevel->next_layout.should_constrain) + { + gdk_surface_constrain_size (&wayland_toplevel->geometry_hints, + wayland_toplevel->geometry_mask, + width, height, + &width, &height); + } + gdk_wayland_surface_update_size (surface, width, height, wayland_surface->scale); + + if (!wayland_toplevel->next_layout.size_is_fixed) + { + wayland_toplevel->next_layout.should_constrain = FALSE; + wayland_surface->next_layout.configured_width = 0; + wayland_surface->next_layout.configured_height = 0; + } + } + else + { + int width, height; + + width = size.width; + height = size.height; + gdk_surface_constrain_size (&geometry, mask, + width, height, + &width, &height); + gdk_wayland_surface_update_size (surface, width, height, wayland_surface->scale); + } +} + +static void +gdk_wayland_surface_configure_toplevel (GdkWaylandToplevel *wayland_toplevel) +{ + GdkSurface *surface = GDK_SURFACE (wayland_toplevel); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); + GdkWaylandDisplay *display_wayland = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + GdkToplevelState new_state; + int width, height; + gboolean is_resizing; + gboolean fixed_size; + gboolean was_fixed_size; + gboolean saved_size; + + new_state = wayland_toplevel->pending.state; + wayland_toplevel->pending.state = 0; + + is_resizing = wayland_toplevel->pending.is_resizing; + wayland_toplevel->pending.is_resizing = FALSE; + + if (wayland_toplevel->pending.has_bounds) + { + wayland_toplevel->bounds_width = wayland_toplevel->pending.bounds_width; + wayland_toplevel->bounds_height = wayland_toplevel->pending.bounds_height; + wayland_toplevel->has_bounds = TRUE; + } + + fixed_size = + new_state & (GDK_TOPLEVEL_STATE_MAXIMIZED | + GDK_TOPLEVEL_STATE_FULLSCREEN | + GDK_TOPLEVEL_STATE_TILED) || + is_resizing; + + was_fixed_size = + surface->state & (GDK_TOPLEVEL_STATE_MAXIMIZED | + GDK_TOPLEVEL_STATE_FULLSCREEN | + GDK_TOPLEVEL_STATE_TILED); + + width = wayland_toplevel->pending.width; + height = wayland_toplevel->pending.height; + + saved_size = (width == 0 && height == 0); + /* According to xdg_shell, an xdg_surface.configure with size 0x0 + * should be interpreted as that it is up to the client to set a + * size. + * + * When transitioning from maximize or fullscreen state, this means + * the client should configure its size back to what it was before + * being maximize or fullscreen. + */ + if (saved_size && !fixed_size && was_fixed_size) + { + width = wayland_surface->saved_width; + height = wayland_surface->saved_height; + } + + if (width > 0 && height > 0) + { + if (!saved_size) + { + wayland_toplevel->next_layout.should_constrain = TRUE; + + /* Save size for next time we get 0x0 */ + _gdk_wayland_surface_save_size (surface); + } + else if (is_resizing) + { + wayland_toplevel->next_layout.should_constrain = TRUE; + } + else + { + wayland_toplevel->next_layout.should_constrain = FALSE; + } + + wayland_toplevel->next_layout.size_is_fixed = fixed_size; + wayland_surface->next_layout.configured_width = width; + wayland_surface->next_layout.configured_height = height; + } + else + { + wayland_toplevel->next_layout.should_constrain = FALSE; + wayland_toplevel->next_layout.size_is_fixed = FALSE; + wayland_surface->next_layout.configured_width = 0; + wayland_surface->next_layout.configured_height = 0; + } - reposition_popup (wayland_popup, width, height, layout); - } + wayland_surface->next_layout.surface_geometry_dirty = TRUE; + gdk_surface_request_layout (surface); - while (wayland_popup->display_server.xdg_popup && !is_relayout_finished (surface)) - wl_display_dispatch_queue (display_wayland->wl_display, wayland_surface->event_queue); + GDK_DISPLAY_DEBUG (gdk_surface_get_display (surface), EVENTS, + "configure, surface %p %dx%d,%s%s%s%s", + surface, width, height, + (new_state & GDK_TOPLEVEL_STATE_FULLSCREEN) ? " fullscreen" : "", + (new_state & GDK_TOPLEVEL_STATE_MAXIMIZED) ? " maximized" : "", + (new_state & GDK_TOPLEVEL_STATE_FOCUSED) ? " focused" : "", + (new_state & GDK_TOPLEVEL_STATE_TILED) ? " tiled" : ""); - if (wayland_popup->display_server.xdg_popup) - { - gdk_surface_invalidate_rect (surface, NULL); - return TRUE; - } - else + gdk_surface_queue_state_change (surface, ~0 & ~new_state, new_state); + + switch (display_wayland->shell_variant) { - return FALSE; + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + xdg_surface_ack_configure (wayland_surface->display_server.xdg_surface, + wayland_surface->pending.serial); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + zxdg_surface_v6_ack_configure (wayland_surface->display_server.zxdg_surface_v6, + wayland_surface->pending.serial); + break; + default: + g_assert_not_reached (); } } static void -gdk_wayland_surface_get_geometry (GdkSurface *surface, - int *x, - int *y, - int *width, - int *height) +xdg_toplevel_configure (void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states) { - if (!GDK_SURFACE_DESTROYED (surface)) + GdkSurface *surface = GDK_SURFACE (data); + GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); + uint32_t *p; + GdkToplevelState pending_state = 0; + + toplevel->pending.is_resizing = FALSE; + + wl_array_for_each (p, states) { - if (x) - *x = surface->x; - if (y) - *y = surface->y; - if (width) - *width = surface->width; - if (height) - *height = surface->height; + uint32_t state = *p; + + switch (state) + { + case XDG_TOPLEVEL_STATE_FULLSCREEN: + pending_state |= GDK_TOPLEVEL_STATE_FULLSCREEN; + break; + case XDG_TOPLEVEL_STATE_MAXIMIZED: + pending_state |= GDK_TOPLEVEL_STATE_MAXIMIZED; + break; + case XDG_TOPLEVEL_STATE_ACTIVATED: + pending_state |= GDK_TOPLEVEL_STATE_FOCUSED; + break; + case XDG_TOPLEVEL_STATE_RESIZING: + toplevel->pending.is_resizing = TRUE; + break; + case XDG_TOPLEVEL_STATE_TILED_TOP: + pending_state |= (GDK_TOPLEVEL_STATE_TILED | + GDK_TOPLEVEL_STATE_TOP_TILED); + break; + case XDG_TOPLEVEL_STATE_TILED_RIGHT: + pending_state |= (GDK_TOPLEVEL_STATE_TILED | + GDK_TOPLEVEL_STATE_RIGHT_TILED); + break; + case XDG_TOPLEVEL_STATE_TILED_BOTTOM: + pending_state |= (GDK_TOPLEVEL_STATE_TILED | + GDK_TOPLEVEL_STATE_BOTTOM_TILED); + break; + case XDG_TOPLEVEL_STATE_TILED_LEFT: + pending_state |= (GDK_TOPLEVEL_STATE_TILED | + GDK_TOPLEVEL_STATE_LEFT_TILED); + break; + default: + /* Unknown state */ + break; + } } + + gdk_wayland_toplevel_handle_configure (toplevel, width, height, pending_state); } static void -gdk_wayland_surface_get_root_coords (GdkSurface *surface, - int x, - int y, - int *root_x, - int *root_y) +gdk_wayland_toplevel_handle_configure (GdkWaylandToplevel *toplevel, + int32_t width, + int32_t height, + GdkToplevelState state) { - /* - * Wayland does not have a global coordinate space shared between surfaces. In - * fact, for regular toplevels, we have no idea where our surfaces are - * positioned, relatively. - * - * However, there are some cases like popups and subsurfaces where we do have - * some amount of control over the placement of our surface, and we can - * semi-accurately control the x/y position of these surfaces, if they are - * relative to another surface. - * - * To pretend we have something called a root coordinate space, assume all - * parent-less surfaces are positioned in (0, 0), and all relative positioned - * popups and subsurfaces are placed within this fake root coordinate space. - * - * For example a 200x200 large toplevel surface will have the position (0, 0). - * If a popup positioned in the middle of the toplevel will have the fake - * position (100,100). Furthermore, if a positioned is placed in the middle - * that popup, will have the fake position (150,150), even though it has the - * relative position (50,50). These three surfaces would make up one single - * fake root coordinate space. - */ - - if (root_x) - *root_x = surface->x + x; - - if (root_y) - *root_y = surface->y + y; + toplevel->pending.state |= state; + toplevel->pending.width = width; + toplevel->pending.height = height; } -static gboolean -gdk_wayland_surface_get_device_state (GdkSurface *surface, - GdkDevice *device, - double *x, - double *y, - GdkModifierType *mask) +static void +xdg_toplevel_close (void *data, + struct xdg_toplevel *xdg_toplevel) { - if (GDK_SURFACE_DESTROYED (surface)) - return FALSE; - - gdk_wayland_device_query_state (device, surface, x, y, mask); + GdkSurface *surface = GDK_SURFACE (data); - return *x >= 0 && *y >= 0 && *x < surface->width && *y < surface->height; + gdk_wayland_surface_handle_close (surface); } static void -gdk_wayland_surface_set_input_region (GdkSurface *surface, - cairo_region_t *input_region) +xdg_toplevel_configure_bounds (void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height) { - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + GdkSurface *surface = GDK_SURFACE (data); + GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - if (GDK_SURFACE_DESTROYED (surface)) - return; + toplevel->pending.bounds_width = width; + toplevel->pending.bounds_height = height; + toplevel->pending.has_bounds = TRUE; +} - g_clear_pointer (&impl->input_region, cairo_region_destroy); +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + xdg_toplevel_configure, + xdg_toplevel_close, + xdg_toplevel_configure_bounds, +}; - if (input_region) - impl->input_region = cairo_region_copy (input_region); +static void +create_xdg_toplevel_resources (GdkWaylandToplevel *toplevel) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); - impl->input_region_dirty = TRUE; + toplevel->display_server.xdg_toplevel = + xdg_surface_get_toplevel (impl->display_server.xdg_surface); + xdg_toplevel_add_listener (toplevel->display_server.xdg_toplevel, + &xdg_toplevel_listener, + toplevel); } static void -gdk_wayland_surface_destroy (GdkSurface *surface, - gboolean foreign_destroy) +zxdg_toplevel_v6_configure (void *data, + struct zxdg_toplevel_v6 *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states) { - GdkWaylandDisplay *display; - GdkFrameClock *frame_clock; + GdkSurface *surface = GDK_SURFACE (data); + GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); + uint32_t *p; + GdkToplevelState pending_state = 0; - g_return_if_fail (GDK_IS_SURFACE (surface)); + toplevel->pending.is_resizing = FALSE; - /* Wayland surfaces can't be externally destroyed; we may possibly - * eventually want to use this path at display close-down - */ - g_return_if_fail (!foreign_destroy); + wl_array_for_each (p, states) + { + uint32_t state = *p; - gdk_wayland_surface_hide_surface (surface); + switch (state) + { + case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN: + pending_state |= GDK_TOPLEVEL_STATE_FULLSCREEN; + break; + case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED: + pending_state |= GDK_TOPLEVEL_STATE_MAXIMIZED; + break; + case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED: + pending_state |= GDK_TOPLEVEL_STATE_FOCUSED; + break; + case ZXDG_TOPLEVEL_V6_STATE_RESIZING: + toplevel->pending.is_resizing = TRUE; + break; + default: + /* Unknown state */ + break; + } + } - frame_clock = gdk_surface_get_frame_clock (surface); - g_signal_handlers_disconnect_by_func (frame_clock, on_frame_clock_before_paint, surface); - g_signal_handlers_disconnect_by_func (frame_clock, on_frame_clock_after_paint, surface); + gdk_wayland_toplevel_handle_configure (toplevel, width, height, pending_state); +} - display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - display->toplevels = g_list_remove (display->toplevels, surface); +static void +zxdg_toplevel_v6_close (void *data, + struct zxdg_toplevel_v6 *xdg_toplevel) +{ + GdkSurface *surface = GDK_SURFACE (data); + + gdk_wayland_surface_handle_close (surface); } +static const struct zxdg_toplevel_v6_listener zxdg_toplevel_v6_listener = { + zxdg_toplevel_v6_configure, + zxdg_toplevel_v6_close, +}; + static void -token_done (gpointer data, - struct xdg_activation_token_v1 *provider, - const char *token) +create_zxdg_toplevel_v6_resources (GdkWaylandToplevel *toplevel) { - char **token_out = data; + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); - *token_out = g_strdup (token); + toplevel->display_server.zxdg_toplevel_v6 = + zxdg_surface_v6_get_toplevel (impl->display_server.zxdg_surface_v6); + zxdg_toplevel_v6_add_listener (toplevel->display_server.zxdg_toplevel_v6, + &zxdg_toplevel_v6_listener, + toplevel); } -static const struct xdg_activation_token_v1_listener token_listener = { - token_done, -}; - static void -gdk_wayland_toplevel_focus (GdkToplevel *toplevel, - guint32 timestamp) +gdk_wayland_surface_create_xdg_toplevel (GdkWaylandToplevel *wayland_toplevel) { - GdkSurface *surface = GDK_SURFACE (toplevel); - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (toplevel); - GdkDisplay *display = gdk_surface_get_display (surface); - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); - gchar *startup_id = NULL; + GdkSurface *surface = GDK_SURFACE (wayland_toplevel); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); + const char *app_id; - startup_id = g_steal_pointer (&display_wayland->startup_notification_id); + gdk_surface_freeze_updates (surface); + gdk_wayland_surface_create_xdg_surface_resources (surface); - if (display_wayland->xdg_activation) + switch (display_wayland->shell_variant) { - GdkWaylandSeat *seat = - GDK_WAYLAND_SEAT (gdk_display_get_default_seat (display)); - - /* If the focus request does not have a startup ID associated, get a - * new token to activate the window. - */ - if (!startup_id) - { - struct xdg_activation_token_v1 *token; - struct wl_event_queue *event_queue; - struct wl_surface *wl_surface = NULL; - GdkSurface *focus_surface; + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + create_xdg_toplevel_resources (wayland_toplevel); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + create_zxdg_toplevel_v6_resources (wayland_toplevel); + break; + default: + g_assert_not_reached (); + } - event_queue = wl_display_create_queue (display_wayland->wl_display); + gdk_wayland_toplevel_sync_parent (wayland_toplevel); + gdk_wayland_toplevel_sync_parent_of_imported (wayland_toplevel); + gdk_wayland_toplevel_sync_title (wayland_toplevel); - token = xdg_activation_v1_get_activation_token (display_wayland->xdg_activation); - wl_proxy_set_queue ((struct wl_proxy *) token, event_queue); + switch (display_wayland->shell_variant) + { + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + if (wayland_toplevel->initial_state.set_flags & GDK_TOPLEVEL_STATE_MAXIMIZED) + xdg_toplevel_set_maximized (wayland_toplevel->display_server.xdg_toplevel); + if (wayland_toplevel->initial_state.set_flags & GDK_TOPLEVEL_STATE_MINIMIZED) + xdg_toplevel_set_minimized (wayland_toplevel->display_server.xdg_toplevel); + if (wayland_toplevel->initial_state.set_flags & GDK_TOPLEVEL_STATE_FULLSCREEN) + xdg_toplevel_set_fullscreen (wayland_toplevel->display_server.xdg_toplevel, + wayland_toplevel->initial_fullscreen_output); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + if (wayland_toplevel->initial_state.set_flags & GDK_TOPLEVEL_STATE_MAXIMIZED) + zxdg_toplevel_v6_set_maximized (wayland_toplevel->display_server.zxdg_toplevel_v6); + if (wayland_toplevel->initial_state.set_flags & GDK_TOPLEVEL_STATE_MINIMIZED) + zxdg_toplevel_v6_set_minimized (wayland_toplevel->display_server.zxdg_toplevel_v6); + if (wayland_toplevel->initial_state.set_flags & GDK_TOPLEVEL_STATE_FULLSCREEN) + zxdg_toplevel_v6_set_fullscreen (wayland_toplevel->display_server.zxdg_toplevel_v6, + wayland_toplevel->initial_fullscreen_output); + break; + default: + g_assert_not_reached (); + } - xdg_activation_token_v1_add_listener (token, - &token_listener, - &startup_id); - xdg_activation_token_v1_set_serial (token, - _gdk_wayland_seat_get_last_implicit_grab_serial (seat, NULL), - gdk_wayland_seat_get_wl_seat (GDK_SEAT (seat))); + wayland_toplevel->initial_fullscreen_output = NULL; - focus_surface = gdk_wayland_device_get_focus (gdk_seat_get_keyboard (GDK_SEAT (seat))); - if (focus_surface) - wl_surface = gdk_wayland_surface_get_wl_surface (focus_surface); - if (wl_surface) - xdg_activation_token_v1_set_surface (token, wl_surface); + app_id = wayland_toplevel->application.application_id; + if (app_id == NULL) + app_id = g_get_prgname (); - xdg_activation_token_v1_commit (token); + if (app_id == NULL) + app_id = "GTK Application"; - while (startup_id == NULL) - wl_display_dispatch_queue (display_wayland->wl_display, event_queue); + gdk_wayland_toplevel_set_application_id (GDK_TOPLEVEL (wayland_toplevel), app_id); - xdg_activation_token_v1_destroy (token); - wl_event_queue_destroy (event_queue); - } + maybe_set_gtk_surface_dbus_properties (wayland_toplevel); + maybe_set_gtk_surface_modal (wayland_toplevel); - xdg_activation_v1_activate (display_wayland->xdg_activation, - startup_id, - wayland_surface->display_server.wl_surface); - } - else if (wayland_toplevel->display_server.gtk_surface) - { - if (timestamp != GDK_CURRENT_TIME) - gtk_surface1_present (wayland_toplevel->display_server.gtk_surface, timestamp); - else if (startup_id && display_wayland->gtk_shell_version >= 3) - gtk_surface1_request_focus (wayland_toplevel->display_server.gtk_surface, - startup_id); - } + gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "wayland", "surface commit"); + wl_surface_commit (wayland_surface->display_server.wl_surface); +} - g_free (startup_id); +static void +gdk_wayland_toplevel_init (GdkWaylandToplevel *toplevel) +{ + toplevel->initial_fullscreen_output = NULL; + toplevel->shortcuts_inhibitors = g_hash_table_new (NULL, NULL); } static void @@ -3667,6 +3681,63 @@ gdk_wayland_toplevel_init_gtk_surface (GdkWaylandToplevel *wayland_toplevel) wayland_surface); } +static void +gdk_wayland_toplevel_set_title (GdkWaylandToplevel *toplevel, + const char *title) +{ + const char *end; + gsize title_length; + + g_return_if_fail (title != NULL); + + if (GDK_SURFACE_DESTROYED (GDK_SURFACE (toplevel))) + return; + + if (g_strcmp0 (toplevel->title, title) == 0) + return; + + g_free (toplevel->title); + + title_length = MIN (strlen (title), MAX_WL_BUFFER_SIZE); + if (g_utf8_validate (title, title_length, &end)) + { + toplevel->title = g_malloc (end - title + 1); + memcpy (toplevel->title, title, end - title); + toplevel->title[end - title] = '\0'; + } + else + { + toplevel->title = g_utf8_make_valid (title, title_length); + g_warning ("Invalid utf8 passed to gdk_surface_set_title: '%s'", title); + } + + gdk_wayland_toplevel_sync_title (toplevel); +} + +static void +gdk_wayland_toplevel_set_startup_id (GdkWaylandToplevel *toplevel, + const char *startup_id) +{ + GdkWaylandDisplay *display = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); + char *free_me = NULL; + + if (!startup_id) + { + free_me = g_steal_pointer (&display_wayland->startup_notification_id); + startup_id = free_me; + } + + if (startup_id) + { + xdg_activation_v1_activate (display_wayland->xdg_activation, + startup_id, + wayland_toplevel->display_server.wl_surface); + } + + g_free (free_me); +} + static void maybe_set_gtk_surface_modal (GdkWaylandToplevel *wayland_toplevel) { @@ -3695,14 +3766,18 @@ gdk_wayland_toplevel_set_geometry_hints (GdkWaylandToplevel *toplevel, GdkSurfaceHints geom_mask) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); - GdkWaylandDisplay *display_wayland; + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); int min_width, min_height; int max_width, max_height; if (GDK_SURFACE_DESTROYED (toplevel)) return; - display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); + if (!geometry) + { + geometry = &toplevel->geometry_hints; + geom_mask = toplevel->geometry_mask; + } toplevel->geometry_hints = *geometry; toplevel->geometry_mask = geom_mask; @@ -3730,98 +3805,40 @@ gdk_wayland_toplevel_set_geometry_hints (GdkWaylandToplevel *toplevel, max_height = MAX (0, (geometry->max_height - (impl->shadow_top + impl->shadow_bottom))); } - else - { - max_width = 0; - max_height = 0; - } - - if (toplevel->last_sent_geometry_hints.min_width == min_width && - toplevel->last_sent_geometry_hints.min_height == min_height && - toplevel->last_sent_geometry_hints.max_width == max_width && - toplevel->last_sent_geometry_hints.max_height == max_height) - return; - - switch (display_wayland->shell_variant) - { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_toplevel_set_min_size (toplevel->display_server.xdg_toplevel, - min_width, min_height); - xdg_toplevel_set_max_size (toplevel->display_server.xdg_toplevel, - max_width, max_height); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_set_min_size (toplevel->display_server.zxdg_toplevel_v6, - min_width, min_height); - zxdg_toplevel_v6_set_max_size (toplevel->display_server.zxdg_toplevel_v6, - max_width, max_height); - break; - default: - g_assert_not_reached (); - } - - toplevel->last_sent_geometry_hints.min_width = min_width; - toplevel->last_sent_geometry_hints.min_height = min_height; - toplevel->last_sent_geometry_hints.max_width = max_width; - toplevel->last_sent_geometry_hints.max_height = max_height; -} - -static void -gdk_wayland_toplevel_set_title (GdkWaylandToplevel *toplevel, - const char *title) -{ - const char *end; - gsize title_length; - - g_return_if_fail (title != NULL); - - if (GDK_SURFACE_DESTROYED (GDK_SURFACE (toplevel))) - return; - - if (g_strcmp0 (toplevel->title, title) == 0) - return; - - g_free (toplevel->title); - - title_length = MIN (strlen (title), MAX_WL_BUFFER_SIZE); - if (g_utf8_validate (title, title_length, &end)) - { - toplevel->title = g_malloc (end - title + 1); - memcpy (toplevel->title, title, end - title); - toplevel->title[end - title] = '\0'; - } - else - { - toplevel->title = g_utf8_make_valid (title, title_length); - g_warning ("Invalid utf8 passed to gdk_surface_set_title: '%s'", title); - } - - gdk_wayland_toplevel_sync_title (toplevel); -} - -static void -gdk_wayland_surface_set_startup_id (GdkSurface *surface, - const char *startup_id) -{ - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (surface); - GdkDisplay *display = gdk_surface_get_display (surface); - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); - gchar *free_me = NULL; - - if (!startup_id) + else { - free_me = g_steal_pointer (&display_wayland->startup_notification_id); - startup_id = free_me; + max_width = 0; + max_height = 0; } - if (startup_id) + if (toplevel->last_sent_geometry_hints.min_width == min_width && + toplevel->last_sent_geometry_hints.min_height == min_height && + toplevel->last_sent_geometry_hints.max_width == max_width && + toplevel->last_sent_geometry_hints.max_height == max_height) + return; + + switch (display_wayland->shell_variant) { - xdg_activation_v1_activate (display_wayland->xdg_activation, - startup_id, - wayland_surface->display_server.wl_surface); + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + xdg_toplevel_set_min_size (toplevel->display_server.xdg_toplevel, + min_width, min_height); + xdg_toplevel_set_max_size (toplevel->display_server.xdg_toplevel, + max_width, max_height); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + zxdg_toplevel_v6_set_min_size (toplevel->display_server.zxdg_toplevel_v6, + min_width, min_height); + zxdg_toplevel_v6_set_max_size (toplevel->display_server.zxdg_toplevel_v6, + max_width, max_height); + break; + default: + g_assert_not_reached (); } - g_free (free_me); + toplevel->last_sent_geometry_hints.min_width = min_width; + toplevel->last_sent_geometry_hints.min_height = min_height; + toplevel->last_sent_geometry_hints.max_width = max_width; + toplevel->last_sent_geometry_hints.max_height = max_height; } static gboolean @@ -3864,118 +3881,207 @@ gdk_wayland_toplevel_set_transient_for (GdkWaylandToplevel *toplevel, else toplevel->transient_for = NULL; - gdk_wayland_surface_sync_parent (GDK_SURFACE (toplevel), NULL); + gdk_wayland_toplevel_sync_parent (toplevel); } -static gboolean -gdk_wayland_toplevel_minimize (GdkToplevel *toplevel) +static void +gdk_wayland_toplevel_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - GdkSurface *surface = GDK_SURFACE (toplevel); - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (surface); - GdkWaylandDisplay *display_wayland; + GdkSurface *surface = GDK_SURFACE (object); + GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - if (GDK_SURFACE_DESTROYED (surface)) - return TRUE; + switch (prop_id) + { + case LAST_PROP + GDK_TOPLEVEL_PROP_TITLE: + gdk_wayland_toplevel_set_title (toplevel, g_value_get_string (value)); + g_object_notify_by_pspec (object, pspec); + break; - if (!is_realized_toplevel (impl)) - return TRUE; + case LAST_PROP + GDK_TOPLEVEL_PROP_STARTUP_ID: + gdk_wayland_toplevel_set_startup_id (toplevel, g_value_get_string (value)); + g_object_notify_by_pspec (object, pspec); + break; - /* FIXME: xdg_toplevel does not come with a minimized state that we can - * query or get notified of. This means we cannot implement the full - * GdkSurface API, and our state will not reflect minimization. - */ - display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - switch (display_wayland->shell_variant) - { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_toplevel_set_minimized (wayland_toplevel->display_server.xdg_toplevel); + case LAST_PROP + GDK_TOPLEVEL_PROP_TRANSIENT_FOR: + gdk_wayland_toplevel_set_transient_for (toplevel, g_value_get_object (value)); + g_object_notify_by_pspec (object, pspec); break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_set_minimized (wayland_toplevel->display_server.zxdg_toplevel_v6); + + case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL: + gdk_wayland_toplevel_set_modal_hint (toplevel, g_value_get_boolean (value)); + g_object_notify_by_pspec (object, pspec); + break; + + case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST: + break; + + case LAST_PROP + GDK_TOPLEVEL_PROP_DECORATED: break; + + case LAST_PROP + GDK_TOPLEVEL_PROP_DELETABLE: + break; + + case LAST_PROP + GDK_TOPLEVEL_PROP_FULLSCREEN_MODE: + surface->fullscreen_mode = g_value_get_enum (value); + g_object_notify_by_pspec (object, pspec); + break; + + case LAST_PROP + GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED: + break; + default: - g_assert_not_reached (); + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; } - - return TRUE; } static void -gdk_wayland_toplevel_maximize (GdkToplevel *toplevel) +gdk_wayland_toplevel_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) { - GdkSurface *surface = GDK_SURFACE (toplevel); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (surface); - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); + GdkSurface *surface = GDK_SURFACE (object); + GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - if (GDK_SURFACE_DESTROYED (surface)) - return; + switch (prop_id) + { + case LAST_PROP + GDK_TOPLEVEL_PROP_STATE: + g_value_set_flags (value, surface->state); + break; - _gdk_wayland_surface_save_size (surface); + case LAST_PROP + GDK_TOPLEVEL_PROP_TITLE: + g_value_set_string (value, toplevel->title); + break; - if (is_realized_toplevel (wayland_surface)) - { - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + case LAST_PROP + GDK_TOPLEVEL_PROP_STARTUP_ID: + g_value_set_string (value, ""); + break; - switch (display_wayland->shell_variant) - { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_toplevel_set_maximized (wayland_toplevel->display_server.xdg_toplevel); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_set_maximized (wayland_toplevel->display_server.zxdg_toplevel_v6); - break; - default: - g_assert_not_reached (); - } - } - else - { - synthesize_initial_surface_state (wayland_toplevel, 0, GDK_TOPLEVEL_STATE_MAXIMIZED); + case LAST_PROP + GDK_TOPLEVEL_PROP_TRANSIENT_FOR: + g_value_set_object (value, toplevel->transient_for); + break; + + case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL: + g_value_set_boolean (value, surface->modal_hint); + break; + + case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST: + g_value_set_pointer (value, NULL); + break; + + case LAST_PROP + GDK_TOPLEVEL_PROP_DECORATED: + break; + + case LAST_PROP + GDK_TOPLEVEL_PROP_DELETABLE: + break; + + case LAST_PROP + GDK_TOPLEVEL_PROP_FULLSCREEN_MODE: + g_value_set_enum (value, surface->fullscreen_mode); + break; + + case LAST_PROP + GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED: + g_value_set_boolean (value, surface->shortcuts_inhibited); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; } } static void -gdk_wayland_toplevel_unmaximize (GdkToplevel *toplevel) +gdk_wayland_toplevel_finalize (GObject *object) +{ + GdkWaylandToplevel *wayland_toplevel; + + g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (object)); + + wayland_toplevel = GDK_WAYLAND_TOPLEVEL (object); + + if (gdk_wayland_toplevel_is_exported (wayland_toplevel)) + gdk_wayland_toplevel_unexport_handle (GDK_TOPLEVEL (wayland_toplevel)); + + g_free (wayland_toplevel->application.application_id); + g_free (wayland_toplevel->application.app_menu_path); + g_free (wayland_toplevel->application.menubar_path); + g_free (wayland_toplevel->application.window_object_path); + g_free (wayland_toplevel->application.application_object_path); + g_free (wayland_toplevel->application.unique_bus_name); + + g_free (wayland_toplevel->title); + g_clear_pointer (&wayland_toplevel->shortcuts_inhibitors, g_hash_table_unref); + + G_OBJECT_CLASS (gdk_wayland_toplevel_parent_class)->finalize (object); +} + +static void +gdk_wayland_toplevel_class_init (GdkWaylandToplevelClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->get_property = gdk_wayland_toplevel_get_property; + object_class->set_property = gdk_wayland_toplevel_set_property; + object_class->finalize = gdk_wayland_toplevel_finalize; + + gdk_toplevel_install_properties (object_class, 1); +} + +static void +synthesize_initial_surface_state (GdkWaylandToplevel *wayland_toplevel, + GdkToplevelState unset_flags, + GdkToplevelState set_flags) +{ + wayland_toplevel->initial_state.unset_flags |= unset_flags; + wayland_toplevel->initial_state.set_flags &= ~unset_flags; + + wayland_toplevel->initial_state.set_flags |= set_flags; + wayland_toplevel->initial_state.unset_flags &= ~set_flags; +} + +static gboolean +gdk_wayland_toplevel_minimize (GdkToplevel *toplevel) { GdkSurface *surface = GDK_SURFACE (toplevel); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (surface); - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (surface); + GdkWaylandDisplay *display_wayland; if (GDK_SURFACE_DESTROYED (surface)) - return; + return TRUE; - if (is_realized_toplevel (wayland_surface)) - { - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + if (!is_realized_toplevel (impl)) + return TRUE; - switch (display_wayland->shell_variant) - { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_toplevel_unset_maximized (wayland_toplevel->display_server.xdg_toplevel); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_unset_maximized (wayland_toplevel->display_server.zxdg_toplevel_v6); - break; - default: - g_assert_not_reached (); - } - } - else + /* FIXME: xdg_toplevel does not come with a minimized state that we can + * query or get notified of. This means we cannot implement the full + * GdkSurface API, and our state will not reflect minimization. + */ + display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + switch (display_wayland->shell_variant) { - synthesize_initial_surface_state (wayland_toplevel, GDK_TOPLEVEL_STATE_MAXIMIZED, 0); + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + xdg_toplevel_set_minimized (wayland_toplevel->display_server.xdg_toplevel); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + zxdg_toplevel_v6_set_minimized (wayland_toplevel->display_server.zxdg_toplevel_v6); + break; + default: + g_assert_not_reached (); } + + return TRUE; } static void -gdk_wayland_toplevel_fullscreen_on_monitor (GdkWaylandToplevel *wayland_toplevel, - GdkMonitor *monitor) +gdk_wayland_toplevel_maximize (GdkToplevel *toplevel) { - GdkSurface *surface = GDK_SURFACE (wayland_toplevel); + GdkSurface *surface = GDK_SURFACE (toplevel); GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (surface); - struct wl_output *output = ((GdkWaylandMonitor *)monitor)->output; + GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); if (GDK_SURFACE_DESTROYED (surface)) return; @@ -3990,10 +4096,10 @@ gdk_wayland_toplevel_fullscreen_on_monitor (GdkWaylandToplevel *wayland_toplevel switch (display_wayland->shell_variant) { case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_toplevel_set_fullscreen (wayland_toplevel->display_server.xdg_toplevel, output); + xdg_toplevel_set_maximized (wayland_toplevel->display_server.xdg_toplevel); break; case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_set_fullscreen (wayland_toplevel->display_server.zxdg_toplevel_v6, output); + zxdg_toplevel_v6_set_maximized (wayland_toplevel->display_server.zxdg_toplevel_v6); break; default: g_assert_not_reached (); @@ -4001,24 +4107,20 @@ gdk_wayland_toplevel_fullscreen_on_monitor (GdkWaylandToplevel *wayland_toplevel } else { - synthesize_initial_surface_state (wayland_toplevel, 0, GDK_TOPLEVEL_STATE_FULLSCREEN); - wayland_toplevel->initial_fullscreen_output = output; + synthesize_initial_surface_state (wayland_toplevel, 0, GDK_TOPLEVEL_STATE_MAXIMIZED); } } static void -gdk_wayland_toplevel_fullscreen (GdkWaylandToplevel *wayland_toplevel) +gdk_wayland_toplevel_unmaximize (GdkToplevel *toplevel) { - GdkSurface *surface = GDK_SURFACE (wayland_toplevel); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); + GdkSurface *surface = GDK_SURFACE (toplevel); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (surface); + GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); if (GDK_SURFACE_DESTROYED (surface)) return; - wayland_toplevel->initial_fullscreen_output = NULL; - - _gdk_wayland_surface_save_size (surface); - if (is_realized_toplevel (wayland_surface)) { GdkWaylandDisplay *display_wayland = @@ -4027,10 +4129,10 @@ gdk_wayland_toplevel_fullscreen (GdkWaylandToplevel *wayland_toplevel) switch (display_wayland->shell_variant) { case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_toplevel_set_fullscreen (wayland_toplevel->display_server.xdg_toplevel, NULL); + xdg_toplevel_unset_maximized (wayland_toplevel->display_server.xdg_toplevel); break; case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_set_fullscreen (wayland_toplevel->display_server.zxdg_toplevel_v6, NULL); + zxdg_toplevel_v6_unset_maximized (wayland_toplevel->display_server.zxdg_toplevel_v6); break; default: g_assert_not_reached (); @@ -4038,20 +4140,22 @@ gdk_wayland_toplevel_fullscreen (GdkWaylandToplevel *wayland_toplevel) } else { - synthesize_initial_surface_state (wayland_toplevel, 0, GDK_TOPLEVEL_STATE_FULLSCREEN); + synthesize_initial_surface_state (wayland_toplevel, GDK_TOPLEVEL_STATE_MAXIMIZED, 0); } } static void -gdk_wayland_toplevel_unfullscreen (GdkWaylandToplevel *wayland_toplevel) +gdk_wayland_toplevel_fullscreen_on_monitor (GdkWaylandToplevel *wayland_toplevel, + GdkMonitor *monitor) { GdkSurface *surface = GDK_SURFACE (wayland_toplevel); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (surface); + struct wl_output *output = ((GdkWaylandMonitor *)monitor)->output; if (GDK_SURFACE_DESTROYED (surface)) return; - wayland_toplevel->initial_fullscreen_output = NULL; + _gdk_wayland_surface_save_size (surface); if (is_realized_toplevel (wayland_surface)) { @@ -4061,10 +4165,10 @@ gdk_wayland_toplevel_unfullscreen (GdkWaylandToplevel *wayland_toplevel) switch (display_wayland->shell_variant) { case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_toplevel_unset_fullscreen (wayland_toplevel->display_server.xdg_toplevel); + xdg_toplevel_set_fullscreen (wayland_toplevel->display_server.xdg_toplevel, output); break; case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_unset_fullscreen (wayland_toplevel->display_server.zxdg_toplevel_v6); + zxdg_toplevel_v6_set_fullscreen (wayland_toplevel->display_server.zxdg_toplevel_v6, output); break; default: g_assert_not_reached (); @@ -4072,1218 +4176,1103 @@ gdk_wayland_toplevel_unfullscreen (GdkWaylandToplevel *wayland_toplevel) } else { - synthesize_initial_surface_state (wayland_toplevel, GDK_TOPLEVEL_STATE_FULLSCREEN, 0); - } -} - -static void -gdk_wayland_toplevel_begin_resize (GdkToplevel *toplevel, - GdkSurfaceEdge edge, - GdkDevice *device, - int button, - double x, - double y, - guint32 timestamp) -{ - GdkSurface *surface = GDK_SURFACE (toplevel); - GdkWaylandSurface *impl; - GdkWaylandToplevel *wayland_toplevel; - GdkWaylandDisplay *display_wayland; - GdkEventSequence *sequence; - uint32_t resize_edges, serial; - - if (GDK_SURFACE_DESTROYED (surface)) - return; - - switch (edge) - { - case GDK_SURFACE_EDGE_NORTH_WEST: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT; - break; - - case GDK_SURFACE_EDGE_NORTH: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP; - break; - - case GDK_SURFACE_EDGE_NORTH_EAST: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT; - break; - - case GDK_SURFACE_EDGE_WEST: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT; - break; - - case GDK_SURFACE_EDGE_EAST: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT; - break; - - case GDK_SURFACE_EDGE_SOUTH_WEST: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT; - break; - - case GDK_SURFACE_EDGE_SOUTH: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM; - break; - - case GDK_SURFACE_EDGE_SOUTH_EAST: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT; - break; - - default: - g_warning ("gdk_toplevel_begin_resize: bad resize edge %d!", edge); - return; - } - - impl = GDK_WAYLAND_SURFACE (surface); - wayland_toplevel = GDK_WAYLAND_TOPLEVEL (surface); - display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - - if (!is_realized_toplevel (impl)) - return; - - serial = _gdk_wayland_seat_get_last_implicit_grab_serial (GDK_WAYLAND_SEAT (gdk_device_get_seat (device)), - &sequence); - - switch (display_wayland->shell_variant) - { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_toplevel_resize (wayland_toplevel->display_server.xdg_toplevel, - gdk_wayland_device_get_wl_seat (device), - serial, resize_edges); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_resize (wayland_toplevel->display_server.zxdg_toplevel_v6, - gdk_wayland_device_get_wl_seat (device), - serial, resize_edges); - break; - default: - g_assert_not_reached (); - } - - if (sequence) - gdk_wayland_device_unset_touch_grab (device, sequence); -} - -static void -gdk_wayland_toplevel_begin_move (GdkToplevel *toplevel, - GdkDevice *device, - int button, - double x, - double y, - guint32 timestamp) -{ - GdkSurface *surface = GDK_SURFACE (toplevel); - GdkWaylandSurface *impl; - GdkWaylandToplevel *wayland_toplevel; - GdkWaylandDisplay *display_wayland; - GdkEventSequence *sequence; - uint32_t serial; - - if (GDK_SURFACE_DESTROYED (surface)) - return; - - impl = GDK_WAYLAND_SURFACE (surface); - wayland_toplevel = GDK_WAYLAND_TOPLEVEL (surface); - display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - - if (!is_realized_toplevel (impl)) - return; - - serial = _gdk_wayland_seat_get_last_implicit_grab_serial (GDK_WAYLAND_SEAT (gdk_device_get_seat (device)), - &sequence); - switch (display_wayland->shell_variant) - { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_toplevel_move (wayland_toplevel->display_server.xdg_toplevel, - gdk_wayland_device_get_wl_seat (device), - serial); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_move (wayland_toplevel->display_server.zxdg_toplevel_v6, - gdk_wayland_device_get_wl_seat (device), - serial); - break; - default: - g_assert_not_reached (); - } - - if (sequence) - gdk_wayland_device_unset_touch_grab (device, sequence); -} - -static void -gdk_wayland_surface_destroy_notify (GdkSurface *surface) -{ - if (!GDK_SURFACE_DESTROYED (surface)) - { - g_warning ("GdkSurface %p unexpectedly destroyed", surface); - _gdk_surface_destroy (surface, TRUE); + synthesize_initial_surface_state (wayland_toplevel, 0, GDK_TOPLEVEL_STATE_FULLSCREEN); + wayland_toplevel->initial_fullscreen_output = output; } - - g_object_unref (surface); -} - -static int -gdk_wayland_surface_get_scale_factor (GdkSurface *surface) -{ - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - - if (GDK_SURFACE_DESTROYED (surface)) - return 1; - - return impl->scale; } static void -gdk_wayland_surface_set_opaque_region (GdkSurface *surface, - cairo_region_t *region) +gdk_wayland_toplevel_fullscreen (GdkWaylandToplevel *wayland_toplevel) { - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + GdkSurface *surface = GDK_SURFACE (wayland_toplevel); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); if (GDK_SURFACE_DESTROYED (surface)) return; - g_clear_pointer (&impl->opaque_region, cairo_region_destroy); - impl->opaque_region = cairo_region_reference (region); - impl->opaque_region_dirty = TRUE; -} - -static gboolean -gdk_wayland_toplevel_show_window_menu (GdkToplevel *toplevel, - GdkEvent *event) -{ - GdkSurface *surface = GDK_SURFACE (toplevel); - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (surface); - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - GdkSeat *seat; - struct wl_seat *wl_seat; - double x, y; - uint32_t serial; - - GdkEventType event_type = gdk_event_get_event_type (event); - switch ((guint) event_type) - { - case GDK_BUTTON_PRESS: - case GDK_BUTTON_RELEASE: - case GDK_TOUCH_BEGIN: - case GDK_TOUCH_END: - break; - default: - return FALSE; - } - - if (!is_realized_toplevel (impl)) - return FALSE; + wayland_toplevel->initial_fullscreen_output = NULL; - seat = gdk_event_get_seat (event); - wl_seat = gdk_wayland_seat_get_wl_seat (seat); - gdk_event_get_position (event, &x, &y); + _gdk_wayland_surface_save_size (surface); - serial = _gdk_wayland_seat_get_implicit_grab_serial (seat, event); + if (is_realized_toplevel (wayland_surface)) + { + GdkWaylandDisplay *display_wayland = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - switch (display_wayland->shell_variant) + switch (display_wayland->shell_variant) + { + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + xdg_toplevel_set_fullscreen (wayland_toplevel->display_server.xdg_toplevel, NULL); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + zxdg_toplevel_v6_set_fullscreen (wayland_toplevel->display_server.zxdg_toplevel_v6, NULL); + break; + default: + g_assert_not_reached (); + } + } + else { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_toplevel_show_window_menu (wayland_toplevel->display_server.xdg_toplevel, - wl_seat, serial, x, y); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_show_window_menu (wayland_toplevel->display_server.zxdg_toplevel_v6, - wl_seat, serial, x, y); - break; - default: - g_assert_not_reached (); + synthesize_initial_surface_state (wayland_toplevel, 0, GDK_TOPLEVEL_STATE_FULLSCREEN); } - - return TRUE; } -static gboolean -translate_gesture (GdkTitlebarGesture gesture, - enum gtk_surface1_gesture *out_gesture) +static void +gdk_wayland_toplevel_unfullscreen (GdkWaylandToplevel *wayland_toplevel) { - switch (gesture) - { - case GDK_TITLEBAR_GESTURE_DOUBLE_CLICK: - *out_gesture = GTK_SURFACE1_GESTURE_DOUBLE_CLICK; - break; + GdkSurface *surface = GDK_SURFACE (wayland_toplevel); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); - case GDK_TITLEBAR_GESTURE_RIGHT_CLICK: - *out_gesture = GTK_SURFACE1_GESTURE_RIGHT_CLICK; - break; + if (GDK_SURFACE_DESTROYED (surface)) + return; - case GDK_TITLEBAR_GESTURE_MIDDLE_CLICK: - *out_gesture = GTK_SURFACE1_GESTURE_MIDDLE_CLICK; - break; + wayland_toplevel->initial_fullscreen_output = NULL; - default: - g_warning ("Not handling unknown titlebar gesture %u", gesture); - return FALSE; - } + if (is_realized_toplevel (wayland_surface)) + { + GdkWaylandDisplay *display_wayland = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - return TRUE; + switch (display_wayland->shell_variant) + { + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + xdg_toplevel_unset_fullscreen (wayland_toplevel->display_server.xdg_toplevel); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + zxdg_toplevel_v6_unset_fullscreen (wayland_toplevel->display_server.zxdg_toplevel_v6); + break; + default: + g_assert_not_reached (); + } + } + else + { + synthesize_initial_surface_state (wayland_toplevel, GDK_TOPLEVEL_STATE_FULLSCREEN, 0); + } } -static gboolean -gdk_wayland_toplevel_titlebar_gesture (GdkToplevel *toplevel, - GdkTitlebarGesture gesture) +static void +gdk_wayland_toplevel_present (GdkToplevel *toplevel, + GdkToplevelLayout *layout) { GdkSurface *surface = GDK_SURFACE (toplevel); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (toplevel); GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - struct gtk_surface1 *gtk_surface = wayland_toplevel->display_server.gtk_surface; - enum gtk_surface1_gesture gtk_gesture; - GdkSeat *seat; - struct wl_seat *wl_seat; - uint32_t serial; - - if (!gtk_surface) - return FALSE; + gboolean pending_configure = FALSE; + gboolean maximize; + gboolean fullscreen; - if (gtk_surface1_get_version (gtk_surface) < GTK_SURFACE1_TITLEBAR_GESTURE_SINCE_VERSION) - return FALSE; + if (gdk_toplevel_layout_get_maximized (layout, &maximize)) + { + if (maximize) + gdk_wayland_toplevel_maximize (toplevel); + else + gdk_wayland_toplevel_unmaximize (toplevel); + pending_configure = TRUE; + } - if (!translate_gesture (gesture, >k_gesture)) - return FALSE; + if (gdk_toplevel_layout_get_fullscreen (layout, &fullscreen)) + { + if (fullscreen) + { + GdkMonitor *monitor; - seat = gdk_display_get_default_seat (surface->display); - wl_seat = gdk_wayland_seat_get_wl_seat (seat); + monitor = gdk_toplevel_layout_get_fullscreen_monitor (layout); + if (monitor) + gdk_wayland_toplevel_fullscreen_on_monitor (wayland_toplevel, monitor); + else + gdk_wayland_toplevel_fullscreen (wayland_toplevel); + } + else + { + gdk_wayland_toplevel_unfullscreen (wayland_toplevel); + } + pending_configure = TRUE; + } - serial = _gdk_wayland_seat_get_last_implicit_grab_serial (GDK_WAYLAND_SEAT (seat), NULL); + g_clear_pointer (&wayland_toplevel->layout, gdk_toplevel_layout_unref); + wayland_toplevel->layout = gdk_toplevel_layout_copy (layout); - gtk_surface1_titlebar_gesture (wayland_toplevel->display_server.gtk_surface, - serial, - wl_seat, - gtk_gesture); + gdk_wayland_surface_show (surface); - return TRUE; + if (!pending_configure) + { + wayland_surface->next_layout.surface_geometry_dirty = TRUE; + gdk_surface_request_layout (surface); + } } static gboolean -gdk_wayland_toplevel_supports_edge_constraints (GdkToplevel *toplevel) +gdk_wayland_toplevel_lower (GdkToplevel *toplevel) { - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - struct gtk_surface1 *gtk_surface = wayland_toplevel->display_server.gtk_surface; + return FALSE; +} - if (!gtk_surface) - return FALSE; +static void +inhibitor_active (void *data, + struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor) +{ + GdkToplevel *toplevel = GDK_TOPLEVEL (data); + GdkSurface *surface = GDK_SURFACE (toplevel); - return gtk_surface1_get_version (gtk_surface) >= GTK_SURFACE1_CONFIGURE_EDGES_SINCE_VERSION; + surface->shortcuts_inhibited = TRUE; + g_object_notify (G_OBJECT (toplevel), "shortcuts-inhibited"); } static void -gdk_wayland_surface_class_init (GdkWaylandSurfaceClass *klass) +inhibitor_inactive (void *data, + struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GdkSurfaceClass *impl_class = GDK_SURFACE_CLASS (klass); + GdkToplevel *toplevel = GDK_TOPLEVEL (data); + GdkSurface *surface = GDK_SURFACE (toplevel); - object_class->constructed = gdk_wayland_surface_constructed; - object_class->dispose = gdk_wayland_surface_dispose; - object_class->finalize = gdk_wayland_surface_finalize; + surface->shortcuts_inhibited = FALSE; + g_object_notify (G_OBJECT (toplevel), "shortcuts-inhibited"); +} - impl_class->hide = gdk_wayland_surface_hide; - impl_class->get_geometry = gdk_wayland_surface_get_geometry; - impl_class->get_root_coords = gdk_wayland_surface_get_root_coords; - impl_class->get_device_state = gdk_wayland_surface_get_device_state; - impl_class->set_input_region = gdk_wayland_surface_set_input_region; - impl_class->destroy = gdk_wayland_surface_destroy; - impl_class->beep = gdk_wayland_surface_beep; +static const struct zwp_keyboard_shortcuts_inhibitor_v1_listener +zwp_keyboard_shortcuts_inhibitor_listener = { + inhibitor_active, + inhibitor_inactive, +}; - impl_class->destroy_notify = gdk_wayland_surface_destroy_notify; - impl_class->drag_begin = _gdk_wayland_surface_drag_begin; - impl_class->get_scale_factor = gdk_wayland_surface_get_scale_factor; - impl_class->set_opaque_region = gdk_wayland_surface_set_opaque_region; - impl_class->request_layout = gdk_wayland_surface_request_layout; - impl_class->compute_size = gdk_wayland_surface_compute_size; +static struct zwp_keyboard_shortcuts_inhibitor_v1 * +gdk_wayland_toplevel_get_inhibitor (GdkWaylandToplevel *toplevel, + GdkSeat *gdk_seat) +{ + return g_hash_table_lookup (toplevel->shortcuts_inhibitors, gdk_seat); } void -_gdk_wayland_surface_set_grab_seat (GdkSurface *surface, - GdkSeat *seat) +gdk_wayland_surface_inhibit_shortcuts (GdkSurface *surface, + GdkSeat *gdk_seat) { - GdkWaylandPopup *popup; - - g_return_if_fail (surface != NULL); - - popup = GDK_WAYLAND_POPUP (surface); - popup->grab_input_seat = seat; -} + GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + struct wl_surface *wl_surface = impl->display_server.wl_surface; + struct wl_seat *seat = gdk_wayland_seat_get_wl_seat (gdk_seat); + struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor; + GdkWaylandToplevel *toplevel; -/** - * gdk_wayland_surface_get_wl_surface: (skip) - * @surface: (type GdkWaylandSurface): a `GdkSurface` - * - * Returns the Wayland `wl_surface` of a `GdkSurface`. - * - * Returns: (transfer none): a Wayland `wl_surface` - */ -struct wl_surface * -gdk_wayland_surface_get_wl_surface (GdkSurface *surface) -{ - g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (surface), NULL); + if (display->keyboard_shortcuts_inhibit == NULL) + return; - return GDK_WAYLAND_SURFACE (surface)->display_server.wl_surface; -} + if (!is_realized_toplevel (GDK_WAYLAND_SURFACE (surface))) + return; -struct wl_output * -gdk_wayland_surface_get_wl_output (GdkSurface *surface) -{ - GdkWaylandSurface *impl; + toplevel = GDK_WAYLAND_TOPLEVEL (surface); - g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (surface), NULL); + if (gdk_wayland_toplevel_get_inhibitor (toplevel, gdk_seat)) + return; /* Already inhibited */ - impl = GDK_WAYLAND_SURFACE (surface); - /* We pick the head of the list as this is the last entered output */ - if (impl->display_server.outputs) - return (struct wl_output *) impl->display_server.outputs->data; + inhibitor = + zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts ( + display->keyboard_shortcuts_inhibit, wl_surface, seat); - return NULL; + g_hash_table_insert (toplevel->shortcuts_inhibitors, gdk_seat, inhibitor); } void -gdk_wayland_surface_ensure_wl_egl_window (GdkSurface *surface) +gdk_wayland_surface_restore_shortcuts (GdkSurface *surface, + GdkSeat *gdk_seat) { + GdkWaylandToplevel *toplevel; GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor; - if (impl->display_server.egl_window == NULL) - { - impl->display_server.egl_window = - wl_egl_window_create (impl->display_server.wl_surface, - surface->width * impl->scale, - surface->height * impl->scale); - wl_surface_set_buffer_scale (impl->display_server.wl_surface, impl->scale); - - gdk_surface_set_egl_native_window (surface, impl->display_server.egl_window); - } -} + if (!is_realized_toplevel (impl)) + return; -struct gtk_surface1 * -gdk_wayland_toplevel_get_gtk_surface (GdkWaylandToplevel *wayland_toplevel) -{ - return wayland_toplevel->display_server.gtk_surface; + toplevel = GDK_WAYLAND_TOPLEVEL (impl); + + inhibitor = gdk_wayland_toplevel_get_inhibitor (toplevel, gdk_seat); + if (inhibitor == NULL) + return; /* Not inhibitted */ + + zwp_keyboard_shortcuts_inhibitor_v1_destroy (inhibitor); + g_hash_table_remove (toplevel->shortcuts_inhibitors, gdk_seat); } static void -maybe_set_gtk_surface_dbus_properties (GdkWaylandToplevel *wayland_toplevel) +gdk_wayland_toplevel_inhibit_system_shortcuts (GdkToplevel *toplevel, + GdkEvent *event) { - if (wayland_toplevel->application.was_set) - return; + struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor; + GdkSurface *surface = GDK_SURFACE (toplevel); + GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); + GdkSeat *gdk_seat; - if (wayland_toplevel->application.application_id == NULL && - wayland_toplevel->application.app_menu_path == NULL && - wayland_toplevel->application.menubar_path == NULL && - wayland_toplevel->application.window_object_path == NULL && - wayland_toplevel->application.application_object_path == NULL && - wayland_toplevel->application.unique_bus_name == NULL) + if (surface->shortcuts_inhibited) return; - gdk_wayland_toplevel_init_gtk_surface (wayland_toplevel); - if (wayland_toplevel->display_server.gtk_surface == NULL) + gdk_seat = gdk_surface_get_seat_from_event (surface, event); + gdk_wayland_surface_inhibit_shortcuts (surface, gdk_seat); + inhibitor = gdk_wayland_toplevel_get_inhibitor (wayland_toplevel, gdk_seat); + if (!inhibitor) return; - gtk_surface1_set_dbus_properties (wayland_toplevel->display_server.gtk_surface, - wayland_toplevel->application.application_id, - wayland_toplevel->application.app_menu_path, - wayland_toplevel->application.menubar_path, - wayland_toplevel->application.window_object_path, - wayland_toplevel->application.application_object_path, - wayland_toplevel->application.unique_bus_name); - wayland_toplevel->application.was_set = TRUE; + surface->current_shortcuts_inhibited_seat = gdk_seat; + zwp_keyboard_shortcuts_inhibitor_v1_add_listener + (inhibitor, &zwp_keyboard_shortcuts_inhibitor_listener, toplevel); } -void -gdk_wayland_toplevel_set_dbus_properties (GdkToplevel *toplevel, - const char *application_id, - const char *app_menu_path, - const char *menubar_path, - const char *window_object_path, - const char *application_object_path, - const char *unique_bus_name) +static void +gdk_wayland_toplevel_restore_system_shortcuts (GdkToplevel *toplevel) { - GdkWaylandToplevel *wayland_toplevel; + GdkSurface *surface = GDK_SURFACE (toplevel); - g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); + gdk_wayland_surface_restore_shortcuts (surface, surface->current_shortcuts_inhibited_seat); + surface->current_shortcuts_inhibited_seat = NULL; + surface->shortcuts_inhibited = FALSE; + g_object_notify (G_OBJECT (toplevel), "shortcuts-inhibited"); +} - wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); +static void +xdg_exported_handle_v1 (void *data, + struct zxdg_exported_v1 *zxdg_exported_v1, + const char *handle) +{ + g_task_return_pointer (G_TASK (data), g_strdup (handle), g_free); + g_object_unref (data); +} - wayland_toplevel->application.application_id = g_strdup (application_id); - wayland_toplevel->application.app_menu_path = g_strdup (app_menu_path); - wayland_toplevel->application.menubar_path = g_strdup (menubar_path); - wayland_toplevel->application.window_object_path = g_strdup (window_object_path); - wayland_toplevel->application.application_object_path = - g_strdup (application_object_path); - wayland_toplevel->application.unique_bus_name = g_strdup (unique_bus_name); +static const struct zxdg_exported_v1_listener xdg_exported_listener_v1 = { + xdg_exported_handle_v1 +}; - maybe_set_gtk_surface_dbus_properties (wayland_toplevel); +static void +xdg_exported_handle_v2 (void *data, + struct zxdg_exported_v2 *zxdg_exported_v2, + const char *handle) +{ + g_task_return_pointer (G_TASK (data), g_strdup (handle), g_free); + g_object_unref (data); } -void -_gdk_wayland_surface_offset_next_wl_buffer (GdkSurface *surface, - int x, - int y) -{ - GdkWaylandSurface *impl; +static const struct zxdg_exported_v2_listener xdg_exported_listener_v2 = { + xdg_exported_handle_v2 +}; - g_return_if_fail (GDK_IS_WAYLAND_SURFACE (surface)); +static void +gdk_wayland_toplevel_real_export_handle (GdkToplevel *toplevel, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); + GdkSurface *surface = GDK_SURFACE (toplevel); + GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (toplevel)); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); + GTask *task; - impl = GDK_WAYLAND_SURFACE (surface); + task = g_task_new (toplevel, cancellable, callback, user_data); - impl->pending_buffer_offset_x = x; - impl->pending_buffer_offset_y = y; + if (display_wayland->xdg_exporter_v2) + { + wayland_toplevel->xdg_exported_v2 = + zxdg_exporter_v2_export_toplevel (display_wayland->xdg_exporter_v2, + gdk_wayland_surface_get_wl_surface (surface)); + zxdg_exported_v2_add_listener (wayland_toplevel->xdg_exported_v2, + &xdg_exported_listener_v2, task); + } + else if (display_wayland->xdg_exporter) + { + wayland_toplevel->xdg_exported = + zxdg_exporter_v1_export (display_wayland->xdg_exporter, + gdk_wayland_surface_get_wl_surface (surface)); + zxdg_exported_v1_add_listener (wayland_toplevel->xdg_exported, + &xdg_exported_listener_v1, task); + } + else + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Exporting surface handles not supported"); + g_object_unref (task); + return; + } } -/** - * GdkWaylandToplevelExported: - * @toplevel: (type GdkWaylandToplevel): the `GdkToplevel` that is exported - * @handle: the handle - * @user_data: user data that was passed to [method@GdkWayland.WaylandToplevel.export_handle] - * - * Callback that gets called when the handle for a surface has been - * obtained from the Wayland compositor. - * - * This callback is used in [method@GdkWayland.WaylandToplevel.export_handle]. - * - * The @handle can be passed to other processes, for the purpose of - * marking surfaces as transient for out-of-process surfaces. - */ - -static gboolean -gdk_wayland_toplevel_is_exported (GdkWaylandToplevel *wayland_toplevel) +static char * +gdk_wayland_toplevel_real_export_handle_finish (GdkToplevel *toplevel, + GAsyncResult *result, + GError **error) { - return wayland_toplevel->xdg_exported != NULL || wayland_toplevel->xdg_exported_v2 != NULL; + return g_task_propagate_pointer (G_TASK (result), error); } -typedef struct { - GdkWaylandToplevelExported callback; - gpointer user_data; - GDestroyNotify destroy; -} ExportHandleData; - static void -export_handle_done (GObject *source, - GAsyncResult *result, - void *user_data) +gdk_wayland_toplevel_real_unexport_handle (GdkToplevel *toplevel) { - GdkToplevel *toplevel = GDK_TOPLEVEL (source); - ExportHandleData *data = (ExportHandleData *)user_data; - char *handle; - - handle = gdk_toplevel_export_handle_finish (toplevel, result, NULL); - data->callback (toplevel, handle, data->user_data); - g_free (handle); + GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - if (data->destroy) - data->destroy (data->user_data); + g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); + g_return_if_fail (wayland_toplevel->xdg_exported_v2 || wayland_toplevel->xdg_exported); - g_free (data); + g_clear_pointer (&wayland_toplevel->xdg_exported_v2, zxdg_exported_v2_destroy); + g_clear_pointer (&wayland_toplevel->xdg_exported, zxdg_exported_v1_destroy); } -/** - * gdk_wayland_toplevel_export_handle: - * @toplevel: (type GdkWaylandToplevel): the `GdkToplevel` to obtain a handle for - * @callback: callback to call with the handle - * @user_data: (closure): user data for @callback - * @destroy_func: destroy notify for @user_data - * - * Asynchronously obtains a handle for a surface that can be passed - * to other processes. - * - * When the handle has been obtained, @callback will be called. - * - * It is an error to call this function on a surface that is already - * exported. - * - * When the handle is no longer needed, [method@GdkWayland.WaylandToplevel.unexport_handle] - * should be called to clean up resources. - * - * The main purpose for obtaining a handle is to mark a surface - * from another surface as transient for this one, see - * [method@GdkWayland.WaylandToplevel.set_transient_for_exported]. - * - * Note that this API depends on an unstable Wayland protocol, - * and thus may require changes in the future. - * - * Return value: %TRUE if the handle has been requested, %FALSE if - * an error occurred. - */ -gboolean -gdk_wayland_toplevel_export_handle (GdkToplevel *toplevel, - GdkWaylandToplevelExported callback, - gpointer user_data, - GDestroyNotify destroy_func) +static gboolean +gdk_wayland_toplevel_show_window_menu (GdkToplevel *toplevel, + GdkEvent *event) { - ExportHandleData *data; + GdkSurface *surface = GDK_SURFACE (toplevel); + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (surface); + GdkWaylandDisplay *display_wayland = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + GdkSeat *seat; + struct wl_seat *wl_seat; + double x, y; + uint32_t serial; - g_return_val_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel), FALSE); + GdkEventType event_type = gdk_event_get_event_type (event); + switch ((guint) event_type) + { + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + case GDK_TOUCH_BEGIN: + case GDK_TOUCH_END: + break; + default: + return FALSE; + } - data = g_new (ExportHandleData, 1); - data->callback = callback; - data->user_data = user_data; - data->destroy = destroy_func; + if (!is_realized_toplevel (impl)) + return FALSE; - gdk_toplevel_export_handle (toplevel, NULL, export_handle_done, data); + seat = gdk_event_get_seat (event); + wl_seat = gdk_wayland_seat_get_wl_seat (seat); + gdk_event_get_position (event, &x, &y); - return TRUE; -} + serial = _gdk_wayland_seat_get_implicit_grab_serial (seat, event); -/** - * gdk_wayland_toplevel_unexport_handle: - * @toplevel: (type GdkWaylandToplevel): the `GdkToplevel` to unexport - * - * Destroys the handle that was obtained with - * gdk_wayland_toplevel_export_handle(). - * - * It is an error to call this function on a surface that - * does not have a handle. - * - * Note that this API depends on an unstable Wayland protocol, - * and thus may require changes in the future. - */ -void -gdk_wayland_toplevel_unexport_handle (GdkToplevel *toplevel) -{ - g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); + switch (display_wayland->shell_variant) + { + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + xdg_toplevel_show_window_menu (wayland_toplevel->display_server.xdg_toplevel, + wl_seat, serial, x, y); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + zxdg_toplevel_v6_show_window_menu (wayland_toplevel->display_server.zxdg_toplevel_v6, + wl_seat, serial, x, y); + break; + default: + g_assert_not_reached (); + } - gdk_toplevel_unexport_handle (toplevel); + return TRUE; } -static void -unset_transient_for_exported (GdkSurface *surface) +static gboolean +translate_gesture (GdkTitlebarGesture gesture, + enum gtk_surface1_gesture *out_gesture) { - if (GDK_IS_WAYLAND_TOPLEVEL (surface)) + switch (gesture) { - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); + case GDK_TITLEBAR_GESTURE_DOUBLE_CLICK: + *out_gesture = GTK_SURFACE1_GESTURE_DOUBLE_CLICK; + break; - g_clear_pointer (&toplevel->imported_transient_for, zxdg_imported_v1_destroy); - g_clear_pointer (&toplevel->imported_transient_for_v2, zxdg_imported_v2_destroy); - } -} + case GDK_TITLEBAR_GESTURE_RIGHT_CLICK: + *out_gesture = GTK_SURFACE1_GESTURE_RIGHT_CLICK; + break; -static void -xdg_imported_destroyed (void *data, - struct zxdg_imported_v1 *imported) -{ - unset_transient_for_exported (GDK_SURFACE (data)); -} + case GDK_TITLEBAR_GESTURE_MIDDLE_CLICK: + *out_gesture = GTK_SURFACE1_GESTURE_MIDDLE_CLICK; + break; -static const struct zxdg_imported_v1_listener xdg_imported_listener = { - xdg_imported_destroyed, -}; + default: + g_warning ("Not handling unknown titlebar gesture %u", gesture); + return FALSE; + } -static void -xdg_imported_v2_destroyed (void *data, - struct zxdg_imported_v2 *imported) -{ - unset_transient_for_exported (GDK_SURFACE (data)); + return TRUE; } -static const struct zxdg_imported_v2_listener xdg_imported_listener_v2 = { - xdg_imported_v2_destroyed, -}; - -/** - * gdk_wayland_toplevel_set_transient_for_exported: - * @toplevel: (type GdkWaylandToplevel): the `GdkToplevel` to make as transient - * @parent_handle_str: an exported handle for a surface - * - * Marks @toplevel as transient for the surface to which the given - * @parent_handle_str refers. - * - * Typically, the handle will originate from a - * [method@GdkWayland.WaylandToplevel.export_handle] call in another process. - * - * Note that this API depends on an unstable Wayland protocol, - * and thus may require changes in the future. - * - * Return value: %TRUE if the surface has been marked as transient, - * %FALSE if an error occurred. - */ -gboolean -gdk_wayland_toplevel_set_transient_for_exported (GdkToplevel *toplevel, - const char *parent_handle_str) +static gboolean +gdk_wayland_toplevel_titlebar_gesture (GdkToplevel *toplevel, + GdkTitlebarGesture gesture) { + GdkSurface *surface = GDK_SURFACE (toplevel); GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (toplevel)); - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); + struct gtk_surface1 *gtk_surface = wayland_toplevel->display_server.gtk_surface; + enum gtk_surface1_gesture gtk_gesture; + GdkSeat *seat; + struct wl_seat *wl_seat; + uint32_t serial; - g_return_val_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel), FALSE); - g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY (display), FALSE); + if (!gtk_surface) + return FALSE; - display_wayland = GDK_WAYLAND_DISPLAY (display); + if (gtk_surface1_get_version (gtk_surface) < GTK_SURFACE1_TITLEBAR_GESTURE_SINCE_VERSION) + return FALSE; - if (!display_wayland->xdg_importer && !display_wayland->xdg_importer_v2) - { - g_warning ("Server is missing xdg_foreign support"); - return FALSE; - } + if (!translate_gesture (gesture, >k_gesture)) + return FALSE; - gdk_wayland_toplevel_set_transient_for (wayland_toplevel, NULL); + seat = gdk_display_get_default_seat (surface->display); + wl_seat = gdk_wayland_seat_get_wl_seat (seat); - if (display_wayland->xdg_importer) - { - wayland_toplevel->imported_transient_for = - zxdg_importer_v1_import (display_wayland->xdg_importer, parent_handle_str); - zxdg_imported_v1_add_listener (wayland_toplevel->imported_transient_for, - &xdg_imported_listener, - toplevel); - } - else - { - wayland_toplevel->imported_transient_for_v2 = - zxdg_importer_v2_import_toplevel (display_wayland->xdg_importer_v2, parent_handle_str); - zxdg_imported_v2_add_listener (wayland_toplevel->imported_transient_for_v2, - &xdg_imported_listener_v2, - toplevel); - } + serial = _gdk_wayland_seat_get_last_implicit_grab_serial (GDK_WAYLAND_SEAT (seat), NULL); - gdk_wayland_toplevel_sync_parent_of_imported (wayland_toplevel); + gtk_surface1_titlebar_gesture (wayland_toplevel->display_server.gtk_surface, + serial, + wl_seat, + gtk_gesture); return TRUE; } -static struct zwp_keyboard_shortcuts_inhibitor_v1 * -gdk_wayland_toplevel_get_inhibitor (GdkWaylandToplevel *toplevel, - GdkSeat *gdk_seat) +static gboolean +gdk_wayland_toplevel_supports_edge_constraints (GdkToplevel *toplevel) { - return g_hash_table_lookup (toplevel->shortcuts_inhibitors, gdk_seat); + GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); + struct gtk_surface1 *gtk_surface = wayland_toplevel->display_server.gtk_surface; + + if (!gtk_surface) + return FALSE; + + return gtk_surface1_get_version (gtk_surface) >= GTK_SURFACE1_CONFIGURE_EDGES_SINCE_VERSION; } -void -gdk_wayland_surface_inhibit_shortcuts (GdkSurface *surface, - GdkSeat *gdk_seat) +static void +gdk_wayland_toplevel_begin_resize (GdkToplevel *toplevel, + GdkSurfaceEdge edge, + GdkDevice *device, + int button, + double x, + double y, + guint32 timestamp) { - GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - struct wl_surface *wl_surface = impl->display_server.wl_surface; - struct wl_seat *seat = gdk_wayland_seat_get_wl_seat (gdk_seat); - struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor; - GdkWaylandToplevel *toplevel; - - if (display->keyboard_shortcuts_inhibit == NULL) - return; + GdkSurface *surface = GDK_SURFACE (toplevel); + GdkWaylandSurface *impl; + GdkWaylandToplevel *wayland_toplevel; + GdkWaylandDisplay *display_wayland; + GdkEventSequence *sequence; + uint32_t resize_edges, serial; - if (!is_realized_toplevel (GDK_WAYLAND_SURFACE (surface))) + if (GDK_SURFACE_DESTROYED (surface)) return; - toplevel = GDK_WAYLAND_TOPLEVEL (surface); + switch (edge) + { + case GDK_SURFACE_EDGE_NORTH_WEST: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT; + break; - if (gdk_wayland_toplevel_get_inhibitor (toplevel, gdk_seat)) - return; /* Already inhibited */ + case GDK_SURFACE_EDGE_NORTH: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP; + break; - inhibitor = - zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts ( - display->keyboard_shortcuts_inhibit, wl_surface, seat); + case GDK_SURFACE_EDGE_NORTH_EAST: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT; + break; - g_hash_table_insert (toplevel->shortcuts_inhibitors, gdk_seat, inhibitor); -} + case GDK_SURFACE_EDGE_WEST: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT; + break; -void -gdk_wayland_surface_restore_shortcuts (GdkSurface *surface, - GdkSeat *gdk_seat) -{ - GdkWaylandToplevel *toplevel; - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor; + case GDK_SURFACE_EDGE_EAST: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT; + break; - if (!is_realized_toplevel (impl)) - return; + case GDK_SURFACE_EDGE_SOUTH_WEST: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT; + break; - toplevel = GDK_WAYLAND_TOPLEVEL (impl); + case GDK_SURFACE_EDGE_SOUTH: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM; + break; - inhibitor = gdk_wayland_toplevel_get_inhibitor (toplevel, gdk_seat); - if (inhibitor == NULL) - return; /* Not inhibitted */ + case GDK_SURFACE_EDGE_SOUTH_EAST: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT; + break; - zwp_keyboard_shortcuts_inhibitor_v1_destroy (inhibitor); - g_hash_table_remove (toplevel->shortcuts_inhibitors, gdk_seat); -} + default: + g_warning ("gdk_toplevel_begin_resize: bad resize edge %d!", edge); + return; + } -#define LAST_PROP 1 + impl = GDK_WAYLAND_SURFACE (surface); + wayland_toplevel = GDK_WAYLAND_TOPLEVEL (surface); + display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); -static void -gdk_wayland_popup_init (GdkWaylandPopup *popup) -{ -} + if (!is_realized_toplevel (impl)) + return; -static void -gdk_wayland_popup_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GdkSurface *surface = GDK_SURFACE (object); + serial = _gdk_wayland_seat_get_last_implicit_grab_serial (GDK_WAYLAND_SEAT (gdk_device_get_seat (device)), + &sequence); - switch (prop_id) + switch (display_wayland->shell_variant) { - case LAST_PROP + GDK_POPUP_PROP_PARENT: - g_value_set_object (value, surface->parent); + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + xdg_toplevel_resize (wayland_toplevel->display_server.xdg_toplevel, + gdk_wayland_device_get_wl_seat (device), + serial, resize_edges); break; - - case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE: - g_value_set_boolean (value, surface->autohide); + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + zxdg_toplevel_v6_resize (wayland_toplevel->display_server.zxdg_toplevel_v6, + gdk_wayland_device_get_wl_seat (device), + serial, resize_edges); break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; + g_assert_not_reached (); } + + if (sequence) + gdk_wayland_device_unset_touch_grab (device, sequence); } static void -gdk_wayland_popup_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) +gdk_wayland_toplevel_begin_move (GdkToplevel *toplevel, + GdkDevice *device, + int button, + double x, + double y, + guint32 timestamp) { - GdkSurface *surface = GDK_SURFACE (object); + GdkSurface *surface = GDK_SURFACE (toplevel); + GdkWaylandSurface *impl; + GdkWaylandToplevel *wayland_toplevel; + GdkWaylandDisplay *display_wayland; + GdkEventSequence *sequence; + uint32_t serial; - switch (prop_id) + if (GDK_SURFACE_DESTROYED (surface)) + return; + + impl = GDK_WAYLAND_SURFACE (surface); + wayland_toplevel = GDK_WAYLAND_TOPLEVEL (surface); + display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + + if (!is_realized_toplevel (impl)) + return; + + serial = _gdk_wayland_seat_get_last_implicit_grab_serial (GDK_WAYLAND_SEAT (gdk_device_get_seat (device)), + &sequence); + switch (display_wayland->shell_variant) { - case LAST_PROP + GDK_POPUP_PROP_PARENT: - surface->parent = g_value_dup_object (value); - if (surface->parent != NULL) - surface->parent->children = g_list_prepend (surface->parent->children, surface); + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + xdg_toplevel_move (wayland_toplevel->display_server.xdg_toplevel, + gdk_wayland_device_get_wl_seat (device), + serial); break; - - case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE: - surface->autohide = g_value_get_boolean (value); + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + zxdg_toplevel_v6_move (wayland_toplevel->display_server.zxdg_toplevel_v6, + gdk_wayland_device_get_wl_seat (device), + serial); break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; + g_assert_not_reached (); } + + if (sequence) + gdk_wayland_device_unset_touch_grab (device, sequence); } static void -gdk_wayland_popup_class_init (GdkWaylandPopupClass *class) +token_done (gpointer data, + struct xdg_activation_token_v1 *provider, + const char *token) { - GObjectClass *object_class = G_OBJECT_CLASS (class); - - object_class->get_property = gdk_wayland_popup_get_property; - object_class->set_property = gdk_wayland_popup_set_property; + char **token_out = data; - gdk_popup_install_properties (object_class, 1); + *token_out = g_strdup (token); } -static gboolean -gdk_wayland_popup_present (GdkPopup *popup, - int width, - int height, - GdkPopupLayout *layout) -{ - return gdk_wayland_surface_present_popup (GDK_WAYLAND_POPUP (popup), width, height, layout); -} +static const struct xdg_activation_token_v1_listener token_listener = { + token_done, +}; -static GdkGravity -gdk_wayland_popup_get_surface_anchor (GdkPopup *popup) +static void +gdk_wayland_toplevel_focus (GdkToplevel *toplevel, + guint32 timestamp) { - return GDK_SURFACE (popup)->popup.surface_anchor; -} + GdkSurface *surface = GDK_SURFACE (toplevel); + GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (toplevel); + GdkDisplay *display = gdk_surface_get_display (surface); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); + gchar *startup_id = NULL; -static GdkGravity -gdk_wayland_popup_get_rect_anchor (GdkPopup *popup) -{ - return GDK_SURFACE (popup)->popup.rect_anchor; -} + startup_id = g_steal_pointer (&display_wayland->startup_notification_id); -static int -gdk_wayland_popup_get_position_x (GdkPopup *popup) -{ - return GDK_SURFACE (popup)->x; -} + if (display_wayland->xdg_activation) + { + GdkWaylandSeat *seat = + GDK_WAYLAND_SEAT (gdk_display_get_default_seat (display)); -static int -gdk_wayland_popup_get_position_y (GdkPopup *popup) -{ - return GDK_SURFACE (popup)->y; + /* If the focus request does not have a startup ID associated, get a + * new token to activate the window. + */ + if (!startup_id) + { + struct xdg_activation_token_v1 *token; + struct wl_event_queue *event_queue; + struct wl_surface *wl_surface = NULL; + GdkSurface *focus_surface; + + event_queue = wl_display_create_queue (display_wayland->wl_display); + + token = xdg_activation_v1_get_activation_token (display_wayland->xdg_activation); + wl_proxy_set_queue ((struct wl_proxy *) token, event_queue); + + xdg_activation_token_v1_add_listener (token, + &token_listener, + &startup_id); + xdg_activation_token_v1_set_serial (token, + _gdk_wayland_seat_get_last_implicit_grab_serial (seat, NULL), + gdk_wayland_seat_get_wl_seat (GDK_SEAT (seat))); + + + focus_surface = gdk_wayland_device_get_focus (gdk_seat_get_keyboard (GDK_SEAT (seat))); + if (focus_surface) + wl_surface = gdk_wayland_surface_get_wl_surface (focus_surface); + if (wl_surface) + xdg_activation_token_v1_set_surface (token, wl_surface); + + xdg_activation_token_v1_commit (token); + + while (startup_id == NULL) + wl_display_dispatch_queue (display_wayland->wl_display, event_queue); + + xdg_activation_token_v1_destroy (token); + wl_event_queue_destroy (event_queue); + } + + xdg_activation_v1_activate (display_wayland->xdg_activation, + startup_id, + wayland_surface->display_server.wl_surface); + } + else if (wayland_toplevel->display_server.gtk_surface) + { + if (timestamp != GDK_CURRENT_TIME) + gtk_surface1_present (wayland_toplevel->display_server.gtk_surface, timestamp); + else if (startup_id && display_wayland->gtk_shell_version >= 3) + gtk_surface1_request_focus (wayland_toplevel->display_server.gtk_surface, + startup_id); + } + + g_free (startup_id); } static void -gdk_wayland_popup_iface_init (GdkPopupInterface *iface) +gdk_wayland_toplevel_iface_init (GdkToplevelInterface *iface) { - iface->present = gdk_wayland_popup_present; - iface->get_surface_anchor = gdk_wayland_popup_get_surface_anchor; - iface->get_rect_anchor = gdk_wayland_popup_get_rect_anchor; - iface->get_position_x = gdk_wayland_popup_get_position_x; - iface->get_position_y = gdk_wayland_popup_get_position_y; + iface->present = gdk_wayland_toplevel_present; + iface->minimize = gdk_wayland_toplevel_minimize; + iface->lower = gdk_wayland_toplevel_lower; + iface->focus = gdk_wayland_toplevel_focus; + iface->show_window_menu = gdk_wayland_toplevel_show_window_menu; + iface->titlebar_gesture = gdk_wayland_toplevel_titlebar_gesture; + iface->supports_edge_constraints = gdk_wayland_toplevel_supports_edge_constraints; + iface->inhibit_system_shortcuts = gdk_wayland_toplevel_inhibit_system_shortcuts; + iface->restore_system_shortcuts = gdk_wayland_toplevel_restore_system_shortcuts; + iface->begin_resize = gdk_wayland_toplevel_begin_resize; + iface->begin_move = gdk_wayland_toplevel_begin_move; + iface->export_handle = gdk_wayland_toplevel_real_export_handle; + iface->export_handle_finish = gdk_wayland_toplevel_real_export_handle_finish; + iface->unexport_handle = gdk_wayland_toplevel_real_unexport_handle; } -static void -gdk_wayland_toplevel_init (GdkWaylandToplevel *toplevel) +/* }}} */ +/* {{{ Private Toplevel API */ + +struct gtk_surface1 * +gdk_wayland_toplevel_get_gtk_surface (GdkWaylandToplevel *wayland_toplevel) { - toplevel->initial_fullscreen_output = NULL; - toplevel->shortcuts_inhibitors = g_hash_table_new (NULL, NULL); + return wayland_toplevel->display_server.gtk_surface; } static void -gdk_wayland_toplevel_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) +maybe_set_gtk_surface_dbus_properties (GdkWaylandToplevel *wayland_toplevel) { - GdkSurface *surface = GDK_SURFACE (object); - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - - switch (prop_id) - { - case LAST_PROP + GDK_TOPLEVEL_PROP_TITLE: - gdk_wayland_toplevel_set_title (toplevel, g_value_get_string (value)); - g_object_notify_by_pspec (G_OBJECT (surface), pspec); - break; - - case LAST_PROP + GDK_TOPLEVEL_PROP_STARTUP_ID: - gdk_wayland_surface_set_startup_id (surface, g_value_get_string (value)); - g_object_notify_by_pspec (G_OBJECT (surface), pspec); - break; + if (wayland_toplevel->application.was_set) + return; - case LAST_PROP + GDK_TOPLEVEL_PROP_TRANSIENT_FOR: - gdk_wayland_toplevel_set_transient_for (toplevel, - g_value_get_object (value)); - g_object_notify_by_pspec (G_OBJECT (surface), pspec); - break; + if (wayland_toplevel->application.application_id == NULL && + wayland_toplevel->application.app_menu_path == NULL && + wayland_toplevel->application.menubar_path == NULL && + wayland_toplevel->application.window_object_path == NULL && + wayland_toplevel->application.application_object_path == NULL && + wayland_toplevel->application.unique_bus_name == NULL) + return; - case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL: - gdk_wayland_toplevel_set_modal_hint (toplevel, g_value_get_boolean (value)); - g_object_notify_by_pspec (G_OBJECT (surface), pspec); - break; + gdk_wayland_toplevel_init_gtk_surface (wayland_toplevel); + if (wayland_toplevel->display_server.gtk_surface == NULL) + return; - case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST: - break; + gtk_surface1_set_dbus_properties (wayland_toplevel->display_server.gtk_surface, + wayland_toplevel->application.application_id, + wayland_toplevel->application.app_menu_path, + wayland_toplevel->application.menubar_path, + wayland_toplevel->application.window_object_path, + wayland_toplevel->application.application_object_path, + wayland_toplevel->application.unique_bus_name); + wayland_toplevel->application.was_set = TRUE; +} - case LAST_PROP + GDK_TOPLEVEL_PROP_DECORATED: - break; +void +gdk_wayland_toplevel_set_dbus_properties (GdkToplevel *toplevel, + const char *application_id, + const char *app_menu_path, + const char *menubar_path, + const char *window_object_path, + const char *application_object_path, + const char *unique_bus_name) +{ + GdkWaylandToplevel *wayland_toplevel; - case LAST_PROP + GDK_TOPLEVEL_PROP_DELETABLE: - break; + g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); - case LAST_PROP + GDK_TOPLEVEL_PROP_FULLSCREEN_MODE: - surface->fullscreen_mode = g_value_get_enum (value); - g_object_notify_by_pspec (G_OBJECT (surface), pspec); - break; + wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - case LAST_PROP + GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED: - break; + wayland_toplevel->application.application_id = g_strdup (application_id); + wayland_toplevel->application.app_menu_path = g_strdup (app_menu_path); + wayland_toplevel->application.menubar_path = g_strdup (menubar_path); + wayland_toplevel->application.window_object_path = g_strdup (window_object_path); + wayland_toplevel->application.application_object_path = + g_strdup (application_object_path); + wayland_toplevel->application.unique_bus_name = g_strdup (unique_bus_name); - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } + maybe_set_gtk_surface_dbus_properties (wayland_toplevel); } -static void -gdk_wayland_toplevel_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GdkSurface *surface = GDK_SURFACE (object); - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - - switch (prop_id) - { - case LAST_PROP + GDK_TOPLEVEL_PROP_STATE: - g_value_set_flags (value, surface->state); - break; +/* }}} */ +/* {{{ Toplevel API */ - case LAST_PROP + GDK_TOPLEVEL_PROP_TITLE: - g_value_set_string (value, toplevel->title); - break; +/** + * gdk_wayland_toplevel_set_application_id: + * @toplevel: (type GdkWaylandToplevel): a `GdkToplevel` + * @application_id: the application id for the @toplevel + * + * Sets the application id on a `GdkToplevel`. + */ +void +gdk_wayland_toplevel_set_application_id (GdkToplevel *toplevel, + const char *application_id) +{ + GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); + GdkWaylandSurface *impl; + GdkWaylandDisplay *display_wayland; - case LAST_PROP + GDK_TOPLEVEL_PROP_STARTUP_ID: - g_value_set_string (value, ""); - break; + g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); - case LAST_PROP + GDK_TOPLEVEL_PROP_TRANSIENT_FOR: - g_value_set_object (value, toplevel->transient_for); - break; + g_return_if_fail (application_id != NULL); - case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL: - g_value_set_boolean (value, surface->modal_hint); - break; + if (GDK_SURFACE_DESTROYED (toplevel)) + return; - case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST: - g_value_set_pointer (value, NULL); - break; + impl = GDK_WAYLAND_SURFACE (toplevel); - case LAST_PROP + GDK_TOPLEVEL_PROP_DECORATED: - break; + if (!is_realized_toplevel (impl)) + return; - case LAST_PROP + GDK_TOPLEVEL_PROP_DELETABLE: - break; + wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); + display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); - case LAST_PROP + GDK_TOPLEVEL_PROP_FULLSCREEN_MODE: - g_value_set_enum (value, surface->fullscreen_mode); + switch (display_wayland->shell_variant) + { + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + xdg_toplevel_set_app_id (wayland_toplevel->display_server.xdg_toplevel, application_id); break; - - case LAST_PROP + GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED: - g_value_set_boolean (value, surface->shortcuts_inhibited); + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + zxdg_toplevel_v6_set_app_id (wayland_toplevel->display_server.zxdg_toplevel_v6, application_id); break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; + g_assert_not_reached (); } } -static void -gdk_wayland_toplevel_finalize (GObject *object) +void +gdk_wayland_toplevel_announce_csd (GdkToplevel *toplevel) { - GdkWaylandToplevel *wayland_toplevel; - - g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (object)); - - wayland_toplevel = GDK_WAYLAND_TOPLEVEL (object); - - if (gdk_wayland_toplevel_is_exported (wayland_toplevel)) - gdk_wayland_toplevel_unexport_handle (GDK_TOPLEVEL (wayland_toplevel)); - - g_free (wayland_toplevel->application.application_id); - g_free (wayland_toplevel->application.app_menu_path); - g_free (wayland_toplevel->application.menubar_path); - g_free (wayland_toplevel->application.window_object_path); - g_free (wayland_toplevel->application.application_object_path); - g_free (wayland_toplevel->application.unique_bus_name); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); + GdkWaylandToplevel *toplevel_wayland; - g_free (wayland_toplevel->title); - g_clear_pointer (&wayland_toplevel->shortcuts_inhibitors, g_hash_table_unref); + g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); + toplevel_wayland = GDK_WAYLAND_TOPLEVEL (toplevel); - G_OBJECT_CLASS (gdk_wayland_toplevel_parent_class)->finalize (object); + if (!display_wayland->server_decoration_manager) + return; + toplevel_wayland->server_decoration = + org_kde_kwin_server_decoration_manager_create (display_wayland->server_decoration_manager, + gdk_wayland_surface_get_wl_surface (GDK_SURFACE (toplevel_wayland))); + if (toplevel_wayland->server_decoration) + org_kde_kwin_server_decoration_request_mode (toplevel_wayland->server_decoration, + ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT); } -static void -gdk_wayland_toplevel_class_init (GdkWaylandToplevelClass *class) +void +gdk_wayland_toplevel_announce_ssd (GdkToplevel *toplevel) { - GObjectClass *object_class = G_OBJECT_CLASS (class); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); + GdkWaylandToplevel *toplevel_wayland; - object_class->get_property = gdk_wayland_toplevel_get_property; - object_class->set_property = gdk_wayland_toplevel_set_property; - object_class->finalize = gdk_wayland_toplevel_finalize; + g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); + toplevel_wayland = GDK_WAYLAND_TOPLEVEL (toplevel); - gdk_toplevel_install_properties (object_class, 1); + if (!display_wayland->server_decoration_manager) + return; + toplevel_wayland->server_decoration = + org_kde_kwin_server_decoration_manager_create (display_wayland->server_decoration_manager, + gdk_wayland_surface_get_wl_surface (GDK_SURFACE (toplevel_wayland))); + if (toplevel_wayland->server_decoration) + org_kde_kwin_server_decoration_request_mode (toplevel_wayland->server_decoration, + ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER); } -static void -gdk_wayland_toplevel_present (GdkToplevel *toplevel, - GdkToplevelLayout *layout) +gboolean +gdk_wayland_toplevel_inhibit_idle (GdkToplevel *toplevel) { - GdkSurface *surface = GDK_SURFACE (toplevel); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (toplevel); - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - gboolean pending_configure = FALSE; - gboolean maximize; - gboolean fullscreen; + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); + GdkWaylandToplevel *wayland_toplevel; - if (gdk_toplevel_layout_get_maximized (layout, &maximize)) - { - if (maximize) - gdk_wayland_toplevel_maximize (toplevel); - else - gdk_wayland_toplevel_unmaximize (toplevel); - pending_configure = TRUE; - } + g_return_val_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel), FALSE); + wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - if (gdk_toplevel_layout_get_fullscreen (layout, &fullscreen)) + if (!display_wayland->idle_inhibit_manager) + return FALSE; + + if (!wayland_toplevel->idle_inhibitor) { - if (fullscreen) - { - GdkMonitor *monitor; + g_assert (wayland_toplevel->idle_inhibitor_refcount == 0); - monitor = gdk_toplevel_layout_get_fullscreen_monitor (layout); - if (monitor) - gdk_wayland_toplevel_fullscreen_on_monitor (wayland_toplevel, monitor); - else - gdk_wayland_toplevel_fullscreen (wayland_toplevel); - } - else - { - gdk_wayland_toplevel_unfullscreen (wayland_toplevel); - } - pending_configure = TRUE; + wayland_toplevel->idle_inhibitor = + zwp_idle_inhibit_manager_v1_create_inhibitor (display_wayland->idle_inhibit_manager, + gdk_wayland_surface_get_wl_surface (GDK_SURFACE (wayland_toplevel))); } + ++wayland_toplevel->idle_inhibitor_refcount; - g_clear_pointer (&wayland_toplevel->layout, gdk_toplevel_layout_unref); - wayland_toplevel->layout = gdk_toplevel_layout_copy (layout); + return TRUE; +} - gdk_wayland_surface_show (surface); +void +gdk_wayland_toplevel_uninhibit_idle (GdkToplevel *toplevel) +{ + GdkWaylandToplevel *wayland_toplevel; - if (!pending_configure) + g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); + wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); + + g_assert (wayland_toplevel->idle_inhibitor && + wayland_toplevel->idle_inhibitor_refcount > 0); + + if (--wayland_toplevel->idle_inhibitor_refcount == 0) { - wayland_surface->next_layout.surface_geometry_dirty = TRUE; - gdk_surface_request_layout (surface); + g_clear_pointer (&wayland_toplevel->idle_inhibitor, + zwp_idle_inhibitor_v1_destroy); } } +/** + * GdkWaylandToplevelExported: + * @toplevel: (type GdkWaylandToplevel): the `GdkToplevel` that is exported + * @handle: the handle + * @user_data: user data that was passed to [method@GdkWayland.WaylandToplevel.export_handle] + * + * Callback that gets called when the handle for a surface has been + * obtained from the Wayland compositor. + * + * This callback is used in [method@GdkWayland.WaylandToplevel.export_handle]. + * + * The @handle can be passed to other processes, for the purpose of + * marking surfaces as transient for out-of-process surfaces. + */ + static gboolean -gdk_wayland_toplevel_lower (GdkToplevel *toplevel) +gdk_wayland_toplevel_is_exported (GdkWaylandToplevel *wayland_toplevel) { - return FALSE; + return wayland_toplevel->xdg_exported != NULL || wayland_toplevel->xdg_exported_v2 != NULL; } +typedef struct { + GdkWaylandToplevelExported callback; + gpointer user_data; + GDestroyNotify destroy; +} ExportHandleData; + static void -inhibitor_active (void *data, - struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor) +export_handle_done (GObject *source, + GAsyncResult *result, + void *user_data) { - GdkToplevel *toplevel = GDK_TOPLEVEL (data); - GdkSurface *surface = GDK_SURFACE (toplevel); + GdkToplevel *toplevel = GDK_TOPLEVEL (source); + ExportHandleData *data = (ExportHandleData *)user_data; + char *handle; - surface->shortcuts_inhibited = TRUE; - g_object_notify (G_OBJECT (toplevel), "shortcuts-inhibited"); + handle = gdk_toplevel_export_handle_finish (toplevel, result, NULL); + data->callback (toplevel, handle, data->user_data); + g_free (handle); + + if (data->destroy) + data->destroy (data->user_data); + + g_free (data); } -static void -inhibitor_inactive (void *data, - struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor) +/** + * gdk_wayland_toplevel_export_handle: + * @toplevel: (type GdkWaylandToplevel): the `GdkToplevel` to obtain a handle for + * @callback: callback to call with the handle + * @user_data: (closure): user data for @callback + * @destroy_func: destroy notify for @user_data + * + * Asynchronously obtains a handle for a surface that can be passed + * to other processes. + * + * When the handle has been obtained, @callback will be called. + * + * It is an error to call this function on a surface that is already + * exported. + * + * When the handle is no longer needed, [method@GdkWayland.WaylandToplevel.unexport_handle] + * should be called to clean up resources. + * + * The main purpose for obtaining a handle is to mark a surface + * from another surface as transient for this one, see + * [method@GdkWayland.WaylandToplevel.set_transient_for_exported]. + * + * Note that this API depends on an unstable Wayland protocol, + * and thus may require changes in the future. + * + * Return value: %TRUE if the handle has been requested, %FALSE if + * an error occurred. + */ +gboolean +gdk_wayland_toplevel_export_handle (GdkToplevel *toplevel, + GdkWaylandToplevelExported callback, + gpointer user_data, + GDestroyNotify destroy_func) { - GdkToplevel *toplevel = GDK_TOPLEVEL (data); - GdkSurface *surface = GDK_SURFACE (toplevel); + ExportHandleData *data; - surface->shortcuts_inhibited = FALSE; - g_object_notify (G_OBJECT (toplevel), "shortcuts-inhibited"); -} + g_return_val_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel), FALSE); -static const struct zwp_keyboard_shortcuts_inhibitor_v1_listener -zwp_keyboard_shortcuts_inhibitor_listener = { - inhibitor_active, - inhibitor_inactive, -}; + data = g_new (ExportHandleData, 1); + data->callback = callback; + data->user_data = user_data; + data->destroy = destroy_func; -static void -gdk_wayland_toplevel_inhibit_system_shortcuts (GdkToplevel *toplevel, - GdkEvent *event) -{ - struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor; - GdkSurface *surface = GDK_SURFACE (toplevel); - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - GdkSeat *gdk_seat; + gdk_toplevel_export_handle (toplevel, NULL, export_handle_done, data); - if (surface->shortcuts_inhibited) - return; + return TRUE; +} - gdk_seat = gdk_surface_get_seat_from_event (surface, event); - gdk_wayland_surface_inhibit_shortcuts (surface, gdk_seat); - inhibitor = gdk_wayland_toplevel_get_inhibitor (wayland_toplevel, gdk_seat); - if (!inhibitor) - return; +/** + * gdk_wayland_toplevel_unexport_handle: + * @toplevel: (type GdkWaylandToplevel): the `GdkToplevel` to unexport + * + * Destroys the handle that was obtained with + * gdk_wayland_toplevel_export_handle(). + * + * It is an error to call this function on a surface that + * does not have a handle. + * + * Note that this API depends on an unstable Wayland protocol, + * and thus may require changes in the future. + */ +void +gdk_wayland_toplevel_unexport_handle (GdkToplevel *toplevel) +{ + g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); - surface->current_shortcuts_inhibited_seat = gdk_seat; - zwp_keyboard_shortcuts_inhibitor_v1_add_listener - (inhibitor, &zwp_keyboard_shortcuts_inhibitor_listener, toplevel); + gdk_toplevel_unexport_handle (toplevel); } static void -gdk_wayland_toplevel_restore_system_shortcuts (GdkToplevel *toplevel) +unset_transient_for_exported (GdkSurface *surface) { - GdkSurface *surface = GDK_SURFACE (toplevel); + if (GDK_IS_WAYLAND_TOPLEVEL (surface)) + { + GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - gdk_wayland_surface_restore_shortcuts (surface, surface->current_shortcuts_inhibited_seat); - surface->current_shortcuts_inhibited_seat = NULL; - surface->shortcuts_inhibited = FALSE; - g_object_notify (G_OBJECT (toplevel), "shortcuts-inhibited"); + g_clear_pointer (&toplevel->imported_transient_for, zxdg_imported_v1_destroy); + g_clear_pointer (&toplevel->imported_transient_for_v2, zxdg_imported_v2_destroy); + } } static void -xdg_exported_handle_v1 (void *data, - struct zxdg_exported_v1 *zxdg_exported_v1, - const char *handle) +xdg_imported_destroyed (void *data, + struct zxdg_imported_v1 *imported) { - g_task_return_pointer (G_TASK (data), g_strdup (handle), g_free); - g_object_unref (data); + unset_transient_for_exported (GDK_SURFACE (data)); } -static const struct zxdg_exported_v1_listener xdg_exported_listener_v1 = { - xdg_exported_handle_v1 +static const struct zxdg_imported_v1_listener xdg_imported_listener = { + xdg_imported_destroyed, }; static void -xdg_exported_handle_v2 (void *data, - struct zxdg_exported_v2 *zxdg_exported_v2, - const char *handle) +xdg_imported_v2_destroyed (void *data, + struct zxdg_imported_v2 *imported) { - g_task_return_pointer (G_TASK (data), g_strdup (handle), g_free); - g_object_unref (data); + unset_transient_for_exported (GDK_SURFACE (data)); } -static const struct zxdg_exported_v2_listener xdg_exported_listener_v2 = { - xdg_exported_handle_v2 +static const struct zxdg_imported_v2_listener xdg_imported_listener_v2 = { + xdg_imported_v2_destroyed, }; -static void -gdk_wayland_toplevel_real_export_handle (GdkToplevel *toplevel, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +/** + * gdk_wayland_toplevel_set_transient_for_exported: + * @toplevel: (type GdkWaylandToplevel): the `GdkToplevel` to make as transient + * @parent_handle_str: an exported handle for a surface + * + * Marks @toplevel as transient for the surface to which the given + * @parent_handle_str refers. + * + * Typically, the handle will originate from a + * [method@GdkWayland.WaylandToplevel.export_handle] call in another process. + * + * Note that this API depends on an unstable Wayland protocol, + * and thus may require changes in the future. + * + * Return value: %TRUE if the surface has been marked as transient, + * %FALSE if an error occurred. + */ +gboolean +gdk_wayland_toplevel_set_transient_for_exported (GdkToplevel *toplevel, + const char *parent_handle_str) { GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - GdkSurface *surface = GDK_SURFACE (toplevel); GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (toplevel)); GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); - GTask *task; - task = g_task_new (toplevel, cancellable, callback, user_data); + g_return_val_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel), FALSE); + g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY (display), FALSE); - if (display_wayland->xdg_exporter_v2) + display_wayland = GDK_WAYLAND_DISPLAY (display); + + if (!display_wayland->xdg_importer && !display_wayland->xdg_importer_v2) { - wayland_toplevel->xdg_exported_v2 = - zxdg_exporter_v2_export_toplevel (display_wayland->xdg_exporter_v2, - gdk_wayland_surface_get_wl_surface (surface)); - zxdg_exported_v2_add_listener (wayland_toplevel->xdg_exported_v2, - &xdg_exported_listener_v2, task); + g_warning ("Server is missing xdg_foreign support"); + return FALSE; } - else if (display_wayland->xdg_exporter) + + gdk_wayland_toplevel_set_transient_for (wayland_toplevel, NULL); + + if (display_wayland->xdg_importer) { - wayland_toplevel->xdg_exported = - zxdg_exporter_v1_export (display_wayland->xdg_exporter, - gdk_wayland_surface_get_wl_surface (surface)); - zxdg_exported_v1_add_listener (wayland_toplevel->xdg_exported, - &xdg_exported_listener_v1, task); + wayland_toplevel->imported_transient_for = + zxdg_importer_v1_import (display_wayland->xdg_importer, parent_handle_str); + zxdg_imported_v1_add_listener (wayland_toplevel->imported_transient_for, + &xdg_imported_listener, + toplevel); } else { - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Exporting surface handles not supported"); - g_object_unref (task); - return; + wayland_toplevel->imported_transient_for_v2 = + zxdg_importer_v2_import_toplevel (display_wayland->xdg_importer_v2, parent_handle_str); + zxdg_imported_v2_add_listener (wayland_toplevel->imported_transient_for_v2, + &xdg_imported_listener_v2, + toplevel); } -} - -static char * -gdk_wayland_toplevel_real_export_handle_finish (GdkToplevel *toplevel, - GAsyncResult *result, - GError **error) -{ - return g_task_propagate_pointer (G_TASK (result), error); -} - -static void -gdk_wayland_toplevel_real_unexport_handle (GdkToplevel *toplevel) -{ - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - - g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); - g_return_if_fail (wayland_toplevel->xdg_exported_v2 || wayland_toplevel->xdg_exported); - g_clear_pointer (&wayland_toplevel->xdg_exported_v2, zxdg_exported_v2_destroy); - g_clear_pointer (&wayland_toplevel->xdg_exported, zxdg_exported_v1_destroy); -} + gdk_wayland_toplevel_sync_parent_of_imported (wayland_toplevel); -static void -gdk_wayland_toplevel_iface_init (GdkToplevelInterface *iface) -{ - iface->present = gdk_wayland_toplevel_present; - iface->minimize = gdk_wayland_toplevel_minimize; - iface->lower = gdk_wayland_toplevel_lower; - iface->focus = gdk_wayland_toplevel_focus; - iface->show_window_menu = gdk_wayland_toplevel_show_window_menu; - iface->titlebar_gesture = gdk_wayland_toplevel_titlebar_gesture; - iface->supports_edge_constraints = gdk_wayland_toplevel_supports_edge_constraints; - iface->inhibit_system_shortcuts = gdk_wayland_toplevel_inhibit_system_shortcuts; - iface->restore_system_shortcuts = gdk_wayland_toplevel_restore_system_shortcuts; - iface->begin_resize = gdk_wayland_toplevel_begin_resize; - iface->begin_move = gdk_wayland_toplevel_begin_move; - iface->export_handle = gdk_wayland_toplevel_real_export_handle; - iface->export_handle_finish = gdk_wayland_toplevel_real_export_handle_finish; - iface->unexport_handle = gdk_wayland_toplevel_real_unexport_handle; + return TRUE; } - +/* }}} */ +/* vim:set foldmethod=marker expandtab: */ -- 2.30.2