[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, ®ion);
+ 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, ®ion);
+ }
+
+ 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), ®ion);
+
+ /*
+ * 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, ®);
+ *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, ®ion);
+
+ 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