From 120396fa40d31437641f4525ee6baf7c107a6cc9 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 17 Nov 2022 08:24:40 -0500 Subject: [PATCH] wip: Split off GdkWaylandPopup Like the GdkWaylandToplevel split-off, this needs some more cleanup. --- gdk/wayland/gdkpopup-wayland-private.h | 32 + gdk/wayland/gdkpopup-wayland.c | 1448 ++++++++++++++++++++++ gdk/wayland/gdksurface-wayland-private.h | 11 + gdk/wayland/gdksurface-wayland.c | 1416 +-------------------- gdk/wayland/meson.build | 1 + gtk/gtkwindow.c | 1 + 6 files changed, 1499 insertions(+), 1410 deletions(-) create mode 100644 gdk/wayland/gdkpopup-wayland-private.h create mode 100644 gdk/wayland/gdkpopup-wayland.c diff --git a/gdk/wayland/gdkpopup-wayland-private.h b/gdk/wayland/gdkpopup-wayland-private.h new file mode 100644 index 0000000000..94e4ee82ab --- /dev/null +++ b/gdk/wayland/gdkpopup-wayland-private.h @@ -0,0 +1,32 @@ +/* + * 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 + + +void update_popup_layout_state (GdkWaylandPopup *wayland_popup, + int x, + int y, + int width, + int height, + GdkPopupLayout *layout); + +void configure_popup_geometry (GdkWaylandPopup *popup); +void gdk_wayland_surface_configure_popup (GdkWaylandPopup *popup); +void frame_callback_popup (GdkWaylandPopup *popup); +void gdk_wayland_popup_hide_surface (GdkWaylandPopup *popup); + diff --git a/gdk/wayland/gdkpopup-wayland.c b/gdk/wayland/gdkpopup-wayland.c new file mode 100644 index 0000000000..d0668aa028 --- /dev/null +++ b/gdk/wayland/gdkpopup-wayland.c @@ -0,0 +1,1448 @@ +/* + * 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 "gdksurface-wayland.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 "gdkpopup-wayland-private.h" + +/* {{{ Utilities */ + +static gboolean +is_realized_shell_surface (GdkWaylandSurface *impl) +{ + return (impl->display_server.xdg_surface || + impl->display_server.zxdg_surface_v6); +} + +static enum xdg_positioner_anchor +rect_anchor_to_anchor (GdkGravity rect_anchor) +{ + switch (rect_anchor) + { + case GDK_GRAVITY_NORTH_WEST: + case GDK_GRAVITY_STATIC: + return XDG_POSITIONER_ANCHOR_TOP_LEFT; + case GDK_GRAVITY_NORTH: + return XDG_POSITIONER_ANCHOR_TOP; + case GDK_GRAVITY_NORTH_EAST: + return XDG_POSITIONER_ANCHOR_TOP_RIGHT; + case GDK_GRAVITY_WEST: + return XDG_POSITIONER_ANCHOR_LEFT; + case GDK_GRAVITY_CENTER: + return XDG_POSITIONER_ANCHOR_NONE; + case GDK_GRAVITY_EAST: + return XDG_POSITIONER_ANCHOR_RIGHT; + case GDK_GRAVITY_SOUTH_WEST: + return XDG_POSITIONER_ANCHOR_BOTTOM_LEFT; + case GDK_GRAVITY_SOUTH: + return XDG_POSITIONER_ANCHOR_BOTTOM; + case GDK_GRAVITY_SOUTH_EAST: + return XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT; + default: + g_assert_not_reached (); + } +} + +static enum xdg_positioner_gravity +surface_anchor_to_gravity (GdkGravity rect_anchor) +{ + switch (rect_anchor) + { + case GDK_GRAVITY_NORTH_WEST: + case GDK_GRAVITY_STATIC: + return XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT; + case GDK_GRAVITY_NORTH: + return XDG_POSITIONER_GRAVITY_BOTTOM; + case GDK_GRAVITY_NORTH_EAST: + return XDG_POSITIONER_GRAVITY_BOTTOM_LEFT; + case GDK_GRAVITY_WEST: + return XDG_POSITIONER_GRAVITY_RIGHT; + case GDK_GRAVITY_CENTER: + return XDG_POSITIONER_GRAVITY_NONE; + case GDK_GRAVITY_EAST: + return XDG_POSITIONER_GRAVITY_LEFT; + case GDK_GRAVITY_SOUTH_WEST: + return XDG_POSITIONER_GRAVITY_TOP_RIGHT; + case GDK_GRAVITY_SOUTH: + return XDG_POSITIONER_GRAVITY_TOP; + case GDK_GRAVITY_SOUTH_EAST: + return XDG_POSITIONER_GRAVITY_TOP_LEFT; + default: + g_assert_not_reached (); + } +} + +static enum zxdg_positioner_v6_anchor +rect_anchor_to_anchor_legacy (GdkGravity rect_anchor) +{ + switch (rect_anchor) + { + case GDK_GRAVITY_NORTH_WEST: + case GDK_GRAVITY_STATIC: + return (ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_LEFT); + case GDK_GRAVITY_NORTH: + return ZXDG_POSITIONER_V6_ANCHOR_TOP; + case GDK_GRAVITY_NORTH_EAST: + return (ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT); + case GDK_GRAVITY_WEST: + return ZXDG_POSITIONER_V6_ANCHOR_LEFT; + case GDK_GRAVITY_CENTER: + return ZXDG_POSITIONER_V6_ANCHOR_NONE; + case GDK_GRAVITY_EAST: + return ZXDG_POSITIONER_V6_ANCHOR_RIGHT; + case GDK_GRAVITY_SOUTH_WEST: + return (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_LEFT); + case GDK_GRAVITY_SOUTH: + return ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; + case GDK_GRAVITY_SOUTH_EAST: + return (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT); + default: + g_assert_not_reached (); + } + + return (ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_LEFT); +} + +static enum zxdg_positioner_v6_gravity +surface_anchor_to_gravity_legacy (GdkGravity rect_anchor) +{ + switch (rect_anchor) + { + case GDK_GRAVITY_NORTH_WEST: + case GDK_GRAVITY_STATIC: + return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT); + case GDK_GRAVITY_NORTH: + return ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; + case GDK_GRAVITY_NORTH_EAST: + return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_LEFT); + case GDK_GRAVITY_WEST: + return ZXDG_POSITIONER_V6_GRAVITY_RIGHT; + case GDK_GRAVITY_CENTER: + return ZXDG_POSITIONER_V6_GRAVITY_NONE; + case GDK_GRAVITY_EAST: + return ZXDG_POSITIONER_V6_GRAVITY_LEFT; + case GDK_GRAVITY_SOUTH_WEST: + return (ZXDG_POSITIONER_V6_GRAVITY_TOP | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT); + case GDK_GRAVITY_SOUTH: + return ZXDG_POSITIONER_V6_GRAVITY_TOP; + case GDK_GRAVITY_SOUTH_EAST: + return (ZXDG_POSITIONER_V6_GRAVITY_TOP | + ZXDG_POSITIONER_V6_GRAVITY_LEFT); + default: + g_assert_not_reached (); + } + + return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT); +} + + +/* }}} */ +/* {{{ GdkWaylandPopup definition */ + +/** + * GdkWaylandPopup: + * + * The Wayland implementation of `GdkPopup`. + */ + +struct _GdkWaylandPopup +{ + GdkWaylandSurface parent_instance; + + struct { + struct xdg_popup *xdg_popup; + struct zxdg_popup_v6 *zxdg_popup_v6; + } display_server; + + PopupState state; + unsigned int thaw_upon_show : 1; + GdkPopupLayout *layout; + int unconstrained_width; + int unconstrained_height; + + struct { + int x; + int y; + int width; + int height; + uint32_t repositioned_token; + gboolean has_repositioned_token; + } pending; + + struct { + int x; + int y; + } next_layout; + + uint32_t reposition_token; + uint32_t received_reposition_token; + + GdkSeat *grab_input_seat; +}; + +typedef struct +{ + GdkWaylandSurfaceClass parent_class; +} GdkWaylandPopupClass; + +static void gdk_wayland_popup_iface_init (GdkPopupInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (GdkWaylandPopup, gdk_wayland_popup, GDK_TYPE_WAYLAND_SURFACE, + G_IMPLEMENT_INTERFACE (GDK_TYPE_POPUP, + gdk_wayland_popup_iface_init)) + +/* }}} */ +/* {{{ Popup implementation */ + +static GdkSurface * +get_popup_toplevel (GdkSurface *surface) +{ + if (surface->parent) + return get_popup_toplevel (surface->parent); + else + return surface; +} + +static void +freeze_popup_toplevel_state (GdkWaylandPopup *wayland_popup) +{ + GdkSurface *toplevel; + + toplevel = get_popup_toplevel (GDK_SURFACE (wayland_popup)); + gdk_wayland_surface_freeze_state (toplevel); +} + +static void +thaw_popup_toplevel_state (GdkWaylandPopup *wayland_popup) +{ + GdkSurface *toplevel; + + toplevel = get_popup_toplevel (GDK_SURFACE (wayland_popup)); + gdk_wayland_surface_thaw_state (toplevel); +} + +void +gdk_wayland_popup_hide_surface (GdkWaylandPopup *popup) +{ + GdkSurface *surface = GDK_SURFACE (popup); + GdkDisplay *display = gdk_surface_get_display (surface); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); + + g_clear_pointer (&popup->display_server.xdg_popup, xdg_popup_destroy); + g_clear_pointer (&popup->display_server.zxdg_popup_v6, zxdg_popup_v6_destroy); + display_wayland->current_popups = + g_list_remove (display_wayland->current_popups, surface); + display_wayland->current_grabbing_popups = + g_list_remove (display_wayland->current_grabbing_popups, surface); + + popup->thaw_upon_show = TRUE; + gdk_surface_freeze_updates (surface); + + switch (popup->state) + { + case POPUP_STATE_WAITING_FOR_REPOSITIONED: + gdk_surface_thaw_updates (surface); + G_GNUC_FALLTHROUGH; + case POPUP_STATE_WAITING_FOR_CONFIGURE: + case POPUP_STATE_WAITING_FOR_FRAME: + thaw_popup_toplevel_state (popup); + break; + case POPUP_STATE_IDLE: + break; + default: + g_assert_not_reached (); + } + + popup->state = POPUP_STATE_IDLE; + + g_clear_pointer (&popup->layout, gdk_popup_layout_unref); +} + +static gboolean +is_realized_popup (GdkWaylandSurface *impl) +{ + GdkWaylandPopup *popup; + + if (!GDK_IS_WAYLAND_POPUP (impl)) + return FALSE; + + popup = GDK_WAYLAND_POPUP (impl); + + return (popup->display_server.xdg_popup || + popup->display_server.zxdg_popup_v6); +} + +static void +finish_pending_relayout (GdkWaylandPopup *wayland_popup) +{ + g_assert (wayland_popup->state == POPUP_STATE_WAITING_FOR_FRAME); + wayland_popup->state = POPUP_STATE_IDLE; + + thaw_popup_toplevel_state (wayland_popup); +} + +void +frame_callback_popup (GdkWaylandPopup *wayland_popup) +{ + switch (wayland_popup->state) + { + case POPUP_STATE_IDLE: + case POPUP_STATE_WAITING_FOR_REPOSITIONED: + case POPUP_STATE_WAITING_FOR_CONFIGURE: + break; + case POPUP_STATE_WAITING_FOR_FRAME: + finish_pending_relayout (wayland_popup); + break; + default: + g_assert_not_reached (); + } +} + +void +configure_popup_geometry (GdkWaylandPopup *wayland_popup) +{ + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); + int x, y; + int width, height; + + x = wayland_popup->next_layout.x - wayland_surface->shadow_left; + y = wayland_popup->next_layout.y - wayland_surface->shadow_top; + width = + wayland_surface->next_layout.configured_width + + (wayland_surface->shadow_left + wayland_surface->shadow_right); + height = + wayland_surface->next_layout.configured_height + + (wayland_surface->shadow_top + wayland_surface->shadow_bottom); + + gdk_wayland_surface_move_resize (GDK_SURFACE (wayland_popup), x, y, width, height); +} + +void +gdk_wayland_surface_configure_popup (GdkWaylandPopup *wayland_popup) +{ + GdkSurface *surface = GDK_SURFACE (wayland_popup); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); + GdkRectangle parent_geometry; + int x, y, width, height; + + if (wayland_popup->display_server.xdg_popup) + { + xdg_surface_ack_configure (wayland_surface->display_server.xdg_surface, + wayland_surface->pending.serial); + } + else if (wayland_popup->display_server.zxdg_popup_v6) + { + zxdg_surface_v6_ack_configure (wayland_surface->display_server.zxdg_surface_v6, + wayland_surface->pending.serial); + } + else + g_warn_if_reached (); + + if (wayland_popup->pending.has_repositioned_token) + wayland_popup->received_reposition_token = wayland_popup->pending.repositioned_token; + + switch (wayland_popup->state) + { + case POPUP_STATE_WAITING_FOR_REPOSITIONED: + if (wayland_popup->received_reposition_token != wayland_popup->reposition_token) + return; + else + gdk_surface_thaw_updates (surface); + G_GNUC_FALLTHROUGH; + case POPUP_STATE_WAITING_FOR_CONFIGURE: + wayland_popup->state = POPUP_STATE_WAITING_FOR_FRAME; + break; + case POPUP_STATE_IDLE: + case POPUP_STATE_WAITING_FOR_FRAME: + break; + default: + g_assert_not_reached (); + } + + x = wayland_popup->pending.x; + y = wayland_popup->pending.y; + width = wayland_popup->pending.width; + height = wayland_popup->pending.height; + + gdk_wayland_surface_get_window_geometry (surface->parent, &parent_geometry); + x += parent_geometry.x; + y += parent_geometry.y; + + update_popup_layout_state (wayland_popup, + x, y, + width, height, + wayland_popup->layout); + + wayland_popup->next_layout.x = x; + wayland_popup->next_layout.y = y; + wayland_surface->next_layout.configured_width = width; + wayland_surface->next_layout.configured_height = height; + wayland_surface->next_layout.surface_geometry_dirty = TRUE; + gdk_surface_request_layout (surface); +} + +static void +gdk_wayland_surface_handle_configure_popup (GdkWaylandPopup *wayland_popup, + int32_t x, + int32_t y, + int32_t width, + int32_t height) +{ + wayland_popup->pending.x = x; + wayland_popup->pending.y = y; + wayland_popup->pending.width = width; + wayland_popup->pending.height = height; +} + +static void +xdg_popup_configure (void *data, + struct xdg_popup *xdg_popup, + int32_t x, + int32_t y, + int32_t width, + int32_t height) +{ + GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (data); + + gdk_wayland_surface_handle_configure_popup (wayland_popup, x, y, width, height); +} + +static void +xdg_popup_done (void *data, + struct xdg_popup *xdg_popup) +{ + GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (data); + GdkSurface *surface = GDK_SURFACE (wayland_popup); + + GDK_DISPLAY_DEBUG (gdk_surface_get_display (surface), EVENTS, "done %p", surface); + + gdk_surface_hide (surface); +} + +static void +xdg_popup_repositioned (void *data, + struct xdg_popup *xdg_popup, + uint32_t token) +{ + GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (data); + + GDK_DISPLAY_DEBUG (gdk_surface_get_display (GDK_SURFACE (wayland_popup)), EVENTS, + "repositioned %p", wayland_popup); + + if (wayland_popup->state != POPUP_STATE_WAITING_FOR_REPOSITIONED) + { + g_warning ("Unexpected xdg_popup.repositioned event, probably buggy compositor"); + return; + } + + wayland_popup->pending.repositioned_token = token; + wayland_popup->pending.has_repositioned_token = TRUE; +} + +static const struct xdg_popup_listener xdg_popup_listener = { + xdg_popup_configure, + xdg_popup_done, + xdg_popup_repositioned, +}; + +static void +zxdg_popup_v6_configure (void *data, + struct zxdg_popup_v6 *xdg_popup, + int32_t x, + int32_t y, + int32_t width, + int32_t height) +{ + GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (data); + + gdk_wayland_surface_handle_configure_popup (wayland_popup, x, y, width, height); +} + +static void +zxdg_popup_v6_done (void *data, + struct zxdg_popup_v6 *xdg_popup) +{ + GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (data); + GdkSurface *surface = GDK_SURFACE (wayland_popup); + + GDK_DEBUG (EVENTS, "done %p", surface); + + gdk_surface_hide (surface); +} + +static const struct zxdg_popup_v6_listener zxdg_popup_v6_listener = { + zxdg_popup_v6_configure, + zxdg_popup_v6_done, +}; + +static void +calculate_popup_rect (GdkWaylandPopup *wayland_popup, + GdkPopupLayout *layout, + GdkRectangle *out_rect) +{ + int width, height; + GdkRectangle anchor_rect; + int dx, dy; + int shadow_left, shadow_right, shadow_top, shadow_bottom; + int x = 0, y = 0; + + gdk_popup_layout_get_shadow_width (layout, + &shadow_left, + &shadow_right, + &shadow_top, + &shadow_bottom); + + width = (wayland_popup->unconstrained_width - (shadow_left + shadow_right)); + height = (wayland_popup->unconstrained_height - (shadow_top + shadow_bottom)); + + anchor_rect = *gdk_popup_layout_get_anchor_rect (layout); + gdk_popup_layout_get_offset (layout, &dx, &dy); + anchor_rect.x += dx; + anchor_rect.y += dy; + + switch (gdk_popup_layout_get_rect_anchor (layout)) + { + default: + case GDK_GRAVITY_STATIC: + case GDK_GRAVITY_NORTH_WEST: + x = anchor_rect.x; + y = anchor_rect.y; + break; + case GDK_GRAVITY_NORTH: + x = anchor_rect.x + (anchor_rect.width / 2); + y = anchor_rect.y; + break; + case GDK_GRAVITY_NORTH_EAST: + x = anchor_rect.x + anchor_rect.width; + y = anchor_rect.y; + break; + case GDK_GRAVITY_WEST: + x = anchor_rect.x; + y = anchor_rect.y + (anchor_rect.height / 2); + break; + case GDK_GRAVITY_CENTER: + x = anchor_rect.x + (anchor_rect.width / 2); + y = anchor_rect.y + (anchor_rect.height / 2); + break; + case GDK_GRAVITY_EAST: + x = anchor_rect.x + anchor_rect.width; + y = anchor_rect.y + (anchor_rect.height / 2); + break; + case GDK_GRAVITY_SOUTH_WEST: + x = anchor_rect.x; + y = anchor_rect.y + anchor_rect.height; + break; + case GDK_GRAVITY_SOUTH: + x = anchor_rect.x + (anchor_rect.width / 2); + y = anchor_rect.y + anchor_rect.height; + break; + case GDK_GRAVITY_SOUTH_EAST: + x = anchor_rect.x + anchor_rect.width; + y = anchor_rect.y + anchor_rect.height; + break; + } + + switch (gdk_popup_layout_get_surface_anchor (layout)) + { + default: + case GDK_GRAVITY_STATIC: + case GDK_GRAVITY_NORTH_WEST: + break; + case GDK_GRAVITY_NORTH: + x -= width / 2; + break; + case GDK_GRAVITY_NORTH_EAST: + x -= width; + break; + case GDK_GRAVITY_WEST: + y -= height / 2; + break; + case GDK_GRAVITY_CENTER: + x -= width / 2; + y -= height / 2; + break; + case GDK_GRAVITY_EAST: + x -= width; + y -= height / 2; + break; + case GDK_GRAVITY_SOUTH_WEST: + y -= height; + break; + case GDK_GRAVITY_SOUTH: + x -= width / 2; + y -= height; + break; + case GDK_GRAVITY_SOUTH_EAST: + x -= width; + y -= height; + break; + } + + *out_rect = (GdkRectangle) { + .x = x, + .y = y, + .width = width, + .height = height + }; +} + +void +update_popup_layout_state (GdkWaylandPopup *wayland_popup, + int x, + int y, + int width, + int height, + GdkPopupLayout *layout) +{ + GdkRectangle best_rect; + GdkRectangle flipped_rect; + GdkGravity rect_anchor; + GdkGravity surface_anchor; + GdkAnchorHints anchor_hints; + + rect_anchor = gdk_popup_layout_get_rect_anchor (layout); + surface_anchor = gdk_popup_layout_get_surface_anchor (layout); + anchor_hints = gdk_popup_layout_get_anchor_hints (layout); + + calculate_popup_rect (wayland_popup, layout, &best_rect); + + flipped_rect = best_rect; + + if (x != best_rect.x && + anchor_hints & GDK_ANCHOR_FLIP_X) + { + GdkRectangle flipped_x_rect; + GdkGravity flipped_rect_anchor; + GdkGravity flipped_surface_anchor; + GdkPopupLayout *flipped_layout; + + flipped_rect_anchor = gdk_gravity_flip_horizontally (rect_anchor); + flipped_surface_anchor = gdk_gravity_flip_horizontally (surface_anchor); + flipped_layout = gdk_popup_layout_copy (layout); + gdk_popup_layout_set_rect_anchor (flipped_layout, + flipped_rect_anchor); + gdk_popup_layout_set_surface_anchor (flipped_layout, + flipped_surface_anchor); + calculate_popup_rect (wayland_popup, + flipped_layout, + &flipped_x_rect); + gdk_popup_layout_unref (flipped_layout); + + if (flipped_x_rect.x == x) + flipped_rect.x = x; + } + if (y != best_rect.y && + anchor_hints & GDK_ANCHOR_FLIP_Y) + { + GdkRectangle flipped_y_rect; + GdkGravity flipped_rect_anchor; + GdkGravity flipped_surface_anchor; + GdkPopupLayout *flipped_layout; + + flipped_rect_anchor = gdk_gravity_flip_vertically (rect_anchor); + flipped_surface_anchor = gdk_gravity_flip_vertically (surface_anchor); + flipped_layout = gdk_popup_layout_copy (layout); + gdk_popup_layout_set_rect_anchor (flipped_layout, + flipped_rect_anchor); + gdk_popup_layout_set_surface_anchor (flipped_layout, + flipped_surface_anchor); + calculate_popup_rect (wayland_popup, + flipped_layout, + &flipped_y_rect); + gdk_popup_layout_unref (flipped_layout); + + if (flipped_y_rect.y == y) + flipped_rect.y = y; + } + + if (flipped_rect.x != best_rect.x) + { + rect_anchor = gdk_gravity_flip_horizontally (rect_anchor); + surface_anchor = gdk_gravity_flip_horizontally (surface_anchor); + } + if (flipped_rect.y != best_rect.y) + { + rect_anchor = gdk_gravity_flip_vertically (rect_anchor); + surface_anchor = gdk_gravity_flip_vertically (surface_anchor); + } + + GDK_SURFACE (wayland_popup)->popup.rect_anchor = rect_anchor; + GDK_SURFACE (wayland_popup)->popup.surface_anchor = surface_anchor; +} + +static gpointer +create_dynamic_positioner (GdkWaylandPopup *wayland_popup, + int width, + int height, + GdkPopupLayout *layout, + gboolean ack_parent_configure) +{ + GdkSurface *surface = GDK_SURFACE (wayland_popup); + GdkSurface *parent = surface->parent; + GdkWaylandSurface *parent_impl = GDK_WAYLAND_SURFACE (parent); + GdkWaylandDisplay *display = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + GdkRectangle geometry; + uint32_t constraint_adjustment = ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE; + const GdkRectangle *anchor_rect; + int real_anchor_rect_x, real_anchor_rect_y; + int anchor_rect_width, anchor_rect_height; + int rect_anchor_dx; + int rect_anchor_dy; + GdkGravity rect_anchor; + GdkGravity surface_anchor; + GdkAnchorHints anchor_hints; + GdkRectangle parent_geometry; + int shadow_left; + int shadow_right; + int shadow_top; + int shadow_bottom; + + gdk_popup_layout_get_shadow_width (layout, + &shadow_left, + &shadow_right, + &shadow_top, + &shadow_bottom); + geometry = (GdkRectangle) { + .x = shadow_left, + .y = shadow_top, + .width = width - (shadow_left + shadow_right), + .height = height - (shadow_top + shadow_bottom), + }; + + gdk_wayland_surface_get_window_geometry (surface->parent, &parent_geometry); + + anchor_rect = gdk_popup_layout_get_anchor_rect (layout); + real_anchor_rect_x = anchor_rect->x - parent_geometry.x; + real_anchor_rect_y = anchor_rect->y - parent_geometry.y; + + anchor_rect_width = MAX (anchor_rect->width, 1); + anchor_rect_height = MAX (anchor_rect->height, 1); + + gdk_popup_layout_get_offset (layout, &rect_anchor_dx, &rect_anchor_dy); + + rect_anchor = gdk_popup_layout_get_rect_anchor (layout); + surface_anchor = gdk_popup_layout_get_surface_anchor (layout); + + anchor_hints = gdk_popup_layout_get_anchor_hints (layout); + + switch (display->shell_variant) + { + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + { + struct xdg_positioner *positioner; + enum xdg_positioner_anchor anchor; + enum xdg_positioner_gravity gravity; + + positioner = xdg_wm_base_create_positioner (display->xdg_wm_base); + + xdg_positioner_set_size (positioner, geometry.width, geometry.height); + xdg_positioner_set_anchor_rect (positioner, + real_anchor_rect_x, + real_anchor_rect_y, + anchor_rect_width, + anchor_rect_height); + xdg_positioner_set_offset (positioner, rect_anchor_dx, rect_anchor_dy); + + anchor = rect_anchor_to_anchor (rect_anchor); + xdg_positioner_set_anchor (positioner, anchor); + + gravity = surface_anchor_to_gravity (surface_anchor); + xdg_positioner_set_gravity (positioner, gravity); + + if (anchor_hints & GDK_ANCHOR_FLIP_X) + constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X; + if (anchor_hints & GDK_ANCHOR_FLIP_Y) + constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y; + if (anchor_hints & GDK_ANCHOR_SLIDE_X) + constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X; + if (anchor_hints & GDK_ANCHOR_SLIDE_Y) + constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y; + if (anchor_hints & GDK_ANCHOR_RESIZE_X) + constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X; + if (anchor_hints & GDK_ANCHOR_RESIZE_Y) + constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y; + xdg_positioner_set_constraint_adjustment (positioner, + constraint_adjustment); + + if (xdg_positioner_get_version (positioner) >= + XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION) + xdg_positioner_set_reactive (positioner); + + if (ack_parent_configure && + xdg_positioner_get_version (positioner) >= + XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION) + { + xdg_positioner_set_parent_size (positioner, + parent_geometry.width, + parent_geometry.height); + xdg_positioner_set_parent_configure (positioner, + parent_impl->last_configure_serial); + } + + return positioner; + } + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + { + struct zxdg_positioner_v6 *positioner; + enum zxdg_positioner_v6_anchor anchor; + enum zxdg_positioner_v6_gravity gravity; + + positioner = zxdg_shell_v6_create_positioner (display->zxdg_shell_v6); + + zxdg_positioner_v6_set_size (positioner, geometry.width, geometry.height); + zxdg_positioner_v6_set_anchor_rect (positioner, + real_anchor_rect_x, + real_anchor_rect_y, + anchor_rect_width, + anchor_rect_height); + zxdg_positioner_v6_set_offset (positioner, + rect_anchor_dx, + rect_anchor_dy); + + anchor = rect_anchor_to_anchor_legacy (rect_anchor); + zxdg_positioner_v6_set_anchor (positioner, anchor); + + gravity = surface_anchor_to_gravity_legacy (surface_anchor); + zxdg_positioner_v6_set_gravity (positioner, gravity); + + if (anchor_hints & GDK_ANCHOR_FLIP_X) + constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X; + if (anchor_hints & GDK_ANCHOR_FLIP_Y) + constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y; + if (anchor_hints & GDK_ANCHOR_SLIDE_X) + constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X; + if (anchor_hints & GDK_ANCHOR_SLIDE_Y) + constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y; + if (anchor_hints & GDK_ANCHOR_RESIZE_X) + constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X; + if (anchor_hints & GDK_ANCHOR_RESIZE_Y) + constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y; + zxdg_positioner_v6_set_constraint_adjustment (positioner, + constraint_adjustment); + + return positioner; + } + default: + g_assert_not_reached (); + } + + g_assert_not_reached (); +} + +static gboolean +can_map_grabbing_popup (GdkSurface *surface, + GdkSurface *parent) +{ + GdkDisplay *display = gdk_surface_get_display (surface); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); + GdkSurface *top_most_popup; + + if (!display_wayland->current_grabbing_popups) + return TRUE; + + top_most_popup = g_list_first (display_wayland->current_grabbing_popups)->data; + return top_most_popup == parent; +} + +static gboolean +gdk_wayland_surface_create_xdg_popup (GdkWaylandPopup *wayland_popup, + GdkSurface *parent, + GdkWaylandSeat *grab_input_seat, + int width, + int height, + GdkPopupLayout *layout) +{ + GdkSurface *surface = GDK_SURFACE (wayland_popup); + GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + GdkWaylandSurface *parent_impl = GDK_WAYLAND_SURFACE (parent); + gpointer positioner; + + if (!impl->display_server.wl_surface) + return FALSE; + + if (!is_realized_shell_surface (parent_impl)) + return FALSE; + + if (is_realized_popup (impl)) + { + g_warning ("Can't map popup, already mapped"); + return FALSE; + } + + if (grab_input_seat && + !can_map_grabbing_popup (surface, parent)) + { + g_warning ("Tried to map a grabbing popup with a non-top most parent"); + return FALSE; + } + + gdk_surface_freeze_updates (surface); + + positioner = create_dynamic_positioner (wayland_popup, width, height, layout, FALSE); + gdk_wayland_surface_create_xdg_surface_resources (surface); + + switch (display->shell_variant) + { + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + wayland_popup->display_server.xdg_popup = + xdg_surface_get_popup (impl->display_server.xdg_surface, + parent_impl->display_server.xdg_surface, + positioner); + xdg_popup_add_listener (wayland_popup->display_server.xdg_popup, + &xdg_popup_listener, + wayland_popup); + xdg_positioner_destroy (positioner); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + wayland_popup->display_server.zxdg_popup_v6 = + zxdg_surface_v6_get_popup (impl->display_server.zxdg_surface_v6, + parent_impl->display_server.zxdg_surface_v6, + positioner); + zxdg_popup_v6_add_listener (wayland_popup->display_server.zxdg_popup_v6, + &zxdg_popup_v6_listener, + wayland_popup); + zxdg_positioner_v6_destroy (positioner); + break; + default: + g_assert_not_reached (); + } + + gdk_popup_layout_get_shadow_width (layout, + &impl->shadow_left, + &impl->shadow_right, + &impl->shadow_top, + &impl->shadow_bottom); + + if (grab_input_seat) + { + struct wl_seat *seat; + guint32 serial; + + seat = gdk_wayland_seat_get_wl_seat (GDK_SEAT (grab_input_seat)); + serial = _gdk_wayland_seat_get_last_implicit_grab_serial (grab_input_seat, NULL); + + switch (display->shell_variant) + { + case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: + xdg_popup_grab (wayland_popup->display_server.xdg_popup, seat, serial); + break; + case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: + zxdg_popup_v6_grab (wayland_popup->display_server.zxdg_popup_v6, seat, serial); + break; + default: + g_assert_not_reached (); + } + } + + gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "wayland", "surface commit"); + wl_surface_commit (impl->display_server.wl_surface); + + if (GDK_IS_POPUP (surface)) + { + g_assert (wayland_popup->state == POPUP_STATE_IDLE); + wayland_popup->state = POPUP_STATE_WAITING_FOR_CONFIGURE; + freeze_popup_toplevel_state (wayland_popup); + } + + display->current_popups = g_list_append (display->current_popups, surface); + if (grab_input_seat) + { + display->current_grabbing_popups = + g_list_prepend (display->current_grabbing_popups, surface); + } + + return TRUE; +} + + +#define LAST_PROP 1 + +static void +gdk_wayland_popup_init (GdkWaylandPopup *popup) +{ +} + +static void +gdk_wayland_popup_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdkSurface *surface = GDK_SURFACE (object); + + switch (prop_id) + { + case LAST_PROP + GDK_POPUP_PROP_PARENT: + g_value_set_object (value, surface->parent); + break; + + case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE: + g_value_set_boolean (value, surface->autohide); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdk_wayland_popup_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdkSurface *surface = GDK_SURFACE (object); + + switch (prop_id) + { + case LAST_PROP + GDK_POPUP_PROP_PARENT: + surface->parent = g_value_dup_object (value); + if (surface->parent != NULL) + surface->parent->children = g_list_prepend (surface->parent->children, surface); + break; + + case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE: + surface->autohide = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdk_wayland_popup_class_init (GdkWaylandPopupClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->get_property = gdk_wayland_popup_get_property; + object_class->set_property = gdk_wayland_popup_set_property; + + gdk_popup_install_properties (object_class, 1); +} + +static gboolean +is_fallback_relayout_possible (GdkWaylandPopup *wayland_popup) +{ + GList *l; + + for (l = GDK_SURFACE (wayland_popup)->children; l; l = l->next) + { + GdkSurface *child = l->data; + + if (GDK_WAYLAND_SURFACE (child)->mapped) + return FALSE; + } + + return TRUE; +} + +static gboolean gdk_wayland_surface_present_popup (GdkWaylandPopup *wayland_popup, + int width, + int height, + GdkPopupLayout *layout); + +static void +queue_relayout_fallback (GdkWaylandPopup *wayland_popup, + GdkPopupLayout *layout) +{ + if (!is_fallback_relayout_possible (wayland_popup)) + return; + + gdk_wayland_surface_hide_surface (GDK_SURFACE (wayland_popup)); + gdk_wayland_surface_present_popup (wayland_popup, + wayland_popup->unconstrained_width, + wayland_popup->unconstrained_height, + layout); +} + +static void +do_queue_relayout (GdkWaylandPopup *wayland_popup, + int width, + int height, + GdkPopupLayout *layout) +{ + struct xdg_positioner *positioner; + + g_assert (is_realized_popup (GDK_WAYLAND_SURFACE (wayland_popup))); + g_assert (wayland_popup->state == POPUP_STATE_IDLE || + wayland_popup->state == POPUP_STATE_WAITING_FOR_FRAME); + + g_clear_pointer (&wayland_popup->layout, gdk_popup_layout_unref); + wayland_popup->layout = gdk_popup_layout_copy (layout); + wayland_popup->unconstrained_width = width; + wayland_popup->unconstrained_height = height; + + if (!wayland_popup->display_server.xdg_popup || + xdg_popup_get_version (wayland_popup->display_server.xdg_popup) < + XDG_POPUP_REPOSITION_SINCE_VERSION) + { + g_warning_once ("Compositor doesn't support moving popups, " + "relying on remapping"); + queue_relayout_fallback (wayland_popup, layout); + + return; + } + + positioner = create_dynamic_positioner (wayland_popup, + width, height, layout, + TRUE); + xdg_popup_reposition (wayland_popup->display_server.xdg_popup, + positioner, + ++wayland_popup->reposition_token); + xdg_positioner_destroy (positioner); + + gdk_surface_freeze_updates (GDK_SURFACE (wayland_popup)); + + switch (wayland_popup->state) + { + case POPUP_STATE_IDLE: + freeze_popup_toplevel_state (wayland_popup); + break; + case POPUP_STATE_WAITING_FOR_FRAME: + break; + case POPUP_STATE_WAITING_FOR_CONFIGURE: + case POPUP_STATE_WAITING_FOR_REPOSITIONED: + default: + g_assert_not_reached (); + } + + wayland_popup->state = POPUP_STATE_WAITING_FOR_REPOSITIONED; +} + +static gboolean +is_relayout_finished (GdkSurface *surface) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + + if (!impl->initial_configure_received) + return FALSE; + + if (GDK_IS_WAYLAND_POPUP (surface)) + { + GdkWaylandPopup *popup = GDK_WAYLAND_POPUP (surface); + if (popup->reposition_token != popup->received_reposition_token) + return FALSE; + } + + return TRUE; +} + +static GdkWaylandSeat * +find_grab_input_seat (GdkSurface *surface, + GdkSurface *parent) +{ + GdkWaylandPopup *popup = GDK_WAYLAND_POPUP (surface); + GdkWaylandPopup *tmp_popup; + + /* Use the device that was used for the grab as the device for + * the popup surface setup - so this relies on GTK taking the + * grab before showing the popup surface. + */ + if (popup->grab_input_seat) + return GDK_WAYLAND_SEAT (popup->grab_input_seat); + + while (parent) + { + if (!GDK_IS_WAYLAND_POPUP (parent)) + break; + + tmp_popup = GDK_WAYLAND_POPUP (parent); + + if (tmp_popup->grab_input_seat) + return GDK_WAYLAND_SEAT (tmp_popup->grab_input_seat); + + parent = parent->parent; + } + + return NULL; +} + +static void +gdk_wayland_surface_map_popup (GdkWaylandPopup *wayland_popup, + int width, + int height, + GdkPopupLayout *layout) +{ + GdkSurface *surface = GDK_SURFACE (wayland_popup); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); + GdkSurface *parent; + GdkWaylandSeat *grab_input_seat; + + parent = surface->parent; + if (!parent) + { + g_warning ("Couldn't map as surface %p as popup because it doesn't have a parent", + surface); + return; + } + + if (surface->autohide) + grab_input_seat = find_grab_input_seat (surface, parent); + else + grab_input_seat = NULL; + + if (!gdk_wayland_surface_create_xdg_popup (wayland_popup, + parent, + grab_input_seat, + width, height, + layout)) + return; + + wayland_popup->layout = gdk_popup_layout_copy (layout); + wayland_popup->unconstrained_width = width; + wayland_popup->unconstrained_height = height; + wayland_surface->mapped = TRUE; +} + +static void +show_popup (GdkWaylandPopup *wayland_popup, + int width, + int height, + GdkPopupLayout *layout) +{ + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); + + if (!wayland_surface->display_server.wl_surface) + gdk_wayland_surface_create_wl_surface (GDK_SURFACE (wayland_popup)); + + if (wayland_popup->thaw_upon_show) + { + wayland_popup->thaw_upon_show = FALSE; + gdk_surface_thaw_updates (GDK_SURFACE (wayland_popup)); + } + + gdk_wayland_surface_map_popup (wayland_popup, width, height, layout); +} + +typedef struct +{ + int width; + int height; + GdkPopupLayout *layout; +} GrabPrepareData; + +static void +show_grabbing_popup (GdkSeat *seat, + GdkSurface *surface, + gpointer user_data) +{ + GrabPrepareData *data = user_data; + + g_return_if_fail (GDK_IS_WAYLAND_POPUP (surface)); + GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (surface); + + show_popup (wayland_popup, data->width, data->height, data->layout); +} + +static void +reposition_popup (GdkWaylandPopup *wayland_popup, + int width, + int height, + GdkPopupLayout *layout) +{ + switch (wayland_popup->state) + { + case POPUP_STATE_IDLE: + case POPUP_STATE_WAITING_FOR_FRAME: + do_queue_relayout (wayland_popup, width, height, layout); + break; + case POPUP_STATE_WAITING_FOR_REPOSITIONED: + case POPUP_STATE_WAITING_FOR_CONFIGURE: + g_warn_if_reached (); + break; + default: + g_assert_not_reached (); + } +} + +static gboolean +gdk_wayland_surface_present_popup (GdkWaylandPopup *wayland_popup, + int width, + int height, + GdkPopupLayout *layout) +{ + GdkSurface *surface = GDK_SURFACE (wayland_popup); + GdkWaylandDisplay *display_wayland = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); + + if (!wayland_surface->mapped) + { + if (surface->autohide) + { + GdkSeat *seat; + + seat = gdk_display_get_default_seat (surface->display); + if (seat) + { + GrabPrepareData data; + GdkGrabStatus result; + + data = (GrabPrepareData) { + .width = width, + .height = height, + .layout = layout, + }; + + result = gdk_seat_grab (seat, + surface, + GDK_SEAT_CAPABILITY_ALL, + TRUE, + NULL, NULL, + show_grabbing_popup, &data); + if (result != GDK_GRAB_SUCCESS) + { + const char *grab_status[] = { + "success", "already grabbed", "invalid time", + "not viewable", "frozen", "failed" + }; + g_warning ("Grab failed: %s", grab_status[result]); + } + } + } + else + { + show_popup (wayland_popup, width, height, layout); + } + } + else + { + if (wayland_popup->unconstrained_width == width && + wayland_popup->unconstrained_height == height && + gdk_popup_layout_equal (wayland_popup->layout, layout)) + return TRUE; + + reposition_popup (wayland_popup, width, height, layout); + } + + while (wayland_popup->display_server.xdg_popup && !is_relayout_finished (surface)) + wl_display_dispatch_queue (display_wayland->wl_display, wayland_surface->event_queue); + + if (wayland_popup->display_server.xdg_popup) + { + gdk_surface_invalidate_rect (surface, NULL); + return TRUE; + } + else + { + return FALSE; + } +} + +static gboolean +gdk_wayland_popup_present (GdkPopup *popup, + int width, + int height, + GdkPopupLayout *layout) +{ + return gdk_wayland_surface_present_popup (GDK_WAYLAND_POPUP (popup), width, height, layout); +} + +static GdkGravity +gdk_wayland_popup_get_surface_anchor (GdkPopup *popup) +{ + return GDK_SURFACE (popup)->popup.surface_anchor; +} + +static GdkGravity +gdk_wayland_popup_get_rect_anchor (GdkPopup *popup) +{ + return GDK_SURFACE (popup)->popup.rect_anchor; +} + +static int +gdk_wayland_popup_get_position_x (GdkPopup *popup) +{ + return GDK_SURFACE (popup)->x; +} + +static int +gdk_wayland_popup_get_position_y (GdkPopup *popup) +{ + return GDK_SURFACE (popup)->y; +} + +static void +gdk_wayland_popup_iface_init (GdkPopupInterface *iface) +{ + iface->present = gdk_wayland_popup_present; + iface->get_surface_anchor = gdk_wayland_popup_get_surface_anchor; + iface->get_rect_anchor = gdk_wayland_popup_get_rect_anchor; + iface->get_position_x = gdk_wayland_popup_get_position_x; + iface->get_position_y = gdk_wayland_popup_get_position_y; +} + +/* }}} */ +/* {{{ Private Popup API */ + +void +_gdk_wayland_surface_set_grab_seat (GdkSurface *surface, + GdkSeat *seat) +{ + GdkWaylandPopup *popup; + + g_return_if_fail (surface != NULL); + + popup = GDK_WAYLAND_POPUP (surface); + popup->grab_input_seat = seat; +} + +/* }}} */ +/* vim:set foldmethod=marker expandtab: */ diff --git a/gdk/wayland/gdksurface-wayland-private.h b/gdk/wayland/gdksurface-wayland-private.h index 8bb895b630..58746aba80 100644 --- a/gdk/wayland/gdksurface-wayland-private.h +++ b/gdk/wayland/gdksurface-wayland-private.h @@ -98,6 +98,17 @@ void gdk_wayland_surface_update_size (GdkSurface *surface, void gdk_wayland_surface_create_xdg_surface_resources (GdkSurface *surface); void _gdk_wayland_surface_save_size (GdkSurface *surface); +void gdk_wayland_surface_hide_surface (GdkSurface *surface); +void gdk_wayland_surface_move_resize (GdkSurface *surface, + int x, + int y, + int width, + int height); +void gdk_wayland_surface_get_window_geometry (GdkSurface *surface, + GdkRectangle *geometry); +void gdk_wayland_surface_freeze_state (GdkSurface *surface); +void gdk_wayland_surface_thaw_state (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 e4e8721494..93d1620f24 100644 --- a/gdk/wayland/gdksurface-wayland.c +++ b/gdk/wayland/gdksurface-wayland.c @@ -47,6 +47,7 @@ #include "gdksurface-wayland-private.h" #include "gdktoplevel-wayland-private.h" +#include "gdkpopup-wayland-private.h" /** * GdkWaylandSurface: @@ -74,24 +75,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 gdk_wayland_surface_move_resize (GdkSurface *surface, - int x, - int y, - int width, - int height); - -static void update_popup_layout_state (GdkWaylandPopup *wayland_popup, - int x, - int y, - int width, - int height, - GdkPopupLayout *layout); - -static void configure_popup_geometry (GdkWaylandPopup *popup); -static void gdk_wayland_surface_configure_popup (GdkWaylandPopup *popup); -static void frame_callback_popup (GdkWaylandPopup *popup); -static void gdk_wayland_popup_hide_surface (GdkWaylandPopup *popup); - /* {{{ Utilities */ static void @@ -159,137 +142,7 @@ is_realized_shell_surface (GdkWaylandSurface *impl) impl->display_server.zxdg_surface_v6); } -static enum xdg_positioner_anchor -rect_anchor_to_anchor (GdkGravity rect_anchor) -{ - switch (rect_anchor) - { - case GDK_GRAVITY_NORTH_WEST: - case GDK_GRAVITY_STATIC: - return XDG_POSITIONER_ANCHOR_TOP_LEFT; - case GDK_GRAVITY_NORTH: - return XDG_POSITIONER_ANCHOR_TOP; - case GDK_GRAVITY_NORTH_EAST: - return XDG_POSITIONER_ANCHOR_TOP_RIGHT; - case GDK_GRAVITY_WEST: - return XDG_POSITIONER_ANCHOR_LEFT; - case GDK_GRAVITY_CENTER: - return XDG_POSITIONER_ANCHOR_NONE; - case GDK_GRAVITY_EAST: - return XDG_POSITIONER_ANCHOR_RIGHT; - case GDK_GRAVITY_SOUTH_WEST: - return XDG_POSITIONER_ANCHOR_BOTTOM_LEFT; - case GDK_GRAVITY_SOUTH: - return XDG_POSITIONER_ANCHOR_BOTTOM; - case GDK_GRAVITY_SOUTH_EAST: - return XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT; - default: - g_assert_not_reached (); - } -} - -static enum xdg_positioner_gravity -surface_anchor_to_gravity (GdkGravity rect_anchor) -{ - switch (rect_anchor) - { - case GDK_GRAVITY_NORTH_WEST: - case GDK_GRAVITY_STATIC: - return XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT; - case GDK_GRAVITY_NORTH: - return XDG_POSITIONER_GRAVITY_BOTTOM; - case GDK_GRAVITY_NORTH_EAST: - return XDG_POSITIONER_GRAVITY_BOTTOM_LEFT; - case GDK_GRAVITY_WEST: - return XDG_POSITIONER_GRAVITY_RIGHT; - case GDK_GRAVITY_CENTER: - return XDG_POSITIONER_GRAVITY_NONE; - case GDK_GRAVITY_EAST: - return XDG_POSITIONER_GRAVITY_LEFT; - case GDK_GRAVITY_SOUTH_WEST: - return XDG_POSITIONER_GRAVITY_TOP_RIGHT; - case GDK_GRAVITY_SOUTH: - return XDG_POSITIONER_GRAVITY_TOP; - case GDK_GRAVITY_SOUTH_EAST: - return XDG_POSITIONER_GRAVITY_TOP_LEFT; - default: - g_assert_not_reached (); - } -} - -static enum zxdg_positioner_v6_anchor -rect_anchor_to_anchor_legacy (GdkGravity rect_anchor) -{ - switch (rect_anchor) - { - case GDK_GRAVITY_NORTH_WEST: - case GDK_GRAVITY_STATIC: - return (ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_LEFT); - case GDK_GRAVITY_NORTH: - return ZXDG_POSITIONER_V6_ANCHOR_TOP; - case GDK_GRAVITY_NORTH_EAST: - return (ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_RIGHT); - case GDK_GRAVITY_WEST: - return ZXDG_POSITIONER_V6_ANCHOR_LEFT; - case GDK_GRAVITY_CENTER: - return ZXDG_POSITIONER_V6_ANCHOR_NONE; - case GDK_GRAVITY_EAST: - return ZXDG_POSITIONER_V6_ANCHOR_RIGHT; - case GDK_GRAVITY_SOUTH_WEST: - return (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | - ZXDG_POSITIONER_V6_ANCHOR_LEFT); - case GDK_GRAVITY_SOUTH: - return ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; - case GDK_GRAVITY_SOUTH_EAST: - return (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | - ZXDG_POSITIONER_V6_ANCHOR_RIGHT); - default: - g_assert_not_reached (); - } - - return (ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_LEFT); -} - -static enum zxdg_positioner_v6_gravity -surface_anchor_to_gravity_legacy (GdkGravity rect_anchor) -{ - switch (rect_anchor) - { - case GDK_GRAVITY_NORTH_WEST: - case GDK_GRAVITY_STATIC: - return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | - ZXDG_POSITIONER_V6_GRAVITY_RIGHT); - case GDK_GRAVITY_NORTH: - return ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; - case GDK_GRAVITY_NORTH_EAST: - return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | - ZXDG_POSITIONER_V6_GRAVITY_LEFT); - case GDK_GRAVITY_WEST: - return ZXDG_POSITIONER_V6_GRAVITY_RIGHT; - case GDK_GRAVITY_CENTER: - return ZXDG_POSITIONER_V6_GRAVITY_NONE; - case GDK_GRAVITY_EAST: - return ZXDG_POSITIONER_V6_GRAVITY_LEFT; - case GDK_GRAVITY_SOUTH_WEST: - return (ZXDG_POSITIONER_V6_GRAVITY_TOP | - ZXDG_POSITIONER_V6_GRAVITY_RIGHT); - case GDK_GRAVITY_SOUTH: - return ZXDG_POSITIONER_V6_GRAVITY_TOP; - case GDK_GRAVITY_SOUTH_EAST: - return (ZXDG_POSITIONER_V6_GRAVITY_TOP | - ZXDG_POSITIONER_V6_GRAVITY_LEFT); - default: - g_assert_not_reached (); - } - - return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | - ZXDG_POSITIONER_V6_GRAVITY_RIGHT); -} - -static void +void gdk_wayland_surface_get_window_geometry (GdkSurface *surface, GdkRectangle *geometry) { @@ -336,7 +189,7 @@ gdk_wayland_surface_init (GdkWaylandSurface *impl) impl->saved_height = -1; } -static void +void gdk_wayland_surface_freeze_state (GdkSurface *surface) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); @@ -344,7 +197,7 @@ gdk_wayland_surface_freeze_state (GdkSurface *surface) impl->state_freeze_count++; } -static void +void gdk_wayland_surface_thaw_state (GdkSurface *surface) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); @@ -426,33 +279,6 @@ gdk_wayland_surface_update_size (GdkSurface *surface, _gdk_surface_update_size (surface); } -static GdkSurface * -get_popup_toplevel (GdkSurface *surface) -{ - if (surface->parent) - return get_popup_toplevel (surface->parent); - else - return surface; -} - -static void -freeze_popup_toplevel_state (GdkWaylandPopup *wayland_popup) -{ - GdkSurface *toplevel; - - toplevel = get_popup_toplevel (GDK_SURFACE (wayland_popup)); - gdk_wayland_surface_freeze_state (toplevel); -} - -static void -thaw_popup_toplevel_state (GdkWaylandPopup *wayland_popup) -{ - GdkSurface *toplevel; - - toplevel = get_popup_toplevel (GDK_SURFACE (wayland_popup)); - gdk_wayland_surface_thaw_state (toplevel); -} - static void frame_callback (void *data, struct wl_callback *callback, @@ -1250,7 +1076,7 @@ unmap_popups_for_surface (GdkSurface *surface) } } -static void +void gdk_wayland_surface_hide_surface (GdkSurface *surface) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); @@ -1332,7 +1158,7 @@ gdk_wayland_surface_hide (GdkSurface *surface) _gdk_surface_clear_update_area (surface); } -static void +void gdk_wayland_surface_move_resize (GdkSurface *surface, int x, int y, @@ -1591,1234 +1417,4 @@ gdk_wayland_surface_get_wl_surface (GdkSurface *surface) } /* }}}} */ -/* {{{ GdkWaylandPopup definition */ - -/** - * GdkWaylandPopup: - * - * The Wayland implementation of `GdkPopup`. - */ - -struct _GdkWaylandPopup -{ - GdkWaylandSurface parent_instance; - - struct { - struct xdg_popup *xdg_popup; - struct zxdg_popup_v6 *zxdg_popup_v6; - } display_server; - - PopupState state; - unsigned int thaw_upon_show : 1; - GdkPopupLayout *layout; - int unconstrained_width; - int unconstrained_height; - - struct { - int x; - int y; - int width; - int height; - uint32_t repositioned_token; - gboolean has_repositioned_token; - } pending; - - struct { - int x; - int y; - } next_layout; - - uint32_t reposition_token; - uint32_t received_reposition_token; - - GdkSeat *grab_input_seat; -}; - -typedef struct -{ - GdkWaylandSurfaceClass parent_class; -} GdkWaylandPopupClass; - -static void gdk_wayland_popup_iface_init (GdkPopupInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (GdkWaylandPopup, gdk_wayland_popup, GDK_TYPE_WAYLAND_SURFACE, - G_IMPLEMENT_INTERFACE (GDK_TYPE_POPUP, - gdk_wayland_popup_iface_init)) - -/* }}} */ -/* {{{ Popup implementation */ - -static void -gdk_wayland_popup_hide_surface (GdkWaylandPopup *popup) -{ - GdkSurface *surface = GDK_SURFACE (popup); - GdkDisplay *display = gdk_surface_get_display (surface); - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); - - g_clear_pointer (&popup->display_server.xdg_popup, xdg_popup_destroy); - g_clear_pointer (&popup->display_server.zxdg_popup_v6, zxdg_popup_v6_destroy); - display_wayland->current_popups = - g_list_remove (display_wayland->current_popups, surface); - display_wayland->current_grabbing_popups = - g_list_remove (display_wayland->current_grabbing_popups, surface); - - popup->thaw_upon_show = TRUE; - gdk_surface_freeze_updates (surface); - - switch (popup->state) - { - case POPUP_STATE_WAITING_FOR_REPOSITIONED: - gdk_surface_thaw_updates (surface); - G_GNUC_FALLTHROUGH; - case POPUP_STATE_WAITING_FOR_CONFIGURE: - case POPUP_STATE_WAITING_FOR_FRAME: - thaw_popup_toplevel_state (popup); - break; - case POPUP_STATE_IDLE: - break; - default: - g_assert_not_reached (); - } - - popup->state = POPUP_STATE_IDLE; - - g_clear_pointer (&popup->layout, gdk_popup_layout_unref); -} - -static gboolean -is_realized_popup (GdkWaylandSurface *impl) -{ - GdkWaylandPopup *popup; - - if (!GDK_IS_WAYLAND_POPUP (impl)) - return FALSE; - - popup = GDK_WAYLAND_POPUP (impl); - - return (popup->display_server.xdg_popup || - popup->display_server.zxdg_popup_v6); -} - -static void -finish_pending_relayout (GdkWaylandPopup *wayland_popup) -{ - g_assert (wayland_popup->state == POPUP_STATE_WAITING_FOR_FRAME); - wayland_popup->state = POPUP_STATE_IDLE; - - thaw_popup_toplevel_state (wayland_popup); -} - -static void -frame_callback_popup (GdkWaylandPopup *wayland_popup) -{ - switch (wayland_popup->state) - { - case POPUP_STATE_IDLE: - case POPUP_STATE_WAITING_FOR_REPOSITIONED: - case POPUP_STATE_WAITING_FOR_CONFIGURE: - break; - case POPUP_STATE_WAITING_FOR_FRAME: - finish_pending_relayout (wayland_popup); - break; - default: - g_assert_not_reached (); - } -} - -static void -configure_popup_geometry (GdkWaylandPopup *wayland_popup) -{ - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); - int x, y; - int width, height; - - x = wayland_popup->next_layout.x - wayland_surface->shadow_left; - y = wayland_popup->next_layout.y - wayland_surface->shadow_top; - width = - wayland_surface->next_layout.configured_width + - (wayland_surface->shadow_left + wayland_surface->shadow_right); - height = - wayland_surface->next_layout.configured_height + - (wayland_surface->shadow_top + wayland_surface->shadow_bottom); - - gdk_wayland_surface_move_resize (GDK_SURFACE (wayland_popup), x, y, width, height); -} - -static void -gdk_wayland_surface_configure_popup (GdkWaylandPopup *wayland_popup) -{ - GdkSurface *surface = GDK_SURFACE (wayland_popup); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); - GdkRectangle parent_geometry; - int x, y, width, height; - - if (wayland_popup->display_server.xdg_popup) - { - xdg_surface_ack_configure (wayland_surface->display_server.xdg_surface, - wayland_surface->pending.serial); - } - else if (wayland_popup->display_server.zxdg_popup_v6) - { - zxdg_surface_v6_ack_configure (wayland_surface->display_server.zxdg_surface_v6, - wayland_surface->pending.serial); - } - else - g_warn_if_reached (); - - if (wayland_popup->pending.has_repositioned_token) - wayland_popup->received_reposition_token = wayland_popup->pending.repositioned_token; - - switch (wayland_popup->state) - { - case POPUP_STATE_WAITING_FOR_REPOSITIONED: - if (wayland_popup->received_reposition_token != wayland_popup->reposition_token) - return; - else - gdk_surface_thaw_updates (surface); - G_GNUC_FALLTHROUGH; - case POPUP_STATE_WAITING_FOR_CONFIGURE: - wayland_popup->state = POPUP_STATE_WAITING_FOR_FRAME; - break; - case POPUP_STATE_IDLE: - case POPUP_STATE_WAITING_FOR_FRAME: - break; - default: - g_assert_not_reached (); - } - - x = wayland_popup->pending.x; - y = wayland_popup->pending.y; - width = wayland_popup->pending.width; - height = wayland_popup->pending.height; - - gdk_wayland_surface_get_window_geometry (surface->parent, &parent_geometry); - x += parent_geometry.x; - y += parent_geometry.y; - - update_popup_layout_state (wayland_popup, - x, y, - width, height, - wayland_popup->layout); - - wayland_popup->next_layout.x = x; - wayland_popup->next_layout.y = y; - wayland_surface->next_layout.configured_width = width; - wayland_surface->next_layout.configured_height = height; - wayland_surface->next_layout.surface_geometry_dirty = TRUE; - gdk_surface_request_layout (surface); -} - -static void -gdk_wayland_surface_handle_configure_popup (GdkWaylandPopup *wayland_popup, - int32_t x, - int32_t y, - int32_t width, - int32_t height) -{ - wayland_popup->pending.x = x; - wayland_popup->pending.y = y; - wayland_popup->pending.width = width; - wayland_popup->pending.height = height; -} - -static void -xdg_popup_configure (void *data, - struct xdg_popup *xdg_popup, - int32_t x, - int32_t y, - int32_t width, - int32_t height) -{ - GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (data); - - gdk_wayland_surface_handle_configure_popup (wayland_popup, x, y, width, height); -} - -static void -xdg_popup_done (void *data, - struct xdg_popup *xdg_popup) -{ - GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (data); - GdkSurface *surface = GDK_SURFACE (wayland_popup); - - GDK_DISPLAY_DEBUG (gdk_surface_get_display (surface), EVENTS, "done %p", surface); - - gdk_surface_hide (surface); -} - -static void -xdg_popup_repositioned (void *data, - struct xdg_popup *xdg_popup, - uint32_t token) -{ - GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (data); - - GDK_DISPLAY_DEBUG (gdk_surface_get_display (GDK_SURFACE (wayland_popup)), EVENTS, - "repositioned %p", wayland_popup); - - if (wayland_popup->state != POPUP_STATE_WAITING_FOR_REPOSITIONED) - { - g_warning ("Unexpected xdg_popup.repositioned event, probably buggy compositor"); - return; - } - - wayland_popup->pending.repositioned_token = token; - wayland_popup->pending.has_repositioned_token = TRUE; -} - -static const struct xdg_popup_listener xdg_popup_listener = { - xdg_popup_configure, - xdg_popup_done, - xdg_popup_repositioned, -}; - -static void -zxdg_popup_v6_configure (void *data, - struct zxdg_popup_v6 *xdg_popup, - int32_t x, - int32_t y, - int32_t width, - int32_t height) -{ - GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (data); - - gdk_wayland_surface_handle_configure_popup (wayland_popup, x, y, width, height); -} - -static void -zxdg_popup_v6_done (void *data, - struct zxdg_popup_v6 *xdg_popup) -{ - GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (data); - GdkSurface *surface = GDK_SURFACE (wayland_popup); - - GDK_DEBUG (EVENTS, "done %p", surface); - - gdk_surface_hide (surface); -} - -static const struct zxdg_popup_v6_listener zxdg_popup_v6_listener = { - zxdg_popup_v6_configure, - zxdg_popup_v6_done, -}; - -static void -calculate_popup_rect (GdkWaylandPopup *wayland_popup, - GdkPopupLayout *layout, - GdkRectangle *out_rect) -{ - int width, height; - GdkRectangle anchor_rect; - int dx, dy; - int shadow_left, shadow_right, shadow_top, shadow_bottom; - int x = 0, y = 0; - - gdk_popup_layout_get_shadow_width (layout, - &shadow_left, - &shadow_right, - &shadow_top, - &shadow_bottom); - - width = (wayland_popup->unconstrained_width - (shadow_left + shadow_right)); - height = (wayland_popup->unconstrained_height - (shadow_top + shadow_bottom)); - - anchor_rect = *gdk_popup_layout_get_anchor_rect (layout); - gdk_popup_layout_get_offset (layout, &dx, &dy); - anchor_rect.x += dx; - anchor_rect.y += dy; - - switch (gdk_popup_layout_get_rect_anchor (layout)) - { - default: - case GDK_GRAVITY_STATIC: - case GDK_GRAVITY_NORTH_WEST: - x = anchor_rect.x; - y = anchor_rect.y; - break; - case GDK_GRAVITY_NORTH: - x = anchor_rect.x + (anchor_rect.width / 2); - y = anchor_rect.y; - break; - case GDK_GRAVITY_NORTH_EAST: - x = anchor_rect.x + anchor_rect.width; - y = anchor_rect.y; - break; - case GDK_GRAVITY_WEST: - x = anchor_rect.x; - y = anchor_rect.y + (anchor_rect.height / 2); - break; - case GDK_GRAVITY_CENTER: - x = anchor_rect.x + (anchor_rect.width / 2); - y = anchor_rect.y + (anchor_rect.height / 2); - break; - case GDK_GRAVITY_EAST: - x = anchor_rect.x + anchor_rect.width; - y = anchor_rect.y + (anchor_rect.height / 2); - break; - case GDK_GRAVITY_SOUTH_WEST: - x = anchor_rect.x; - y = anchor_rect.y + anchor_rect.height; - break; - case GDK_GRAVITY_SOUTH: - x = anchor_rect.x + (anchor_rect.width / 2); - y = anchor_rect.y + anchor_rect.height; - break; - case GDK_GRAVITY_SOUTH_EAST: - x = anchor_rect.x + anchor_rect.width; - y = anchor_rect.y + anchor_rect.height; - break; - } - - switch (gdk_popup_layout_get_surface_anchor (layout)) - { - default: - case GDK_GRAVITY_STATIC: - case GDK_GRAVITY_NORTH_WEST: - break; - case GDK_GRAVITY_NORTH: - x -= width / 2; - break; - case GDK_GRAVITY_NORTH_EAST: - x -= width; - break; - case GDK_GRAVITY_WEST: - y -= height / 2; - break; - case GDK_GRAVITY_CENTER: - x -= width / 2; - y -= height / 2; - break; - case GDK_GRAVITY_EAST: - x -= width; - y -= height / 2; - break; - case GDK_GRAVITY_SOUTH_WEST: - y -= height; - break; - case GDK_GRAVITY_SOUTH: - x -= width / 2; - y -= height; - break; - case GDK_GRAVITY_SOUTH_EAST: - x -= width; - y -= height; - break; - } - - *out_rect = (GdkRectangle) { - .x = x, - .y = y, - .width = width, - .height = height - }; -} - -static void -update_popup_layout_state (GdkWaylandPopup *wayland_popup, - int x, - int y, - int width, - int height, - GdkPopupLayout *layout) -{ - GdkRectangle best_rect; - GdkRectangle flipped_rect; - GdkGravity rect_anchor; - GdkGravity surface_anchor; - GdkAnchorHints anchor_hints; - - rect_anchor = gdk_popup_layout_get_rect_anchor (layout); - surface_anchor = gdk_popup_layout_get_surface_anchor (layout); - anchor_hints = gdk_popup_layout_get_anchor_hints (layout); - - calculate_popup_rect (wayland_popup, layout, &best_rect); - - flipped_rect = best_rect; - - if (x != best_rect.x && - anchor_hints & GDK_ANCHOR_FLIP_X) - { - GdkRectangle flipped_x_rect; - GdkGravity flipped_rect_anchor; - GdkGravity flipped_surface_anchor; - GdkPopupLayout *flipped_layout; - - flipped_rect_anchor = gdk_gravity_flip_horizontally (rect_anchor); - flipped_surface_anchor = gdk_gravity_flip_horizontally (surface_anchor); - flipped_layout = gdk_popup_layout_copy (layout); - gdk_popup_layout_set_rect_anchor (flipped_layout, - flipped_rect_anchor); - gdk_popup_layout_set_surface_anchor (flipped_layout, - flipped_surface_anchor); - calculate_popup_rect (wayland_popup, - flipped_layout, - &flipped_x_rect); - gdk_popup_layout_unref (flipped_layout); - - if (flipped_x_rect.x == x) - flipped_rect.x = x; - } - if (y != best_rect.y && - anchor_hints & GDK_ANCHOR_FLIP_Y) - { - GdkRectangle flipped_y_rect; - GdkGravity flipped_rect_anchor; - GdkGravity flipped_surface_anchor; - GdkPopupLayout *flipped_layout; - - flipped_rect_anchor = gdk_gravity_flip_vertically (rect_anchor); - flipped_surface_anchor = gdk_gravity_flip_vertically (surface_anchor); - flipped_layout = gdk_popup_layout_copy (layout); - gdk_popup_layout_set_rect_anchor (flipped_layout, - flipped_rect_anchor); - gdk_popup_layout_set_surface_anchor (flipped_layout, - flipped_surface_anchor); - calculate_popup_rect (wayland_popup, - flipped_layout, - &flipped_y_rect); - gdk_popup_layout_unref (flipped_layout); - - if (flipped_y_rect.y == y) - flipped_rect.y = y; - } - - if (flipped_rect.x != best_rect.x) - { - rect_anchor = gdk_gravity_flip_horizontally (rect_anchor); - surface_anchor = gdk_gravity_flip_horizontally (surface_anchor); - } - if (flipped_rect.y != best_rect.y) - { - rect_anchor = gdk_gravity_flip_vertically (rect_anchor); - surface_anchor = gdk_gravity_flip_vertically (surface_anchor); - } - - GDK_SURFACE (wayland_popup)->popup.rect_anchor = rect_anchor; - GDK_SURFACE (wayland_popup)->popup.surface_anchor = surface_anchor; -} - -static gpointer -create_dynamic_positioner (GdkWaylandPopup *wayland_popup, - int width, - int height, - GdkPopupLayout *layout, - gboolean ack_parent_configure) -{ - GdkSurface *surface = GDK_SURFACE (wayland_popup); - GdkSurface *parent = surface->parent; - GdkWaylandSurface *parent_impl = GDK_WAYLAND_SURFACE (parent); - GdkWaylandDisplay *display = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - GdkRectangle geometry; - uint32_t constraint_adjustment = ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE; - const GdkRectangle *anchor_rect; - int real_anchor_rect_x, real_anchor_rect_y; - int anchor_rect_width, anchor_rect_height; - int rect_anchor_dx; - int rect_anchor_dy; - GdkGravity rect_anchor; - GdkGravity surface_anchor; - GdkAnchorHints anchor_hints; - GdkRectangle parent_geometry; - int shadow_left; - int shadow_right; - int shadow_top; - int shadow_bottom; - - gdk_popup_layout_get_shadow_width (layout, - &shadow_left, - &shadow_right, - &shadow_top, - &shadow_bottom); - geometry = (GdkRectangle) { - .x = shadow_left, - .y = shadow_top, - .width = width - (shadow_left + shadow_right), - .height = height - (shadow_top + shadow_bottom), - }; - - gdk_wayland_surface_get_window_geometry (surface->parent, &parent_geometry); - - anchor_rect = gdk_popup_layout_get_anchor_rect (layout); - real_anchor_rect_x = anchor_rect->x - parent_geometry.x; - real_anchor_rect_y = anchor_rect->y - parent_geometry.y; - - anchor_rect_width = MAX (anchor_rect->width, 1); - anchor_rect_height = MAX (anchor_rect->height, 1); - - gdk_popup_layout_get_offset (layout, &rect_anchor_dx, &rect_anchor_dy); - - rect_anchor = gdk_popup_layout_get_rect_anchor (layout); - surface_anchor = gdk_popup_layout_get_surface_anchor (layout); - - anchor_hints = gdk_popup_layout_get_anchor_hints (layout); - - switch (display->shell_variant) - { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - { - struct xdg_positioner *positioner; - enum xdg_positioner_anchor anchor; - enum xdg_positioner_gravity gravity; - - positioner = xdg_wm_base_create_positioner (display->xdg_wm_base); - - xdg_positioner_set_size (positioner, geometry.width, geometry.height); - xdg_positioner_set_anchor_rect (positioner, - real_anchor_rect_x, - real_anchor_rect_y, - anchor_rect_width, - anchor_rect_height); - xdg_positioner_set_offset (positioner, rect_anchor_dx, rect_anchor_dy); - - anchor = rect_anchor_to_anchor (rect_anchor); - xdg_positioner_set_anchor (positioner, anchor); - - gravity = surface_anchor_to_gravity (surface_anchor); - xdg_positioner_set_gravity (positioner, gravity); - - if (anchor_hints & GDK_ANCHOR_FLIP_X) - constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X; - if (anchor_hints & GDK_ANCHOR_FLIP_Y) - constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y; - if (anchor_hints & GDK_ANCHOR_SLIDE_X) - constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X; - if (anchor_hints & GDK_ANCHOR_SLIDE_Y) - constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y; - if (anchor_hints & GDK_ANCHOR_RESIZE_X) - constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X; - if (anchor_hints & GDK_ANCHOR_RESIZE_Y) - constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y; - xdg_positioner_set_constraint_adjustment (positioner, - constraint_adjustment); - - if (xdg_positioner_get_version (positioner) >= - XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION) - xdg_positioner_set_reactive (positioner); - - if (ack_parent_configure && - xdg_positioner_get_version (positioner) >= - XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION) - { - xdg_positioner_set_parent_size (positioner, - parent_geometry.width, - parent_geometry.height); - xdg_positioner_set_parent_configure (positioner, - parent_impl->last_configure_serial); - } - - return positioner; - } - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - { - struct zxdg_positioner_v6 *positioner; - enum zxdg_positioner_v6_anchor anchor; - enum zxdg_positioner_v6_gravity gravity; - - positioner = zxdg_shell_v6_create_positioner (display->zxdg_shell_v6); - - zxdg_positioner_v6_set_size (positioner, geometry.width, geometry.height); - zxdg_positioner_v6_set_anchor_rect (positioner, - real_anchor_rect_x, - real_anchor_rect_y, - anchor_rect_width, - anchor_rect_height); - zxdg_positioner_v6_set_offset (positioner, - rect_anchor_dx, - rect_anchor_dy); - - anchor = rect_anchor_to_anchor_legacy (rect_anchor); - zxdg_positioner_v6_set_anchor (positioner, anchor); - - gravity = surface_anchor_to_gravity_legacy (surface_anchor); - zxdg_positioner_v6_set_gravity (positioner, gravity); - - if (anchor_hints & GDK_ANCHOR_FLIP_X) - constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X; - if (anchor_hints & GDK_ANCHOR_FLIP_Y) - constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y; - if (anchor_hints & GDK_ANCHOR_SLIDE_X) - constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X; - if (anchor_hints & GDK_ANCHOR_SLIDE_Y) - constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y; - if (anchor_hints & GDK_ANCHOR_RESIZE_X) - constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X; - if (anchor_hints & GDK_ANCHOR_RESIZE_Y) - constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y; - zxdg_positioner_v6_set_constraint_adjustment (positioner, - constraint_adjustment); - - return positioner; - } - default: - g_assert_not_reached (); - } - - g_assert_not_reached (); -} - -static gboolean -can_map_grabbing_popup (GdkSurface *surface, - GdkSurface *parent) -{ - GdkDisplay *display = gdk_surface_get_display (surface); - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); - GdkSurface *top_most_popup; - - if (!display_wayland->current_grabbing_popups) - return TRUE; - - top_most_popup = g_list_first (display_wayland->current_grabbing_popups)->data; - return top_most_popup == parent; -} - -static gboolean -gdk_wayland_surface_create_xdg_popup (GdkWaylandPopup *wayland_popup, - GdkSurface *parent, - GdkWaylandSeat *grab_input_seat, - int width, - int height, - GdkPopupLayout *layout) -{ - GdkSurface *surface = GDK_SURFACE (wayland_popup); - GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - GdkWaylandSurface *parent_impl = GDK_WAYLAND_SURFACE (parent); - gpointer positioner; - - if (!impl->display_server.wl_surface) - return FALSE; - - if (!is_realized_shell_surface (parent_impl)) - return FALSE; - - if (is_realized_popup (impl)) - { - g_warning ("Can't map popup, already mapped"); - return FALSE; - } - - if (grab_input_seat && - !can_map_grabbing_popup (surface, parent)) - { - g_warning ("Tried to map a grabbing popup with a non-top most parent"); - return FALSE; - } - - gdk_surface_freeze_updates (surface); - - positioner = create_dynamic_positioner (wayland_popup, width, height, layout, FALSE); - gdk_wayland_surface_create_xdg_surface_resources (surface); - - switch (display->shell_variant) - { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - wayland_popup->display_server.xdg_popup = - xdg_surface_get_popup (impl->display_server.xdg_surface, - parent_impl->display_server.xdg_surface, - positioner); - xdg_popup_add_listener (wayland_popup->display_server.xdg_popup, - &xdg_popup_listener, - wayland_popup); - xdg_positioner_destroy (positioner); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - wayland_popup->display_server.zxdg_popup_v6 = - zxdg_surface_v6_get_popup (impl->display_server.zxdg_surface_v6, - parent_impl->display_server.zxdg_surface_v6, - positioner); - zxdg_popup_v6_add_listener (wayland_popup->display_server.zxdg_popup_v6, - &zxdg_popup_v6_listener, - wayland_popup); - zxdg_positioner_v6_destroy (positioner); - break; - default: - g_assert_not_reached (); - } - - gdk_popup_layout_get_shadow_width (layout, - &impl->shadow_left, - &impl->shadow_right, - &impl->shadow_top, - &impl->shadow_bottom); - - if (grab_input_seat) - { - struct wl_seat *seat; - guint32 serial; - - seat = gdk_wayland_seat_get_wl_seat (GDK_SEAT (grab_input_seat)); - serial = _gdk_wayland_seat_get_last_implicit_grab_serial (grab_input_seat, NULL); - - switch (display->shell_variant) - { - case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL: - xdg_popup_grab (wayland_popup->display_server.xdg_popup, seat, serial); - break; - case GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6: - zxdg_popup_v6_grab (wayland_popup->display_server.zxdg_popup_v6, seat, serial); - break; - default: - g_assert_not_reached (); - } - } - - gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "wayland", "surface commit"); - wl_surface_commit (impl->display_server.wl_surface); - - if (GDK_IS_POPUP (surface)) - { - g_assert (wayland_popup->state == POPUP_STATE_IDLE); - wayland_popup->state = POPUP_STATE_WAITING_FOR_CONFIGURE; - freeze_popup_toplevel_state (wayland_popup); - } - - display->current_popups = g_list_append (display->current_popups, surface); - if (grab_input_seat) - { - display->current_grabbing_popups = - g_list_prepend (display->current_grabbing_popups, surface); - } - - return TRUE; -} - - -#define LAST_PROP 1 - -static void -gdk_wayland_popup_init (GdkWaylandPopup *popup) -{ -} - -static void -gdk_wayland_popup_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GdkSurface *surface = GDK_SURFACE (object); - - switch (prop_id) - { - case LAST_PROP + GDK_POPUP_PROP_PARENT: - g_value_set_object (value, surface->parent); - break; - - case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE: - g_value_set_boolean (value, surface->autohide); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gdk_wayland_popup_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GdkSurface *surface = GDK_SURFACE (object); - - switch (prop_id) - { - case LAST_PROP + GDK_POPUP_PROP_PARENT: - surface->parent = g_value_dup_object (value); - if (surface->parent != NULL) - surface->parent->children = g_list_prepend (surface->parent->children, surface); - break; - - case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE: - surface->autohide = g_value_get_boolean (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gdk_wayland_popup_class_init (GdkWaylandPopupClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - - object_class->get_property = gdk_wayland_popup_get_property; - object_class->set_property = gdk_wayland_popup_set_property; - - gdk_popup_install_properties (object_class, 1); -} - -static gboolean -is_fallback_relayout_possible (GdkWaylandPopup *wayland_popup) -{ - GList *l; - - for (l = GDK_SURFACE (wayland_popup)->children; l; l = l->next) - { - GdkSurface *child = l->data; - - if (GDK_WAYLAND_SURFACE (child)->mapped) - return FALSE; - } - - return TRUE; -} - -static gboolean gdk_wayland_surface_present_popup (GdkWaylandPopup *wayland_popup, - int width, - int height, - GdkPopupLayout *layout); - -static void -queue_relayout_fallback (GdkWaylandPopup *wayland_popup, - GdkPopupLayout *layout) -{ - if (!is_fallback_relayout_possible (wayland_popup)) - return; - - gdk_wayland_surface_hide_surface (GDK_SURFACE (wayland_popup)); - gdk_wayland_surface_present_popup (wayland_popup, - wayland_popup->unconstrained_width, - wayland_popup->unconstrained_height, - layout); -} - -static void -do_queue_relayout (GdkWaylandPopup *wayland_popup, - int width, - int height, - GdkPopupLayout *layout) -{ - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); - struct xdg_positioner *positioner; - - g_assert (is_realized_popup (wayland_surface)); - g_assert (wayland_popup->state == POPUP_STATE_IDLE || - wayland_popup->state == POPUP_STATE_WAITING_FOR_FRAME); - - g_clear_pointer (&wayland_popup->layout, gdk_popup_layout_unref); - wayland_popup->layout = gdk_popup_layout_copy (layout); - wayland_popup->unconstrained_width = width; - wayland_popup->unconstrained_height = height; - - if (!wayland_popup->display_server.xdg_popup || - xdg_popup_get_version (wayland_popup->display_server.xdg_popup) < - XDG_POPUP_REPOSITION_SINCE_VERSION) - { - g_warning_once ("Compositor doesn't support moving popups, " - "relying on remapping"); - queue_relayout_fallback (wayland_popup, layout); - - return; - } - - positioner = create_dynamic_positioner (wayland_popup, - width, height, layout, - TRUE); - xdg_popup_reposition (wayland_popup->display_server.xdg_popup, - positioner, - ++wayland_popup->reposition_token); - xdg_positioner_destroy (positioner); - - gdk_surface_freeze_updates (GDK_SURFACE (wayland_popup)); - - switch (wayland_popup->state) - { - case POPUP_STATE_IDLE: - freeze_popup_toplevel_state (wayland_popup); - break; - case POPUP_STATE_WAITING_FOR_FRAME: - break; - case POPUP_STATE_WAITING_FOR_CONFIGURE: - case POPUP_STATE_WAITING_FOR_REPOSITIONED: - default: - g_assert_not_reached (); - } - - wayland_popup->state = POPUP_STATE_WAITING_FOR_REPOSITIONED; -} - -static gboolean -is_relayout_finished (GdkSurface *surface) -{ - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); - - if (!impl->initial_configure_received) - return FALSE; - - if (GDK_IS_WAYLAND_POPUP (surface)) - { - GdkWaylandPopup *popup = GDK_WAYLAND_POPUP (surface); - if (popup->reposition_token != popup->received_reposition_token) - return FALSE; - } - - return TRUE; -} - -static GdkWaylandSeat * -find_grab_input_seat (GdkSurface *surface, - GdkSurface *parent) -{ - GdkWaylandPopup *popup = GDK_WAYLAND_POPUP (surface); - GdkWaylandPopup *tmp_popup; - - /* Use the device that was used for the grab as the device for - * the popup surface setup - so this relies on GTK taking the - * grab before showing the popup surface. - */ - if (popup->grab_input_seat) - return GDK_WAYLAND_SEAT (popup->grab_input_seat); - - while (parent) - { - if (!GDK_IS_WAYLAND_POPUP (parent)) - break; - - tmp_popup = GDK_WAYLAND_POPUP (parent); - - if (tmp_popup->grab_input_seat) - return GDK_WAYLAND_SEAT (tmp_popup->grab_input_seat); - - parent = parent->parent; - } - - return NULL; -} - -static void -gdk_wayland_surface_map_popup (GdkWaylandPopup *wayland_popup, - int width, - int height, - GdkPopupLayout *layout) -{ - GdkSurface *surface = GDK_SURFACE (wayland_popup); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); - GdkSurface *parent; - GdkWaylandSeat *grab_input_seat; - - parent = surface->parent; - if (!parent) - { - g_warning ("Couldn't map as surface %p as popup because it doesn't have a parent", - surface); - return; - } - - if (surface->autohide) - grab_input_seat = find_grab_input_seat (surface, parent); - else - grab_input_seat = NULL; - - if (!gdk_wayland_surface_create_xdg_popup (wayland_popup, - parent, - grab_input_seat, - width, height, - layout)) - return; - - wayland_popup->layout = gdk_popup_layout_copy (layout); - wayland_popup->unconstrained_width = width; - wayland_popup->unconstrained_height = height; - wayland_surface->mapped = TRUE; -} - -static void -show_popup (GdkWaylandPopup *wayland_popup, - int width, - int height, - GdkPopupLayout *layout) -{ - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); - - if (!wayland_surface->display_server.wl_surface) - gdk_wayland_surface_create_wl_surface (GDK_SURFACE (wayland_popup)); - - if (wayland_popup->thaw_upon_show) - { - wayland_popup->thaw_upon_show = FALSE; - gdk_surface_thaw_updates (GDK_SURFACE (wayland_popup)); - } - - gdk_wayland_surface_map_popup (wayland_popup, width, height, layout); -} - -typedef struct -{ - int width; - int height; - GdkPopupLayout *layout; -} GrabPrepareData; - -static void -show_grabbing_popup (GdkSeat *seat, - GdkSurface *surface, - gpointer user_data) -{ - GrabPrepareData *data = user_data; - - g_return_if_fail (GDK_IS_WAYLAND_POPUP (surface)); - GdkWaylandPopup *wayland_popup = GDK_WAYLAND_POPUP (surface); - - show_popup (wayland_popup, data->width, data->height, data->layout); -} - -static void -reposition_popup (GdkWaylandPopup *wayland_popup, - int width, - int height, - GdkPopupLayout *layout) -{ - switch (wayland_popup->state) - { - case POPUP_STATE_IDLE: - case POPUP_STATE_WAITING_FOR_FRAME: - do_queue_relayout (wayland_popup, width, height, layout); - break; - case POPUP_STATE_WAITING_FOR_REPOSITIONED: - case POPUP_STATE_WAITING_FOR_CONFIGURE: - g_warn_if_reached (); - break; - default: - g_assert_not_reached (); - } -} - -static gboolean -gdk_wayland_surface_present_popup (GdkWaylandPopup *wayland_popup, - int width, - int height, - GdkPopupLayout *layout) -{ - GdkSurface *surface = GDK_SURFACE (wayland_popup); - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); - GdkWaylandSurface *wayland_surface = GDK_WAYLAND_SURFACE (wayland_popup); - - if (!wayland_surface->mapped) - { - if (surface->autohide) - { - GdkSeat *seat; - - seat = gdk_display_get_default_seat (surface->display); - if (seat) - { - GrabPrepareData data; - GdkGrabStatus result; - - data = (GrabPrepareData) { - .width = width, - .height = height, - .layout = layout, - }; - - result = gdk_seat_grab (seat, - surface, - GDK_SEAT_CAPABILITY_ALL, - TRUE, - NULL, NULL, - show_grabbing_popup, &data); - if (result != GDK_GRAB_SUCCESS) - { - const char *grab_status[] = { - "success", "already grabbed", "invalid time", - "not viewable", "frozen", "failed" - }; - g_warning ("Grab failed: %s", grab_status[result]); - } - } - } - else - { - show_popup (wayland_popup, width, height, layout); - } - } - else - { - if (wayland_popup->unconstrained_width == width && - wayland_popup->unconstrained_height == height && - gdk_popup_layout_equal (wayland_popup->layout, layout)) - return TRUE; - - reposition_popup (wayland_popup, width, height, layout); - } - - while (wayland_popup->display_server.xdg_popup && !is_relayout_finished (surface)) - wl_display_dispatch_queue (display_wayland->wl_display, wayland_surface->event_queue); - - if (wayland_popup->display_server.xdg_popup) - { - gdk_surface_invalidate_rect (surface, NULL); - return TRUE; - } - else - { - return FALSE; - } -} - -static gboolean -gdk_wayland_popup_present (GdkPopup *popup, - int width, - int height, - GdkPopupLayout *layout) -{ - return gdk_wayland_surface_present_popup (GDK_WAYLAND_POPUP (popup), width, height, layout); -} - -static GdkGravity -gdk_wayland_popup_get_surface_anchor (GdkPopup *popup) -{ - return GDK_SURFACE (popup)->popup.surface_anchor; -} - -static GdkGravity -gdk_wayland_popup_get_rect_anchor (GdkPopup *popup) -{ - return GDK_SURFACE (popup)->popup.rect_anchor; -} - -static int -gdk_wayland_popup_get_position_x (GdkPopup *popup) -{ - return GDK_SURFACE (popup)->x; -} - -static int -gdk_wayland_popup_get_position_y (GdkPopup *popup) -{ - return GDK_SURFACE (popup)->y; -} - -static void -gdk_wayland_popup_iface_init (GdkPopupInterface *iface) -{ - iface->present = gdk_wayland_popup_present; - iface->get_surface_anchor = gdk_wayland_popup_get_surface_anchor; - iface->get_rect_anchor = gdk_wayland_popup_get_rect_anchor; - iface->get_position_x = gdk_wayland_popup_get_position_x; - iface->get_position_y = gdk_wayland_popup_get_position_y; -} - -/* }}} */ -/* {{{ Private Popup API */ - -void -_gdk_wayland_surface_set_grab_seat (GdkSurface *surface, - GdkSeat *seat) -{ - GdkWaylandPopup *popup; - - g_return_if_fail (surface != NULL); - - popup = GDK_WAYLAND_POPUP (surface); - popup->grab_input_seat = seat; -} - -/* }}} */ /* vim:set foldmethod=marker expandtab: */ diff --git a/gdk/wayland/meson.build b/gdk/wayland/meson.build index 8fa6c34a05..7e7672af9f 100644 --- a/gdk/wayland/meson.build +++ b/gdk/wayland/meson.build @@ -17,6 +17,7 @@ gdk_wayland_sources = files([ 'gdkprimary-wayland.c', 'gdksurface-wayland.c', 'gdktoplevel-wayland.c', + 'gdkpopup-wayland.c', 'gdkvulkancontext-wayland.c', 'wm-button-layout-translation.c', ]) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index d5cb7cc121..909154c231 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -93,6 +93,7 @@ #include "wayland/gdkwayland.h" #include "wayland/gdkdisplay-wayland.h" #include "wayland/gdksurface-wayland.h" +#include "wayland/gdktoplevel-wayland-private.h" #endif #ifdef GDK_WINDOWING_MACOS -- 2.30.2