From 257608a9a170c6b378a7a83180fe9b40a27584bb Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann <kraxel@redhat.com>
Date: Thu, 11 Mar 2010 11:13:30 -0300
Subject: [PATCH 5/9] spice: simple display

RH-Author: Gerd Hoffmann <kraxel@redhat.com>
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 <quintela@redhat.com>
RH-Acked-by: Izik Eidus <ieidus@redhat.com>
RH-Acked-by: Yonit Halperin <yhalperi@redhat.com>

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 <kraxel@redhat.com>
---
 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 <ehabkost@redhat.com>
---
 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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <pthread.h>
+
+#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 <stdint.h>
+#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 <basetsd.h>
+#pragma pack(push)
+#pragma pack(1)
+#define ATTR_PACKED
+#pragma warning(disable:4200)
+#endif
+
+#ifdef _WIN32_WCE
+#include <stdint.h>
+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

