/* * 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 #include #include #include #include /* ------------------------------------------------------------------ */ /* 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; case MapRequest: out->type = WME_MAP_REQUEST; out->u.mapRequest.window = (WNativeWindow)xev->xmaprequest.window; out->u.mapRequest.parent = (WNativeWindow)xev->xmaprequest.parent; break; case ConfigureRequest: out->type = WME_CONFIGURE_REQUEST; out->u.configureRequest.window = (WNativeWindow)xev->xconfigurerequest.window; out->u.configureRequest.parent = (WNativeWindow)xev->xconfigurerequest.parent; out->u.configureRequest.x = xev->xconfigurerequest.x; out->u.configureRequest.y = xev->xconfigurerequest.y; out->u.configureRequest.width = xev->xconfigurerequest.width; out->u.configureRequest.height = xev->xconfigurerequest.height; out->u.configureRequest.border_width = xev->xconfigurerequest.border_width; out->u.configureRequest.above = (WNativeWindow)xev->xconfigurerequest.above; out->u.configureRequest.detail = xev->xconfigurerequest.detail; out->u.configureRequest.value_mask = xev->xconfigurerequest.value_mask; break; case ReparentNotify: out->type = WME_REPARENT; out->u.reparent.window = (WNativeWindow)xev->xreparent.window; out->u.reparent.event = (WNativeWindow)xev->xreparent.event; out->u.reparent.parent = (WNativeWindow)xev->xreparent.parent; out->u.reparent.x = xev->xreparent.x; out->u.reparent.y = xev->xreparent.y; out->u.reparent.override_redirect = xev->xreparent.override_redirect; 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); /* SubstructureRedirect events (MapRequest, ConfigureRequest) are * delivered to the root window but must reach the WM's DispatchEvent * via extraEventHandler. Skip view lookup so they fall through. */ if (slot->type == MapRequest || slot->type == ConfigureRequest) ev->_view_target = NULL; else 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 #include 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, };