[client] spice: make display switching atomic and failure-aware

This commit is contained in:
Geoffrey McRae
2026-07-28 12:24:51 +10:00
parent 5c7431dfd6
commit e4888c362a
3 changed files with 77 additions and 19 deletions

View File

@@ -1162,38 +1162,92 @@ void app_stopVideo(bool stop)
bool app_useSpiceDisplay(bool enable) bool app_useSpiceDisplay(bool enable)
{ {
static bool lastState = false; if (!g_params.useSpice)
if (!g_params.useSpice || lastState == enable) return false;
return g_params.useSpice && lastState;
atomic_store_explicit(&g_state.spiceDisplayRequested, enable,
memory_order_release);
// if spice is not yet ready, flag the state we want for when it is // if spice is not yet ready, flag the state we want for when it is
if (!g_state.spiceReady) if (!g_state.spiceReady)
{
g_state.initialSpiceDisplay = enable;
return false; return false;
}
if (!purespice_hasChannel(PS_CHANNEL_DISPLAY)) bool active = atomic_load_explicit(&g_state.spiceDisplayActive,
return false; memory_order_acquire);
if (active == enable)
return active;
// do not allow stopping of the host app if not connected // do not allow stopping of the host app if not connected
if (!enable && !g_state.lgHostConnected) if (!enable && !g_state.lgHostConnected)
{
atomic_store_explicit(&g_state.spiceDisplayRequested, active,
memory_order_release);
return false; return false;
}
bool expected = false;
if (!atomic_compare_exchange_strong_explicit(
&g_state.spiceDisplayTransition, &expected, true,
memory_order_acquire, memory_order_relaxed))
return atomic_load_explicit(&g_state.spiceDisplayActive,
memory_order_acquire);
active = atomic_load_explicit(&g_state.spiceDisplayActive,
memory_order_relaxed);
if (active == enable)
goto done;
lastState = enable;
if (enable) if (enable)
{ {
purespice_connectChannel(PS_CHANNEL_DISPLAY); if (!purespice_hasChannel(PS_CHANNEL_DISPLAY) ||
purespice_connectChannel(PS_CHANNEL_CURSOR); !purespice_hasChannel(PS_CHANNEL_CURSOR))
goto fail;
if (!purespice_connectChannel(PS_CHANNEL_DISPLAY))
goto fail;
if (!purespice_connectChannel(PS_CHANNEL_CURSOR))
{
purespice_disconnectChannel(PS_CHANNEL_DISPLAY);
goto fail;
}
renderQueue_spiceShow(true); renderQueue_spiceShow(true);
} }
else else
{ {
renderQueue_spiceShow(false); if (!purespice_disconnectChannel(PS_CHANNEL_DISPLAY))
purespice_disconnectChannel(PS_CHANNEL_DISPLAY); goto fail;
purespice_disconnectChannel(PS_CHANNEL_CURSOR);
if (!purespice_disconnectChannel(PS_CHANNEL_CURSOR))
{
purespice_connectChannel(PS_CHANNEL_DISPLAY);
goto fail;
} }
overlayStatus_set(LG_USER_STATUS_SPICE, enable); renderQueue_spiceShow(false);
return enable; }
active = enable;
atomic_store_explicit(&g_state.spiceDisplayActive, active,
memory_order_release);
overlayStatus_set(LG_USER_STATUS_SPICE, enable);
done:
atomic_store_explicit(&g_state.spiceDisplayTransition, false,
memory_order_release);
enable = atomic_load_explicit(&g_state.spiceDisplayRequested,
memory_order_acquire);
if (enable != active)
return app_useSpiceDisplay(enable);
return active;
fail:
DEBUG_ERROR("Failed to %s the SPICE display",
enable ? "enable" : "disable");
atomic_store_explicit(&g_state.spiceDisplayRequested, active,
memory_order_release);
goto done;
} }

View File

@@ -959,7 +959,8 @@ static void checkUUID(void)
void spiceReady(void) void spiceReady(void)
{ {
g_state.spiceReady = true; g_state.spiceReady = true;
if (g_state.initialSpiceDisplay) if (atomic_load_explicit(&g_state.spiceDisplayRequested,
memory_order_acquire))
app_useSpiceDisplay(true); app_useSpiceDisplay(true);
// set the intial mouse mode // set the intial mouse mode

View File

@@ -80,8 +80,11 @@ struct AppState
uint8_t spiceUUID[16]; uint8_t spiceUUID[16];
bool spiceReady; bool spiceReady;
bool initialSpiceDisplay; atomic_bool spiceDisplayRequested;
atomic_bool spiceDisplayActive;
atomic_bool spiceDisplayTransition;
bool spicePrimarySurfaceValid; bool spicePrimarySurfaceValid;
uint8_t guestUUID[16]; uint8_t guestUUID[16];
bool guestUUIDValid; bool guestUUIDValid;
KVMFROS guestOS; KVMFROS guestOS;