[mutter/wip/wayland: 4/9] wayland: Adds basic hybrid X + Wayland support



commit 006695314c27238a31ee9f5ca9a8e1bcc540de99
Author: Robert Bragg <robert linux intel com>
Date:   Sat Jan 7 22:21:32 2012 +0000

    wayland: Adds basic hybrid X + Wayland support
    
    This adds support for running mutter as a hybrid X and Wayland
    compositor. It runs a headless XWayland server for X applications
    that presents wayland surfaces back to mutter which mutter can then
    composite.
    
    This aims to not break Mutter's existing support for the traditional X
    compositing model which means a single build of Mutter can be
    distributed supporting the traditional model and the new Wayland based
    compositing model.
    
    TODO: although building with --disable-wayland has at least been tested,
    I still haven't actually verified that running as a traditional
    compositor isn't broken currently.
    
    Note: At this point no input is supported
    
    Note: multiple authors have contributed to this patch:
    Authored-by: Robert Bragg <robert linux intel com>
    Authored-by: Neil Roberts <neil linux intel com>
    Authored-by: Rico Tzschichholz.
    Authored-by: Giovanni Campagna <gcampagna src gnome org>

 configure.ac                               |    2 +-
 src/Makefile.am                            |   15 +
 src/compositor/compositor.c                |  343 +++++----
 src/compositor/meta-plugin-manager.c       |   14 +-
 src/compositor/meta-shaped-texture.c       |  351 ++++++++--
 src/compositor/meta-window-actor-private.h |   21 +-
 src/compositor/meta-window-actor.c         |  468 ++++++++----
 src/compositor/meta-window-group.c         |   32 +-
 src/core/display.c                         |   28 +-
 src/core/main.c                            |  116 +++-
 src/core/screen-private.h                  |    2 +
 src/core/screen.c                          |   31 +-
 src/core/window-private.h                  |   24 +
 src/core/window.c                          |  652 ++++++++++-------
 src/meta/compositor.h                      |    4 +-
 src/meta/meta-shaped-texture.h             |   17 +-
 src/wayland/meta-wayland-private.h         |  171 +++++
 src/wayland/meta-wayland.c                 | 1145 ++++++++++++++++++++++++++++
 src/wayland/meta-xwayland-private.h        |   33 +
 src/wayland/meta-xwayland.c                |  310 ++++++++
 20 files changed, 3129 insertions(+), 650 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index deecd1f..7c20ac5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,7 +15,7 @@ AC_INIT([mutter], [mutter_version],
 AC_CONFIG_SRCDIR(src/core/display.c)
 AC_CONFIG_HEADERS(config.h)
 
-AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz tar-ustar])
+AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz tar-ustar])
 m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],)
 AM_MAINTAINER_MODE([enable])
 
diff --git a/src/Makefile.am b/src/Makefile.am
index c0884c5..e25d4a8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -39,6 +39,13 @@ mutter_built_sources = \
        mutter-enum-types.h \
        mutter-enum-types.c
 
+if HAVE_WAYLAND
+mutter_built_sources += \
+       wayland/xserver-protocol.c              \
+       wayland/xserver-server-protocol.h       \
+       wayland/xserver-client-protocol.h
+endif
+
 libmutter_la_SOURCES =                         \
        core/async-getprop.c                    \
        core/async-getprop.h                    \
@@ -167,6 +174,14 @@ libmutter_la_SOURCES =                             \
        ui/preview-widget.c                     \
        $(mutter_built_sources)
 
+if HAVE_WAYLAND
+libmutter_la_SOURCES +=                                \
+       wayland/meta-wayland.c                  \
+       wayland/meta-wayland-private.h          \
+       wayland/meta-xwayland-private.h         \
+       wayland/meta-xwayland.c
+endif
+
 libmutter_la_LDFLAGS = -no-undefined
 libmutter_la_LIBADD  = $(MUTTER_LIBS)
 
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index 89235e6..2d4817f 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -84,6 +84,9 @@
 #include "meta-window-group.h"
 #include "window-private.h" /* to check window->hidden */
 #include "display-private.h" /* for meta_display_lookup_x_window() */
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#endif
 #include <X11/extensions/shape.h>
 #include <X11/extensions/Xcomposite.h>
 
@@ -172,7 +175,7 @@ process_damage (MetaCompositor     *compositor,
   if (window_actor == NULL)
     return;
 
-  meta_window_actor_process_damage (window_actor, event);
+  meta_window_actor_process_x11_damage (window_actor, event);
 }
 
 static void
@@ -327,29 +330,37 @@ void
 meta_set_stage_input_region (MetaScreen   *screen,
                              XserverRegion region)
 {
-  MetaCompScreen *info = meta_screen_get_compositor_data (screen);
-  MetaDisplay  *display = meta_screen_get_display (screen);
-  Display      *xdpy    = meta_display_get_xdisplay (display);
-
-  if (info->stage && info->output)
-    {
-      do_set_stage_input_region (screen, region);
-    }
-  else 
+  /* As a wayland compositor we can simply ignore all this trickery
+   * for setting an input region on the stage for capturing events in
+   * clutter since all input comes to us first and we get to choose
+   * who else sees them.
+   */
+  if (!meta_is_wayland_compositor ())
     {
-      /* Reset info->pending_input_region if one existed before and set the new
-       * one to use it later. */ 
-      if (info->pending_input_region)
+      MetaCompScreen *info = meta_screen_get_compositor_data (screen);
+      MetaDisplay  *display = meta_screen_get_display (screen);
+      Display      *xdpy    = meta_display_get_xdisplay (display);
+
+      if (info->stage && info->output)
         {
-          XFixesDestroyRegion (xdpy, info->pending_input_region);
-          info->pending_input_region = None;
+          do_set_stage_input_region (screen, region);
         }
-      if (region != None)
+      else 
         {
-          info->pending_input_region = XFixesCreateRegion (xdpy, NULL, 0);
-          XFixesCopyRegion (xdpy, info->pending_input_region, region);
-        }
-    } 
+          /* Reset info->pending_input_region if one existed before and set the new
+           * one to use it later. */ 
+          if (info->pending_input_region)
+            {
+              XFixesDestroyRegion (xdpy, info->pending_input_region);
+              info->pending_input_region = None;
+            }
+          if (region != None)
+            {
+              info->pending_input_region = XFixesCreateRegion (xdpy, NULL, 0);
+              XFixesCopyRegion (xdpy, info->pending_input_region, region);
+            }
+        } 
+    }
 }
 
 void
@@ -562,6 +573,11 @@ redirect_windows (MetaCompositor *compositor,
   guint        n_retries;
   guint        max_retries;
 
+  /* If we're running with wayland, connected to a headless xwayland
+   * server then all the windows are implicitly redirected offscreen
+   * already and it would generate an error to try and explicitly
+   * redirect them via XCompositeRedirectSubwindows() */
+
   if (meta_get_replace_current_wm ())
     max_retries = 5;
   else
@@ -604,6 +620,9 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
   Display        *xdisplay      = meta_display_get_xdisplay (display);
   Window          xwin;
   gint            width, height;
+#ifdef HAVE_WAYLAND
+  MetaWaylandCompositor *wayland_compositor;
+#endif
 
   /* Check if the screen is already managed */
   if (meta_screen_get_compositor_data (screen))
@@ -616,7 +635,14 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
    * We have to initialize info->pending_input_region to an empty region explicitly, 
    * because None value is used to mean that the whole screen is an input region.
    */
-  info->pending_input_region = XFixesCreateRegion (xdisplay, NULL, 0);
+  if (!meta_is_wayland_compositor ())
+    info->pending_input_region = XFixesCreateRegion (xdisplay, NULL, 0);
+  else
+    {
+      /* Stage input region trickery isn't needed when we're running as a
+       * wayland compositor. */
+      info->pending_input_region = None;
+    }
 
   info->screen = screen;
 
@@ -627,7 +653,55 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
 
   meta_screen_set_cm_selection (screen);
 
-  info->stage = clutter_stage_new ();
+  /* We will have already created a stage if running as a wayland
+   * compositor... */
+#ifdef HAVE_WAYLAND
+  if (meta_is_wayland_compositor ())
+    {
+      wayland_compositor = meta_wayland_compositor_get_default ();
+      info->stage = wayland_compositor->stage;
+    }
+  else
+#endif /* HAVE_WAYLAND */
+    {
+      info->stage = clutter_stage_new ();
+
+      meta_screen_get_size (screen, &width, &height);
+      clutter_actor_realize (info->stage);
+
+      xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
+
+      XResizeWindow (xdisplay, xwin, width, height);
+
+        {
+          long event_mask;
+          unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
+          XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
+          XWindowAttributes attr;
+
+          meta_core_add_old_event_mask (xdisplay, xwin, &mask);
+
+          XISetMask (mask.mask, XI_KeyPress);
+          XISetMask (mask.mask, XI_KeyRelease);
+          XISetMask (mask.mask, XI_ButtonPress);
+          XISetMask (mask.mask, XI_ButtonRelease);
+          XISetMask (mask.mask, XI_Enter);
+          XISetMask (mask.mask, XI_Leave);
+          XISetMask (mask.mask, XI_FocusIn);
+          XISetMask (mask.mask, XI_FocusOut);
+          XISetMask (mask.mask, XI_Motion);
+          XIClearMask (mask.mask, XI_TouchBegin);
+          XIClearMask (mask.mask, XI_TouchEnd);
+          XIClearMask (mask.mask, XI_TouchUpdate);
+          XISelectEvents (xdisplay, xwin, &mask, 1);
+
+          event_mask = ExposureMask | PropertyChangeMask | StructureNotifyMask;
+          if (XGetWindowAttributes (xdisplay, xwin, &attr))
+            event_mask |= attr.your_event_mask;
+
+          XSelectInput (xdisplay, xwin, event_mask);
+        }
+    }
 
   clutter_stage_set_paint_callback (CLUTTER_STAGE (info->stage),
                                     after_stage_paint,
@@ -636,42 +710,6 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
 
   clutter_stage_set_sync_delay (CLUTTER_STAGE (info->stage), META_SYNC_DELAY);
 
-  meta_screen_get_size (screen, &width, &height);
-  clutter_actor_realize (info->stage);
-
-  xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
-
-  XResizeWindow (xdisplay, xwin, width, height);
-
-  {
-    long event_mask;
-    unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
-    XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
-    XWindowAttributes attr;
-
-    meta_core_add_old_event_mask (xdisplay, xwin, &mask);
-
-    XISetMask (mask.mask, XI_KeyPress);
-    XISetMask (mask.mask, XI_KeyRelease);
-    XISetMask (mask.mask, XI_ButtonPress);
-    XISetMask (mask.mask, XI_ButtonRelease);
-    XISetMask (mask.mask, XI_Enter);
-    XISetMask (mask.mask, XI_Leave);
-    XISetMask (mask.mask, XI_FocusIn);
-    XISetMask (mask.mask, XI_FocusOut);
-    XISetMask (mask.mask, XI_Motion);
-    XIClearMask (mask.mask, XI_TouchBegin);
-    XIClearMask (mask.mask, XI_TouchEnd);
-    XIClearMask (mask.mask, XI_TouchUpdate);
-    XISelectEvents (xdisplay, xwin, &mask, 1);
-
-    event_mask = ExposureMask | PropertyChangeMask | StructureNotifyMask;
-    if (XGetWindowAttributes (xdisplay, xwin, &attr))
-      event_mask |= attr.your_event_mask;
-
-    XSelectInput (xdisplay, xwin, event_mask);
-  }
-
   info->window_group = meta_window_group_new (screen);
   info->top_window_group = meta_window_group_new (screen);
 
@@ -680,53 +718,66 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
 
   info->plugin_mgr = meta_plugin_manager_new (screen);
 
-  /*
-   * Delay the creation of the overlay window as long as we can, to avoid
-   * blanking out the screen. This means that during the plugin loading, the
-   * overlay window is not accessible; if the plugin needs to access it
-   * directly, it should hook into the "show" signal on stage, and do
-   * its stuff there.
-   */
-  info->output = get_output_window (screen);
-  XReparentWindow (xdisplay, xwin, info->output, 0, 0);
-
- /* Make sure there isn't any left-over output shape on the 
-  * overlay window by setting the whole screen to be an
-  * output region.
-  * 
-  * Note: there doesn't seem to be any real chance of that
-  *  because the X server will destroy the overlay window
-  *  when the last client using it exits.
-  */
-  XFixesSetWindowShapeRegion (xdisplay, info->output, ShapeBounding, 0, 0, None);
-
-  do_set_stage_input_region (screen, info->pending_input_region);
-  if (info->pending_input_region != None)
+  if (meta_is_wayland_compositor ())
     {
-      XFixesDestroyRegion (xdisplay, info->pending_input_region);
-      info->pending_input_region = None;
+      /* NB: When running as a wayland compositor we don't need an X
+       * composite overlay window, and we don't need to play any input
+       * region tricks to redirect events into clutter. */
+      info->output = None;
     }
+  else
+    {
+      /*
+       * Delay the creation of the overlay window as long as we can, to avoid
+       * blanking out the screen. This means that during the plugin loading, the
+       * overlay window is not accessible; if the plugin needs to access it
+       * directly, it should hook into the "show" signal on stage, and do
+       * its stuff there.
+       */
+      info->output = get_output_window (screen);
+      XReparentWindow (xdisplay, xwin, info->output, 0, 0);
+
+      /* Make sure there isn't any left-over output shape on the 
+       * overlay window by setting the whole screen to be an
+       * output region.
+       * 
+       * Note: there doesn't seem to be any real chance of that
+       *  because the X server will destroy the overlay window
+       *  when the last client using it exits.
+       */
+      XFixesSetWindowShapeRegion (xdisplay, info->output, ShapeBounding, 0, 0, None);
 
-  /* Map overlay window before redirecting windows offscreen so we catch their
-   * contents until we show the stage.
-   */
-  XMapWindow (xdisplay, info->output);
+      do_set_stage_input_region (screen, info->pending_input_region);
+      if (info->pending_input_region != None)
+        {
+          XFixesDestroyRegion (xdisplay, info->pending_input_region);
+          info->pending_input_region = None;
+        }
+
+      /* Map overlay window before redirecting windows offscreen so we catch their
+       * contents until we show the stage.
+       */
+      XMapWindow (xdisplay, info->output);
 
-  redirect_windows (compositor, screen);
+      redirect_windows (compositor, screen);
+    }
 }
 
 void
 meta_compositor_unmanage_screen (MetaCompositor *compositor,
                                  MetaScreen     *screen)
 {
-  MetaDisplay    *display       = meta_screen_get_display (screen);
-  Display        *xdisplay      = meta_display_get_xdisplay (display);
-  Window          xroot         = meta_screen_get_xroot (screen);
-
-  /* This is the most important part of cleanup - we have to do this
-   * before giving up the window manager selection or the next
-   * window manager won't be able to redirect subwindows */
-  XCompositeUnredirectSubwindows (xdisplay, xroot, CompositeRedirectManual);
+  if (!meta_is_wayland_compositor ())
+    {
+      MetaDisplay    *display       = meta_screen_get_display (screen);
+      Display        *xdisplay      = meta_display_get_xdisplay (display);
+      Window          xroot         = meta_screen_get_xroot (screen);
+
+      /* This is the most important part of cleanup - we have to do this
+       * before giving up the window manager selection or the next
+       * window manager won't be able to redirect subwindows */
+      XCompositeUnredirectSubwindows (xdisplay, xroot, CompositeRedirectManual);
+    }
 }
 
 /*
@@ -798,15 +849,18 @@ meta_compositor_remove_window (MetaCompositor *compositor,
   if (!window_actor)
     return;
 
-  screen = meta_window_get_screen (window);
-  info = meta_screen_get_compositor_data (screen);
-
-  if (window_actor == info->unredirected_window)
+  if (!meta_is_wayland_compositor ())
     {
-      meta_window_actor_set_redirected (window_actor, TRUE);
-      meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window 
(info->unredirected_window)),
-                                 NULL);
-      info->unredirected_window = NULL;
+      screen = meta_window_get_screen (window);
+      info = meta_screen_get_compositor_data (screen);
+
+      if (window_actor == info->unredirected_window)
+        {
+          meta_window_actor_set_redirected (window_actor, TRUE);
+          meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window 
(info->unredirected_window)),
+                                     NULL);
+          info->unredirected_window = NULL;
+        }
     }
 
   meta_window_actor_destroy (window_actor);
@@ -866,8 +920,8 @@ is_grabbed_event (MetaDisplay *display,
 }
 
 void
-meta_compositor_window_shape_changed (MetaCompositor *compositor,
-                                      MetaWindow     *window)
+meta_compositor_window_x11_shape_changed (MetaCompositor *compositor,
+                                          MetaWindow     *window)
 {
   MetaWindowActor *window_actor;
   window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
@@ -993,7 +1047,8 @@ meta_compositor_process_event (MetaCompositor *compositor,
       break;
 
     default:
-      if (event->type == meta_display_get_damage_event_base (compositor->display) + XDamageNotify)
+      if (!meta_is_wayland_compositor () &&
+          event->type == meta_display_get_damage_event_base (compositor->display) + XDamageNotify)
         {
           /* Core code doesn't handle damage events, so we need to extract the MetaWindow
            * ourselves
@@ -1012,7 +1067,7 @@ meta_compositor_process_event (MetaCompositor *compositor,
 
   /* Clutter needs to know about MapNotify events otherwise it will
      think the stage is invisible */
-  if (event->type == MapNotify)
+  if (!meta_is_wayland_compositor () && event->type == MapNotify)
     clutter_x11_handle_event (event);
 
   /* The above handling is basically just "observing" the events, so we return
@@ -1354,22 +1409,33 @@ meta_compositor_sync_screen_size (MetaCompositor  *compositor,
                                  guint            width,
                                  guint            height)
 {
-  MetaDisplay    *display = meta_screen_get_display (screen);
-  MetaCompScreen *info    = meta_screen_get_compositor_data (screen);
-  Display        *xdisplay;
-  Window          xwin;
+  if (meta_is_wayland_compositor ())
+    {
+      /* It's not clear at the moment how we will be dealing with screen
+       * resizing as a Wayland compositor so for now just abort if we
+       * hit this code. */
+      g_critical ("Unexpected call to meta_compositor_sync_screen_size() "
+                  "when running as a wayland compositor");
+    }
+  else
+    {
+      MetaDisplay    *display = meta_screen_get_display (screen);
+      MetaCompScreen *info    = meta_screen_get_compositor_data (screen);
+      Display        *xdisplay;
+      Window          xwin;
 
-  DEBUG_TRACE ("meta_compositor_sync_screen_size\n");
-  g_return_if_fail (info);
+      DEBUG_TRACE ("meta_compositor_sync_screen_size\n");
+      g_return_if_fail (info);
 
-  xdisplay = meta_display_get_xdisplay (display);
-  xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
+      xdisplay = meta_display_get_xdisplay (display);
+      xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
 
-  XResizeWindow (xdisplay, xwin, width, height);
+      XResizeWindow (xdisplay, xwin, width, height);
 
-  meta_verbose ("Changed size for stage on screen %d to %dx%d\n",
-               meta_screen_get_screen_number (screen),
-               width, height);
+      meta_verbose ("Changed size for stage on screen %d to %dx%d\n",
+                    meta_screen_get_screen_number (screen),
+                    width, height);
+    }
 }
 
 static void
@@ -1433,29 +1499,32 @@ pre_paint_windows (MetaCompScreen *info)
   if (info->windows == NULL)
     return;
 
-  top_window = g_list_last (info->windows)->data;
+  if (!meta_is_wayland_compositor ())
+    {
+      top_window = g_list_last (info->windows)->data;
 
-  if (meta_window_actor_should_unredirect (top_window) &&
-      info->disable_unredirect_count == 0)
-    expected_unredirected_window = top_window;
+      if (meta_window_actor_should_unredirect (top_window) &&
+          info->disable_unredirect_count == 0)
+        expected_unredirected_window = top_window;
 
-  if (info->unredirected_window != expected_unredirected_window)
-    {
-      if (info->unredirected_window != NULL)
+      if (info->unredirected_window != expected_unredirected_window)
         {
-          meta_window_actor_set_redirected (info->unredirected_window, TRUE);
-          meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window 
(info->unredirected_window)),
-                                     NULL);
-        }
+          if (info->unredirected_window != NULL)
+            {
+              meta_window_actor_set_redirected (info->unredirected_window, TRUE);
+              meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window 
(info->unredirected_window)),
+                                         NULL);
+            }
 
-      if (expected_unredirected_window != NULL)
-        {
-          meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (top_window)),
-                                     meta_window_actor_get_meta_window (top_window));
-          meta_window_actor_set_redirected (top_window, FALSE);
-        }
+          if (expected_unredirected_window != NULL)
+            {
+              meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window 
(top_window)),
+                                         meta_window_actor_get_meta_window (top_window));
+              meta_window_actor_set_redirected (top_window, FALSE);
+            }
 
-      info->unredirected_window = expected_unredirected_window;
+          info->unredirected_window = expected_unredirected_window;
+        }
     }
 
   for (l = info->windows; l; l = l->next)
diff --git a/src/compositor/meta-plugin-manager.c b/src/compositor/meta-plugin-manager.c
index 130a82b..2ae100e 100644
--- a/src/compositor/meta-plugin-manager.c
+++ b/src/compositor/meta-plugin-manager.c
@@ -317,6 +317,16 @@ meta_plugin_manager_xevent_filter (MetaPluginManager *plugin_mgr,
    */
   if (klass->xevent_filter)
     return klass->xevent_filter (plugin, xev);
-  else
-    return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
+
+  /* When mutter is running as a wayland compositor, things like input
+   * events just come directly from clutter so it won't have disabled
+   * clutter's event retrieval and won't need to forward it events (if
+   * it did it would lead to recursion). Also when running as a
+   * wayland compositor we shouldn't be assuming that we're running
+   * with the clutter x11 backend.
+   */
+  if (meta_is_wayland_compositor ())
+    return FALSE;
+
+  return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
 }
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
index c93bb47..cc09575 100644
--- a/src/compositor/meta-shaped-texture.c
+++ b/src/compositor/meta-shaped-texture.c
@@ -30,8 +30,14 @@
 #include <config.h>
 
 #include <meta/meta-shaped-texture.h>
+#include <meta/util.h>
 #include "meta-texture-tower.h"
 
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#include <cogl/cogl-wayland-server.h>
+#endif
+
 #include <clutter/clutter.h>
 #include <cogl/cogl.h>
 #include <cogl/cogl-texture-pixmap-x11.h>
@@ -55,6 +61,15 @@ static void meta_shaped_texture_get_preferred_height (ClutterActor *self,
 
 static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume);
 
+typedef enum _MetaShapedTextureType
+{
+  META_SHAPED_TEXTURE_TYPE_X11_PIXMAP,
+#ifdef HAVE_WAYLAND
+  META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE,
+#endif
+} MetaShapedTextureType;
+
+
 G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture,
                CLUTTER_TYPE_ACTOR);
 
@@ -65,8 +80,21 @@ G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture,
 struct _MetaShapedTexturePrivate
 {
   MetaTextureTower *paint_tower;
-  Pixmap pixmap;
-  CoglTexturePixmapX11 *texture;
+
+  MetaShapedTextureType type;
+  union {
+    struct {
+      Pixmap pixmap;
+    } x11;
+#ifdef HAVE_WAYLAND
+    struct {
+      MetaWaylandSurface *surface;
+    } wayland;
+#endif
+  };
+
+  CoglTexture *texture;
+
   CoglTexture *mask_texture;
   CoglPipeline *pipeline;
   CoglPipeline *pipeline_unshaped;
@@ -104,7 +132,10 @@ meta_shaped_texture_init (MetaShapedTexture *self)
   priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self);
 
   priv->paint_tower = meta_texture_tower_new ();
+
+  priv->type = META_SHAPED_TEXTURE_TYPE_X11_PIXMAP;
   priv->texture = NULL;
+
   priv->mask_texture = NULL;
   priv->create_mipmaps = TRUE;
 }
@@ -130,6 +161,56 @@ meta_shaped_texture_dispose (GObject *object)
 }
 
 static void
+set_cogl_texture (MetaShapedTexture *stex,
+                  CoglTexture       *cogl_tex)
+{
+  MetaShapedTexturePrivate *priv;
+  guint width, height;
+
+  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
+
+  priv = stex->priv;
+
+  if (priv->texture)
+    cogl_object_unref (priv->texture);
+
+  priv->texture = cogl_tex;
+
+  if (priv->pipeline != NULL)
+    cogl_pipeline_set_layer_texture (priv->pipeline, 0, COGL_TEXTURE (cogl_tex));
+
+  if (priv->pipeline_unshaped != NULL)
+    cogl_pipeline_set_layer_texture (priv->pipeline_unshaped, 0, COGL_TEXTURE (cogl_tex));
+
+  if (cogl_tex != NULL)
+    {
+      width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
+      height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex));
+
+      if (width != priv->tex_width ||
+          height != priv->tex_height)
+        {
+          priv->tex_width = width;
+          priv->tex_height = height;
+
+          clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
+        }
+    }
+  else
+    {
+      /* size changed to 0 going to an invalid handle */
+      priv->tex_width = 0;
+      priv->tex_height = 0;
+      clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
+    }
+
+  /* NB: We don't queue a redraw of the actor here because we don't
+   * know how much of the buffer has changed with respect to the
+   * previous buffer. We only queue a redraw in response to surface
+   * damage. */
+}
+
+static void
 meta_shaped_texture_paint (ClutterActor *actor)
 {
   MetaShapedTexture *stex = (MetaShapedTexture *) actor;
@@ -312,6 +393,7 @@ meta_shaped_texture_pick (ClutterActor       *actor,
        */
 
       n_rects = cairo_region_num_rectangles (priv->input_shape_region);
+      rectangles = g_alloca (sizeof (float) * 4 * n_rects);
 
       for (i = 0; i < n_rects; i++)
         {
@@ -380,12 +462,56 @@ meta_shaped_texture_get_paint_volume (ClutterActor *self,
   return clutter_paint_volume_set_from_allocation (volume, self);
 }
 
+#ifdef HAVE_WAYLAND
 ClutterActor *
-meta_shaped_texture_new (void)
+meta_shaped_texture_new_with_wayland_surface (MetaWaylandSurface *surface)
+{
+  ClutterActor *actor = g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
+  MetaShapedTexturePrivate *priv = META_SHAPED_TEXTURE (actor)->priv;
+
+  /* XXX: it could probably be better to have a "type" construct-only
+   * property or create wayland/x11 subclasses */
+  priv->type = META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE;
+
+  meta_shaped_texture_set_wayland_surface (META_SHAPED_TEXTURE (actor),
+                                           surface);
+
+  return actor;
+}
+
+void
+meta_shaped_texture_set_wayland_surface (MetaShapedTexture *stex,
+                                         MetaWaylandSurface *surface)
 {
-  ClutterActor *self = g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
+  MetaShapedTexturePrivate *priv = stex->priv;
+
+  priv->wayland.surface = surface;
 
-  return self;
+  if (surface && surface->buffer_ref.buffer)
+    meta_shaped_texture_attach_wayland_buffer (stex,
+                                               surface->buffer_ref.buffer);
+}
+
+MetaWaylandSurface *
+meta_shaped_texture_get_wayland_surface (MetaShapedTexture *stex)
+{
+  MetaShapedTexturePrivate *priv = stex->priv;
+  return priv->wayland.surface;
+}
+#endif /* HAVE_WAYLAND */
+
+ClutterActor *
+meta_shaped_texture_new_with_xwindow (Window xwindow)
+{
+#ifdef HAVE_WAYLAND
+  if (meta_is_wayland_compositor ())
+    {
+      MetaWaylandSurface *surface = meta_wayland_lookup_surface_for_xid (xwindow);
+      return meta_shaped_texture_new_with_wayland_surface (surface);
+    }
+  else
+#endif
+    return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
 }
 
 void
@@ -404,8 +530,7 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
     {
       CoglTexture *base_texture;
       priv->create_mipmaps = create_mipmaps;
-      base_texture = create_mipmaps ?
-        COGL_TEXTURE (priv->texture) : NULL;
+      base_texture = create_mipmaps ? priv->texture : NULL;
       meta_texture_tower_set_base_texture (priv->paint_tower, base_texture);
     }
 }
@@ -431,74 +556,146 @@ meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
   clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
 }
 
-void
-meta_shaped_texture_update_area (MetaShapedTexture *stex,
-                                int                x,
-                                int                y,
-                                int                width,
-                                int                height)
+#ifdef HAVE_WAYLAND
+static void
+wayland_surface_update_area (MetaShapedTexture *stex,
+                             int                x,
+                             int                y,
+                             int                width,
+                             int                height)
 {
   MetaShapedTexturePrivate *priv;
-  const cairo_rectangle_int_t clip = { x, y, width, height };
+  MetaWaylandBuffer *buffer;
 
   priv = stex->priv;
 
-  if (priv->texture == NULL)
-    return;
+  g_return_if_fail (priv->type == META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE);
+  g_return_if_fail (priv->texture != NULL);
 
-  cogl_texture_pixmap_x11_update_area (priv->texture,
-                                       x, y, width, height);
+  buffer = priv->wayland.surface->buffer_ref.buffer;
 
-  meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);
+  if (buffer)
+    {
+      struct wl_resource *resource = buffer->resource;
+      struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get (resource);
+
+      if (shm_buffer)
+        {
+          CoglPixelFormat format;
+
+          switch (wl_shm_buffer_get_format (shm_buffer))
+            {
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+            case WL_SHM_FORMAT_ARGB8888:
+              format = COGL_PIXEL_FORMAT_ARGB_8888_PRE;
+              break;
+            case WL_SHM_FORMAT_XRGB8888:
+              format = COGL_PIXEL_FORMAT_ARGB_8888;
+              break;
+#elif G_BYTE_ORDER == G_LITTLE_ENDIAN
+            case WL_SHM_FORMAT_ARGB8888:
+              format = COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+              break;
+            case WL_SHM_FORMAT_XRGB8888:
+              format = COGL_PIXEL_FORMAT_BGRA_8888;
+              break;
+#endif
+            default:
+              g_warn_if_reached ();
+              format = COGL_PIXEL_FORMAT_ARGB_8888;
+            }
 
-  clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip);
+          cogl_texture_set_region (priv->texture,
+                                   x, y,
+                                   x, y,
+                                   width, height,
+                                   width, height,
+                                   format,
+                                   wl_shm_buffer_get_stride (shm_buffer),
+                                   wl_shm_buffer_get_data (shm_buffer));
+        }
+    }
 }
+#endif /* HAVE_WAYLAND */
 
 static void
-set_cogl_texture (MetaShapedTexture    *stex,
-                  CoglTexturePixmapX11 *cogl_tex)
+queue_damage_redraw_with_clip (MetaShapedTexture *stex,
+                               int x,
+                               int y,
+                               int width,
+                               int height)
 {
+  ClutterActor *self = CLUTTER_ACTOR (stex);
   MetaShapedTexturePrivate *priv;
-  guint width, height;
+  ClutterActorBox allocation;
+  float scale_x;
+  float scale_y;
+  cairo_rectangle_int_t clip;
+
+  /* NB: clutter_actor_queue_redraw_with_clip expects a box in the actor's
+   * coordinate space so we need to convert from surface coordinates to
+   * actor coordinates...
+   */
 
-  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
+  /* Calling clutter_actor_get_allocation_box() is enormously expensive
+   * if the actor has an out-of-date allocation, since it triggers
+   * a full redraw. clutter_actor_queue_redraw_with_clip() would redraw
+   * the whole stage anyways in that case, so just go ahead and do
+   * it here.
+   */
+  if (!clutter_actor_has_allocation (self))
+    {
+      clutter_actor_queue_redraw (self);
+      return;
+    }
 
   priv = stex->priv;
 
-  if (priv->texture != NULL)
-    cogl_object_unref (priv->texture);
+  if (priv->tex_width == 0 || priv->tex_height == 0)
+    return;
 
-  priv->texture = cogl_tex;
+  clutter_actor_get_allocation_box (self, &allocation);
 
-  if (priv->pipeline != NULL)
-    cogl_pipeline_set_layer_texture (priv->pipeline, 0, COGL_TEXTURE (cogl_tex));
+  scale_x = (allocation.x2 - allocation.x1) / priv->tex_width;
+  scale_y = (allocation.y2 - allocation.y1) / priv->tex_height;
 
-  if (priv->pipeline_unshaped != NULL)
-    cogl_pipeline_set_layer_texture (priv->pipeline_unshaped, 0, COGL_TEXTURE (cogl_tex));
+  clip.x = x * scale_x;
+  clip.y = y * scale_y;
+  clip.width = width * scale_x;
+  clip.height = height * scale_y;
+  clutter_actor_queue_redraw_with_clip (self, &clip);
+}
 
-  if (cogl_tex != NULL)
-    {
-      width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
-      height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex));
+void
+meta_shaped_texture_update_area (MetaShapedTexture *stex,
+                                 int                x,
+                                 int                y,
+                                 int                width,
+                                 int                height)
+{
+  MetaShapedTexturePrivate *priv;
 
-      if (width != priv->tex_width ||
-          height != priv->tex_height)
-        {
-          priv->tex_width = width;
-          priv->tex_height = height;
+  priv = stex->priv;
 
-          clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
-        }
-    }
-  else
+  if (priv->texture == NULL)
+    return;
+
+  switch (priv->type)
     {
-      /* size changed to 0 going to an inavlid texture */
-      priv->tex_width = 0;
-      priv->tex_height = 0;
-      clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
+    case META_SHAPED_TEXTURE_TYPE_X11_PIXMAP:
+      cogl_texture_pixmap_x11_update_area (COGL_TEXTURE_PIXMAP_X11 (priv->texture),
+                                           x, y, width, height);
+      break;
+#ifdef HAVE_WAYLAND
+    case META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE:
+      wayland_surface_update_area (stex, x, y, width, height);
+      break;
+#endif
     }
 
-  clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
+  meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);
+
+  queue_damage_redraw_with_clip (stex, x, y, width, height);
 }
 
 /**
@@ -516,16 +713,65 @@ meta_shaped_texture_set_pixmap (MetaShapedTexture *stex,
 
   priv = stex->priv;
 
-  if (priv->pixmap == pixmap)
+  if (priv->x11.pixmap == pixmap)
     return;
 
-  priv->pixmap = pixmap;
+  priv->x11.pixmap = pixmap;
 
   if (pixmap != None)
     {
       CoglContext *ctx =
         clutter_backend_get_cogl_context (clutter_get_default_backend ());
-      set_cogl_texture (stex, cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, NULL));
+      CoglTexture *texture =
+        COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, NULL));
+      set_cogl_texture (stex, texture);
+    }
+  else
+    set_cogl_texture (stex, NULL);
+
+  if (priv->create_mipmaps)
+    meta_texture_tower_set_base_texture (priv->paint_tower,
+                                         COGL_TEXTURE (priv->texture));
+}
+
+#ifdef HAVE_WAYLAND
+void
+meta_shaped_texture_attach_wayland_buffer (MetaShapedTexture  *stex,
+                                           MetaWaylandBuffer  *buffer)
+{
+  MetaShapedTexturePrivate *priv;
+
+  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
+
+  priv = stex->priv;
+
+  /* TODO: we should change this api to be something like
+   * meta_shaped_texture_notify_buffer_attach() since we now maintain
+   * a reference to the MetaWaylandSurface where we can access the
+   * buffer without it being explicitly passed as an argument.
+   */
+  g_return_if_fail (priv->wayland.surface->buffer_ref.buffer == buffer);
+
+  if (buffer)
+    {
+      CoglContext *ctx =
+        clutter_backend_get_cogl_context (clutter_get_default_backend ());
+      CoglError *catch_error = NULL;
+      CoglTexture *texture =
+        COGL_TEXTURE (cogl_wayland_texture_2d_new_from_buffer (ctx,
+                                                               buffer->resource,
+                                                               &catch_error));
+      if (!texture)
+        {
+          cogl_error_free (catch_error);
+        }
+      else
+        {
+          buffer->width = cogl_texture_get_width (texture);
+          buffer->height = cogl_texture_get_height (texture);
+        }
+
+      set_cogl_texture (stex, texture);
     }
   else
     set_cogl_texture (stex, NULL);
@@ -534,6 +780,7 @@ meta_shaped_texture_set_pixmap (MetaShapedTexture *stex,
     meta_texture_tower_set_base_texture (priv->paint_tower,
                                          COGL_TEXTURE (priv->texture));
 }
+#endif /* HAVE_WAYLAND */
 
 /**
  * meta_shaped_texture_get_texture:
diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h
index 90a9e35..d37ee2d 100644
--- a/src/compositor/meta-window-actor-private.h
+++ b/src/compositor/meta-window-actor-private.h
@@ -5,6 +5,11 @@
 
 #include <config.h>
 
+#ifdef HAVE_WAYLAND
+#include <wayland-server.h>
+#include <meta-wayland-private.h>
+#endif
+
 #include <X11/extensions/Xdamage.h>
 #include <meta/compositor-mutter.h>
 
@@ -24,8 +29,20 @@ void meta_window_actor_unmaximize (MetaWindowActor *self,
                                    MetaRectangle   *old_rect,
                                    MetaRectangle   *new_rect);
 
-void meta_window_actor_process_damage (MetaWindowActor    *self,
-                                       XDamageNotifyEvent *event);
+void meta_window_actor_process_x11_damage (MetaWindowActor    *self,
+                                           XDamageNotifyEvent *event);
+
+#ifdef HAVE_WAYLAND
+void meta_window_actor_process_wayland_damage (MetaWindowActor *self,
+                                               int              x,
+                                               int              y,
+                                               int              width,
+                                               int              height);
+void meta_window_actor_set_wayland_surface    (MetaWindowActor    *self,
+                                               MetaWaylandSurface *surface);
+void meta_window_actor_attach_wayland_buffer  (MetaWindowActor   *self,
+                                               MetaWaylandBuffer *buffer);
+#endif
 
 void meta_window_actor_pre_paint      (MetaWindowActor    *self);
 void meta_window_actor_post_paint     (MetaWindowActor    *self);
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index 34a8fa1..54f6838 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -32,6 +32,9 @@
 #include "meta-window-actor-private.h"
 #include "meta-texture-rectangle.h"
 #include "region-utils.h"
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#endif
 
 enum {
   POSITION_CHANGED,
@@ -64,10 +67,6 @@ struct _MetaWindowActorPrivate
   MetaShadow       *focused_shadow;
   MetaShadow       *unfocused_shadow;
 
-  Pixmap            back_pixmap;
-
-  Damage            damage;
-
   guint8            opacity;
 
   /* A region that matches the shape of the window, including frame bounds */
@@ -104,31 +103,41 @@ struct _MetaWindowActorPrivate
   /* List of FrameData for recent frames */
   GList            *frames;
 
+  Pixmap            back_pixmap; /* Not used in wayland compositor mode */
+  Damage            damage; /* Not used in wayland compositor mode */
+
   guint                    visible                : 1;
   guint                    mapped                 : 1;
   guint                    argb32                 : 1;
   guint                    disposed               : 1;
   guint             redecorating           : 1;
 
-  guint                    needs_damage_all       : 1;
-  guint                    received_damage        : 1;
-  guint             repaint_scheduled      : 1;
-
   /* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
    * client message using the most recent frame in ->frames */
   guint             needs_frame_drawn      : 1;
+  guint             repaint_scheduled      : 1;
 
-  guint                    needs_pixmap           : 1;
   guint             needs_reshape          : 1;
   guint             recompute_focused_shadow   : 1;
   guint             recompute_unfocused_shadow : 1;
-  guint                    size_changed           : 1;
-  guint             updates_frozen         : 1;
 
   guint                    needs_destroy          : 1;
 
   guint             no_shadow              : 1;
 
+
+  /* 
+   * None of these are used in wayland compositor mode...
+   */
+
+  guint                    needs_damage_all       : 1;
+  guint                    received_x11_damage    : 1;
+
+  guint                    needs_pixmap           : 1;
+
+  guint                    x11_size_changed       : 1;
+  guint             updates_frozen         : 1;
+
   guint             unredirected           : 1;
 
   /* This is used to detect fullscreen windows that need to be unredirected */
@@ -172,7 +181,7 @@ static gboolean meta_window_actor_get_paint_volume (ClutterActor       *actor,
                                                     ClutterPaintVolume *volume);
 
 
-static void     meta_window_actor_detach     (MetaWindowActor *self);
+static void meta_window_actor_detach_x11_pixmap     (MetaWindowActor *self);
 static gboolean meta_window_actor_has_shadow (MetaWindowActor *self);
 
 static void meta_window_actor_handle_updates (MetaWindowActor *self);
@@ -306,18 +315,21 @@ window_decorated_notify (MetaWindow *mw,
   else
     new_xwindow = meta_window_get_xwindow (mw);
 
-  meta_window_actor_detach (self);
-
-  /*
-   * First of all, clean up any resources we are currently using and will
-   * be replacing.
-   */
-  if (priv->damage != None)
+  if (!meta_is_wayland_compositor ())
     {
-      meta_error_trap_push (display);
-      XDamageDestroy (xdisplay, priv->damage);
-      meta_error_trap_pop (display);
-      priv->damage = None;
+      meta_window_actor_detach_x11_pixmap (self);
+
+      /*
+       * First of all, clean up any resources we are currently using and will
+       * be replacing.
+       */
+      if (priv->damage != None)
+        {
+          meta_error_trap_push (display);
+          XDamageDestroy (xdisplay, priv->damage);
+          meta_error_trap_pop (display);
+          priv->damage = None;
+        }
     }
 
   priv->xwindow = new_xwindow;
@@ -348,8 +360,9 @@ meta_window_actor_constructed (GObject *object)
   Display                *xdisplay = meta_display_get_xdisplay (display);
   XRenderPictFormat      *format;
 
-  priv->damage = XDamageCreate (xdisplay, xwindow,
-                                XDamageReportBoundingBox);
+  if (!meta_is_wayland_compositor ())
+    priv->damage = XDamageCreate (xdisplay, xwindow,
+                                  XDamageReportBoundingBox);
 
   format = XRenderFindVisualFormat (xdisplay, window->xvisual);
 
@@ -358,7 +371,12 @@ meta_window_actor_constructed (GObject *object)
 
   if (!priv->actor)
     {
-      priv->actor = meta_shaped_texture_new ();
+      if (window->client_type == META_WINDOW_CLIENT_TYPE_X11)
+        priv->actor = meta_shaped_texture_new_with_xwindow (xwindow);
+#ifdef HAVE_WAYLAND
+      else
+        priv->actor = meta_shaped_texture_new_with_wayland_surface (window->surface);
+#endif
 
       clutter_actor_add_child (CLUTTER_ACTOR (self), priv->actor);
 
@@ -387,9 +405,10 @@ meta_window_actor_constructed (GObject *object)
 
   meta_window_actor_update_opacity (self);
 
-  /* Start off with an empty region to maintain the invariant that
-     the shape region is always set */
+  /* Start off with empty regions to maintain the invariant that
+     these regions are always set */
   priv->shape_region = cairo_region_create();
+  priv->input_shape_region = cairo_region_create();
 }
 
 static void
@@ -408,11 +427,15 @@ meta_window_actor_dispose (GObject *object)
   priv->disposed = TRUE;
 
   screen   = priv->screen;
-  display  = meta_screen_get_display (screen);
-  xdisplay = meta_display_get_xdisplay (display);
   info     = meta_screen_get_compositor_data (screen);
 
-  meta_window_actor_detach (self);
+  if (!meta_is_wayland_compositor ())
+    {
+      display  = meta_screen_get_display (screen);
+      xdisplay = meta_display_get_xdisplay (display);
+
+      meta_window_actor_detach_x11_pixmap (self);
+    }
 
   g_clear_pointer (&priv->shape_region, cairo_region_destroy);
   g_clear_pointer (&priv->input_shape_region, cairo_region_destroy);
@@ -424,7 +447,7 @@ meta_window_actor_dispose (GObject *object)
   g_clear_pointer (&priv->unfocused_shadow, meta_shadow_unref);
   g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref);
 
-  if (priv->damage != None)
+  if (!meta_is_wayland_compositor () && priv->damage != None)
     {
       meta_error_trap_push (display);
       XDamageDestroy (xdisplay, priv->damage);
@@ -896,7 +919,8 @@ meta_window_actor_showing_on_its_workspace (MetaWindowActor *self)
 static void
 meta_window_actor_freeze (MetaWindowActor *self)
 {
-  self->priv->freeze_count++;
+  if (!meta_is_wayland_compositor ())
+    self->priv->freeze_count++;
 }
 
 static void
@@ -925,30 +949,33 @@ meta_window_actor_damage_all (MetaWindowActor *self)
 static void
 meta_window_actor_thaw (MetaWindowActor *self)
 {
-  self->priv->freeze_count--;
-
-  if (G_UNLIKELY (self->priv->freeze_count < 0))
+  if (!meta_is_wayland_compositor ())
     {
-      g_warning ("Error in freeze/thaw accounting.");
-      self->priv->freeze_count = 0;
-      return;
-    }
+      self->priv->freeze_count--;
 
-  if (self->priv->freeze_count)
-    return;
+      if (G_UNLIKELY (self->priv->freeze_count < 0))
+        {
+          g_warning ("Error in freeze/thaw accounting.");
+          self->priv->freeze_count = 0;
+          return;
+        }
 
-  /* We sometimes ignore moves and resizes on frozen windows */
-  meta_window_actor_sync_actor_geometry (self, FALSE);
+      if (self->priv->freeze_count)
+        return;
 
-  /* We do this now since we might be going right back into the
-   * frozen state */
-  meta_window_actor_handle_updates (self);
+      /* We sometimes ignore moves and resizes on frozen windows */
+      meta_window_actor_sync_actor_geometry (self, FALSE);
+
+      /* We do this now since we might be going right back into the
+       * frozen state */
+      meta_window_actor_handle_updates (self);
 
-  /* Since we ignore damage events while a window is frozen for certain effects
-   * we may need to issue an update_area() covering the whole pixmap if we
-   * don't know what real damage has happened. */
-  if (self->priv->needs_damage_all)
-    meta_window_actor_damage_all (self);
+      /* Since we ignore damage events while a window is frozen for certain effects
+       * we may need to issue an update_area() covering the whole pixmap if we
+       * don't know what real damage has happened. */
+      if (self->priv->needs_damage_all)
+        meta_window_actor_damage_all (self);
+    }
 }
 
 void
@@ -979,7 +1006,7 @@ meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
        * send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get
        * consistent timing with non-empty frames.
        */
-      if (priv->mapped && !priv->needs_pixmap)
+      if (priv->mapped && (!meta_is_wayland_compositor () || !priv->needs_pixmap))
         {
           const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
           clutter_actor_queue_redraw_with_clip (priv->actor, &clip);
@@ -1005,7 +1032,7 @@ is_frozen (MetaWindowActor *self)
 }
 
 static void
-meta_window_actor_queue_create_pixmap (MetaWindowActor *self)
+meta_window_actor_queue_create_x11_pixmap (MetaWindowActor *self)
 {
   MetaWindowActorPrivate *priv = self->priv;
 
@@ -1109,11 +1136,14 @@ meta_window_actor_after_effects (MetaWindowActor *self)
   meta_window_actor_sync_visibility (self);
   meta_window_actor_sync_actor_geometry (self, FALSE);
 
-  if (!meta_window_is_mapped (priv->window))
-    meta_window_actor_detach (self);
+  if (!meta_is_wayland_compositor ())
+    {
+      if (!meta_window_is_mapped (priv->window))
+        meta_window_actor_detach_x11_pixmap (self);
 
-  if (priv->needs_pixmap)
-    clutter_actor_queue_redraw (priv->actor);
+      if (priv->needs_pixmap)
+        clutter_actor_queue_redraw (priv->actor);
+    }
 }
 
 void
@@ -1194,7 +1224,7 @@ meta_window_actor_effect_completed (MetaWindowActor *self,
  * pixmap for a new size.
  */
 static void
-meta_window_actor_detach (MetaWindowActor *self)
+meta_window_actor_detach_x11_pixmap (MetaWindowActor *self)
 {
   MetaWindowActorPrivate *priv     = self->priv;
   MetaScreen            *screen   = priv->screen;
@@ -1215,7 +1245,7 @@ meta_window_actor_detach (MetaWindowActor *self)
   XFreePixmap (xdisplay, priv->back_pixmap);
   priv->back_pixmap = None;
 
-  meta_window_actor_queue_create_pixmap (self);
+  meta_window_actor_queue_create_x11_pixmap (self);
 }
 
 gboolean
@@ -1245,7 +1275,7 @@ meta_window_actor_should_unredirect (MetaWindowActor *self)
   if (meta_window_is_override_redirect (metaWindow))
     return TRUE;
 
-  if (priv->does_full_damage)
+  if (!meta_is_wayland_compositor () && priv->does_full_damage)
     return TRUE;
 
   return FALSE;
@@ -1265,7 +1295,7 @@ meta_window_actor_set_redirected (MetaWindowActor *self, gboolean state)
       meta_error_trap_push (display);
       XCompositeRedirectWindow (xdisplay, xwin, CompositeRedirectManual);
       meta_error_trap_pop (display);
-      meta_window_actor_detach (self);
+      meta_window_actor_detach_x11_pixmap (self);
       self->priv->unredirected = FALSE;
     }
   else
@@ -1338,15 +1368,21 @@ meta_window_actor_sync_actor_geometry (MetaWindowActor *self,
 
   meta_window_get_input_rect (priv->window, &window_rect);
 
-  if (priv->last_width != window_rect.width ||
-      priv->last_height != window_rect.height)
+  /* When running as a display server then we instead catch size changes when
+   * new buffers are attached */
+  if (!meta_is_wayland_compositor ())
     {
-      priv->size_changed = TRUE;
-      meta_window_actor_queue_create_pixmap (self);
-      meta_window_actor_update_shape (self);
+      if (priv->last_width != window_rect.width ||
+          priv->last_height != window_rect.height)
+        {
+          priv->x11_size_changed = TRUE;
+          meta_window_actor_queue_create_x11_pixmap (self);
 
-      priv->last_width = window_rect.width;
-      priv->last_height = window_rect.height;
+          meta_window_actor_update_shape (self);
+
+          priv->last_width = window_rect.width;
+          priv->last_height = window_rect.height;
+        }
     }
 
   if (meta_window_actor_effect_in_progress (self))
@@ -1510,16 +1546,27 @@ meta_window_actor_new (MetaWindow *window)
   MetaWindowActor        *self;
   MetaWindowActorPrivate *priv;
   MetaFrame             *frame;
-  Window                 top_window;
+  Window                 top_window = None;
   ClutterActor           *window_group;
 
-  frame = meta_window_get_frame (window);
-  if (frame)
-    top_window = meta_frame_get_xwindow (frame);
-  else
-    top_window = meta_window_get_xwindow (window);
+  if (window->client_type == META_WINDOW_CLIENT_TYPE_X11)
+    {
+      frame = meta_window_get_frame (window);
+      if (frame)
+        top_window = meta_frame_get_xwindow (frame);
+      else
+        top_window = meta_window_get_xwindow (window);
 
-  meta_verbose ("add window: Meta %p, xwin 0x%x\n", window, (guint)top_window);
+      meta_verbose ("add window: Meta %p, xwin 0x%x\n", window, (guint)top_window);
+    }
+#ifdef HAVE_WAYLAND
+  else
+    {
+      meta_verbose ("add window: Meta %p, wayland surface %p\n",
+                    window, window->surface);
+      top_window = None;
+    }
+#endif
 
   self = g_object_new (META_TYPE_WINDOW_ACTOR,
                        "meta-window",         window,
@@ -1529,21 +1576,24 @@ meta_window_actor_new (MetaWindow *window)
 
   priv = self->priv;
 
-  priv->last_width = -1;
-  priv->last_height = -1;
+  if (!meta_is_wayland_compositor ())
+    {
+      priv->last_width = -1;
+      priv->last_height = -1;
 
-  priv->mapped = meta_window_toplevel_is_mapped (priv->window);
-  if (priv->mapped)
-    meta_window_actor_queue_create_pixmap (self);
+      priv->mapped = meta_window_toplevel_is_mapped (priv->window);
+      if (priv->mapped)
+        meta_window_actor_queue_create_x11_pixmap (self);
 
-  meta_window_actor_set_updates_frozen (self,
-                                        meta_window_updates_are_frozen (priv->window));
+      meta_window_actor_set_updates_frozen (self,
+                                            meta_window_updates_are_frozen (priv->window));
 
-  /* If a window doesn't start off with updates frozen, we should
-   * we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
-   */
-  if (priv->window->extended_sync_request_counter && !priv->updates_frozen)
-    meta_window_actor_queue_frame_drawn (self, FALSE);
+      /* If a window doesn't start off with updates frozen, we should
+       * we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
+       */
+      if (priv->window->extended_sync_request_counter && !priv->updates_frozen)
+        meta_window_actor_queue_frame_drawn (self, FALSE);
+    }
 
   meta_window_actor_sync_actor_geometry (self, priv->window->placed);
 
@@ -1578,7 +1628,8 @@ meta_window_actor_mapped (MetaWindowActor *self)
 
   priv->mapped = TRUE;
 
-  meta_window_actor_queue_create_pixmap (self);
+  if (!meta_is_wayland_compositor ())
+    meta_window_actor_queue_create_x11_pixmap (self);
 }
 
 void
@@ -1593,8 +1644,11 @@ meta_window_actor_unmapped (MetaWindowActor *self)
   if (meta_window_actor_effect_in_progress (self))
     return;
 
-  meta_window_actor_detach (self);
-  priv->needs_pixmap = FALSE;
+  if (!meta_is_wayland_compositor ())
+    {
+      meta_window_actor_detach_x11_pixmap (self);
+      priv->needs_pixmap = FALSE;
+    }
 }
 
 /**
@@ -1612,10 +1666,21 @@ meta_window_actor_get_obscured_region (MetaWindowActor *self)
 {
   MetaWindowActorPrivate *priv = self->priv;
 
-  if (priv->back_pixmap && priv->opacity == 0xff && !priv->window->shaded)
-    return priv->opaque_region;
-  else
-    return NULL;
+  if (!priv->window->shaded)
+    {
+      if (meta_is_wayland_compositor ())
+        {
+          if (priv->opacity == 0xff)
+            return priv->opaque_region;
+        }
+      else
+        {
+          if (priv->back_pixmap && priv->opacity == 0xff)
+            return priv->opaque_region;
+        }
+    }
+
+  return NULL;
 }
 
 #if 0
@@ -1728,8 +1793,11 @@ meta_window_actor_reset_visible_regions (MetaWindowActor *self)
   g_clear_pointer (&priv->shadow_clip, cairo_region_destroy);
 }
 
+/* When running as a wayland compositor we don't make requests for
+ * replacement pixmaps when resizing windows, we will instead be
+ * asked to attach replacement buffers by the clients. */
 static void
-check_needs_pixmap (MetaWindowActor *self)
+check_needs_x11_pixmap (MetaWindowActor *self)
 {
   MetaWindowActorPrivate *priv     = self->priv;
   MetaScreen          *screen   = priv->screen;
@@ -1751,10 +1819,10 @@ check_needs_pixmap (MetaWindowActor *self)
 
   compositor = meta_display_get_compositor (display);
 
-  if (priv->size_changed)
+  if (priv->x11_size_changed)
     {
-      meta_window_actor_detach (self);
-      priv->size_changed = FALSE;
+      meta_window_actor_detach_x11_pixmap (self);
+      priv->x11_size_changed = FALSE;
     }
 
   meta_error_trap_push (display);
@@ -1886,13 +1954,13 @@ check_needs_shadow (MetaWindowActor *self)
 }
 
 void
-meta_window_actor_process_damage (MetaWindowActor    *self,
-                                  XDamageNotifyEvent *event)
+meta_window_actor_process_x11_damage (MetaWindowActor    *self,
+                                      XDamageNotifyEvent *event)
 {
   MetaWindowActorPrivate *priv = self->priv;
   MetaCompScreen *info = meta_screen_get_compositor_data (priv->screen);
 
-  priv->received_damage = TRUE;
+  priv->received_x11_damage = TRUE;
 
   if (meta_window_is_fullscreen (priv->window) && g_list_last (info->windows)->data == self && 
!priv->unredirected)
     {
@@ -1946,6 +2014,25 @@ meta_window_actor_process_damage (MetaWindowActor    *self,
   priv->repaint_scheduled = TRUE;
 }
 
+#ifdef HAVE_WAYLAND
+void
+meta_window_actor_process_wayland_damage (MetaWindowActor *self,
+                                          int x,
+                                          int y,
+                                          int width,
+                                          int height)
+{
+  MetaWindowActorPrivate *priv = self->priv;
+
+  if (!priv->mapped)
+    return;
+
+  meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
+                                   x, y, width, height);
+  priv->repaint_scheduled = TRUE;
+}
+#endif
+
 void
 meta_window_actor_sync_visibility (MetaWindowActor *self)
 {
@@ -2106,8 +2193,8 @@ region_create_from_x_rectangles (const XRectangle *rects,
 }
 
 static void
-meta_window_actor_update_shape_region (MetaWindowActor *self,
-                                       cairo_rectangle_int_t *client_area)
+meta_window_actor_update_x11_shape_region (MetaWindowActor *self,
+                                           cairo_rectangle_int_t *client_area)
 {
   MetaWindowActorPrivate *priv = self->priv;
   cairo_region_t *region = NULL;
@@ -2212,8 +2299,8 @@ meta_window_actor_update_shape_region (MetaWindowActor *self,
 }
 
 static void
-meta_window_actor_update_input_shape_region (MetaWindowActor *self,
-                                             cairo_rectangle_int_t *client_area)
+meta_window_actor_update_x11_input_shape_region (MetaWindowActor *self,
+                                                 cairo_rectangle_int_t *client_area)
 {
   MetaWindowActorPrivate *priv = self->priv;
   cairo_region_t *region = NULL;
@@ -2299,8 +2386,24 @@ check_needs_reshape (MetaWindowActor *self)
   else
     client_area.height = priv->window->rect.height;
 
-  meta_window_actor_update_shape_region (self, &client_area);
-  meta_window_actor_update_input_shape_region (self, &client_area);
+  if (priv->window->client_type == META_WINDOW_CLIENT_TYPE_X11)
+    {
+      meta_window_actor_update_x11_shape_region (self, &client_area);
+      meta_window_actor_update_x11_input_shape_region (self, &client_area);
+    }
+  else
+    {
+      /* TODO: properly support setting an input region as specified
+       * via the wayland protocol */
+
+      g_clear_pointer (&priv->shape_region, cairo_region_destroy);
+      g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
+      g_clear_pointer (&priv->input_shape_region, cairo_region_destroy);
+
+      priv->shape_region = cairo_region_create_rectangle (&client_area);
+      priv->opaque_region = cairo_region_reference (priv->shape_region);
+      priv->input_shape_region = cairo_region_reference (priv->shape_region);
+    }
 
   priv->needs_reshape = FALSE;
 }
@@ -2318,6 +2421,69 @@ meta_window_actor_update_shape (MetaWindowActor *self)
   clutter_actor_queue_redraw (priv->actor);
 }
 
+#ifdef HAVE_WAYLAND
+static void
+maybe_emit_size_changed (MetaWindowActor *self,
+                         MetaWaylandBuffer *new_buffer)
+{
+  MetaWindowActorPrivate *priv = self->priv;
+  int                     width = 0, height = 0;
+
+  if (new_buffer)
+    {
+      width = new_buffer->width;
+      height = new_buffer->height;
+    }
+
+  if (priv->last_width != width || priv->last_height != height)
+    {
+      meta_window_actor_update_shape (self);
+
+      /* ::size-changed is supposed to refer to meta_window_get_outer_rect()
+       * but here we are only looking at buffer size changes.
+       *
+       * Emitting it here works pretty much OK because a new buffer size (which
+       * will correspond to the outer rect with the addition of invisible
+       * borders) also normally implies a change to the outer rect. In the rare
+       * case where a change to the window size was exactly balanced by a
+       * change to the invisible borders, we would miss emitting the signal.
+       */
+      g_signal_emit (self, signals[SIZE_CHANGED], 0);
+
+      priv->last_width = width;
+      priv->last_height = height;
+    }
+}
+
+void
+meta_window_actor_set_wayland_surface (MetaWindowActor *self,
+                                       MetaWaylandSurface *surface)
+{
+  MetaWindowActorPrivate *priv = self->priv;
+
+  meta_shaped_texture_set_wayland_surface (META_SHAPED_TEXTURE (priv->actor),
+                                           surface);
+  if (surface->buffer_ref.buffer)
+    maybe_emit_size_changed (self, surface->buffer_ref.buffer);
+}
+
+void
+meta_window_actor_attach_wayland_buffer (MetaWindowActor *self,
+                                         MetaWaylandBuffer *buffer)
+{
+  MetaWindowActorPrivate *priv = self->priv;
+  MetaShapedTexture *stex = META_SHAPED_TEXTURE (priv->actor);
+  CoglTexture *prev_tex = meta_shaped_texture_get_texture (stex);
+
+  meta_shaped_texture_attach_wayland_buffer (stex, buffer);
+
+  if (!prev_tex)
+    meta_window_actor_sync_actor_geometry (self, FALSE);
+
+  maybe_emit_size_changed (self, buffer);
+}
+#endif /* HAVE_WAYLAND */
+
 static void
 meta_window_actor_handle_updates (MetaWindowActor *self)
 {
@@ -2333,42 +2499,46 @@ meta_window_actor_handle_updates (MetaWindowActor *self)
       return;
     }
 
-  if (priv->unredirected)
+  if (!meta_is_wayland_compositor ())
     {
-      /* Nothing to do here until/if the window gets redirected again */
-      return;
-    }
+      if (priv->unredirected)
+        {
+          /* Nothing to do here until/if the window gets redirected again */
+          return;
+        }
 
-  if (priv->received_damage)
-    {
-      meta_error_trap_push (display);
-      XDamageSubtract (xdisplay, priv->damage, None, None);
-      meta_error_trap_pop (display);
+      if (priv->received_x11_damage)
+        {
+          meta_error_trap_push (display);
+          XDamageSubtract (xdisplay, priv->damage, None, None);
+          meta_error_trap_pop (display);
+
+          /* We need to make sure that any X drawing that happens before the
+           * XDamageSubtract() above is visible to subsequent GL rendering;
+           * the only standardized way to do this is EXT_x11_sync_object,
+           * which isn't yet widely available. For now, we count on details
+           * of Xorg and the open source drivers, and hope for the best
+           * otherwise.
+           *
+           * Xorg and open source driver specifics:
+           *
+           * The X server makes sure to flush drawing to the kernel before
+           * sending out damage events, but since we use DamageReportBoundingBox
+           * there may be drawing between the last damage event and the
+           * XDamageSubtract() that needs to be flushed as well.
+           *
+           * Xorg always makes sure that drawing is flushed to the kernel
+           * before writing events or responses to the client, so any round trip
+           * request at this point is sufficient to flush the GLX buffers.
+           */
+          XSync (xdisplay, False);
 
-      /* We need to make sure that any X drawing that happens before the
-       * XDamageSubtract() above is visible to subsequent GL rendering;
-       * the only standardized way to do this is EXT_x11_sync_object,
-       * which isn't yet widely available. For now, we count on details
-       * of Xorg and the open source drivers, and hope for the best
-       * otherwise.
-       *
-       * Xorg and open source driver specifics:
-       *
-       * The X server makes sure to flush drawing to the kernel before
-       * sending out damage events, but since we use DamageReportBoundingBox
-       * there may be drawing between the last damage event and the
-       * XDamageSubtract() that needs to be flushed as well.
-       *
-       * Xorg always makes sure that drawing is flushed to the kernel
-       * before writing events or responses to the client, so any round trip
-       * request at this point is sufficient to flush the GLX buffers.
-       */
-      XSync (xdisplay, False);
+          priv->received_x11_damage = FALSE;
+        }
 
-      priv->received_damage = FALSE;
+      check_needs_x11_pixmap (self);
     }
 
-  check_needs_pixmap (self);
   check_needs_reshape (self);
   check_needs_shadow (self);
 }
@@ -2547,16 +2717,20 @@ void
 meta_window_actor_set_updates_frozen (MetaWindowActor *self,
                                       gboolean         updates_frozen)
 {
-  MetaWindowActorPrivate *priv = self->priv;
+  /* On wayland we shouldn't need to ever freeze updates... */
+  if (!meta_is_wayland_compositor ())
+    {
+      MetaWindowActorPrivate *priv = self->priv;
 
-  updates_frozen = updates_frozen != FALSE;
+      updates_frozen = updates_frozen != FALSE;
 
-  if (priv->updates_frozen != updates_frozen)
-    {
-      priv->updates_frozen = updates_frozen;
-      if (updates_frozen)
-        meta_window_actor_freeze (self);
-      else
-        meta_window_actor_thaw (self);
+      if (priv->updates_frozen != updates_frozen)
+        {
+          priv->updates_frozen = updates_frozen;
+          if (updates_frozen)
+            meta_window_actor_freeze (self);
+          else
+            meta_window_actor_thaw (self);
+        }
     }
 }
diff --git a/src/compositor/meta-window-group.c b/src/compositor/meta-window-group.c
index ec22af3..8fddc5e 100644
--- a/src/compositor/meta-window-group.c
+++ b/src/compositor/meta-window-group.c
@@ -13,6 +13,7 @@
 #include "meta-window-group.h"
 #include "meta-background-actor-private.h"
 #include "meta-background-group-private.h"
+#include "window-private.h"
 
 struct _MetaWindowGroupClass
 {
@@ -99,7 +100,7 @@ meta_window_group_paint (ClutterActor *actor)
   int paint_x_offset, paint_y_offset;
 
   MetaWindowGroup *window_group = META_WINDOW_GROUP (actor);
-  MetaCompScreen *info = meta_screen_get_compositor_data (window_group->screen);
+  MetaCompScreen *info;
 
   /* Normally we expect an actor to be drawn at it's position on the screen.
    * However, if we're inside the paint of a ClutterClone, that won't be the
@@ -136,13 +137,17 @@ meta_window_group_paint (ClutterActor *actor)
 
   visible_region = cairo_region_create_rectangle (&visible_rect);
 
-  if (info->unredirected_window != NULL)
+  if (!meta_is_wayland_compositor ())
     {
-      cairo_rectangle_int_t unredirected_rect;
-      MetaWindow *window = meta_window_actor_get_meta_window (info->unredirected_window);
+      info = meta_screen_get_compositor_data (window_group->screen);
+      if (info->unredirected_window != NULL)
+        {
+          cairo_rectangle_int_t unredirected_rect;
+          MetaWindow *window = meta_window_actor_get_meta_window (info->unredirected_window);
 
-      meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect);
-      cairo_region_subtract_rectangle (visible_region, &unredirected_rect);
+          meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect);
+          cairo_region_subtract_rectangle (visible_region, &unredirected_rect);
+        }
     }
 
   /* We walk the list from top to bottom (opposite of painting order),
@@ -155,7 +160,8 @@ meta_window_group_paint (ClutterActor *actor)
       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
         continue;
 
-      if (info->unredirected_window != NULL &&
+      if (!meta_is_wayland_compositor () &&
+          info->unredirected_window != NULL &&
           child == CLUTTER_ACTOR (info->unredirected_window))
         continue;
 
@@ -180,7 +186,8 @@ meta_window_group_paint (ClutterActor *actor)
 
       if (META_IS_WINDOW_ACTOR (child))
         {
-          MetaWindowActor *window_actor = META_WINDOW_ACTOR (child);
+          MetaWindow *meta_window;
+          MetaWindowActor *window_actor = child;
           int x, y;
 
           if (!meta_actor_is_untransformed (CLUTTER_ACTOR (window_actor), &x, &y))
@@ -194,7 +201,14 @@ meta_window_group_paint (ClutterActor *actor)
 
           meta_window_actor_set_visible_region (window_actor, visible_region);
 
-          if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
+          /* TODO: Track the opaque regions of wayland clients.
+           * Although wayland clients can report opaque window
+           * regions, for now we assume that all wayland clients are
+           * transparent... */
+          meta_window = meta_window_actor_get_meta_window (window_actor);
+
+          if (meta_window->client_type != META_WINDOW_CLIENT_TYPE_WAYLAND &&
+              clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
             {
               cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor);
               if (obscured_region)
diff --git a/src/core/display.c b/src/core/display.c
index 9bcb5e5..5e19f14 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -931,8 +931,24 @@ meta_display_open (void)
   while (tmp != NULL)
     {
       MetaScreen *screen = tmp->data;
-       
-      meta_screen_manage_all_windows (screen);
+
+      if (meta_is_wayland_compositor ())
+        {
+          /* Instead of explicitly enumerating all windows during
+           * initialization, when we run as a wayland compositor we can rely on
+           * xwayland notifying us of all top level windows so we create
+           * MetaWindows when we get those notifications.
+           *
+           * We still want a guard window so we can avoid
+           * unmapping/withdrawing minimized windows for live
+           * thumbnails...
+           */
+          if (screen->guard_window == None)
+            screen->guard_window =
+              meta_screen_create_guard_window (screen->display->xdisplay, screen);
+        }
+      else
+        meta_screen_manage_all_windows (screen);
 
       tmp = tmp->next;
     }
@@ -2284,8 +2300,8 @@ event_callback (XEvent   *event,
                 }
 
               if (display->compositor)
-                meta_compositor_window_shape_changed (display->compositor,
-                                                      window);
+                meta_compositor_window_x11_shape_changed (display->compositor,
+                                                          window);
             }
           else if (sev->kind == ShapeInput)
             {
@@ -2311,8 +2327,8 @@ event_callback (XEvent   *event,
                 }
 
               if (display->compositor)
-                meta_compositor_window_shape_changed (display->compositor,
-                                                      window);
+                meta_compositor_window_x11_shape_changed (display->compositor,
+                                                          window);
             }
         }
       else
diff --git a/src/core/main.c b/src/core/main.c
index 4bec3d2..9dc758e 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -55,6 +55,9 @@
 #include "session.h"
 #include <meta/prefs.h>
 #include <meta/compositor.h>
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#endif
 
 #include <glib-object.h>
 #include <gdk/gdkx.h>
@@ -346,28 +349,74 @@ meta_finalize (void)
   if (display)
     meta_display_close (display,
                         CurrentTime); /* I doubt correct timestamps matter here */
+
+#ifdef HAVE_WAYLAND
+  if (meta_is_wayland_compositor ())
+    meta_wayland_finalize ();
+#endif
 }
 
-static int sigterm_pipe_fds[2] = { -1, -1 };
+static int signal_pipe_fds[2] = { -1, -1 };
 
 static void
-sigterm_handler (int signum)
+signal_handler (int signum)
 {
-  if (sigterm_pipe_fds[1] >= 0)
+  if (signal_pipe_fds[1] >= 0)
     {
-      int G_GNUC_UNUSED dummy;
-
-      dummy = write (sigterm_pipe_fds[1], "", 1);
-      close (sigterm_pipe_fds[1]);
-      sigterm_pipe_fds[1] = -1;
+      switch (signum)
+        {
+        case SIGTERM:
+          write (signal_pipe_fds[1], "T", 1);
+          break;
+        case SIGCHLD:
+          write (signal_pipe_fds[1], "C", 1);
+          break;
+        default:
+          break;
+        }
     }
 }
 
 static gboolean
-on_sigterm (void)
+on_signal (GIOChannel *source,
+           GIOCondition condition,
+           void *data)
 {
-  meta_quit (META_EXIT_SUCCESS);
-  return FALSE;
+  char signal;
+  int count;
+
+  for (;;)
+    {
+      count = read (signal_pipe_fds[0], &signal, 1);
+      if (count == EINTR)
+        continue;
+      if (count < 0)
+        {
+          const char *msg = strerror (errno);
+          g_warning ("Error handling signal: %s", msg);
+        }
+      if (count != 1)
+        {
+          g_warning ("Unexpectedly failed to read byte from signal pipe\n");
+          return TRUE;
+        }
+      break;
+    }
+  switch (signal)
+    {
+    case 'T': /* SIGTERM */
+      meta_quit (META_EXIT_SUCCESS);
+      break;
+#ifdef HAVE_WAYLAND
+    case 'C': /* SIGCHLD */
+      meta_wayland_handle_sig_child ();
+      break;
+#endif
+    default:
+      g_warning ("Spurious character '%c' read from signal pipe", signal);
+    }
+
+  return TRUE;
 }
 
 /**
@@ -396,21 +445,28 @@ meta_init (void)
                 g_strerror (errno));
 #endif
 
-  if (pipe (sigterm_pipe_fds) != 0)
-    g_printerr ("Failed to create SIGTERM pipe: %s\n",
+  if (pipe (signal_pipe_fds) != 0)
+    g_printerr ("Failed to create signal pipe: %s\n",
                 g_strerror (errno));
 
-  channel = g_io_channel_unix_new (sigterm_pipe_fds[0]);
+  channel = g_io_channel_unix_new (signal_pipe_fds[0]);
   g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
-  g_io_add_watch (channel, G_IO_IN, (GIOFunc) on_sigterm, NULL);
+  g_io_add_watch (channel, G_IO_IN, (GIOFunc) on_signal, NULL);
   g_io_channel_set_close_on_unref (channel, TRUE);
   g_io_channel_unref (channel);
 
-  act.sa_handler = &sigterm_handler;
+  act.sa_handler = &signal_handler;
   if (sigaction (SIGTERM, &act, NULL) < 0)
     g_printerr ("Failed to register SIGTERM handler: %s\n",
                g_strerror (errno));
 
+  if (meta_is_wayland_compositor ())
+    {
+      if (sigaction (SIGCHLD, &act, NULL) < 0)
+        g_printerr ("Failed to register SIGCHLD handler: %s\n",
+                    g_strerror (errno));
+    }
+
   if (g_getenv ("MUTTER_VERBOSE"))
     meta_set_verbose (TRUE);
   if (g_getenv ("MUTTER_DEBUG"))
@@ -427,9 +483,18 @@ meta_init (void)
   g_irepository_prepend_search_path (MUTTER_PKGLIBDIR);
 #endif
 
-  meta_set_syncing (opt_sync || (g_getenv ("MUTTER_SYNC") != NULL));
+#ifdef HAVE_WAYLAND
+  if (meta_is_wayland_compositor ())
+    {
+      /* NB: When running as a hybrid wayland compositor we run our own headless X
+       * server so the user can't control the X display to connect too. */
+      meta_wayland_init ();
+    }
+  else
+#endif
+    meta_select_display (opt_display_name);
 
-  meta_select_display (opt_display_name);
+  meta_set_syncing (opt_sync || (g_getenv ("MUTTER_SYNC") != NULL));
   
   if (opt_replace_wm)
     meta_set_replace_current_wm (TRUE);
@@ -441,10 +506,17 @@ meta_init (void)
   
   meta_ui_init ();
 
-  /*
-   * Clutter can only be initialized after the UI.
-   */
-  meta_clutter_init ();
+  /* If we are running with wayland then we don't wait until we have
+   * an X connection before initializing clutter we instead initialize
+   * it earlier since we need to initialize the GL driver so the driver
+   * can register any needed wayland extensions. */
+  if (!meta_is_wayland_compositor ())
+    {
+      /*
+       * Clutter can only be initialized after the UI.
+       */
+      meta_clutter_init ();
+    }
 }
 
 /**
diff --git a/src/core/screen-private.h b/src/core/screen-private.h
index 83adb83..7e8a133 100644
--- a/src/core/screen-private.h
+++ b/src/core/screen-private.h
@@ -257,4 +257,6 @@ void     meta_screen_workspace_switched (MetaScreen         *screen,
 
 void meta_screen_set_active_workspace_hint (MetaScreen *screen);
 
+Window   meta_screen_create_guard_window (Display *xdisplay, MetaScreen *screen);
+
 #endif
diff --git a/src/core/screen.c b/src/core/screen.c
index 6db3ea3..e3c997e 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -45,6 +45,9 @@
 #include <meta/compositor.h>
 #include "mutter-enum-types.h"
 #include "core.h"
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#endif
 
 #include <X11/extensions/Xinerama.h>
 
@@ -603,8 +606,8 @@ reload_monitor_infos (MetaScreen *screen)
  * should effectively be forwarded to events on the background actor,
  * providing that the scene graph is set up correctly.
  */
-static Window
-create_guard_window (Display *xdisplay, MetaScreen *screen)
+Window
+meta_screen_create_guard_window (Display *xdisplay, MetaScreen *screen)
 {
   XSetWindowAttributes attributes;
   Window guard_window;
@@ -668,6 +671,9 @@ meta_screen_new (MetaDisplay *display,
   char buf[128];
   guint32 manager_timestamp;
   gulong current_workspace;
+#ifdef HAVE_WAYLAND
+  MetaWaylandCompositor *compositor;
+#endif
   
   replace_current_wm = meta_get_replace_current_wm ();
   
@@ -826,8 +832,21 @@ meta_screen_new (MetaDisplay *display,
   screen->xscreen = ScreenOfDisplay (xdisplay, number);
   screen->xroot = xroot;
   screen->rect.x = screen->rect.y = 0;
-  screen->rect.width = WidthOfScreen (screen->xscreen);
-  screen->rect.height = HeightOfScreen (screen->xscreen);
+  
+#ifdef HAVE_WAYLAND
+  if (meta_is_wayland_compositor ())
+    {
+      compositor = meta_wayland_compositor_get_default ();
+      screen->rect.width = clutter_actor_get_width (compositor->stage);
+      screen->rect.height = clutter_actor_get_height (compositor->stage);
+    }
+  else
+#endif
+    {
+      screen->rect.width = WidthOfScreen (screen->xscreen);
+      screen->rect.height = HeightOfScreen (screen->xscreen);
+    }
+
   screen->current_cursor = -1; /* invalid/unset */
   screen->default_xvisual = DefaultVisualOfScreen (screen->xscreen);
   screen->default_depth = DefaultDepthOfScreen (screen->xscreen);
@@ -1082,8 +1101,8 @@ meta_screen_manage_all_windows (MetaScreen *screen)
   meta_display_grab (screen->display);
 
   if (screen->guard_window == None)
-    screen->guard_window = create_guard_window (screen->display->xdisplay,
-                                                screen);
+    screen->guard_window =
+      meta_screen_create_guard_window (screen->display->xdisplay, screen);
 
   windows = list_windows (screen);
 
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 42d380f..044d897 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -44,6 +44,17 @@
 #include <X11/Xutil.h>
 #include <cairo.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#endif
+
+/* XXX: We should find a nicer approach to deal with the
+ * circular dependency we have with the current headers
+ * (meta-wayland-private.h which typedefs MetaWaylandSurface
+ *  also includes window-private.h) */
+#ifndef HAVE_META_WAYLAND_SURFACE_TYPE
+typedef struct _MetaWaylandSurface MetaWaylandSurface;
+#endif
 
 typedef struct _MetaWindowQueue MetaWindowQueue;
 
@@ -69,6 +80,11 @@ typedef enum {
   _NET_WM_BYPASS_COMPOSITOR_HINT_OFF = 2,
 } MetaBypassCompositorHintValue;
 
+typedef enum {
+  META_WINDOW_CLIENT_TYPE_WAYLAND,
+  META_WINDOW_CLIENT_TYPE_X11
+} MetaWindowClientType;
+
 struct _MetaWindow
 {
   GObject parent_instance;
@@ -77,6 +93,10 @@ struct _MetaWindow
   MetaScreen *screen;
   const MetaMonitorInfo *monitor;
   MetaWorkspace *workspace;
+  MetaWindowClientType client_type;
+#ifdef HAVE_WAYLAND
+  MetaWaylandSurface *surface;
+#endif
   Window xwindow;
   /* may be NULL! not all windows get decorated */
   MetaFrame *frame;
@@ -489,6 +509,10 @@ MetaWindow* meta_window_new_with_attrs     (MetaDisplay       *display,
                                             gboolean           must_be_viewable,
                                             MetaCompEffect     effect,
                                             XWindowAttributes *attrs);
+MetaWindow *meta_window_new_for_wayland    (MetaDisplay        *display,
+                                            int                 width,
+                                            int                 height,
+                                            MetaWaylandSurface *surface);
 void        meta_window_unmanage           (MetaWindow  *window,
                                             guint32      timestamp);
 void        meta_window_calc_showing       (MetaWindow  *window);
diff --git a/src/core/window.c b/src/core/window.c
index faa9c77..df53528 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -713,10 +713,10 @@ meta_window_new (MetaDisplay *display,
  * Returns TRUE if window has been filtered out and should be ignored.
  */
 static gboolean
-maybe_filter_window (MetaDisplay       *display,
-                     Window             xwindow,
-                     gboolean           must_be_viewable,
-                     XWindowAttributes *attrs)
+maybe_filter_xwindow (MetaDisplay       *display,
+                      Window             xwindow,
+                      gboolean           must_be_viewable,
+                      XWindowAttributes *attrs)
 {
   static char **filter_wm_classes = NULL;
   static gboolean initialized = FALSE;
@@ -812,81 +812,25 @@ meta_window_should_attach_to_parent (MetaWindow *window)
     }
 }
 
-MetaWindow*
-meta_window_new_with_attrs (MetaDisplay       *display,
-                            Window             xwindow,
-                            gboolean           must_be_viewable,
-                            MetaCompEffect     effect,
-                            XWindowAttributes *attrs)
+static MetaWindow*
+meta_window_new_shared (MetaDisplay         *display,
+                        MetaScreen          *screen,
+                        MetaWindowClientType client_type,
+                        MetaWaylandSurface  *surface,
+                        Window               xwindow,
+                        gboolean             must_be_viewable,
+                        gulong               existing_wm_state,
+                        gboolean             has_shape,
+                        gboolean             has_input_shape,
+                        MetaCompEffect       effect,
+                        XWindowAttributes   *attrs)
 {
   MetaWindow *window;
-  GSList *tmp;
   MetaWorkspace *space;
-  gulong existing_wm_state;
-  gulong event_mask;
   MetaMoveResizeFlags flags;
-  gboolean has_shape;
-  gboolean has_input_shape;
-  MetaScreen *screen;
 
   g_assert (attrs != NULL);
 
-  meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
-
-  if (meta_display_xwindow_is_a_no_focus_window (display, xwindow))
-    {
-      meta_verbose ("Not managing no_focus_window 0x%lx\n",
-                    xwindow);
-      return NULL;
-    }
-
-  screen = NULL;
-  for (tmp = display->screens; tmp != NULL; tmp = tmp->next)
-    {
-      MetaScreen *scr = tmp->data;
-
-      if (scr->xroot == attrs->root)
-        {
-          screen = tmp->data;
-          break;
-        }
-    }
-
-  g_assert (screen);
-
-  /* A black list of override redirect windows that we don't need to manage: */
-  if (attrs->override_redirect &&
-      (xwindow == screen->no_focus_window ||
-       xwindow == screen->flash_window ||
-       xwindow == screen->wm_sn_selection_window ||
-       attrs->class == InputOnly ||
-       /* any windows created via meta_create_offscreen_window: */
-       (attrs->x == -100 && attrs->y == -100
-       && attrs->width == 1 && attrs->height == 1) ||
-       xwindow == screen->wm_cm_selection_window ||
-       xwindow == screen->guard_window ||
-       (display->compositor &&
-        xwindow == XCompositeGetOverlayWindow (display->xdisplay,
-                                              screen->xroot)
-       )
-      )
-     ) {
-    meta_verbose ("Not managing our own windows\n");
-    return NULL;
-  }
-
-  if (maybe_filter_window (display, xwindow, must_be_viewable, attrs))
-    {
-      meta_verbose ("Not managing filtered window\n");
-      return NULL;
-    }
-
-  /* Grab server */
-  meta_display_grab (display);
-  meta_error_trap_push (display); /* Push a trap over all of window
-                                   * creation, to reduce XSync() calls
-                                   */
-
   meta_verbose ("must_be_viewable = %d attrs->map_state = %d (%s)\n",
                 must_be_viewable,
                 attrs->map_state,
@@ -898,150 +842,16 @@ meta_window_new_with_attrs (MetaDisplay       *display,
                 "IsUnviewable" :
                 "(unknown)");
 
-  existing_wm_state = WithdrawnState;
-  if (must_be_viewable && attrs->map_state != IsViewable)
-    {
-      /* Only manage if WM_STATE is IconicState or NormalState */
-      gulong state;
-
-      /* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
-      if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
-                                                   display->atom_WM_STATE,
-                                                   display->atom_WM_STATE,
-                                                   &state) &&
-            (state == IconicState || state == NormalState)))
-        {
-          meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
-          meta_error_trap_pop (display);
-          meta_display_ungrab (display);
-          return NULL;
-        }
-
-      existing_wm_state = state;
-      meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
-                    wm_state_to_string (existing_wm_state));
-    }
-
-  meta_error_trap_push_with_return (display);
-
-  /*
-   * XAddToSaveSet can only be called on windows created by a different client.
-   * with Mutter we want to be able to create manageable windows from within
-   * the process (such as a dummy desktop window), so we do not want this
-   * call failing to prevent the window from being managed -- wrap it in its
-   * own error trap (we use the _with_return() version here to ensure that
-   * XSync() is done on the pop, otherwise the error will not get caught).
-   */
-  meta_error_trap_push_with_return (display);
-  XAddToSaveSet (display->xdisplay, xwindow);
-  meta_error_trap_pop_with_return (display);
-
-  event_mask = PropertyChangeMask | ColormapChangeMask;
-  if (attrs->override_redirect)
-    event_mask |= StructureNotifyMask;
-
-  /* If the window is from this client (a menu, say) we need to augment
-   * the event mask, not replace it. For windows from other clients,
-   * attrs->your_event_mask will be empty at this point.
-   */
-  XSelectInput (display->xdisplay, xwindow, attrs->your_event_mask | event_mask);
-
-  {
-    unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
-    XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
-
-    meta_core_add_old_event_mask (display->xdisplay, xwindow, &mask);
-
-    XISetMask (mask.mask, XI_Enter);
-    XISetMask (mask.mask, XI_Leave);
-    XISetMask (mask.mask, XI_FocusIn);
-    XISetMask (mask.mask, XI_FocusOut);
-
-    XISelectEvents (display->xdisplay, xwindow, &mask, 1);
-  }
-
-  has_shape = FALSE;
-  has_input_shape = FALSE;
-#ifdef HAVE_SHAPE
-  if (META_DISPLAY_HAS_SHAPE (display))
-    {
-      int x_bounding, y_bounding, x_clip, y_clip;
-      unsigned w_bounding, h_bounding, w_clip, h_clip;
-      int bounding_shaped, clip_shaped;
-      XRectangle *input_rectangles;
-      int n_rects, ordering;
-
-      XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
-
-      XShapeQueryExtents (display->xdisplay, xwindow,
-                          &bounding_shaped, &x_bounding, &y_bounding,
-                          &w_bounding, &h_bounding,
-                          &clip_shaped, &x_clip, &y_clip,
-                          &w_clip, &h_clip);
-
-      has_shape = bounding_shaped != FALSE;
-
-      /* XXX: The x shape extension doesn't provide a way to only test if an
-       * input shape has been specified, so we have to query and throw away the
-       * rectangles. */
-      meta_error_trap_push (display);
-      input_rectangles = XShapeGetRectangles (display->xdisplay, xwindow,
-                                              ShapeInput, &n_rects, &ordering);
-      meta_error_trap_pop (display);
-      if (input_rectangles)
-        {
-          if (n_rects > 1 ||
-              (n_rects == 1 &&
-               (input_rectangles[0].x != x_bounding ||
-                input_rectangles[1].y != y_bounding ||
-                input_rectangles[2].width != w_bounding ||
-                input_rectangles[3].height != h_bounding)))
-            {
-              has_input_shape = TRUE;
-            }
-          XFree (input_rectangles);
-        }
-
-      meta_topic (META_DEBUG_SHAPES,
-                  "Window has_shape = %d extents %d,%d %u x %u\n",
-                  has_shape, x_bounding, y_bounding,
-                  w_bounding, h_bounding);
-    }
-#endif
-
-  /* Get rid of any borders */
-  if (attrs->border_width != 0)
-    XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
-
-  /* Get rid of weird gravities */
-  if (attrs->win_gravity != NorthWestGravity)
-    {
-      XSetWindowAttributes set_attrs;
-
-      set_attrs.win_gravity = NorthWestGravity;
-
-      XChangeWindowAttributes (display->xdisplay,
-                               xwindow,
-                               CWWinGravity,
-                               &set_attrs);
-    }
-
-  if (meta_error_trap_pop_with_return (display) != Success)
-    {
-      meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
-                    xwindow);
-      meta_error_trap_pop (display);
-      meta_display_ungrab (display);
-      return NULL;
-    }
-
-
   window = g_object_new (META_TYPE_WINDOW, NULL);
 
   window->constructing = TRUE;
 
   window->dialog_pid = -1;
 
+  window->client_type = client_type;
+#ifdef HAVE_WAYLAND
+  window->surface = surface;
+#endif
   window->xwindow = xwindow;
 
   /* this is in window->screen->display, but that's too annoying to
@@ -1168,7 +978,11 @@ meta_window_new_with_attrs (MetaDisplay       *display,
   window->mwm_has_move_func = TRUE;
   window->mwm_has_resize_func = TRUE;
 
-  window->decorated = TRUE;
+  if (client_type == META_WINDOW_CLIENT_TYPE_X11)
+    window->decorated = TRUE;
+  else
+    window->decorated = FALSE;
+
   window->has_close_func = TRUE;
   window->has_minimize_func = TRUE;
   window->has_maximize_func = TRUE;
@@ -1227,23 +1041,26 @@ meta_window_new_with_attrs (MetaDisplay       *display,
 
   window->tile_match = NULL;
 
-  if (window->override_redirect)
-    {
-      window->decorated = FALSE;
-      window->always_sticky = TRUE;
-      window->has_close_func = FALSE;
-      window->has_shade_func = FALSE;
-      window->has_move_func = FALSE;
-      window->has_resize_func = FALSE;
-    }
-
-  meta_display_register_x_window (display, &window->xwindow, window);
-
   /* Assign this #MetaWindow a sequence number which can be used
    * for sorting.
    */
   window->stable_sequence = ++display->window_sequence_counter;
 
+  if (client_type == META_WINDOW_CLIENT_TYPE_X11)
+    {
+      if (window->override_redirect)
+        {
+          window->decorated = FALSE;
+          window->always_sticky = TRUE;
+          window->has_close_func = FALSE;
+          window->has_shade_func = FALSE;
+          window->has_move_func = FALSE;
+          window->has_resize_func = FALSE;
+        }
+
+      meta_display_register_x_window (display, &window->xwindow, window);
+    }
+
   /* assign the window to its group, or create a new group if needed
    */
   window->group = NULL;
@@ -1252,7 +1069,8 @@ meta_window_new_with_attrs (MetaDisplay       *display,
 
   meta_window_load_initial_properties (window);
 
-  if (!window->override_redirect)
+  if (!window->override_redirect &&
+      client_type == META_WINDOW_CLIENT_TYPE_X11)
     {
       update_sm_hints (window); /* must come after transient_for */
 
@@ -1526,11 +1344,14 @@ meta_window_new_with_attrs (MetaDisplay       *display,
       !window->initially_iconic)
     unminimize_window_and_all_transient_parents (window);
 
-  meta_error_trap_pop (display); /* pop the XSync()-reducing trap */
-  meta_display_ungrab (display);
-
   window->constructing = FALSE;
 
+  return window;
+}
+
+static void
+display_notify_window (MetaDisplay *display, MetaWindow *window)
+{
   meta_display_notify_window_created (display, window);
 
   if (window->wm_state_demands_attention)
@@ -1538,6 +1359,309 @@ meta_window_new_with_attrs (MetaDisplay       *display,
 
   if (window->wm_hints_urgent)
     g_signal_emit_by_name (window->display, "window-marked-urgent", window);
+}
+
+#ifdef HAVE_WAYLAND
+MetaWindow *
+meta_window_new_for_wayland (MetaDisplay        *display,
+                             int                 width,
+                             int                 height,
+                             MetaWaylandSurface *surface)
+{
+  XWindowAttributes attrs;
+  MetaScreen *scr = display->screens->data;
+  MetaWindow *window;
+
+  attrs.x = 0;
+  attrs.y = 0;
+  attrs.width = width;
+  attrs.height = height;
+  attrs.border_width = 0;
+  attrs.depth = 24;
+  attrs.visual = NULL;
+  attrs.root = scr->xroot;
+  attrs.class = InputOutput;
+  attrs.bit_gravity = NorthWestGravity;
+  attrs.win_gravity = NorthWestGravity;
+  attrs.backing_store = 0;
+  attrs.backing_planes = ~0;
+  attrs.backing_pixel = 0;
+  attrs.save_under = 0;
+  attrs.colormap = 0;
+  attrs.map_installed = 1;
+  attrs.map_state = IsUnmapped;
+  attrs.all_event_masks = ~0;
+  attrs.your_event_mask = 0;
+  attrs.do_not_propagate_mask = 0;
+  attrs.override_redirect = 0;
+  attrs.screen = scr->xscreen;
+
+  /* XXX: Note: In the Wayland case we currently still grab the
+   * xserver and trap X errors while creating a MetaWindow because we
+   * will still be making various redundant X requests (passing a
+   * window xid of None) until we thoroughly audit all the code to
+   * make sure it knows about non X based clients...
+   */
+
+  /* Grab server */
+  meta_display_grab (display);
+  meta_error_trap_push (display); /* Push a trap over all of window
+                                   * creation, to reduce XSync() calls
+                                   */
+
+  window = meta_window_new_shared (display,
+                                   scr,
+                                   META_WINDOW_CLIENT_TYPE_WAYLAND,
+                                   surface,
+                                   None,
+                                   TRUE,
+                                   WithdrawnState,
+                                   FALSE, /* has shape */
+                                   FALSE, /* has input shape */
+                                   META_COMP_EFFECT_NONE,
+                                   &attrs);
+
+  meta_error_trap_pop (display); /* pop the XSync()-reducing trap */
+  meta_display_ungrab (display);
+
+  /* XXX: Maybe this could be called in meta_window_new_shared() but
+   * before splitting the X11 specific code out it came after the
+   * meta_display_ungrab() and we wanted to minimize the risk of
+   * breaking something.
+   */
+  display_notify_window (window->display, window);
+
+  return window;
+}
+#endif
+
+MetaWindow*
+meta_window_new_with_attrs (MetaDisplay       *display,
+                            Window             xwindow,
+                            gboolean           must_be_viewable,
+                            MetaCompEffect     effect,
+                            XWindowAttributes *attrs)
+{
+  MetaScreen *screen = NULL;
+  GSList *tmp;
+  gulong existing_wm_state;
+  MetaWindow *window;
+  gulong event_mask;
+  gboolean has_shape = FALSE;
+  gboolean has_input_shape = FALSE;
+
+  meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
+
+  if (meta_display_xwindow_is_a_no_focus_window (display, xwindow))
+    {
+      meta_verbose ("Not managing no_focus_window 0x%lx\n",
+                    xwindow);
+      return NULL;
+    }
+
+  for (tmp = display->screens; tmp != NULL; tmp = tmp->next)
+    {
+      MetaScreen *scr = tmp->data;
+
+      if (scr->xroot == attrs->root)
+        {
+          screen = tmp->data;
+          break;
+        }
+    }
+
+  g_assert (screen);
+
+  /* A black list of override redirect windows that we don't need to manage: */
+  if (attrs->override_redirect &&
+      (xwindow == screen->no_focus_window ||
+       xwindow == screen->flash_window ||
+       xwindow == screen->wm_sn_selection_window ||
+       attrs->class == InputOnly ||
+       /* any windows created via meta_create_offscreen_window: */
+       (attrs->x == -100 && attrs->y == -100
+       && attrs->width == 1 && attrs->height == 1) ||
+       xwindow == screen->wm_cm_selection_window ||
+       xwindow == screen->guard_window ||
+       (display->compositor &&
+        xwindow == XCompositeGetOverlayWindow (display->xdisplay,
+                                              screen->xroot)
+       )
+      )
+     ) {
+    meta_verbose ("Not managing our own windows\n");
+    return NULL;
+  }
+
+  if (maybe_filter_xwindow (display, xwindow, must_be_viewable, attrs))
+    {
+      meta_verbose ("Not managing filtered window\n");
+      return NULL;
+    }
+
+  /* Grab server */
+  meta_display_grab (display);
+  meta_error_trap_push (display); /* Push a trap over all of window
+                                   * creation, to reduce XSync() calls
+                                   */
+
+  existing_wm_state = WithdrawnState;
+  if (must_be_viewable && attrs->map_state != IsViewable)
+    {
+      /* Only manage if WM_STATE is IconicState or NormalState */
+      gulong state;
+
+      /* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
+      if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
+                                                   display->atom_WM_STATE,
+                                                   display->atom_WM_STATE,
+                                                   &state) &&
+            (state == IconicState || state == NormalState)))
+        {
+          meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
+          meta_error_trap_pop (display);
+          meta_display_ungrab (display);
+          return NULL;
+        }
+
+      existing_wm_state = state;
+      meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
+                    wm_state_to_string (existing_wm_state));
+    }
+
+  meta_error_trap_push_with_return (display);
+
+  /*
+   * XAddToSaveSet can only be called on windows created by a different
+   * client.  with Mutter we want to be able to create manageable windows
+   * from within the process (such as a dummy desktop window), so we do not
+   * want this call failing to prevent the window from being managed -- wrap
+   * it in its own error trap (we use the _with_return() version here to
+   * ensure that XSync() is done on the pop, otherwise the error will not
+   * get caught).
+   */
+  meta_error_trap_push_with_return (display);
+  XAddToSaveSet (display->xdisplay, xwindow);
+  meta_error_trap_pop_with_return (display);
+
+  event_mask = PropertyChangeMask | ColormapChangeMask;
+  if (attrs->override_redirect)
+    event_mask |= StructureNotifyMask;
+
+  /* If the window is from this client (a menu, say) we need to augment
+   * the event mask, not replace it. For windows from other clients,
+   * attrs->your_event_mask will be empty at this point.
+   */
+  XSelectInput (display->xdisplay, xwindow, attrs->your_event_mask | event_mask);
+
+    {
+      unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
+      XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
+
+      meta_core_add_old_event_mask (display->xdisplay, xwindow, &mask);
+
+      XISetMask (mask.mask, XI_Enter);
+      XISetMask (mask.mask, XI_Leave);
+      XISetMask (mask.mask, XI_FocusIn);
+      XISetMask (mask.mask, XI_FocusOut);
+
+      XISelectEvents (display->xdisplay, xwindow, &mask, 1);
+    }
+
+#ifdef HAVE_SHAPE
+  if (META_DISPLAY_HAS_SHAPE (display))
+    {
+      int x_bounding, y_bounding, x_clip, y_clip;
+      unsigned w_bounding, h_bounding, w_clip, h_clip;
+      int bounding_shaped, clip_shaped;
+      XRectangle *input_rectangles;
+      int n_rects, ordering;
+
+      XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
+
+      XShapeQueryExtents (display->xdisplay, xwindow,
+                          &bounding_shaped, &x_bounding, &y_bounding,
+                          &w_bounding, &h_bounding,
+                          &clip_shaped, &x_clip, &y_clip,
+                          &w_clip, &h_clip);
+
+      has_shape = bounding_shaped != FALSE;
+
+      /* XXX: The x shape extension doesn't provide a way to only test if an
+       * input shape has been specified, so we have to query and throw away the
+       * rectangles. */
+      meta_error_trap_push (display);
+      input_rectangles = XShapeGetRectangles (display->xdisplay, xwindow,
+                                              ShapeInput, &n_rects, &ordering);
+      meta_error_trap_pop (display);
+      if (input_rectangles)
+        {
+          if (n_rects > 1 ||
+              (n_rects == 1 &&
+               (input_rectangles[0].x != x_bounding ||
+                input_rectangles[1].y != y_bounding ||
+                input_rectangles[2].width != w_bounding ||
+                input_rectangles[3].height != h_bounding)))
+            {
+              has_input_shape = TRUE;
+            }
+          XFree (input_rectangles);
+        }
+
+      meta_topic (META_DEBUG_SHAPES,
+                  "Window has_shape = %d extents %d,%d %u x %u\n",
+                  has_shape, x_bounding, y_bounding,
+                  w_bounding, h_bounding);
+    }
+#endif
+
+  /* Get rid of any borders */
+  if (attrs->border_width != 0)
+    XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
+
+  /* Get rid of weird gravities */
+  if (attrs->win_gravity != NorthWestGravity)
+    {
+      XSetWindowAttributes set_attrs;
+
+      set_attrs.win_gravity = NorthWestGravity;
+
+      XChangeWindowAttributes (display->xdisplay,
+                               xwindow,
+                               CWWinGravity,
+                               &set_attrs);
+    }
+
+  if (meta_error_trap_pop_with_return (display) != Success)
+    {
+      meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
+                    xwindow);
+      meta_error_trap_pop (display);
+      meta_display_ungrab (display);
+      return NULL;
+    }
+
+  window = meta_window_new_shared (display,
+                                   screen,
+                                   META_WINDOW_CLIENT_TYPE_X11,
+                                   NULL,
+                                   xwindow,
+                                   must_be_viewable,
+                                   existing_wm_state,
+                                   has_shape,
+                                   has_input_shape,
+                                   effect,
+                                   attrs);
+
+  meta_error_trap_pop (display); /* pop the XSync()-reducing trap */
+  meta_display_ungrab (display);
+
+  /* XXX: Maybe this could be called in meta_window_new_shared() but
+   * before splitting the X11 specific code out it came after the
+   * meta_display_ungrab() and we wanted to minimize the risk of
+   * breaking something.
+   */
+  display_notify_window (display, window);
 
   return window;
 }
@@ -1914,48 +2038,50 @@ meta_window_unmanage (MetaWindow  *window,
   meta_display_ungrab_window_buttons (window->display, window->xwindow);
   meta_display_ungrab_focus_window_button (window->display, window);
 
-  meta_display_unregister_x_window (window->display, window->xwindow);
-
+  if (window->client_type == META_WINDOW_CLIENT_TYPE_X11)
+    {
+      meta_display_unregister_x_window (window->display, window->xwindow);
 
-  meta_error_trap_push (window->display);
+      meta_error_trap_push (window->display);
 
-  /* Put back anything we messed up */
-  if (window->border_width != 0)
-    XSetWindowBorderWidth (window->display->xdisplay,
-                           window->xwindow,
-                           window->border_width);
-
-  /* No save set */
-  XRemoveFromSaveSet (window->display->xdisplay,
-                      window->xwindow);
-
-  /* Even though the window is now unmanaged, we can't unselect events. This
-   * window might be a window from this process, like a GdkMenu, in
-   * which case it will have pointer events and so forth selected
-   * for it by GDK. There's no way to disentangle those events from the events
-   * we've selected. Even for a window from a different X client,
-   * GDK could also have selected events for it for IPC purposes, so we
-   * can't unselect in that case either.
-   *
-   * Similarly, we can't unselected for events on window->user_time_window.
-   * It might be our own GDK focus window, or it might be a window that a
-   * different client is using for multiple different things:
-   * _NET_WM_USER_TIME_WINDOW and IPC, perhaps.
-   */
+      /* Put back anything we messed up */
+      if (window->border_width != 0)
+        XSetWindowBorderWidth (window->display->xdisplay,
+                               window->xwindow,
+                               window->border_width);
+
+      /* No save set */
+      XRemoveFromSaveSet (window->display->xdisplay,
+                          window->xwindow);
+
+      /* Even though the window is now unmanaged, we can't unselect events. This
+       * window might be a window from this process, like a GdkMenu, in
+       * which case it will have pointer events and so forth selected
+       * for it by GDK. There's no way to disentangle those events from the events
+       * we've selected. Even for a window from a different X client,
+       * GDK could also have selected events for it for IPC purposes, so we
+       * can't unselect in that case either.
+       *
+       * Similarly, we can't unselected for events on window->user_time_window.
+       * It might be our own GDK focus window, or it might be a window that a
+       * different client is using for multiple different things:
+       * _NET_WM_USER_TIME_WINDOW and IPC, perhaps.
+       */
 
-  if (window->user_time_window != None)
-    {
-      meta_display_unregister_x_window (window->display,
-                                        window->user_time_window);
-      window->user_time_window = None;
-    }
+      if (window->user_time_window != None)
+        {
+          meta_display_unregister_x_window (window->display,
+                                            window->user_time_window);
+          window->user_time_window = None;
+        }
 
 #ifdef HAVE_SHAPE
-  if (META_DISPLAY_HAS_SHAPE (window->display))
-    XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask);
+      if (META_DISPLAY_HAS_SHAPE (window->display))
+        XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask);
 #endif
 
-  meta_error_trap_pop (window->display);
+      meta_error_trap_pop (window->display);
+    }
 
   meta_prefs_remove_listener (prefs_changed_callback, window);
 
@@ -7697,7 +7823,7 @@ meta_window_update_opaque_region (MetaWindow *window)
   meta_XFree (region);
 
   if (window->display->compositor)
-    meta_compositor_window_shape_changed (window->display->compositor, window);
+    meta_compositor_window_x11_shape_changed (window->display->compositor, window);
 }
 
 static void
diff --git a/src/meta/compositor.h b/src/meta/compositor.h
index 13143c9..de81c20 100644
--- a/src/meta/compositor.h
+++ b/src/meta/compositor.h
@@ -64,8 +64,8 @@ void meta_compositor_manage_screen   (MetaCompositor *compositor,
 void meta_compositor_unmanage_screen (MetaCompositor *compositor,
                                       MetaScreen     *screen);
 
-void meta_compositor_window_shape_changed (MetaCompositor *compositor,
-                                           MetaWindow     *window);
+void meta_compositor_window_x11_shape_changed (MetaCompositor *compositor,
+                                               MetaWindow     *window);
 
 gboolean meta_compositor_process_event (MetaCompositor *compositor,
                                         XEvent         *event,
diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h
index 2e12284..2809b25 100644
--- a/src/meta/meta-shaped-texture.h
+++ b/src/meta/meta-shaped-texture.h
@@ -29,6 +29,11 @@
 #include <clutter/clutter.h>
 #include <X11/Xlib.h>
 
+#ifdef HAVE_WAYLAND
+#include <wayland-server.h>
+#include "meta-wayland-private.h"
+#endif
+
 G_BEGIN_DECLS
 
 #define META_TYPE_SHAPED_TEXTURE            (meta_shaped_texture_get_type())
@@ -64,7 +69,13 @@ struct _MetaShapedTexture
 
 GType meta_shaped_texture_get_type (void) G_GNUC_CONST;
 
-ClutterActor *meta_shaped_texture_new (void);
+ClutterActor *meta_shaped_texture_new_with_xwindow (Window xwindow);
+#ifdef HAVE_WAYLAND
+ClutterActor *meta_shaped_texture_new_with_wayland_surface  (MetaWaylandSurface *surface);
+void meta_shaped_texture_set_wayland_surface                (MetaShapedTexture  *stex,
+                                                             MetaWaylandSurface *surface);
+MetaWaylandSurface *meta_shaped_texture_get_wayland_surface (MetaShapedTexture *stex);
+#endif
 
 void meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
                                             gboolean           create_mipmaps);
@@ -77,6 +88,10 @@ void meta_shaped_texture_update_area (MetaShapedTexture *stex,
 
 void meta_shaped_texture_set_pixmap (MetaShapedTexture *stex,
                                      Pixmap             pixmap);
+#ifdef HAVE_WAYLAND
+void meta_shaped_texture_attach_wayland_buffer (MetaShapedTexture  *stex,
+                                                MetaWaylandBuffer  *buffer);
+#endif
 
 CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex);
 
diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h
new file mode 100644
index 0000000..59cc6d6
--- /dev/null
+++ b/src/wayland/meta-wayland-private.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_WAYLAND_PRIVATE_H
+#define META_WAYLAND_PRIVATE_H
+
+#include <wayland-server.h>
+
+#include <clutter/clutter.h>
+
+#include <glib.h>
+#include <cairo.h>
+
+#include "window-private.h"
+
+typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
+
+typedef struct
+{
+  struct wl_resource *resource;
+  struct wl_signal destroy_signal;
+  struct wl_listener destroy_listener;
+
+  union
+  {
+    struct wl_shm_buffer *shm_buffer;
+    struct wl_buffer *legacy_buffer;
+  };
+
+  int32_t width, height;
+  uint32_t busy_count;
+} MetaWaylandBuffer;
+
+typedef struct
+{
+  MetaWaylandBuffer *buffer;
+  struct wl_listener destroy_listener;
+} MetaWaylandBufferReference;
+
+typedef struct
+{
+  struct wl_resource *resource;
+  cairo_region_t *region;
+} MetaWaylandRegion;
+
+struct _MetaWaylandSurface
+{
+  struct wl_resource *resource;
+  MetaWaylandCompositor *compositor;
+  guint32 xid;
+  int x;
+  int y;
+  MetaWaylandBufferReference buffer_ref;
+  MetaWindow *window;
+  gboolean has_shell_surface;
+
+  /* All the pending state, that wl_surface.commit will apply. */
+  struct
+  {
+    /* wl_surface.attach */
+    gboolean newly_attached;
+    MetaWaylandBuffer *buffer;
+    struct wl_listener buffer_destroy_listener;
+    int32_t sx;
+    int32_t sy;
+
+    /* wl_surface.damage */
+    cairo_region_t *damage;
+
+    /* wl_surface.frame */
+    struct wl_list frame_callback_list;
+  } pending;
+};
+
+#ifndef HAVE_META_WAYLAND_SURFACE_TYPE
+typedef struct _MetaWaylandSurface MetaWaylandSurface;
+#endif
+
+typedef struct
+{
+  MetaWaylandSurface *surface;
+  struct wl_resource *resource;
+  struct wl_listener surface_destroy_listener;
+} MetaWaylandShellSurface;
+
+typedef struct
+{
+  guint32 flags;
+  int width;
+  int height;
+  int refresh;
+} MetaWaylandMode;
+
+typedef struct
+{
+  struct wl_object wayland_output;
+  int x;
+  int y;
+  int width_mm;
+  int height_mm;
+  /* XXX: with sliced stages we'd reference a CoglFramebuffer here. */
+
+  GList *modes;
+} MetaWaylandOutput;
+
+typedef struct
+{
+  GSource source;
+  GPollFD pfd;
+  struct wl_display *display;
+} WaylandEventSource;
+
+typedef struct
+{
+  struct wl_list link;
+
+  /* Pointer back to the compositor */
+  MetaWaylandCompositor *compositor;
+
+  struct wl_resource *resource;
+} MetaWaylandFrameCallback;
+
+struct _MetaWaylandCompositor
+{
+  struct wl_display *wayland_display;
+  struct wl_event_loop *wayland_loop;
+  GMainLoop *init_loop;
+  ClutterActor *stage;
+  GList *outputs;
+  GSource *wayland_event_source;
+  GList *surfaces;
+  struct wl_list frame_callbacks;
+
+  int xwayland_display_index;
+  char *xwayland_lockfile;
+  int xwayland_abstract_fd;
+  int xwayland_unix_fd;
+  pid_t xwayland_pid;
+  struct wl_client *xwayland_client;
+  struct wl_resource *xserver_resource;
+  GHashTable *window_surfaces;
+};
+
+void                    meta_wayland_init                   (void);
+void                    meta_wayland_finalize               (void);
+
+/* We maintain a singleton MetaWaylandCompositor which can be got at via this
+ * API after meta_wayland_init() has been called. */
+MetaWaylandCompositor  *meta_wayland_compositor_get_default (void);
+
+void                    meta_wayland_handle_sig_child       (void);
+
+MetaWaylandSurface     *meta_wayland_lookup_surface_for_xid (guint32 xid);
+
+#endif /* META_WAYLAND_PRIVATE_H */
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
new file mode 100644
index 0000000..4374068
--- /dev/null
+++ b/src/wayland/meta-wayland.c
@@ -0,0 +1,1145 @@
+/*
+ * Wayland Support
+ *
+ * Copyright (C) 2012,2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <clutter/clutter.h>
+#include <clutter/wayland/clutter-wayland-compositor.h>
+#include <clutter/wayland/clutter-wayland-surface.h>
+
+#include <glib.h>
+#include <sys/time.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include <wayland-server.h>
+
+#include "xserver-server-protocol.h"
+
+#include "meta-wayland-private.h"
+#include "meta-xwayland-private.h"
+#include "meta-window-actor-private.h"
+#include "display-private.h"
+#include "window-private.h"
+#include <meta/types.h>
+#include <meta/main.h>
+#include "frame.h"
+
+static MetaWaylandCompositor _meta_wayland_compositor;
+
+MetaWaylandCompositor *
+meta_wayland_compositor_get_default (void)
+{
+  return &_meta_wayland_compositor;
+}
+
+static guint32
+get_time (void)
+{
+  struct timeval tv;
+  gettimeofday (&tv, NULL);
+  return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+static gboolean
+wayland_event_source_prepare (GSource *base, int *timeout)
+{
+  WaylandEventSource *source = (WaylandEventSource *)base;
+
+  *timeout = -1;
+
+  wl_display_flush_clients (source->display);
+
+  return FALSE;
+}
+
+static gboolean
+wayland_event_source_check (GSource *base)
+{
+  WaylandEventSource *source = (WaylandEventSource *)base;
+  return source->pfd.revents;
+}
+
+static gboolean
+wayland_event_source_dispatch (GSource *base,
+                               GSourceFunc callback,
+                               void *data)
+{
+  WaylandEventSource *source = (WaylandEventSource *)base;
+  struct wl_event_loop *loop = wl_display_get_event_loop (source->display);
+
+  wl_event_loop_dispatch (loop, 0);
+
+  return TRUE;
+}
+
+static GSourceFuncs wayland_event_source_funcs =
+{
+  wayland_event_source_prepare,
+  wayland_event_source_check,
+  wayland_event_source_dispatch,
+  NULL
+};
+
+static GSource *
+wayland_event_source_new (struct wl_display *display)
+{
+  WaylandEventSource *source;
+  struct wl_event_loop *loop = wl_display_get_event_loop (display);
+
+  source = (WaylandEventSource *) g_source_new (&wayland_event_source_funcs,
+                                                sizeof (WaylandEventSource));
+  source->display = display;
+  source->pfd.fd = wl_event_loop_get_fd (loop);
+  source->pfd.events = G_IO_IN | G_IO_ERR;
+  g_source_add_poll (&source->source, &source->pfd);
+
+  return &source->source;
+}
+
+static void
+meta_wayland_buffer_destroy_handler (struct wl_listener *listener,
+                                     void *data)
+{
+  MetaWaylandBuffer *buffer =
+    wl_container_of (listener, buffer, destroy_listener);
+
+  wl_signal_emit (&buffer->destroy_signal, buffer);
+  g_slice_free (MetaWaylandBuffer, buffer);
+}
+
+static MetaWaylandBuffer *
+meta_wayland_buffer_from_resource (struct wl_resource *resource)
+{
+  MetaWaylandBuffer *buffer;
+  struct wl_listener *listener;
+
+  listener =
+    wl_resource_get_destroy_listener (resource,
+                                      meta_wayland_buffer_destroy_handler);
+
+  if (listener)
+    {
+      buffer = wl_container_of (listener, buffer, destroy_listener);
+    }
+  else
+    {
+      buffer = g_slice_new0 (MetaWaylandBuffer);
+
+      buffer->resource = resource;
+      wl_signal_init (&buffer->destroy_signal);
+      buffer->destroy_listener.notify = meta_wayland_buffer_destroy_handler;
+      wl_resource_add_destroy_listener (resource, &buffer->destroy_listener);
+    }
+
+  return buffer;
+}
+
+static void
+meta_wayland_buffer_reference_handle_destroy (struct wl_listener *listener,
+                                          void *data)
+{
+  MetaWaylandBufferReference *ref =
+    wl_container_of (listener, ref, destroy_listener);
+
+  g_assert (data == ref->buffer);
+
+  ref->buffer = NULL;
+}
+
+static void
+meta_wayland_buffer_reference (MetaWaylandBufferReference *ref,
+                               MetaWaylandBuffer *buffer)
+{
+  if (ref->buffer && buffer != ref->buffer)
+    {
+      ref->buffer->busy_count--;
+
+      if (ref->buffer->busy_count == 0)
+        {
+          g_assert (wl_resource_get_client (ref->buffer->resource));
+          wl_resource_queue_event (ref->buffer->resource, WL_BUFFER_RELEASE);
+        }
+
+      wl_list_remove (&ref->destroy_listener.link);
+    }
+
+  if (buffer && buffer != ref->buffer)
+    {
+      buffer->busy_count++;
+      wl_signal_add (&buffer->destroy_signal, &ref->destroy_listener);
+    }
+
+  ref->buffer = buffer;
+  ref->destroy_listener.notify = meta_wayland_buffer_reference_handle_destroy;
+}
+
+static void
+surface_process_damage (MetaWaylandSurface *surface,
+                        cairo_region_t *region)
+{
+  if (surface->window &&
+      surface->buffer_ref.buffer)
+    {
+      MetaWindowActor *window_actor =
+        META_WINDOW_ACTOR (meta_window_get_compositor_private (surface->window));
+
+      if (window_actor)
+        {
+          int i, n_rectangles = cairo_region_num_rectangles (region);
+
+          for (i = 0; i < n_rectangles; i++)
+            {
+              cairo_rectangle_int_t rectangle;
+
+              cairo_region_get_rectangle (region, i, &rectangle);
+
+              meta_window_actor_process_wayland_damage (window_actor,
+                                                        rectangle.x,
+                                                        rectangle.y,
+                                                        rectangle.width,
+                                                        rectangle.height);
+            }
+        }
+    }
+}
+
+static void
+meta_wayland_surface_destroy (struct wl_client *wayland_client,
+                              struct wl_resource *wayland_resource)
+{
+  wl_resource_destroy (wayland_resource);
+}
+
+static void
+meta_wayland_surface_attach (struct wl_client *wayland_client,
+                             struct wl_resource *wayland_surface_resource,
+                             struct wl_resource *wayland_buffer_resource,
+                             gint32 sx, gint32 sy)
+{
+  MetaWaylandSurface *surface =
+    wl_resource_get_user_data (wayland_surface_resource);
+  MetaWaylandBuffer *buffer;
+
+  if (wayland_buffer_resource)
+    buffer = meta_wayland_buffer_from_resource (wayland_buffer_resource);
+  else
+    buffer = NULL;
+
+  /* Attach without commit in between does not send wl_buffer.release */
+  if (surface->pending.buffer)
+    wl_list_remove (&surface->pending.buffer_destroy_listener.link);
+
+  surface->pending.sx = sx;
+  surface->pending.sy = sy;
+  surface->pending.buffer = buffer;
+  surface->pending.newly_attached = TRUE;
+
+  if (buffer)
+    wl_signal_add (&buffer->destroy_signal,
+                   &surface->pending.buffer_destroy_listener);
+}
+
+static void
+meta_wayland_surface_damage (struct wl_client *client,
+                             struct wl_resource *surface_resource,
+                             gint32 x,
+                             gint32 y,
+                             gint32 width,
+                             gint32 height)
+{
+  MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
+  cairo_rectangle_int_t rectangle = { x, y, width, height };
+
+  cairo_region_union_rectangle (surface->pending.damage, &rectangle);
+}
+
+static void
+destroy_frame_callback (struct wl_resource *callback_resource)
+{
+  MetaWaylandFrameCallback *callback =
+    wl_resource_get_user_data (callback_resource);
+
+  wl_list_remove (&callback->link);
+  g_slice_free (MetaWaylandFrameCallback, callback);
+}
+
+static void
+meta_wayland_surface_frame (struct wl_client *client,
+                            struct wl_resource *surface_resource,
+                            guint32 callback_id)
+{
+  MetaWaylandFrameCallback *callback;
+  MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
+
+  callback = g_slice_new0 (MetaWaylandFrameCallback);
+  callback->compositor = surface->compositor;
+  callback->resource = wl_resource_create (client,
+                                          &wl_callback_interface, 1,
+                                          callback_id);
+  wl_resource_set_user_data (callback->resource, callback);
+  wl_resource_set_destructor (callback->resource, destroy_frame_callback);
+
+  wl_list_insert (surface->pending.frame_callback_list.prev, &callback->link);
+}
+
+static void
+meta_wayland_surface_set_opaque_region (struct wl_client *client,
+                                        struct wl_resource *resource,
+                                        struct wl_resource *region)
+{
+  g_warning ("TODO: support set_opaque_region request");
+}
+
+static void
+meta_wayland_surface_set_input_region (struct wl_client *client,
+                                       struct wl_resource *resource,
+                                       struct wl_resource *region)
+{
+  g_warning ("TODO: support set_input_region request");
+}
+
+static void
+empty_region (cairo_region_t *region)
+{
+  cairo_rectangle_int_t rectangle = { 0, 0, 0, 0 };
+  cairo_region_intersect_rectangle (region, &rectangle);
+}
+
+static void
+meta_wayland_surface_commit (struct wl_client *client,
+                             struct wl_resource *resource)
+{
+  MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
+  MetaWaylandCompositor *compositor = surface->compositor;
+
+  /* wl_surface.attach */
+  if (surface->pending.newly_attached &&
+      surface->buffer_ref.buffer != surface->pending.buffer)
+    {
+      /* Note: we set this before informing any window-actor since the
+       * window actor will expect to find the new buffer within the
+       * surface. */
+      meta_wayland_buffer_reference (&surface->buffer_ref,
+                                     surface->pending.buffer);
+
+      if (surface->pending.buffer)
+        {
+          MetaWaylandBuffer *buffer = surface->pending.buffer;
+
+          if (surface->window)
+            {
+              MetaWindow *window = surface->window;
+              MetaWindowActor *window_actor =
+                META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
+              MetaRectangle rect;
+
+              meta_window_get_input_rect (surface->window, &rect);
+
+              if (window_actor)
+                meta_window_actor_attach_wayland_buffer (window_actor, buffer);
+
+              /* XXX: we resize X based surfaces according to X events */
+              if (surface->xid == 0 &&
+                  (buffer->width != rect.width || buffer->height != rect.height))
+                meta_window_resize (surface->window, FALSE, buffer->width, buffer->height);
+            }
+        }
+    }
+
+  if (surface->pending.buffer)
+    {
+      wl_list_remove (&surface->pending.buffer_destroy_listener.link);
+      surface->pending.buffer = NULL;
+    }
+  surface->pending.sx = 0;
+  surface->pending.sy = 0;
+  surface->pending.newly_attached = FALSE;
+
+  surface_process_damage (surface, surface->pending.damage);
+  empty_region (surface->pending.damage);
+
+  /* wl_surface.frame */
+  wl_list_insert_list (&compositor->frame_callbacks,
+                       &surface->pending.frame_callback_list);
+  wl_list_init (&surface->pending.frame_callback_list);
+}
+
+static void
+meta_wayland_surface_set_buffer_transform (struct wl_client *client,
+                                           struct wl_resource *resource,
+                                           int32_t transform)
+{
+  g_warning ("TODO: support set_buffer_transform request");
+}
+
+static void
+meta_wayland_surface_set_buffer_scale (struct wl_client *client,
+                                       struct wl_resource *resource,
+                                       int scale)
+{
+  g_warning ("TODO: support set_buffer_scale request");
+}
+
+const struct wl_surface_interface meta_wayland_surface_interface = {
+  meta_wayland_surface_destroy,
+  meta_wayland_surface_attach,
+  meta_wayland_surface_damage,
+  meta_wayland_surface_frame,
+  meta_wayland_surface_set_opaque_region,
+  meta_wayland_surface_set_input_region,
+  meta_wayland_surface_commit,
+  meta_wayland_surface_set_buffer_transform,
+  meta_wayland_surface_set_buffer_scale
+};
+
+static void
+window_destroyed_cb (void *user_data, GObject *old_object)
+{
+  MetaWaylandSurface *surface = user_data;
+
+  surface->window = NULL;
+}
+
+static void
+meta_wayland_surface_free (MetaWaylandSurface *surface)
+{
+  MetaWaylandCompositor *compositor = surface->compositor;
+  MetaWaylandFrameCallback *cb, *next;
+
+  compositor->surfaces = g_list_remove (compositor->surfaces, surface);
+
+  meta_wayland_buffer_reference (&surface->buffer_ref, NULL);
+
+  if (surface->window)
+    g_object_weak_unref (G_OBJECT (surface->window),
+                         window_destroyed_cb,
+                         surface);
+
+  /* NB: If the surface corresponds to an X window then we will be
+   * sure to free the MetaWindow according to some X event. */
+  if (surface->window &&
+      surface->window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
+    {
+      MetaDisplay *display = meta_get_display ();
+      guint32 timestamp = meta_display_get_current_time_roundtrip (display);
+      meta_window_unmanage (surface->window, timestamp);
+    }
+
+  if (surface->pending.buffer)
+    wl_list_remove (&surface->pending.buffer_destroy_listener.link);
+
+  cairo_region_destroy (surface->pending.damage);
+
+  wl_list_for_each_safe (cb, next,
+                         &surface->pending.frame_callback_list, link)
+    wl_resource_destroy (cb->resource);
+
+  g_slice_free (MetaWaylandSurface, surface);
+}
+
+static void
+meta_wayland_surface_resource_destroy_cb (struct wl_resource *resource)
+{
+  MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
+  meta_wayland_surface_free (surface);
+}
+
+static void
+surface_handle_pending_buffer_destroy (struct wl_listener *listener,
+                                       void *data)
+{
+  MetaWaylandSurface *surface =
+    wl_container_of (listener, surface, pending.buffer_destroy_listener);
+
+  surface->pending.buffer = NULL;
+}
+
+static void
+meta_wayland_compositor_create_surface (struct wl_client *wayland_client,
+                                        struct wl_resource *wayland_compositor_resource,
+                                        guint32 id)
+{
+  MetaWaylandCompositor *compositor =
+    wl_resource_get_user_data (wayland_compositor_resource);
+  MetaWaylandSurface *surface = g_slice_new0 (MetaWaylandSurface);
+
+  surface->compositor = compositor;
+
+  /* a surface inherits the version from the compositor */
+  surface->resource = wl_resource_create (wayland_client,
+                                         &wl_surface_interface,
+                                         wl_resource_get_version (wayland_compositor_resource),
+                                         id);
+  wl_resource_set_implementation (surface->resource, &meta_wayland_surface_interface, surface,
+                                 meta_wayland_surface_resource_destroy_cb);
+
+  surface->pending.damage = cairo_region_create ();
+
+  surface->pending.buffer_destroy_listener.notify =
+    surface_handle_pending_buffer_destroy;
+  wl_list_init (&surface->pending.frame_callback_list);
+
+  compositor->surfaces = g_list_prepend (compositor->surfaces, surface);
+}
+
+static void
+meta_wayland_region_destroy (struct wl_client *client,
+                             struct wl_resource *resource)
+{
+  wl_resource_destroy (resource);
+}
+
+static void
+meta_wayland_region_add (struct wl_client *client,
+                         struct wl_resource *resource,
+                         gint32 x,
+                         gint32 y,
+                         gint32 width,
+                         gint32 height)
+{
+  MetaWaylandRegion *region = wl_resource_get_user_data (resource);
+  cairo_rectangle_int_t rectangle = { x, y, width, height };
+
+  cairo_region_union_rectangle (region->region, &rectangle);
+}
+
+static void
+meta_wayland_region_subtract (struct wl_client *client,
+                              struct wl_resource *resource,
+                              gint32 x,
+                              gint32 y,
+                              gint32 width,
+                              gint32 height)
+{
+  MetaWaylandRegion *region = wl_resource_get_user_data (resource);
+  cairo_rectangle_int_t rectangle = { x, y, width, height };
+
+  cairo_region_subtract_rectangle (region->region, &rectangle);
+}
+
+const struct wl_region_interface meta_wayland_region_interface = {
+  meta_wayland_region_destroy,
+  meta_wayland_region_add,
+  meta_wayland_region_subtract
+};
+
+static void
+meta_wayland_region_resource_destroy_cb (struct wl_resource *resource)
+{
+  MetaWaylandRegion *region = wl_resource_get_user_data (resource);
+
+  cairo_region_destroy (region->region);
+  g_slice_free (MetaWaylandRegion, region);
+}
+
+static void
+meta_wayland_compositor_create_region (struct wl_client *wayland_client,
+                                       struct wl_resource *compositor_resource,
+                                       uint32_t id)
+{
+  MetaWaylandRegion *region = g_slice_new0 (MetaWaylandRegion);
+
+  region->resource = wl_resource_create (wayland_client,
+                                        &wl_region_interface, 1,
+                                        id);
+  wl_resource_set_implementation (region->resource,
+                                 &meta_wayland_region_interface, region,
+                                 meta_wayland_region_resource_destroy_cb);
+
+  region->region = cairo_region_create ();
+}
+
+static void
+bind_output (struct wl_client *client,
+             void *data,
+             guint32 version,
+             guint32 id)
+{
+  MetaWaylandOutput *output = data;
+  struct wl_resource *resource =
+    wl_resource_create (client, &wl_output_interface, version, id);
+  GList *l;
+
+  wl_resource_post_event (resource,
+                          WL_OUTPUT_GEOMETRY,
+                          output->x, output->y,
+                          output->width_mm,
+                          output->height_mm,
+                          0, /* subpixel: unknown */
+                          "unknown", /* make */
+                          "unknown"); /* model */
+
+  for (l = output->modes; l; l = l->next)
+    {
+      MetaWaylandMode *mode = l->data;
+      wl_resource_post_event (resource,
+                              WL_OUTPUT_MODE,
+                              mode->flags,
+                              mode->width,
+                              mode->height,
+                              mode->refresh);
+    }
+}
+
+static void
+meta_wayland_compositor_create_output (MetaWaylandCompositor *compositor,
+                                       int x,
+                                       int y,
+                                       int width,
+                                       int height,
+                                       int width_mm,
+                                       int height_mm)
+{
+  MetaWaylandOutput *output = g_slice_new0 (MetaWaylandOutput);
+  MetaWaylandMode *mode;
+  float final_width, final_height;
+
+  /* XXX: eventually we will support sliced stages and an output should
+   * correspond to a slice/CoglFramebuffer, but for now we only support
+   * one output so we make sure it always matches the size of the stage
+   */
+  clutter_actor_set_size (compositor->stage, width, height);
+
+  /* Read back the actual size we were given.
+   * XXX: This really needs re-thinking later though so we know the
+   * correct output geometry to use. */
+  clutter_actor_get_size (compositor->stage, &final_width, &final_height);
+  width = final_width;
+  height = final_height;
+
+  output->wayland_output.interface = &wl_output_interface;
+
+  output->x = x;
+  output->y = y;
+  output->width_mm = width_mm;
+  output->height_mm = height_mm;
+
+  wl_global_create (compositor->wayland_display,
+                   &wl_output_interface, 2,
+                   output, bind_output);
+
+  mode = g_slice_new0 (MetaWaylandMode);
+  mode->flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+  mode->width = width;
+  mode->height = height;
+  mode->refresh = 60;
+
+  output->modes = g_list_prepend (output->modes, mode);
+
+  compositor->outputs = g_list_prepend (compositor->outputs, output);
+}
+
+const static struct wl_compositor_interface meta_wayland_compositor_interface = {
+  meta_wayland_compositor_create_surface,
+  meta_wayland_compositor_create_region
+};
+
+static void
+paint_finished_cb (ClutterActor *self, void *user_data)
+{
+  MetaWaylandCompositor *compositor = user_data;
+
+  while (!wl_list_empty (&compositor->frame_callbacks))
+    {
+      MetaWaylandFrameCallback *callback =
+        wl_container_of (compositor->frame_callbacks.next, callback, link);
+
+      wl_resource_post_event (callback->resource,
+                              WL_CALLBACK_DONE, get_time ());
+      wl_resource_destroy (callback->resource);
+    }
+}
+
+static void
+compositor_bind (struct wl_client *client,
+                void *data,
+                 guint32 version,
+                 guint32 id)
+{
+  MetaWaylandCompositor *compositor = data;
+  struct wl_resource *resource;
+
+  resource = wl_resource_create (client, &wl_compositor_interface, version, id);
+  wl_resource_set_implementation (resource, &meta_wayland_compositor_interface, compositor, NULL);
+}
+
+static void
+shell_surface_pong (struct wl_client *client,
+                    struct wl_resource *resource,
+                    guint32 serial)
+{
+}
+
+static void
+shell_surface_move (struct wl_client *client,
+                    struct wl_resource *resource,
+                    struct wl_resource *seat,
+                    guint32 serial)
+{
+}
+
+static void
+shell_surface_resize (struct wl_client *client,
+                      struct wl_resource *resource,
+                      struct wl_resource *seat,
+                      guint32 serial,
+                      guint32 edges)
+{
+  g_warning ("TODO: support shell_surface_resize request");
+}
+
+static void
+ensure_surface_window (MetaWaylandSurface *surface)
+{
+  MetaDisplay *display = meta_get_display ();
+
+  if (!surface->window)
+    {
+      int width, height;
+
+      if (surface->buffer_ref.buffer)
+        {
+          MetaWaylandBuffer *buffer = surface->buffer_ref.buffer;
+          width = buffer->width;
+          height = buffer->width;
+        }
+      else
+        {
+          width = 0;
+          height = 0;
+        }
+
+      surface->window =
+        meta_window_new_for_wayland (display, width, height, surface);
+
+      /* If the MetaWindow becomes unmanaged (surface->window will be
+       * freed in this case) we need to make sure to clear our
+       * ->window pointers. */
+      g_object_weak_ref (G_OBJECT (surface->window),
+                         window_destroyed_cb,
+                         surface);
+
+      meta_window_calc_showing (surface->window);
+    }
+}
+
+static void
+shell_surface_set_toplevel (struct wl_client *client,
+                            struct wl_resource *resource)
+{
+  MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
+  MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
+  MetaWaylandSurface *surface = shell_surface->surface;
+
+  /* NB: Surfaces from xwayland become managed based on X events. */
+  if (client == compositor->xwayland_client)
+    return;
+
+  ensure_surface_window (surface);
+
+  meta_window_unmake_fullscreen (surface->window);
+}
+
+static void
+shell_surface_set_transient (struct wl_client *client,
+                             struct wl_resource *resource,
+                             struct wl_resource *parent,
+                             int x,
+                             int y,
+                             guint32 flags)
+{
+  MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
+  MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
+  MetaWaylandSurface *surface = shell_surface->surface;
+
+  /* NB: Surfaces from xwayland become managed based on X events. */
+  if (client == compositor->xwayland_client)
+    return;
+
+  ensure_surface_window (surface);
+}
+
+static void
+shell_surface_set_fullscreen (struct wl_client *client,
+                              struct wl_resource *resource,
+                              guint32 method,
+                              guint32 framerate,
+                              struct wl_resource *output)
+{
+  MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
+  MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
+  MetaWaylandSurface *surface = shell_surface->surface;
+
+  /* NB: Surfaces from xwayland become managed based on X events. */
+  if (client == compositor->xwayland_client)
+    return;
+
+  ensure_surface_window (surface);
+
+  meta_window_make_fullscreen (surface->window);
+}
+
+static void
+shell_surface_set_popup (struct wl_client *client,
+                         struct wl_resource *resource,
+                         struct wl_resource *seat,
+                         guint32 serial,
+                         struct wl_resource *parent,
+                         gint32 x,
+                         gint32 y,
+                         guint32 flags)
+{
+}
+
+static void
+shell_surface_set_maximized (struct wl_client *client,
+                             struct wl_resource *resource,
+                             struct wl_resource *output)
+{
+  g_warning ("TODO: support shell_surface_set_maximized request");
+}
+
+static void
+shell_surface_set_title (struct wl_client *client,
+                         struct wl_resource *resource,
+                         const char *title)
+{
+  g_warning ("TODO: support shell_surface_set_title request");
+}
+
+static void
+shell_surface_set_class (struct wl_client *client,
+                         struct wl_resource *resource,
+                         const char *class_)
+{
+  g_warning ("TODO: support shell_surface_set_class request");
+}
+
+static const struct wl_shell_surface_interface meta_wayland_shell_surface_interface =
+{
+  shell_surface_pong,
+  shell_surface_move,
+  shell_surface_resize,
+  shell_surface_set_toplevel,
+  shell_surface_set_transient,
+  shell_surface_set_fullscreen,
+  shell_surface_set_popup,
+  shell_surface_set_maximized,
+  shell_surface_set_title,
+  shell_surface_set_class
+};
+
+static void
+shell_handle_surface_destroy (struct wl_listener *listener,
+                              void *data)
+{
+  MetaWaylandShellSurface *shell_surface =
+    wl_container_of (listener, shell_surface, surface_destroy_listener);
+  shell_surface->surface->has_shell_surface = FALSE;
+  shell_surface->surface = NULL;
+  wl_resource_destroy (shell_surface->resource);
+}
+
+static void
+destroy_shell_surface (struct wl_resource *resource)
+{
+  MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
+
+  /* In case cleaning up a dead client destroys shell_surface first */
+  if (shell_surface->surface)
+    {
+      wl_list_remove (&shell_surface->surface_destroy_listener.link);
+      shell_surface->surface->has_shell_surface = FALSE;
+    }
+
+  g_free (shell_surface);
+}
+
+static void
+get_shell_surface (struct wl_client *client,
+                   struct wl_resource *resource,
+                   guint32 id,
+                   struct wl_resource *surface_resource)
+{
+  MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
+  MetaWaylandShellSurface *shell_surface;
+
+  if (surface->has_shell_surface)
+    {
+      wl_resource_post_error (surface_resource,
+                              WL_DISPLAY_ERROR_INVALID_OBJECT,
+                              "wl_shell::get_shell_surface already requested");
+      return;
+    }
+
+  shell_surface = g_new0 (MetaWaylandShellSurface, 1);
+
+  /* a shell surface inherits the version from the shell */
+  shell_surface->resource =
+    wl_resource_create (client, &wl_shell_surface_interface,
+                       wl_resource_get_version (resource), id);
+  wl_resource_set_implementation (shell_surface->resource, &meta_wayland_shell_surface_interface,
+                                 shell_surface, destroy_shell_surface);
+
+  shell_surface->surface = surface;
+  shell_surface->surface_destroy_listener.notify = shell_handle_surface_destroy;
+  wl_resource_add_destroy_listener (surface->resource,
+                                    &shell_surface->surface_destroy_listener);
+  surface->has_shell_surface = TRUE;
+}
+
+static const struct wl_shell_interface meta_wayland_shell_interface =
+{
+  get_shell_surface
+};
+
+static void
+bind_shell (struct wl_client *client,
+            void *data,
+            guint32 version,
+            guint32 id)
+{
+  struct wl_resource *resource;
+
+  resource = wl_resource_create (client, &wl_shell_interface, version, id);
+  wl_resource_set_implementation (resource, &meta_wayland_shell_interface, data, NULL);
+}
+
+static void
+xserver_set_window_id (struct wl_client *client,
+                       struct wl_resource *compositor_resource,
+                       struct wl_resource *surface_resource,
+                       guint32 xid)
+{
+  MetaWaylandCompositor *compositor =
+    wl_resource_get_user_data (compositor_resource);
+  MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
+  MetaDisplay *display = meta_get_display ();
+  MetaWindow *window;
+
+  g_return_if_fail (surface->xid == None);
+
+  surface->xid = xid;
+
+  g_hash_table_insert (compositor->window_surfaces, &xid, surface);
+
+  window  = meta_display_lookup_x_window (display, xid);
+  if (window)
+    {
+      MetaWindowActor *window_actor =
+        META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
+
+      meta_window_actor_set_wayland_surface (window_actor, surface);
+
+      surface->window = window;
+      window->surface = surface;
+
+      /* If the MetaWindow becomes unmanaged (surface->window will be
+       * freed in this case) we need to make sure to clear our
+       * ->window pointers in this case. */
+      g_object_weak_ref (G_OBJECT (surface->window),
+                         window_destroyed_cb,
+                         surface);
+    }
+#warning "FIXME: Handle surface destroy and remove window_surfaces mapping"
+}
+
+MetaWaylandSurface *
+meta_wayland_lookup_surface_for_xid (guint32 xid)
+{
+  return g_hash_table_lookup (_meta_wayland_compositor.window_surfaces, &xid);
+}
+
+static const struct xserver_interface xserver_implementation = {
+    xserver_set_window_id
+};
+
+static void
+bind_xserver (struct wl_client *client,
+             void *data,
+              guint32 version,
+              guint32 id)
+{
+  MetaWaylandCompositor *compositor = data;
+
+  /* If it's a different client than the xserver we launched,
+   * don't start the wm. */
+  if (client != compositor->xwayland_client)
+    return;
+
+  compositor->xserver_resource =
+    wl_resource_create (client, &xserver_interface, version, id);
+  wl_resource_set_implementation (compositor->xserver_resource,
+                                 &xserver_implementation, compositor, NULL);
+
+  wl_resource_post_event (compositor->xserver_resource,
+                          XSERVER_LISTEN_SOCKET,
+                          compositor->xwayland_abstract_fd);
+
+  wl_resource_post_event (compositor->xserver_resource,
+                          XSERVER_LISTEN_SOCKET,
+                          compositor->xwayland_unix_fd);
+
+  /* Make sure xwayland will recieve the above sockets in a finite
+   * time before unblocking the initialization mainloop since we are
+   * then going to immediately try and connect to those as the window
+   * manager. */
+  wl_client_flush (client);
+
+  /* At this point xwayland is all setup to start accepting
+   * connections so we can quit the transient initialization mainloop
+   * and unblock meta_wayland_init() to continue initializing mutter.
+   * */
+  g_main_loop_quit (compositor->init_loop);
+  compositor->init_loop = NULL;
+}
+
+static void
+stage_destroy_cb (void)
+{
+  meta_quit (META_EXIT_SUCCESS);
+}
+
+void
+meta_wayland_init (void)
+{
+  MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
+
+  memset (compositor, 0, sizeof (MetaWaylandCompositor));
+
+  compositor->wayland_display = wl_display_create ();
+  if (compositor->wayland_display == NULL)
+    g_error ("failed to create wayland display");
+
+  wl_display_init_shm (compositor->wayland_display);
+
+  wl_list_init (&compositor->frame_callbacks);
+
+  if (!wl_global_create (compositor->wayland_display,
+                        &wl_compositor_interface, 3,
+                        compositor, compositor_bind))
+    g_error ("Failed to register wayland compositor object");
+
+  compositor->wayland_loop =
+    wl_display_get_event_loop (compositor->wayland_display);
+  compositor->wayland_event_source =
+    wayland_event_source_new (compositor->wayland_display);
+
+  /* XXX: Here we are setting the wayland event source to have a
+   * slightly lower priority than the X event source, because we are
+   * much more likely to get confused being told about surface changes
+   * relating to X clients when we don't know what's happened to them
+   * according to the X protocol.
+   *
+   * At some point we could perhaps try and get the X protocol proxied
+   * over the wayland protocol so that we don't have to worry about
+   * synchronizing the two command streams. */
+  g_source_set_priority (compositor->wayland_event_source,
+                         GDK_PRIORITY_EVENTS + 1);
+  g_source_attach (compositor->wayland_event_source, NULL);
+
+  clutter_wayland_set_compositor_display (compositor->wayland_display);
+
+  if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS)
+    g_error ("Failed to initialize Clutter");
+
+  compositor->stage = clutter_stage_new ();
+  clutter_stage_set_user_resizable (CLUTTER_STAGE (compositor->stage), FALSE);
+  g_signal_connect_after (compositor->stage, "paint",
+                          G_CALLBACK (paint_finished_cb), compositor);
+  g_signal_connect (compositor->stage, "destroy",
+                    G_CALLBACK (stage_destroy_cb), NULL);
+
+  meta_wayland_compositor_create_output (compositor, 0, 0, 1024, 600, 222, 125);
+
+  if (wl_global_create (compositor->wayland_display,
+                       &wl_shell_interface, 1,
+                       compositor, bind_shell) == NULL)
+    g_error ("Failed to register a global shell object");
+
+  clutter_actor_show (compositor->stage);
+
+  if (wl_display_add_socket (compositor->wayland_display, "wayland-0"))
+    g_error ("Failed to create socket");
+
+  wl_global_create (compositor->wayland_display,
+                   &xserver_interface, 1,
+                   compositor, bind_xserver);
+
+  /* We need a mapping from xids to wayland surfaces... */
+  compositor->window_surfaces = g_hash_table_new (g_int_hash, g_int_equal);
+
+  /* XXX: It's important that we only try and start xwayland after we
+   * have initialized EGL because EGL implements the "wl_drm"
+   * interface which xwayland requires to determine what drm device
+   * name it should use.
+   *
+   * By waiting until we've shown the stage above we ensure that the
+   * underlying GL resources for the surface have also been allocated
+   * and so EGL must be initialized by this point.
+   */
+
+  if (!meta_xwayland_start (compositor))
+    g_error ("Failed to start X Wayland");
+
+  putenv (g_strdup_printf ("DISPLAY=:%d", compositor->xwayland_display_index));
+
+  /* We need to run a mainloop until we know xwayland has a binding
+   * for our xserver interface at which point we can assume it's
+   * ready to start accepting connections. */
+  compositor->init_loop = g_main_loop_new (NULL, FALSE);
+
+  g_main_loop_run (compositor->init_loop);
+}
+
+void
+meta_wayland_finalize (void)
+{
+  meta_xwayland_stop (meta_wayland_compositor_get_default ());
+}
+
+void
+meta_wayland_handle_sig_child (void)
+{
+  int status;
+  pid_t pid = waitpid (-1, &status, WNOHANG);
+  MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
+
+  /* The simplest measure to avoid infinitely re-spawning a crashing
+   * X server */
+  if (pid == compositor->xwayland_pid)
+    {
+      if (!WIFEXITED (status))
+        g_critical ("X Wayland crashed; aborting");
+      else
+        {
+          /* For now we simply abort if we see the server exit.
+           *
+           * In the future X will only be loaded lazily for legacy X support
+           * but for now it's a hard requirement. */
+          g_critical ("Spurious exit of X Wayland server");
+        }
+    }
+}
diff --git a/src/wayland/meta-xwayland-private.h b/src/wayland/meta-xwayland-private.h
new file mode 100644
index 0000000..0d6716b
--- /dev/null
+++ b/src/wayland/meta-xwayland-private.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_XWAYLAND_PRIVATE_H
+#define META_XWAYLAND_PRIVATE_H
+
+#include "meta-wayland-private.h"
+
+#include <glib.h>
+
+gboolean
+meta_xwayland_start (MetaWaylandCompositor *compositor);
+
+void
+meta_xwayland_stop (MetaWaylandCompositor *compositor);
+
+#endif /* META_XWAYLAND_PRIVATE_H */
diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c
new file mode 100644
index 0000000..7ff30bf
--- /dev/null
+++ b/src/wayland/meta-xwayland.c
@@ -0,0 +1,310 @@
+/*
+ * X Wayland Support
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "meta-xwayland-private.h"
+
+#include <glib.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+static char *
+create_lockfile (int display, int *display_out)
+{
+  char *filename;
+  int size;
+  char pid[11];
+  int fd;
+
+  do
+    {
+      char *end;
+      pid_t other;
+
+      filename = g_strdup_printf ("/tmp/.X%d-lock", display);
+      fd = open (filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444);
+
+      if (fd < 0 && errno == EEXIST)
+        {
+          fd = open (filename, O_CLOEXEC, O_RDONLY);
+          if (fd < 0 || read (fd, pid, 11) != 11)
+            {
+              const char *msg = strerror (errno);
+              g_warning ("can't read lock file %s: %s", filename, msg);
+              g_free (filename);
+
+              /* ignore error and try the next display number */
+              display++;
+              continue;
+          }
+          close (fd);
+
+          other = strtol (pid, &end, 0);
+          if (end != pid + 10)
+            {
+              g_warning ("can't parse lock file %s", filename);
+              g_free (filename);
+
+              /* ignore error and try the next display number */
+              display++;
+              continue;
+          }
+
+          if (kill (other, 0) < 0 && errno == ESRCH)
+            {
+              g_warning ("unlinking stale lock file %s", filename);
+              if (unlink (filename) < 0)
+                {
+                  const char *msg = strerror (errno);
+                  g_warning ("failed to unlink stale lock file: %s", msg);
+                  display++;
+                }
+              g_free (filename);
+              continue;
+          }
+
+          g_free (filename);
+          display++;
+          continue;
+        }
+      else if (fd < 0)
+        {
+          const char *msg = strerror (errno);
+          g_warning ("failed to create lock file %s: %s", filename , msg);
+          g_free (filename);
+          return NULL;
+        }
+
+      break;
+    }
+  while (1);
+
+  /* Subtle detail: we use the pid of the wayland compositor, not the xserver
+   * in the lock file. */
+  size = snprintf (pid, 11, "%10d\n", getpid ());
+  if (size != 11 || write (fd, pid, 11) != 11)
+    {
+      unlink (filename);
+      close (fd);
+      g_warning ("failed to write pid to lock file %s", filename);
+      g_free (filename);
+      return NULL;
+    }
+
+  close (fd);
+
+  *display_out = display;
+  return filename;
+}
+
+static int
+bind_to_abstract_socket (int display)
+{
+  struct sockaddr_un addr;
+  socklen_t size, name_size;
+  int fd;
+
+  fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+  if (fd < 0)
+    return -1;
+
+  addr.sun_family = AF_LOCAL;
+  name_size = snprintf (addr.sun_path, sizeof addr.sun_path,
+                        "%c/tmp/.X11-unix/X%d", 0, display);
+  size = offsetof (struct sockaddr_un, sun_path) + name_size;
+  if (bind (fd, (struct sockaddr *) &addr, size) < 0)
+    {
+      g_warning ("failed to bind to @%s: %s\n",
+                 addr.sun_path + 1, strerror (errno));
+      close (fd);
+      return -1;
+    }
+
+  if (listen (fd, 1) < 0)
+    {
+      close (fd);
+      return -1;
+    }
+
+  return fd;
+}
+
+static int
+bind_to_unix_socket (int display)
+{
+  struct sockaddr_un addr;
+  socklen_t size, name_size;
+  int fd;
+
+  fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+  if (fd < 0)
+    return -1;
+
+  addr.sun_family = AF_LOCAL;
+  name_size = snprintf (addr.sun_path, sizeof addr.sun_path,
+                        "/tmp/.X11-unix/X%d", display) + 1;
+  size = offsetof (struct sockaddr_un, sun_path) + name_size;
+  unlink (addr.sun_path);
+  if (bind (fd, (struct sockaddr *) &addr, size) < 0)
+    {
+      char *msg = strerror (errno);
+      g_warning ("failed to bind to %s (%s)\n", addr.sun_path, msg);
+      close (fd);
+      return -1;
+    }
+
+  if (listen (fd, 1) < 0) {
+      unlink (addr.sun_path);
+      close (fd);
+      return -1;
+  }
+
+  return fd;
+}
+
+gboolean
+meta_xwayland_start (MetaWaylandCompositor *compositor)
+{
+  int display = 0;
+  char *lockfile = NULL;
+  int sp[2];
+  pid_t pid;
+
+  do
+    {
+      lockfile = create_lockfile (display, &display);
+      if (!lockfile)
+        {
+         g_warning ("Failed to create an X lock file");
+         return FALSE;
+        }
+
+      compositor->xwayland_abstract_fd = bind_to_abstract_socket (display);
+      if (compositor->xwayland_abstract_fd < 0)
+        {
+          unlink (lockfile);
+
+          if (errno == EADDRINUSE)
+            {
+              display++;
+              continue;
+            }
+          else
+            return FALSE;
+        }
+
+      compositor->xwayland_unix_fd = bind_to_unix_socket (display);
+      if (compositor->xwayland_abstract_fd < 0)
+        {
+          unlink (lockfile);
+          close (compositor->xwayland_abstract_fd);
+          return FALSE;
+        }
+
+      break;
+    }
+  while (1);
+
+  compositor->xwayland_display_index = display;
+  compositor->xwayland_lockfile = lockfile;
+
+  /* We want xwayland to be a wayland client so we make a socketpair to setup a
+   * wayland protocol connection. */
+  if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sp) < 0)
+    {
+      g_warning ("socketpair failed\n");
+      unlink (lockfile);
+      return 1;
+    }
+
+  switch ((pid = fork()))
+    {
+    case 0:
+        {
+          char *fd_string;
+          char *display_name;
+          /* Make sure the client end of the socket pair doesn't get closed
+           * when we exec xwayland. */
+          int flags = fcntl (sp[1], F_GETFD);
+          if (flags != -1)
+            fcntl (sp[1], F_SETFD, flags & ~FD_CLOEXEC);
+
+          fd_string = g_strdup_printf ("%d", sp[1]);
+          setenv ("WAYLAND_SOCKET", fd_string, 1);
+          g_free (fd_string);
+
+          display_name = g_strdup_printf (":%d",
+                                          compositor->xwayland_display_index);
+
+          if (execl (XWAYLAND_PATH,
+                     XWAYLAND_PATH,
+                     display_name,
+                     "-wayland",
+                     "-rootless",
+                     "-retro",
+                     "-noreset",
+                     /* FIXME: does it make sense to log to the filesystem by
+                      * default? */
+                     "-logfile", "/tmp/xwayland.log",
+                     "-nolisten", "all",
+                     NULL) < 0)
+            {
+              char *msg = strerror (errno);
+              g_warning ("xwayland exec failed: %s", msg);
+            }
+          exit (-1);
+          return FALSE;
+        }
+    default:
+      g_message ("forked X server, pid %d\n", pid);
+
+      close (sp[1]);
+      compositor->xwayland_client =
+        wl_client_create (compositor->wayland_display, sp[0]);
+
+      compositor->xwayland_pid = pid;
+      break;
+
+    case -1:
+      g_error ("Failed to fork for xwayland server");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+void
+meta_xwayland_stop (MetaWaylandCompositor *compositor)
+{
+  char path[256];
+
+  snprintf (path, sizeof path, "/tmp/.X%d-lock",
+            compositor->xwayland_display_index);
+  unlink (path);
+  snprintf (path, sizeof path, "/tmp/.X11-unix/X%d",
+            compositor->xwayland_display_index);
+  unlink (path);
+
+  unlink (compositor->xwayland_lockfile);
+}


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