[common/client] platform: abstract runtime services

This commit is contained in:
Geoffrey McRae
2026-09-12 12:51:17 +10:00
parent a7d2e4592f
commit eabc56a6de
49 changed files with 4064 additions and 940 deletions

View File

@@ -21,11 +21,14 @@
#define _GNU_SOURCE
#include "wayland.h"
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <wayland-client.h>
#include <sys/socket.h>
#include <unistd.h>
#include "common/debug.h"
#include "common/option.h"
@@ -106,12 +109,53 @@ static bool getCompositor(char * dst, size_t size)
return true;
}
static bool eventSourceWake(void * opaque)
{
const int fd = *(const int *)opaque;
int result;
do
result = eventfd_write(fd, 1);
while (result < 0 && errno == EINTR);
if (result == 0 || errno == EAGAIN)
return true;
DEBUG_WARN("Failed to signal the display event wake fd: %s",
strerror(errno));
return false;
}
static void eventSourceCallback(uint32_t events, void * opaque)
{
(void)events;
(void)opaque;
const LG_DSEventSource * source = (const LG_DSEventSource *)opaque;
source->callback(source->opaque);
eventfd_t value;
for (;;)
{
if (eventfd_read(wlWm.eventSourceFd, &value) == 0 || errno == EINTR)
continue;
if (errno != EAGAIN)
DEBUG_WARN("Failed to drain the display event wake fd: %s",
strerror(errno));
break;
}
wlWm.eventSource.dispatch(wlWm.eventSource.opaque);
}
static void eventSourceFree(void)
{
if (wlWm.eventSourceSubscribed)
{
wlWm.eventSource.unsubscribe(wlWm.eventSource.opaque);
wlWm.eventSourceSubscribed = false;
}
if (wlWm.eventSourceFd >= 0)
{
waylandPollUnregister(wlWm.eventSourceFd);
close(wlWm.eventSourceFd);
wlWm.eventSourceFd = -1;
}
}
static bool waylandInit(const LG_DSInitParams params)
@@ -130,6 +174,7 @@ static bool waylandInit(const LG_DSInitParams params)
atomic_init(&wlWm.hdrScRGBWhiteLevel, 80);
atomic_init(&wlWm.pendingResize, 0);
wlWm.resizeEventFd = -1;
wlWm.eventSourceFd = -1;
wlWm.eventSource = params.eventSource;
wlWm.display = wl_display_connect(NULL);
@@ -162,42 +207,90 @@ 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))
if (lgEventSourceValid(&wlWm.eventSource))
{
DEBUG_ERROR("Failed to register the external event source");
return false;
wlWm.eventSourceFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (wlWm.eventSourceFd < 0)
{
DEBUG_ERROR("Failed to create the display event wake fd: %s",
strerror(errno));
return false;
}
if (!waylandPollRegister(wlWm.eventSourceFd, eventSourceCallback,
NULL, EPOLLIN))
{
DEBUG_ERROR("Failed to register the display event wake fd");
close(wlWm.eventSourceFd);
wlWm.eventSourceFd = -1;
return false;
}
if (!wlWm.eventSource.subscribe(wlWm.eventSource.opaque,
eventSourceWake, &wlWm.eventSourceFd))
{
DEBUG_ERROR("Failed to subscribe to the display event source");
eventSourceFree();
return false;
}
wlWm.eventSourceSubscribed = true;
}
if (!waylandOutputInit())
{
eventSourceFree();
return false;
}
if (!waylandRegistryInit())
{
eventSourceFree();
return false;
}
if (!waylandColorMgmtInit())
{
eventSourceFree();
return false;
}
if (!waylandActivationInit())
{
eventSourceFree();
return false;
}
if (!waylandIdleInit())
{
eventSourceFree();
return false;
}
if (!waylandPresentationInit())
{
eventSourceFree();
return false;
}
if (!waylandCursorInit())
{
eventSourceFree();
return false;
}
if (!waylandInputInit(params.allowNoInput))
{
eventSourceFree();
return false;
}
wlWm.desktop->setSize(params.w, params.h);
if (!waylandWindowInit(params.title, params.appId, params.fullscreen, params.maximize,
params.borderless, params.resizable))
{
eventSourceFree();
return false;
}
int width, height;
wlWm.desktop->getSize(&width, &height);
@@ -206,6 +299,7 @@ static bool waylandInit(const LG_DSInitParams params)
waylandScaleMulInt(wlWm.scale, height)))
{
waylandWindowFree();
eventSourceFree();
return false;
}
@@ -218,6 +312,7 @@ static bool waylandInit(const LG_DSInitParams params)
if (params.opengl && !waylandOpenGLInit())
{
waylandWindowFree();
eventSourceFree();
return false;
}
#endif
@@ -235,6 +330,7 @@ static void waylandShutdown(void)
static void waylandFree(void)
{
eventSourceFree();
waylandCBFree();
waylandIdleFree();
waylandPresentationFree();

View File

@@ -283,6 +283,8 @@ struct WaylandDSState
unsigned int pollWaiters;
int epollFd;
int displayFd;
int eventSourceFd;
bool eventSourceSubscribed;
LG_DSEventSource eventSource;
};

View File

@@ -70,6 +70,7 @@ enum WindowsMessage
WM_LG_ACTIVATE,
WM_LG_INHIBIT_IDLE,
WM_LG_SET_HANDLER,
WM_LG_EVENT_SOURCE,
};
struct WindowsPointMessage
@@ -104,6 +105,8 @@ struct WindowsDSState
WindowsInput input;
LG_DSEventSource eventSource;
bool eventSourceSubscribed;
WindowsMessageHandler messageHandler;
void * messageOpaque;
@@ -820,12 +823,33 @@ static void setCursorShape(LG_DSPointer pointer)
SetCursor(windows.cursors[pointer]);
}
static bool eventSourceWake(void * opaque)
{
const HWND window = (HWND)opaque;
if (PostMessageW(window, WM_LG_EVENT_SOURCE, 0, 0))
return true;
DEBUG_WINERROR("Failed to signal the display event source",
GetLastError());
return false;
}
static void eventSourceFree(void)
{
if (!windows.eventSourceSubscribed)
return;
windows.eventSource.unsubscribe(windows.eventSource.opaque);
windows.eventSourceSubscribed = false;
}
static LRESULT handleCustomMessage(UINT message, WPARAM wParam,
LPARAM lParam)
{
switch (message)
{
case WM_LG_SHUTDOWN:
eventSourceFree();
setKeyboardGrab(false);
setPointerGrab(false, false);
if (windows.idleInhibited)
@@ -894,6 +918,10 @@ static LRESULT handleCustomMessage(UINT message, WPARAM wParam,
windows.messageOpaque = handler->opaque;
return 0;
}
case WM_LG_EVENT_SOURCE:
windows.eventSource.dispatch(windows.eventSource.opaque);
return 0;
}
return 0;
@@ -902,7 +930,7 @@ static LRESULT handleCustomMessage(UINT message, WPARAM wParam,
static LRESULT CALLBACK windowProc(HWND window, UINT message,
WPARAM wParam, LPARAM lParam)
{
if (message >= WM_LG_SHUTDOWN && message <= WM_LG_SET_HANDLER)
if (message >= WM_LG_SHUTDOWN && message <= WM_LG_EVENT_SOURCE)
return handleCustomMessage(message, wParam, lParam);
if (windows.dispatchReady && windows.messageHandler)
@@ -1309,6 +1337,19 @@ static int eventThread(void * opaque)
return 0;
}
if (lgEventSourceValid(&windows.eventSource) &&
!windows.eventSource.subscribe(windows.eventSource.opaque,
eventSourceWake, windows.window))
{
DEBUG_ERROR("Failed to subscribe to the display event source");
DestroyWindow(windows.window);
windows.window = NULL;
UnregisterClassW(LG_WINDOW_CLASS, windows.instance);
lgSignalEvent(windows.initEvent);
return 0;
}
windows.eventSourceSubscribed = lgEventSourceValid(&windows.eventSource);
windows.dispatchReady = true;
windows.initOK = true;
updateWindowGeometry();
@@ -1325,6 +1366,7 @@ static int eventThread(void * opaque)
DispatchMessageW(&message);
}
eventSourceFree();
windows.dispatchReady = false;
windows.window = NULL;
removeKeyboardHook();
@@ -1446,6 +1488,7 @@ static bool windowsInit(const LG_DSInitParams params)
windowsInputInit(&windows.input, &inputSink, NULL);
windows.instance = GetModuleHandleW(NULL);
windows.eventSource = params.eventSource;
windows.title = lgUtf8ToWide(params.title);
windows.x = params.x;
windows.y = params.y;

View File

@@ -33,6 +33,7 @@
#include <time.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <errno.h>
#include <X11/extensions/XInput2.h>
@@ -1063,12 +1064,27 @@ static bool x11GetProp(LG_DSProperty prop, void *ret)
}
}
static bool x11EventSourceWake(void * opaque)
{
const int fd = *(const int *)opaque;
int result;
do
result = eventfd_write(fd, 1);
while (result < 0 && errno == EINTR);
if (result == 0 || errno == EAGAIN)
return true;
DEBUG_WARN("Failed to signal the display event wake fd: %s",
strerror(errno));
return false;
}
static int x11EventThread(void * unused)
{
enum
{
EPOLL_SOURCE_X11,
EPOLL_SOURCE_EXTERNAL,
EPOLL_SOURCE_EVENT,
};
int epollfd = epoll_create1(0);
@@ -1091,16 +1107,36 @@ static int x11EventThread(void * unused)
return 0;
}
int eventFd = -1;
bool eventSubscribed = false;
bool pollEventSource = false;
if (x11.eventSource.fd >= 0 && x11.eventSource.callback)
if (lgEventSourceValid(&x11.eventSource))
{
ev.data.u32 = EPOLL_SOURCE_EXTERNAL;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD,
x11.eventSource.fd, &ev) == -1)
eventFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (eventFd < 0)
{
DEBUG_WARN("Failed to register the external event source; polling");
DEBUG_WARN("Failed to create the display event wake fd: %s",
strerror(errno));
pollEventSource = true;
}
else
{
ev.data.u32 = EPOLL_SOURCE_EVENT;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, eventFd, &ev) == -1)
{
DEBUG_WARN("Failed to register the display event wake fd: %s",
strerror(errno));
pollEventSource = true;
}
else if (!x11.eventSource.subscribe(x11.eventSource.opaque,
x11EventSourceWake, &eventFd))
{
DEBUG_WARN("Failed to subscribe to the display event source");
pollEventSource = true;
}
else
eventSubscribed = true;
}
}
while(app_isRunning())
@@ -1121,17 +1157,28 @@ static int x11EventThread(void * unused)
if (errno == EINTR)
continue;
close(epollfd);
DEBUG_ERROR("epoll_wait failure");
return 0;
break;
}
for (int i = 0; i < nfds; ++i)
if (events[i].data.u32 == EPOLL_SOURCE_EXTERNAL)
x11.eventSource.callback(x11.eventSource.opaque);
if (events[i].data.u32 == EPOLL_SOURCE_EVENT)
{
eventfd_t value;
for (;;)
{
if (eventfd_read(eventFd, &value) == 0 || errno == EINTR)
continue;
if (errno != EAGAIN)
DEBUG_WARN("Failed to drain the display event wake fd: %s",
strerror(errno));
break;
}
x11.eventSource.dispatch(x11.eventSource.opaque);
}
if (pollEventSource)
x11.eventSource.callback(x11.eventSource.opaque);
x11.eventSource.dispatch(x11.eventSource.opaque);
if (!XPending(x11.display))
{
@@ -1310,6 +1357,10 @@ static int x11EventThread(void * unused)
}
}
if (eventSubscribed)
x11.eventSource.unsubscribe(x11.eventSource.opaque);
if (eventFd >= 0)
close(eventFd);
close(epollfd);
return 0;
}