-
-
Save lxylxy123456/e5dc4446914782e446adf7c60af5c4bd to your computer and use it in GitHub Desktop.
Draw cursor to workaround https://gitlab.gnome.org/GNOME/mutter/-/issues/2344 & https://bugzilla.redhat.com/show_bug.cgi?id=2100321
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
/* | |
* This program is free software: you can redistribute it and/or modify it | |
* under the terms of the GNU General Public License as published by the Free | |
* Software Foundation, either version 3 of the License, or (at your option) | |
* any later version. | |
* | |
* This program is distributed in the hope that it will be useful, but WITHOUT | |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
* more details. | |
* | |
* You should have received a copy of the GNU General Public License along with | |
* this program. If not, see <https://www.gnu.org/licenses/>. | |
*/ | |
/* | |
* This program draws a cursor to workaround a GNOME Shell bug. | |
* https://gitlab.gnome.org/GNOME/mutter/-/issues/2344 | |
* https://bugzilla.redhat.com/show_bug.cgi?id=2100321 | |
* | |
* Install dependencies on Fedora: | |
* sudo dnf install libXi-devel | |
* | |
* Compile: | |
* gcc -Wall -Werror -lX11 -lXi -lXfixes -o m m.c | |
* | |
* Run: | |
* ./m | |
*/ | |
#include <stdio.h> | |
#include <X11/extensions/XInput.h> | |
#include <X11/extensions/shape.h> | |
#include <X11/extensions/Xfixes.h> | |
#include <errno.h> | |
#include <time.h> | |
#include <string.h> | |
#include <assert.h> | |
#include <stdint.h> | |
/* Will draw a triangle (0, 0), (MOUSE_W, MOUSE_W), (0, MOUSE_H) */ | |
#define MOUSE_W 10 | |
#define MOUSE_H 14 | |
/* Global variables used to pass states */ | |
Display *display; | |
unsigned long screen; | |
Window root_win; | |
GC gc; | |
Window window; | |
XVisualInfo vinfo; | |
/* Represent position of mouse on screen */ | |
typedef struct { | |
int x; | |
int y; | |
} pos_t; | |
/* Get the position of pointer */ | |
pos_t get_pointer() | |
{ | |
Window r, c; | |
int rx, ry, wx, wy; | |
unsigned int m; | |
XQueryPointer(display, root_win, &r, &c, &rx, &ry, &wx, &wy, &m); | |
pos_t pos = { rx, ry }; | |
return pos; | |
} | |
/* Create a window, transparent background, penetrate all mouse events */ | |
Window create_window(int x, int y, unsigned int w, unsigned int h, int border) | |
{ | |
XMatchVisualInfo(display, screen, 32, TrueColor, &vinfo); | |
/* | |
* References: | |
* * x11perf.c CreatePerfWindow | |
* * Transparent window: https://stackoverflow.com/questions/3645632/how-to | |
* * Also: https://stackoverflow.com/questions/61622397/x11-cannot-draw-an | |
*/ | |
XSetWindowAttributes xswa; | |
xswa.override_redirect = True; | |
xswa.colormap = XCreateColormap(display, root_win, vinfo.visual, AllocNone); | |
xswa.border_pixel = 0; | |
xswa.background_pixel = 0; /* This is the background color of the window */ | |
Window window = | |
XCreateWindow(display, root_win, x, y, w, h, border, vinfo.depth, | |
CopyFromParent, vinfo.visual, | |
CWColormap | CWBorderPixel | CWBackPixel | | |
CWOverrideRedirect, &xswa); | |
XSelectInput(display, window, ExposureMask); | |
XMapWindow(display, window); | |
/* Penatrate mouse events: https://stackoverflow.com/questions/9046440/ */ | |
XRectangle rect; | |
XserverRegion region = XFixesCreateRegion(display, &rect, 1); | |
XFixesSetWindowShapeRegion(display, window, ShapeInput, 0, 0, region); | |
XFixesDestroyRegion(display, region); | |
return window; | |
} | |
/* (Re)draw the mouse during exposure events */ | |
void redraw_window() | |
{ | |
XPoint points[4] = { | |
{0, 0}, | |
{0, MOUSE_H}, | |
{MOUSE_W, MOUSE_W}, | |
{0, 0}, | |
}; | |
int npoints = sizeof(points) / sizeof(points[0]); | |
XSetForeground(display, gc, 0xc0000099); | |
XFillPolygon(display, window, gc, points, npoints, Convex, CoordModeOrigin); | |
/* XFillRectangle(display, window, gc, 0, 0, MOUSE_W, MOUSE_H); */ | |
XSetForeground(display, gc, 0xffffffff); | |
XDrawLines(display, window, gc, points, npoints, CoordModeOrigin); | |
/* XDrawRectangle(display, window, gc, 0, 0, MOUSE_W, MOUSE_H); */ | |
} | |
/* Send event to callback function when mouse moves */ | |
void register_extension_events(void (*callback)(void *, pos_t *), void *data) | |
{ | |
/* Source: test.c in xinput */ | |
XDeviceInfo *devices; | |
int ndevices; | |
int motion_type = -1; | |
devices = XListInputDevices(display, &ndevices); | |
for (int i = 0; i < ndevices; i++) { | |
/* Filter out master devices. See XListInputDevices(3) and XI.h */ | |
if (devices[i].use < IsXExtensionDevice) { | |
continue; | |
} | |
if (!"list all devices") { | |
printf("%d\t%ld\t%s\t%s\n", i, devices[i].id, | |
devices[i].type ? XGetAtomName(display, | |
devices[i].type) : "", | |
devices[i].name); | |
} | |
XDevice *device = XOpenDevice(display, devices[i].id); | |
for (int i = 0; i < device->num_classes; i++) { | |
if (device->classes[i].input_class != ValuatorClass) { | |
continue; | |
} | |
int _motion_type = -1; | |
XEventClass event; | |
DeviceMotionNotify(device, _motion_type, event); | |
if (motion_type == -1) { | |
motion_type = _motion_type; | |
} else { | |
assert(_motion_type == motion_type); | |
} | |
if (XSelectExtensionEvent(display, root_win, &event, 1)) { | |
fprintf(stderr, "error selecting extended events\n"); | |
} | |
} | |
} | |
pos_t pos = get_pointer(); | |
/* Create the window */ | |
window = create_window(pos.x, pos.y, MOUSE_W + 1, MOUSE_H + 1, 10); | |
gc = XCreateGC(display, window, 0, 0); | |
while (1) { | |
XEvent Event; | |
XNextEvent(display, &Event); | |
if (Event.type == motion_type) { | |
XDeviceMotionEvent *motion = (XDeviceMotionEvent *) & Event; | |
static int a[2]; | |
if (motion->first_axis >= 2) { /* Happens for touchpad motion */ | |
continue; | |
} | |
for (int loop = 0; loop < motion->axes_count; loop++) { | |
if (motion->first_axis + loop >= 2) { | |
break; | |
} | |
a[motion->first_axis + loop] = motion->axis_data[loop]; | |
} | |
/* Check whether in range () */ | |
XWindowAttributes gwa; | |
XGetWindowAttributes(display, root_win, &gwa); | |
if (a[0] <= gwa.width && a[1] <= gwa.height) { | |
pos.x = a[0]; | |
pos.y = a[1]; | |
} | |
} else if (Event.type == Expose) { | |
redraw_window(); | |
} else { | |
assert(0 && "unknown event type"); | |
} | |
/* Call the callback function until the event queue is empty */ | |
if (!XPending(display)) { | |
callback(data, &pos); | |
} | |
} | |
} | |
/* Draw a small window at the position of the mouse. Follow the mouse. */ | |
void draw(void *data, pos_t * pos) | |
{ | |
/* Source: x11perf.c CreatePerfWindow */ | |
printf("\033[1G\033[K%d %d", pos->x, pos->y); | |
fflush(stdout); | |
XMoveWindow(display, window, pos->x, pos->y); | |
} | |
int main(int argc, char *argv[]) | |
{ | |
display = XOpenDisplay(NULL); | |
if (display == NULL) { | |
fprintf(stderr, "Unable to connect to X server\n"); | |
return 1; | |
} | |
screen = DefaultScreen(display); | |
root_win = RootWindow(display, screen); | |
typedef void (callback_t) (void *, pos_t *); | |
typedef void (caller_t) (callback_t, void *data); | |
caller_t *cr = register_extension_events; | |
callback_t *cb = draw; | |
cr(cb, NULL); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Demo video: https://youtu.be/vI9ovpao7oo