--- /dev/null
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#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);
+
--- /dev/null
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 <wayland/xdg-shell-unstable-v6-client-protocol.h>
+#include <wayland/xdg-foreign-unstable-v2-client-protocol.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <netinet/in.h>
+#include <unistd.h>
+
+#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: */
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;
#include "gdksurface-wayland-private.h"
#include "gdktoplevel-wayland-private.h"
+#include "gdkpopup-wayland-private.h"
/**
* GdkWaylandSurface:
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
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)
{
impl->saved_height = -1;
}
-static void
+void
gdk_wayland_surface_freeze_state (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
impl->state_freeze_count++;
}
-static void
+void
gdk_wayland_surface_thaw_state (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (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,
}
}
-static void
+void
gdk_wayland_surface_hide_surface (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
_gdk_surface_clear_update_area (surface);
}
-static void
+void
gdk_wayland_surface_move_resize (GdkSurface *surface,
int x,
int y,
}
/* }}}} */
-/* {{{ 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: */
'gdkprimary-wayland.c',
'gdksurface-wayland.c',
'gdktoplevel-wayland.c',
+ 'gdkpopup-wayland.c',
'gdkvulkancontext-wayland.c',
'wm-button-layout-translation.c',
])
#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