Add support for DRM leasing and significantly rework composition code

* 12to11.c (HandleCmdline): Improve messages printed during
-help.
(XLMain): Initialize DRM leasing.
* 12to11.man: Document changes.
* Imakefile (LOCAL_LIBRARIES): Add xcb-randr, xf86drm and
XPresent.
(SRCS): Add drm_lease.c.
(OBJS): Add drm_lease.o.
* atoms.c (names): Add CONNECTOR_ID.
(CONNECTOR_ID): New atom.
(XLInitAtoms): Intern CONNECTOR_ID.
* compositor.h (struct _RenderFuncs): Require event mask to be
passed to target_from_window.  New functions
`set_standard_event_mask', `present_to_window',
`cancel_presentation_callback', and `cancel_presentation'.
(struct _BufferFuncs): Move buffer release machinery here.
(enum _FrameMode): New enum.
* egl.c (TargetFromWindow, SetStandardEventMask,
egl_render_funcs): Adjust functions accordingly.
* frame_clock.c (struct _FrameClock): New fields
`end_frame_called' and `pending_sync_value'.
(HandleEndFrame, PostEndFrame, StartFrame, EndFrame): Clean up
frame synchronization code.  Do not end upon predicted
presentation time if EndFrame was not called in time.
(FreezeForValue): New function.
(XLFrameClockHandleFrameEvent): Defer actually freezing until
StartFrame happens.
(XLFrameClockGetFrameTime): New function.
* icon_surface.c (ReleaseBuffer): Use RenderWaitForIdle.
(RunFrameCallbacks, AfterFrame): Pass frame clock to callbacks.
Use frame time if available.
(XLGetIconSurface): Require wait_for_idle to work.
* output.c (change_hook): New hook.
(XLHandleOneXEventForOutputs): Run hook if set.
(XLOutputSetChangeFunction): New function.
(XLInitRROutputs): Select for RRResourceChangeNotify if
providers are supported.
* picture_renderer.c (struct _PresentRecord)
(struct _BufferActivityRecord, struct _IdleCallback)
(struct _PictureBuffer, struct _PictureTarget)
(struct _DrmFormatInfo, struct _DmaBufRecord)
(struct _PresentCompletionCallback): New record structures.
(all_formats, SendRoundtripMessage, FindBufferActivityRecord)
(RecordBufferActivity, RunIdleCallbacks, MaybeRunIdleCallbacks)
(UnlinkActivityRecord, HandleActivityEvent, InitRenderFuncs)
(TargetFromDrawable, TargetFromPixmap, TargetFromWindow)
(SetStandardEventMask, NoteTargetSize, PictureFromTarget)
(DestroyRenderTarget, FillBoxesWithTransparency)
(ServerRegionFromRegion, ClearRectangle, Composite)
(FindPresentRecord, AllocateRecord, PresentToWindow)
(CancelPresentationCallback, CancelPresentation)
(picture_render_funcs, FindSupportedModifiers, InitDrmFormats)
(PictFormatIsPresentable, BufferFromDmaBuf, FinishDmaBufRecord)
(BufferFromDmaBufAsync, BufferFromShm, BufferFromSinglePixel)
(FreeShmBuffer, FreeDmabufBuffer, FreeSinglePixelBuffer)
(AddIdleCallback, CancelIdleCallback, IsBufferIdle)
(IdleEventPredicate, WaitForIdle, SetNeedWaitForIdle)
(picture_buffer_funcs, HandlePresentCompleteNotify)
(HandlePresentIdleNotify, HandlePresentationEvent)
(HandleOneXEventForPictureRenderer, InitPictureRenderer): Allow
presenting pixmaps directly, and move buffer release tracking
machinery here.
* renderer.c (RenderTargetFromWindow): Update signature.
(RenderPresentToWindow, RenderCancelPresentationCallback)
(RenderCancelPresentation, RenderAddIdleCallback)
(RegisterStaticRenderer): New wrapper functions.
* run.c (HandleOneXEvent): Handle picture renderer events
earlier.
* seat.c (MaybeCreateCursor): Require wait_for_idle.
(ReleaseBuffer): Wait for buffer to become idle on each target
in the cursor ring.
* subcompositor.c (struct _Subcompositor): New callback
`note_frame'.
(SubcompositorSetNoteFrameCallback, NoViewsAfter)
(PresentCompletedCallback): New functions.
(SubcompositorUpdate): Try to present the buffer if possible,
and run completion callbacks.
(SubcompositorFree): Free presentation key.
* surface.c (XLSurfaceRunFrameCallbacksMs): New function.
* text_input.c: Improve commentary.
* xdg-shell.xml: Update from wayland-protocols.
* xdg_surface.c (struct _XdgRole): New fields `pending_frame',
`last_specified_serial'.
(struct _ReleaseLaterRecord): Replace free function with idle
callback key and xdg role pointers.
(RemoveRecord): Delete function.
(FreeRecords): Stop cancelling buffer destroy listener.
(ReleaseLaterExtBufferFunc): Delete function.
(RunFrameCallbacks): Use frame clock time if it is set.
(HandleReleaseLaterMessage): Delete function.
(BufferIdleCallback): New function.
(ReleaseLater): Delete function.
(XLHandleXEventForXdgSurfaces): Stop handling buffer release
events here.
(AckConfigure): Improve debug code and reject duplicate serials.
(Commit): Unfreeze earlier; also, in general...
(NoteFrame): Move frame handling implementation here.
(ReleaseBuffer, Subframe, EndSubframe, AfterFrame, ResizeForMap)
(SelectExtraEvents): Set standard event mask.
(XLGetXdgSurface, XLXdgRoleSendConfigure): ...Replace frame
clock logic with that in NoteFrame.
This commit is contained in:
oldosfan 2022-10-12 12:53:09 +00:00
parent de26ffa123
commit 713eb811ea
18 changed files with 2046 additions and 311 deletions

View file

@ -98,7 +98,7 @@ HandleCmdline (Display *dpy, int argc, char **argv)
{ {
print_usage: print_usage:
fprintf (stderr, fprintf (stderr,
"usage: %s [-name name] [-class class] [-xrm resourcestring]...\n", "usage: %s [-name name] [-class class] [-xrm resourcestring...]\n",
argv[0]); argv[0]);
exit (!strcmp (argv[i], "-help") ? 0 : 1); exit (!strcmp (argv[i], "-help") ? 0 : 1);
} }
@ -224,6 +224,7 @@ XLMain (int argc, char **argv)
XLInitDecoration (); XLInitDecoration ();
XLInitTextInput (); XLInitTextInput ();
XLInitSinglePixelBuffer (); XLInitSinglePixelBuffer ();
XLInitDrmLease ();
/* This has to come after the rest of the initialization. */ /* This has to come after the rest of the initialization. */
DetermineServerTime (); DetermineServerTime ();

View file

@ -3,7 +3,7 @@
12to11 - Wayland to X protocol translator 12to11 - Wayland to X protocol translator
.SH SYNOPSIS .SH SYNOPSIS
.B 12to11 .B 12to11
[\-\fBclass\fP \fIclass\fP] [\-\fBname\fP \fIname\fP] [\-\fBxrm\fP \fIresourcestring\fP]... [\-\fBclass\fP \fIclass\fP] [\-\fBname\fP \fIname\fP] [\-\fBxrm\fP \fIresourcestring\fP...]
.SH DESCRIPTION .SH DESCRIPTION
.I 12to11 .I 12to11
starts a Wayland compositor on the next available socket; starts a Wayland compositor on the next available socket;
@ -212,6 +212,16 @@ Protocol Version
zwp_linux_explicit_synchronization_v1 2 zwp_linux_explicit_synchronization_v1 2
.TE .TE
.PP .PP
When the X server supports version 1.6 or later of the X Resize,
Rotate and Reflect Extension, the following Wayland protocol is also
supported:
.TS H
lb lb
lb n .
Protocol Version
wp_drm_lease_device_v1 1
.TE
.PP
However, Wayland clients are allowed to continue to access data from However, Wayland clients are allowed to continue to access data from
the \fBCLIPBOARD\fP and \fBPRIMARY\fP selections even when they do not the \fBCLIPBOARD\fP and \fBPRIMARY\fP selections even when they do not
have the keyboard focus, against the restrictions put out in the have the keyboard focus, against the restrictions put out in the
@ -242,5 +252,5 @@ vast majority of clients seem not to make use of this feature, and
implementing it would be a lot of trouble. implementing it would be a lot of trouble.
.SH "SEE ALSO" .SH "SEE ALSO"
X(7), Xorg(1) X(7), Xorg(1)
.SH AUTHOR .SH AUTHORS
Various contributors. Various contributors.

View file

@ -1,4 +1,4 @@
#include "libraries.def" #include "12to11.conf"
#ifndef HasPosixThreads #ifndef HasPosixThreads
#error "Posix threads are required" #error "Posix threads are required"
@ -10,7 +10,8 @@ DEPLIBS = $(DEPXLIB) $(DEPEXTENSIONLIB) $(DEPXRANDRLIB) $(DEPXRENDERLIB) \
LOCAL_LIBRARIES = $(XLIB) $(EXTENSIONLIB) $(XCBLIB) $(XCB) $(XCB_SHM) \ LOCAL_LIBRARIES = $(XLIB) $(EXTENSIONLIB) $(XCBLIB) $(XCB) $(XCB_SHM) \
$(XRANDRLIB) $(PIXMAN) $(XRENDERLIB) $(XILIB) $(XKBFILELIB) $(XFIXESLIB) \ $(XRANDRLIB) $(PIXMAN) $(XRENDERLIB) $(XILIB) $(XKBFILELIB) $(XFIXESLIB) \
$(XCB_DRI3) $(XCB_SHAPE) $(WAYLAND_SERVER) $(XCB_DRI3) $(XCB_SHAPE) $(WAYLAND_SERVER) $(XCB_RANDR) $(DRM) \
$(XPRESENTLIB)
INCLUDES := $(DRMINCLUDES) $(PIXMANINCLUDES) INCLUDES := $(DRMINCLUDES) $(PIXMANINCLUDES)
@ -22,7 +23,7 @@ SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c \
icon_surface.c primary_selection.c renderer.c \ icon_surface.c primary_selection.c renderer.c \
picture_renderer.c explicit_synchronization.c transform.c \ picture_renderer.c explicit_synchronization.c transform.c \
wp_viewporter.c decoration.c text_input.c \ wp_viewporter.c decoration.c text_input.c \
single_pixel_buffer.c single_pixel_buffer.c drm_lease.c
OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \ 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 \ surface.o region.o shm.o atoms.o subcompositor.o positioner.o \
@ -32,7 +33,7 @@ OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
icon_surface.o primary_selection.o renderer.o \ icon_surface.o primary_selection.o renderer.o \
picture_renderer.o explicit_synchronization.o transform.o \ picture_renderer.o explicit_synchronization.o transform.o \
wp_viewporter.o decoration.o text_input.o \ wp_viewporter.o decoration.o text_input.o \
single_pixel_buffer.o single_pixel_buffer.o drm_lease.o
GENHEADERS = transfer_atoms.h GENHEADERS = transfer_atoms.h
@ -115,6 +116,7 @@ ScannerTarget(viewporter)
ScannerTarget(xdg-decoration-unstable-v1) ScannerTarget(xdg-decoration-unstable-v1)
ScannerTarget(text-input-unstable-v3) ScannerTarget(text-input-unstable-v3)
ScannerTarget(single-pixel-buffer-v1) ScannerTarget(single-pixel-buffer-v1)
ScannerTarget(drm-lease-v1)
/* Make OBJS depend on scanner headers, and depend on both them and SRCS. */ /* Make OBJS depend on scanner headers, and depend on both them and SRCS. */
$(OBJS): $(GENHEADERS) $(OBJS): $(GENHEADERS)

View file

@ -108,6 +108,7 @@ static const char *names[] =
"_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE",
"_NET_WM_WINDOW_TYPE_MENU", "_NET_WM_WINDOW_TYPE_MENU",
"_NET_WM_WINDOW_TYPE_DND", "_NET_WM_WINDOW_TYPE_DND",
"CONNECTOR_ID",
/* These are automatically generated from mime.txt. */ /* These are automatically generated from mime.txt. */
DirectTransferAtomNames DirectTransferAtomNames
@ -129,7 +130,8 @@ Atom _NET_WM_OPAQUE_REGION, _XL_BUFFER_RELEASE, _NET_WM_SYNC_REQUEST_COUNTER,
XdndActionPrivate, XdndActionList, XdndActionDescription, XdndProxy, XdndActionPrivate, XdndActionList, XdndActionDescription, XdndProxy,
XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished,
_NET_WM_FRAME_TIMINGS, _NET_WM_BYPASS_COMPOSITOR, WM_STATE, _NET_WM_FRAME_TIMINGS, _NET_WM_BYPASS_COMPOSITOR, WM_STATE,
_NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_MENU, _NET_WM_WINDOW_TYPE_DND; _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_MENU, _NET_WM_WINDOW_TYPE_DND,
CONNECTOR_ID;
XrmQuark resource_quark, app_quark, QString; XrmQuark resource_quark, app_quark, QString;
@ -283,9 +285,10 @@ XLInitAtoms (void)
_NET_WM_WINDOW_TYPE = atoms[58]; _NET_WM_WINDOW_TYPE = atoms[58];
_NET_WM_WINDOW_TYPE_MENU = atoms[59]; _NET_WM_WINDOW_TYPE_MENU = atoms[59];
_NET_WM_WINDOW_TYPE_DND = atoms[60]; _NET_WM_WINDOW_TYPE_DND = atoms[60];
CONNECTOR_ID = atoms[61];
/* This is automatically generated. */ /* This is automatically generated. */
DirectTransferAtomInit (atoms, 61); DirectTransferAtomInit (atoms, 62);
/* Now, initialize quarks. */ /* Now, initialize quarks. */
resource_quark = XrmPermStringToQuark (compositor.resource_name); resource_quark = XrmPermStringToQuark (compositor.resource_name);

View file

@ -121,9 +121,15 @@ typedef struct _ShmFormat ShmFormat;
typedef enum _Operation Operation; typedef enum _Operation Operation;
typedef void *IdleCallbackKey;
typedef void *PresentCompletionKey;
typedef void (*DmaBufSuccessFunc) (RenderBuffer, void *); typedef void (*DmaBufSuccessFunc) (RenderBuffer, void *);
typedef void (*DmaBufFailureFunc) (void *); typedef void (*DmaBufFailureFunc) (void *);
typedef void (*BufferIdleFunc) (RenderBuffer, void *);
typedef void (*PresentCompletionFunc) (void *);
enum _Operation enum _Operation
{ {
OperationOver, OperationOver,
@ -250,11 +256,14 @@ struct _RenderFuncs
Bool (*init_render_funcs) (void); Bool (*init_render_funcs) (void);
/* Create a rendering target for the given window. */ /* Create a rendering target for the given window. */
RenderTarget (*target_from_window) (Window); RenderTarget (*target_from_window) (Window, unsigned long);
/* Create a rendering target for the given pixmap. */ /* Create a rendering target for the given pixmap. */
RenderTarget (*target_from_pixmap) (Pixmap); RenderTarget (*target_from_pixmap) (Pixmap);
/* Set the standard event mask of the target. */
void (*set_standard_event_mask) (RenderTarget, unsigned long);
/* Set the target width and height. This can be NULL. */ /* Set the target width and height. This can be NULL. */
void (*note_target_size) (RenderTarget, int, int); void (*note_target_size) (RenderTarget, int, int);
@ -327,6 +336,22 @@ struct _RenderFuncs
called. */ called. */
int (*get_finish_fence) (Bool *); int (*get_finish_fence) (Bool *);
/* Directly present the given buffer to the window, without copying,
and possibly flip the buffer contents to the screen, all while
possibly being synchronized with the vertical refresh. Call the
supplied callback when the presentation completes. May be
NULL. */
PresentCompletionKey (*present_to_window) (RenderTarget, RenderBuffer,
pixman_region32_t *,
PresentCompletionFunc, void *);
/* Cancel the given presentation callback. */
void (*cancel_presentation_callback) (PresentCompletionKey);
/* Cancel any presentation that might have happened to the window
backing the given target. */
void (*cancel_presentation) (RenderTarget);
/* Some flags. NeverAges means targets always preserve contents /* Some flags. NeverAges means targets always preserve contents
that were previously drawn. */ that were previously drawn. */
int flags; int flags;
@ -404,6 +429,28 @@ struct _BufferFuncs
by being copied to an offscreen buffer. */ by being copied to an offscreen buffer. */
Bool (*can_release_now) (RenderBuffer); Bool (*can_release_now) (RenderBuffer);
/* Run a callback once the buffer contents become idle on the given
target. NULL if flags contains ImmediateRelease. The callback
is also run when the buffer is destroyed, but not when the target
is destroyed; in that case, the callback key simply becomes
invalid. */
IdleCallbackKey (*add_idle_callback) (RenderBuffer, RenderTarget,
BufferIdleFunc, void *);
/* Cancel the given idle callback. */
void (*cancel_idle_callback) (IdleCallbackKey);
/* Return whether or not the buffer is idle. NULL if flags contains
ImmediateRelease. */
Bool (*is_buffer_idle) (RenderBuffer, RenderTarget);
/* Wait for a buffer to become idle on the given target. May be
NULL. */
void (*wait_for_idle) (RenderBuffer, RenderTarget);
/* Ensure wait_for_idle can be called. May be NULL. */
void (*set_need_wait_for_idle) (RenderTarget);
/* Called during renderer initialization. */ /* Called during renderer initialization. */
void (*init_buffer_funcs) (void); void (*init_buffer_funcs) (void);
}; };
@ -414,8 +461,9 @@ extern void RegisterStaticRenderer (const char *, RenderFuncs *,
BufferFuncs *); BufferFuncs *);
extern void InitRenderers (void); extern void InitRenderers (void);
extern RenderTarget RenderTargetFromWindow (Window); extern RenderTarget RenderTargetFromWindow (Window, unsigned long);
extern RenderTarget RenderTargetFromPixmap (Pixmap); extern RenderTarget RenderTargetFromPixmap (Pixmap);
extern void RenderSetStandardEventMask (RenderTarget, unsigned long);
extern void RenderNoteTargetSize (RenderTarget, int, int); extern void RenderNoteTargetSize (RenderTarget, int, int);
extern Picture RenderPictureFromTarget (RenderTarget); extern Picture RenderPictureFromTarget (RenderTarget);
extern void RenderFreePictureFromTarget (Picture); extern void RenderFreePictureFromTarget (Picture);
@ -432,6 +480,12 @@ extern RenderFence RenderImportFdFence (int, Bool *);
extern void RenderWaitFence (RenderFence); extern void RenderWaitFence (RenderFence);
extern void RenderDeleteFence (RenderFence); extern void RenderDeleteFence (RenderFence);
extern int RenderGetFinishFence (Bool *); extern int RenderGetFinishFence (Bool *);
extern PresentCompletionKey RenderPresentToWindow (RenderTarget, RenderBuffer,
pixman_region32_t *,
PresentCompletionFunc,
void *);
extern void RenderCancelPresentationCallback (PresentCompletionKey);
extern void RenderCancelPresentation (RenderTarget);
extern DrmFormat *RenderGetDrmFormats (int *); extern DrmFormat *RenderGetDrmFormats (int *);
extern dev_t RenderGetRenderDevice (Bool *); extern dev_t RenderGetRenderDevice (Bool *);
@ -450,6 +504,12 @@ extern void RenderFreeSinglePixelBuffer (RenderBuffer);
extern void RenderUpdateBufferForDamage (RenderBuffer, pixman_region32_t *, extern void RenderUpdateBufferForDamage (RenderBuffer, pixman_region32_t *,
DrawParams *); DrawParams *);
extern Bool RenderCanReleaseNow (RenderBuffer); extern Bool RenderCanReleaseNow (RenderBuffer);
extern IdleCallbackKey RenderAddIdleCallback (RenderBuffer, RenderTarget,
BufferIdleFunc, void *);
extern void RenderCancelIdleCallback (IdleCallbackKey);
extern Bool RenderIsBufferIdle (RenderBuffer, RenderTarget);
extern void RenderWaitForIdle (RenderBuffer, RenderTarget);
extern void RenderSetNeedWaitForIdle (RenderTarget);
/* Defined in run.c. */ /* Defined in run.c. */
@ -610,6 +670,15 @@ typedef struct _View View;
typedef struct _List List; typedef struct _List List;
typedef struct _Subcompositor Subcompositor; typedef struct _Subcompositor Subcompositor;
typedef enum _FrameMode FrameMode;
enum _FrameMode
{
ModeStarted,
ModeComplete,
ModePresented,
};
extern void SubcompositorInit (void); extern void SubcompositorInit (void);
extern Subcompositor *MakeSubcompositor (void); extern Subcompositor *MakeSubcompositor (void);
@ -634,6 +703,10 @@ extern void SubcompositorSetBoundsCallback (Subcompositor *,
void (*) (void *, int, int, void (*) (void *, int, int,
int, int), int, int),
void *); void *);
extern void SubcompositorSetNoteFrameCallback (Subcompositor *,
void (*) (FrameMode, uint64_t,
void *),
void *);
extern void SubcompositorBounds (Subcompositor *, int *, int *, int *, int *); extern void SubcompositorBounds (Subcompositor *, int *, int *, int *, int *);
extern void SubcompositorSetProjectiveTransform (Subcompositor *, int, int); extern void SubcompositorSetProjectiveTransform (Subcompositor *, int, int);
@ -964,6 +1037,7 @@ extern void XLDefaultCommit (Surface *);
extern void XLStateAttachBuffer (State *, ExtBuffer *); extern void XLStateAttachBuffer (State *, ExtBuffer *);
extern void XLStateDetachBuffer (State *); extern void XLStateDetachBuffer (State *);
extern void XLSurfaceRunFrameCallbacks (Surface *, struct timespec); extern void XLSurfaceRunFrameCallbacks (Surface *, struct timespec);
extern void XLSurfaceRunFrameCallbacksMs (Surface *, uint32_t);
extern CommitCallback *XLSurfaceRunAtCommit (Surface *, extern CommitCallback *XLSurfaceRunAtCommit (Surface *,
void (*) (Surface *, void *), void (*) (Surface *, void *),
void *); void *);
@ -1004,6 +1078,7 @@ extern Bool XLGetOutputRectAt (int, int, int *, int *, int *, int *);
extern void *XLAddScaleChangeCallback (void *, void (*) (void *, int)); extern void *XLAddScaleChangeCallback (void *, void (*) (void *, int));
extern void XLRemoveScaleChangeCallback (void *); extern void XLRemoveScaleChangeCallback (void *);
extern void XLClearOutputs (Surface *); extern void XLClearOutputs (Surface *);
extern void XLOutputSetChangeFunction (void (*) (Time));
/* Defined in atoms.c. */ /* Defined in atoms.c. */
@ -1022,7 +1097,8 @@ extern Atom _NET_WM_OPAQUE_REGION, _XL_BUFFER_RELEASE,
XdndActionAsk, XdndActionPrivate, XdndActionList, XdndActionDescription, XdndActionAsk, XdndActionPrivate, XdndActionList, XdndActionDescription,
XdndProxy, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndProxy, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop,
XdndFinished, _NET_WM_FRAME_TIMINGS, _NET_WM_BYPASS_COMPOSITOR, WM_STATE, XdndFinished, _NET_WM_FRAME_TIMINGS, _NET_WM_BYPASS_COMPOSITOR, WM_STATE,
_NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_MENU, _NET_WM_WINDOW_TYPE_DND; _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_MENU, _NET_WM_WINDOW_TYPE_DND,
CONNECTOR_ID;
extern XrmQuark resource_quark, app_quark, QString; extern XrmQuark resource_quark, app_quark, QString;
@ -1062,6 +1138,7 @@ extern void XLFrameClockSetPredictRefresh (FrameClock *);
extern void XLFrameClockDisablePredictRefresh (FrameClock *); extern void XLFrameClockDisablePredictRefresh (FrameClock *);
extern void XLFrameClockSetFreezeCallback (FrameClock *, void (*) (void *), extern void XLFrameClockSetFreezeCallback (FrameClock *, void (*) (void *),
void *); void *);
extern uint64_t XLFrameClockGetFrameTime (FrameClock *);
extern void *XLAddCursorClockCallback (void (*) (void *, struct timespec), extern void *XLAddCursorClockCallback (void (*) (void *, struct timespec),
void *); void *);
extern void XLStopCursorClockCallback (void *); extern void XLStopCursorClockCallback (void *);
@ -1477,6 +1554,10 @@ extern void XLInitDecoration (void);
extern void XLInitSinglePixelBuffer (void); extern void XLInitSinglePixelBuffer (void);
/* Defined in drm_lease.h. */
extern void XLInitDrmLease (void);
/* Utility functions that don't belong in a specific file. */ /* Utility functions that don't belong in a specific file. */
#define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0]) #define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0])

9
egl.c
View file

@ -1077,7 +1077,7 @@ TryPreserveOnSwap (EGLSurface *surface)
} }
static RenderTarget static RenderTarget
TargetFromWindow (Window window) TargetFromWindow (Window window, unsigned long standard_event_mask)
{ {
EglTarget *target; EglTarget *target;
@ -1125,6 +1125,12 @@ TargetFromPixmap (Pixmap pixmap)
return (RenderTarget) (void *) target; return (RenderTarget) (void *) target;
} }
static void
SetStandardEventMask (RenderTarget target, unsigned long standard_event_mask)
{
/* Ignored. */
}
static void static void
NoteTargetSize (RenderTarget target, int width, int height) NoteTargetSize (RenderTarget target, int width, int height)
{ {
@ -1659,6 +1665,7 @@ static RenderFuncs egl_render_funcs =
.init_render_funcs = InitRenderFuncs, .init_render_funcs = InitRenderFuncs,
.target_from_window = TargetFromWindow, .target_from_window = TargetFromWindow,
.target_from_pixmap = TargetFromPixmap, .target_from_pixmap = TargetFromPixmap,
.set_standard_event_mask = SetStandardEventMask,
.note_target_size = NoteTargetSize, .note_target_size = NoteTargetSize,
.picture_from_target = PictureFromTarget, .picture_from_target = PictureFromTarget,
.free_picture_from_target = FreePictureFromTarget, .free_picture_from_target = FreePictureFromTarget,

View file

@ -90,6 +90,9 @@ struct _FrameClock
unfrozen until EndFrame. */ unfrozen until EndFrame. */
Bool need_configure, frozen, frozen_until_end_frame; Bool need_configure, frozen, frozen_until_end_frame;
/* Whether or not EndFrame was called after StartFrame. */
Bool end_frame_called;
/* The wanted configure value. */ /* The wanted configure value. */
uint64_t configure_id; uint64_t configure_id;
@ -111,6 +114,9 @@ struct _FrameClock
/* Data for that callback. */ /* Data for that callback. */
void *freeze_callback_data; void *freeze_callback_data;
/* Any pending frame synchronization counter value, or 0. */
uint64_t pending_sync_value;
}; };
struct _CursorClockCallback struct _CursorClockCallback
@ -221,9 +227,53 @@ HandleEndFrame (Timer *timer, void *data, struct timespec time)
the frame. */ the frame. */
RemoveTimer (timer); RemoveTimer (timer);
clock->end_frame_timer = NULL; clock->end_frame_timer = NULL;
if (clock->end_frame_called)
EndFrame (clock); EndFrame (clock);
} }
/* Forward declaration. */
static void RunFrameCallbacks (FrameClock *);
static void
FreezeForValue (FrameClock *clock, uint64_t counter_value)
{
/* If it took too long (1 second at 60fps) to obtain the counter
value, and said value is now out of date, don't do anything. */
if (clock->next_frame_id > counter_value)
return;
/* The frame clock is now frozen, and we will have to wait for a
client to ack_configure and then commit something. */
if (clock->end_frame_timer)
{
/* End the frame now, and clear in_frame early. */
RemoveTimer (clock->end_frame_timer);
clock->end_frame_timer = NULL;
if (clock->end_frame_called)
EndFrame (clock);
}
/* counter_value - 240 is the value seen by the compositor when the
frame contents were frozen in response to a resize. If it is
less than finished_frame_id, run frame callbacks now, or clients
like Chromium are confused and hang waiting for frame callbacks
to be called. */
if (counter_value - 240 <= clock->finished_frame_id)
RunFrameCallbacks (clock);
/* The reason for clearing in_frame is that otherwise a future
Commit after the configuration is acknowledged will not be able
to start a new frame and restart the frame clock. */
clock->in_frame = False;
clock->need_configure = True;
clock->configure_id = counter_value;
clock->frozen = True;
}
static void static void
PostEndFrame (FrameClock *clock) PostEndFrame (FrameClock *clock)
{ {
@ -320,6 +370,9 @@ StartFrame (FrameClock *clock, Bool urgent, Bool predict)
if (clock->frozen_until_end_frame) if (clock->frozen_until_end_frame)
return; return;
if (clock->in_frame)
return;
if (clock->need_configure) if (clock->need_configure)
{ {
clock->next_frame_id = clock->configure_id; clock->next_frame_id = clock->configure_id;
@ -327,6 +380,7 @@ StartFrame (FrameClock *clock, Bool urgent, Bool predict)
} }
clock->in_frame = True; clock->in_frame = True;
clock->end_frame_called = False;
/* Set the clock to an odd value; if we want the compositor to /* Set the clock to an odd value; if we want the compositor to
redraw this frame immediately (since it is running late), make it redraw this frame immediately (since it is running late), make it
@ -371,6 +425,10 @@ EndFrame (FrameClock *clock)
clock->frozen_until_end_frame = False; clock->frozen_until_end_frame = False;
/* Signal that end_frame was called and it is now safe to finish the
frame from the timer. */
clock->end_frame_called = True;
if (!clock->in_frame if (!clock->in_frame
/* If the end of the frame has already been signalled, this /* If the end of the frame has already been signalled, this
function should just return instead of increasing the counter function should just return instead of increasing the counter
@ -389,6 +447,12 @@ EndFrame (FrameClock *clock)
clock->next_frame_id += 1; clock->next_frame_id += 1;
clock->finished_frame_id = clock->next_frame_id; clock->finished_frame_id = clock->next_frame_id;
/* The frame has ended. Freeze the frame clock if there is a
pending sync value. */
if (clock->pending_sync_value)
FreezeForValue (clock, clock->pending_sync_value);
clock->pending_sync_value = 0;
if (!frame_sync_supported) if (!frame_sync_supported)
return; return;
@ -570,28 +634,15 @@ XLFrameClockHandleFrameEvent (FrameClock *clock, XEvent *event)
if (value % 2) if (value % 2)
value += 1; value += 1;
/* The frame clock is now frozen, and we will have to wait for a /* If a frame is in progress, postpone this frame
client to ack_configure and then commit something. */ synchronization message. */
if (clock->in_frame && !clock->end_frame_called)
if (clock->end_frame_timer) clock->pending_sync_value = value;
{ else
/* End the frame now, and clear in_frame early. */ FreezeForValue (clock, value);
RemoveTimer (clock->end_frame_timer);
clock->end_frame_timer = NULL;
EndFrame (clock);
/* The reason for clearing in_frame is that otherwise a
future Commit after the configuration is acknowledged
will not be able to start a new frame and restart the
frame clock. */
clock->in_frame = False;
}
clock->need_configure = True;
clock->configure_id = value;
clock->frozen = True;
if (clock->freeze_callback) if (clock->freeze_callback)
/* Call the freeze callback in any case. */
clock->freeze_callback (clock->freeze_callback_data); clock->freeze_callback (clock->freeze_callback_data);
} }
} }
@ -731,6 +782,16 @@ XLFrameClockSetFreezeCallback (FrameClock *clock, void (*callback) (void *),
clock->freeze_callback_data = data; clock->freeze_callback_data = data;
} }
uint64_t
XLFrameClockGetFrameTime (FrameClock *clock)
{
/* Only return the time if it is actually a valid clock time. */
if (!compositor.server_time_monotonic)
return 0;
return clock->last_frame_time;
}
/* Cursor animation clock-related functions. */ /* Cursor animation clock-related functions. */

View file

@ -155,9 +155,14 @@ Setup (Surface *surface, Role *role)
static void static void
ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer) ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer)
{ {
IconSurface *icon;
icon = IconSurfaceFromRole (role);
/* Icon surfaces are not supposed to change much, so doing an XSync /* Icon surfaces are not supposed to change much, so doing an XSync
here is okay. */ (or XIfEvent) here is okay. */
XSync (compositor.display, False); RenderWaitForIdle (XLRenderBufferFromBuffer (buffer),
icon->target);
/* Now really release the buffer. */ /* Now really release the buffer. */
XLReleaseBuffer (buffer); XLReleaseBuffer (buffer);
@ -213,17 +218,25 @@ NoteBounds (void *data, int min_x, int min_y, int max_x, int max_y)
} }
static void static void
RunFrameCallbacks (Surface *surface) RunFrameCallbacks (Surface *surface, FrameClock *clock)
{ {
struct timespec time; struct timespec time;
uint64_t last_drawn_time;
/* Surface can be NULL for various reasons, especially events /* Surface can be NULL for various reasons, especially events
arriving after the icon surface is detached. */ arriving after the icon surface is detached. */
if (!surface) if (!surface)
return; return;
last_drawn_time = XLFrameClockGetFrameTime (clock);
if (!last_drawn_time)
{
clock_gettime (CLOCK_MONOTONIC, &time); clock_gettime (CLOCK_MONOTONIC, &time);
XLSurfaceRunFrameCallbacks (surface, time); XLSurfaceRunFrameCallbacks (surface, time);
}
else
XLSurfaceRunFrameCallbacksMs (surface, last_drawn_time / 1000);
} }
static void static void
@ -246,7 +259,7 @@ AfterFrame (FrameClock *clock, void *data)
return; return;
} }
RunFrameCallbacks (icon->role.surface); RunFrameCallbacks (icon->role.surface, clock);
} }
static void static void
@ -410,7 +423,11 @@ XLGetIconSurface (Surface *surface)
(unsigned char *) &_NET_WM_WINDOW_TYPE_DND, 1); (unsigned char *) &_NET_WM_WINDOW_TYPE_DND, 1);
/* Create a target associated with the window. */ /* Create a target associated with the window. */
role->target = RenderTargetFromWindow (role->window); role->target = RenderTargetFromWindow (role->window, None);
/* For simplicity reasons we do not handle idle notifications
asynchronously. */
RenderSetNeedWaitForIdle (role->target);
/* Create a subcompositor associated with the window. */ /* Create a subcompositor associated with the window. */
role->subcompositor = MakeSubcompositor (); role->subcompositor = MakeSubcompositor ();

View file

@ -98,6 +98,9 @@ static ScaleChangeCallback scale_callbacks;
/* The scale factor currently applied on a global basis. */ /* The scale factor currently applied on a global basis. */
int global_scale_factor; int global_scale_factor;
/* Function run upon any kind of XRandR notify event. */
static void (*change_hook) (Time);
static Bool static Bool
ApplyEnvironment (const char *name, int *variable) ApplyEnvironment (const char *name, int *variable)
{ {
@ -903,9 +906,38 @@ XLGetOutputRectAt (int x, int y, int *x_out, int *y_out,
Bool Bool
XLHandleOneXEventForOutputs (XEvent *event) XLHandleOneXEventForOutputs (XEvent *event)
{ {
XRRNotifyEvent *notify;
XRROutputPropertyNotifyEvent *property;
XRRResourceChangeNotifyEvent *resource;
Time time;
notify = (XRRNotifyEvent *) event;
if (event->type == compositor.rr_event_base + RRNotify) if (event->type == compositor.rr_event_base + RRNotify)
{ {
NoticeOutputsMaybeChanged (); NoticeOutputsMaybeChanged ();
if (change_hook)
{
time = CurrentTime;
/* See if a timestamp of some sort can be extracted. */
switch (notify->subtype)
{
case RRNotify_OutputProperty:
property = (XRROutputPropertyNotifyEvent *) notify;
time = property->timestamp;
break;
case RRNotify_ResourceChange:
resource = (XRRResourceChangeNotifyEvent *) notify;
time = resource->timestamp;
break;
}
change_hook (time);
}
return True; return True;
} }
@ -1053,6 +1085,12 @@ XLClearOutputs (Surface *surface)
surface->n_outputs = 0; surface->n_outputs = 0;
} }
void
XLOutputSetChangeFunction (void (*change_func) (Time))
{
change_hook = change_func;
}
void void
XLInitRROutputs (void) XLInitRROutputs (void)
{ {
@ -1096,6 +1134,16 @@ XLInitRROutputs (void)
scale_callbacks.next = &scale_callbacks; scale_callbacks.next = &scale_callbacks;
scale_callbacks.last = &scale_callbacks; scale_callbacks.last = &scale_callbacks;
if (compositor.rr_major > 1
&& (compositor.rr_major == 1
&& compositor.rr_minor >= 4))
XRRSelectInput (compositor.display,
DefaultRootWindow (compositor.display),
(RRCrtcChangeNotifyMask
| RROutputChangeNotifyMask
| RROutputPropertyNotifyMask
| RRResourceChangeNotifyMask));
else
XRRSelectInput (compositor.display, XRRSelectInput (compositor.display,
DefaultRootWindow (compositor.display), DefaultRootWindow (compositor.display),
(RRCrtcChangeNotifyMask (RRCrtcChangeNotifyMask

File diff suppressed because it is too large Load diff

View file

@ -69,9 +69,9 @@ AllocateRenderer (void)
} }
RenderTarget RenderTarget
RenderTargetFromWindow (Window window) RenderTargetFromWindow (Window window, unsigned long standard_event_mask)
{ {
return render_funcs.target_from_window (window); return render_funcs.target_from_window (window, standard_event_mask);
} }
RenderTarget RenderTarget
@ -80,6 +80,13 @@ RenderTargetFromPixmap (Pixmap pixmap)
return render_funcs.target_from_pixmap (pixmap); return render_funcs.target_from_pixmap (pixmap);
} }
void
RenderSetStandardEventMask (RenderTarget target,
unsigned long standard_event_mask)
{
render_funcs.set_standard_event_mask (target, standard_event_mask);
}
void void
RenderNoteTargetSize (RenderTarget target, int width, int height) RenderNoteTargetSize (RenderTarget target, int width, int height)
{ {
@ -177,6 +184,37 @@ RenderGetFinishFence (Bool *error)
return render_funcs.get_finish_fence (error); return render_funcs.get_finish_fence (error);
} }
PresentCompletionKey
RenderPresentToWindow (RenderTarget target, RenderBuffer source,
pixman_region32_t *damage,
PresentCompletionFunc callback, void *data)
{
if (!render_funcs.present_to_window)
return NULL;
return render_funcs.present_to_window (target, source,
damage, callback,
data);
}
void
RenderCancelPresentationCallback (PresentCompletionKey key)
{
if (!render_funcs.cancel_presentation_callback)
return;
render_funcs.cancel_presentation_callback (key);
}
void
RenderCancelPresentation (RenderTarget target)
{
if (!render_funcs.cancel_presentation)
return;
render_funcs.cancel_presentation (target);
}
DrmFormat * DrmFormat *
RenderGetDrmFormats (int *n_formats) RenderGetDrmFormats (int *n_formats)
{ {
@ -269,6 +307,43 @@ RenderCanReleaseNow (RenderBuffer buffer)
return buffer_funcs.can_release_now (buffer); return buffer_funcs.can_release_now (buffer);
} }
IdleCallbackKey
RenderAddIdleCallback (RenderBuffer buffer, RenderTarget target,
BufferIdleFunc function, void *data)
{
return buffer_funcs.add_idle_callback (buffer, target, function, data);
}
void
RenderCancelIdleCallback (IdleCallbackKey key)
{
return buffer_funcs.cancel_idle_callback (key);
}
Bool
RenderIsBufferIdle (RenderBuffer buffer, RenderTarget target)
{
return buffer_funcs.is_buffer_idle (buffer, target);
}
void
RenderWaitForIdle (RenderBuffer buffer, RenderTarget target)
{
if (!buffer_funcs.wait_for_idle)
return;
buffer_funcs.wait_for_idle (buffer, target);
}
void
RenderSetNeedWaitForIdle (RenderTarget target)
{
if (!buffer_funcs.set_need_wait_for_idle)
return;
buffer_funcs.set_need_wait_for_idle (target);
}
void void
RegisterStaticRenderer (const char *name, RegisterStaticRenderer (const char *name,
RenderFuncs *render_funcs, RenderFuncs *render_funcs,

6
run.c
View file

@ -139,6 +139,9 @@ HandleOneXEvent (XEvent *event)
if (XLHandleXEventForXdgSurfaces (event)) if (XLHandleXEventForXdgSurfaces (event))
return; return;
if (HandleOneXEventForPictureRenderer (event))
return;
if (XLHandleXEventForXdgToplevels (event)) if (XLHandleXEventForXdgToplevels (event))
return; return;
@ -156,9 +159,6 @@ HandleOneXEvent (XEvent *event)
return; return;
#endif #endif
if (HandleOneXEventForPictureRenderer (event))
return;
if (XLHandleOneXEventForXData (event)) if (XLHandleOneXEventForXData (event))
return; return;

17
seat.c
View file

@ -711,6 +711,10 @@ MaybeCreateCursor (CursorRing *ring, int index)
compositor.n_planes); compositor.n_planes);
ring->targets[index] ring->targets[index]
= RenderTargetFromPixmap (ring->pixmaps[index]); = RenderTargetFromPixmap (ring->pixmaps[index]);
/* For simplicity reasons we do not handle idle notifications
asynchronously. */
RenderSetNeedWaitForIdle (ring->targets[index]);
} }
static int static int
@ -1208,9 +1212,20 @@ Setup (Surface *surface, Role *role)
static void static void
ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer) ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer)
{ {
SeatCursor *cursor;
int i;
cursor = CursorFromRole (role);
/* Cursors are generally committed only once, so syncing here is /* Cursors are generally committed only once, so syncing here is
OK in terms of efficiency. */ OK in terms of efficiency. */
XSync (compositor.display, False); for (i = 0; i < CursorRingElements; ++i)
{
if (cursor->cursor_ring->pixmaps[i])
RenderWaitForIdle (XLRenderBufferFromBuffer (buffer),
cursor->cursor_ring->targets[i]);
}
XLReleaseBuffer (buffer); XLReleaseBuffer (buffer);
} }

View file

@ -310,9 +310,6 @@ struct _View
struct _Subcompositor struct _Subcompositor
{ {
/* Various flags describing the state of this subcompositor. */
int state;
/* List of all inferiors in compositing order. */ /* List of all inferiors in compositing order. */
List *inferiors, *last; List *inferiors, *last;
@ -334,9 +331,29 @@ struct _Subcompositor
/* Function called with the bounds before each update. */ /* Function called with the bounds before each update. */
void (*note_bounds) (void *, int, int, int, int); void (*note_bounds) (void *, int, int, int, int);
/* Function called with the frame counter on each update. */
void (*note_frame) (FrameMode, uint64_t, void *);
/* The current frame counter, incremented with each frame. */
uint64_t frame_counter;
/* Data for those three functions. */ /* Data for those three functions. */
void *opaque_change_data, *input_change_data, *note_bounds_data; void *opaque_change_data, *input_change_data, *note_bounds_data;
/* Data for the fourth. */
void *note_frame_data;
/* Buffers used to store that damage. */
pixman_region32_t prior_damage[2];
/* The damage region of previous updates. last_damage is what the
damage region was 1 update ago, and before_damage is what the
damage region was 2 updates ago. */
pixman_region32_t *last_damage, *before_damage;
/* The last attached presentation callback, if any. */
PresentCompletionKey present_key;
/* The minimum origin of any surface in this subcompositor. Used to /* The minimum origin of any surface in this subcompositor. Used to
compute the actual size of the subcompositor. */ compute the actual size of the subcompositor. */
int min_x, min_y; int min_x, min_y;
@ -347,15 +364,10 @@ struct _Subcompositor
/* An additional offset to apply when drawing to the target. */ /* An additional offset to apply when drawing to the target. */
int tx, ty; int tx, ty;
/* Buffers used to store that damage. */
pixman_region32_t prior_damage[2];
/* The damage region of previous updates. last_damage is what the
damage region was 1 update ago, and before_damage is what the
damage region was 2 updates ago. */
pixman_region32_t *last_damage, *before_damage;
#endif #endif
/* Various flags describing the state of this subcompositor. */
int state;
}; };
#ifndef TEST #ifndef TEST
@ -1807,6 +1819,16 @@ SubcompositorSetBoundsCallback (Subcompositor *subcompositor,
subcompositor->note_bounds_data = data; subcompositor->note_bounds_data = data;
} }
void
SubcompositorSetNoteFrameCallback (Subcompositor *subcompositor,
void (*note_frame) (FrameMode, uint64_t,
void *),
void *data)
{
subcompositor->note_frame = note_frame;
subcompositor->note_frame_data = data;
}
static void static void
FillBoxesWithTransparency (Subcompositor *subcompositor, FillBoxesWithTransparency (Subcompositor *subcompositor,
pixman_box32_t *boxes, int nboxes) pixman_box32_t *boxes, int nboxes)
@ -1917,6 +1939,69 @@ IntersectBoxes (pixman_box32_t *in, pixman_box32_t *other,
return True; return True;
} }
static Bool
NoViewsAfter (View *first_view)
{
List *list;
View *view;
list = first_view->link->next;
while (list != first_view->link)
{
view = list->view;
if (!view)
goto next_1;
if (IsViewUnmapped (view))
{
/* Skip the unmapped view. */
list = view->inferior;
goto next_1;
}
if (IsSkipped (view))
{
/* We must skip this view, as it represents (for
instance) a subsurface that has been added, but not
committed. */
goto next_1;
}
if (!view->buffer)
goto next_1;
/* There is view in front of view that potentially obscures it.
Bail out! */
return False;
next_1:
list = list->next;
}
return True;
}
static void
PresentCompletedCallback (void *data)
{
Subcompositor *subcompositor;
subcompositor = data;
/* The presentation callback should still be set here. */
XLAssert (subcompositor->present_key != NULL);
subcompositor->present_key = NULL;
/* Call the presentation callback if it is still set. */
if (subcompositor->note_frame)
subcompositor->note_frame (ModePresented,
subcompositor->frame_counter,
subcompositor->note_frame_data);
}
void void
SubcompositorUpdate (Subcompositor *subcompositor) SubcompositorUpdate (Subcompositor *subcompositor)
{ {
@ -1928,9 +2013,10 @@ SubcompositorUpdate (Subcompositor *subcompositor)
int nboxes, i, tx, ty, view_width, view_height; int nboxes, i, tx, ty, view_width, view_height;
Operation op; Operation op;
RenderBuffer buffer; RenderBuffer buffer;
int min_x, min_y; int min_x, min_y, age, n_seen;
int age;
DrawParams draw_params; DrawParams draw_params;
Bool presented;
PresentCompletionKey key;
/* Just return if no target was specified. */ /* Just return if no target was specified. */
if (!IsTargetAttached (subcompositor)) if (!IsTargetAttached (subcompositor))
@ -1940,6 +2026,13 @@ SubcompositorUpdate (Subcompositor *subcompositor)
if (IsFrozen (subcompositor)) if (IsFrozen (subcompositor))
return; return;
if (subcompositor->present_key)
/* Cancel the presentation callback. The next presentation will
either be to an unmapped window, cancel the presentation, or
start a new one. */
RenderCancelPresentationCallback (subcompositor->present_key);
subcompositor->present_key = NULL;
list = subcompositor->inferiors; list = subcompositor->inferiors;
min_x = subcompositor->min_x; min_x = subcompositor->min_x;
min_y = subcompositor->min_y; min_y = subcompositor->min_y;
@ -1949,6 +2042,8 @@ SubcompositorUpdate (Subcompositor *subcompositor)
original_start = NULL; original_start = NULL;
pixman_region32_init (&temp); pixman_region32_init (&temp);
pixman_region32_init (&update_region); pixman_region32_init (&update_region);
n_seen = 0;
presented = False;
start = subcompositor->inferiors->next->view; start = subcompositor->inferiors->next->view;
original_start = subcompositor->inferiors->next->view; original_start = subcompositor->inferiors->next->view;
@ -2042,6 +2137,9 @@ SubcompositorUpdate (Subcompositor *subcompositor)
if (!view->buffer) if (!view->buffer)
goto next; goto next;
/* Increase the number of views seen count. */
n_seen++;
/* Obtain the view width and height here. */ /* Obtain the view width and height here. */
view_width = ViewWidth (view); view_width = ViewWidth (view);
view_height = ViewHeight (view); view_height = ViewHeight (view);
@ -2265,8 +2363,16 @@ SubcompositorUpdate (Subcompositor *subcompositor)
/* If there's nothing to do, return. */ /* If there's nothing to do, return. */
if (!start) if (!start)
/* There is no starting view. Presentation is not cancelled in
this case, because the surface should now be unmapped. */
goto complete; goto complete;
/* Increase the frame count and announce the new frame number. */
if (subcompositor->note_frame)
subcompositor->note_frame (ModeStarted,
++subcompositor->frame_counter,
subcompositor->note_frame_data);
/* Now update all views from start onwards. */ /* Now update all views from start onwards. */
list = start->link; list = start->link;
@ -2311,6 +2417,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
view_width = ViewWidth (view); view_width = ViewWidth (view);
view_height = ViewHeight (view); view_height = ViewHeight (view);
/* And the buffer. */
buffer = XLRenderBufferFromBuffer (view->buffer); buffer = XLRenderBufferFromBuffer (view->buffer);
if (IsGarbaged (subcompositor)) if (IsGarbaged (subcompositor))
@ -2337,8 +2444,67 @@ SubcompositorUpdate (Subcompositor *subcompositor)
&draw_params); &draw_params);
} }
/* Compute the transform and put it in draw_params. */
ViewComputeTransform (view, &draw_params, True);
if (!first) if (!first)
{ {
/* See if the first mapped and visible view after start is eligible
for direct presentation. It is considered eligible if:
- its bounds match that of the subcompositor.
- its depth and masks match that of the subcompositor.
- it is not occluded by any other view, above or below.
- it has no transform whatsoever.
Also, presentation is done asynchronously, so we only
consider the view as eligible for presentation if
completion callbacks are attached. */
if (!draw_params.flags
&& view->abs_x == subcompositor->min_x
&& view->abs_y == subcompositor->min_y
&& view_width == SubcompositorWidth (subcompositor)
&& view_height == SubcompositorHeight (subcompositor)
/* N.B. that n_seen is not set (0) if the view is
garbaged. */
&& (n_seen == 1 || (!n_seen && NoViewsAfter (view)))
&& subcompositor->note_frame)
{
/* Direct presentation is okay. Present the pixmap to
the drawable. */
if (IsGarbaged (subcompositor))
key = RenderPresentToWindow (subcompositor->target, buffer,
NULL, PresentCompletedCallback,
subcompositor);
else
key = RenderPresentToWindow (subcompositor->target, buffer,
&update_region,
PresentCompletedCallback,
subcompositor);
/* Now set presented to whether or not key is non-NULL. */
presented = key != NULL;
/* And set the presentation callback. */
subcompositor->present_key = key;
if (presented)
{
/* If presentation succeeded, don't composite.
Instead, just continue looping to set the input
region if garbaged. */
if (!IsGarbaged (subcompositor) && (age >= 0 && age < 3))
/* And if not garbaged, skip everything. */
goto present_success;
else
goto present_success_garbaged;
}
}
else
RenderCancelPresentation (subcompositor->target);
/* The first view with an attached buffer should be drawn /* The first view with an attached buffer should be drawn
with PictOpSrc so that transparency is applied correctly, with PictOpSrc so that transparency is applied correctly,
if it contains the entire update region. */ if it contains the entire update region. */
@ -2388,10 +2554,11 @@ SubcompositorUpdate (Subcompositor *subcompositor)
else else
op = OperationOver; op = OperationOver;
first = view; if (presented && (IsGarbaged (subcompositor)
|| age < 0 || age >= 3))
goto present_success_garbaged;
/* Compute the transform and put it in draw_params. */ first = view;
ViewComputeTransform (view, &draw_params, True);
if (!IsGarbaged (subcompositor) && (age >= 0 && age < 3)) if (!IsGarbaged (subcompositor) && (age >= 0 && age < 3))
{ {
@ -2428,11 +2595,6 @@ SubcompositorUpdate (Subcompositor *subcompositor)
} }
else else
{ {
/* Clear the damaged area, since it will either be drawn or
be obscured. We didn't get a chance to clear the damage
earlier, since the compositor was garbaged. */
pixman_region32_clear (&view->damage);
/* If the subcompositor is garbaged, composite the entire /* If the subcompositor is garbaged, composite the entire
view to the right location. */ view to the right location. */
RenderComposite (buffer, subcompositor->target, op, RenderComposite (buffer, subcompositor->target, op,
@ -2447,6 +2609,12 @@ SubcompositorUpdate (Subcompositor *subcompositor)
/* height, draw-params. */ /* height, draw-params. */
view_height, &draw_params); view_height, &draw_params);
present_success_garbaged:
/* Clear the damaged area, since it will either be drawn or
be obscured. We didn't get a chance to clear the damage
earlier, since the compositor was garbaged. */
pixman_region32_clear (&view->damage);
/* Also adjust the opaque and input regions here. */ /* Also adjust the opaque and input regions here. */
if (pixman_region32_not_empty (&view->opaque) if (pixman_region32_not_empty (&view->opaque)
@ -2501,6 +2669,8 @@ SubcompositorUpdate (Subcompositor *subcompositor)
} }
while (list != subcompositor->inferiors); while (list != subcompositor->inferiors);
present_success:
/* Swap changes to display. */ /* Swap changes to display. */
if (IsGarbaged (subcompositor) || age < 0 || age >= 3) if (IsGarbaged (subcompositor) || age < 0 || age >= 3)
@ -2561,6 +2731,12 @@ SubcompositorUpdate (Subcompositor *subcompositor)
subcompositor->state &= ~SubcompositorIsGarbaged; subcompositor->state &= ~SubcompositorIsGarbaged;
subcompositor->state &= ~SubcompositorIsOpaqueDirty; subcompositor->state &= ~SubcompositorIsOpaqueDirty;
subcompositor->state &= ~SubcompositorIsInputDirty; subcompositor->state &= ~SubcompositorIsInputDirty;
/* Call the frame complete function if presentation did not happen. */
if (subcompositor->note_frame && !presented)
subcompositor->note_frame (ModeComplete,
subcompositor->frame_counter,
subcompositor->note_frame_data);
} }
void void
@ -2735,6 +2911,10 @@ SubcompositorFree (Subcompositor *subcompositor)
pixman_region32_fini (&subcompositor->prior_damage[0]); pixman_region32_fini (&subcompositor->prior_damage[0]);
pixman_region32_fini (&subcompositor->prior_damage[1]); pixman_region32_fini (&subcompositor->prior_damage[1]);
/* Remove the presentation key. */
if (subcompositor->present_key)
RenderCancelPresentationCallback (subcompositor->present_key);
XLFree (subcompositor); XLFree (subcompositor);
} }

View file

@ -1599,6 +1599,19 @@ XLSurfaceRunFrameCallbacks (Surface *surface, struct timespec time)
XLSurfaceRunFrameCallbacks (list->data, time); XLSurfaceRunFrameCallbacks (list->data, time);
} }
void
XLSurfaceRunFrameCallbacksMs (Surface *surface, uint32_t ms_time)
{
XLList *list;
RunFrameCallbacks (&surface->current_state.frame_callbacks,
ms_time);
/* Run frame callbacks for each attached subsurface as well. */
for (list = surface->subsurfaces; list; list = list->next)
XLSurfaceRunFrameCallbacksMs (list->data, ms_time);
}
CommitCallback * CommitCallback *
XLSurfaceRunAtCommit (Surface *surface, XLSurfaceRunAtCommit (Surface *surface,
void (*commit_func) (Surface *, void *), void (*commit_func) (Surface *, void *),

View file

@ -72,7 +72,7 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
That means it is impossible to attribute forwarded events or That means it is impossible to attribute forwarded events or
committed text to the correct XIC, and thus it is impossible to committed text to the correct XIC, and thus it is impossible to
look up which seat's TextInput resource an event is actually bound look up which seat's TextInput resource an event is actually bound
for. If one day we move to our own implementation of the XIC for. If one day we move to our own implementation of the XIM
protocol, then it will become possible to properly support protocol, then it will become possible to properly support
multi-seat setups, with one XIC per-client and per-seat. multi-seat setups, with one XIC per-client and per-seat.
@ -86,7 +86,7 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
converted string before being sent to the client. converted string before being sent to the client.
This code has many inherent race conditions, just like the This code has many inherent race conditions, just like the
zwp_text_input_v3 protocol itself. As described above, it only zwp_text_input_v3 protocol itself. And as described above, it only
supports one seat due to limitations of the Xlib XIM wrapper. */ supports one seat due to limitations of the Xlib XIM wrapper. */
typedef struct _TextInputClientInfo TextInputClientInfo; typedef struct _TextInputClientInfo TextInputClientInfo;

View file

@ -454,6 +454,7 @@
<entry name="not_constructed" value="1"/> <entry name="not_constructed" value="1"/>
<entry name="already_constructed" value="2"/> <entry name="already_constructed" value="2"/>
<entry name="unconfigured_buffer" value="3"/> <entry name="unconfigured_buffer" value="3"/>
<entry name="invalid_serial" value="4"/>
</enum> </enum>
<request name="destroy" type="destructor"> <request name="destroy" type="destructor">
@ -549,6 +550,17 @@
A client may send multiple ack_configure requests before committing, but A client may send multiple ack_configure requests before committing, but
only the last request sent before a commit indicates which configure only the last request sent before a commit indicates which configure
event the client really is responding to. event the client really is responding to.
Sending an ack_configure request consumes the serial number sent with
the request, as well as serial numbers sent by all configure events
sent on this xdg_surface prior to the configure event referenced by
the committed serial.
It is an error to issue multiple ack_configure requests referencing a
serial from the same configure event, or to issue an ack_configure
request referencing a serial from a configure event issued before the
event identified by the last ack_configure request for the same
xdg_surface. Doing so will raise an invalid_serial error.
</description> </description>
<arg name="serial" type="uint" summary="the serial from the configure event"/> <arg name="serial" type="uint" summary="the serial from the configure event"/>
</request> </request>

View file

@ -44,6 +44,7 @@ enum
StateMaybeConfigure = (1 << 6), StateMaybeConfigure = (1 << 6),
StateDirtyFrameExtents = (1 << 7), StateDirtyFrameExtents = (1 << 7),
StateTemporaryBounds = (1 << 8), StateTemporaryBounds = (1 << 8),
StateFrameStarted = (1 << 9),
}; };
typedef struct _XdgRole XdgRole; typedef struct _XdgRole XdgRole;
@ -106,6 +107,9 @@ struct _XdgRole
/* The implementation of this role. */ /* The implementation of this role. */
XdgRoleImplementation *impl; XdgRoleImplementation *impl;
/* The pending frame ID. */
uint64_t pending_frame;
/* Various role state. */ /* Various role state. */
int state; int state;
@ -123,7 +127,7 @@ struct _XdgRole
XdgState current_state; XdgState current_state;
/* Configure event serial. */ /* Configure event serial. */
uint32_t conf_serial; uint32_t conf_serial, last_specified_serial;
/* The current bounds of the subcompositor. */ /* The current bounds of the subcompositor. */
int min_x, min_y, max_x, max_y; int min_x, min_y, max_x, max_y;
@ -156,13 +160,16 @@ struct _ReleaseLaterRecord
/* A monotonically (overflow aside) increasing identifier. */ /* A monotonically (overflow aside) increasing identifier. */
uint64_t id; uint64_t id;
/* Key for the free func. */
void *free_func_key;
/* The buffer that should be released upon receiving this /* The buffer that should be released upon receiving this
message. */ message. */
ExtBuffer *buffer; ExtBuffer *buffer;
/* The idle callback. */
IdleCallbackKey key;
/* The XdgRole. */
XdgRole *role;
/* The next and last records. */ /* The next and last records. */
ReleaseLaterRecord *next, *last; ReleaseLaterRecord *next, *last;
}; };
@ -170,24 +177,6 @@ struct _ReleaseLaterRecord
/* Event base of the XShape extension. */ /* Event base of the XShape extension. */
int shape_base; int shape_base;
static void
RemoveRecord (ReleaseLaterRecord *record)
{
/* Removing the sentinel record is invalid. */
XLAssert (record->buffer != NULL);
/* First, make the rest of the list skip RECORD. */
record->last->next = record->next;
record->next->last = record->last;
/* Next, remove the buffer listener. */
XLBufferCancelRunOnFree (record->buffer,
record->free_func_key);
/* Finally, free RECORD. */
XLFree (record);
}
static void static void
DeleteRecord (ReleaseLaterRecord *record) DeleteRecord (ReleaseLaterRecord *record)
{ {
@ -217,10 +206,6 @@ FreeRecords (ReleaseLaterRecord *records)
/* Release the buffer now. */ /* Release the buffer now. */
XLReleaseBuffer (last->buffer); XLReleaseBuffer (last->buffer);
/* And cancel the destroy listener. */
XLBufferCancelRunOnFree (last->buffer,
last->free_func_key);
/* Before freeing the record itself. */ /* Before freeing the record itself. */
XLFree (last); XLFree (last);
} }
@ -311,24 +296,26 @@ FreeReconstrainCallbacks (XdgRole *role)
} }
} }
static void
ReleaseLaterExtBufferFunc (ExtBuffer *buffer, void *data)
{
DeleteRecord (data);
}
static void static void
RunFrameCallbacks (Surface *surface, XdgRole *role) RunFrameCallbacks (Surface *surface, XdgRole *role)
{ {
struct timespec time; struct timespec time;
uint64_t last_drawn_time;
/* Surface can be NULL for various reasons, especially events /* Surface can be NULL for various reasons, especially events
arriving after the shell surface is detached. */ arriving after the shell surface is detached. */
if (!surface) if (!surface)
return; return;
last_drawn_time = XLFrameClockGetFrameTime (role->clock);
if (!last_drawn_time)
{
clock_gettime (CLOCK_MONOTONIC, &time); clock_gettime (CLOCK_MONOTONIC, &time);
XLSurfaceRunFrameCallbacks (surface, time); XLSurfaceRunFrameCallbacks (surface, time);
}
else
XLSurfaceRunFrameCallbacksMs (surface, last_drawn_time / 1000);
} }
static void static void
@ -344,25 +331,18 @@ RunFrameCallbacksConditionally (XdgRole *role)
} }
static void static void
HandleReleaseLaterMessage (XdgRole *role, uint64_t id) BufferIdleCallback (RenderBuffer buffer, void *data)
{ {
ReleaseLaterRecord *record; ReleaseLaterRecord *record;
XdgRole *role;
Surface *surface; Surface *surface;
record = role->release_records->last; record = data;
role = record->role;
if (record == role->release_records)
return;
/* Since the list of release records is a (circular) queue, ID will
either be the last element or invalid as the buffer has been
destroyed. */
if (record->id == id)
{
XLReleaseBuffer (record->buffer); XLReleaseBuffer (record->buffer);
RemoveRecord (record); DeleteRecord (record);
}
surface = role->role.surface; surface = role->role.surface;
@ -377,70 +357,12 @@ HandleReleaseLaterMessage (XdgRole *role, uint64_t id)
} }
} }
/* It shouldn't be possible for billions of frames to pile up without
a response from the X server, so relying on wraparound to handle id
overflow should be fine. */
static void
ReleaseLater (XdgRole *role, ExtBuffer *buffer)
{
ReleaseLaterRecord *record;
XEvent event;
static uint64_t id;
/* Send a message to the X server; once it is received, release the
given buffer. This is necessary because the connection to the X
server does not behave synchronously, and receiving the message
tells us that the X server has finished processing all requests
that access the buffer. */
record = AddRecordAfter (role->release_records);
record->id = ++id;
record->buffer = buffer;
record->free_func_key
= XLBufferRunOnFree (buffer, ReleaseLaterExtBufferFunc,
record);
memset (&event, 0, sizeof event);
event.xclient.type = ClientMessage;
event.xclient.window = role->window;
event.xclient.message_type = _XL_BUFFER_RELEASE;
event.xclient.format = 32;
event.xclient.data.l[0] = id >> 31 >> 1;
event.xclient.data.l[1] = id & 0xffffffff;
XSendEvent (compositor.display, role->window, False,
NoEventMask, &event);
}
Bool Bool
XLHandleXEventForXdgSurfaces (XEvent *event) XLHandleXEventForXdgSurfaces (XEvent *event)
{ {
XdgRole *role; XdgRole *role;
uint64_t id, low, high;
Window window; Window window;
if (event->type == ClientMessage
&& event->xclient.message_type == _XL_BUFFER_RELEASE)
{
role = XLLookUpAssoc (surfaces, event->xclient.window);
if (role)
{
/* These values are masked because Xlib sign-extends 32-bit
values to fit potentially 64-bit long. */
low = event->xclient.data.l[0] & 0xffffffff;
high = event->xclient.data.l[1] & 0xffffffff;
id = (low << 32) | high;
HandleReleaseLaterMessage (role, id);
}
return True;
}
if (event->type == ClientMessage if (event->type == ClientMessage
&& ((event->xclient.message_type == _NET_WM_FRAME_DRAWN && ((event->xclient.message_type == _NET_WM_FRAME_DRAWN
|| event->xclient.message_type == _NET_WM_FRAME_TIMINGS) || event->xclient.message_type == _NET_WM_FRAME_TIMINGS)
@ -611,8 +533,29 @@ AckConfigure (struct wl_client *client, struct wl_resource *resource,
if (!xdg_role->role.surface) if (!xdg_role->role.surface)
return; return;
#ifdef DEBUG_GEOMETRY_CALCULATION
fprintf (stderr, "ack_configure: %"PRIu32"\n", serial);
#endif
if (serial < xdg_role->conf_serial)
{
/* The client specified an outdated serial. */
wl_resource_post_error (resource, XDG_SURFACE_ERROR_INVALID_SERIAL,
"serial specified not monotonic");
return;
}
if (serial && serial == xdg_role->last_specified_serial)
{
/* The client specified the same serial twice. */
wl_resource_post_error (resource, XDG_SURFACE_ERROR_INVALID_SERIAL,
"same serial specified twice");
return;
}
if (serial == xdg_role->conf_serial) if (serial == xdg_role->conf_serial)
{ {
xdg_role->last_specified_serial = serial;
xdg_role->state &= ~StateWaitingForAckConfigure; xdg_role->state &= ~StateWaitingForAckConfigure;
/* Garbage the subcompositor too, since contents could be /* Garbage the subcompositor too, since contents could be
@ -720,6 +663,12 @@ Commit (Surface *surface, Role *role)
if (!IsRoleMapped (xdg_role)) if (!IsRoleMapped (xdg_role))
goto start_drawing; goto start_drawing;
/* If the frame clock is frozen but we are no longer waiting for the
configure event to be acknowledged by the client, unfreeze the
frame clock. */
if (!(xdg_role->state & StateWaitingForAckConfigure))
Unfreeze (xdg_role);
/* A frame is already in progress, so instead say that an urgent /* A frame is already in progress, so instead say that an urgent
update is needed immediately after the frame completes. In any update is needed immediately after the frame completes. In any
case, don't run frame callbacks upon buffer release anymore. */ case, don't run frame callbacks upon buffer release anymore. */
@ -727,8 +676,9 @@ Commit (Surface *surface, Role *role)
{ {
if (XLFrameClockCanBatch (xdg_role->clock)) if (XLFrameClockCanBatch (xdg_role->clock))
/* But if we can squeeze the frame inside the vertical /* But if we can squeeze the frame inside the vertical
blanking period, go ahead. */ blanking period, or a frame is in progress but EndFrame has
goto just_draw_then_return; not yet been called, go ahead. */
goto start_drawing;
xdg_role->state |= StateLateFrame; xdg_role->state |= StateLateFrame;
xdg_role->state &= ~StatePendingFrameCallback; xdg_role->state &= ~StatePendingFrameCallback;
@ -743,38 +693,12 @@ Commit (Surface *surface, Role *role)
start_drawing: start_drawing:
/* If the frame clock is frozen but we are no longer waiting for the
configure event to be acknowledged by the client, unfreeze the
frame clock. */
if (!(xdg_role->state & StateWaitingForAckConfigure))
Unfreeze (xdg_role);
XLFrameClockStartFrame (xdg_role->clock, False);
SubcompositorUpdate (xdg_role->subcompositor); SubcompositorUpdate (xdg_role->subcompositor);
/* Also run role "commit inside frame" hook. */ /* Do not end frames explicitly. Instead, wait for the
if (xdg_role->impl && xdg_role->impl->funcs.commit_inside_frame) NoteFrameCallback to end the frame. */
xdg_role->impl->funcs.commit_inside_frame (role, xdg_role->impl);
XLFrameClockEndFrame (xdg_role->clock);
/* Clients which commit when a configure event that has not yet been
acked still expect frame callbacks to be called; however, frame
callbacks are not provided by the frame clock while it is frozen.
If that happens, just run the frame callback immediately. */
if (XLFrameClockIsFrozen (xdg_role->clock)
/* If the window is not mapped, then the native frame clock will
not draw frames. Some clients do commit before the initial
configure event and wait for the frame callback to be called
after or before ack_configure, leading to the mapping commit
never being performed. */
|| !IsRoleMapped (xdg_role))
RunFrameCallbacksConditionally (xdg_role);
return; return;
just_draw_then_return:
SubcompositorUpdate (xdg_role->subcompositor);
} }
static Bool static Bool
@ -887,7 +811,27 @@ Teardown (Surface *surface, Role *role)
static void static void
ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer) ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer)
{ {
ReleaseLater (XdgRoleFromRole (role), buffer); RenderBuffer render_buffer;
ReleaseLaterRecord *record;
XdgRole *xdg_role;
render_buffer = XLRenderBufferFromBuffer (buffer);
xdg_role = XdgRoleFromRole (role);
if (RenderIsBufferIdle (render_buffer, xdg_role->target))
/* If the buffer is already idle, release it now. */
XLReleaseBuffer (buffer);
else
{
/* Release the buffer once it is destroyed or becomes idle. */
record = AddRecordAfter (xdg_role->release_records);
record->buffer = buffer;
record->key = RenderAddIdleCallback (render_buffer,
xdg_role->target,
BufferIdleCallback,
record);
record->role = xdg_role;
}
} }
static Bool static Bool
@ -915,7 +859,8 @@ Subframe (Surface *surface, Role *role)
{ {
if (XLFrameClockCanBatch (xdg_role->clock)) if (XLFrameClockCanBatch (xdg_role->clock))
/* But if we can squeeze the frame inside the vertical /* But if we can squeeze the frame inside the vertical
blanking period, go ahead. */ blanking period, or a frame is in progress but EndFrame has
not yet been called, go ahead. */
return True; return True;
xdg_role->state |= StateLateFrame; xdg_role->state |= StateLateFrame;
@ -927,18 +872,14 @@ Subframe (Surface *surface, Role *role)
return False; return False;
} }
/* I guess subsurface updates don't count as urgent frames? */
XLFrameClockStartFrame (xdg_role->clock, False);
return True; return True;
} }
static void static void
EndSubframe (Surface *surface, Role *role) EndSubframe (Surface *surface, Role *role)
{ {
XdgRole *xdg_role; /* Don't end the frame here; instead, wait for the frame callback to
note that drawing the frame has finished. */
xdg_role = XdgRoleFromRole (role);
XLFrameClockEndFrame (xdg_role->clock);
} }
static Window static Window
@ -969,34 +910,6 @@ AfterFrame (FrameClock *clock, void *data)
role = data; role = data;
if (role->state & StateLateFrame)
{
role->state &= ~StateLateFrame;
if (role->state & StateLateFrameAcked)
XLFrameClockUnfreeze (role->clock);
/* Since we are running late, make the compositor draw the frame
now. */
XLFrameClockStartFrame (clock, True);
SubcompositorUpdate (role->subcompositor);
/* Also run role "commit inside frame" hook. */
if (role->impl
&& role->impl->funcs.commit_inside_frame)
role->impl->funcs.commit_inside_frame (&role->role,
role->impl);
XLFrameClockEndFrame (clock);
/* See the comment in Commit about frame callbacks being
attached while the frame clock is frozen. */
if (XLFrameClockIsFrozen (role->clock)
&& role->role.surface)
RunFrameCallbacksConditionally (role);
return;
}
/* If all pending frames have been drawn, run frame callbacks. /* If all pending frames have been drawn, run frame callbacks.
Unless some buffers have not yet been released, in which case the Unless some buffers have not yet been released, in which case the
callbacks will be run when they are. */ callbacks will be run when they are. */
@ -1234,6 +1147,63 @@ NoteBounds (void *data, int min_x, int min_y,
RunReconstrainCallbacks (role); RunReconstrainCallbacks (role);
} }
static void
NoteFrame (FrameMode mode, uint64_t id, void *data)
{
XdgRole *role;
role = data;
switch (mode)
{
case ModeStarted:
/* Record this frame counter as the pending frame. */
role->pending_frame = id;
if (!(role->state & StateFrameStarted))
{
role->state |= StateFrameStarted;
XLFrameClockStartFrame (role->clock, False);
}
/* Also run role "commit inside frame" hook. */
if (role->impl && role->impl->funcs.commit_inside_frame)
role->impl->funcs.commit_inside_frame (&role->role,
role->impl);
break;
case ModePresented:
case ModeComplete:
/* The frame was completed. */
if (id == role->pending_frame)
{
/* End the frame. */
XLFrameClockEndFrame (role->clock);
/* Clear the frame completed flag. */
role->state &= ~StateFrameStarted;
/* Clients which commit when a configure event that has not
yet been acked still expect frame callbacks to be called;
however, frame callbacks are not provided by the frame
clock while it is frozen.
If that happens, just run the frame callback
immediately. */
if (XLFrameClockIsFrozen (role->clock)
/* If the window is not mapped, then the native frame
clock will not draw frames. Some clients do commit
before the initial configure event and wait for the
frame callback to be called after or before
ack_configure, leading to the mapping commit never
being performed. */
|| !IsRoleMapped (role))
RunFrameCallbacksConditionally (role);
}
}
}
static void static void
ResizeForMap (XdgRole *role) ResizeForMap (XdgRole *role)
{ {
@ -1387,6 +1357,10 @@ SelectExtraEvents (Surface *surface, Role *role,
/* Select extra events for the input method. */ /* Select extra events for the input method. */
XSelectInput (compositor.display, xdg_role->window, XSelectInput (compositor.display, xdg_role->window,
DefaultEventMask | event_mask); DefaultEventMask | event_mask);
/* Set the target standard event mask. */
RenderSetStandardEventMask (xdg_role->target,
DefaultEventMask | event_mask);
} }
void void
@ -1482,7 +1456,7 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
0, 0, 20, 20, 0, compositor.n_planes, 0, 0, 20, 20, 0, compositor.n_planes,
InputOutput, compositor.visual, flags, InputOutput, compositor.visual, flags,
&attrs); &attrs);
role->target = RenderTargetFromWindow (role->window); role->target = RenderTargetFromWindow (role->window, DefaultEventMask);
role->subcompositor = MakeSubcompositor (); role->subcompositor = MakeSubcompositor ();
role->clock = XLMakeFrameClockForWindow (role->window); role->clock = XLMakeFrameClockForWindow (role->window);
@ -1496,6 +1470,8 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
OpaqueRegionChanged, role); OpaqueRegionChanged, role);
SubcompositorSetBoundsCallback (role->subcompositor, SubcompositorSetBoundsCallback (role->subcompositor,
NoteBounds, role); NoteBounds, role);
SubcompositorSetNoteFrameCallback (role->subcompositor,
NoteFrame, role);
XLSelectStandardEvents (role->window); XLSelectStandardEvents (role->window);
XLMakeAssoc (surfaces, role->window, role); XLMakeAssoc (surfaces, role->window, role);
@ -1581,7 +1557,8 @@ XLXdgRoleSendConfigure (Role *role, uint32_t serial)
xdg_role->state &= ~StateMaybeConfigure; xdg_role->state &= ~StateMaybeConfigure;
#ifdef DEBUG_GEOMETRY_CALCULATION #ifdef DEBUG_GEOMETRY_CALCULATION
fprintf (stderr, "Waiting for ack_configure...\n"); fprintf (stderr, "Waiting for ack_configure (%"PRIu32")...\n",
xdg_role->conf_serial);
#endif #endif
xdg_surface_send_configure (role->resource, serial); xdg_surface_send_configure (role->resource, serial);