xref: /qemu/ui/spice-display.c (revision ec7e5a90fea996f04ea24e81b680a87bc975354a)
1 /*
2  * Copyright (C) 2010 Red Hat, Inc.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 or
7  * (at your option) version 3 of the License.
8  *
9  * This program 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
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "qemu/osdep.h"
19 #include "ui/qemu-spice.h"
20 #include "qemu/error-report.h"
21 #include "qemu/timer.h"
22 #include "qemu/lockable.h"
23 #include "qemu/main-loop.h"
24 #include "qemu/option.h"
25 #include "qemu/queue.h"
26 #include "ui/console.h"
27 #include "trace.h"
28 
29 #include "ui/spice-display.h"
30 
31 #include "standard-headers/drm/drm_fourcc.h"
32 
33 bool spice_opengl;
34 
35 int qemu_spice_rect_is_empty(const QXLRect* r)
36 {
37     return r->top == r->bottom || r->left == r->right;
38 }
39 
40 void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r)
41 {
42     if (qemu_spice_rect_is_empty(r)) {
43         return;
44     }
45 
46     if (qemu_spice_rect_is_empty(dest)) {
47         *dest = *r;
48         return;
49     }
50 
51     dest->top = MIN(dest->top, r->top);
52     dest->left = MIN(dest->left, r->left);
53     dest->bottom = MAX(dest->bottom, r->bottom);
54     dest->right = MAX(dest->right, r->right);
55 }
56 
57 QXLCookie *qxl_cookie_new(int type, uint64_t io)
58 {
59     QXLCookie *cookie;
60 
61     cookie = g_malloc0(sizeof(*cookie));
62     cookie->type = type;
63     cookie->io = io;
64     return cookie;
65 }
66 
67 void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot,
68                             qxl_async_io async)
69 {
70     trace_qemu_spice_add_memslot(ssd->qxl.id, memslot->slot_id,
71                                 memslot->virt_start, memslot->virt_end,
72                                 async);
73 
74     if (async != QXL_SYNC) {
75         spice_qxl_add_memslot_async(&ssd->qxl, memslot,
76                 (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
77                                           QXL_IO_MEMSLOT_ADD_ASYNC));
78     } else {
79         spice_qxl_add_memslot(&ssd->qxl, memslot);
80     }
81 }
82 
83 void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, uint32_t sid)
84 {
85     trace_qemu_spice_del_memslot(ssd->qxl.id, gid, sid);
86     spice_qxl_del_memslot(&ssd->qxl, gid, sid);
87 }
88 
89 void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id,
90                                        QXLDevSurfaceCreate *surface,
91                                        qxl_async_io async)
92 {
93     trace_qemu_spice_create_primary_surface(ssd->qxl.id, id, surface, async);
94     if (async != QXL_SYNC) {
95         spice_qxl_create_primary_surface_async(&ssd->qxl, id, surface,
96                 (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
97                                           QXL_IO_CREATE_PRIMARY_ASYNC));
98     } else {
99         spice_qxl_create_primary_surface(&ssd->qxl, id, surface);
100     }
101 }
102 
103 void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd,
104                                         uint32_t id, qxl_async_io async)
105 {
106     trace_qemu_spice_destroy_primary_surface(ssd->qxl.id, id, async);
107     if (async != QXL_SYNC) {
108         spice_qxl_destroy_primary_surface_async(&ssd->qxl, id,
109                 (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
110                                           QXL_IO_DESTROY_PRIMARY_ASYNC));
111     } else {
112         spice_qxl_destroy_primary_surface(&ssd->qxl, id);
113     }
114 }
115 
116 void qemu_spice_wakeup(SimpleSpiceDisplay *ssd)
117 {
118     trace_qemu_spice_wakeup(ssd->qxl.id);
119     spice_qxl_wakeup(&ssd->qxl);
120 }
121 
122 static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd,
123                                          QXLRect *rect)
124 {
125     SimpleSpiceUpdate *update;
126     QXLDrawable *drawable;
127     QXLImage *image;
128     QXLCommand *cmd;
129     int bw, bh;
130     struct timespec time_space;
131     pixman_image_t *dest;
132 
133     trace_qemu_spice_create_update(
134            rect->left, rect->right,
135            rect->top, rect->bottom);
136 
137     update   = g_malloc0(sizeof(*update));
138     drawable = &update->drawable;
139     image    = &update->image;
140     cmd      = &update->ext.cmd;
141 
142     bw       = rect->right - rect->left;
143     bh       = rect->bottom - rect->top;
144     update->bitmap = g_malloc(bw * bh * 4);
145 
146     drawable->bbox            = *rect;
147     drawable->clip.type       = SPICE_CLIP_TYPE_NONE;
148     drawable->effect          = QXL_EFFECT_OPAQUE;
149     drawable->release_info.id = (uintptr_t)(&update->ext);
150     drawable->type            = QXL_DRAW_COPY;
151     drawable->surfaces_dest[0] = -1;
152     drawable->surfaces_dest[1] = -1;
153     drawable->surfaces_dest[2] = -1;
154     clock_gettime(CLOCK_MONOTONIC, &time_space);
155     /* time in milliseconds from epoch. */
156     drawable->mm_time = time_space.tv_sec * 1000
157                       + time_space.tv_nsec / 1000 / 1000;
158 
159     drawable->u.copy.rop_descriptor  = SPICE_ROPD_OP_PUT;
160     drawable->u.copy.src_bitmap      = (uintptr_t)image;
161     drawable->u.copy.src_area.right  = bw;
162     drawable->u.copy.src_area.bottom = bh;
163 
164     QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ssd->unique++);
165     image->descriptor.type   = SPICE_IMAGE_TYPE_BITMAP;
166     image->bitmap.flags      = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN;
167     image->bitmap.stride     = bw * 4;
168     image->descriptor.width  = image->bitmap.x = bw;
169     image->descriptor.height = image->bitmap.y = bh;
170     image->bitmap.data = (uintptr_t)(update->bitmap);
171     image->bitmap.palette = 0;
172     image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
173 
174     dest = pixman_image_create_bits(PIXMAN_LE_x8r8g8b8, bw, bh,
175                                     (void *)update->bitmap, bw * 4);
176     pixman_image_composite(PIXMAN_OP_SRC, ssd->surface, NULL, ssd->mirror,
177                            rect->left, rect->top, 0, 0,
178                            rect->left, rect->top, bw, bh);
179     pixman_image_composite(PIXMAN_OP_SRC, ssd->mirror, NULL, dest,
180                            rect->left, rect->top, 0, 0,
181                            0, 0, bw, bh);
182     pixman_image_unref(dest);
183 
184     cmd->type = QXL_CMD_DRAW;
185     cmd->data = (uintptr_t)drawable;
186 
187     QTAILQ_INSERT_TAIL(&ssd->updates, update, next);
188 }
189 
190 static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
191 {
192     static const int blksize = 32;
193     int blocks = DIV_ROUND_UP(surface_width(ssd->ds), blksize);
194     g_autofree int *dirty_top = NULL;
195     int y, yoff1, yoff2, x, xoff, blk, bw;
196     int bpp = surface_bytes_per_pixel(ssd->ds);
197     uint8_t *guest, *mirror;
198 
199     if (qemu_spice_rect_is_empty(&ssd->dirty)) {
200         return;
201     };
202 
203     dirty_top = g_new(int, blocks);
204     for (blk = 0; blk < blocks; blk++) {
205         dirty_top[blk] = -1;
206     }
207 
208     guest = surface_data(ssd->ds);
209     mirror = (void *)pixman_image_get_data(ssd->mirror);
210     for (y = ssd->dirty.top; y < ssd->dirty.bottom; y++) {
211         yoff1 = y * surface_stride(ssd->ds);
212         yoff2 = y * pixman_image_get_stride(ssd->mirror);
213         for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) {
214             xoff = x * bpp;
215             blk = x / blksize;
216             bw = MIN(blksize, ssd->dirty.right - x);
217             if (memcmp(guest + yoff1 + xoff,
218                        mirror + yoff2 + xoff,
219                        bw * bpp) == 0) {
220                 if (dirty_top[blk] != -1) {
221                     QXLRect update = {
222                         .top    = dirty_top[blk],
223                         .bottom = y,
224                         .left   = x,
225                         .right  = x + bw,
226                     };
227                     qemu_spice_create_one_update(ssd, &update);
228                     dirty_top[blk] = -1;
229                 }
230             } else {
231                 if (dirty_top[blk] == -1) {
232                     dirty_top[blk] = y;
233                 }
234             }
235         }
236     }
237 
238     for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) {
239         blk = x / blksize;
240         bw = MIN(blksize, ssd->dirty.right - x);
241         if (dirty_top[blk] != -1) {
242             QXLRect update = {
243                 .top    = dirty_top[blk],
244                 .bottom = ssd->dirty.bottom,
245                 .left   = x,
246                 .right  = x + bw,
247             };
248             qemu_spice_create_one_update(ssd, &update);
249             dirty_top[blk] = -1;
250         }
251     }
252 
253     memset(&ssd->dirty, 0, sizeof(ssd->dirty));
254 }
255 
256 static SimpleSpiceCursor*
257 qemu_spice_create_cursor_update(SimpleSpiceDisplay *ssd,
258                                 QEMUCursor *c,
259                                 bool on)
260 {
261     size_t size = c ? c->width * c->height * 4 : 0;
262     SimpleSpiceCursor *update;
263     QXLCursorCmd *ccmd;
264     QXLCursor *cursor;
265     QXLCommand *cmd;
266 
267     update   = g_malloc0(sizeof(*update) + size);
268     ccmd     = &update->cmd;
269     cursor   = &update->cursor;
270     cmd      = &update->ext.cmd;
271 
272     if (c) {
273         ccmd->type = QXL_CURSOR_SET;
274         ccmd->u.set.position.x = ssd->ptr_x + ssd->hot_x;
275         ccmd->u.set.position.y = ssd->ptr_y + ssd->hot_y;
276         ccmd->u.set.visible    = true;
277         ccmd->u.set.shape      = (uintptr_t)cursor;
278         cursor->header.unique     = ssd->unique++;
279         cursor->header.type       = SPICE_CURSOR_TYPE_ALPHA;
280         cursor->header.width      = c->width;
281         cursor->header.height     = c->height;
282         cursor->header.hot_spot_x = c->hot_x;
283         cursor->header.hot_spot_y = c->hot_y;
284         cursor->data_size         = size;
285         cursor->chunk.data_size   = size;
286         memcpy(cursor->chunk.data, c->data, size);
287     } else if (!on) {
288         ccmd->type = QXL_CURSOR_HIDE;
289     } else {
290         ccmd->type = QXL_CURSOR_MOVE;
291         ccmd->u.position.x = ssd->ptr_x + ssd->hot_x;
292         ccmd->u.position.y = ssd->ptr_y + ssd->hot_y;
293     }
294     ccmd->release_info.id = (uintptr_t)(&update->ext);
295 
296     cmd->type = QXL_CMD_CURSOR;
297     cmd->data = (uintptr_t)ccmd;
298 
299     return update;
300 }
301 
302 /*
303  * Called from spice server thread context (via interface_release_resource)
304  * We do *not* hold the global qemu mutex here, so extra care is needed
305  * when calling qemu functions.  QEMU interfaces used:
306  *    - g_free (underlying glibc free is re-entrant).
307  */
308 void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update)
309 {
310     g_free(update->bitmap);
311     g_free(update);
312 }
313 
314 void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
315 {
316     QXLDevMemSlot memslot;
317 
318     memset(&memslot, 0, sizeof(memslot));
319     memslot.slot_group_id = MEMSLOT_GROUP_HOST;
320     memslot.virt_end = ~0;
321     qemu_spice_add_memslot(ssd, &memslot, QXL_SYNC);
322 }
323 
324 void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
325 {
326     QXLDevSurfaceCreate surface;
327     uint64_t surface_size;
328 
329     memset(&surface, 0, sizeof(surface));
330 
331     surface_size = (uint64_t) surface_width(ssd->ds) *
332         surface_height(ssd->ds) * 4;
333     assert(surface_size > 0);
334     assert(surface_size < INT_MAX);
335     if (ssd->bufsize < surface_size) {
336         ssd->bufsize = surface_size;
337         g_free(ssd->buf);
338         ssd->buf = g_malloc(ssd->bufsize);
339     }
340 
341     surface.format     = SPICE_SURFACE_FMT_32_xRGB;
342     surface.width      = surface_width(ssd->ds);
343     surface.height     = surface_height(ssd->ds);
344     surface.stride     = -surface.width * 4;
345     surface.mouse_mode = true;
346     surface.flags      = 0;
347     surface.type       = 0;
348     surface.mem        = (uintptr_t)ssd->buf;
349     surface.group_id   = MEMSLOT_GROUP_HOST;
350 
351     qemu_spice_create_primary_surface(ssd, 0, &surface, QXL_SYNC);
352 }
353 
354 void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
355 {
356     qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC);
357 }
358 
359 void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd)
360 {
361     qemu_mutex_init(&ssd->lock);
362     QTAILQ_INIT(&ssd->updates);
363     ssd->mouse_x = -1;
364     ssd->mouse_y = -1;
365     if (ssd->num_surfaces == 0) {
366         ssd->num_surfaces = 1024;
367     }
368 }
369 
370 /* display listener callbacks */
371 
372 void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
373                                int x, int y, int w, int h)
374 {
375     QXLRect update_area;
376 
377     trace_qemu_spice_display_update(ssd->qxl.id, x, y, w, h);
378     update_area.left = x,
379     update_area.right = x + w;
380     update_area.top = y;
381     update_area.bottom = y + h;
382 
383     if (qemu_spice_rect_is_empty(&ssd->dirty)) {
384         ssd->notify++;
385     }
386     qemu_spice_rect_union(&ssd->dirty, &update_area);
387 }
388 
389 void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
390                                DisplaySurface *surface)
391 {
392     SimpleSpiceUpdate *update;
393     bool need_destroy;
394 
395     if (ssd->surface &&
396         surface_width(surface) == pixman_image_get_width(ssd->surface) &&
397         surface_height(surface) == pixman_image_get_height(ssd->surface) &&
398         surface_format(surface) == pixman_image_get_format(ssd->surface)) {
399         /* no-resize fast path: just swap backing store */
400         trace_qemu_spice_display_surface(ssd->qxl.id,
401                                          surface_width(surface),
402                                          surface_height(surface),
403                                          true);
404         qemu_mutex_lock(&ssd->lock);
405         ssd->ds = surface;
406         pixman_image_unref(ssd->surface);
407         ssd->surface = pixman_image_ref(ssd->ds->image);
408         qemu_mutex_unlock(&ssd->lock);
409         qemu_spice_display_update(ssd, 0, 0,
410                                   surface_width(surface),
411                                   surface_height(surface));
412         return;
413     }
414 
415     /* full mode switch */
416     trace_qemu_spice_display_surface(ssd->qxl.id,
417                                      surface_width(surface),
418                                      surface_height(surface),
419                                      false);
420 
421     memset(&ssd->dirty, 0, sizeof(ssd->dirty));
422     if (ssd->surface) {
423         pixman_image_unref(ssd->surface);
424         ssd->surface = NULL;
425         pixman_image_unref(ssd->mirror);
426         ssd->mirror = NULL;
427     }
428 
429     qemu_mutex_lock(&ssd->lock);
430     need_destroy = (ssd->ds != NULL);
431     ssd->ds = surface;
432     while ((update = QTAILQ_FIRST(&ssd->updates)) != NULL) {
433         QTAILQ_REMOVE(&ssd->updates, update, next);
434         qemu_spice_destroy_update(ssd, update);
435     }
436     qemu_mutex_unlock(&ssd->lock);
437     if (need_destroy) {
438         qemu_spice_destroy_host_primary(ssd);
439     }
440     if (ssd->ds) {
441         ssd->surface = pixman_image_ref(ssd->ds->image);
442         ssd->mirror  = qemu_pixman_mirror_create(surface_format(ssd->ds),
443                                                  ssd->ds->image);
444         qemu_spice_create_host_primary(ssd);
445     }
446 
447     memset(&ssd->dirty, 0, sizeof(ssd->dirty));
448     ssd->notify++;
449 
450     qemu_mutex_lock(&ssd->lock);
451     if (ssd->cursor) {
452         g_free(ssd->ptr_define);
453         ssd->ptr_define =
454             qemu_spice_create_cursor_update(ssd, ssd->cursor, false);
455     }
456     qemu_mutex_unlock(&ssd->lock);
457 }
458 
459 void qemu_spice_cursor_refresh_bh(void *opaque)
460 {
461     SimpleSpiceDisplay *ssd = opaque;
462 
463     qemu_mutex_lock(&ssd->lock);
464     if (ssd->cursor) {
465         QEMUCursor *c = ssd->cursor;
466         assert(ssd->dcl.con);
467         cursor_ref(c);
468         qemu_mutex_unlock(&ssd->lock);
469         dpy_cursor_define(ssd->dcl.con, c);
470         qemu_mutex_lock(&ssd->lock);
471         cursor_unref(c);
472     }
473 
474     if (ssd->mouse_x != -1 && ssd->mouse_y != -1) {
475         int x, y;
476         assert(ssd->dcl.con);
477         x = ssd->mouse_x;
478         y = ssd->mouse_y;
479         ssd->mouse_x = -1;
480         ssd->mouse_y = -1;
481         qemu_mutex_unlock(&ssd->lock);
482         dpy_mouse_set(ssd->dcl.con, x, y, true);
483     } else {
484         qemu_mutex_unlock(&ssd->lock);
485     }
486 }
487 
488 void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
489 {
490     graphic_hw_update(ssd->dcl.con);
491 
492     WITH_QEMU_LOCK_GUARD(&ssd->lock) {
493         if (QTAILQ_EMPTY(&ssd->updates) && ssd->ds) {
494             qemu_spice_create_update(ssd);
495             ssd->notify++;
496         }
497     }
498 
499     trace_qemu_spice_display_refresh(ssd->qxl.id, ssd->notify);
500     if (ssd->notify) {
501         ssd->notify = 0;
502         qemu_spice_wakeup(ssd);
503     }
504 }
505 
506 /* spice display interface callbacks */
507 
508 #if SPICE_HAS_ATTACHED_WORKER
509 static void interface_attached_worker(QXLInstance *sin)
510 {
511     /* nothing to do */
512 }
513 #else
514 static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
515 {
516     /* nothing to do */
517 }
518 #endif
519 
520 static void interface_set_compression_level(QXLInstance *sin, int level)
521 {
522     /* nothing to do */
523 }
524 
525 static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
526 {
527     SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
528 
529     info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
530     info->memslot_id_bits  = MEMSLOT_SLOT_BITS;
531     info->num_memslots = NUM_MEMSLOTS;
532     info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
533     info->internal_groupslot_id = 0;
534     info->qxl_ram_size = 16 * 1024 * 1024;
535     info->n_surfaces = ssd->num_surfaces;
536 }
537 
538 static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext)
539 {
540     SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
541     SimpleSpiceUpdate *update;
542     int ret = false;
543 
544     qemu_mutex_lock(&ssd->lock);
545     update = QTAILQ_FIRST(&ssd->updates);
546     if (update != NULL) {
547         QTAILQ_REMOVE(&ssd->updates, update, next);
548         *ext = update->ext;
549         ret = true;
550     }
551     qemu_mutex_unlock(&ssd->lock);
552 
553     return ret;
554 }
555 
556 static int interface_req_cmd_notification(QXLInstance *sin)
557 {
558     return 1;
559 }
560 
561 static void interface_release_resource(QXLInstance *sin,
562                                        QXLReleaseInfoExt rext)
563 {
564     SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
565     SimpleSpiceUpdate *update;
566     SimpleSpiceCursor *cursor;
567     QXLCommandExt *ext;
568 
569     if (!rext.info) {
570         return;
571     }
572 
573     ext = (void *)(intptr_t)(rext.info->id);
574     switch (ext->cmd.type) {
575     case QXL_CMD_DRAW:
576         update = container_of(ext, SimpleSpiceUpdate, ext);
577         qemu_spice_destroy_update(ssd, update);
578         break;
579     case QXL_CMD_CURSOR:
580         cursor = container_of(ext, SimpleSpiceCursor, ext);
581         g_free(cursor);
582         break;
583     default:
584         g_assert_not_reached();
585     }
586 }
587 
588 static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext)
589 {
590     SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
591     int ret;
592 
593     QEMU_LOCK_GUARD(&ssd->lock);
594     if (ssd->ptr_define) {
595         *ext = ssd->ptr_define->ext;
596         ssd->ptr_define = NULL;
597         ret = true;
598     } else if (ssd->ptr_move) {
599         *ext = ssd->ptr_move->ext;
600         ssd->ptr_move = NULL;
601         ret = true;
602     } else {
603         ret = false;
604     }
605     return ret;
606 }
607 
608 static int interface_req_cursor_notification(QXLInstance *sin)
609 {
610     return 1;
611 }
612 
613 static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
614 {
615     fprintf(stderr, "%s: abort()\n", __func__);
616     abort();
617 }
618 
619 static int interface_flush_resources(QXLInstance *sin)
620 {
621     fprintf(stderr, "%s: abort()\n", __func__);
622     abort();
623     return 0;
624 }
625 
626 static void interface_update_area_complete(QXLInstance *sin,
627         uint32_t surface_id,
628         QXLRect *dirty, uint32_t num_updated_rects)
629 {
630     /* should never be called, used in qxl native mode only */
631     fprintf(stderr, "%s: abort()\n", __func__);
632     abort();
633 }
634 
635 /* called from spice server thread context only */
636 static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
637 {
638     QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token;
639 
640     switch (cookie->type) {
641 #ifdef HAVE_SPICE_GL
642     case QXL_COOKIE_TYPE_GL_DRAW_DONE:
643     {
644         SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
645         qemu_bh_schedule(ssd->gl_unblock_bh);
646         break;
647     }
648     case QXL_COOKIE_TYPE_IO:
649         if (cookie->io == QXL_IO_MONITORS_CONFIG_ASYNC) {
650             g_free(cookie->u.data);
651         }
652         break;
653 #endif
654     default:
655         /* should never be called, used in qxl native mode only */
656         fprintf(stderr, "%s: abort()\n", __func__);
657         abort();
658     }
659     g_free(cookie);
660 }
661 
662 static void interface_set_client_capabilities(QXLInstance *sin,
663                                               uint8_t client_present,
664                                               uint8_t caps[58])
665 {
666     /* nothing to do */
667 }
668 
669 static int interface_client_monitors_config(QXLInstance *sin,
670                                             VDAgentMonitorsConfig *mc)
671 {
672     SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
673     QemuUIInfo info;
674     int head;
675 
676     if (!dpy_ui_info_supported(ssd->dcl.con)) {
677         return 0; /* == not supported by guest */
678     }
679 
680     if (!mc) {
681         return 1;
682     }
683 
684     info = *dpy_get_ui_info(ssd->dcl.con);
685 
686     head = qemu_console_get_index(ssd->dcl.con);
687     if (mc->num_of_monitors > head) {
688         info.width  = mc->monitors[head].width;
689         info.height = mc->monitors[head].height;
690 #if SPICE_SERVER_VERSION >= 0x000e04 /* release 0.14.4 */
691         if (mc->flags & VD_AGENT_CONFIG_MONITORS_FLAG_PHYSICAL_SIZE) {
692             VDAgentMonitorMM *mm = (void *)&mc->monitors[mc->num_of_monitors];
693             info.width_mm = mm[head].width;
694             info.height_mm = mm[head].height;
695         }
696 #endif
697     }
698 
699     trace_qemu_spice_ui_info(ssd->qxl.id, info.width, info.height);
700     dpy_set_ui_info(ssd->dcl.con, &info, false);
701     return 1;
702 }
703 
704 static const QXLInterface dpy_interface = {
705     .base.type               = SPICE_INTERFACE_QXL,
706     .base.description        = "qemu simple display",
707     .base.major_version      = SPICE_INTERFACE_QXL_MAJOR,
708     .base.minor_version      = SPICE_INTERFACE_QXL_MINOR,
709 
710 #if SPICE_HAS_ATTACHED_WORKER
711     .attached_worker         = interface_attached_worker,
712 #else
713     .attache_worker          = interface_attach_worker,
714 #endif
715     .set_compression_level   = interface_set_compression_level,
716     .get_init_info           = interface_get_init_info,
717 
718     /* the callbacks below are called from spice server thread context */
719     .get_command             = interface_get_command,
720     .req_cmd_notification    = interface_req_cmd_notification,
721     .release_resource        = interface_release_resource,
722     .get_cursor_command      = interface_get_cursor_command,
723     .req_cursor_notification = interface_req_cursor_notification,
724     .notify_update           = interface_notify_update,
725     .flush_resources         = interface_flush_resources,
726     .async_complete          = interface_async_complete,
727     .update_area_complete    = interface_update_area_complete,
728     .set_client_capabilities = interface_set_client_capabilities,
729     .client_monitors_config  = interface_client_monitors_config,
730 };
731 
732 static void display_update(DisplayChangeListener *dcl,
733                            int x, int y, int w, int h)
734 {
735     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
736     qemu_spice_display_update(ssd, x, y, w, h);
737 }
738 
739 static void display_switch(DisplayChangeListener *dcl,
740                            DisplaySurface *surface)
741 {
742     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
743     qemu_spice_display_switch(ssd, surface);
744 }
745 
746 static void display_refresh(DisplayChangeListener *dcl)
747 {
748     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
749     qemu_spice_display_refresh(ssd);
750 }
751 
752 static void display_mouse_set(DisplayChangeListener *dcl,
753                               int x, int y, bool on)
754 {
755     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
756 
757     qemu_mutex_lock(&ssd->lock);
758     ssd->ptr_x = x;
759     ssd->ptr_y = y;
760     g_free(ssd->ptr_move);
761     ssd->ptr_move = qemu_spice_create_cursor_update(ssd, NULL, on);
762     qemu_mutex_unlock(&ssd->lock);
763     qemu_spice_wakeup(ssd);
764 }
765 
766 static void display_mouse_define(DisplayChangeListener *dcl,
767                                  QEMUCursor *c)
768 {
769     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
770 
771     qemu_mutex_lock(&ssd->lock);
772     cursor_ref(c);
773     cursor_unref(ssd->cursor);
774     ssd->cursor = c;
775     ssd->hot_x = c->hot_x;
776     ssd->hot_y = c->hot_y;
777     g_free(ssd->ptr_move);
778     ssd->ptr_move = NULL;
779     g_free(ssd->ptr_define);
780     ssd->ptr_define = qemu_spice_create_cursor_update(ssd, c, false);
781     qemu_mutex_unlock(&ssd->lock);
782     qemu_spice_wakeup(ssd);
783 }
784 
785 static const DisplayChangeListenerOps display_listener_ops = {
786     .dpy_name             = "spice",
787     .dpy_gfx_update       = display_update,
788     .dpy_gfx_switch       = display_switch,
789     .dpy_gfx_check_format = qemu_pixman_check_format,
790     .dpy_refresh          = display_refresh,
791     .dpy_mouse_set        = display_mouse_set,
792     .dpy_cursor_define    = display_mouse_define,
793 };
794 
795 #ifdef HAVE_SPICE_GL
796 
797 static void qemu_spice_gl_monitor_config(SimpleSpiceDisplay *ssd,
798                                          int x, int y, int w, int h)
799 {
800     QXLMonitorsConfig *config;
801     QXLCookie *cookie;
802 
803     config = g_malloc0(sizeof(QXLMonitorsConfig) + sizeof(QXLHead));
804     config->count = 1;
805     config->max_allowed = 1;
806     config->heads[0].x = x;
807     config->heads[0].y = y;
808     config->heads[0].width = w;
809     config->heads[0].height = h;
810     cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
811                             QXL_IO_MONITORS_CONFIG_ASYNC);
812     cookie->u.data = config;
813 
814     spice_qxl_monitors_config_async(&ssd->qxl,
815                                     (uintptr_t)config,
816                                     MEMSLOT_GROUP_HOST,
817                                     (uintptr_t)cookie);
818 }
819 
820 static void qemu_spice_gl_block(SimpleSpiceDisplay *ssd, bool block)
821 {
822     uint64_t timeout;
823 
824     if (block) {
825         timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
826         timeout += 1000; /* one sec */
827         timer_mod(ssd->gl_unblock_timer, timeout);
828     } else {
829         timer_del(ssd->gl_unblock_timer);
830     }
831     graphic_hw_gl_block(ssd->dcl.con, block);
832 }
833 
834 static void qemu_spice_gl_unblock_bh(void *opaque)
835 {
836     SimpleSpiceDisplay *ssd = opaque;
837 
838     qemu_spice_gl_block(ssd, false);
839 }
840 
841 static void qemu_spice_gl_block_timer(void *opaque)
842 {
843     warn_report("spice: no gl-draw-done within one second");
844 }
845 
846 static void spice_gl_refresh(DisplayChangeListener *dcl)
847 {
848     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
849     uint64_t cookie;
850 
851     if (!ssd->ds || qemu_console_is_gl_blocked(ssd->dcl.con)) {
852         return;
853     }
854 
855     graphic_hw_update(dcl->con);
856     if (ssd->gl_updates && ssd->have_surface) {
857         qemu_spice_gl_block(ssd, true);
858         glFlush();
859         cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
860         spice_qxl_gl_draw_async(&ssd->qxl, 0, 0,
861                                 surface_width(ssd->ds),
862                                 surface_height(ssd->ds),
863                                 cookie);
864         ssd->gl_updates = 0;
865     }
866 }
867 
868 static void spice_gl_update(DisplayChangeListener *dcl,
869                             int x, int y, int w, int h)
870 {
871     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
872 
873     surface_gl_update_texture(ssd->gls, ssd->ds, x, y, w, h);
874     ssd->gl_updates++;
875 }
876 
877 static void spice_server_gl_scanout(QXLInstance *qxl,
878                                     const int *fd,
879                                     uint32_t width, uint32_t height,
880                                     const uint32_t *offset,
881                                     const uint32_t *stride,
882                                     uint32_t num_planes, uint32_t format,
883                                     uint64_t modifier, int y_0_top)
884 {
885 #ifdef HAVE_SPICE_QXL_GL_SCANOUT2
886     spice_qxl_gl_scanout2(qxl, fd, width, height, offset, stride,
887                           num_planes, format, modifier, y_0_top);
888 #else
889     if (num_planes <= 1) {
890         spice_qxl_gl_scanout(qxl, fd[0], width, height, stride[0], format, y_0_top);
891     } else {
892         error_report("SPICE server does not support multi plane GL scanout");
893     }
894 #endif
895 }
896 
897 static void spice_gl_switch(DisplayChangeListener *dcl,
898                             struct DisplaySurface *new_surface)
899 {
900     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
901 
902     if (ssd->ds) {
903         surface_gl_destroy_texture(ssd->gls, ssd->ds);
904     }
905     ssd->ds = new_surface;
906     if (ssd->ds) {
907         uint32_t offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES];
908         int fd[DMABUF_MAX_PLANES], num_planes, fourcc;
909         uint64_t modifier;
910 
911         surface_gl_create_texture(ssd->gls, ssd->ds);
912         if (!egl_dmabuf_export_texture(ssd->ds->texture,
913                                        fd,
914                                        (EGLint *)offset,
915                                        (EGLint *)stride,
916                                        &fourcc,
917                                        &num_planes,
918                                        &modifier)) {
919             surface_gl_destroy_texture(ssd->gls, ssd->ds);
920             return;
921         }
922 
923         trace_qemu_spice_gl_surface(ssd->qxl.id,
924                                     surface_width(ssd->ds),
925                                     surface_height(ssd->ds),
926                                     fourcc);
927 
928         /* note: spice server will close the fd */
929         spice_server_gl_scanout(&ssd->qxl, fd,
930                                 surface_width(ssd->ds),
931                                 surface_height(ssd->ds),
932                                 offset, stride, num_planes,
933                                 fourcc, modifier, false);
934         ssd->have_surface = true;
935         ssd->have_scanout = false;
936 
937         qemu_spice_gl_monitor_config(ssd, 0, 0,
938                                      surface_width(ssd->ds),
939                                      surface_height(ssd->ds));
940     }
941 }
942 
943 static QEMUGLContext qemu_spice_gl_create_context(DisplayGLCtx *dgc,
944                                                   QEMUGLParams *params)
945 {
946     eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
947                    qemu_egl_rn_ctx);
948     return qemu_egl_create_context(dgc, params);
949 }
950 
951 static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl)
952 {
953     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
954 
955     trace_qemu_spice_gl_scanout_disable(ssd->qxl.id);
956     spice_server_gl_scanout(&ssd->qxl, NULL, 0, 0, NULL, NULL, 0, DRM_FORMAT_INVALID,
957                             DRM_FORMAT_MOD_INVALID, false);
958     qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0);
959     ssd->have_surface = false;
960     ssd->have_scanout = false;
961 }
962 
963 static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
964                                           uint32_t tex_id,
965                                           bool y_0_top,
966                                           uint32_t backing_width,
967                                           uint32_t backing_height,
968                                           uint32_t x, uint32_t y,
969                                           uint32_t w, uint32_t h,
970                                           void *d3d_tex2d)
971 {
972     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
973     EGLint offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc = 0;
974     int fd[DMABUF_MAX_PLANES], num_planes;
975     uint64_t modifier;
976 
977     assert(tex_id);
978     if (!egl_dmabuf_export_texture(tex_id, fd, offset, stride, &fourcc,
979                                    &num_planes, &modifier)) {
980         fprintf(stderr, "%s: failed to export dmabuf for texture\n", __func__);
981         return;
982     }
983 
984     trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc);
985 
986     /* note: spice server will close the fd */
987     spice_server_gl_scanout(&ssd->qxl, fd, backing_width, backing_height,
988                             (uint32_t *)offset, (uint32_t *)stride, num_planes,
989                             fourcc, modifier, y_0_top);
990     qemu_spice_gl_monitor_config(ssd, x, y, w, h);
991     ssd->have_surface = false;
992     ssd->have_scanout = true;
993 }
994 
995 static void qemu_spice_gl_scanout_dmabuf(DisplayChangeListener *dcl,
996                                          QemuDmaBuf *dmabuf)
997 {
998     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
999 
1000     ssd->guest_dmabuf = dmabuf;
1001     ssd->guest_dmabuf_refresh = true;
1002 
1003     ssd->have_surface = false;
1004     ssd->have_scanout = true;
1005 }
1006 
1007 static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl,
1008                                         QemuDmaBuf *dmabuf, bool have_hot,
1009                                         uint32_t hot_x, uint32_t hot_y)
1010 {
1011     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
1012     uint32_t width, height, texture;
1013 
1014     ssd->have_hot = have_hot;
1015     ssd->hot_x = hot_x;
1016     ssd->hot_y = hot_y;
1017 
1018     trace_qemu_spice_gl_cursor(ssd->qxl.id, dmabuf != NULL, have_hot);
1019     if (dmabuf) {
1020         egl_dmabuf_import_texture(dmabuf);
1021         texture = qemu_dmabuf_get_texture(dmabuf);
1022         if (!texture) {
1023             return;
1024         }
1025         width = qemu_dmabuf_get_width(dmabuf);
1026         height = qemu_dmabuf_get_height(dmabuf);
1027         egl_fb_setup_for_tex(&ssd->cursor_fb, width, height, texture, false);
1028     } else {
1029         egl_fb_destroy(&ssd->cursor_fb);
1030     }
1031 }
1032 
1033 static void qemu_spice_gl_cursor_position(DisplayChangeListener *dcl,
1034                                           uint32_t pos_x, uint32_t pos_y)
1035 {
1036     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
1037 
1038     qemu_mutex_lock(&ssd->lock);
1039     ssd->ptr_x = pos_x;
1040     ssd->ptr_y = pos_y;
1041     qemu_mutex_unlock(&ssd->lock);
1042 }
1043 
1044 static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl,
1045                                          QemuDmaBuf *dmabuf)
1046 {
1047     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
1048 
1049     if (ssd->guest_dmabuf == dmabuf) {
1050         ssd->guest_dmabuf = NULL;
1051         ssd->guest_dmabuf_refresh = false;
1052     }
1053     egl_dmabuf_release_texture(dmabuf);
1054 }
1055 
1056 static void qemu_spice_gl_update(DisplayChangeListener *dcl,
1057                                  uint32_t x, uint32_t y, uint32_t w, uint32_t h)
1058 {
1059     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
1060     EGLint fourcc = 0;
1061     bool render_cursor = false;
1062     bool y_0_top = false; /* FIXME */
1063     uint64_t cookie;
1064     uint32_t width, height, texture;
1065 
1066     if (!ssd->have_scanout) {
1067         return;
1068     }
1069 
1070     if (ssd->cursor_fb.texture) {
1071         render_cursor = true;
1072     }
1073     if (ssd->render_cursor != render_cursor) {
1074         ssd->render_cursor = render_cursor;
1075         ssd->guest_dmabuf_refresh = true;
1076         egl_fb_destroy(&ssd->blit_fb);
1077     }
1078 
1079     if (ssd->guest_dmabuf_refresh) {
1080         QemuDmaBuf *dmabuf = ssd->guest_dmabuf;
1081         width = qemu_dmabuf_get_width(dmabuf);
1082         height = qemu_dmabuf_get_height(dmabuf);
1083 
1084         if (render_cursor) {
1085             egl_dmabuf_import_texture(dmabuf);
1086             texture = qemu_dmabuf_get_texture(dmabuf);
1087             if (!texture) {
1088                 return;
1089             }
1090 
1091             /* source framebuffer */
1092             egl_fb_setup_for_tex(&ssd->guest_fb, width, height,
1093                                  texture, false);
1094 
1095             /* dest framebuffer */
1096             if (ssd->blit_fb.width  != width ||
1097                 ssd->blit_fb.height != height) {
1098                 int fds[DMABUF_MAX_PLANES], num_planes;
1099                 uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES];
1100                 uint64_t modifier;
1101 
1102                 trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, width,
1103                                                   height);
1104                 egl_fb_destroy(&ssd->blit_fb);
1105                 egl_fb_setup_new_tex(&ssd->blit_fb,
1106                                      width, height);
1107                 if (!egl_dmabuf_export_texture(ssd->blit_fb.texture, fds,
1108                                                (EGLint *)offsets, (EGLint *)strides,
1109                                                &fourcc, &num_planes, &modifier)) {
1110                     fprintf(stderr,
1111                             "%s: failed to export dmabuf for texture\n", __func__);
1112                     return;
1113                 }
1114 
1115                 spice_server_gl_scanout(&ssd->qxl, fds, width, height, offsets, strides,
1116                                         num_planes, fourcc, modifier, false);
1117             }
1118         } else {
1119             int fds[DMABUF_MAX_PLANES];
1120             int noffsets, nstrides;
1121             const uint32_t *offsets = qemu_dmabuf_get_offsets(dmabuf, &noffsets);
1122             const uint32_t *strides = qemu_dmabuf_get_strides(dmabuf, &nstrides);
1123             uint32_t num_planes = qemu_dmabuf_get_num_planes(dmabuf);
1124 
1125             assert(noffsets >= num_planes);
1126             assert(nstrides >= num_planes);
1127 
1128             fourcc = qemu_dmabuf_get_fourcc(dmabuf);
1129             y_0_top = qemu_dmabuf_get_y0_top(dmabuf);
1130             qemu_dmabuf_dup_fds(dmabuf, fds, DMABUF_MAX_PLANES);
1131 
1132             trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id, width, height);
1133             /* note: spice server will close the fd, so hand over a dup */
1134             spice_server_gl_scanout(&ssd->qxl, fds, width, height,
1135                                     offsets, strides, num_planes,
1136                                     fourcc,
1137                                     qemu_dmabuf_get_modifier(dmabuf),
1138                                     y_0_top);
1139         }
1140         qemu_spice_gl_monitor_config(ssd, 0, 0, width, height);
1141         ssd->guest_dmabuf_refresh = false;
1142     }
1143 
1144     if (render_cursor) {
1145         int ptr_x, ptr_y;
1146 
1147         qemu_mutex_lock(&ssd->lock);
1148         ptr_x = ssd->ptr_x;
1149         ptr_y = ssd->ptr_y;
1150         qemu_mutex_unlock(&ssd->lock);
1151         egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb,
1152                          !y_0_top);
1153         egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb,
1154                           !y_0_top, ptr_x, ptr_y, 1.0, 1.0);
1155         glFlush();
1156     }
1157 
1158     trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y);
1159     qemu_spice_gl_block(ssd, true);
1160     glFlush();
1161     cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
1162     spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie);
1163 }
1164 
1165 static const DisplayChangeListenerOps display_listener_gl_ops = {
1166     .dpy_name                = "spice-egl",
1167     .dpy_gfx_update          = spice_gl_update,
1168     .dpy_gfx_switch          = spice_gl_switch,
1169     .dpy_gfx_check_format    = console_gl_check_format,
1170     .dpy_refresh             = spice_gl_refresh,
1171     .dpy_mouse_set           = display_mouse_set,
1172     .dpy_cursor_define       = display_mouse_define,
1173 
1174     .dpy_gl_scanout_disable  = qemu_spice_gl_scanout_disable,
1175     .dpy_gl_scanout_texture  = qemu_spice_gl_scanout_texture,
1176     .dpy_gl_scanout_dmabuf   = qemu_spice_gl_scanout_dmabuf,
1177     .dpy_gl_cursor_dmabuf    = qemu_spice_gl_cursor_dmabuf,
1178     .dpy_gl_cursor_position  = qemu_spice_gl_cursor_position,
1179     .dpy_gl_release_dmabuf   = qemu_spice_gl_release_dmabuf,
1180     .dpy_gl_update           = qemu_spice_gl_update,
1181 };
1182 
1183 static bool
1184 qemu_spice_is_compatible_dcl(DisplayGLCtx *dgc,
1185                              DisplayChangeListener *dcl)
1186 {
1187     return dcl->ops == &display_listener_gl_ops;
1188 }
1189 
1190 static const DisplayGLCtxOps gl_ctx_ops = {
1191     .dpy_gl_ctx_is_compatible_dcl = qemu_spice_is_compatible_dcl,
1192     .dpy_gl_ctx_create       = qemu_spice_gl_create_context,
1193     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
1194     .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
1195 };
1196 
1197 #endif /* HAVE_SPICE_GL */
1198 
1199 static void qemu_spice_display_init_one(QemuConsole *con)
1200 {
1201     SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1);
1202 
1203     qemu_spice_display_init_common(ssd);
1204 
1205     ssd->dcl.ops = &display_listener_ops;
1206 #ifdef HAVE_SPICE_GL
1207     if (spice_opengl) {
1208         ssd->dcl.ops = &display_listener_gl_ops;
1209         ssd->dgc.ops = &gl_ctx_ops;
1210         ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd);
1211         ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1212                                              qemu_spice_gl_block_timer, ssd);
1213         ssd->gls = qemu_gl_init_shader();
1214         ssd->have_surface = false;
1215         ssd->have_scanout = false;
1216     }
1217 #endif
1218     ssd->dcl.con = con;
1219 
1220     ssd->qxl.base.sif = &dpy_interface.base;
1221     qemu_spice_add_display_interface(&ssd->qxl, con);
1222 
1223 #if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */
1224     Error *err = NULL;
1225     char device_address[256] = "";
1226     if (qemu_console_fill_device_address(con, device_address, 256, &err)) {
1227         spice_qxl_set_device_info(&ssd->qxl,
1228                                   device_address,
1229                                   qemu_console_get_head(con),
1230                                   1);
1231     } else {
1232         error_report_err(err);
1233     }
1234 #endif
1235 
1236     qemu_spice_create_host_memslot(ssd);
1237 
1238     if (spice_opengl) {
1239         qemu_console_set_display_gl_ctx(con, &ssd->dgc);
1240     }
1241     register_displaychangelistener(&ssd->dcl);
1242 }
1243 
1244 void qemu_spice_display_init(void)
1245 {
1246     QemuOptsList *olist = qemu_find_opts("spice");
1247     QemuOpts *opts = QTAILQ_FIRST(&olist->head);
1248     QemuConsole *spice_con, *con;
1249     const char *str;
1250     int i;
1251 
1252     str = qemu_opt_get(opts, "display");
1253     if (str) {
1254         int head = qemu_opt_get_number(opts, "head", 0);
1255         Error *err = NULL;
1256 
1257         spice_con = qemu_console_lookup_by_device_name(str, head, &err);
1258         if (err) {
1259             error_report("Failed to lookup display/head");
1260             exit(1);
1261         }
1262     } else {
1263         spice_con = NULL;
1264     }
1265 
1266     for (i = 0;; i++) {
1267         con = qemu_console_lookup_by_index(i);
1268         if (!con || !qemu_console_is_graphic(con)) {
1269             break;
1270         }
1271         if (qemu_spice_have_display_interface(con)) {
1272             continue;
1273         }
1274         if (spice_con != NULL && spice_con != con) {
1275             continue;
1276         }
1277         qemu_spice_display_init_one(con);
1278     }
1279 
1280     qemu_spice_display_init_done();
1281 }
1282