Commit a19cbfb3 authored by Gerd Hoffmann's avatar Gerd Hoffmann

spice: add qxl device

qxl is a paravirtual graphics card.  The qxl device is the bridge
between the guest and the spice server (aka libspice-server).  The
spice server will send the rendering commands to the spice client, which
will actually render them.

The spice server is also able to render locally, which is done in case
the guest wants read something from video memory.  Local rendering is
also used to support display over vnc and sdl.

qxl is activated using "-vga qxl".  qxl supports multihead, additional
cards can be added via '-device qxl".

[ v2: add copyright to files                     ]
[ v2: use qemu-common.h for standard includes    ]
[ v2: create separate qxl-vga device for primary ]
Signed-off-by: default avatarGerd Hoffmann <kraxel@redhat.com>
parent 7943a2fa
......@@ -224,6 +224,7 @@ obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.o
obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
obj-i386-y += debugcon.o multiboot.o
obj-i386-y += pc_piix.o
obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
# shared objects
obj-ppc-y = ppc.o
......
......@@ -528,6 +528,17 @@ extern const VMStateInfo vmstate_info_unused_buffer;
.start = (_start), \
}
#define VMSTATE_VBUFFER_UINT32(_field, _state, _version, _test, _start, _field_size) { \
.name = (stringify(_field)), \
.version_id = (_version), \
.field_exists = (_test), \
.size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\
.info = &vmstate_info_buffer, \
.flags = VMS_VBUFFER|VMS_POINTER, \
.offset = offsetof(_state, _field), \
.start = (_start), \
}
#define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) { \
.name = (stringify(_field)), \
.version_id = (_version), \
......@@ -745,6 +756,9 @@ extern const VMStateDescription vmstate_i2c_slave;
#define VMSTATE_PARTIAL_VBUFFER(_f, _s, _size) \
VMSTATE_VBUFFER(_f, _s, 0, NULL, 0, _size)
#define VMSTATE_PARTIAL_VBUFFER_UINT32(_f, _s, _size) \
VMSTATE_VBUFFER_UINT32(_f, _s, 0, NULL, 0, _size)
#define VMSTATE_SUB_VBUFFER(_f, _s, _start, _size) \
VMSTATE_VBUFFER(_f, _s, 0, NULL, _start, _size)
......
......@@ -40,6 +40,7 @@
#include "sysbus.h"
#include "sysemu.h"
#include "blockdev.h"
#include "ui/qemu-spice.h"
/* output Bochs bios info messages */
//#define DEBUG_BIOS
......@@ -991,6 +992,13 @@ void pc_vga_init(PCIBus *pci_bus)
pci_vmsvga_init(pci_bus);
else
fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
#ifdef CONFIG_SPICE
} else if (qxl_enabled) {
if (pci_bus)
pci_create_simple(pci_bus, -1, "qxl-vga");
else
fprintf(stderr, "%s: qxl: no PCI bus\n", __FUNCTION__);
#endif
} else if (std_vga_enabled) {
if (pci_bus) {
pci_vga_init(pci_bus);
......
/*
* qxl command logging -- for debug purposes
*
* Copyright (C) 2010 Red Hat, Inc.
*
* maintained by Gerd Hoffmann <kraxel@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qxl.h"
static const char *qxl_type[] = {
[ QXL_CMD_NOP ] = "nop",
[ QXL_CMD_DRAW ] = "draw",
[ QXL_CMD_UPDATE ] = "update",
[ QXL_CMD_CURSOR ] = "cursor",
[ QXL_CMD_MESSAGE ] = "message",
[ QXL_CMD_SURFACE ] = "surface",
};
static const char *qxl_draw_type[] = {
[ QXL_DRAW_NOP ] = "nop",
[ QXL_DRAW_FILL ] = "fill",
[ QXL_DRAW_OPAQUE ] = "opaque",
[ QXL_DRAW_COPY ] = "copy",
[ QXL_COPY_BITS ] = "copy-bits",
[ QXL_DRAW_BLEND ] = "blend",
[ QXL_DRAW_BLACKNESS ] = "blackness",
[ QXL_DRAW_WHITENESS ] = "whitemess",
[ QXL_DRAW_INVERS ] = "invers",
[ QXL_DRAW_ROP3 ] = "rop3",
[ QXL_DRAW_STROKE ] = "stroke",
[ QXL_DRAW_TEXT ] = "text",
[ QXL_DRAW_TRANSPARENT ] = "transparent",
[ QXL_DRAW_ALPHA_BLEND ] = "alpha-blend",
};
static const char *qxl_draw_effect[] = {
[ QXL_EFFECT_BLEND ] = "blend",
[ QXL_EFFECT_OPAQUE ] = "opaque",
[ QXL_EFFECT_REVERT_ON_DUP ] = "revert-on-dup",
[ QXL_EFFECT_BLACKNESS_ON_DUP ] = "blackness-on-dup",
[ QXL_EFFECT_WHITENESS_ON_DUP ] = "whiteness-on-dup",
[ QXL_EFFECT_NOP_ON_DUP ] = "nop-on-dup",
[ QXL_EFFECT_NOP ] = "nop",
[ QXL_EFFECT_OPAQUE_BRUSH ] = "opaque-brush",
};
static const char *qxl_surface_cmd[] = {
[ QXL_SURFACE_CMD_CREATE ] = "create",
[ QXL_SURFACE_CMD_DESTROY ] = "destroy",
};
static const char *spice_surface_fmt[] = {
[ SPICE_SURFACE_FMT_INVALID ] = "invalid",
[ SPICE_SURFACE_FMT_1_A ] = "alpha/1",
[ SPICE_SURFACE_FMT_8_A ] = "alpha/8",
[ SPICE_SURFACE_FMT_16_555 ] = "555/16",
[ SPICE_SURFACE_FMT_16_565 ] = "565/16",
[ SPICE_SURFACE_FMT_32_xRGB ] = "xRGB/32",
[ SPICE_SURFACE_FMT_32_ARGB ] = "ARGB/32",
};
static const char *qxl_cursor_cmd[] = {
[ QXL_CURSOR_SET ] = "set",
[ QXL_CURSOR_MOVE ] = "move",
[ QXL_CURSOR_HIDE ] = "hide",
[ QXL_CURSOR_TRAIL ] = "trail",
};
static const char *spice_cursor_type[] = {
[ SPICE_CURSOR_TYPE_ALPHA ] = "alpha",
[ SPICE_CURSOR_TYPE_MONO ] = "mono",
[ SPICE_CURSOR_TYPE_COLOR4 ] = "color4",
[ SPICE_CURSOR_TYPE_COLOR8 ] = "color8",
[ SPICE_CURSOR_TYPE_COLOR16 ] = "color16",
[ SPICE_CURSOR_TYPE_COLOR24 ] = "color24",
[ SPICE_CURSOR_TYPE_COLOR32 ] = "color32",
};
static const char *qxl_v2n(const char *n[], size_t l, int v)
{
if (v >= l || !n[v]) {
return "???";
}
return n[v];
}
#define qxl_name(_list, _value) qxl_v2n(_list, ARRAY_SIZE(_list), _value)
static void qxl_log_image(PCIQXLDevice *qxl, QXLPHYSICAL addr, int group_id)
{
QXLImage *image;
QXLImageDescriptor *desc;
image = qxl_phys2virt(qxl, addr, group_id);
desc = &image->descriptor;
fprintf(stderr, " (id %" PRIx64 " type %d flags %d width %d height %d",
desc->id, desc->type, desc->flags, desc->width, desc->height);
switch (desc->type) {
case SPICE_IMAGE_TYPE_BITMAP:
fprintf(stderr, ", fmt %d flags %d x %d y %d stride %d"
" palette %" PRIx64 " data %" PRIx64,
image->bitmap.format, image->bitmap.flags,
image->bitmap.x, image->bitmap.y,
image->bitmap.stride,
image->bitmap.palette, image->bitmap.data);
break;
}
fprintf(stderr, ")");
}
static void qxl_log_rect(QXLRect *rect)
{
fprintf(stderr, " %dx%d+%d+%d",
rect->right - rect->left,
rect->bottom - rect->top,
rect->left, rect->top);
}
static void qxl_log_cmd_draw_copy(PCIQXLDevice *qxl, QXLCopy *copy, int group_id)
{
fprintf(stderr, " src %" PRIx64,
copy->src_bitmap);
qxl_log_image(qxl, copy->src_bitmap, group_id);
fprintf(stderr, " area");
qxl_log_rect(&copy->src_area);
fprintf(stderr, " rop %d", copy->rop_descriptor);
}
static void qxl_log_cmd_draw(PCIQXLDevice *qxl, QXLDrawable *draw, int group_id)
{
fprintf(stderr, ": surface_id %d type %s effect %s",
draw->surface_id,
qxl_name(qxl_draw_type, draw->type),
qxl_name(qxl_draw_effect, draw->effect));
switch (draw->type) {
case QXL_DRAW_COPY:
qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id);
break;
}
}
static void qxl_log_cmd_draw_compat(PCIQXLDevice *qxl, QXLCompatDrawable *draw,
int group_id)
{
fprintf(stderr, ": type %s effect %s",
qxl_name(qxl_draw_type, draw->type),
qxl_name(qxl_draw_effect, draw->effect));
if (draw->bitmap_offset) {
fprintf(stderr, ": bitmap %d",
draw->bitmap_offset);
qxl_log_rect(&draw->bitmap_area);
}
switch (draw->type) {
case QXL_DRAW_COPY:
qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id);
break;
}
}
static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd)
{
fprintf(stderr, ": %s id %d",
qxl_name(qxl_surface_cmd, cmd->type),
cmd->surface_id);
if (cmd->type == QXL_SURFACE_CMD_CREATE) {
fprintf(stderr, " size %dx%d stride %d format %s (count %d, max %d)",
cmd->u.surface_create.width,
cmd->u.surface_create.height,
cmd->u.surface_create.stride,
qxl_name(spice_surface_fmt, cmd->u.surface_create.format),
qxl->guest_surfaces.count, qxl->guest_surfaces.max);
}
if (cmd->type == QXL_SURFACE_CMD_DESTROY) {
fprintf(stderr, " (count %d)", qxl->guest_surfaces.count);
}
}
void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id)
{
QXLCursor *cursor;
fprintf(stderr, ": %s",
qxl_name(qxl_cursor_cmd, cmd->type));
switch (cmd->type) {
case QXL_CURSOR_SET:
fprintf(stderr, " +%d+%d visible %s, shape @ 0x%" PRIx64,
cmd->u.set.position.x,
cmd->u.set.position.y,
cmd->u.set.visible ? "yes" : "no",
cmd->u.set.shape);
cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id);
fprintf(stderr, " type %s size %dx%d hot-spot +%d+%d"
" unique 0x%" PRIx64 " data-size %d",
qxl_name(spice_cursor_type, cursor->header.type),
cursor->header.width, cursor->header.height,
cursor->header.hot_spot_x, cursor->header.hot_spot_y,
cursor->header.unique, cursor->data_size);
break;
case QXL_CURSOR_MOVE:
fprintf(stderr, " +%d+%d", cmd->u.position.x, cmd->u.position.y);
break;
}
}
void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext)
{
bool compat = ext->flags & QXL_COMMAND_FLAG_COMPAT;
void *data;
if (!qxl->cmdlog) {
return;
}
fprintf(stderr, "qxl-%d/%s:", qxl->id, ring);
fprintf(stderr, " cmd @ 0x%" PRIx64 " %s%s", ext->cmd.data,
qxl_name(qxl_type, ext->cmd.type),
compat ? "(compat)" : "");
data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
switch (ext->cmd.type) {
case QXL_CMD_DRAW:
if (!compat) {
qxl_log_cmd_draw(qxl, data, ext->group_id);
} else {
qxl_log_cmd_draw_compat(qxl, data, ext->group_id);
}
break;
case QXL_CMD_SURFACE:
qxl_log_cmd_surface(qxl, data);
break;
case QXL_CMD_CURSOR:
qxl_log_cmd_cursor(qxl, data, ext->group_id);
break;
}
fprintf(stderr, "\n");
}
/*
* qxl local rendering (aka display on sdl/vnc)
*
* Copyright (C) 2010 Red Hat, Inc.
*
* maintained by Gerd Hoffmann <kraxel@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qxl.h"
static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect)
{
uint8_t *src = qxl->guest_primary.data;
uint8_t *dst = qxl->guest_primary.flipped;
int len, i;
src += (qxl->guest_primary.surface.height - rect->top - 1) *
qxl->guest_primary.stride;
dst += rect->top * qxl->guest_primary.stride;
src += rect->left * qxl->guest_primary.bytes_pp;
dst += rect->left * qxl->guest_primary.bytes_pp;
len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp;
for (i = rect->top; i < rect->bottom; i++) {
memcpy(dst, src, len);
dst += qxl->guest_primary.stride;
src -= qxl->guest_primary.stride;
}
}
void qxl_render_resize(PCIQXLDevice *qxl)
{
QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
qxl->guest_primary.stride = sc->stride;
qxl->guest_primary.resized++;
switch (sc->format) {
case SPICE_SURFACE_FMT_16_555:
qxl->guest_primary.bytes_pp = 2;
qxl->guest_primary.bits_pp = 15;
break;
case SPICE_SURFACE_FMT_16_565:
qxl->guest_primary.bytes_pp = 2;
qxl->guest_primary.bits_pp = 16;
break;
case SPICE_SURFACE_FMT_32_xRGB:
case SPICE_SURFACE_FMT_32_ARGB:
qxl->guest_primary.bytes_pp = 4;
qxl->guest_primary.bits_pp = 32;
break;
default:
fprintf(stderr, "%s: unhandled format: %x\n", __FUNCTION__,
qxl->guest_primary.surface.format);
qxl->guest_primary.bytes_pp = 4;
qxl->guest_primary.bits_pp = 32;
break;
}
}
void qxl_render_update(PCIQXLDevice *qxl)
{
VGACommonState *vga = &qxl->vga;
QXLRect dirty[32], update;
void *ptr;
int i;
if (qxl->guest_primary.resized) {
qxl->guest_primary.resized = 0;
if (qxl->guest_primary.flipped) {
qemu_free(qxl->guest_primary.flipped);
qxl->guest_primary.flipped = NULL;
}
qemu_free_displaysurface(vga->ds);
qxl->guest_primary.data = qemu_get_ram_ptr(qxl->vga.vram_offset);
if (qxl->guest_primary.stride < 0) {
/* spice surface is upside down -> need extra buffer to flip */
qxl->guest_primary.stride = -qxl->guest_primary.stride;
qxl->guest_primary.flipped = qemu_malloc(qxl->guest_primary.surface.width *
qxl->guest_primary.stride);
ptr = qxl->guest_primary.flipped;
} else {
ptr = qxl->guest_primary.data;
}
dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n",
__FUNCTION__,
qxl->guest_primary.surface.width,
qxl->guest_primary.surface.height,
qxl->guest_primary.stride,
qxl->guest_primary.bytes_pp,
qxl->guest_primary.bits_pp,
qxl->guest_primary.flipped ? "yes" : "no");
vga->ds->surface =
qemu_create_displaysurface_from(qxl->guest_primary.surface.width,
qxl->guest_primary.surface.height,
qxl->guest_primary.bits_pp,
qxl->guest_primary.stride,
ptr);
dpy_resize(vga->ds);
}
if (!qxl->guest_primary.commands) {
return;
}
qxl->guest_primary.commands = 0;
update.left = 0;
update.right = qxl->guest_primary.surface.width;
update.top = 0;
update.bottom = qxl->guest_primary.surface.height;
memset(dirty, 0, sizeof(dirty));
qxl->ssd.worker->update_area(qxl->ssd.worker, 0, &update,
dirty, ARRAY_SIZE(dirty), 1);
for (i = 0; i < ARRAY_SIZE(dirty); i++) {
if (qemu_spice_rect_is_empty(dirty+i)) {
break;
}
if (qxl->guest_primary.flipped) {
qxl_flip(qxl, dirty+i);
}
dpy_update(vga->ds,
dirty[i].left, dirty[i].top,
dirty[i].right - dirty[i].left,
dirty[i].bottom - dirty[i].top);
}
}
static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor)
{
QEMUCursor *c;
uint8_t *image, *mask;
int size;
c = cursor_alloc(cursor->header.width, cursor->header.height);
c->hot_x = cursor->header.hot_spot_x;
c->hot_y = cursor->header.hot_spot_y;
switch (cursor->header.type) {
case SPICE_CURSOR_TYPE_ALPHA:
size = cursor->header.width * cursor->header.height * sizeof(uint32_t);
memcpy(c->data, cursor->chunk.data, size);
if (qxl->debug > 2) {
cursor_print_ascii_art(c, "qxl/alpha");
}
break;
case SPICE_CURSOR_TYPE_MONO:
mask = cursor->chunk.data;
image = mask + cursor_get_mono_bpl(c) * c->width;
cursor_set_mono(c, 0xffffff, 0x000000, image, 1, mask);
if (qxl->debug > 2) {
cursor_print_ascii_art(c, "qxl/mono");
}
break;
default:
fprintf(stderr, "%s: not implemented: type %d\n",
__FUNCTION__, cursor->header.type);
goto fail;
}
return c;
fail:
cursor_put(c);
return NULL;
}
/* called from spice server thread context only */
void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
{
QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
QXLCursor *cursor;
QEMUCursor *c;
int x = -1, y = -1;
if (!qxl->ssd.ds->mouse_set || !qxl->ssd.ds->cursor_define) {
return;
}
if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) {
fprintf(stderr, "%s", __FUNCTION__);
qxl_log_cmd_cursor(qxl, cmd, ext->group_id);
fprintf(stderr, "\n");
}
switch (cmd->type) {
case QXL_CURSOR_SET:
x = cmd->u.set.position.x;
y = cmd->u.set.position.y;
cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
if (cursor->chunk.data_size != cursor->data_size) {
fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__);
return;
}
c = qxl_cursor(qxl, cursor);
if (c == NULL) {
c = cursor_builtin_left_ptr();
}
qemu_mutex_lock_iothread();
qxl->ssd.ds->cursor_define(c);
qxl->ssd.ds->mouse_set(x, y, 1);
qemu_mutex_unlock_iothread();
cursor_put(c);
break;
case QXL_CURSOR_MOVE:
x = cmd->u.position.x;
y = cmd->u.position.y;
qemu_mutex_lock_iothread();
qxl->ssd.ds->mouse_set(x, y, 1);
qemu_mutex_unlock_iothread();
break;
}
}
This diff is collapsed.
#include "qemu-common.h"
#include "console.h"
#include "hw.h"
#include "pci.h"
#include "vga_int.h"
#include "ui/qemu-spice.h"
#include "ui/spice-display.h"
enum qxl_mode {
QXL_MODE_UNDEFINED,
QXL_MODE_VGA,
QXL_MODE_COMPAT, /* spice 0.4.x */
QXL_MODE_NATIVE,
};
typedef struct PCIQXLDevice {
PCIDevice pci;
SimpleSpiceDisplay ssd;
int id;
uint32_t debug;
uint32_t guestdebug;
uint32_t cmdlog;
enum qxl_mode mode;
uint32_t cmdflags;
int generation;
uint32_t revision;
int32_t num_memslots;
int32_t num_surfaces;
struct guest_slots {
QXLMemSlot slot;
void *ptr;
uint64_t size;
uint64_t delta;
uint32_t active;
} guest_slots[NUM_MEMSLOTS];
struct guest_primary {
QXLSurfaceCreate surface;
uint32_t commands;
uint32_t resized;
int32_t stride;
uint32_t bits_pp;
uint32_t bytes_pp;
uint8_t *data, *flipped;
} guest_primary;
struct surfaces {
QXLPHYSICAL cmds[NUM_SURFACES];
uint32_t count;
uint32_t max;
} guest_surfaces;
QXLPHYSICAL guest_cursor;
/* thread signaling */
pthread_t main;
int pipe[2];
/* ram pci bar */
QXLRam *ram;
VGACommonState vga;
uint32_t num_free_res;
QXLReleaseInfo *last_release;
uint32_t last_release_offset;
uint32_t oom_running;
/* rom pci bar */
QXLRom shadow_rom;
QXLRom *rom;
QXLModes *modes;
uint32_t rom_size;
uint64_t rom_offset;
/* vram pci bar */
uint32_t vram_size;
uint64_t vram_offset;
/* io bar */
uint32_t io_base;
/* spice 0.4 loadvm compatibility */
void *worker_data;
uint32_t worker_data_size;
} PCIQXLDevice;
#define PANIC_ON(x) if ((x)) { \
printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \
exit(-1); \
}
#define dprint(_qxl, _level, _fmt, ...) \
do { \
if (_qxl->debug >= _level) { \
fprintf(stderr, "qxl-%d: ", _qxl->id); \
fprintf(stderr, _fmt, ## __VA_ARGS__); \
} \
} while (0)
/* qxl.c */
void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
/* qxl-logger.c */
void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id);
void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext);
/* qxl-render.c */
void qxl_render_resize(PCIQXLDevice *qxl);
void qxl_render_update(PCIQXLDevice *qxl);
void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext);
......@@ -106,7 +106,7 @@ typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s);
typedef struct VGACommonState {
uint8_t *vram_ptr;
ram_addr_t vram_offset;
unsigned int vram_size;
uint32_t vram_size;
uint32_t lfb_addr;
uint32_t lfb_end;
uint32_t map_addr;
......
......@@ -751,7 +751,7 @@ Rotate graphical output 90 deg left (only PXA LCD).
ETEXI
DEF("vga", HAS_ARG, QEMU_OPTION_vga,
"-vga [std|cirrus|vmware|xenfb|none]\n"
"-vga [std|cirrus|vmware|qxl|xenfb|none]\n"
" select video card type\n", QEMU_ARCH_ALL)
STEXI
@item -vga @var{type}
......@@ -772,6 +772,10 @@ this option.
VMWare SVGA-II compatible adapter. Use it if you have sufficiently
recent XFree86/XOrg server or Windows guest with a driver for this
card.
@item qxl
QXL paravirtual graphic card. It is VGA compatible (including VESA
2.0 VBE support). Works best with qxl guest drivers installed though.
Recommended choice when using the spice protocol.
@item none
Disable VGA card.
@end table
......
......@@ -102,7 +102,7 @@ extern int incoming_expected;
extern int bios_size;
typedef enum {
VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB
VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL,
} VGAInterfaceType;
extern int vga_interface_type;
......@@ -110,6 +110,7 @@ extern int vga_interface_type;
#define std_vga_enabled (vga_interface_type == VGA_STD)
#define xenfb_enabled (vga_interface_type == VGA_XENFB)
#define vmsvga_enabled (vga_interface_type == VGA_VMWARE)
#define qxl_enabled (vga_interface_type == VGA_QXL)
extern int graphic_width;
extern int graphic_height;
......
......@@ -370,6 +370,21 @@ void qemu_spice_init(void)
int qemu_spice_add_interface(SpiceBaseInstance *sin)
{
if (!spice_server) {
if (QTAILQ_FIRST(&qemu_spice_opts.head) != NULL) {
fprintf(stderr, "Oops: spice configured but not active\n");
exit(1);
}
/*
* Create a spice server instance.
* It does *not* listen on the network.
* It handles QXL local rendering only.
*
* With a command line like '-vnc :0 -vga qxl' you'll end up here.
*/
spice_server = spice_server_new();
spice_server_init(spice_server, &core_interface);
}
return spice_server_add_interface(spice_server, sin);
}
......
......@@ -1411,6 +1411,8 @@ static void select_vgahw (const char *p)
vga_interface_type = VGA_VMWARE;
} else if (strstart(p, "xenfb", &opts)) {
vga_interface_type = VGA_XENFB;
} else if (strstart(p, "qxl", &opts)) {
vga_interface_type = VGA_QXL;