/* Linux/glibc X11 capture adapter. Only preload into a leased Sunshine process. * Sunshine resolves XFixesGetCursorImage with dlsym(), so a plain symbol * interposer is insufficient. No system library or desktop theme is replaced. * ABI matches XFixesCursorImage from the Xorg Xfixes.h public header. */ #define _GNU_SOURCE #include extern void *dlvsym(void *, const char *, const char *); extern char *getenv(const char *); extern int strcmp(const char *, const char *); extern void abort(void); extern int open(const char *, int, ...); extern long write(int, const void *, unsigned long); extern int close(int); typedef struct { short x, y; unsigned short width, height, xhot, yhot; unsigned long serial; unsigned long *pixels; unsigned long atom; const char *name; } CursorImage; typedef void *(*Lookup)(void *, const char *); typedef CursorImage *(*GetImage)(void *); static GetImage real_image; static CursorImage *capture_image(void *display) { GetImage fn = __atomic_load_n(&real_image, __ATOMIC_ACQUIRE); if (!fn) abort(); CursorImage *image = fn(display); if (image && image->pixels) { unsigned long n = (unsigned long)image->width * image->height; if (n > 1048576) abort(); for (unsigned long i = 0; i < n; ++i) image->pixels[i] = 0; } return image; } void *dlsym(void *handle, const char *name) { Lookup lookup = (Lookup)dlvsym((void *)-1L, "dlsym", "GLIBC_2.2.5"); if (!lookup) abort(); void *result = lookup(handle, name); if (result && getenv("VDI_CURSOR_LEASE") && strcmp(name, "XFixesGetCursorImage") == 0) { __atomic_store_n(&real_image, (GetImage)result, __ATOMIC_RELEASE); const char *receipt = getenv("VDI_CURSOR_RECEIPT"); if (receipt) { int fd = open(receipt, 0101 | 01000 | 02000000, 0600); if (fd >= 0) { write(fd, "xfixes-v1\n", 10); close(fd); } } return (void *)capture_image; } return result; }