Add support for EGL, for YUV image formats

EGL support is still incomplete; stuff like scaling is missing.

* 12to11.c (PickVisual, XLMain): Remove function.  Initialize
renderers, and rely on that to initialize the visuals.
* 12to11.man: Document new environment variables.
* Imakefile (SRCS, OBJS): Add picture_renderer.c and renderer.c
and their associated objects.
(GENHEADERS): New variable for generated headers.
(EGL_SRCS, EGL_OBJS): New variables.
(LOCAL_LIBRARIES, SRCS, OBJS, DEFINES): [HaveEglSupport]: Add
EGL defines, libraries, and objects.:(shaders.h): New rule.
(depend, cleandir): Depend on generated headers and SRCs.
$($(OBJS)): Depend on $(GENHEADERS).
* README: Write how to build with EGL support and update content
descriptions.
* buffer.c (XLPictureFromBuffer): Delete function.
(XLRenderBufferFromBuffer): New function.
(XLPixmapFromBuffer): Delete function.
* compositor.h (enum _Operation, struct _SharedMemoryAttributes)
(struct _DmaBufAttributes, union _RenderTarget)
(union _RenderBuffer, struct _RenderFuncs, struct _DrmFormat)
(struct _ShmFormat, struct _BufferFuncs, struct
_ExtBufferFuncs): New structures.
(Fallthrough): New define.  Define to __attribute__
((fallthrough)) wherever supported.  Replace uses of Picture
with RenderBuffer.
* dmabuf.c (struct _DrmFormatInfo): Remove structures.
(struct _BufferParams): Remove link.
(struct _Buffer): Replace pixmap and picture with render buffer
object.
(dri3_opcode, all_formats, pending_success, next_roundtrip_id)
(round_trip_window): Delete.
(ReleaseBufferParams, HandleParamsResourceDestroy, ForceRoundTrip)
(DepthForFormat, XLHandleErrorForDmabuf, PictFormatForFormat)
(DestroyBacking, GetPictureFunc, GetPixmapFunc, GetBufferFunc)
(CreateBufferFor, ModifierHigh, FinishBufferCreation)
(XLHandleOneXEventForDmabuf, CreateHeader, Create, CreateImmed)
(FindFormatMatching, FindSupportedModifiers, FindSupportedFormats)
(SendSupportedFormats, HandleBind, InitDrmDevice, WriteFormatTable)
(ReallyInitDmabuf, ReadSupportedFormats, XLInitDmabuf): Remove
various functions and reimplement as a wrapper around the
renderer abstraction, instead of DRI3.
* fns.c (struct _Busfault): New structure.
(busfault_tree, bus_handler_installed): New variables.
(GetHeight, FixHeights, RotateLeft, RotateRight, RebalanceBusfault)
(RecordBusfault, DetectBusfault, DeleteMin, RemoveBusfault)
(HandleBusfault, MaybeInstallBusHandler, BlockSigbus)
(UnblockSigbus, XLRecordBusfault, XLRemoveBusfaults): New
functions to store non-overlapping regions of mapped memory in
an AVL tree, and to ignore SIGBUS events that trap within.
* icon_surface.c (struct _IconSurface): Replace picture with
rendering target.
(ReleaseBacking, XLGetIconSurface): Use rendering targets
instead of pictures.
* libraries.def: Add defines for EGL.
* run.c (HandleOneXEvent): Replace old dmabuf code with renderer
code.
* seat.c (CursorFromRole, PictureForCursor, ApplyCursor)
(UpdateCursorFromSubcompositor, Subframe, EndSubframe): Rewrite
cursor code to use rendering targets and not pictures.
* shm.c (Buffer, DereferencePool): Remove SIGBUS protection.
(DereferenceBuffer): Reimplement in terms of renderer functions.
(GetPictureFunc): Delete function.
(GetBufferFunc): New function.
(GetPixmapFunc): Delete function.
(PrintBuffer): Remove now-broken implementation.
(DepthForFormat, PictFormatForFormat): Delete functions, as they
are now part of the rendering backend.
(IsFormatSupported): New function.
(CreateBuffer): Reimplement in terms of renderer functions.
(ResizePool): Change SIGBUS protection for resize.
(CreatePool): Protect buffer data from SIGBUS should a client
truncate the file.
(PostFormats): Reimplement in terms of RenderGetShmFormats.
(XLInitShm): Stop initializing MIT-SHM.
* subcompositor.c (IsTargetAttached): New flag and associated
macros.
(struct _View): Remove useless field.
(struct _Subcompositor): Change target to RenderTarget.  Add
fields for prior damage.
(MakeSubcompositor): Initialize new fields.
(SubcompositorSetTarget): Accept RenderTarget, not picture, and
set target attached flag.
(ViewGetTransform): Delete function.
(ViewApplyTransform): New function.
(FillBoxesWithTransparency): Reimplement in terms of renderer
functions.
(StorePreviousDamage): New function.
(SubcompositorUpdate): Reimplement in terms of renderer
functions.  Also, learn to deal with situations where the buffer
reflects the state 1 to 2 swaps ago.
(SubcompositorExpose): Reimplement in terms of renderer
functions.
(SubcompositorFree): Free new fields.
(SubcompositorInit): Remove no longer required initialization of
identity matrix.
* xdata.c (ReceiveBody): Fix coding style.
* xdg_surface.c (struct _XdgRole, ReleaseBacking,
XLGetXdgSurface): Switch from pictures to RenderTargets.
* xerror.c (ErrorHandler): Handle errors for picture renderer
instead of dmabuf.c.
This commit is contained in:
oldosfan 2022-09-23 08:44:37 +00:00
parent 970b60268f
commit b4ee06589e
17 changed files with 1606 additions and 1181 deletions

View file

@ -28,35 +28,6 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
/* Globals. */
Compositor compositor;
static Visual *
PickVisual (int *depth)
{
int n_visuals;
XVisualInfo vinfo, *visuals;
Visual *selection;
vinfo.screen = DefaultScreen (compositor.display);
vinfo.class = TrueColor;
vinfo.depth = 32;
visuals = XGetVisualInfo (compositor.display, (VisualScreenMask
| VisualClassMask
| VisualDepthMask),
&vinfo, &n_visuals);
if (n_visuals)
{
selection = visuals[0].visual;
*depth = visuals[0].depth;
XFree (visuals);
return selection;
}
fprintf (stderr, "A 32-bit TrueColor visual was not found\n");
exit (1);
}
static Colormap
MakeColormap (void)
{
@ -129,8 +100,6 @@ XLMain (int argc, char **argv)
compositor.wl_socket = socket;
compositor.wl_event_loop
= wl_display_get_event_loop (wl_display);
compositor.visual = PickVisual (&compositor.n_planes);
compositor.colormap = MakeColormap ();
InitXErrors ();
SubcompositorInit ();
@ -138,6 +107,15 @@ XLMain (int argc, char **argv)
XLInitTimers ();
XLInitAtoms ();
/* Initialize renderers immediately after timers and atoms are set
up. */
InitRenderers ();
/* Set up the colormap. Initializing renderers should also cause
the visual to be set. */
compositor.colormap = MakeColormap ();
XLInitRROutputs ();
XLInitCompositor ();
XLInitSurfaces ();

View file

@ -67,6 +67,18 @@ variable, if set, forces ConfigureNotify events from the window
manager to be handled directly, without waiting some time for a
corresponding _NET_WM_STATE event to arrive. This is only
useful for debugging the window resizing logic.
.PP
The
.B RENDERER
variable, if set, controls the rendering backend used by the
protocol translator. When set to
.I help
it prints a list of available rendering backends instead.
.PP
The
.B RENDER_VISUAL
variable, if set to a number, contains the ID of the visual used
when the EGL rendering backend is in use.
.SH BUGS
There is a hard to catch bug where Wayland programs leaving the
fullscreen or maximized state may abruptly return to their maximized

View file

@ -22,7 +22,8 @@ SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c \
frame_clock.c xerror.c ewmh.c timer.c subsurface.c seat.c \
data_device.c xdg_popup.c linux-dmabuf-unstable-v1.c dmabuf.c \
buffer.c select.c xdata.c xsettings.c dnd.c icon_surface.c \
primary-selection-unstable-v1.c primary_selection.c
primary-selection-unstable-v1.c primary_selection.c \
renderer.c picture_renderer.c
OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
xdg-shell.o surface.o region.o shm.o atoms.o subcompositor.o \
@ -30,7 +31,32 @@ OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
frame_clock.o xerror.o ewmh.o timer.o subsurface.o seat.o \
data_device.o xdg_popup.o linux-dmabuf-unstable-v1.o dmabuf.o \
buffer.o select.o xdata.o xsettings.o dnd.o icon_surface.o \
primary-selection-unstable-v1.o primary_selection.o
primary-selection-unstable-v1.o primary_selection.o \
renderer.o picture_renderer.o
GENHEADERS = transfer_atoms.h primary-selection-unstable-v1.h \
linux-dmabuf-unstable-v1.h xdg-shell.h
#ifdef HaveEglSupport
EGL_SRCS = egl.c
EGL_OBJS = egl.o
LOCAL_LIBRARIES ::= $(LOCAL_LIBRARIES) $(EGL) $(GLES)
SRCS ::= $(SRCS) $(EGL_SRCS)
OBJS ::= $(OBJS) $(EGL_OBJS)
DEFINES ::= $(DEFINES) -DHaveEglSupport
shaders.h: shaders.txt shaders.awk
awk -f shaders.awk shaders.txt > $@
depend:: shaders.h
$(EGL_OBJS): shaders.h
cleandir::
$(RM) shaders.h
#endif
OPTIMIZE = -O0
ANALYZE = -fanalyzer
@ -68,8 +94,11 @@ transfer_atoms.h: short_types.txt mime0.awk mime1.awk mime2.awk mime3.awk \
awk -f mime3.awk short_types.txt >> $@
awk -f mime4.awk short_types.txt >> $@
$(OBJS): transfer_atoms.h primary-selection-unstable-v1.h \
linux-dmabuf-unstable-v1.h xdg-shell.h
$(OBJS): $(GENHEADERS)
/* depend somehow does not depend on SRCS, even though some of OBJS
are generated. */
depend:: $(GENHEADERS) $(SRCS)
linux-dmabuf-unstable-v1.h: linux-dmabuf-unstable-v1.xml
$(WAYLAND_SCANNER) server-header $< $@

24
README
View file

@ -31,6 +31,27 @@ extensions:
In addition, it requires Xlib to be built with the XCB transport, and
the XCB bindings for MIT-SHM and DRI3 to be available.
Sometimes, it might be desirable to build with EGL, and use OpenGL ES
2.0 for i.e. YUV video format support. To do so, uncomment the block
of code for EGL support in libraries.def before running `xmkmf'. This
will additionally require the EGL and GLESv2 development files, and
for the following EGL and GLES extensions to be present at runtime:
EGL_EXT_platform_base
EGL_EXT_device_query
EGL_KHR_image_base
EGL_EXT_image_dma_buf_import_modifiers
EGL_EXT_image_dma_buf_import
EGL_EXT_buffer_age
GL_OES_EGL_image
GL_OES_EGL_image_external
GL_EXT_read_format_bgra
GL_EXT_unpack_subimage
After building with EGL support, the renderer must be enabled by
setting the environment variable "RENDERER" to "egl".
The following Wayland protocols are implemented to a more-or-less
complete degree:
@ -58,6 +79,9 @@ This directory is organized as follows:
libraries.def - files for libraries that don't provide Imakefiles
*.xml - Wayland protocol definition source
*.c, *.h - C source code
*.awk - scripts used to generate headers
*.txt - text data used to generate some headers, i.e.
those containing MIME types or shaders
Building the source code is simple, provided that you have the
necessary libwayland-server library, wayland-scanner, pixman, XCB, and

View file

@ -30,7 +30,6 @@ struct _DestroyListener
void *data;
};
void
XLRetainBuffer (ExtBuffer *buffer)
{
@ -43,16 +42,10 @@ XLDereferenceBuffer (ExtBuffer *buffer)
buffer->funcs.dereference (buffer);
}
Picture
XLPictureFromBuffer (ExtBuffer *buffer)
RenderBuffer
XLRenderBufferFromBuffer (ExtBuffer *buffer)
{
return buffer->funcs.get_picture (buffer);
}
Pixmap
XLPixmapFromBuffer (ExtBuffer *buffer)
{
return buffer->funcs.get_pixmap (buffer);
return buffer->funcs.get_buffer (buffer);
}
unsigned int

View file

@ -94,6 +94,271 @@ typedef struct _PDataSource PDataSource;
extern Compositor compositor;
/* Defined in renderer.c. */
typedef struct _RenderFuncs RenderFuncs;
typedef struct _BufferFuncs BufferFuncs;
typedef union _RenderTarget RenderTarget;
typedef union _RenderBuffer RenderBuffer;
typedef struct _DmaBufAttributes DmaBufAttributes;
typedef struct _SharedMemoryAttributes SharedMemoryAttributes;
typedef struct _DrmFormat DrmFormat;
typedef struct _ShmFormat ShmFormat;
typedef enum _Operation Operation;
typedef void (*DmaBufSuccessFunc) (RenderBuffer, void *);
typedef void (*DmaBufFailureFunc) (void *);
enum _Operation
{
OperationOver,
OperationSource,
};
struct _SharedMemoryAttributes
{
/* The format of the buffer. */
uint32_t format;
/* The offset, width, height, and stride of the buffer. */
int32_t offset, width, height, stride;
/* The pool file descriptor. */
int fd;
/* Pointer to a pointer to the pool data. */
void **data;
/* Size of the pool. */
size_t pool_size;
};
struct _DmaBufAttributes
{
/* The file descriptors. They should be closed by the time the
callback returns. */
int fds[4];
/* The modifier. */
uint64_t modifier;
/* Strides. */
unsigned int strides[4];
/* Offsets. */
unsigned int offsets[4];
/* The number of planes set. */
int n_planes;
/* The width and height of the buffer. */
int width, height;
/* Flags. */
int flags;
/* The DRM format of the buffer. */
uint32_t drm_format;
};
union _RenderTarget
{
/* The XID of the target resource, if that is what it is. */
XID xid;
/* The pointer to the target, if that is what it is. */
void *pointer;
};
union _RenderBuffer
{
/* The XID of the buffer resource, if that is what it is. */
XID xid;
/* The pointer to the buffer, if that is what it is. */
void *pointer;
};
enum
{
/* The render target always preserves previously drawn contents;
IOW, target_age always returns 0. */
NeverAges = 1,
};
struct _RenderFuncs
{
/* Initialize the visual and depth. */
Bool (*init_render_funcs) (void);
/* Create a rendering target for the given window. */
RenderTarget (*target_from_window) (Window);
/* Create a rendering target for the given pixmap. */
RenderTarget (*target_from_pixmap) (Pixmap);
/* Set the target width and height. This can be NULL. */
void (*note_target_size) (RenderTarget, int, int);
/* Get an XRender Picture from the given rendering target. The
picture should only be used to create a cursor, and must be
destroyed by calling FreePictureFromTarget immediately
afterwards. */
Picture (*picture_from_target) (RenderTarget);
/* Free a picture created that way. */
void (*free_picture_from_target) (Picture);
/* Destroy the given rendering target. */
void (*destroy_render_target) (RenderTarget);
/* Begin rendering. This can be NULL, but if not, must be called
before any drawing operations. */
void (*start_render) (RenderTarget);
/* Fill the target with transparency in the given rectangles. */
void (*fill_boxes_with_transparency) (RenderTarget, pixman_box32_t *, int,
int, int);
/* Clear the given rectangle. */
void (*clear_rectangle) (RenderTarget, int, int, int, int);
/* Apply a projective transform to the given buffer. The first
argument is a scale factor. */
void (*apply_transform) (RenderBuffer, double);
/* Composite width, height, from the given buffer onto the given
target, at x, y. The arguments are: buffer, target, operation,
source_x, source_y, x, y, width, height. */
void (*composite) (RenderBuffer, RenderTarget, Operation, int, int,
int, int, int, int);
/* Reset the transform for the given buffer. */
void (*reset_transform) (RenderBuffer);
/* Finish rendering, and swap changes to display. May be NULL. */
void (*finish_render) (RenderTarget);
/* Return the age of the target. Value is a number not less than
-1, describing the "age" of the contents of the target.
-1 means the buffer contains no valid contents, and must be
redrawn from scratch. 0 means the buffer contains the contents
at the time of the last call to `finish_render', 1 means the
buffer contains the contents at the time of the second last
call to `finish_render', and so on.
Note that when a render target is first created, the renderer
may chose to return 0 instead of 1. */
int (*target_age) (RenderTarget);
/* Some flags. NeverAges means targets always preserve contents
that were previously drawn. */
int flags;
};
struct _DrmFormat
{
/* The supported DRM format. */
uint32_t drm_format;
/* The supported modifier. */
uint64_t drm_modifier;
/* Backend specific flags associated with this DRM format. */
int flags;
};
struct _ShmFormat
{
/* The Wayland type code of the format. */
uint32_t format;
};
struct _BufferFuncs
{
/* Get DRM formats and modifiers supported by this renderer. Return
0 formats if nothing is supported. */
DrmFormat *(*get_drm_formats) (int *);
/* Get the DRM device node. */
dev_t (*get_render_device) (Bool *);
/* Get SHM formats supported by this renderer. */
ShmFormat *(*get_shm_formats) (int *);
/* Create a buffer from the given dma-buf attributes. */
RenderBuffer (*buffer_from_dma_buf) (DmaBufAttributes *, Bool *);
/* Create a buffer from the given dma-buf attributes
asynchronously. */
void (*buffer_from_dma_buf_async) (DmaBufAttributes *, DmaBufSuccessFunc,
DmaBufFailureFunc, void *);
/* Create a buffer from the given shared memory attributes. */
RenderBuffer (*buffer_from_shm) (SharedMemoryAttributes *, Bool *);
/* Validate the shared memory attributes passed as args. Return
false if they are not valid. */
Bool (*validate_shm_params) (uint32_t, uint32_t, uint32_t, int32_t,
int32_t, size_t);
/* Free a buffer created from shared memory. */
void (*free_shm_buffer) (RenderBuffer);
/* Free a dma-buf buffer. */
void (*free_dmabuf_buffer) (RenderBuffer);
/* Notice that the given buffer has been damaged. May be NULL. If
the given NULL damage, assume that the entire buffer has been
damaged. Must be called at least once before any rendering can
be performed on the buffer. */
void (*update_buffer_for_damage) (RenderBuffer, pixman_region32_t *);
/* Called during renderer initialization. */
void (*init_buffer_funcs) (void);
};
extern int renderer_flags;
extern void RegisterStaticRenderer (const char *, RenderFuncs *,
BufferFuncs *);
extern void InitRenderers (void);
extern RenderTarget RenderTargetFromWindow (Window);
extern RenderTarget RenderTargetFromPixmap (Pixmap);
extern void RenderNoteTargetSize (RenderTarget, int, int);
extern Picture RenderPictureFromTarget (RenderTarget);
extern void RenderFreePictureFromTarget (Picture);
extern void RenderDestroyRenderTarget (RenderTarget);
extern void RenderStartRender (RenderTarget);
extern void RenderFillBoxesWithTransparency (RenderTarget, pixman_box32_t *,
int, int, int);
extern void RenderClearRectangle (RenderTarget, int, int, int, int);
extern void RenderApplyTransform (RenderBuffer, double);
extern void RenderComposite (RenderBuffer, RenderTarget, Operation, int,
int, int, int, int, int);
extern void RenderResetTransform (RenderBuffer);
extern void RenderFinishRender (RenderTarget);
extern int RenderTargetAge (RenderTarget);
extern DrmFormat *RenderGetDrmFormats (int *);
extern dev_t RenderGetRenderDevice (Bool *);
extern ShmFormat *RenderGetShmFormats (int *);
extern RenderBuffer RenderBufferFromDmaBuf (DmaBufAttributes *, Bool *);
extern void RenderBufferFromDmaBufAsync (DmaBufAttributes *, DmaBufSuccessFunc,
DmaBufFailureFunc, void *);
extern RenderBuffer RenderBufferFromShm (SharedMemoryAttributes *, Bool *);
extern Bool RenderValidateShmParams (uint32_t, uint32_t, uint32_t, int32_t,
int32_t, size_t);
extern void RenderFreeShmBuffer (RenderBuffer);
extern void RenderFreeDmabufBuffer (RenderBuffer);
extern void RenderUpdateBufferForDamage (RenderBuffer, pixman_region32_t *);
/* Defined in run.c. */
typedef struct _PollFd WriteFd;
@ -189,6 +454,9 @@ extern Time XLGetServerTimeRoundtrip (void);
extern RootWindowSelection *XLSelectInputFromRootWindow (unsigned long);
extern void XLDeselectInputFromRootWindow (RootWindowSelection *);
extern void XLRecordBusfault (void *, size_t);
extern void XLRemoveBusfault (void *);
/* Defined in compositor.c. */
extern void XLInitCompositor (void);
@ -209,8 +477,7 @@ struct _ExtBufferFuncs
{
void (*retain) (ExtBuffer *);
void (*dereference) (ExtBuffer *);
Picture (*get_picture) (ExtBuffer *);
Pixmap (*get_pixmap) (ExtBuffer *);
RenderBuffer (*get_buffer) (ExtBuffer *);
unsigned int (*width) (ExtBuffer *);
unsigned int (*height) (ExtBuffer *);
void (*release) (ExtBuffer *);
@ -228,8 +495,7 @@ struct _ExtBuffer
extern void XLRetainBuffer (ExtBuffer *);
extern void XLDereferenceBuffer (ExtBuffer *);
extern Picture XLPictureFromBuffer (ExtBuffer *);
extern Pixmap XLPixmapFromBuffer (ExtBuffer *);
extern RenderBuffer XLRenderBufferFromBuffer (ExtBuffer *);
extern unsigned int XLBufferWidth (ExtBuffer *);
extern unsigned int XLBufferHeight (ExtBuffer *);
extern void XLReleaseBuffer (ExtBuffer *);
@ -255,7 +521,7 @@ extern void SubcompositorInit (void);
extern Subcompositor *MakeSubcompositor (void);
extern View *MakeView (void);
extern void SubcompositorSetTarget (Subcompositor *, Picture);
extern void SubcompositorSetTarget (Subcompositor *, RenderTarget *);
extern void SubcompositorInsert (Subcompositor *, View *);
extern void SubcompositorInsertBefore (Subcompositor *, View *, View *);
extern void SubcompositorInsertAfter (Subcompositor *, View *, View *);
@ -883,8 +1149,6 @@ extern Cursor InitDefaultCursor (void);
/* Defined in dmabuf.c. */
extern void XLInitDmabuf (void);
extern Bool XLHandleErrorForDmabuf (XErrorEvent *);
extern Bool XLHandleOneXEventForDmabuf (XEvent *);
/* Defined in select.c. */
@ -985,6 +1249,20 @@ extern Bool XLPDataSourceHasTarget (PDataSource *, const char *);
extern int XLPDataSourceTargetCount (PDataSource *);
extern void XLPDataSourceGetTargets (PDataSource *, Atom *);
/* Defined in picture_renderer.c. */
extern Bool HandleErrorForPictureRenderer (XErrorEvent *);
extern Bool HandleOneXEventForPictureRenderer (XEvent *);
extern void InitPictureRenderer (void);
#ifdef HaveEglSupport
/* Defined in egl.c. */
extern void InitEgl (void);
#endif
/* Utility functions that don't belong in a specific file. */
#define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0])
@ -999,6 +1277,12 @@ extern void XLPDataSourceGetTargets (PDataSource *, Atom *);
#define SafeCmp(n1, n2) (((n1) > (n2)) - ((n1) < (n2)))
#if __GNUC__ >= 7
#define Fallthrough __attribute__ ((fallthrough))
#else
#define Fallthrough ((void) 0)
#endif
/* Taken from intprops.h in gnulib. */
/* True if the real type T is signed. */

1157
dmabuf.c

File diff suppressed because it is too large Load diff

326
fns.c
View file

@ -22,13 +22,17 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
#include <sys/stat.h>
#include <sys/errno.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "compositor.h"
typedef struct _Busfault Busfault;
struct _RootWindowSelection
{
/* The next and last event selection records in this chain. */
@ -38,9 +42,30 @@ struct _RootWindowSelection
unsigned long event_mask;
};
struct _Busfault
{
/* Nodes to the left and right. */
Busfault *left, *right;
/* Start of the ignored area. */
char *data;
/* Size of the ignored area. */
size_t ignored_area;
/* Height of this node. */
int height;
};
/* Events that are being selected for on the root window. */
static RootWindowSelection root_window_events;
/* All busfaults. */
static Busfault *busfault_tree;
/* Whether or not the SIGBUS handler has been installed. */
static Bool bus_handler_installed;
void
XLListFree (XLList *list, void (*item_func) (void *))
{
@ -473,3 +498,304 @@ XLDeselectInputFromRootWindow (RootWindowSelection *key)
XLFree (key);
ReselectRootWindowInput ();
}
static int
GetHeight (Busfault *busfault)
{
if (!busfault)
return 0;
return busfault->height;
}
static void
FixHeights (Busfault *busfault)
{
XLAssert (busfault != NULL);
busfault->height = 1 + MAX (GetHeight (busfault->left),
GetHeight (busfault->right));
}
static void
RotateLeft (Busfault **root)
{
Busfault *old_root, *new_root, *old_middle;
/* Rotate *root->left to *root. */
old_root = *root;
new_root = old_root->left;
old_middle = new_root->right;
old_root->left = old_middle;
new_root->right = old_root;
*root = new_root;
/* Update heights. */
FixHeights ((*root)->right);
FixHeights (*root);
}
static void
RotateRight (Busfault **root)
{
Busfault *old_root, *new_root, *old_middle;
/* Rotate *root->right to *root. */
old_root = *root;
new_root = old_root->right;
old_middle = new_root->left;
old_root->right = old_middle;
new_root->left = old_root;
*root = new_root;
/* Update heights. */
FixHeights ((*root)->left);
FixHeights (*root);
}
static void
RebalanceBusfault (Busfault **tree)
{
if (*tree)
{
/* Check the left. It could be too tall. */
if (GetHeight ((*tree)->left)
> GetHeight ((*tree)->right) + 1)
{
/* The left side is unbalanced. Look for a taller
grandchild of tree->left. */
if (GetHeight ((*tree)->left->left)
> GetHeight ((*tree)->left->right))
RotateLeft (tree);
else
{
RotateRight (&(*tree)->left);
RotateLeft (tree);
}
return;
}
/* Then, check the right. */
else if (GetHeight ((*tree)->right)
> GetHeight ((*tree)->left) + 1)
{
/* The right side is unbalanced. Look for a taller
grandchild of tree->right. */
if (GetHeight ((*tree)->right->right)
> GetHeight ((*tree)->right->left))
RotateRight (tree);
else
{
RotateLeft (&(*tree)->right);
RotateRight (tree);
}
return;
}
/* Nothing was called, so fix heights. */
FixHeights (*tree);
}
}
static void
RecordBusfault (Busfault **tree, Busfault *node)
{
if (!*tree)
{
/* Tree is empty. Replace it with node. */
*tree = node;
node->left = NULL;
node->right = NULL;
node->height = 1;
return;
}
/* Record busfault into the correct subtree. */
if (node->data > (*tree)->data)
RecordBusfault (&(*tree)->right, node);
else
RecordBusfault (&(*tree)->left, node);
/* Balance the tree. */
RebalanceBusfault (tree);
}
static Busfault *
DetectBusfault (Busfault *tree, char *address)
{
/* This function is reentrant, but the functions that remove and
insert busfaults are not. */
if (!tree)
return NULL;
else if (address >= tree->data
&& address < tree->data + tree->ignored_area)
return tree;
/* Continue searching in the tree. */
if (address > tree->data)
/* Search in the right node. */
return DetectBusfault (tree->right, address);
/* Search in the left. */
return DetectBusfault (tree->left, address);
}
static void
DeleteMin (Busfault **tree, Busfault *out)
{
Busfault *old_root;
/* Delete and return the minimum value of the tree. */
XLAssert (*tree != NULL);
if (!(*tree)->left)
{
/* *tree contains the smallest value. */
old_root = *tree;
out->data = old_root->data;
out->ignored_area = old_root->ignored_area;
*tree = old_root->right;
XLFree (old_root);
}
else
/* Keep looking to the left. */
DeleteMin (&(*tree)->left, out);
RebalanceBusfault (tree);
}
static void
RemoveBusfault (Busfault **tree, char *data)
{
Busfault *old_root;
if (!*tree)
/* There should always be a busfault. */
abort ();
else if ((*tree)->data == data)
{
if ((*tree)->right)
/* Replace with min value of right subtree. */
DeleteMin (&(*tree)->right, *tree);
else
{
/* Splice out old root. */
old_root = *tree;
*tree = (*tree)->left;
XLFree (old_root);
}
}
else if (data > (*tree)->data)
/* Delete child from the right. */
RemoveBusfault (&(*tree)->right, data);
else
/* Delete from the left. */
RemoveBusfault (&(*tree)->left, data);
RebalanceBusfault (tree);
}
static void
HandleBusfault (int signal, siginfo_t *siginfo, void *ucontext)
{
/* SIGBUS received. If the faulting address is currently part of a
shared memory buffer, ignore. Otherwise, print an error and
return. Only reentrant functions must be called within this
signal handler. */
if (DetectBusfault (busfault_tree, siginfo->si_addr))
return;
write (2, "unexpected bus fault\n",
sizeof "unexpected bus fault\n");
_exit (EXIT_FAILURE);
}
static void
MaybeInstallBusHandler (void)
{
struct sigaction act;
/* If the SIGBUS handler is already installed, return. */
if (bus_handler_installed)
return;
bus_handler_installed = 1;
memset (&act, 0, sizeof act);
/* Install a SIGBUS handler. When a client truncates the file
backing a shared memory buffer without notifying the compositor,
attempting to access the contents of the mapped memory beyond the
end of the file will result in SIGBUS being called, which we
handle here. */
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = HandleBusfault;
if (sigaction (SIGBUS, &act, NULL))
{
perror ("sigaction");
abort ();
}
}
static void
BlockSigbus (void)
{
sigset_t sigset;
sigemptyset (&sigset);
sigaddset (&sigset, SIGBUS);
if (sigprocmask (SIG_BLOCK, &sigset, NULL))
{
perror ("sigprocmask");
abort ();
}
}
static void
UnblockSigbus (void)
{
sigset_t sigset;
sigemptyset (&sigset);
if (sigprocmask (SIG_BLOCK, &sigset, NULL))
{
perror ("sigprocmask");
abort ();
}
}
/* These must not overlap. */
void
XLRecordBusfault (void *data, size_t data_size)
{
Busfault *node;
MaybeInstallBusHandler ();
BlockSigbus ();
node = XLMalloc (sizeof *node);
node->data = data;
node->ignored_area = data_size;
RecordBusfault (&busfault_tree, node);
UnblockSigbus ();
}
void
XLRemoveBusfault (void *data)
{
BlockSigbus ();
RemoveBusfault (&busfault_tree, data);
UnblockSigbus ();
}

View file

@ -42,8 +42,8 @@ struct _IconSurface
/* The window used by this role. */
Window window;
/* The picture associated with this role. */
Picture picture;
/* The rendering target associated with this role. */
RenderTarget target;
/* The subcompositor associated with this role. */
Subcompositor *subcompositor;
@ -87,7 +87,7 @@ ReleaseBacking (IconSurface *icon)
return;
/* Release all allocated resources. */
XRenderFreePicture (compositor.display, icon->picture);
RenderDestroyRenderTarget (icon->target);
XDestroyWindow (compositor.display, icon->window);
/* And the association. */
@ -372,7 +372,6 @@ XLGetIconSurface (Surface *surface)
{
IconSurface *role;
XSetWindowAttributes attrs;
XRenderPictureAttributes picture_attrs;
unsigned int flags;
role = XLCalloc (1, sizeof *role);
@ -410,20 +409,15 @@ XLGetIconSurface (Surface *surface)
PropModeReplace,
(unsigned char *) &_NET_WM_WINDOW_TYPE_DND, 1);
/* Create a picture associated with the window. */
role->picture = XRenderCreatePicture (compositor.display,
role->window,
/* TODO: get this from the
visual instead. */
compositor.argb_format,
0, &picture_attrs);
/* Create a target associated with the window. */
role->target = RenderTargetFromWindow (role->window);
/* Create a subcompositor associated with the window. */
role->subcompositor = MakeSubcompositor ();
role->clock = XLMakeFrameClockForWindow (role->window);
/* Set the subcompositor target and some callbacks. */
SubcompositorSetTarget (role->subcompositor, role->picture);
SubcompositorSetTarget (role->subcompositor, &role->target);
SubcompositorSetBoundsCallback (role->subcompositor,
NoteBounds, role);

View file

@ -15,3 +15,12 @@ PIXMANINCLUDES = -I$(INCROOT)/pixman-1
system. */
WAYLAND_SCANNER = wayland-scanner
/* Uncomment the following code if building with EGL support.
#define HaveEglSupport
EGL = -lEGL
GLES = -lGLESv2
*/

5
run.c
View file

@ -143,8 +143,13 @@ HandleOneXEvent (XEvent *event)
if (XLHandleOneXEventForIconSurfaces (event))
return;
#if 0
if (XLHandleOneXEventForDmabuf (event))
return;
#endif
if (HandleOneXEventForPictureRenderer (event))
return;
if (XLHandleOneXEventForXData (event))
return;

70
seat.c
View file

@ -513,7 +513,7 @@ struct _DeviceInfo
#define CursorFromRole(role) ((SeatCursor *) (role))
/* Subcompositor targets used inside cursor subframes. */
static Picture cursor_subframe_picture;
static RenderTarget cursor_subframe_target;
/* Its associated pixmap. */
static Pixmap cursor_subframe_pixmap;
@ -856,22 +856,6 @@ ReleaseSeat (Seat *seat)
XLFree (seat);
}
static Picture
PictureForCursor (Drawable drawable)
{
XRenderPictureAttributes attrs;
Picture picture;
/* This is only required to pacfy -Wmaybe-uninitialized, since
valuemask is 0. */
memset (&attrs, 0, sizeof attrs);
picture = XRenderCreatePicture (compositor.display, drawable,
compositor.argb_format, 0,
&attrs);
return picture;
}
static void
ComputeHotspot (SeatCursor *cursor, int min_x, int min_y,
int *x, int *y)
@ -899,19 +883,24 @@ ComputeHotspot (SeatCursor *cursor, int min_x, int min_y,
}
static void
ApplyCursor (SeatCursor *cursor, Picture picture,
ApplyCursor (SeatCursor *cursor, RenderTarget target,
int min_x, int min_y)
{
Window window;
int x, y;
Picture picture;
if (cursor->cursor)
XFreeCursor (compositor.display, cursor->cursor);
ComputeHotspot (cursor, min_x, min_y, &x, &y);
picture = RenderPictureFromTarget (target);
cursor->cursor = XRenderCreateCursor (compositor.display,
picture, MAX (0, x),
MAX (0, y));
RenderFreePictureFromTarget (picture);
window = CursorWindow (cursor);
if (!(cursor->seat->flags & IsInert) && window != None)
@ -923,9 +912,8 @@ ApplyCursor (SeatCursor *cursor, Picture picture,
static void
UpdateCursorFromSubcompositor (SeatCursor *cursor)
{
static XRenderColor empty;
Picture picture;
Pixmap pixmap;
RenderTarget target;
int min_x, min_y, max_x, max_y, width, height, x, y;
Bool need_clear;
@ -963,13 +951,12 @@ UpdateCursorFromSubcompositor (SeatCursor *cursor)
pixmap = XCreatePixmap (compositor.display,
DefaultRootWindow (compositor.display),
width, height, compositor.n_planes);
picture = PictureForCursor (pixmap);
target = RenderTargetFromPixmap (pixmap);
/* If the bounds extend beyond the subcompositor, clear the
picture. */
if (need_clear)
XRenderFillRectangle (compositor.display, PictOpClear,
picture, &empty, 0, 0, width, height);
RenderClearRectangle (target, 0, 0, width, height);
/* Garbage the subcompositor, since cursor contents are not
preserved. */
@ -979,13 +966,13 @@ UpdateCursorFromSubcompositor (SeatCursor *cursor)
SubcompositorSetProjectiveTransform (cursor->subcompositor,
MAX (0, -x), MAX (0, -x));
SubcompositorSetTarget (cursor->subcompositor, picture);
SubcompositorSetTarget (cursor->subcompositor, &target);
SubcompositorUpdate (cursor->subcompositor);
SubcompositorSetTarget (cursor->subcompositor, None);
SubcompositorSetTarget (cursor->subcompositor, NULL);
ApplyCursor (cursor, picture, min_x, min_y);
ApplyCursor (cursor, target, min_x, min_y);
XRenderFreePicture (compositor.display, picture);
RenderDestroyRenderTarget (target);
XFreePixmap (compositor.display, pixmap);
}
@ -1081,8 +1068,7 @@ ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer)
static Bool
Subframe (Surface *surface, Role *role)
{
static XRenderColor empty;
Picture picture;
RenderTarget target;
Pixmap pixmap;
int min_x, min_y, max_x, max_y, width, height, x, y;
Bool need_clear;
@ -1129,13 +1115,12 @@ Subframe (Surface *surface, Role *role)
pixmap = XCreatePixmap (compositor.display,
DefaultRootWindow (compositor.display),
width, height, compositor.n_planes);
picture = PictureForCursor (pixmap);
target = RenderTargetFromPixmap (pixmap);
/* If the bounds extend beyond the subcompositor, clear the
picture. */
if (need_clear)
XRenderFillRectangle (compositor.display, PictOpClear,
picture, &empty, 0, 0, width, height);
RenderClearRectangle (target, 0, 0, width, height);
/* Garbage the subcompositor, since cursor contents are not
preserved. */
@ -1144,10 +1129,14 @@ Subframe (Surface *surface, Role *role)
/* Set the right transform if the hotspot is negative. */
SubcompositorSetProjectiveTransform (cursor->subcompositor,
MAX (0, -x), MAX (0, -x));
SubcompositorSetTarget (cursor->subcompositor, picture);
/* Set the subframe picture to the picture in question. */
cursor_subframe_picture = picture;
/* Attach the rendering target. */
SubcompositorSetTarget (cursor->subcompositor, &target);
/* Set the subframe target and pixmap to the target and pixmap in
use. */
cursor_subframe_target = target;
cursor_subframe_pixmap = pixmap;
/* Return True to let the drawing proceed. */
return True;
@ -1161,21 +1150,20 @@ EndSubframe (Surface *surface, Role *role)
cursor = CursorFromRole (role);
if (cursor_subframe_picture)
if (cursor_subframe_pixmap != None)
{
/* First, compute the bounds of the subcompositor. */
SubcompositorBounds (cursor->subcompositor,
&min_x, &min_y, &max_x, &max_y);
/* Apply the cursor. */
ApplyCursor (cursor, cursor_subframe_picture, min_x, min_y);
ApplyCursor (cursor, cursor_subframe_target, min_x, min_y);
/* Then, free the temporary target. */
XRenderFreePicture (compositor.display,
cursor_subframe_picture);
RenderDestroyRenderTarget (cursor_subframe_target);
/* Finally, clear the target. */
SubcompositorSetTarget (cursor->subcompositor, None);
SubcompositorSetTarget (cursor->subcompositor, NULL);
}
else
ApplyEmptyCursor (cursor);
@ -1183,6 +1171,8 @@ EndSubframe (Surface *surface, Role *role)
if (cursor_subframe_pixmap)
XFreePixmap (compositor.display,
cursor_subframe_pixmap);
cursor_subframe_pixmap = None;
}
static void

244
shm.c
View file

@ -21,11 +21,8 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include "compositor.h"
@ -52,24 +49,12 @@ typedef struct _Buffer
/* The ExtBuffer associated with this buffer. */
ExtBuffer buffer;
/* The pixmap associated with this buffer. */
Pixmap pixmap;
/* The picture associated with this buffer. */
Picture picture;
/* The rendering buffer associated with this buffer. */
RenderBuffer render_buffer;
/* The width and height of this buffer. */
unsigned int width, height;
/* The stride of this buffer. */
int stride;
/* The offset of this buffer. */
int offset;
/* The format of this buffer. */
uint32_t format;
/* The wl_resource corresponding to this buffer. */
struct wl_resource *resource;
@ -93,6 +78,14 @@ DereferencePool (Pool *pool)
return;
munmap (pool->data, pool->size);
/* Cancel the busfault trap. */
if (pool->data != (void *) -1
/* If the pool is of size 0, no busfault was installed. */
&& pool->size)
XLRemoveBusfault (pool->data);
close (pool->fd);
XLFree (pool);
}
@ -115,8 +108,7 @@ DereferenceBuffer (Buffer *buffer)
if (--buffer->refcount)
return;
XRenderFreePicture (compositor.display, buffer->picture);
XFreePixmap (compositor.display, buffer->pixmap);
RenderFreeShmBuffer (buffer->render_buffer);
DereferencePool (buffer->pool);
ExtBufferDestroy (&buffer->buffer);
@ -142,16 +134,10 @@ DereferenceBufferFunc (ExtBuffer *buffer)
DereferenceBuffer ((Buffer *) buffer);
}
static Picture
GetPictureFunc (ExtBuffer *buffer)
static RenderBuffer
GetBufferFunc (ExtBuffer *buffer)
{
return ((Buffer *) buffer)->picture;
}
static Pixmap
GetPixmapFunc (ExtBuffer *buffer)
{
return ((Buffer *) buffer)->pixmap;
return ((Buffer *) buffer)->render_buffer;
}
static unsigned int
@ -175,20 +161,7 @@ DestroyBuffer (struct wl_client *client, struct wl_resource *resource)
static void
PrintBuffer (Buffer *buffer)
{
int x, y;
char *base;
unsigned int *base32;
for (y = 0; y < buffer->height; ++y)
{
base = buffer->pool->data;
base += buffer->stride * y;
base32 = (unsigned int *) base;
for (x = 0; x < buffer->width; ++x)
fprintf (stderr, "#x%8x ", base32[x]);
fprintf (stderr, "\n");
}
/* Not implemented. */
}
static void
@ -197,37 +170,6 @@ PrintBufferFunc (ExtBuffer *buffer)
PrintBuffer ((Buffer *) buffer);
}
static int
DepthForFormat (uint32_t format)
{
switch (format)
{
case WL_SHM_FORMAT_ARGB8888:
return 32;
case WL_SHM_FORMAT_XRGB8888:
return 24;
default:
return 0;
}
}
static XRenderPictFormat *
PictFormatForFormat (uint32_t format)
{
switch (format)
{
case WL_SHM_FORMAT_ARGB8888:
return compositor.argb_format;
case WL_SHM_FORMAT_XRGB8888:
return compositor.xrgb_format;
default:
return 0;
}
}
static void
HandleBufferResourceDestroy (struct wl_resource *resource)
@ -245,22 +187,35 @@ static const struct wl_buffer_interface wl_shm_buffer_impl =
.destroy = DestroyBuffer,
};
static Bool
IsFormatSupported (uint32_t format)
{
ShmFormat *formats;
int nformats, i;
formats = RenderGetShmFormats (&nformats);
for (i = 0; i < nformats; ++i)
{
if (formats[i].format == format)
return True;
}
return False;
}
static void
CreateBuffer (struct wl_client *client, struct wl_resource *resource,
uint32_t id, int32_t offset, int32_t width, int32_t height,
int32_t stride, uint32_t format)
{
XRenderPictureAttributes picture_attrs;
Pool *pool;
Buffer *buffer;
xcb_shm_seg_t seg;
Pixmap pixmap;
Picture picture;
int fd, depth;
RenderBuffer render_buffer;
SharedMemoryAttributes attrs;
Bool failure;
depth = DepthForFormat (format);
if (!depth)
if (!IsFormatSupported (format))
{
wl_resource_post_error (resource, WL_SHM_ERROR_INVALID_FORMAT,
"the specified format is not supported");
@ -269,9 +224,8 @@ CreateBuffer (struct wl_client *client, struct wl_resource *resource,
pool = wl_resource_get_user_data (resource);
if (pool->size < offset || stride != width * 4
|| offset + stride * height > pool->size
|| offset < 0)
if (!RenderValidateShmParams (format, width, height,
offset, stride, pool->size))
{
wl_resource_post_error (resource, WL_SHM_ERROR_INVALID_STRIDE,
"invalid offset or stride, or pool too small");
@ -314,46 +268,43 @@ CreateBuffer (struct wl_client *client, struct wl_resource *resource,
return;
}
/* XCB closes fds after sending them. */
fd = fcntl (pool->fd, F_DUPFD_CLOEXEC, 0);
attrs.format = format;
attrs.offset = offset;
attrs.width = width;
attrs.height = height;
attrs.stride = stride;
attrs.fd = pool->fd;
if (fd < 0)
/* Pass a reference instead of the pointer itself. The pool will
stay valid as long as the buffer is still alive, and the data
pointer can change if the client resizes the pool. */
attrs.data = &pool->data;
attrs.pool_size = pool->size;
/* Now, create the renderer buffer. */
failure = False;
render_buffer = RenderBufferFromShm (&attrs, &failure);
/* If a platform specific error happened, fail now. */
if (failure)
{
wl_resource_destroy (buffer->resource);
XLFree (buffer);
wl_resource_post_error (resource, WL_SHM_ERROR_INVALID_FD,
"fcntl: %s", strerror (errno));
"unknown error creating buffer");
return;
}
seg = xcb_generate_id (compositor.conn);
pixmap = xcb_generate_id (compositor.conn);
xcb_shm_attach_fd (compositor.conn, seg, fd, false);
xcb_shm_create_pixmap (compositor.conn, pixmap,
DefaultRootWindow (compositor.display),
width, height, depth, seg, offset);
xcb_shm_detach (compositor.conn, seg);
picture = XRenderCreatePicture (compositor.display, pixmap,
PictFormatForFormat (format),
0, &picture_attrs);
buffer->pixmap = pixmap;
buffer->picture = picture;
buffer->render_buffer = render_buffer;
buffer->width = width;
buffer->height = height;
buffer->stride = stride;
buffer->offset = offset;
buffer->format = format;
buffer->pool = pool;
buffer->refcount = 1;
/* Initialize function pointers. */
buffer->buffer.funcs.retain = RetainBufferFunc;
buffer->buffer.funcs.dereference = DereferenceBufferFunc;
buffer->buffer.funcs.get_picture = GetPictureFunc;
buffer->buffer.funcs.get_pixmap = GetPixmapFunc;
buffer->buffer.funcs.get_buffer = GetBufferFunc;
buffer->buffer.funcs.width = WidthFunc;
buffer->buffer.funcs.height = HeightFunc;
buffer->buffer.funcs.release = ReleaseBufferFunc;
@ -390,6 +341,19 @@ ResizePool (struct wl_client *client, struct wl_resource *resource,
void *data;
pool = wl_resource_get_user_data (resource);
if (size == pool->size)
/* There is no need to do anything, since the pool is still the
same size. */
return;
if (size < pool->size)
{
wl_resource_post_error (resource, WL_SHM_ERROR_INVALID_FD,
"shared memory pools cannot be shrunk");
return;
}
data = mremap (pool->data, pool->size, size, MREMAP_MAYMOVE);
if (data == MAP_FAILED)
@ -399,8 +363,17 @@ ResizePool (struct wl_client *client, struct wl_resource *resource,
return;
}
/* Now cancel the existing bus fault handler, should it have been
installed. */
if (pool->size)
XLRemoveBusfault (pool->data);
pool->size = size;
pool->data = data;
/* And add a new handler. */
if (pool->size)
XLRecordBusfault (pool->data, pool->size);
}
static const struct wl_shm_pool_interface wl_shm_pool_impl =
@ -417,9 +390,8 @@ HandleResourceDestroy (struct wl_resource *resource)
}
static void
CreatePool (struct wl_client *client,
struct wl_resource *resource,
uint32_t id, int fd, int size)
CreatePool (struct wl_client *client, struct wl_resource *resource,
uint32_t id, int32_t fd, int32_t size)
{
Pool *pool;
@ -459,6 +431,13 @@ CreatePool (struct wl_client *client,
pool, HandlePoolResourceDestroy);
pool->size = size;
/* Begin trapping SIGBUS from this pool. The client may truncate
the file without telling us, in which case accessing its contents
will cause crashes. */
if (pool->size)
XLRecordBusfault (pool->data, pool->size);
pool->fd = fd;
pool->refcount = 1;
@ -473,10 +452,13 @@ static const struct wl_shm_interface wl_shm_impl =
static void
PostFormats (struct wl_resource *resource)
{
/* TODO: don't hard-code visuals and be slightly more versatile. */
ShmFormat *formats;
int nformats, i;
wl_shm_send_format (resource, WL_SHM_FORMAT_XRGB8888);
wl_shm_send_format (resource, WL_SHM_FORMAT_ARGB8888);
formats = RenderGetShmFormats (&nformats);
for (i = 0; i < nformats; ++i)
wl_shm_send_format (resource, formats[i].format);
}
static void
@ -544,40 +526,6 @@ InitRender (void)
void
XLInitShm (void)
{
xcb_shm_query_version_reply_t *reply;
xcb_shm_query_version_cookie_t cookie;
/* This shouldn't be freed. */
const xcb_query_extension_reply_t *ext;
ext = xcb_get_extension_data (compositor.conn, &xcb_shm_id);
if (!ext || !ext->present)
{
fprintf (stderr, "The MIT-SHM extension is not supported by this X server.\n");
exit (1);
}
cookie = xcb_shm_query_version (compositor.conn);
reply = xcb_shm_query_version_reply (compositor.conn,
cookie, NULL);
if (!reply)
{
fprintf (stderr, "The MIT-SHM extension on this X server is too old.\n");
exit (1);
}
else if (reply->major_version < 1
|| (reply->major_version == 1
&& reply->minor_version < 2))
{
fprintf (stderr, "The MIT-SHM extension on this X server is too old"
" to support POSIX shared memory.\n");
exit (1);
}
free (reply);
InitRender ();
global_shm = wl_global_create (compositor.wl_display,

View file

@ -152,6 +152,8 @@ enum
/* This means that the subcompositor is frozen and updates should
do nothing. */
SubcompositorIsFrozen = (1 << 5),
/* This means that the subcompositor has a target attached. */
SubcompositorIsTargetAttached = (1 << 6),
};
#define IsGarbaged(subcompositor) \
@ -179,6 +181,11 @@ enum
#define IsFrozen(subcompositor) \
((subcompositor)->state & SubcompositorIsFrozen)
#define SetTargetAttached(subcompositor) \
((subcompositor)->state |= SubcompositorIsTargetAttached)
#define IsTargetAttached(subcompositor) \
((subcompositor)->state & SubcompositorIsTargetAttached)
#ifndef TEST
enum
@ -218,11 +225,6 @@ struct _View
into a compositor. */
Subcompositor *subcompositor;
#ifndef TEST
/* Picture this subcompositor draws to. */
Picture picture;
#endif
/* Pointer to the parent view. NULL if the parent is the
subcompositor itself. */
View *parent;
@ -289,8 +291,8 @@ struct _Subcompositor
List *children, *last_children;
#ifndef TEST
/* The picture that is rendered to. */
Picture target;
/* Target this subcompositor draws to. */
RenderTarget target;
/* Function called when the opaque region changes. */
void (*opaque_change) (Subcompositor *, void *,
@ -316,6 +318,14 @@ struct _Subcompositor
/* An additional offset to apply when drawing to the target. */
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
};
@ -330,10 +340,6 @@ enum
DoAll = 0xf,
};
/* The identity transform. */
static XTransform identity_transform;
#endif
@ -411,6 +417,10 @@ MakeSubcompositor (void)
subcompositor->last = subcompositor->inferiors;
subcompositor->last_children = subcompositor->children;
/* Initialize the buffers used to store previous damage. */
pixman_region32_init (&subcompositor->prior_damage[0]);
pixman_region32_init (&subcompositor->prior_damage[1]);
return subcompositor;
}
@ -562,9 +572,16 @@ SubcompositorUpdateBoundsForInsert (Subcompositor *subcompositor,
#ifndef TEST
void
SubcompositorSetTarget (Subcompositor *compositor, Picture picture)
SubcompositorSetTarget (Subcompositor *compositor,
RenderTarget *target_in)
{
compositor->target = picture;
if (target_in)
{
compositor->target = *target_in;
SetTargetAttached (compositor);
}
else
compositor->state &= SubcompositorIsTargetAttached;
/* We don't know if the new picture has the previous state left
over. */
@ -1171,6 +1188,19 @@ main (int argc, char **argv)
the target drawable. Afterwards, the damage region of each
inferior is cleared, and the process can begin again.
However, under some situations, the contents of the target drawable
may reflect what was drawn two or three invocations of
SubcompositorUpdate ago. To enable efficient updates when that is
the case, the subcompositor will keep track of the global damage
regions of the past two updates, and intersect the resulting global
damage region of each invocation with the appropriate number of
previous regions.
For simplicity's sake, the update inferior is reset to the first
view in the subcompositor's inferior list whenever the global
damage region is intersected with the damage region of a previous
update.
Such computation is not reliable, however, if the size or position
of a view changes. In the interest of keeping thing simple, every
inferior is composited onto the target drawable whenever a view
@ -1560,21 +1590,10 @@ GetTxTy (int scale)
return 1.0 / (-scale + 1);
}
static XTransform
ViewGetTransform (View *view)
static void
ViewApplyTransform (View *view, RenderBuffer buffer)
{
XTransform transform;
double transform_value;
/* Perform scaling in the transform matrix. */
memset (&transform, 0, sizeof transform);
transform_value = GetTxTy (view->scale);
transform.matrix[0][0] = XDoubleToFixed (transform_value);
transform.matrix[1][1] = XDoubleToFixed (transform_value);
transform.matrix[2][2] = XDoubleToFixed (1);
return transform;
RenderApplyTransform (buffer, GetTxTy (view->scale));
}
/* TODO: the callers of this can be optimized by setting the picture
@ -1625,31 +1644,9 @@ static void
FillBoxesWithTransparency (Subcompositor *subcompositor,
pixman_box32_t *boxes, int nboxes)
{
XRectangle *rects;
static XRenderColor color;
int i;
Picture picture;
picture = subcompositor->target;
if (nboxes < 256)
rects = alloca (sizeof *rects * nboxes);
else
rects = XLMalloc (sizeof *rects * nboxes);
for (i = 0; i < nboxes; ++i)
{
rects[i].x = BoxStartX (boxes[i]) - subcompositor->min_x;
rects[i].y = BoxStartY (boxes[i]) - subcompositor->min_y;
rects[i].width = BoxWidth (boxes[i]);
rects[i].height = BoxHeight (boxes[i]);
}
XRenderFillRectangles (compositor.display, PictOpClear, picture,
&color, rects, nboxes);
if (nboxes >= 256)
XLFree (rects);
RenderFillBoxesWithTransparency (subcompositor->target, boxes,
nboxes, subcompositor->min_x,
subcompositor->min_y);
}
static Bool
@ -1684,6 +1681,52 @@ SubcompositorIsEmpty (Subcompositor *subcompositor)
&& subcompositor->min_y == subcompositor->max_y);
}
static void
StorePreviousDamage (Subcompositor *subcompositor,
pixman_region32_t *update_region)
{
pixman_region32_t *prior;
if (renderer_flags & NeverAges)
/* Aging never happens, so recording prior damage is
unnecessary. */
return;
/* Move last_damage to prior_damage if it already exists, and find
something to hold more damage and set it as last_damage. There
is no need to do this if the render target age never exceeds
0. */
if (!subcompositor->last_damage)
subcompositor->last_damage = &subcompositor->prior_damage[0];
else if (!subcompositor->before_damage)
{
subcompositor->before_damage = subcompositor->last_damage;
subcompositor->last_damage = &subcompositor->prior_damage[1];
}
else
{
prior = subcompositor->before_damage;
subcompositor->before_damage = subcompositor->last_damage;
subcompositor->last_damage = prior;
}
/* NULL means use the bounds of the subcompositor. */
if (!update_region)
{
pixman_region32_fini (subcompositor->last_damage);
pixman_region32_init_rect (subcompositor->last_damage,
subcompositor->min_x,
subcompositor->min_y,
subcompositor->max_x,
subcompositor->max_y);
}
else
/* Copy the update region to last_damage. */
pixman_region32_copy (subcompositor->last_damage,
update_region);
}
void
SubcompositorUpdate (Subcompositor *subcompositor)
{
@ -1692,13 +1735,14 @@ SubcompositorUpdate (Subcompositor *subcompositor)
View *start, *original_start, *view, *first;
List *list;
pixman_box32_t *boxes, *extents, temp_boxes;
int nboxes, i, op, tx, ty;
Picture picture;
XTransform transform;
int nboxes, i, tx, ty;
Operation op;
RenderBuffer buffer;
int min_x, min_y;
int age;
/* Just return if no target was specified. */
if (subcompositor->target == None)
if (!IsTargetAttached (subcompositor))
return;
/* Likewise if the subcompositor is "frozen". */
@ -1717,6 +1761,31 @@ SubcompositorUpdate (Subcompositor *subcompositor)
start = subcompositor->inferiors->next->view;
original_start = subcompositor->inferiors->next->view;
age = RenderTargetAge (subcompositor->target);
/* If there is not enough prior damage available to satisfy age, set
it to -1. */
if (age > 0 && !subcompositor->last_damage)
age = -1;
if (age > 2 && !subcompositor->before_damage)
age = -1;
/* If the subcompositor is garbaged, clear all prior damage. */
if (IsGarbaged (subcompositor))
{
if (subcompositor->last_damage)
pixman_region32_clear (subcompositor->last_damage);
if (subcompositor->before_damage)
pixman_region32_clear (subcompositor->before_damage);
/* Reset these fields to NULL, so we do not try to use them
later on. */
subcompositor->last_damage = NULL;
subcompositor->before_damage = NULL;
}
/* Clear the "is partially mapped" flag. It will be set later on if
there is actually a partially mapped view. */
@ -1727,7 +1796,17 @@ SubcompositorUpdate (Subcompositor *subcompositor)
min_x, min_y, subcompositor->max_x,
subcompositor->max_y);
if (!IsGarbaged (subcompositor))
/* Note the size of this subcompositor, so the viewport can be set
accordingly. */
RenderNoteTargetSize (subcompositor->target,
subcompositor->max_x - min_x + 1,
subcompositor->max_y - min_y + 1);
if (!IsGarbaged (subcompositor)
/* If the target contents are too old or invalid, we go down the
usual IsGarbaged code path, except we do not recompute the
input or opaque regions unless they are dirty. */
&& (age >= 0 && age < 3))
{
start = NULL;
original_start = NULL;
@ -1793,8 +1872,10 @@ SubcompositorUpdate (Subcompositor *subcompositor)
&update_region, &temp);
/* This view will obscure all preceding damaged areas,
so make start here. */
if (!pixman_region32_not_empty (&update_region))
so make start here. This optimization is disabled if
the target contents are too old, as prior damage
could reveal contents below. */
if (!pixman_region32_not_empty (&update_region) && !age)
{
start = list->view;
@ -1834,6 +1915,14 @@ SubcompositorUpdate (Subcompositor *subcompositor)
if (pixman_region32_not_empty (&list->view->damage))
{
/* Update the attached buffer from the damage. This is
only required on some backends, where we have to
upload data from a shared memory buffer to the
graphics hardware. */
buffer = XLRenderBufferFromBuffer (view->buffer);
RenderUpdateBufferForDamage (buffer, &list->view->damage);
/* Translate the region into the subcompositor
coordinate space. */
pixman_region32_translate (&list->view->damage,
@ -1906,14 +1995,49 @@ SubcompositorUpdate (Subcompositor *subcompositor)
}
pixman_region32_fini (&start_opaque);
/* First store previous damage. */
StorePreviousDamage (subcompositor, &update_region);
/* Now, apply any prior damage that might be required. */
if (age > 0)
pixman_region32_union (&update_region, &update_region,
/* This is checked to exist upon
entering this code path. */
subcompositor->last_damage);
if (age > 1)
pixman_region32_union (&update_region, &update_region,
/* This is checked to exist upon
entering this code path. */
subcompositor->before_damage);
}
else
{
/* To save from iterating over all the views twice, perform the
input and opaque region updates in the draw loop instead. */
if (IsGarbaged (subcompositor))
{
pixman_region32_init (&total_opaque);
pixman_region32_init (&total_input);
}
else
{
/* Otherwise, we are in the IsGarbaged code because the
target contents are too old. Only initialize the opaque
and input regions if they are dirty. */
if (IsOpaqueDirty (subcompositor))
pixman_region32_init (&total_opaque);
if (IsInputDirty (subcompositor))
pixman_region32_init (&total_input);
}
/* Either way, put something in the prior damage ring. */
StorePreviousDamage (subcompositor, NULL);
}
/* If there's nothing to do, return. */
@ -1925,6 +2049,10 @@ SubcompositorUpdate (Subcompositor *subcompositor)
list = start->link;
first = NULL;
/* Begin rendering. This is unnecessary on XRender, but required on
EGL to make the surface current and set the viewport. */
RenderStartRender (subcompositor->target);
do
{
view = list->view;
@ -1947,7 +2075,24 @@ SubcompositorUpdate (Subcompositor *subcompositor)
if (!view->buffer)
goto next_1;
picture = XLPictureFromBuffer (view->buffer);
buffer = XLRenderBufferFromBuffer (view->buffer);
if (IsGarbaged (subcompositor))
/* Update the attached buffer from the damage. This is only
required on some backends, where we have to upload data
from a shared memory buffer to the graphics hardware.
As the damage cannot be trusted while the subcompositor is
update, pass NULL; this tells the renderer to update the
entire buffer.
Note that if the subcompositor is not garbaged, then this
has already been done. */
RenderUpdateBufferForDamage (buffer, NULL);
else if (age < 0 || age > 3)
/* The target contents are too old, but the damage can be
trusted. */
RenderUpdateBufferForDamage (buffer, &view->damage);
if (!first)
{
@ -1955,7 +2100,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
with PictOpSrc so that transparency is applied correctly,
if it contains the entire update region. */
if (IsGarbaged (subcompositor))
if (IsGarbaged (subcompositor) || age < 0 || age > 3)
{
extents = &temp_boxes;
@ -1971,14 +2116,14 @@ SubcompositorUpdate (Subcompositor *subcompositor)
if (ViewContainsExtents (view, extents))
/* The update region is contained by the entire view, so
use PictOpSrc. */
op = PictOpSrc;
use source. */
op = OperationSource;
else
{
/* Otherwise, fill the whole update region with
transparency. */
if (IsGarbaged (subcompositor))
if (IsGarbaged (subcompositor) || age < 0 || age > 3)
{
/* Use the entire subcompositor bounds if
garbaged. */
@ -1993,24 +2138,19 @@ SubcompositorUpdate (Subcompositor *subcompositor)
FillBoxesWithTransparency (subcompositor,
boxes, nboxes);
/* And use PictOpOver as usual. */
op = PictOpOver;
/* And use over as usual. */
op = OperationOver;
}
}
else
op = PictOpOver;
op = OperationOver;
first = view;
if (ViewHaveTransform (view))
{
transform = ViewGetTransform (view);
ViewApplyTransform (view, buffer);
XRenderSetPictureTransform (compositor.display, picture,
&transform);
}
if (!IsGarbaged (subcompositor))
if (!IsGarbaged (subcompositor) && (age >= 0 && age < 3))
{
/* First, obtain a new region that is the intersection of
the view with the global update region. */
@ -2023,14 +2163,11 @@ SubcompositorUpdate (Subcompositor *subcompositor)
boxes = pixman_region32_rectangles (&temp, &nboxes);
for (i = 0; i < nboxes; ++i)
XRenderComposite (compositor.display, op, picture,
None, subcompositor->target,
RenderComposite (buffer, subcompositor->target, op,
/* src-x. */
BoxStartX (boxes[i]) - view->abs_x,
/* src-y. */
BoxStartY (boxes[i]) - view->abs_y,
/* mask-x, mask-y. */
0, 0,
/* dst-x. */
BoxStartX (boxes[i]) - min_x + tx,
/* dst-y. */
@ -2045,14 +2182,11 @@ SubcompositorUpdate (Subcompositor *subcompositor)
earlier, since the compositor was garbaged. */
pixman_region32_clear (&view->damage);
/* If the subcompositor is garbaged, composite the entire view
to the right location. */
XRenderComposite (compositor.display, op, picture,
None, subcompositor->target,
/* If the subcompositor is garbaged, composite the entire
view to the right location. */
RenderComposite (buffer, subcompositor->target, op,
/* src-x, src-y. */
0, 0,
/* mask-x, mask-y. */
0, 0,
/* dst-x. */
view->abs_x - min_x + tx,
/* dst-y. */
@ -2064,7 +2198,13 @@ SubcompositorUpdate (Subcompositor *subcompositor)
/* Also adjust the opaque and input regions here. */
if (pixman_region32_not_empty (&view->opaque))
if (pixman_region32_not_empty (&view->opaque)
/* If the subcompositor is garbaged, the opaque region
must always be updated. But if we are here because
the target is too old, it must only be updated if the
opaque region is also dirty. */
&& (IsGarbaged (subcompositor)
|| IsOpaqueDirty (subcompositor)))
{
/* Translate the region into the global coordinate
space. */
@ -2084,7 +2224,10 @@ SubcompositorUpdate (Subcompositor *subcompositor)
-list->view->abs_y);
}
if (pixman_region32_not_empty (&view->input))
if (pixman_region32_not_empty (&view->input)
/* Ditto for the input region. */
&& (IsGarbaged (subcompositor)
|| IsInputDirty (subcompositor)))
{
/* Translate the region into the global coordinate
space. */
@ -2105,17 +2248,25 @@ SubcompositorUpdate (Subcompositor *subcompositor)
}
if (ViewHaveTransform (view))
XRenderSetPictureTransform (compositor.display, picture,
&identity_transform);
RenderResetTransform (buffer);
next_1:
list = list->next;
}
while (list != subcompositor->inferiors);
/* Swap changes to display. */
RenderFinishRender (subcompositor->target);
complete:
if (IsGarbaged (subcompositor))
if (IsGarbaged (subcompositor)
|| ((age < 0 || age > 3)
&& (IsInputDirty (subcompositor)
|| IsOpaqueDirty (subcompositor))))
{
if (IsGarbaged (subcompositor)
|| IsOpaqueDirty (subcompositor))
{
/* The opaque region changed, so run any callbacks. */
if (subcompositor->opaque_change)
@ -2130,7 +2281,11 @@ SubcompositorUpdate (Subcompositor *subcompositor)
}
pixman_region32_fini (&total_opaque);
}
if (IsGarbaged (subcompositor)
|| IsInputDirty (subcompositor))
{
/* The input region changed, so run any callbacks. */
if (subcompositor->input_change)
{
@ -2145,6 +2300,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
pixman_region32_fini (&total_input);
}
}
pixman_region32_fini (&temp);
pixman_region32_fini (&update_region);
@ -2163,17 +2319,17 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
View *view;
int x, y, width, height, nboxes, min_x, min_y, tx, ty;
pixman_box32_t extents, *boxes;
int op, i;
int i;
Operation op;
pixman_region32_t temp;
Picture picture;
XTransform transform;
RenderBuffer buffer;
/* Graphics exposures are not yet handled. */
if (event->type == GraphicsExpose)
return;
/* No target? No update. */
if (subcompositor->target == None)
if (!IsTargetAttached (subcompositor))
return;
x = event->xexpose.x + subcompositor->min_x;
@ -2200,6 +2356,10 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
list = subcompositor->inferiors;
/* Begin rendering. This is unnecessary on XRender, but required on
EGL to make the surface current and set the viewport. */
RenderStartRender (subcompositor->target);
do
{
if (!list->view)
@ -2217,7 +2377,7 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
/* If the first mapped view contains everything, draw it with
PictOpSrc. */
if (!view && ViewContainsExtents (list->view, &extents))
op = PictOpSrc;
op = OperationSource;
else
{
/* Otherwise, fill the region with transparency for the
@ -2227,7 +2387,7 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
FillBoxesWithTransparency (subcompositor,
&extents, 1);
op = PictOpOver;
op = OperationOver;
}
view = list->view;
@ -2240,27 +2400,21 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
ViewHeight (view));
/* Composite the contents according to OP. */
picture = XLPictureFromBuffer (view->buffer);
buffer = XLRenderBufferFromBuffer (view->buffer);
boxes = pixman_region32_rectangles (&temp, &nboxes);
if (ViewHaveTransform (view))
{
/* Set up transforms if necessary. */
transform = ViewGetTransform (view);
/* Update the attached buffer from any damage. */
RenderUpdateBufferForDamage (buffer, &list->view->damage);
XRenderSetPictureTransform (compositor.display, picture,
&transform);
}
if (ViewHaveTransform (view))
ViewApplyTransform (view, buffer);
for (i = 0; i < nboxes; ++i)
XRenderComposite (compositor.display, op, picture,
None, subcompositor->target,
RenderComposite (buffer, subcompositor->target, op,
/* src-x. */
BoxStartX (boxes[i]) - view->abs_x,
/* src-y. */
BoxStartY (boxes[i]) - view->abs_y,
/* mask-x, mask-y. */
0, 0,
/* dst-x. */
BoxStartX (boxes[i]) - min_x + tx,
/* dst-y. */
@ -2270,8 +2424,7 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
/* Undo transforms that were applied. */
if (ViewHaveTransform (view))
XRenderSetPictureTransform (compositor.display, picture,
&identity_transform);
RenderResetTransform (buffer);
/* Free the scratch region used to compute the intersection. */
pixman_region32_fini (&temp);
@ -2281,6 +2434,9 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
list = list->next;
}
while (list != subcompositor->inferiors);
/* Swap changes to display. */
RenderFinishRender (subcompositor->target);
}
void
@ -2309,6 +2465,10 @@ SubcompositorFree (Subcompositor *subcompositor)
XLFree (subcompositor->children);
XLFree (subcompositor->inferiors);
/* Finalize the buffers used to store previous damage. */
pixman_region32_fini (&subcompositor->prior_damage[0]);
pixman_region32_fini (&subcompositor->prior_damage[1]);
XLFree (subcompositor);
}
@ -2401,9 +2561,7 @@ ViewGetParent (View *view)
void
SubcompositorInit (void)
{
identity_transform.matrix[0][0] = 1;
identity_transform.matrix[1][1] = 1;
identity_transform.matrix[2][2] = 1;
/* Nothing to do here... */
}
int

View file

@ -91,8 +91,8 @@ struct _XdgRole
/* The window backing this role. */
Window window;
/* The picture backing this role. */
Picture picture;
/* The render target backing this role. */
RenderTarget target;
/* The subcompositor backing this role. */
Subcompositor *subcompositor;
@ -775,7 +775,7 @@ ReleaseBacking (XdgRole *role)
XLXdgRoleDetachImplementation (&role->role, role->impl);
/* Release all allocated resources. */
XRenderFreePicture (compositor.display, role->picture);
RenderDestroyRenderTarget (role->target);
XDestroyWindow (compositor.display, role->window);
/* And the association. */
@ -1268,7 +1268,6 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
{
XdgRole *role;
XSetWindowAttributes attrs;
XRenderPictureAttributes picture_attrs;
unsigned int flags;
Surface *surface;
@ -1356,18 +1355,14 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
0, 0, 20, 20, 0, compositor.n_planes,
InputOutput, compositor.visual, flags,
&attrs);
role->picture = XRenderCreatePicture (compositor.display,
role->window,
/* TODO: get this from the
visual instead. */
compositor.argb_format,
0, &picture_attrs);
role->target = RenderTargetFromWindow (role->window);
role->subcompositor = MakeSubcompositor ();
role->clock = XLMakeFrameClockForWindow (role->window);
XLFrameClockSetFreezeCallback (role->clock, HandleFreeze, role);
SubcompositorSetTarget (role->subcompositor, role->picture);
SubcompositorSetTarget (role->subcompositor, &role->target);
SubcompositorSetInputCallback (role->subcompositor,
InputRegionChanged, role);
SubcompositorSetOpaqueCallback (role->subcompositor,

View file

@ -92,8 +92,13 @@ ErrorHandler (Display *display, XErrorEvent *event)
return 0;
}
#if 0
if (XLHandleErrorForDmabuf (event))
return 0;
#endif
if (HandleErrorForPictureRenderer (event))
return 0;
if (event->error_code == (xi_first_error + XI_BadDevice))
/* Various XI requests can result in XI_BadDevice errors if the