qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

spice-display.c (39135B)


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