Skip to content

Instantly share code, notes, and snippets.

@evenfrost
Created March 31, 2025 16:14
Show Gist options
  • Save evenfrost/963e8bfdb982c45f427664a457d2aa03 to your computer and use it in GitHub Desktop.
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
#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