From 257608a9a170c6b378a7a83180fe9b40a27584bb Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 11 Mar 2010 11:13:30 -0300 Subject: [PATCH 5/9] spice: simple display RH-Author: Gerd Hoffmann Message-id: <1268306015-11724-6-git-send-email-kraxel@redhat.com> Patchwork-id: 7664 O-Subject: [RHEL-6 kvm PATCH v6 05/10] spice: simple display Bugzilla: 549757 RH-Acked-by: Juan Quintela RH-Acked-by: Izik Eidus RH-Acked-by: Yonit Halperin With that patch applied you'll actually see the guests screen in the spice client. This does *not* bring qxl and full spice support though. This is basically the qxl vga mode made more generic, so it plays together with any qemu-emulated gfx card. You can display stdvga or cirrus via spice client. You can have both vnc and spice enabled and clients connected at the same time. [ v5: force dirty->left = 0 when creating update commands. ] [ v5: don't use vnc/sdl by default when spice is active. ] Signed-off-by: Gerd Hoffmann --- Makefile.target | 2 +- qemu-spice.h | 1 + spice-cmd.h | 339 ++++++++++++++++++++++++++++++++++++++++++++++++ spice-display.c | 346 +++++++++++++++++++++++++++++++++++++++++++++++++ spice-display.h | 34 +++++ spice-draw.h | 389 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ spice-ring.h | 104 +++++++++++++++ vl.c | 7 +- 8 files changed, 1220 insertions(+), 2 deletions(-) create mode 100644 spice-cmd.h create mode 100644 spice-display.c create mode 100644 spice-display.h create mode 100644 spice-draw.h create mode 100644 spice-ring.h Signed-off-by: Eduardo Habkost --- Makefile.target | 2 +- qemu-spice.h | 1 + spice-cmd.h | 339 ++++++++++++++++++++++++++++++++++++++++++++++++ spice-display.c | 346 +++++++++++++++++++++++++++++++++++++++++++++++++ spice-display.h | 34 +++++ spice-draw.h | 389 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ spice-ring.h | 104 +++++++++++++++ vl.c | 7 +- 8 files changed, 1220 insertions(+), 2 deletions(-) create mode 100644 spice-cmd.h create mode 100644 spice-display.c create mode 100644 spice-display.h create mode 100644 spice-draw.h create mode 100644 spice-ring.h diff --git a/Makefile.target b/Makefile.target index 117b847..925d329 100644 --- a/Makefile.target +++ b/Makefile.target @@ -217,7 +217,7 @@ obj-i386-y += testdev.o obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o obj-i386-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += device-assignment.o -obj-i386-$(CONFIG_SPICE) += spice.o spice-input.o +obj-i386-$(CONFIG_SPICE) += spice.o spice-input.o spice-display.o # Hardware support obj-ia64-y += ide.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) diff --git a/qemu-spice.h b/qemu-spice.h index 8376ce5..3a77157 100644 --- a/qemu-spice.h +++ b/qemu-spice.h @@ -13,6 +13,7 @@ extern int using_spice; void qemu_spice_init(void); void qemu_spice_input_init(SpiceServer *s); +void qemu_spice_display_init(DisplayState *ds); void qemu_spice_add_interface(struct VDInterface *interface); void qemu_spice_remove_interface(struct VDInterface *interface); diff --git a/spice-cmd.h b/spice-cmd.h new file mode 100644 index 0000000..be384ff --- /dev/null +++ b/spice-cmd.h @@ -0,0 +1,339 @@ +#ifndef SPICE_CMD_H +#define SPICE_CMD_H + +#ifdef __GNUC__ +#ifdef __i386__ +#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory") +#else +//mfence +#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%rsp)": : :"memory") +#endif +#define ATTR_PACKED __attribute__ ((__packed__)) +#else +#pragma pack(push) +#pragma pack(1) +#define ATTR_PACKED +#define mb() __asm {lock add [esp], 0} +#endif + +#define REDHAT_PCI_VENDOR_ID 0x1b36 +#define QXL_DEVICE_ID 0x0100 /* 0x100-0x11f reserved for spice */ +#define QXL_REVISION 0x01 + +#define QXL_ROM_MAGIC (*(UINT32*)"QXRO") +#define QXL_RAM_MAGIC (*(UINT32*)"QXRA") + +enum { + QXL_RAM_RANGE_INDEX, + QXL_VRAM_RANGE_INDEX, + QXL_ROM_RANGE_INDEX, + QXL_IO_RANGE_INDEX, + + QXL_PCI_RANGES +}; + +enum { + QXL_IO_NOTIFY_CMD, + QXL_IO_NOTIFY_CURSOR, + QXL_IO_UPDATE_AREA, + QXL_IO_UPDATE_IRQ, + QXL_IO_NOTIFY_OOM, + QXL_IO_RESET, + QXL_IO_SET_MODE, + QXL_IO_LOG, + + QXL_IO_RANGE_SIZE +}; + +typedef struct ATTR_PACKED QXLRom { + UINT32 magic; + UINT32 id; + UINT32 update_id; + UINT32 compression_level; + UINT32 log_level; + UINT32 mode; + UINT32 modes_offset; + UINT32 num_io_pages; + UINT32 pages_offset; + UINT32 draw_area_offset; + UINT32 draw_area_size; + UINT32 ram_header_offset; + UINT32 mm_clock; +} QXLRom; + +typedef struct ATTR_PACKED QXLMode { + UINT32 id; + UINT32 x_res; + UINT32 y_res; + UINT32 bits; + UINT32 stride; + UINT32 x_mili; + UINT32 y_mili; + UINT32 orientation; +} QXLMode; + +typedef struct ATTR_PACKED QXLModes { + UINT32 n_modes; + QXLMode modes[0]; +} QXLModes; + +typedef UINT64 PHYSICAL; +typedef UINT32 QXLFIXED; //fixed 28.4 + +enum QXLCmdType { + QXL_CMD_NOP, + QXL_CMD_DRAW, + QXL_CMD_UPDATE, + QXL_CMD_CURSOR, + QXL_CMD_MESSAGE, +}; + +typedef struct ATTR_PACKED QXLCommand { + PHYSICAL data; + UINT32 type; + UINT32 ped; +} QXLCommand; + + +RING_DECLARE(QXLCommandRing, QXLCommand, 32); +RING_DECLARE(QXLCursorRing, QXLCommand, 32); + +RING_DECLARE(QXLReleaseRing, UINT64, 8); + +#define QXL_LOG_BUF_SIZE 4096 + +#define QXL_INTERRUPT_DISPLAY (1 << 0) +#define QXL_INTERRUPT_CURSOR (1 << 1) + +typedef struct ATTR_PACKED QXLRam { + UINT32 magic; + UINT32 int_pending; + UINT32 int_mask; + UINT8 log_buf[QXL_LOG_BUF_SIZE]; + QXLCommandRing cmd_ring; + QXLCursorRing cursor_ring; + QXLReleaseRing release_ring; + Rect update_area; +} QXLRam; + +typedef union QXLReleaseInfo{ + UINT64 id; // in + UINT64 next; // out +} QXLReleaseInfo; + +typedef struct ATTR_PACKED QXLDataChunk { + UINT32 data_size; + PHYSICAL prev_chunk; + PHYSICAL next_chunk; + UINT8 data[0]; +} QXLDataChunk; + +typedef struct ATTR_PACKED QXLMessage { + QXLReleaseInfo release_info; + UINT8 data[0]; +} QXLMessage; + +typedef struct ATTR_PACKED QXLUpdateCmd { + QXLReleaseInfo release_info; + Rect area; + UINT32 update_id; +} QXLUpdateCmd; + +typedef struct ATTR_PACKED QXLCursor { + CursorHeader header; + UINT32 data_size; + QXLDataChunk chunk; +} QXLCursor; + +enum { + QXL_CURSOR_SET, + QXL_CURSOR_MOVE, + QXL_CURSOR_HIDE, + QXL_CURSOR_TRAIL, +}; + +#define QXL_CURSUR_DEVICE_DATA_SIZE 128 + +typedef struct ATTR_PACKED QXLCursorCmd { + QXLReleaseInfo release_info; + UINT8 type; + union { + struct ATTR_PACKED { + Point16 position; + UINT8 visible; + PHYSICAL shape; + } set; + struct ATTR_PACKED { + UINT16 length; + UINT16 frequency; + } trail; + Point16 position; + } u; + UINT8 device_data[QXL_CURSUR_DEVICE_DATA_SIZE]; //todo: use host memory +} QXLCursorCmd; + +enum { + QXL_DRAW_NOP, + QXL_DRAW_FILL, + QXL_DRAW_OPAQUE, + QXL_DRAW_COPY, + QXL_COPY_BITS, + QXL_DRAW_BLEND, + QXL_DRAW_BLACKNESS, + QXL_DRAW_WHITENESS, + QXL_DRAW_INVERS, + QXL_DRAW_ROP3, + QXL_DRAW_STROKE, + QXL_DRAW_TEXT, + QXL_DRAW_TRANSPARENT, + QXL_DRAW_ALPHA_BLEND, +}; + +typedef struct ATTR_PACKED QXLString{ + UINT32 data_size; + UINT16 length; + UINT16 flags; + QXLDataChunk chunk; +} QXLString; + +typedef struct ATTR_PACKED QXLCopyBits { + Point src_pos; +} QXLCopyBits; + +#define QXL_EFFECT_BLEND 0 +#define QXL_EFFECT_OPAQUE 1 +#define QXL_EFFECT_REVERT_ON_DUP 2 +#define QXL_EFFECT_BLACKNESS_ON_DUP 3 +#define QXL_EFFECT_WHITENESS_ON_DUP 4 +#define QXL_EFFECT_NOP_ON_DUP 5 +#define QXL_EFFECT_NOP 6 +#define QXL_EFFECT_OPAQUE_BRUSH 7 + +typedef struct ATTR_PACKED QXLDrawable { + QXLReleaseInfo release_info; + UINT8 effect; + UINT8 type; + UINT16 bitmap_offset; + Rect bitmap_area; + Rect bbox; + Clip clip; + UINT32 mm_time; + union { + Fill fill; + Opaque opaque; + Copy copy; + Transparent transparent; + AlphaBlnd alpha_blend; + QXLCopyBits copy_bits; + Blend blend; + Rop3 rop3; + Stroke stroke; + Text text; + Blackness blackness; + Invers invers; + Whiteness whiteness; + } u; +} QXLDrawable; + +typedef struct ATTR_PACKED QXLClipRects{ + UINT32 num_rects; + QXLDataChunk chunk; +} QXLClipRects; + +enum { + QXL_PATH_BEGIN = (1 << 0), + QXL_PATH_END = (1 << 1), + QXL_PATH_CLOSE = (1 << 3), + QXL_PATH_BEZIER = (1 << 4), +}; + +typedef struct ATTR_PACKED QXLPath { + UINT32 data_size; + QXLDataChunk chunk; +} QXLPath; + +enum { + QXL_IMAGE_GROUP_DRIVER, + QXL_IMAGE_GROUP_DEVICE, + QXL_IMAGE_GROUP_RED, + QXL_IMAGE_GROUP_DRIVER_DONT_CACHE, +}; + +typedef struct ATTR_PACKED QXLImageID{ + UINT32 group; + UINT32 unique; +} QXLImageID; + +enum { + QXL_IMAGE_CACHE = (1 << 0), +}; + +enum { + QXL_BITMAP_DIRECT = (1 << 0), + QXL_BITMAP_UNSTABLE = (1 << 1), + QXL_BITMAP_TOP_DOWN = (1 << 2), // == BITMAP_TOP_DOWN +}; + +#define QXL_SET_IMAGE_ID(image, _group, _unique) { \ + UINT64* id_ptr = &(image)->descriptor.id; \ + QXLImageID *image_id = (QXLImageID *)id_ptr; \ + image_id->group = _group; \ + image_id->unique = _unique; \ +} + +typedef struct ATTR_PACKED QXLImage { + ImageDescriptor descriptor; + union { // variable length + Bitmap bitmap; + PNGData png; + QUICData quic; + }; +} QXLImage; + + +#define VDI_PORT_DEVICE_ID 0x0105 +#define VDI_PORT_REVISION 0x01 + +#define VDI_PORT_INTERRUPT (1 << 0) + +#define VDI_PORT_MAGIC (*(UINT32*)"VDIP") + +typedef struct ATTR_PACKED VDIPortPacket { + UINT32 gen; + UINT32 size; + UINT8 data[512 - 2 * sizeof(UINT32)]; +} VDIPortPacket; + +RING_DECLARE(VDIPortRing, VDIPortPacket, 32); + +enum { + VDI_PORT_IO_RANGE_INDEX, + VDI_PORT_RAM_RANGE_INDEX, +}; + +enum { + VDI_PORT_IO_CONNECTION, + VDI_PORT_IO_NOTIFY = 4, + VDI_PORT_IO_UPDATE_IRQ = 8, + + VDI_PORT_IO_RANGE_SIZE = 12 +}; + +typedef struct ATTR_PACKED VDIPortRam { + UINT32 magic; + UINT32 generation; + UINT32 int_pending; + UINT32 int_mask; + VDIPortRing input; + VDIPortRing output; + UINT32 reserv[32]; +} VDIPortRam; + + +#ifndef __GNUC__ +#pragma pack(pop) +#endif + +#undef ATTR_PACKED + +#endif diff --git a/spice-display.c b/spice-display.c new file mode 100644 index 0000000..54c7663 --- /dev/null +++ b/spice-display.c @@ -0,0 +1,346 @@ +#include +#include +#include +#include +#include + +#include "qemu-common.h" +#include "qemu-spice.h" +#include "qemu-timer.h" +#include "qemu-queue.h" +#include "monitor.h" +#include "console.h" +#include "sysemu.h" + +#include "spice-display.h" + +#define REDHAT_PCI_VENDOR_ID 0x1b36 +#define QXL_DEVICE_ID 0x0100 /* 0x100-0x11f reserved for spice */ +#define QXL_REVISION 0x01 + +static struct SpiceDisplay { + DisplayState *ds; + void *buf; + int bufsize; + QXLWorker *worker; + Rect dirty; + int unique; + int is_attached:1; + pthread_mutex_t lock; +} sdpy; + +QXLUpdate *qemu_spice_display_create_update(DisplayState *ds, Rect *dirty, int unique) +{ + QXLUpdate *update; + QXLDrawable *drawable; + QXLImage *image; + QXLCommand *cmd; + + dirty->left = 0; +#if 0 + fprintf(stderr, "%s: lr %d -> %d, tb -> %d -> %d\n", __FUNCTION__, + dirty->left, dirty->right, + dirty->top, dirty->bottom); +#endif + + update = qemu_mallocz(sizeof(*update)); + drawable = &update->drawable; + image = &update->image; + cmd = &update->cmd; + + drawable->bbox = *dirty; + drawable->clip.type = CLIP_TYPE_NONE; + drawable->clip.data = 0; + drawable->effect = QXL_EFFECT_OPAQUE; + drawable->release_info.id = (UINT64)update; + drawable->bitmap_offset = 0; + drawable->type = QXL_DRAW_COPY; + + drawable->u.copy.rop_decriptor = ROPD_OP_PUT; + drawable->u.copy.src_bitmap = (PHYSICAL)image; + drawable->u.copy.src_area.left = drawable->u.copy.src_area.top = 0; + drawable->u.copy.src_area.right = dirty->right - dirty->left; + drawable->u.copy.src_area.bottom = dirty->bottom - dirty->top; + drawable->u.copy.scale_mode = 0; + memset(&drawable->u.copy.mask, 0, sizeof(QMask)); + + image->descriptor.type = IMAGE_TYPE_BITMAP; + image->descriptor.flags = 0; + QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, unique); + image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN | QXL_BITMAP_UNSTABLE; + image->bitmap.stride = ds_get_linesize(ds); + image->descriptor.width = image->bitmap.x = drawable->u.copy.src_area.right; + image->descriptor.height = image->bitmap.y = drawable->u.copy.src_area.bottom; + image->bitmap.data = (PHYSICAL)(ds_get_data(ds) + + dirty->top * image->bitmap.stride + + dirty->left * ds_get_bytes_per_pixel(ds)); + image->bitmap.palette = 0; + switch (ds_get_bits_per_pixel(ds)) { + case 16: + image->bitmap.format = BITMAP_FMT_16BIT; + break; + case 32: + image->bitmap.format = BITMAP_FMT_32BIT; + break; + default: + fprintf(stderr, "%s: unhandled depth: %d bits\n", __FUNCTION__, + ds_get_bits_per_pixel(ds)); + abort(); + } + + cmd->type = QXL_CMD_DRAW; + cmd->data = (PHYSICAL)drawable; + return update; +} + +static void spice_vm_change_state_handler(void *opaque, int running, int reason) +{ + if (!sdpy.worker) { + return; + } + + if (running) { + sdpy.worker->start(sdpy.worker); + } else { + sdpy.worker->stop(sdpy.worker); + } +} + +/* display listener callbacks */ + +static void spice_display_update(struct DisplayState *ds, int x, int y, int w, int h) +{ + Rect update_area; + + update_area.left = x, + update_area.right = x + w; + update_area.top = y; + update_area.bottom = y + h; + pthread_mutex_lock(&sdpy.lock); + rect_union(&sdpy.dirty, &update_area); + pthread_mutex_unlock(&sdpy.lock); +} + +static void spice_display_resize(struct DisplayState *ds) +{ + if (sdpy.is_attached) { + sdpy.is_attached = 0; + sdpy.worker->detach(sdpy.worker); + } + + pthread_mutex_lock(&sdpy.lock); + sdpy.dirty.left = 0; + sdpy.dirty.right = ds_get_width(ds); + sdpy.dirty.top = 0; + sdpy.dirty.bottom = ds_get_height(ds); + pthread_mutex_unlock(&sdpy.lock); + + if (!sdpy.is_attached && sdpy.worker) { + sdpy.is_attached = 1; + sdpy.worker->attach(sdpy.worker); + } +} + +static void spice_display_refresh(struct DisplayState *ds) +{ + vga_hw_update(); + if (rect_is_empty(&sdpy.dirty)) + return; + if (sdpy.is_attached) { + sdpy.worker->wakeup(sdpy.worker); + } +} + +/* spice display interface callbacks */ + +static void interface_attach_worker(QXLInterface *qxl, QXLWorker *qxl_worker) +{ + sdpy.worker = qxl_worker; +} + +static void interface_set_compression_level(QXLInterface *qxl, int level) +{ + /* nothing to do */ +} + +static void interface_set_mm_time(QXLInterface *qxl, uint32_t mm_time) +{ + /* nothing to do */ +} + +static VDObjectRef interface_register_mode_change(QXLInterface *qxl, + qxl_mode_change_notifier_t notifier, + void *opaque) +{ + fprintf(stderr, "%s:\n", __FUNCTION__); + abort(); + return 0; +} + +static void interface_unregister_mode_change(QXLInterface *qxl, VDObjectRef notifier) +{ + fprintf(stderr, "%s:\n", __FUNCTION__); + abort(); +} + +static void interface_get_info(QXLInterface *qxl, QXLDevInfo *info) +{ + int stride = ds_get_width(sdpy.ds) * 4; + int size; + +#if 0 + fprintf(stderr, "%s: %dx%d @ %d\n", __FUNCTION__, + ds_get_width(sdpy.ds), ds_get_height(sdpy.ds), + ds_get_bits_per_pixel(sdpy.ds)); +#endif + + info->x_res = ds_get_width(sdpy.ds); + info->y_res = ds_get_height(sdpy.ds); + info->bits = 32; + + size = stride * info->y_res; + if (sdpy.bufsize < size) { + sdpy.bufsize = size; + sdpy.buf = qemu_realloc(sdpy.buf, sdpy.bufsize); + } + + info->ram_size = sdpy.bufsize; + info->use_hardware_cursor = false; + info->phys_start = 0; + info->phys_end = ~info->phys_start; + info->phys_delta = 0; + + info->draw_area.buf = sdpy.buf; + info->draw_area.size = sdpy.bufsize; + info->draw_area.line_0 = info->draw_area.buf; + info->draw_area.stride = stride; + info->draw_area.width = info->x_res; + info->draw_area.heigth = info->y_res; +} + +static int interface_get_command(QXLInterface *qxl, struct QXLCommand *cmd) +{ + QXLUpdate *update; + + if (rect_is_empty(&sdpy.dirty)) + return false; + pthread_mutex_lock(&sdpy.lock); + update = qemu_spice_display_create_update(sdpy.ds, &sdpy.dirty, ++sdpy.unique); + memset(&sdpy.dirty, 0, sizeof(sdpy.dirty)); + pthread_mutex_unlock(&sdpy.lock); + *cmd = update->cmd; + return true; +} + +static int interface_req_cmd_notification(QXLInterface *qxl) +{ + /* nothing to do */ + return 1; +} + +static int interface_has_command(QXLInterface *qxl) +{ + fprintf(stderr, "%s:\n", __FUNCTION__); + abort(); + return 0; +} + +static void interface_release_resource(QXLInterface *qxl, union QXLReleaseInfo *release_info) +{ + UINT64 id = release_info->id; + qemu_free((void *)id); +} + +static int interface_get_cursor_command(QXLInterface *qxl, struct QXLCommand *cmd) +{ + /* nothing to do */ + return 0; +} + +static int interface_req_cursor_notification(QXLInterface *qxl) +{ + /* nothing to do */ + return 1; +} + +static const struct Rect *interface_get_update_area(QXLInterface *qxl) +{ + fprintf(stderr, "%s:\n", __FUNCTION__); + abort(); + return NULL; +} + +static void interface_notify_update(QXLInterface *qxl, uint32_t update_id) +{ + fprintf(stderr, "%s:\n", __FUNCTION__); + abort(); +} + +static void interface_set_save_data(QXLInterface *qxl, void *data, int size) +{ + fprintf(stderr, "%s:\n", __FUNCTION__); + abort(); +} + +static void *interface_get_save_data(QXLInterface *qxl) +{ + fprintf(stderr, "%s:\n", __FUNCTION__); + abort(); + return NULL; +} + +static int interface_flush_resources(QXLInterface *qxl) +{ + fprintf(stderr, "%s:\n", __FUNCTION__); + abort(); + return 0; +} + +static QXLInterface dpy_interface = { + .base.base_version = VM_INTERFACE_VERSION, + .base.type = VD_INTERFACE_QXL, + .base.description = "display", + .base.major_version = VD_INTERFACE_QXL_MAJOR, + .base.minor_version = VD_INTERFACE_QXL_MINOR, + + .pci_vendor = REDHAT_PCI_VENDOR_ID, + .pci_id = QXL_DEVICE_ID, + .pci_revision = QXL_REVISION, + + .attache_worker = interface_attach_worker, + .set_compression_level = interface_set_compression_level, + .set_mm_time = interface_set_mm_time, + .register_mode_change = interface_register_mode_change, + .unregister_mode_change = interface_unregister_mode_change, + + .get_info = interface_get_info, + .get_command = interface_get_command, + .req_cmd_notification = interface_req_cmd_notification, + .has_command = interface_has_command, + .release_resource = interface_release_resource, + .get_cursor_command = interface_get_cursor_command, + .req_cursor_notification = interface_req_cursor_notification, + .get_update_area = interface_get_update_area, + .notify_update = interface_notify_update, + .set_save_data = interface_set_save_data, + .get_save_data = interface_get_save_data, + .flush_resources = interface_flush_resources, +}; + +void qemu_spice_display_init(DisplayState *ds) +{ + DisplayChangeListener *display_listener; + + assert(sdpy.ds == NULL); + sdpy.ds = ds; + pthread_mutex_init(&sdpy.lock, NULL); + + display_listener = qemu_mallocz(sizeof(DisplayChangeListener)); + display_listener->dpy_update = spice_display_update; + display_listener->dpy_resize = spice_display_resize; + display_listener->dpy_refresh = spice_display_refresh; + register_displaychangelistener(ds, display_listener); + + qemu_spice_add_interface(&dpy_interface.base); + qemu_add_vm_change_state_handler(spice_vm_change_state_handler, NULL); +} diff --git a/spice-display.h b/spice-display.h new file mode 100644 index 0000000..17de7b3 --- /dev/null +++ b/spice-display.h @@ -0,0 +1,34 @@ +#include "spice-ring.h" +#include "spice-draw.h" +#include "spice-cmd.h" + +typedef struct QXLUpdate { + QXLDrawable drawable; + QXLImage image; + QXLCommand cmd; +} QXLUpdate; + +QXLUpdate *qemu_spice_display_create_update(DisplayState *ds, Rect *dirty, int unique); + +static inline int rect_is_empty(const Rect* r) +{ + return r->top == r->bottom || r->left == r->right; +} + +static inline void rect_union(Rect *dest, const Rect *r) +{ + if (rect_is_empty(r)) { + return; + } + + if (rect_is_empty(dest)) { + *dest = *r; + return; + } + + dest->top = MIN(dest->top, r->top); + dest->left = MIN(dest->left, r->left); + dest->bottom = MAX(dest->bottom, r->bottom); + dest->right = MAX(dest->right, r->right); +} + diff --git a/spice-draw.h b/spice-draw.h new file mode 100644 index 0000000..d2587d5 --- /dev/null +++ b/spice-draw.h @@ -0,0 +1,389 @@ +#ifndef SPICE_DRAW_H +#define SPICE_DRAW_H + +#ifndef _WIN32 +#include +#endif + +#ifdef __GNUC__ +#define ATTR_PACKED __attribute__ ((__packed__)) +typedef uint64_t UINT64; +typedef uint32_t UINT32; +typedef uint16_t UINT16; +typedef uint8_t UINT8; + +typedef int16_t INT16; +typedef int32_t INT32; +#else +#include +#pragma pack(push) +#pragma pack(1) +#define ATTR_PACKED +#pragma warning(disable:4200) +#endif + +#ifdef _WIN32_WCE +#include +typedef uint64_t UINT64; +typedef uint32_t UINT32; +typedef uint16_t UINT16; +typedef uint8_t UINT8; + +typedef int16_t INT16; +typedef int32_t INT32; +#endif + +#define GET_ADDRESS(addr) ((void *)(unsigned long)(addr)) +#define SET_ADDRESS(addr, val) ((addr) = (unsigned long)(val)) + +typedef INT32 FIXED28_4; +typedef UINT64 ADDRESS; + +enum { + PATH_BEGIN = (1 << 0), + PATH_END = (1 << 1), + PATH_CLOSE = (1 << 3), + PATH_BEZIER = (1 << 4), +}; + +enum { + LINE_ATTR_STARTGAP = (1 << 2), + LINE_ATTR_STYLED = (1 << 3), +}; + +typedef struct ATTR_PACKED PointFix { + FIXED28_4 x; + FIXED28_4 y; +} PointFix; + +typedef struct ATTR_PACKED Point { + INT32 x; + INT32 y; +} Point; + +typedef struct ATTR_PACKED Point16 { + INT16 x; + INT16 y; +} Point16; + +typedef struct ATTR_PACKED Rect { + INT32 top; + INT32 left; + INT32 bottom; + INT32 right; +} Rect; + +typedef struct ATTR_PACKED PathSeg { + UINT32 flags; + UINT32 count; + UINT8 data[0]; +} PathSeg; + +enum ClipType { + CLIP_TYPE_NONE, + CLIP_TYPE_RECTS, + CLIP_TYPE_PATH, +}; + +typedef struct ATTR_PACKED Clip { + UINT32 type; + ADDRESS data; +} Clip; + +enum ROPDescriptor { + ROPD_INVERS_SRC = (1 << 0), + ROPD_INVERS_BRUSH = (1 << 1), + ROPD_INVERS_DEST = (1 << 2), + ROPD_OP_PUT = (1 << 3), + ROPD_OP_OR = (1 << 4), + ROPD_OP_AND = (1 << 5), + ROPD_OP_XOR = (1 << 6), + ROPD_OP_BLACKNESS = (1 << 7), + ROPD_OP_WHITENESS = (1 << 8), + ROPD_OP_INVERS = (1 << 9), + ROPD_INVERS_RES = (1 <<10), +}; + +typedef struct ATTR_PACKED Pattern { + ADDRESS pat; + Point pos; +} Pattern; + +enum { + BRUSH_TYPE_NONE, + BRUSH_TYPE_SOLID, + BRUSH_TYPE_PATTERN, +}; + +typedef struct ATTR_PACKED Brush { + UINT32 type; + union { + UINT32 color; + Pattern pattern; + } u; +} Brush; + +enum { + MASK_INVERS = (1 << 0), +}; + +typedef struct ATTR_PACKED QMask { + UINT8 flags; + Point pos; + ADDRESS bitmap; +} QMask; + +typedef struct ATTR_PACKED Fill { + Brush brush; + UINT16 rop_decriptor; + QMask mask; +} Fill; + +typedef struct ATTR_PACKED Palette { + UINT64 unique; + UINT16 num_ents; + UINT32 ents[0]; +} Palette; + +enum { + IMAGE_TYPE_BITMAP, + IMAGE_TYPE_QUIC, + IMAGE_TYPE_LZ_PLT, + IMAGE_TYPE_LZ_RGB, + IMAGE_TYPE_PNG, + IMAGE_TYPE_FROM_CACHE, +}; + +enum { + IMAGE_CACHE_ME = (1 << 0), +}; + +typedef struct ATTR_PACKED ImageDescriptor { + UINT64 id; + UINT8 type; + UINT8 flags; + UINT32 width; + UINT32 height; +} ImageDescriptor; + +enum { + BITMAP_FMT_INVALID, + BITMAP_FMT_1BIT_LE, + BITMAP_FMT_1BIT_BE, + BITMAP_FMT_4BIT_LE, + BITMAP_FMT_4BIT_BE, + BITMAP_FMT_8BIT, + BITMAP_FMT_16BIT, + BITMAP_FMT_24BIT, + BITMAP_FMT_32BIT, + BITMAP_FMT_RGBA, +}; + +enum { + BITMAP_PAL_CACHE_ME = (1 << 0), + BITMAP_PAL_FROM_CACHE = (1 << 1), + BITMAP_TOP_DOWN = (1 << 2), +}; + +typedef struct ATTR_PACKED Bitmap { + UINT8 format; + UINT8 flags; + UINT32 x; + UINT32 y; + UINT32 stride; + ADDRESS palette; + ADDRESS data; //data[0] ? +} Bitmap; + +typedef struct ATTR_PACKED BitmapImage { + ImageDescriptor descriptor; + Bitmap bitmap; +} BitmapImage; + +typedef struct ATTR_PACKED PNGData { + UINT32 data_size; + UINT8 data[0]; +} PNGData, QUICData, LZ_RGBData; + +typedef struct ATTR_PACKED PNGImage { + ImageDescriptor descriptor; + PNGData png; +} PNGImage; + +typedef struct ATTR_PACKED QUICImage { + ImageDescriptor descriptor; + QUICData quic; +} QUICImage; + +typedef struct ATTR_PACKED LZ_RGBImage { + ImageDescriptor descriptor; + LZ_RGBData lz_rgb; +} LZ_RGBImage; + +typedef struct ATTR_PACKED LZ_PLTData { + UINT8 flags; + UINT32 data_size; + ADDRESS palette; + UINT8 data[0]; +} LZ_PLTData; + +typedef struct ATTR_PACKED LZ_PLTImage { + ImageDescriptor descriptor; + LZ_PLTData lz_plt; +} LZ_PLTImage; + +typedef struct ATTR_PACKED LZImage { + ImageDescriptor descriptor; + union { + LZ_RGBData lz_rgb; + LZ_PLTData lz_plt; + }; +} LZImage; + +enum { + IMAGE_SCALE_INTERPOLATE, + IMAGE_SCALE_NEAREST, +}; + +typedef struct ATTR_PACKED Opaque { + ADDRESS src_bitmap; + Rect src_area; + Brush brush; + UINT16 rop_decriptor; + UINT8 scale_mode; + QMask mask; +} Opaque; + +typedef struct ATTR_PACKED Copy { + ADDRESS src_bitmap; + Rect src_area; + UINT16 rop_decriptor; + UINT8 scale_mode; + QMask mask; +} Copy, Blend; + +typedef struct ATTR_PACKED Transparent { + ADDRESS src_bitmap; + Rect src_area; + UINT32 src_color; + UINT32 true_color; +} Transparent; + +typedef struct ATTR_PACKED AlphaBlnd { + UINT8 alpha; + ADDRESS src_bitmap; + Rect src_area; +} AlphaBlnd; + +typedef struct ATTR_PACKED Rop3 { + ADDRESS src_bitmap; + Rect src_area; + Brush brush; + UINT8 rop3; + UINT8 scale_mode; + QMask mask; +} Rop3; + +typedef struct ATTR_PACKED Blackness { + QMask mask; +} Blackness, Invers, Whiteness; + +enum { + LINE_SCALABLE = (1 << 0), + LINE_STYLED = (1 << 3), + LINE_START_WITH_GAP = (1 << 2), +}; + +enum { + LINE_CAP_ROUND, + LINE_CAP_SQUARE, + LINE_CAP_BUTT, +}; + +enum { + LINE_JOIN_ROUND, + LINE_JOIN_BEVEL, + LINE_JOIN_MITER, +}; + +typedef struct ATTR_PACKED LineAttr { + UINT8 flags; + UINT8 join_style; + UINT8 end_style; + UINT8 style_nseg; + FIXED28_4 width; + FIXED28_4 miter_limit; + ADDRESS style; //data[0] ? +} LineAttr; + +typedef struct ATTR_PACKED Stroke { + ADDRESS path; + LineAttr attr; + Brush brush; + UINT16 fore_mode; + UINT16 back_mode; +} Stroke; + +typedef struct ATTR_PACKED RasterGlyph { + Point render_pos; + Point glyph_origin; + UINT16 width; + UINT16 height; + UINT8 data[0]; +} RasterGlyph; + +typedef struct ATTR_PACKED VectotGlyph { + Point render_pos; + UINT32 data_size; + UINT8 data[0]; //PathSeg[] +} VectotGlyph; + +enum { + STRING_RASTER_A1 = 1 << 0, + STRING_RASTER_A4 = 1 << 1, + STRING_RASTER_A8 = 1 << 2, + STRING_RASTER_TOP_DOWN = 1 << 3, +}; + +typedef struct ATTR_PACKED String { + UINT16 length; + UINT16 flags; + UINT8 data[0]; +} String; + +typedef struct ATTR_PACKED Text { + ADDRESS str; + Rect back_area; + Brush fore_brush; + Brush back_brush; + UINT16 fore_mode; + UINT16 back_mode; +} Text; + +enum { + CURSOR_TYPE_ALPHA, + CURSOR_TYPE_MONO, + CURSOR_TYPE_COLOR4, + CURSOR_TYPE_COLOR8, + CURSOR_TYPE_COLOR16, + CURSOR_TYPE_COLOR24, + CURSOR_TYPE_COLOR32, +}; + +typedef struct ATTR_PACKED CursorHeader { + UINT64 unique; + UINT16 type; + UINT16 width; + UINT16 height; + UINT16 hot_spot_x; + UINT16 hot_spot_y; +} CursorHeader; + + +#ifndef __GNUC__ +#pragma pack(pop) +#endif + +#undef ATTR_PACKED + +#endif diff --git a/spice-ring.h b/spice-ring.h new file mode 100644 index 0000000..0d93a08 --- /dev/null +++ b/spice-ring.h @@ -0,0 +1,104 @@ +#ifndef SPICE_RING_H +#define SPICE_RING_H + + +#define MSB_MASK4(x) \ + (((x) & 0x8) ? 0x8 : \ + ((x) & 0x4) ? 0x4 : \ + ((x) & 0x2) ? 0x2 : \ + ((x) & 0x1) ? 0x1 : 0) + +#define MSB_MASK8(x) \ + (((x) & 0xf0) ? MSB_MASK4((x) >> 4) << 4 : MSB_MASK4(x)) + +#define MSB_MASK16(x) \ + (((x) & 0xff00) ? MSB_MASK8((x) >> 8) << 8 : MSB_MASK8(x)) + +#define MSB_MASK(x) \ + (((x) & 0xffff0000) ? MSB_MASK16((x) >> 16) << 16 : MSB_MASK16(x)) + +#define POWER2_ALIGN(x) MSB_MASK((x) * 2 - 1) + + +#define _TOSHIFT_4(x) \ + (((x) & 0x8) ? 3 : \ + ((x) & 0x4) ? 2 : \ + ((x) & 0x2) ? 1 : 0) + +#define _TOSHIFT_8(x) \ + (((x) & 0xf0) ? _TOSHIFT_4((x) >> 4) + 4 : _TOSHIFT_4(x)) + +#define _TOSHIFT_16(x) \ + (((x) & 0xff00) ? _TOSHIFT_8((x) >> 8) + 8 : _TOSHIFT_8(x)) + +#define PAWER2_TO_SHIFT(x) \ + (((x) & 0xffff0000) ? _TOSHIFT_16((x) >> 16) + 16 : _TOSHIFT_16(x)) + + + +#define RING_DECLARE(name, el_type, size) \ +typedef struct ATTR_PACKED name##_ring_el { \ + union { \ + el_type el; \ + UINT8 data[POWER2_ALIGN(sizeof(el_type))]; \ + } ; \ +} name##_ring_el; \ + \ +typedef struct ATTR_PACKED name { \ + UINT32 num_items; \ + UINT32 prod; \ + UINT32 notify_on_prod; \ + UINT32 cons; \ + UINT32 notify_on_cons; \ + name##_ring_el items[POWER2_ALIGN(size)]; \ +} name; + + +#define RING_INIT(r) \ + (r)->num_items = sizeof((r)->items) >> \ + PAWER2_TO_SHIFT(sizeof((r)->items[0])); \ + (r)->prod = (r)->cons = 0; \ + (r)->notify_on_prod = 1; \ + (r)->notify_on_cons = 0; + + +#define RING_INDEX_MASK(r) ((r)->num_items - 1) + +#define RING_IS_PACKED(r) (sizeof((r)->items[0]) == sizeof((r)->items[0]).el) + +#define RING_IS_EMPTY(r) ((r)->cons == (r)->prod) + +#define RING_IS_FULL(r) (((r)->prod - (r)->cons) == (r)->num_items) + +#define RING_PROD_ITEM(r) (&(r)->items[(r)->prod & RING_INDEX_MASK(r)].el) + +#define RING_PROD_WAIT(r, wait) \ + if (((wait) = RING_IS_FULL(r))) { \ + (r)->notify_on_cons = (r)->cons + 1; \ + mb(); \ + (wait) = RING_IS_FULL(r); \ + } + +#define RING_PUSH(r, notify) \ + (r)->prod++; \ + mb(); \ + (notify) = (r)->prod == (r)->notify_on_prod; + + +#define RING_CONS_ITEM(r) (&(r)->items[(r)->cons & RING_INDEX_MASK(r)].el) + +#define RING_CONS_WAIT(r, wait) \ + if (((wait) = RING_IS_EMPTY(r))) { \ + (r)->notify_on_prod = (r)->prod + 1; \ + mb(); \ + (wait) = RING_IS_EMPTY(r); \ + } + +#define RING_POP(r, notify) \ + (r)->cons++; \ + mb(); \ + (notify) = (r)->cons == (r)->notify_on_cons; + + + +#endif diff --git a/vl.c b/vl.c index 623116d..aa2bcb9 100644 --- a/vl.c +++ b/vl.c @@ -6153,7 +6153,7 @@ int main(int argc, char **argv, char **envp) /* just use the first displaystate for the moment */ ds = display_state; - if (display_type == DT_DEFAULT) { + if (display_type == DT_DEFAULT && !using_spice) { #if defined(CONFIG_SDL) || defined(CONFIG_COCOA) display_type = DT_SDL; #else @@ -6193,6 +6193,11 @@ int main(int argc, char **argv, char **envp) default: break; } +#ifdef CONFIG_SPICE + if (using_spice) { + qemu_spice_display_init(ds); + } +#endif dpy_resize(ds); dcl = ds->listeners; -- 1.6.3.rc4.29.g8146