diff options
-rw-r--r-- | Makefile.objs | 3 | ||||
-rwxr-xr-x | configure | 42 | ||||
-rw-r--r-- | fsdev/qemu-fsdev.c | 9 | ||||
-rw-r--r-- | pflib.c | 213 | ||||
-rw-r--r-- | pflib.h | 20 | ||||
-rw-r--r-- | qemu-config.c | 18 | ||||
-rw-r--r-- | qemu-config.h | 1 | ||||
-rw-r--r-- | qemu-options.hx | 21 | ||||
-rw-r--r-- | sysemu.h | 1 | ||||
-rw-r--r-- | ui/qemu-spice.h | 41 | ||||
-rw-r--r-- | ui/spice-core.c | 189 | ||||
-rw-r--r-- | ui/spice-display.c | 412 | ||||
-rw-r--r-- | ui/spice-display.h | 69 | ||||
-rw-r--r-- | ui/spice-input.c | 217 | ||||
-rw-r--r-- | vl.c | 50 |
15 files changed, 1287 insertions, 19 deletions
diff --git a/Makefile.objs b/Makefile.objs index 9c13bb3..816194a 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -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 @@ -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 diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c index ad69b0e..280b8f5 100644 --- a/fsdev/qemu-fsdev.c +++ b/fsdev/qemu-fsdev.c @@ -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); + @@ -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); + } +} @@ -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 diff --git a/qemu-config.c b/qemu-config.c index 6052a28..32917cb 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -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, diff --git a/qemu-config.h b/qemu-config.h index 533a049..20d707f 100644 --- a/qemu-config.h +++ b/qemu-config.h @@ -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); diff --git a/qemu-options.hx b/qemu-options.hx index a0b5ae9..718d47a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -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) @@ -94,7 +94,6 @@ typedef enum DisplayType DT_DEFAULT, DT_CURSES, DT_SDL, - DT_VNC, DT_NOGRAPHIC, } DisplayType; diff --git a/ui/qemu-spice.h b/ui/qemu-spice.h new file mode 100644 index 0000000..063c7dc --- /dev/null +++ b/ui/qemu-spice.h @@ -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 */ diff --git a/ui/spice-core.c b/ui/spice-core.c new file mode 100644 index 0000000..8b5e4a8 --- /dev/null +++ b/ui/spice-core.c @@ -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); diff --git a/ui/spice-display.c b/ui/spice-display.c new file mode 100644 index 0000000..6702dfd --- /dev/null +++ b/ui/spice-display.c @@ -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); +} diff --git a/ui/spice-display.h b/ui/spice-display.h new file mode 100644 index 0000000..e17671c --- /dev/null +++ b/ui/spice-display.h @@ -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); diff --git a/ui/spice-input.c b/ui/spice-input.c new file mode 100644 index 0000000..37c8578 --- /dev/null +++ b/ui/spice-input.c @@ -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); +} @@ -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) { |