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-----

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>
This commit is contained in:
Richard Henderson 2023-07-17 15:44:54 +01:00
commit f44ccac2c0
32 changed files with 187 additions and 207 deletions

View File

@ -1,5 +1,5 @@
/* /*
* QEMU Pipewire audio driver * QEMU PipeWire audio driver
* *
* Copyright (c) 2023 Red Hat Inc. * Copyright (c) 2023 Red Hat Inc.
* *
@ -66,6 +66,9 @@ typedef struct PWVoiceIn {
PWVoice v; PWVoice v;
} PWVoiceIn; } PWVoiceIn;
#define PW_VOICE_IN(v) ((PWVoiceIn *)v)
#define PW_VOICE_OUT(v) ((PWVoiceOut *)v)
static void static void
stream_destroy(void *data) stream_destroy(void *data)
{ {
@ -197,16 +200,6 @@ on_stream_state_changed(void *data, enum pw_stream_state old,
trace_pw_state_changed(pw_stream_get_node_id(v->stream), trace_pw_state_changed(pw_stream_get_node_id(v->stream),
pw_stream_state_as_string(state)); 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 = { static const struct pw_stream_events capture_stream_events = {
@ -424,8 +417,8 @@ pw_to_audfmt(enum spa_audio_format fmt, int *endianness,
} }
static int static int
create_stream(pwaudio *c, PWVoice *v, const char *stream_name, qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name,
const char *name, enum spa_direction dir) const char *name, enum spa_direction dir)
{ {
int res; int res;
uint32_t n_params; uint32_t n_params;
@ -436,6 +429,10 @@ create_stream(pwaudio *c, PWVoice *v, const char *stream_name,
struct pw_properties *props; struct pw_properties *props;
props = pw_properties_new(NULL, NULL); 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 */ /* 75% of the timer period for faster updates */
buf_samples = (uint64_t)v->g->dev->timer_period * v->info.rate buf_samples = (uint64_t)v->g->dev->timer_period * v->info.rate
@ -448,8 +445,8 @@ create_stream(pwaudio *c, PWVoice *v, const char *stream_name,
pw_properties_set(props, PW_KEY_TARGET_OBJECT, name); pw_properties_set(props, PW_KEY_TARGET_OBJECT, name);
} }
v->stream = pw_stream_new(c->core, stream_name, props); v->stream = pw_stream_new(c->core, stream_name, props);
if (v->stream == NULL) { if (v->stream == NULL) {
error_report("Failed to create PW stream: %s", g_strerror(errno));
return -1; return -1;
} }
@ -477,6 +474,7 @@ create_stream(pwaudio *c, PWVoice *v, const char *stream_name,
PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS, params, n_params); PW_STREAM_FLAG_RT_PROCESS, params, n_params);
if (res < 0) { if (res < 0) {
error_report("Failed to connect PW stream: %s", g_strerror(errno));
pw_stream_destroy(v->stream); pw_stream_destroy(v->stream);
return -1; return -1;
} }
@ -484,71 +482,37 @@ create_stream(pwaudio *c, PWVoice *v, const char *stream_name,
return 0; return 0;
} }
static int static void
qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name, qpw_set_position(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS])
const char *name, enum spa_direction dir)
{ {
int r; memcpy(position, (uint32_t[SPA_AUDIO_MAX_CHANNELS]) { SPA_AUDIO_CHANNEL_UNKNOWN, },
sizeof(uint32_t) * SPA_AUDIO_MAX_CHANNELS);
switch (v->info.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: case 8:
v->info.position[0] = SPA_AUDIO_CHANNEL_FL; position[6] = SPA_AUDIO_CHANNEL_SL;
v->info.position[1] = SPA_AUDIO_CHANNEL_FR; position[7] = SPA_AUDIO_CHANNEL_SR;
v->info.position[2] = SPA_AUDIO_CHANNEL_FC; /* fallthrough */
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;
case 6: case 6:
v->info.position[0] = SPA_AUDIO_CHANNEL_FL; position[2] = SPA_AUDIO_CHANNEL_FC;
v->info.position[1] = SPA_AUDIO_CHANNEL_FR; position[3] = SPA_AUDIO_CHANNEL_LFE;
v->info.position[2] = SPA_AUDIO_CHANNEL_FC; position[4] = SPA_AUDIO_CHANNEL_RL;
v->info.position[3] = SPA_AUDIO_CHANNEL_LFE; position[5] = SPA_AUDIO_CHANNEL_RR;
v->info.position[4] = SPA_AUDIO_CHANNEL_RL; /* fallthrough */
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;
case 2: case 2:
v->info.position[0] = SPA_AUDIO_CHANNEL_FL; position[0] = SPA_AUDIO_CHANNEL_FL;
v->info.position[1] = SPA_AUDIO_CHANNEL_FR; position[1] = SPA_AUDIO_CHANNEL_FR;
break; break;
case 1: case 1:
v->info.position[0] = SPA_AUDIO_CHANNEL_MONO; position[0] = SPA_AUDIO_CHANNEL_MONO;
break; break;
default: default:
for (size_t i = 0; i < v->info.channels; i++) { dolog("Internal error: unsupported channel count %d\n", channels);
v->info.position[i] = SPA_AUDIO_CHANNEL_UNKNOWN;
}
break;
} }
/* 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 static int
@ -566,6 +530,7 @@ qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
v->info.format = audfmt_to_pw(as->fmt, as->endianness); v->info.format = audfmt_to_pw(as->fmt, as->endianness);
v->info.channels = as->nchannels; v->info.channels = as->nchannels;
qpw_set_position(as->nchannels, v->info.position);
v->info.rate = as->freq; v->info.rate = as->freq;
obt_as.fmt = obt_as.fmt =
@ -579,7 +544,6 @@ qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id, r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id,
ppdo->name, SPA_DIRECTION_OUTPUT); ppdo->name, SPA_DIRECTION_OUTPUT);
if (r < 0) { if (r < 0) {
error_report("qpw_stream_new for playback failed");
pw_thread_loop_unlock(c->thread_loop); pw_thread_loop_unlock(c->thread_loop);
return -1; return -1;
} }
@ -613,6 +577,7 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
v->info.format = audfmt_to_pw(as->fmt, as->endianness); v->info.format = audfmt_to_pw(as->fmt, as->endianness);
v->info.channels = as->nchannels; v->info.channels = as->nchannels;
qpw_set_position(as->nchannels, v->info.position);
v->info.rate = as->freq; v->info.rate = as->freq;
obt_as.fmt = obt_as.fmt =
@ -623,7 +588,6 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id, r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id,
ppdo->name, SPA_DIRECTION_INPUT); ppdo->name, SPA_DIRECTION_INPUT);
if (r < 0) { if (r < 0) {
error_report("qpw_stream_new for recording failed");
pw_thread_loop_unlock(c->thread_loop); pw_thread_loop_unlock(c->thread_loop);
return -1; return -1;
} }
@ -639,106 +603,86 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
return 0; return 0;
} }
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 static void
qpw_fini_out(HWVoiceOut *hw) qpw_fini_out(HWVoiceOut *hw)
{ {
PWVoiceOut *pw = (PWVoiceOut *) hw; qpw_voice_fini(&PW_VOICE_OUT(hw)->v);
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);
}
} }
static void static void
qpw_fini_in(HWVoiceIn *hw) qpw_fini_in(HWVoiceIn *hw)
{ {
PWVoiceIn *pw = (PWVoiceIn *) hw; qpw_voice_fini(&PW_VOICE_IN(hw)->v);
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);
}
} }
static void 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; pwaudio *c = v->g;
pw_thread_loop_lock(c->thread_loop); pw_thread_loop_lock(c->thread_loop);
pw_stream_set_active(v->stream, enable); pw_stream_set_active(v->stream, enable);
pw_thread_loop_unlock(c->thread_loop); pw_thread_loop_unlock(c->thread_loop);
} }
static void
qpw_enable_out(HWVoiceOut *hw, bool enable)
{
qpw_voice_set_enabled(&PW_VOICE_OUT(hw)->v, enable);
}
static void static void
qpw_enable_in(HWVoiceIn *hw, bool enable) qpw_enable_in(HWVoiceIn *hw, bool enable)
{ {
PWVoiceIn *pi = (PWVoiceIn *) hw; qpw_voice_set_enabled(&PW_VOICE_IN(hw)->v, enable);
PWVoice *v = &pi->v; }
static void
qpw_voice_set_volume(PWVoice *v, Volume *vol)
{
pwaudio *c = v->g; pwaudio *c = v->g;
int i, ret;
pw_thread_loop_lock(c->thread_loop); 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); pw_thread_loop_unlock(c->thread_loop);
} }
static void static void
qpw_volume_out(HWVoiceOut *hw, Volume *vol) qpw_volume_out(HWVoiceOut *hw, Volume *vol)
{ {
PWVoiceOut *pw = (PWVoiceOut *) hw; qpw_voice_set_volume(&PW_VOICE_OUT(hw)->v, vol);
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);
} }
static void static void
qpw_volume_in(HWVoiceIn *hw, Volume *vol) qpw_volume_in(HWVoiceIn *hw, Volume *vol)
{ {
PWVoiceIn *pw = (PWVoiceIn *) hw; qpw_voice_set_volume(&PW_VOICE_IN(hw)->v, vol);
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);
} }
static int wait_resync(pwaudio *pw) static int wait_resync(pwaudio *pw)
@ -760,6 +704,7 @@ static int wait_resync(pwaudio *pw)
} }
return 0; return 0;
} }
static void static void
on_core_error(void *data, uint32_t id, int seq, int res, const char *message) on_core_error(void *data, uint32_t id, int seq, int res, const char *message)
{ {
@ -794,27 +739,28 @@ static void *
qpw_audio_init(Audiodev *dev) qpw_audio_init(Audiodev *dev)
{ {
g_autofree pwaudio *pw = g_new0(pwaudio, 1); g_autofree pwaudio *pw = g_new0(pwaudio, 1);
assert(dev->driver == AUDIODEV_DRIVER_PIPEWIRE);
trace_pw_audio_init();
pw_init(NULL, NULL); pw_init(NULL, NULL);
trace_pw_audio_init();
assert(dev->driver == AUDIODEV_DRIVER_PIPEWIRE);
pw->dev = dev; 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) { 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; goto fail;
} }
pw->context = pw->context =
pw_context_new(pw_thread_loop_get_loop(pw->thread_loop), NULL, 0); pw_context_new(pw_thread_loop_get_loop(pw->thread_loop), NULL, 0);
if (pw->context == NULL) { if (pw->context == NULL) {
error_report("Could not create Pipewire context"); error_report("Could not create PipeWire context: %s", g_strerror(errno));
goto fail; goto fail;
} }
if (pw_thread_loop_start(pw->thread_loop) < 0) { 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; goto fail;
} }
@ -844,12 +790,8 @@ fail:
if (pw->thread_loop) { if (pw->thread_loop) {
pw_thread_loop_stop(pw->thread_loop); pw_thread_loop_stop(pw->thread_loop);
} }
if (pw->context) { g_clear_pointer(&pw->context, pw_context_destroy);
g_clear_pointer(&pw->context, pw_context_destroy); g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy);
}
if (pw->thread_loop) {
g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy);
}
return NULL; return NULL;
} }

View File

@ -24,7 +24,7 @@ pw_read(int32_t avail, uint32_t index, size_t len) "avail=%d index=%u len=%zu"
pw_write(int32_t filled, int32_t avail, uint32_t index, size_t len) "filled=%d avail=%d index=%u len=%zu" 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_vol(const char *ret) "set volume: %s"
pw_period(uint64_t quantum, uint32_t rate) "period =%" PRIu64 "/%u" 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.c
audio_timer_start(int interval) "interval %d ms" audio_timer_start(int interval) "interval %d ms"

View File

@ -181,13 +181,13 @@ static VGPUDMABuf
} }
dmabuf = g_new0(VGPUDMABuf, 1); dmabuf = g_new0(VGPUDMABuf, 1);
dmabuf->buf.width = fb->width; dmabuf->buf.width = r->width;
dmabuf->buf.height = fb->height; dmabuf->buf.height = r->height;
dmabuf->buf.stride = fb->stride; dmabuf->buf.stride = fb->stride;
dmabuf->buf.x = r->x; dmabuf->buf.x = r->x;
dmabuf->buf.y = r->y; dmabuf->buf.y = r->y;
dmabuf->buf.scanout_width = r->width; dmabuf->buf.backing_width = fb->width;
dmabuf->buf.scanout_height = r->height; dmabuf->buf.backing_height = fb->height;
dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format); dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format);
dmabuf->buf.fd = res->dmabuf_fd; dmabuf->buf.fd = res->dmabuf_fd;
dmabuf->buf.allow_fences = true; dmabuf->buf.allow_fences = true;
@ -218,8 +218,8 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g,
g->dmabuf.primary[scanout_id] = new_primary; g->dmabuf.primary[scanout_id] = new_primary;
qemu_console_resize(scanout->con, qemu_console_resize(scanout->con,
new_primary->buf.scanout_width, new_primary->buf.width,
new_primary->buf.scanout_height); new_primary->buf.height);
dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf); dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf);
if (old_primary) { if (old_primary) {

View File

@ -303,10 +303,11 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
goto end; goto end;
} }
#endif #endif
res->image = pixman_image_create_bits(pformat, res->image = pixman_image_create_bits(
c2d.width, pformat,
c2d.height, c2d.width,
bits, res->hostmem / c2d.height); c2d.height,
bits, c2d.height ? res->hostmem / c2d.height : 0);
#ifdef WIN32 #ifdef WIN32
if (res->image) { if (res->image) {
pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle);
@ -1272,9 +1273,10 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
return -EINVAL; return -EINVAL;
} }
#endif #endif
res->image = pixman_image_create_bits(pformat, res->image = pixman_image_create_bits(
res->width, res->height, pformat,
bits, res->hostmem / res->height); res->width, res->height,
bits, res->height ? res->hostmem / res->height : 0);
if (!res->image) { if (!res->image) {
g_free(res); g_free(res);
return -EINVAL; return -EINVAL;
@ -1395,6 +1397,7 @@ void virtio_gpu_reset(VirtIODevice *vdev)
VirtIOGPU *g = VIRTIO_GPU(vdev); VirtIOGPU *g = VIRTIO_GPU(vdev);
struct virtio_gpu_simple_resource *res, *tmp; struct virtio_gpu_simple_resource *res, *tmp;
struct virtio_gpu_ctrl_command *cmd; struct virtio_gpu_ctrl_command *cmd;
int i = 0;
QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) {
virtio_gpu_resource_destroy(g, res); virtio_gpu_resource_destroy(g, res);
@ -1413,6 +1416,10 @@ void virtio_gpu_reset(VirtIODevice *vdev)
g_free(cmd); 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)); virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev));
} }

View File

@ -201,8 +201,8 @@ typedef struct QemuDmaBuf {
uint32_t texture; uint32_t texture;
uint32_t x; uint32_t x;
uint32_t y; uint32_t y;
uint32_t scanout_width; uint32_t backing_width;
uint32_t scanout_height; uint32_t backing_height;
bool y0_top; bool y0_top;
void *sync; void *sync;
int fence_fd; int fence_fd;

View File

@ -4251,7 +4251,7 @@ if targetos == 'linux'
summary_info += {'ALSA support': alsa} summary_info += {'ALSA support': alsa}
summary_info += {'PulseAudio support': pulse} summary_info += {'PulseAudio support': pulse}
endif endif
summary_info += {'Pipewire support': pipewire} summary_info += {'PipeWire support': pipewire}
summary_info += {'JACK support': jack} summary_info += {'JACK support': jack}
summary(summary_info, bool_yn: true, section: 'Audio backends') summary(summary_info, bool_yn: true, section: 'Audio backends')

View File

@ -267,7 +267,7 @@ option('oss', type: 'feature', value: 'auto',
option('pa', type: 'feature', value: 'auto', option('pa', type: 'feature', value: 'auto',
description: 'PulseAudio sound support') description: 'PulseAudio sound support')
option('pipewire', type: 'feature', value: 'auto', option('pipewire', type: 'feature', value: 'auto',
description: 'Pipewire sound support') description: 'PipeWire sound support')
option('sndio', type: 'feature', value: 'auto', option('sndio', type: 'feature', value: 'auto',
description: 'sndio sound support') description: 'sndio sound support')

View File

@ -328,17 +328,17 @@
## ##
# @AudiodevPipewirePerDirectionOptions: # @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. # recording.
# #
# @name: name of the sink/source to use # @name: name of the sink/source to use
# #
# @stream-name: name of the Pipewire stream created by qemu. Can be # @stream-name: name of the PipeWire stream created by qemu. Can be
# used to identify the stream in Pipewire when you create multiple # used to identify the stream in PipeWire when you create multiple
# Pipewire devices or run multiple qemu instances (default: # PipeWire devices or run multiple qemu instances (default:
# audiodev's id) # audiodev's id)
# #
# @latency: latency you want Pipewire to achieve in microseconds # @latency: latency you want PipeWire to achieve in microseconds
# (default 46000) # (default 46000)
# #
# Since: 8.1 # Since: 8.1
@ -353,7 +353,7 @@
## ##
# @AudiodevPipewireOptions: # @AudiodevPipewireOptions:
# #
# Options of the Pipewire audio backend. # Options of the PipeWire audio backend.
# #
# @in: options of the capture stream # @in: options of the capture stream
# #

View File

@ -963,10 +963,10 @@ SRST
to honor this value but actual latencies may be lower or higher. to honor this value but actual latencies may be lower or higher.
``-audiodev pipewire,id=id[,prop[=value][,...]]`` ``-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. most systems.
Pipewire specific options are: PipeWire specific options are:
``in|out.latency=usecs`` ``in|out.latency=usecs``
Desired latency in microseconds. Desired latency in microseconds.

View File

@ -145,7 +145,7 @@ meson_options_help() {
printf "%s\n" ' oss OSS sound support' printf "%s\n" ' oss OSS sound support'
printf "%s\n" ' pa PulseAudio sound support' printf "%s\n" ' pa PulseAudio sound support'
printf "%s\n" ' parallels parallels image format 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" ' png PNG support with libpng'
printf "%s\n" ' pvrdma Enable PVRDMA support' printf "%s\n" ' pvrdma Enable PVRDMA support'
printf "%s\n" ' qcow1 qcow1 image format support' printf "%s\n" ' qcow1 qcow1 image format support'

View File

@ -77,6 +77,7 @@ RUN apk update && \
numactl-dev \ numactl-dev \
openssh-client \ openssh-client \
pcre-dev \ pcre-dev \
pipewire-dev \
pixman-dev \ pixman-dev \
pkgconf \ pkgconf \
pulseaudio-dev \ pulseaudio-dev \

View File

@ -90,6 +90,7 @@ RUN dnf distro-sync -y && \
openssh-clients \ openssh-clients \
pam-devel \ pam-devel \
pcre-static \ pcre-static \
pipewire-devel \
pixman-devel \ pixman-devel \
pkgconfig \ pkgconfig \
pulseaudio-libs-devel \ pulseaudio-libs-devel \

View File

@ -116,6 +116,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libnfs-dev:amd64 \ libnfs-dev:amd64 \
libnuma-dev:amd64 \ libnuma-dev:amd64 \
libpam0g-dev:amd64 \ libpam0g-dev:amd64 \
libpipewire-0.3-dev:amd64 \
libpixman-1-dev:amd64 \ libpixman-1-dev:amd64 \
libpmem-dev:amd64 \ libpmem-dev:amd64 \
libpng-dev:amd64 \ libpng-dev:amd64 \

View File

@ -69,6 +69,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libnuma-dev \ libnuma-dev \
libpam0g-dev \ libpam0g-dev \
libpcre2-dev \ libpcre2-dev \
libpipewire-0.3-dev \
libpixman-1-dev \ libpixman-1-dev \
libpmem-dev \ libpmem-dev \
libpng-dev \ libpng-dev \

View File

@ -116,6 +116,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libnfs-dev:arm64 \ libnfs-dev:arm64 \
libnuma-dev:arm64 \ libnuma-dev:arm64 \
libpam0g-dev:arm64 \ libpam0g-dev:arm64 \
libpipewire-0.3-dev:arm64 \
libpixman-1-dev:arm64 \ libpixman-1-dev:arm64 \
libpng-dev:arm64 \ libpng-dev:arm64 \
libpulse-dev:arm64 \ libpulse-dev:arm64 \

View File

@ -116,6 +116,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libnfs-dev:armel \ libnfs-dev:armel \
libnuma-dev:armel \ libnuma-dev:armel \
libpam0g-dev:armel \ libpam0g-dev:armel \
libpipewire-0.3-dev:armel \
libpixman-1-dev:armel \ libpixman-1-dev:armel \
libpng-dev:armel \ libpng-dev:armel \
libpulse-dev:armel \ libpulse-dev:armel \

View File

@ -116,6 +116,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libnfs-dev:armhf \ libnfs-dev:armhf \
libnuma-dev:armhf \ libnuma-dev:armhf \
libpam0g-dev:armhf \ libpam0g-dev:armhf \
libpipewire-0.3-dev:armhf \
libpixman-1-dev:armhf \ libpixman-1-dev:armhf \
libpng-dev:armhf \ libpng-dev:armhf \
libpulse-dev:armhf \ libpulse-dev:armhf \

View File

@ -115,6 +115,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libnfs-dev:mips64el \ libnfs-dev:mips64el \
libnuma-dev:mips64el \ libnuma-dev:mips64el \
libpam0g-dev:mips64el \ libpam0g-dev:mips64el \
libpipewire-0.3-dev:mips64el \
libpixman-1-dev:mips64el \ libpixman-1-dev:mips64el \
libpng-dev:mips64el \ libpng-dev:mips64el \
libpulse-dev:mips64el \ libpulse-dev:mips64el \

View File

@ -115,6 +115,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libnfs-dev:mipsel \ libnfs-dev:mipsel \
libnuma-dev:mipsel \ libnuma-dev:mipsel \
libpam0g-dev:mipsel \ libpam0g-dev:mipsel \
libpipewire-0.3-dev:mipsel \
libpixman-1-dev:mipsel \ libpixman-1-dev:mipsel \
libpng-dev:mipsel \ libpng-dev:mipsel \
libpulse-dev:mipsel \ libpulse-dev:mipsel \

View File

@ -116,6 +116,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libnfs-dev:ppc64el \ libnfs-dev:ppc64el \
libnuma-dev:ppc64el \ libnuma-dev:ppc64el \
libpam0g-dev:ppc64el \ libpam0g-dev:ppc64el \
libpipewire-0.3-dev:ppc64el \
libpixman-1-dev:ppc64el \ libpixman-1-dev:ppc64el \
libpng-dev:ppc64el \ libpng-dev:ppc64el \
libpulse-dev:ppc64el \ libpulse-dev:ppc64el \

View File

@ -116,6 +116,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libnfs-dev:s390x \ libnfs-dev:s390x \
libnuma-dev:s390x \ libnuma-dev:s390x \
libpam0g-dev:s390x \ libpam0g-dev:s390x \
libpipewire-0.3-dev:s390x \
libpixman-1-dev:s390x \ libpixman-1-dev:s390x \
libpng-dev:s390x \ libpng-dev:s390x \
libpulse-dev:s390x \ libpulse-dev:s390x \

View File

@ -98,6 +98,7 @@ exec "$@"\n' > /usr/bin/nosync && \
openssh-clients \ openssh-clients \
pam-devel \ pam-devel \
pcre-static \ pcre-static \
pipewire-devel \
pixman-devel \ pixman-devel \
pkgconfig \ pkgconfig \
pulseaudio-libs-devel \ pulseaudio-libs-devel \

View File

@ -88,6 +88,7 @@ RUN zypper update -y && \
openssh \ openssh \
pam-devel \ pam-devel \
pcre-devel-static \ pcre-devel-static \
pipewire-devel \
pkgconfig \ pkgconfig \
python39-base \ python39-base \
python39-pip \ python39-pip \

View File

@ -69,6 +69,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libnuma-dev \ libnuma-dev \
libpam0g-dev \ libpam0g-dev \
libpcre2-dev \ libpcre2-dev \
libpipewire-0.3-dev \
libpixman-1-dev \ libpixman-1-dev \
libpmem-dev \ libpmem-dev \
libpng-dev \ libpng-dev \

@ -1 +1 @@
Subproject commit b0f44f929a81c0a604fb7fbf8afc34d37ab0eae9 Subproject commit 9bff3b763b5531a1490e238bfbf77306dc3a6dbb

View File

@ -85,6 +85,7 @@ packages:
- pam - pam
- pcre-static - pcre-static
- pixman - pixman
- pipewire
- pkg-config - pkg-config
- pulseaudio - pulseaudio
- python3 - python3

View File

@ -1898,6 +1898,7 @@ void dpy_gfx_replace_surface(QemuConsole *con,
static const char placeholder_msg[] = "Display output is not active."; static const char placeholder_msg[] = "Display output is not active.";
DisplayState *s = con->ds; DisplayState *s = con->ds;
DisplaySurface *old_surface = con->surface; DisplaySurface *old_surface = con->surface;
DisplaySurface *new_surface = surface;
DisplayChangeListener *dcl; DisplayChangeListener *dcl;
int width; int width;
int height; int height;
@ -1911,19 +1912,19 @@ void dpy_gfx_replace_surface(QemuConsole *con,
height = 480; 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->scanout.kind = SCANOUT_SURFACE;
con->surface = surface; con->surface = new_surface;
dpy_gfx_create_texture(con, surface); dpy_gfx_create_texture(con, new_surface);
QLIST_FOREACH(dcl, &s->listeners, next) { QLIST_FOREACH(dcl, &s->listeners, next) {
if (con != (dcl->con ? dcl->con : active_console)) { if (con != (dcl->con ? dcl->con : active_console)) {
continue; continue;
} }
displaychangelistener_gfx_switch(dcl, surface, FALSE); displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE);
} }
dpy_gfx_destroy_texture(con, old_surface); dpy_gfx_destroy_texture(con, old_surface);
qemu_free_displaysurface(old_surface); qemu_free_displaysurface(old_surface);

View File

@ -415,13 +415,13 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl,
backing_width, backing_height, x, y, w, h); backing_width, backing_height, x, y, w, h);
#ifdef CONFIG_GBM #ifdef CONFIG_GBM
QemuDmaBuf dmabuf = { QemuDmaBuf dmabuf = {
.width = backing_width, .width = w,
.height = backing_height, .height = h,
.y0_top = backing_y_0_top, .y0_top = backing_y_0_top,
.x = x, .x = x,
.y = y, .y = y,
.scanout_width = w, .backing_width = backing_width,
.scanout_height = h, .backing_height = backing_height,
}; };
assert(tex_id); assert(tex_id);

View File

@ -148,8 +148,8 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip)
if (src->dmabuf) { if (src->dmabuf) {
x1 = src->dmabuf->x; x1 = src->dmabuf->x;
y1 = src->dmabuf->y; y1 = src->dmabuf->y;
w = src->dmabuf->scanout_width; w = src->dmabuf->width;
h = src->dmabuf->scanout_height; h = src->dmabuf->height;
} }
w = (x1 + w) > src->width ? src->width - x1 : w; w = (x1 + w) > src->width ? src->width - x1 : w;
@ -314,9 +314,9 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf)
} }
attrs[i++] = EGL_WIDTH; attrs[i++] = EGL_WIDTH;
attrs[i++] = dmabuf->width; attrs[i++] = dmabuf->backing_width;
attrs[i++] = EGL_HEIGHT; attrs[i++] = EGL_HEIGHT;
attrs[i++] = dmabuf->height; attrs[i++] = dmabuf->backing_height;
attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT; attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT;
attrs[i++] = dmabuf->fourcc; attrs[i++] = dmabuf->fourcc;

View File

@ -32,6 +32,8 @@ static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout)
vc->gfx.scanout_mode = scanout; vc->gfx.scanout_mode = scanout;
if (!vc->gfx.scanout_mode) { 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); egl_fb_destroy(&vc->gfx.guest_fb);
if (vc->gfx.surface) { if (vc->gfx.surface) {
surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
@ -135,6 +137,8 @@ void gd_egl_update(DisplayChangeListener *dcl,
vc->gfx.esurface, vc->gfx.ectx); vc->gfx.esurface, vc->gfx.ectx);
surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h); surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h);
vc->gfx.glupdates++; vc->gfx.glupdates++;
eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
EGL_NO_SURFACE, EGL_NO_CONTEXT);
} }
void gd_egl_refresh(DisplayChangeListener *dcl) void gd_egl_refresh(DisplayChangeListener *dcl)
@ -144,6 +148,10 @@ void gd_egl_refresh(DisplayChangeListener *dcl)
gd_update_monitor_refresh_rate( gd_update_monitor_refresh_rate(
vc, vc->window ? vc->window : vc->gfx.drawing_area); 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) { if (!vc->gfx.esurface) {
gd_egl_init(vc); gd_egl_init(vc);
if (!vc->gfx.esurface) { if (!vc->gfx.esurface) {
@ -238,7 +246,6 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl,
eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
vc->gfx.esurface, vc->gfx.ectx); 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, egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height,
backing_id, false); backing_id, false);
} }
@ -258,9 +265,10 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
} }
gd_egl_scanout_texture(dcl, dmabuf->texture, gd_egl_scanout_texture(dcl, dmabuf->texture,
dmabuf->y0_top, dmabuf->width, dmabuf->height, dmabuf->y0_top,
dmabuf->x, dmabuf->y, dmabuf->scanout_width, dmabuf->backing_width, dmabuf->backing_height,
dmabuf->scanout_height, NULL); dmabuf->x, dmabuf->y, dmabuf->width,
dmabuf->height, NULL);
if (dmabuf->allow_fences) { if (dmabuf->allow_fences) {
vc->gfx.guest_fb.dmabuf = dmabuf; vc->gfx.guest_fb.dmabuf = dmabuf;
@ -280,7 +288,8 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
if (!dmabuf->texture) { if (!dmabuf->texture) {
return; 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); dmabuf->texture, false);
} else { } else {
egl_fb_destroy(&vc->gfx.cursor_fb); egl_fb_destroy(&vc->gfx.cursor_fb);
@ -347,6 +356,7 @@ void gd_egl_flush(DisplayChangeListener *dcl,
if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) { if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) {
graphic_hw_gl_block(vc->gfx.dcl.con, true); graphic_hw_gl_block(vc->gfx.dcl.con, true);
vc->gfx.guest_fb.dmabuf->draw_submitted = 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); gtk_widget_queue_draw_area(area, x, y, w, h);
return; return;
} }

View File

@ -26,6 +26,7 @@ static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout)
vc->gfx.scanout_mode = scanout; vc->gfx.scanout_mode = scanout;
if (!vc->gfx.scanout_mode) { if (!vc->gfx.scanout_mode) {
gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
egl_fb_destroy(&vc->gfx.guest_fb); egl_fb_destroy(&vc->gfx.guest_fb);
if (vc->gfx.surface) { if (vc->gfx.surface) {
surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
@ -115,6 +116,7 @@ void gd_gl_area_update(DisplayChangeListener *dcl,
gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 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); surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h);
vc->gfx.glupdates++; vc->gfx.glupdates++;
gdk_gl_context_clear_current();
} }
void gd_gl_area_refresh(DisplayChangeListener *dcl) void gd_gl_area_refresh(DisplayChangeListener *dcl)
@ -123,6 +125,10 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl)
gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area); 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 (!vc->gfx.gls) {
if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
return; return;
@ -262,7 +268,6 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
return; return;
} }
gtk_gl_area_set_scanout_mode(vc, true);
egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height, egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height,
backing_id, false); backing_id, false);
} }
@ -282,6 +287,7 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) { if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) {
graphic_hw_gl_block(vc->gfx.dcl.con, true); graphic_hw_gl_block(vc->gfx.dcl.con, true);
vc->gfx.guest_fb.dmabuf->draw_submitted = 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)); gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
} }
@ -299,9 +305,10 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
} }
gd_gl_area_scanout_texture(dcl, dmabuf->texture, gd_gl_area_scanout_texture(dcl, dmabuf->texture,
dmabuf->y0_top, dmabuf->width, dmabuf->height, dmabuf->y0_top,
dmabuf->x, dmabuf->y, dmabuf->scanout_width, dmabuf->backing_width, dmabuf->backing_height,
dmabuf->scanout_height, NULL); dmabuf->x, dmabuf->y, dmabuf->width,
dmabuf->height, NULL);
if (dmabuf->allow_fences) { if (dmabuf->allow_fences) {
vc->gfx.guest_fb.dmabuf = dmabuf; vc->gfx.guest_fb.dmabuf = dmabuf;

View File

@ -50,8 +50,11 @@ static uint8_t *inflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size)
ret = inflate(&stream, Z_FINISH); ret = inflate(&stream, Z_FINISH);
switch (ret) { switch (ret) {
case Z_OK: case Z_OK:
case Z_STREAM_END:
break; break;
case Z_STREAM_END:
*size = stream.total_out;
inflateEnd(&stream);
return out;
case Z_BUF_ERROR: case Z_BUF_ERROR:
out_len <<= 1; out_len <<= 1;
if (out_len > (1 << 20)) { if (out_len > (1 << 20)) {
@ -66,11 +69,6 @@ static uint8_t *inflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size)
} }
} }
*size = stream.total_out;
inflateEnd(&stream);
return out;
err_end: err_end:
inflateEnd(&stream); inflateEnd(&stream);
err: err: