12to11/fns.c
oldosfan b4ee06589e 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.
2022-09-23 08:44:37 +00:00

801 lines
15 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Wayland compositor running on top of an X server.
Copyright (C) 2022 to various contributors.
This file is part of 12to11.
12to11 is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
12to11 is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
#include <sys/mman.h>
#include <sys/fcntl.h>
#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. */
RootWindowSelection *next, *last;
/* The event mask one piece of code has selected for. */
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 *))
{
XLList *tem, *last;
tem = list;
while (tem)
{
last = tem;
tem = tem->next;
if (item_func)
item_func (last->data);
XLFree (last);
}
}
XLList *
XLListRemove (XLList *list, void *data)
{
XLList *tem, **last;
last = &list;
while (*last)
{
tem = *last;
if (tem->data == data)
{
*last = tem->next;
XLFree (tem);
}
else
last = &tem->next;
}
return list;
}
XLList *
XLListPrepend (XLList *list, void *data)
{
XLList *tem;
tem = XLMalloc (sizeof *tem);
tem->data = data;
tem->next = list;
return tem;
}
/* List of XIDs (not pointers). */
void
XIDListFree (XIDList *list, void (*item_func) (XID))
{
XIDList *tem, *last;
tem = list;
while (tem)
{
last = tem;
tem = tem->next;
if (item_func)
item_func (last->data);
XLFree (last);
}
}
XIDList *
XIDListRemove (XIDList *list, XID resource)
{
XIDList *tem, **last;
last = &list;
while (*last)
{
tem = *last;
if (tem->data == resource)
{
*last = tem->next;
XLFree (tem);
}
else
last = &tem->next;
}
return list;
}
XIDList *
XIDListPrepend (XIDList *list, XID resource)
{
XIDList *tem;
tem = XLMalloc (sizeof *tem);
tem->data = resource;
tem->next = list;
return tem;
}
/* Hash tables between XIDs and arbitrary data. */
XLAssocTable *
XLCreateAssocTable (int size)
{
XLAssocTable *table;
XLAssoc *buckets;
table = XLMalloc (sizeof *table);
buckets = XLCalloc (size, sizeof *buckets);
table->buckets = buckets;
table->size = size;
while (--size >= 0)
{
/* Initialize each bucket with the sentinel node. */
buckets->prev = buckets;
buckets->next = buckets;
buckets++;
}
return table;
}
static void
Insque (XLAssoc *velem, XLAssoc *vprev)
{
XLAssoc *elem, *prev, *next;
elem = velem;
prev = vprev;
next = prev->next;
prev->next = elem;
if (next)
next->prev = elem;
elem->next = next;
elem->prev = prev;
}
static void
Remque (XLAssoc *velem)
{
XLAssoc *elem, *prev, *next;
elem = velem;
next = elem->next;
prev = elem->prev;
if (next)
next->prev = prev;
if (prev)
prev->next = next;
}
void
XLMakeAssoc (XLAssocTable *table, XID x_id, void *data)
{
int hash;
XLAssoc *bucket, *entry, *new_entry;
hash = x_id % table->size;
bucket = &table->buckets[hash];
entry = bucket->next;
if (entry != bucket)
{
/* Bucket isn't empty, start searching. */
for (; entry != bucket; entry = entry->next)
{
if (entry->x_id == x_id)
{
entry->data = data;
return;
}
if (entry->x_id > x_id)
break;
}
}
/* Insert new_entry immediately before entry. */
new_entry = XLMalloc (sizeof *new_entry);
new_entry->x_id = x_id;
new_entry->data = data;
Insque (new_entry, entry->prev);
}
void *
XLLookUpAssoc (XLAssocTable *table, XID x_id)
{
int hash;
XLAssoc *bucket, *entry;
hash = x_id % table->size;
bucket = &table->buckets[hash];
entry = bucket->next;
for (; entry != bucket; entry = entry->next)
{
if (entry->x_id == x_id)
return entry->data;
if (entry->x_id > x_id)
return NULL;
}
return NULL;
}
void
XLDeleteAssoc (XLAssocTable *table, XID x_id)
{
int hash;
XLAssoc *bucket, *entry;
hash = x_id % table->size;
bucket = &table->buckets[hash];
entry = bucket->next;
for (; entry != bucket; entry = entry->next)
{
if (entry->x_id == x_id)
{
Remque (entry);
XLFree (entry);
return;
}
if (entry->x_id > x_id)
return;
}
}
void
XLDestroyAssocTable (XLAssocTable *table)
{
int i;
XLAssoc *bucket, *entry, *entry_next;
for (i = 0; i < table->size; i++)
{
bucket = &table->buckets[i];
for (entry = bucket->next; entry != bucket;
entry = entry_next)
{
entry_next = entry->next;
XLFree (entry);
}
}
XLFree (table->buckets);
XLFree (table);
}
void
XLAssert (Bool condition)
{
if (!condition)
abort ();
}
void
XLScaleRegion (pixman_region32_t *dst, pixman_region32_t *src,
float scale_x, float scale_y)
{
int nrects, i;
pixman_box32_t *src_rects;
pixman_box32_t *dst_rects;
if (scale_x == 1.0f && scale_y == 1.0f)
{
pixman_region32_copy (dst, src);
return;
}
src_rects = pixman_region32_rectangles (src, &nrects);
if (nrects < 128)
dst_rects = alloca (nrects * sizeof *dst_rects);
else
dst_rects = XLMalloc (nrects * sizeof *dst_rects);
for (i = 0; i < nrects; ++i)
{
dst_rects[i].x1 = floor (src_rects[i].x1 * scale_x);
dst_rects[i].x2 = ceil (src_rects[i].x2 * scale_x);
dst_rects[i].y1 = floor (src_rects[i].y1 * scale_y);
dst_rects[i].y2 = ceil (src_rects[i].y2 * scale_y);
}
pixman_region32_fini (dst);
pixman_region32_init_rects (dst, dst_rects, nrects);
if (nrects >= 128)
XLFree (dst_rects);
}
int
XLOpenShm (void)
{
char name[sizeof "SharedBufferXXXXXXXX"];
int fd;
unsigned int i;
i = 0;
while (i <= 0xffffffff)
{
sprintf (name, "SharedBuffer%x", i);
fd = shm_open (name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0)
{
shm_unlink (name);
return fd;
}
if (errno == EEXIST)
++i;
else
{
perror ("shm_open");
exit (1);
}
}
return -1;
}
static Bool
ServerTimePredicate (Display *display, XEvent *event, XPointer arg)
{
return (event->type == PropertyNotify
&& event->xproperty.window == selection_transfer_window
&& event->xproperty.atom == _XL_SERVER_TIME_ATOM);
}
Time
XLGetServerTimeRoundtrip (void)
{
XEvent event;
XChangeProperty (compositor.display, selection_transfer_window,
_XL_SERVER_TIME_ATOM, XA_ATOM, 32, PropModeReplace,
(unsigned char *) &_XL_SERVER_TIME_ATOM, 1);
XIfEvent (compositor.display, &event, ServerTimePredicate, NULL);
return event.xproperty.time;
}
static void
ReselectRootWindowInput (void)
{
unsigned long effective;
RootWindowSelection *record;
effective = NoEventMask;
record = root_window_events.next;
if (!record)
return;
while (record != &root_window_events)
{
effective |= record->event_mask;
record = record->next;
}
XSelectInput (compositor.display,
DefaultRootWindow (compositor.display),
effective);
}
RootWindowSelection *
XLSelectInputFromRootWindow (unsigned long event_mask)
{
RootWindowSelection *selection;
/* This lets different pieces of code select for input from the root
window without clobbering eachothers event masks. */
selection = XLMalloc (sizeof *selection);
/* If the global chain has not yet been initialized, initialize it
now. */
if (!root_window_events.next)
{
root_window_events.next = &root_window_events;
root_window_events.last = &root_window_events;
}
/* Link this onto the chain of events being selected for on the root
window. */
selection->next = root_window_events.next;
selection->last = &root_window_events;
root_window_events.next->last = selection;
root_window_events.next = selection;
/* Set the event mask. */
selection->event_mask = event_mask;
/* Actually select for events. */
ReselectRootWindowInput ();
return selection;
}
void
XLDeselectInputFromRootWindow (RootWindowSelection *key)
{
key->last->next = key->next;
key->next->last = key->last;
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 ();
}