Skip to content

Instantly share code, notes, and snippets.

@lxylxy123456
Last active February 25, 2023 08:04
Show Gist options
  • Save lxylxy123456/e5dc4446914782e446adf7c60af5c4bd to your computer and use it in GitHub Desktop.
Save lxylxy123456/e5dc4446914782e446adf7c60af5c4bd to your computer and use it in GitHub Desktop.
/*
* 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;
}
@lxylxy123456
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment