diff --git a/12to11.conf b/12to11.conf
index f24aed1..cb7f974 100644
--- a/12to11.conf
+++ b/12to11.conf
@@ -14,6 +14,7 @@
PIXMANINCLUDES = -I$(INCROOT)/pixman-1
XPRESENTLIB = -lXpresent
WAYLAND_SCANNER = wayland-scanner
+ XSHMFENCELIB = -lxshmfence
/* Uncomment the following code if building with EGL support. */
diff --git a/12to11.man b/12to11.man
index 8d8d0a9..dd7c83b 100644
--- a/12to11.man
+++ b/12to11.man
@@ -293,9 +293,6 @@ least support the
.B _NET_WM_SYNC_REQUEST
window manager protocol will result in Wayland programs running badly.
.PP
-In addition, surfaces transforms are not supported nor reported. The
-vast majority of clients seem not to make use of this feature, and
-implementing it would be a lot of trouble.
.SH "SEE ALSO"
X(7), Xorg(1)
.SH AUTHORS
diff --git a/Imakefile b/Imakefile
index c01bf7f..59363eb 100644
--- a/Imakefile
+++ b/Imakefile
@@ -11,7 +11,7 @@ DEPLIBS = $(DEPXLIB) $(DEPEXTENSIONLIB) $(DEPXRANDRLIB) $(DEPXRENDERLIB) \
LOCAL_LIBRARIES = $(XLIB) $(EXTENSIONLIB) $(XCBLIB) $(XCB) $(XCB_SHM) \
$(XRANDRLIB) $(PIXMAN) $(XRENDERLIB) $(XILIB) $(XKBFILELIB) $(XFIXESLIB) \
$(XCB_DRI3) $(XCB_SHAPE) $(WAYLAND_SERVER) $(XCB_RANDR) $(DRM) \
- $(XPRESENTLIB)
+ $(XPRESENTLIB) $(XSHMFENCELIB)
INCLUDES := $(DRMINCLUDES) $(PIXMANINCLUDES)
@@ -25,7 +25,7 @@ SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c \
wp_viewporter.c decoration.c text_input.c \
single_pixel_buffer.c drm_lease.c pointer_constraints.c \
time.c relative_pointer.c keyboard_shortcuts_inhibit.c \
- idle_inhibit.c process.c
+ idle_inhibit.c process.c fence_ring.c
OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
surface.o region.o shm.o atoms.o subcompositor.o positioner.o \
@@ -37,7 +37,7 @@ OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
wp_viewporter.o decoration.o text_input.o \
single_pixel_buffer.o drm_lease.o pointer_constraints.o \
time.o relative_pointer.o keyboard_shortcuts_inhibit.o \
- idle_inhibit.o process.o
+ idle_inhibit.o process.o fence_ring.o
GENHEADERS = transfer_atoms.h
diff --git a/README b/README
index e7fc12f..a67797f 100644
--- a/README
+++ b/README
@@ -98,8 +98,8 @@ This directory is organized as follows:
those containing MIME types or shaders
Building the source code is simple, provided that you have the
-necessary libwayland-server library, pixman, XCB, DRM, and X extension
-libraries installed:
+necessary libwayland-server library, pixman, XCB, DRM, xshmfence, and
+X extension libraries installed:
xmkmf # to generate the Makefile
make # to build the binary
diff --git a/buffer.c b/buffer.c
index fd4a4f8..72a3f38 100644
--- a/buffer.c
+++ b/buffer.c
@@ -17,6 +17,8 @@ for more details.
You should have received a copy of the GNU General Public License
along with 12to11. If not, see . */
+#include
+
#include "compositor.h"
typedef struct _DestroyListener DestroyListener;
diff --git a/compositor.h b/compositor.h
index 6553fa2..7916b37 100644
--- a/compositor.h
+++ b/compositor.h
@@ -157,6 +157,7 @@ typedef struct _DrmFormat DrmFormat;
typedef struct _ShmFormat ShmFormat;
typedef enum _Operation Operation;
+typedef enum _BufferTransform BufferTransform;
typedef void *IdleCallbackKey;
typedef void *PresentCompletionKey;
@@ -167,6 +168,18 @@ typedef void (*DmaBufFailureFunc) (void *);
typedef void (*BufferIdleFunc) (RenderBuffer, void *);
typedef void (*PresentCompletionFunc) (void *);
+enum _BufferTransform
+ {
+ Normal,
+ CounterClockwise90,
+ CounterClockwise180,
+ CounterClockwise270,
+ Flipped,
+ Flipped90,
+ Flipped180,
+ Flipped270,
+ };
+
enum _Operation
{
OperationOver,
@@ -176,20 +189,27 @@ enum _Operation
enum
{
/* Scale has been set. */
- ScaleSet = 1,
-
+ ScaleSet = 1,
+ /* Transform has been set. */
+ TransformSet = (1 << 1),
/* Offset has been set. */
- OffsetSet = (1 << 2),
-
+ OffsetSet = (1 << 2),
/* Stretch has been set. */
- StretchSet = (1 << 3),
+ StretchSet = (1 << 3),
};
+/* The transforms specified in the draw parameters are applied in the
+ following order: transform, scale, off_x, off_y, crop_width,
+ crop_height, stretch_width, stretch_height. */
+
struct _DrawParams
{
/* Which fields are set. */
int flags;
+ /* A transform by which to rotate the buffer. */
+ BufferTransform transform;
+
/* A scale factor to apply to the buffer. */
double scale;
@@ -643,6 +663,8 @@ extern void XLScaleRegion (pixman_region32_t *, pixman_region32_t *,
float, float);
extern void XLExtendRegion (pixman_region32_t *, pixman_region32_t *,
int, int);
+extern void XLTransformRegion (pixman_region32_t *, pixman_region32_t *,
+ BufferTransform, int, int);
extern Time XLGetServerTimeRoundtrip (void);
extern RootWindowSelection *XLSelectInputFromRootWindow (unsigned long);
@@ -786,6 +808,7 @@ extern void ViewSkip (View *);
extern void ViewUnskip (View *);
extern void ViewMoveFractional (View *, double, double);
+extern void ViewSetTransform (View *, BufferTransform);
extern void ViewSetViewport (View *, double, double, double, double,
double, double);
extern void ViewClearViewport (View *);
@@ -837,19 +860,26 @@ enum _RoleType
DndIconType,
};
+#define RotatesDimensions(transform) \
+ ((transform) == CounterClockwise90 \
+ || (transform) == CounterClockwise270 \
+ || (transform) == Flipped90 \
+ || (transform) == Flipped270)
+
enum
{
- PendingNone = 0,
- PendingOpaqueRegion = 1,
- PendingInputRegion = (1 << 2),
- PendingDamage = (1 << 3),
- PendingSurfaceDamage = (1 << 4),
- PendingBuffer = (1 << 5),
- PendingFrameCallbacks = (1 << 6),
- PendingBufferScale = (1 << 7),
- PendingAttachments = (1 << 8),
- PendingViewportSrc = (1 << 9),
- PendingViewportDest = (1 << 10),
+ PendingNone = 0,
+ PendingOpaqueRegion = 1,
+ PendingInputRegion = (1 << 2),
+ PendingDamage = (1 << 3),
+ PendingSurfaceDamage = (1 << 4),
+ PendingBuffer = (1 << 5),
+ PendingFrameCallbacks = (1 << 6),
+ PendingBufferScale = (1 << 7),
+ PendingAttachments = (1 << 8),
+ PendingViewportSrc = (1 << 9),
+ PendingViewportDest = (1 << 10),
+ PendingBufferTransform = (1 << 11),
/* Flags here are stored in `pending' of the current state for
space reasons. */
@@ -889,6 +919,9 @@ struct _State
/* The scale of this buffer. */
int buffer_scale;
+ /* The buffer transform. */
+ BufferTransform transform;
+
/* List of frame callbacks. */
FrameCallback frame_callbacks;
@@ -1109,8 +1142,6 @@ extern void XLSurfaceReleaseRole (Surface *, Role *);
extern void XLDefaultCommit (Surface *);
-extern void XLStateAttachBuffer (State *, ExtBuffer *);
-extern void XLStateDetachBuffer (State *);
extern void XLSurfaceRunFrameCallbacks (Surface *, struct timespec);
extern void XLSurfaceRunFrameCallbacksMs (Surface *, uint32_t);
extern CommitCallback *XLSurfaceRunAtCommit (Surface *,
@@ -1661,6 +1692,8 @@ extern void MatrixIdentity (Matrix *);
extern void MatrixTranslate (Matrix *, float, float);
extern void MatrixScale (Matrix *, float, float);
extern void MatrixExport (Matrix *, XTransform *);
+extern void MatrixRotate (Matrix *, float, float, float);
+extern void MatrixMirrorHorizontal (Matrix *, float);
/* Defined in wp_viewporter.c. */
@@ -1717,6 +1750,15 @@ extern void RunProcess (ProcessQueue *, char **);
extern ProcessQueue *MakeProcessQueue (void);
extern int ProcessPoll (struct pollfd *, nfds_t, struct timespec *);
+/* Defined in fence_ring.c. */
+
+typedef struct _Fence Fence;
+
+extern Fence *GetFence (void);
+extern void FenceRetain (Fence *);
+extern void FenceAwait (Fence *);
+extern XSyncFence FenceToXFence (Fence *);
+
/* Utility functions that don't belong in a specific file. */
#define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0])
diff --git a/egl.c b/egl.c
index 2ecbbaf..0f79d15 100644
--- a/egl.c
+++ b/egl.c
@@ -35,6 +35,8 @@ along with 12to11. If not, see . */
#include "linux-dmabuf-unstable-v1.h"
+/* TODO: implement buffer transforms. */
+
/* These are flags for the DrmFormats. */
enum
diff --git a/fns.c b/fns.c
index 2ecdc40..ca3e44d 100644
--- a/fns.c
+++ b/fns.c
@@ -403,6 +403,101 @@ XLExtendRegion (pixman_region32_t *dst, pixman_region32_t *src,
XLFree (dst_rects);
}
+static void
+TransformBox (pixman_box32_t *box, BufferTransform transform,
+ int width, int height)
+{
+ pixman_box32_t work;
+
+ switch (transform)
+ {
+ case Normal:
+ work = *box;
+ break;
+
+ case CounterClockwise90:
+ work.x1 = height - box->y2;
+ work.y1 = box->x1;
+ work.x2 = height - box->y1;
+ work.y2 = box->x2;
+ break;
+
+ case CounterClockwise180:
+ work.x1 = width - box->x2;
+ work.y1 = height - box->y2;
+ work.x2 = width - box->x1;
+ work.y2 = height - box->y1;
+ break;
+
+ case CounterClockwise270:
+ work.x1 = box->y1;
+ work.y1 = width - box->x2;
+ work.x2 = box->y2;
+ work.y2 = width - box->x1;
+ break;
+
+ case Flipped:
+ work.x1 = width - box->x2;
+ work.y1 = box->y1;
+ work.x2 = width - box->x1;
+ work.y2 = box->y2;
+ break;
+
+ case Flipped90:
+ work.x1 = box->y1;
+ work.y1 = box->x1;
+ work.x2 = box->y2;
+ work.y2 = box->x2;
+ break;
+
+ case Flipped180:
+ work.x1 = box->x1;
+ work.y1 = height - box->y2;
+ work.x2 = box->x2;
+ work.y2 = height - box->y1;
+ break;
+
+ case Flipped270:
+ work.x1 = height - box->y2;
+ work.y1 = width - box->x2;
+ work.x2 = height - box->y1;
+ work.y2 = width - box->x1;
+ break;
+ }
+
+ *box = work;
+}
+
+void
+XLTransformRegion (pixman_region32_t *dst, pixman_region32_t *src,
+ BufferTransform transform, int width, int height)
+{
+ int nrects, i;
+ pixman_box32_t *src_rects;
+ pixman_box32_t *dst_rects;
+
+ src_rects = pixman_region32_rectangles (src, &nrects);
+
+ if (nrects < 128)
+ dst_rects = alloca (nrects * sizeof *dst_rects);
+ else
+ dst_rects = XLMalloc (nrects * sizeof *dst_rects);
+
+ for (i = 0; i < nrects; ++i)
+ {
+ dst_rects[i] = src_rects[i];
+
+ /* width and height should be in the transformed space! */
+ TransformBox (&dst_rects[i], transform, width, height);
+ }
+
+ pixman_region32_fini (dst);
+ pixman_region32_init_rects (dst, dst_rects, nrects);
+
+ if (nrects >= 128)
+ XLFree (dst_rects);
+}
+
int
XLOpenShm (void)
{
diff --git a/picture_renderer.c b/picture_renderer.c
index e0c4855..b16cffd 100644
--- a/picture_renderer.c
+++ b/picture_renderer.c
@@ -81,6 +81,9 @@ struct _BufferActivityRecord
/* The counter ID. */
uint64_t id;
+ /* The sync fence. */
+ Fence *fence;
+
/* The forward links to the three lists. */
BufferActivityRecord *buffer_next, *target_next, *global_next;
@@ -127,6 +130,9 @@ struct _PictureBuffer
/* Flags. */
int flags;
+ /* The width and height of the buffer. */
+ short width, height;
+
/* The last draw params associated with the picture. */
DrawParams params;
@@ -138,6 +144,8 @@ struct _PictureBuffer
/* Ongoing buffer activity. */
BufferActivityRecord activity;
+
+ int compositable;
};
enum
@@ -264,6 +272,9 @@ struct _DmaBufRecord
/* The depth of the pixmap. */
int depth;
+
+ /* The width and height. */
+ short width, height;
};
/* Hash table mapping between presentation windows and targets. */
@@ -388,6 +399,9 @@ static PresentCompletionCallback all_completion_callbacks;
/* Whether or not direct presentation should be used. */
static Bool use_direct_presentation;
+/* Whether or not to use sync fences. Currently never set. */
+static Bool use_sync_fences;
+
/* XRender, DRI3 and XPresent-based renderer. A RenderTarget is just
a Picture. Here is a rough explanation of how the buffer release
machinery works.
@@ -489,7 +503,7 @@ FindBufferActivityRecord (PictureBuffer *buffer, PictureTarget *target)
static void
RecordBufferActivity (PictureBuffer *buffer, PictureTarget *target,
- uint64_t roundtrip_id)
+ uint64_t roundtrip_id, Fence *fence)
{
BufferActivityRecord *record;
@@ -530,7 +544,23 @@ RecordBufferActivity (PictureBuffer *buffer, PictureTarget *target,
record->target = target;
}
+ /* If there is already a fence, wait for it to complete. */
+ if (record->fence)
+ {
+ /* Flush any trigger fence request that might have been
+ made. */
+ XFlush (compositor.display);
+
+ /* Start waiting. */
+ FenceAwait (record->fence);
+ }
+
+ if (fence)
+ /* Retain the fence. */
+ FenceRetain (fence);
+
record->id = roundtrip_id;
+ record->fence = fence;
}
static void
@@ -599,6 +629,13 @@ MaybeRunIdleCallbacks (PictureBuffer *buffer, PictureTarget *target)
static void
UnlinkActivityRecord (BufferActivityRecord *record)
{
+ /* Wait for the sync fence. */
+
+ if (record->fence)
+ FenceAwait (record->fence);
+
+ record->fence = NULL;
+
record->buffer_last->buffer_next = record->buffer_next;
record->buffer_next->buffer_last = record->buffer_last;
record->target_last->target_next = record->target_next;
@@ -943,6 +980,11 @@ DestroyRenderTarget (RenderTarget target)
activity_last = activity_record;
activity_record = activity_record->target_next;
+ if (activity_last->fence)
+ /* The TriggerFence request might not have been flushed. So
+ flush it now to avoid hangs. */
+ XFlush (compositor.display);
+
UnlinkActivityRecord (activity_last);
XLFree (activity_last);
}
@@ -1099,6 +1141,15 @@ GetSourceY (DrawParams *params)
return 0.0;
}
+static BufferTransform
+GetBufferTransform (DrawParams *params)
+{
+ if (params->flags & TransformSet)
+ return params->transform;
+
+ return Normal;
+}
+
static Bool
CompareStretch (DrawParams *params, DrawParams *other)
{
@@ -1115,6 +1166,82 @@ CompareStretch (DrawParams *params, DrawParams *other)
return True;
}
+static void
+ApplyInverseTransform (PictureBuffer *buffer, Matrix *matrix,
+ BufferTransform transform)
+{
+ float width, height;
+
+ /* Note that the transform is applied in reverse, meaning that a
+ counterclockwise rotation is done clockwise, etc, as TRANSFORM
+ transforms destination coordinates to source ones. */
+
+ width = buffer->width;
+ height = buffer->height;
+
+ switch (transform)
+ {
+ case Normal:
+ break;
+
+ case CounterClockwise90:
+ /* Apply clockwise 270 degree rotation around the origin. */
+ MatrixRotate (matrix, M_PI * 1.5, 0, 0);
+
+ /* Translate y by the width. */
+ MatrixTranslate (matrix, 0, -width);
+ break;
+
+ case CounterClockwise180:
+ /* Apply clockwise 180 degree rotation around the center. */
+ MatrixRotate (matrix, M_PI, width / 2.0f, height / 2.0f);
+ break;
+
+ case CounterClockwise270:
+ /* Apply clockwise 90 degree rotation around the origin. */
+ MatrixRotate (matrix, M_PI * 0.5, 0, 0);
+
+ /* Translate by the height. */
+ MatrixTranslate (matrix, -height, 0);
+ break;
+
+ case Flipped:
+ /* Apply horizontal flip. */
+ MatrixMirrorHorizontal (matrix, width);
+ break;
+
+ case Flipped90:
+ /* Apply horizontal flip. */
+ MatrixMirrorHorizontal (matrix, width);
+
+ /* Apply clockwise 90 degree rotation around the origin. */
+ MatrixRotate (matrix, M_PI * 0.5, 0, 0);
+
+ /* Translate by the height. */
+ MatrixTranslate (matrix, -height, 0);
+ break;
+
+ case Flipped180:
+ /* Apply horizontal flip. */
+ MatrixMirrorHorizontal (matrix, width);
+
+ /* Apply clockwise 180 degree rotation around the center. */
+ MatrixRotate (matrix, M_PI, width / 2.0f, height / 2.0f);
+ break;
+
+ case Flipped270:
+ /* Apply horizontal flip. */
+ MatrixMirrorHorizontal (matrix, width);
+
+ /* Apply clockwise 270 degree rotation around the origin. */
+ MatrixRotate (matrix, M_PI * 1.5, 0, 0);
+
+ /* Translate y by the width. */
+ MatrixTranslate (matrix, 0, -width);
+ break;
+ }
+}
+
static void
MaybeApplyTransform (PictureBuffer *buffer, DrawParams *params)
{
@@ -1124,6 +1251,8 @@ MaybeApplyTransform (PictureBuffer *buffer, DrawParams *params)
if (GetScale (params) == GetScale (&buffer->params)
&& GetSourceX (params) == GetSourceX (&buffer->params)
&& GetSourceY (params) == GetSourceY (&buffer->params)
+ && (GetBufferTransform (params)
+ == GetBufferTransform (&buffer->params))
&& CompareStretch (params, &buffer->params))
/* Nothing changed. */
return;
@@ -1138,6 +1267,10 @@ MaybeApplyTransform (PictureBuffer *buffer, DrawParams *params)
{
MatrixIdentity (&ftransform);
+ if (params->flags & TransformSet)
+ ApplyInverseTransform (buffer, &ftransform,
+ params->transform);
+
/* Note that these must be applied in the right order. First,
the scale is applied. Then, the offset, and finally the
stretch. */
@@ -1173,6 +1306,7 @@ Composite (RenderBuffer buffer, RenderTarget target,
{
PictureBuffer *picture_buffer;
PictureTarget *picture_target;
+ XLList *tem;
picture_buffer = buffer.pointer;
picture_target = target.pointer;
@@ -1191,8 +1325,19 @@ Composite (RenderBuffer buffer, RenderTarget target,
/* dst-x, dst-y, width, height. */
x, y, width, height);
+ for (tem = picture_target->buffers_used; tem; tem = tem->next)
+ {
+ /* Return if the buffer is already in the buffers_used list.
+ Otherwise, FinishRender will try to wait for a fence that has
+ not yet been triggered. */
+
+ if (tem->data == picture_buffer)
+ return;
+ }
+
/* Record pending buffer activity; the roundtrip message is then
sent later. */
+
picture_target->buffers_used
= XLListPrepend (picture_target->buffers_used, picture_buffer);
}
@@ -1203,22 +1348,41 @@ FinishRender (RenderTarget target, pixman_region32_t *damage)
XLList *tem, *last;
PictureTarget *pict_target;
uint64_t roundtrip_id;
+ Fence *fence;
+
+ pict_target = target.pointer;
+
+ if (!pict_target->buffers_used)
+ /* No buffers were used. */
+ return;
/* Finish rendering. This function then sends a single roundtrip
message and records buffer activity for each buffer involved in
the update based on that. */
roundtrip_id = SendRoundtripMessage ();
- pict_target = target.pointer;
tem = pict_target->buffers_used;
+
+ if (use_sync_fences)
+ {
+ /* Get a sync fence, then trigger it. */
+ fence = GetFence ();
+
+ /* Trigger the fence. */
+ XSyncTriggerFence (compositor.display, FenceToXFence (fence));
+ }
+ else
+ fence = NULL;
+
while (tem)
{
last = tem;
tem = tem->next;
- /* Record buffer activity on this one buffer. */
+ /* Record buffer activity on this one buffer. A reference to
+ the fence will be held. */
RecordBufferActivity (last->data, pict_target,
- roundtrip_id);
+ roundtrip_id, fence);
/* Free the list element. */
XLFree (last);
@@ -1933,6 +2097,8 @@ BufferFromDmaBuf (DmaBufAttributes *attributes, Bool *error)
buffer->picture = picture;
buffer->pixmap = pixmap;
buffer->depth = depth;
+ buffer->width = attributes->width;
+ buffer->height = attributes->height;
/* Initialize the list of release records. */
buffer->pending.buffer_next = &buffer->pending;
@@ -2008,6 +2174,8 @@ FinishDmaBufRecord (DmaBufRecord *pending, Bool success)
buffer->picture = picture;
buffer->pixmap = pending->pixmap;
buffer->depth = pending->depth;
+ buffer->width = pending->width;
+ buffer->height = pending->height;
/* Initialize the list of release records. */
buffer->pending.buffer_next = &buffer->pending;
@@ -2110,6 +2278,9 @@ BufferFromDmaBufAsync (DmaBufAttributes *attributes,
= PictFormatForDmabufFormat (attributes->drm_format);
record->pixmap = pixmap;
record->depth = depth;
+ record->width = attributes->width;
+ record->height = attributes->height;
+
XLAssert (record->format != NULL);
record->next = pending_success.next;
@@ -2218,6 +2389,8 @@ BufferFromShm (SharedMemoryAttributes *attributes, Bool *error)
buffer->picture = picture;
buffer->pixmap = pixmap;
buffer->depth = depth;
+ buffer->width = attributes->width;
+ buffer->height = attributes->height;
/* Initialize the list of release records. */
buffer->pending.buffer_next = &buffer->pending;
@@ -2380,6 +2553,11 @@ FreeAnyBuffer (RenderBuffer buffer)
activity_last = activity_record;
activity_record = activity_record->buffer_next;
+ if (activity_last->fence)
+ /* The TriggerFence request might not have been flushed. So
+ flush it now to avoid hangs. */
+ XFlush (compositor.display);
+
UnlinkActivityRecord (activity_last);
XLFree (activity_last);
}
diff --git a/subcompositor.c b/subcompositor.c
index 7737b16..0d7e151 100644
--- a/subcompositor.c
+++ b/subcompositor.c
@@ -276,6 +276,10 @@ struct _View
/* Function called upon the view potentially being resized. */
void (*maybe_resized) (View *);
+ /* Some data associated with this view. Can be a surface or
+ something else. */
+ void *data;
+
/* The damaged and opaque regions. */
pixman_region32_t damage, opaque;
@@ -289,16 +293,15 @@ struct _View
(or topmost parent if the view hierarchy is detached). */
int abs_x, abs_y;
- /* Some data associated with this view. Can be a surface or
- something else. */
- void *data;
-
/* The scale of this view. */
int scale;
/* Flags; whether or not this view is unmapped, etc. */
int flags;
+ /* Any transform associated with this view. */
+ BufferTransform transform;
+
/* The viewport data. */
double src_x, src_y, crop_width, crop_height, dest_width, dest_height;
@@ -495,6 +498,8 @@ MakeView (void)
pixman_region32_init (&view->damage);
pixman_region32_init (&view->opaque);
pixman_region32_init (&view->input);
+
+ view->transform = Normal;
#endif
return view;
@@ -1627,6 +1632,60 @@ GetContentScale (int scale)
return -scale + 1;
}
+static BufferTransform
+InvertTransform (BufferTransform transform)
+{
+ switch (transform)
+ {
+ case CounterClockwise270:
+ return CounterClockwise90;
+
+ case CounterClockwise90:
+ return CounterClockwise270;
+
+ default:
+ return transform;
+ }
+}
+
+static int
+BufferWidthAfterTransform (View *view)
+{
+ if (RotatesDimensions (view->transform))
+ return XLBufferHeight (view->buffer);
+
+ return XLBufferWidth (view->buffer);
+}
+
+static int
+BufferHeightAfterTransform (View *view)
+{
+ if (RotatesDimensions (view->transform))
+ return XLBufferWidth (view->buffer);
+
+ return XLBufferHeight (view->buffer);
+}
+
+static void
+TransformBufferDamage (pixman_region32_t *damage,
+ pixman_region32_t *source,
+ View *view)
+{
+ int width, height;
+ BufferTransform inverse;
+
+ /* Invert the transform. */
+ inverse = InvertTransform (view->transform);
+
+ /* Calculate the width and height of the buffer after the
+ transform. */
+ width = XLBufferWidth (view->buffer);
+ height = XLBufferHeight (view->buffer);
+
+ /* Transform the damage. */
+ XLTransformRegion (damage, source, inverse, width, height);
+}
+
void
ViewDamageBuffer (View *view, pixman_region32_t *damage)
{
@@ -1638,9 +1697,10 @@ ViewDamageBuffer (View *view, pixman_region32_t *damage)
if (!view->buffer)
return;
- if (!view->scale && !IsViewported (view))
- /* There is no scale nor viewport. Just damage the view
- directly. */
+ if (view->transform == Normal
+ && !view->scale && !IsViewported (view))
+ /* There is no scale, transform, nor viewport. Just damage the
+ view directly. */
ViewDamage (view, damage);
else
{
@@ -1651,8 +1711,17 @@ ViewDamageBuffer (View *view, pixman_region32_t *damage)
x_factor = GetContentScale (view->scale);
y_factor = GetContentScale (view->scale);
- /* Scale the region. */
- XLScaleRegion (&temp, damage, x_factor, y_factor);
+ if (view->transform != Normal)
+ {
+ /* Transform the given buffer damage if need be. */
+ TransformBufferDamage (&temp, damage, view);
+
+ /* Scale the region. */
+ XLScaleRegion (&temp, &temp, x_factor, y_factor);
+ }
+ else
+ /* Scale the region. */
+ XLScaleRegion (&temp, damage, x_factor, y_factor);
/* Next, apply the viewport. */
if (IsViewported (view))
@@ -1671,9 +1740,9 @@ ViewDamageBuffer (View *view, pixman_region32_t *damage)
current buffer width/height. */
if (crop_width == -1)
{
- crop_width = (XLBufferWidth (view->buffer)
+ crop_width = (BufferWidthAfterTransform (view)
* GetContentScale (view->scale));
- crop_height = (XLBufferHeight (view->buffer)
+ crop_height = (BufferHeightAfterTransform (view)
* GetContentScale (view->scale));
}
@@ -1738,7 +1807,7 @@ ViewWidth (View *view)
scaling. */
return ceil (view->dest_width);
- width = XLBufferWidth (view->buffer);
+ width = BufferWidthAfterTransform (view);
if (view->scale < 0)
return ceil (width * (abs (view->scale) + 1));
@@ -1761,7 +1830,7 @@ ViewHeight (View *view)
scaling. */
return ceil (view->dest_height);
- height = XLBufferHeight (view->buffer);
+ height = BufferHeightAfterTransform (view);
if (view->scale < 0)
return ceil (height * (abs (view->scale) + 1));
@@ -1781,6 +1850,23 @@ ViewSetScale (View *view, int scale)
ViewAfterSizeUpdate (view);
}
+void
+ViewSetTransform (View *view, BufferTransform transform)
+{
+ BufferTransform old_transform;
+
+ if (view->transform == transform)
+ return;
+
+ old_transform = view->transform;
+ view->transform = transform;
+
+ if (RotatesDimensions (transform)
+ != RotatesDimensions (old_transform))
+ /* Subcompositor bounds may have changed. */
+ ViewAfterSizeUpdate (view);
+}
+
void
ViewSetViewport (View *view, double src_x, double src_y,
double crop_width, double crop_height,
@@ -1829,6 +1915,12 @@ ViewComputeTransform (View *view, DrawParams *params, Bool draw)
params->off_x = 0.0;
params->off_y = 0.0;
+ if (view->transform != Normal)
+ {
+ params->flags |= TransformSet;
+ params->transform = view->transform;
+ }
+
if (view->scale)
{
/* There is a scale, so set it. */
@@ -1856,9 +1948,9 @@ ViewComputeTransform (View *view, DrawParams *params, Bool draw)
if (params->crop_width == -1)
{
- params->crop_width = (XLBufferWidth (view->buffer)
+ params->crop_width = (BufferWidthAfterTransform (view)
* GetContentScale (view->scale));
- params->crop_height = (XLBufferHeight (view->buffer)
+ params->crop_height = (BufferHeightAfterTransform (view)
* GetContentScale (view->scale));
}
}
diff --git a/surface.c b/surface.c
index 9379ed6..766d9d6 100644
--- a/surface.c
+++ b/surface.c
@@ -506,6 +506,13 @@ GetEffectiveScale (int scale)
return scale;
}
+static void
+ApplyBufferTransform (Surface *surface)
+{
+ ViewSetTransform (surface->view,
+ surface->current_state.transform);
+}
+
static void
ApplyScale (Surface *surface)
{
@@ -665,8 +672,12 @@ ApplyViewport (Surface *surface)
if (state->buffer)
{
- max_width = XLBufferWidth (state->buffer);
- max_height = XLBufferHeight (state->buffer);
+ max_width = (RotatesDimensions (state->transform)
+ ? XLBufferHeight (state->buffer)
+ : XLBufferWidth (state->buffer));
+ max_height = (RotatesDimensions (state->transform)
+ ? XLBufferWidth (state->buffer)
+ : XLBufferHeight (state->buffer));
}
else
{
@@ -787,8 +798,16 @@ CheckViewportValues (Surface *surface)
/* A buffer is attached and a viewport source rectangle is set;
check that it remains in bounds. */
- width = XLBufferWidth (state->buffer);
- height = XLBufferHeight (state->buffer);
+ if (RotatesDimensions (state->transform))
+ {
+ width = XLBufferHeight (state->buffer);
+ height = XLBufferWidth (state->buffer);
+ }
+ else
+ {
+ width = XLBufferWidth (state->buffer);
+ height = XLBufferHeight (state->buffer);
+ }
if (state->src_x + state->src_width - 1 >= width / state->buffer_scale
|| state->src_y + state->src_height - 1 >= height / state->buffer_scale)
@@ -897,6 +916,9 @@ SavePendingState (Surface *surface)
surface->cached_state.buffer_scale
= surface->pending_state.buffer_scale;
+ if (surface->pending_state.pending & PendingBufferTransform)
+ surface->cached_state.transform = surface->pending_state.transform;
+
if (surface->pending_state.pending & PendingViewportDest)
{
surface->cached_state.dest_width
@@ -1029,6 +1051,12 @@ InternalCommit (Surface *surface, State *pending)
ApplyScale (surface);
}
+ if (pending->pending & PendingBufferTransform)
+ {
+ surface->current_state.transform = pending->transform;
+ ApplyBufferTransform (surface);
+ }
+
if (pending->pending & PendingInputRegion)
{
pixman_region32_copy (&surface->current_state.input,
@@ -1173,13 +1201,61 @@ Commit (struct wl_client *client, struct wl_resource *resource)
InternalCommit (surface, &surface->pending_state);
}
+static Bool
+GetBufferTransform (int32_t wayland_transform,
+ BufferTransform *transform)
+{
+ switch (wayland_transform)
+ {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ *transform = Normal;
+ return True;
+
+ case WL_OUTPUT_TRANSFORM_90:
+ *transform = CounterClockwise90;
+ return True;
+
+ case WL_OUTPUT_TRANSFORM_180:
+ *transform = CounterClockwise180;
+ return True;
+
+ case WL_OUTPUT_TRANSFORM_270:
+ *transform = CounterClockwise270;
+ return True;
+
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ *transform = Flipped;
+ return True;
+
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ *transform = Flipped90;
+ return True;
+
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ *transform = Flipped180;
+ return True;
+
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ *transform = Flipped270;
+ return True;
+ }
+
+ return False;
+}
+
static void
SetBufferTransform (struct wl_client *client, struct wl_resource *resource,
int32_t transform)
{
- if (transform != WL_OUTPUT_TRANSFORM_NORMAL)
- wl_resource_post_error (resource, WL_DISPLAY_ERROR_IMPLEMENTATION,
- "this compositor does not support buffer transforms");
+ Surface *surface;
+
+ surface = wl_resource_get_user_data (resource);
+
+ if (!GetBufferTransform (transform, &surface->pending_state.transform))
+ wl_resource_post_error (resource, WL_SURFACE_ERROR_INVALID_TRANSFORM,
+ "invalid transform specified");
+ else
+ surface->pending_state.pending |= PendingBufferTransform;
}
static void
@@ -1249,6 +1325,7 @@ InitState (State *state)
state->pending = PendingNone;
state->buffer = NULL;
state->buffer_scale = 1;
+ state->transform = Normal;
/* Initialize the sentinel node. */
state->frame_callbacks.next = &state->frame_callbacks;
@@ -1508,18 +1585,6 @@ XLSurfaceReleaseRole (Surface *surface, Role *role)
RunUnmapCallbacks (surface);
}
-void
-XLStateAttachBuffer (State *state, ExtBuffer *buffer)
-{
- AttachBuffer (state, buffer);
-}
-
-void
-XLStateDetachBuffer (State *state)
-{
- ClearBuffer (state);
-}
-
/* Various other functions exported for roles. */
diff --git a/time.c b/time.c
index 0703c6d..c648d2c 100644
--- a/time.c
+++ b/time.c
@@ -303,6 +303,12 @@ InitTime (void)
supported = XSyncInitialize (compositor.display,
&xsync_major, &xsync_minor);
+ if (xsync_major < 3 || (xsync_major == 3 && xsync_minor < 1))
+ {
+ fprintf (stderr, "Sync fences are not supported by this X server\n");
+ exit (1);
+ }
+
if (!supported)
{
fprintf (stderr, "A compatible version of the synchronization"
diff --git a/transform.c b/transform.c
index 621d7ab..a11493e 100644
--- a/transform.c
+++ b/transform.c
@@ -132,6 +132,58 @@ MatrixScale (Matrix *transform, float sx, float sy)
MatrixMultiply (copy, temp, transform);
}
+void
+MatrixRotate (Matrix *transform, float theta, float x, float y)
+{
+ Matrix temp, copy;
+
+ /* Translate the matrix to x, y, and then perform rotation by the
+ given angle in radians and translate back. As the transform is
+ being performed in the X coordinate system, the given angle
+ describes a clockwise rotation. */
+
+ MatrixIdentity (&temp);
+ memcpy (copy, transform, sizeof copy);
+
+ Index (temp, 0, 2) = x;
+ Index (temp, 1, 2) = y;
+
+ MatrixMultiply (copy, temp, transform);
+ MatrixIdentity (&temp);
+ memcpy (copy, transform, sizeof copy);
+
+ Index (temp, 0, 0) = cosf (theta);
+ Index (temp, 0, 1) = sinf (theta);
+ Index (temp, 1, 0) = -sinf (theta);
+ Index (temp, 1, 1) = cosf (theta);
+
+ MatrixMultiply (copy, temp, transform);
+ MatrixIdentity (&temp);
+ memcpy (copy, transform, sizeof copy);
+
+ Index (temp, 0, 2) = -x;
+ Index (temp, 1, 2) = -y;
+
+ MatrixMultiply (copy, temp, transform);
+}
+
+void
+MatrixMirrorHorizontal (Matrix *transform, float width)
+{
+ Matrix temp, copy;
+
+ /* Scale the matrix by -1, and then apply a tx of width, in effect
+ flipping the image horizontally. */
+
+ MatrixIdentity (&temp);
+ memcpy (copy, transform, sizeof copy);
+
+ Index (temp, 0, 0) = -1.0f;
+ Index (temp, 0, 2) = width;
+
+ MatrixMultiply (copy, temp, transform);
+}
+
void
MatrixExport (Matrix *transform, XTransform *xtransform)
{