ui: add support for -display spice-app
ui: gtk+sdl bugfixes. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJcb6ppAAoJEEy22O7T6HE4aeAQALeT8KprIaGqAqem/8xQftGE UfZxGIVC9GfVaMG91nPxfCxAEDe9Iyndpmau/l27aA7QrqY+lGsD2Zk1gykCHTd8 1ElRcn2LXQduFdlTA+r6ibbRkx4LMHCo7KVTvjx+5+SETV/FyDteNWKMKj0K58R8 f8pkyffOG94LbxukEtmN+IJwtK7xpQhUDGCRNSeBxgFejJVlgquGjII6UcFIBHUT xDLgrpfKsVGYCUymIviTGhRC9Ep1fsgtHsv6IHRB+zbUHGncFAjLkBPmCYEc0NFX xQNBNQwN12IGbD/BPUyjt/J3BXNUUeBckdnbcLNoA0J+6CgXL6QLaA3RGMidiIwf TzY+/464CG/t9YIW6Voh8xyQeqKRiKARkfHuR68avCzuXCMrsOJKlmnszHqmtXTO +nVk9vjzCH8LUA0tv8fGrdbu2Ai5Jr0GVknex3eiOPoJSCTIBFyzDO8vOqaBYu8d zxBdVXZOpvn4XpoSwtcO9pY+HozeThA3Kv4bTTvnJBwBwjM4vUWHXhnZCFuy+MjU tKeAna7nPfztvUQFBD0sBZ0Lj5q5GKQdLwDzTtD4coNoJfaZGRrjpHG/v8uR/bs3 p3lTO2O9JWj2AMXpEuK7py3IuyNMLV0uFz6rZSKuw+67URdtRb8+/aM34ows892x UqlZxU0nUnfVCclaVo6t =tArC -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kraxel/tags/ui-20190222-pull-request' into staging ui: add support for -display spice-app ui: gtk+sdl bugfixes. # gpg: Signature made Fri 22 Feb 2019 07:53:13 GMT # gpg: using RSA key 4CB6D8EED3E87138 # gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full] # gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" [full] # gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full] # Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138 * remotes/kraxel/tags/ui-20190222-pull-request: display: add -display spice-app launching a Spice client spice: use a default name for the server qapi: document DisplayType enum build-sys: add gio-2.0 check char: register spice ports after spice started char: move SpiceChardev and open_spice_port() to spice.h header spice: do not stop spice if VM is paused spice: merge options lists spice: avoid spice runtime assert char/spice: discard write() if backend is disconnected char/spice: trigger HUP event ui/gtk: Fix the license information sdl2: drop qemu_input_event_send_key_qcode call spice: set device address and device display ID in QXL interface kbd-state: don't block auto-repeat events Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
8a4c08b161
@ -2,30 +2,12 @@
|
||||
#include "trace.h"
|
||||
#include "ui/qemu-spice.h"
|
||||
#include "chardev/char.h"
|
||||
#include "chardev/spice.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/option.h"
|
||||
#include <spice.h>
|
||||
#include <spice/protocol.h>
|
||||
|
||||
|
||||
typedef struct SpiceChardev {
|
||||
Chardev parent;
|
||||
|
||||
SpiceCharDeviceInstance sin;
|
||||
bool active;
|
||||
bool blocked;
|
||||
const uint8_t *datapos;
|
||||
int datalen;
|
||||
QLIST_ENTRY(SpiceChardev) next;
|
||||
} SpiceChardev;
|
||||
|
||||
#define TYPE_CHARDEV_SPICE "chardev-spice"
|
||||
#define TYPE_CHARDEV_SPICEVMC "chardev-spicevmc"
|
||||
#define TYPE_CHARDEV_SPICEPORT "chardev-spiceport"
|
||||
|
||||
#define SPICE_CHARDEV(obj) OBJECT_CHECK(SpiceChardev, (obj), TYPE_CHARDEV_SPICE)
|
||||
|
||||
typedef struct SpiceCharSource {
|
||||
GSource source;
|
||||
SpiceChardev *scd;
|
||||
@ -148,15 +130,25 @@ static void vmc_unregister_interface(SpiceChardev *scd)
|
||||
static gboolean spice_char_source_prepare(GSource *source, gint *timeout)
|
||||
{
|
||||
SpiceCharSource *src = (SpiceCharSource *)source;
|
||||
Chardev *chr = CHARDEV(src->scd);
|
||||
|
||||
*timeout = -1;
|
||||
|
||||
if (!chr->be_open) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !src->scd->blocked;
|
||||
}
|
||||
|
||||
static gboolean spice_char_source_check(GSource *source)
|
||||
{
|
||||
SpiceCharSource *src = (SpiceCharSource *)source;
|
||||
Chardev *chr = CHARDEV(src->scd);
|
||||
|
||||
if (!chr->be_open) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !src->scd->blocked;
|
||||
}
|
||||
@ -164,9 +156,12 @@ static gboolean spice_char_source_check(GSource *source)
|
||||
static gboolean spice_char_source_dispatch(GSource *source,
|
||||
GSourceFunc callback, gpointer user_data)
|
||||
{
|
||||
SpiceCharSource *src = (SpiceCharSource *)source;
|
||||
Chardev *chr = CHARDEV(src->scd);
|
||||
GIOFunc func = (GIOFunc)callback;
|
||||
GIOCondition cond = chr->be_open ? G_IO_OUT : G_IO_HUP;
|
||||
|
||||
return func(NULL, G_IO_OUT, user_data);
|
||||
return func(NULL, cond, user_data);
|
||||
}
|
||||
|
||||
static GSourceFuncs SpiceCharSourceFuncs = {
|
||||
@ -195,6 +190,12 @@ static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
int read_bytes;
|
||||
|
||||
assert(s->datalen == 0);
|
||||
|
||||
if (!chr->be_open) {
|
||||
trace_spice_chr_discard_write(len);
|
||||
return len;
|
||||
}
|
||||
|
||||
s->datapos = buf;
|
||||
s->datalen = len;
|
||||
spice_server_char_device_wakeup(&s->sin);
|
||||
@ -287,13 +288,19 @@ static void qemu_chr_open_spice_vmc(Chardev *chr,
|
||||
}
|
||||
|
||||
*be_opened = false;
|
||||
#if SPICE_SERVER_VERSION < 0x000e02
|
||||
/* Spice < 0.14.2 doesn't explicitly open smartcard chardev */
|
||||
if (strcmp(type, "smartcard") == 0) {
|
||||
*be_opened = true;
|
||||
}
|
||||
#endif
|
||||
chr_open(chr, type);
|
||||
}
|
||||
|
||||
static void qemu_chr_open_spice_port(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
void qemu_chr_open_spice_port(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevSpicePort *spiceport = backend->u.spiceport.data;
|
||||
const char *name = spiceport->fqdn;
|
||||
@ -309,6 +316,11 @@ static void qemu_chr_open_spice_port(Chardev *chr,
|
||||
*be_opened = false;
|
||||
s = SPICE_CHARDEV(chr);
|
||||
s->sin.portname = g_strdup(name);
|
||||
|
||||
if (using_spice) {
|
||||
/* spice server already created */
|
||||
vmc_register_interface(s);
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_spice_register_ports(void)
|
||||
|
@ -10,6 +10,7 @@ wct_cmd_other(const char *cmd) "%s"
|
||||
wct_speed(int speed) "%d"
|
||||
|
||||
# chardev/spice.c
|
||||
spice_chr_discard_write(int len) "spice chr write discarded %d"
|
||||
spice_vmc_write(ssize_t out, int len) "spice wrote %zd of requested %d"
|
||||
spice_vmc_read(int bytes, int len) "spice read %d of requested %d"
|
||||
spice_vmc_register_interface(void *scd) "spice vmc registered interface %p"
|
||||
|
13
configure
vendored
13
configure
vendored
@ -3503,6 +3503,14 @@ for i in $glib_modules; do
|
||||
fi
|
||||
done
|
||||
|
||||
if $pkg_config --atleast-version=$glib_req_ver gio-2.0; then
|
||||
gio=yes
|
||||
gio_cflags=$($pkg_config --cflags gio-2.0)
|
||||
gio_libs=$($pkg_config --libs gio-2.0)
|
||||
else
|
||||
gio=no
|
||||
fi
|
||||
|
||||
# Sanity check that the current size_t matches the
|
||||
# size that glib thinks it should be. This catches
|
||||
# problems on multi-arch where people try to build
|
||||
@ -6520,6 +6528,11 @@ if test "$gtk" = "yes" ; then
|
||||
echo "CONFIG_GTK_GL=y" >> $config_host_mak
|
||||
fi
|
||||
fi
|
||||
if test "$gio" = "yes" ; then
|
||||
echo "CONFIG_GIO=y" >> $config_host_mak
|
||||
echo "GIO_CFLAGS=$gio_cflags" >> $config_host_mak
|
||||
echo "GIO_LIBS=$gio_libs" >> $config_host_mak
|
||||
fi
|
||||
echo "CONFIG_TLS_PRIORITY=\"$tls_priority\"" >> $config_host_mak
|
||||
if test "$gnutls" = "yes" ; then
|
||||
echo "CONFIG_GNUTLS=y" >> $config_host_mak
|
||||
|
@ -276,7 +276,8 @@ static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay)
|
||||
QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG,
|
||||
0));
|
||||
} else {
|
||||
#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
|
||||
/* >= release 0.12.6, < release 0.14.2 */
|
||||
#if SPICE_SERVER_VERSION >= 0x000c06 && SPICE_SERVER_VERSION < 0x000e02
|
||||
if (qxl->max_outputs) {
|
||||
spice_qxl_set_max_monitors(&qxl->ssd.qxl, qxl->max_outputs);
|
||||
}
|
||||
@ -2188,6 +2189,17 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp)
|
||||
SPICE_INTERFACE_QXL_MAJOR, SPICE_INTERFACE_QXL_MINOR);
|
||||
return;
|
||||
}
|
||||
|
||||
#if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */
|
||||
char device_address[256] = "";
|
||||
if (qemu_spice_fill_device_address(qxl->vga.con, device_address, 256)) {
|
||||
spice_qxl_set_device_info(&qxl->ssd.qxl,
|
||||
device_address,
|
||||
0,
|
||||
qxl->max_outputs);
|
||||
}
|
||||
#endif
|
||||
|
||||
qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl);
|
||||
|
||||
qxl->update_irq = qemu_bh_new(qxl_update_irq_bh, qxl);
|
||||
|
27
include/chardev/spice.h
Normal file
27
include/chardev/spice.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef CHARDEV_SPICE_H_
|
||||
#define CHARDEV_SPICE_H_
|
||||
|
||||
#include <spice.h>
|
||||
#include "chardev/char-fe.h"
|
||||
|
||||
typedef struct SpiceChardev {
|
||||
Chardev parent;
|
||||
|
||||
SpiceCharDeviceInstance sin;
|
||||
bool active;
|
||||
bool blocked;
|
||||
const uint8_t *datapos;
|
||||
int datalen;
|
||||
QLIST_ENTRY(SpiceChardev) next;
|
||||
} SpiceChardev;
|
||||
|
||||
#define TYPE_CHARDEV_SPICE "chardev-spice"
|
||||
#define TYPE_CHARDEV_SPICEVMC "chardev-spicevmc"
|
||||
#define TYPE_CHARDEV_SPICEPORT "chardev-spiceport"
|
||||
|
||||
#define SPICE_CHARDEV(obj) OBJECT_CHECK(SpiceChardev, (obj), TYPE_CHARDEV_SPICE)
|
||||
|
||||
void qemu_chr_open_spice_port(Chardev *chr, ChardevBackend *backend,
|
||||
bool *be_opened, Error **errp);
|
||||
|
||||
#endif
|
@ -179,3 +179,7 @@ void qemu_spice_wakeup(SimpleSpiceDisplay *ssd);
|
||||
void qemu_spice_display_start(void);
|
||||
void qemu_spice_display_stop(void);
|
||||
int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd);
|
||||
|
||||
bool qemu_spice_fill_device_address(QemuConsole *con,
|
||||
char *device_address,
|
||||
size_t size);
|
||||
|
32
qapi/ui.json
32
qapi/ui.json
@ -1085,12 +1085,42 @@
|
||||
#
|
||||
# Display (user interface) type.
|
||||
#
|
||||
# @default: The default user interface, selecting from the first available
|
||||
# of gtk, sdl, cocoa, and vnc.
|
||||
#
|
||||
# @none: No user interface or video output display. The guest will
|
||||
# still see an emulated graphics card, but its output will not
|
||||
# be displayed to the QEMU user.
|
||||
#
|
||||
# @gtk: The GTK user interface.
|
||||
#
|
||||
# @sdl: The SDL user interface.
|
||||
#
|
||||
# @egl-headless: No user interface, offload GL operations to a local
|
||||
# DRI device. Graphical display need to be paired with
|
||||
# VNC or Spice. (Since 3.1)
|
||||
#
|
||||
# @curses: Display video output via curses. For graphics device
|
||||
# models which support a text mode, QEMU can display this
|
||||
# output using a curses/ncurses interface. Nothing is
|
||||
# displayed when the graphics device is in graphical mode or
|
||||
# if the graphics device does not support a text
|
||||
# mode. Generally only the VGA device models support text
|
||||
# mode.
|
||||
#
|
||||
# @cocoa: The Cocoa user interface.
|
||||
#
|
||||
# @spice-app: Set up a Spice server and run the default associated
|
||||
# application to connect to it. The server will redirect
|
||||
# the serial console and QEMU monitors. (Since 4.0)
|
||||
#
|
||||
# Since: 2.12
|
||||
#
|
||||
##
|
||||
{ 'enum' : 'DisplayType',
|
||||
'data' : [ 'default', 'none', 'gtk', 'sdl',
|
||||
'egl-headless', 'curses', 'cocoa' ] }
|
||||
'egl-headless', 'curses', 'cocoa',
|
||||
'spice-app'] }
|
||||
|
||||
##
|
||||
# @DisplayOptions:
|
||||
|
@ -1211,6 +1211,7 @@ STEXI
|
||||
ETEXI
|
||||
|
||||
DEF("display", HAS_ARG, QEMU_OPTION_display,
|
||||
"-display spice-app[,gl=on|off]\n"
|
||||
"-display sdl[,frame=on|off][,alt_grab=on|off][,ctrl_grab=on|off]\n"
|
||||
" [,window_close=on|off][,gl=on|core|es|off]\n"
|
||||
"-display gtk[,grab_on_hover=on|off][,gl=on|off]|\n"
|
||||
@ -1262,6 +1263,10 @@ Start a VNC server on display <arg>
|
||||
@item egl-headless
|
||||
Offload all OpenGL operations to a local DRI device. For any graphical display,
|
||||
this display needs to be paired with either VNC or SPICE displays.
|
||||
@item spice-app
|
||||
Start QEMU as a Spice server and launch the default Spice client
|
||||
application. The Spice server will redirect the serial consoles and
|
||||
QEMU monitors. (Since 4.0)
|
||||
@end table
|
||||
ETEXI
|
||||
|
||||
|
@ -49,6 +49,11 @@ curses.mo-objs := curses.o
|
||||
curses.mo-cflags := $(CURSES_CFLAGS)
|
||||
curses.mo-libs := $(CURSES_LIBS)
|
||||
|
||||
common-obj-$(call land,$(CONFIG_SPICE),$(CONFIG_GIO)) += spice-app.mo
|
||||
spice-app.mo-objs := spice-app.o
|
||||
spice-app.mo-cflags := $(GIO_CFLAGS)
|
||||
spice-app.mo-libs := $(GIO_LIBS)
|
||||
|
||||
common-obj-$(CONFIG_OPENGL) += shader.o
|
||||
common-obj-$(CONFIG_OPENGL) += console-gl.o
|
||||
common-obj-$(CONFIG_OPENGL) += egl-helpers.o
|
||||
|
30
ui/gtk.c
30
ui/gtk.c
@ -6,29 +6,25 @@
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
* 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 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Portions from gtk-vnc:
|
||||
* 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/>.
|
||||
*
|
||||
* Portions from gtk-vnc (originally licensed under the LGPL v2+):
|
||||
*
|
||||
* GTK VNC Widget
|
||||
*
|
||||
* Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
|
||||
* Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define GETTEXT_PACKAGE "qemu"
|
||||
|
@ -42,14 +42,18 @@ void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down)
|
||||
{
|
||||
bool state = test_bit(qcode, kbd->keys);
|
||||
|
||||
if (state == down) {
|
||||
if (down == false /* got key-up event */ &&
|
||||
state == false /* key is not pressed */) {
|
||||
/*
|
||||
* Filter out events which don't change the keyboard state.
|
||||
* Filter out suspicious key-up events.
|
||||
*
|
||||
* Most notably this allows to simply send along all key-up
|
||||
* events, and this function will filter out everything where
|
||||
* the corresponding key-down event wasn't send to the guest,
|
||||
* for example due to being a host hotkey.
|
||||
* This allows simply sending along all key-up events, and
|
||||
* this function will filter out everything where the
|
||||
* corresponding key-down event wasn't sent to the guest, for
|
||||
* example due to being a host hotkey.
|
||||
*
|
||||
* Note that key-down events on already pressed keys are *not*
|
||||
* suspicious, those are keyboard autorepeat events.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
@ -54,8 +54,5 @@ void sdl2_process_key(struct sdl2_console *scon,
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qemu_input_event_send_key_qcode(con, qcode,
|
||||
ev->type == SDL_KEYDOWN);
|
||||
}
|
||||
}
|
||||
|
202
ui/spice-app.c
Normal file
202
ui/spice-app.c
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* QEMU external Spice client display driver
|
||||
*
|
||||
* Copyright (c) 2018 Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "ui/console.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
#include "io/channel-command.h"
|
||||
#include "chardev/spice.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
static const char *tmp_dir;
|
||||
static char *app_dir;
|
||||
static char *sock_path;
|
||||
|
||||
typedef struct VCChardev {
|
||||
SpiceChardev parent;
|
||||
} VCChardev;
|
||||
|
||||
#define TYPE_CHARDEV_VC "chardev-vc"
|
||||
#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
|
||||
|
||||
static ChardevBackend *
|
||||
chr_spice_backend_new(void)
|
||||
{
|
||||
ChardevBackend *be = g_new0(ChardevBackend, 1);
|
||||
|
||||
be->type = CHARDEV_BACKEND_KIND_SPICEPORT;
|
||||
be->u.spiceport.data = g_new0(ChardevSpicePort, 1);
|
||||
|
||||
return be;
|
||||
}
|
||||
|
||||
static void vc_chr_open(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevBackend *be;
|
||||
const char *fqdn = NULL;
|
||||
|
||||
if (strstart(chr->label, "serial", NULL)) {
|
||||
fqdn = "org.qemu.console.serial.0";
|
||||
} else if (strstart(chr->label, "parallel", NULL)) {
|
||||
fqdn = "org.qemu.console.parallel.0";
|
||||
} else if (strstart(chr->label, "compat_monitor", NULL)) {
|
||||
fqdn = "org.qemu.monitor.hmp.0";
|
||||
}
|
||||
|
||||
be = chr_spice_backend_new();
|
||||
be->u.spiceport.data->fqdn = fqdn ?
|
||||
g_strdup(fqdn) : g_strdup_printf("org.qemu.console.%s", chr->label);
|
||||
qemu_chr_open_spice_port(chr, be, be_opened, errp);
|
||||
qapi_free_ChardevBackend(be);
|
||||
}
|
||||
|
||||
static void vc_chr_set_echo(Chardev *chr, bool echo)
|
||||
{
|
||||
/* TODO: set echo for frontends QMP and qtest */
|
||||
}
|
||||
|
||||
static void char_vc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_vc;
|
||||
cc->open = vc_chr_open;
|
||||
cc->chr_set_echo = vc_chr_set_echo;
|
||||
}
|
||||
|
||||
static const TypeInfo char_vc_type_info = {
|
||||
.name = TYPE_CHARDEV_VC,
|
||||
.parent = TYPE_CHARDEV_SPICEPORT,
|
||||
.instance_size = sizeof(VCChardev),
|
||||
.class_init = char_vc_class_init,
|
||||
};
|
||||
|
||||
static void spice_app_atexit(void)
|
||||
{
|
||||
if (sock_path) {
|
||||
unlink(sock_path);
|
||||
}
|
||||
if (tmp_dir) {
|
||||
rmdir(tmp_dir);
|
||||
}
|
||||
g_free(sock_path);
|
||||
g_free(app_dir);
|
||||
}
|
||||
|
||||
static void spice_app_display_early_init(DisplayOptions *opts)
|
||||
{
|
||||
QemuOpts *qopts;
|
||||
ChardevBackend *be = chr_spice_backend_new();
|
||||
GError *err = NULL;
|
||||
|
||||
if (opts->has_full_screen) {
|
||||
error_report("spice-app full-screen isn't supported yet.");
|
||||
exit(1);
|
||||
}
|
||||
if (opts->has_window_close) {
|
||||
error_report("spice-app window-close isn't supported yet.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
atexit(spice_app_atexit);
|
||||
|
||||
if (qemu_name) {
|
||||
app_dir = g_build_filename(g_get_user_runtime_dir(),
|
||||
"qemu", qemu_name, NULL);
|
||||
if (g_mkdir_with_parents(app_dir, S_IRWXU) < -1) {
|
||||
error_report("Failed to create directory %s: %s",
|
||||
app_dir, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
app_dir = g_dir_make_tmp(NULL, &err);
|
||||
tmp_dir = app_dir;
|
||||
if (err) {
|
||||
error_report("Failed to create temporary directory: %s",
|
||||
err->message);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
type_register(&char_vc_type_info);
|
||||
|
||||
sock_path = g_strjoin("", app_dir, "/", "spice.sock", NULL);
|
||||
qopts = qemu_opts_create(qemu_find_opts("spice"), NULL, 0, &error_abort);
|
||||
qemu_opt_set(qopts, "disable-ticketing", "on", &error_abort);
|
||||
qemu_opt_set(qopts, "unix", "on", &error_abort);
|
||||
qemu_opt_set(qopts, "addr", sock_path, &error_abort);
|
||||
qemu_opt_set(qopts, "image-compression", "off", &error_abort);
|
||||
qemu_opt_set(qopts, "streaming-video", "off", &error_abort);
|
||||
qemu_opt_set(qopts, "gl", opts->has_gl ? "on" : "off", &error_abort);
|
||||
display_opengl = opts->has_gl;
|
||||
|
||||
be->u.spiceport.data->fqdn = g_strdup("org.qemu.monitor.qmp.0");
|
||||
qemu_chardev_new("org.qemu.monitor.qmp", TYPE_CHARDEV_SPICEPORT,
|
||||
be, NULL, &error_abort);
|
||||
qopts = qemu_opts_create(qemu_find_opts("mon"),
|
||||
NULL, 0, &error_fatal);
|
||||
qemu_opt_set(qopts, "chardev", "org.qemu.monitor.qmp", &error_abort);
|
||||
qemu_opt_set(qopts, "mode", "control", &error_abort);
|
||||
|
||||
qapi_free_ChardevBackend(be);
|
||||
}
|
||||
|
||||
static void spice_app_display_init(DisplayState *ds, DisplayOptions *opts)
|
||||
{
|
||||
GError *err = NULL;
|
||||
gchar *uri;
|
||||
|
||||
uri = g_strjoin("", "spice+unix://", app_dir, "/", "spice.sock", NULL);
|
||||
info_report("Launching display with URI: %s", uri);
|
||||
g_app_info_launch_default_for_uri(uri, NULL, &err);
|
||||
if (err) {
|
||||
error_report("Failed to launch %s URI: %s", uri, err->message);
|
||||
error_report("You need a capable Spice client, "
|
||||
"such as virt-viewer 8.0");
|
||||
exit(1);
|
||||
}
|
||||
g_free(uri);
|
||||
}
|
||||
|
||||
static QemuDisplay qemu_display_spice_app = {
|
||||
.type = DISPLAY_TYPE_SPICE_APP,
|
||||
.early_init = spice_app_display_early_init,
|
||||
.init = spice_app_display_init,
|
||||
};
|
||||
|
||||
static void register_spice_app(void)
|
||||
{
|
||||
qemu_display_register(&qemu_display_spice_app);
|
||||
}
|
||||
|
||||
type_init(register_spice_app);
|
@ -34,6 +34,7 @@
|
||||
#include "qemu/option.h"
|
||||
#include "migration/misc.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "ui/spice-display.h"
|
||||
|
||||
/* core bits */
|
||||
@ -397,6 +398,7 @@ static SpiceChannelList *qmp_query_spice_channels(void)
|
||||
static QemuOptsList qemu_spice_opts = {
|
||||
.name = "spice",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head),
|
||||
.merge_lists = true,
|
||||
.desc = {
|
||||
{
|
||||
.name = "port",
|
||||
@ -626,7 +628,7 @@ static void vm_change_state_handler(void *opaque, int running,
|
||||
{
|
||||
if (running) {
|
||||
qemu_spice_display_start();
|
||||
} else {
|
||||
} else if (state != RUN_STATE_PAUSED) {
|
||||
qemu_spice_display_stop();
|
||||
}
|
||||
}
|
||||
@ -783,7 +785,7 @@ void qemu_spice_init(void)
|
||||
|
||||
qemu_opt_foreach(opts, add_channel, &tls_port, &error_fatal);
|
||||
|
||||
spice_server_set_name(spice_server, qemu_name);
|
||||
spice_server_set_name(spice_server, qemu_name ?: "QEMU " QEMU_VERSION);
|
||||
spice_server_set_uuid(spice_server, (unsigned char *)&qemu_uuid);
|
||||
|
||||
seamless_migration = qemu_opt_get_bool(opts, "seamless-migration", 0);
|
||||
@ -863,6 +865,56 @@ bool qemu_spice_have_display_interface(QemuConsole *con)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively (in reverse order) appends addresses of PCI devices as it moves
|
||||
* up in the PCI hierarchy.
|
||||
*
|
||||
* @returns true on success, false when the buffer wasn't large enough
|
||||
*/
|
||||
static bool append_pci_address(char *buf, size_t buf_size, const PCIDevice *pci)
|
||||
{
|
||||
PCIBus *bus = pci_get_bus(pci);
|
||||
/*
|
||||
* equivalent to if (!pci_bus_is_root(bus)), but the function is not built
|
||||
* with PCI_CONFIG=n, avoid using an #ifdef by checking directly
|
||||
*/
|
||||
if (bus->parent_dev != NULL) {
|
||||
append_pci_address(buf, buf_size, bus->parent_dev);
|
||||
}
|
||||
|
||||
size_t len = strlen(buf);
|
||||
ssize_t written = snprintf(buf + len, buf_size - len, "/%02x.%x",
|
||||
PCI_SLOT(pci->devfn), PCI_FUNC(pci->devfn));
|
||||
|
||||
return written > 0 && written < buf_size - len;
|
||||
}
|
||||
|
||||
bool qemu_spice_fill_device_address(QemuConsole *con,
|
||||
char *device_address,
|
||||
size_t size)
|
||||
{
|
||||
DeviceState *dev = DEVICE(object_property_get_link(OBJECT(con),
|
||||
"device",
|
||||
&error_abort));
|
||||
PCIDevice *pci = (PCIDevice *) object_dynamic_cast(OBJECT(dev),
|
||||
TYPE_PCI_DEVICE);
|
||||
|
||||
if (pci == NULL) {
|
||||
warn_report("Setting device address of a display device to SPICE: "
|
||||
"Not a PCI device.");
|
||||
return false;
|
||||
}
|
||||
|
||||
strncpy(device_address, "pci/0000", size);
|
||||
if (!append_pci_address(device_address, size, pci)) {
|
||||
warn_report("Setting device address of a display device to SPICE: "
|
||||
"Too many PCI devices in the chain.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int qemu_spice_add_display_interface(QXLInstance *qxlin, QemuConsole *con)
|
||||
{
|
||||
if (g_slist_find(spice_consoles, con)) {
|
||||
@ -921,12 +973,20 @@ int qemu_spice_display_add_client(int csock, int skipauth, int tls)
|
||||
|
||||
void qemu_spice_display_start(void)
|
||||
{
|
||||
if (spice_display_is_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
spice_display_is_running = true;
|
||||
spice_server_vm_start(spice_server);
|
||||
}
|
||||
|
||||
void qemu_spice_display_stop(void)
|
||||
{
|
||||
if (!spice_display_is_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
spice_server_vm_stop(spice_server);
|
||||
spice_display_is_running = false;
|
||||
}
|
||||
|
@ -1147,6 +1147,17 @@ static void qemu_spice_display_init_one(QemuConsole *con)
|
||||
|
||||
ssd->qxl.base.sif = &dpy_interface.base;
|
||||
qemu_spice_add_display_interface(&ssd->qxl, con);
|
||||
|
||||
#if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */
|
||||
char device_address[256] = "";
|
||||
if (qemu_spice_fill_device_address(con, device_address, 256)) {
|
||||
spice_qxl_set_device_info(&ssd->qxl,
|
||||
device_address,
|
||||
qemu_console_get_head(con),
|
||||
1);
|
||||
}
|
||||
#endif
|
||||
|
||||
qemu_spice_create_host_memslot(ssd);
|
||||
|
||||
register_displaychangelistener(&ssd->dcl);
|
||||
|
Loading…
Reference in New Issue
Block a user