Merge remote branch 'spice/submit.6' into staging

Conflicts:
	configure

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
Anthony Liguori 2010-10-05 14:14:19 -05:00
commit 4447d60968
15 changed files with 1287 additions and 19 deletions

View File

@ -84,11 +84,14 @@ common-obj-y += qemu-char.o savevm.o #aio.o
common-obj-y += msmouse.o ps2.o
common-obj-y += qdev.o qdev-properties.o
common-obj-y += block-migration.o
common-obj-y += pflib.o
common-obj-$(CONFIG_BRLAPI) += baum.o
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
common-obj-$(CONFIG_WIN32) += version.o
common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o
audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
audio-obj-$(CONFIG_SDL) += sdlaudio.o
audio-obj-$(CONFIG_OSS) += ossaudio.o

42
configure vendored
View File

@ -18,15 +18,18 @@ TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe"
# NB: do not call "exit" in the trap handler; this is buggy with some shells;
# see <1285349658-3122-1-git-send-email-loic.minier@linaro.org>
trap "rm -f $TMPC $TMPO $TMPE" EXIT INT QUIT TERM
rm -f config.log
compile_object() {
$cc $QEMU_CFLAGS -c -o $TMPO $TMPC > /dev/null 2> /dev/null
echo $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log
$cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log 2>&1
}
compile_prog() {
local_cflags="$1"
local_ldflags="$2"
$cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags > /dev/null 2> /dev/null
echo $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log
$cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log 2>&1
}
# check whether a command is available to this shell (may be either an
@ -327,6 +330,7 @@ user_pie="no"
zero_malloc=""
trace_backend="nop"
trace_file="trace"
spice=""
# OS specific
if check_define __linux__ ; then
@ -639,6 +643,10 @@ for opt do
;;
--enable-kvm) kvm="yes"
;;
--disable-spice) spice="no"
;;
--enable-spice) spice="yes"
;;
--enable-profiler) profiler="yes"
;;
--enable-cocoa)
@ -921,6 +929,8 @@ echo " --enable-vhost-net enable vhost-net acceleration support"
echo " --trace-backend=B Trace backend nop simple ust"
echo " --trace-file=NAME Full PATH,NAME of file to store traces"
echo " Default:trace-<pid>"
echo " --disable-spice disable spice"
echo " --enable-spice enable spice"
echo ""
echo "NOTE: The object files are built at the place where configure is launched"
exit 1
@ -2075,6 +2085,29 @@ if compile_prog "" ""; then
gcc_attribute_warn_unused_result=yes
fi
# spice probe
if test "$spice" != "no" ; then
cat > $TMPC << EOF
#include <spice.h>
int main(void) { spice_server_new(); return 0; }
EOF
spice_cflags=$($pkgconfig --cflags spice-protocol spice-server 2>/dev/null)
spice_libs=$($pkgconfig --libs spice-protocol spice-server 2>/dev/null)
if $pkgconfig --atleast-version=0.5.3 spice-server &&\
compile_prog "$spice_cflags" "$spice_libs" ; then
spice="yes"
libs_softmmu="$libs_softmmu $spice_libs"
QEMU_CFLAGS="$QEMU_CFLAGS $spice_cflags"
else
if test "$spice" = "yes" ; then
feature_not_found "spice"
fi
spice="no"
fi
fi
##########################################
##########################################
# check if we have fdatasync
@ -2285,6 +2318,7 @@ echo "uuid support $uuid"
echo "vhost-net support $vhost_net"
echo "Trace backend $trace_backend"
echo "Trace output file $trace_file-<pid>"
echo "spice support $spice"
if test $sdl_too_old = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support"
@ -2540,6 +2574,10 @@ if test "$posix_madvise" = "yes" ; then
echo "CONFIG_POSIX_MADVISE=y" >> $config_host_mak
fi
if test "$spice" = "yes" ; then
echo "CONFIG_SPICE=y" >> $config_host_mak
fi
# XXX: suppress that
if [ "$bsd" = "yes" ] ; then
echo "CONFIG_BSD=y" >> $config_host_mak

View File

@ -16,6 +16,7 @@
#include "qemu-queue.h"
#include "osdep.h"
#include "qemu-common.h"
#include "qemu-config.h"
static QTAILQ_HEAD(FsTypeEntry_head, FsTypeListEntry) fstype_entries =
QTAILQ_HEAD_INITIALIZER(fstype_entries);
@ -75,3 +76,11 @@ FsTypeEntry *get_fsdev_fsentry(char *id)
}
return NULL;
}
static void fsdev_register_config(void)
{
qemu_add_opts(&qemu_fsdev_opts);
qemu_add_opts(&qemu_virtfs_opts);
}
machine_init(fsdev_register_config);

213
pflib.c Normal file
View File

@ -0,0 +1,213 @@
/*
* PixelFormat conversion library.
*
* Author: Gerd Hoffmann <kraxel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
*/
#include "qemu-common.h"
#include "console.h"
#include "pflib.h"
typedef struct QemuPixel QemuPixel;
typedef void (*pf_convert)(QemuPfConv *conv,
void *dst, void *src, uint32_t cnt);
typedef void (*pf_convert_from)(PixelFormat *pf,
QemuPixel *dst, void *src, uint32_t cnt);
typedef void (*pf_convert_to)(PixelFormat *pf,
void *dst, QemuPixel *src, uint32_t cnt);
struct QemuPfConv {
pf_convert convert;
PixelFormat src;
PixelFormat dst;
/* for copy_generic() */
pf_convert_from conv_from;
pf_convert_to conv_to;
QemuPixel *conv_buf;
uint32_t conv_cnt;
};
struct QemuPixel {
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t alpha;
};
/* ----------------------------------------------------------------------- */
/* PixelFormat -> QemuPixel conversions */
static void conv_16_to_pixel(PixelFormat *pf,
QemuPixel *dst, void *src, uint32_t cnt)
{
uint16_t *src16 = src;
while (cnt > 0) {
dst->red = ((*src16 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
dst->green = ((*src16 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
dst->blue = ((*src16 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
dst->alpha = ((*src16 & pf->amask) >> pf->ashift) << (8 - pf->abits);
dst++, src16++, cnt--;
}
}
/* assumes pf->{r,g,b,a}bits == 8 */
static void conv_32_to_pixel_fast(PixelFormat *pf,
QemuPixel *dst, void *src, uint32_t cnt)
{
uint32_t *src32 = src;
while (cnt > 0) {
dst->red = (*src32 & pf->rmask) >> pf->rshift;
dst->green = (*src32 & pf->gmask) >> pf->gshift;
dst->blue = (*src32 & pf->bmask) >> pf->bshift;
dst->alpha = (*src32 & pf->amask) >> pf->ashift;
dst++, src32++, cnt--;
}
}
static void conv_32_to_pixel_generic(PixelFormat *pf,
QemuPixel *dst, void *src, uint32_t cnt)
{
uint32_t *src32 = src;
while (cnt > 0) {
if (pf->rbits < 8) {
dst->red = ((*src32 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
} else {
dst->red = ((*src32 & pf->rmask) >> pf->rshift) >> (pf->rbits - 8);
}
if (pf->gbits < 8) {
dst->green = ((*src32 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
} else {
dst->green = ((*src32 & pf->gmask) >> pf->gshift) >> (pf->gbits - 8);
}
if (pf->bbits < 8) {
dst->blue = ((*src32 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
} else {
dst->blue = ((*src32 & pf->bmask) >> pf->bshift) >> (pf->bbits - 8);
}
if (pf->abits < 8) {
dst->alpha = ((*src32 & pf->amask) >> pf->ashift) << (8 - pf->abits);
} else {
dst->alpha = ((*src32 & pf->amask) >> pf->ashift) >> (pf->abits - 8);
}
dst++, src32++, cnt--;
}
}
/* ----------------------------------------------------------------------- */
/* QemuPixel -> PixelFormat conversions */
static void conv_pixel_to_16(PixelFormat *pf,
void *dst, QemuPixel *src, uint32_t cnt)
{
uint16_t *dst16 = dst;
while (cnt > 0) {
*dst16 = ((uint16_t)src->red >> (8 - pf->rbits)) << pf->rshift;
*dst16 |= ((uint16_t)src->green >> (8 - pf->gbits)) << pf->gshift;
*dst16 |= ((uint16_t)src->blue >> (8 - pf->bbits)) << pf->bshift;
*dst16 |= ((uint16_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
dst16++, src++, cnt--;
}
}
static void conv_pixel_to_32(PixelFormat *pf,
void *dst, QemuPixel *src, uint32_t cnt)
{
uint32_t *dst32 = dst;
while (cnt > 0) {
*dst32 = ((uint32_t)src->red >> (8 - pf->rbits)) << pf->rshift;
*dst32 |= ((uint32_t)src->green >> (8 - pf->gbits)) << pf->gshift;
*dst32 |= ((uint32_t)src->blue >> (8 - pf->bbits)) << pf->bshift;
*dst32 |= ((uint32_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
dst32++, src++, cnt--;
}
}
/* ----------------------------------------------------------------------- */
/* PixelFormat -> PixelFormat conversions */
static void convert_copy(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
{
uint32_t bytes = cnt * conv->src.bytes_per_pixel;
memcpy(dst, src, bytes);
}
static void convert_generic(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
{
if (conv->conv_cnt < cnt) {
conv->conv_cnt = cnt;
conv->conv_buf = qemu_realloc(conv->conv_buf, sizeof(QemuPixel) * conv->conv_cnt);
}
conv->conv_from(&conv->src, conv->conv_buf, src, cnt);
conv->conv_to(&conv->dst, dst, conv->conv_buf, cnt);
}
/* ----------------------------------------------------------------------- */
/* public interface */
QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src)
{
QemuPfConv *conv = qemu_mallocz(sizeof(QemuPfConv));
conv->src = *src;
conv->dst = *dst;
if (memcmp(&conv->src, &conv->dst, sizeof(PixelFormat)) == 0) {
/* formats identical, can simply copy */
conv->convert = convert_copy;
} else {
/* generic two-step conversion: src -> QemuPixel -> dst */
switch (conv->src.bytes_per_pixel) {
case 2:
conv->conv_from = conv_16_to_pixel;
break;
case 4:
if (conv->src.rbits == 8 && conv->src.gbits == 8 && conv->src.bbits == 8) {
conv->conv_from = conv_32_to_pixel_fast;
} else {
conv->conv_from = conv_32_to_pixel_generic;
}
break;
default:
goto err;
}
switch (conv->dst.bytes_per_pixel) {
case 2:
conv->conv_to = conv_pixel_to_16;
break;
case 4:
conv->conv_to = conv_pixel_to_32;
break;
default:
goto err;
}
conv->convert = convert_generic;
}
return conv;
err:
qemu_free(conv);
return NULL;
}
void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
{
conv->convert(conv, dst, src, cnt);
}
void qemu_pf_conv_put(QemuPfConv *conv)
{
if (conv) {
qemu_free(conv->conv_buf);
qemu_free(conv);
}
}

20
pflib.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef __QEMU_PFLIB_H
#define __QEMU_PFLIB_H
/*
* PixelFormat conversion library.
*
* Author: Gerd Hoffmann <kraxel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
*/
typedef struct QemuPfConv QemuPfConv;
QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src);
void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt);
void qemu_pf_conv_put(QemuPfConv *conv);
#endif

View File

@ -354,6 +354,24 @@ static QemuOptsList qemu_cpudef_opts = {
},
};
QemuOptsList qemu_spice_opts = {
.name = "spice",
.head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head),
.desc = {
{
.name = "port",
.type = QEMU_OPT_NUMBER,
},{
.name = "password",
.type = QEMU_OPT_STRING,
},{
.name = "disable-ticketing",
.type = QEMU_OPT_BOOL,
},
{ /* end if list */ }
},
};
static QemuOptsList *vm_config_groups[32] = {
&qemu_drive_opts,
&qemu_chardev_opts,

View File

@ -3,6 +3,7 @@
extern QemuOptsList qemu_fsdev_opts;
extern QemuOptsList qemu_virtfs_opts;
extern QemuOptsList qemu_spice_opts;
QemuOptsList *qemu_find_opts(const char *group);
void qemu_add_opts(QemuOptsList *list);

View File

@ -670,6 +670,27 @@ STEXI
Enable SDL.
ETEXI
DEF("spice", HAS_ARG, QEMU_OPTION_spice,
"-spice <args> enable spice\n", QEMU_ARCH_ALL)
STEXI
@item -spice @var{option}[,@var{option}[,...]]
@findex -spice
Enable the spice remote desktop protocol. Valid options are
@table @option
@item port=<nr>
Set the TCP port spice is listening on.
@item password=<secret>
Set the password you need to authenticate.
@item disable-ticketing
Allow client connects without authentication.
@end table
ETEXI
DEF("portrait", 0, QEMU_OPTION_portrait,
"-portrait rotate graphical output 90 deg left (only PXA LCD)\n",
QEMU_ARCH_ALL)

View File

@ -94,7 +94,6 @@ typedef enum DisplayType
DT_DEFAULT,
DT_CURSES,
DT_SDL,
DT_VNC,
DT_NOGRAPHIC,
} DisplayType;

41
ui/qemu-spice.h Normal file
View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2010 Red Hat, Inc.
*
* 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/>.
*/
#ifndef QEMU_SPICE_H
#define QEMU_SPICE_H
#ifdef CONFIG_SPICE
#include <spice.h>
#include "qemu-option.h"
#include "qemu-config.h"
extern int using_spice;
void qemu_spice_init(void);
void qemu_spice_input_init(void);
void qemu_spice_display_init(DisplayState *ds);
int qemu_spice_add_interface(SpiceBaseInstance *sin);
#else /* CONFIG_SPICE */
#define using_spice 0
#endif /* CONFIG_SPICE */
#endif /* QEMU_SPICE_H */

189
ui/spice-core.c Normal file
View File

@ -0,0 +1,189 @@
/*
* Copyright (C) 2010 Red Hat, Inc.
*
* 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 <spice.h>
#include <spice-experimental.h>
#include "qemu-common.h"
#include "qemu-spice.h"
#include "qemu-timer.h"
#include "qemu-queue.h"
#include "monitor.h"
/* core bits */
static SpiceServer *spice_server;
int using_spice = 0;
struct SpiceTimer {
QEMUTimer *timer;
QTAILQ_ENTRY(SpiceTimer) next;
};
static QTAILQ_HEAD(, SpiceTimer) timers = QTAILQ_HEAD_INITIALIZER(timers);
static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
{
SpiceTimer *timer;
timer = qemu_mallocz(sizeof(*timer));
timer->timer = qemu_new_timer(rt_clock, func, opaque);
QTAILQ_INSERT_TAIL(&timers, timer, next);
return timer;
}
static void timer_start(SpiceTimer *timer, uint32_t ms)
{
qemu_mod_timer(timer->timer, qemu_get_clock(rt_clock) + ms);
}
static void timer_cancel(SpiceTimer *timer)
{
qemu_del_timer(timer->timer);
}
static void timer_remove(SpiceTimer *timer)
{
qemu_del_timer(timer->timer);
qemu_free_timer(timer->timer);
QTAILQ_REMOVE(&timers, timer, next);
qemu_free(timer);
}
struct SpiceWatch {
int fd;
int event_mask;
SpiceWatchFunc func;
void *opaque;
QTAILQ_ENTRY(SpiceWatch) next;
};
static QTAILQ_HEAD(, SpiceWatch) watches = QTAILQ_HEAD_INITIALIZER(watches);
static void watch_read(void *opaque)
{
SpiceWatch *watch = opaque;
watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque);
}
static void watch_write(void *opaque)
{
SpiceWatch *watch = opaque;
watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque);
}
static void watch_update_mask(SpiceWatch *watch, int event_mask)
{
IOHandler *on_read = NULL;
IOHandler *on_write = NULL;
watch->event_mask = event_mask;
if (watch->event_mask & SPICE_WATCH_EVENT_READ) {
on_read = watch_read;
}
if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) {
on_read = watch_write;
}
qemu_set_fd_handler(watch->fd, on_read, on_write, watch);
}
static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque)
{
SpiceWatch *watch;
watch = qemu_mallocz(sizeof(*watch));
watch->fd = fd;
watch->func = func;
watch->opaque = opaque;
QTAILQ_INSERT_TAIL(&watches, watch, next);
watch_update_mask(watch, event_mask);
return watch;
}
static void watch_remove(SpiceWatch *watch)
{
watch_update_mask(watch, 0);
QTAILQ_REMOVE(&watches, watch, next);
qemu_free(watch);
}
static SpiceCoreInterface core_interface = {
.base.type = SPICE_INTERFACE_CORE,
.base.description = "qemu core services",
.base.major_version = SPICE_INTERFACE_CORE_MAJOR,
.base.minor_version = SPICE_INTERFACE_CORE_MINOR,
.timer_add = timer_add,
.timer_start = timer_start,
.timer_cancel = timer_cancel,
.timer_remove = timer_remove,
.watch_add = watch_add,
.watch_update_mask = watch_update_mask,
.watch_remove = watch_remove,
};
/* functions for the rest of qemu */
void qemu_spice_init(void)
{
QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
const char *password;
int port;
if (!opts) {
return;
}
port = qemu_opt_get_number(opts, "port", 0);
if (!port) {
return;
}
password = qemu_opt_get(opts, "password");
spice_server = spice_server_new();
spice_server_set_port(spice_server, port);
if (password) {
spice_server_set_ticket(spice_server, password, 0, 0, 0);
}
if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) {
spice_server_set_noauth(spice_server);
}
/* TODO: make configurable via cmdline */
spice_server_set_image_compression(spice_server, SPICE_IMAGE_COMPRESS_AUTO_GLZ);
spice_server_init(spice_server, &core_interface);
using_spice = 1;
qemu_spice_input_init();
}
int qemu_spice_add_interface(SpiceBaseInstance *sin)
{
return spice_server_add_interface(spice_server, sin);
}
static void spice_register_config(void)
{
qemu_add_opts(&qemu_spice_opts);
}
machine_init(spice_register_config);
static void spice_initialize(void)
{
qemu_spice_init();
}
device_init(spice_initialize);

412
ui/spice-display.c Normal file
View File

@ -0,0 +1,412 @@
/*
* Copyright (C) 2010 Red Hat, Inc.
*
* 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 <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"
static int debug = 0;
static void __attribute__((format(printf,2,3)))
dprint(int level, const char *fmt, ...)
{
va_list args;
if (level <= debug) {
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
}
int qemu_spice_rect_is_empty(const QXLRect* r)
{
return r->top == r->bottom || r->left == r->right;
}
void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r)
{
if (qemu_spice_rect_is_empty(r)) {
return;
}
if (qemu_spice_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);
}
/*
* Called from spice server thread context (via interface_get_command).
* We do *not* hold the global qemu mutex here, so extra care is needed
* when calling qemu functions. Qemu interfaces used:
* - pflib (is re-entrant).
* - qemu_malloc (underlying glibc malloc is re-entrant).
*/
SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd)
{
SimpleSpiceUpdate *update;
QXLDrawable *drawable;
QXLImage *image;
QXLCommand *cmd;
uint8_t *src, *dst;
int by, bw, bh;
if (qemu_spice_rect_is_empty(&ssd->dirty)) {
return NULL;
};
pthread_mutex_lock(&ssd->lock);
dprint(2, "%s: lr %d -> %d, tb -> %d -> %d\n", __FUNCTION__,
ssd->dirty.left, ssd->dirty.right,
ssd->dirty.top, ssd->dirty.bottom);
update = qemu_mallocz(sizeof(*update));
drawable = &update->drawable;
image = &update->image;
cmd = &update->ext.cmd;
bw = ssd->dirty.right - ssd->dirty.left;
bh = ssd->dirty.bottom - ssd->dirty.top;
update->bitmap = qemu_malloc(bw * bh * 4);
drawable->bbox = ssd->dirty;
drawable->clip.type = SPICE_CLIP_TYPE_NONE;
drawable->effect = QXL_EFFECT_OPAQUE;
drawable->release_info.id = (intptr_t)update;
drawable->type = QXL_DRAW_COPY;
drawable->surfaces_dest[0] = -1;
drawable->surfaces_dest[1] = -1;
drawable->surfaces_dest[2] = -1;
drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT;
drawable->u.copy.src_bitmap = (intptr_t)image;
drawable->u.copy.src_area.right = bw;
drawable->u.copy.src_area.bottom = bh;
QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ssd->unique++);
image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN;
image->bitmap.stride = bw * 4;
image->descriptor.width = image->bitmap.x = bw;
image->descriptor.height = image->bitmap.y = bh;
image->bitmap.data = (intptr_t)(update->bitmap);
image->bitmap.palette = 0;
image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
if (ssd->conv == NULL) {
PixelFormat dst = qemu_default_pixelformat(32);
ssd->conv = qemu_pf_conv_get(&dst, &ssd->ds->surface->pf);
assert(ssd->conv);
}
src = ds_get_data(ssd->ds) +
ssd->dirty.top * ds_get_linesize(ssd->ds) +
ssd->dirty.left * ds_get_bytes_per_pixel(ssd->ds);
dst = update->bitmap;
for (by = 0; by < bh; by++) {
qemu_pf_conv_run(ssd->conv, dst, src, bw);
src += ds_get_linesize(ssd->ds);
dst += image->bitmap.stride;
}
cmd->type = QXL_CMD_DRAW;
cmd->data = (intptr_t)drawable;
memset(&ssd->dirty, 0, sizeof(ssd->dirty));
pthread_mutex_unlock(&ssd->lock);
return update;
}
/*
* Called from spice server thread context (via interface_release_ressource)
* We do *not* hold the global qemu mutex here, so extra care is needed
* when calling qemu functions. Qemu interfaces used:
* - qemu_free (underlying glibc free is re-entrant).
*/
void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update)
{
qemu_free(update->bitmap);
qemu_free(update);
}
void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
{
QXLDevMemSlot memslot;
dprint(1, "%s:\n", __FUNCTION__);
memset(&memslot, 0, sizeof(memslot));
memslot.slot_group_id = MEMSLOT_GROUP_HOST;
memslot.virt_end = ~0;
ssd->worker->add_memslot(ssd->worker, &memslot);
}
void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
{
QXLDevSurfaceCreate surface;
dprint(1, "%s: %dx%d\n", __FUNCTION__,
ds_get_width(ssd->ds), ds_get_height(ssd->ds));
surface.format = SPICE_SURFACE_FMT_32_xRGB;
surface.width = ds_get_width(ssd->ds);
surface.height = ds_get_height(ssd->ds);
surface.stride = -surface.width * 4;
surface.mouse_mode = true;
surface.flags = 0;
surface.type = 0;
surface.mem = (intptr_t)ssd->buf;
surface.group_id = MEMSLOT_GROUP_HOST;
ssd->worker->create_primary_surface(ssd->worker, 0, &surface);
}
void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
{
dprint(1, "%s:\n", __FUNCTION__);
ssd->worker->destroy_primary_surface(ssd->worker, 0);
}
void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason)
{
SimpleSpiceDisplay *ssd = opaque;
if (running) {
ssd->worker->start(ssd->worker);
} else {
ssd->worker->stop(ssd->worker);
}
ssd->running = running;
}
/* display listener callbacks */
void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
int x, int y, int w, int h)
{
QXLRect update_area;
dprint(2, "%s: x %d y %d w %d h %d\n", __FUNCTION__, x, y, w, h);
update_area.left = x,
update_area.right = x + w;
update_area.top = y;
update_area.bottom = y + h;
pthread_mutex_lock(&ssd->lock);
if (qemu_spice_rect_is_empty(&ssd->dirty)) {
ssd->notify++;
}
qemu_spice_rect_union(&ssd->dirty, &update_area);
pthread_mutex_unlock(&ssd->lock);
}
void qemu_spice_display_resize(SimpleSpiceDisplay *ssd)
{
dprint(1, "%s:\n", __FUNCTION__);
pthread_mutex_lock(&ssd->lock);
memset(&ssd->dirty, 0, sizeof(ssd->dirty));
qemu_pf_conv_put(ssd->conv);
ssd->conv = NULL;
pthread_mutex_unlock(&ssd->lock);
qemu_spice_destroy_host_primary(ssd);
qemu_spice_create_host_primary(ssd);
pthread_mutex_lock(&ssd->lock);
memset(&ssd->dirty, 0, sizeof(ssd->dirty));
ssd->notify++;
pthread_mutex_unlock(&ssd->lock);
}
void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
{
dprint(3, "%s:\n", __FUNCTION__);
vga_hw_update();
if (ssd->notify) {
ssd->notify = 0;
ssd->worker->wakeup(ssd->worker);
dprint(2, "%s: notify\n", __FUNCTION__);
}
}
/* spice display interface callbacks */
static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
{
SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
dprint(1, "%s:\n", __FUNCTION__);
ssd->worker = qxl_worker;
}
static void interface_set_compression_level(QXLInstance *sin, int level)
{
dprint(1, "%s:\n", __FUNCTION__);
/* nothing to do */
}
static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
{
dprint(3, "%s:\n", __FUNCTION__);
/* nothing to do */
}
static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
{
SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
info->memslot_id_bits = MEMSLOT_SLOT_BITS;
info->num_memslots = NUM_MEMSLOTS;
info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
info->internal_groupslot_id = 0;
info->qxl_ram_size = ssd->bufsize;
info->n_surfaces = NUM_SURFACES;
}
static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
{
SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
SimpleSpiceUpdate *update;
dprint(3, "%s:\n", __FUNCTION__);
update = qemu_spice_create_update(ssd);
if (update == NULL) {
return false;
}
*ext = update->ext;
return true;
}
static int interface_req_cmd_notification(QXLInstance *sin)
{
dprint(1, "%s:\n", __FUNCTION__);
return 1;
}
static void interface_release_resource(QXLInstance *sin,
struct QXLReleaseInfoExt ext)
{
SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
uintptr_t id;
dprint(2, "%s:\n", __FUNCTION__);
id = ext.info->id;
qemu_spice_destroy_update(ssd, (void*)id);
}
static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
{
dprint(3, "%s:\n", __FUNCTION__);
return false;
}
static int interface_req_cursor_notification(QXLInstance *sin)
{
dprint(1, "%s:\n", __FUNCTION__);
return 1;
}
static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
{
fprintf(stderr, "%s: abort()\n", __FUNCTION__);
abort();
}
static int interface_flush_resources(QXLInstance *sin)
{
fprintf(stderr, "%s: abort()\n", __FUNCTION__);
abort();
return 0;
}
static const QXLInterface dpy_interface = {
.base.type = SPICE_INTERFACE_QXL,
.base.description = "qemu simple display",
.base.major_version = SPICE_INTERFACE_QXL_MAJOR,
.base.minor_version = SPICE_INTERFACE_QXL_MINOR,
.attache_worker = interface_attach_worker,
.set_compression_level = interface_set_compression_level,
.set_mm_time = interface_set_mm_time,
.get_init_info = interface_get_init_info,
/* the callbacks below are called from spice server thread context */
.get_command = interface_get_command,
.req_cmd_notification = interface_req_cmd_notification,
.release_resource = interface_release_resource,
.get_cursor_command = interface_get_cursor_command,
.req_cursor_notification = interface_req_cursor_notification,
.notify_update = interface_notify_update,
.flush_resources = interface_flush_resources,
};
static SimpleSpiceDisplay sdpy;
static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
{
qemu_spice_display_update(&sdpy, x, y, w, h);
}
static void display_resize(struct DisplayState *ds)
{
qemu_spice_display_resize(&sdpy);
}
static void display_refresh(struct DisplayState *ds)
{
qemu_spice_display_refresh(&sdpy);
}
static DisplayChangeListener display_listener = {
.dpy_update = display_update,
.dpy_resize = display_resize,
.dpy_refresh = display_refresh,
};
void qemu_spice_display_init(DisplayState *ds)
{
assert(sdpy.ds == NULL);
sdpy.ds = ds;
sdpy.bufsize = (16 * 1024 * 1024);
sdpy.buf = qemu_malloc(sdpy.bufsize);
pthread_mutex_init(&sdpy.lock, NULL);
register_displaychangelistener(ds, &display_listener);
sdpy.qxl.base.sif = &dpy_interface.base;
qemu_spice_add_interface(&sdpy.qxl.base);
assert(sdpy.worker);
qemu_add_vm_change_state_handler(qemu_spice_vm_change_state_handler, &sdpy);
qemu_spice_create_host_memslot(&sdpy);
qemu_spice_create_host_primary(&sdpy);
}

69
ui/spice-display.h Normal file
View File

@ -0,0 +1,69 @@
/*
* Copyright (C) 2010 Red Hat, Inc.
*
* 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 <spice/ipc_ring.h>
#include <spice/enums.h>
#include <spice/qxl_dev.h>
#include "pflib.h"
#define NUM_MEMSLOTS 8
#define MEMSLOT_GENERATION_BITS 8
#define MEMSLOT_SLOT_BITS 8
#define MEMSLOT_GROUP_HOST 0
#define MEMSLOT_GROUP_GUEST 1
#define NUM_MEMSLOTS_GROUPS 2
#define NUM_SURFACES 1024
typedef struct SimpleSpiceDisplay {
DisplayState *ds;
void *buf;
int bufsize;
QXLWorker *worker;
QXLInstance qxl;
uint32_t unique;
QemuPfConv *conv;
pthread_mutex_t lock;
QXLRect dirty;
int notify;
int running;
} SimpleSpiceDisplay;
typedef struct SimpleSpiceUpdate {
QXLDrawable drawable;
QXLImage image;
QXLCommandExt ext;
uint8_t *bitmap;
} SimpleSpiceUpdate;
int qemu_spice_rect_is_empty(const QXLRect* r);
void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r);
SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *sdpy);
void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update);
void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd);
void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd);
void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd);
void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason);
void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
int x, int y, int w, int h);
void qemu_spice_display_resize(SimpleSpiceDisplay *ssd);
void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd);

217
ui/spice-input.c Normal file
View File

@ -0,0 +1,217 @@
/*
* Copyright (C) 2010 Red Hat, Inc.
*
* 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 <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <spice.h>
#include <spice/enums.h>
#include "qemu-common.h"
#include "qemu-spice.h"
#include "console.h"
/* keyboard bits */
typedef struct QemuSpiceKbd {
SpiceKbdInstance sin;
int ledstate;
} QemuSpiceKbd;
static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag);
static uint8_t kbd_get_leds(SpiceKbdInstance *sin);
static void kbd_leds(void *opaque, int l);
static const SpiceKbdInterface kbd_interface = {
.base.type = SPICE_INTERFACE_KEYBOARD,
.base.description = "qemu keyboard",
.base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR,
.base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR,
.push_scan_freg = kbd_push_key,
.get_leds = kbd_get_leds,
};
static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
{
kbd_put_keycode(frag);
}
static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
{
QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin);
return kbd->ledstate;
}
static void kbd_leds(void *opaque, int ledstate)
{
QemuSpiceKbd *kbd = opaque;
kbd->ledstate = 0;
if (ledstate & QEMU_SCROLL_LOCK_LED) {
kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK;
}
if (ledstate & QEMU_NUM_LOCK_LED) {
kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK;
}
if (ledstate & QEMU_CAPS_LOCK_LED) {
kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK;
}
spice_server_kbd_leds(&kbd->sin, ledstate);
}
/* mouse bits */
typedef struct QemuSpicePointer {
SpiceMouseInstance mouse;
SpiceTabletInstance tablet;
int width, height, x, y;
Notifier mouse_mode;
bool absolute;
} QemuSpicePointer;
static int map_buttons(int spice_buttons)
{
int qemu_buttons = 0;
/*
* Note: SPICE_MOUSE_BUTTON_* specifies the wire protocol but this
* isn't what we get passed in via interface callbacks for the
* middle and right button ...
*/
if (spice_buttons & SPICE_MOUSE_BUTTON_MASK_LEFT) {
qemu_buttons |= MOUSE_EVENT_LBUTTON;
}
if (spice_buttons & 0x04 /* SPICE_MOUSE_BUTTON_MASK_MIDDLE */) {
qemu_buttons |= MOUSE_EVENT_MBUTTON;
}
if (spice_buttons & 0x02 /* SPICE_MOUSE_BUTTON_MASK_RIGHT */) {
qemu_buttons |= MOUSE_EVENT_RBUTTON;
}
return qemu_buttons;
}
static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz,
uint32_t buttons_state)
{
kbd_mouse_event(dx, dy, dz, map_buttons(buttons_state));
}
static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state)
{
kbd_mouse_event(0, 0, 0, map_buttons(buttons_state));
}
static const SpiceMouseInterface mouse_interface = {
.base.type = SPICE_INTERFACE_MOUSE,
.base.description = "mouse",
.base.major_version = SPICE_INTERFACE_MOUSE_MAJOR,
.base.minor_version = SPICE_INTERFACE_MOUSE_MINOR,
.motion = mouse_motion,
.buttons = mouse_buttons,
};
static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height)
{
QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
if (height < 16) {
height = 16;
}
if (width < 16) {
width = 16;
}
pointer->width = width;
pointer->height = height;
}
static void tablet_position(SpiceTabletInstance* sin, int x, int y,
uint32_t buttons_state)
{
QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
pointer->x = x * 0x7FFF / (pointer->width - 1);
pointer->y = y * 0x7FFF / (pointer->height - 1);
kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state));
}
static void tablet_wheel(SpiceTabletInstance* sin, int wheel,
uint32_t buttons_state)
{
QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
kbd_mouse_event(pointer->x, pointer->y, wheel, map_buttons(buttons_state));
}
static void tablet_buttons(SpiceTabletInstance *sin,
uint32_t buttons_state)
{
QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state));
}
static const SpiceTabletInterface tablet_interface = {
.base.type = SPICE_INTERFACE_TABLET,
.base.description = "tablet",
.base.major_version = SPICE_INTERFACE_TABLET_MAJOR,
.base.minor_version = SPICE_INTERFACE_TABLET_MINOR,
.set_logical_size = tablet_set_logical_size,
.position = tablet_position,
.wheel = tablet_wheel,
.buttons = tablet_buttons,
};
static void mouse_mode_notifier(Notifier *notifier)
{
QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode);
bool is_absolute = kbd_mouse_is_absolute();
if (pointer->absolute == is_absolute) {
return;
}
if (is_absolute) {
qemu_spice_add_interface(&pointer->tablet.base);
} else {
spice_server_remove_interface(&pointer->tablet.base);
}
pointer->absolute = is_absolute;
}
void qemu_spice_input_init(void)
{
QemuSpiceKbd *kbd;
QemuSpicePointer *pointer;
kbd = qemu_mallocz(sizeof(*kbd));
kbd->sin.base.sif = &kbd_interface.base;
qemu_spice_add_interface(&kbd->sin.base);
qemu_add_led_event_handler(kbd_leds, kbd);
pointer = qemu_mallocz(sizeof(*pointer));
pointer->mouse.base.sif = &mouse_interface.base;
pointer->tablet.base.sif = &tablet_interface.base;
qemu_spice_add_interface(&pointer->mouse.base);
pointer->absolute = false;
pointer->mouse_mode.notify = mouse_mode_notifier;
qemu_add_mouse_mode_change_notifier(&pointer->mouse_mode);
mouse_mode_notifier(&pointer->mouse_mode);
}

50
vl.c
View File

@ -162,6 +162,8 @@ int main(int argc, char **argv)
#include "cpus.h"
#include "arch_init.h"
#include "ui/qemu-spice.h"
//#define DEBUG_NET
//#define DEBUG_SLIRP
@ -173,6 +175,7 @@ static const char *data_dir;
const char *bios_name = NULL;
enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB;
DisplayType display_type = DT_DEFAULT;
int display_remote = 0;
const char* keyboard_layout = NULL;
ram_addr_t ram_size;
const char *mem_path = NULL;
@ -1862,11 +1865,6 @@ int main(int argc, char **argv, char **envp)
tb_size = 0;
autostart= 1;
#ifdef CONFIG_VIRTFS
qemu_add_opts(&qemu_fsdev_opts);
qemu_add_opts(&qemu_virtfs_opts);
#endif
/* first pass of option parsing */
optind = 1;
while (optind < argc) {
@ -2477,7 +2475,7 @@ int main(int argc, char **argv, char **envp)
}
break;
case QEMU_OPTION_vnc:
display_type = DT_VNC;
display_remote++;
vnc_display = optarg;
break;
case QEMU_OPTION_no_acpi:
@ -2620,6 +2618,18 @@ int main(int argc, char **argv, char **envp)
}
break;
}
case QEMU_OPTION_spice:
olist = qemu_find_opts("spice");
if (!olist) {
fprintf(stderr, "spice is not supported by this qemu build.\n");
exit(1);
}
opts = qemu_opts_parse(olist, optarg, 0);
if (!opts) {
fprintf(stderr, "parse error: %s\n", optarg);
exit(1);
}
break;
case QEMU_OPTION_writeconfig:
{
FILE *fp;
@ -2921,17 +2931,19 @@ int main(int argc, char **argv, char **envp)
/* just use the first displaystate for the moment */
ds = get_displaystate();
if (display_type == DT_DEFAULT) {
if (using_spice)
display_remote++;
if (display_type == DT_DEFAULT && !display_remote) {
#if defined(CONFIG_SDL) || defined(CONFIG_COCOA)
display_type = DT_SDL;
#else
display_type = DT_VNC;
vnc_display = "localhost:0,to=99";
show_vnc_port = 1;
#endif
}
/* init local displays */
switch (display_type) {
case DT_NOGRAPHIC:
break;
@ -2949,7 +2961,12 @@ int main(int argc, char **argv, char **envp)
cocoa_display_init(ds, full_screen);
break;
#endif
case DT_VNC:
default:
break;
}
/* init remote displays */
if (vnc_display) {
vnc_display_init(ds);
if (vnc_display_open(ds, vnc_display) < 0)
exit(1);
@ -2957,12 +2974,15 @@ int main(int argc, char **argv, char **envp)
if (show_vnc_port) {
printf("VNC server running on `%s'\n", vnc_display_local_addr(ds));
}
break;
default:
break;
}
dpy_resize(ds);
#ifdef CONFIG_SPICE
if (using_spice) {
qemu_spice_display_init(ds);
}
#endif
/* display setup */
dpy_resize(ds);
dcl = ds->listeners;
while (dcl != NULL) {
if (dcl->dpy_refresh != NULL) {
@ -2972,12 +2992,10 @@ int main(int argc, char **argv, char **envp)
}
dcl = dcl->next;
}
if (display_type == DT_NOGRAPHIC || display_type == DT_VNC) {
if (ds->gui_timer == NULL) {
nographic_timer = qemu_new_timer(rt_clock, nographic_update, NULL);
qemu_mod_timer(nographic_timer, qemu_get_clock(rt_clock));
}
text_consoles_set_display(ds);
if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) {