spice-display.c 38 KB

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