vdi-cloud/terminal/native-cursor/capture-no-cursor.c

132 lines
5.2 KiB
C

/* 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 <stddef.h>
#ifdef VDI_NVFBC
#include "NvFBC.h"
#endif
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 void receipt(const char *value, unsigned long size) {
const char *path = getenv("VDI_CURSOR_RECEIPT");
if (path) {
int fd = open(path, 0101 | 01000 | 02000000, 0600);
if (fd >= 0) { write(fd, value, size); close(fd); }
}
}
#ifdef VDI_NVFBC
static PNVFBCCREATEINSTANCE real_instance;
static PNVFBCCREATECAPTURESESSION real_capture;
static PNVFBCGETSTATUS real_status;
/* Some Optimus cards expose a real NVIDIA X screen without RandR outputs.
* Sunshine's enumeration rejects that otherwise capturable screen. Explicit
* single-seat mode enumerates the whole 2K X screen, with RandR unavailable,
* so Sunshine selects NVFBC_TRACKING_SCREEN instead of an invented output ID.
* Never report capture support or dimensions that the driver did not return.
*/
static NVFBCSTATUS NVFBCAPI screen_status(const NVFBC_SESSION_HANDLE session,
NVFBC_GET_STATUS_PARAMS *params) {
if (!params || params->dwVersion != NVFBC_GET_STATUS_PARAMS_VER) abort();
PNVFBCGETSTATUS fn = __atomic_load_n(&real_status, __ATOMIC_ACQUIRE);
if (!fn) abort();
NVFBCSTATUS status = fn(session, params);
if (status == NVFBC_SUCCESS && params->bIsCapturePossible &&
params->dwOutputNum == 0 && params->screenSize.w == 2560 &&
params->screenSize.h == 1440) {
params->bXRandRAvailable = NVFBC_FALSE;
params->dwOutputNum = 1;
params->outputs[0] = (NVFBC_RANDR_OUTPUT_INFO){
.dwId = 0, .name = "VDI-X-screen",
.trackedBox = {0, 0, 2560, 1440}
};
}
return status;
}
static NVFBCSTATUS NVFBCAPI capture_session(const NVFBC_SESSION_HANDLE session,
NVFBC_CREATE_CAPTURE_SESSION_PARAMS *params) {
if (!params || params->dwVersion != NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER)
abort(); /* A new SDK ABI must be reviewed before suppressing its cursor. */
NVFBC_CREATE_CAPTURE_SESSION_PARAMS local = *params;
if (getenv("VDI_CURSOR_LEASE")) local.bWithCursor = NVFBC_FALSE;
PNVFBCCREATECAPTURESESSION fn = __atomic_load_n(&real_capture, __ATOMIC_ACQUIRE);
if (!fn) abort();
NVFBCSTATUS status = fn(session, &local);
if (status == NVFBC_SUCCESS) receipt("nvfbc-v1\n", 9);
return status;
}
static NVFBCSTATUS NVFBCAPI create_instance(NVFBC_API_FUNCTION_LIST *functions) {
PNVFBCCREATEINSTANCE fn = __atomic_load_n(&real_instance, __ATOMIC_ACQUIRE);
if (!fn) abort();
NVFBCSTATUS status = fn(functions);
if (status == NVFBC_SUCCESS) {
if (!functions->nvFBCCreateCaptureSession) abort();
__atomic_store_n(&real_capture, functions->nvFBCCreateCaptureSession, __ATOMIC_RELEASE);
functions->nvFBCCreateCaptureSession = capture_session;
if (getenv("VDI_NVFBC_HEADLESS")) {
if (!functions->nvFBCGetStatus) abort();
__atomic_store_n(&real_status, functions->nvFBCGetStatus, __ATOMIC_RELEASE);
functions->nvFBCGetStatus = screen_status;
}
/* Startup probes do not create a capture session until Moonlight connects. */
receipt("nvfbc-ready-v1\n", 15);
}
return status;
}
#endif
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);
receipt("xfixes-v1\n", 10);
return (void *)capture_image;
}
#ifdef VDI_NVFBC
if (result && (getenv("VDI_CURSOR_LEASE") || getenv("VDI_NVFBC_HEADLESS")) &&
strcmp(name, "NvFBCCreateInstance") == 0) {
__atomic_store_n(&real_instance, (PNVFBCCREATEINSTANCE)result, __ATOMIC_RELEASE);
return (void *)create_instance;
}
#endif
return result;
}