790 lines
28 KiB
C
790 lines
28 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 <strings.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xutil.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);
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Keyboard/pointer grab */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static Bool
|
|
x11_grab_keyboard(void *ectx, WNativeWindow window)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
int r = XGrabKeyboard(ctx->dpy, (Window)window, True,
|
|
GrabModeAsync, GrabModeAsync, CurrentTime);
|
|
return (r == GrabSuccess) ? True : False;
|
|
}
|
|
|
|
static void
|
|
x11_ungrab_keyboard(void *ectx)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
XUngrabKeyboard(ctx->dpy, CurrentTime);
|
|
}
|
|
|
|
static void
|
|
x11_allow_events(void *ectx)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
XAllowEvents(ctx->dpy, AsyncKeyboard, CurrentTime);
|
|
}
|
|
|
|
static Bool
|
|
x11_grab_pointer(void *ectx, WNativeWindow window, WMNativePixmap cursor)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
int r = XGrabPointer(ctx->dpy, (Window)window, True,
|
|
ButtonPressMask | ButtonReleaseMask |
|
|
ButtonMotionMask | PointerMotionMask,
|
|
GrabModeAsync, GrabModeAsync,
|
|
None, (Cursor)cursor, CurrentTime);
|
|
return (r == GrabSuccess) ? True : False;
|
|
}
|
|
|
|
static void
|
|
x11_ungrab_pointer(void *ectx)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
XUngrabPointer(ctx->dpy, CurrentTime);
|
|
}
|
|
|
|
static WNativeWindow
|
|
x11_query_pointer(void *ectx, WNativeWindow window,
|
|
int *root_x, int *root_y,
|
|
int *win_x, int *win_y, unsigned int *mask)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
Window root_ret, child_ret;
|
|
int rx, ry, wx, wy;
|
|
unsigned int m;
|
|
XQueryPointer(ctx->dpy, (Window)window, &root_ret, &child_ret,
|
|
&rx, &ry, &wx, &wy, &m);
|
|
if (root_x) *root_x = rx;
|
|
if (root_y) *root_y = ry;
|
|
if (win_x) *win_x = wx;
|
|
if (win_y) *win_y = wy;
|
|
if (mask) *mask = m;
|
|
return (WNativeWindow)child_ret;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Window geometry */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static Bool
|
|
x11_get_window_geometry(void *ectx, WNativeWindow window,
|
|
int *x, int *y, unsigned int *width, unsigned int *height)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
Window root;
|
|
int xr, yr;
|
|
unsigned int w, h, bw, depth;
|
|
Status s = XGetGeometry(ctx->dpy, (Window)window, &root,
|
|
&xr, &yr, &w, &h, &bw, &depth);
|
|
if (!s) return False;
|
|
if (x) *x = xr;
|
|
if (y) *y = yr;
|
|
if (width) *width = w;
|
|
if (height) *height = h;
|
|
return True;
|
|
}
|
|
|
|
static void
|
|
x11_move_window(void *ectx, WNativeWindow window, int x, int y)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
XMoveWindow(ctx->dpy, (Window)window, x, y);
|
|
}
|
|
|
|
static Bool
|
|
x11_window_begin_move(void *ectx, WNativeWindow window)
|
|
{
|
|
(void)ectx; (void)window;
|
|
/* X11 uses manual drag loop with grab_pointer; no backend assist. */
|
|
return False;
|
|
}
|
|
|
|
static WNativeWindow
|
|
x11_translate_coordinates(void *ectx, WNativeWindow src, WNativeWindow dst,
|
|
int src_x, int src_y, int *dst_x, int *dst_y)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
Window child;
|
|
int dx, dy;
|
|
XTranslateCoordinates(ctx->dpy, (Window)src, (Window)dst,
|
|
src_x, src_y, &dx, &dy, &child);
|
|
if (dst_x) *dst_x = dx;
|
|
if (dst_y) *dst_y = dy;
|
|
return (WNativeWindow)child;
|
|
}
|
|
|
|
static WNativeWindow
|
|
x11_get_root_window(void *ectx)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
return (WNativeWindow)DefaultRootWindow(ctx->dpy);
|
|
}
|
|
|
|
static Bool
|
|
x11_query_tree(void *ectx, WNativeWindow window,
|
|
WNativeWindow **children, unsigned int *nchildren)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
Window root, parent;
|
|
Window *kids = NULL;
|
|
unsigned int nkids = 0;
|
|
if (!XQueryTree(ctx->dpy, (Window)window, &root, &parent, &kids, &nkids)) {
|
|
if (children) *children = NULL;
|
|
if (nchildren) *nchildren = 0;
|
|
return False;
|
|
}
|
|
if (children) {
|
|
if (nkids > 0) {
|
|
*children = wmalloc(nkids * sizeof(WNativeWindow));
|
|
for (unsigned int i = 0; i < nkids; i++)
|
|
(*children)[i] = (WNativeWindow)kids[i];
|
|
} else {
|
|
*children = NULL;
|
|
}
|
|
}
|
|
if (nchildren) *nchildren = nkids;
|
|
if (kids) XFree(kids);
|
|
return True;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Pointer control */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static void
|
|
x11_get_pointer_control(void *ectx, int *accel_num, int *accel_denom, int *threshold)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
XGetPointerControl(ctx->dpy, accel_num, accel_denom, threshold);
|
|
}
|
|
|
|
static void
|
|
x11_set_pointer_control(void *ectx, int accel_num, int accel_denom, int threshold)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
XChangePointerControl(ctx->dpy, True, True, accel_num, accel_denom, threshold);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Modifier mapping */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
#include <X11/keysym.h>
|
|
#include <X11/XKBlib.h>
|
|
|
|
static int x11_MetaIndex, x11_HyperIndex, x11_SuperIndex, x11_AltIndex, x11_ModeIndex;
|
|
static int x11_modmap_done;
|
|
|
|
static void
|
|
x11_compute_modifier_mapping(Display *display)
|
|
{
|
|
int modifier_index, modifier_key, column, mkpm;
|
|
int meta_bit = 0, hyper_bit = 0, super_bit = 0, alt_bit = 0, mode_bit = 0;
|
|
XModifierKeymap *xmk = XGetModifierMapping(display);
|
|
if (!xmk) return;
|
|
|
|
mkpm = xmk->max_keypermod;
|
|
for (modifier_index = 0; modifier_index < 8; modifier_index++) {
|
|
for (modifier_key = 0; modifier_key < mkpm; modifier_key++) {
|
|
KeyCode code = xmk->modifiermap[modifier_index * mkpm + modifier_key];
|
|
if (!code) continue;
|
|
for (column = 0; column < 4; column += 2) {
|
|
KeySym sym = XkbKeycodeToKeysym(display, code, 0, column);
|
|
if (sym == NoSymbol) continue;
|
|
switch (sym) {
|
|
case XK_Meta_L: case XK_Meta_R: meta_bit = modifier_index; break;
|
|
case XK_Super_L: case XK_Super_R: super_bit = modifier_index; break;
|
|
case XK_Hyper_L: case XK_Hyper_R: hyper_bit = modifier_index; break;
|
|
case XK_Alt_L: case XK_Alt_R: alt_bit = modifier_index; break;
|
|
case XK_Mode_switch: mode_bit = modifier_index; break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
XFreeModifiermap(xmk);
|
|
|
|
if (!meta_bit && alt_bit) meta_bit = alt_bit, alt_bit = 0;
|
|
if (mode_bit) {
|
|
if (mode_bit == meta_bit) meta_bit = 0;
|
|
else if (mode_bit == hyper_bit) hyper_bit = 0;
|
|
else if (mode_bit == super_bit) super_bit = 0;
|
|
else if (mode_bit == alt_bit) alt_bit = 0;
|
|
}
|
|
x11_MetaIndex = meta_bit;
|
|
x11_HyperIndex = hyper_bit;
|
|
x11_SuperIndex = super_bit;
|
|
x11_AltIndex = alt_bit;
|
|
x11_ModeIndex = mode_bit;
|
|
x11_modmap_done = 1;
|
|
}
|
|
|
|
static int
|
|
x11_modifier_from_key(void *ectx, const char *key)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
if (!x11_modmap_done)
|
|
x11_compute_modifier_mapping(ctx->dpy);
|
|
|
|
if (strcasecmp(key, "SHIFT") == 0) return ShiftMapIndex;
|
|
if (strcasecmp(key, "CONTROL") == 0) return ControlMapIndex;
|
|
if (strcasecmp(key, "ALT") == 0) return x11_AltIndex;
|
|
if (strcasecmp(key, "META") == 0) return x11_MetaIndex;
|
|
if (strcasecmp(key, "SUPER") == 0) return x11_SuperIndex;
|
|
if (strcasecmp(key, "HYPER") == 0) return x11_HyperIndex;
|
|
if (strcasecmp(key, "MOD1") == 0) return Mod1MapIndex;
|
|
if (strcasecmp(key, "MOD2") == 0) return Mod2MapIndex;
|
|
if (strcasecmp(key, "MOD3") == 0) return Mod3MapIndex;
|
|
if (strcasecmp(key, "MOD4") == 0) return Mod4MapIndex;
|
|
if (strcasecmp(key, "MOD5") == 0) return Mod5MapIndex;
|
|
return -1;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Keysym helpers */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static int
|
|
x11_get_modifier_keysyms(void *ectx, WMNativeKeysym *keysyms_out, int max_per_mod)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
XModifierKeymap *xmk = XGetModifierMapping(ctx->dpy);
|
|
if (!xmk) return 0;
|
|
|
|
int mkpm = xmk->max_keypermod;
|
|
int out_per_mod = (mkpm < max_per_mod) ? mkpm : max_per_mod;
|
|
|
|
memset(keysyms_out, 0, 8 * max_per_mod * sizeof(WMNativeKeysym));
|
|
|
|
for (int j = 0; j < 8; j++) {
|
|
int slot = 0;
|
|
for (int i = 0; i < mkpm && slot < out_per_mod; i++) {
|
|
KeyCode kc = xmk->modifiermap[j * mkpm + i];
|
|
if (!kc) continue;
|
|
KeySym ks = XkbKeycodeToKeysym(ctx->dpy, kc, 0, 0);
|
|
if (ks == NoSymbol) {
|
|
ks = XkbKeycodeToKeysym(ctx->dpy, kc, 0, 1);
|
|
}
|
|
if (ks != NoSymbol) {
|
|
keysyms_out[j * max_per_mod + slot] = (WMNativeKeysym)ks;
|
|
slot++;
|
|
}
|
|
}
|
|
}
|
|
XFreeModifiermap(xmk);
|
|
return mkpm;
|
|
}
|
|
|
|
static const char *
|
|
x11_keysym_to_string(void *ectx, WMNativeKeysym keysym)
|
|
{
|
|
(void)ectx;
|
|
return XKeysymToString((KeySym)keysym);
|
|
}
|
|
|
|
static unsigned int
|
|
x11_keysym_to_keycode(void *ectx, WMNativeKeysym keysym)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
return (unsigned int)XKeysymToKeycode(ctx->dpy, (KeySym)keysym);
|
|
}
|
|
|
|
static WMNativeKeysym
|
|
x11_keycode_to_keysym(void *ectx, unsigned int keycode, int index)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
return (WMNativeKeysym)XkbKeycodeToKeysym(ctx->dpy, (KeyCode)keycode, 0, index);
|
|
}
|
|
|
|
static void
|
|
x11_keysym_convert_case(void *ectx, WMNativeKeysym sym,
|
|
WMNativeKeysym *lower, WMNativeKeysym *upper)
|
|
{
|
|
(void)ectx;
|
|
KeySym lo, up;
|
|
XConvertCase((KeySym)sym, &lo, &up);
|
|
if (lower) *lower = (WMNativeKeysym)lo;
|
|
if (upper) *upper = (WMNativeKeysym)up;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* WM notification */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static void
|
|
x11_send_wm_command(void *ectx, const char *command)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
XEvent ev;
|
|
memset(&ev, 0, sizeof(ev));
|
|
ev.xclient.type = ClientMessage;
|
|
ev.xclient.message_type = XInternAtom(ctx->dpy, "_WINDOWMAKER_COMMAND", False);
|
|
ev.xclient.window = DefaultRootWindow(ctx->dpy);
|
|
ev.xclient.format = 8;
|
|
strncpy(ev.xclient.data.b, command, sizeof(ev.xclient.data.b));
|
|
XSendEvent(ctx->dpy, DefaultRootWindow(ctx->dpy), False,
|
|
SubstructureRedirectMask, &ev);
|
|
XFlush(ctx->dpy);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Window properties */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static void
|
|
x11_set_window_hints(void *ectx, WNativeWindow window,
|
|
int min_w, int min_h, int max_w, int max_h,
|
|
int base_w, int base_h)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
XSizeHints *hints = XAllocSizeHints();
|
|
if (!hints) return;
|
|
hints->flags = PMinSize | PMaxSize | PBaseSize;
|
|
hints->min_width = min_w; hints->min_height = min_h;
|
|
hints->max_width = max_w; hints->max_height = max_h;
|
|
hints->base_width = base_w; hints->base_height = base_h;
|
|
XSetWMNormalHints(ctx->dpy, (Window)window, hints);
|
|
XFree(hints);
|
|
}
|
|
|
|
static void
|
|
x11_set_window_property_string(void *ectx, WNativeWindow window,
|
|
const char *atom_name, const char *value)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
Atom atom = XInternAtom(ctx->dpy, atom_name, False);
|
|
XChangeProperty(ctx->dpy, (Window)window, atom, XA_STRING, 8,
|
|
PropModeReplace, (unsigned char *)value, strlen(value));
|
|
}
|
|
|
|
static void
|
|
x11_set_window_property_data(void *ectx, WNativeWindow window,
|
|
WMNativeAtom property, WMNativeAtom type,
|
|
int format, const void *data, int nelements)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
XChangeProperty(ctx->dpy, (Window)window, (Atom)property, (Atom)type,
|
|
format, PropModeReplace, (const unsigned char *)data, nelements);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Cursor */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static void
|
|
x11_recolor_cursor(void *ectx, WMNativePixmap cursor,
|
|
unsigned short fr, unsigned short fg, unsigned short fb,
|
|
unsigned short br, unsigned short bg, unsigned short bb)
|
|
{
|
|
X11EventCtx *ctx = (X11EventCtx *)ectx;
|
|
XColor fg_c, bg_c;
|
|
fg_c.red = fr; fg_c.green = fg; fg_c.blue = fb; fg_c.flags = DoRed|DoGreen|DoBlue;
|
|
bg_c.red = br; bg_c.green = bg; bg_c.blue = bb; bg_c.flags = DoRed|DoGreen|DoBlue;
|
|
XRecolorCursor(ctx->dpy, (Cursor)cursor, &fg_c, &bg_c);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Error handling */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static int (*x11_saved_error_handler)(Display *, XErrorEvent *);
|
|
|
|
static void
|
|
x11_set_error_handler(void *ectx, void *handler)
|
|
{
|
|
(void)ectx;
|
|
x11_saved_error_handler = XSetErrorHandler((int (*)(Display *, XErrorEvent *))handler);
|
|
}
|
|
|
|
static void
|
|
x11_restore_error_handler(void *ectx)
|
|
{
|
|
(void)ectx;
|
|
XSetErrorHandler(x11_saved_error_handler);
|
|
x11_saved_error_handler = NULL;
|
|
}
|
|
|
|
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,
|
|
.grab_keyboard = x11_grab_keyboard,
|
|
.ungrab_keyboard = x11_ungrab_keyboard,
|
|
.allow_events = x11_allow_events,
|
|
.grab_pointer = x11_grab_pointer,
|
|
.ungrab_pointer = x11_ungrab_pointer,
|
|
.query_pointer = x11_query_pointer,
|
|
.get_window_geometry = x11_get_window_geometry,
|
|
.move_window = x11_move_window,
|
|
.window_begin_move = x11_window_begin_move,
|
|
.translate_coordinates = x11_translate_coordinates,
|
|
.get_root_window = x11_get_root_window,
|
|
.query_tree = x11_query_tree,
|
|
.get_pointer_control = x11_get_pointer_control,
|
|
.set_pointer_control = x11_set_pointer_control,
|
|
.modifier_from_key = x11_modifier_from_key,
|
|
.keysym_to_string = x11_keysym_to_string,
|
|
.keysym_to_keycode = x11_keysym_to_keycode,
|
|
.keycode_to_keysym = x11_keycode_to_keysym,
|
|
.keysym_convert_case = x11_keysym_convert_case,
|
|
.get_modifier_keysyms = x11_get_modifier_keysyms,
|
|
.send_wm_command = x11_send_wm_command,
|
|
.set_window_hints = x11_set_window_hints,
|
|
.set_window_property_string = x11_set_window_property_string,
|
|
.set_window_property_data = x11_set_window_property_data,
|
|
.recolor_cursor = x11_recolor_cursor,
|
|
.set_error_handler = x11_set_error_handler,
|
|
.restore_error_handler = x11_restore_error_handler,
|
|
};
|