[mutter] wayland/xdg-shell: Port to unstable v6



commit ef3e036b45facfa8aa755eadd289ae8ab23ced5b
Author: Jonas Ådahl <jadahl gmail com>
Date:   Fri Jul 1 16:30:14 2016 +0800

    wayland/xdg-shell: Port to unstable v6
    
    Port the xdg_shell implementation to use the unstable v6 protocol. This
    includes:
    
     - making xdg_surface a generic base interface for xdg_shell surface
       roles
     - create a xdg_toplevel role replacing the old xdg_surface
     - change the xdg_opup role to be based on xdg_surface
     - make xdg_popup not grab by default
     - add support for xdg_positioner
    
    https://bugzilla.gnome.org/show_bug.cgi?id=769936

 configure.ac                         |    2 +-
 src/Makefile.am                      |    4 +-
 src/wayland/meta-wayland-xdg-shell.c | 1551 ++++++++++++++++++++++++++--------
 src/wayland/meta-wayland-xdg-shell.h |   23 +-
 4 files changed, 1220 insertions(+), 360 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b13090b..9fc9868 100644
--- a/configure.ac
+++ b/configure.ac
@@ -272,7 +272,7 @@ AS_IF([test "$have_wayland" = "yes"], [
   AC_SUBST([WAYLAND_SCANNER])
   AC_DEFINE([HAVE_WAYLAND],[1],[Define if you want to enable Wayland support])
 
-  PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.6],
+  PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.7],
                    [ac_wayland_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`])
   AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $ac_wayland_protocols_pkgdatadir)
 ])
diff --git a/src/Makefile.am b/src/Makefile.am
index e6b8bf3..6299702 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -61,8 +61,8 @@ mutter_built_sources += \
        gtk-shell-server-protocol.h             \
        gtk-primary-selection-protocol.c                                \
        gtk-primary-selection-server-protocol.h                         \
-       xdg-shell-unstable-v5-protocol.c                                \
-       xdg-shell-unstable-v5-server-protocol.h                         \
+       xdg-shell-unstable-v6-protocol.c                                \
+       xdg-shell-unstable-v6-server-protocol.h                         \
        relative-pointer-unstable-v1-protocol.c                         \
        relative-pointer-unstable-v1-server-protocol.h                  \
        pointer-constraints-unstable-v1-protocol.c                      \
diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c
index 6a7441d..e8c56bb 100644
--- a/src/wayland/meta-wayland-xdg-shell.c
+++ b/src/wayland/meta-wayland-xdg-shell.c
@@ -33,33 +33,87 @@
 #include "wayland/meta-wayland-surface.h"
 #include "wayland/meta-wayland-versions.h"
 #include "wayland/meta-window-wayland.h"
-#include "xdg-shell-unstable-v5-server-protocol.h"
+#include "xdg-shell-unstable-v6-server-protocol.h"
 
-struct _MetaWaylandXdgSurface
+enum
 {
-  MetaWaylandSurfaceRoleShellSurface parent;
+  XDG_SURFACE_PROP_0,
 
+  XDG_SURFACE_PROP_SHELL_CLIENT,
+  XDG_SURFACE_PROP_RESOURCE,
+};
+
+typedef struct _MetaWaylandXdgShellClient
+{
+  struct wl_resource *resource;
+  GList *surfaces;
+  GList *surface_constructors;
+} MetaWaylandXdgShellClient;
+
+typedef struct _MetaWaylandXdgPositioner
+{
+  MetaRectangle anchor_rect;
+  int32_t width;
+  int32_t height;
+  uint32_t gravity;
+  uint32_t anchor;
+  uint32_t constraint_adjustment;
+  int32_t offset_x;
+  int32_t offset_y;
+} MetaWaylandXdgPositioner;
+
+typedef struct _MetaWaylandXdgSurfaceConstructor
+{
+  MetaWaylandSurface *surface;
+  struct wl_resource *resource;
+  MetaWaylandXdgShellClient *shell_client;
+} MetaWaylandXdgSurfaceConstructor;
+
+typedef struct _MetaWaylandXdgSurfacePrivate
+{
   struct wl_resource *resource;
-  struct wl_resource *xdg_shell_resource;
+  MetaWaylandXdgShellClient *shell_client;
   MetaWaylandSerial acked_configure_serial;
-  gboolean has_set_geometry;
+  MetaRectangle geometry;
+
+  guint configure_sent : 1;
+  guint first_buffer_attached : 1;
+  guint has_set_geometry : 1;
+} MetaWaylandXdgSurfacePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandXdgSurface,
+                            meta_wayland_xdg_surface,
+                            META_TYPE_WAYLAND_SURFACE_ROLE_SHELL_SURFACE);
+
+struct _MetaWaylandXdgToplevel
+{
+  MetaWaylandXdgSurface parent;
+
+  struct wl_resource *resource;
 };
 
-G_DEFINE_TYPE (MetaWaylandXdgSurface,
-               meta_wayland_xdg_surface,
-               META_TYPE_WAYLAND_SURFACE_ROLE_SHELL_SURFACE);
+G_DEFINE_TYPE (MetaWaylandXdgToplevel,
+               meta_wayland_xdg_toplevel,
+               META_TYPE_WAYLAND_XDG_SURFACE);
 
 struct _MetaWaylandXdgPopup
 {
-  MetaWaylandSurfaceRoleShellSurface parent;
+  MetaWaylandXdgSurface parent;
 
   struct wl_resource *resource;
-  struct wl_resource *xdg_shell_resource;
 
   MetaWaylandSurface *parent_surface;
   struct wl_listener parent_destroy_listener;
 
   MetaWaylandPopup *popup;
+
+  struct {
+    MetaWaylandSurface *parent_surface;
+    MetaPlacementRule placement_rule;
+
+    MetaWaylandSeat *grab_seat;
+    uint32_t grab_serial;
+  } setup;
 };
 
 static void
@@ -67,55 +121,58 @@ popup_surface_iface_init (MetaWaylandPopupSurfaceInterface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (MetaWaylandXdgPopup,
                          meta_wayland_xdg_popup,
-                         META_TYPE_WAYLAND_SURFACE_ROLE_SHELL_SURFACE,
+                         META_TYPE_WAYLAND_XDG_SURFACE,
                          G_IMPLEMENT_INTERFACE (META_TYPE_WAYLAND_POPUP_SURFACE,
                                                 popup_surface_iface_init));
 
+static MetaPlacementRule
+meta_wayland_xdg_positioner_to_placement (MetaWaylandXdgPositioner *xdg_positioner);
+
+static struct wl_resource *
+meta_wayland_xdg_surface_get_shell_resource (MetaWaylandXdgSurface *xdg_surface);
+
+static MetaRectangle
+meta_wayland_xdg_surface_get_window_geometry (MetaWaylandXdgSurface *xdg_surface);
+
+static uint32_t
+meta_wayland_xdg_surface_send_configure (MetaWaylandXdgSurface *xdg_surface);
+
 static MetaWaylandSurface *
 surface_from_xdg_surface_resource (struct wl_resource *resource)
 {
-  MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource);
-  MetaWaylandSurfaceRole *surface_role =
-    META_WAYLAND_SURFACE_ROLE (xdg_surface);
-
+  MetaWaylandSurfaceRole *surface_role = wl_resource_get_user_data (resource);
   return meta_wayland_surface_role_get_surface (surface_role);
 }
 
 static MetaWaylandSurface *
-surface_from_xdg_popup_resource (struct wl_resource *resource)
+surface_from_xdg_toplevel_resource (struct wl_resource *resource)
 {
-  MetaWaylandXdgPopup *xdg_popup = wl_resource_get_user_data (resource);
-  MetaWaylandSurfaceRole *surface_role =
-    META_WAYLAND_SURFACE_ROLE (xdg_popup);
-
-  return meta_wayland_surface_role_get_surface (surface_role);
+  return surface_from_xdg_surface_resource (resource);
 }
 
 static void
-xdg_surface_destructor (struct wl_resource *resource)
+xdg_toplevel_destructor (struct wl_resource *resource)
 {
-  MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource);
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandXdgToplevel *xdg_toplevel = wl_resource_get_user_data (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
 
-  meta_wayland_compositor_destroy_frame_callbacks (surface->compositor,
-                                                   surface);
   meta_wayland_surface_destroy_window (surface);
-  xdg_surface->resource = NULL;
+  xdg_toplevel->resource = NULL;
 }
 
 static void
-xdg_surface_destroy (struct wl_client   *client,
-                     struct wl_resource *resource)
+xdg_toplevel_destroy (struct wl_client   *client,
+                      struct wl_resource *resource)
 {
   wl_resource_destroy (resource);
 }
 
 static void
-xdg_surface_set_parent (struct wl_client   *client,
-                        struct wl_resource *resource,
-                        struct wl_resource *parent_resource)
+xdg_toplevel_set_parent (struct wl_client   *client,
+                         struct wl_resource *resource,
+                         struct wl_resource *parent_resource)
 {
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
   MetaWindow *transient_for = NULL;
 
   if (parent_resource)
@@ -130,11 +187,11 @@ xdg_surface_set_parent (struct wl_client   *client,
 }
 
 static void
-xdg_surface_set_title (struct wl_client   *client,
-                       struct wl_resource *resource,
-                       const char         *title)
+xdg_toplevel_set_title (struct wl_client   *client,
+                        struct wl_resource *resource,
+                        const char         *title)
 {
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
 
   if (!g_utf8_validate (title, -1, NULL))
     title = "";
@@ -143,11 +200,11 @@ xdg_surface_set_title (struct wl_client   *client,
 }
 
 static void
-xdg_surface_set_app_id (struct wl_client   *client,
-                        struct wl_resource *resource,
-                        const char         *app_id)
+xdg_toplevel_set_app_id (struct wl_client   *client,
+                         struct wl_resource *resource,
+                         const char         *app_id)
 {
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
 
   if (!g_utf8_validate (app_id, -1, NULL))
     app_id = "";
@@ -156,15 +213,15 @@ xdg_surface_set_app_id (struct wl_client   *client,
 }
 
 static void
-xdg_surface_show_window_menu (struct wl_client   *client,
-                              struct wl_resource *resource,
-                              struct wl_resource *seat_resource,
-                              uint32_t            serial,
-                              int32_t             x,
-                              int32_t             y)
+xdg_toplevel_show_window_menu (struct wl_client   *client,
+                               struct wl_resource *resource,
+                               struct wl_resource *seat_resource,
+                               uint32_t            serial,
+                               int32_t             x,
+                               int32_t             y)
 {
   MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
 
   if (!meta_wayland_seat_get_grab_info (seat, surface, serial, FALSE, NULL, NULL))
     return;
@@ -175,13 +232,13 @@ xdg_surface_show_window_menu (struct wl_client   *client,
 }
 
 static void
-xdg_surface_move (struct wl_client   *client,
-                  struct wl_resource *resource,
-                  struct wl_resource *seat_resource,
-                  guint32             serial)
+xdg_toplevel_move (struct wl_client   *client,
+                   struct wl_resource *resource,
+                   struct wl_resource *seat_resource,
+                   uint32_t            serial)
 {
   MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
   gfloat x, y;
 
   if (!meta_wayland_seat_get_grab_info (seat, surface, serial, TRUE, &x, &y))
@@ -191,17 +248,17 @@ xdg_surface_move (struct wl_client   *client,
 }
 
 static MetaGrabOp
-grab_op_for_xdg_surface_resize_edge (int edge)
+grab_op_for_xdg_toplevel_resize_edge (int edge)
 {
   MetaGrabOp op = META_GRAB_OP_WINDOW_BASE;
 
-  if (edge & XDG_SURFACE_RESIZE_EDGE_TOP)
+  if (edge & ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP)
     op |= META_GRAB_OP_WINDOW_DIR_NORTH;
-  if (edge & XDG_SURFACE_RESIZE_EDGE_BOTTOM)
+  if (edge & ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM)
     op |= META_GRAB_OP_WINDOW_DIR_SOUTH;
-  if (edge & XDG_SURFACE_RESIZE_EDGE_LEFT)
+  if (edge & ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT)
     op |= META_GRAB_OP_WINDOW_DIR_WEST;
-  if (edge & XDG_SURFACE_RESIZE_EDGE_RIGHT)
+  if (edge & ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT)
     op |= META_GRAB_OP_WINDOW_DIR_EAST;
 
   if (op == META_GRAB_OP_WINDOW_BASE)
@@ -214,124 +271,111 @@ grab_op_for_xdg_surface_resize_edge (int edge)
 }
 
 static void
-xdg_surface_resize (struct wl_client   *client,
-                    struct wl_resource *resource,
-                    struct wl_resource *seat_resource,
-                    guint32             serial,
-                    guint32             edges)
+xdg_toplevel_resize (struct wl_client   *client,
+                     struct wl_resource *resource,
+                     struct wl_resource *seat_resource,
+                     uint32_t            serial,
+                     uint32_t            edges)
 {
   MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
   gfloat x, y;
   MetaGrabOp grab_op;
 
   if (!meta_wayland_seat_get_grab_info (seat, surface, serial, TRUE, &x, &y))
     return;
 
-  grab_op = grab_op_for_xdg_surface_resize_edge (edges);
+  grab_op = grab_op_for_xdg_toplevel_resize_edge (edges);
   meta_wayland_surface_begin_grab_op (surface, seat, grab_op, x, y);
 }
 
 static void
-xdg_surface_ack_configure (struct wl_client   *client,
+xdg_toplevel_set_max_size (struct wl_client   *client,
                            struct wl_resource *resource,
-                           uint32_t            serial)
+                           int32_t             width,
+                           int32_t             height)
 {
-  MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource);
-
-  xdg_surface->acked_configure_serial.set = TRUE;
-  xdg_surface->acked_configure_serial.value = serial;
+  /* TODO */
 }
 
 static void
-xdg_surface_set_window_geometry (struct wl_client   *client,
-                                 struct wl_resource *resource,
-                                 int32_t             x,
-                                 int32_t             y,
-                                 int32_t             width,
-                                 int32_t             height)
+xdg_toplevel_set_min_size (struct wl_client   *client,
+                           struct wl_resource *resource,
+                           int32_t             width,
+                           int32_t             height)
 {
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
-
-  surface->pending->has_new_geometry = TRUE;
-  surface->pending->new_geometry.x = x;
-  surface->pending->new_geometry.y = y;
-  surface->pending->new_geometry.width = width;
-  surface->pending->new_geometry.height = height;
+  /* TODO */
 }
 
 static void
-xdg_surface_set_maximized (struct wl_client   *client,
-                           struct wl_resource *resource)
+xdg_toplevel_set_maximized (struct wl_client   *client,
+                            struct wl_resource *resource)
 {
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
 
   meta_window_maximize (surface->window, META_MAXIMIZE_BOTH);
 }
 
 static void
-xdg_surface_unset_maximized (struct wl_client   *client,
-                             struct wl_resource *resource)
+xdg_toplevel_unset_maximized (struct wl_client   *client,
+                              struct wl_resource *resource)
 {
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
 
   meta_window_unmaximize (surface->window, META_MAXIMIZE_BOTH);
 }
 
 static void
-xdg_surface_set_fullscreen (struct wl_client   *client,
-                            struct wl_resource *resource,
-                            struct wl_resource *output_resource)
+xdg_toplevel_set_fullscreen (struct wl_client   *client,
+                             struct wl_resource *resource,
+                             struct wl_resource *output_resource)
 {
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
 
   meta_window_make_fullscreen (surface->window);
 }
 
 static void
-xdg_surface_unset_fullscreen (struct wl_client   *client,
-                              struct wl_resource *resource)
+xdg_toplevel_unset_fullscreen (struct wl_client   *client,
+                               struct wl_resource *resource)
 {
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
 
   meta_window_unmake_fullscreen (surface->window);
 }
 
 static void
-xdg_surface_set_minimized (struct wl_client   *client,
-                           struct wl_resource *resource)
+xdg_toplevel_set_minimized (struct wl_client   *client,
+                            struct wl_resource *resource)
 {
-  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource);
 
   meta_window_minimize (surface->window);
 }
 
-static const struct xdg_surface_interface meta_wayland_xdg_surface_interface = {
-  xdg_surface_destroy,
-  xdg_surface_set_parent,
-  xdg_surface_set_title,
-  xdg_surface_set_app_id,
-  xdg_surface_show_window_menu,
-  xdg_surface_move,
-  xdg_surface_resize,
-  xdg_surface_ack_configure,
-  xdg_surface_set_window_geometry,
-  xdg_surface_set_maximized,
-  xdg_surface_unset_maximized,
-  xdg_surface_set_fullscreen,
-  xdg_surface_unset_fullscreen,
-  xdg_surface_set_minimized,
+static const struct zxdg_toplevel_v6_interface meta_wayland_xdg_toplevel_interface = {
+  xdg_toplevel_destroy,
+  xdg_toplevel_set_parent,
+  xdg_toplevel_set_title,
+  xdg_toplevel_set_app_id,
+  xdg_toplevel_show_window_menu,
+  xdg_toplevel_move,
+  xdg_toplevel_resize,
+  xdg_toplevel_set_max_size,
+  xdg_toplevel_set_min_size,
+  xdg_toplevel_set_maximized,
+  xdg_toplevel_unset_maximized,
+  xdg_toplevel_set_fullscreen,
+  xdg_toplevel_unset_fullscreen,
+  xdg_toplevel_set_minimized,
 };
 
 static void
 xdg_popup_destructor (struct wl_resource *resource)
 {
-  MetaWaylandSurface *surface = surface_from_xdg_popup_resource (resource);
   MetaWaylandXdgPopup *xdg_popup =
     META_WAYLAND_XDG_POPUP (wl_resource_get_user_data (resource));
 
-  meta_wayland_compositor_destroy_frame_callbacks (surface->compositor,
-                                                   surface);
   if (xdg_popup->parent_surface)
     {
       wl_list_remove (&xdg_popup->parent_destroy_listener.link);
@@ -345,14 +389,55 @@ xdg_popup_destructor (struct wl_resource *resource)
 }
 
 static void
-xdg_popup_destroy (struct wl_client *client,
+xdg_popup_destroy (struct wl_client   *client,
                    struct wl_resource *resource)
 {
   wl_resource_destroy (resource);
 }
 
-static const struct xdg_popup_interface meta_wayland_xdg_popup_interface = {
+static void
+xdg_popup_grab (struct wl_client   *client,
+                struct wl_resource *resource,
+                struct wl_resource *seat_resource,
+                uint32_t            serial)
+{
+  MetaWaylandXdgPopup *xdg_popup =
+    META_WAYLAND_XDG_POPUP (wl_resource_get_user_data (resource));
+  MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
+  MetaWaylandSurface *parent_surface;
+  MetaWaylandSurface *top_popup;
+
+  parent_surface = xdg_popup->setup.parent_surface;
+  if (!parent_surface)
+    {
+      wl_resource_post_error (resource,
+                              ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
+                              "tried to grab after popup was mapped");
+      return;
+    }
+
+  top_popup = meta_wayland_pointer_get_top_popup (&seat->pointer);
+  if ((top_popup == NULL &&
+       !META_IS_WAYLAND_XDG_SURFACE (parent_surface->role)) ||
+      (top_popup != NULL && parent_surface != top_popup))
+    {
+      MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup);
+      struct wl_resource *xdg_shell_resource =
+        meta_wayland_xdg_surface_get_shell_resource (xdg_surface);
+
+      wl_resource_post_error (xdg_shell_resource,
+                              ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP,
+                              "parent not top most surface");
+      return;
+    }
+
+  xdg_popup->setup.grab_seat = seat;
+  xdg_popup->setup.grab_serial = serial;
+}
+
+static const struct zxdg_popup_v6_interface meta_wayland_xdg_popup_interface = {
   xdg_popup_destroy,
+  xdg_popup_grab,
 };
 
 static void
@@ -361,13 +446,16 @@ handle_popup_parent_destroyed (struct wl_listener *listener,
 {
   MetaWaylandXdgPopup *xdg_popup =
     wl_container_of (listener, xdg_popup, parent_destroy_listener);
+  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup);
+  struct wl_resource *xdg_shell_resource =
+    meta_wayland_xdg_surface_get_shell_resource (xdg_surface);
   MetaWaylandSurfaceRole *surface_role =
     META_WAYLAND_SURFACE_ROLE (xdg_popup);
   MetaWaylandSurface *surface =
     meta_wayland_surface_role_get_surface (surface_role);
 
-  wl_resource_post_error (xdg_popup->xdg_shell_resource,
-                          XDG_SHELL_ERROR_NOT_THE_TOPMOST_POPUP,
+  wl_resource_post_error (xdg_shell_resource,
+                          ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP,
                           "destroyed popup not top most popup");
   xdg_popup->parent_surface = NULL;
 
@@ -382,46 +470,77 @@ fill_states (struct wl_array *states, MetaWindow *window)
   if (META_WINDOW_MAXIMIZED (window))
     {
       s = wl_array_add (states, sizeof *s);
-      *s = XDG_SURFACE_STATE_MAXIMIZED;
+      *s = ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED;
     }
   if (meta_window_is_fullscreen (window))
     {
       s = wl_array_add (states, sizeof *s);
-      *s = XDG_SURFACE_STATE_FULLSCREEN;
+      *s = ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN;
     }
   if (meta_grab_op_is_resizing (window->display->grab_op))
     {
       s = wl_array_add (states, sizeof *s);
-      *s = XDG_SURFACE_STATE_RESIZING;
+      *s = ZXDG_TOPLEVEL_V6_STATE_RESIZING;
     }
   if (meta_window_appears_focused (window))
     {
       s = wl_array_add (states, sizeof *s);
-      *s = XDG_SURFACE_STATE_ACTIVATED;
+      *s = ZXDG_TOPLEVEL_V6_STATE_ACTIVATED;
     }
 }
 
 static void
-xdg_surface_role_commit (MetaWaylandSurfaceRole  *surface_role,
-                         MetaWaylandPendingState *pending)
+meta_wayland_xdg_toplevel_send_configure (MetaWaylandXdgToplevel *xdg_toplevel,
+                                          int                     new_width,
+                                          int                     new_height,
+                                          MetaWaylandSerial      *sent_serial)
 {
-  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role);
+  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_toplevel);
+  MetaWaylandSurfaceRole *surface_role =
+    META_WAYLAND_SURFACE_ROLE (xdg_toplevel);
+  MetaWaylandSurface *surface =
+    meta_wayland_surface_role_get_surface (surface_role);
+  struct wl_array states;
+  uint32_t serial;
+
+  wl_array_init (&states);
+  fill_states (&states, surface->window);
+
+  zxdg_toplevel_v6_send_configure (xdg_toplevel->resource,
+                                   new_width, new_height,
+                                   &states);
+  wl_array_release (&states);
+
+  serial = meta_wayland_xdg_surface_send_configure (xdg_surface);
+
+  if (sent_serial)
+    {
+      sent_serial->set = TRUE;
+      sent_serial->value = serial;
+    }
+}
+
+static void
+xdg_toplevel_role_commit (MetaWaylandSurfaceRole  *surface_role,
+                          MetaWaylandPendingState *pending)
+{
+  MetaWaylandXdgToplevel *xdg_toplevel = META_WAYLAND_XDG_TOPLEVEL (surface_role);
+  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_toplevel);
+  MetaWaylandXdgSurfacePrivate *xdg_surface_priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
   MetaWaylandSurfaceRoleClass *surface_role_class;
   MetaWaylandSurface *surface =
     meta_wayland_surface_role_get_surface (surface_role);
   MetaWindow *window = surface->window;
-  MetaRectangle geom = { 0 };
+  MetaRectangle window_geometry;
 
   surface_role_class =
-    META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_surface_parent_class);
+    META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_toplevel_parent_class);
   surface_role_class->commit (surface_role, pending);
 
-  if (surface->buffer_ref.buffer == NULL)
+  if (!xdg_surface_priv->configure_sent)
     {
-      /* XDG surfaces can't commit NULL buffers */
-      wl_resource_post_error (surface->resource,
-                              WL_DISPLAY_ERROR_INVALID_OBJECT,
-                              "Cannot commit a NULL buffer to an xdg_surface");
+      meta_wayland_xdg_toplevel_send_configure (xdg_toplevel, 0, 0, NULL);
       return;
     }
 
@@ -432,182 +551,225 @@ xdg_surface_role_commit (MetaWaylandSurfaceRole  *surface_role,
   if (!window)
     return;
 
-  if (pending->has_new_geometry)
-    {
-      /* If we have new geometry, use it. */
-      geom = pending->new_geometry;
-      xdg_surface->has_set_geometry = TRUE;
-    }
-  else if (!xdg_surface->has_set_geometry)
-    {
-      /* If the surface has never set any geometry, calculate
-       * a default one unioning the surface and all subsurfaces together. */
-      meta_wayland_surface_calculate_window_geometry (surface, &geom, 0, 0);
-    }
-  else
+  if (!pending->has_new_geometry)
     {
-      /* Otherwise, keep the geometry the same. */
-
-      /* XXX: We don't store the geometry in any consistent place
-       * right now, so we can't re-fetch it. We should change
-       * meta_window_wayland_move_resize. */
-
-      /* XXX: This is the common case. Recognize it to prevent
-       * a warning. */
-      if (pending->dx == 0 && pending->dy == 0)
-        return;
-
-      g_warning ("XXX: Attach-initiated move without a new geometry. This is unimplemented right now.");
+      if (pending->dx != 0 || pending->dx != 0)
+        {
+          g_warning ("XXX: Attach-initiated move without a new geometry. This is unimplemented right now.");
+        }
       return;
     }
 
+  window_geometry = meta_wayland_xdg_surface_get_window_geometry (xdg_surface);
   meta_window_wayland_move_resize (window,
-                                   &xdg_surface->acked_configure_serial,
-                                   geom, pending->dx, pending->dy);
-  xdg_surface->acked_configure_serial.set = FALSE;
+                                   &xdg_surface_priv->acked_configure_serial,
+                                   window_geometry,
+                                   pending->dx, pending->dy);
+  xdg_surface_priv->acked_configure_serial.set = FALSE;
 }
 
 static MetaWaylandSurface *
-xdg_surface_role_get_toplevel (MetaWaylandSurfaceRole *surface_role)
+xdg_toplevel_role_get_toplevel (MetaWaylandSurfaceRole *surface_role)
 {
   return meta_wayland_surface_role_get_surface (surface_role);
 }
 
 static void
-xdg_surface_role_configure (MetaWaylandSurfaceRoleShellSurface *shell_surface_role,
-                            int                                 new_x,
-                            int                                 new_y,
-                            int                                 new_width,
-                            int                                 new_height,
-                            MetaWaylandSerial                  *sent_serial)
+xdg_toplevel_role_configure (MetaWaylandSurfaceRoleShellSurface *shell_surface_role,
+                             int                                 new_x,
+                             int                                 new_y,
+                             int                                 new_width,
+                             int                                 new_height,
+                             MetaWaylandSerial                  *sent_serial)
 {
-  MetaWaylandXdgSurface *xdg_surface =
-    META_WAYLAND_XDG_SURFACE (shell_surface_role);
-  MetaWaylandSurfaceRole *surface_role =
-    META_WAYLAND_SURFACE_ROLE (shell_surface_role);
-  MetaWaylandSurface *surface =
-    meta_wayland_surface_role_get_surface (surface_role);
-  struct wl_client *client = wl_resource_get_client (xdg_surface->resource);
-  struct wl_display *display = wl_client_get_display (client);
-  uint32_t serial = wl_display_next_serial (display);
-  struct wl_array states;
+  MetaWaylandXdgToplevel *xdg_toplevel =
+    META_WAYLAND_XDG_TOPLEVEL (shell_surface_role);
+  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_toplevel);
+  MetaWaylandXdgSurfacePrivate *xdg_surface_priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
 
-  if (!xdg_surface->resource)
+  if (!xdg_surface_priv->resource)
     return;
 
-  wl_array_init (&states);
-  fill_states (&states, surface->window);
-
-  xdg_surface_send_configure (xdg_surface->resource,
-                              new_width, new_height,
-                              &states,
-                              serial);
-
-  wl_array_release (&states);
+  if (!xdg_toplevel->resource)
+    return;
 
-  if (sent_serial)
-    {
-      sent_serial->set = TRUE;
-      sent_serial->value = serial;
-    }
+  meta_wayland_xdg_toplevel_send_configure (xdg_toplevel,
+                                            new_width, new_height,
+                                            sent_serial);
 }
 
 static void
-xdg_surface_role_managed (MetaWaylandSurfaceRoleShellSurface *shell_surface_role,
-                          MetaWindow                         *window)
+xdg_toplevel_role_managed (MetaWaylandSurfaceRoleShellSurface *shell_surface_role,
+                           MetaWindow                         *window)
 {
 }
 
 static void
-xdg_surface_role_ping (MetaWaylandSurfaceRoleShellSurface *shell_surface_role,
-                       uint32_t                            serial)
+xdg_toplevel_role_close (MetaWaylandSurfaceRoleShellSurface *shell_surface_role)
 {
-  MetaWaylandXdgSurface *xdg_surface =
-    META_WAYLAND_XDG_SURFACE (shell_surface_role);
+  MetaWaylandXdgToplevel *xdg_toplevel =
+    META_WAYLAND_XDG_TOPLEVEL (shell_surface_role);
 
-  xdg_shell_send_ping (xdg_surface->xdg_shell_resource, serial);
+  zxdg_toplevel_v6_send_close (xdg_toplevel->resource);
 }
 
 static void
-xdg_surface_role_close (MetaWaylandSurfaceRoleShellSurface *shell_surface_role)
+xdg_toplevel_role_shell_client_destroyed (MetaWaylandXdgSurface *xdg_surface)
 {
-  MetaWaylandXdgSurface *xdg_surface =
-    META_WAYLAND_XDG_SURFACE (shell_surface_role);
+  MetaWaylandXdgToplevel *xdg_toplevel =
+    META_WAYLAND_XDG_TOPLEVEL (xdg_surface);
+  struct wl_resource *xdg_shell_resource =
+    meta_wayland_xdg_surface_get_shell_resource (xdg_surface);
+  MetaWaylandXdgSurfaceClass *xdg_surface_class =
+    META_WAYLAND_XDG_SURFACE_CLASS (meta_wayland_xdg_toplevel_parent_class);
 
-  xdg_surface_send_close (xdg_surface->resource);
+  xdg_surface_class->shell_client_destroyed (xdg_surface);
+
+  if (xdg_toplevel->resource)
+    {
+      wl_resource_post_error (xdg_shell_resource,
+                              ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES,
+                              "xdg_shell of xdg_toplevel@%d was destroyed",
+                              wl_resource_get_id (xdg_toplevel->resource));
+
+      wl_resource_destroy (xdg_toplevel->resource);
+    }
 }
 
 static void
-xdg_surface_role_finalize (GObject *object)
+xdg_toplevel_role_finalize (GObject *object)
 {
-  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (object);
+  MetaWaylandXdgToplevel *xdg_toplevel = META_WAYLAND_XDG_TOPLEVEL (object);
 
-  g_clear_pointer (&xdg_surface->resource, wl_resource_destroy);
+  g_clear_pointer (&xdg_toplevel->resource, wl_resource_destroy);
 
-  G_OBJECT_CLASS (meta_wayland_xdg_surface_parent_class)->finalize (object);
+  G_OBJECT_CLASS (meta_wayland_xdg_toplevel_parent_class)->finalize (object);
 }
 
 static void
-meta_wayland_xdg_surface_init (MetaWaylandXdgSurface *role)
+meta_wayland_xdg_toplevel_init (MetaWaylandXdgToplevel *role)
 {
 }
 
 static void
-meta_wayland_xdg_surface_class_init (MetaWaylandXdgSurfaceClass *klass)
+meta_wayland_xdg_toplevel_class_init (MetaWaylandXdgToplevelClass *klass)
 {
   GObjectClass *object_class;
   MetaWaylandSurfaceRoleClass *surface_role_class;
   MetaWaylandSurfaceRoleShellSurfaceClass *shell_surface_role_class;
+  MetaWaylandXdgSurfaceClass *xdg_surface_class;
 
   object_class = G_OBJECT_CLASS (klass);
-  object_class->finalize = xdg_surface_role_finalize;
+  object_class->finalize = xdg_toplevel_role_finalize;
 
   surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass);
-  surface_role_class->commit = xdg_surface_role_commit;
-  surface_role_class->get_toplevel = xdg_surface_role_get_toplevel;
+  surface_role_class->commit = xdg_toplevel_role_commit;
+  surface_role_class->get_toplevel = xdg_toplevel_role_get_toplevel;
 
   shell_surface_role_class =
     META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE_CLASS (klass);
-  shell_surface_role_class->configure = xdg_surface_role_configure;
-  shell_surface_role_class->managed = xdg_surface_role_managed;
-  shell_surface_role_class->ping = xdg_surface_role_ping;
-  shell_surface_role_class->close = xdg_surface_role_close;
+  shell_surface_role_class->configure = xdg_toplevel_role_configure;
+  shell_surface_role_class->managed = xdg_toplevel_role_managed;
+  shell_surface_role_class->close = xdg_toplevel_role_close;
+
+  xdg_surface_class = META_WAYLAND_XDG_SURFACE_CLASS (klass);
+  xdg_surface_class->shell_client_destroyed =
+    xdg_toplevel_role_shell_client_destroyed;
+}
+
+static void
+finish_popup_setup (MetaWaylandXdgPopup *xdg_popup)
+{
+  MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_popup);
+  MetaWaylandSurface *surface =
+    meta_wayland_surface_role_get_surface (surface_role);
+  MetaWaylandSurface *parent_surface;
+  MetaPlacementRule placement_rule;
+  MetaWaylandSeat *seat;
+  uint32_t serial;
+  MetaDisplay *display = meta_get_display ();
+  MetaWindow *window;
+
+  parent_surface = xdg_popup->setup.parent_surface;
+  placement_rule = xdg_popup->setup.placement_rule;
+  seat = xdg_popup->setup.grab_seat;
+  serial = xdg_popup->setup.grab_serial;
+
+  xdg_popup->setup.parent_surface = NULL;
+  xdg_popup->setup.grab_seat = NULL;
+
+  if (seat)
+    {
+      if (!meta_wayland_seat_can_popup (seat, serial))
+        {
+          zxdg_popup_v6_send_popup_done (xdg_popup->resource);
+          return;
+        }
+    }
+
+  xdg_popup->parent_surface = parent_surface;
+  xdg_popup->parent_destroy_listener.notify = handle_popup_parent_destroyed;
+  wl_resource_add_destroy_listener (parent_surface->resource,
+                                    &xdg_popup->parent_destroy_listener);
+
+  window = meta_window_wayland_new (display, surface);
+  meta_window_place_with_placement_rule (window, &placement_rule);
+  meta_wayland_surface_set_window (surface, window);
+
+  if (seat)
+    {
+      MetaWaylandPopupSurface *popup_surface;
+      MetaWaylandPopup *popup;
+
+      meta_window_focus (window, meta_display_get_current_time (display));
+      popup_surface = META_WAYLAND_POPUP_SURFACE (surface->role);
+      popup = meta_wayland_pointer_start_popup_grab (&seat->pointer,
+                                                     popup_surface);
+      if (popup == NULL)
+        {
+          zxdg_popup_v6_send_popup_done (xdg_popup->resource);
+          meta_wayland_surface_destroy_window (surface);
+          return;
+        }
+
+      xdg_popup->popup = popup;
+    }
 }
 
 static void
 xdg_popup_role_commit (MetaWaylandSurfaceRole  *surface_role,
                        MetaWaylandPendingState *pending)
 {
+  MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (surface_role);
+  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role);
   MetaWaylandSurfaceRoleClass *surface_role_class;
   MetaWaylandSurface *surface =
     meta_wayland_surface_role_get_surface (surface_role);
-  MetaWindow *window = surface->window;
-  MetaRectangle geom = { 0 };
+  MetaRectangle window_geometry;
+
+  if (xdg_popup->setup.parent_surface)
+    finish_popup_setup (xdg_popup);
+
+  /* If the window disappeared the surface is not coming back. */
+  if (!surface->window)
+    return;
 
   surface_role_class =
     META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_popup_parent_class);
   surface_role_class->commit (surface_role, pending);
 
-  if (surface->buffer_ref.buffer == NULL)
-    {
-      /* XDG surfaces can't commit NULL buffers */
-      wl_resource_post_error (surface->resource,
-                              WL_DISPLAY_ERROR_INVALID_OBJECT,
-                              "Cannot commit a NULL buffer to an xdg_popup");
-      return;
-    }
-
   if (!pending->newly_attached)
     return;
 
-  /* If the window disappeared the surface is not coming back. */
-  if (!window)
+  if (!surface->buffer_ref.buffer)
     return;
 
-  meta_wayland_surface_calculate_window_geometry (surface, &geom, 0, 0);
-  meta_window_wayland_move_resize (window,
+  window_geometry = meta_wayland_xdg_surface_get_window_geometry (xdg_surface);
+  meta_window_wayland_move_resize (surface->window,
                                    NULL,
-                                   geom, pending->dx, pending->dy);
+                                   window_geometry,
+                                   pending->dx, pending->dy);
 }
 
 static MetaWaylandSurface *
@@ -626,8 +788,27 @@ xdg_popup_role_configure (MetaWaylandSurfaceRoleShellSurface *shell_surface_role
                           int                                 new_height,
                           MetaWaylandSerial                  *sent_serial)
 {
-  /* This can happen if the popup window loses or receives focus.
-   * Just ignore it. */
+  MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (shell_surface_role);
+  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup);
+  MetaWindow *parent_window = xdg_popup->parent_surface->window;
+  int x, y;
+
+  /* If the parent surface was destroyed, its window will be destroyed
+   * before the popup receives the parent-destroy signal. This means that
+   * the popup may potentially get temporary focus until itself is destroyed.
+   * If this happen, don't try to configure the xdg_popup surface.
+   *
+   * FIXME: Could maybe add a signal that is emitted before the window is
+   * created so that we can avoid incorrect intermediate foci.
+   */
+  if (!parent_window)
+    return;
+
+  x = new_x - parent_window->rect.x;
+  y = new_y - parent_window->rect.y;
+  zxdg_popup_v6_send_configure (xdg_popup->resource,
+                                x, y, new_width, new_height);
+  meta_wayland_xdg_surface_send_configure (xdg_surface);
 }
 
 static void
@@ -644,12 +825,25 @@ xdg_popup_role_managed (MetaWaylandSurfaceRoleShellSurface *shell_surface_role,
 }
 
 static void
-xdg_popup_role_ping (MetaWaylandSurfaceRoleShellSurface *shell_surface_role,
-                     uint32_t                            serial)
+xdg_popup_role_shell_client_destroyed (MetaWaylandXdgSurface *xdg_surface)
 {
-  MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (shell_surface_role);
+  MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (xdg_surface);
+  struct wl_resource *xdg_shell_resource =
+    meta_wayland_xdg_surface_get_shell_resource (xdg_surface);
+  MetaWaylandXdgSurfaceClass *xdg_surface_class =
+    META_WAYLAND_XDG_SURFACE_CLASS (meta_wayland_xdg_popup_parent_class);
 
-  xdg_shell_send_ping (xdg_popup->xdg_shell_resource, serial);
+  xdg_surface_class->shell_client_destroyed (xdg_surface);
+
+  if (xdg_popup->resource)
+    {
+      wl_resource_post_error (xdg_shell_resource,
+                              ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES,
+                              "xdg_shell of xdg_popup@%d was destroyed",
+                              wl_resource_get_id (xdg_popup->resource));
+
+      wl_resource_destroy (xdg_popup->resource);
+    }
 }
 
 static void
@@ -657,13 +851,16 @@ meta_wayland_xdg_popup_done (MetaWaylandPopupSurface *popup_surface)
 {
   MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (popup_surface);
 
-  xdg_popup_send_popup_done (xdg_popup->resource);
+  zxdg_popup_v6_send_popup_done (xdg_popup->resource);
 }
 
 static void
 meta_wayland_xdg_popup_dismiss (MetaWaylandPopupSurface *popup_surface)
 {
   MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (popup_surface);
+  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup);
+  struct wl_resource *xdg_shell_resource =
+    meta_wayland_xdg_surface_get_shell_resource (xdg_surface);
   MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_popup);
   MetaWaylandSurface *surface =
     meta_wayland_surface_role_get_surface (surface_role);
@@ -672,8 +869,8 @@ meta_wayland_xdg_popup_dismiss (MetaWaylandPopupSurface *popup_surface)
   top_popup = meta_wayland_popup_get_top_popup (xdg_popup->popup);
   if (surface != top_popup)
     {
-      wl_resource_post_error (xdg_popup->xdg_shell_resource,
-                              XDG_SHELL_ERROR_NOT_THE_TOPMOST_POPUP,
+      wl_resource_post_error (xdg_shell_resource,
+                              ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP,
                               "destroyed popup not top most popup");
     }
 
@@ -720,6 +917,7 @@ meta_wayland_xdg_popup_class_init (MetaWaylandXdgPopupClass *klass)
   GObjectClass *object_class;
   MetaWaylandSurfaceRoleClass *surface_role_class;
   MetaWaylandSurfaceRoleShellSurfaceClass *shell_surface_role_class;
+  MetaWaylandXdgSurfaceClass *xdg_surface_class;
 
   object_class = G_OBJECT_CLASS (klass);
   object_class->finalize = xdg_popup_role_finalize;
@@ -732,169 +930,795 @@ meta_wayland_xdg_popup_class_init (MetaWaylandXdgPopupClass *klass)
     META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE_CLASS (klass);
   shell_surface_role_class->configure = xdg_popup_role_configure;
   shell_surface_role_class->managed = xdg_popup_role_managed;
-  shell_surface_role_class->ping = xdg_popup_role_ping;
+
+  xdg_surface_class = META_WAYLAND_XDG_SURFACE_CLASS (klass);
+  xdg_surface_class->shell_client_destroyed =
+    xdg_popup_role_shell_client_destroyed;
+}
+
+static struct wl_resource *
+meta_wayland_xdg_surface_get_shell_resource (MetaWaylandXdgSurface *xdg_surface)
+{
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+
+  return priv->shell_client->resource;
+}
+
+static MetaRectangle
+meta_wayland_xdg_surface_get_window_geometry (MetaWaylandXdgSurface *xdg_surface)
+{
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+
+  return priv->geometry;
+}
+
+static gboolean
+meta_wayland_xdg_surface_is_assigned (MetaWaylandXdgSurface *xdg_surface)
+{
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+
+  return priv->resource != NULL;
+}
+
+static uint32_t
+meta_wayland_xdg_surface_send_configure (MetaWaylandXdgSurface *xdg_surface)
+{
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+  struct wl_display *display;
+  uint32_t serial;
+
+  display = wl_client_get_display (wl_resource_get_client (priv->resource));
+  serial = wl_display_next_serial (display);
+  zxdg_surface_v6_send_configure (priv->resource, serial);
+
+  priv->configure_sent = TRUE;
+
+  return serial;
 }
 
 static void
-xdg_shell_get_xdg_surface (struct wl_client   *client,
+xdg_surface_destructor (struct wl_resource *resource)
+{
+  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource);
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+
+  meta_wayland_compositor_destroy_frame_callbacks (surface->compositor,
+                                                   surface);
+
+  priv->shell_client->surfaces = g_list_remove (priv->shell_client->surfaces,
+                                                xdg_surface);
+
+  priv->resource = NULL;
+  priv->first_buffer_attached = FALSE;
+}
+
+static void
+xdg_surface_destroy (struct wl_client   *client,
+                     struct wl_resource *resource)
+{
+  wl_resource_destroy (resource);
+}
+
+static void
+xdg_surface_get_toplevel (struct wl_client   *client,
+                          struct wl_resource *resource,
+                          uint32_t            id)
+{
+  MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource);
+  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+  struct wl_resource *xdg_shell_resource =
+    meta_wayland_xdg_surface_get_shell_resource (xdg_surface);
+
+  wl_resource_post_error (xdg_shell_resource, ZXDG_SHELL_V6_ERROR_ROLE,
+                          "wl_surface@%d already has a role assigned",
+                          wl_resource_get_id (surface->resource));
+}
+
+static void
+xdg_surface_get_popup (struct wl_client   *client,
+                       struct wl_resource *resource,
+                       uint32_t            id,
+                       struct wl_resource *parent_resource,
+                       struct wl_resource *positioner_resource)
+{
+  MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource);
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+
+  wl_resource_post_error (priv->shell_client->resource,
+                          ZXDG_SHELL_V6_ERROR_ROLE,
+                          "wl_surface@%d already has a role assigned",
+                          wl_resource_get_id (surface->resource));
+}
+
+static void
+xdg_surface_set_window_geometry (struct wl_client   *client,
+                                 struct wl_resource *resource,
+                                 int32_t             x,
+                                 int32_t             y,
+                                 int32_t             width,
+                                 int32_t             height)
+{
+  MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource);
+
+  surface->pending->has_new_geometry = TRUE;
+  surface->pending->new_geometry.x = x;
+  surface->pending->new_geometry.y = y;
+  surface->pending->new_geometry.width = width;
+  surface->pending->new_geometry.height = height;
+}
+
+static void
+xdg_surface_ack_configure (struct wl_client   *client,
                            struct wl_resource *resource,
-                           guint32             id,
-                           struct wl_resource *surface_resource)
+                           uint32_t            serial)
 {
-  MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
-  MetaWaylandXdgSurface *xdg_surface;
-  MetaWindow *window;
+  MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource);
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
 
-  if (META_IS_WAYLAND_XDG_SURFACE (surface->role) &&
-      META_WAYLAND_XDG_SURFACE (surface->role)->resource)
+  priv->acked_configure_serial.set = TRUE;
+  priv->acked_configure_serial.value = serial;
+}
+
+static const struct zxdg_surface_v6_interface meta_wayland_xdg_surface_interface = {
+  xdg_surface_destroy,
+  xdg_surface_get_toplevel,
+  xdg_surface_get_popup,
+  xdg_surface_set_window_geometry,
+  xdg_surface_ack_configure,
+};
+
+static void
+xdg_surface_role_finalize (GObject *object)
+{
+  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (object);
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+
+  g_clear_pointer (&priv->resource, wl_resource_destroy);
+
+  G_OBJECT_CLASS (meta_wayland_xdg_surface_parent_class)->finalize (object);
+}
+
+static void
+xdg_surface_role_commit (MetaWaylandSurfaceRole  *surface_role,
+                         MetaWaylandPendingState *pending)
+{
+  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role);
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+  MetaWaylandSurface *surface =
+    meta_wayland_surface_role_get_surface (surface_role);
+  MetaWindow *window = surface->window;
+  MetaWaylandSurfaceRoleClass *surface_role_class;
+
+  surface_role_class =
+    META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_surface_parent_class);
+  surface_role_class->commit (surface_role, pending);
+
+  /* Ignore commits when unassigned. */
+  if (!priv->resource)
+    return;
+
+  if (surface->buffer_ref.buffer == NULL && priv->first_buffer_attached)
     {
-      wl_resource_post_error (surface_resource,
+      /* XDG surfaces can't commit NULL buffers */
+      wl_resource_post_error (surface->resource,
                               WL_DISPLAY_ERROR_INVALID_OBJECT,
-                              "xdg_shell::get_xdg_surface already requested");
+                              "Cannot commit a NULL buffer to an xdg_surface");
       return;
     }
 
-  if (!meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_XDG_SURFACE,
-                                         NULL))
+  if (surface->buffer_ref.buffer && !priv->configure_sent)
     {
-      wl_resource_post_error (resource, XDG_SHELL_ERROR_ROLE,
-                              "wl_surface@%d already has a different role",
+      wl_resource_post_error (surface->resource,
+                              ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER,
+                              "buffer committed to unconfigured xdg_surface");
+      return;
+    }
+
+  if (!window)
+    return;
+
+  if (surface->buffer_ref.buffer)
+    priv->first_buffer_attached = TRUE;
+  else
+    return;
+
+  if (pending->has_new_geometry)
+    {
+      /* If we have new geometry, use it. */
+      priv->geometry = pending->new_geometry;
+      priv->has_set_geometry = TRUE;
+    }
+  else if (!priv->has_set_geometry)
+    {
+      /* If the surface has never set any geometry, calculate
+       * a default one unioning the surface and all subsurfaces together. */
+      meta_wayland_surface_calculate_window_geometry (surface,
+                                                      &priv->geometry,
+                                                      0, 0);
+    }
+}
+
+static void
+xdg_surface_role_assigned (MetaWaylandSurfaceRole *surface_role)
+{
+  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role);
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+  MetaWaylandSurface *surface =
+    meta_wayland_surface_role_get_surface (surface_role);
+  struct wl_resource *xdg_shell_resource =
+    meta_wayland_xdg_surface_get_shell_resource (xdg_surface);
+  MetaWaylandSurfaceRoleClass *surface_role_class;
+
+  priv->configure_sent = FALSE;
+  priv->first_buffer_attached = FALSE;
+
+  if (surface->buffer_ref.buffer)
+    {
+      wl_resource_post_error (xdg_shell_resource,
+                              ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE,
+                              "wl_surface@%d already has a buffer committed",
                               wl_resource_get_id (surface->resource));
       return;
     }
 
-  xdg_surface = META_WAYLAND_XDG_SURFACE (surface->role);
-  xdg_surface->resource = wl_resource_create (client,
-                                              &xdg_surface_interface,
-                                              wl_resource_get_version (resource),
-                                              id);
-  wl_resource_set_implementation (xdg_surface->resource,
+  surface_role_class =
+    META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_surface_parent_class);
+  surface_role_class->assigned (surface_role);
+}
+
+static void
+xdg_surface_role_ping (MetaWaylandSurfaceRoleShellSurface *shell_surface_role,
+                       uint32_t                            serial)
+{
+  MetaWaylandXdgSurface *xdg_surface =
+    META_WAYLAND_XDG_SURFACE (shell_surface_role);
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+
+  zxdg_shell_v6_send_ping (priv->shell_client->resource, serial);
+}
+
+static void
+xdg_surface_role_shell_client_destroyed (MetaWaylandXdgSurface *xdg_surface)
+{
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+
+  if (priv->resource)
+    {
+      wl_resource_post_error (priv->shell_client->resource,
+                              ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES,
+                              "xdg_shell of xdg_surface@%d was destroyed",
+                              wl_resource_get_id (priv->resource));
+
+      wl_resource_destroy (priv->resource);
+    }
+}
+
+static void
+meta_wayland_xdg_surface_set_property (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (object);
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+
+  switch (prop_id)
+    {
+    case XDG_SURFACE_PROP_SHELL_CLIENT:
+      priv->shell_client  = g_value_get_pointer (value);
+      break;
+
+    case XDG_SURFACE_PROP_RESOURCE:
+      priv->resource  = g_value_get_pointer (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+meta_wayland_xdg_surface_get_property (GObject      *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+  MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (object);
+  MetaWaylandXdgSurfacePrivate *priv =
+    meta_wayland_xdg_surface_get_instance_private (xdg_surface);
+
+  switch (prop_id)
+    {
+    case XDG_SURFACE_PROP_SHELL_CLIENT:
+      g_value_set_pointer (value, priv->shell_client);
+      break;
+
+    case XDG_SURFACE_PROP_RESOURCE:
+      g_value_set_pointer (value, priv->resource);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+meta_wayland_xdg_surface_init (MetaWaylandXdgSurface *xdg_surface)
+{
+}
+
+static void
+meta_wayland_xdg_surface_class_init (MetaWaylandXdgSurfaceClass *klass)
+{
+  GObjectClass *object_class;
+  MetaWaylandSurfaceRoleClass *surface_role_class;
+  MetaWaylandSurfaceRoleShellSurfaceClass *shell_surface_role_class;
+  GParamSpec *pspec;
+
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->finalize = xdg_surface_role_finalize;
+  object_class->set_property = meta_wayland_xdg_surface_set_property;
+  object_class->get_property = meta_wayland_xdg_surface_get_property;
+
+  surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass);
+  surface_role_class->commit = xdg_surface_role_commit;
+  surface_role_class->assigned = xdg_surface_role_assigned;
+
+  shell_surface_role_class =
+    META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE_CLASS (klass);
+  shell_surface_role_class->ping = xdg_surface_role_ping;
+
+  klass->shell_client_destroyed = xdg_surface_role_shell_client_destroyed;
+
+  pspec = g_param_spec_pointer ("shell-client",
+                                "MetaWaylandXdgShellClient",
+                                "The shell client instance",
+                                G_PARAM_READWRITE |
+                                G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class,
+                                   XDG_SURFACE_PROP_SHELL_CLIENT,
+                                   pspec);
+  pspec = g_param_spec_pointer ("xdg-surface-resource",
+                                "xdg_surface wl_resource",
+                                "The xdg_surface wl_resource instance",
+                                G_PARAM_READWRITE |
+                                G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class,
+                                   XDG_SURFACE_PROP_RESOURCE,
+                                   pspec);
+}
+
+static void
+meta_wayland_xdg_surface_shell_client_destroyed (MetaWaylandXdgSurface *xdg_surface)
+{
+  MetaWaylandXdgSurfaceClass *xdg_surface_class =
+    META_WAYLAND_XDG_SURFACE_GET_CLASS (xdg_surface);
+
+  xdg_surface_class->shell_client_destroyed (xdg_surface);
+}
+
+static void
+meta_wayland_xdg_surface_constructor_finalize (MetaWaylandXdgSurfaceConstructor *constructor,
+                                               MetaWaylandXdgSurface            *xdg_surface)
+{
+  MetaWaylandXdgShellClient *shell_client = constructor->shell_client;
+
+  shell_client->surface_constructors =
+    g_list_remove (shell_client->surface_constructors, constructor);
+  shell_client->surfaces = g_list_append (shell_client->surfaces, xdg_surface);
+
+  wl_resource_set_implementation (constructor->resource,
                                   &meta_wayland_xdg_surface_interface,
                                   xdg_surface,
                                   xdg_surface_destructor);
 
-  xdg_surface->xdg_shell_resource = resource;
+  g_free (constructor);
+}
 
-  window = meta_window_wayland_new (meta_get_display (), surface);
-  meta_wayland_surface_set_window (surface, window);
+static void
+xdg_surface_constructor_destroy (struct wl_client   *client,
+                                 struct wl_resource *resource)
+{
+  wl_resource_post_error (resource,
+                          ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED,
+                          "xdg_surface destroyed before constructed");
+  wl_resource_destroy (resource);
 }
 
 static void
-xdg_shell_get_xdg_popup (struct wl_client   *client,
-                         struct wl_resource *resource,
-                         uint32_t            id,
-                         struct wl_resource *surface_resource,
-                         struct wl_resource *parent_resource,
-                         struct wl_resource *seat_resource,
-                         uint32_t            serial,
-                         int32_t             x,
-                         int32_t             y)
+xdg_surface_constructor_get_toplevel (struct wl_client   *client,
+                                      struct wl_resource *resource,
+                                      uint32_t            id)
 {
-  MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
-  MetaWaylandPopupSurface *popup_surface;
-  MetaWaylandSurface *parent_surf = wl_resource_get_user_data (parent_resource);
-  MetaWaylandSurface *top_popup;
-  MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
+  MetaWaylandXdgSurfaceConstructor *constructor =
+    wl_resource_get_user_data (resource);
+  MetaWaylandXdgShellClient *shell_client = constructor->shell_client;
+  struct wl_resource *xdg_surface_resource = constructor->resource;
+  MetaWaylandSurface *surface = constructor->surface;
+  MetaWaylandXdgToplevel *xdg_toplevel;
+  MetaWaylandXdgSurface *xdg_surface;
   MetaWindow *window;
-  MetaDisplay *display = meta_get_display ();
-  MetaWaylandXdgPopup *xdg_popup;
-  MetaWaylandPopup *popup;
 
-  if (META_IS_WAYLAND_XDG_POPUP (surface->role) &&
-      META_WAYLAND_XDG_POPUP (surface->role)->resource)
+  if (!meta_wayland_surface_assign_role (surface,
+                                         META_TYPE_WAYLAND_XDG_TOPLEVEL,
+                                         "shell-client", shell_client,
+                                         "xdg-surface-resource", xdg_surface_resource,
+                                         NULL))
     {
-      wl_resource_post_error (surface_resource,
-                              WL_DISPLAY_ERROR_INVALID_OBJECT,
-                              "xdg_shell::get_xdg_popup already requested");
+      wl_resource_post_error (resource, ZXDG_SHELL_V6_ERROR_ROLE,
+                              "wl_surface@%d already has a different role",
+                              wl_resource_get_id (surface->resource));
       return;
     }
 
-  if (!meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_XDG_POPUP,
+  xdg_toplevel = META_WAYLAND_XDG_TOPLEVEL (surface->role);
+  xdg_toplevel->resource = wl_resource_create (client,
+                                               &zxdg_toplevel_v6_interface,
+                                               wl_resource_get_version (resource),
+                                               id);
+  wl_resource_set_implementation (xdg_toplevel->resource,
+                                  &meta_wayland_xdg_toplevel_interface,
+                                  xdg_toplevel,
+                                  xdg_toplevel_destructor);
+
+  xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_toplevel);
+  meta_wayland_xdg_surface_constructor_finalize (constructor, xdg_surface);
+
+  window = meta_window_wayland_new (meta_get_display (), surface);
+  meta_wayland_surface_set_window (surface, window);
+}
+
+static void
+xdg_surface_constructor_get_popup (struct wl_client   *client,
+                                   struct wl_resource *resource,
+                                   uint32_t            id,
+                                   struct wl_resource *parent_resource,
+                                   struct wl_resource *positioner_resource)
+{
+  MetaWaylandXdgSurfaceConstructor *constructor =
+    wl_resource_get_user_data (resource);
+  MetaWaylandXdgShellClient *shell_client = constructor->shell_client;
+  MetaWaylandSurface *surface = constructor->surface;
+  struct wl_resource *xdg_shell_resource = constructor->shell_client->resource;
+  struct wl_resource *xdg_surface_resource = constructor->resource;
+  MetaWaylandSurface *parent_surface =
+    surface_from_xdg_surface_resource (parent_resource);
+  MetaWaylandXdgPositioner *xdg_positioner;
+  MetaWaylandXdgPopup *xdg_popup;
+  MetaWaylandXdgSurface *xdg_surface;
+
+  if (!meta_wayland_surface_assign_role (surface,
+                                         META_TYPE_WAYLAND_XDG_POPUP,
+                                         "shell-client", shell_client,
+                                         "xdg-surface-resource", xdg_surface_resource,
                                          NULL))
     {
-      wl_resource_post_error (resource, XDG_SHELL_ERROR_ROLE,
+      wl_resource_post_error (xdg_shell_resource, ZXDG_SHELL_V6_ERROR_ROLE,
                               "wl_surface@%d already has a different role",
                               wl_resource_get_id (surface->resource));
       return;
     }
 
-  if (parent_surf == NULL ||
-      parent_surf->window == NULL ||
-      (!META_IS_WAYLAND_XDG_POPUP (parent_surf->role) &&
-       !META_IS_WAYLAND_XDG_SURFACE (parent_surf->role)))
+  xdg_popup = META_WAYLAND_XDG_POPUP (surface->role);
+  xdg_popup->resource = wl_resource_create (client,
+                                            &zxdg_popup_v6_interface,
+                                            wl_resource_get_version (resource),
+                                            id);
+  wl_resource_set_implementation (xdg_popup->resource,
+                                  &meta_wayland_xdg_popup_interface,
+                                  xdg_popup,
+                                  xdg_popup_destructor);
+
+  xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup);
+  meta_wayland_xdg_surface_constructor_finalize (constructor, xdg_surface);
+
+  xdg_positioner = wl_resource_get_user_data (positioner_resource);
+  xdg_popup->setup.placement_rule =
+    meta_wayland_xdg_positioner_to_placement (xdg_positioner);
+  xdg_popup->setup.parent_surface = parent_surface;
+}
+
+static void
+xdg_surface_constructor_set_window_geometry (struct wl_client   *client,
+                                             struct wl_resource *resource,
+                                             int32_t             x,
+                                             int32_t             y,
+                                             int32_t             width,
+                                             int32_t             height)
+{
+  wl_resource_post_error (resource,
+                          ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED,
+                          "xdg_surface::set_window_geometry called before constructed");
+}
+
+static void
+xdg_surface_constructor_ack_configure (struct wl_client   *client,
+                                       struct wl_resource *resource,
+                                       uint32_t            serial)
+{
+  wl_resource_post_error (resource,
+                          ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED,
+                          "xdg_surface::ack_configure called before constructed");
+}
+
+static const struct zxdg_surface_v6_interface meta_wayland_xdg_surface_constructor_interface = {
+  xdg_surface_constructor_destroy,
+  xdg_surface_constructor_get_toplevel,
+  xdg_surface_constructor_get_popup,
+  xdg_surface_constructor_set_window_geometry,
+  xdg_surface_constructor_ack_configure,
+};
+
+static void
+xdg_surface_constructor_destructor (struct wl_resource *resource)
+{
+  MetaWaylandXdgSurfaceConstructor *constructor =
+    wl_resource_get_user_data (resource);
+
+  constructor->shell_client->surface_constructors =
+    g_list_remove (constructor->shell_client->surface_constructors,
+                   constructor);
+
+  g_free (constructor);
+}
+
+static MetaPlacementRule
+meta_wayland_xdg_positioner_to_placement (MetaWaylandXdgPositioner *xdg_positioner)
+{
+  return (MetaPlacementRule) {
+    .anchor_rect = xdg_positioner->anchor_rect,
+    .gravity = xdg_positioner->gravity,
+    .anchor = xdg_positioner->anchor,
+    .constraint_adjustment = xdg_positioner->constraint_adjustment,
+    .offset_x = xdg_positioner->offset_x,
+    .offset_y = xdg_positioner->offset_y,
+    .width = xdg_positioner->width,
+    .height = xdg_positioner->height,
+  };
+}
+
+static void
+meta_wayland_xdg_positioner_destroy (struct wl_client   *client,
+                                     struct wl_resource *resource)
+{
+  wl_resource_destroy (resource);
+}
+
+static void
+meta_wayland_xdg_positioner_set_size (struct wl_client *client,
+                                      struct wl_resource *resource,
+                                      int32_t width,
+                                      int32_t height)
+{
+  MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource);
+
+  if (width <= 0 || height <= 0)
     {
-      wl_resource_post_error (resource,
-                              XDG_SHELL_ERROR_INVALID_POPUP_PARENT,
-                              "invalid parent surface");
+      wl_resource_post_error (resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                              "Invalid size");
       return;
     }
 
-  top_popup = meta_wayland_pointer_get_top_popup (&seat->pointer);
-  if ((top_popup == NULL && !META_IS_WAYLAND_XDG_SURFACE (parent_surf->role)) ||
-      (top_popup != NULL && parent_surf != top_popup))
+  positioner->width = width;
+  positioner->height = height;
+}
+
+static void
+meta_wayland_xdg_positioner_set_anchor_rect (struct wl_client   *client,
+                                             struct wl_resource *resource,
+                                             int32_t             x,
+                                             int32_t             y,
+                                             int32_t             width,
+                                             int32_t             height)
+{
+  MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource);
+
+  if (width <= 0 || height <= 0)
     {
-      wl_resource_post_error (resource,
-                              XDG_SHELL_ERROR_NOT_THE_TOPMOST_POPUP,
-                              "parent not top most surface");
+      wl_resource_post_error (resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                              "Invalid anchor rectangle size");
       return;
     }
 
-  xdg_popup = META_WAYLAND_XDG_POPUP (surface->role);
-  xdg_popup->resource = wl_resource_create (client, &xdg_popup_interface,
-                                            wl_resource_get_version (resource), id);
-  wl_resource_set_implementation (xdg_popup->resource,
-                                  &meta_wayland_xdg_popup_interface,
-                                  xdg_popup,
-                                  xdg_popup_destructor);
+  positioner->anchor_rect = (MetaRectangle) {
+    .x = x,
+    .y = y,
+    .width = width,
+    .height = height,
+  };
+}
 
-  xdg_popup->xdg_shell_resource = resource;
+static void
+meta_wayland_xdg_positioner_set_anchor (struct wl_client   *client,
+                                        struct wl_resource *resource,
+                                        uint32_t            anchor)
+{
+  MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource);
 
-  if (!meta_wayland_seat_can_popup (seat, serial))
+  if ((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT &&
+       anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) ||
+      (anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP &&
+       anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM))
     {
-      xdg_popup_send_popup_done (xdg_popup->resource);
+      wl_resource_post_error (resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                              "Invalid anchor");
       return;
     }
 
-  xdg_popup->parent_surface = parent_surf;
-  xdg_popup->parent_destroy_listener.notify = handle_popup_parent_destroyed;
-  wl_resource_add_destroy_listener (parent_surf->resource,
-                                    &xdg_popup->parent_destroy_listener);
+  positioner->anchor = anchor;
+}
 
-  window = meta_window_wayland_new (display, surface);
-  meta_window_wayland_place_relative_to (window, parent_surf->window, x, y);
-  window->showing_for_first_time = FALSE;
+static void
+meta_wayland_xdg_positioner_set_gravity (struct wl_client   *client,
+                                         struct wl_resource *resource,
+                                         uint32_t            gravity)
+{
+  MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource);
 
-  meta_wayland_surface_set_window (surface, window);
+  if ((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT &&
+       gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) ||
+      (gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP &&
+       gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM))
+    {
+      wl_resource_post_error (resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                              "Invalid gravity");
+      return;
+    }
+
+  positioner->gravity = gravity;
+}
 
-  meta_window_focus (window, meta_display_get_current_time (display));
-  popup_surface = META_WAYLAND_POPUP_SURFACE (surface->role);
-  popup = meta_wayland_pointer_start_popup_grab (&seat->pointer,
-                                                 popup_surface);
-  if (popup == NULL)
+static void
+meta_wayland_xdg_positioner_set_constraint_adjustment (struct wl_client   *client,
+                                                      struct wl_resource *resource,
+                                                      uint32_t            constraint_adjustment)
+{
+  MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource);
+  uint32_t all_adjustments = (ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X |
+                              ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X |
+                              ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y |
+                              ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y |
+                              ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X |
+                              ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y);
+
+
+  if ((constraint_adjustment & ~all_adjustments) != 0)
     {
-      xdg_popup_send_popup_done (xdg_popup->resource);
-      meta_wayland_surface_destroy_window (surface);
+      wl_resource_post_error (resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                              "Invalid constraint action");
       return;
     }
 
-  xdg_popup->popup = popup;
+  positioner->constraint_adjustment = constraint_adjustment;
+}
+
+static void
+meta_wayland_xdg_positioner_set_offset (struct wl_client   *client,
+                                        struct wl_resource *resource,
+                                        int32_t             x,
+                                        int32_t             y)
+{
+  MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource);
+
+  positioner->offset_x = x;
+  positioner->offset_y = y;
+}
+
+static const struct zxdg_positioner_v6_interface meta_wayland_xdg_positioner_interface = {
+  meta_wayland_xdg_positioner_destroy,
+  meta_wayland_xdg_positioner_set_size,
+  meta_wayland_xdg_positioner_set_anchor_rect,
+  meta_wayland_xdg_positioner_set_anchor,
+  meta_wayland_xdg_positioner_set_gravity,
+  meta_wayland_xdg_positioner_set_constraint_adjustment,
+  meta_wayland_xdg_positioner_set_offset,
+};
+
+static void
+xdg_positioner_destructor (struct wl_resource *resource)
+{
+  MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource);
+
+  g_free (positioner);
 }
 
 static void
 xdg_shell_destroy (struct wl_client   *client,
                    struct wl_resource *resource)
 {
+  MetaWaylandXdgShellClient *shell_client = wl_resource_get_user_data (resource);
+
+  if (shell_client->surfaces || shell_client->surface_constructors)
+    wl_resource_post_error (resource, ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES,
+                            "xdg_shell destroyed before its surfaces");
+
   wl_resource_destroy (resource);
 }
 
 static void
-xdg_shell_use_unstable_version (struct wl_client   *client,
-                                struct wl_resource *resource,
-                                int32_t             version)
+xdg_shell_create_positioner (struct wl_client   *client,
+                             struct wl_resource *resource,
+                             uint32_t            id)
 {
-  if (version != XDG_SHELL_VERSION_CURRENT)
-    wl_resource_post_error (resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
-                            "bad xdg-shell version: %d\n", version);
+  MetaWaylandXdgPositioner *positioner;
+  struct wl_resource *positioner_resource;
+
+  positioner = g_new0 (MetaWaylandXdgPositioner, 1);
+  positioner_resource = wl_resource_create (client,
+                                            &zxdg_positioner_v6_interface,
+                                            wl_resource_get_version (resource),
+                                            id);
+  wl_resource_set_implementation (positioner_resource,
+                                  &meta_wayland_xdg_positioner_interface,
+                                  positioner,
+                                  xdg_positioner_destructor);
+}
+
+static void
+xdg_shell_get_xdg_surface (struct wl_client   *client,
+                           struct wl_resource *resource,
+                           uint32_t            id,
+                           struct wl_resource *surface_resource)
+{
+  MetaWaylandXdgShellClient *shell_client = wl_resource_get_user_data (resource);
+  MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
+  MetaWaylandXdgSurfaceConstructor *constructor;
+
+  if (surface->role && !META_IS_WAYLAND_XDG_SURFACE (surface->role))
+    {
+      wl_resource_post_error (resource, ZXDG_SHELL_V6_ERROR_ROLE,
+                              "wl_surface@%d already has a different role",
+                              wl_resource_get_id (surface->resource));
+      return;
+    }
+
+  if (surface->role && META_IS_WAYLAND_XDG_SURFACE (surface->role) &&
+      meta_wayland_xdg_surface_is_assigned (META_WAYLAND_XDG_SURFACE (surface->role)))
+    {
+      wl_resource_post_error (surface_resource,
+                              WL_DISPLAY_ERROR_INVALID_OBJECT,
+                              "xdg_shell::get_xdg_surface already requested");
+      return;
+    }
+
+  if (surface->buffer_ref.buffer)
+    {
+      wl_resource_post_error (resource,
+                              ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE,
+                              "wl_surface@%d already has a buffer committed",
+                              wl_resource_get_id (surface->resource));
+      return;
+    }
+
+  constructor = g_new0 (MetaWaylandXdgSurfaceConstructor, 1);
+  constructor->surface = surface;
+  constructor->shell_client = shell_client;
+  constructor->resource = wl_resource_create (client,
+                                              &zxdg_surface_v6_interface,
+                                              wl_resource_get_version (resource),
+                                              id);
+  wl_resource_set_implementation (constructor->resource,
+                                  &meta_wayland_xdg_surface_constructor_interface,
+                                  constructor,
+                                  xdg_surface_constructor_destructor);
+
+  shell_client->surface_constructors =
+    g_list_append (shell_client->surface_constructors, constructor);
 }
 
 static void
@@ -907,39 +1731,62 @@ xdg_shell_pong (struct wl_client   *client,
   meta_display_pong_for_serial (display, serial);
 }
 
-static const struct xdg_shell_interface meta_wayland_xdg_shell_interface = {
+static const struct zxdg_shell_v6_interface meta_wayland_xdg_shell_interface = {
   xdg_shell_destroy,
-  xdg_shell_use_unstable_version,
+  xdg_shell_create_positioner,
   xdg_shell_get_xdg_surface,
-  xdg_shell_get_xdg_popup,
   xdg_shell_pong,
 };
 
 static void
+xdg_shell_destructor (struct wl_resource *resource)
+{
+  MetaWaylandXdgShellClient *shell_client = wl_resource_get_user_data (resource);
+
+  while (shell_client->surface_constructors)
+    {
+      MetaWaylandXdgSurfaceConstructor *constructor =
+        g_list_first (shell_client->surface_constructors)->data;
+
+      wl_resource_destroy (constructor->resource);
+    }
+  g_list_free (shell_client->surface_constructors);
+
+  while (shell_client->surfaces)
+    {
+      MetaWaylandXdgSurface *xdg_surface =
+        g_list_first (shell_client->surfaces)->data;
+
+      meta_wayland_xdg_surface_shell_client_destroyed  (xdg_surface);
+    }
+  g_list_free (shell_client->surfaces);
+
+  g_free (shell_client);
+}
+
+static void
 bind_xdg_shell (struct wl_client *client,
                 void             *data,
                 guint32           version,
                 guint32           id)
 {
-  struct wl_resource *resource;
+  MetaWaylandXdgShellClient *shell_client;
 
-  if (version != META_XDG_SHELL_VERSION)
-    {
-      g_warning ("using xdg-shell without stable version %d\n",
-                 META_XDG_SHELL_VERSION);
-      return;
-    }
+  shell_client = g_new0 (MetaWaylandXdgShellClient, 1);
 
-  resource = wl_resource_create (client, &xdg_shell_interface, version, id);
-  wl_resource_set_implementation (resource, &meta_wayland_xdg_shell_interface,
-                                  data, NULL);
+  shell_client->resource = wl_resource_create (client,
+                                               &zxdg_shell_v6_interface,
+                                               version, id);
+  wl_resource_set_implementation (shell_client->resource,
+                                  &meta_wayland_xdg_shell_interface,
+                                  shell_client, xdg_shell_destructor);
 }
 
 void
 meta_wayland_xdg_shell_init (MetaWaylandCompositor *compositor)
 {
   if (wl_global_create (compositor->wayland_display,
-                        &xdg_shell_interface,
+                        &zxdg_shell_v6_interface,
                         META_XDG_SHELL_VERSION,
                         compositor, bind_xdg_shell) == NULL)
     g_error ("Failed to register a global xdg-shell object");
diff --git a/src/wayland/meta-wayland-xdg-shell.h b/src/wayland/meta-wayland-xdg-shell.h
index d0b1953..8249f0f 100644
--- a/src/wayland/meta-wayland-xdg-shell.h
+++ b/src/wayland/meta-wayland-xdg-shell.h
@@ -23,16 +23,29 @@
 #include "wayland/meta-wayland-surface.h"
 
 #define META_TYPE_WAYLAND_XDG_SURFACE (meta_wayland_xdg_surface_get_type ())
-G_DECLARE_FINAL_TYPE (MetaWaylandXdgSurface,
-                      meta_wayland_xdg_surface,
-                      META, WAYLAND_XDG_SURFACE,
-                      MetaWaylandSurfaceRoleShellSurface);
+G_DECLARE_DERIVABLE_TYPE (MetaWaylandXdgSurface,
+                          meta_wayland_xdg_surface,
+                          META, WAYLAND_XDG_SURFACE,
+                          MetaWaylandSurfaceRoleShellSurface);
+
+struct _MetaWaylandXdgSurfaceClass
+{
+  MetaWaylandSurfaceRoleShellSurfaceClass parent_class;
+
+  void (*shell_client_destroyed) (MetaWaylandXdgSurface *xdg_surface);
+};
+
+#define META_TYPE_WAYLAND_XDG_TOPLEVEL (meta_wayland_xdg_toplevel_get_type ())
+G_DECLARE_FINAL_TYPE (MetaWaylandXdgToplevel,
+                      meta_wayland_xdg_toplevel,
+                      META, WAYLAND_XDG_TOPLEVEL,
+                      MetaWaylandXdgSurface);
 
 #define META_TYPE_WAYLAND_XDG_POPUP (meta_wayland_xdg_popup_get_type ())
 G_DECLARE_FINAL_TYPE (MetaWaylandXdgPopup,
                       meta_wayland_xdg_popup,
                       META, WAYLAND_XDG_POPUP,
-                      MetaWaylandSurfaceRoleShellSurface);
+                      MetaWaylandXdgSurface);
 
 void meta_wayland_xdg_shell_init (MetaWaylandCompositor *compositor);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]