summaryrefslogtreecommitdiff
path: root/main.c
blob: 2a74d11309d8ce887cb32b5a929a2e2cc01f35ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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);
}