[PATCH 10/10] present: Add Present extension

Keith Packard keithp at keithp.com
Thu Oct 31 23:43:42 CET 2013


Provides both a software implementation using timers and driver hooks
to base everything on vblank intervals.

Signed-off-by: Keith Packard <keithp at keithp.com>
---
 Makefile.am               |   5 +
 configure.ac              |  23 +-
 hw/dmx/dmxextension.c     |   3 +
 include/dix-config.h.in   |   3 +
 include/extinit.h         |   5 +
 include/xorg-server.h.in  |   3 +
 mi/miinitext.c            |   3 +
 present/Makefile.am       |  17 +
 present/present.c         | 775 ++++++++++++++++++++++++++++++++++++++++++++++
 present/present.h         | 118 +++++++
 present/present_event.c   | 239 ++++++++++++++
 present/present_fake.c    | 135 ++++++++
 present/present_fence.c   | 112 +++++++
 present/present_priv.h    | 288 +++++++++++++++++
 present/present_request.c | 330 ++++++++++++++++++++
 present/present_screen.c  | 231 ++++++++++++++
 present/presentext.h      |  29 ++
 17 files changed, 2313 insertions(+), 6 deletions(-)
 create mode 100644 present/Makefile.am
 create mode 100644 present/present.c
 create mode 100644 present/present.h
 create mode 100644 present/present_event.c
 create mode 100644 present/present_fake.c
 create mode 100644 present/present_fence.c
 create mode 100644 present/present_priv.h
 create mode 100644 present/present_request.c
 create mode 100644 present/present_screen.c
 create mode 100644 present/presentext.h

diff --git a/Makefile.am b/Makefile.am
index 7a8fc5b..28266c5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,6 +21,10 @@ if DRI3
 DRI3_DIR=dri3
 endif
 
+if PRESENT
+PRESENT_DIR=present
+endif
+
 SUBDIRS = \
 	doc \
 	man \
@@ -42,6 +46,7 @@ SUBDIRS = \
 	damageext \
 	$(COMPOSITE_DIR) \
 	$(GLX_DIR) \
+	$(PRESENT_DIR) \
 	$(DRI3_DIR) \
 	exa \
 	config \
diff --git a/configure.ac b/configure.ac
index 546790d..7432912 100644
--- a/configure.ac
+++ b/configure.ac
@@ -614,6 +614,7 @@ AC_ARG_ENABLE(glx,            AS_HELP_STRING([--disable-glx], [Build GLX extensi
 AC_ARG_ENABLE(dri,            AS_HELP_STRING([--enable-dri], [Build DRI extension (default: auto)]), [DRI=$enableval])
 AC_ARG_ENABLE(dri2,           AS_HELP_STRING([--enable-dri2], [Build DRI2 extension (default: auto)]), [DRI2=$enableval], [DRI2=auto])
 AC_ARG_ENABLE(dri3,           AS_HELP_STRING([--enable-dri3], [Build DRI3 extension (default: auto)]), [DRI3=$enableval], [DRI3=auto])
+AC_ARG_ENABLE(present,	      AS_HELP_STRING([--disable-present], [Build Present extension (default: enabled)]), [PRESENT=$enableval], [PRESENT=yes])
 AC_ARG_ENABLE(xinerama,	      AS_HELP_STRING([--disable-xinerama], [Build Xinerama extension (default: enabled)]), [XINERAMA=$enableval], [XINERAMA=yes])
 AC_ARG_ENABLE(xf86vidmode,    AS_HELP_STRING([--disable-xf86vidmode], [Build XF86VidMode extension (default: auto)]), [XF86VIDMODE=$enableval], [XF86VIDMODE=auto])
 AC_ARG_ENABLE(xace,           AS_HELP_STRING([--disable-xace], [Build X-ACE extension (default: enabled)]), [XACE=$enableval], [XACE=yes])
@@ -807,6 +808,7 @@ DAMAGEPROTO="damageproto >= 1.1"
 XCMISCPROTO="xcmiscproto >= 1.2.0"
 BIGREQSPROTO="bigreqsproto >= 1.1.0"
 XTRANS="xtrans >= 1.2.2"
+PRESENTPROTO="presentproto >= 1.0"
 
 dnl List of libraries that require a specific version
 LIBAPPLEWM="applewm >= 1.4"
@@ -1160,6 +1162,14 @@ if test "x$DRI2" = xyes; then
 fi
 AM_CONDITIONAL(DRI2_AIGLX, test "x$DRI2_AIGLX" = xyes)
 
+AM_CONDITIONAL(PRESENT, [test "x$PRESENT" = xyes])
+if test "x$PRESENT" = xyes; then
+	AC_DEFINE(PRESENT, 1, [Support Present extension])
+	REQUIRED_MODULES="$REQUIRED_MODULES $PRESENTPROTO"
+	SDK_REQUIRED_MODULES="$SDK_REQUIRED_MODULES $PRESENTPROTO"
+	PRESENT_INC='-I$(top_srcdir)/present'
+	PRESENT_LIB='$(top_builddir)/present/libpresent.la'
+fi
 
 AM_CONDITIONAL(XINERAMA, [test "x$XINERAMA" = xyes])
 if test "x$XINERAMA" = xyes; then
@@ -1584,7 +1594,7 @@ AC_EGREP_CPP([I_AM_SVR4],[
 AC_DEFINE([SVR4],1,[Define to 1 on systems derived from System V Release 4])
 AC_MSG_RESULT([yes])], AC_MSG_RESULT([no]))
 
-XSERVER_CFLAGS="$XSERVER_CFLAGS $CORE_INCS $XEXT_INC $COMPOSITE_INC $DAMAGE_INC $FIXES_INC $XI_INC $MI_INC $MIEXT_SYNC_INC $MIEXT_SHADOW_INC $MIEXT_LAYER_INC $MIEXT_DAMAGE_INC $RENDER_INC $RANDR_INC $FB_INC $DBE_INC"
+XSERVER_CFLAGS="$XSERVER_CFLAGS $CORE_INCS $XEXT_INC $COMPOSITE_INC $DAMAGE_INC $FIXES_INC $XI_INC $MI_INC $MIEXT_SYNC_INC $MIEXT_SHADOW_INC $MIEXT_LAYER_INC $MIEXT_DAMAGE_INC $RENDER_INC $RANDR_INC $FB_INC $DBE_INC $PRESENT_INC"
 
 dnl ---------------------------------------------------------------------------
 dnl DDX section.
@@ -1597,7 +1607,7 @@ AC_MSG_RESULT([$XVFB])
 AM_CONDITIONAL(XVFB, [test "x$XVFB" = xyes])
 
 if test "x$XVFB" = xyes; then
-	XVFB_LIBS="$FB_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB"
+	XVFB_LIBS="$FB_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $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"
 	XVFB_SYS_LIBS="$XVFBMODULES_LIBS $GLX_SYS_LIBS"
 	AC_SUBST([XVFB_LIBS])
 	AC_SUBST([XVFB_SYS_LIBS])
@@ -1618,7 +1628,7 @@ if test "x$XNEST" = xyes; then
 	if test "x$have_xnest" = xno; then
 		AC_MSG_ERROR([Xnest build explicitly requested, but required modules not found.])
 	fi
-	XNEST_LIBS="$FB_LIB $FIXES_LIB $MI_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB  $DRI3_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"
+	XNEST_LIBS="$FB_LIB $FIXES_LIB $MI_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $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"
 	XNEST_SYS_LIBS="$XNESTMODULES_LIBS $GLX_SYS_LIBS"
 	AC_SUBST([XNEST_LIBS])
 	AC_SUBST([XNEST_SYS_LIBS])
@@ -1643,7 +1653,7 @@ if test "x$XORG" = xyes; then
 	XORG_OSINCS='-I$(top_srcdir)/hw/xfree86/os-support -I$(top_srcdir)/hw/xfree86/os-support/bus -I$(top_srcdir)/os'
 	XORG_INCS="$XORG_DDXINCS $XORG_OSINCS"
 	XORG_CFLAGS="$XORGSERVER_CFLAGS -DHAVE_XORG_CONFIG_H"
-	XORG_LIBS="$COMPOSITE_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $XI_LIB $XKB_LIB"
+	XORG_LIBS="$COMPOSITE_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $PRESENT_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $XI_LIB $XKB_LIB"
 
 	dnl ==================================================================
 	dnl symbol visibility
@@ -2056,7 +2066,7 @@ if test "x$DMX" = xyes; then
 	fi
 	DMX_INCLUDES="$XEXT_INC $RENDER_INC $RECORD_INC"
 	XDMX_CFLAGS="$DMXMODULES_CFLAGS"
-	XDMX_LIBS="$FB_LIB $MI_LIB $XEXT_LIB $RENDER_LIB $RECORD_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB  $DRI3_LIB $MIEXT_SYNC_LIB $MIEXT_SHADOW_LIB $MIEXT_DAMAGE_LIB $COMPOSITE_LIB $DAMAGE_LIB $MAIN_LIB $DIX_LIB $CONFIG_LIB $OS_LIB $FIXES_LIB"
+	XDMX_LIBS="$FB_LIB $MI_LIB $XEXT_LIB $RENDER_LIB $RECORD_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB  $DRI3_LIB $PRESENT_LIB $MIEXT_SYNC_LIB $MIEXT_SHADOW_LIB $MIEXT_DAMAGE_LIB $COMPOSITE_LIB $DAMAGE_LIB $MAIN_LIB $DIX_LIB $CONFIG_LIB $OS_LIB $FIXES_LIB"
 	XDMX_SYS_LIBS="$DMXMODULES_LIBS"
 	AC_SUBST([XDMX_CFLAGS])
 	AC_SUBST([XDMX_LIBS])
@@ -2165,7 +2175,7 @@ if test "$KDRIVE" = yes; then
     
     KDRIVE_CFLAGS="$XSERVER_CFLAGS -DHAVE_KDRIVE_CONFIG_H $TSLIB_CFLAGS"
 
-    KDRIVE_PURE_LIBS="$FB_LIB $MI_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB $OS_LIB"
+    KDRIVE_PURE_LIBS="$FB_LIB $MI_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $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 $OS_LIB"
     KDRIVE_LIB='$(top_builddir)/hw/kdrive/src/libkdrive.la'
     case $host_os in
 	*linux*)
@@ -2288,6 +2298,7 @@ Xi/Makefile
 xfixes/Makefile
 exa/Makefile
 dri3/Makefile
+present/Makefile
 hw/Makefile
 hw/xfree86/Makefile
 hw/xfree86/common/Makefile
diff --git a/hw/dmx/dmxextension.c b/hw/dmx/dmxextension.c
index d7296ae..c6c6a8e 100644
--- a/hw/dmx/dmxextension.c
+++ b/hw/dmx/dmxextension.c
@@ -68,6 +68,9 @@
  * _any_ header files. */
 extern FontPtr defaultFont;
 
+/* Hack to get Present to build (present requires RandR) */
+RESTYPE RRCrtcType;
+
 /** This routine provides information to the DMX protocol extension
  * about a particular screen. */
 Bool
diff --git a/include/dix-config.h.in b/include/dix-config.h.in
index d96da6a..397ee96 100644
--- a/include/dix-config.h.in
+++ b/include/dix-config.h.in
@@ -255,6 +255,9 @@
 /* Internal define for Xinerama */
 #undef PANORAMIX
 
+/* Support Present extension */
+#undef PRESENT
+
 /* Overall prefix */
 #undef PROJECTROOT
 
diff --git a/include/extinit.h b/include/extinit.h
index bdb149c..fa5f293 100644
--- a/include/extinit.h
+++ b/include/extinit.h
@@ -181,4 +181,9 @@ extern void XvMCExtensionInit(void);
 extern void dri3_extension_init(void);
 #endif
 
+#if defined(PRESENT)
+#include <X11/extensions/presentproto.h>
+#include "presentext.h"
+#endif
+
 #endif
diff --git a/include/xorg-server.h.in b/include/xorg-server.h.in
index 5b3b664..1281b3e 100644
--- a/include/xorg-server.h.in
+++ b/include/xorg-server.h.in
@@ -70,6 +70,9 @@
 /* Internal define for Xinerama */
 #undef PANORAMIX
 
+/* Support Present extension */
+#undef PRESENT
+
 /* Support RANDR extension */
 #undef RANDR
 
diff --git a/mi/miinitext.c b/mi/miinitext.c
index e49948b..6366182 100644
--- a/mi/miinitext.c
+++ b/mi/miinitext.c
@@ -287,7 +287,10 @@ static ExtensionModule staticExtensions[] = {
 #ifdef DPMSExtension
     {DPMSExtensionInit, DPMSExtensionName, &noDPMSExtension},
 #endif
+    {present_extension_init, PRESENT_NAME, NULL},
+#ifdef DRI3
     {dri3_extension_init, DRI3_NAME, NULL},
+#endif
 #ifdef RES
     {ResExtensionInit, XRES_NAME, &noResExtension},
 #endif
diff --git a/present/Makefile.am b/present/Makefile.am
new file mode 100644
index 0000000..7fea669
--- /dev/null
+++ b/present/Makefile.am
@@ -0,0 +1,17 @@
+noinst_LTLIBRARIES = libpresent.la
+AM_CFLAGS = \
+	-DHAVE_XORG_CONFIG_H \
+	@DIX_CFLAGS@ @XORG_CFLAGS@
+
+libpresent_la_SOURCES = \
+	present.h \
+	present.c \
+	present_event.c \
+	present_fake.c \
+	present_fence.c \
+	present_notify.c \
+	present_priv.h \
+	present_request.c \
+	present_screen.c
+
+sdk_HEADERS = present.h presentext.h
diff --git a/present/present.c b/present/present.c
new file mode 100644
index 0000000..4c97ce4
--- /dev/null
+++ b/present/present.c
@@ -0,0 +1,775 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * 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_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include "present_priv.h"
+#include <gcstruct.h>
+#include <misync.h>
+#include <misyncstr.h>
+#ifdef MONOTONIC_CLOCK
+#include <time.h>
+#endif
+
+static uint64_t         present_event_id;
+static struct xorg_list present_exec_queue;
+static struct xorg_list present_flip_queue;
+
+#if 0
+#define DebugPresent(x) ErrorF x
+#else
+#define DebugPresent(x)
+#endif
+
+/*
+ * Copies the update region from a pixmap to the target drawable
+ */
+static void
+present_copy_region(DrawablePtr drawable,
+                    PixmapPtr pixmap,
+                    RegionPtr update,
+                    int16_t x_off,
+                    int16_t y_off)
+{
+    ScreenPtr   screen = drawable->pScreen;
+    GCPtr       gc;
+
+    gc = GetScratchGC(drawable->depth, screen);
+    if (update) {
+        ChangeGCVal     changes[2];
+
+        changes[0].val = x_off;
+        changes[1].val = y_off;
+        ChangeGC(serverClient, gc,
+                 GCClipXOrigin|GCClipYOrigin,
+                 changes);
+        (*gc->funcs->ChangeClip)(gc, CT_REGION, update, 0);
+    }
+    ValidateGC(drawable, gc);
+    (*gc->ops->CopyArea)(&pixmap->drawable,
+                         drawable,
+                         gc,
+                         0, 0,
+                         pixmap->drawable.width, pixmap->drawable.height,
+                         x_off, y_off);
+    if (update)
+        (*gc->funcs->ChangeClip)(gc, CT_NONE, NULL, 0);
+    FreeScratchGC(gc);
+}
+
+static Bool
+present_check_flip(RRCrtcPtr    crtc,
+                   WindowPtr    window,
+                   PixmapPtr    pixmap,
+                   Bool         sync_flip,
+                   RegionPtr    valid,
+                   int16_t      x_off,
+                   int16_t      y_off)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    WindowPtr                   root = screen->root;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (!screen_priv)
+        return FALSE;
+
+    if (!screen_priv->info)
+        return FALSE;
+
+    if (!crtc)
+        return FALSE;
+
+    /* Check to see if the driver supports flips at all */
+    if (!screen_priv->info->flip)
+        return FALSE;
+
+    /* Can't pend a flip while unflipping */
+    if (screen_priv->unflip_event_id) {
+        return FALSE;
+    }
+
+    /* Can't have two pending flips at the same time */
+    if (screen_priv->flip_pending) {
+        return FALSE;
+    }
+
+    /* Make sure the window hasn't been redirected with Composite */
+    if (screen->GetWindowPixmap(window) != screen->GetScreenPixmap(screen))
+        return FALSE;
+
+    /* Check for full-screen window */
+    if (!RegionEqual(&window->clipList, &root->winSize)) {
+        return FALSE;
+    }
+
+    /* Source pixmap must align with window exactly */
+    if (x_off || y_off) {
+        return FALSE;
+    }
+
+    /* Make sure the area marked as valid fills the screen */
+    if (valid && !RegionEqual(valid, &root->winSize)) {
+        return FALSE;
+    }
+
+    /* Does the window match the pixmap exactly? */
+    if (window->drawable.x != 0 || window->drawable.y != 0 ||
+#ifdef COMPOSITE
+        window->drawable.x != pixmap->screen_x || window->drawable.y != pixmap->screen_y ||
+#endif
+        window->drawable.width != pixmap->drawable.width ||
+        window->drawable.height != pixmap->drawable.height) {
+        return FALSE;
+    }
+
+    /* Ask the driver for permission */
+    if (screen_priv->info->check_flip) {
+        if (!(*screen_priv->info->check_flip) (crtc, window, pixmap, sync_flip)) {
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static Bool
+present_flip(RRCrtcPtr crtc,
+             uint64_t event_id,
+             uint64_t target_msc,
+             PixmapPtr pixmap,
+             Bool sync_flip)
+{
+    ScreenPtr                   screen = crtc->pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    return (*screen_priv->info->flip) (crtc, event_id, target_msc, pixmap, sync_flip);
+}
+
+static void
+present_vblank_notify(present_vblank_ptr vblank, CARD8 kind, CARD8 mode, uint64_t ust, uint64_t crtc_msc)
+{
+    int         n;
+
+    present_send_complete_notify(vblank->window, kind, mode, vblank->serial, ust, crtc_msc - vblank->msc_offset);
+    for (n = 0; n < vblank->num_notifies; n++) {
+        WindowPtr   window = vblank->notifies[n].window;
+        CARD32      serial = vblank->notifies[n].serial;
+
+        if (window)
+            present_send_complete_notify(window, kind, mode, serial, ust, crtc_msc - vblank->msc_offset);
+    }
+}
+
+static void
+present_pixmap_idle(PixmapPtr pixmap, WindowPtr window, CARD32 serial, struct present_fence *present_fence)
+{
+    present_fence_set_triggered(present_fence);
+    present_send_idle_notify(window, serial, pixmap, present_fence);
+}
+
+RRCrtcPtr
+present_get_crtc(WindowPtr window)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (!screen_priv)
+        return NULL;
+
+    if (!screen_priv->info)
+        return NULL;
+
+    return (*screen_priv->info->get_crtc)(window);
+}
+
+uint32_t
+present_query_capabilities(RRCrtcPtr crtc)
+{
+    present_screen_priv_ptr     screen_priv;
+
+    if (!crtc)
+        return 0;
+
+    screen_priv = present_screen_priv(crtc->pScreen);
+
+    if (!screen_priv)
+        return 0;
+
+    if (!screen_priv->info)
+        return 0;
+
+    return screen_priv->info->capabilities;
+}
+
+static int
+present_get_ust_msc(WindowPtr window, RRCrtcPtr crtc, uint64_t *ust, uint64_t *msc)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (crtc == NULL)
+        return present_fake_get_ust_msc(screen, ust, msc);
+    else
+        return (*screen_priv->info->get_ust_msc)(crtc, ust, msc);
+}
+
+static void
+present_flush(WindowPtr window)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (!screen_priv)
+        return;
+
+    if (!screen_priv->info)
+        return;
+
+    (*screen_priv->info->flush) (window);
+}
+
+static int
+present_queue_vblank(ScreenPtr screen,
+                     RRCrtcPtr crtc,
+                     uint64_t event_id,
+                     uint64_t msc)
+{
+    Bool                        ret;
+
+    if (crtc == NULL)
+        ret = present_fake_queue_vblank(screen, event_id, msc);
+    else
+    {
+        present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+        ret = (*screen_priv->info->queue_vblank) (crtc, event_id, msc);
+    }
+    return ret;
+}
+
+static uint64_t
+present_window_to_crtc_msc(WindowPtr window, RRCrtcPtr crtc, uint64_t window_msc, uint64_t new_msc)
+{
+    present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
+
+    if (crtc != window_priv->crtc) {
+        uint64_t        old_ust, old_msc;
+
+        /* The old CRTC may have been turned off, in which case
+         * we'll just use whatever previous MSC we'd seen from this CRTC
+         */
+
+        if (present_get_ust_msc(window, window_priv->crtc, &old_ust, &old_msc) != Success)
+            old_msc = window_priv->msc;
+
+        window_priv->msc_offset += new_msc - old_msc;
+        window_priv->crtc = crtc;
+    }
+
+    return window_msc + window_priv->msc_offset;
+}
+
+static void
+present_flip_idle(ScreenPtr screen)
+{
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    if (screen_priv->flip_pixmap) {
+        present_pixmap_idle(screen_priv->flip_pixmap, screen_priv->flip_window,
+                            screen_priv->flip_serial, screen_priv->flip_idle_fence);
+        present_fence_destroy(screen_priv->flip_idle_fence);
+        dixDestroyPixmap(screen_priv->flip_pixmap, screen_priv->flip_pixmap->drawable.id);
+        screen_priv->flip_crtc = NULL;
+        screen_priv->flip_window = NULL;
+        screen_priv->flip_serial = 0;
+        screen_priv->flip_pixmap = NULL;
+        screen_priv->flip_idle_fence = NULL;
+    }
+}
+
+static void
+present_unflip(ScreenPtr screen)
+{
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    assert (!screen_priv->unflip_event_id);
+    assert (!screen_priv->flip_pending);
+
+    /* Update the screen pixmap with the current flip pixmap contents
+     */
+    if (screen_priv->flip_pixmap) {
+        present_copy_region(&screen->GetScreenPixmap(screen)->drawable,
+                            screen_priv->flip_pixmap,
+                            NULL, 0, 0);
+    }
+    screen_priv->unflip_event_id = ++present_event_id;
+    DebugPresent(("u %lld\n", screen_priv->unflip_event_id));
+    (*screen_priv->info->unflip) (screen, screen_priv->unflip_event_id);
+}
+
+static void
+present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc);
+
+static void
+present_flip_notify(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
+{
+    WindowPtr                   window = vblank->window;
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    DebugPresent(("\tn %p %8lld: %08lx -> %08lx\n", vblank, vblank->target_msc,
+           vblank->pixmap ? vblank->pixmap->drawable.id : 0,
+                  vblank->window->drawable.id));
+
+    assert (vblank == screen_priv->flip_pending);
+
+    present_flip_idle(screen);
+
+    /* Transfer reference for pixmap and fence from vblank to screen_priv */
+    screen_priv->flip_crtc = vblank->crtc;
+    screen_priv->flip_window = vblank->window;
+    screen_priv->flip_serial = vblank->serial;
+    screen_priv->flip_pixmap = vblank->pixmap;
+    screen_priv->flip_idle_fence = vblank->idle_fence;
+
+    vblank->pixmap = NULL;
+    vblank->idle_fence = NULL;
+
+    screen_priv->flip_pending = NULL;
+
+    if (vblank->abort_flip)
+        present_unflip(screen);
+
+    if (!vblank->window_destroyed)
+        present_vblank_notify(vblank, PresentCompleteKindPixmap, PresentCompleteModeFlip, ust, crtc_msc);
+    present_vblank_destroy(vblank);
+}
+
+void
+present_event_notify(uint64_t event_id, uint64_t ust, uint64_t msc)
+{
+    present_vblank_ptr  vblank, tmp;
+    int                 s;
+
+    xorg_list_for_each_entry_safe(vblank, tmp, &present_exec_queue, event_queue) {
+        if (vblank->event_id == event_id) {
+            xorg_list_del(&vblank->event_queue);
+            present_execute(vblank, ust, msc);
+            return;
+        }
+    }
+    xorg_list_for_each_entry_safe(vblank, tmp, &present_flip_queue, event_queue) {
+        if (vblank->event_id == event_id) {
+            xorg_list_del(&vblank->event_queue);
+            present_flip_notify(vblank, ust, msc);
+            return;
+        }
+    }
+
+    for (s = 0; s < screenInfo.numScreens; s++) {
+        ScreenPtr               screen = screenInfo.screens[s];
+        present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+        if (event_id == screen_priv->unflip_event_id) {
+            DebugPresent(("\tun %lld\n", event_id));
+            screen_priv->unflip_event_id = 0;
+            present_flip_idle(screen);
+        }
+    }
+}
+
+/*
+ * 'window' is being reconfigured. Check to see if it is involved
+ * in flipping and clean up as necessary
+ */
+void
+present_check_flip_window (WindowPtr window)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+    present_window_priv_ptr     window_priv = present_window_priv(window);
+    present_vblank_ptr          flip_pending = screen_priv->flip_pending;
+    present_vblank_ptr          vblank;
+
+    /* If this window hasn't ever been used with Present, it can't be
+     * flipping
+     */
+    if (!window_priv)
+        return;
+
+    if (screen_priv->unflip_event_id)
+        return;
+
+    if (flip_pending) {
+        /*
+         * Check pending flip
+         */
+        if (flip_pending->window == window) {
+            if (!present_check_flip(flip_pending->crtc, window, flip_pending->pixmap,
+                                    flip_pending->sync_flip, NULL, 0, 0))
+                flip_pending->abort_flip = TRUE;
+        }
+    } else {
+        /*
+         * Check current flip
+         */
+        if (window == screen_priv->flip_window) {
+            if (!present_check_flip(screen_priv->flip_crtc, window, screen_priv->flip_pixmap, FALSE, NULL, 0, 0))
+                present_unflip(screen);
+        }
+    }
+
+    /* Now check any queued vblanks */
+    xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) {
+        if (vblank->flip && !present_check_flip(vblank->crtc, window, vblank->pixmap, FALSE, NULL, 0, 0))
+            vblank->flip = FALSE;
+    }
+}
+
+/*
+ * Once the required MSC has been reached, execute the pending request.
+ *
+ * For requests to actually present something, either blt contents to
+ * the screen or queue a frame buffer swap.
+ *
+ * For requests to just get the current MSC/UST combo, skip that part and
+ * go straight to event delivery
+ */
+
+static void
+present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
+{
+    WindowPtr                   window = vblank->window;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(window->drawable.pScreen);
+
+    if (vblank->wait_fence) {
+        /* XXX check fence, queue if not ready */
+    }
+
+    xorg_list_del(&vblank->event_queue);
+    if (vblank->pixmap) {
+
+        if (vblank->flip && screen_priv->flip_pending == NULL && !screen_priv->unflip_event_id) {
+
+            DebugPresent(("\tf %p %8lld: %08lx -> %08lx\n", vblank, crtc_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id));
+            /* Prepare to flip by removing from the window/screen lists
+             * and sticking it into the flip_pending field
+             */
+            screen_priv->flip_pending = vblank;
+            xorg_list_del(&vblank->window_list);
+
+            xorg_list_add(&vblank->event_queue, &present_flip_queue);
+            /* Try to flip
+             */
+            if (present_flip(vblank->crtc, vblank->event_id, vblank->target_msc, vblank->pixmap, vblank->sync_flip))
+                return;
+
+            xorg_list_del(&vblank->event_queue);
+            /* Oops, flip failed. Clear the flip_pending field
+              */
+            screen_priv->flip_pending = NULL;
+            vblank->flip = FALSE;
+        }
+        DebugPresent(("\tc %p %8lld: %08lx -> %08lx\n", vblank, crtc_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id));
+        if (screen_priv->flip_pending) {
+
+            /* Check pending flip
+             */
+            if (window == screen_priv->flip_pending->window)
+                screen_priv->flip_pending->abort_flip = TRUE;
+        } else if (!screen_priv->unflip_event_id) {
+
+            /* Check current flip
+             */
+            if (window == screen_priv->flip_window)
+                present_unflip(window->drawable.pScreen);
+        }
+        present_copy_region(&window->drawable, vblank->pixmap, vblank->update, vblank->x_off, vblank->y_off);
+
+        /* present_copy_region sticks the region into a scratch GC,
+         * which is then freed, freeing the region
+         */
+        vblank->update = NULL;
+        present_flush(window);
+
+        present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+    }
+    present_vblank_notify(vblank, vblank->kind, PresentCompleteModeCopy, ust, crtc_msc);
+    present_vblank_destroy(vblank);
+}
+
+int
+present_pixmap(WindowPtr window,
+               PixmapPtr pixmap,
+               CARD32 serial,
+               RegionPtr valid,
+               RegionPtr update,
+               int16_t x_off,
+               int16_t y_off,
+               RRCrtcPtr target_crtc,
+               SyncFence *wait_fence,
+               SyncFence *idle_fence,
+               uint32_t options,
+               uint64_t window_msc,
+               uint64_t divisor,
+               uint64_t remainder,
+               present_notify_ptr notifies,
+               int num_notifies)
+{
+    uint64_t                    ust;
+    uint64_t                    target_msc;
+    uint64_t                    crtc_msc;
+    int                         ret;
+    present_vblank_ptr          vblank;
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    if (!window_priv)
+        return BadAlloc;
+
+    if (!target_crtc) {
+        /* Update the CRTC if we have a pixmap or we don't have a CRTC
+         */
+        if (!pixmap)
+            target_crtc = window_priv->crtc;
+
+        if (!target_crtc)
+            target_crtc = present_get_crtc(window);
+    }
+
+    present_get_ust_msc(window, target_crtc, &ust, &crtc_msc);
+
+    target_msc = present_window_to_crtc_msc(window, target_crtc, window_msc, crtc_msc);
+
+    /* Stash the current MSC away in case we need it later
+     */
+    window_priv->msc = crtc_msc;
+
+    /* Adjust target_msc to match modulus
+     */
+    if (crtc_msc >= target_msc) {
+        if (divisor != 0) {
+            target_msc = crtc_msc - (crtc_msc % divisor) + remainder;
+            if (target_msc <= crtc_msc)
+                target_msc += divisor;
+        } else
+            target_msc = crtc_msc;
+    }
+
+    /*
+     * Look for a matching presentation already on the list and
+     * don't bother doing the previous one if this one will overwrite it
+     * in the same frame
+     */
+
+    if (!update) {
+        xorg_list_for_each_entry(vblank, &window_priv->vblank, window_list) {
+
+            if (!vblank->pixmap)
+                continue;
+
+            if (vblank->crtc != target_crtc || vblank->target_msc != target_msc)
+                continue;
+
+            present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+            present_fence_destroy(vblank->idle_fence);
+            dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id);
+
+            vblank->pixmap = NULL;
+            vblank->idle_fence = NULL;
+        }
+    }
+
+    vblank = calloc (1, sizeof (present_vblank_rec));
+    if (!vblank)
+        return BadAlloc;
+
+    xorg_list_append(&vblank->window_list, &window_priv->vblank);
+    xorg_list_init(&vblank->event_queue);
+
+    vblank->screen = screen;
+    vblank->window = window;
+    vblank->pixmap = pixmap;
+    vblank->event_id = ++present_event_id;
+    if (pixmap) {
+        vblank->kind = PresentCompleteKindPixmap;
+        pixmap->refcnt++;
+    } else
+        vblank->kind = PresentCompleteKindNotifyMSC;
+
+    vblank->serial = serial;
+
+    if (valid) {
+        vblank->valid = RegionDuplicate(valid);
+        if (!vblank->valid)
+            goto no_mem;
+    }
+    if (update) {
+        vblank->update = RegionDuplicate(update);
+        if (!vblank->update)
+            goto no_mem;
+    }
+
+    vblank->x_off = x_off;
+    vblank->y_off = y_off;
+    vblank->target_msc = target_msc;
+    vblank->crtc = target_crtc;
+    vblank->msc_offset = window_priv->msc_offset;
+    vblank->notifies = notifies;
+    vblank->num_notifies = num_notifies;
+
+    if (!screen_priv->info || !(screen_priv->info->capabilities & PresentCapabilityAsync))
+        vblank->sync_flip = TRUE;
+
+    if (pixmap && present_check_flip (target_crtc, window, pixmap, vblank->sync_flip, valid, x_off, y_off)) {
+        vblank->flip = TRUE;
+        if (vblank->sync_flip)
+            target_msc--;
+    }
+
+    if (idle_fence) {
+        vblank->idle_fence = present_fence_create(idle_fence);
+        if (!vblank->idle_fence)
+            goto no_mem;
+    }
+
+    if (pixmap)
+        DebugPresent(("q %p %8lld: %08lx -> %08lx (crtc %d)\n",
+                      vblank, target_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id, target_crtc ? 1 : 0));
+
+    xorg_list_add(&vblank->event_queue, &present_exec_queue);
+    if (target_msc >= crtc_msc) {
+        ret = present_queue_vblank(screen, target_crtc, vblank->event_id, target_msc);
+        if (ret != Success) {
+            xorg_list_del(&vblank->event_queue);
+            goto failure;
+        }
+    } else
+        present_execute(vblank, ust, crtc_msc);
+
+    return Success;
+
+no_mem:
+    ret = BadAlloc;
+failure:
+    vblank->notifies = NULL;
+    present_vblank_destroy(vblank);
+    return ret;
+}
+
+void
+present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
+{
+    present_vblank_ptr  vblank, tmp;
+
+    if (crtc == NULL)
+        present_fake_abort_vblank(screen, event_id, msc);
+    else
+    {
+        present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+        (*screen_priv->info->abort_vblank) (crtc, event_id, msc);
+    }
+
+    xorg_list_for_each_entry_safe(vblank, tmp, &present_exec_queue, event_queue) {
+        if (vblank->event_id == event_id) {
+            xorg_list_del(&vblank->event_queue);
+            return;
+        }
+    }
+    xorg_list_for_each_entry_safe(vblank, tmp, &present_flip_queue, event_queue) {
+        if (vblank->event_id == event_id) {
+            xorg_list_del(&vblank->event_queue);
+            return;
+        }
+    }
+}
+
+int
+present_notify_msc(WindowPtr window,
+                   CARD32 serial,
+                   uint64_t target_msc,
+                   uint64_t divisor,
+                   uint64_t remainder)
+{
+    return present_pixmap(window,
+                          NULL,
+                          serial,
+                          NULL, NULL,
+                          0, 0,
+                          NULL,
+                          NULL, NULL,
+                          0,
+                          target_msc, divisor, remainder, NULL, 0);
+}
+
+void
+present_flip_destroy(ScreenPtr screen)
+{
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+
+    /* XXX this needs to be synchronous for server reset */
+
+    /* Do the actual cleanup once the flip has been performed by the hardware */
+    if (screen_priv->flip_pending)
+        screen_priv->flip_pending->abort_flip = TRUE;
+}
+
+void
+present_vblank_destroy(present_vblank_ptr vblank)
+{
+    /* Remove vblank from window and screen lists */
+    xorg_list_del(&vblank->window_list);
+
+    DebugPresent(("\td %p %8lld: %08lx -> %08lx\n", vblank, vblank->target_msc,
+                  vblank->pixmap ? vblank->pixmap->drawable.id : 0,
+                  vblank->window->drawable.id));
+
+    /* Drop pixmap reference */
+    if (vblank->pixmap)
+        dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id);
+
+    /* Free regions */
+    if (vblank->valid)
+        RegionDestroy(vblank->valid);
+    if (vblank->update)
+        RegionDestroy(vblank->update);
+
+    if (vblank->idle_fence)
+        present_fence_destroy(vblank->idle_fence);
+
+    if (vblank->notifies)
+        present_destroy_notifies(vblank->notifies, vblank->num_notifies);
+
+    free(vblank);
+}
+
+Bool
+present_init(void)
+{
+    xorg_list_init(&present_exec_queue);
+    xorg_list_init(&present_flip_queue);
+    present_fake_queue_init();
+    return TRUE;
+}
diff --git a/present/present.h b/present/present.h
new file mode 100644
index 0000000..6a451fb
--- /dev/null
+++ b/present/present.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * 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 _PRESENT_H_
+#define _PRESENT_H_
+
+#include <X11/extensions/presentproto.h>
+#include "randrstr.h"
+#include "presentext.h"
+
+typedef struct present_vblank present_vblank_rec, *present_vblank_ptr;
+
+/* Return the current CRTC for 'window'.
+ */
+typedef RRCrtcPtr (*present_get_crtc_ptr) (WindowPtr window);
+
+/* Return the current ust/msc for 'crtc'
+ */
+typedef int (*present_get_ust_msc_ptr) (RRCrtcPtr crtc, uint64_t *ust, uint64_t *msc);
+
+/* Queue callback on 'crtc' for time 'msc'. Call present_event_notify with 'event_id'
+ * at or after 'msc'. Return false if it didn't happen (which might occur if 'crtc'
+ * is not currently generating vblanks).
+ */
+typedef Bool (*present_queue_vblank_ptr) (RRCrtcPtr crtc,
+                                          uint64_t event_id,
+                                          uint64_t msc);
+
+/* Abort pending vblank. The extension is no longer interested in
+ * 'event_id' which was to be notified at 'msc'. If possible, the
+ * driver is free to de-queue the notification.
+ */
+typedef void (*present_abort_vblank_ptr) (RRCrtcPtr crtc, uint64_t event_id, uint64_t msc);
+
+/* Flush pending drawing on 'window' to the hardware.
+ */
+typedef void (*present_flush_ptr) (WindowPtr window);
+
+/* Check if 'pixmap' is suitable for flipping to 'window'.
+ */
+typedef Bool (*present_check_flip_ptr) (RRCrtcPtr crtc, WindowPtr window, PixmapPtr pixmap, Bool sync_flip);
+
+/* Flip pixmap, return false if it didn't happen.
+ *
+ * 'crtc' is to be used for any necessary synchronization.
+ *
+ * 'sync_flip' requests that the flip be performed at the next
+ * vertical blank interval to avoid tearing artifacts. If false, the
+ * flip should be performed as soon as possible.
+ *
+ * present_event_notify should be called with 'event_id' when the flip
+ * occurs
+ */
+typedef Bool (*present_flip_ptr) (RRCrtcPtr crtc,
+                                  uint64_t event_id,
+                                  uint64_t target_msc,
+                                  PixmapPtr pixmap,
+                                  Bool sync_flip);
+
+/* "unflip" back to the regular screen scanout buffer
+ *
+ * present_event_notify should be called with 'event_id' when the unflip occurs.
+ */
+typedef void (*present_unflip_ptr) (ScreenPtr screen,
+                                    uint64_t event_id);
+
+#define PRESENT_SCREEN_INFO_VERSION        0
+
+typedef struct present_screen_info {
+    uint32_t                            version;
+
+    present_get_crtc_ptr                get_crtc;
+    present_get_ust_msc_ptr             get_ust_msc;
+    present_queue_vblank_ptr            queue_vblank;
+    present_abort_vblank_ptr            abort_vblank;
+    present_flush_ptr                   flush;
+    uint32_t                            capabilities;
+    present_check_flip_ptr              check_flip;
+    present_flip_ptr                    flip;
+    present_unflip_ptr                  unflip;
+
+} present_screen_info_rec, *present_screen_info_ptr;
+
+/*
+ * Called when 'event_id' occurs. 'ust' and 'msc' indicate when the
+ * event actually happened
+ */
+extern _X_EXPORT void
+present_event_notify(uint64_t event_id, uint64_t ust, uint64_t msc);
+
+/* 'crtc' has been turned off, so any pending events will never occur.
+ */
+extern _X_EXPORT void
+present_event_abandon(RRCrtcPtr crtc);
+
+extern _X_EXPORT Bool
+present_screen_init(ScreenPtr screen, present_screen_info_ptr info);
+
+#endif /* _PRESENT_H_ */
diff --git a/present/present_event.c b/present/present_event.c
new file mode 100644
index 0000000..a30bc82
--- /dev/null
+++ b/present/present_event.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * 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_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include "present_priv.h"
+
+#include "present_priv.h"
+
+RESTYPE present_event_type;
+
+static int
+present_free_event(pointer data, XID id)
+{
+    present_event_ptr present_event = (present_event_ptr) data;
+    present_window_priv_ptr window_priv = present_window_priv(present_event->window);
+    present_event_ptr *previous, current;
+
+    for (previous = &window_priv->events; (current = *previous); previous = &current->next) {
+        if (current == present_event) {
+            *previous = present_event->next;
+            break;
+        }
+    }
+    free((pointer) present_event);
+    return 1;
+
+}
+
+void
+present_free_events(WindowPtr window)
+{
+    present_window_priv_ptr window_priv = present_window_priv(window);
+    present_event_ptr event;
+
+    if (!window_priv)
+        return;
+
+    while ((event = window_priv->events))
+        FreeResource(event->id, RT_NONE);
+}
+
+static void
+present_event_swap(xGenericEvent *from, xGenericEvent *to)
+{
+    *to = *from;
+    swaps(&to->sequenceNumber);
+    swapl(&to->length);
+    swaps(&to->evtype);
+    switch (from->evtype) {
+    case PresentConfigureNotify: {
+        xPresentConfigureNotify *c = (xPresentConfigureNotify *) to;
+
+        swapl(&c->eid);
+        swapl(&c->window);
+        swaps(&c->x);
+        swaps(&c->y);
+        swaps(&c->width);
+        swaps(&c->height);
+        swaps(&c->off_x);
+        swaps(&c->off_y);
+        swaps(&c->pixmap_width);
+        swaps(&c->pixmap_height);
+        swapl(&c->pixmap_flags);
+        break;
+    }
+    case PresentCompleteNotify:
+    {
+        xPresentCompleteNotify *c = (xPresentCompleteNotify *) to;
+        swapl(&c->eid);
+        swapl(&c->window);
+        swapl(&c->serial);
+        swapll(&c->ust);
+        swapll(&c->msc);
+    }
+    case PresentIdleNotify:
+    {
+        xPresentIdleNotify *c = (xPresentIdleNotify *) to;
+        swapl(&c->eid);
+        swapl(&c->window);
+        swapl(&c->serial);
+        swapl(&c->idle_fence);
+    }
+    }
+}
+
+void
+present_send_config_notify(WindowPtr window, int x, int y, int w, int h, int bw, WindowPtr sibling)
+{
+    present_window_priv_ptr window_priv = present_window_priv(window);
+
+    if (window_priv) {
+        xPresentConfigureNotify cn = {
+            .type = GenericEvent,
+            .extension = present_request,
+            .length = (sizeof(xPresentConfigureNotify) - 32) >> 2,
+            .evtype = PresentConfigureNotify,
+            .eid = 0,
+            .window = window->drawable.id,
+            .x = x,
+            .y = y,
+            .width = w,
+            .height = h,
+            .off_x = 0,
+            .off_y = 0,
+            .pixmap_width = w,
+            .pixmap_height = h,
+            .pixmap_flags = 0
+        };
+        present_event_ptr event;
+
+        for (event = window_priv->events; event; event = event->next) {
+            if (event->mask & (1 << PresentConfigureNotify)) {
+                cn.eid = event->id;
+                WriteEventsToClient(event->client, 1, (xEvent *) &cn);
+            }
+        }
+    }
+}
+
+void
+present_send_complete_notify(WindowPtr window, CARD8 kind, CARD8 mode, CARD32 serial, uint64_t ust, uint64_t msc)
+{
+    present_window_priv_ptr window_priv = present_window_priv(window);
+
+    if (window_priv) {
+        xPresentCompleteNotify cn = {
+            .type = GenericEvent,
+            .extension = present_request,
+            .length = (sizeof(xPresentCompleteNotify) - 32) >> 2,
+            .evtype = PresentCompleteNotify,
+            .kind = kind,
+            .mode = mode,
+            .eid = 0,
+            .window = window->drawable.id,
+            .serial = serial,
+            .ust = ust,
+            .msc = msc,
+        };
+        present_event_ptr event;
+
+        for (event = window_priv->events; event; event = event->next) {
+            if (event->mask & PresentCompleteNotifyMask) {
+                cn.eid = event->id;
+                WriteEventsToClient(event->client, 1, (xEvent *) &cn);
+            }
+        }
+    }
+}
+
+void
+present_send_idle_notify(WindowPtr window, CARD32 serial, PixmapPtr pixmap, struct present_fence *idle_fence)
+{
+    present_window_priv_ptr window_priv = present_window_priv(window);
+
+    if (window_priv) {
+        xPresentIdleNotify in = {
+            .type = GenericEvent,
+            .extension = present_request,
+            .length = (sizeof(xPresentIdleNotify) - 32) >> 2,
+            .evtype = PresentIdleNotify,
+            .eid = 0,
+            .window = window->drawable.id,
+            .serial = serial,
+            .pixmap = pixmap->drawable.id,
+            .idle_fence = present_fence_id(idle_fence)
+        };
+        present_event_ptr event;
+
+        for (event = window_priv->events; event; event = event->next) {
+            if (event->mask & PresentIdleNotifyMask) {
+                in.eid = event->id;
+                WriteEventsToClient(event->client, 1, (xEvent *) &in);
+            }
+        }
+    }
+}
+
+int
+present_select_input(ClientPtr client, XID eid, WindowPtr window, CARD32 mask)
+{
+    present_window_priv_ptr window_priv = present_get_window_priv(window, mask != 0);
+    present_event_ptr event;
+
+    if (!window_priv) {
+        if (mask)
+            return BadAlloc;
+        return Success;
+    }
+
+    event = calloc (1, sizeof (present_event_rec));
+    if (!event)
+        return BadAlloc;
+
+    event->client = client;
+    event->window = window;
+    event->id = eid;
+    event->mask = mask;
+
+    event->next = window_priv->events;
+    window_priv->events = event;
+
+    if (!AddResource(event->id, present_event_type, (pointer) event))
+        return BadAlloc;
+
+    return Success;
+}
+
+Bool
+present_event_init(void)
+{
+    present_event_type = CreateNewResourceType(present_free_event, "PresentEvent");
+    if (!present_event_type)
+        return FALSE;
+
+    GERegisterExtension(present_request, present_event_swap);
+    return TRUE;
+}
diff --git a/present/present_fake.c b/present/present_fake.c
new file mode 100644
index 0000000..b8f3fc7
--- /dev/null
+++ b/present/present_fake.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp at keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifdef HAVE_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include "present_priv.h"
+#include "list.h"
+
+static struct xorg_list fake_vblank_queue;
+
+typedef struct present_fake_vblank {
+    struct xorg_list            list;
+    uint64_t                    event_id;
+    OsTimerPtr                  timer;
+    ScreenPtr                   screen;
+} present_fake_vblank_rec, *present_fake_vblank_ptr;
+
+int
+present_fake_get_ust_msc(ScreenPtr screen, uint64_t *ust, uint64_t *msc)
+{
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    *ust = GetTimeInMicros();
+    *msc = *ust / screen_priv->fake_interval;
+    return Success;
+}
+
+static void
+present_fake_notify(ScreenPtr screen, uint64_t event_id)
+{
+    uint64_t                    ust, msc;
+
+    present_fake_get_ust_msc(screen, &ust, &msc);
+    present_event_notify(event_id, ust, msc);
+}
+
+static CARD32
+present_fake_do_timer(OsTimerPtr timer,
+                      CARD32 time,
+                      pointer arg)
+{
+    present_fake_vblank_ptr     fake_vblank = arg;
+
+    present_fake_notify(fake_vblank->screen, fake_vblank->event_id);
+    xorg_list_del(&fake_vblank->list);
+    free(fake_vblank);
+    return 0;
+}
+
+void
+present_fake_abort_vblank(ScreenPtr screen, uint64_t event_id, uint64_t msc)
+{
+    present_fake_vblank_ptr     fake_vblank, tmp;
+
+    xorg_list_for_each_entry_safe(fake_vblank, tmp, &fake_vblank_queue, list) {
+        if (fake_vblank->event_id == event_id) {
+            TimerCancel(fake_vblank->timer);
+            xorg_list_del(&fake_vblank->list);
+            free (fake_vblank);
+            break;
+        }
+    }
+}
+
+int
+present_fake_queue_vblank(ScreenPtr     screen,
+                          uint64_t      event_id,
+                          uint64_t      msc)
+{
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+    uint64_t                    ust = msc * screen_priv->fake_interval;
+    uint64_t                    now = GetTimeInMicros();
+    INT32                       delay = ((int64_t) (ust - now)) / 1000;
+    present_fake_vblank_ptr     fake_vblank;
+
+    if (delay <= 0) {
+        present_fake_notify(screen, event_id);
+        return Success;
+    }
+
+    fake_vblank = calloc (1, sizeof (present_fake_vblank_rec));
+    if (!fake_vblank)
+        return BadAlloc;
+
+    fake_vblank->screen = screen;
+    fake_vblank->event_id = event_id;
+    fake_vblank->timer = TimerSet(NULL, 0, delay, present_fake_do_timer, fake_vblank);
+    if (!fake_vblank->timer) {
+        free(fake_vblank);
+        return BadAlloc;
+    }
+
+    xorg_list_add(&fake_vblank->list, &fake_vblank_queue);
+
+    return Success;
+}
+
+void
+present_fake_screen_init(ScreenPtr screen)
+{
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    /* For screens with hardware vblank support, the fake code
+     * will be used for off-screen windows and while screens are blanked,
+     * in which case we want a slow interval here
+     *
+     * Otherwise, pretend that the screen runs at 60Hz
+     */
+    if (screen_priv->info && screen_priv->info->get_crtc)
+        screen_priv->fake_interval = 1000000;
+    else
+        screen_priv->fake_interval = 16667;
+}
+
+void
+present_fake_queue_init(void)
+{
+    xorg_list_init(&fake_vblank_queue);
+}
diff --git a/present/present_fence.c b/present/present_fence.c
new file mode 100644
index 0000000..db5efca
--- /dev/null
+++ b/present/present_fence.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * 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_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include "present_priv.h"
+#include <gcstruct.h>
+#include <misync.h>
+#include <misyncstr.h>
+
+/*
+ * Wraps SyncFence objects so we can add a SyncTrigger to find out
+ * when the SyncFence gets destroyed and clean up appropriately
+ */
+
+struct present_fence {
+    SyncTrigger         trigger;
+    SyncFence           *fence;
+};
+
+/*
+ * SyncTrigger callbacks
+ */
+static Bool
+present_fence_sync_check_trigger(SyncTrigger *trigger, XSyncValue oldval)
+{
+    return FALSE;
+}
+
+static void
+present_fence_sync_trigger_fired(SyncTrigger *trigger)
+{
+}
+
+static void
+present_fence_sync_counter_destroyed(SyncTrigger *trigger)
+{
+    struct present_fence        *present_fence = container_of(trigger, struct present_fence, trigger);
+
+    present_fence->fence = NULL;
+}
+
+struct present_fence *
+present_fence_create(SyncFence *fence)
+{
+    struct present_fence        *present_fence;
+
+    present_fence = calloc (1, sizeof (struct present_fence));
+    if (!present_fence)
+        return NULL;
+
+    present_fence->fence = fence;
+    present_fence->trigger.pSync = (SyncObject *) fence;
+    present_fence->trigger.CheckTrigger = present_fence_sync_check_trigger;
+    present_fence->trigger.TriggerFired = present_fence_sync_trigger_fired;
+    present_fence->trigger.CounterDestroyed = present_fence_sync_counter_destroyed;
+
+    if (SyncAddTriggerToSyncObject(&present_fence->trigger) != Success) {
+        free (present_fence);
+        return NULL;
+    }
+    return present_fence;
+}
+
+void
+present_fence_destroy(struct present_fence *present_fence)
+{
+    if (present_fence) {
+        if (present_fence->fence)
+            SyncDeleteTriggerFromSyncObject(&present_fence->trigger);
+        free(present_fence);
+    }
+}
+
+void
+present_fence_set_triggered(struct present_fence *present_fence)
+{
+    if (present_fence)
+        if (present_fence->fence)
+            (*present_fence->fence->funcs.SetTriggered) (present_fence->fence);
+}
+
+XID
+present_fence_id(struct present_fence *present_fence)
+{
+    if (!present_fence)
+        return None;
+    if (!present_fence->fence)
+        return None;
+    return present_fence->fence->sync.id;
+}
diff --git a/present/present_priv.h b/present/present_priv.h
new file mode 100644
index 0000000..a92b62a
--- /dev/null
+++ b/present/present_priv.h
@@ -0,0 +1,288 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * 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 _PRESENT_PRIV_H_
+#define _PRESENT_PRIV_H_
+
+#include <X11/X.h>
+#include "scrnintstr.h"
+#include "misc.h"
+#include "list.h"
+#include "windowstr.h"
+#include "dixstruct.h"
+#include "present.h"
+#include <syncsdk.h>
+#include <syncsrv.h>
+#include <xfixes.h>
+#include <randrstr.h>
+
+extern int present_request;
+
+extern DevPrivateKeyRec present_screen_private_key;
+
+typedef struct present_fence *present_fence_ptr;
+
+typedef struct present_notify present_notify_rec, *present_notify_ptr;
+
+struct present_notify {
+    struct xorg_list    window_list;
+    WindowPtr           window;
+    CARD32              serial;
+};
+
+struct present_vblank {
+    struct xorg_list    window_list;
+    struct xorg_list    event_queue;
+    ScreenPtr           screen;
+    WindowPtr           window;
+    PixmapPtr           pixmap;
+    RegionPtr           valid;
+    RegionPtr           update;
+    RRCrtcPtr           crtc;
+    uint32_t            serial;
+    int16_t             x_off;
+    int16_t             y_off;
+    CARD16              kind;
+    uint64_t            event_id;
+    uint64_t            target_msc;
+    uint64_t            msc_offset;
+    present_fence_ptr   idle_fence;
+    present_fence_ptr   wait_fence;
+    present_notify_ptr  notifies;
+    int                 num_notifies;
+    Bool                flip;
+    Bool                sync_flip;
+    Bool                abort_flip;
+
+    Bool                window_destroyed;
+};
+
+typedef struct present_screen_priv {
+    CloseScreenProcPtr          CloseScreen;
+    ConfigNotifyProcPtr         ConfigNotify;
+    DestroyWindowProcPtr        DestroyWindow;
+    ClipNotifyProcPtr           ClipNotify;
+
+    present_vblank_ptr          flip_pending;
+    uint64_t                    unflip_event_id;
+
+    uint32_t                    fake_interval;
+
+    /* Currently active flipped pixmap and fence */
+    RRCrtcPtr                   flip_crtc;
+    WindowPtr                   flip_window;
+    uint32_t                    flip_serial;
+    PixmapPtr                   flip_pixmap;
+    present_fence_ptr           flip_idle_fence;
+
+    present_screen_info_ptr     info;
+} present_screen_priv_rec, *present_screen_priv_ptr;
+
+#define wrap(priv,real,mem,func) {\
+    priv->mem = real->mem; \
+    real->mem = func; \
+}
+
+#define unwrap(priv,real,mem) {\
+    real->mem = priv->mem; \
+}
+
+static inline present_screen_priv_ptr
+present_screen_priv(ScreenPtr screen)
+{
+    return (present_screen_priv_ptr)dixLookupPrivate(&(screen)->devPrivates, &present_screen_private_key);
+}
+
+/*
+ * Each window has a list of clients and event masks
+ */
+typedef struct present_event *present_event_ptr;
+
+typedef struct present_event {
+    present_event_ptr next;
+    ClientPtr client;
+    WindowPtr window;
+    XID id;
+    int mask;
+} present_event_rec;
+
+typedef struct present_window_priv {
+    present_event_ptr      events;
+    RRCrtcPtr              crtc;        /* Last reported CRTC from get_ust_msc */
+    uint64_t               msc_offset;
+    uint64_t               msc;         /* Last reported MSC from the current crtc */
+    struct xorg_list       vblank;
+    struct xorg_list       notifies;
+} present_window_priv_rec, *present_window_priv_ptr;
+
+extern DevPrivateKeyRec present_window_private_key;
+
+static inline present_window_priv_ptr
+present_window_priv(WindowPtr window)
+{
+    return (present_window_priv_ptr)dixGetPrivate(&(window)->devPrivates, &present_window_private_key);
+}
+
+present_window_priv_ptr
+present_get_window_priv(WindowPtr window, Bool create);
+
+extern RESTYPE present_event_type;
+
+/*
+ * present.c
+ */
+int
+present_pixmap(WindowPtr window,
+               PixmapPtr pixmap,
+               CARD32 serial,
+               RegionPtr valid,
+               RegionPtr update,
+               int16_t x_off,
+               int16_t y_off,
+               RRCrtcPtr target_crtc,
+               SyncFence *wait_fence,
+               SyncFence *idle_fence,
+               uint32_t options,
+               uint64_t target_msc,
+               uint64_t divisor,
+               uint64_t remainder,
+               present_notify_ptr notifies,
+               int num_notifies);
+
+int
+present_notify_msc(WindowPtr window,
+                   CARD32 serial,
+                   uint64_t target_msc,
+                   uint64_t divisor,
+                   uint64_t remainder);
+
+void
+present_abort_vblank(ScreenPtr screen, RRCrtcPtr crtc, uint64_t event_id, uint64_t msc);
+
+void
+present_vblank_destroy(present_vblank_ptr vblank);
+
+void
+present_flip_destroy(ScreenPtr screen);
+
+void
+present_check_flip_window(WindowPtr window);
+
+RRCrtcPtr
+present_get_crtc(WindowPtr window);
+
+uint32_t
+present_query_capabilities(RRCrtcPtr crtc);
+
+Bool
+present_init(void);
+
+/*
+ * present_event.c
+ */
+
+void
+present_free_events(WindowPtr window);
+
+void
+present_send_config_notify(WindowPtr window, int x, int y, int w, int h, int bw, WindowPtr sibling);
+
+void
+present_send_complete_notify(WindowPtr window, CARD8 kind, CARD8 mode, CARD32 serial, uint64_t ust, uint64_t msc);
+
+void
+present_send_idle_notify(WindowPtr window, CARD32 serial, PixmapPtr pixmap, present_fence_ptr idle_fence);
+
+int
+present_select_input(ClientPtr client,
+                     CARD32 eid,
+                     WindowPtr window,
+                     CARD32 event_mask);
+
+Bool
+present_event_init(void);
+
+/*
+ * present_fake.c
+ */
+int
+present_fake_get_ust_msc(ScreenPtr screen, uint64_t *ust, uint64_t *msc);
+
+int
+present_fake_queue_vblank(ScreenPtr screen, uint64_t event_id, uint64_t msc);
+
+void
+present_fake_abort_vblank(ScreenPtr screen, uint64_t event_id, uint64_t msc);
+
+void
+present_fake_screen_init(ScreenPtr screen);
+
+void
+present_fake_queue_init(void);
+
+/*
+ * present_fence.c
+ */
+struct present_fence *
+present_fence_create(SyncFence *sync_fence);
+
+void
+present_fence_destroy(struct present_fence *present_fence);
+
+void
+present_fence_set_triggered(struct present_fence *present_fence);
+
+XID
+present_fence_id(struct present_fence *present_fence);
+
+/*
+ * present_notify.c
+ */
+void
+present_clear_window_notifies(WindowPtr window);
+
+void
+present_free_window_notify(present_notify_ptr notify);
+
+int
+present_add_window_notify(present_notify_ptr notify);
+
+int
+present_create_notifies(ClientPtr client, int num_notifies, xPresentNotify *x_notifies, present_notify_ptr *p_notifies);
+
+void
+present_destroy_notifies(present_notify_ptr notifies, int num_notifies);
+
+/*
+ * present_request.c
+ */
+int
+proc_present_dispatch(ClientPtr client);
+
+int
+sproc_present_dispatch(ClientPtr client);
+
+/*
+ * present_screen.c
+ */
+
+#endif /*  _PRESENT_PRIV_H_ */
diff --git a/present/present_request.c b/present/present_request.c
new file mode 100644
index 0000000..095fa2d
--- /dev/null
+++ b/present/present_request.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * 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_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include "present_priv.h"
+#include "randrstr.h"
+
+static int
+proc_present_query_version(ClientPtr client)
+{
+    REQUEST(xPresentQueryVersionReq);
+    xPresentQueryVersionReply rep = {
+        .type = X_Reply,
+        .sequenceNumber = client->sequence,
+        .length = 0,
+        .majorVersion = PRESENT_MAJOR,
+        .minorVersion = PRESENT_MINOR
+    };
+
+    REQUEST_SIZE_MATCH(xPresentQueryVersionReq);
+    (void) stuff;
+    if (client->swapped) {
+        swaps(&rep.sequenceNumber);
+        swapl(&rep.length);
+        swapl(&rep.majorVersion);
+        swapl(&rep.minorVersion);
+    }
+    WriteToClient(client, sizeof(rep), &rep);
+    return Success;
+}
+
+#define VERIFY_FENCE_OR_NONE(fence_ptr, fence_id, client, access) do {  \
+        if ((fence_id) == None)                                         \
+            (fence_ptr) = NULL;                                         \
+        else {                                                          \
+            int __rc__ = SyncVerifyFence(&fence_ptr, fence_id, client, access); \
+            if (__rc__ != Success)                                      \
+                return __rc__;                                          \
+        }                                                               \
+    } while (0)
+
+#define VERIFY_CRTC_OR_NONE(crtc_ptr, crtc_id, client, access) do {     \
+        if ((crtc_id) == None)                                          \
+            (crtc_ptr) = NULL;                                          \
+        else {                                                          \
+            VERIFY_RR_CRTC(crtc_id, crtc_ptr, access);                  \
+        }                                                               \
+    } while (0)
+
+static int
+proc_present_pixmap(ClientPtr client)
+{
+    REQUEST(xPresentPixmapReq);
+    WindowPtr           window;
+    PixmapPtr           pixmap;
+    RegionPtr           valid = NULL;
+    RegionPtr           update = NULL;
+    SyncFence           *wait_fence;
+    SyncFence           *idle_fence;
+    RRCrtcPtr           target_crtc;
+    int                 ret;
+    int                 nnotifies;
+    present_notify_ptr  notifies = NULL;
+
+    REQUEST_AT_LEAST_SIZE(xPresentPixmapReq);
+    ret = dixLookupWindow(&window, stuff->window, client, DixWriteAccess);
+    if (ret != Success)
+        return ret;
+    ret = dixLookupResourceByType((pointer *) &pixmap, stuff->pixmap, RT_PIXMAP, client, DixReadAccess);
+    if (ret != Success)
+        return ret;
+
+    if (window->drawable.depth != pixmap->drawable.depth)
+        return BadMatch;
+
+    VERIFY_REGION_OR_NONE(valid, stuff->valid, client, DixReadAccess);
+    VERIFY_REGION_OR_NONE(update, stuff->update, client, DixReadAccess);
+
+    VERIFY_CRTC_OR_NONE(target_crtc, stuff->target_crtc, client, DixReadAccess);
+
+    VERIFY_FENCE_OR_NONE(wait_fence, stuff->wait_fence, client, DixReadAccess);
+    VERIFY_FENCE_OR_NONE(idle_fence, stuff->idle_fence, client, DixWriteAccess);
+
+    if (stuff->options & ~(PresentAllOptions)) {
+        client->errorValue = stuff->options;
+        return BadValue;
+    }
+
+    /*
+     * Check to see if remainder is sane
+     */
+    if (stuff->divisor == 0) {
+        if (stuff->remainder != 0) {
+            client->errorValue = (CARD32) stuff->remainder;
+            return BadValue;
+        }
+    } else {
+        if (stuff->remainder >= stuff->divisor) {
+            client->errorValue = (CARD32) stuff->remainder;
+            return BadValue;
+        }
+    }
+
+    nnotifies = (client->req_len << 2) - sizeof (xPresentPixmapReq);
+    if (nnotifies % sizeof (xPresentNotify))
+        return BadLength;
+
+    nnotifies /= sizeof (xPresentNotify);
+    if (nnotifies) {
+        ret = present_create_notifies(client, nnotifies, (xPresentNotify *) (stuff + 1), &notifies);
+        if (ret != Success)
+            return ret;
+    }
+
+    ret = present_pixmap(window, pixmap, stuff->serial, valid, update,
+                         stuff->x_off, stuff->y_off, target_crtc,
+                         wait_fence, idle_fence, stuff->options,
+                         stuff->target_msc, stuff->divisor, stuff->remainder, notifies, nnotifies);
+    if (ret != Success)
+        present_destroy_notifies(notifies, nnotifies);
+    return ret;
+}
+
+static int
+proc_present_notify_msc(ClientPtr client)
+{
+    REQUEST(xPresentNotifyMSCReq);
+    WindowPtr   window;
+    int         rc;
+
+    REQUEST_SIZE_MATCH(xPresentNotifyMSCReq);
+    rc = dixLookupWindow(&window, stuff->window, client, DixReadAccess);
+    if (rc != Success)
+        return rc;
+
+    /*
+     * Check to see if remainder is sane
+     */
+    if (stuff->divisor == 0) {
+        if (stuff->remainder != 0) {
+            client->errorValue = (CARD32) stuff->remainder;
+            return BadValue;
+        }
+    } else {
+        if (stuff->remainder >= stuff->divisor) {
+            client->errorValue = (CARD32) stuff->remainder;
+            return BadValue;
+        }
+    }
+
+    return present_notify_msc(window, stuff->serial,
+                              stuff->target_msc, stuff->divisor, stuff->remainder);
+}
+
+static int
+proc_present_select_input (ClientPtr client)
+{
+    REQUEST(xPresentSelectInputReq);
+    WindowPtr window;
+    int rc;
+
+    REQUEST_SIZE_MATCH(xPresentSelectInputReq);
+
+    LEGAL_NEW_RESOURCE(stuff->eid, client);
+
+    rc = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess);
+    if (rc != Success)
+        return rc;
+
+    if (stuff->eventMask & ~PresentAllEvents) {
+        client->errorValue = stuff->eventMask;
+        return BadValue;
+    }
+    return present_select_input(client, stuff->eid, window, stuff->eventMask);
+}
+
+static int
+proc_present_query_capabilities (ClientPtr client)
+{
+    REQUEST(xPresentQueryCapabilitiesReq);
+    xPresentQueryCapabilitiesReply rep = {
+        .type = X_Reply,
+        .sequenceNumber = client->sequence,
+        .length = 0,
+    };
+    WindowPtr   window;
+    RRCrtcPtr   crtc = NULL;
+    int         r;
+
+    r = dixLookupWindow(&window, stuff->target, client, DixGetAttrAccess);
+    switch (r) {
+    case Success:
+        crtc = present_get_crtc(window);
+        break;
+    case BadWindow:
+        VERIFY_RR_CRTC(stuff->target, crtc, DixGetAttrAccess);
+        break;
+    default:
+        return r;
+    }
+
+    rep.capabilities = present_query_capabilities(crtc);
+
+    if (client->swapped) {
+        swaps(&rep.sequenceNumber);
+        swapl(&rep.length);
+        swapl(&rep.capabilities);
+    }
+    WriteToClient(client, sizeof(rep), &rep);
+    return Success;
+}
+
+int (*proc_present_vector[PresentNumberRequests]) (ClientPtr) = {
+    proc_present_query_version,            /* 0 */
+    proc_present_pixmap,                   /* 1 */
+    proc_present_notify_msc,               /* 2 */
+    proc_present_select_input,             /* 3 */
+    proc_present_query_capabilities,       /* 4 */
+};
+
+int
+proc_present_dispatch(ClientPtr client)
+{
+    REQUEST(xReq);
+    if (stuff->data >= PresentNumberRequests || !proc_present_vector[stuff->data])
+        return BadRequest;
+    return (*proc_present_vector[stuff->data]) (client);
+}
+
+static int
+sproc_present_query_version(ClientPtr client)
+{
+    REQUEST(xPresentQueryVersionReq);
+
+    swaps(&stuff->length);
+    swapl(&stuff->majorVersion);
+    swapl(&stuff->minorVersion);
+    return (*proc_present_vector[stuff->presentReqType]) (client);
+}
+
+static int
+sproc_present_pixmap(ClientPtr client)
+{
+    REQUEST(xPresentPixmapReq);
+
+    swaps(&stuff->length);
+    swapl(&stuff->window);
+    swapl(&stuff->pixmap);
+    swapl(&stuff->valid);
+    swapl(&stuff->update);
+    swaps(&stuff->x_off);
+    swaps(&stuff->y_off);
+    swapll(&stuff->target_msc);
+    swapll(&stuff->divisor);
+    swapll(&stuff->remainder);
+    swapl(&stuff->idle_fence);
+    return (*proc_present_vector[stuff->presentReqType]) (client);
+}
+
+static int
+sproc_present_notify_msc(ClientPtr client)
+{
+    REQUEST(xPresentNotifyMSCReq);
+
+    swaps(&stuff->length);
+    swapl(&stuff->window);
+    swapll(&stuff->target_msc);
+    swapll(&stuff->divisor);
+    swapll(&stuff->remainder);
+    return (*proc_present_vector[stuff->presentReqType]) (client);
+}
+
+static int
+sproc_present_select_input (ClientPtr client)
+{
+    REQUEST(xPresentSelectInputReq);
+
+    swaps(&stuff->length);
+    swapl(&stuff->window);
+    swapl(&stuff->eventMask);
+    return (*proc_present_vector[stuff->presentReqType]) (client);
+}
+
+static int
+sproc_present_query_capabilities (ClientPtr client)
+{
+    REQUEST(xPresentQueryCapabilitiesReq);
+    swaps(&stuff->length);
+    swapl(&stuff->target);
+    return (*proc_present_vector[stuff->presentReqType]) (client);
+}
+
+int (*sproc_present_vector[PresentNumberRequests]) (ClientPtr) = {
+    sproc_present_query_version,           /* 0 */
+    sproc_present_pixmap,                  /* 1 */
+    sproc_present_notify_msc,              /* 2 */
+    sproc_present_select_input,            /* 3 */
+    sproc_present_query_capabilities,      /* 4 */
+};
+
+int
+sproc_present_dispatch(ClientPtr client)
+{
+    REQUEST(xReq);
+    if (stuff->data >= PresentNumberRequests || !sproc_present_vector[stuff->data])
+        return BadRequest;
+    return (*sproc_present_vector[stuff->data]) (client);
+}
diff --git a/present/present_screen.c b/present/present_screen.c
new file mode 100644
index 0000000..50b2b2d
--- /dev/null
+++ b/present/present_screen.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * 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_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include "present_priv.h"
+
+int present_request;
+DevPrivateKeyRec present_screen_private_key;
+DevPrivateKeyRec present_window_private_key;
+
+/*
+ * Get a pointer to a present window private, creating if necessary
+ */
+present_window_priv_ptr
+present_get_window_priv(WindowPtr window, Bool create)
+{
+    present_window_priv_ptr window_priv = present_window_priv(window);
+
+    if (!create || window_priv != NULL)
+        return window_priv;
+    window_priv = calloc (1, sizeof (present_window_priv_rec));
+    if (!window_priv)
+        return NULL;
+    xorg_list_init(&window_priv->vblank);
+    xorg_list_init(&window_priv->notifies);
+    dixSetPrivate(&window->devPrivates, &present_window_private_key, window_priv);
+    return window_priv;
+}
+
+/*
+ * Hook the close screen function to clean up our screen private
+ */
+static Bool
+present_close_screen(ScreenPtr screen)
+{
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    present_flip_destroy(screen);
+
+    unwrap(screen_priv, screen, CloseScreen);
+    (*screen->CloseScreen) (screen);
+    free(screen_priv);
+    return TRUE;
+}
+
+/*
+ * Free any queued presentations for this window
+ */
+static void
+present_free_window_vblank(WindowPtr window)
+{
+    present_window_priv_ptr     window_priv = present_window_priv(window);
+    present_vblank_ptr          vblank, tmp;
+
+    xorg_list_for_each_entry_safe(vblank, tmp, &window_priv->vblank, window_list) {
+        present_abort_vblank(window->drawable.pScreen, vblank->crtc, vblank->event_id, vblank->target_msc);
+        present_vblank_destroy(vblank);
+    }
+}
+
+/*
+ * Clean up any pending or current flips for this window
+ */
+static void
+present_clear_window_flip(WindowPtr window)
+{
+    ScreenPtr                   screen = window->drawable.pScreen;
+    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+    present_vblank_ptr          flip_pending = screen_priv->flip_pending;
+
+    if (flip_pending && flip_pending->window == window) {
+        assert (flip_pending->abort_flip);
+        flip_pending->window_destroyed = TRUE;
+    }
+    if (screen_priv->flip_window == window)
+        screen_priv->flip_window = NULL;
+}
+
+/*
+ * Hook the close window function to clean up our window private
+ */
+static Bool
+present_destroy_window(WindowPtr window)
+{
+    Bool ret;
+    ScreenPtr screen = window->drawable.pScreen;
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+    present_window_priv_ptr window_priv = present_window_priv(window);
+
+    if (window_priv) {
+        present_clear_window_notifies(window);
+        present_free_events(window);
+        present_free_window_vblank(window);
+        present_clear_window_flip(window);
+        free(window_priv);
+    }
+    unwrap(screen_priv, screen, DestroyWindow);
+    if (screen->DestroyWindow)
+        ret = screen->DestroyWindow (window);
+    else
+        ret = TRUE;
+    wrap(screen_priv, screen, DestroyWindow, present_destroy_window);
+    return ret;
+}
+
+/*
+ * Hook the config notify screen function to deliver present config notify events
+ */
+static int
+present_config_notify(WindowPtr window,
+                   int x, int y, int w, int h, int bw,
+                   WindowPtr sibling)
+{
+    int ret;
+    ScreenPtr screen = window->drawable.pScreen;
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    present_send_config_notify(window, x, y, w, h, bw, sibling);
+
+    unwrap(screen_priv, screen, ConfigNotify);
+    if (screen->ConfigNotify)
+        ret = screen->ConfigNotify (window, x, y, w, h, bw, sibling);
+    else
+        ret = 0;
+    wrap(screen_priv, screen, ConfigNotify, present_config_notify);
+    return ret;
+}
+
+/*
+ * Hook the clip notify screen function to un-flip as necessary
+ */
+
+static void
+present_clip_notify(WindowPtr window, int dx, int dy)
+{
+    ScreenPtr screen = window->drawable.pScreen;
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    present_check_flip_window(window);
+    unwrap(screen_priv, screen, ClipNotify)
+    if (screen->ClipNotify)
+        screen->ClipNotify (window, dx, dy);
+    wrap(screen_priv, screen, ClipNotify, present_clip_notify);
+}
+
+/*
+ * Initialize a screen for use with present
+ */
+int
+present_screen_init(ScreenPtr screen, present_screen_info_ptr info)
+{
+    if (!dixRegisterPrivateKey(&present_screen_private_key, PRIVATE_SCREEN, 0))
+        return FALSE;
+
+    if (!dixRegisterPrivateKey(&present_window_private_key, PRIVATE_WINDOW, 0))
+        return FALSE;
+
+    if (!present_screen_priv(screen)) {
+        present_screen_priv_ptr screen_priv = calloc(1, sizeof (present_screen_priv_rec));
+        if (!screen_priv)
+            return FALSE;
+
+        wrap(screen_priv, screen, CloseScreen, present_close_screen);
+        wrap(screen_priv, screen, DestroyWindow, present_destroy_window);
+        wrap(screen_priv, screen, ConfigNotify, present_config_notify);
+        wrap(screen_priv, screen, ClipNotify, present_clip_notify);
+
+        screen_priv->info = info;
+
+        dixSetPrivate(&screen->devPrivates, &present_screen_private_key, screen_priv);
+
+        present_fake_screen_init(screen);
+    }
+
+    return TRUE;
+}
+
+/*
+ * Initialize the present extension
+ */
+void
+present_extension_init(void)
+{
+    ExtensionEntry *extension;
+    int i;
+
+    extension = AddExtension(PRESENT_NAME, PresentNumberEvents, PresentNumberErrors,
+                             proc_present_dispatch, sproc_present_dispatch,
+                             NULL, StandardMinorOpcode);
+    if (!extension)
+        goto bail;
+
+    present_request = extension->base;
+
+    if (!present_init())
+        goto bail;
+
+    if (!present_event_init())
+        goto bail;
+
+    for (i = 0; i < screenInfo.numScreens; i++) {
+        if (!present_screen_init(screenInfo.screens[i], NULL))
+            goto bail;
+    }
+    return;
+
+bail:
+    FatalError("Cannot initialize Present extension");
+}
diff --git a/present/presentext.h b/present/presentext.h
new file mode 100644
index 0000000..f177f55
--- /dev/null
+++ b/present/presentext.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * 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 _PRESENTEXT_H_
+#define _PRESENTEXT_H_
+
+extern _X_EXPORT void
+present_extension_init(void);
+
+#endif /* _PRESENTEXT_H_ */
-- 
1.8.4.rc3



More information about the xorg-devel mailing list