Merge tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging
ui & audio fixes
# -----BEGIN PGP SIGNATURE-----
#
# iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmS1N6IcHG1hcmNhbmRy
# ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5SYsD/44+FIoik9v478pZDTp
# CpaezX+DfsW1zee4Ana5eKJkrVld/xEa6i9/msfUHy12bha+kiJ4a6wLu3H4KRZc
# vX/t6sehG2wNcsV5wLhfcjsKzaNUkYpnxLhIZ0fOYXKA0fSBuM/Bsj6zzGTG6kQA
# nt/cK58r1wy63V7werZbA7BI8PF0opDUw5SrZqN0GeoN5clbdyLdcXvD50ibvkDf
# eOVjNQ3QH8IbihmgBVm1wUV8hTuvYRpBmeLJyk7NeR4bnPl3XGIAgtAY8hJL5LdY
# Bm+I3AuxMSskVcag/22QR8mGR0HhDbf3NZauw4ND3LhSctvNN5syaKHVnY5a9aGe
# QLVEV9pxXGfqzWQcsD2HmbupRoBihmp6+WsIpV8ZtuSfeD6slyObw+lqarSQL9b5
# 2C4UFmGCsCOk8rrczZRDp9IWbm23toc/QcQZtg/LhdlCr8nM+7m0XtyEY5WtT3U1
# 8rJEmjOHHqlD4cVBathc8+ZRjKr8HFRRo1ed6WKMoP6voTsw2fiR7I3Vdc7jO7h9
# A1lMiMoLdAXi0Q2VqbmBdLMgb4fXtLzYl2mcbzW0aEUm8uyUfDy2bkVIIUopu40M
# pROmLjaUzUVE3CruckBUCvoYZtJ5hBtvy3W2k8drBNylnP5B8tEqpxpPb+tSFk82
# xgT6oLp8En8asE293eaACbswuw==
# =W2Xa
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 17 Jul 2023 01:44:18 PM 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]
* tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu:
audio/pw: improve channel position code
audio/pw: remove wrong comment
audio/pw: simplify error reporting in stream creation
audio/pw: add more error reporting
audio/pw: factorize some common code
audio/pw: add more details on error
audio/pw: trace during init before calling pipewire API
audio/pw: needless check for NULL
audio/pw: drop needless case statement
audio/pw: Pipewire->PipeWire case fix for user-visible text
tests/lcitool: add pipewire
libvirt-ci: update submodule to cover pipewire
ui/gtk: skip refresh if new dmabuf has been submitted
ui/gtk: set scanout-mode right before scheduling draw
virtio-gpu-udmabuf: correct naming of QemuDmaBuf size properties
virtio-gpu: replace the surface with null surface when resetting
ui/gtk: Make sure the right EGL context is currently bound
ui/vnc-clipboard: fix infinite loop in inflate_buffer (CVE-2023-3255)
virtio-gpu: fix potential divide-by-zero regression
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
diff --git a/audio/pwaudio.c b/audio/pwaudio.c
index 1d108bd..b6a3873 100644
--- a/audio/pwaudio.c
+++ b/audio/pwaudio.c
@@ -1,5 +1,5 @@
/*
- * QEMU Pipewire audio driver
+ * QEMU PipeWire audio driver
*
* Copyright (c) 2023 Red Hat Inc.
*
@@ -66,6 +66,9 @@
PWVoice v;
} PWVoiceIn;
+#define PW_VOICE_IN(v) ((PWVoiceIn *)v)
+#define PW_VOICE_OUT(v) ((PWVoiceOut *)v)
+
static void
stream_destroy(void *data)
{
@@ -197,16 +200,6 @@
trace_pw_state_changed(pw_stream_get_node_id(v->stream),
pw_stream_state_as_string(state));
-
- switch (state) {
- case PW_STREAM_STATE_ERROR:
- case PW_STREAM_STATE_UNCONNECTED:
- break;
- case PW_STREAM_STATE_PAUSED:
- case PW_STREAM_STATE_CONNECTING:
- case PW_STREAM_STATE_STREAMING:
- break;
- }
}
static const struct pw_stream_events capture_stream_events = {
@@ -424,8 +417,8 @@
}
static int
-create_stream(pwaudio *c, PWVoice *v, const char *stream_name,
- const char *name, enum spa_direction dir)
+qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name,
+ const char *name, enum spa_direction dir)
{
int res;
uint32_t n_params;
@@ -436,6 +429,10 @@
struct pw_properties *props;
props = pw_properties_new(NULL, NULL);
+ if (!props) {
+ error_report("Failed to create PW properties: %s", g_strerror(errno));
+ return -1;
+ }
/* 75% of the timer period for faster updates */
buf_samples = (uint64_t)v->g->dev->timer_period * v->info.rate
@@ -448,8 +445,8 @@
pw_properties_set(props, PW_KEY_TARGET_OBJECT, name);
}
v->stream = pw_stream_new(c->core, stream_name, props);
-
if (v->stream == NULL) {
+ error_report("Failed to create PW stream: %s", g_strerror(errno));
return -1;
}
@@ -477,6 +474,7 @@
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS, params, n_params);
if (res < 0) {
+ error_report("Failed to connect PW stream: %s", g_strerror(errno));
pw_stream_destroy(v->stream);
return -1;
}
@@ -484,71 +482,37 @@
return 0;
}
-static int
-qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name,
- const char *name, enum spa_direction dir)
+static void
+qpw_set_position(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS])
{
- int r;
-
- switch (v->info.channels) {
+ memcpy(position, (uint32_t[SPA_AUDIO_MAX_CHANNELS]) { SPA_AUDIO_CHANNEL_UNKNOWN, },
+ sizeof(uint32_t) * SPA_AUDIO_MAX_CHANNELS);
+ /*
+ * TODO: This currently expects the only frontend supporting more than 2
+ * channels is the usb-audio. We will need some means to set channel
+ * order when a new frontend gains multi-channel support.
+ */
+ switch (channels) {
case 8:
- v->info.position[0] = SPA_AUDIO_CHANNEL_FL;
- v->info.position[1] = SPA_AUDIO_CHANNEL_FR;
- v->info.position[2] = SPA_AUDIO_CHANNEL_FC;
- v->info.position[3] = SPA_AUDIO_CHANNEL_LFE;
- v->info.position[4] = SPA_AUDIO_CHANNEL_RL;
- v->info.position[5] = SPA_AUDIO_CHANNEL_RR;
- v->info.position[6] = SPA_AUDIO_CHANNEL_SL;
- v->info.position[7] = SPA_AUDIO_CHANNEL_SR;
- break;
+ position[6] = SPA_AUDIO_CHANNEL_SL;
+ position[7] = SPA_AUDIO_CHANNEL_SR;
+ /* fallthrough */
case 6:
- v->info.position[0] = SPA_AUDIO_CHANNEL_FL;
- v->info.position[1] = SPA_AUDIO_CHANNEL_FR;
- v->info.position[2] = SPA_AUDIO_CHANNEL_FC;
- v->info.position[3] = SPA_AUDIO_CHANNEL_LFE;
- v->info.position[4] = SPA_AUDIO_CHANNEL_RL;
- v->info.position[5] = SPA_AUDIO_CHANNEL_RR;
- break;
- case 5:
- v->info.position[0] = SPA_AUDIO_CHANNEL_FL;
- v->info.position[1] = SPA_AUDIO_CHANNEL_FR;
- v->info.position[2] = SPA_AUDIO_CHANNEL_FC;
- v->info.position[3] = SPA_AUDIO_CHANNEL_LFE;
- v->info.position[4] = SPA_AUDIO_CHANNEL_RC;
- break;
- case 4:
- v->info.position[0] = SPA_AUDIO_CHANNEL_FL;
- v->info.position[1] = SPA_AUDIO_CHANNEL_FR;
- v->info.position[2] = SPA_AUDIO_CHANNEL_FC;
- v->info.position[3] = SPA_AUDIO_CHANNEL_RC;
- break;
- case 3:
- v->info.position[0] = SPA_AUDIO_CHANNEL_FL;
- v->info.position[1] = SPA_AUDIO_CHANNEL_FR;
- v->info.position[2] = SPA_AUDIO_CHANNEL_LFE;
- break;
+ position[2] = SPA_AUDIO_CHANNEL_FC;
+ position[3] = SPA_AUDIO_CHANNEL_LFE;
+ position[4] = SPA_AUDIO_CHANNEL_RL;
+ position[5] = SPA_AUDIO_CHANNEL_RR;
+ /* fallthrough */
case 2:
- v->info.position[0] = SPA_AUDIO_CHANNEL_FL;
- v->info.position[1] = SPA_AUDIO_CHANNEL_FR;
+ position[0] = SPA_AUDIO_CHANNEL_FL;
+ position[1] = SPA_AUDIO_CHANNEL_FR;
break;
case 1:
- v->info.position[0] = SPA_AUDIO_CHANNEL_MONO;
+ position[0] = SPA_AUDIO_CHANNEL_MONO;
break;
default:
- for (size_t i = 0; i < v->info.channels; i++) {
- v->info.position[i] = SPA_AUDIO_CHANNEL_UNKNOWN;
- }
- break;
+ dolog("Internal error: unsupported channel count %d\n", channels);
}
-
- /* create a new unconnected pwstream */
- r = create_stream(c, v, stream_name, name, dir);
- if (r < 0) {
- AUD_log(AUDIO_CAP, "Failed to create stream.");
- return -1;
- }
-
- return r;
}
static int
@@ -566,6 +530,7 @@
v->info.format = audfmt_to_pw(as->fmt, as->endianness);
v->info.channels = as->nchannels;
+ qpw_set_position(as->nchannels, v->info.position);
v->info.rate = as->freq;
obt_as.fmt =
@@ -579,7 +544,6 @@
r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id,
ppdo->name, SPA_DIRECTION_OUTPUT);
if (r < 0) {
- error_report("qpw_stream_new for playback failed");
pw_thread_loop_unlock(c->thread_loop);
return -1;
}
@@ -613,6 +577,7 @@
v->info.format = audfmt_to_pw(as->fmt, as->endianness);
v->info.channels = as->nchannels;
+ qpw_set_position(as->nchannels, v->info.position);
v->info.rate = as->freq;
obt_as.fmt =
@@ -623,7 +588,6 @@
r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id,
ppdo->name, SPA_DIRECTION_INPUT);
if (r < 0) {
- error_report("qpw_stream_new for recording failed");
pw_thread_loop_unlock(c->thread_loop);
return -1;
}
@@ -640,40 +604,34 @@
}
static void
+qpw_voice_fini(PWVoice *v)
+{
+ pwaudio *c = v->g;
+
+ if (!v->stream) {
+ return;
+ }
+ pw_thread_loop_lock(c->thread_loop);
+ pw_stream_destroy(v->stream);
+ v->stream = NULL;
+ pw_thread_loop_unlock(c->thread_loop);
+}
+
+static void
qpw_fini_out(HWVoiceOut *hw)
{
- PWVoiceOut *pw = (PWVoiceOut *) hw;
- PWVoice *v = &pw->v;
-
- if (v->stream) {
- pwaudio *c = v->g;
- pw_thread_loop_lock(c->thread_loop);
- pw_stream_destroy(v->stream);
- v->stream = NULL;
- pw_thread_loop_unlock(c->thread_loop);
- }
+ qpw_voice_fini(&PW_VOICE_OUT(hw)->v);
}
static void
qpw_fini_in(HWVoiceIn *hw)
{
- PWVoiceIn *pw = (PWVoiceIn *) hw;
- PWVoice *v = &pw->v;
-
- if (v->stream) {
- pwaudio *c = v->g;
- pw_thread_loop_lock(c->thread_loop);
- pw_stream_destroy(v->stream);
- v->stream = NULL;
- pw_thread_loop_unlock(c->thread_loop);
- }
+ qpw_voice_fini(&PW_VOICE_IN(hw)->v);
}
static void
-qpw_enable_out(HWVoiceOut *hw, bool enable)
+qpw_voice_set_enabled(PWVoice *v, bool enable)
{
- PWVoiceOut *po = (PWVoiceOut *) hw;
- PWVoice *v = &po->v;
pwaudio *c = v->g;
pw_thread_loop_lock(c->thread_loop);
pw_stream_set_active(v->stream, enable);
@@ -681,64 +639,50 @@
}
static void
+qpw_enable_out(HWVoiceOut *hw, bool enable)
+{
+ qpw_voice_set_enabled(&PW_VOICE_OUT(hw)->v, enable);
+}
+
+static void
qpw_enable_in(HWVoiceIn *hw, bool enable)
{
- PWVoiceIn *pi = (PWVoiceIn *) hw;
- PWVoice *v = &pi->v;
+ qpw_voice_set_enabled(&PW_VOICE_IN(hw)->v, enable);
+}
+
+static void
+qpw_voice_set_volume(PWVoice *v, Volume *vol)
+{
pwaudio *c = v->g;
+ int i, ret;
+
pw_thread_loop_lock(c->thread_loop);
- pw_stream_set_active(v->stream, enable);
+ v->volume.channels = vol->channels;
+
+ for (i = 0; i < vol->channels; ++i) {
+ v->volume.values[i] = (float)vol->vol[i] / 255;
+ }
+
+ ret = pw_stream_set_control(v->stream,
+ SPA_PROP_channelVolumes, v->volume.channels, v->volume.values, 0);
+ trace_pw_vol(ret == 0 ? "success" : "failed");
+
+ v->muted = vol->mute;
+ float val = v->muted ? 1.f : 0.f;
+ ret = pw_stream_set_control(v->stream, SPA_PROP_mute, 1, &val, 0);
pw_thread_loop_unlock(c->thread_loop);
}
static void
qpw_volume_out(HWVoiceOut *hw, Volume *vol)
{
- PWVoiceOut *pw = (PWVoiceOut *) hw;
- PWVoice *v = &pw->v;
- pwaudio *c = v->g;
- int i, ret;
-
- pw_thread_loop_lock(c->thread_loop);
- v->volume.channels = vol->channels;
-
- for (i = 0; i < vol->channels; ++i) {
- v->volume.values[i] = (float)vol->vol[i] / 255;
- }
-
- ret = pw_stream_set_control(v->stream,
- SPA_PROP_channelVolumes, v->volume.channels, v->volume.values, 0);
- trace_pw_vol(ret == 0 ? "success" : "failed");
-
- v->muted = vol->mute;
- float val = v->muted ? 1.f : 0.f;
- ret = pw_stream_set_control(v->stream, SPA_PROP_mute, 1, &val, 0);
- pw_thread_loop_unlock(c->thread_loop);
+ qpw_voice_set_volume(&PW_VOICE_OUT(hw)->v, vol);
}
static void
qpw_volume_in(HWVoiceIn *hw, Volume *vol)
{
- PWVoiceIn *pw = (PWVoiceIn *) hw;
- PWVoice *v = &pw->v;
- pwaudio *c = v->g;
- int i, ret;
-
- pw_thread_loop_lock(c->thread_loop);
- v->volume.channels = vol->channels;
-
- for (i = 0; i < vol->channels; ++i) {
- v->volume.values[i] = (float)vol->vol[i] / 255;
- }
-
- ret = pw_stream_set_control(v->stream,
- SPA_PROP_channelVolumes, v->volume.channels, v->volume.values, 0);
- trace_pw_vol(ret == 0 ? "success" : "failed");
-
- v->muted = vol->mute;
- float val = v->muted ? 1.f : 0.f;
- ret = pw_stream_set_control(v->stream, SPA_PROP_mute, 1, &val, 0);
- pw_thread_loop_unlock(c->thread_loop);
+ qpw_voice_set_volume(&PW_VOICE_IN(hw)->v, vol);
}
static int wait_resync(pwaudio *pw)
@@ -760,6 +704,7 @@
}
return 0;
}
+
static void
on_core_error(void *data, uint32_t id, int seq, int res, const char *message)
{
@@ -794,27 +739,28 @@
qpw_audio_init(Audiodev *dev)
{
g_autofree pwaudio *pw = g_new0(pwaudio, 1);
+
+ assert(dev->driver == AUDIODEV_DRIVER_PIPEWIRE);
+ trace_pw_audio_init();
+
pw_init(NULL, NULL);
- trace_pw_audio_init();
- assert(dev->driver == AUDIODEV_DRIVER_PIPEWIRE);
-
pw->dev = dev;
- pw->thread_loop = pw_thread_loop_new("Pipewire thread loop", NULL);
+ pw->thread_loop = pw_thread_loop_new("PipeWire thread loop", NULL);
if (pw->thread_loop == NULL) {
- error_report("Could not create Pipewire loop");
+ error_report("Could not create PipeWire loop: %s", g_strerror(errno));
goto fail;
}
pw->context =
pw_context_new(pw_thread_loop_get_loop(pw->thread_loop), NULL, 0);
if (pw->context == NULL) {
- error_report("Could not create Pipewire context");
+ error_report("Could not create PipeWire context: %s", g_strerror(errno));
goto fail;
}
if (pw_thread_loop_start(pw->thread_loop) < 0) {
- error_report("Could not start Pipewire loop");
+ error_report("Could not start PipeWire loop: %s", g_strerror(errno));
goto fail;
}
@@ -844,12 +790,8 @@
if (pw->thread_loop) {
pw_thread_loop_stop(pw->thread_loop);
}
- if (pw->context) {
- g_clear_pointer(&pw->context, pw_context_destroy);
- }
- if (pw->thread_loop) {
- g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy);
- }
+ g_clear_pointer(&pw->context, pw_context_destroy);
+ g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy);
return NULL;
}
diff --git a/audio/trace-events b/audio/trace-events
index 85dbb50..ab04f02 100644
--- a/audio/trace-events
+++ b/audio/trace-events
@@ -24,7 +24,7 @@
pw_write(int32_t filled, int32_t avail, uint32_t index, size_t len) "filled=%d avail=%d index=%u len=%zu"
pw_vol(const char *ret) "set volume: %s"
pw_period(uint64_t quantum, uint32_t rate) "period =%" PRIu64 "/%u"
-pw_audio_init(void) "Initialize Pipewire context"
+pw_audio_init(void) "Initialize PipeWire context"
# audio.c
audio_timer_start(int interval) "interval %d ms"
diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index ef1a740..d51184d 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -181,13 +181,13 @@
}
dmabuf = g_new0(VGPUDMABuf, 1);
- dmabuf->buf.width = fb->width;
- dmabuf->buf.height = fb->height;
+ dmabuf->buf.width = r->width;
+ dmabuf->buf.height = r->height;
dmabuf->buf.stride = fb->stride;
dmabuf->buf.x = r->x;
dmabuf->buf.y = r->y;
- dmabuf->buf.scanout_width = r->width;
- dmabuf->buf.scanout_height = r->height;
+ dmabuf->buf.backing_width = fb->width;
+ dmabuf->buf.backing_height = fb->height;
dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format);
dmabuf->buf.fd = res->dmabuf_fd;
dmabuf->buf.allow_fences = true;
@@ -218,8 +218,8 @@
g->dmabuf.primary[scanout_id] = new_primary;
qemu_console_resize(scanout->con,
- new_primary->buf.scanout_width,
- new_primary->buf.scanout_height);
+ new_primary->buf.width,
+ new_primary->buf.height);
dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf);
if (old_primary) {
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index befa7d6..e8603d7 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -303,10 +303,11 @@
goto end;
}
#endif
- res->image = pixman_image_create_bits(pformat,
- c2d.width,
- c2d.height,
- bits, res->hostmem / c2d.height);
+ 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);
@@ -1272,9 +1273,10 @@
return -EINVAL;
}
#endif
- res->image = pixman_image_create_bits(pformat,
- res->width, res->height,
- bits, res->hostmem / res->height);
+ 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;
@@ -1395,6 +1397,7 @@
VirtIOGPU *g = VIRTIO_GPU(vdev);
struct virtio_gpu_simple_resource *res, *tmp;
struct virtio_gpu_ctrl_command *cmd;
+ int i = 0;
QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) {
virtio_gpu_resource_destroy(g, res);
@@ -1413,6 +1416,10 @@
g_free(cmd);
}
+ for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
+ dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL);
+ }
+
virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev));
}
diff --git a/include/ui/console.h b/include/ui/console.h
index f27b2aa..3e8b22d 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -201,8 +201,8 @@
uint32_t texture;
uint32_t x;
uint32_t y;
- uint32_t scanout_width;
- uint32_t scanout_height;
+ uint32_t backing_width;
+ uint32_t backing_height;
bool y0_top;
void *sync;
int fence_fd;
diff --git a/meson.build b/meson.build
index 5fcdb37..98e68ef 100644
--- a/meson.build
+++ b/meson.build
@@ -4251,7 +4251,7 @@
summary_info += {'ALSA support': alsa}
summary_info += {'PulseAudio support': pulse}
endif
-summary_info += {'Pipewire support': pipewire}
+summary_info += {'PipeWire support': pipewire}
summary_info += {'JACK support': jack}
summary(summary_info, bool_yn: true, section: 'Audio backends')
diff --git a/meson_options.txt b/meson_options.txt
index bbb5c7e..aaea5dd 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -267,7 +267,7 @@
option('pa', type: 'feature', value: 'auto',
description: 'PulseAudio sound support')
option('pipewire', type: 'feature', value: 'auto',
- description: 'Pipewire sound support')
+ description: 'PipeWire sound support')
option('sndio', type: 'feature', value: 'auto',
description: 'sndio sound support')
diff --git a/qapi/audio.json b/qapi/audio.json
index 534f10d..519697c 100644
--- a/qapi/audio.json
+++ b/qapi/audio.json
@@ -328,17 +328,17 @@
##
# @AudiodevPipewirePerDirectionOptions:
#
-# Options of the Pipewire backend that are used for both playback and
+# Options of the PipeWire backend that are used for both playback and
# recording.
#
# @name: name of the sink/source to use
#
-# @stream-name: name of the Pipewire stream created by qemu. Can be
-# used to identify the stream in Pipewire when you create multiple
-# Pipewire devices or run multiple qemu instances (default:
+# @stream-name: name of the PipeWire stream created by qemu. Can be
+# used to identify the stream in PipeWire when you create multiple
+# PipeWire devices or run multiple qemu instances (default:
# audiodev's id)
#
-# @latency: latency you want Pipewire to achieve in microseconds
+# @latency: latency you want PipeWire to achieve in microseconds
# (default 46000)
#
# Since: 8.1
@@ -353,7 +353,7 @@
##
# @AudiodevPipewireOptions:
#
-# Options of the Pipewire audio backend.
+# Options of the PipeWire audio backend.
#
# @in: options of the capture stream
#
diff --git a/qemu-options.hx b/qemu-options.hx
index f8f384e..29b98c3 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -963,10 +963,10 @@
to honor this value but actual latencies may be lower or higher.
``-audiodev pipewire,id=id[,prop[=value][,...]]``
- Creates a backend using Pipewire. This backend is available on
+ Creates a backend using PipeWire. This backend is available on
most systems.
- Pipewire specific options are:
+ PipeWire specific options are:
``in|out.latency=usecs``
Desired latency in microseconds.
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 7dd5709..9da3fe2 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -145,7 +145,7 @@
printf "%s\n" ' oss OSS sound support'
printf "%s\n" ' pa PulseAudio sound support'
printf "%s\n" ' parallels parallels image format support'
- printf "%s\n" ' pipewire Pipewire sound support'
+ printf "%s\n" ' pipewire PipeWire sound support'
printf "%s\n" ' png PNG support with libpng'
printf "%s\n" ' pvrdma Enable PVRDMA support'
printf "%s\n" ' qcow1 qcow1 image format support'
diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker
index 43370f7..fa455f1 100644
--- a/tests/docker/dockerfiles/alpine.docker
+++ b/tests/docker/dockerfiles/alpine.docker
@@ -77,6 +77,7 @@
numactl-dev \
openssh-client \
pcre-dev \
+ pipewire-dev \
pixman-dev \
pkgconf \
pulseaudio-dev \
diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker
index 78f454b..da7dc81 100644
--- a/tests/docker/dockerfiles/centos8.docker
+++ b/tests/docker/dockerfiles/centos8.docker
@@ -90,6 +90,7 @@
openssh-clients \
pam-devel \
pcre-static \
+ pipewire-devel \
pixman-devel \
pkgconfig \
pulseaudio-libs-devel \
diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker
index 016c232..b7bdc01 100644
--- a/tests/docker/dockerfiles/debian-amd64-cross.docker
+++ b/tests/docker/dockerfiles/debian-amd64-cross.docker
@@ -116,6 +116,7 @@
libnfs-dev:amd64 \
libnuma-dev:amd64 \
libpam0g-dev:amd64 \
+ libpipewire-0.3-dev:amd64 \
libpixman-1-dev:amd64 \
libpmem-dev:amd64 \
libpng-dev:amd64 \
diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker
index e39871c..6d2fa38 100644
--- a/tests/docker/dockerfiles/debian-amd64.docker
+++ b/tests/docker/dockerfiles/debian-amd64.docker
@@ -69,6 +69,7 @@
libnuma-dev \
libpam0g-dev \
libpcre2-dev \
+ libpipewire-0.3-dev \
libpixman-1-dev \
libpmem-dev \
libpng-dev \
diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker
index 3c114ef..68165c2f 100644
--- a/tests/docker/dockerfiles/debian-arm64-cross.docker
+++ b/tests/docker/dockerfiles/debian-arm64-cross.docker
@@ -116,6 +116,7 @@
libnfs-dev:arm64 \
libnuma-dev:arm64 \
libpam0g-dev:arm64 \
+ libpipewire-0.3-dev:arm64 \
libpixman-1-dev:arm64 \
libpng-dev:arm64 \
libpulse-dev:arm64 \
diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker
index dfbd47d..2fb6530 100644
--- a/tests/docker/dockerfiles/debian-armel-cross.docker
+++ b/tests/docker/dockerfiles/debian-armel-cross.docker
@@ -116,6 +116,7 @@
libnfs-dev:armel \
libnuma-dev:armel \
libpam0g-dev:armel \
+ libpipewire-0.3-dev:armel \
libpixman-1-dev:armel \
libpng-dev:armel \
libpulse-dev:armel \
diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker
index 4e0084e..df77ccb 100644
--- a/tests/docker/dockerfiles/debian-armhf-cross.docker
+++ b/tests/docker/dockerfiles/debian-armhf-cross.docker
@@ -116,6 +116,7 @@
libnfs-dev:armhf \
libnuma-dev:armhf \
libpam0g-dev:armhf \
+ libpipewire-0.3-dev:armhf \
libpixman-1-dev:armhf \
libpng-dev:armhf \
libpulse-dev:armhf \
diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker
index 88adf33..63a3d7a 100644
--- a/tests/docker/dockerfiles/debian-mips64el-cross.docker
+++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker
@@ -115,6 +115,7 @@
libnfs-dev:mips64el \
libnuma-dev:mips64el \
libpam0g-dev:mips64el \
+ libpipewire-0.3-dev:mips64el \
libpixman-1-dev:mips64el \
libpng-dev:mips64el \
libpulse-dev:mips64el \
diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker
index 256e8b5..ac87bbb 100644
--- a/tests/docker/dockerfiles/debian-mipsel-cross.docker
+++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker
@@ -115,6 +115,7 @@
libnfs-dev:mipsel \
libnuma-dev:mipsel \
libpam0g-dev:mipsel \
+ libpipewire-0.3-dev:mipsel \
libpixman-1-dev:mipsel \
libpng-dev:mipsel \
libpulse-dev:mipsel \
diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker
index 4d19cd2..def11f1 100644
--- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker
+++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker
@@ -116,6 +116,7 @@
libnfs-dev:ppc64el \
libnuma-dev:ppc64el \
libpam0g-dev:ppc64el \
+ libpipewire-0.3-dev:ppc64el \
libpixman-1-dev:ppc64el \
libpng-dev:ppc64el \
libpulse-dev:ppc64el \
diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker
index 642bbde..80028e1 100644
--- a/tests/docker/dockerfiles/debian-s390x-cross.docker
+++ b/tests/docker/dockerfiles/debian-s390x-cross.docker
@@ -116,6 +116,7 @@
libnfs-dev:s390x \
libnuma-dev:s390x \
libpam0g-dev:s390x \
+ libpipewire-0.3-dev:s390x \
libpixman-1-dev:s390x \
libpng-dev:s390x \
libpulse-dev:s390x \
diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker
index 8a35a17..c5b6c96 100644
--- a/tests/docker/dockerfiles/fedora.docker
+++ b/tests/docker/dockerfiles/fedora.docker
@@ -98,6 +98,7 @@
openssh-clients \
pam-devel \
pcre-static \
+ pipewire-devel \
pixman-devel \
pkgconfig \
pulseaudio-libs-devel \
diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker
index 185abe5..37c83e5 100644
--- a/tests/docker/dockerfiles/opensuse-leap.docker
+++ b/tests/docker/dockerfiles/opensuse-leap.docker
@@ -88,6 +88,7 @@
openssh \
pam-devel \
pcre-devel-static \
+ pipewire-devel \
pkgconfig \
python39-base \
python39-pip \
diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker
index 1d442cd..8f93987 100644
--- a/tests/docker/dockerfiles/ubuntu2204.docker
+++ b/tests/docker/dockerfiles/ubuntu2204.docker
@@ -69,6 +69,7 @@
libnuma-dev \
libpam0g-dev \
libpcre2-dev \
+ libpipewire-0.3-dev \
libpixman-1-dev \
libpmem-dev \
libpng-dev \
diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci
index b0f44f9..9bff3b7 160000
--- a/tests/lcitool/libvirt-ci
+++ b/tests/lcitool/libvirt-ci
@@ -1 +1 @@
-Subproject commit b0f44f929a81c0a604fb7fbf8afc34d37ab0eae9
+Subproject commit 9bff3b763b5531a1490e238bfbf77306dc3a6dbb
diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml
index 21fd3d2..d452a89 100644
--- a/tests/lcitool/projects/qemu.yml
+++ b/tests/lcitool/projects/qemu.yml
@@ -85,6 +85,7 @@
- pam
- pcre-static
- pixman
+ - pipewire
- pkg-config
- pulseaudio
- python3
diff --git a/ui/console.c b/ui/console.c
index c1544e0..8da2170 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1898,6 +1898,7 @@
static const char placeholder_msg[] = "Display output is not active.";
DisplayState *s = con->ds;
DisplaySurface *old_surface = con->surface;
+ DisplaySurface *new_surface = surface;
DisplayChangeListener *dcl;
int width;
int height;
@@ -1911,19 +1912,19 @@
height = 480;
}
- surface = qemu_create_placeholder_surface(width, height, placeholder_msg);
+ new_surface = qemu_create_placeholder_surface(width, height, placeholder_msg);
}
- assert(old_surface != surface);
+ assert(old_surface != new_surface);
con->scanout.kind = SCANOUT_SURFACE;
- con->surface = surface;
- dpy_gfx_create_texture(con, surface);
+ con->surface = new_surface;
+ dpy_gfx_create_texture(con, new_surface);
QLIST_FOREACH(dcl, &s->listeners, next) {
if (con != (dcl->con ? dcl->con : active_console)) {
continue;
}
- displaychangelistener_gfx_switch(dcl, surface, FALSE);
+ displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE);
}
dpy_gfx_destroy_texture(con, old_surface);
qemu_free_displaysurface(old_surface);
diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
index 0240c39..68ff343 100644
--- a/ui/dbus-listener.c
+++ b/ui/dbus-listener.c
@@ -415,13 +415,13 @@
backing_width, backing_height, x, y, w, h);
#ifdef CONFIG_GBM
QemuDmaBuf dmabuf = {
- .width = backing_width,
- .height = backing_height,
+ .width = w,
+ .height = h,
.y0_top = backing_y_0_top,
.x = x,
.y = y,
- .scanout_width = w,
- .scanout_height = h,
+ .backing_width = backing_width,
+ .backing_height = backing_height,
};
assert(tex_id);
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index 8f9fbf5..3d19dbe 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -148,8 +148,8 @@
if (src->dmabuf) {
x1 = src->dmabuf->x;
y1 = src->dmabuf->y;
- w = src->dmabuf->scanout_width;
- h = src->dmabuf->scanout_height;
+ w = src->dmabuf->width;
+ h = src->dmabuf->height;
}
w = (x1 + w) > src->width ? src->width - x1 : w;
@@ -314,9 +314,9 @@
}
attrs[i++] = EGL_WIDTH;
- attrs[i++] = dmabuf->width;
+ attrs[i++] = dmabuf->backing_width;
attrs[i++] = EGL_HEIGHT;
- attrs[i++] = dmabuf->height;
+ attrs[i++] = dmabuf->backing_height;
attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT;
attrs[i++] = dmabuf->fourcc;
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index d59b8cd..4c29ac1 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -32,6 +32,8 @@
vc->gfx.scanout_mode = scanout;
if (!vc->gfx.scanout_mode) {
+ eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, vc->gfx.ectx);
egl_fb_destroy(&vc->gfx.guest_fb);
if (vc->gfx.surface) {
surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
@@ -135,6 +137,8 @@
vc->gfx.esurface, vc->gfx.ectx);
surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h);
vc->gfx.glupdates++;
+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
void gd_egl_refresh(DisplayChangeListener *dcl)
@@ -144,6 +148,10 @@
gd_update_monitor_refresh_rate(
vc, vc->window ? vc->window : vc->gfx.drawing_area);
+ if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) {
+ return;
+ }
+
if (!vc->gfx.esurface) {
gd_egl_init(vc);
if (!vc->gfx.esurface) {
@@ -238,7 +246,6 @@
eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
vc->gfx.esurface, vc->gfx.ectx);
- gtk_egl_set_scanout_mode(vc, true);
egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height,
backing_id, false);
}
@@ -258,9 +265,10 @@
}
gd_egl_scanout_texture(dcl, dmabuf->texture,
- dmabuf->y0_top, dmabuf->width, dmabuf->height,
- dmabuf->x, dmabuf->y, dmabuf->scanout_width,
- dmabuf->scanout_height, NULL);
+ dmabuf->y0_top,
+ dmabuf->backing_width, dmabuf->backing_height,
+ dmabuf->x, dmabuf->y, dmabuf->width,
+ dmabuf->height, NULL);
if (dmabuf->allow_fences) {
vc->gfx.guest_fb.dmabuf = dmabuf;
@@ -280,7 +288,8 @@
if (!dmabuf->texture) {
return;
}
- egl_fb_setup_for_tex(&vc->gfx.cursor_fb, dmabuf->width, dmabuf->height,
+ egl_fb_setup_for_tex(&vc->gfx.cursor_fb,
+ dmabuf->backing_width, dmabuf->backing_height,
dmabuf->texture, false);
} else {
egl_fb_destroy(&vc->gfx.cursor_fb);
@@ -347,6 +356,7 @@
if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) {
graphic_hw_gl_block(vc->gfx.dcl.con, true);
vc->gfx.guest_fb.dmabuf->draw_submitted = true;
+ gtk_egl_set_scanout_mode(vc, true);
gtk_widget_queue_draw_area(area, x, y, w, h);
return;
}
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index 7367dfd..1ce34a2 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -26,6 +26,7 @@
vc->gfx.scanout_mode = scanout;
if (!vc->gfx.scanout_mode) {
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
egl_fb_destroy(&vc->gfx.guest_fb);
if (vc->gfx.surface) {
surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
@@ -115,6 +116,7 @@
gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h);
vc->gfx.glupdates++;
+ gdk_gl_context_clear_current();
}
void gd_gl_area_refresh(DisplayChangeListener *dcl)
@@ -123,6 +125,10 @@
gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area);
+ if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) {
+ return;
+ }
+
if (!vc->gfx.gls) {
if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
return;
@@ -262,7 +268,6 @@
return;
}
- gtk_gl_area_set_scanout_mode(vc, true);
egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height,
backing_id, false);
}
@@ -282,6 +287,7 @@
if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) {
graphic_hw_gl_block(vc->gfx.dcl.con, true);
vc->gfx.guest_fb.dmabuf->draw_submitted = true;
+ gtk_gl_area_set_scanout_mode(vc, true);
}
gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
}
@@ -299,9 +305,10 @@
}
gd_gl_area_scanout_texture(dcl, dmabuf->texture,
- dmabuf->y0_top, dmabuf->width, dmabuf->height,
- dmabuf->x, dmabuf->y, dmabuf->scanout_width,
- dmabuf->scanout_height, NULL);
+ dmabuf->y0_top,
+ dmabuf->backing_width, dmabuf->backing_height,
+ dmabuf->x, dmabuf->y, dmabuf->width,
+ dmabuf->height, NULL);
if (dmabuf->allow_fences) {
vc->gfx.guest_fb.dmabuf = dmabuf;
diff --git a/ui/vnc-clipboard.c b/ui/vnc-clipboard.c
index 8aeadfa..c759be3 100644
--- a/ui/vnc-clipboard.c
+++ b/ui/vnc-clipboard.c
@@ -50,8 +50,11 @@
ret = inflate(&stream, Z_FINISH);
switch (ret) {
case Z_OK:
- case Z_STREAM_END:
break;
+ case Z_STREAM_END:
+ *size = stream.total_out;
+ inflateEnd(&stream);
+ return out;
case Z_BUF_ERROR:
out_len <<= 1;
if (out_len > (1 << 20)) {
@@ -66,11 +69,6 @@
}
}
- *size = stream.total_out;
- inflateEnd(&stream);
-
- return out;
-
err_end:
inflateEnd(&stream);
err: