diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c
index 5cac2e1..7087140 100644
--- a/gdk/wayland/gdkdisplay-wayland.c
+++ b/gdk/wayland/gdkdisplay-wayland.c
@@ -174,6 +174,9 @@ gdk_registry_handle_global(void *data, struct wl_registry *registry, uint32_t id
     display_wayland->compositor =
       wl_registry_bind(display_wayland->wl_registry, id, &wl_compositor_interface, MIN (version, 3));
     display_wayland->compositor_version = MIN (version, 3);
+  } else if (strcmp(interface, "wl_subcompositor") == 0) {
+    display_wayland->subcompositor =
+      wl_registry_bind(display_wayland->wl_registry, id, &wl_subcompositor_interface, 1);
   } else if (strcmp(interface, "wl_shm") == 0) {
    display_wayland->shm =
 	wl_registry_bind(display_wayland->wl_registry, id, &wl_shm_interface, 1);
diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h
index a68753c..161716e 100644
--- a/gdk/wayland/gdkdisplay-wayland.h
+++ b/gdk/wayland/gdkdisplay-wayland.h
@@ -58,6 +58,7 @@ struct _GdkWaylandDisplay
   struct wl_display *wl_display;
   struct wl_registry *wl_registry;
   struct wl_compositor *compositor;
+  struct wl_subcompositor *subcompositor;
   struct wl_shm *shm;
   struct xdg_shell *xdg_shell;
   struct gtk_shell *gtk_shell;
diff --git a/gdk/wayland/gdkwaylandwindow.h b/gdk/wayland/gdkwaylandwindow.h
index 73f1da0..71f6495 100644
--- a/gdk/wayland/gdkwaylandwindow.h
+++ b/gdk/wayland/gdkwaylandwindow.h
@@ -51,6 +51,14 @@ struct wl_surface       *gdk_wayland_window_get_wl_surface       (GdkWindow *win
 GDK_AVAILABLE_IN_ALL
 void                     gdk_wayland_window_set_use_custom_surface (GdkWindow *window);
 
+GDK_AVAILABLE_IN_ALL
+void                     gdk_wayland_window_set_use_custom_subsurface (GdkWindow *window,
+                                                                       gint x,
+                                                                       gint y);
+
+GDK_AVAILABLE_IN_ALL
+struct wl_surface       *gdk_wayland_window_get_subsurface       (GdkWindow *window);
+
 GDK_AVAILABLE_IN_3_10
 void                     gdk_wayland_window_set_dbus_properties_libgtk_only (GdkWindow  *window,
 									     const char *application_id,
diff --git a/gdk/wayland/gdkwindow-wayland.c b/gdk/wayland/gdkwindow-wayland.c
index b172ad6..1bc58bc 100644
--- a/gdk/wayland/gdkwindow-wayland.c
+++ b/gdk/wayland/gdkwindow-wayland.c
@@ -100,6 +100,7 @@ struct _GdkWindowImplWayland
   GSList *outputs;
 
   struct wl_surface *surface;
+  struct wl_surface *subsurface;
 
   struct xdg_surface *xdg_surface;
   struct xdg_popup   *xdg_popup;
@@ -392,6 +393,8 @@ on_frame_clock_after_paint (GdkFrameClock *clock,
   _gdk_frame_clock_freeze (clock);
 
   wl_surface_commit (impl->surface);
+  if (impl->subsurface)
+    wl_surface_commit (impl->subsurface);
   _gdk_wayland_shm_surface_set_busy (impl->cairo_surface);
 }
 
@@ -796,6 +799,32 @@ gdk_wayland_window_create_surface (GdkWindow *window)
   gdk_wayland_window_sync_input_region (window);
 }
 
+static gboolean
+gdk_wayland_window_create_subsurface (GdkWindow *window,
+                                      int32_t x,
+                                      int32_t y)
+{
+  struct wl_subsurface *subsurface;
+  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
+  GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_window_get_display (window));
+
+  if ((!impl->surface) ||
+      (impl->subsurface))
+    return FALSE;
+
+  impl->subsurface = wl_compositor_create_surface (display_wayland->compositor);
+  wl_surface_attach (impl->subsurface, NULL, 0, 0);
+  wl_surface_commit (impl->subsurface);
+
+  subsurface = wl_subcompositor_get_subsurface (display_wayland->subcompositor,
+                                                impl->subsurface,
+                                                impl->surface);
+
+  wl_subsurface_set_position (subsurface, x, y);
+
+  return TRUE;
+}
+
 static void
 xdg_surface_configure (void               *data,
                        struct xdg_surface *xdg_surface,
@@ -2105,6 +2134,28 @@ gdk_wayland_window_get_wl_surface (GdkWindow *window)
 }
 
 /**
+ * gdk_wayland_window_get_subsurface
+ * @window: (type GdkWaylandWindow): a #GdkWindow
+ *
+ * Returns the Wayland subsurface of a #GdkWindow
+ *
+ * Returns: (transfer none): a Wayland wl_surface
+ *
+ * Since: 3.14
+ */
+struct wl_surface *
+gdk_wayland_window_get_subsurface (GdkWindow *window)
+{
+  GdkWindowImplWayland *impl;
+
+  g_return_val_if_fail (GDK_IS_WAYLAND_WINDOW (window), NULL);
+
+  impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
+
+  return impl->subsurface;
+}
+
+/**
  * gdk_wayland_window_set_use_custom_surface:
  * @window: (type GdkWaylandWindow): a #GdkWindow
  *
@@ -2164,6 +2215,24 @@ gdk_wayland_window_set_use_custom_surface (GdkWindow *window)
 }
 
 void
+gdk_wayland_window_set_use_custom_subsurface (GdkWindow *window,
+                                              gint x,
+                                              gint y)
+{
+  GdkWindowImplWayland *impl;
+
+  g_return_if_fail (GDK_IS_WAYLAND_WINDOW (window));
+
+  impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
+
+  g_return_if_fail (impl->surface);
+
+  if (!impl->subsurface)
+    gdk_wayland_window_create_subsurface (window,
+                                          x, y);
+}
+
+void
 gdk_wayland_window_set_dbus_properties_libgtk_only (GdkWindow  *window,
                                                     const char *application_id,
                                                     const char *app_menu_path,
