summaryrefslogtreecommitdiff
path: root/present
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2013-07-11 16:11:57 -0700
committerKeith Packard <keithp@keithp.com>2013-10-31 16:59:18 -0700
commit5c5c1b77982a9af7279a90bc3c2be48adaa9c778 (patch)
tree98dbaa447cea20b00708347d6805ba5da43cdc7e /present
parent563138298868f62501875d3016f03469dcffaad0 (diff)
present: Add Present extension
Provides both a software implementation using timers and driver hooks to base everything on vblank intervals. Signed-off-by: Keith Packard <keithp@keithp.com> Reviewed-by: Adam Jackson <ajax@redhat.com>
Diffstat (limited to 'present')
-rw-r--r--present/Makefile.am17
-rw-r--r--present/present.c775
-rw-r--r--present/present.h118
-rw-r--r--present/present_event.c239
-rw-r--r--present/present_fake.c140
-rw-r--r--present/present_fence.c112
-rw-r--r--present/present_notify.c114
-rw-r--r--present/present_priv.h288
-rw-r--r--present/present_request.c330
-rw-r--r--present/present_screen.c231
-rw-r--r--present/presentext.h29
11 files changed, 2393 insertions, 0 deletions
diff --git a/present/Makefile.am b/present/Makefile.am
new file mode 100644
index 000000000..7fea6699f
--- /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 000000000..4c97ce40c
--- /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 000000000..6a451fb23
--- /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 000000000..a30bc8286
--- /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 000000000..a67759254
--- /dev/null
+++ b/present/present_fake.c
@@ -0,0 +1,140 @@
+/*
+ * 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 "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 000000000..db5efcaad
--- /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_notify.c b/present/present_notify.c
new file mode 100644
index 000000000..e272e08dc
--- /dev/null
+++ b/present/present_notify.c
@@ -0,0 +1,114 @@
+/*
+ * 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"
+
+/*
+ * Mark all pending notifies for 'window' as invalid when
+ * the window is destroyed
+ */
+
+void
+present_clear_window_notifies(WindowPtr window)
+{
+ present_notify_ptr notify;
+ present_window_priv_ptr window_priv = present_window_priv(window);
+
+ if (!window_priv)
+ return;
+
+ xorg_list_for_each_entry(notify, &window_priv->notifies, window_list) {
+ notify->window = NULL;
+ }
+}
+
+/*
+ * 'notify' is being freed; remove it from the window's notify list
+ */
+
+void
+present_free_window_notify(present_notify_ptr notify)
+{
+ xorg_list_del(&notify->window_list);
+}
+
+/*
+ * 'notify' is new; add it to the specified window
+ */
+
+int
+present_add_window_notify(present_notify_ptr notify)
+{
+ WindowPtr window = notify->window;
+ present_window_priv_ptr window_priv = present_get_window_priv(window, TRUE);
+
+ if (!window_priv)
+ return BadAlloc;
+
+ xorg_list_add(&notify->window_list, &window_priv->notifies);
+ return Success;
+}
+
+int
+present_create_notifies(ClientPtr client, int num_notifies, xPresentNotify *x_notifies, present_notify_ptr *p_notifies)
+{
+ present_notify_ptr notifies;
+ int i;
+ int added = 0;
+ int status;
+
+ notifies = calloc (num_notifies, sizeof (present_notify_rec));
+ if (!notifies)
+ return BadAlloc;
+
+ for (i = 0; i < num_notifies; i++) {
+ status = dixLookupWindow(&notifies[i].window, x_notifies[i].window, client, DixGetAttrAccess);
+ if (status != Success)
+ goto bail;
+
+ notifies[i].serial = x_notifies[i].serial;
+ status = present_add_window_notify(&notifies[i]);
+ if (status != Success)
+ goto bail;
+
+ added = i;
+ }
+ return Success;
+
+bail:
+ present_destroy_notifies(notifies, added);
+ return status;
+}
+
+void
+present_destroy_notifies(present_notify_ptr notifies, int num_notifies)
+{
+ int i;
+ for (i = 0; i < num_notifies; i++)
+ present_free_window_notify(&notifies[i]);
+
+ free(notifies);
+}
diff --git a/present/present_priv.h b/present/present_priv.h
new file mode 100644
index 000000000..a92b62a2d
--- /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 000000000..095fa2daf
--- /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 000000000..50b2b2d23
--- /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 000000000..f177f55dc
--- /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_ */