1 /*
2 Copyright (C) 2009 Red Hat, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 */
17 #include <config.h>
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <pthread.h>
25 #include <inttypes.h>
26
27 #include <spice/qxl_dev.h>
28 #include <common/quic.h>
29 #include <common/sw_canvas.h>
30
31 #include "spice-wrapped.h"
32 #include "red-worker.h"
33 #include "reds.h"
34 #include "dispatcher.h"
35 #include "red-parse-qxl.h"
36 #include "red-channel-client.h"
37 #include "display-limits.h"
38
39 #include "red-qxl.h"
40
41
42 #define MAX_MONITORS_COUNT 16
43
44 struct QXLState {
45 SPICE_CXX_GLIB_ALLOCATOR
46 QXLInstance *qxl;
47 red::shared_ptr<Dispatcher> dispatcher;
48 uint32_t pending;
49 int primary_active;
50 int x_res;
51 int y_res;
52 int use_hardware_cursor;
53 unsigned int max_monitors;
54 RedsState *reds;
55 RedWorker *worker;
56 char device_address[MAX_DEVICE_ADDRESS_LEN];
57 uint32_t device_display_ids[MAX_MONITORS_COUNT];
58 size_t monitors_count; // length of ^^^
59
60 bool running;
61
62 pthread_mutex_t scanout_mutex;
63 SpiceMsgDisplayGlScanoutUnix scanout;
64 uint64_t gl_draw_cookie;
65 };
66
67 #define GL_DRAW_COOKIE_INVALID (~((uint64_t) 0))
68
69 /* used by RedWorker */
red_qxl_is_running(QXLInstance * qxl)70 bool red_qxl_is_running(QXLInstance *qxl)
71 {
72 return qxl->st->running;
73 }
74
75 /* used by RedWorker */
red_qxl_set_running(QXLInstance * qxl,bool running)76 void red_qxl_set_running(QXLInstance *qxl, bool running)
77 {
78 qxl->st->running = running;
79 }
80
red_qxl_check_qxl_version(QXLInstance * qxl,int major,int minor)81 int red_qxl_check_qxl_version(QXLInstance *qxl, int major, int minor)
82 {
83 int qxl_major = qxl_get_interface(qxl)->base.major_version;
84 int qxl_minor = qxl_get_interface(qxl)->base.minor_version;
85
86 return ((qxl_major > major) ||
87 ((qxl_major == major) && (qxl_minor >= minor)));
88 }
89
90 SPICE_GNUC_VISIBLE
spice_qxl_update_area(QXLInstance * instance,uint32_t surface_id,struct QXLRect * qxl_area,struct QXLRect * qxl_dirty_rects,uint32_t num_dirty_rects,uint32_t clear_dirty_region)91 void spice_qxl_update_area(QXLInstance *instance, uint32_t surface_id,
92 struct QXLRect *qxl_area, struct QXLRect *qxl_dirty_rects,
93 uint32_t num_dirty_rects, uint32_t clear_dirty_region)
94 {
95 RedWorkerMessageUpdate payload = {0,};
96
97 payload.surface_id = surface_id;
98 payload.qxl_area = qxl_area;
99 payload.qxl_dirty_rects = qxl_dirty_rects;
100 payload.num_dirty_rects = num_dirty_rects;
101 payload.clear_dirty_region = clear_dirty_region;
102 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_UPDATE, &payload);
103 }
104
red_qxl_client_monitors_config(QXLInstance * qxl,VDAgentMonitorsConfig * monitors_config)105 gboolean red_qxl_client_monitors_config(QXLInstance *qxl,
106 VDAgentMonitorsConfig *monitors_config)
107 {
108 return (red_qxl_check_qxl_version(qxl, 3, 3) &&
109 qxl_get_interface(qxl)->client_monitors_config &&
110 qxl_get_interface(qxl)->client_monitors_config(qxl, monitors_config));
111 }
112
113 SPICE_GNUC_VISIBLE
spice_qxl_update_area_async(QXLInstance * instance,uint32_t surface_id,QXLRect * qxl_area,uint32_t clear_dirty_region,uint64_t cookie)114 void spice_qxl_update_area_async(QXLInstance *instance, uint32_t surface_id, QXLRect *qxl_area,
115 uint32_t clear_dirty_region, uint64_t cookie)
116 {
117 RedWorkerMessage message = RED_WORKER_MESSAGE_UPDATE_ASYNC;
118 RedWorkerMessageUpdateAsync payload;
119
120 payload.base.cookie = cookie;
121 payload.surface_id = surface_id;
122 payload.qxl_area = *qxl_area;
123 payload.clear_dirty_region = clear_dirty_region;
124 instance->st->dispatcher->send_message(message, &payload);
125 }
126
127 SPICE_GNUC_VISIBLE
spice_qxl_add_memslot(QXLInstance * instance,QXLDevMemSlot * mem_slot)128 void spice_qxl_add_memslot(QXLInstance *instance, QXLDevMemSlot *mem_slot)
129 {
130 RedWorkerMessageAddMemslot payload;
131
132 payload.mem_slot = *mem_slot;
133 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_ADD_MEMSLOT, &payload);
134 }
135
136 SPICE_GNUC_VISIBLE
spice_qxl_add_memslot_async(QXLInstance * instance,QXLDevMemSlot * mem_slot,uint64_t cookie)137 void spice_qxl_add_memslot_async(QXLInstance *instance, QXLDevMemSlot *mem_slot, uint64_t cookie)
138 {
139 RedWorkerMessageAddMemslotAsync payload;
140 RedWorkerMessage message = RED_WORKER_MESSAGE_ADD_MEMSLOT_ASYNC;
141
142 payload.base.cookie = cookie;
143 payload.mem_slot = *mem_slot;
144 instance->st->dispatcher->send_message(message, &payload);
145 }
146
147 SPICE_GNUC_VISIBLE
spice_qxl_del_memslot(QXLInstance * instance,uint32_t slot_group_id,uint32_t slot_id)148 void spice_qxl_del_memslot(QXLInstance *instance, uint32_t slot_group_id, uint32_t slot_id)
149 {
150 RedWorkerMessageDelMemslot payload;
151 RedWorkerMessage message = RED_WORKER_MESSAGE_DEL_MEMSLOT;
152
153 payload.slot_group_id = slot_group_id;
154 payload.slot_id = slot_id;
155 instance->st->dispatcher->send_message(message, &payload);
156 }
157
158 SPICE_GNUC_VISIBLE
spice_qxl_destroy_surfaces(QXLInstance * instance)159 void spice_qxl_destroy_surfaces(QXLInstance *instance)
160 {
161 RedWorkerMessageDestroySurfaces payload;
162
163 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_DESTROY_SURFACES, &payload);
164 }
165
166 SPICE_GNUC_VISIBLE
spice_qxl_destroy_surfaces_async(QXLInstance * instance,uint64_t cookie)167 void spice_qxl_destroy_surfaces_async(QXLInstance *instance, uint64_t cookie)
168 {
169 RedWorkerMessageDestroySurfacesAsync payload;
170 RedWorkerMessage message = RED_WORKER_MESSAGE_DESTROY_SURFACES_ASYNC;
171
172 payload.base.cookie = cookie;
173 instance->st->dispatcher->send_message(message, &payload);
174 }
175
176 /* used by RedWorker */
red_qxl_destroy_primary_surface_complete(QXLState * qxl_state)177 void red_qxl_destroy_primary_surface_complete(QXLState *qxl_state)
178 {
179 qxl_state->x_res = 0;
180 qxl_state->y_res = 0;
181 qxl_state->use_hardware_cursor = FALSE;
182 qxl_state->primary_active = FALSE;
183
184 reds_update_client_mouse_allowed(qxl_state->reds);
185 }
186
187 SPICE_GNUC_VISIBLE
spice_qxl_destroy_primary_surface(QXLInstance * instance,uint32_t surface_id)188 void spice_qxl_destroy_primary_surface(QXLInstance *instance, uint32_t surface_id)
189 {
190 RedWorkerMessageDestroyPrimarySurface payload;
191 payload.surface_id = surface_id;
192 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_DESTROY_PRIMARY_SURFACE, &payload);
193 red_qxl_destroy_primary_surface_complete(instance->st);
194 }
195
196 SPICE_GNUC_VISIBLE
spice_qxl_destroy_primary_surface_async(QXLInstance * instance,uint32_t surface_id,uint64_t cookie)197 void spice_qxl_destroy_primary_surface_async(QXLInstance *instance,
198 uint32_t surface_id, uint64_t cookie)
199 {
200 RedWorkerMessageDestroyPrimarySurfaceAsync payload;
201 RedWorkerMessage message = RED_WORKER_MESSAGE_DESTROY_PRIMARY_SURFACE_ASYNC;
202
203 payload.base.cookie = cookie;
204 payload.surface_id = surface_id;
205 instance->st->dispatcher->send_message(message, &payload);
206 }
207
208 /* used by RedWorker */
red_qxl_create_primary_surface_complete(QXLState * qxl_state,const QXLDevSurfaceCreate * surface)209 void red_qxl_create_primary_surface_complete(QXLState *qxl_state, const QXLDevSurfaceCreate *surface)
210 {
211 qxl_state->x_res = surface->width;
212 qxl_state->y_res = surface->height;
213 // mouse_mode is a boolean value, enforce it
214 qxl_state->use_hardware_cursor = !!surface->mouse_mode;
215 qxl_state->primary_active = TRUE;
216
217 reds_update_client_mouse_allowed(qxl_state->reds);
218 }
219
220 SPICE_GNUC_VISIBLE
spice_qxl_create_primary_surface_async(QXLInstance * instance,uint32_t surface_id,QXLDevSurfaceCreate * surface,uint64_t cookie)221 void spice_qxl_create_primary_surface_async(QXLInstance *instance, uint32_t surface_id,
222 QXLDevSurfaceCreate *surface, uint64_t cookie)
223 {
224 RedWorkerMessageCreatePrimarySurfaceAsync payload;
225 RedWorkerMessage message = RED_WORKER_MESSAGE_CREATE_PRIMARY_SURFACE_ASYNC;
226
227 payload.base.cookie = cookie;
228 payload.surface_id = surface_id;
229 payload.surface = *surface;
230 instance->st->dispatcher->send_message(message, &payload);
231 }
232
233 SPICE_GNUC_VISIBLE
spice_qxl_create_primary_surface(QXLInstance * instance,uint32_t surface_id,QXLDevSurfaceCreate * surface)234 void spice_qxl_create_primary_surface(QXLInstance *instance, uint32_t surface_id,
235 QXLDevSurfaceCreate *surface)
236 {
237 RedWorkerMessageCreatePrimarySurface payload = {0,};
238
239 payload.surface_id = surface_id;
240 payload.surface = *surface;
241 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_CREATE_PRIMARY_SURFACE, &payload);
242 red_qxl_create_primary_surface_complete(instance->st, surface);
243 }
244
245 SPICE_GNUC_VISIBLE
spice_qxl_reset_image_cache(QXLInstance * instance)246 void spice_qxl_reset_image_cache(QXLInstance *instance)
247 {
248 RedWorkerMessageResetImageCache payload;
249
250 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_RESET_IMAGE_CACHE, &payload);
251 }
252
253 SPICE_GNUC_VISIBLE
spice_qxl_reset_cursor(QXLInstance * instance)254 void spice_qxl_reset_cursor(QXLInstance *instance)
255 {
256 RedWorkerMessageResetCursor payload;
257
258 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_RESET_CURSOR, &payload);
259 }
260
261 SPICE_GNUC_VISIBLE
spice_qxl_destroy_surface_wait(QXLInstance * instance,uint32_t surface_id)262 void spice_qxl_destroy_surface_wait(QXLInstance *instance, uint32_t surface_id)
263 {
264 RedWorkerMessageDestroySurfaceWait payload;
265
266 payload.surface_id = surface_id;
267 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_DESTROY_SURFACE_WAIT, &payload);
268 }
269
270 SPICE_GNUC_VISIBLE
spice_qxl_destroy_surface_async(QXLInstance * instance,uint32_t surface_id,uint64_t cookie)271 void spice_qxl_destroy_surface_async(QXLInstance *instance, uint32_t surface_id, uint64_t cookie)
272 {
273 RedWorkerMessageDestroySurfaceWaitAsync payload;
274 RedWorkerMessage message = RED_WORKER_MESSAGE_DESTROY_SURFACE_WAIT_ASYNC;
275
276 payload.base.cookie = cookie;
277 payload.surface_id = surface_id;
278 instance->st->dispatcher->send_message(message, &payload);
279 }
280
281 SPICE_GNUC_VISIBLE
spice_qxl_reset_memslots(QXLInstance * instance)282 void spice_qxl_reset_memslots(QXLInstance *instance)
283 {
284 RedWorkerMessageResetMemslots payload;
285
286 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_RESET_MEMSLOTS, &payload);
287 }
288
red_qxl_set_pending(QXLState * qxl_state,int pending)289 static bool red_qxl_set_pending(QXLState *qxl_state, int pending)
290 {
291 // this is not atomic but is not an issue
292 if (test_bit(pending, qxl_state->pending)) {
293 return TRUE;
294 }
295
296 set_bit(pending, &qxl_state->pending);
297 return FALSE;
298 }
299
300 SPICE_GNUC_VISIBLE
spice_qxl_wakeup(QXLInstance * instance)301 void spice_qxl_wakeup(QXLInstance *instance)
302 {
303 RedWorkerMessageWakeup payload;
304
305 if (red_qxl_set_pending(instance->st, RED_DISPATCHER_PENDING_WAKEUP))
306 return;
307
308 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_WAKEUP, &payload);
309 }
310
311 SPICE_GNUC_VISIBLE
spice_qxl_oom(QXLInstance * instance)312 void spice_qxl_oom(QXLInstance *instance)
313 {
314 RedWorkerMessageOom payload;
315
316 if (red_qxl_set_pending(instance->st, RED_DISPATCHER_PENDING_OOM))
317 return;
318
319 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_OOM, &payload);
320 }
321
red_qxl_start(QXLInstance * qxl)322 void red_qxl_start(QXLInstance *qxl)
323 {
324 RedWorkerMessageStart payload;
325
326 qxl->st->dispatcher->send_message(RED_WORKER_MESSAGE_START, &payload);
327 }
328
329 SPICE_GNUC_VISIBLE
spice_qxl_flush_surfaces_async(QXLInstance * instance,uint64_t cookie)330 void spice_qxl_flush_surfaces_async(QXLInstance *instance, uint64_t cookie)
331 {
332 RedWorkerMessageFlushSurfacesAsync payload;
333 RedWorkerMessage message = RED_WORKER_MESSAGE_FLUSH_SURFACES_ASYNC;
334
335 payload.base.cookie = cookie;
336 instance->st->dispatcher->send_message(message, &payload);
337 }
338
339 SPICE_GNUC_VISIBLE
spice_qxl_monitors_config_async(QXLInstance * instance,QXLPHYSICAL monitors_config,int group_id,uint64_t cookie)340 void spice_qxl_monitors_config_async(QXLInstance *instance, QXLPHYSICAL monitors_config,
341 int group_id, uint64_t cookie)
342 {
343 RedWorkerMessageMonitorsConfigAsync payload;
344 RedWorkerMessage message = RED_WORKER_MESSAGE_MONITORS_CONFIG_ASYNC;
345
346 payload.base.cookie = cookie;
347 payload.monitors_config = monitors_config;
348 payload.group_id = group_id;
349 payload.max_monitors = instance->st->max_monitors;
350
351 instance->st->dispatcher->send_message(message, &payload);
352 }
353
354 SPICE_GNUC_VISIBLE
spice_qxl_driver_unload(QXLInstance * instance)355 void spice_qxl_driver_unload(QXLInstance *instance)
356 {
357 RedWorkerMessageDriverUnload payload;
358
359 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_DRIVER_UNLOAD, &payload);
360 }
361
red_qxl_stop(QXLInstance * qxl)362 void red_qxl_stop(QXLInstance *qxl)
363 {
364 RedWorkerMessageStop payload;
365
366 qxl->st->dispatcher->send_message(RED_WORKER_MESSAGE_STOP, &payload);
367 }
368
369 SPICE_GNUC_VISIBLE
spice_qxl_loadvm_commands(QXLInstance * instance,struct QXLCommandExt * ext,uint32_t count)370 void spice_qxl_loadvm_commands(QXLInstance *instance, struct QXLCommandExt *ext, uint32_t count)
371 {
372 RedWorkerMessageLoadvmCommands payload;
373
374 payload.count = count;
375 payload.ext = ext;
376 instance->st->dispatcher->send_message(RED_WORKER_MESSAGE_LOADVM_COMMANDS, &payload);
377 }
378
red_qxl_get_ram_size(QXLInstance * qxl)379 uint32_t red_qxl_get_ram_size(QXLInstance *qxl)
380 {
381 QXLDevInitInfo qxl_info;
382
383 red_qxl_get_init_info(qxl, &qxl_info);
384
385 return qxl_info.qxl_ram_size;
386 }
387
388 SPICE_GNUC_VISIBLE
spice_qxl_start(QXLInstance * instance)389 void spice_qxl_start(QXLInstance *instance)
390 {
391 red_qxl_start(instance);
392 }
393
394 SPICE_GNUC_VISIBLE
spice_qxl_stop(QXLInstance * instance)395 void spice_qxl_stop(QXLInstance *instance)
396 {
397 red_qxl_stop(instance);
398 }
399
400 SPICE_GNUC_VISIBLE
spice_qxl_set_max_monitors(QXLInstance * instance,unsigned int max_monitors)401 void spice_qxl_set_max_monitors(QXLInstance *instance, unsigned int max_monitors)
402 {
403 instance->st->max_monitors = MAX(1u, max_monitors);
404 }
405
red_qxl_get_gl_scanout(QXLInstance * qxl)406 SpiceMsgDisplayGlScanoutUnix *red_qxl_get_gl_scanout(QXLInstance *qxl)
407 {
408 pthread_mutex_lock(&qxl->st->scanout_mutex);
409 if (qxl->st->scanout.drm_dma_buf_fd >= 0) {
410 return &qxl->st->scanout;
411 }
412 pthread_mutex_unlock(&qxl->st->scanout_mutex);
413 return nullptr;
414 }
415
red_qxl_put_gl_scanout(QXLInstance * qxl,SpiceMsgDisplayGlScanoutUnix * scanout)416 void red_qxl_put_gl_scanout(QXLInstance *qxl, SpiceMsgDisplayGlScanoutUnix *scanout)
417 {
418 if (scanout) {
419 pthread_mutex_unlock(&qxl->st->scanout_mutex);
420 }
421 }
422
423 SPICE_GNUC_VISIBLE
spice_qxl_gl_scanout(QXLInstance * qxl,int fd,uint32_t width,uint32_t height,uint32_t stride,uint32_t format,int y_0_top)424 void spice_qxl_gl_scanout(QXLInstance *qxl,
425 int fd,
426 uint32_t width, uint32_t height,
427 uint32_t stride, uint32_t format,
428 int y_0_top)
429 {
430 RedWorkerMessageGlScanout payload = { /* empty */ };
431 spice_return_if_fail(qxl != nullptr);
432
433 QXLState *qxl_state = qxl->st;
434 spice_return_if_fail(qxl_state->gl_draw_cookie == GL_DRAW_COOKIE_INVALID);
435
436 pthread_mutex_lock(&qxl_state->scanout_mutex);
437
438 if (qxl_state->scanout.drm_dma_buf_fd >= 0) {
439 close(qxl_state->scanout.drm_dma_buf_fd);
440 }
441
442 qxl_state->scanout = (SpiceMsgDisplayGlScanoutUnix) {
443 .drm_dma_buf_fd = fd,
444 .width = width,
445 .height = height,
446 .stride = stride,
447 .drm_fourcc_format = format,
448 .flags = y_0_top ? SPICE_GL_SCANOUT_FLAGS_Y0TOP : 0,
449 };
450
451 pthread_mutex_unlock(&qxl_state->scanout_mutex);
452
453 /* FIXME: find a way to coallesce all pending SCANOUTs */
454 qxl_state->dispatcher->send_message(RED_WORKER_MESSAGE_GL_SCANOUT, &payload);
455 reds_update_client_mouse_allowed(qxl_state->reds);
456 }
457
458 SPICE_GNUC_VISIBLE
spice_qxl_gl_draw_async(QXLInstance * qxl,uint32_t x,uint32_t y,uint32_t w,uint32_t h,uint64_t cookie)459 void spice_qxl_gl_draw_async(QXLInstance *qxl,
460 uint32_t x, uint32_t y,
461 uint32_t w, uint32_t h,
462 uint64_t cookie)
463 {
464 QXLState *qxl_state;
465 RedWorkerMessage message = RED_WORKER_MESSAGE_GL_DRAW_ASYNC;
466 RedWorkerMessageGlDraw draw = {
467 {
468 .x = x,
469 .y = y,
470 .w = w,
471 .h = h
472 },
473 };
474
475 spice_return_if_fail(qxl != nullptr);
476 qxl_state = qxl->st;
477 if (qxl_state->scanout.drm_dma_buf_fd < 0) {
478 spice_warning("called spice_qxl_gl_draw_async without a buffer");
479 red_qxl_async_complete(qxl, cookie);
480 return;
481 }
482 spice_return_if_fail(qxl_state->gl_draw_cookie == GL_DRAW_COOKIE_INVALID);
483
484 qxl_state->gl_draw_cookie = cookie;
485 qxl_state->dispatcher->send_message(message, &draw);
486 }
487
red_qxl_gl_draw_async_complete(QXLInstance * qxl)488 void red_qxl_gl_draw_async_complete(QXLInstance *qxl)
489 {
490 /* this reset before usage prevent a possible race condition */
491 uint64_t cookie = qxl->st->gl_draw_cookie;
492 qxl->st->gl_draw_cookie = GL_DRAW_COOKIE_INVALID;
493 red_qxl_async_complete(qxl, cookie);
494 }
495
496 SPICE_GNUC_VISIBLE
spice_qxl_set_device_info(QXLInstance * instance,const char * device_address,uint32_t device_display_id_start,uint32_t device_display_id_count)497 void spice_qxl_set_device_info(QXLInstance *instance,
498 const char *device_address,
499 uint32_t device_display_id_start,
500 uint32_t device_display_id_count)
501 {
502 g_return_if_fail(device_address != nullptr);
503
504 size_t da_len = strnlen(device_address, MAX_DEVICE_ADDRESS_LEN);
505 if (da_len >= MAX_DEVICE_ADDRESS_LEN) {
506 spice_error("Device address too long: %" G_GSIZE_FORMAT " > %u",
507 da_len, MAX_DEVICE_ADDRESS_LEN);
508 return;
509 }
510
511 if (device_display_id_count > MAX_MONITORS_COUNT) {
512 spice_error("Device display ID count (%u) is greater than limit %u",
513 device_display_id_count,
514 MAX_MONITORS_COUNT);
515 return;
516 }
517
518 g_strlcpy(instance->st->device_address, device_address, MAX_DEVICE_ADDRESS_LEN);
519
520 g_debug("QXL Instance %d setting device address \"%s\" and monitor -> device display mapping:",
521 instance->id,
522 device_address);
523
524 // store the mapping monitor_id -> device_display_id
525 for (uint32_t monitor_id = 0; monitor_id < device_display_id_count; ++monitor_id) {
526 uint32_t device_display_id = device_display_id_start + monitor_id;
527 instance->st->device_display_ids[monitor_id] = device_display_id;
528 g_debug(" monitor ID %u -> device display ID %u",
529 monitor_id, device_display_id);
530 }
531
532 instance->st->monitors_count = device_display_id_count;
533 instance->st->max_monitors = MAX(1u, device_display_id_count);
534
535 reds_send_device_display_info(red_qxl_get_server(instance->st));
536 }
537
red_qxl_marshall_device_display_info(const QXLInstance * qxl,SpiceMarshaller * m)538 uint32_t red_qxl_marshall_device_display_info(const QXLInstance *qxl, SpiceMarshaller *m)
539 {
540 const QXLState *qxl_state = qxl->st;
541 uint32_t device_count = 0;
542 const char *const device_address = qxl_state->device_address;
543 const size_t device_address_len = strlen(device_address) + 1;
544
545 if (device_address_len == 1) {
546 return 0;
547 }
548 for (size_t i = 0; i < qxl_state->monitors_count; ++i) {
549 spice_marshaller_add_uint32(m, qxl->id);
550 spice_marshaller_add_uint32(m, i);
551 spice_marshaller_add_uint32(m, qxl_state->device_display_ids[i]);
552 spice_marshaller_add_uint32(m, device_address_len);
553 spice_marshaller_add(m, (const uint8_t*) (void*) device_address, device_address_len);
554 ++device_count;
555
556 g_debug(" (qxl) channel_id: %u monitor_id: %zu, device_address: %s, "
557 "device_display_id: %u",
558 qxl->id, i, device_address,
559 qxl_state->device_display_ids[i]);
560 }
561 return device_count;
562 }
563
red_qxl_init(RedsState * reds,QXLInstance * qxl)564 void red_qxl_init(RedsState *reds, QXLInstance *qxl)
565 {
566 QXLState *qxl_state;
567
568 spice_return_if_fail(qxl != nullptr);
569
570 qxl_state = new QXLState();
571 qxl_state->reds = reds;
572 qxl_state->qxl = qxl;
573 pthread_mutex_init(&qxl_state->scanout_mutex, nullptr);
574 qxl_state->scanout.drm_dma_buf_fd = -1;
575 qxl_state->gl_draw_cookie = GL_DRAW_COOKIE_INVALID;
576 qxl_state->dispatcher = red::make_shared<Dispatcher>(RED_WORKER_MESSAGE_COUNT);
577
578 qxl_state->max_monitors = UINT_MAX;
579 qxl->st = qxl_state;
580
581 qxl_state->worker = red_worker_new(qxl);
582
583 red_worker_run(qxl_state->worker);
584 }
585
red_qxl_destroy(QXLInstance * qxl)586 void red_qxl_destroy(QXLInstance *qxl)
587 {
588 spice_return_if_fail(qxl->st != nullptr && qxl->st->dispatcher);
589
590 QXLState *qxl_state = qxl->st;
591
592 /* send message to close thread */
593 RedWorkerMessageClose message;
594 qxl_state->dispatcher->send_message(RED_WORKER_MESSAGE_CLOSE_WORKER, &message);
595 red_worker_free(qxl_state->worker);
596 /* this must be done after calling red_worker_free */
597 qxl->st = nullptr;
598 pthread_mutex_destroy(&qxl_state->scanout_mutex);
599 delete qxl_state;
600 }
601
red_qxl_get_dispatcher(QXLInstance * qxl)602 Dispatcher *red_qxl_get_dispatcher(QXLInstance *qxl)
603 {
604 return qxl->st->dispatcher.get();
605 }
606
red_qxl_clear_pending(QXLState * qxl_state,int pending)607 void red_qxl_clear_pending(QXLState *qxl_state, int pending)
608 {
609 spice_return_if_fail(qxl_state != nullptr);
610
611 clear_bit(pending, &qxl_state->pending);
612 }
613
red_qxl_get_allow_client_mouse(QXLInstance * qxl,int * x_res,int * y_res,int * allow_now)614 bool red_qxl_get_allow_client_mouse(QXLInstance *qxl, int *x_res, int *y_res, int *allow_now)
615 {
616 // try to get resolution when 3D enabled, since qemu did not create QXL primary surface
617 SpiceMsgDisplayGlScanoutUnix *gl;
618 if ((gl = red_qxl_get_gl_scanout(qxl))) {
619 *x_res = gl->width;
620 *y_res = gl->height;
621 *allow_now = TRUE;
622 red_qxl_put_gl_scanout(qxl, gl);
623 return true;
624 }
625
626 // check for 2D
627 if (!qxl->st->primary_active) {
628 return false;
629 }
630
631 if (qxl->st->use_hardware_cursor) {
632 *x_res = qxl->st->x_res;
633 *y_res = qxl->st->y_res;
634 }
635 *allow_now = qxl->st->use_hardware_cursor;
636 return true;
637 }
638
red_qxl_on_ic_change(QXLInstance * qxl,SpiceImageCompression ic)639 void red_qxl_on_ic_change(QXLInstance *qxl, SpiceImageCompression ic)
640 {
641 RedWorkerMessageSetCompression payload;
642 payload.image_compression = ic;
643 qxl->st->dispatcher->send_message(RED_WORKER_MESSAGE_SET_COMPRESSION, &payload);
644 }
645
red_qxl_on_sv_change(QXLInstance * qxl,int sv)646 void red_qxl_on_sv_change(QXLInstance *qxl, int sv)
647 {
648 RedWorkerMessageSetStreamingVideo payload;
649 payload.streaming_video = sv;
650 qxl->st->dispatcher->send_message(RED_WORKER_MESSAGE_SET_STREAMING_VIDEO, &payload);
651 }
652
red_qxl_on_vc_change(QXLInstance * qxl,GArray * video_codecs)653 void red_qxl_on_vc_change(QXLInstance *qxl, GArray *video_codecs)
654 {
655 RedWorkerMessageSetVideoCodecs payload;
656 payload.video_codecs = g_array_ref(video_codecs);
657 qxl->st->dispatcher->send_message(RED_WORKER_MESSAGE_SET_VIDEO_CODECS, &payload);
658 }
659
red_qxl_set_mouse_mode(QXLInstance * qxl,uint32_t mode)660 void red_qxl_set_mouse_mode(QXLInstance *qxl, uint32_t mode)
661 {
662 RedWorkerMessageSetMouseMode payload;
663 payload.mode = mode;
664 qxl->st->dispatcher->send_message(RED_WORKER_MESSAGE_SET_MOUSE_MODE, &payload);
665 }
666
red_qxl_get_server(QXLState * qxl_state)667 RedsState* red_qxl_get_server(QXLState *qxl_state)
668 {
669 return qxl_state->reds;
670 }
671
red_qxl_attach_worker(QXLInstance * qxl)672 void red_qxl_attach_worker(QXLInstance *qxl)
673 {
674 QXLInterface *qxl_interface = qxl_get_interface(qxl);
675
676 /* cast for compatibility with spice_replay_next_cmd
677 * In the past spice_replay_next_cmd received a QXLWorker instead of
678 * a QXLInstance. Users of this function could have retrieved this pointer
679 * only by attache_worker callback but this structure was all deprecated.
680 * Passing QXLInstance pointer instead allows these programs to keep working
681 * although spice_replay_next_cmd declaration changed */
682 if (qxl_interface->attache_worker) {
683 qxl_interface->attache_worker(qxl, (QXLWorker *) qxl);
684 }
685 }
686
red_qxl_set_compression_level(QXLInstance * qxl,int level)687 void red_qxl_set_compression_level(QXLInstance *qxl, int level)
688 {
689 QXLInterface *qxl_interface = qxl_get_interface(qxl);
690 qxl_interface->set_compression_level(qxl, level);
691 }
692
red_qxl_get_init_info(QXLInstance * qxl,QXLDevInitInfo * info)693 void red_qxl_get_init_info(QXLInstance *qxl, QXLDevInitInfo *info)
694 {
695 QXLInterface *qxl_interface = qxl_get_interface(qxl);
696
697 qxl_interface->get_init_info(qxl, info);
698 }
699
red_qxl_get_command(QXLInstance * qxl,struct QXLCommandExt * cmd)700 int red_qxl_get_command(QXLInstance *qxl, struct QXLCommandExt *cmd)
701 {
702 QXLInterface *qxl_interface = qxl_get_interface(qxl);
703
704 return qxl_interface->get_command(qxl, cmd);
705 }
706
red_qxl_req_cmd_notification(QXLInstance * qxl)707 int red_qxl_req_cmd_notification(QXLInstance *qxl)
708 {
709 QXLInterface *qxl_interface = qxl_get_interface(qxl);
710
711 return qxl_interface->req_cmd_notification(qxl);
712 }
713
red_qxl_release_resource(QXLInstance * qxl,struct QXLReleaseInfoExt release_info)714 void red_qxl_release_resource(QXLInstance *qxl, struct QXLReleaseInfoExt release_info)
715 {
716 QXLInterface *qxl_interface = qxl_get_interface(qxl);
717
718 qxl_interface->release_resource(qxl, release_info);
719 }
720
red_qxl_get_cursor_command(QXLInstance * qxl,struct QXLCommandExt * cmd)721 int red_qxl_get_cursor_command(QXLInstance *qxl, struct QXLCommandExt *cmd)
722 {
723 QXLInterface *qxl_interface = qxl_get_interface(qxl);
724
725 return qxl_interface->get_cursor_command(qxl, cmd);
726 }
727
red_qxl_req_cursor_notification(QXLInstance * qxl)728 int red_qxl_req_cursor_notification(QXLInstance *qxl)
729 {
730 QXLInterface *qxl_interface = qxl_get_interface(qxl);
731
732 return qxl_interface->req_cursor_notification(qxl);
733 }
734
red_qxl_notify_update(QXLInstance * qxl,uint32_t update_id)735 void red_qxl_notify_update(QXLInstance *qxl, uint32_t update_id)
736 {
737 QXLInterface *qxl_interface = qxl_get_interface(qxl);
738
739 qxl_interface->notify_update(qxl, update_id);
740 }
741
red_qxl_flush_resources(QXLInstance * qxl)742 int red_qxl_flush_resources(QXLInstance *qxl)
743 {
744 QXLInterface *qxl_interface = qxl_get_interface(qxl);
745
746 return qxl_interface->flush_resources(qxl);
747 }
748
red_qxl_update_area_complete(QXLInstance * qxl,uint32_t surface_id,struct QXLRect * updated_rects,uint32_t num_updated_rects)749 void red_qxl_update_area_complete(QXLInstance *qxl, uint32_t surface_id,
750 struct QXLRect *updated_rects,
751 uint32_t num_updated_rects)
752 {
753 QXLInterface *qxl_interface = qxl_get_interface(qxl);
754
755 qxl_interface->update_area_complete(qxl, surface_id, updated_rects, num_updated_rects);
756 }
757
red_qxl_set_client_capabilities(QXLInstance * qxl,uint8_t client_present,uint8_t caps[SPICE_CAPABILITIES_SIZE])758 void red_qxl_set_client_capabilities(QXLInstance *qxl,
759 uint8_t client_present,
760 uint8_t caps[SPICE_CAPABILITIES_SIZE])
761 {
762 QXLInterface *qxl_interface = qxl_get_interface(qxl);
763
764 if (qxl->st->running) {
765 qxl_interface->set_client_capabilities(qxl, client_present, caps);
766 }
767 }
768
red_qxl_async_complete(QXLInstance * qxl,uint64_t cookie)769 void red_qxl_async_complete(QXLInstance *qxl, uint64_t cookie)
770 {
771 QXLInterface *qxl_interface = qxl_get_interface(qxl);
772
773 qxl_interface->async_complete(qxl, cookie);
774 }
775