|
|
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 <pthread.h> ! 19: ! 20: #include "qemu-common.h" ! 21: #include "qemu-spice.h" ! 22: #include "qemu-timer.h" ! 23: #include "qemu-queue.h" ! 24: #include "monitor.h" ! 25: #include "console.h" ! 26: #include "sysemu.h" ! 27: ! 28: #include "spice-display.h" ! 29: ! 30: static int debug = 0; ! 31: ! 32: static void GCC_FMT_ATTR(2, 3) dprint(int level, const char *fmt, ...) ! 33: { ! 34: va_list args; ! 35: ! 36: if (level <= debug) { ! 37: va_start(args, fmt); ! 38: vfprintf(stderr, fmt, args); ! 39: va_end(args); ! 40: } ! 41: } ! 42: ! 43: int qemu_spice_rect_is_empty(const QXLRect* r) ! 44: { ! 45: return r->top == r->bottom || r->left == r->right; ! 46: } ! 47: ! 48: void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r) ! 49: { ! 50: if (qemu_spice_rect_is_empty(r)) { ! 51: return; ! 52: } ! 53: ! 54: if (qemu_spice_rect_is_empty(dest)) { ! 55: *dest = *r; ! 56: return; ! 57: } ! 58: ! 59: dest->top = MIN(dest->top, r->top); ! 60: dest->left = MIN(dest->left, r->left); ! 61: dest->bottom = MAX(dest->bottom, r->bottom); ! 62: dest->right = MAX(dest->right, r->right); ! 63: } ! 64: ! 65: /* ! 66: * Called from spice server thread context (via interface_get_command). ! 67: * ! 68: * We must aquire the global qemu mutex here to make sure the ! 69: * DisplayState (+DisplaySurface) we are accessing doesn't change ! 70: * underneath us. ! 71: */ ! 72: SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) ! 73: { ! 74: SimpleSpiceUpdate *update; ! 75: QXLDrawable *drawable; ! 76: QXLImage *image; ! 77: QXLCommand *cmd; ! 78: uint8_t *src, *dst; ! 79: int by, bw, bh; ! 80: ! 81: qemu_mutex_lock_iothread(); ! 82: if (qemu_spice_rect_is_empty(&ssd->dirty)) { ! 83: qemu_mutex_unlock_iothread(); ! 84: return NULL; ! 85: }; ! 86: ! 87: dprint(2, "%s: lr %d -> %d, tb -> %d -> %d\n", __FUNCTION__, ! 88: ssd->dirty.left, ssd->dirty.right, ! 89: ssd->dirty.top, ssd->dirty.bottom); ! 90: ! 91: update = qemu_mallocz(sizeof(*update)); ! 92: drawable = &update->drawable; ! 93: image = &update->image; ! 94: cmd = &update->ext.cmd; ! 95: ! 96: bw = ssd->dirty.right - ssd->dirty.left; ! 97: bh = ssd->dirty.bottom - ssd->dirty.top; ! 98: update->bitmap = qemu_malloc(bw * bh * 4); ! 99: ! 100: drawable->bbox = ssd->dirty; ! 101: drawable->clip.type = SPICE_CLIP_TYPE_NONE; ! 102: drawable->effect = QXL_EFFECT_OPAQUE; ! 103: drawable->release_info.id = (intptr_t)update; ! 104: drawable->type = QXL_DRAW_COPY; ! 105: drawable->surfaces_dest[0] = -1; ! 106: drawable->surfaces_dest[1] = -1; ! 107: drawable->surfaces_dest[2] = -1; ! 108: ! 109: drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT; ! 110: drawable->u.copy.src_bitmap = (intptr_t)image; ! 111: drawable->u.copy.src_area.right = bw; ! 112: drawable->u.copy.src_area.bottom = bh; ! 113: ! 114: QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ssd->unique++); ! 115: image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP; ! 116: image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN; ! 117: image->bitmap.stride = bw * 4; ! 118: image->descriptor.width = image->bitmap.x = bw; ! 119: image->descriptor.height = image->bitmap.y = bh; ! 120: image->bitmap.data = (intptr_t)(update->bitmap); ! 121: image->bitmap.palette = 0; ! 122: image->bitmap.format = SPICE_BITMAP_FMT_32BIT; ! 123: ! 124: if (ssd->conv == NULL) { ! 125: PixelFormat dst = qemu_default_pixelformat(32); ! 126: ssd->conv = qemu_pf_conv_get(&dst, &ssd->ds->surface->pf); ! 127: assert(ssd->conv); ! 128: } ! 129: ! 130: src = ds_get_data(ssd->ds) + ! 131: ssd->dirty.top * ds_get_linesize(ssd->ds) + ! 132: ssd->dirty.left * ds_get_bytes_per_pixel(ssd->ds); ! 133: dst = update->bitmap; ! 134: for (by = 0; by < bh; by++) { ! 135: qemu_pf_conv_run(ssd->conv, dst, src, bw); ! 136: src += ds_get_linesize(ssd->ds); ! 137: dst += image->bitmap.stride; ! 138: } ! 139: ! 140: cmd->type = QXL_CMD_DRAW; ! 141: cmd->data = (intptr_t)drawable; ! 142: ! 143: memset(&ssd->dirty, 0, sizeof(ssd->dirty)); ! 144: qemu_mutex_unlock_iothread(); ! 145: return update; ! 146: } ! 147: ! 148: /* ! 149: * Called from spice server thread context (via interface_release_ressource) ! 150: * We do *not* hold the global qemu mutex here, so extra care is needed ! 151: * when calling qemu functions. Qemu interfaces used: ! 152: * - qemu_free (underlying glibc free is re-entrant). ! 153: */ ! 154: void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update) ! 155: { ! 156: qemu_free(update->bitmap); ! 157: qemu_free(update); ! 158: } ! 159: ! 160: void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd) ! 161: { ! 162: QXLDevMemSlot memslot; ! 163: ! 164: dprint(1, "%s:\n", __FUNCTION__); ! 165: ! 166: memset(&memslot, 0, sizeof(memslot)); ! 167: memslot.slot_group_id = MEMSLOT_GROUP_HOST; ! 168: memslot.virt_end = ~0; ! 169: ssd->worker->add_memslot(ssd->worker, &memslot); ! 170: } ! 171: ! 172: void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd) ! 173: { ! 174: QXLDevSurfaceCreate surface; ! 175: ! 176: dprint(1, "%s: %dx%d\n", __FUNCTION__, ! 177: ds_get_width(ssd->ds), ds_get_height(ssd->ds)); ! 178: ! 179: surface.format = SPICE_SURFACE_FMT_32_xRGB; ! 180: surface.width = ds_get_width(ssd->ds); ! 181: surface.height = ds_get_height(ssd->ds); ! 182: surface.stride = -surface.width * 4; ! 183: surface.mouse_mode = true; ! 184: surface.flags = 0; ! 185: surface.type = 0; ! 186: surface.mem = (intptr_t)ssd->buf; ! 187: surface.group_id = MEMSLOT_GROUP_HOST; ! 188: ! 189: qemu_mutex_unlock_iothread(); ! 190: ssd->worker->create_primary_surface(ssd->worker, 0, &surface); ! 191: qemu_mutex_lock_iothread(); ! 192: } ! 193: ! 194: void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd) ! 195: { ! 196: dprint(1, "%s:\n", __FUNCTION__); ! 197: ! 198: qemu_mutex_unlock_iothread(); ! 199: ssd->worker->destroy_primary_surface(ssd->worker, 0); ! 200: qemu_mutex_lock_iothread(); ! 201: } ! 202: ! 203: void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason) ! 204: { ! 205: SimpleSpiceDisplay *ssd = opaque; ! 206: ! 207: if (running) { ! 208: ssd->worker->start(ssd->worker); ! 209: } else { ! 210: qemu_mutex_unlock_iothread(); ! 211: ssd->worker->stop(ssd->worker); ! 212: qemu_mutex_lock_iothread(); ! 213: } ! 214: ssd->running = running; ! 215: } ! 216: ! 217: /* display listener callbacks */ ! 218: ! 219: void qemu_spice_display_update(SimpleSpiceDisplay *ssd, ! 220: int x, int y, int w, int h) ! 221: { ! 222: QXLRect update_area; ! 223: ! 224: dprint(2, "%s: x %d y %d w %d h %d\n", __FUNCTION__, x, y, w, h); ! 225: update_area.left = x, ! 226: update_area.right = x + w; ! 227: update_area.top = y; ! 228: update_area.bottom = y + h; ! 229: ! 230: if (qemu_spice_rect_is_empty(&ssd->dirty)) { ! 231: ssd->notify++; ! 232: } ! 233: qemu_spice_rect_union(&ssd->dirty, &update_area); ! 234: } ! 235: ! 236: void qemu_spice_display_resize(SimpleSpiceDisplay *ssd) ! 237: { ! 238: dprint(1, "%s:\n", __FUNCTION__); ! 239: ! 240: memset(&ssd->dirty, 0, sizeof(ssd->dirty)); ! 241: qemu_pf_conv_put(ssd->conv); ! 242: ssd->conv = NULL; ! 243: ! 244: qemu_spice_destroy_host_primary(ssd); ! 245: qemu_spice_create_host_primary(ssd); ! 246: ! 247: memset(&ssd->dirty, 0, sizeof(ssd->dirty)); ! 248: ssd->notify++; ! 249: } ! 250: ! 251: void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) ! 252: { ! 253: dprint(3, "%s:\n", __FUNCTION__); ! 254: vga_hw_update(); ! 255: if (ssd->notify) { ! 256: ssd->notify = 0; ! 257: ssd->worker->wakeup(ssd->worker); ! 258: dprint(2, "%s: notify\n", __FUNCTION__); ! 259: } ! 260: } ! 261: ! 262: /* spice display interface callbacks */ ! 263: ! 264: static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) ! 265: { ! 266: SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); ! 267: ! 268: dprint(1, "%s:\n", __FUNCTION__); ! 269: ssd->worker = qxl_worker; ! 270: } ! 271: ! 272: static void interface_set_compression_level(QXLInstance *sin, int level) ! 273: { ! 274: dprint(1, "%s:\n", __FUNCTION__); ! 275: /* nothing to do */ ! 276: } ! 277: ! 278: static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) ! 279: { ! 280: dprint(3, "%s:\n", __FUNCTION__); ! 281: /* nothing to do */ ! 282: } ! 283: ! 284: static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) ! 285: { ! 286: SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); ! 287: ! 288: info->memslot_gen_bits = MEMSLOT_GENERATION_BITS; ! 289: info->memslot_id_bits = MEMSLOT_SLOT_BITS; ! 290: info->num_memslots = NUM_MEMSLOTS; ! 291: info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; ! 292: info->internal_groupslot_id = 0; ! 293: info->qxl_ram_size = ssd->bufsize; ! 294: info->n_surfaces = NUM_SURFACES; ! 295: } ! 296: ! 297: static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) ! 298: { ! 299: SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); ! 300: SimpleSpiceUpdate *update; ! 301: ! 302: dprint(3, "%s:\n", __FUNCTION__); ! 303: update = qemu_spice_create_update(ssd); ! 304: if (update == NULL) { ! 305: return false; ! 306: } ! 307: *ext = update->ext; ! 308: return true; ! 309: } ! 310: ! 311: static int interface_req_cmd_notification(QXLInstance *sin) ! 312: { ! 313: dprint(1, "%s:\n", __FUNCTION__); ! 314: return 1; ! 315: } ! 316: ! 317: static void interface_release_resource(QXLInstance *sin, ! 318: struct QXLReleaseInfoExt ext) ! 319: { ! 320: SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); ! 321: uintptr_t id; ! 322: ! 323: dprint(2, "%s:\n", __FUNCTION__); ! 324: id = ext.info->id; ! 325: qemu_spice_destroy_update(ssd, (void*)id); ! 326: } ! 327: ! 328: static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext) ! 329: { ! 330: dprint(3, "%s:\n", __FUNCTION__); ! 331: return false; ! 332: } ! 333: ! 334: static int interface_req_cursor_notification(QXLInstance *sin) ! 335: { ! 336: dprint(1, "%s:\n", __FUNCTION__); ! 337: return 1; ! 338: } ! 339: ! 340: static void interface_notify_update(QXLInstance *sin, uint32_t update_id) ! 341: { ! 342: fprintf(stderr, "%s: abort()\n", __FUNCTION__); ! 343: abort(); ! 344: } ! 345: ! 346: static int interface_flush_resources(QXLInstance *sin) ! 347: { ! 348: fprintf(stderr, "%s: abort()\n", __FUNCTION__); ! 349: abort(); ! 350: return 0; ! 351: } ! 352: ! 353: static const QXLInterface dpy_interface = { ! 354: .base.type = SPICE_INTERFACE_QXL, ! 355: .base.description = "qemu simple display", ! 356: .base.major_version = SPICE_INTERFACE_QXL_MAJOR, ! 357: .base.minor_version = SPICE_INTERFACE_QXL_MINOR, ! 358: ! 359: .attache_worker = interface_attach_worker, ! 360: .set_compression_level = interface_set_compression_level, ! 361: .set_mm_time = interface_set_mm_time, ! 362: .get_init_info = interface_get_init_info, ! 363: ! 364: /* the callbacks below are called from spice server thread context */ ! 365: .get_command = interface_get_command, ! 366: .req_cmd_notification = interface_req_cmd_notification, ! 367: .release_resource = interface_release_resource, ! 368: .get_cursor_command = interface_get_cursor_command, ! 369: .req_cursor_notification = interface_req_cursor_notification, ! 370: .notify_update = interface_notify_update, ! 371: .flush_resources = interface_flush_resources, ! 372: }; ! 373: ! 374: static SimpleSpiceDisplay sdpy; ! 375: ! 376: static void display_update(struct DisplayState *ds, int x, int y, int w, int h) ! 377: { ! 378: qemu_spice_display_update(&sdpy, x, y, w, h); ! 379: } ! 380: ! 381: static void display_resize(struct DisplayState *ds) ! 382: { ! 383: qemu_spice_display_resize(&sdpy); ! 384: } ! 385: ! 386: static void display_refresh(struct DisplayState *ds) ! 387: { ! 388: qemu_spice_display_refresh(&sdpy); ! 389: } ! 390: ! 391: static DisplayChangeListener display_listener = { ! 392: .dpy_update = display_update, ! 393: .dpy_resize = display_resize, ! 394: .dpy_refresh = display_refresh, ! 395: }; ! 396: ! 397: void qemu_spice_display_init(DisplayState *ds) ! 398: { ! 399: assert(sdpy.ds == NULL); ! 400: sdpy.ds = ds; ! 401: sdpy.bufsize = (16 * 1024 * 1024); ! 402: sdpy.buf = qemu_malloc(sdpy.bufsize); ! 403: register_displaychangelistener(ds, &display_listener); ! 404: ! 405: sdpy.qxl.base.sif = &dpy_interface.base; ! 406: qemu_spice_add_interface(&sdpy.qxl.base); ! 407: assert(sdpy.worker); ! 408: ! 409: qemu_add_vm_change_state_handler(qemu_spice_vm_change_state_handler, &sdpy); ! 410: qemu_spice_create_host_memslot(&sdpy); ! 411: qemu_spice_create_host_primary(&sdpy); ! 412: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.