forked from 12to11/12to11

* 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.
306 lines
7 KiB
C
306 lines
7 KiB
C
/* Wayland compositor running on top of an X serer.
|
|
|
|
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/param.h>
|
|
|
|
#include <stdio.h>
|
|
#include <poll.h>
|
|
#include <alloca.h>
|
|
|
|
#include "compositor.h"
|
|
|
|
typedef struct _PollFd PollFd;
|
|
|
|
struct _PollFd
|
|
{
|
|
/* The next and last writable fd in this chain. */
|
|
PollFd *next, *last;
|
|
|
|
/* The file descriptor itself. */
|
|
int write_fd;
|
|
|
|
/* Callback run with the fd number and data when the fd becomes
|
|
writable or readable. */
|
|
void (*poll_callback) (int, void *, PollFd *);
|
|
|
|
/* Data for the callback. */
|
|
void *data;
|
|
|
|
/* The direction; 1 means write, 0 means read. */
|
|
int direction;
|
|
};
|
|
|
|
/* Number of file descriptors for which we are waiting for something
|
|
to be written. */
|
|
static int num_poll_fd;
|
|
|
|
/* Linked list of file descriptors and write callbacks. */
|
|
static PollFd poll_fds;
|
|
|
|
WriteFd *
|
|
XLAddWriteFd (int fd, void *data, void (*poll_callback) (int, void *,
|
|
WriteFd *))
|
|
{
|
|
WriteFd *record;
|
|
|
|
record = XLMalloc (sizeof *record);
|
|
record->next = poll_fds.next;
|
|
record->last = &poll_fds;
|
|
record->write_fd = fd;
|
|
record->poll_callback = poll_callback;
|
|
record->data = data;
|
|
record->direction = 1;
|
|
|
|
poll_fds.next->last = record;
|
|
poll_fds.next = record;
|
|
|
|
num_poll_fd++;
|
|
|
|
return record;
|
|
}
|
|
|
|
ReadFd *
|
|
XLAddReadFd (int fd, void *data, void (*poll_callback) (int, void *,
|
|
ReadFd *))
|
|
{
|
|
WriteFd *record;
|
|
|
|
record = XLMalloc (sizeof *record);
|
|
record->next = poll_fds.next;
|
|
record->last = &poll_fds;
|
|
record->write_fd = fd;
|
|
record->poll_callback = poll_callback;
|
|
record->data = data;
|
|
record->direction = 0;
|
|
|
|
poll_fds.next->last = record;
|
|
poll_fds.next = record;
|
|
|
|
num_poll_fd++;
|
|
|
|
return record;
|
|
}
|
|
|
|
void
|
|
XLRemoveWriteFd (WriteFd *fd)
|
|
{
|
|
/* Mark this record as invalid. Records cannot safely change while
|
|
the event loop is in progress, so we remove all invalid records
|
|
immediately before polling. */
|
|
fd->write_fd = -1;
|
|
}
|
|
|
|
void
|
|
XLRemoveReadFd (ReadFd *fd)
|
|
{
|
|
/* Mark this record as invalid. Records cannot safely change while
|
|
the event loop is in progress, so we remove all invalid records
|
|
immediately before polling. */
|
|
fd->write_fd = -1;
|
|
}
|
|
|
|
static void
|
|
RemoveWriteFd (WriteFd *fd)
|
|
{
|
|
fd->next->last = fd->last;
|
|
fd->last->next = fd->next;
|
|
|
|
num_poll_fd--;
|
|
XLFree (fd);
|
|
}
|
|
|
|
static void
|
|
HandleOneXEvent (XEvent *event)
|
|
{
|
|
XLHandleOneXEventForDnd (event);
|
|
|
|
/* Filter all non-GenericEvents through the input method
|
|
infrastructure. */
|
|
if (event->type != GenericEvent
|
|
&& XFilterEvent (event, event->xany.window))
|
|
return;
|
|
|
|
if (XLHandleXEventForXdgSurfaces (event))
|
|
return;
|
|
|
|
if (HandleOneXEventForPictureRenderer (event))
|
|
return;
|
|
|
|
if (XLHandleXEventForXdgToplevels (event))
|
|
return;
|
|
|
|
if (XLHandleXEventForXdgPopups (event))
|
|
return;
|
|
|
|
if (XLHandleOneXEventForSeats (event))
|
|
return;
|
|
|
|
if (XLHandleOneXEventForIconSurfaces (event))
|
|
return;
|
|
|
|
#if 0
|
|
if (XLHandleOneXEventForDmabuf (event))
|
|
return;
|
|
#endif
|
|
|
|
if (XLHandleOneXEventForXData (event))
|
|
return;
|
|
|
|
if (XLHandleOneXEventForOutputs (event))
|
|
return;
|
|
|
|
if (XLHandleOneXEventForXSettings (event))
|
|
return;
|
|
}
|
|
|
|
static void
|
|
ReadXEvents (void)
|
|
{
|
|
XEvent event;
|
|
|
|
while (XPending (compositor.display))
|
|
{
|
|
XNextEvent (compositor.display, &event);
|
|
|
|
/* We failed to get event data for a generic event, so there's
|
|
no point in continuing. */
|
|
if (event.type == GenericEvent
|
|
&& !XGetEventData (compositor.display, &event.xcookie))
|
|
continue;
|
|
|
|
if (!HookSelectionEvent (&event))
|
|
HandleOneXEvent (&event);
|
|
|
|
if (event.type == GenericEvent)
|
|
XFreeEventData (compositor.display, &event.xcookie);
|
|
}
|
|
}
|
|
|
|
static void
|
|
RunStep (void)
|
|
{
|
|
int x_connection, wl_connection, rc, i, j;
|
|
struct timespec timeout;
|
|
struct pollfd *fds;
|
|
PollFd **pollfds, *item, *last;
|
|
|
|
XFlush (compositor.display);
|
|
wl_display_flush_clients (compositor.wl_display);
|
|
|
|
fds = alloca (sizeof fds * (num_poll_fd + 2));
|
|
|
|
/* This is used as an optimization to not have to loop over the
|
|
entire descriptor list twice. */
|
|
pollfds = alloca (sizeof *pollfds * num_poll_fd);
|
|
|
|
/* Run timers. This, and draining selection transfers, must be done
|
|
before setting up poll file descriptors, since timer callbacks
|
|
can change the write fd list. */
|
|
timeout = TimerCheck ();
|
|
|
|
/* Drain complete selection transfers. */
|
|
FinishTransfers ();
|
|
|
|
x_connection = ConnectionNumber (compositor.display);
|
|
wl_connection = wl_event_loop_get_fd (compositor.wl_event_loop);
|
|
|
|
fds[0].fd = x_connection;
|
|
fds[1].fd = wl_connection;
|
|
fds[0].events = POLLIN;
|
|
fds[1].events = POLLIN;
|
|
fds[0].revents = 0;
|
|
fds[1].revents = 0;
|
|
|
|
/* Copy valid write file descriptors into the pollfd array, while
|
|
removing invalid ones. */
|
|
item = poll_fds.next;
|
|
i = 0;
|
|
|
|
while (item != &poll_fds)
|
|
{
|
|
last = item;
|
|
item = item->next;
|
|
|
|
if (last->write_fd == -1)
|
|
{
|
|
/* Remove this invalid pollfd. */
|
|
RemoveWriteFd (last);
|
|
continue;
|
|
}
|
|
|
|
/* Otherwise, add the fd and bump i. */
|
|
|
|
fds[2 + i].fd = last->write_fd;
|
|
fds[2 + i].events = POLLOUT;
|
|
fds[2 + i].revents = 0;
|
|
pollfds[i] = last;
|
|
|
|
if (!last->direction)
|
|
/* See https://www.greenend.org.uk/rjk/tech/poll.html for why
|
|
POLLHUP. */
|
|
fds[2 + i].events = POLLIN | POLLHUP;
|
|
|
|
i += 1;
|
|
}
|
|
|
|
/* Handle any events already in the queue, which can happen if
|
|
something inside ReadXEvents synced. */
|
|
if (XEventsQueued (compositor.display, QueuedAlready))
|
|
{
|
|
ReadXEvents ();
|
|
|
|
XFlush (compositor.display);
|
|
wl_display_flush_clients (compositor.wl_display);
|
|
}
|
|
|
|
rc = ppoll (fds, 2 + i, &timeout, NULL);
|
|
|
|
if (rc > 0)
|
|
{
|
|
if (fds[0].revents & POLLIN)
|
|
ReadXEvents ();
|
|
|
|
if (fds[1].revents & POLLIN)
|
|
wl_event_loop_dispatch (compositor.wl_event_loop, -1);
|
|
|
|
/* Now see how many write fds are set. */
|
|
for (j = 0; j < i; ++j)
|
|
{
|
|
if (fds[2 + j].revents & (POLLOUT | POLLIN | POLLHUP)
|
|
/* Check that pollfds[j] is still valid, and wasn't
|
|
removed while handling X events. */
|
|
&& pollfds[j]->write_fd != -1)
|
|
/* Then call the poll callback. */
|
|
pollfds[j]->poll_callback (pollfds[j]->write_fd,
|
|
pollfds[j]->data, pollfds[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void __attribute__ ((noreturn))
|
|
XLRunCompositor (void)
|
|
{
|
|
/* Set up the sentinel node for file descriptors that are being
|
|
polled from. */
|
|
poll_fds.next = &poll_fds;
|
|
poll_fds.last = &poll_fds;
|
|
|
|
while (true)
|
|
RunStep ();
|
|
}
|