-
Notifications
You must be signed in to change notification settings - Fork 125
/
Guest.cpp
340 lines (304 loc) · 14.7 KB
/
Guest.cpp
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/*
$info$
tags: thunklibs|wayland-client
$end_info$
*/
#include <wayland-util.h>
#include <wayland-client.h>
// These must be re-declared with an initializer here, since they don't get exported otherwise
// NOTE: The initializers for these must be fetched from the host Wayland library, however
// we can't control how these symbols are loaded since they are global const objects.
// LD puts them in the application rodata section and ignores any nontrivial library-provided
// initializers. There is a workaround to enable late initialization anyway in OnInit.
// NOTE: We only need to do this for interfaces exported by libwayland-client itself. Interfaces
// defined by external libraries work fine.
extern "C" const wl_interface wl_output_interface {};
extern "C" const wl_interface wl_shm_pool_interface {};
extern "C" const wl_interface wl_pointer_interface {};
extern "C" const wl_interface wl_compositor_interface {};
extern "C" const wl_interface wl_shm_interface {};
extern "C" const wl_interface wl_registry_interface {};
extern "C" const wl_interface wl_buffer_interface {};
extern "C" const wl_interface wl_seat_interface {};
extern "C" const wl_interface wl_surface_interface {};
extern "C" const wl_interface wl_keyboard_interface {};
extern "C" const wl_interface wl_callback_interface {};
#include <algorithm>
#include <array>
#include <charconv>
#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <string>
#include "common/Guest.h"
#include "thunkgen_guest_libwayland-client.inl"
// See wayland-util.h for documentation on protocol message signatures
template<char>
struct ArgType;
template<>
struct ArgType<'s'> {
using type = const char*;
};
template<>
struct ArgType<'u'> {
using type = uint32_t;
};
template<>
struct ArgType<'i'> {
using type = int32_t;
};
template<>
struct ArgType<'o'> {
using type = wl_proxy*;
};
template<>
struct ArgType<'n'> {
using type = wl_proxy*;
};
template<>
struct ArgType<'a'> {
using type = wl_array*;
};
template<>
struct ArgType<'f'> {
using type = wl_fixed_t;
};
template<>
struct ArgType<'h'> {
using type = int32_t;
}; // fd?
template<char... Signature>
static uint64_t WaylandAllocateHostTrampolineForGuestListener(void (*callback)()) {
using cb = void(void*, wl_proxy*, typename ArgType<Signature>::type...);
return (uint64_t)(uintptr_t)(void*)AllocateHostTrampolineForGuestFunction((cb*)callback);
}
#define WL_CLOSURE_MAX_ARGS 20
extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), void* data) {
// Replace guest-provided callback table with host-callable function pointers
// NOTE: A reference to this table is stored in the wl_proxy, so the data
// must remain valid until the proxy is destroyed (or another listener
// is added)
delete[] (uint64_t*)wl_proxy_get_listener(proxy); // Delete previous substitute, if any
auto host_callbacks = new uint64_t[WL_CLOSURE_MAX_ARGS];
for (int i = 0; i < fex_wl_get_interface_event_count(proxy); ++i) {
char event_signature[16];
fex_wl_get_interface_event_signature(proxy, i, event_signature);
auto signature2 = std::string_view {event_signature};
// A leading number indicates the minimum protocol version
uint32_t since_version = 0;
auto [ptr, res] = std::from_chars(signature2.begin(), signature2.end(), since_version, 10);
auto signature = std::string {signature2.substr(ptr - signature2.begin())};
// ? just indicates that the argument may be null, so it doesn't change the signature
signature.erase(std::remove(signature.begin(), signature.end(), '?'), signature.end());
if (signature == "") {
// E.g. xdg_toplevel::close
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<>(callback[i]);
} else if (signature == "a") {
// E.g. xdg_toplevel::wm_capabilities
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'a'>(callback[i]);
} else if (signature == "hu") {
// E.g. zwp_linux_dmabuf_feedback_v1::format_table
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'h', 'u'>(callback[i]);
} else if (signature == "i") {
// E.g. wl_output_listener::scale
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'i'>(callback[i]);
} else if (signature == "if") {
// E.g. wl_touch_listener::orientation
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'i', 'f'>(callback[i]);
} else if (signature == "iff") {
// E.g. wl_touch_listener::shape
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'i', 'f', 'f'>(callback[i]);
} else if (signature == "ii") {
// E.g. xdg_toplevel::configure_bounds
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'i', 'i'>(callback[i]);
} else if (signature == "iia") {
// E.g. xdg_toplevel::configure
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'i', 'i', 'a'>(callback[i]);
} else if (signature == "iiiiissi") {
// E.g. wl_output_listener::geometry
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'i', 'i', 'i', 'i', 'i', 's', 's', 'i'>(callback[i]);
} else if (signature == "n") {
// E.g. wl_data_device_listener::data_offer
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'n'>(callback[i]);
} else if (signature == "o") {
// E.g. wl_data_device_listener::selection
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'o'>(callback[i]);
} else if (signature == "u") {
// E.g. wl_registry::global_remove
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u'>(callback[i]);
} else if (signature == "uff") {
// E.g. wl_pointer_listener::motion
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'f', 'f'>(callback[i]);
} else if (signature == "uhu") {
// E.g. wl_keyboard_listener::keymap
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'h', 'u'>(callback[i]);
} else if (signature == "ui") {
// E.g. wl_pointer_listener::axis_discrete
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'i'>(callback[i]);
} else if (signature == "uiff") {
// E.g. wl_touch_listener::motion
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'i', 'f', 'f'>(callback[i]);
} else if (signature == "uiii") {
// E.g. wl_output_listener::mode
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'i', 'i', 'i'>(callback[i]);
} else if (signature == "uo") {
// E.g. wl_pointer_listener::leave
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'o'>(callback[i]);
} else if (signature == "uoa") {
// E.g. wl_keyboard_listener::enter
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'o', 'a'>(callback[i]);
} else if (signature == "uoff") {
// E.g. wl_pointer_listener::enter
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'o', 'f', 'f'>(callback[i]);
} else if (signature == "uoffo") {
// E.g. wl_data_device_listener::enter
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'o', 'f', 'f', 'o'>(callback[i]);
} else if (signature == "usu") {
// E.g. wl_registry::global
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 's', 'u'>(callback[i]);
} else if (signature == "uu") {
// E.g. wl_pointer_listener::axis_stop
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u'>(callback[i]);
} else if (signature == "uuf") {
// E.g. wl_pointer_listener::axis
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u', 'f'>(callback[i]);
} else if (signature == "uui") {
// E.g. wl_touch_listener::up
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u', 'i'>(callback[i]);
} else if (signature == "uuoiff") {
// E.g. wl_touch_listener::down
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u', 'o', 'i', 'f', 'f'>(callback[i]);
} else if (signature == "uuu") {
// E.g. zwp_linux_dmabuf_v1::modifier
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u', 'u'>(callback[i]);
} else if (signature == "uuuu") {
// E.g. wl_pointer_listener::button
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u', 'u', 'u'>(callback[i]);
} else if (signature == "uuuuu") {
// E.g. wl_keyboard_listener::modifiers
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u', 'u', 'u', 'u'>(callback[i]);
} else if (signature == "s") {
// E.g. wl_seat::name
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'s'>(callback[i]);
} else if (signature == "sii") {
// E.g. zwp_text_input_v3::preedit_string
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'s', 'i', 'i'>(callback[i]);
} else {
fprintf(stderr, "TODO: Unknown wayland event signature descriptor %s\n", signature.data());
std::abort();
}
}
return fexfn_pack_wl_proxy_add_listener(proxy, (void (**)())host_callbacks, data);
}
extern "C" void wl_proxy_destroy(wl_proxy* proxy) {
// Delete substitute callback table (if any), then the proxy itself
delete[] (uint64_t*)wl_proxy_get_listener(proxy);
fexfn_pack_wl_proxy_destroy(proxy);
}
// Adapted from the Wayland sources
static const char* get_next_argument_type(const char* signature, char& type) {
for (; *signature; ++signature) {
switch (*signature) {
case 'i':
case 'u':
case 'f':
case 's':
case 'o':
case 'n':
case 'a':
case 'h': type = *signature; return signature + 1;
default: continue;
}
}
type = 0;
return signature;
}
static void wl_argument_from_va_list(const char* signature, wl_argument* args, int count, va_list ap) {
auto sig_iter = signature;
for (int i = 0; i < count; i++) {
char arg_type;
sig_iter = get_next_argument_type(sig_iter, arg_type);
switch (arg_type) {
case 'i': args[i].i = va_arg(ap, int32_t); break;
case 'u': args[i].u = va_arg(ap, uint32_t); break;
case 'f': args[i].f = va_arg(ap, wl_fixed_t); break;
case 's': args[i].s = va_arg(ap, const char*); break;
case 'o': args[i].o = va_arg(ap, struct wl_object*); break;
case 'n': args[i].o = va_arg(ap, struct wl_object*); break;
case 'a': args[i].a = va_arg(ap, struct wl_array*); break;
case 'h': args[i].h = va_arg(ap, int32_t); break;
case '\0': return;
}
}
}
extern "C" void wl_proxy_marshal(wl_proxy* proxy, uint32_t opcode, ...) {
wl_argument args[WL_CLOSURE_MAX_ARGS];
va_list ap;
va_start(ap, opcode);
// This is equivalent to reading proxy->interface->methods[opcode].signature on 64-bit.
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
char signature[64];
fex_wl_get_method_signature(proxy, opcode, signature);
wl_argument_from_va_list(signature, args, WL_CLOSURE_MAX_ARGS, ap);
va_end(ap);
wl_proxy_marshal_array(proxy, opcode, args);
}
extern "C" wl_proxy* wl_proxy_marshal_constructor(wl_proxy* proxy, uint32_t opcode, const wl_interface* interface, ...) {
wl_argument args[WL_CLOSURE_MAX_ARGS];
va_list ap;
va_start(ap, interface);
// This is equivalent to reading ((wl_proxy_private*)proxy)->interface->methods[opcode].signature on 64-bit.
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
char signature[64];
fex_wl_get_method_signature(proxy, opcode, signature);
wl_argument_from_va_list(signature, args, WL_CLOSURE_MAX_ARGS, ap);
va_end(ap);
return wl_proxy_marshal_array_constructor(proxy, opcode, args, interface);
}
extern "C" wl_proxy* wl_proxy_marshal_constructor_versioned(wl_proxy* proxy, uint32_t opcode, const wl_interface* interface, uint32_t version, ...) {
wl_argument args[WL_CLOSURE_MAX_ARGS];
va_list ap;
va_start(ap, version);
// This is equivalent to reading ((wl_proxy_private*)proxy)->interface->methods[opcode].signature on 64-bit.
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
char signature[64];
fex_wl_get_method_signature(proxy, opcode, signature);
wl_argument_from_va_list(signature, args, WL_CLOSURE_MAX_ARGS, ap);
va_end(ap);
return wl_proxy_marshal_array_constructor_versioned(proxy, opcode, args, interface, version);
}
extern "C" wl_proxy* wl_proxy_marshal_flags(wl_proxy* proxy, uint32_t opcode, const wl_interface* interface, uint32_t version, uint32_t flags, ...) {
wl_argument args[WL_CLOSURE_MAX_ARGS];
va_list ap;
va_start(ap, flags);
// This is equivalent to reading proxy->interface->methods[opcode].signature on 64-bit.
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
char signature[64];
fex_wl_get_method_signature(proxy, opcode, signature);
wl_argument_from_va_list(signature, args, WL_CLOSURE_MAX_ARGS, ap);
va_end(ap);
// wl_proxy_marshal_array_flags is only available starting from Wayland 1.19.91
#if WAYLAND_VERSION_MAJOR * 10000 + WAYLAND_VERSION_MINOR * 100 + WAYLAND_VERSION_MICRO >= 11991
return wl_proxy_marshal_array_flags(proxy, opcode, interface, version, flags, args);
#else
fprintf(stderr, "Host Wayland version is too old to support FEX thunking\n");
__builtin_trap();
#endif
}
extern "C" void wl_log_set_handler_client(wl_log_func_t handler) {
// Ignore
}
void OnInit() {
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_output_interface), "wl_output_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shm_pool_interface), "wl_shm_pool_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_pointer_interface), "wl_pointer_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_compositor_interface), "wl_compositor_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shm_interface), "wl_shm_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_registry_interface), "wl_registry_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_buffer_interface), "wl_buffer_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_seat_interface), "wl_seat_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_surface_interface), "wl_surface_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_keyboard_interface), "wl_keyboard_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_callback_interface), "wl_callback_interface");
}
LOAD_LIB_INIT(libwayland - client, OnInit)