[client] displayserver: support external event sources

Allow display servers to dispatch a caller-owned event source on their
native event thread. This lets background input readers marshal events
to the thread that owns application and display-server state.

Poll X11 fairly when native events remain continuously available, and
use bounded polling if the external descriptor cannot be registered.
This commit is contained in:
Geoffrey McRae
2026-08-21 05:43:11 +10:00
parent 71733433ed
commit 8b38546049
5 changed files with 89 additions and 26 deletions

View File

@@ -23,6 +23,7 @@
#include <signal.h>
#include <string.h>
#include <sys/epoll.h>
#include <wayland-client.h>
#include <sys/socket.h>
@@ -105,6 +106,14 @@ static bool getCompositor(char * dst, size_t size)
return true;
}
static void eventSourceCallback(uint32_t events, void * opaque)
{
(void)events;
const LG_DSEventSource * source = (const LG_DSEventSource *)opaque;
source->callback(source->opaque);
}
static bool waylandInit(const LG_DSInitParams params)
{
memset(&wlWm, 0, sizeof(wlWm));
@@ -121,6 +130,7 @@ static bool waylandInit(const LG_DSInitParams params)
atomic_init(&wlWm.hdrScRGBWhiteLevel, 80);
atomic_init(&wlWm.pendingResize, 0);
wlWm.resizeEventFd = -1;
wlWm.eventSource = params.eventSource;
wlWm.display = wl_display_connect(NULL);
if (!wlWm.display)
@@ -152,6 +162,14 @@ static bool waylandInit(const LG_DSInitParams params)
if (!waylandPollInit())
return false;
if (wlWm.eventSource.fd >= 0 && wlWm.eventSource.callback &&
!waylandPollRegister(wlWm.eventSource.fd, eventSourceCallback,
&wlWm.eventSource, EPOLLIN))
{
DEBUG_ERROR("Failed to register the external event source");
return false;
}
if (!waylandOutputInit())
return false;

View File

@@ -276,13 +276,14 @@ struct WaylandDSState
_Atomic(unsigned) frameEventFlags;
LGEvent * frameEvent;
struct wl_list poll; // WaylandPoll::link
struct wl_list pollFree; // WaylandPoll::link
LG_Lock pollLock;
LG_Lock pollFreeLock;
unsigned int pollWaiters;
int epollFd;
int displayFd;
struct wl_list poll; // WaylandPoll::link
struct wl_list pollFree; // WaylandPoll::link
LG_Lock pollLock;
LG_Lock pollFreeLock;
unsigned int pollWaiters;
int epollFd;
int displayFd;
LG_DSEventSource eventSource;
};
struct WCBTransfer

View File

@@ -443,6 +443,7 @@ static bool x11Init(const LG_DSInitParams params)
x11InputInit(&x11.input, &inputSink, NULL);
LG_LOCK_INIT(x11.pointerLock);
atomic_init(&x11.captureActive, false);
x11.eventSource = params.eventSource;
x11.xValuator = -1;
x11.yValuator = -1;
x11.numLockIndicator = -1;
@@ -1063,6 +1064,12 @@ static bool x11GetProp(LG_DSProperty prop, void *ret)
static int x11EventThread(void * unused)
{
enum
{
EPOLL_SOURCE_X11,
EPOLL_SOURCE_EXTERNAL,
};
int epollfd = epoll_create1(0);
if (epollfd == -1)
{
@@ -1070,7 +1077,11 @@ static int x11EventThread(void * unused)
return 0;
}
struct epoll_event ev = { .events = EPOLLIN };
struct epoll_event ev =
{
.events = EPOLLIN,
.data.u32 = EPOLL_SOURCE_X11,
};
const int fd = ConnectionNumber(x11.display);
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1)
{
@@ -1079,6 +1090,18 @@ static int x11EventThread(void * unused)
return 0;
}
bool pollEventSource = false;
if (x11.eventSource.fd >= 0 && x11.eventSource.callback)
{
ev.data.u32 = EPOLL_SOURCE_EXTERNAL;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD,
x11.eventSource.fd, &ev) == -1)
{
DEBUG_WARN("Failed to register the external event source; polling");
pollEventSource = true;
}
}
while(app_isRunning())
{
const uint64_t lastWMEvent = atomic_load(&x11.lastWMEvent);
@@ -1088,24 +1111,30 @@ static int x11EventThread(void * unused)
app_invalidateWindow(true);
}
if (!XPending(x11.display))
const bool xPending = XPending(x11.display) != 0;
struct epoll_event events[2];
const int nfds = epoll_wait(epollfd, events, ARRAY_LENGTH(events),
xPending ? 0 : pollEventSource ? 10 : 100);
if (nfds == -1)
{
struct epoll_event events[1];
int nfds = epoll_wait(epollfd, events, 1, 100);
if (nfds == -1)
{
if (errno == EINTR)
continue;
close(epollfd);
DEBUG_ERROR("epoll_wait failure");
return 0;
}
if (nfds == 0 || !XPending(x11.display))
if (errno == EINTR)
continue;
close(epollfd);
DEBUG_ERROR("epoll_wait failure");
return 0;
}
for (int i = 0; i < nfds; ++i)
if (events[i].data.u32 == EPOLL_SOURCE_EXTERNAL)
x11.eventSource.callback(x11.eventSource.opaque);
if (pollEventSource)
x11.eventSource.callback(x11.eventSource.opaque);
if (!XPending(x11.display))
continue;
XEvent xe;
XNextEvent(x11.display, &xe);

View File

@@ -101,10 +101,11 @@ struct X11DSState
int xValuator;
int yValuator;
X11Input input;
LG_Lock pointerLock;
_Atomic(bool) captureActive;
bool fullscreen;
X11Input input;
LG_DSEventSource eventSource;
LG_Lock pointerLock;
_Atomic(bool) captureActive;
bool fullscreen;
struct Rect rect;
struct Border border;