diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | main.c | 108 |
3 files changed, 111 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d5b507 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +qxl_setup.so diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8c1dba5 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +qxl_setup.so: main.c Makefile + gcc -s -O2 $(CPPFLAGS) $(shell pkg-config --cflags spice-protocol) -Wall -Werror --shared -fPIC -DPIC -o $@ $< -ldl -lepoxy -lgdm @@ -0,0 +1,108 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dlfcn.h> +#include <spice-server/spice.h> + +// definitions +// +struct gbm_device; + +typedef void *EGLNativeDisplayType; +typedef void *EGLDisplay; + +typedef void spice_qxl_gl_setup_p(QXLInstance *instance, EGLDisplay egl_display); +typedef EGLDisplay epoxy_eglGetDisplay_p(EGLNativeDisplayType display_id); +typedef int spice_server_add_interface_p(SpiceServer *reds, SpiceBaseInstance *sin); +typedef struct gbm_device *gbm_create_device_p(int fd); + +extern epoxy_eglGetDisplay_p *epoxy_eglGetDisplay; + +//variables +// +static epoxy_eglGetDisplay_p *old_epoxy_eglGetDisplay = NULL; +static EGLDisplay saved_display = NULL; +#define MAX_INSTANCES 4 +static QXLInstance *instances[MAX_INSTANCES]; + +#define GET_FUNC(name, ptr, dl) name ## _p *ptr = (name ## _p *) dlsym(dl, #name) +#define GET_FUNC_DEFAULT(name, ptr) GET_FUNC(name, ptr, RTLD_DEFAULT) +#define GET_FUNC_NEXT(name, ptr) GET_FUNC(name, ptr, RTLD_NEXT) + +// implementation +// +static void +add_display(QXLInstance *qxl) +{ + int i; + for (i = 0; i < MAX_INSTANCES; ++i) { + if (!instances[i]) { + instances[i] = qxl; + break; + } + } +} + +static void +set_saved_display(EGLDisplay display) +{ + if (!display || display == saved_display) + return; + + saved_display = display; + GET_FUNC_DEFAULT(spice_qxl_gl_setup, qxl_setup); + // send to all instances saved + int i; + for (i = 0; i < MAX_INSTANCES; ++i) { + if (!instances[i]) + continue; + qxl_setup(instances[i], saved_display); + instances[i] = NULL; + } +} + +// capture QXL interface creations +int +spice_server_add_interface(SpiceServer *reds, SpiceBaseInstance *sin) +{ + const SpiceBaseInterface *interface = sin->sif; + GET_FUNC_NEXT(spice_server_add_interface, add_interface); + if (strcmp(interface->type, SPICE_INTERFACE_QXL) != 0) + return add_interface(reds, sin); + + int res = add_interface(reds, sin); + if (res >= 0 && saved_display) { + GET_FUNC_DEFAULT(spice_qxl_gl_setup, qxl_setup); + qxl_setup((QXLInstance *) sin, saved_display); + return res; + } + if (res >= 0) + add_display((QXLInstance *) sin); + return res; +} + +static EGLDisplay +epoxy_eglGetDisplay_replace(EGLNativeDisplayType display_id) +{ + // save last display we got + EGLDisplay display = old_epoxy_eglGetDisplay(display_id); + set_saved_display(display); + return display; +} + +struct gbm_device * +gbm_create_device(int fd) +{ + // replace epoxy_eglGetDisplay + if (!old_epoxy_eglGetDisplay) { + old_epoxy_eglGetDisplay = epoxy_eglGetDisplay; + epoxy_eglGetDisplay = epoxy_eglGetDisplay_replace; + } + + // get old function + GET_FUNC_NEXT(gbm_create_device, create_device); + + // call old function + return create_device(fd); +} |