Created
March 31, 2025 16:14
-
-
Save evenfrost/963e8bfdb982c45f427664a457d2aa03 to your computer and use it in GitHub Desktop.
Fixes the cursor for LotRO in Ubuntu on Wayland. To build: gcc lotro_hide.c -lX11 -lXi -lXfixes -o lotro_hide
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <X11/extensions/XInput2.h> | |
#include <X11/extensions/Xfixes.h> | |
#include <X11/Xlib.h> | |
#include <X11/Xatom.h> | |
static Display *dpy; | |
static int buttons_held = 0; // Tracks number of buttons currently held | |
static int cursor_hidden = 0; // Tracks cursor visibility state | |
XIEventMask evmasks; | |
/* Return 1 if XI2 is available, 0 otherwise */ | |
static int has_xi2(Display *dpy){ | |
int major = 2, minor = 2; | |
int rc = XIQueryVersion(dpy, &major, &minor); | |
if (rc == BadRequest) { | |
printf("No XI2 support. Server supports version %d.%d only.\n", major, minor); | |
return 0; | |
} else if (rc != Success) { | |
fprintf(stderr, "Internal Error! This is a bug in Xlib.\n"); | |
} | |
printf("XI2 supported. Server provides version %d.%d.\n", major, minor); | |
return 1; | |
} | |
static void select_events(Display *dpy, Window win){ | |
evmasks.deviceid = XIAllDevices; | |
evmasks.mask_len = XIMaskLen(XI_LASTEVENT); | |
evmasks.mask = (unsigned char*)calloc(evmasks.mask_len, sizeof(char)); | |
XISetMask(evmasks.mask, XI_RawButtonPress); | |
XISetMask(evmasks.mask, XI_RawButtonRelease); | |
XISelectEvents(dpy, win, &evmasks, 1); | |
XFlush(dpy); | |
} | |
static Window get_root_window(Display *dpy) { | |
Window root, parent, *children; | |
unsigned int nchildren; | |
XQueryTree(dpy, DefaultRootWindow(dpy), &root, &parent, &children, &nchildren); | |
if (children) XFree(children); | |
return root; | |
} | |
static void safe_show_cursor(Display *dpy) { | |
if (cursor_hidden) { | |
XFixesShowCursor(dpy, get_root_window(dpy)); | |
cursor_hidden = 0; | |
} | |
} | |
static void safe_hide_cursor(Display *dpy) { | |
if (!cursor_hidden) { | |
XFixesHideCursor(dpy, get_root_window(dpy)); | |
cursor_hidden = 1; | |
} | |
} | |
int main(int argc, char **argv){ | |
int xi_opcode, event, error; | |
XEvent ev; | |
dpy = XOpenDisplay(NULL); | |
if (!dpy) { | |
fprintf(stderr, "Failed to open display.\n"); | |
return -1; | |
} | |
if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) { | |
printf("X Input extension not available.\n"); | |
return -1; | |
} | |
if (!has_xi2(dpy)) return -1; | |
/* Select for XI2 events */ | |
select_events(dpy, get_root_window(dpy)); | |
/* Select for focus change events */ | |
XSelectInput(dpy, get_root_window(dpy), FocusChangeMask); | |
while (1) { | |
XNextEvent(dpy, &ev); | |
if (ev.type == FocusOut) { | |
buttons_held = 0; // Reset button state on focus loss | |
safe_show_cursor(dpy); | |
continue; | |
} | |
if (ev.type == FocusIn) { | |
usleep(50000); // Allow focus transition to complete | |
continue; | |
} | |
XGenericEventCookie *cookie = &ev.xcookie; | |
if (cookie->type != GenericEvent || cookie->extension != xi_opcode || | |
!XGetEventData(dpy, cookie)) | |
continue; | |
XIDeviceEvent *xie = (XIDeviceEvent *)cookie->data; | |
switch (cookie->evtype) { | |
case XI_RawButtonPress: | |
if (xie->detail >= 4 && xie->detail <= 7) break; // Ignore scroll | |
if (buttons_held == 0) { | |
safe_hide_cursor(dpy); | |
} | |
buttons_held++; | |
break; | |
case XI_RawButtonRelease: | |
if (xie->detail >= 4 && xie->detail <= 7) break; // Ignore scroll | |
if (buttons_held > 0) { | |
buttons_held--; | |
} | |
if (buttons_held == 0) { | |
safe_show_cursor(dpy); | |
} | |
break; | |
} | |
XFreeEventData(dpy, cookie); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment