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 += qdev.o qdev-properties.o
|
||||
common-obj-y += block-migration.o
|
||||
common-obj-y += pflib.o
|
||||
|
||||
common-obj-$(CONFIG_BRLAPI) += baum.o
|
||||
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
|
||||
common-obj-$(CONFIG_WIN32) += version.o
|
||||
|
||||
common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o
|
||||
|
||||
audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
|
||||
audio-obj-$(CONFIG_SDL) += sdlaudio.o
|
||||
audio-obj-$(CONFIG_OSS) += ossaudio.o
|
||||
|
42
configure
vendored
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;
|
||||
# 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
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "qemu-queue.h"
|
||||
#include "osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-config.h"
|
||||
|
||||
static QTAILQ_HEAD(FsTypeEntry_head, FsTypeListEntry) fstype_entries =
|
||||
QTAILQ_HEAD_INITIALIZER(fstype_entries);
|
||||
@ -75,3 +76,11 @@ FsTypeEntry *get_fsdev_fsentry(char *id)
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void fsdev_register_config(void)
|
||||
{
|
||||
qemu_add_opts(&qemu_fsdev_opts);
|
||||
qemu_add_opts(&qemu_virtfs_opts);
|
||||
}
|
||||
machine_init(fsdev_register_config);
|
||||
|
||||
|
213
pflib.c
Normal file
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] = {
|
||||
&qemu_drive_opts,
|
||||
&qemu_chardev_opts,
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
1
sysemu.h
1
sysemu.h
@ -94,7 +94,6 @@ typedef enum DisplayType
|
||||
DT_DEFAULT,
|
||||
DT_CURSES,
|
||||
DT_SDL,
|
||||
DT_VNC,
|
||||
DT_NOGRAPHIC,
|
||||
} DisplayType;
|
||||
|
||||
|
41
ui/qemu-spice.h
Normal file
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 "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) {
|
||||
|
Loading…
Reference in New Issue
Block a user