From: Matthias Clasen Date: Fri, 6 Jan 2023 18:53:27 +0000 (-0500) Subject: wip: Split off GdkWaylandToplevel X-Git-Tag: archive/raspbian/4.12.3+ds-1+rpi1~1^2^2^2~22^2~8^2~92^2~7 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=d61ec389740fffca68791002da96709b9d7ffacd;p=gtk4.git wip: Split off GdkWaylandToplevel This still needs some more cleanup. --- diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c index ff6334904f..0f745c28dc 100644 --- a/gdk/wayland/gdkdisplay-wayland.c +++ b/gdk/wayland/gdkdisplay-wayland.c @@ -49,6 +49,7 @@ #include "gdkvulkancontext-wayland.h" #include "gdkwaylandmonitor.h" #include "gdkprofilerprivate.h" +#include "gdktoplevel-wayland-private.h" #include #include "tablet-unstable-v2-client-protocol.h" #include diff --git a/gdk/wayland/gdksurface-wayland-private.h b/gdk/wayland/gdksurface-wayland-private.h index 7aaa341039..8bb895b630 100644 --- a/gdk/wayland/gdksurface-wayland-private.h +++ b/gdk/wayland/gdksurface-wayland-private.h @@ -95,6 +95,9 @@ void gdk_wayland_surface_update_size (GdkSurface *surface, int32_t width, int32_t height, int scale); +void gdk_wayland_surface_create_xdg_surface_resources (GdkSurface *surface); +void _gdk_wayland_surface_save_size (GdkSurface *surface); + #define GDK_TYPE_WAYLAND_DRAG_SURFACE (gdk_wayland_drag_surface_get_type ()) GType gdk_wayland_drag_surface_get_type (void) G_GNUC_CONST; diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c index 725404c327..e4e8721494 100644 --- a/gdk/wayland/gdksurface-wayland.c +++ b/gdk/wayland/gdksurface-wayland.c @@ -46,6 +46,7 @@ #include #include "gdksurface-wayland-private.h" +#include "gdktoplevel-wayland-private.h" /** * GdkWaylandSurface: @@ -57,8 +58,6 @@ * [method@GdkWayland.WaylandSurface.get_wl_surface]. */ -#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) static void gdk_wayland_surface_maybe_resize (GdkSurface *surface, @@ -75,8 +74,6 @@ static void gdk_wayland_surface_sync_shadow (GdkSurface *surface); static void gdk_wayland_surface_sync_input_region (GdkSurface *surface); static void gdk_wayland_surface_sync_opaque_region (GdkSurface *surface); -static void unset_transient_for_exported (GdkSurface *surface); - static void gdk_wayland_surface_move_resize (GdkSurface *surface, int x, int y, @@ -90,23 +87,6 @@ static void update_popup_layout_state (GdkWaylandPopup *wayland_popup, int height, GdkPopupLayout *layout); -static gboolean gdk_wayland_toplevel_is_exported (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); @@ -158,42 +138,6 @@ fill_presentation_time_from_frame_time (GdkFrameTimings *timings, } } -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 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 const char * get_default_title (void) { @@ -345,6 +289,42 @@ surface_anchor_to_gravity_legacy (GdkGravity rect_anchor) ZXDG_POSITIONER_V6_GRAVITY_RIGHT); } +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 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; +} + /* }}} */ /* {{{ Surface implementation */ @@ -380,7 +360,7 @@ gdk_wayland_surface_thaw_state (GdkSurface *surface) gdk_wayland_surface_configure (surface); } -static void +void _gdk_wayland_surface_save_size (GdkSurface *surface) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); @@ -1166,21 +1146,6 @@ gdk_wayland_surface_handle_configure (GdkWaylandSurface *impl, gdk_wayland_surface_configure (GDK_SURFACE (impl)); } -static void -gdk_wayland_surface_handle_close (GdkSurface *surface) -{ - GdkDisplay *display; - GdkEvent *event; - - display = gdk_surface_get_display (surface); - - GDK_DISPLAY_DEBUG (display, EVENTS, "close %p", surface); - - event = gdk_delete_event_new (surface); - - _gdk_wayland_display_deliver_event (display, event); -} - static void xdg_surface_configure (void *data, struct xdg_surface *xdg_surface, @@ -1205,7 +1170,7 @@ static const struct zxdg_surface_v6_listener zxdg_surface_v6_listener = { zxdg_surface_v6_configure, }; -static void +void gdk_wayland_surface_create_xdg_surface_resources (GdkSurface *surface) { GdkWaylandDisplay *display = @@ -2855,2424 +2820,5 @@ _gdk_wayland_surface_set_grab_seat (GdkSurface *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; - } - - wayland_surface->next_layout.surface_geometry_dirty = TRUE; - gdk_surface_request_layout (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_surface_queue_state_change (surface, ~0 & ~new_state, new_state); - - switch (display_wayland->shell_variant) - { - 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 -xdg_toplevel_configure (void *data, - struct xdg_toplevel *xdg_toplevel, - int32_t width, - int32_t height, - struct wl_array *states) -{ - 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; - - 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_toplevel_handle_configure (GdkWaylandToplevel *toplevel, - int32_t width, - int32_t height, - GdkToplevelState state) -{ - toplevel->pending.state |= state; - toplevel->pending.width = width; - toplevel->pending.height = height; -} - -static void -xdg_toplevel_close (void *data, - struct xdg_toplevel *xdg_toplevel) -{ - GdkSurface *surface = GDK_SURFACE (data); - - gdk_wayland_surface_handle_close (surface); -} - -static void -xdg_toplevel_configure_bounds (void *data, - struct xdg_toplevel *xdg_toplevel, - int32_t width, - int32_t height) -{ - GdkSurface *surface = GDK_SURFACE (data); - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - - toplevel->pending.bounds_width = width; - toplevel->pending.bounds_height = height; - toplevel->pending.has_bounds = TRUE; -} - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - xdg_toplevel_configure, - xdg_toplevel_close, - xdg_toplevel_configure_bounds, -}; - -static void -create_xdg_toplevel_resources (GdkWaylandToplevel *toplevel) -{ - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); - - 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 -zxdg_toplevel_v6_configure (void *data, - struct zxdg_toplevel_v6 *xdg_toplevel, - int32_t width, - int32_t height, - struct wl_array *states) -{ - 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; - - 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; - } - } - - gdk_wayland_toplevel_handle_configure (toplevel, width, height, pending_state); -} - -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 -create_zxdg_toplevel_v6_resources (GdkWaylandToplevel *toplevel) -{ - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); - - 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 void -gdk_wayland_surface_create_xdg_toplevel (GdkWaylandToplevel *wayland_toplevel) -{ - 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; - - gdk_surface_freeze_updates (surface); - gdk_wayland_surface_create_xdg_surface_resources (surface); - - switch (display_wayland->shell_variant) - { - 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 (); - } - - gdk_wayland_toplevel_sync_parent (wayland_toplevel); - gdk_wayland_toplevel_sync_parent_of_imported (wayland_toplevel); - gdk_wayland_toplevel_sync_title (wayland_toplevel); - - 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 (); - } - - 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"; - - gdk_wayland_toplevel_set_application_id (GDK_TOPLEVEL (wayland_toplevel), app_id); - - maybe_set_gtk_surface_dbus_properties (wayland_toplevel); - maybe_set_gtk_surface_modal (wayland_toplevel); - - gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "wayland", "surface commit"); - wl_surface_commit (wayland_surface->display_server.wl_surface); -} - -static void -gdk_wayland_toplevel_init (GdkWaylandToplevel *toplevel) -{ - toplevel->initial_fullscreen_output = NULL; - toplevel->shortcuts_inhibitors = g_hash_table_new (NULL, NULL); -} - -static void -gtk_surface_configure (void *data, - struct gtk_surface1 *gtk_surface, - struct wl_array *states) -{ - GdkSurface *surface = GDK_SURFACE (data); - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - GdkToplevelState new_state = 0; - uint32_t *p; - - wl_array_for_each (p, states) - { - uint32_t state = *p; - - switch (state) - { - case GTK_SURFACE1_STATE_TILED: - new_state |= GDK_TOPLEVEL_STATE_TILED; - break; - - /* Since v2 */ - case GTK_SURFACE1_STATE_TILED_TOP: - new_state |= (GDK_TOPLEVEL_STATE_TILED | GDK_TOPLEVEL_STATE_TOP_TILED); - break; - case GTK_SURFACE1_STATE_TILED_RIGHT: - new_state |= (GDK_TOPLEVEL_STATE_TILED | GDK_TOPLEVEL_STATE_RIGHT_TILED); - break; - case GTK_SURFACE1_STATE_TILED_BOTTOM: - new_state |= (GDK_TOPLEVEL_STATE_TILED | GDK_TOPLEVEL_STATE_BOTTOM_TILED); - break; - case GTK_SURFACE1_STATE_TILED_LEFT: - new_state |= (GDK_TOPLEVEL_STATE_TILED | GDK_TOPLEVEL_STATE_LEFT_TILED); - break; - default: - /* Unknown state */ - break; - } - } - - toplevel->pending.state |= new_state; -} - -static void -gtk_surface_configure_edges (void *data, - struct gtk_surface1 *gtk_surface, - struct wl_array *edge_constraints) -{ - GdkSurface *surface = GDK_SURFACE (data); - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - GdkToplevelState new_state = 0; - uint32_t *p; - - wl_array_for_each (p, edge_constraints) - { - uint32_t constraint = *p; - - switch (constraint) - { - case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_TOP: - new_state |= GDK_TOPLEVEL_STATE_TOP_RESIZABLE; - break; - case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_RIGHT: - new_state |= GDK_TOPLEVEL_STATE_RIGHT_RESIZABLE; - break; - case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_BOTTOM: - new_state |= GDK_TOPLEVEL_STATE_BOTTOM_RESIZABLE; - break; - case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_LEFT: - new_state |= GDK_TOPLEVEL_STATE_LEFT_RESIZABLE; - break; - default: - /* Unknown state */ - break; - } - } - - toplevel->pending.state |= new_state; -} - -static const struct gtk_surface1_listener gtk_surface_listener = { - gtk_surface_configure, - gtk_surface_configure_edges -}; - -static void -gdk_wayland_toplevel_init_gtk_surface (GdkWaylandToplevel *wayland_toplevel) -{ - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); - GdkWaylandDisplay *display = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (wayland_toplevel))); - - if (wayland_toplevel->display_server.gtk_surface != NULL) - return; - if (!is_realized_toplevel (wayland_surface)) - return; - if (display->gtk_shell == NULL) - return; - - wayland_toplevel->display_server.gtk_surface = - gtk_shell1_get_gtk_surface (display->gtk_shell, - wayland_surface->display_server.wl_surface); - wl_proxy_set_queue ((struct wl_proxy *) wayland_toplevel->display_server.gtk_surface, - wayland_surface->event_queue); - gdk_wayland_toplevel_set_geometry_hints (wayland_toplevel, - &wayland_toplevel->geometry_hints, - wayland_toplevel->geometry_mask); - gtk_surface1_add_listener (wayland_toplevel->display_server.gtk_surface, - >k_surface_listener, - 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) -{ - gdk_wayland_toplevel_init_gtk_surface (wayland_toplevel); - if (wayland_toplevel->display_server.gtk_surface == NULL) - return; - - if (GDK_SURFACE (wayland_toplevel)->modal_hint) - gtk_surface1_set_modal (wayland_toplevel->display_server.gtk_surface); - else - gtk_surface1_unset_modal (wayland_toplevel->display_server.gtk_surface); - -} - -static void -gdk_wayland_toplevel_set_modal_hint (GdkWaylandToplevel *wayland_toplevel, - gboolean modal) -{ - GDK_SURFACE (wayland_toplevel)->modal_hint = modal; - maybe_set_gtk_surface_modal (wayland_toplevel); -} - -static void -gdk_wayland_toplevel_set_geometry_hints (GdkWaylandToplevel *toplevel, - const GdkGeometry *geometry, - GdkSurfaceHints geom_mask) -{ - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); - 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; - - if (!geometry) - { - geometry = &toplevel->geometry_hints; - geom_mask = toplevel->geometry_mask; - } - - toplevel->geometry_hints = *geometry; - toplevel->geometry_mask = geom_mask; - - if (!is_realized_toplevel (impl)) - return; - - if (geom_mask & GDK_HINT_MIN_SIZE) - { - min_width = MAX (0, (geometry->min_width - - (impl->shadow_left + impl->shadow_right))); - min_height = MAX (0, (geometry->min_height - - (impl->shadow_top + impl->shadow_bottom))); - } - else - { - min_width = 0; - min_height = 0; - } - - if (geom_mask & GDK_HINT_MAX_SIZE) - { - max_width = MAX (0, (geometry->max_width - - (impl->shadow_left + impl->shadow_right))); - 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 gboolean -check_transient_for_loop (GdkWaylandToplevel *toplevel, - GdkWaylandToplevel *parent) -{ - while (parent) - { - if (parent->transient_for == toplevel) - return TRUE; - parent = parent->transient_for; - } - return FALSE; -} - -static void -gdk_wayland_toplevel_set_transient_for (GdkWaylandToplevel *toplevel, - GdkSurface *parent) -{ - g_return_if_fail (!parent || GDK_IS_WAYLAND_TOPLEVEL (parent)); - g_return_if_fail (!parent || - gdk_surface_get_display (GDK_SURFACE (toplevel)) == gdk_surface_get_display (parent)); - - if (parent) - { - GdkWaylandToplevel *parent_toplevel = GDK_WAYLAND_TOPLEVEL (parent); - - if (check_transient_for_loop (toplevel, parent_toplevel)) - { - g_warning ("Setting %p transient for %p would create a loop", - toplevel, parent); - return; - } - } - - unset_transient_for_exported (GDK_SURFACE (toplevel)); - - if (parent) - toplevel->transient_for = GDK_WAYLAND_TOPLEVEL (parent); - else - toplevel->transient_for = NULL; - - gdk_wayland_toplevel_sync_parent (toplevel); -} - -static void -gdk_wayland_toplevel_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - 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 (object, pspec); - break; - - 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; - - 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 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_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -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; - - case LAST_PROP + GDK_TOPLEVEL_PROP_TITLE: - g_value_set_string (value, toplevel->title); - break; - - case LAST_PROP + GDK_TOPLEVEL_PROP_STARTUP_ID: - g_value_set_string (value, ""); - break; - - 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_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 *impl = GDK_WAYLAND_SURFACE (surface); - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (surface); - GdkWaylandDisplay *display_wayland; - - if (GDK_SURFACE_DESTROYED (surface)) - return TRUE; - - if (!is_realized_toplevel (impl)) - return TRUE; - - /* 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); - 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_maximize (GdkToplevel *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; - - _gdk_wayland_surface_save_size (surface); - - if (is_realized_toplevel (wayland_surface)) - { - GdkWaylandDisplay *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_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); - } -} - -static void -gdk_wayland_toplevel_unmaximize (GdkToplevel *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; - - if (is_realized_toplevel (wayland_surface)) - { - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - - 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 - { - synthesize_initial_surface_state (wayland_toplevel, GDK_TOPLEVEL_STATE_MAXIMIZED, 0); - } -} - -static void -gdk_wayland_toplevel_fullscreen_on_monitor (GdkWaylandToplevel *wayland_toplevel, - GdkMonitor *monitor) -{ - GdkSurface *surface = GDK_SURFACE (wayland_toplevel); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (surface); - struct wl_output *output = ((GdkWaylandMonitor *)monitor)->output; - - if (GDK_SURFACE_DESTROYED (surface)) - return; - - _gdk_wayland_surface_save_size (surface); - - if (is_realized_toplevel (wayland_surface)) - { - GdkWaylandDisplay *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_fullscreen (wayland_toplevel->display_server.xdg_toplevel, output); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_toplevel_v6_set_fullscreen (wayland_toplevel->display_server.zxdg_toplevel_v6, output); - break; - default: - g_assert_not_reached (); - } - } - else - { - synthesize_initial_surface_state (wayland_toplevel, 0, GDK_TOPLEVEL_STATE_FULLSCREEN); - wayland_toplevel->initial_fullscreen_output = output; - } -} - -static void -gdk_wayland_toplevel_fullscreen (GdkWaylandToplevel *wayland_toplevel) -{ - GdkSurface *surface = GDK_SURFACE (wayland_toplevel); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_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 = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - - 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 - { - synthesize_initial_surface_state (wayland_toplevel, 0, GDK_TOPLEVEL_STATE_FULLSCREEN); - } -} - -static void -gdk_wayland_toplevel_unfullscreen (GdkWaylandToplevel *wayland_toplevel) -{ - GdkSurface *surface = GDK_SURFACE (wayland_toplevel); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); - - if (GDK_SURFACE_DESTROYED (surface)) - return; - - wayland_toplevel->initial_fullscreen_output = NULL; - - if (is_realized_toplevel (wayland_surface)) - { - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - - 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 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); - gboolean pending_configure = FALSE; - gboolean maximize; - gboolean fullscreen; - - 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 (gdk_toplevel_layout_get_fullscreen (layout, &fullscreen)) - { - if (fullscreen) - { - GdkMonitor *monitor; - - 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; - } - - g_clear_pointer (&wayland_toplevel->layout, gdk_toplevel_layout_unref); - wayland_toplevel->layout = gdk_toplevel_layout_copy (layout); - - gdk_wayland_surface_show (surface); - - if (!pending_configure) - { - wayland_surface->next_layout.surface_geometry_dirty = TRUE; - gdk_surface_request_layout (surface); - } -} - -static gboolean -gdk_wayland_toplevel_lower (GdkToplevel *toplevel) -{ - 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); - - surface->shortcuts_inhibited = TRUE; - g_object_notify (G_OBJECT (toplevel), "shortcuts-inhibited"); -} - -static void -inhibitor_inactive (void *data, - struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor) -{ - GdkToplevel *toplevel = GDK_TOPLEVEL (data); - GdkSurface *surface = GDK_SURFACE (toplevel); - - surface->shortcuts_inhibited = FALSE; - g_object_notify (G_OBJECT (toplevel), "shortcuts-inhibited"); -} - -static const struct zwp_keyboard_shortcuts_inhibitor_v1_listener -zwp_keyboard_shortcuts_inhibitor_listener = { - inhibitor_active, - inhibitor_inactive, -}; - -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_inhibit_shortcuts (GdkSurface *surface, - GdkSeat *gdk_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; - - if (display->keyboard_shortcuts_inhibit == NULL) - return; - - if (!is_realized_toplevel (GDK_WAYLAND_SURFACE (surface))) - return; - - toplevel = GDK_WAYLAND_TOPLEVEL (surface); - - if (gdk_wayland_toplevel_get_inhibitor (toplevel, gdk_seat)) - return; /* Already inhibited */ - - inhibitor = - zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts ( - display->keyboard_shortcuts_inhibit, wl_surface, seat); - - g_hash_table_insert (toplevel->shortcuts_inhibitors, gdk_seat, inhibitor); -} - -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; - - if (!is_realized_toplevel (impl)) - return; - - 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 -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; - - if (surface->shortcuts_inhibited) - return; - - 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; - - surface->current_shortcuts_inhibited_seat = gdk_seat; - zwp_keyboard_shortcuts_inhibitor_v1_add_listener - (inhibitor, &zwp_keyboard_shortcuts_inhibitor_listener, toplevel); -} - -static void -gdk_wayland_toplevel_restore_system_shortcuts (GdkToplevel *toplevel) -{ - GdkSurface *surface = GDK_SURFACE (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"); -} - -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); -} - -static const struct zxdg_exported_v1_listener xdg_exported_listener_v1 = { - xdg_exported_handle_v1 -}; - -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); -} - -static const struct zxdg_exported_v2_listener xdg_exported_listener_v2 = { - xdg_exported_handle_v2 -}; - -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; - - task = g_task_new (toplevel, cancellable, callback, user_data); - - 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; - } -} - -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); -} - -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; - - seat = gdk_event_get_seat (event); - wl_seat = gdk_wayland_seat_get_wl_seat (seat); - gdk_event_get_position (event, &x, &y); - - serial = _gdk_wayland_seat_get_implicit_grab_serial (seat, event); - - 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 (); - } - - return TRUE; -} - -static gboolean -translate_gesture (GdkTitlebarGesture gesture, - enum gtk_surface1_gesture *out_gesture) -{ - switch (gesture) - { - case GDK_TITLEBAR_GESTURE_DOUBLE_CLICK: - *out_gesture = GTK_SURFACE1_GESTURE_DOUBLE_CLICK; - break; - - case GDK_TITLEBAR_GESTURE_RIGHT_CLICK: - *out_gesture = GTK_SURFACE1_GESTURE_RIGHT_CLICK; - break; - - case GDK_TITLEBAR_GESTURE_MIDDLE_CLICK: - *out_gesture = GTK_SURFACE1_GESTURE_MIDDLE_CLICK; - break; - - default: - g_warning ("Not handling unknown titlebar gesture %u", gesture); - return FALSE; - } - - return TRUE; -} - -static gboolean -gdk_wayland_toplevel_titlebar_gesture (GdkToplevel *toplevel, - GdkTitlebarGesture gesture) -{ - GdkSurface *surface = GDK_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; - - if (gtk_surface1_get_version (gtk_surface) < GTK_SURFACE1_TITLEBAR_GESTURE_SINCE_VERSION) - return FALSE; - - if (!translate_gesture (gesture, >k_gesture)) - return FALSE; - - seat = gdk_display_get_default_seat (surface->display); - wl_seat = gdk_wayland_seat_get_wl_seat (seat); - - serial = _gdk_wayland_seat_get_last_implicit_grab_serial (GDK_WAYLAND_SEAT (seat), NULL); - - gtk_surface1_titlebar_gesture (wayland_toplevel->display_server.gtk_surface, - serial, - wl_seat, - gtk_gesture); - - return TRUE; -} - -static gboolean -gdk_wayland_toplevel_supports_edge_constraints (GdkToplevel *toplevel) -{ - 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; -} - -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 -token_done (gpointer data, - struct xdg_activation_token_v1 *provider, - const char *token) -{ - char **token_out = data; - - *token_out = g_strdup (token); -} - -static const struct xdg_activation_token_v1_listener token_listener = { - token_done, -}; - -static void -gdk_wayland_toplevel_focus (GdkToplevel *toplevel, - guint32 timestamp) -{ - 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; - - startup_id = g_steal_pointer (&display_wayland->startup_notification_id); - - if (display_wayland->xdg_activation) - { - 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; - - 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_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; -} - -/* }}} */ -/* {{{ Private Toplevel API */ - -struct gtk_surface1 * -gdk_wayland_toplevel_get_gtk_surface (GdkWaylandToplevel *wayland_toplevel) -{ - return wayland_toplevel->display_server.gtk_surface; -} - -static void -maybe_set_gtk_surface_dbus_properties (GdkWaylandToplevel *wayland_toplevel) -{ - if (wayland_toplevel->application.was_set) - return; - - 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; - - gdk_wayland_toplevel_init_gtk_surface (wayland_toplevel); - if (wayland_toplevel->display_server.gtk_surface == NULL) - 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; -} - -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; - - g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); - - wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - - 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); - - maybe_set_gtk_surface_dbus_properties (wayland_toplevel); -} - -/* }}} */ -/* {{{ Toplevel API */ - -/** - * 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; - - g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); - - g_return_if_fail (application_id != NULL); - - if (GDK_SURFACE_DESTROYED (toplevel)) - return; - - impl = GDK_WAYLAND_SURFACE (toplevel); - - if (!is_realized_toplevel (impl)) - return; - - wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); - - 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 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_assert_not_reached (); - } -} - -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); - } -} - -/** - * 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) -{ - return wayland_toplevel->xdg_exported != NULL || wayland_toplevel->xdg_exported_v2 != NULL; -} - -typedef struct { - GdkWaylandToplevelExported callback; - gpointer user_data; - GDestroyNotify destroy; -} ExportHandleData; - -static void -export_handle_done (GObject *source, - GAsyncResult *result, - void *user_data) -{ - 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); - - if (data->destroy) - data->destroy (data->user_data); - - g_free (data); -} - -/** - * 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) -{ - ExportHandleData *data; - - g_return_val_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel), FALSE); - - data = g_new (ExportHandleData, 1); - data->callback = callback; - data->user_data = user_data; - data->destroy = destroy_func; - - gdk_toplevel_export_handle (toplevel, NULL, export_handle_done, data); - - return TRUE; -} - -/** - * 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)); - - gdk_toplevel_unexport_handle (toplevel); -} - -static void -unset_transient_for_exported (GdkSurface *surface) -{ - if (GDK_IS_WAYLAND_TOPLEVEL (surface)) - { - GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); - - 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_imported_destroyed (void *data, - struct zxdg_imported_v1 *imported) -{ - unset_transient_for_exported (GDK_SURFACE (data)); -} - -static const struct zxdg_imported_v1_listener xdg_imported_listener = { - xdg_imported_destroyed, -}; - -static void -xdg_imported_v2_destroyed (void *data, - struct zxdg_imported_v2 *imported) -{ - unset_transient_for_exported (GDK_SURFACE (data)); -} - -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) -{ - GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); - GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (toplevel)); - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); - - g_return_val_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel), FALSE); - g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY (display), FALSE); - - display_wayland = GDK_WAYLAND_DISPLAY (display); - - if (!display_wayland->xdg_importer && !display_wayland->xdg_importer_v2) - { - g_warning ("Server is missing xdg_foreign support"); - return FALSE; - } - - gdk_wayland_toplevel_set_transient_for (wayland_toplevel, NULL); - - 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); - } - - gdk_wayland_toplevel_sync_parent_of_imported (wayland_toplevel); - - return TRUE; -} - /* }}} */ /* vim:set foldmethod=marker expandtab: */ diff --git a/gdk/wayland/gdksurface-wayland.h b/gdk/wayland/gdksurface-wayland.h index 67ed926afd..b64a29eedb 100644 --- a/gdk/wayland/gdksurface-wayland.h +++ b/gdk/wayland/gdksurface-wayland.h @@ -26,23 +26,6 @@ G_BEGIN_DECLS -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); - -void gdk_wayland_toplevel_announce_csd (GdkToplevel *toplevel); -void gdk_wayland_toplevel_announce_ssd (GdkToplevel *toplevel); - -gboolean gdk_wayland_toplevel_inhibit_idle (GdkToplevel *toplevel); -void gdk_wayland_toplevel_uninhibit_idle (GdkToplevel *toplevel); - - -struct gtk_surface1 * gdk_wayland_toplevel_get_gtk_surface (GdkWaylandToplevel *wayland_toplevel); - void gdk_wayland_surface_ensure_wl_egl_window (GdkSurface *surface); G_END_DECLS diff --git a/gdk/wayland/gdktoplevel-wayland-private.h b/gdk/wayland/gdktoplevel-wayland-private.h new file mode 100644 index 0000000000..b79d8fff6e --- /dev/null +++ b/gdk/wayland/gdktoplevel-wayland-private.h @@ -0,0 +1,55 @@ +/* + * Copyright © 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#pragma once + + +gboolean gdk_wayland_toplevel_is_exported (GdkWaylandToplevel *wayland_toplevel); + +void configure_toplevel_geometry (GdkWaylandToplevel *toplevel); +void gdk_wayland_surface_create_xdg_toplevel (GdkWaylandToplevel *toplevel); +void gdk_wayland_surface_configure_toplevel (GdkWaylandToplevel *toplevel); +void gdk_wayland_toplevel_sync_parent (GdkWaylandToplevel *toplevel); +void gdk_wayland_toplevel_sync_parent_of_imported (GdkWaylandToplevel *toplevel); +void gdk_wayland_toplevel_sync_title (GdkWaylandToplevel *toplevel); +void gdk_wayland_toplevel_set_geometry_hints (GdkWaylandToplevel *toplevel, + const GdkGeometry *geometry, + GdkSurfaceHints geom_mask); +void gdk_wayland_toplevel_handle_configure (GdkWaylandToplevel *toplevel, + int32_t width, + int32_t height, + GdkToplevelState state); +void gdk_wayland_toplevel_hide_surface (GdkWaylandToplevel *toplevel); +void unset_transient_for_exported (GdkSurface *surface); + +struct gtk_surface1 * + gdk_wayland_toplevel_get_gtk_surface (GdkWaylandToplevel *wayland_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); + +void gdk_wayland_toplevel_announce_csd (GdkToplevel *toplevel); +void gdk_wayland_toplevel_announce_ssd (GdkToplevel *toplevel); + +gboolean gdk_wayland_toplevel_inhibit_idle (GdkToplevel *toplevel); +void gdk_wayland_toplevel_uninhibit_idle (GdkToplevel *toplevel); + diff --git a/gdk/wayland/gdktoplevel-wayland.c b/gdk/wayland/gdktoplevel-wayland.c new file mode 100644 index 0000000000..a73ecb3d07 --- /dev/null +++ b/gdk/wayland/gdktoplevel-wayland.c @@ -0,0 +1,2508 @@ +/* + * Copyright © 2010 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "gdkwaylandtoplevel.h" + +#include "gdkdeviceprivate.h" +#include "gdkdisplay-wayland.h" +#include "gdkdragsurfaceprivate.h" +#include "gdkeventsprivate.h" +#include "gdkframeclockidleprivate.h" +#include "gdkglcontext-wayland.h" +#include "gdkmonitor-wayland.h" +#include "gdkpopupprivate.h" +#include "gdkprivate-wayland.h" +#include "gdkprivate-wayland.h" +#include "gdkseat-wayland.h" +#include "gdksurfaceprivate.h" +#include "gdktoplevelprivate.h" +#include "gdkdevice-wayland-private.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "gdksurface-wayland-private.h" +#include "gdktoplevel-wayland-private.h" + +#define MAX_WL_BUFFER_SIZE (4083) /* 4096 minus header, string argument length and NUL byte */ + +/* {{{ Utilities */ + +static gboolean +is_realized_shell_surface (GdkWaylandSurface *impl) +{ + return (impl->display_server.xdg_surface || + impl->display_server.zxdg_surface_v6); +} + +/* }}} */ +/* {{{ 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); + +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); +} + +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 (); + } +} + +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); +} + +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 (); + } +} + +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); + } +} + +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; + } + + wayland_surface->next_layout.surface_geometry_dirty = TRUE; + gdk_surface_request_layout (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_surface_queue_state_change (surface, ~0 & ~new_state, new_state); + + switch (display_wayland->shell_variant) + { + 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 +xdg_toplevel_configure (void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states) +{ + 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; + + 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); +} + +void +gdk_wayland_toplevel_handle_configure (GdkWaylandToplevel *toplevel, + int32_t width, + int32_t height, + GdkToplevelState state) +{ + toplevel->pending.state |= state; + toplevel->pending.width = width; + toplevel->pending.height = height; +} + +static void +gdk_wayland_surface_handle_close (GdkSurface *surface) +{ + GdkDisplay *display; + GdkEvent *event; + + display = gdk_surface_get_display (surface); + + GDK_DISPLAY_DEBUG (display, EVENTS, "close %p", surface); + + event = gdk_delete_event_new (surface); + + _gdk_wayland_display_deliver_event (display, event); +} + +static void +xdg_toplevel_close (void *data, + struct xdg_toplevel *xdg_toplevel) +{ + gdk_wayland_surface_handle_close (GDK_SURFACE (data)); +} + +static void +xdg_toplevel_configure_bounds (void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height) +{ + GdkSurface *surface = GDK_SURFACE (data); + GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); + + toplevel->pending.bounds_width = width; + toplevel->pending.bounds_height = height; + toplevel->pending.has_bounds = TRUE; +} + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + xdg_toplevel_configure, + xdg_toplevel_close, + xdg_toplevel_configure_bounds, +}; + +static void +create_xdg_toplevel_resources (GdkWaylandToplevel *toplevel) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); + + 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 +zxdg_toplevel_v6_configure (void *data, + struct zxdg_toplevel_v6 *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states) +{ + 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; + + 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; + } + } + + gdk_wayland_toplevel_handle_configure (toplevel, width, height, pending_state); +} + +static void +zxdg_toplevel_v6_close (void *data, + struct zxdg_toplevel_v6 *xdg_toplevel) +{ + gdk_wayland_surface_handle_close (GDK_SURFACE (data)); +} + +static const struct zxdg_toplevel_v6_listener zxdg_toplevel_v6_listener = { + zxdg_toplevel_v6_configure, + zxdg_toplevel_v6_close, +}; + +static void +create_zxdg_toplevel_v6_resources (GdkWaylandToplevel *toplevel) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); + + 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); +} + +void +gdk_wayland_surface_create_xdg_toplevel (GdkWaylandToplevel *wayland_toplevel) +{ + 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; + + gdk_surface_freeze_updates (surface); + gdk_wayland_surface_create_xdg_surface_resources (surface); + + switch (display_wayland->shell_variant) + { + 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 (); + } + + gdk_wayland_toplevel_sync_parent (wayland_toplevel); + gdk_wayland_toplevel_sync_parent_of_imported (wayland_toplevel); + gdk_wayland_toplevel_sync_title (wayland_toplevel); + + 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 (); + } + + 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"; + + gdk_wayland_toplevel_set_application_id (GDK_TOPLEVEL (wayland_toplevel), app_id); + + maybe_set_gtk_surface_dbus_properties (wayland_toplevel); + maybe_set_gtk_surface_modal (wayland_toplevel); + + gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "wayland", "surface commit"); + wl_surface_commit (wayland_surface->display_server.wl_surface); +} + +static void +gdk_wayland_toplevel_init (GdkWaylandToplevel *toplevel) +{ + toplevel->initial_fullscreen_output = NULL; + toplevel->shortcuts_inhibitors = g_hash_table_new (NULL, NULL); +} + +static void +gtk_surface_configure (void *data, + struct gtk_surface1 *gtk_surface, + struct wl_array *states) +{ + GdkSurface *surface = GDK_SURFACE (data); + GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); + GdkToplevelState new_state = 0; + uint32_t *p; + + wl_array_for_each (p, states) + { + uint32_t state = *p; + + switch (state) + { + case GTK_SURFACE1_STATE_TILED: + new_state |= GDK_TOPLEVEL_STATE_TILED; + break; + + /* Since v2 */ + case GTK_SURFACE1_STATE_TILED_TOP: + new_state |= (GDK_TOPLEVEL_STATE_TILED | GDK_TOPLEVEL_STATE_TOP_TILED); + break; + case GTK_SURFACE1_STATE_TILED_RIGHT: + new_state |= (GDK_TOPLEVEL_STATE_TILED | GDK_TOPLEVEL_STATE_RIGHT_TILED); + break; + case GTK_SURFACE1_STATE_TILED_BOTTOM: + new_state |= (GDK_TOPLEVEL_STATE_TILED | GDK_TOPLEVEL_STATE_BOTTOM_TILED); + break; + case GTK_SURFACE1_STATE_TILED_LEFT: + new_state |= (GDK_TOPLEVEL_STATE_TILED | GDK_TOPLEVEL_STATE_LEFT_TILED); + break; + default: + /* Unknown state */ + break; + } + } + + toplevel->pending.state |= new_state; +} + +static void +gtk_surface_configure_edges (void *data, + struct gtk_surface1 *gtk_surface, + struct wl_array *edge_constraints) +{ + GdkSurface *surface = GDK_SURFACE (data); + GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); + GdkToplevelState new_state = 0; + uint32_t *p; + + wl_array_for_each (p, edge_constraints) + { + uint32_t constraint = *p; + + switch (constraint) + { + case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_TOP: + new_state |= GDK_TOPLEVEL_STATE_TOP_RESIZABLE; + break; + case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_RIGHT: + new_state |= GDK_TOPLEVEL_STATE_RIGHT_RESIZABLE; + break; + case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_BOTTOM: + new_state |= GDK_TOPLEVEL_STATE_BOTTOM_RESIZABLE; + break; + case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_LEFT: + new_state |= GDK_TOPLEVEL_STATE_LEFT_RESIZABLE; + break; + default: + /* Unknown state */ + break; + } + } + + toplevel->pending.state |= new_state; +} + +static const struct gtk_surface1_listener gtk_surface_listener = { + gtk_surface_configure, + gtk_surface_configure_edges +}; + +static void +gdk_wayland_toplevel_init_gtk_surface (GdkWaylandToplevel *wayland_toplevel) +{ + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); + GdkWaylandDisplay *display = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (wayland_toplevel))); + + if (wayland_toplevel->display_server.gtk_surface != NULL) + return; + if (!is_realized_toplevel (wayland_surface)) + return; + if (display->gtk_shell == NULL) + return; + + wayland_toplevel->display_server.gtk_surface = + gtk_shell1_get_gtk_surface (display->gtk_shell, + wayland_surface->display_server.wl_surface); + wl_proxy_set_queue ((struct wl_proxy *) wayland_toplevel->display_server.gtk_surface, + wayland_surface->event_queue); + gdk_wayland_toplevel_set_geometry_hints (wayland_toplevel, + &wayland_toplevel->geometry_hints, + wayland_toplevel->geometry_mask); + gtk_surface1_add_listener (wayland_toplevel->display_server.gtk_surface, + >k_surface_listener, + 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))); + GdkWaylandSurface *surface = GDK_WAYLAND_SURFACE (toplevel); + char *free_me = NULL; + + if (!startup_id) + { + free_me = g_steal_pointer (&display->startup_notification_id); + startup_id = free_me; + } + + if (startup_id) + xdg_activation_v1_activate (display->xdg_activation, + startup_id, + surface->display_server.wl_surface); + + g_free (free_me); +} + +static void +maybe_set_gtk_surface_modal (GdkWaylandToplevel *wayland_toplevel) +{ + gdk_wayland_toplevel_init_gtk_surface (wayland_toplevel); + if (wayland_toplevel->display_server.gtk_surface == NULL) + return; + + if (GDK_SURFACE (wayland_toplevel)->modal_hint) + gtk_surface1_set_modal (wayland_toplevel->display_server.gtk_surface); + else + gtk_surface1_unset_modal (wayland_toplevel->display_server.gtk_surface); + +} + +static void +gdk_wayland_toplevel_set_modal_hint (GdkWaylandToplevel *wayland_toplevel, + gboolean modal) +{ + GDK_SURFACE (wayland_toplevel)->modal_hint = modal; + maybe_set_gtk_surface_modal (wayland_toplevel); +} + +void +gdk_wayland_toplevel_set_geometry_hints (GdkWaylandToplevel *toplevel, + const GdkGeometry *geometry, + GdkSurfaceHints geom_mask) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); + 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; + + if (!geometry) + { + geometry = &toplevel->geometry_hints; + geom_mask = toplevel->geometry_mask; + } + + toplevel->geometry_hints = *geometry; + toplevel->geometry_mask = geom_mask; + + if (!is_realized_toplevel (impl)) + return; + + if (geom_mask & GDK_HINT_MIN_SIZE) + { + min_width = MAX (0, (geometry->min_width - + (impl->shadow_left + impl->shadow_right))); + min_height = MAX (0, (geometry->min_height - + (impl->shadow_top + impl->shadow_bottom))); + } + else + { + min_width = 0; + min_height = 0; + } + + if (geom_mask & GDK_HINT_MAX_SIZE) + { + max_width = MAX (0, (geometry->max_width - + (impl->shadow_left + impl->shadow_right))); + 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 gboolean +check_transient_for_loop (GdkWaylandToplevel *toplevel, + GdkWaylandToplevel *parent) +{ + while (parent) + { + if (parent->transient_for == toplevel) + return TRUE; + parent = parent->transient_for; + } + return FALSE; +} + +static void +gdk_wayland_toplevel_set_transient_for (GdkWaylandToplevel *toplevel, + GdkSurface *parent) +{ + g_return_if_fail (!parent || GDK_IS_WAYLAND_TOPLEVEL (parent)); + g_return_if_fail (!parent || + gdk_surface_get_display (GDK_SURFACE (toplevel)) == gdk_surface_get_display (parent)); + + if (parent) + { + GdkWaylandToplevel *parent_toplevel = GDK_WAYLAND_TOPLEVEL (parent); + + if (check_transient_for_loop (toplevel, parent_toplevel)) + { + g_warning ("Setting %p transient for %p would create a loop", + toplevel, parent); + return; + } + } + + unset_transient_for_exported (GDK_SURFACE (toplevel)); + + if (parent) + toplevel->transient_for = GDK_WAYLAND_TOPLEVEL (parent); + else + toplevel->transient_for = NULL; + + gdk_wayland_toplevel_sync_parent (toplevel); +} + +#define LAST_PROP 1 + +static void +gdk_wayland_toplevel_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + 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 (object, pspec); + break; + + 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; + + 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 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_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +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; + + case LAST_PROP + GDK_TOPLEVEL_PROP_TITLE: + g_value_set_string (value, toplevel->title); + break; + + case LAST_PROP + GDK_TOPLEVEL_PROP_STARTUP_ID: + g_value_set_string (value, ""); + break; + + 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_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 *impl = GDK_WAYLAND_SURFACE (surface); + GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (surface); + GdkWaylandDisplay *display_wayland; + + if (GDK_SURFACE_DESTROYED (surface)) + return TRUE; + + if (!is_realized_toplevel (impl)) + return TRUE; + + /* 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); + 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_maximize (GdkToplevel *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; + + _gdk_wayland_surface_save_size (surface); + + if (is_realized_toplevel (wayland_surface)) + { + GdkWaylandDisplay *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_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); + } +} + +static void +gdk_wayland_toplevel_unmaximize (GdkToplevel *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; + + if (is_realized_toplevel (wayland_surface)) + { + GdkWaylandDisplay *display_wayland = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + + 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 + { + synthesize_initial_surface_state (wayland_toplevel, GDK_TOPLEVEL_STATE_MAXIMIZED, 0); + } +} + +static void +gdk_wayland_toplevel_fullscreen_on_monitor (GdkWaylandToplevel *wayland_toplevel, + GdkMonitor *monitor) +{ + GdkSurface *surface = GDK_SURFACE (wayland_toplevel); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (surface); + struct wl_output *output = ((GdkWaylandMonitor *)monitor)->output; + + if (GDK_SURFACE_DESTROYED (surface)) + return; + + _gdk_wayland_surface_save_size (surface); + + if (is_realized_toplevel (wayland_surface)) + { + GdkWaylandDisplay *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_fullscreen (wayland_toplevel->display_server.xdg_toplevel, output); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + zxdg_toplevel_v6_set_fullscreen (wayland_toplevel->display_server.zxdg_toplevel_v6, output); + break; + default: + g_assert_not_reached (); + } + } + else + { + synthesize_initial_surface_state (wayland_toplevel, 0, GDK_TOPLEVEL_STATE_FULLSCREEN); + wayland_toplevel->initial_fullscreen_output = output; + } +} + +static void +gdk_wayland_toplevel_fullscreen (GdkWaylandToplevel *wayland_toplevel) +{ + GdkSurface *surface = GDK_SURFACE (wayland_toplevel); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_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 = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + + 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 + { + synthesize_initial_surface_state (wayland_toplevel, 0, GDK_TOPLEVEL_STATE_FULLSCREEN); + } +} + +static void +gdk_wayland_toplevel_unfullscreen (GdkWaylandToplevel *wayland_toplevel) +{ + GdkSurface *surface = GDK_SURFACE (wayland_toplevel); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_toplevel); + + if (GDK_SURFACE_DESTROYED (surface)) + return; + + wayland_toplevel->initial_fullscreen_output = NULL; + + if (is_realized_toplevel (wayland_surface)) + { + GdkWaylandDisplay *display_wayland = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + + 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 void +gdk_wayland_toplevel_show (GdkWaylandToplevel *toplevel) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); + + if (!impl->display_server.wl_surface) + gdk_wayland_surface_create_wl_surface (GDK_SURFACE (impl)); + + if (impl->mapped) + return; + + gdk_wayland_surface_create_xdg_toplevel (toplevel); + + impl->mapped = TRUE; +} + +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); + gboolean pending_configure = FALSE; + gboolean maximize; + gboolean fullscreen; + + 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 (gdk_toplevel_layout_get_fullscreen (layout, &fullscreen)) + { + if (fullscreen) + { + GdkMonitor *monitor; + + 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; + } + + g_clear_pointer (&wayland_toplevel->layout, gdk_toplevel_layout_unref); + wayland_toplevel->layout = gdk_toplevel_layout_copy (layout); + + gdk_wayland_toplevel_show (wayland_toplevel); + + if (!pending_configure) + { + wayland_surface->next_layout.surface_geometry_dirty = TRUE; + gdk_surface_request_layout (surface); + } +} + +static gboolean +gdk_wayland_toplevel_lower (GdkToplevel *toplevel) +{ + 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); + + surface->shortcuts_inhibited = TRUE; + g_object_notify (G_OBJECT (toplevel), "shortcuts-inhibited"); +} + +static void +inhibitor_inactive (void *data, + struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor) +{ + GdkToplevel *toplevel = GDK_TOPLEVEL (data); + GdkSurface *surface = GDK_SURFACE (toplevel); + + surface->shortcuts_inhibited = FALSE; + g_object_notify (G_OBJECT (toplevel), "shortcuts-inhibited"); +} + +static const struct zwp_keyboard_shortcuts_inhibitor_v1_listener +zwp_keyboard_shortcuts_inhibitor_listener = { + inhibitor_active, + inhibitor_inactive, +}; + +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_inhibit_shortcuts (GdkSurface *surface, + GdkSeat *gdk_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; + + if (display->keyboard_shortcuts_inhibit == NULL) + return; + + if (!is_realized_toplevel (GDK_WAYLAND_SURFACE (surface))) + return; + + toplevel = GDK_WAYLAND_TOPLEVEL (surface); + + if (gdk_wayland_toplevel_get_inhibitor (toplevel, gdk_seat)) + return; /* Already inhibited */ + + inhibitor = + zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts ( + display->keyboard_shortcuts_inhibit, wl_surface, seat); + + g_hash_table_insert (toplevel->shortcuts_inhibitors, gdk_seat, inhibitor); +} + +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; + + if (!is_realized_toplevel (impl)) + return; + + 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 +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; + + if (surface->shortcuts_inhibited) + return; + + 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; + + surface->current_shortcuts_inhibited_seat = gdk_seat; + zwp_keyboard_shortcuts_inhibitor_v1_add_listener + (inhibitor, &zwp_keyboard_shortcuts_inhibitor_listener, toplevel); +} + +static void +gdk_wayland_toplevel_restore_system_shortcuts (GdkToplevel *toplevel) +{ + GdkSurface *surface = GDK_SURFACE (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"); +} + +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); +} + +static const struct zxdg_exported_v1_listener xdg_exported_listener_v1 = { + xdg_exported_handle_v1 +}; + +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); +} + +static const struct zxdg_exported_v2_listener xdg_exported_listener_v2 = { + xdg_exported_handle_v2 +}; + +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; + + task = g_task_new (toplevel, cancellable, callback, user_data); + + 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; + } +} + +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); +} + +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; + + seat = gdk_event_get_seat (event); + wl_seat = gdk_wayland_seat_get_wl_seat (seat); + gdk_event_get_position (event, &x, &y); + + serial = _gdk_wayland_seat_get_implicit_grab_serial (seat, event); + + 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 (); + } + + return TRUE; +} + +static gboolean +translate_gesture (GdkTitlebarGesture gesture, + enum gtk_surface1_gesture *out_gesture) +{ + switch (gesture) + { + case GDK_TITLEBAR_GESTURE_DOUBLE_CLICK: + *out_gesture = GTK_SURFACE1_GESTURE_DOUBLE_CLICK; + break; + + case GDK_TITLEBAR_GESTURE_RIGHT_CLICK: + *out_gesture = GTK_SURFACE1_GESTURE_RIGHT_CLICK; + break; + + case GDK_TITLEBAR_GESTURE_MIDDLE_CLICK: + *out_gesture = GTK_SURFACE1_GESTURE_MIDDLE_CLICK; + break; + + default: + g_warning ("Not handling unknown titlebar gesture %u", gesture); + return FALSE; + } + + return TRUE; +} + +static gboolean +gdk_wayland_toplevel_titlebar_gesture (GdkToplevel *toplevel, + GdkTitlebarGesture gesture) +{ + GdkSurface *surface = GDK_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; + + if (gtk_surface1_get_version (gtk_surface) < GTK_SURFACE1_TITLEBAR_GESTURE_SINCE_VERSION) + return FALSE; + + if (!translate_gesture (gesture, >k_gesture)) + return FALSE; + + seat = gdk_display_get_default_seat (surface->display); + wl_seat = gdk_wayland_seat_get_wl_seat (seat); + + serial = _gdk_wayland_seat_get_last_implicit_grab_serial (GDK_WAYLAND_SEAT (seat), NULL); + + gtk_surface1_titlebar_gesture (wayland_toplevel->display_server.gtk_surface, + serial, + wl_seat, + gtk_gesture); + + return TRUE; +} + +static gboolean +gdk_wayland_toplevel_supports_edge_constraints (GdkToplevel *toplevel) +{ + 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; +} + +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 +token_done (gpointer data, + struct xdg_activation_token_v1 *provider, + const char *token) +{ + char **token_out = data; + + *token_out = g_strdup (token); +} + +static const struct xdg_activation_token_v1_listener token_listener = { + token_done, +}; + +static void +gdk_wayland_toplevel_focus (GdkToplevel *toplevel, + guint32 timestamp) +{ + 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; + + startup_id = g_steal_pointer (&display_wayland->startup_notification_id); + + if (display_wayland->xdg_activation) + { + 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; + + 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_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; +} + +/* }}} */ +/* {{{ Private Toplevel API */ + +struct gtk_surface1 * +gdk_wayland_toplevel_get_gtk_surface (GdkWaylandToplevel *wayland_toplevel) +{ + return wayland_toplevel->display_server.gtk_surface; +} + +static void +maybe_set_gtk_surface_dbus_properties (GdkWaylandToplevel *wayland_toplevel) +{ + if (wayland_toplevel->application.was_set) + return; + + 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; + + gdk_wayland_toplevel_init_gtk_surface (wayland_toplevel); + if (wayland_toplevel->display_server.gtk_surface == NULL) + 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; +} + +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; + + g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); + + wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); + + 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); + + maybe_set_gtk_surface_dbus_properties (wayland_toplevel); +} + +/* }}} */ +/* {{{ Toplevel API */ + +/** + * 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; + + g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel)); + + g_return_if_fail (application_id != NULL); + + if (GDK_SURFACE_DESTROYED (toplevel)) + return; + + impl = GDK_WAYLAND_SURFACE (toplevel); + + if (!is_realized_toplevel (impl)) + return; + + wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); + display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel))); + + 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 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_assert_not_reached (); + } +} + +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); + } +} + +/** + * 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. + */ + +gboolean +gdk_wayland_toplevel_is_exported (GdkWaylandToplevel *wayland_toplevel) +{ + return wayland_toplevel->xdg_exported != NULL || wayland_toplevel->xdg_exported_v2 != NULL; +} + +typedef struct { + GdkWaylandToplevelExported callback; + gpointer user_data; + GDestroyNotify destroy; +} ExportHandleData; + +static void +export_handle_done (GObject *source, + GAsyncResult *result, + void *user_data) +{ + 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); + + if (data->destroy) + data->destroy (data->user_data); + + g_free (data); +} + +/** + * 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) +{ + ExportHandleData *data; + + g_return_val_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel), FALSE); + + data = g_new (ExportHandleData, 1); + data->callback = callback; + data->user_data = user_data; + data->destroy = destroy_func; + + gdk_toplevel_export_handle (toplevel, NULL, export_handle_done, data); + + return TRUE; +} + +/** + * 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)); + + gdk_toplevel_unexport_handle (toplevel); +} + +void +unset_transient_for_exported (GdkSurface *surface) +{ + if (GDK_IS_WAYLAND_TOPLEVEL (surface)) + { + GdkWaylandToplevel *toplevel = GDK_WAYLAND_TOPLEVEL (surface); + + 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_imported_destroyed (void *data, + struct zxdg_imported_v1 *zxdg_imported_v1) +{ + unset_transient_for_exported (GDK_SURFACE (data)); +} + +static const struct zxdg_imported_v1_listener xdg_imported_listener = { + xdg_imported_destroyed, +}; + +static void +xdg_imported_v2_destroyed (void *data, + struct zxdg_imported_v2 *zxdg_imported_v1) +{ + unset_transient_for_exported (GDK_SURFACE (data)); +} + +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) +{ + GdkWaylandToplevel *wayland_toplevel = GDK_WAYLAND_TOPLEVEL (toplevel); + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel); + GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (toplevel)); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); + + 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_importer) + { + g_warning ("Server is missing xdg_foreign support"); + return FALSE; + } + + gdk_wayland_toplevel_set_transient_for (GDK_WAYLAND_TOPLEVEL (impl), NULL); + + 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); + } + + gdk_wayland_toplevel_sync_parent_of_imported (wayland_toplevel); + + return TRUE; +} + +/* }}} */ +/* vim:set foldmethod=marker expandtab: */ diff --git a/gdk/wayland/meson.build b/gdk/wayland/meson.build index f63f149230..8fa6c34a05 100644 --- a/gdk/wayland/meson.build +++ b/gdk/wayland/meson.build @@ -16,6 +16,7 @@ gdk_wayland_sources = files([ 'gdkmonitor-wayland.c', 'gdkprimary-wayland.c', 'gdksurface-wayland.c', + 'gdktoplevel-wayland.c', 'gdkvulkancontext-wayland.c', 'wm-button-layout-translation.c', ]) diff --git a/gtk/gtkapplication-wayland.c b/gtk/gtkapplication-wayland.c index 7bc311b7f1..42a6e029c7 100644 --- a/gtk/gtkapplication-wayland.c +++ b/gtk/gtkapplication-wayland.c @@ -27,6 +27,7 @@ #include #include #include +#include #include typedef struct