[PATCH xserver] XMir DDX

Robert Ancell robert.ancell at canonical.com
Fri Apr 29 09:11:20 UTC 2016


Contributions from:
  Andreas Pokorny <andreas.pokorny at canonical.com>
  Chris Townsend <christopher.townsend at canonical.com>
  Christopher James Halse Rogers <christopher.halse.rogers at canonical.com>
  Daniel van Vugt <vanvugt at gmail.com>
  Maarten Lankhorst <maarten.lankhorst at ubuntu.com>
  Robert Ancell <robert.ancell at canonical.com>

Signed-off-by: Robert Ancell <robert.ancell at canonical.com>
---
 configure.ac                |   24 +
 hw/Makefile.am              |    9 +-
 hw/xmir/.gitignore          |    1 +
 hw/xmir/Makefile.am         |   27 +
 hw/xmir/xmir-cursor.c       |  210 +++++++
 hw/xmir/xmir-cvt.c          |  304 ++++++++++
 hw/xmir/xmir-input.c        |  592 ++++++++++++++++++
 hw/xmir/xmir-output.c       |  424 +++++++++++++
 hw/xmir/xmir-thread-proxy.c |  109 ++++
 hw/xmir/xmir.c              | 1416 +++++++++++++++++++++++++++++++++++++++++++
 hw/xmir/xmir.h              |  165 +++++
 11 files changed, 3279 insertions(+), 2 deletions(-)
 create mode 100644 hw/xmir/.gitignore
 create mode 100644 hw/xmir/Makefile.am
 create mode 100644 hw/xmir/xmir-cursor.c
 create mode 100644 hw/xmir/xmir-cvt.c
 create mode 100644 hw/xmir/xmir-input.c
 create mode 100644 hw/xmir/xmir-output.c
 create mode 100644 hw/xmir/xmir-thread-proxy.c
 create mode 100644 hw/xmir/xmir.c
 create mode 100644 hw/xmir/xmir.h

diff --git a/configure.ac b/configure.ac
index db87ff9..49b1b1b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -628,6 +628,7 @@ AC_ARG_ENABLE(xvfb,    	      AS_HELP_STRING([--enable-xvfb], [Build Xvfb server
 AC_ARG_ENABLE(xnest,   	      AS_HELP_STRING([--enable-xnest], [Build Xnest server (default: auto)]), [XNEST=$enableval], [XNEST=auto])
 AC_ARG_ENABLE(xquartz,        AS_HELP_STRING([--enable-xquartz], [Build Xquartz server for OS-X (default: auto)]), [XQUARTZ=$enableval], [XQUARTZ=auto])
 AC_ARG_ENABLE(xwayland,       AS_HELP_STRING([--enable-xwayland], [Build Xwayland server (default: auto)]), [XWAYLAND=$enableval], [XWAYLAND=auto])
+AC_ARG_ENABLE(xmir,           AS_HELP_STRING([--enable-xmir], [Build Xmir server (default: auto)]), [XMIR=$enableval], [XMIR=auto])
 AC_ARG_ENABLE(standalone-xpbproxy, AS_HELP_STRING([--enable-standalone-xpbproxy], [Build a standalone xpbproxy (in addition to the one integrated into Xquartz as a separate thread) (default: no)]), [STANDALONE_XPBPROXY=$enableval], [STANDALONE_XPBPROXY=no])
 AC_ARG_ENABLE(xwin,    	      AS_HELP_STRING([--enable-xwin], [Build XWin server (default: auto)]), [XWIN=$enableval], [XWIN=auto])
 AC_ARG_ENABLE(glamor,         AS_HELP_STRING([--enable-glamor], [Build glamor dix module (default: no)]), [GLAMOR=$enableval], [GLAMOR=no])
@@ -746,6 +747,7 @@ case $host_os in
 			XVFB=no
 			XNEST=no
 			XWAYLAND=no
+			XMIR=no
 
 			COMPOSITE=no
 			DGA=no
@@ -2482,6 +2484,27 @@ if test "x$XWAYLAND" = xyes; then
 		     [${WAYLAND_PREFIX}/bin$PATH_SEPARATOR$PATH])
 fi
 
+dnl Xmir DDX
+
+PKG_CHECK_MODULES(XMIRMODULES, [mirclient >= 0.13.1 mir-client-platform-mesa], [have_xmir=yes], [have_xmir=no])
+AC_MSG_CHECKING([whether to build Xmir DDX])
+if test "x$XMIR" = xauto; then
+   XMIR="$have_xmir"
+fi
+AC_MSG_RESULT([$XMIR])
+AM_CONDITIONAL(XMIR, [test "x$XMIR" = xyes])
+
+if test "x$XMIR" = xyes; then
+	if test "x$have_xmir" = xno; then
+		AC_MSG_ERROR([Xmir build explicitly requested, but required modules not found.])
+	fi
+
+	XMIR_LIBS="$FB_LIB $FIXES_LIB $MI_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $PRESENT_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB $MAIN_LIB $DIX_LIB $OS_LIB"
+	XMIR_SYS_LIBS="$XMIRMODULES_LIBS $GLX_SYS_LIBS"
+	AC_SUBST([XMIR_LIBS])
+	AC_SUBST([XMIR_SYS_LIBS])
+fi
+
 
 dnl and the rest of these are generic, so they're in config.h
 dnl 
@@ -2630,6 +2653,7 @@ hw/kdrive/fbdev/Makefile
 hw/kdrive/linux/Makefile
 hw/kdrive/src/Makefile
 hw/xwayland/Makefile
+hw/xmir/Makefile
 test/Makefile
 test/xi1/Makefile
 test/xi2/Makefile
diff --git a/hw/Makefile.am b/hw/Makefile.am
index 19895dc..b7b958c 100644
--- a/hw/Makefile.am
+++ b/hw/Makefile.am
@@ -30,6 +30,10 @@ if XWAYLAND
 XWAYLAND_SUBDIRS = xwayland
 endif
 
+if XMIR
+XMIR_SUBDIRS = xmir
+endif
+
 SUBDIRS =			\
 	$(XORG_SUBDIRS)		\
 	$(XWIN_SUBDIRS)		\
@@ -38,9 +42,10 @@ SUBDIRS =			\
 	$(DMX_SUBDIRS)		\
 	$(KDRIVE_SUBDIRS)	\
 	$(XQUARTZ_SUBDIRS)	\
-	$(XWAYLAND_SUBDIRS)
+	$(XWAYLAND_SUBDIRS) \
+	$(XMIR_SUBDIRS)
 
-DIST_SUBDIRS = dmx xfree86 vfb xnest xwin xquartz kdrive xwayland
+DIST_SUBDIRS = dmx xfree86 vfb xnest xwin xquartz kdrive xwayland xmir
 
 relink:
 	$(AM_V_at)for i in $(SUBDIRS) ; do $(MAKE) -C $$i relink || exit 1 ; done
diff --git a/hw/xmir/.gitignore b/hw/xmir/.gitignore
new file mode 100644
index 0000000..b6a16d4
--- /dev/null
+++ b/hw/xmir/.gitignore
@@ -0,0 +1 @@
+Xmir
diff --git a/hw/xmir/Makefile.am b/hw/xmir/Makefile.am
new file mode 100644
index 0000000..be373f4
--- /dev/null
+++ b/hw/xmir/Makefile.am
@@ -0,0 +1,27 @@
+bin_PROGRAMS = Xmir
+
+Xmir_CFLAGS =				\
+	-DHAVE_DIX_CONFIG_H		\
+	$(XMIRMODULES_CFLAGS)		\
+	$(DIX_CFLAGS)			\
+	$(GBM_CFLAGS)
+
+Xmir_SOURCES =				\
+	xmir.c				\
+	xmir-cursor.c			\
+	xmir-input.c			\
+	xmir-output.c			\
+	xmir-cvt.c			\
+	xmir-thread-proxy.c		\
+	xmir.h				\
+	$(top_srcdir)/Xi/stubs.c	\
+	$(top_srcdir)/mi/miinitext.c
+
+Xmir_LDADD =				\
+	$(XMIR_LIBS)			\
+	$(XMIR_SYS_LIBS)		\
+	$(XSERVER_SYS_LIBS)
+Xmir_LDFLAGS = $(LD_EXPORT_SYMBOLS_FLAG)
+
+relink:
+	$(AM_V_at)rm -f Xmir$(EXEEXT) && $(MAKE) Xmir$(EXEEXT)
diff --git a/hw/xmir/xmir-cursor.c b/hw/xmir/xmir-cursor.c
new file mode 100644
index 0000000..3cbcc7b
--- /dev/null
+++ b/hw/xmir/xmir-cursor.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright © 2015-2016 Canonical Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "xmir.h"
+
+#include <mipointer.h>
+
+static DevPrivateKeyRec xmir_cursor_private_key;
+
+static void
+expand_source_and_mask(CursorPtr cursor, void *data)
+{
+    CARD32 *p, d, fg, bg;
+    CursorBitsPtr bits = cursor->bits;
+    int x, y, stride, i, bit;
+
+    p = data;
+    fg = ((cursor->foreRed & 0xff00) << 8) |
+        (cursor->foreGreen & 0xff00) | (cursor->foreGreen >> 8);
+    bg = ((cursor->backRed & 0xff00) << 8) |
+        (cursor->backGreen & 0xff00) | (cursor->backGreen >> 8);
+    stride = (bits->width / 8 + 3) & ~3;
+    for (y = 0; y < bits->height; y++)
+        for (x = 0; x < bits->width; x++) {
+            i = y * stride + x / 8;
+            bit = 1 << (x & 7);
+            if (bits->source[i] & bit)
+                d = fg;
+            else
+                d = bg;
+            if (bits->mask[i] & bit)
+                d |= 0xff000000;
+            else
+                d = 0x00000000;
+
+            *p++ = d;
+        }
+}
+
+static Bool
+xmir_realize_cursor(DeviceIntPtr device, ScreenPtr screen, CursorPtr cursor)
+{
+    return TRUE;
+}
+
+static void xmir_input_set_cursor(struct xmir_input *xmir_input, CursorPtr cursor);
+
+static Bool
+xmir_unrealize_cursor(DeviceIntPtr device, ScreenPtr screen, CursorPtr cursor)
+{
+    struct xmir_input *xmir_input = device ? device->public.devicePrivate : NULL;
+    MirBufferStream *stream;
+
+    stream = dixGetPrivate(&cursor->devPrivates, &xmir_cursor_private_key);
+    dixSetPrivate(&cursor->devPrivates, &xmir_cursor_private_key, NULL);
+
+    if (xmir_input)
+        xmir_input_set_cursor(xmir_input, rootCursor);
+
+    if (stream)
+        mir_buffer_stream_release_sync(stream);
+
+    return TRUE;
+}
+
+static void
+xmir_input_set_cursor(struct xmir_input *xmir_input, CursorPtr cursor)
+{
+    MirGraphicsRegion region;
+    MirCursorConfiguration *config;
+    MirBufferStream *stream;
+
+    if (!cursor) {
+        config = mir_cursor_configuration_from_name(mir_disabled_cursor_name);
+        goto apply;
+    } else if (cursor == rootCursor) {
+        /* Avoid using the old style X default black cross cursor */
+        config = mir_cursor_configuration_from_name(mir_arrow_cursor_name);
+        goto apply;
+    }
+
+    stream = dixGetPrivate(&cursor->devPrivates, &xmir_cursor_private_key);
+    if (stream) {
+        mir_buffer_stream_get_graphics_region(stream, &region);
+        if (region.width != cursor->bits->width || region.height != cursor->bits->height) {
+            mir_buffer_stream_release_sync(stream);
+            stream = NULL;
+        }
+    }
+
+    if (!stream) {
+        stream = mir_connection_create_buffer_stream_sync(xmir_input->xmir_screen->conn, cursor->bits->width, cursor->bits->height, mir_pixel_format_argb_8888, mir_buffer_usage_software);
+        dixSetPrivate(&cursor->devPrivates, &xmir_cursor_private_key, stream);
+        mir_buffer_stream_get_graphics_region(stream, &region);
+    }
+
+    if (cursor->bits->argb) {
+        int y, stride;
+
+        stride = cursor->bits->width * 4;
+        for (y = 0; y < cursor->bits->height; y++)
+           memcpy(region.vaddr + y * region.stride,
+                  (char*)cursor->bits->argb + y * stride, stride);
+    }
+    else
+        expand_source_and_mask(cursor, region.vaddr);
+
+    mir_buffer_stream_swap_buffers(stream, NULL, NULL);
+    config = mir_cursor_configuration_from_buffer_stream(stream, cursor->bits->xhot, cursor->bits->yhot);
+
+apply:
+    if (!xmir_input->xmir_screen->rootless)
+        mir_wait_for(mir_surface_configure_cursor(xmir_window_get(xmir_input->xmir_screen->screen->root)->surface, config));
+    else if (xmir_input->focus_window)
+        mir_wait_for(mir_surface_configure_cursor(xmir_input->focus_window->surface, config));
+    mir_cursor_configuration_destroy(config);
+}
+
+static void
+xmir_set_cursor(DeviceIntPtr device,
+                ScreenPtr screen, CursorPtr cursor, int x, int y)
+{
+    struct xmir_input *xmir_input;
+
+    xmir_input = device->public.devicePrivate;
+    if (xmir_input == NULL)
+        return;
+
+    xmir_input_set_cursor(xmir_input, cursor);
+}
+
+static void
+xmir_move_cursor(DeviceIntPtr device, ScreenPtr screen, int x, int y)
+{
+}
+
+static Bool
+xmir_device_cursor_initialize(DeviceIntPtr device, ScreenPtr screen)
+{
+    return TRUE;
+}
+
+static void
+xmir_device_cursor_cleanup(DeviceIntPtr device, ScreenPtr screen)
+{
+}
+
+static miPointerSpriteFuncRec xmir_pointer_sprite_funcs = {
+    xmir_realize_cursor,
+    xmir_unrealize_cursor,
+    xmir_set_cursor,
+    xmir_move_cursor,
+    xmir_device_cursor_initialize,
+    xmir_device_cursor_cleanup
+};
+
+static Bool
+xmir_cursor_off_screen(ScreenPtr *ppScreen, int *x, int *y)
+{
+    return FALSE;
+}
+
+static void
+xmir_cross_screen(ScreenPtr pScreen, Bool entering)
+{
+}
+
+static void
+xmir_pointer_warp_cursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y)
+{
+}
+
+static miPointerScreenFuncRec xmir_pointer_screen_funcs = {
+    xmir_cursor_off_screen,
+    xmir_cross_screen,
+    xmir_pointer_warp_cursor
+};
+
+Bool
+xmir_screen_init_cursor(struct xmir_screen *xmir_screen)
+{
+    if (!dixRegisterPrivateKey(&xmir_cursor_private_key, PRIVATE_CURSOR_BITS, 0))
+        return FALSE;
+
+    return miPointerInitialize(xmir_screen->screen,
+                               &xmir_pointer_sprite_funcs,
+                               &xmir_pointer_screen_funcs, TRUE);
+}
diff --git a/hw/xmir/xmir-cvt.c b/hw/xmir/xmir-cvt.c
new file mode 100644
index 0000000..6070d77
--- /dev/null
+++ b/hw/xmir/xmir-cvt.c
@@ -0,0 +1,304 @@
+/* Copied from hw/xfree86/modes/xf86cvt.c into xmir DDX and
+ * changed to generate an RRMode */
+
+/*
+ * Copyright 2005-2006 Luc Verhaegen.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The reason for having this function in a file of its own is
+ * so that ../utils/cvt/cvt can link to it, and that xf86CVTMode
+ * code is shared directly.
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include <string.h>
+#include <randrstr.h>
+#include "xmir.h"
+
+/*
+ * Generate a CVT standard mode from HDisplay, VDisplay and VRefresh.
+ *
+ * These calculations are stolen from the CVT calculation spreadsheet written
+ * by Graham Loveridge. He seems to be claiming no copyright and there seems to
+ * be no license attached to this. He apparently just wants to see his name
+ * mentioned.
+ *
+ * This file can be found at http://www.vesa.org/Public/CVT/CVTd6r1.xls
+ *
+ * Comments and structure corresponds to the comments and structure of the xls.
+ * This should ease importing of future changes to the standard (not very
+ * likely though).
+ *
+ * About margins; i'm sure that they are to be the bit between HDisplay and
+ * HBlankStart, HBlankEnd and HTotal, VDisplay and VBlankStart, VBlankEnd and
+ * VTotal, where the overscan colour is shown. FB seems to call _all_ blanking
+ * outside sync "margin" for some reason. Since we prefer seeing proper
+ * blanking instead of the overscan colour, and since the Crtc* values will
+ * probably get altered after us, we will disable margins altogether. With
+ * these calculations, Margins will plainly expand H/VDisplay, and we don't
+ * want that. -- libv
+ *
+ */
+RRModePtr
+xmir_cvt(int HDisplay, int VDisplay, float VRefresh, Bool Reduced,
+             Bool Interlaced)
+{
+    /* 1) top/bottom margin size (% of height) - default: 1.8 */
+#define CVT_MARGIN_PERCENTAGE 1.8
+
+    /* 2) character cell horizontal granularity (pixels) - default 8 */
+#define CVT_H_GRANULARITY 8
+
+    /* 4) Minimum vertical porch (lines) - default 3 */
+#define CVT_MIN_V_PORCH 3
+
+    /* 4) Minimum number of vertical back porch lines - default 6 */
+#define CVT_MIN_V_BPORCH 6
+
+    /* Pixel Clock step (kHz) */
+#define CVT_CLOCK_STEP 250
+
+    Bool Margins = FALSE;
+    float VFieldRate, HPeriod;
+    int HDisplayRnd, HMargin;
+    int VDisplayRnd, VMargin, VSync;
+    float Interlace;            /* Please rename this */
+    char name[128];
+    xRRModeInfo modeinfo;
+
+    memset(&modeinfo, 0, sizeof modeinfo);
+
+    /* CVT default is 60.0Hz */
+    if (!VRefresh)
+        VRefresh = 60.0;
+
+    /* 1. Required field rate */
+    if (Interlaced)
+        VFieldRate = VRefresh * 2;
+    else
+        VFieldRate = VRefresh;
+
+    /* 2. Horizontal pixels */
+    HDisplayRnd = HDisplay - (HDisplay % CVT_H_GRANULARITY);
+
+    /* 3. Determine left and right borders */
+    if (Margins) {
+        /* right margin is actually exactly the same as left */
+        HMargin = (((float) HDisplayRnd) * CVT_MARGIN_PERCENTAGE / 100.0);
+        HMargin -= HMargin % CVT_H_GRANULARITY;
+    }
+    else
+        HMargin = 0;
+
+    /* 4. Find total active pixels */
+    modeinfo.width = HDisplayRnd + 2 * HMargin;
+
+    /* 5. Find number of lines per field */
+    if (Interlaced)
+        VDisplayRnd = VDisplay / 2;
+    else
+        VDisplayRnd = VDisplay;
+
+    /* 6. Find top and bottom margins */
+    /* nope. */
+    if (Margins)
+        /* top and bottom margins are equal again. */
+        VMargin = (((float) VDisplayRnd) * CVT_MARGIN_PERCENTAGE / 100.0);
+    else
+        VMargin = 0;
+
+    modeinfo.height = VDisplay + 2 * VMargin;
+
+    /* 7. Interlace */
+    if (Interlaced)
+        Interlace = 0.5;
+    else
+        Interlace = 0.0;
+
+    /* Determine VSync Width from aspect ratio */
+    if (!(VDisplay % 3) && ((VDisplay * 4 / 3) == HDisplay))
+        VSync = 4;
+    else if (!(VDisplay % 9) && ((VDisplay * 16 / 9) == HDisplay))
+        VSync = 5;
+    else if (!(VDisplay % 10) && ((VDisplay * 16 / 10) == HDisplay))
+        VSync = 6;
+    else if (!(VDisplay % 4) && ((VDisplay * 5 / 4) == HDisplay))
+        VSync = 7;
+    else if (!(VDisplay % 9) && ((VDisplay * 15 / 9) == HDisplay))
+        VSync = 7;
+    else                        /* Custom */
+        VSync = 10;
+
+    if (!Reduced) {             /* simplified GTF calculation */
+
+        /* 4) Minimum time of vertical sync + back porch interval (µs)
+         * default 550.0 */
+#define CVT_MIN_VSYNC_BP 550.0
+
+        /* 3) Nominal HSync width (% of line period) - default 8 */
+#define CVT_HSYNC_PERCENTAGE 8
+
+        float HBlankPercentage;
+        int VSyncAndBackPorch, VBackPorch;
+        int HBlank;
+
+        /* 8. Estimated Horizontal period */
+        HPeriod = ((float) (1000000.0 / VFieldRate - CVT_MIN_VSYNC_BP)) /
+            (VDisplayRnd + 2 * VMargin + CVT_MIN_V_PORCH + Interlace);
+
+        /* 9. Find number of lines in sync + backporch */
+        if (((int) (CVT_MIN_VSYNC_BP / HPeriod) + 1) <
+            (VSync + CVT_MIN_V_PORCH))
+            VSyncAndBackPorch = VSync + CVT_MIN_V_PORCH;
+        else
+            VSyncAndBackPorch = (int) (CVT_MIN_VSYNC_BP / HPeriod) + 1;
+
+        /* 10. Find number of lines in back porch */
+        VBackPorch = VSyncAndBackPorch - VSync;
+        (void) VBackPorch;
+
+        /* 11. Find total number of lines in vertical field */
+        modeinfo.vTotal =
+            VDisplayRnd + 2 * VMargin + VSyncAndBackPorch + Interlace +
+            CVT_MIN_V_PORCH;
+
+        /* 5) Definition of Horizontal blanking time limitation */
+        /* Gradient (%/kHz) - default 600 */
+#define CVT_M_FACTOR 600
+
+        /* Offset (%) - default 40 */
+#define CVT_C_FACTOR 40
+
+        /* Blanking time scaling factor - default 128 */
+#define CVT_K_FACTOR 128
+
+        /* Scaling factor weighting - default 20 */
+#define CVT_J_FACTOR 20
+
+#define CVT_M_PRIME CVT_M_FACTOR * CVT_K_FACTOR / 256
+#define CVT_C_PRIME (CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \
+        CVT_J_FACTOR
+
+        /* 12. Find ideal blanking duty cycle from formula */
+        HBlankPercentage = CVT_C_PRIME - CVT_M_PRIME * HPeriod / 1000.0;
+
+        /* 13. Blanking time */
+        if (HBlankPercentage < 20)
+            HBlankPercentage = 20;
+
+        HBlank = modeinfo.width * HBlankPercentage / (100.0 - HBlankPercentage);
+        HBlank -= HBlank % (2 * CVT_H_GRANULARITY);
+
+        /* 14. Find total number of pixels in a line. */
+        modeinfo.hTotal = modeinfo.width + HBlank;
+
+        /* Fill in HSync values */
+        modeinfo.hSyncEnd = modeinfo.width + HBlank / 2;
+
+        modeinfo.hSyncStart = modeinfo.hSyncEnd -
+            (modeinfo.hTotal * CVT_HSYNC_PERCENTAGE) / 100;
+        modeinfo.hSyncStart += CVT_H_GRANULARITY -
+            modeinfo.hSyncStart % CVT_H_GRANULARITY;
+
+        /* Fill in VSync values */
+        modeinfo.vSyncStart = modeinfo.height + CVT_MIN_V_PORCH;
+        modeinfo.vSyncEnd = modeinfo.vSyncStart + VSync;
+
+    }
+    else {                      /* Reduced blanking */
+        /* Minimum vertical blanking interval time (µs) - default 460 */
+#define CVT_RB_MIN_VBLANK 460.0
+
+        /* Fixed number of clocks for horizontal sync */
+#define CVT_RB_H_SYNC 32.0
+
+        /* Fixed number of clocks for horizontal blanking */
+#define CVT_RB_H_BLANK 160.0
+
+        /* Fixed number of lines for vertical front porch - default 3 */
+#define CVT_RB_VFPORCH 3
+
+        int VBILines;
+
+        /* 8. Estimate Horizontal period. */
+        HPeriod = ((float) (1000000.0 / VFieldRate - CVT_RB_MIN_VBLANK)) /
+            (VDisplayRnd + 2 * VMargin);
+
+        /* 9. Find number of lines in vertical blanking */
+        VBILines = ((float) CVT_RB_MIN_VBLANK) / HPeriod + 1;
+
+        /* 10. Check if vertical blanking is sufficient */
+        if (VBILines < (CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH))
+            VBILines = CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH;
+
+        /* 11. Find total number of lines in vertical field */
+        modeinfo.vTotal = VDisplayRnd + 2 * VMargin + Interlace + VBILines;
+
+        /* 12. Find total number of pixels in a line */
+        modeinfo.hTotal = modeinfo.width + CVT_RB_H_BLANK;
+
+        /* Fill in HSync values */
+        modeinfo.hSyncEnd = modeinfo.width + CVT_RB_H_BLANK / 2;
+        modeinfo.hSyncStart = modeinfo.hSyncEnd - CVT_RB_H_SYNC;
+
+        /* Fill in VSync values */
+        modeinfo.vSyncStart = modeinfo.height + CVT_RB_VFPORCH;
+        modeinfo.vSyncEnd = modeinfo.vSyncStart + VSync;
+    }
+
+    /* 15/13. Find pixel clock frequency (kHz for xf86) */
+    modeinfo.dotClock = modeinfo.hTotal * 1000.0 / HPeriod;
+    modeinfo.dotClock -= modeinfo.dotClock % CVT_CLOCK_STEP;
+    modeinfo.dotClock *= 1000.0;
+#if 0
+    /* 16/14. Find actual Horizontal Frequency (kHz) */
+    modeinfo.hSync = ((float) modeinfo.dotClock) / ((float) modeinfo.hTotal);
+#endif
+
+#if 0
+    /* 17/15. Find actual Field rate */
+    modeinfo.vRefresh = (1000.0 * ((float) modeinfo.dotClock)) /
+        ((float) (modeinfo.hTotal * modeinfo.vTotal));
+#endif
+
+    /* 18/16. Find actual vertical frame frequency */
+    /* ignore - just set the mode flag for interlaced */
+    if (Interlaced)
+        modeinfo.vTotal *= 2;
+
+    if (Reduced)
+        modeinfo.modeFlags |= RR_HSyncPositive | RR_VSyncNegative;
+    else
+        modeinfo.modeFlags |= RR_HSyncNegative | RR_VSyncPositive;
+
+    if (Interlaced)
+        modeinfo.modeFlags |= RR_Interlace;
+
+    snprintf(name, sizeof name, "%dx%d@%.1fHz",
+             modeinfo.width, modeinfo.height, VRefresh);
+    modeinfo.nameLength = strlen(name);
+
+    return RRModeGet(&modeinfo, name);
+}
diff --git a/hw/xmir/xmir-input.c b/hw/xmir/xmir-input.c
new file mode 100644
index 0000000..758c7ae
--- /dev/null
+++ b/hw/xmir/xmir-input.c
@@ -0,0 +1,592 @@
+/*
+ * Copyright © 2015-2016 Canonical Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "xmir.h"
+
+#include <linux/input.h>
+
+#include <sys/mman.h>
+#include <xkbsrv.h>
+#include <xserver-properties.h>
+#include <inpututils.h>
+
+static void
+xmir_pointer_control(DeviceIntPtr device, PtrCtrl *ctrl)
+{
+    /* Nothing to do, dix handles all settings */
+}
+
+static int
+xmir_pointer_proc(DeviceIntPtr device, int what)
+{
+#define NBUTTONS 10
+#define NAXES 2
+    BYTE map[NBUTTONS + 1];
+    int i = 0;
+    Atom btn_labels[NBUTTONS] = { 0 };
+    Atom axes_labels[NAXES] = { 0 };
+
+    switch (what) {
+    case DEVICE_INIT:
+        device->public.on = FALSE;
+
+        for (i = 1; i <= NBUTTONS; i++)
+            map[i] = i;
+
+        btn_labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT);
+        btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE);
+        btn_labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);
+        btn_labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP);
+        btn_labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN);
+        btn_labels[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT);
+        btn_labels[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT);
+        /* Don't know about the rest */
+
+        axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X);
+        axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y);
+
+        if (!InitValuatorClassDeviceStruct(device, 2, btn_labels,
+                                           GetMotionHistorySize(), Absolute))
+            return BadValue;
+
+        /* Valuators */
+        InitValuatorAxisStruct(device, 0, axes_labels[0],
+                               0, 0xFFFF, 10000, 0, 10000, Absolute);
+        InitValuatorAxisStruct(device, 1, axes_labels[1],
+                               0, 0xFFFF, 10000, 0, 10000, Absolute);
+
+        if (!InitPtrFeedbackClassDeviceStruct(device, xmir_pointer_control))
+            return BadValue;
+
+        if (!InitButtonClassDeviceStruct(device, 3, btn_labels, map))
+            return BadValue;
+
+        return Success;
+
+    case DEVICE_ON:
+        device->public.on = TRUE;
+        return Success;
+
+    case DEVICE_OFF:
+    case DEVICE_CLOSE:
+        device->public.on = FALSE;
+        return Success;
+    }
+
+    return BadMatch;
+
+#undef NBUTTONS
+#undef NAXES
+}
+
+static void
+xmir_keyboard_control(DeviceIntPtr device, KeybdCtrl *ctrl)
+{
+}
+
+static int
+xmir_keyboard_proc(DeviceIntPtr device, int what)
+{
+    switch (what) {
+    case DEVICE_INIT:
+        device->public.on = FALSE;
+        if (!InitKeyboardDeviceStructFromString(device,
+                                                NULL /*xmir_input->keymap*/, 0,
+                                                NULL, xmir_keyboard_control))
+            return BadValue;
+
+        return Success;
+    case DEVICE_ON:
+        device->public.on = TRUE;
+        return Success;
+
+    case DEVICE_OFF:
+    case DEVICE_CLOSE:
+        device->public.on = FALSE;
+        return Success;
+    }
+
+    return BadMatch;
+}
+
+static void
+pointer_convert_xy(struct xmir_input *xmir_input,
+                   struct xmir_window *xmir_window,
+                   int *x, int *y)
+{
+    bool reflect_x = false;
+    bool reflect_y = false;
+    bool swap_xy = false;
+    int dx = xmir_window->window->drawable.x;
+    int dy = xmir_window->window->drawable.y;
+    int sx = *x, sy = *y;
+    int w = xmir_window->window->drawable.width;
+    int h = xmir_window->window->drawable.height;
+
+    /* reflection test parameters */
+    bool magic_x_invert = false, magic_y_invert = false;
+
+    DebugF("Raw input %i,%i in window (%i,%i)->(%i,%i) orientation %i\n", *x, *y, dx, dy, dx + w, dy + h, xmir_window->orientation);
+
+    if (magic_x_invert)
+        reflect_x = !reflect_x;
+
+    if (magic_y_invert)
+        reflect_y = !reflect_y;
+
+    switch (xmir_window->orientation) {
+    case 90:
+        reflect_x = !reflect_x; swap_xy = true; break;
+    case 180:
+        reflect_x = !reflect_x; reflect_y = !reflect_y; break;
+    case 270:
+        reflect_y = !reflect_y; swap_xy = true; break;
+    }
+
+    if (!swap_xy) {
+        sx = *x;
+        sy = *y;
+    } else {
+        sx = *y;
+        sy = *x;
+    }
+
+    if (!reflect_x)
+        *x = sx + dx;
+    else
+        *x = w + dx - sx;
+
+    if (!reflect_y)
+        *y = sy + dy;
+    else
+        *y = h + dy - sy;
+
+    DebugF("Converted to %i, %i\n", *x, *y);
+}
+
+static Bool
+pointer_ensure_focus(struct xmir_input *xmir_input,
+                     struct xmir_window *xmir_window,
+                     DeviceIntPtr dev, int sx, int sy)
+{
+    ScreenPtr screen = xmir_window->window->drawable.pScreen;
+
+    if (xmir_input->focus_window == xmir_window)
+        return FALSE;
+
+    if (xmir_input->focus_window) {
+        xmir_input->focus_window = NULL;
+        CheckMotion(NULL, GetMaster(dev, MASTER_POINTER));
+    }
+
+    xmir_input->focus_window = xmir_window;
+
+    pointer_convert_xy(xmir_input, xmir_window, &sx, &sy);
+
+    (screen->SetCursorPosition) (dev, screen, sx, sy, TRUE);
+    CheckMotion(NULL, GetMaster(dev, MASTER_POINTER));
+
+    return TRUE;
+}
+
+static void
+pointer_handle_motion(struct xmir_input *xmir_input,
+                      struct xmir_window *xmir_window,
+                      MirPointerEvent const *pev)
+{
+    int sx = mir_pointer_event_axis_value(pev, mir_pointer_axis_x);
+    int sy = mir_pointer_event_axis_value(pev, mir_pointer_axis_y);
+    int vscroll = 0;
+    ValuatorMask mask;
+
+    pointer_ensure_focus(xmir_input, xmir_window, xmir_input->pointer, sx, sy);
+
+    pointer_convert_xy(xmir_input, xmir_window, &sx, &sy);
+
+    valuator_mask_zero(&mask);
+    valuator_mask_set(&mask, 0, sx);
+    valuator_mask_set(&mask, 1, sy);
+
+    QueuePointerEvents(xmir_input->pointer, MotionNotify, 0,
+                       POINTER_ABSOLUTE | POINTER_SCREEN, &mask);
+
+    /* Mouse wheel: Moving the wheel is a press+release of button 4/5 */
+    vscroll = mir_pointer_event_axis_value(pev, mir_pointer_axis_vscroll);
+    if (vscroll) {
+        int button = vscroll < 0 ? 5 : 4;
+        valuator_mask_zero(&mask);
+        QueuePointerEvents(xmir_input->pointer, ButtonPress, button, 0, &mask);
+        QueuePointerEvents(xmir_input->pointer, ButtonRelease, button, 0, &mask);
+    }
+}
+
+static void
+pointer_handle_button(struct xmir_input *xmir_input,
+                      struct xmir_window *xmir_window,
+                      MirPointerEvent const *pev)
+{
+    DeviceIntPtr dev = xmir_input->pointer;
+    struct {MirPointerButton mir_button; int x_button;} map[3] =
+    {
+        {mir_pointer_button_primary, 1},   /* Usually left button */
+        {mir_pointer_button_secondary, 3}, /* Middle button */
+        {mir_pointer_button_tertiary, 2},  /* Right button */
+    };
+    int i;
+    ValuatorMask mask;
+    valuator_mask_zero(&mask);
+
+    for (i = 0; i < 3; ++i) {
+        MirPointerButton mir_button = map[i].mir_button;
+        int x_button = map[i].x_button;
+        int oldstate = BitIsOn(dev->button->down, x_button) ?
+                       ButtonPress : ButtonRelease;
+        int newstate = mir_pointer_event_button_state(pev, mir_button) ?
+                       ButtonPress : ButtonRelease;
+
+        if (oldstate != newstate)
+            QueuePointerEvents(dev, newstate, x_button, 0, &mask);
+    }
+
+    /* XXX: Map rest of input buttons too! */
+}
+
+static DeviceIntPtr
+add_device(struct xmir_input *xmir_input,
+           const char *driver, DeviceProc device_proc)
+{
+    DeviceIntPtr dev = NULL;
+    static Atom type_atom;
+    char name[32];
+
+    dev = AddInputDevice(serverClient, device_proc, TRUE);
+    if (dev == NULL)
+        return NULL;
+
+    if (type_atom == None)
+        type_atom = MakeAtom(driver, strlen(driver), TRUE);
+    snprintf(name, sizeof name, "%s:%d", driver, xmir_input->id);
+    AssignTypeAndName(dev, type_atom, name);
+    dev->public.devicePrivate = xmir_input;
+    dev->type = SLAVE;
+    dev->spriteInfo->spriteOwner = FALSE;
+
+    return dev;
+}
+
+static void
+xmir_input_destroy(struct xmir_input *xmir_input)
+{
+    RemoveDevice(xmir_input->pointer, FALSE);
+    RemoveDevice(xmir_input->keyboard, FALSE);
+    free(xmir_input);
+}
+
+Bool
+LegalModifier(unsigned int key, DeviceIntPtr pDev)
+{
+    return TRUE;
+}
+
+void
+ProcessInputEvents(void)
+{
+    mieqProcessInputEvents();
+}
+
+void
+DDXRingBell(int volume, int pitch, int duration)
+{
+}
+
+static WindowPtr
+xmir_xy_to_window(ScreenPtr screen, SpritePtr sprite, int x, int y)
+{
+    struct xmir_input *xmir_input = NULL;
+    DeviceIntPtr device;
+
+    for (device = inputInfo.devices; device; device = device->next) {
+        if (device->deviceProc == xmir_pointer_proc &&
+            device->spriteInfo->sprite == sprite) {
+            xmir_input = device->public.devicePrivate;
+            break;
+        }
+    }
+
+    if (xmir_input == NULL) {
+        /* XTEST device */
+        sprite->spriteTraceGood = 1;
+        return sprite->spriteTrace[0];
+    }
+
+    if (xmir_input->focus_window) {
+        sprite->spriteTraceGood = 2;
+        sprite->spriteTrace[1] = xmir_input->focus_window->window;
+        return miSpriteTrace(sprite, x, y);
+    }
+    else {
+        sprite->spriteTraceGood = 1;
+        return sprite->spriteTrace[0];
+    }
+}
+
+static void
+fake_touch_move(struct xmir_input *xmir_input, struct xmir_window *xmir_window, int sx, int sy)
+{
+    ValuatorMask mask;
+
+    pointer_convert_xy(xmir_input, xmir_window, &sx, &sy);
+
+    valuator_mask_zero(&mask);
+    valuator_mask_set(&mask, 0, sx);
+    valuator_mask_set(&mask, 1, sy);
+
+    QueuePointerEvents(xmir_input->touch, MotionNotify, 0,
+                       POINTER_ABSOLUTE | POINTER_SCREEN, &mask);
+}
+
+static void
+xmir_window_handle_input_event(struct xmir_input *xmir_input,
+                               struct xmir_window *xmir_window,
+                               MirInputEvent const* ev)
+{
+    switch (mir_input_event_get_type(ev)) {
+    case mir_input_event_type_key: {
+        MirKeyboardEvent const *kev;
+        MirKeyboardAction action;
+
+        kev = mir_input_event_get_keyboard_event(ev);
+        action = mir_keyboard_event_action(kev);
+
+        QueueKeyboardEvents(xmir_input->keyboard,
+                            action == mir_keyboard_action_up ? KeyRelease : KeyPress,
+                            mir_keyboard_event_scan_code(kev) + 8);
+        break;
+    }
+    case mir_input_event_type_touch: {
+        MirTouchEvent const *tev;
+        int i = 0, count, sx, sy;
+        ValuatorMask mask;
+
+        tev = mir_input_event_get_touch_event(ev);
+        count = mir_touch_event_point_count(tev);
+
+        /* Do we really need this multifinger tracking at all?... */
+        if (count < 1) {
+            xmir_input->touch_id = -1;
+            break;
+        }
+
+        if (xmir_input->touch_id != -1) {
+            for (i = 0; i < count; ++i)
+                if (mir_touch_event_id(tev, i) == xmir_input->touch_id)
+                    break;
+        }
+        if (i >= count) {
+            for (i = 0; i < count; ++i)
+                if (mir_touch_event_action(tev, i) == mir_touch_action_down)
+                    break;
+        }
+
+        if (i >= count)
+            break;
+
+        sx = mir_touch_event_axis_value(tev, i, mir_touch_axis_x);
+        sy = mir_touch_event_axis_value(tev, i, mir_touch_axis_y);
+        valuator_mask_zero(&mask);
+
+        switch (mir_touch_event_action(tev, i)) {
+        case mir_touch_action_up:
+            fake_touch_move(xmir_input, xmir_window, sx, sy);
+            QueuePointerEvents(xmir_input->touch, ButtonRelease, 1, 0, &mask);
+            xmir_input->touch_id = -1;
+            break;
+        case mir_touch_action_down:
+            xmir_input->touch_id = mir_touch_event_id(tev, i);
+            if (!pointer_ensure_focus(xmir_input, xmir_window, xmir_input->touch, sx, sy))
+                fake_touch_move(xmir_input, xmir_window, sx, sy);
+            QueuePointerEvents(xmir_input->touch, ButtonPress, 1, 0, &mask);
+            break;
+        case mir_touch_action_change:
+            fake_touch_move(xmir_input, xmir_window, sx, sy);
+            break;
+        }
+        break;
+
+
+    }
+    case mir_input_event_type_pointer: {
+        MirPointerEvent const *pev;
+
+        pev = mir_input_event_get_pointer_event(ev);
+        switch (mir_pointer_event_action(pev)) {
+        case mir_pointer_action_button_up:
+        case mir_pointer_action_button_down:
+            pointer_handle_motion(xmir_input, xmir_window, pev);
+            pointer_handle_button(xmir_input, xmir_window, pev);
+            break;
+        case mir_pointer_action_motion:
+            pointer_handle_motion(xmir_input, xmir_window, pev);
+            break;
+        default:
+            ErrorF("Unknown action: %u\n", mir_pointer_event_action(pev));
+        case mir_pointer_action_enter:
+        case mir_pointer_action_leave:
+            break;
+        }
+        break;
+    }
+    default: ErrorF("Unknown input type: %u\n", mir_input_event_get_type(ev));
+    }
+}
+
+static void
+xmir_handle_keymap_event(struct xmir_input *xmir_input,
+                                MirKeymapEvent const* ev)
+{
+    char * buffer = NULL;
+    size_t length = 0;
+    DeviceIntPtr master;
+    XkbDescPtr xkb;
+    XkbChangesRec changes = { 0 };
+
+    mir_keymap_event_get_keymap_buffer(ev, (char const **)&buffer, &length);
+
+    buffer[length] = '\0';
+
+    xkb = XkbCompileKeymapFromString(xmir_input->keyboard, buffer, length);
+
+    XkbUpdateDescActions(xkb, xkb->min_key_code, XkbNumKeys(xkb), &changes);
+
+    XkbDeviceApplyKeymap(xmir_input->keyboard, xkb);
+
+    master = GetMaster(xmir_input->keyboard, MASTER_KEYBOARD);
+    if (master && master->lastSlave == xmir_input->keyboard)
+        XkbDeviceApplyKeymap(master, xkb);
+
+    XkbFreeKeyboard(xkb, XkbAllComponentsMask, TRUE);
+}
+
+static void
+xmir_handle_surface_event_in_main_thread(struct xmir_screen *xmir_screen,
+                                         struct xmir_window *xmir_window,
+                                         void *arg)
+{
+    const MirEvent *ev = arg;
+    struct xmir_input *xmir_input = xorg_list_first_entry(&xmir_screen->input_list, struct xmir_input, link);
+
+    switch (mir_event_get_type(ev))
+    {
+    case mir_event_type_input:
+        xmir_window_handle_input_event(xmir_input, xmir_window, mir_event_get_input_event(ev));
+        break;
+    case mir_event_type_surface:
+        xmir_handle_surface_event(xmir_window, mir_surface_event_get_attribute(mir_event_get_surface_event(ev)), mir_surface_event_get_attribute_value(mir_event_get_surface_event(ev)));
+        break;
+    case mir_event_type_resize: {
+        WindowPtr window = xmir_window->window;
+        const MirResizeEvent *resize = mir_event_get_resize_event(ev);
+        unsigned future_width = mir_resize_event_get_width(resize);
+        unsigned future_height = mir_resize_event_get_height(resize);
+        XMIR_DEBUG(("Mir surface for win %p resized to %ux%u (buffers arriving soon)\n",
+                    window, future_width, future_height));
+        xmir_window->surface_width = future_width;
+        xmir_window->surface_height = future_height;
+        if (xmir_window->damage)
+            DamageDamageRegion(&window->drawable, &xmir_window->region);
+        }
+        break;
+    case mir_event_type_prompt_session_state_change:
+        ErrorF("No idea about prompt_session_state_change\n");
+        break;
+    case mir_event_type_orientation:
+        xmir_output_handle_orientation(xmir_window, mir_orientation_event_get_direction(mir_event_get_orientation_event(ev)));
+        break;
+    case mir_event_type_close_surface:
+        xmir_close_surface(xmir_window);
+        break;
+    case mir_event_type_surface_output:
+        break;
+    case mir_event_type_keymap:
+        xmir_handle_keymap_event(xmir_input, mir_event_get_keymap_event(ev));
+        break;
+    default:
+        ErrorF("Received an unknown %u event\n", mir_event_get_type(ev));
+        break;
+    }
+    mir_event_unref(ev);
+}
+
+void
+xmir_surface_handle_event(MirSurface *surface, MirEvent const* ev,
+                          void *context)
+{
+    struct xmir_window *xmir_window = context;
+    struct xmir_screen *xmir_screen = xmir_window->xmir_screen;
+
+    /* We are in a Mir event thread, so unsafe to do X things. Post the event
+     * to the X event loop thread...
+     */
+    xmir_post_to_eventloop(&xmir_handle_surface_event_in_main_thread,
+        xmir_screen, xmir_window, (void*)mir_event_ref(ev));
+}
+
+void
+InitInput(int argc, char *argv[])
+{
+    ScreenPtr pScreen = screenInfo.screens[0];
+    struct xmir_screen *xmir_screen = xmir_screen_get(pScreen);
+    struct xmir_input *xmir_input;
+
+    if (xmir_screen->rootless)
+        pScreen->XYToWindow = xmir_xy_to_window;
+
+    mieqInit();
+
+    xmir_input = calloc(1, sizeof(*xmir_input));
+    if (!xmir_input)
+        FatalError("Failed to allocate input\n");
+
+    xmir_input->xmir_screen = xmir_screen;
+    xorg_list_add(&xmir_input->link, &xmir_screen->input_list);
+    xmir_input->touch_id = -1;
+    xmir_input->pointer = add_device(xmir_input, "xmir-pointer", xmir_pointer_proc);
+    xmir_input->touch = add_device(xmir_input, "xmir-fake-touch-pointer", xmir_pointer_proc);
+    xmir_input->keyboard = add_device(xmir_input, "xmir-keyboard", xmir_keyboard_proc);
+}
+
+void
+CloseInput(void)
+{
+    ScreenPtr pScreen = screenInfo.screens[0];
+    struct xmir_screen *xmir_screen = xmir_screen_get(pScreen);
+    struct xmir_input *xmir_input, *next_xmir_input;
+
+    xorg_list_for_each_entry_safe(xmir_input, next_xmir_input,
+                                  &xmir_screen->input_list, link)
+        xmir_input_destroy(xmir_input);
+
+    mieqFini();
+}
diff --git a/hw/xmir/xmir-output.c b/hw/xmir/xmir-output.c
new file mode 100644
index 0000000..1eb356d
--- /dev/null
+++ b/hw/xmir/xmir-output.c
@@ -0,0 +1,424 @@
+/*
+ * Copyright © 2015-2016 Canonical Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include "xmir.h"
+#include <randrstr.h>
+#include "mipointer.h"
+
+static const char*
+xmir_get_output_type_str(MirDisplayOutput *mir_output)
+{
+    const char *str = "Invalid";
+
+    switch(mir_output->type)
+    {
+    case mir_display_output_type_vga: str = "VGA"; break;
+    case mir_display_output_type_dvii: str = "DVI"; break;
+    case mir_display_output_type_dvid: str = "DVI"; break;
+    case mir_display_output_type_dvia: str = "DVI"; break;
+    case mir_display_output_type_composite: str = "Composite"; break;
+    case mir_display_output_type_svideo: str = "TV"; break;
+    case mir_display_output_type_lvds: str = "LVDS"; break;
+    case mir_display_output_type_component: str = "CTV"; break;
+    case mir_display_output_type_ninepindin: str = "DIN"; break;
+    case mir_display_output_type_displayport: str = "DP"; break;
+    case mir_display_output_type_hdmia: str = "HDMI"; break;
+    case mir_display_output_type_hdmib: str = "HDMI"; break;
+    case mir_display_output_type_tv: str = "TV"; break;
+    case mir_display_output_type_edp: str = "eDP"; break;
+    case mir_display_output_type_unknown: str = "None"; break;
+    default: break;
+    }
+
+    return str;
+}
+
+static Rotation
+to_rr_rotation(MirOrientation orient)
+{
+    switch (orient) {
+    default: return RR_Rotate_0;
+    case mir_orientation_left: return RR_Rotate_90;
+    case mir_orientation_inverted: return RR_Rotate_180;
+    case mir_orientation_right: return RR_Rotate_270;
+    }
+}
+
+Bool
+xmir_output_dpms(struct xmir_screen *xmir_screen, int mode)
+{
+    MirDisplayConfiguration *display_config = xmir_screen->display;
+    MirPowerMode mir_mode = mir_power_mode_on;
+    Bool unchanged = TRUE;
+
+    if (xmir_screen->rootless)
+        return FALSE;
+
+    switch (mode) {
+    case DPMSModeOn:      mir_mode = mir_power_mode_on; break;
+    case DPMSModeStandby: mir_mode = mir_power_mode_standby; break;
+    case DPMSModeSuspend: mir_mode = mir_power_mode_suspend; break;
+    case DPMSModeOff:     mir_mode = mir_power_mode_off; break;
+    }
+
+    DebugF("Setting DPMS mode to %d\n", mode);
+
+    for (int i = 0; i < display_config->num_outputs; i++) {
+        if (display_config->outputs[i].power_mode != mir_mode) {
+            display_config->outputs[i].power_mode = mir_mode;
+            unchanged = FALSE;
+        }
+    }
+
+    if (!unchanged)
+        mir_wait_for(mir_connection_apply_display_config(xmir_screen->conn, xmir_screen->display));
+
+    return TRUE;
+}
+
+static void
+xmir_output_update(struct xmir_output *xmir_output, MirDisplayOutput *mir_output)
+{
+    RROutputSetConnection(xmir_output->randr_output, mir_output->connected ? RR_Connected : RR_Disconnected);
+    RROutputSetSubpixelOrder(xmir_output->randr_output, SubPixelUnknown);
+
+    if (mir_output->connected && mir_output->used) {
+        MirDisplayMode *mode = &mir_output->modes[mir_output->current_mode];
+        RRModePtr randr_mode;
+
+        xmir_output->width = mode->horizontal_resolution;
+        xmir_output->height = mode->vertical_resolution;
+        xmir_output->x = mir_output->position_x;
+        xmir_output->y = mir_output->position_y;
+
+        randr_mode = xmir_cvt(xmir_output->width, xmir_output->height, mode->refresh_rate, 0, 0);
+        /* Odd resolutions like 1366x768 don't show correctly otherwise */
+        randr_mode->mode.width = mode->horizontal_resolution;
+        randr_mode->mode.height = mode->vertical_resolution;
+        sprintf(randr_mode->name, "%dx%d@%.1fHz",
+                randr_mode->mode.width, randr_mode->mode.height, mode->refresh_rate);
+
+        RROutputSetPhysicalSize(xmir_output->randr_output, mir_output->physical_width_mm, mir_output->physical_height_mm);
+        RROutputSetModes(xmir_output->randr_output, &randr_mode, 1, 1);
+
+        /* TODO: Hook up subpixel order when available (LP: #1393578) */
+        RRCrtcNotify(xmir_output->randr_crtc, randr_mode,
+                     xmir_output->x, xmir_output->y,
+                     to_rr_rotation(mir_output->orientation), NULL, 1, &xmir_output->randr_output);
+    } else {
+        xmir_output->width = xmir_output->height = xmir_output->x = xmir_output->y = 0;
+
+        RROutputSetPhysicalSize(xmir_output->randr_output, 0, 0);
+        RROutputSetModes(xmir_output->randr_output, NULL, 0, 0);
+
+        RRCrtcNotify(xmir_output->randr_crtc, NULL,
+                     0, 0, RR_Rotate_0, NULL, 1, &xmir_output->randr_output);
+    }
+}
+
+static void
+xmir_output_screen_resized(struct xmir_screen *xmir_screen)
+{
+    ScreenPtr screen = xmir_screen->screen;
+    struct xmir_output *xmir_output;
+    int width, height;
+
+    width = 0;
+    height = 0;
+    xorg_list_for_each_entry(xmir_output, &xmir_screen->output_list, link) {
+        if (width < xmir_output->x + xmir_output->width)
+            width = xmir_output->x + xmir_output->width;
+        if (height < xmir_output->y + xmir_output->height)
+            height = xmir_output->y + xmir_output->height;
+    }
+
+    screen->width = width;
+    screen->height = height;
+    if (ConnectionInfo)
+        RRScreenSizeNotify(xmir_screen->screen);
+    update_desktop_dimensions();
+}
+
+static void
+xmir_output_create(struct xmir_screen *xmir_screen, MirDisplayOutput *mir_output, const char *name)
+{
+    struct xmir_output *xmir_output;
+
+    xmir_output = calloc(sizeof *xmir_output, 1);
+    if (xmir_output == NULL) {
+        FatalError("No memory for creating output\n");
+        return;
+    }
+
+    xmir_output->xmir_screen = xmir_screen;
+    xmir_output->randr_crtc = RRCrtcCreate(xmir_screen->screen, xmir_output);
+    xmir_output->randr_output = RROutputCreate(xmir_screen->screen, name, strlen(name), xmir_output);
+
+    RRCrtcGammaSetSize(xmir_output->randr_crtc, 256);
+    RROutputSetCrtcs(xmir_output->randr_output, &xmir_output->randr_crtc, 1);
+    xorg_list_append(&xmir_output->link, &xmir_screen->output_list);
+    if (mir_output)
+        xmir_output_update(xmir_output, mir_output);
+}
+
+void
+xmir_output_destroy(struct xmir_output *xmir_output)
+{
+    xorg_list_del(&xmir_output->link);
+    free(xmir_output);
+}
+
+static Bool
+xmir_randr_get_info(ScreenPtr pScreen, Rotation * rotations)
+{
+    *rotations = 0;
+
+    return TRUE;
+}
+
+static Bool
+xmir_randr_set_config(ScreenPtr pScreen,
+                     Rotation rotation, int rate, RRScreenSizePtr pSize)
+{
+    return FALSE;
+}
+
+static void
+xmir_update_config(struct xmir_screen *xmir_screen)
+{
+    MirDisplayConfiguration *new_config;
+    MirDisplayOutput **mir_output;
+    struct xmir_output *xmir_output;
+
+    if (xmir_screen->windowed)
+        return;
+
+    new_config = mir_connection_create_display_config(xmir_screen->conn);
+    if (new_config->num_outputs != xmir_screen->display->num_outputs)
+        FatalError("Number of outputs changed on update.\n");
+
+    mir_display_config_destroy(xmir_screen->display);
+    xmir_screen->display = new_config;
+
+    mir_output = &new_config->outputs;
+    xorg_list_for_each_entry(xmir_output, &xmir_screen->output_list, link) {
+        xmir_output_update(xmir_output, *mir_output);
+        mir_output++;
+    }
+    xmir_output_screen_resized(xmir_screen);
+}
+
+void
+xmir_output_handle_orientation(struct xmir_window *xmir_window, MirOrientation dir)
+{
+    XMIR_DEBUG(("Orientation: %i\n", dir));
+
+    xmir_output_handle_resize(xmir_window, -1, -1);
+}
+
+void
+xmir_output_handle_resize(struct xmir_window *xmir_window, int width, int height)
+{
+    WindowPtr window = xmir_window->window;
+    ScreenPtr screen = window->drawable.pScreen;
+    struct xmir_screen *xmir_screen = xmir_screen_get(screen);
+    PixmapPtr pixmap, old_pixmap;
+    DrawablePtr oldroot = &screen->root->drawable;
+    BoxRec box;
+    BoxRec copy_box;
+    int window_width, window_height;
+    DeviceIntPtr pDev;
+    int src_stride, dst_stride, bpp, line_len, y;
+    const char *src;
+    char *dst;
+
+    MirOrientation old = xmir_window->orientation;
+    xmir_window->orientation = mir_surface_get_orientation(xmir_window->surface);
+
+    if (width < 0 && height < 0) {
+        if (old % 180 == xmir_window->orientation % 180) {
+            window_width = window->drawable.width;
+            window_height = window->drawable.height;
+        } else {
+            window_width = window->drawable.height;
+            window_height = window->drawable.width;
+        }
+    } else if (xmir_window->orientation == 0 || xmir_window->orientation == 180) {
+        window_width = width;
+        window_height = height;
+    } else {
+        window_width = height;
+        window_height = width;
+    }
+
+    if (window_width == window->drawable.width &&
+        window_height == window->drawable.height) {
+        /* Damage window if rotated */
+        if (old != xmir_window->orientation)
+            DamageDamageRegion(&window->drawable, &xmir_window->region);
+        return;
+    }
+
+    if (xmir_screen->rootless)
+        return;
+
+    if (!xmir_screen->windowed) {
+        xmir_screen->windowed = 1;
+
+        XMIR_DEBUG(("Root resized, removing all outputs and inserting fake output\n"));
+
+        while (!xorg_list_is_empty(&xmir_screen->output_list)) {
+            struct xmir_output *xmir_output = xorg_list_first_entry(&xmir_screen->output_list, typeof(*xmir_output), link);
+
+            RRCrtcDestroy(xmir_output->randr_crtc);
+            RROutputDestroy(xmir_output->randr_output);
+            xmir_output_destroy(xmir_output);
+        }
+    }
+
+    XMIR_DEBUG(("Output resized %ix%i with rotation %i\n",
+                width, height, xmir_window->orientation));
+
+    pixmap = screen->CreatePixmap(screen, window_width, window_height, screen->rootDepth, CREATE_PIXMAP_USAGE_BACKING_PIXMAP);
+
+    copy_box.x1 = copy_box.y1 = 0;
+    copy_box.x2 = min(window_width, oldroot->width);
+    copy_box.y2 = min(window_height, oldroot->height);
+
+    old_pixmap = screen->GetWindowPixmap(window);
+    src_stride = old_pixmap->devKind;
+    dst_stride = pixmap->devKind;
+    bpp = oldroot->bitsPerPixel >> 3;
+    src = (char*)old_pixmap->devPrivate.ptr +
+          src_stride * copy_box.y1 +
+          copy_box.x1 * bpp;
+    dst = (char*)pixmap->devPrivate.ptr +
+          dst_stride * copy_box.y1 +
+          copy_box.x1 * bpp;
+    line_len = (copy_box.x2 - copy_box.x1) * bpp;
+    for (y = copy_box.y1; y < copy_box.y2; ++y) {
+        memcpy(dst, src, line_len);
+        /* Bother filling the edges?
+        memset(dst+line_len, 0, dst_stride-line_len);
+        */
+        src += src_stride;
+        dst += dst_stride;
+    }
+    /* Bother filling the edges?
+    if (y < window_height)
+        memset(dst, 0, (window_height - y) * dst_stride);
+    */
+
+    screen->width = window_width;
+    screen->height = window_height;
+    screen->mmWidth = screen->width * 254 / (10 * xmir_screen->dpi);
+    screen->mmHeight = screen->height * 254 / (10 * xmir_screen->dpi);
+
+    screen->SetScreenPixmap(pixmap);
+
+    SetRootClip(screen, TRUE);
+
+    box.x1 = box.y1 = 0;
+    box.x2 = window_width;
+    box.y2 = window_height;
+    RegionReset(&xmir_window->region, &box);
+    DamageDamageRegion(&window->drawable, &xmir_window->region);
+
+    /* Update cursor info too */
+    for (pDev = inputInfo.devices; pDev; pDev = pDev->next) {
+        int x;
+
+        if (!IsPointerDevice(pDev))
+            continue;
+
+        miPointerGetPosition(pDev, &x, &y);
+        UpdateSpriteForScreen(pDev, screen);
+        miPointerSetScreen(pDev, 0, x, y);
+    }
+
+    if (ConnectionInfo)
+        RRScreenSizeNotify(xmir_screen->screen);
+    update_desktop_dimensions();
+}
+
+static void
+xmir_handle_hotplug(struct xmir_screen *xmir_screen,
+                    struct xmir_window *unused1,
+                    void *unused2)
+{
+    xmir_update_config(xmir_screen);
+
+    /* Trigger RANDR refresh */
+    RRGetInfo(screenInfo.screens[0], TRUE);
+}
+
+static void
+xmir_display_config_callback(MirConnection *conn, void *ctx)
+{
+    struct xmir_screen *xmir_screen = ctx;
+    xmir_post_to_eventloop(xmir_handle_hotplug, xmir_screen, 0, 0);
+}
+
+Bool
+xmir_screen_init_output(struct xmir_screen *xmir_screen)
+{
+    rrScrPrivPtr rp;
+    int i;
+    MirDisplayConfiguration *display_config = xmir_screen->display;
+    int output_type_count[mir_display_output_type_edp + 1] = {};
+
+    if (!RRScreenInit(xmir_screen->screen))
+        return FALSE;
+
+    mir_connection_set_display_config_change_callback(xmir_screen->conn, &xmir_display_config_callback, xmir_screen);
+
+    for (i = 0; i < display_config->num_outputs; i++) {
+        char name[32];
+        MirDisplayOutput *mir_output = &display_config->outputs[i];
+        const char* output_type_str = xmir_get_output_type_str(mir_output);
+        int type_count = i;
+
+        if (mir_output->type >= 0 && mir_output->type <= mir_display_output_type_edp)
+            type_count = output_type_count[mir_output->type]++;
+
+        snprintf(name, sizeof name, "%s-%d", output_type_str, type_count);
+        xmir_output_create(xmir_screen, mir_output, name);
+    }
+
+    RRScreenSetSizeRange(xmir_screen->screen, 320, 200, INT16_MAX, INT16_MAX);
+
+    xmir_output_screen_resized(xmir_screen);
+
+    rp = rrGetScrPriv(xmir_screen->screen);
+    rp->rrGetInfo = xmir_randr_get_info;
+    rp->rrSetConfig = xmir_randr_set_config;
+    // TODO: rp->rrCrtcSet = xmir_randr_set_crtc;
+
+    return TRUE;
+}
diff --git a/hw/xmir/xmir-thread-proxy.c b/hw/xmir/xmir-thread-proxy.c
new file mode 100644
index 0000000..cadb6fd
--- /dev/null
+++ b/hw/xmir/xmir-thread-proxy.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright © 2012-2016 Canonical Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Soft-
+ * ware"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, provided that the above copyright
+ * notice(s) and this permission notice appear in all copies of the Soft-
+ * ware and that both the above copyright notice(s) and this permission
+ * notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
+ * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
+ * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
+ * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
+ * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR-
+ * MANCE OF THIS SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or
+ * other dealings in this Software without prior written authorization of
+ * the copyright holder.
+ *
+ * Authors:
+ *   Christopher James Halse Rogers (christopher.halse.rogers at canonical.com)
+ * Later rewritten, simplified and optimized by:
+ *   Daniel van Vugt <daniel.van.vugt at canonical.com>
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "xmir.h"
+
+struct message {
+    xmir_event_callback *callback;
+    struct xmir_screen *xmir_screen;
+    struct xmir_window *xmir_window;
+    void *arg;
+};
+
+static int pipefds[2];
+
+static void
+xmir_wakeup_handler(void* data, int err, void* read_mask)
+{
+    if (err >= 0 && FD_ISSET(pipefds[0], (fd_set *)read_mask))
+        xmir_process_from_eventloop();
+}
+
+void
+xmir_init_thread_to_eventloop(void)
+{
+	int err = pipe(pipefds);
+	if (err == -1)
+		FatalError("[XMIR] Failed to create thread-proxy pipes: %s\n", strerror(errno));
+
+	/* Set the read end to not block; we'll pull from this in the event loop
+	 * We don't need to care about the write end, as that'll be written to
+	 * from its own thread
+	 */
+	fcntl(pipefds[0], F_SETFL, O_NONBLOCK);
+
+	AddGeneralSocket(pipefds[0]);
+	RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
+				       xmir_wakeup_handler,
+				       NULL);
+}
+
+void
+xmir_fini_thread_to_eventloop(void)
+{
+	RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
+				     xmir_wakeup_handler, NULL);
+	RemoveGeneralSocket(pipefds[0]);
+	close(pipefds[1]);
+	close(pipefds[0]);
+}
+
+void
+xmir_post_to_eventloop(xmir_event_callback *cb,
+                       struct xmir_screen *s, struct xmir_window *w, void *a)
+{
+    struct message msg = {cb, s, w, a};
+    ssize_t written = write(pipefds[1], &msg, sizeof msg);
+    if (written != sizeof(msg))
+        ErrorF("[XMIR] Failed to proxy message to mainloop\n");
+}
+
+void
+xmir_process_from_eventloop(void)
+{
+    for (;;) {
+        struct message msg;
+        ssize_t got = read(pipefds[0], &msg, sizeof msg);
+        if (got < 0)
+            return;
+        if (got == sizeof(msg))
+            msg.callback(msg.xmir_screen, msg.xmir_window, msg.arg);
+    }
+}
diff --git a/hw/xmir/xmir.c b/hw/xmir/xmir.c
new file mode 100644
index 0000000..82b8daf
--- /dev/null
+++ b/hw/xmir/xmir.c
@@ -0,0 +1,1416 @@
+/*
+ * Copyright © 2015-2016 Canonical Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "xmir.h"
+
+#include <stdio.h>
+#include <signal.h>
+#include <dlfcn.h>
+
+#include <selection.h>
+#include <micmap.h>
+#include <misyncshm.h>
+#include <glx_extinit.h>
+#include <X11/Xatom.h>
+#include "propertyst.h"
+
+#include <mir_toolkit/mir_surface.h>
+
+#include "compint.h"
+#include "dpmsproc.h"
+
+static struct {
+    Atom UTF8_STRING;
+    Atom _NET_WM_NAME;
+    Atom WM_PROTOCOLS;
+    Atom WM_DELETE_WINDOW;
+    Atom _NET_WM_WINDOW_TYPE;
+    Atom _NET_WM_WINDOW_TYPE_DESKTOP;
+    Atom _NET_WM_WINDOW_TYPE_DOCK;
+    Atom _NET_WM_WINDOW_TYPE_TOOLBAR;
+    Atom _NET_WM_WINDOW_TYPE_MENU;
+    Atom _NET_WM_WINDOW_TYPE_UTILITY;
+    Atom _NET_WM_WINDOW_TYPE_SPLASH;
+    Atom _NET_WM_WINDOW_TYPE_DIALOG;
+    Atom _NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
+    Atom _NET_WM_WINDOW_TYPE_POPUP_MENU;
+    Atom _NET_WM_WINDOW_TYPE_TOOLTIP;
+    Atom _NET_WM_WINDOW_TYPE_NOTIFICATION;
+    Atom _NET_WM_WINDOW_TYPE_COMBO;
+    Atom _NET_WM_WINDOW_TYPE_DND;
+    Atom _NET_WM_WINDOW_TYPE_NORMAL;
+} known_atom;
+
+static Atom get_atom(const char *name, Atom *cache)
+{
+    if (!*cache) {
+        *cache = MakeAtom(name, strlen(name), FALSE);
+        if (*cache)
+            XMIR_DEBUG(("Atom %s = %lu\n", name, (unsigned long)*cache));
+    }
+    return *cache;
+}
+
+#define GET_ATOM(_a) get_atom(#_a, &known_atom._a)
+
+Bool xmir_debug_logging = FALSE;
+
+static const char get_title_from_top_window[] = "@";
+
+static void xmir_handle_buffer_received(MirBufferStream *stream, void *ctx);
+
+void
+ddxGiveUp(enum ExitCode error)
+{
+}
+
+void
+AbortDDX(enum ExitCode error)
+{
+    ddxGiveUp(error);
+}
+
+void
+OsVendorInit(void)
+{
+}
+
+void
+OsVendorFatalError(const char *f, va_list args)
+{
+}
+
+#if defined(DDXBEFORERESET)
+void
+ddxBeforeReset(void)
+{
+    return;
+}
+#endif
+
+void
+ddxUseMsg(void)
+{
+    ErrorF("-rootless              Run rootless\n");
+    ErrorF("  -flatten             Flatten rootless X windows into a single surface\n");
+    ErrorF("                       (Unity8 requires -flatten; LP: #1497085)\n");
+    ErrorF("    -neverclose        Never close the flattened rootless window\n");
+    ErrorF("                       (ugly workaround for Unity8 bug LP: #1501346)\n");
+    ErrorF("-title <name>          Set window title (@ = automatic)\n");
+    ErrorF("-fd <num>              force client connection on only fd\n");
+    ErrorF("-shared                open default listening sockets even when -fd is passed\n");
+    ErrorF("-mir <appid>           set mir's application id.\n");
+    ErrorF("-mirSocket <socket>    use the specified socket for mir\n");
+    ErrorF("-debug                 Log everything Xmir is doing\n");
+}
+
+int
+ddxProcessArgument(int argc, char *argv[], int i)
+{
+    static int seen_shared;
+
+    if (strcmp(argv[i], "-rootless") == 0 ||
+        strcmp(argv[i], "-flatten") == 0 ||
+        strcmp(argv[i], "-neverclose") == 0 ||
+        strcmp(argv[i], "-debug") == 0) {
+        return 1;
+    }
+    else if (strcmp(argv[i], "-mirSocket") == 0 ||
+             strcmp(argv[i], "-title") == 0 ||
+             strcmp(argv[i], "-mir") == 0) {
+        return 2;
+    } else if (!strcmp(argv[i], "-novtswitch") ||
+               !strncmp(argv[i], "vt", 2)) {
+        return 1;
+    /* Bypass unity8 "security" */
+    } else if (!strncmp(argv[i], "--desktop_file_hint=", strlen("--desktop_file_hint="))) {
+        return 1;
+    } else if (!strcmp(argv[i], "-fd")) {
+        if (!seen_shared)
+            NoListenAll = 1;
+
+        return 2;
+    } else if (!strcmp(argv[i], "-shared")) {
+        seen_shared = 1;
+        NoListenAll = 0;
+        return 1;
+    } else if (!strcmp(argv[i], "-listen")) {
+        seen_shared = 1;
+        NoListenAll = 0;
+        return 0;
+    }
+
+    return 0;
+}
+
+static DevPrivateKeyRec xmir_window_private_key;
+static DevPrivateKeyRec xmir_screen_private_key;
+static DevPrivateKeyRec xmir_pixmap_private_key;
+
+struct xmir_screen *
+xmir_screen_get(ScreenPtr screen)
+{
+    return dixLookupPrivate(&screen->devPrivates, &xmir_screen_private_key);
+}
+
+struct xmir_pixmap *
+xmir_pixmap_get(PixmapPtr pixmap)
+{
+    return dixLookupPrivate(&pixmap->devPrivates, &xmir_pixmap_private_key);
+}
+
+struct xmir_window *
+xmir_window_get(WindowPtr window)
+{
+    return dixLookupPrivate(&window->devPrivates, &xmir_window_private_key);
+}
+
+void
+xmir_pixmap_set(PixmapPtr pixmap, struct xmir_pixmap *xmir_pixmap)
+{
+    return dixSetPrivate(&pixmap->devPrivates, &xmir_pixmap_private_key, xmir_pixmap);
+}
+
+static Bool
+xmir_get_window_prop_string8(WindowPtr window, ATOM atom,
+                             char *buf, size_t bufsize)
+{
+    if (window->optional) {
+        PropertyPtr p = window->optional->userProps;
+        while (p) {
+            if (p->propertyName == atom) {
+                if ((  p->type == XA_STRING
+                    || p->type == GET_ATOM(UTF8_STRING)
+                    ) &&
+                    p->format == 8 && p->data) {
+                    size_t len = p->size >= bufsize ? bufsize - 1 : p->size;
+                    memcpy(buf, p->data, len);
+                    buf[len] = '\0';
+                    return TRUE;
+                } else {
+                    ErrorF("xmir_get_window_prop_string8: Atom %d is not "
+                           "an 8-bit string as expected\n", atom);
+                    break;
+                }
+            }
+            p = p->next;
+        }
+    }
+
+    if (bufsize)
+        buf[0] = '\0';
+    return FALSE;
+}
+
+static Bool
+xmir_get_window_name(WindowPtr window, char *buf, size_t bufsize)
+{
+    return xmir_get_window_prop_string8(window, GET_ATOM(_NET_WM_NAME),
+                                        buf, bufsize)
+        || xmir_get_window_prop_string8(window, XA_WM_NAME, buf, bufsize);
+}
+
+static WindowPtr
+xmir_get_window_prop_window(WindowPtr window, ATOM atom)
+{
+    if (window->optional) {
+        PropertyPtr p = window->optional->userProps;
+        while (p) {
+            if (p->propertyName == atom) {
+                if (p->type == XA_WINDOW) {
+                    WindowPtr ptr;
+                    XID id = *(XID*)p->data;
+                    if (dixLookupWindow(&ptr, id, serverClient,
+                                        DixReadAccess) != Success)
+                        ptr = NULL;
+                    return ptr;
+                } else {
+                    ErrorF("xmir_get_window_prop_window: Atom %d is not "
+                           "a Window as expected\n", atom);
+                    return NULL;
+                }
+            }
+            p = p->next;
+        }
+    }
+    return NULL;
+}
+
+static Atom
+xmir_get_window_prop_atom(WindowPtr window, ATOM name)
+{
+    if (window->optional) {
+        PropertyPtr p = window->optional->userProps;
+        while (p) {
+            if (p->propertyName == name) {
+                if (p->type == XA_ATOM) {
+                    return *(Atom*)p->data;
+                } else {
+                    ErrorF("xmir_get_window_prop_atom: Atom %d is not "
+                           "an Atom as expected\n", name);
+                    return 0;
+                }
+            }
+            p = p->next;
+        }
+    }
+    return 0;
+}
+
+static void
+damage_report(DamagePtr pDamage, RegionPtr pRegion, void *data)
+{
+    struct xmir_window *xmir_window = data;
+    struct xmir_screen *xmir_screen = xmir_window->xmir_screen;
+
+    xorg_list_add(&xmir_window->link_damage, &xmir_screen->damage_window_list);
+}
+
+static void
+damage_destroy(DamagePtr pDamage, void *data)
+{
+}
+
+static void
+xmir_window_enable_damage_tracking(struct xmir_window *xmir_win)
+{
+    WindowPtr win = xmir_win->window;
+
+    if (xmir_win->damage != NULL)
+        return;
+
+    xmir_win->damage = DamageCreate(damage_report, damage_destroy,
+                                    DamageReportNonEmpty, FALSE,
+                                    win->drawable.pScreen, xmir_win);
+    DamageRegister(&win->drawable, xmir_win->damage);
+    DamageSetReportAfterOp(xmir_win->damage, TRUE);
+}
+
+static void
+xmir_window_disable_damage_tracking(struct xmir_window *xmir_win)
+{
+    if (xmir_win->damage != NULL) {
+        DamageUnregister(xmir_win->damage);
+        DamageDestroy(xmir_win->damage);
+        xmir_win->damage = NULL;
+    }
+}
+
+static void
+xmir_sw_copy(struct xmir_screen *xmir_screen, struct xmir_window *xmir_win, RegionPtr dirty)
+{
+    PixmapPtr pix = xmir_screen->screen->GetWindowPixmap(xmir_win->window);
+    int x1 = dirty->extents.x1, y1 = dirty->extents.y1;
+    int x2 = dirty->extents.x2, y2 = dirty->extents.y2;
+    int y, line_len, src_stride = pix->devKind;
+    int bpp = pix->drawable.bitsPerPixel >> 3;
+    char *src, *dst;
+    MirGraphicsRegion region;
+
+    mir_buffer_stream_get_graphics_region(
+        mir_surface_get_buffer_stream(xmir_win->surface), &region);
+
+    /*
+     * Our window region (and hence damage region) might be a little ahead of
+     * the current buffer in terms of size, during a resize. So we must accept
+     * that their dimensions might not match and take the safe intersection...
+     */
+    if (x1 < 0) x1 = 0;
+    if (y1 < 0) y1 = 0;
+    if (x2 > region.width) x2 = region.width;
+    if (y2 > region.height) y2 = region.height;
+    if (x2 > pix->drawable.width) x2 = pix->drawable.width;
+    if (y2 > pix->drawable.height) y2 = pix->drawable.height;
+    if (x2 <= x1 || y2 <= y1) return;
+
+    src = (char*)pix->devPrivate.ptr + src_stride*y1 + x1*bpp;
+    dst = region.vaddr + y1*region.stride + x1*bpp;
+
+    line_len = (x2 - x1) * bpp;
+    for (y = y1; y < y2; ++y) {
+        memcpy(dst, src, line_len);
+        src += src_stride;
+        dst += region.stride;
+    }
+}
+
+static void
+xmir_get_current_buffer_dimensions(
+    struct xmir_screen *xmir_screen, struct xmir_window *xmir_win,
+    int *width, int *height)
+{
+    MirGraphicsRegion reg;
+    MirBufferStream *stream = mir_surface_get_buffer_stream(xmir_win->surface);
+
+    mir_buffer_stream_get_graphics_region(stream, &reg);
+    *width = reg.width;
+    *height = reg.height;
+}
+
+void xmir_repaint(struct xmir_window *xmir_win)
+{
+    struct xmir_screen *xmir_screen = xmir_screen_get(xmir_win->window->drawable.pScreen);
+    RegionPtr dirty = &xmir_win->region;
+    MirBufferStream *stream = mir_surface_get_buffer_stream(xmir_win->surface);
+    char wm_name[256];
+    WindowPtr named = NULL;
+
+    if (!xmir_win->has_free_buffer)
+        ErrorF("ERROR: xmir_repaint requested without a buffer to paint to\n");
+
+    if (strcmp(xmir_screen->title, get_title_from_top_window)) {
+        /* Fixed title mode. Never change it. */
+        named = NULL;
+    } else if (xmir_screen->rootless) {
+        named = xmir_win->window;
+    } else { /* Try and guess from the most relevant app window */
+        WindowPtr top = xmir_screen->screen->root->firstChild;
+        WindowPtr top_named = NULL;
+        WindowPtr top_normal = NULL;
+
+        while (top) {
+            Atom wm_type;
+            WindowPtr app_window;
+            if (!top->viewable) {
+                top = top->nextSib;
+                continue;
+            }
+            app_window = xmir_get_window_prop_window(top, XA_WM_TRANSIENT_FOR);
+            if (app_window) {
+                named = app_window;
+                break;
+            }
+            wm_type = xmir_get_window_prop_atom(top,
+                                               GET_ATOM(_NET_WM_WINDOW_TYPE));
+            if (wm_type && wm_type == GET_ATOM(_NET_WM_WINDOW_TYPE_NORMAL))
+                top_normal = top;
+            if (xmir_get_window_name(top, wm_name, sizeof wm_name))
+                top_named = top;
+
+            top = top->firstChild;
+        }
+        if (!named)
+            named = top_normal ? top_normal : top_named;
+    }
+
+    if (named &&
+        xmir_get_window_name(named, wm_name, sizeof wm_name) &&
+        strcmp(wm_name, xmir_win->wm_name)) {
+        MirSurfaceSpec *rename =
+            mir_connection_create_spec_for_changes(xmir_screen->conn);
+        mir_surface_spec_set_name(rename, wm_name);
+        mir_surface_apply_spec(xmir_win->surface, rename);
+        mir_surface_spec_release(rename);
+        strncpy(xmir_win->wm_name, wm_name, sizeof(xmir_win->wm_name));
+    }
+
+    xmir_sw_copy(xmir_screen, xmir_win, dirty);
+    xmir_win->has_free_buffer = FALSE;
+    mir_buffer_stream_swap_buffers(stream, xmir_handle_buffer_received, xmir_win);
+
+    DamageEmpty(xmir_win->damage);
+    xorg_list_del(&xmir_win->link_damage);
+}
+
+void
+xmir_handle_buffer_available(struct xmir_screen *xmir_screen,
+                             struct xmir_window *xmir_win,
+                             void *unused)
+{
+    int buf_width, buf_height;
+    Bool xserver_lagging, xclient_lagging;
+
+    if (!xmir_win->damage || !mir_surface_is_valid(xmir_win->surface)) {
+        if (xmir_win->damage)
+            ErrorF("Buffer-available recieved for invalid surface?\n");
+        return;
+    }
+
+    DebugF("Buffer-available on %p\n", xmir_win);
+    xmir_get_current_buffer_dimensions(xmir_screen, xmir_win,
+                                       &buf_width, &buf_height);
+
+    xmir_win->has_free_buffer = TRUE;
+    xmir_win->buf_width = buf_width;
+    xmir_win->buf_height = buf_height;
+
+    xserver_lagging = buf_width != xmir_win->surface_width ||
+                      buf_height != xmir_win->surface_height;
+
+    xclient_lagging = buf_width != xmir_win->window->drawable.width ||
+                      buf_height != xmir_win->window->drawable.height;
+
+    if (xclient_lagging) {
+        if (xmir_screen->rootless) {
+            XID vlist[2] = {buf_width, buf_height};
+            ConfigureWindow(xmir_win->window, CWWidth|CWHeight, vlist,
+                            serverClient);
+        } else {
+            /* Output resizing takes time, so start it going and let it
+             * finish next frame or so...
+             */
+            xmir_output_handle_resize(xmir_win, buf_width, buf_height);
+        }
+        /* Admittedly the client won't have time to redraw itself in the
+         * new size before the below repaint, but the important bit is that
+         * the X server is using the correct buffer dimensions immediately.
+         */
+    }
+
+    if (xserver_lagging || !xorg_list_is_empty(&xmir_win->link_damage))
+        xmir_repaint(xmir_win);
+
+    if (xserver_lagging)
+        DamageDamageRegion(&xmir_win->window->drawable, &xmir_win->region);
+}
+
+static void
+xmir_handle_buffer_received(MirBufferStream *stream, void *ctx)
+{
+    struct xmir_window *xmir_win = ctx;
+    struct xmir_screen *xmir_screen = xmir_screen_get(xmir_win->window->drawable.pScreen);
+
+    xmir_post_to_eventloop(xmir_handle_buffer_available, xmir_screen,
+                           xmir_win, 0);
+}
+
+static Bool
+xmir_create_window(WindowPtr window)
+{
+    ScreenPtr screen = window->drawable.pScreen;
+    struct xmir_screen *xmir_screen = xmir_screen_get(screen);
+    struct xmir_window *xmir_window = calloc(sizeof(*xmir_window), 1);
+    Bool ret;
+
+    if (!xmir_window)
+        return FALSE;
+
+    xmir_window->xmir_screen = xmir_screen;
+    xmir_window->window = window;
+    xorg_list_init(&xmir_window->link_damage);
+    xorg_list_init(&xmir_window->link_flattened);
+
+    screen->CreateWindow = xmir_screen->CreateWindow;
+    ret = (*screen->CreateWindow) (window);
+    xmir_screen->CreateWindow = screen->CreateWindow;
+    screen->CreateWindow = xmir_create_window;
+
+    if (ret)
+        dixSetPrivate(&window->devPrivates, &xmir_window_private_key, xmir_window);
+    else
+        free(xmir_window);
+
+    return ret;
+}
+
+static void
+xmir_window_update_region(struct xmir_window *xmir_window)
+{
+    WindowPtr window = xmir_window->window;
+    BoxRec box = {0, 0, window->drawable.width, window->drawable.height};
+    RegionReset(&xmir_window->region, &box);
+}
+
+static Bool
+xmir_realize_window(WindowPtr window)
+{
+    ScreenPtr screen = window->drawable.pScreen;
+    struct xmir_screen *xmir_screen = xmir_screen_get(screen);
+    struct xmir_window *xmir_window = xmir_window_get(window);
+    Bool ret;
+    MirPixelFormat pixel_format = mir_pixel_format_invalid;
+    Atom wm_type = 0;
+    int mir_width = window->drawable.width;
+    int mir_height = window->drawable.height;
+    MirSurfaceSpec* spec = NULL;
+    WindowPtr wm_transient_for = NULL, positioning_parent = NULL;
+    char wm_name[1024];
+
+    screen->RealizeWindow = xmir_screen->RealizeWindow;
+    ret = (*screen->RealizeWindow) (window);
+    xmir_screen->RealizeWindow = screen->RealizeWindow;
+    screen->RealizeWindow = xmir_realize_window;
+
+    if (xmir_screen->rootless && !window->parent) {
+        RegionNull(&window->clipList);
+        RegionNull(&window->borderClip);
+        RegionNull(&window->winSize);
+    }
+    xmir_window_update_region(xmir_window);
+
+    xmir_get_window_name(window, wm_name, sizeof wm_name);
+    wm_type = xmir_get_window_prop_atom(window, GET_ATOM(_NET_WM_WINDOW_TYPE));
+    wm_transient_for = xmir_get_window_prop_window(window, XA_WM_TRANSIENT_FOR);
+
+    XMIR_DEBUG(("Realize %swindow %p \"%s\": %dx%d %+d%+d parent=%p\n"
+           "\tdepth=%d redir=%u type=%hu class=%u visibility=%u viewable=%u\n"
+           "\toverride=%d _NET_WM_WINDOW_TYPE=%lu WM_TRANSIENT_FOR=%p\n",
+           window == screen->root ? "ROOT " : "",
+           window, wm_name, mir_width, mir_height,
+           window->drawable.x, window->drawable.y,
+           window->parent,
+           window->drawable.depth,
+           window->redirectDraw, window->drawable.type,
+           window->drawable.class, window->visibility, window->viewable,
+           window->overrideRedirect, (unsigned long)wm_type, wm_transient_for));
+
+    if (!window->viewable) {
+        return ret;
+    } else if (xmir_screen->rootless) {
+        if (!window->parent || window->parent == screen->root) {
+            compRedirectWindow(serverClient, window,
+                               CompositeRedirectManual);
+            compRedirectSubwindows(serverClient, window,
+                                   CompositeRedirectAutomatic);
+        }
+        if (window->redirectDraw != RedirectDrawManual)
+            return ret;
+    } else if (window->parent) {
+        return ret;
+    }
+
+    if (window->drawable.depth == 32)
+        pixel_format = xmir_screen->depth32_pixel_format;
+    else if (window->drawable.depth == 24)
+        pixel_format = xmir_screen->depth24_pixel_format;
+    else {
+        ErrorF("No pixel format available for depth %d\n",
+               (int)window->drawable.depth);
+        return FALSE;
+    }
+
+    /* TODO: Replace pixel_format with the actual right answer from the
+     *       graphics driver when using EGL:
+     *         mir_connection_get_egl_pixel_format()
+     */
+
+    if (!wm_type)   /* Avoid spurious matches with undetected types */
+        wm_type = -1;
+
+    positioning_parent = wm_transient_for;
+    if (!positioning_parent) {
+        /* The toolkit has not provided a definite positioning parent so the
+         * next best option is to guess. But we can only reasonably guess for
+         * window types that are typically subordinate to normal windows...
+         */
+        Bool is_subordinate = wm_type == GET_ATOM(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)
+                           || wm_type == GET_ATOM(_NET_WM_WINDOW_TYPE_POPUP_MENU)
+                           || wm_type == GET_ATOM(_NET_WM_WINDOW_TYPE_MENU)
+                           || wm_type == GET_ATOM(_NET_WM_WINDOW_TYPE_COMBO)
+                           || wm_type == GET_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR)
+                           || wm_type == GET_ATOM(_NET_WM_WINDOW_TYPE_UTILITY)
+                           || wm_type == GET_ATOM(_NET_WM_WINDOW_TYPE_TOOLTIP)
+                           || (wm_type == -1 && window->overrideRedirect);
+
+        if (is_subordinate)
+            positioning_parent = xmir_screen->last_focus;
+    }
+
+    if (xmir_screen->flatten && xmir_screen->flatten_top) {
+        WindowPtr top = xmir_screen->flatten_top->window;
+        int dx = window->drawable.x - top->drawable.x;
+        int dy = window->drawable.y - top->drawable.y;
+        xorg_list_append(&xmir_window->link_flattened,
+                         &xmir_screen->flattened_list);
+        ReparentWindow(window, top, dx, dy, serverClient);
+        XMIR_DEBUG(("Flattened window %p (reparented under %p %+d%+d)\n",
+                    window, top, dx, dy));
+        /* And thanks to the X Composite extension, window will now be
+         * automatically composited into the existing flatten_top surface
+         * so we retain only a single Mir surface, as Unity8 likes to see.
+         */
+        return ret;
+    }
+
+    if (xmir_screen->neverclosed) {
+        spec = mir_connection_create_spec_for_changes(xmir_screen->conn);
+    } else if (positioning_parent) {
+        struct xmir_window *rel = xmir_window_get(positioning_parent);
+        if (rel && rel->surface) {
+            short dx = window->drawable.x - rel->window->drawable.x;
+            short dy = window->drawable.y - rel->window->drawable.y;
+            MirRectangle placement = {dx, dy, 0, 0};
+
+            if (wm_type == GET_ATOM(_NET_WM_WINDOW_TYPE_TOOLTIP)) {
+                spec = mir_connection_create_spec_for_tooltip(
+                    xmir_screen->conn, mir_width, mir_height, pixel_format,
+                    rel->surface, &placement);
+            } else if (wm_type == GET_ATOM(_NET_WM_WINDOW_TYPE_DIALOG)) {
+                spec = mir_connection_create_spec_for_modal_dialog(
+                    xmir_screen->conn, mir_width, mir_height, pixel_format,
+                    rel->surface);
+            } else {  /* Probably a menu. If not, still close enough... */
+                MirEdgeAttachment edge = mir_edge_attachment_any;
+                if (wm_type == GET_ATOM(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU))
+                    edge = mir_edge_attachment_vertical;
+                spec = mir_connection_create_spec_for_menu(
+                    xmir_screen->conn,
+                    mir_width, mir_height, pixel_format, rel->surface,
+                    &placement, edge);
+            }
+        }
+    }
+
+    if (!spec) {
+        if (wm_type == GET_ATOM(_NET_WM_WINDOW_TYPE_DIALOG)) {
+            spec = mir_connection_create_spec_for_dialog(
+                xmir_screen->conn, mir_width, mir_height, pixel_format);
+        } else {
+            spec = mir_connection_create_spec_for_normal_surface(
+                xmir_screen->conn, mir_width, mir_height, pixel_format);
+        }
+    }
+
+    if (strcmp(xmir_screen->title, get_title_from_top_window))
+        mir_surface_spec_set_name(spec, xmir_screen->title);
+    else if (xmir_screen->rootless)
+        mir_surface_spec_set_name(spec, wm_name);
+
+    xmir_window->surface_width = mir_width;
+    xmir_window->surface_height = mir_height;
+    xmir_window->buf_width = mir_width;
+    xmir_window->buf_height = mir_height;
+
+    if (xmir_screen->neverclosed) {
+        mir_surface_spec_set_width(spec, mir_width);
+        mir_surface_spec_set_height(spec, mir_height);
+        mir_surface_spec_set_pixel_format(spec, pixel_format);
+
+        xmir_window->surface = xmir_screen->neverclosed;
+        mir_surface_apply_spec(xmir_window->surface, spec);
+    } else {
+        mir_surface_spec_set_buffer_usage(spec,
+                                          mir_buffer_usage_software);
+        xmir_window->surface = mir_surface_create_sync(spec);
+    }
+    mir_surface_spec_release(spec);
+
+    xmir_window->has_free_buffer = TRUE;
+    if (!mir_surface_is_valid(xmir_window->surface)) {
+        ErrorF("failed to create a surface: %s\n", mir_surface_get_error_message(xmir_window->surface));
+        return FALSE;
+    }
+    if (!xmir_screen->flatten_top)
+        xmir_screen->flatten_top = xmir_window;
+    mir_surface_set_event_handler(xmir_window->surface, xmir_surface_handle_event, xmir_window);
+
+    xmir_window_enable_damage_tracking(xmir_window);
+
+    return ret;
+}
+
+static const char *
+xmir_surface_type_str(MirSurfaceType type)
+{
+    return "unk";
+}
+
+static const char *
+xmir_surface_state_str(MirSurfaceState state)
+{
+    switch (state) {
+    case mir_surface_state_unknown: return "unknown";
+    case mir_surface_state_restored: return "restored";
+    case mir_surface_state_minimized: return "minimized";
+    case mir_surface_state_maximized: return "maximized";
+    case mir_surface_state_vertmaximized: return "vert maximized";
+    case mir_surface_state_fullscreen: return "fullscreen";
+    default: return "???";
+    }
+}
+
+static const char *
+xmir_surface_focus_str(MirSurfaceFocusState focus)
+{
+    switch (focus) {
+    case mir_surface_unfocused: return "unfocused";
+    case mir_surface_focused: return "focused";
+    default: return "???";
+    }
+}
+
+static const char *
+xmir_surface_vis_str(MirSurfaceVisibility vis)
+{
+    switch (vis) {
+    case mir_surface_visibility_occluded: return "hidden";
+    case mir_surface_visibility_exposed: return "visible";
+    default: return "???";
+    }
+}
+
+void
+xmir_handle_surface_event(struct xmir_window *xmir_window, MirSurfaceAttrib attr, int val)
+{
+    switch (attr) {
+    case mir_surface_attrib_type:
+        XMIR_DEBUG(("Type: %s\n", xmir_surface_type_str(val)));
+        break;
+    case mir_surface_attrib_state:
+        XMIR_DEBUG(("State: %s\n", xmir_surface_state_str(val)));
+        break;
+    case mir_surface_attrib_swapinterval:
+        XMIR_DEBUG(("Swap interval: %i\n", val));
+        break;
+    case mir_surface_attrib_focus:
+        XMIR_DEBUG(("Focus: %s\n", xmir_surface_focus_str(val)));
+        if (xmir_window->surface) {  /* It's a real Mir window */
+            xmir_window->xmir_screen->last_focus =
+                (val == mir_surface_focused) ? xmir_window->window : NULL;
+        }
+        break;
+    case mir_surface_attrib_dpi:
+        XMIR_DEBUG(("DPI: %i\n", val));
+        break;
+    case mir_surface_attrib_visibility:
+        XMIR_DEBUG(("Visibility: %s\n", xmir_surface_vis_str(val)));
+        break;
+    default:
+        XMIR_DEBUG(("Unhandled attribute %i\n", attr));
+        break;
+    }
+}
+
+void
+xmir_close_surface(struct xmir_window *xmir_window)
+{
+    WindowPtr window = xmir_window->window;
+    struct xmir_screen *xmir_screen = xmir_screen_get(window->drawable.pScreen);
+
+    if (xmir_screen->rootless) {
+        xEvent event;
+        event.u.u.type = ClientMessage;
+        event.u.u.detail = 32;
+        event.u.clientMessage.window = window->drawable.id;
+        event.u.clientMessage.u.l.type = GET_ATOM(WM_PROTOCOLS);
+        event.u.clientMessage.u.l.longs0 = GET_ATOM(WM_DELETE_WINDOW);
+        event.u.clientMessage.u.l.longs1 = CurrentTime;
+        DeliverEvents(window, &event, 1, NullWindow);
+    } else {
+        ErrorF("Root window closed, shutting down Xmir\n");
+        GiveUp(0);
+        /*DeleteWindow(window, 1); ? */
+    }
+}
+
+static void
+xmir_unmap_input(struct xmir_screen *xmir_screen, WindowPtr window)
+{
+    struct xmir_input *xmir_input;
+
+    xorg_list_for_each_entry(xmir_input, &xmir_screen->input_list, link) {
+        if (xmir_input->focus_window && xmir_input->focus_window->window == window)
+            xmir_input->focus_window = NULL;
+    }
+}
+
+static void
+xmir_bequeath_surface(struct xmir_window *dying, struct xmir_window *benef)
+{
+    struct xmir_screen *xmir_screen = benef->xmir_screen;
+    struct xmir_window *other;
+
+    XMIR_DEBUG(("flatten bequeath: %p --> %p\n",
+                dying->window, benef->window));
+
+    assert(!benef->surface);
+    benef->surface = dying->surface;
+    dying->surface = NULL;
+
+    ReparentWindow(benef->window, xmir_screen->screen->root,
+                   0, 0, serverClient);
+    compRedirectWindow(serverClient, benef->window, CompositeRedirectManual);
+    compRedirectSubwindows(serverClient, benef->window, CompositeRedirectAutomatic);
+
+    xorg_list_for_each_entry(other, &xmir_screen->flattened_list,
+                             link_flattened) {
+        ReparentWindow(other->window, benef->window, 0, 0, serverClient);
+    }
+
+    mir_surface_set_event_handler(benef->surface, xmir_surface_handle_event,
+                                  benef);
+
+    xmir_window_enable_damage_tracking(benef);
+}
+
+static void
+xmir_clear_to_black(MirSurface *surface)
+{   /* Admittedly, this will only work for software surfaces */
+    MirBufferStream *stream = mir_surface_get_buffer_stream(surface);
+    MirGraphicsRegion region;
+
+    /* On error mir_buffer_stream_get_graphics_region leaves us uninitialized */
+    region.pixel_format = mir_pixel_format_invalid;
+    mir_buffer_stream_get_graphics_region(stream, &region);
+
+    switch (region.pixel_format) {
+        case mir_pixel_format_invalid: return; /* Probably hardware surface */
+        case mir_pixel_format_abgr_8888:
+        case mir_pixel_format_xbgr_8888:
+        case mir_pixel_format_argb_8888:
+        case mir_pixel_format_xrgb_8888: {
+            int y;
+            uint32_t *dest = (uint32_t*)region.vaddr;
+            for (y = 0; y < region.height; ++y) {
+                int x;
+                for (x = 0; x < region.width; ++x)
+                    dest[x] = 0xff000000;
+                dest = (uint32_t*)((char*)dest + region.stride);
+            }
+            break;
+        }
+        case mir_pixel_format_bgr_888:
+        case mir_pixel_format_rgb_888: {
+            int y;
+            char *dest = region.vaddr;
+            for (y = 0; y < region.height; ++y) {
+                memset(dest, 0, region.width*3);
+                dest += region.stride;
+            }
+            break;
+        }
+        case mir_pixel_format_rgb_565:
+        case mir_pixel_format_rgba_5551:
+        case mir_pixel_format_rgba_4444: {
+            uint16_t fill = 0;
+            int y;
+            uint16_t *dest = (uint16_t*)region.vaddr;
+            switch (region.pixel_format) {
+                case mir_pixel_format_rgb_565:   fill = 0x0000; break;
+                case mir_pixel_format_rgba_5551: fill = 0x0001; break;
+                case mir_pixel_format_rgba_4444: fill = 0x000f; break;
+                default: fill = 0;
+            }
+            for (y = 0; y < region.height; ++y) {
+                int x;
+                for (x = 0; x < region.width; ++x)
+                    dest[x] = fill;
+                dest = (uint16_t*)((char*)dest + region.stride);
+            }
+            break;
+        }
+        default:
+            return;
+    }
+    mir_buffer_stream_swap_buffers(stream, NULL, NULL);
+}
+
+static void
+xmir_unmap_surface(struct xmir_screen *xmir_screen, WindowPtr window, BOOL destroyed)
+{
+    struct xmir_window *xmir_window =
+        dixLookupPrivate(&window->devPrivates, &xmir_window_private_key);
+
+    if (!xmir_window)
+        return;
+
+    XMIR_DEBUG(("Unmap/unrealize window %p\n", window));
+
+    if (!destroyed)
+        xmir_window_disable_damage_tracking(xmir_window);
+    else
+        xmir_window->damage = NULL;
+
+    xorg_list_del(&xmir_window->link_damage);
+
+    xorg_list_del(&xmir_window->link_flattened);
+
+    if (!xmir_window->surface)
+        return;
+
+    mir_surface_set_event_handler(xmir_window->surface, NULL, NULL);
+
+    if (xmir_screen->flatten && xmir_screen->flatten_top == xmir_window) {
+        xmir_screen->flatten_top = NULL;
+        if (!xorg_list_is_empty(&xmir_screen->flattened_list)) {
+            xmir_screen->flatten_top =
+                xorg_list_first_entry(&xmir_screen->flattened_list,
+                                      struct xmir_window,
+                                      link_flattened);
+            xorg_list_del(&xmir_screen->flatten_top->link_flattened);
+            xmir_bequeath_surface(xmir_window, xmir_screen->flatten_top);
+        }
+    }
+
+    if (xmir_window->surface) {
+        if (xmir_screen->neverclose) {
+            xmir_screen->neverclosed = xmir_window->surface;
+            xmir_clear_to_black(xmir_screen->neverclosed);
+        } else {
+            mir_surface_release_sync(xmir_window->surface);
+        }
+
+        xmir_window->surface = NULL;
+    }
+
+    /* drain all events from input and damage to prevent a race condition after mir_surface_release_sync */
+    xmir_process_from_eventloop();
+
+    RegionUninit(&xmir_window->region);
+}
+
+static Bool
+xmir_unrealize_window(WindowPtr window)
+{
+    ScreenPtr screen = window->drawable.pScreen;
+    struct xmir_screen *xmir_screen = xmir_screen_get(screen);
+    Bool ret;
+
+    if (window == xmir_screen->last_focus)
+        xmir_screen->last_focus = NULL;
+
+    xmir_unmap_input(xmir_screen, window);
+
+    screen->UnrealizeWindow = xmir_screen->UnrealizeWindow;
+    ret = (*screen->UnrealizeWindow) (window);
+    xmir_screen->UnrealizeWindow = screen->UnrealizeWindow;
+    screen->UnrealizeWindow = xmir_unrealize_window;
+
+    xmir_unmap_surface(xmir_screen, window, FALSE);
+
+    return ret;
+}
+
+static Bool
+xmir_destroy_window(WindowPtr window)
+{
+    ScreenPtr screen = window->drawable.pScreen;
+    struct xmir_screen *xmir_screen = xmir_screen_get(screen);
+    Bool ret;
+
+    xmir_unmap_input(xmir_screen, window);
+    xmir_unmap_surface(xmir_screen, window, TRUE);
+
+    screen->DestroyWindow = xmir_screen->DestroyWindow;
+    ret = (*screen->DestroyWindow) (window);
+    xmir_screen->DestroyWindow = screen->DestroyWindow;
+    screen->DestroyWindow = xmir_destroy_window;
+
+    return ret;
+}
+
+static void
+xmir_resize_window(WindowPtr window, int x, int y,
+                   unsigned int w, unsigned int h, WindowPtr sib)
+{
+    ScreenPtr screen = window->drawable.pScreen;
+    struct xmir_screen *xmir_screen = xmir_screen_get(screen);
+    struct xmir_window *xmir_window = xmir_window_get(window);
+
+    screen->ResizeWindow = xmir_screen->ResizeWindow;
+    (*screen->ResizeWindow) (window, x, y, w, h, sib);
+    xmir_screen->ResizeWindow = screen->ResizeWindow;
+    screen->ResizeWindow = xmir_resize_window;
+
+    if (xmir_window->surface) {
+        /* This is correct in theory but most Mir shells don't do it yet */
+        MirSurfaceSpec *changes =
+            mir_connection_create_spec_for_changes(xmir_screen->conn);
+        mir_surface_spec_set_width(changes, w);
+        mir_surface_spec_set_height(changes, h);
+        mir_surface_apply_spec(xmir_window->surface, changes);
+        mir_surface_spec_release(changes);
+
+        XMIR_DEBUG(("X window %p resized to %ux%u %+d%+d with sibling %p\n",
+                    window, w, h, x, y, sib));
+    }
+
+    xmir_window_update_region(xmir_window);
+}
+
+static Bool
+xmir_close_screen(ScreenPtr screen)
+{
+    struct xmir_screen *xmir_screen = xmir_screen_get(screen);
+    struct xmir_output *xmir_output, *next_xmir_output;
+    Bool ret;
+
+    screen->CloseScreen = xmir_screen->CloseScreen;
+    ret = screen->CloseScreen(screen);
+
+    xorg_list_for_each_entry_safe(xmir_output, next_xmir_output,
+                                  &xmir_screen->output_list, link)
+        xmir_output_destroy(xmir_output);
+
+    mir_display_config_destroy(xmir_screen->display);
+    mir_connection_release(xmir_screen->conn);
+
+    xmir_fini_thread_to_eventloop();
+    free(xmir_screen);
+
+    return ret;
+}
+
+static Bool
+xmir_is_unblank(int mode)
+{
+    switch (mode) {
+    case SCREEN_SAVER_OFF:
+    case SCREEN_SAVER_FORCER:
+        return TRUE;
+    case SCREEN_SAVER_ON:
+    case SCREEN_SAVER_CYCLE:
+        return FALSE;
+    default:
+        ErrorF("Unexpected save screen mode: %d\n", mode);
+        return TRUE;
+    }
+}
+
+Bool
+DPMSSupported(void)
+{
+    struct xmir_screen *xmir_screen = xmir_screen_get(screenInfo.screens[0]);
+    return !xmir_screen->rootless;
+}
+
+int
+DPMSSet(ClientPtr client, int level)
+{
+    int rc = Success;
+    struct xmir_screen *xmir_screen = xmir_screen_get(screenInfo.screens[0]);
+
+    DPMSPowerLevel = level;
+
+    if (level != DPMSModeOn) {
+        if (xmir_is_unblank(screenIsSaved))
+            rc = dixSaveScreens(client, SCREEN_SAVER_FORCER, ScreenSaverActive);
+    } else {
+        if (!xmir_is_unblank(screenIsSaved))
+            rc = dixSaveScreens(client, SCREEN_SAVER_OFF, ScreenSaverReset);
+    }
+
+    if (rc != Success)
+        return rc;
+
+    xmir_output_dpms(xmir_screen, level);
+
+    return Success;
+}
+
+static Bool
+xmir_save_screen(ScreenPtr screen, int mode)
+{
+    struct xmir_screen *xmir_screen = xmir_screen_get(screen);
+
+    if (xmir_is_unblank(mode))
+        return xmir_output_dpms(xmir_screen, DPMSModeOn);
+    else
+        return xmir_output_dpms(xmir_screen, DPMSModeOff);
+}
+
+static void
+xmir_block_handler(ScreenPtr screen, void *ptv, void *read_mask)
+{
+    struct xmir_screen *xmir_screen = xmir_screen_get(screen);
+    struct xmir_window *xmir_window, *next;
+
+    xorg_list_for_each_entry_safe(xmir_window, next,
+                                  &xmir_screen->damage_window_list,
+                                  link_damage) {
+        if (xmir_window->has_free_buffer) {
+            xmir_repaint(xmir_window);
+        }
+    }
+}
+
+static Bool
+xmir_create_screen_resources(ScreenPtr screen)
+{
+    struct xmir_screen *xmir_screen = xmir_screen_get(screen);
+    int ret;
+
+    screen->CreateScreenResources = xmir_screen->CreateScreenResources;
+    ret = (*screen->CreateScreenResources) (screen);
+    xmir_screen->CreateScreenResources = screen->CreateScreenResources;
+    screen->CreateScreenResources = xmir_create_screen_resources;
+
+    if (!ret)
+        return ret;
+
+    if (!xmir_screen->rootless)
+        screen->devPrivate = screen->CreatePixmap(screen, screen->width, screen->height, screen->rootDepth, CREATE_PIXMAP_USAGE_BACKING_PIXMAP);
+    else
+        screen->devPrivate = fbCreatePixmap(screen, 0, 0, screen->rootDepth, 0);
+
+    if (!screen->devPrivate)
+        return FALSE;
+
+    return TRUE;
+}
+
+struct xmir_visit_set_pixmap_window {
+    PixmapPtr old, new;
+};
+
+static int
+xmir_visit_set_window_pixmap(WindowPtr window, void *data)
+{
+    struct xmir_visit_set_pixmap_window *visit = data;
+
+    if (fbGetWindowPixmap(window) == visit->old) {
+        window->drawable.pScreen->SetWindowPixmap(window, visit->new);
+        return WT_WALKCHILDREN;
+    }
+
+    return WT_DONTWALKCHILDREN;
+}
+
+static void
+xmir_set_screen_pixmap(PixmapPtr pixmap)
+{
+    ScreenPtr screen = pixmap->drawable.pScreen;
+    PixmapPtr old_front = screen->devPrivate;
+    WindowPtr root;
+
+    root = screen->root;
+    if (root) {
+        struct xmir_visit_set_pixmap_window visit = { old_front, pixmap };
+        assert(fbGetWindowPixmap(root) == old_front);
+        TraverseTree(root, xmir_visit_set_window_pixmap, &visit);
+        assert(fbGetWindowPixmap(root) == pixmap);
+    }
+
+    screen->devPrivate = pixmap;
+
+    if (old_front)
+        screen->DestroyPixmap(old_front);
+}
+
+static Bool
+xmir_screen_init(ScreenPtr pScreen, int argc, char **argv)
+{
+    struct xmir_screen *xmir_screen;
+    MirConnection *conn;
+    Pixel red_mask, blue_mask, green_mask;
+    int ret, bpc, i;
+    int client_fd = -1;
+    char *socket = NULL;
+    const char *appid = "XMIR";
+    unsigned int formats, f;
+    MirPixelFormat format[1024];
+
+    if (!dixRegisterPrivateKey(&xmir_screen_private_key, PRIVATE_SCREEN, 0) ||
+        !dixRegisterPrivateKey(&xmir_window_private_key, PRIVATE_WINDOW, 0) ||
+        !dixRegisterPrivateKey(&xmir_pixmap_private_key, PRIVATE_PIXMAP, 0))
+        return FALSE;
+
+    memset(&known_atom, 0, sizeof known_atom);
+
+    xmir_screen = calloc(sizeof *xmir_screen, 1);
+    if (!xmir_screen)
+        return FALSE;
+
+    xmir_screen->conn = NULL;
+
+    xmir_init_thread_to_eventloop();
+    dixSetPrivate(&pScreen->devPrivates, &xmir_screen_private_key, xmir_screen);
+    xmir_screen->screen = pScreen;
+
+    for (i = 1; i < argc; i++) {
+        if (strcmp(argv[i], "-rootless") == 0) {
+            xmir_screen->rootless = 1;
+        } else if (strcmp(argv[i], "-flatten") == 0) {
+            xmir_screen->flatten = TRUE;
+        } else if (strcmp(argv[i], "-neverclose") == 0) {
+            xmir_screen->neverclose = TRUE;
+        } else if (strcmp(argv[i], "-title") == 0) {
+            xmir_screen->title = argv[++i];
+        } else if (strcmp(argv[i], "-mir") == 0) {
+            appid = argv[++i];
+        } else if (strcmp(argv[i], "-mirSocket") == 0) {
+            socket = argv[++i];
+        } else if (strcmp(argv[i], "-debug") == 0) {
+            xmir_debug_logging = TRUE;
+        } else if (strcmp(argv[i], "-fd") == 0) {
+            client_fd = (int)strtol(argv[++i], (char **)NULL, 0);
+        }
+    }
+
+    if (xmir_screen->flatten && !xmir_screen->rootless) {
+        FatalError("-flatten is not valid without -rootless\n");
+        return FALSE;
+    }
+    if (xmir_screen->neverclose && !xmir_screen->flatten) {
+        FatalError("-neverclose is not valid without -rootless -flatten\n");
+        return FALSE;
+    }
+
+    if (!xmir_screen->title)
+        xmir_screen->title = xmir_screen->rootless ? get_title_from_top_window
+                                                   : "Xmir root window";
+
+    if (client_fd != -1) {
+        if (!AddClientOnOpenFD(client_fd)) {
+            FatalError("failed to connect to client fd %d\n", client_fd);
+            return FALSE;
+        }
+    }
+
+    conn = mir_connect_sync(socket, appid);
+    if (!mir_connection_is_valid(conn)) {
+        FatalError("Failed to connect to Mir: %s\n",
+                   mir_connection_get_error_message(conn));
+        return FALSE;
+    }
+    xmir_screen->conn = conn;
+    mir_connection_get_platform(xmir_screen->conn, &xmir_screen->platform);
+
+    xorg_list_init(&xmir_screen->output_list);
+    xorg_list_init(&xmir_screen->input_list);
+    xorg_list_init(&xmir_screen->damage_window_list);
+    xorg_list_init(&xmir_screen->flattened_list);
+    xmir_screen->depth = 24;
+
+    mir_connection_get_available_surface_formats(xmir_screen->conn,
+        format, sizeof(format)/sizeof(format[0]), &formats);
+    for (f = 0; f < formats; ++f) {
+        switch (format[f]) {
+        case mir_pixel_format_argb_8888:
+        case mir_pixel_format_abgr_8888:
+            xmir_screen->depth32_pixel_format = format[f];
+            break;
+        case mir_pixel_format_xrgb_8888:
+        case mir_pixel_format_xbgr_8888:
+        case mir_pixel_format_bgr_888:
+            xmir_screen->depth24_pixel_format = format[f];
+            break;
+        default:
+            /* Other/new pixel formats don't need mentioning. We only
+               care about Xorg-compatible formats */
+            break;
+        }
+    }
+
+    xmir_screen->display = mir_connection_create_display_config(conn);
+    if (xmir_screen->display == NULL) {
+        FatalError("could not create display config\n");
+        return FALSE;
+    }
+
+    /* Core DPI cannot report correct values (it's one value, we have multiple displays)
+     * Use the value from the -dpi commandline if set, or 96 otherwise.
+     *
+     * This matches the behaviour of all the desktop Xorg drivers. Clients which
+     * care can use the XRANDR extension to get correct per-output DPI information.
+     */
+    xmir_screen->dpi = monitorResolution > 0 ? monitorResolution : 96;
+
+    if (!xmir_screen_init_output(xmir_screen))
+        return FALSE;
+
+    bpc = 8;
+    green_mask = 0x00ff00;
+    switch (xmir_screen->depth24_pixel_format)
+    {
+    case mir_pixel_format_xrgb_8888:
+    case mir_pixel_format_bgr_888:  /* Little endian: Note the reversal */
+        red_mask = 0xff0000;
+        blue_mask = 0x0000ff;
+        break;
+    case mir_pixel_format_xbgr_8888:
+        red_mask = 0x0000ff;
+        blue_mask = 0xff0000;
+        break;
+    default:
+        ErrorF("No Mir-compatible TrueColor formats\n");
+        return FALSE;
+    }
+
+    miSetVisualTypesAndMasks(xmir_screen->depth,
+                             ((1 << TrueColor) | (1 << DirectColor)),
+                             bpc, TrueColor,
+                             red_mask, green_mask, blue_mask);
+
+    miSetPixmapDepths();
+
+    ret = fbScreenInit(pScreen, NULL,
+                       pScreen->width, pScreen->height,
+                       xmir_screen->dpi, xmir_screen->dpi, 0,
+                       BitsPerPixel(xmir_screen->depth));
+    if (!ret)
+        return FALSE;
+
+    fbPictureInit(pScreen, 0, 0);
+
+    pScreen->blackPixel = 0;
+    pScreen->whitePixel = 1;
+
+    ret = fbCreateDefColormap(pScreen);
+
+    if (!xmir_screen_init_cursor(xmir_screen))
+        return FALSE;
+
+    pScreen->SaveScreen = xmir_save_screen;
+    pScreen->BlockHandler = xmir_block_handler;
+    pScreen->SetScreenPixmap = xmir_set_screen_pixmap;
+
+    xmir_screen->CreateScreenResources = pScreen->CreateScreenResources;
+    pScreen->CreateScreenResources = xmir_create_screen_resources;
+
+    xmir_screen->CreateWindow = pScreen->CreateWindow;
+    pScreen->CreateWindow = xmir_create_window;
+
+    xmir_screen->RealizeWindow = pScreen->RealizeWindow;
+    pScreen->RealizeWindow = xmir_realize_window;
+
+    xmir_screen->DestroyWindow = pScreen->DestroyWindow;
+    pScreen->DestroyWindow = xmir_destroy_window;
+
+    xmir_screen->ResizeWindow = pScreen->ResizeWindow;
+    pScreen->ResizeWindow = xmir_resize_window;
+
+    xmir_screen->UnrealizeWindow = pScreen->UnrealizeWindow;
+    pScreen->UnrealizeWindow = xmir_unrealize_window;
+
+    xmir_screen->CloseScreen = pScreen->CloseScreen;
+    pScreen->CloseScreen = xmir_close_screen;
+
+    return ret;
+}
+
+void
+InitOutput(ScreenInfo *screen_info, int argc, char **argv)
+{
+    int depths[] = { 1, 4, 8, 15, 16, 24, 32 };
+    int bpp[] =    { 1, 8, 8, 16, 16, 32, 32 };
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(depths); i++) {
+        screen_info->formats[i].depth = depths[i];
+        screen_info->formats[i].bitsPerPixel = bpp[i];
+        screen_info->formats[i].scanlinePad = BITMAP_SCANLINE_PAD;
+    }
+
+    screen_info->imageByteOrder = IMAGE_BYTE_ORDER;
+    screen_info->bitmapScanlineUnit = BITMAP_SCANLINE_UNIT;
+    screen_info->bitmapScanlinePad = BITMAP_SCANLINE_PAD;
+    screen_info->bitmapBitOrder = BITMAP_BIT_ORDER;
+    screen_info->numPixmapFormats = ARRAY_SIZE(depths);
+
+    if (AddScreen(xmir_screen_init, argc, argv) == -1) {
+        FatalError("Couldn't add screen\n");
+    }
+}
diff --git a/hw/xmir/xmir.h b/hw/xmir/xmir.h
new file mode 100644
index 0000000..7b9ff97
--- /dev/null
+++ b/hw/xmir/xmir.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright © 2015-2016 Canonical Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#ifndef XMIR_H
+#define XMIR_H
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dix-config.h>
+#include <xorg-server.h>
+#include <X11/X.h>
+#include <fb.h>
+#include <input.h>
+#include <dix.h>
+#include <randrstr.h>
+#include <exevents.h>
+
+#include <mir_toolkit/mir_client_library.h>
+
+struct xmir_window;
+
+struct xmir_screen {
+    ScreenPtr screen;
+
+    int depth, rootless, windowed;
+
+    CreateScreenResourcesProcPtr CreateScreenResources;
+    CloseScreenProcPtr CloseScreen;
+    CreateWindowProcPtr CreateWindow;
+    DestroyWindowProcPtr DestroyWindow;
+    RealizeWindowProcPtr RealizeWindow;
+    UnrealizeWindowProcPtr UnrealizeWindow;
+    ResizeWindowProcPtr ResizeWindow;
+
+    struct xorg_list output_list;
+    struct xorg_list input_list;
+    struct xorg_list damage_window_list;
+
+    MirConnection *conn;
+    MirDisplayConfiguration *display;
+    MirPlatformPackage platform;
+
+    MirPixelFormat depth24_pixel_format, depth32_pixel_format;
+    Bool flatten;
+    Bool neverclose;
+    const char *title;
+    MirSurface *neverclosed;
+    struct xorg_list flattened_list;
+    struct xmir_window *flatten_top;
+    WindowPtr last_focus;
+
+    int dpi;
+};
+
+struct xmir_pixmap {
+    unsigned int fake_back;
+    void *image;
+};
+
+struct xmir_window {
+    struct xmir_screen *xmir_screen;
+    MirSurface *surface;
+    WindowPtr window;
+    DamagePtr damage;
+    RegionRec region;
+
+    int surface_width, surface_height;
+    int buf_width, buf_height;
+
+    struct xorg_list link_damage;
+    int orientation;
+    unsigned int has_free_buffer:1;
+
+    struct xorg_list link_flattened;
+
+    char wm_name[256];
+};
+
+struct xmir_input {
+    DeviceIntPtr pointer;
+    DeviceIntPtr keyboard;
+    DeviceIntPtr touch;
+    struct xmir_screen *xmir_screen;
+    struct xmir_window *focus_window;
+    uint32_t id;
+    int touch_id;
+    struct xorg_list link;
+};
+
+struct xmir_output {
+    struct xorg_list link;
+    struct wl_output *output;
+    struct xmir_screen *xmir_screen;
+    RROutputPtr randr_output;
+    RRCrtcPtr randr_crtc;
+    int32_t x, y, width, height;
+    Rotation rotation;
+};
+
+extern Bool xmir_debug_logging;
+#define XMIR_DEBUG(_args)  {if (xmir_debug_logging) ErrorF _args;}
+
+struct xmir_window *xmir_window_get(WindowPtr window);
+struct xmir_screen *xmir_screen_get(ScreenPtr screen);
+struct xmir_pixmap *xmir_pixmap_get(PixmapPtr pixmap);
+void xmir_pixmap_set(PixmapPtr pixmap, struct xmir_pixmap *xmir_pixmap);
+
+void xmir_handle_surface_event(struct xmir_window *, MirSurfaceAttrib, int);
+void xmir_handle_buffer_available(struct xmir_screen *xmir_screen,
+                                  struct xmir_window *xmir_win,
+                                  void *unused);
+void xmir_close_surface(struct xmir_window *);
+
+void xmir_repaint(struct xmir_window *);
+
+/* xmir-input.c */
+Bool xmir_screen_init_cursor(struct xmir_screen *xmir_screen);
+
+/* xmir-output.c */
+Bool xmir_screen_init_output(struct xmir_screen *xmir_screen);
+void xmir_output_destroy(struct xmir_output *xmir_output);
+Bool xmir_output_dpms(struct xmir_screen *xmir_screen, int dpms);
+void xmir_output_handle_resize(struct xmir_window *, int, int);
+void xmir_output_handle_orientation(struct xmir_window *, MirOrientation);
+
+/* xmir-cvt.c */
+RRModePtr xmir_cvt(int HDisplay, int VDisplay, float VRefresh, Bool Reduced, Bool Interlaced);
+
+/* xmir-thread-proxy.c */
+void xmir_init_thread_to_eventloop(void);
+void xmir_fini_thread_to_eventloop(void);
+
+typedef void (xmir_event_callback)(struct xmir_screen*, struct xmir_window*,
+                                   void *arg);
+void xmir_post_to_eventloop(xmir_event_callback *cb,
+                            struct xmir_screen*, struct xmir_window*, void*);
+void xmir_process_from_eventloop(void);
+
+/* xmir-input.c */
+void xmir_surface_handle_event(MirSurface *surface, MirEvent const* ev, void *context);
+
+#endif
-- 
2.7.4



More information about the xorg-devel mailing list