Merge tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging
UI-related fixes & shareable 2d memory with -display dbus
# -----BEGIN PGP SIGNATURE-----
#
# iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmcNHtIcHG1hcmNhbmRy
# ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5SYTD/9fRNrgnZIvIbIGf0kv
# j3LguzwEsfn8eIUbJEIxtDnoS17zX1t981kP9J9ctUM6wnb0iQNYCXeTrF8Xrq0z
# psiPhHGwPyWMdn9SWRfj597ShPn75z340Qve5GUm7clGu2KILh7TqqACH8LzaX+5
# 6jqoZc3kqD+PYZHnYAi6v1YFfLIYfj0n6EaO/J4RRRZSrknpgct7jpmqL4wVzTIo
# KYlG5afdUUfhmSIv5ZDpuuEJppdG74K2H+hJKDPIOOQ8/i/IU2EQPJ00ppiOPbET
# nA0+piLGtHQwU24u5kDdbDlGL/y1KBKvGclOtzLQxWNStch5A6hqllNsuIg+0dJW
# MRO2WZ8C7P7LD1eGmtYVZF/NzjnlTW/hbM5i0poPqhfcwbVmlIXjDs8GUfMGfINr
# 1MVFGNjxfgadYZ1f6Q/JU/KWPJMR4Ik3C/SmGrRBlfra5YIts0ItDeGgfQIW9JGb
# 1CpOng6/3SvW01B6psrPL+wP+6PsK333KPIA77KafOEMyOyEyuSOUrTShXbyXBHc
# r/nLbWw2lZs4U0kgGRQ21+R3huTyw8LnikYpCnGwTWGCpb9NDFYg7z3CRrZW0hWx
# DIWfN7M6YymeYygPUV9Wjo6i4yq4QqWPp7/QXtkSdX3v44/D7NWytKGST+Hwjkpa
# h6U2vrsLdep2m47bnX/dEEP61g==
# =xdt/
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 14 Oct 2024 14:38:26 BST
# gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5
# gpg: issuer "marcandre.lureau@redhat.com"
# gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full]
# gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full]
# Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5
* tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu:
audio/pw: Report more accurate error when connecting to PipeWire fails
tests: add basic -display dbus Map.Unix test
ui: refactor using a common qemu_pixman_shareable
virtio-gpu: allocate shareable 2d resources on !win32
ui/dbus: implement Unix.Map
ui/dbus: add Listener.Unix.Map interface XML
ui/dbus: make Listener.Win32.Map win32-specific
meson: find_program('gdbus-codegen') directly
ui/surface: allocate shared memory on !win32
ui/dbus: add trace for can_share_map
ui/dbus: do not limit to one listener per connection / bus name
ui/pixman: generalize shared_image_destroy
util/memfd: report potential errors on free
ui/dbus: discard pending CursorDefine on new one
ui/dbus: discard display messages on disable
ui/dbus: fix filtering all update messages
ui/win32: fix potential use-after-free with dbus shared memory
ui/dbus: fix leak on message filtering
hw/audio/hda: fix memory leak on audio setup
hw/audio/hda: free timer on exit
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/audio/pwaudio.c b/audio/pwaudio.c
index 3b14e04..8e13b58 100644
--- a/audio/pwaudio.c
+++ b/audio/pwaudio.c
@@ -769,13 +769,15 @@
pw->core = pw_context_connect(pw->context, NULL, 0);
if (pw->core == NULL) {
pw_thread_loop_unlock(pw->thread_loop);
- goto fail_error;
+ error_setg_errno(errp, errno, "Failed to connect to PipeWire instance");
+ goto fail;
}
if (pw_core_add_listener(pw->core, &pw->core_listener,
&core_events, pw) < 0) {
pw_thread_loop_unlock(pw->thread_loop);
- goto fail_error;
+ error_setg(errp, "Failed to add PipeWire listener");
+ goto fail;
}
if (wait_resync(pw) < 0) {
pw_thread_loop_unlock(pw->thread_loop);
@@ -785,8 +787,6 @@
return g_steal_pointer(&pw);
-fail_error:
- error_setg(errp, "Failed to initialize PW context");
fail:
if (pw->thread_loop) {
pw_thread_loop_stop(pw->thread_loop);
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index b40eec960..bc66150 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -472,6 +472,24 @@
}
}
+static void hda_close_stream(HDAAudioState *a, HDAAudioStream *st)
+{
+ if (st->node == NULL) {
+ return;
+ }
+ if (a->use_timer) {
+ timer_free(st->buft);
+ st->buft = NULL;
+ }
+ if (st->output) {
+ AUD_close_out(&a->card, st->voice.out);
+ st->voice.out = NULL;
+ } else {
+ AUD_close_in(&a->card, st->voice.in);
+ st->voice.in = NULL;
+ }
+}
+
static void hda_audio_setup(HDAAudioStream *st)
{
bool use_timer = st->state->use_timer;
@@ -484,6 +502,7 @@
trace_hda_audio_format(st->node->name, st->as.nchannels,
fmt2name[st->as.fmt], st->as.freq);
+ hda_close_stream(st->state, st);
if (st->output) {
if (use_timer) {
cb = hda_audio_output_cb;
@@ -741,23 +760,11 @@
static void hda_audio_exit(HDACodecDevice *hda)
{
HDAAudioState *a = HDA_AUDIO(hda);
- HDAAudioStream *st;
int i;
dprint(a, 1, "%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(a->st); i++) {
- st = a->st + i;
- if (st->node == NULL) {
- continue;
- }
- if (a->use_timer) {
- timer_del(st->buft);
- }
- if (st->output) {
- AUD_close_out(&a->card, st->voice.out);
- } else {
- AUD_close_in(&a->card, st->voice.in);
- }
+ hda_close_stream(a, a->st + i);
}
AUD_remove_card(&a->card);
}
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 3281842..49fd803 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -28,6 +28,7 @@
#include "hw/virtio/virtio-bus.h"
#include "hw/qdev-properties.h"
#include "qemu/log.h"
+#include "qemu/memfd.h"
#include "qemu/module.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
@@ -238,16 +239,6 @@
return height * stride;
}
-#ifdef WIN32
-static void
-win32_pixman_image_destroy(pixman_image_t *image, void *data)
-{
- HANDLE handle = data;
-
- qemu_win32_map_free(pixman_image_get_data(image), handle, &error_warn);
-}
-#endif
-
static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
{
@@ -294,28 +285,20 @@
res->hostmem = calc_image_hostmem(pformat, c2d.width, c2d.height);
if (res->hostmem + g->hostmem < g->conf_max_hostmem) {
- void *bits = NULL;
-#ifdef WIN32
- bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn);
- if (!bits) {
+ if (!qemu_pixman_image_new_shareable(
+ &res->image,
+ &res->share_handle,
+ "virtio-gpu res",
+ pformat,
+ c2d.width,
+ c2d.height,
+ c2d.height ? res->hostmem / c2d.height : 0,
+ &error_warn)) {
goto end;
}
-#endif
- res->image = pixman_image_create_bits(
- pformat,
- c2d.width,
- c2d.height,
- bits, c2d.height ? res->hostmem / c2d.height : 0);
-#ifdef WIN32
- if (res->image) {
- pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle);
- }
-#endif
}
-#ifdef WIN32
end:
-#endif
if (!res->image) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: resource creation failed %d %d %d\n",
@@ -686,9 +669,7 @@
/* realloc the surface ptr */
scanout->ds = qemu_create_displaysurface_pixman(rect);
-#ifdef WIN32
- qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, fb->offset);
-#endif
+ qemu_displaysurface_set_share_handle(scanout->ds, res->share_handle, fb->offset);
pixman_image_unref(rect);
dpy_gfx_replace_surface(g->parent_obj.scanout[scanout_id].con,
@@ -1284,7 +1265,6 @@
VirtIOGPU *g = opaque;
struct virtio_gpu_simple_resource *res;
uint32_t resource_id, pformat;
- void *bits = NULL;
int i;
g->hostmem = 0;
@@ -1311,24 +1291,17 @@
}
res->hostmem = calc_image_hostmem(pformat, res->width, res->height);
-#ifdef WIN32
- bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn);
- if (!bits) {
+ if (!qemu_pixman_image_new_shareable(&res->image,
+ &res->share_handle,
+ "virtio-gpu res",
+ pformat,
+ res->width,
+ res->height,
+ res->height ? res->hostmem / res->height : 0,
+ &error_warn)) {
g_free(res);
return -EINVAL;
}
-#endif
- res->image = pixman_image_create_bits(
- pformat,
- res->width, res->height,
- bits, res->height ? res->hostmem / res->height : 0);
- if (!res->image) {
- g_free(res);
- return -EINVAL;
- }
-#ifdef WIN32
- pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle);
-#endif
res->addrs = g_new(uint64_t, res->iov_cnt);
res->iov = g_new(struct iovec, res->iov_cnt);
@@ -1461,9 +1434,7 @@
return -EINVAL;
}
scanout->ds = qemu_create_displaysurface_pixman(res->image);
-#ifdef WIN32
- qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0);
-#endif
+ qemu_displaysurface_set_share_handle(scanout->ds, res->share_handle, 0);
dpy_gfx_replace_surface(scanout->con, scanout->ds);
}
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 7a59379..e343110 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -51,9 +51,7 @@
unsigned int iov_cnt;
uint32_t scanout_bitmask;
pixman_image_t *image;
-#ifdef WIN32
- HANDLE handle;
-#endif
+ qemu_pixman_shareable share_handle;
uint64_t hostmem;
uint64_t blob_size;
diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h
index ef13a82..193bc04 100644
--- a/include/ui/qemu-pixman.h
+++ b/include/ui/qemu-pixman.h
@@ -12,6 +12,8 @@
#include "pixman-minimal.h"
#endif
+#include "qapi/error.h"
+
/*
* pixman image formats are defined to be native endian,
* that means host byte order on qemu. So we go define
@@ -97,6 +99,28 @@
void qemu_pixman_image_unref(pixman_image_t *image);
+#ifdef WIN32
+typedef HANDLE qemu_pixman_shareable;
+#define SHAREABLE_NONE (NULL)
+#define SHAREABLE_TO_PTR(handle) (handle)
+#define PTR_TO_SHAREABLE(ptr) (ptr)
+#else
+typedef int qemu_pixman_shareable;
+#define SHAREABLE_NONE (-1)
+#define SHAREABLE_TO_PTR(handle) GINT_TO_POINTER(handle)
+#define PTR_TO_SHAREABLE(ptr) GPOINTER_TO_INT(ptr)
+#endif
+
+bool qemu_pixman_image_new_shareable(
+ pixman_image_t **image,
+ qemu_pixman_shareable *handle,
+ const char *name,
+ pixman_format_code_t format,
+ int width,
+ int height,
+ int rowstride_bytes,
+ Error **errp);
+
G_DEFINE_AUTOPTR_CLEANUP_FUNC(pixman_image_t, qemu_pixman_image_unref)
#endif /* QEMU_PIXMAN_H */
diff --git a/include/ui/surface.h b/include/ui/surface.h
index 345b191..f16f7be 100644
--- a/include/ui/surface.h
+++ b/include/ui/surface.h
@@ -23,10 +23,8 @@
GLenum gltype;
GLuint texture;
#endif
-#ifdef WIN32
- HANDLE handle;
- uint32_t handle_offset;
-#endif
+ qemu_pixman_shareable share_handle;
+ uint32_t share_handle_offset;
} DisplaySurface;
PixelFormat qemu_default_pixelformat(int bpp);
@@ -37,10 +35,10 @@
DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image);
DisplaySurface *qemu_create_placeholder_surface(int w, int h,
const char *msg);
-#ifdef WIN32
-void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
- HANDLE h, uint32_t offset);
-#endif
+
+void qemu_displaysurface_set_share_handle(DisplaySurface *surface,
+ qemu_pixman_shareable handle,
+ uint32_t offset);
DisplaySurface *qemu_create_displaysurface(int width, int height);
void qemu_free_displaysurface(DisplaySurface *surface);
diff --git a/meson.build b/meson.build
index c85f964..d26690c 100644
--- a/meson.build
+++ b/meson.build
@@ -1037,7 +1037,7 @@
gio = not_found
endif
if gio.found()
- gdbus_codegen = find_program(gio.get_variable('gdbus_codegen'),
+ gdbus_codegen = find_program('gdbus-codegen',
required: get_option('gio'))
gio_unix = dependency('gio-unix-2.0', required: get_option('gio'),
method: 'pkg-config')
diff --git a/tests/qtest/dbus-display-test.c b/tests/qtest/dbus-display-test.c
index 0390bdc..f7fc873 100644
--- a/tests/qtest/dbus-display-test.c
+++ b/tests/qtest/dbus-display-test.c
@@ -2,9 +2,14 @@
#include "qemu/sockets.h"
#include "qemu/dbus.h"
#include "qemu/sockets.h"
+#include "glib.h"
+#include "glibconfig.h"
#include <gio/gio.h>
#include <gio/gunixfdlist.h>
#include "libqtest.h"
+#ifndef WIN32
+#include <sys/mman.h>
+#endif
#include "ui/dbus-display1.h"
static GDBusConnection*
@@ -82,6 +87,7 @@
GThread *thread;
GDBusConnection *listener_conn;
GDBusObjectManagerServer *server;
+ bool with_map;
} TestDBusConsoleRegister;
static gboolean listener_handle_scanout(
@@ -94,13 +100,49 @@
GVariant *arg_data,
TestDBusConsoleRegister *test)
{
- g_main_loop_quit(test->loop);
+ if (!test->with_map) {
+ g_main_loop_quit(test->loop);
+ }
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#ifndef WIN32
+static gboolean listener_handle_scanout_map(
+ QemuDBusDisplay1ListenerUnixMap *object,
+ GDBusMethodInvocation *invocation,
+ GUnixFDList *fd_list,
+ GVariant *arg_handle,
+ guint arg_offset,
+ guint arg_width,
+ guint arg_height,
+ guint arg_stride,
+ guint arg_pixman_format,
+ TestDBusConsoleRegister *test)
+{
+ int fd = -1;
+ gint32 handle = g_variant_get_handle(arg_handle);
+ g_autoptr(GError) error = NULL;
+ void *addr = NULL;
+ size_t len = arg_height * arg_stride;
+
+ g_assert_cmpuint(g_unix_fd_list_get_length(fd_list), ==, 1);
+ fd = g_unix_fd_list_get(fd_list, handle, &error);
+ g_assert_no_error(error);
+
+ addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, arg_offset);
+ g_assert_no_errno(addr == MAP_FAILED ? -1 : 0);
+ g_assert_no_errno(munmap(addr, len));
+
+ g_main_loop_quit(test->loop);
+
+ close(fd);
+ return DBUS_METHOD_INVOCATION_HANDLED;
+}
+#endif
+
static void
-test_dbus_console_setup_listener(TestDBusConsoleRegister *test)
+test_dbus_console_setup_listener(TestDBusConsoleRegister *test, bool with_map)
{
g_autoptr(GDBusObjectSkeleton) listener = NULL;
g_autoptr(QemuDBusDisplay1ListenerSkeleton) iface = NULL;
@@ -114,6 +156,25 @@
NULL);
g_dbus_object_skeleton_add_interface(listener,
G_DBUS_INTERFACE_SKELETON(iface));
+ if (with_map) {
+#ifdef WIN32
+ g_test_skip("map test lacking on win32");
+ return;
+#else
+ g_autoptr(QemuDBusDisplay1ListenerUnixMapSkeleton) iface_map =
+ QEMU_DBUS_DISPLAY1_LISTENER_UNIX_MAP_SKELETON(
+ qemu_dbus_display1_listener_unix_map_skeleton_new());
+
+ g_object_connect(iface_map,
+ "signal::handle-scanout-map", listener_handle_scanout_map, test,
+ NULL);
+ g_dbus_object_skeleton_add_interface(listener,
+ G_DBUS_INTERFACE_SKELETON(iface_map));
+ g_object_set(iface, "interfaces",
+ (const gchar *[]) { "org.qemu.Display1.Listener.Unix.Map", NULL },
+ NULL);
+#endif
+ }
g_dbus_object_manager_server_export(test->server, listener);
g_dbus_object_manager_server_set_connection(test->server,
test->listener_conn);
@@ -145,7 +206,7 @@
g_assert_no_error(err);
test->listener_conn = g_thread_join(test->thread);
- test_dbus_console_setup_listener(test);
+ test_dbus_console_setup_listener(test, test->with_map);
}
static gpointer
@@ -155,7 +216,7 @@
}
static void
-test_dbus_display_console(void)
+test_dbus_display_console(const void* data)
{
g_autoptr(GError) err = NULL;
g_autoptr(GDBusConnection) conn = NULL;
@@ -163,7 +224,7 @@
g_autoptr(GMainLoop) loop = NULL;
QTestState *qts = NULL;
int pair[2];
- TestDBusConsoleRegister test = { 0, };
+ TestDBusConsoleRegister test = { 0, .with_map = GPOINTER_TO_INT(data) };
#ifdef WIN32
WSAPROTOCOL_INFOW info;
g_autoptr(GVariant) listener = NULL;
@@ -299,7 +360,8 @@
g_test_init(&argc, &argv, NULL);
qtest_add_func("/dbus-display/vm", test_dbus_display_vm);
- qtest_add_func("/dbus-display/console", test_dbus_display_console);
+ qtest_add_data_func("/dbus-display/console", GINT_TO_POINTER(false), test_dbus_display_console);
+ qtest_add_data_func("/dbus-display/console/map", GINT_TO_POINTER(true), test_dbus_display_console);
qtest_add_func("/dbus-display/keyboard", test_dbus_display_keyboard);
return g_test_run();
diff --git a/ui/console.c b/ui/console.c
index 105a0e2..5165f17 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -37,6 +37,7 @@
#include "trace.h"
#include "exec/memory.h"
#include "qom/object.h"
+#include "qemu/memfd.h"
#include "console-priv.h"
@@ -452,60 +453,26 @@
{
}
-#ifdef WIN32
-void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
- HANDLE h, uint32_t offset)
+void qemu_displaysurface_set_share_handle(DisplaySurface *surface,
+ qemu_pixman_shareable handle,
+ uint32_t offset)
{
- assert(!surface->handle);
+ assert(surface->share_handle == SHAREABLE_NONE);
- surface->handle = h;
- surface->handle_offset = offset;
+ surface->share_handle = handle;
+ surface->share_handle_offset = offset;
+
}
-static void
-win32_pixman_image_destroy(pixman_image_t *image, void *data)
-{
- DisplaySurface *surface = data;
-
- if (!surface->handle) {
- return;
- }
-
- assert(surface->handle_offset == 0);
-
- qemu_win32_map_free(
- pixman_image_get_data(surface->image),
- surface->handle,
- &error_warn
- );
-}
-#endif
-
DisplaySurface *qemu_create_displaysurface(int width, int height)
{
- DisplaySurface *surface;
- void *bits = NULL;
-#ifdef WIN32
- HANDLE handle = NULL;
-#endif
-
trace_displaysurface_create(width, height);
-#ifdef WIN32
- bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort);
-#endif
-
- surface = qemu_create_displaysurface_from(
+ return qemu_create_displaysurface_from(
width, height,
PIXMAN_x8r8g8b8,
- width * 4, bits
+ width * 4, NULL
);
- surface->flags = QEMU_ALLOCATED_FLAG;
-
-#ifdef WIN32
- qemu_displaysurface_win32_set_handle(surface, handle, 0);
-#endif
- return surface;
}
DisplaySurface *qemu_create_displaysurface_from(int width, int height,
@@ -515,15 +482,25 @@
DisplaySurface *surface = g_new0(DisplaySurface, 1);
trace_displaysurface_create_from(surface, width, height, format);
- surface->image = pixman_image_create_bits(format,
- width, height,
- (void *)data, linesize);
- assert(surface->image != NULL);
-#ifdef WIN32
- pixman_image_set_destroy_function(surface->image,
- win32_pixman_image_destroy, surface);
-#endif
+ surface->share_handle = SHAREABLE_NONE;
+ if (data) {
+ surface->image = pixman_image_create_bits(format,
+ width, height,
+ (void *)data, linesize);
+ } else {
+ qemu_pixman_image_new_shareable(&surface->image,
+ &surface->share_handle,
+ "displaysurface",
+ format,
+ width,
+ height,
+ linesize,
+ &error_abort);
+ surface->flags = QEMU_ALLOCATED_FLAG;
+ }
+
+ assert(surface->image != NULL);
return surface;
}
@@ -532,6 +509,7 @@
DisplaySurface *surface = g_new0(DisplaySurface, 1);
trace_displaysurface_create_pixman(surface);
+ surface->share_handle = SHAREABLE_NONE;
surface->image = pixman_image_ref(image);
return surface;
diff --git a/ui/dbus-console.c b/ui/dbus-console.c
index 578b67f..5eb1d40 100644
--- a/ui/dbus-console.c
+++ b/ui/dbus-console.c
@@ -41,7 +41,7 @@
DisplayChangeListener dcl;
DBusDisplay *display;
- GHashTable *listeners;
+ GPtrArray *listeners;
QemuDBusDisplay1Console *iface;
QemuDBusDisplay1Keyboard *iface_kbd;
@@ -142,8 +142,7 @@
{
DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
- ddc->listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
- NULL, g_object_unref);
+ ddc->listeners = g_ptr_array_new_with_free_func(g_object_unref);
ddc->dcl.ops = &dbus_console_dcl_ops;
}
@@ -157,7 +156,7 @@
g_clear_object(&ddc->iface_mouse);
g_clear_object(&ddc->iface_kbd);
g_clear_object(&ddc->iface);
- g_clear_pointer(&ddc->listeners, g_hash_table_unref);
+ g_clear_pointer(&ddc->listeners, g_ptr_array_unref);
g_clear_pointer(&ddc->kbd, qkbd_state_free);
G_OBJECT_CLASS(dbus_display_console_parent_class)->dispose(object);
@@ -179,7 +178,7 @@
trace_dbus_listener_vanished(name);
- g_hash_table_remove(ddc->listeners, name);
+ g_ptr_array_remove_fast(ddc->listeners, listener);
qkbd_state_lift_all_keys(ddc->kbd);
}
@@ -267,16 +266,6 @@
DBusDisplayListener *listener;
int fd;
- if (sender && g_hash_table_contains(ddc->listeners, sender)) {
- g_dbus_method_invocation_return_error(
- invocation,
- DBUS_DISPLAY_ERROR,
- DBUS_DISPLAY_ERROR_INVALID,
- "`%s` is already registered!",
- sender);
- return DBUS_METHOD_INVOCATION_HANDLED;
- }
-
#ifdef G_OS_WIN32
if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) {
return DBUS_METHOD_INVOCATION_HANDLED;
@@ -331,9 +320,7 @@
return DBUS_METHOD_INVOCATION_HANDLED;
}
- g_hash_table_insert(ddc->listeners,
- (gpointer)dbus_display_listener_get_bus_name(listener),
- listener);
+ g_ptr_array_add(ddc->listeners, listener);
g_object_connect(listener_conn,
"swapped-signal::closed", listener_vanished_cb, listener,
NULL);
diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml
index ce35d64..e70f284 100644
--- a/ui/dbus-display1.xml
+++ b/ui/dbus-display1.xml
@@ -470,12 +470,60 @@
</interface>
<!--
+ org.qemu.Display1.Listener.Unix.Map:
+
+ This optional client-side interface can complement
+ org.qemu.Display1.Listener on ``/org/qemu/Display1/Listener`` for
+ Unix-specific shared memory scanouts.
+ -->
+ <?if $(env.HOST_OS) != windows?>
+ <interface name="org.qemu.Display1.Listener.Unix.Map">
+ <!--
+ ScanoutMap:
+ @handle: the shared map FD.
+ @offset: mapping offset, in bytes.
+ @width: display width, in pixels.
+ @height: display height, in pixels.
+ @stride: stride, in bytes.
+ @pixman_format: image format (ex: ``PIXMAN_X8R8G8B8``).
+
+ Resize and update the display content with a shared map.
+ -->
+ <method name="ScanoutMap">
+ <arg type="h" name="handle" direction="in"/>
+ <arg type="u" name="offset" direction="in"/>
+ <arg type="u" name="width" direction="in"/>
+ <arg type="u" name="height" direction="in"/>
+ <arg type="u" name="stride" direction="in"/>
+ <arg type="u" name="pixman_format" direction="in"/>
+ </method>
+
+ <!--
+ UpdateMap:
+ @x: the X update position, in pixels.
+ @y: the Y update position, in pixels.
+ @width: the update width, in pixels.
+ @height: the update height, in pixels.
+
+ Update the display content with the current shared map and the given region.
+ -->
+ <method name="UpdateMap">
+ <arg type="i" name="x" direction="in"/>
+ <arg type="i" name="y" direction="in"/>
+ <arg type="i" name="width" direction="in"/>
+ <arg type="i" name="height" direction="in"/>
+ </method>
+ </interface>
+ <?endif?>
+
+ <!--
org.qemu.Display1.Listener.Win32.Map:
This optional client-side interface can complement
org.qemu.Display1.Listener on ``/org/qemu/Display1/Listener`` for Windows
specific shared memory scanouts.
-->
+ <?if $(env.HOST_OS) == windows?>
<interface name="org.qemu.Display1.Listener.Win32.Map">
<!--
ScanoutMap:
@@ -513,6 +561,7 @@
<arg type="i" name="height" direction="in"/>
</method>
</interface>
+ <?endif?>
<!--
org.qemu.Display1.Listener.Win32.D3d11:
diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
index a54123a..99738e7 100644
--- a/ui/dbus-listener.c
+++ b/ui/dbus-listener.c
@@ -26,6 +26,7 @@
#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "dbus.h"
+#include "glib.h"
#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
#endif
@@ -82,10 +83,13 @@
#ifdef CONFIG_OPENGL
egl_fb fb;
#endif
+#else /* !WIN32 */
+ QemuDBusDisplay1ListenerUnixMap *map_proxy;
#endif
guint dbus_filter;
- guint32 out_serial_to_discard;
+ guint32 display_serial_to_discard;
+ guint32 cursor_serial_to_discard;
};
G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT)
@@ -93,10 +97,20 @@
static void dbus_gfx_update(DisplayChangeListener *dcl,
int x, int y, int w, int h);
-static void ddl_discard_pending_messages(DBusDisplayListener *ddl)
+static void ddl_discard_display_messages(DBusDisplayListener *ddl)
{
- ddl->out_serial_to_discard = g_dbus_connection_get_last_serial(
+ guint32 serial = g_dbus_connection_get_last_serial(
g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy)));
+
+ g_atomic_int_set(&ddl->display_serial_to_discard, serial);
+}
+
+static void ddl_discard_cursor_messages(DBusDisplayListener *ddl)
+{
+ guint32 serial = g_dbus_connection_get_last_serial(
+ g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy)));
+
+ g_atomic_int_set(&ddl->cursor_serial_to_discard, serial);
}
#ifdef CONFIG_OPENGL
@@ -104,6 +118,8 @@
{
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+ ddl_discard_display_messages(ddl);
+
qemu_dbus_display1_listener_call_disable(
ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
@@ -290,7 +306,7 @@
return;
}
- ddl_discard_pending_messages(ddl);
+ ddl_discard_display_messages(ddl);
width = qemu_dmabuf_get_width(dmabuf);
height = qemu_dmabuf_get_height(dmabuf);
@@ -320,13 +336,13 @@
return true;
}
- if (!ddl->can_share_map || !ddl->ds->handle) {
+ if (!ddl->can_share_map || !ddl->ds->share_handle) {
return false;
}
success = DuplicateHandle(
GetCurrentProcess(),
- ddl->ds->handle,
+ ddl->ds->share_handle,
ddl->peer_process,
&target_handle,
FILE_MAP_READ | SECTION_QUERY,
@@ -338,12 +354,12 @@
return false;
}
- ddl_discard_pending_messages(ddl);
+ ddl_discard_display_messages(ddl);
if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync(
ddl->map_proxy,
GPOINTER_TO_UINT(target_handle),
- ddl->ds->handle_offset,
+ ddl->ds->share_handle_offset,
surface_width(ddl->ds),
surface_height(ddl->ds),
surface_stride(ddl->ds),
@@ -401,7 +417,7 @@
return false;
}
- ddl_discard_pending_messages(ddl);
+ ddl_discard_display_messages(ddl);
qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d(
ddl->d3d11_proxy,
@@ -427,6 +443,51 @@
return true;
}
#endif /* CONFIG_OPENGL */
+#else /* !WIN32 */
+static bool dbus_scanout_map(DBusDisplayListener *ddl)
+{
+ g_autoptr(GError) err = NULL;
+ g_autoptr(GUnixFDList) fd_list = NULL;
+
+ if (ddl->ds_share == SHARE_KIND_MAPPED) {
+ return true;
+ }
+
+ if (!ddl->can_share_map || ddl->ds->share_handle == SHAREABLE_NONE) {
+ return false;
+ }
+
+ ddl_discard_display_messages(ddl);
+ fd_list = g_unix_fd_list_new();
+ if (g_unix_fd_list_append(fd_list, ddl->ds->share_handle, &err) != 0) {
+ g_debug("Failed to setup scanout map fdlist: %s", err->message);
+ ddl->can_share_map = false;
+ return false;
+ }
+
+ if (!qemu_dbus_display1_listener_unix_map_call_scanout_map_sync(
+ ddl->map_proxy,
+ g_variant_new_handle(0),
+ ddl->ds->share_handle_offset,
+ surface_width(ddl->ds),
+ surface_height(ddl->ds),
+ surface_stride(ddl->ds),
+ surface_format(ddl->ds),
+ G_DBUS_CALL_FLAGS_NONE,
+ DBUS_DEFAULT_TIMEOUT,
+ fd_list,
+ NULL,
+ NULL,
+ &err)) {
+ g_debug("Failed to call ScanoutMap: %s", err->message);
+ ddl->can_share_map = false;
+ return false;
+ }
+
+ ddl->ds_share = SHARE_KIND_MAPPED;
+
+ return true;
+}
#endif /* WIN32 */
#ifdef CONFIG_OPENGL
@@ -497,6 +558,8 @@
return;
}
+ ddl_discard_cursor_messages(ddl);
+
egl_dmabuf_import_texture(dmabuf);
texture = qemu_dmabuf_get_texture(dmabuf);
if (!texture) {
@@ -659,7 +722,7 @@
surface_stride(ddl->ds) * surface_height(ddl->ds), TRUE,
(GDestroyNotify)pixman_image_unref, pixman_image_ref(ddl->ds->image));
- ddl_discard_pending_messages(ddl);
+ ddl_discard_display_messages(ddl);
qemu_dbus_display1_listener_call_scanout(
ddl->proxy, surface_width(ddl->ds), surface_height(ddl->ds),
@@ -677,16 +740,22 @@
trace_dbus_update(x, y, w, h);
-#ifdef WIN32
if (dbus_scanout_map(ddl)) {
+#ifdef WIN32
qemu_dbus_display1_listener_win32_map_call_update_map(
ddl->map_proxy,
x, y, w, h,
G_DBUS_CALL_FLAGS_NONE,
DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
+#else
+ qemu_dbus_display1_listener_unix_map_call_update_map(
+ ddl->map_proxy,
+ x, y, w, h,
+ G_DBUS_CALL_FLAGS_NONE,
+ DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
+#endif
return;
}
-#endif
if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) {
return ddl_scanout(ddl);
@@ -740,6 +809,8 @@
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
GVariant *v_data = NULL;
+ ddl_discard_cursor_messages(ddl);
+
v_data = g_variant_new_from_data(
G_VARIANT_TYPE("ay"),
c->data,
@@ -861,7 +932,6 @@
return ddl->console;
}
-#ifdef WIN32
static bool
dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface)
{
@@ -876,6 +946,7 @@
return implements;
}
+#ifdef WIN32
static bool
dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl)
{
@@ -958,10 +1029,11 @@
static void
dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl)
{
-#ifdef WIN32
g_autoptr(GError) err = NULL;
- if (!dbus_display_listener_implements(ddl, "org.qemu.Display1.Listener.Win32.Map")) {
+#ifdef WIN32
+ if (!dbus_display_listener_implements(
+ ddl, "org.qemu.Display1.Listener.Win32.Map")) {
return;
}
@@ -982,6 +1054,20 @@
}
ddl->can_share_map = true;
+#else /* !WIN32 */
+ if (!dbus_display_listener_implements(
+ ddl, "org.qemu.Display1.Listener.Unix.Map")) {
+ return;
+ }
+ ddl->map_proxy = qemu_dbus_display1_listener_unix_map_proxy_new_sync(
+ ddl->conn, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL,
+ "/org/qemu/Display1/Listener", NULL, &err);
+ if (!ddl->map_proxy) {
+ g_debug("Failed to setup Unix map proxy: %s", err->message);
+ return;
+ }
+
+ ddl->can_share_map = true;
#endif
}
@@ -992,16 +1078,50 @@
gpointer user_data)
{
DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(user_data);
- guint32 serial;
+ guint32 serial, discard_serial;
if (incoming) {
return message;
}
serial = g_dbus_message_get_serial(message);
- if (serial <= ddl->out_serial_to_discard) {
- trace_dbus_filter(serial, ddl->out_serial_to_discard);
- return NULL;
+
+ discard_serial = g_atomic_int_get(&ddl->display_serial_to_discard);
+ if (serial <= discard_serial) {
+ const char *member = g_dbus_message_get_member(message);
+ static const char *const display_messages[] = {
+ "Scanout",
+ "Update",
+#ifdef CONFIG_GBM
+ "ScanoutDMABUF",
+ "UpdateDMABUF",
+#endif
+ "ScanoutMap",
+ "UpdateMap",
+ "Disable",
+ NULL,
+ };
+
+ if (g_strv_contains(display_messages, member)) {
+ trace_dbus_filter(serial, discard_serial);
+ g_object_unref(message);
+ return NULL;
+ }
+ }
+
+ discard_serial = g_atomic_int_get(&ddl->cursor_serial_to_discard);
+ if (serial <= discard_serial) {
+ const gchar *member = g_dbus_message_get_member(message);
+ static const char *const cursor_messages[] = {
+ "CursorDefine",
+ NULL
+ };
+
+ if (g_strv_contains(cursor_messages, member)) {
+ trace_dbus_filter(serial, discard_serial);
+ g_object_unref(message);
+ return NULL;
+ }
}
return message;
@@ -1037,6 +1157,7 @@
ddl->console = console;
dbus_display_listener_setup_shared_map(ddl);
+ trace_dbus_can_share_map(ddl->can_share_map);
dbus_display_listener_setup_d3d11(ddl);
con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c
index 6cada8b..6ef4376 100644
--- a/ui/qemu-pixman.c
+++ b/ui/qemu-pixman.c
@@ -4,7 +4,9 @@
*/
#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "ui/console.h"
+#include "qemu/memfd.h"
#include "standard-headers/drm/drm_fourcc.h"
#include "trace.h"
@@ -267,3 +269,72 @@
pixman_image_unref(ibg);
}
#endif /* CONFIG_PIXMAN */
+
+static void *
+qemu_pixman_shareable_alloc(const char *name, size_t size,
+ qemu_pixman_shareable *handle,
+ Error **errp)
+{
+#ifdef WIN32
+ return qemu_win32_map_alloc(size, handle, errp);
+#else
+ return qemu_memfd_alloc(name, size, 0, handle, errp);
+#endif
+}
+
+static void
+qemu_pixman_shareable_free(qemu_pixman_shareable handle,
+ void *ptr, size_t size)
+{
+#ifdef WIN32
+ qemu_win32_map_free(ptr, handle, &error_warn);
+#else
+ qemu_memfd_free(ptr, size, handle);
+#endif
+}
+
+static void
+qemu_pixman_shared_image_destroy(pixman_image_t *image, void *data)
+{
+ qemu_pixman_shareable handle = PTR_TO_SHAREABLE(data);
+ void *ptr = pixman_image_get_data(image);
+ size_t size = pixman_image_get_height(image) * pixman_image_get_stride(image);
+
+ qemu_pixman_shareable_free(handle, ptr, size);
+}
+
+bool
+qemu_pixman_image_new_shareable(pixman_image_t **image,
+ qemu_pixman_shareable *handle,
+ const char *name,
+ pixman_format_code_t format,
+ int width,
+ int height,
+ int rowstride_bytes,
+ Error **errp)
+{
+ ERRP_GUARD();
+ size_t size = height * rowstride_bytes;
+ void *bits = NULL;
+
+ g_return_val_if_fail(image != NULL, false);
+ g_return_val_if_fail(handle != NULL, false);
+
+ bits = qemu_pixman_shareable_alloc(name, size, handle, errp);
+ if (!bits) {
+ return false;
+ }
+
+ *image = pixman_image_create_bits(format, width, height, bits, rowstride_bytes);
+ if (!*image) {
+ error_setg(errp, "Failed to allocate image");
+ qemu_pixman_shareable_free(*handle, bits, size);
+ return false;
+ }
+
+ pixman_image_set_destroy_function(*image,
+ qemu_pixman_shared_image_destroy,
+ SHAREABLE_TO_PTR(*handle));
+
+ return true;
+}
diff --git a/ui/trace-events b/ui/trace-events
index fb253c1..3da0d5e 100644
--- a/ui/trace-events
+++ b/ui/trace-events
@@ -166,6 +166,7 @@
dbus_scanout_texture(uint32_t tex_id, bool backing_y_0_top, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "tex_id:%u y0top:%d back:%ux%u %u+%u-%ux%u"
dbus_gl_gfx_switch(void *p) "surf: %p"
dbus_filter(unsigned int serial, unsigned int filter) "serial=%u (<= %u)"
+dbus_can_share_map(bool share) "can_share_map: %d"
# egl-helpers.c
egl_init_d3d11_device(void *p) "d3d device: %p"
diff --git a/util/memfd.c b/util/memfd.c
index 4a3c07e..8a2e906 100644
--- a/util/memfd.c
+++ b/util/memfd.c
@@ -28,6 +28,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
+#include "qemu/error-report.h"
#include "qemu/memfd.h"
#include "qemu/host-utils.h"
@@ -149,11 +150,15 @@
void qemu_memfd_free(void *ptr, size_t size, int fd)
{
if (ptr) {
- munmap(ptr, size);
+ if (munmap(ptr, size) != 0) {
+ error_report("memfd munmap() failed: %s", strerror(errno));
+ }
}
if (fd != -1) {
- close(fd);
+ if (close(fd) != 0) {
+ error_report("memfd close() failed: %s", strerror(errno));
+ }
}
}