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