[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 <signal.h>
#include <string.h> #include <string.h>
#include <sys/epoll.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <sys/socket.h> #include <sys/socket.h>
@@ -105,6 +106,14 @@ static bool getCompositor(char * dst, size_t size)
return true; 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) static bool waylandInit(const LG_DSInitParams params)
{ {
memset(&wlWm, 0, sizeof(wlWm)); memset(&wlWm, 0, sizeof(wlWm));
@@ -121,6 +130,7 @@ static bool waylandInit(const LG_DSInitParams params)
atomic_init(&wlWm.hdrScRGBWhiteLevel, 80); atomic_init(&wlWm.hdrScRGBWhiteLevel, 80);
atomic_init(&wlWm.pendingResize, 0); atomic_init(&wlWm.pendingResize, 0);
wlWm.resizeEventFd = -1; wlWm.resizeEventFd = -1;
wlWm.eventSource = params.eventSource;
wlWm.display = wl_display_connect(NULL); wlWm.display = wl_display_connect(NULL);
if (!wlWm.display) if (!wlWm.display)
@@ -152,6 +162,14 @@ static bool waylandInit(const LG_DSInitParams params)
if (!waylandPollInit()) if (!waylandPollInit())
return false; 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()) if (!waylandOutputInit())
return false; return false;

View File

@@ -283,6 +283,7 @@ struct WaylandDSState
unsigned int pollWaiters; unsigned int pollWaiters;
int epollFd; int epollFd;
int displayFd; int displayFd;
LG_DSEventSource eventSource;
}; };
struct WCBTransfer struct WCBTransfer

View File

@@ -443,6 +443,7 @@ static bool x11Init(const LG_DSInitParams params)
x11InputInit(&x11.input, &inputSink, NULL); x11InputInit(&x11.input, &inputSink, NULL);
LG_LOCK_INIT(x11.pointerLock); LG_LOCK_INIT(x11.pointerLock);
atomic_init(&x11.captureActive, false); atomic_init(&x11.captureActive, false);
x11.eventSource = params.eventSource;
x11.xValuator = -1; x11.xValuator = -1;
x11.yValuator = -1; x11.yValuator = -1;
x11.numLockIndicator = -1; x11.numLockIndicator = -1;
@@ -1063,6 +1064,12 @@ static bool x11GetProp(LG_DSProperty prop, void *ret)
static int x11EventThread(void * unused) static int x11EventThread(void * unused)
{ {
enum
{
EPOLL_SOURCE_X11,
EPOLL_SOURCE_EXTERNAL,
};
int epollfd = epoll_create1(0); int epollfd = epoll_create1(0);
if (epollfd == -1) if (epollfd == -1)
{ {
@@ -1070,7 +1077,11 @@ static int x11EventThread(void * unused)
return 0; 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); const int fd = ConnectionNumber(x11.display);
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1)
{ {
@@ -1079,6 +1090,18 @@ static int x11EventThread(void * unused)
return 0; 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()) while(app_isRunning())
{ {
const uint64_t lastWMEvent = atomic_load(&x11.lastWMEvent); const uint64_t lastWMEvent = atomic_load(&x11.lastWMEvent);
@@ -1088,10 +1111,10 @@ static int x11EventThread(void * unused)
app_invalidateWindow(true); app_invalidateWindow(true);
} }
if (!XPending(x11.display)) const bool xPending = XPending(x11.display) != 0;
{ struct epoll_event events[2];
struct epoll_event events[1]; const int nfds = epoll_wait(epollfd, events, ARRAY_LENGTH(events),
int nfds = epoll_wait(epollfd, events, 1, 100); xPending ? 0 : pollEventSource ? 10 : 100);
if (nfds == -1) if (nfds == -1)
{ {
if (errno == EINTR) if (errno == EINTR)
@@ -1102,9 +1125,15 @@ static int x11EventThread(void * unused)
return 0; return 0;
} }
if (nfds == 0 || !XPending(x11.display)) 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; continue;
}
XEvent xe; XEvent xe;
XNextEvent(x11.display, &xe); XNextEvent(x11.display, &xe);

View File

@@ -102,6 +102,7 @@ struct X11DSState
int yValuator; int yValuator;
X11Input input; X11Input input;
LG_DSEventSource eventSource;
LG_Lock pointerLock; LG_Lock pointerLock;
_Atomic(bool) captureActive; _Atomic(bool) captureActive;
bool fullscreen; bool fullscreen;

View File

@@ -103,6 +103,18 @@ LG_DSWaitFrameResult;
#define LG_POINTER_COUNT (LG_POINTER_NOT_ALLOWED + 1) #define LG_POINTER_COUNT (LG_POINTER_NOT_ALLOWED + 1)
typedef void (*LG_DSEventCallback)(void * opaque);
typedef struct LG_DSEventSource
{
/* The callback must be nonblocking and tolerate spurious calls. The caller
* retains ownership of fd and opaque until display-server shutdown. */
int fd;
LG_DSEventCallback callback;
void * opaque;
}
LG_DSEventSource;
typedef struct LG_DSInitParams typedef struct LG_DSInitParams
{ {
const char * title; const char * title;
@@ -122,6 +134,8 @@ typedef struct LG_DSInitParams
// x11 needs to know if this is in use so we can decide to setup for // x11 needs to know if this is in use so we can decide to setup for
// presentation times // presentation times
bool jitRender; bool jitRender;
LG_DSEventSource eventSource;
} }
LG_DSInitParams; LG_DSInitParams;