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) {