windowmaker-wl/WINGs/wevent_wl.c

259 lines
7.5 KiB
C

#define WRASTER_NO_XLIB /* Wayland-only file: no libX11 deps */
/*
* wevent_wl.c — Wayland event backend for WINGs.
*
* Events arrive via wlroots input callbacks (keyboard, pointer, touch) in
* wl_backend.c, which call W_WL_EnqueueEvent(). This backend dequeues them
* in WMNextEvent / WMMaskEvent without touching any Xlib function.
*
* For modal loops (e.g. dialog.c) a dispatch callback is registered via
* W_WL_SetLoopDispatch() so the compositor event loop can be ticked while
* waiting for the next WINGs event.
*/
#include "wconfig.h"
#include "WINGsP.h"
#include <string.h>
/* ------------------------------------------------------------------ */
#define WL_EVENT_QUEUE_CAP 512
typedef struct {
WMEvent ev;
W_View *target; /* direct view routing; NULL = use ev.window */
} WLQEntry;
typedef struct {
WLQEntry buf[WL_EVENT_QUEUE_CAP];
int head; /* next read position */
int tail; /* next write position */
int loop_fd; /* wl_event_loop fd for W_HandleInputEvents */
void (*dispatch_fn)(int timeout_ms, void *ud);
void *dispatch_ud;
} WLEventCtx;
/* ------------------------------------------------------------------ */
static void *
wl_screen_init(W_Screen *scr)
{
(void)scr;
WLEventCtx *ctx = wmalloc(sizeof(*ctx));
memset(ctx, 0, sizeof(*ctx));
ctx->loop_fd = -1;
return ctx;
}
static void
wl_screen_fini(void *ectx)
{
wfree(ectx);
}
static Bool
wl_pending(void *ectx)
{
WLEventCtx *ctx = ectx;
return ctx->head != ctx->tail;
}
static void
wl_fetch(void *ectx, WMEvent *ev)
{
WLEventCtx *ctx = ectx;
WLQEntry *e = &ctx->buf[ctx->head];
*ev = e->ev;
ev->_view_target = e->target;
if (e->target && !W_IsValidView(e->target))
ev->_view_target = NULL;
if (e->target) {
W_Screen *scr = e->target->screen;
if (scr)
scr->event_target = e->target;
}
ctx->head = (ctx->head + 1) % WL_EVENT_QUEUE_CAP;
}
/* Event mask table for matching — same values as wevent.c's wmeventMasks */
static unsigned long
wme_mask(int type)
{
switch (type) {
case WME_KEY_PRESS: return WM_EVMASK_KEY_PRESS;
case WME_KEY_RELEASE: return WM_EVMASK_KEY_RELEASE;
case WME_BUTTON_PRESS: return WM_EVMASK_BUTTON_PRESS;
case WME_BUTTON_RELEASE: return WM_EVMASK_BUTTON_RELEASE;
case WME_MOTION: return WM_EVMASK_POINTER_MOTION | WM_EVMASK_BUTTON_MOTION;
case WME_ENTER: return WM_EVMASK_ENTER_WINDOW;
case WME_LEAVE: return WM_EVMASK_LEAVE_WINDOW;
case WME_EXPOSE: return WM_EVMASK_EXPOSURE;
case WME_CONFIGURE: return WM_EVMASK_STRUCTURE_NOTIFY;
case WME_MAP: return WM_EVMASK_STRUCTURE_NOTIFY;
case WME_UNMAP: return WM_EVMASK_STRUCTURE_NOTIFY;
case WME_DESTROY: return WM_EVMASK_STRUCTURE_NOTIFY;
case WME_FOCUS_IN:
case WME_FOCUS_OUT: return WM_EVMASK_FOCUS_CHANGE;
case WME_PROPERTY: return WM_EVMASK_PROPERTY_CHANGE;
case WME_COLORMAP: return WM_EVMASK_COLORMAP_CHANGE;
case WME_VISIBILITY: return WM_EVMASK_VISIBILITY_CHANGE;
default: return 0;
}
}
static Bool
wl_check_mask(void *ectx, long mask, WMEvent *ev)
{
WLEventCtx *ctx = ectx;
int i = ctx->head;
while (i != ctx->tail) {
unsigned long emask = wme_mask(ctx->buf[i].ev.type);
if (emask & (unsigned long)mask) {
*ev = ctx->buf[i].ev;
if (ev->_view_target && !W_IsValidView(ev->_view_target))
ev->_view_target = NULL;
/* Remove entry by shifting */
int next = (i + 1) % WL_EVENT_QUEUE_CAP;
while (next != ctx->tail) {
ctx->buf[i] = ctx->buf[next];
i = next;
next = (next + 1) % WL_EVENT_QUEUE_CAP;
}
ctx->tail = i;
return True;
}
i = (i + 1) % WL_EVENT_QUEUE_CAP;
}
return False;
}
static Bool
wl_check_typed_window(void *ectx, WNativeWindow w, int type, WMEvent *ev)
{
WLEventCtx *ctx = ectx;
int i = ctx->head;
while (i != ctx->tail) {
if (ctx->buf[i].ev.type == type &&
ctx->buf[i].ev.window == w) {
*ev = ctx->buf[i].ev;
if (ev->_view_target && !W_IsValidView(ev->_view_target))
ev->_view_target = NULL;
int next = (i + 1) % WL_EVENT_QUEUE_CAP;
while (next != ctx->tail) {
ctx->buf[i] = ctx->buf[next];
i = next;
next = (next + 1) % WL_EVENT_QUEUE_CAP;
}
ctx->tail = i;
return True;
}
i = (i + 1) % WL_EVENT_QUEUE_CAP;
}
return False;
}
static void
wl_flush(void *ectx, Bool discard)
{
WLEventCtx *ctx = ectx;
(void)discard;
/* Pump the wlroots event loop so pointer/key events arrive in our queue.
* This is called from waitForEvent/WMMaskEvent when the queue is empty. */
if (ctx->dispatch_fn)
ctx->dispatch_fn(16, ctx->dispatch_ud);
}
static int
wl_fd(void *ectx)
{
WLEventCtx *ctx = ectx;
return ctx->loop_fd;
}
static void
wl_put_back(void *ectx, WMEvent *ev)
{
WLEventCtx *ctx = ectx;
int prev = (ctx->head - 1 + WL_EVENT_QUEUE_CAP) % WL_EVENT_QUEUE_CAP;
if (prev == ctx->tail)
return; /* queue full — drop */
ctx->buf[prev].ev = *ev;
ctx->buf[prev].target = NULL;
ctx->head = prev;
}
const W_EventBackend W_EventBackendWL = {
.screen_init = wl_screen_init,
.screen_fini = wl_screen_fini,
.pending = wl_pending,
.fetch = wl_fetch,
.check_mask = wl_check_mask,
.check_typed_window = wl_check_typed_window,
.flush = wl_flush,
.fd = wl_fd,
.put_back = wl_put_back,
.post_dispatch = NULL,
};
/* ------------------------------------------------------------------ */
/* Called from wl_backend.c */
/* ------------------------------------------------------------------ */
void
W_WL_EnqueueEvent(W_Screen *scr, const WMEvent *ev, W_View *target)
{
if (!scr || !scr->eventBackendCtx)
return;
WLEventCtx *ctx = scr->eventBackendCtx;
int next_tail = (ctx->tail + 1) % WL_EVENT_QUEUE_CAP;
if (next_tail == ctx->head)
return; /* queue full — drop */
ctx->buf[ctx->tail].ev = *ev;
ctx->buf[ctx->tail].target = target;
ctx->tail = next_tail;
}
void
W_WL_SetLoopDispatch(W_Screen *scr,
int loop_fd,
void (*dispatch_fn)(int timeout_ms, void *ud),
void *userdata)
{
if (!scr || !scr->eventBackendCtx)
return;
WLEventCtx *ctx = scr->eventBackendCtx;
ctx->loop_fd = loop_fd;
ctx->dispatch_fn = dispatch_fn;
ctx->dispatch_ud = userdata;
}
/* Tick the Wayland compositor event loop once (used by modal loops). */
void
W_WL_InvalidateView(W_Screen *scr, W_View *target)
{
if (!scr || !scr->eventBackendCtx)
return;
WLEventCtx *ctx = scr->eventBackendCtx;
int i = ctx->head;
while (i != ctx->tail) {
if (ctx->buf[i].target == target) {
ctx->buf[i].target = NULL;
}
i = (i + 1) % WL_EVENT_QUEUE_CAP;
}
}
void
W_WL_DispatchLoop(W_Screen *scr, int timeout_ms)
{
if (!scr || !scr->eventBackendCtx)
return;
WLEventCtx *ctx = scr->eventBackendCtx;
if (ctx->dispatch_fn)
ctx->dispatch_fn(timeout_ms, ctx->dispatch_ud);
}