mirror of
https://git.proxmox.com/git/qemu
synced 2025-08-07 14:59:10 +00:00
Merge remote branch 'spice/submit.6' into staging
Conflicts: configure Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
commit
4447d60968
@ -84,11 +84,14 @@ common-obj-y += qemu-char.o savevm.o #aio.o
|
|||||||
common-obj-y += msmouse.o ps2.o
|
common-obj-y += msmouse.o ps2.o
|
||||||
common-obj-y += qdev.o qdev-properties.o
|
common-obj-y += qdev.o qdev-properties.o
|
||||||
common-obj-y += block-migration.o
|
common-obj-y += block-migration.o
|
||||||
|
common-obj-y += pflib.o
|
||||||
|
|
||||||
common-obj-$(CONFIG_BRLAPI) += baum.o
|
common-obj-$(CONFIG_BRLAPI) += baum.o
|
||||||
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
|
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
|
||||||
common-obj-$(CONFIG_WIN32) += version.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-y = audio.o noaudio.o wavaudio.o mixeng.o
|
||||||
audio-obj-$(CONFIG_SDL) += sdlaudio.o
|
audio-obj-$(CONFIG_SDL) += sdlaudio.o
|
||||||
audio-obj-$(CONFIG_OSS) += ossaudio.o
|
audio-obj-$(CONFIG_OSS) += ossaudio.o
|
||||||
|
42
configure
vendored
42
configure
vendored
@ -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;
|
# 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>
|
# see <1285349658-3122-1-git-send-email-loic.minier@linaro.org>
|
||||||
trap "rm -f $TMPC $TMPO $TMPE" EXIT INT QUIT TERM
|
trap "rm -f $TMPC $TMPO $TMPE" EXIT INT QUIT TERM
|
||||||
|
rm -f config.log
|
||||||
|
|
||||||
compile_object() {
|
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() {
|
compile_prog() {
|
||||||
local_cflags="$1"
|
local_cflags="$1"
|
||||||
local_ldflags="$2"
|
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
|
# check whether a command is available to this shell (may be either an
|
||||||
@ -327,6 +330,7 @@ user_pie="no"
|
|||||||
zero_malloc=""
|
zero_malloc=""
|
||||||
trace_backend="nop"
|
trace_backend="nop"
|
||||||
trace_file="trace"
|
trace_file="trace"
|
||||||
|
spice=""
|
||||||
|
|
||||||
# OS specific
|
# OS specific
|
||||||
if check_define __linux__ ; then
|
if check_define __linux__ ; then
|
||||||
@ -639,6 +643,10 @@ for opt do
|
|||||||
;;
|
;;
|
||||||
--enable-kvm) kvm="yes"
|
--enable-kvm) kvm="yes"
|
||||||
;;
|
;;
|
||||||
|
--disable-spice) spice="no"
|
||||||
|
;;
|
||||||
|
--enable-spice) spice="yes"
|
||||||
|
;;
|
||||||
--enable-profiler) profiler="yes"
|
--enable-profiler) profiler="yes"
|
||||||
;;
|
;;
|
||||||
--enable-cocoa)
|
--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-backend=B Trace backend nop simple ust"
|
||||||
echo " --trace-file=NAME Full PATH,NAME of file to store traces"
|
echo " --trace-file=NAME Full PATH,NAME of file to store traces"
|
||||||
echo " Default:trace-<pid>"
|
echo " Default:trace-<pid>"
|
||||||
|
echo " --disable-spice disable spice"
|
||||||
|
echo " --enable-spice enable spice"
|
||||||
echo ""
|
echo ""
|
||||||
echo "NOTE: The object files are built at the place where configure is launched"
|
echo "NOTE: The object files are built at the place where configure is launched"
|
||||||
exit 1
|
exit 1
|
||||||
@ -2075,6 +2085,29 @@ if compile_prog "" ""; then
|
|||||||
gcc_attribute_warn_unused_result=yes
|
gcc_attribute_warn_unused_result=yes
|
||||||
fi
|
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
|
# check if we have fdatasync
|
||||||
|
|
||||||
@ -2285,6 +2318,7 @@ echo "uuid support $uuid"
|
|||||||
echo "vhost-net support $vhost_net"
|
echo "vhost-net support $vhost_net"
|
||||||
echo "Trace backend $trace_backend"
|
echo "Trace backend $trace_backend"
|
||||||
echo "Trace output file $trace_file-<pid>"
|
echo "Trace output file $trace_file-<pid>"
|
||||||
|
echo "spice support $spice"
|
||||||
|
|
||||||
if test $sdl_too_old = "yes"; then
|
if test $sdl_too_old = "yes"; then
|
||||||
echo "-> Your SDL version is too old - please upgrade to have SDL support"
|
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
|
echo "CONFIG_POSIX_MADVISE=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "$spice" = "yes" ; then
|
||||||
|
echo "CONFIG_SPICE=y" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
|
||||||
# XXX: suppress that
|
# XXX: suppress that
|
||||||
if [ "$bsd" = "yes" ] ; then
|
if [ "$bsd" = "yes" ] ; then
|
||||||
echo "CONFIG_BSD=y" >> $config_host_mak
|
echo "CONFIG_BSD=y" >> $config_host_mak
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "qemu-queue.h"
|
#include "qemu-queue.h"
|
||||||
#include "osdep.h"
|
#include "osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
|
#include "qemu-config.h"
|
||||||
|
|
||||||
static QTAILQ_HEAD(FsTypeEntry_head, FsTypeListEntry) fstype_entries =
|
static QTAILQ_HEAD(FsTypeEntry_head, FsTypeListEntry) fstype_entries =
|
||||||
QTAILQ_HEAD_INITIALIZER(fstype_entries);
|
QTAILQ_HEAD_INITIALIZER(fstype_entries);
|
||||||
@ -75,3 +76,11 @@ FsTypeEntry *get_fsdev_fsentry(char *id)
|
|||||||
}
|
}
|
||||||
return NULL;
|
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
213
pflib.c
Normal 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
20
pflib.h
Normal 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
|
@ -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] = {
|
static QemuOptsList *vm_config_groups[32] = {
|
||||||
&qemu_drive_opts,
|
&qemu_drive_opts,
|
||||||
&qemu_chardev_opts,
|
&qemu_chardev_opts,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
extern QemuOptsList qemu_fsdev_opts;
|
extern QemuOptsList qemu_fsdev_opts;
|
||||||
extern QemuOptsList qemu_virtfs_opts;
|
extern QemuOptsList qemu_virtfs_opts;
|
||||||
|
extern QemuOptsList qemu_spice_opts;
|
||||||
|
|
||||||
QemuOptsList *qemu_find_opts(const char *group);
|
QemuOptsList *qemu_find_opts(const char *group);
|
||||||
void qemu_add_opts(QemuOptsList *list);
|
void qemu_add_opts(QemuOptsList *list);
|
||||||
|
@ -670,6 +670,27 @@ STEXI
|
|||||||
Enable SDL.
|
Enable SDL.
|
||||||
ETEXI
|
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,
|
DEF("portrait", 0, QEMU_OPTION_portrait,
|
||||||
"-portrait rotate graphical output 90 deg left (only PXA LCD)\n",
|
"-portrait rotate graphical output 90 deg left (only PXA LCD)\n",
|
||||||
QEMU_ARCH_ALL)
|
QEMU_ARCH_ALL)
|
||||||
|
1
sysemu.h
1
sysemu.h
@ -94,7 +94,6 @@ typedef enum DisplayType
|
|||||||
DT_DEFAULT,
|
DT_DEFAULT,
|
||||||
DT_CURSES,
|
DT_CURSES,
|
||||||
DT_SDL,
|
DT_SDL,
|
||||||
DT_VNC,
|
|
||||||
DT_NOGRAPHIC,
|
DT_NOGRAPHIC,
|
||||||
} DisplayType;
|
} DisplayType;
|
||||||
|
|
||||||
|
41
ui/qemu-spice.h
Normal file
41
ui/qemu-spice.h
Normal 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
189
ui/spice-core.c
Normal 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
412
ui/spice-display.c
Normal 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
69
ui/spice-display.h
Normal 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
217
ui/spice-input.c
Normal 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
50
vl.c
@ -162,6 +162,8 @@ int main(int argc, char **argv)
|
|||||||
#include "cpus.h"
|
#include "cpus.h"
|
||||||
#include "arch_init.h"
|
#include "arch_init.h"
|
||||||
|
|
||||||
|
#include "ui/qemu-spice.h"
|
||||||
|
|
||||||
//#define DEBUG_NET
|
//#define DEBUG_NET
|
||||||
//#define DEBUG_SLIRP
|
//#define DEBUG_SLIRP
|
||||||
|
|
||||||
@ -173,6 +175,7 @@ static const char *data_dir;
|
|||||||
const char *bios_name = NULL;
|
const char *bios_name = NULL;
|
||||||
enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB;
|
enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB;
|
||||||
DisplayType display_type = DT_DEFAULT;
|
DisplayType display_type = DT_DEFAULT;
|
||||||
|
int display_remote = 0;
|
||||||
const char* keyboard_layout = NULL;
|
const char* keyboard_layout = NULL;
|
||||||
ram_addr_t ram_size;
|
ram_addr_t ram_size;
|
||||||
const char *mem_path = NULL;
|
const char *mem_path = NULL;
|
||||||
@ -1862,11 +1865,6 @@ int main(int argc, char **argv, char **envp)
|
|||||||
tb_size = 0;
|
tb_size = 0;
|
||||||
autostart= 1;
|
autostart= 1;
|
||||||
|
|
||||||
#ifdef CONFIG_VIRTFS
|
|
||||||
qemu_add_opts(&qemu_fsdev_opts);
|
|
||||||
qemu_add_opts(&qemu_virtfs_opts);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* first pass of option parsing */
|
/* first pass of option parsing */
|
||||||
optind = 1;
|
optind = 1;
|
||||||
while (optind < argc) {
|
while (optind < argc) {
|
||||||
@ -2477,7 +2475,7 @@ int main(int argc, char **argv, char **envp)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QEMU_OPTION_vnc:
|
case QEMU_OPTION_vnc:
|
||||||
display_type = DT_VNC;
|
display_remote++;
|
||||||
vnc_display = optarg;
|
vnc_display = optarg;
|
||||||
break;
|
break;
|
||||||
case QEMU_OPTION_no_acpi:
|
case QEMU_OPTION_no_acpi:
|
||||||
@ -2620,6 +2618,18 @@ int main(int argc, char **argv, char **envp)
|
|||||||
}
|
}
|
||||||
break;
|
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:
|
case QEMU_OPTION_writeconfig:
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
@ -2921,17 +2931,19 @@ int main(int argc, char **argv, char **envp)
|
|||||||
/* just use the first displaystate for the moment */
|
/* just use the first displaystate for the moment */
|
||||||
ds = get_displaystate();
|
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)
|
#if defined(CONFIG_SDL) || defined(CONFIG_COCOA)
|
||||||
display_type = DT_SDL;
|
display_type = DT_SDL;
|
||||||
#else
|
#else
|
||||||
display_type = DT_VNC;
|
|
||||||
vnc_display = "localhost:0,to=99";
|
vnc_display = "localhost:0,to=99";
|
||||||
show_vnc_port = 1;
|
show_vnc_port = 1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* init local displays */
|
||||||
switch (display_type) {
|
switch (display_type) {
|
||||||
case DT_NOGRAPHIC:
|
case DT_NOGRAPHIC:
|
||||||
break;
|
break;
|
||||||
@ -2949,7 +2961,12 @@ int main(int argc, char **argv, char **envp)
|
|||||||
cocoa_display_init(ds, full_screen);
|
cocoa_display_init(ds, full_screen);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case DT_VNC:
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* init remote displays */
|
||||||
|
if (vnc_display) {
|
||||||
vnc_display_init(ds);
|
vnc_display_init(ds);
|
||||||
if (vnc_display_open(ds, vnc_display) < 0)
|
if (vnc_display_open(ds, vnc_display) < 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -2957,12 +2974,15 @@ int main(int argc, char **argv, char **envp)
|
|||||||
if (show_vnc_port) {
|
if (show_vnc_port) {
|
||||||
printf("VNC server running on `%s'\n", vnc_display_local_addr(ds));
|
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;
|
dcl = ds->listeners;
|
||||||
while (dcl != NULL) {
|
while (dcl != NULL) {
|
||||||
if (dcl->dpy_refresh != NULL) {
|
if (dcl->dpy_refresh != NULL) {
|
||||||
@ -2972,12 +2992,10 @@ int main(int argc, char **argv, char **envp)
|
|||||||
}
|
}
|
||||||
dcl = dcl->next;
|
dcl = dcl->next;
|
||||||
}
|
}
|
||||||
|
if (ds->gui_timer == NULL) {
|
||||||
if (display_type == DT_NOGRAPHIC || display_type == DT_VNC) {
|
|
||||||
nographic_timer = qemu_new_timer(rt_clock, nographic_update, NULL);
|
nographic_timer = qemu_new_timer(rt_clock, nographic_update, NULL);
|
||||||
qemu_mod_timer(nographic_timer, qemu_get_clock(rt_clock));
|
qemu_mod_timer(nographic_timer, qemu_get_clock(rt_clock));
|
||||||
}
|
}
|
||||||
|
|
||||||
text_consoles_set_display(ds);
|
text_consoles_set_display(ds);
|
||||||
|
|
||||||
if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) {
|
if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) {
|
||||||
|
Loading…
Reference in New Issue
Block a user