wip: Split off GdkWaylandPopup
authorMatthias Clasen <mclasen@redhat.com>
Thu, 17 Nov 2022 13:24:40 +0000 (08:24 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Fri, 6 Jan 2023 21:50:19 +0000 (16:50 -0500)
Like the GdkWaylandToplevel split-off, this needs some
more cleanup.

gdk/wayland/gdkpopup-wayland-private.h [new file with mode: 0644]
gdk/wayland/gdkpopup-wayland.c [new file with mode: 0644]
gdk/wayland/gdksurface-wayland-private.h
gdk/wayland/gdksurface-wayland.c
gdk/wayland/meson.build
gtk/gtkwindow.c

diff --git a/gdk/wayland/gdkpopup-wayland-private.h b/gdk/wayland/gdkpopup-wayland-private.h
new file mode 100644 (file)
index 0000000..94e4ee8
--- /dev/null
@@ -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 <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);
+
diff --git a/gdk/wayland/gdkpopup-wayland.c b/gdk/wayland/gdkpopup-wayland.c
new file mode 100644 (file)
index 0000000..d0668aa
--- /dev/null
@@ -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 <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: */
index 8bb895b63068a87de3873fd5c74d42656f55e014..58746aba80293e1c50715a8aad3811d014674da0 100644 (file)
@@ -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;
index e4e8721494c989b0b4b59d9e936637951948ff7a..93d1620f24b6c94012c5f218a070266cfb8ba546 100644 (file)
@@ -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: */
index 8fa6c34a05f52015054e2d3dc8fd4e0ff11a815f..7e7672af9f748bae1db01d43c6bf3314d0ea0d2b 100644 (file)
@@ -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',
 ])
index d5cb7cc121a9eb8a90239a3c6f751d24e7f902b3..909154c231a7a6369d9936b9716bed56ef93c415 100644 (file)
@@ -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