windowmaker-wl/WINGs/wevent_x11.c

353 lines
13 KiB
C

/*
* wevent_x11.c — X11 event backend for WINGs.
*
* Wraps Xlib event functions and translates XEvent → WMEvent at the
* boundary. All code above this layer sees only WMEvent.
*/
#include "wconfig.h"
#include "WINGsP.h"
#include "WINGs/WINGsX11.h"
#include <string.h>
#include <X11/Xlib.h>
/* ------------------------------------------------------------------ */
/* XEvent → WMEvent translation (inline, X11 backend only) */
/* ------------------------------------------------------------------ */
static void
xev_to_wme(const XEvent *xev, WMEvent *out)
{
memset(out, 0, sizeof(*out));
out->_x11_raw = (void *)xev;
out->window = (WNativeWindow)xev->xany.window;
switch (xev->type) {
case KeyPress:
case KeyRelease:
out->type = (xev->type == KeyPress) ? WME_KEY_PRESS : WME_KEY_RELEASE;
out->u.key.window = (WNativeWindow)xev->xkey.window;
out->u.key.keycode = xev->xkey.keycode;
out->u.key.state = xev->xkey.state;
out->u.key.time = (unsigned long)xev->xkey.time;
out->u.key.x = xev->xkey.x;
out->u.key.y = xev->xkey.y;
out->u.key.x_root = xev->xkey.x_root;
out->u.key.y_root = xev->xkey.y_root;
break;
case ButtonPress:
case ButtonRelease:
out->type = (xev->type == ButtonPress) ? WME_BUTTON_PRESS : WME_BUTTON_RELEASE;
out->u.button.window = (WNativeWindow)xev->xbutton.window;
out->u.button.subwindow = (WNativeWindow)xev->xbutton.subwindow;
out->u.button.button = xev->xbutton.button;
out->u.button.state = xev->xbutton.state;
out->u.button.x = xev->xbutton.x;
out->u.button.y = xev->xbutton.y;
out->u.button.x_root = xev->xbutton.x_root;
out->u.button.y_root = xev->xbutton.y_root;
out->u.button.time = (unsigned long)xev->xbutton.time;
out->u.button.send_event = xev->xbutton.send_event;
break;
case MotionNotify:
out->type = WME_MOTION;
out->u.motion.window = (WNativeWindow)xev->xmotion.window;
out->u.motion.subwindow = (WNativeWindow)xev->xmotion.subwindow;
out->u.motion.state = xev->xmotion.state;
out->u.motion.time = (unsigned long)xev->xmotion.time;
out->u.motion.x = xev->xmotion.x;
out->u.motion.y = xev->xmotion.y;
out->u.motion.x_root = xev->xmotion.x_root;
out->u.motion.y_root = xev->xmotion.y_root;
break;
case EnterNotify:
case LeaveNotify:
out->type = (xev->type == EnterNotify) ? WME_ENTER : WME_LEAVE;
out->u.crossing.window = (WNativeWindow)xev->xcrossing.window;
out->u.crossing.state = xev->xcrossing.state;
out->u.crossing.time = (unsigned long)xev->xcrossing.time;
out->u.crossing.x = xev->xcrossing.x;
out->u.crossing.y = xev->xcrossing.y;
out->u.crossing.x_root = xev->xcrossing.x_root;
out->u.crossing.y_root = xev->xcrossing.y_root;
out->u.crossing.mode = xev->xcrossing.mode;
out->u.crossing.detail = xev->xcrossing.detail;
break;
case Expose:
out->type = WME_EXPOSE;
out->u.expose.window = (WNativeWindow)xev->xexpose.window;
out->u.expose.x = xev->xexpose.x;
out->u.expose.y = xev->xexpose.y;
out->u.expose.width = xev->xexpose.width;
out->u.expose.height = xev->xexpose.height;
out->u.expose.count = xev->xexpose.count;
break;
case ConfigureNotify:
out->type = WME_CONFIGURE;
out->u.configure.window = (WNativeWindow)xev->xconfigure.window;
out->u.configure.event = (WNativeWindow)xev->xconfigure.event;
out->u.configure.x = xev->xconfigure.x;
out->u.configure.y = xev->xconfigure.y;
out->u.configure.width = xev->xconfigure.width;
out->u.configure.height = xev->xconfigure.height;
out->u.configure.border_width = xev->xconfigure.border_width;
out->u.configure.above = (WNativeWindow)xev->xconfigure.above;
out->u.configure.override_redirect = xev->xconfigure.override_redirect;
break;
case MapNotify:
out->type = WME_MAP;
out->u.lifecycle.window = (WNativeWindow)xev->xmap.window;
out->u.lifecycle.event = (WNativeWindow)xev->xmap.event;
out->u.lifecycle.override_redirect = xev->xmap.override_redirect;
break;
case UnmapNotify:
out->type = WME_UNMAP;
out->u.lifecycle.window = (WNativeWindow)xev->xunmap.window;
out->u.lifecycle.event = (WNativeWindow)xev->xunmap.event;
break;
case DestroyNotify:
out->type = WME_DESTROY;
out->u.lifecycle.window = (WNativeWindow)xev->xdestroywindow.window;
out->u.lifecycle.event = (WNativeWindow)xev->xdestroywindow.event;
break;
case FocusIn:
case FocusOut:
out->type = (xev->type == FocusIn) ? WME_FOCUS_IN : WME_FOCUS_OUT;
out->u.focus.window = (WNativeWindow)xev->xfocus.window;
out->u.focus.mode = xev->xfocus.mode;
out->u.focus.detail = xev->xfocus.detail;
break;
case PropertyNotify:
out->type = WME_PROPERTY;
out->u.property.window = (WNativeWindow)xev->xproperty.window;
out->u.property.atom = (unsigned long)xev->xproperty.atom;
out->u.property.time = (unsigned long)xev->xproperty.time;
out->u.property.state = xev->xproperty.state;
break;
case SelectionNotify:
out->type = WME_SELECTION;
out->u.selection.window = (WNativeWindow)xev->xselection.requestor;
out->u.selection.selection = (unsigned long)xev->xselection.selection;
out->u.selection.target = (unsigned long)xev->xselection.target;
out->u.selection.property = (unsigned long)xev->xselection.property;
out->u.selection.time = (unsigned long)xev->xselection.time;
break;
case SelectionRequest:
out->type = WME_SELECTION_REQUEST;
out->u.selection.window = (WNativeWindow)xev->xselectionrequest.owner;
out->u.selection.requestor = (WNativeWindow)xev->xselectionrequest.requestor;
out->u.selection.selection = (unsigned long)xev->xselectionrequest.selection;
out->u.selection.target = (unsigned long)xev->xselectionrequest.target;
out->u.selection.property = (unsigned long)xev->xselectionrequest.property;
out->u.selection.time = (unsigned long)xev->xselectionrequest.time;
break;
case SelectionClear:
out->type = WME_SELECTION_CLEAR;
out->u.selection.window = (WNativeWindow)xev->xselectionclear.window;
out->u.selection.selection = (unsigned long)xev->xselectionclear.selection;
out->u.selection.time = (unsigned long)xev->xselectionclear.time;
break;
case ClientMessage:
out->type = WME_CLIENT_MESSAGE;
out->u.clientMessage.window = (WNativeWindow)xev->xclient.window;
out->u.clientMessage.message_type = (unsigned long)xev->xclient.message_type;
out->u.clientMessage.format = xev->xclient.format;
memcpy(out->u.clientMessage.data.b, xev->xclient.data.b, 20);
break;
case VisibilityNotify:
out->type = WME_VISIBILITY;
out->u.visibility.window = (WNativeWindow)xev->xvisibility.window;
out->u.visibility.state = xev->xvisibility.state;
break;
case ColormapNotify:
out->type = WME_COLORMAP;
out->u.colormap.window = (WNativeWindow)xev->xcolormap.window;
out->u.colormap.colormap = (unsigned long)xev->xcolormap.colormap;
out->u.colormap.c_new = xev->xcolormap.new;
out->u.colormap.state = xev->xcolormap.state;
break;
case MappingNotify:
out->type = WME_MAPPING;
out->u.mapping.request = xev->xmapping.request;
out->u.mapping.first_keycode = xev->xmapping.first_keycode;
out->u.mapping.count = xev->xmapping.count;
break;
default:
out->type = WME_NONE;
break;
}
}
/* ------------------------------------------------------------------ */
/* WMEvent → XEvent reverse translation (for put_back) */
/* We store the _x11_raw pointer, so put_back just uses that. */
/* For events without _x11_raw (shouldn't happen in X11 mode), no-op. */
/* ------------------------------------------------------------------ */
/* ------------------------------------------------------------------ */
/* Backend vtable implementation */
/* ------------------------------------------------------------------ */
/* Per-screen context: just the Display pointer. */
typedef struct {
Display *dpy;
/* We need to store the last fetched XEvent so _x11_raw remains valid
* until the next fetch. Use a small ring buffer. */
XEvent xev_buf[4];
int xev_idx;
} X11EventCtx;
static void *
x11_screen_init(W_Screen *scr)
{
X11EventCtx *ctx = wmalloc(sizeof(*ctx));
memset(ctx, 0, sizeof(*ctx));
ctx->dpy = scr->display;
return ctx;
}
static void
x11_screen_fini(void *ectx)
{
wfree(ectx);
}
static Bool
x11_pending(void *ectx)
{
X11EventCtx *ctx = ectx;
return XPending(ctx->dpy) > 0;
}
static void
x11_fetch(void *ectx, WMEvent *ev)
{
X11EventCtx *ctx = ectx;
XEvent *slot = &ctx->xev_buf[ctx->xev_idx];
ctx->xev_idx = (ctx->xev_idx + 1) % 4;
XNextEvent(ctx->dpy, slot);
/* X11-specific pre-processing */
if (slot->type == MappingNotify)
XRefreshKeyboardMapping(&slot->xmapping);
if (XFilterEvent(slot, WM_NATIVE_NONE) == True) {
/* Consumed by input method — return a no-op event */
memset(ev, 0, sizeof(*ev));
ev->type = WME_NONE;
ev->_view_target = NULL;
return;
}
xev_to_wme(slot, ev);
ev->_view_target = W_GetViewForXWindow(ctx->dpy, slot->xany.window);
}
static Bool
x11_check_mask(void *ectx, long mask, WMEvent *ev)
{
X11EventCtx *ctx = ectx;
XEvent *slot = &ctx->xev_buf[ctx->xev_idx];
if (XCheckMaskEvent(ctx->dpy, mask, slot)) {
ctx->xev_idx = (ctx->xev_idx + 1) % 4;
xev_to_wme(slot, ev);
ev->_view_target = W_GetViewForXWindow(ctx->dpy, slot->xany.window);
return True;
}
return False;
}
static Bool
x11_check_typed_window(void *ectx, WNativeWindow w, int type, WMEvent *ev)
{
X11EventCtx *ctx = ectx;
/* Map WMEventType back to X11 event type for XCheckTypedWindowEvent.
* This is only called for WME_EXPOSE (Expose compression). */
int xtype;
switch (type) {
case WME_EXPOSE: xtype = Expose; break;
case WME_MOTION: xtype = MotionNotify; break;
default: return False;
}
XEvent *slot = &ctx->xev_buf[ctx->xev_idx];
if (XCheckTypedWindowEvent(ctx->dpy, (Window)w, xtype, slot)) {
ctx->xev_idx = (ctx->xev_idx + 1) % 4;
xev_to_wme(slot, ev);
ev->_view_target = W_GetViewForXWindow(ctx->dpy, slot->xany.window);
return True;
}
return False;
}
static void
x11_flush(void *ectx, Bool discard)
{
X11EventCtx *ctx = ectx;
XSync(ctx->dpy, discard);
}
static int
x11_fd(void *ectx)
{
X11EventCtx *ctx = ectx;
return ConnectionNumber(ctx->dpy);
}
static void
x11_put_back(void *ectx, WMEvent *ev)
{
X11EventCtx *ctx = ectx;
/* If we have the original XEvent, put it back via Xlib */
if (ev->_x11_raw) {
XPutBackEvent(ctx->dpy, (XEvent *)ev->_x11_raw);
}
/* If no _x11_raw (shouldn't happen in X11 mode), event is lost */
}
static void
x11_post_dispatch(void *ectx, WMEvent *ev, W_View *toplevel)
{
(void)ectx;
if (!ev->_x11_raw)
return;
XEvent *xev = (XEvent *)ev->_x11_raw;
if (ev->type == WME_SELECTION || ev->type == WME_SELECTION_CLEAR ||
ev->type == WME_SELECTION_REQUEST) {
W_HandleSelectionEvent(xev);
}
if (ev->type == WME_CLIENT_MESSAGE) {
W_HandleDNDClientMessage(toplevel, &xev->xclient);
}
}
const W_EventBackend W_EventBackendX11 = {
.screen_init = x11_screen_init,
.screen_fini = x11_screen_fini,
.pending = x11_pending,
.fetch = x11_fetch,
.check_mask = x11_check_mask,
.check_typed_window = x11_check_typed_window,
.flush = x11_flush,
.fd = x11_fd,
.put_back = x11_put_back,
.post_dispatch = x11_post_dispatch,
};