|
|
1.1 root 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-common.h"
19: #include "qemu-spice.h"
20: #include "qemu-timer.h"
21: #include "qemu-queue.h"
22: #include "monitor.h"
23: #include "console.h"
24: #include "sysemu.h"
1.1.1.4 ! root 25: #include "trace.h"
1.1 root 26:
27: #include "spice-display.h"
28:
29: static int debug = 0;
30:
31: static void GCC_FMT_ATTR(2, 3) dprint(int level, const char *fmt, ...)
32: {
33: va_list args;
34:
35: if (level <= debug) {
36: va_start(args, fmt);
37: vfprintf(stderr, fmt, args);
38: va_end(args);
39: }
40: }
41:
42: int qemu_spice_rect_is_empty(const QXLRect* r)
43: {
44: return r->top == r->bottom || r->left == r->right;
45: }
46:
47: void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r)
48: {
49: if (qemu_spice_rect_is_empty(r)) {
50: return;
51: }
52:
53: if (qemu_spice_rect_is_empty(dest)) {
54: *dest = *r;
55: return;
56: }
57:
58: dest->top = MIN(dest->top, r->top);
59: dest->left = MIN(dest->left, r->left);
60: dest->bottom = MAX(dest->bottom, r->bottom);
61: dest->right = MAX(dest->right, r->right);
62: }
63:
1.1.1.4 ! root 64: QXLCookie *qxl_cookie_new(int type, uint64_t io)
! 65: {
! 66: QXLCookie *cookie;
! 67:
! 68: cookie = g_malloc0(sizeof(*cookie));
! 69: cookie->type = type;
! 70: cookie->io = io;
! 71: return cookie;
! 72: }
! 73:
1.1.1.3 root 74: void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot,
75: qxl_async_io async)
76: {
1.1.1.4 ! root 77: trace_qemu_spice_add_memslot(ssd->qxl.id, memslot->slot_id,
! 78: memslot->virt_start, memslot->virt_end,
! 79: async);
! 80:
1.1.1.3 root 81: if (async != QXL_SYNC) {
1.1.1.4 ! root 82: spice_qxl_add_memslot_async(&ssd->qxl, memslot,
! 83: (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
! 84: QXL_IO_MEMSLOT_ADD_ASYNC));
1.1.1.3 root 85: } else {
86: ssd->worker->add_memslot(ssd->worker, memslot);
87: }
88: }
89:
90: void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, uint32_t sid)
91: {
1.1.1.4 ! root 92: trace_qemu_spice_del_memslot(ssd->qxl.id, gid, sid);
1.1.1.3 root 93: ssd->worker->del_memslot(ssd->worker, gid, sid);
94: }
95:
96: void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id,
97: QXLDevSurfaceCreate *surface,
98: qxl_async_io async)
99: {
1.1.1.4 ! root 100: trace_qemu_spice_create_primary_surface(ssd->qxl.id, id, surface, async);
1.1.1.3 root 101: if (async != QXL_SYNC) {
1.1.1.4 ! root 102: spice_qxl_create_primary_surface_async(&ssd->qxl, id, surface,
! 103: (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
! 104: QXL_IO_CREATE_PRIMARY_ASYNC));
1.1.1.3 root 105: } else {
106: ssd->worker->create_primary_surface(ssd->worker, id, surface);
107: }
108: }
109:
110: void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd,
111: uint32_t id, qxl_async_io async)
112: {
1.1.1.4 ! root 113: trace_qemu_spice_destroy_primary_surface(ssd->qxl.id, id, async);
1.1.1.3 root 114: if (async != QXL_SYNC) {
1.1.1.4 ! root 115: spice_qxl_destroy_primary_surface_async(&ssd->qxl, id,
! 116: (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
! 117: QXL_IO_DESTROY_PRIMARY_ASYNC));
1.1.1.3 root 118: } else {
119: ssd->worker->destroy_primary_surface(ssd->worker, id);
120: }
121: }
122:
123: void qemu_spice_wakeup(SimpleSpiceDisplay *ssd)
124: {
1.1.1.4 ! root 125: trace_qemu_spice_wakeup(ssd->qxl.id);
1.1.1.3 root 126: ssd->worker->wakeup(ssd->worker);
127: }
128:
129: void qemu_spice_start(SimpleSpiceDisplay *ssd)
130: {
1.1.1.4 ! root 131: trace_qemu_spice_start(ssd->qxl.id);
1.1.1.3 root 132: ssd->worker->start(ssd->worker);
133: }
134:
135: void qemu_spice_stop(SimpleSpiceDisplay *ssd)
136: {
1.1.1.4 ! root 137: trace_qemu_spice_stop(ssd->qxl.id);
1.1.1.3 root 138: ssd->worker->stop(ssd->worker);
139: }
140:
1.1.1.2 root 141: static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd)
1.1 root 142: {
143: SimpleSpiceUpdate *update;
144: QXLDrawable *drawable;
145: QXLImage *image;
146: QXLCommand *cmd;
147: uint8_t *src, *dst;
148: int by, bw, bh;
1.1.1.2 root 149: struct timespec time_space;
1.1 root 150:
151: if (qemu_spice_rect_is_empty(&ssd->dirty)) {
152: return NULL;
153: };
154:
1.1.1.4 ! root 155: trace_qemu_spice_create_update(
1.1 root 156: ssd->dirty.left, ssd->dirty.right,
157: ssd->dirty.top, ssd->dirty.bottom);
158:
1.1.1.3 root 159: update = g_malloc0(sizeof(*update));
1.1 root 160: drawable = &update->drawable;
161: image = &update->image;
162: cmd = &update->ext.cmd;
163:
164: bw = ssd->dirty.right - ssd->dirty.left;
165: bh = ssd->dirty.bottom - ssd->dirty.top;
1.1.1.3 root 166: update->bitmap = g_malloc(bw * bh * 4);
1.1 root 167:
168: drawable->bbox = ssd->dirty;
169: drawable->clip.type = SPICE_CLIP_TYPE_NONE;
170: drawable->effect = QXL_EFFECT_OPAQUE;
1.1.1.4 ! root 171: drawable->release_info.id = (uintptr_t)update;
1.1 root 172: drawable->type = QXL_DRAW_COPY;
173: drawable->surfaces_dest[0] = -1;
174: drawable->surfaces_dest[1] = -1;
175: drawable->surfaces_dest[2] = -1;
1.1.1.2 root 176: clock_gettime(CLOCK_MONOTONIC, &time_space);
177: /* time in milliseconds from epoch. */
178: drawable->mm_time = time_space.tv_sec * 1000
179: + time_space.tv_nsec / 1000 / 1000;
1.1 root 180:
181: drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT;
1.1.1.4 ! root 182: drawable->u.copy.src_bitmap = (uintptr_t)image;
1.1 root 183: drawable->u.copy.src_area.right = bw;
184: drawable->u.copy.src_area.bottom = bh;
185:
186: QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ssd->unique++);
187: image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
188: image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN;
189: image->bitmap.stride = bw * 4;
190: image->descriptor.width = image->bitmap.x = bw;
191: image->descriptor.height = image->bitmap.y = bh;
1.1.1.4 ! root 192: image->bitmap.data = (uintptr_t)(update->bitmap);
1.1 root 193: image->bitmap.palette = 0;
194: image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
195:
196: if (ssd->conv == NULL) {
197: PixelFormat dst = qemu_default_pixelformat(32);
198: ssd->conv = qemu_pf_conv_get(&dst, &ssd->ds->surface->pf);
199: assert(ssd->conv);
200: }
201:
202: src = ds_get_data(ssd->ds) +
203: ssd->dirty.top * ds_get_linesize(ssd->ds) +
204: ssd->dirty.left * ds_get_bytes_per_pixel(ssd->ds);
205: dst = update->bitmap;
206: for (by = 0; by < bh; by++) {
207: qemu_pf_conv_run(ssd->conv, dst, src, bw);
208: src += ds_get_linesize(ssd->ds);
209: dst += image->bitmap.stride;
210: }
211:
212: cmd->type = QXL_CMD_DRAW;
1.1.1.4 ! root 213: cmd->data = (uintptr_t)drawable;
1.1 root 214:
215: memset(&ssd->dirty, 0, sizeof(ssd->dirty));
216: return update;
217: }
218:
219: /*
220: * Called from spice server thread context (via interface_release_ressource)
221: * We do *not* hold the global qemu mutex here, so extra care is needed
1.1.1.4 ! root 222: * when calling qemu functions. QEMU interfaces used:
1.1.1.3 root 223: * - g_free (underlying glibc free is re-entrant).
1.1 root 224: */
225: void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update)
226: {
1.1.1.3 root 227: g_free(update->bitmap);
228: g_free(update);
1.1 root 229: }
230:
231: void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
232: {
233: QXLDevMemSlot memslot;
234:
235: dprint(1, "%s:\n", __FUNCTION__);
236:
237: memset(&memslot, 0, sizeof(memslot));
238: memslot.slot_group_id = MEMSLOT_GROUP_HOST;
239: memslot.virt_end = ~0;
1.1.1.3 root 240: qemu_spice_add_memslot(ssd, &memslot, QXL_SYNC);
1.1 root 241: }
242:
243: void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
244: {
245: QXLDevSurfaceCreate surface;
246:
247: dprint(1, "%s: %dx%d\n", __FUNCTION__,
248: ds_get_width(ssd->ds), ds_get_height(ssd->ds));
249:
250: surface.format = SPICE_SURFACE_FMT_32_xRGB;
251: surface.width = ds_get_width(ssd->ds);
252: surface.height = ds_get_height(ssd->ds);
253: surface.stride = -surface.width * 4;
254: surface.mouse_mode = true;
255: surface.flags = 0;
256: surface.type = 0;
1.1.1.4 ! root 257: surface.mem = (uintptr_t)ssd->buf;
1.1 root 258: surface.group_id = MEMSLOT_GROUP_HOST;
259:
1.1.1.3 root 260: qemu_spice_create_primary_surface(ssd, 0, &surface, QXL_SYNC);
1.1 root 261: }
262:
263: void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
264: {
265: dprint(1, "%s:\n", __FUNCTION__);
266:
1.1.1.3 root 267: qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC);
1.1 root 268: }
269:
1.1.1.3 root 270: void qemu_spice_vm_change_state_handler(void *opaque, int running,
271: RunState state)
1.1 root 272: {
273: SimpleSpiceDisplay *ssd = opaque;
274:
275: if (running) {
1.1.1.3 root 276: ssd->running = true;
277: qemu_spice_start(ssd);
1.1 root 278: } else {
1.1.1.3 root 279: qemu_spice_stop(ssd);
280: ssd->running = false;
1.1 root 281: }
1.1.1.3 root 282: }
283:
284: void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds)
285: {
286: ssd->ds = ds;
287: qemu_mutex_init(&ssd->lock);
288: ssd->mouse_x = -1;
289: ssd->mouse_y = -1;
290: ssd->bufsize = (16 * 1024 * 1024);
291: ssd->buf = g_malloc(ssd->bufsize);
1.1 root 292: }
293:
294: /* display listener callbacks */
295:
296: void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
297: int x, int y, int w, int h)
298: {
299: QXLRect update_area;
300:
301: dprint(2, "%s: x %d y %d w %d h %d\n", __FUNCTION__, x, y, w, h);
302: update_area.left = x,
303: update_area.right = x + w;
304: update_area.top = y;
305: update_area.bottom = y + h;
306:
307: if (qemu_spice_rect_is_empty(&ssd->dirty)) {
308: ssd->notify++;
309: }
310: qemu_spice_rect_union(&ssd->dirty, &update_area);
311: }
312:
313: void qemu_spice_display_resize(SimpleSpiceDisplay *ssd)
314: {
315: dprint(1, "%s:\n", __FUNCTION__);
316:
317: memset(&ssd->dirty, 0, sizeof(ssd->dirty));
318: qemu_pf_conv_put(ssd->conv);
319: ssd->conv = NULL;
320:
1.1.1.2 root 321: qemu_mutex_lock(&ssd->lock);
322: if (ssd->update != NULL) {
323: qemu_spice_destroy_update(ssd, ssd->update);
324: ssd->update = NULL;
325: }
326: qemu_mutex_unlock(&ssd->lock);
1.1 root 327: qemu_spice_destroy_host_primary(ssd);
328: qemu_spice_create_host_primary(ssd);
329:
330: memset(&ssd->dirty, 0, sizeof(ssd->dirty));
331: ssd->notify++;
332: }
333:
1.1.1.4 ! root 334: void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd)
1.1 root 335: {
1.1.1.2 root 336: if (ssd->cursor) {
337: ssd->ds->cursor_define(ssd->cursor);
338: cursor_put(ssd->cursor);
339: ssd->cursor = NULL;
340: }
341: if (ssd->mouse_x != -1 && ssd->mouse_y != -1) {
342: ssd->ds->mouse_set(ssd->mouse_x, ssd->mouse_y, 1);
343: ssd->mouse_x = -1;
344: ssd->mouse_y = -1;
345: }
1.1.1.4 ! root 346: }
! 347:
! 348: void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
! 349: {
! 350: dprint(3, "%s:\n", __func__);
! 351: vga_hw_update();
! 352:
! 353: qemu_mutex_lock(&ssd->lock);
! 354: if (ssd->update == NULL) {
! 355: ssd->update = qemu_spice_create_update(ssd);
! 356: ssd->notify++;
! 357: }
! 358: qemu_spice_cursor_refresh_unlocked(ssd);
1.1.1.2 root 359: qemu_mutex_unlock(&ssd->lock);
360:
1.1 root 361: if (ssd->notify) {
362: ssd->notify = 0;
1.1.1.3 root 363: qemu_spice_wakeup(ssd);
1.1 root 364: dprint(2, "%s: notify\n", __FUNCTION__);
365: }
366: }
367:
368: /* spice display interface callbacks */
369:
370: static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
371: {
372: SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
373:
374: dprint(1, "%s:\n", __FUNCTION__);
375: ssd->worker = qxl_worker;
376: }
377:
378: static void interface_set_compression_level(QXLInstance *sin, int level)
379: {
380: dprint(1, "%s:\n", __FUNCTION__);
381: /* nothing to do */
382: }
383:
384: static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
385: {
386: dprint(3, "%s:\n", __FUNCTION__);
387: /* nothing to do */
388: }
389:
390: static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
391: {
392: SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
393:
394: info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
395: info->memslot_id_bits = MEMSLOT_SLOT_BITS;
396: info->num_memslots = NUM_MEMSLOTS;
397: info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
398: info->internal_groupslot_id = 0;
399: info->qxl_ram_size = ssd->bufsize;
400: info->n_surfaces = NUM_SURFACES;
401: }
402:
403: static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
404: {
405: SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
406: SimpleSpiceUpdate *update;
1.1.1.2 root 407: int ret = false;
1.1 root 408:
409: dprint(3, "%s:\n", __FUNCTION__);
1.1.1.2 root 410:
411: qemu_mutex_lock(&ssd->lock);
412: if (ssd->update != NULL) {
413: update = ssd->update;
414: ssd->update = NULL;
415: *ext = update->ext;
416: ret = true;
1.1 root 417: }
1.1.1.2 root 418: qemu_mutex_unlock(&ssd->lock);
419:
420: return ret;
1.1 root 421: }
422:
423: static int interface_req_cmd_notification(QXLInstance *sin)
424: {
425: dprint(1, "%s:\n", __FUNCTION__);
426: return 1;
427: }
428:
429: static void interface_release_resource(QXLInstance *sin,
430: struct QXLReleaseInfoExt ext)
431: {
432: SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
433: uintptr_t id;
434:
435: dprint(2, "%s:\n", __FUNCTION__);
436: id = ext.info->id;
437: qemu_spice_destroy_update(ssd, (void*)id);
438: }
439:
440: static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
441: {
442: dprint(3, "%s:\n", __FUNCTION__);
443: return false;
444: }
445:
446: static int interface_req_cursor_notification(QXLInstance *sin)
447: {
448: dprint(1, "%s:\n", __FUNCTION__);
449: return 1;
450: }
451:
452: static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
453: {
454: fprintf(stderr, "%s: abort()\n", __FUNCTION__);
455: abort();
456: }
457:
458: static int interface_flush_resources(QXLInstance *sin)
459: {
460: fprintf(stderr, "%s: abort()\n", __FUNCTION__);
461: abort();
462: return 0;
463: }
464:
465: static const QXLInterface dpy_interface = {
466: .base.type = SPICE_INTERFACE_QXL,
467: .base.description = "qemu simple display",
468: .base.major_version = SPICE_INTERFACE_QXL_MAJOR,
469: .base.minor_version = SPICE_INTERFACE_QXL_MINOR,
470:
471: .attache_worker = interface_attach_worker,
472: .set_compression_level = interface_set_compression_level,
473: .set_mm_time = interface_set_mm_time,
474: .get_init_info = interface_get_init_info,
475:
476: /* the callbacks below are called from spice server thread context */
477: .get_command = interface_get_command,
478: .req_cmd_notification = interface_req_cmd_notification,
479: .release_resource = interface_release_resource,
480: .get_cursor_command = interface_get_cursor_command,
481: .req_cursor_notification = interface_req_cursor_notification,
482: .notify_update = interface_notify_update,
483: .flush_resources = interface_flush_resources,
484: };
485:
486: static SimpleSpiceDisplay sdpy;
487:
488: static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
489: {
490: qemu_spice_display_update(&sdpy, x, y, w, h);
491: }
492:
493: static void display_resize(struct DisplayState *ds)
494: {
495: qemu_spice_display_resize(&sdpy);
496: }
497:
498: static void display_refresh(struct DisplayState *ds)
499: {
500: qemu_spice_display_refresh(&sdpy);
501: }
502:
503: static DisplayChangeListener display_listener = {
504: .dpy_update = display_update,
505: .dpy_resize = display_resize,
506: .dpy_refresh = display_refresh,
507: };
508:
509: void qemu_spice_display_init(DisplayState *ds)
510: {
511: assert(sdpy.ds == NULL);
1.1.1.3 root 512: qemu_spice_display_init_common(&sdpy, ds);
1.1 root 513: register_displaychangelistener(ds, &display_listener);
514:
515: sdpy.qxl.base.sif = &dpy_interface.base;
516: qemu_spice_add_interface(&sdpy.qxl.base);
517: assert(sdpy.worker);
518:
519: qemu_add_vm_change_state_handler(qemu_spice_vm_change_state_handler, &sdpy);
520: qemu_spice_create_host_memslot(&sdpy);
521: qemu_spice_create_host_primary(&sdpy);
522: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.