diff --git a/audio/pwaudio.c b/audio/pwaudio.c index 3b14e04fbb..8e13b58286 100644 --- a/audio/pwaudio.c +++ b/audio/pwaudio.c @@ -769,13 +769,15 @@ qpw_audio_init(Audiodev *dev, Error **errp) 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 @@ qpw_audio_init(Audiodev *dev, Error **errp) 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 b40eec9604..bc661504cf 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -472,6 +472,24 @@ static void hda_audio_set_amp(HDAAudioStream *st) } } +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 @@ static void hda_audio_setup(HDAAudioStream *st) 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_init(HDACodecDevice *hda, 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 3281842bfe..49fd803393 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 @@ static uint32_t calc_image_hostmem(pixman_format_code_t pformat, 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 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, 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 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, /* 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 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, 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 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, } 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 @@ static int virtio_gpu_post_load(void *opaque, int version_id) 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 7a59379f5a..e343110e23 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -51,9 +51,7 @@ struct virtio_gpu_simple_resource { 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 ef13a8210c..193bc046d1 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_glyph_render(pixman_image_t *glyph, 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 345b19169d..f16f7be8be 100644 --- a/include/ui/surface.h +++ b/include/ui/surface.h @@ -23,10 +23,8 @@ typedef struct DisplaySurface { 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_from(int width, int height, 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 c85f964bed..d26690ce20 100644 --- a/meson.build +++ b/meson.build @@ -1037,7 +1037,7 @@ if not get_option('gio').auto() or have_system 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 0390bdcb41..f7fc873bfb 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 #include #include "libqtest.h" +#ifndef WIN32 +#include +#endif #include "ui/dbus-display1.h" static GDBusConnection* @@ -82,6 +87,7 @@ typedef struct TestDBusConsoleRegister { GThread *thread; GDBusConnection *listener_conn; GDBusObjectManagerServer *server; + bool with_map; } TestDBusConsoleRegister; static gboolean listener_handle_scanout( @@ -94,13 +100,49 @@ static gboolean listener_handle_scanout( 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 @@ test_dbus_console_setup_listener(TestDBusConsoleRegister *test) 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 @@ test_dbus_console_registered(GObject *source_object, 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 @@ test_dbus_p2p_server_setup_thread(gpointer data) } 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 @@ test_dbus_display_console(void) 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 @@ main(int argc, char **argv) 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 105a0e2c70..5165f17125 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 @@ qemu_graphic_console_init(Object *obj) { } -#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->share_handle = handle; + surface->share_handle_offset = offset; - surface->handle = h; - surface->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 *qemu_create_displaysurface_from(int width, int height, 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 *qemu_create_displaysurface_pixman(pixman_image_t *image) 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 578b67f62b..5eb1d40d16 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -41,7 +41,7 @@ struct _DBusDisplayConsole { DisplayChangeListener dcl; DBusDisplay *display; - GHashTable *listeners; + GPtrArray *listeners; QemuDBusDisplay1Console *iface; QemuDBusDisplay1Keyboard *iface_kbd; @@ -142,8 +142,7 @@ dbus_display_console_init(DBusDisplayConsole *object) { 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 @@ dbus_display_console_dispose(GObject *object) 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 @@ listener_vanished_cb(DBusDisplayListener *listener) 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 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, 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 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, 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 ce35d64eea..e70f2848b7 100644 --- a/ui/dbus-display1.xml +++ b/ui/dbus-display1.xml @@ -469,6 +469,53 @@ + + + + + + + + + + + + + + + + + + + + + + + +