mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 15:11:31 +00:00
[client] transport: track video component availability
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
#include "render_queue.h"
|
||||
#include "evdev.h"
|
||||
#include "input.h"
|
||||
#include "sw_surface.h"
|
||||
|
||||
#include "kb.h"
|
||||
|
||||
@@ -59,6 +60,13 @@
|
||||
|
||||
extern _Atomic(enum RunState) p_appState;
|
||||
|
||||
static bool swSurfaceActivationCancelled(void * opaque)
|
||||
{
|
||||
(void)opaque;
|
||||
return app_getState() != APP_STATE_RUNNING ||
|
||||
atomic_load_explicit(&g_state.stopVideo, memory_order_acquire);
|
||||
}
|
||||
|
||||
enum RunState app_getState(void)
|
||||
{
|
||||
return atomic_load_explicit(&p_appState, memory_order_acquire);
|
||||
@@ -670,6 +678,12 @@ void app_handleRenderEvent(const uint64_t timeUs)
|
||||
app_invalidateWindow(false);
|
||||
}
|
||||
|
||||
void app_handleFramePresented(uint64_t frameToken, uint64_t presentTime,
|
||||
bool valid)
|
||||
{
|
||||
main_framePresented(frameToken, presentTime, valid);
|
||||
}
|
||||
|
||||
void app_setFullscreen(bool fs)
|
||||
{
|
||||
g_state.ds->setFullscreen(fs);
|
||||
@@ -1203,11 +1217,19 @@ void app_stopVideo(bool stop)
|
||||
|
||||
if (stop)
|
||||
{
|
||||
if (g_state.fallback)
|
||||
lgTransportFallback_requestVideoActive(g_state.fallback, false);
|
||||
|
||||
if (g_state.videoOps->type == LG_VIDEO_TYPE_FRAME)
|
||||
{
|
||||
core_stopVideoThreads();
|
||||
/* A status callback may have requested fallback before it quiesced. */
|
||||
if (g_state.fallback)
|
||||
lgTransportFallback_requestVideoActive(g_state.fallback, false);
|
||||
}
|
||||
else
|
||||
g_state.videoOps->swSurface->setActive(
|
||||
g_state.transport.handle, false);
|
||||
lgSwSurface_setActive(g_state.videoOps->swSurface,
|
||||
g_state.transport.handle, false, NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1217,14 +1239,19 @@ void app_stopVideo(bool stop)
|
||||
{
|
||||
atomic_store_explicit(
|
||||
&g_state.stopVideo, true, memory_order_release);
|
||||
if (g_state.fallback)
|
||||
lgTransportFallback_requestVideoActive(g_state.fallback, false);
|
||||
app_alert(LG_ALERT_ERROR, "Failed to enable the video stream");
|
||||
}
|
||||
else
|
||||
main_videoStreamEnabled();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_state.videoOps->swSurface->setActive(
|
||||
g_state.transport.handle, true);
|
||||
app_useVideoSource(LG_VIDEO_SOURCE_PRIMARY);
|
||||
if (lgSwSurface_setActive(g_state.videoOps->swSurface,
|
||||
g_state.transport.handle, true,
|
||||
swSurfaceActivationCancelled, NULL))
|
||||
app_useVideoSource(LG_VIDEO_SOURCE_PRIMARY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,9 +627,9 @@ static void cancelVideoWaits(void)
|
||||
return;
|
||||
|
||||
const LG_FrameOps * ops = g_state.videoOps->frame;
|
||||
if (g_state.frameThread && ops->cancelFrameWait)
|
||||
if (g_state.frameThread)
|
||||
ops->cancelFrameWait(g_state.transport.handle);
|
||||
if (g_state.cursorThread && ops->cancelPointerWait)
|
||||
if (g_state.cursorThread)
|
||||
ops->cancelPointerWait(g_state.transport.handle);
|
||||
}
|
||||
|
||||
@@ -650,24 +650,26 @@ void core_stopVideoThreads(void)
|
||||
|
||||
bool core_startVideoThreads(void)
|
||||
{
|
||||
if (g_state.frameThread && g_state.cursorThread)
|
||||
const LG_FrameOps * ops = g_state.videoOps->frame;
|
||||
const bool pointer = ops->nextPointer && ops->releasePointer;
|
||||
if (g_state.frameThread && (!pointer || g_state.cursorThread))
|
||||
return true;
|
||||
if (g_state.frameThread || g_state.cursorThread)
|
||||
core_stopVideoThreads();
|
||||
|
||||
atomic_store_explicit(
|
||||
&g_state.stopVideoThreads, false, memory_order_release);
|
||||
if (!lgCreateThread("cursorThread", main_cursorThread, NULL,
|
||||
&g_state.cursorThread))
|
||||
{
|
||||
DEBUG_ERROR("cursor create thread failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lgCreateThread("frameThread", main_frameThread, NULL,
|
||||
&g_state.frameThread))
|
||||
{
|
||||
DEBUG_ERROR("frame create thread failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pointer && !lgCreateThread("cursorThread", main_cursorThread, NULL,
|
||||
&g_state.cursorThread))
|
||||
{
|
||||
DEBUG_ERROR("cursor create thread failed");
|
||||
core_stopVideoThreads();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
#include "evdev.h"
|
||||
#include "frame_scheduler.h"
|
||||
#include "input.h"
|
||||
#include "sw_surface.h"
|
||||
|
||||
#ifdef ENABLE_TESTS
|
||||
#include "interface/test_capture.h"
|
||||
@@ -85,6 +86,7 @@ _Static_assert((int)LG_CAPTURE_RGBA32F == (int)LG_TEST_CAPTURE_RGBA32F,
|
||||
#define TRANSPORT_LOST_FALLBACK (1U << 1)
|
||||
#define TRANSPORT_LOST_ALL \
|
||||
(TRANSPORT_LOST_PRIMARY | TRANSPORT_LOST_FALLBACK)
|
||||
#define VIDEO_STATUS_ERROR_RETRY_NS 1000000U
|
||||
|
||||
// forwards
|
||||
static int renderThread(void * unused);
|
||||
@@ -93,6 +95,17 @@ static bool videoSourceInvalidate(LG_VideoSource source);
|
||||
static void videoSourceShowSplashIfNeeded(void);
|
||||
static bool fallbackRequestVideo(void);
|
||||
static void primaryLost(void);
|
||||
static bool videoComponentPayloadCurrent(const atomic_bool * available,
|
||||
const atomic_uint_least64_t * epoch, uint64_t payloadEpoch,
|
||||
bool trackEpoch);
|
||||
static bool videoPayloadAccept(const atomic_bool * available,
|
||||
const atomic_uint_least64_t * epoch, uint64_t payloadEpoch,
|
||||
bool trackEpoch, uint64_t sourceGeneration);
|
||||
static void videoPayloadRelease(void);
|
||||
static bool videoFramePayloadAccept(const LG_TransportFrame * frame,
|
||||
uint64_t currentGeneration, bool trackEpoch,
|
||||
uint64_t * sourceGeneration, uint64_t * frameSerial,
|
||||
uint32_t * formatVersion, bool * sourceChanged);
|
||||
|
||||
static LGEvent *e_startup = NULL;
|
||||
static LGEvent *e_cursorRepaint = NULL;
|
||||
@@ -119,6 +132,20 @@ struct CursorState g_cursor;
|
||||
// this structure is initialized in config.c
|
||||
struct AppParams g_params = { 0 };
|
||||
|
||||
static bool videoStatusRetry(LG_TransportStatus status,
|
||||
bool statusAware, bool available)
|
||||
{
|
||||
if (status == LG_TRANSPORT_TIMEOUT ||
|
||||
status == LG_TRANSPORT_UNAVAILABLE)
|
||||
return true;
|
||||
|
||||
if (status != LG_TRANSPORT_ERROR || !statusAware || available)
|
||||
return false;
|
||||
|
||||
nsleep(VIDEO_STATUS_ERROR_RETRY_NS);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_TESTS
|
||||
static struct
|
||||
{
|
||||
@@ -243,6 +270,7 @@ struct FrameTimingRecord
|
||||
uint32_t scheduleDeadlineSerial;
|
||||
unsigned readyMask;
|
||||
bool producerValid;
|
||||
bool providerValid;
|
||||
bool phaseValid;
|
||||
bool transportValid;
|
||||
bool presentValid;
|
||||
@@ -253,6 +281,8 @@ struct FrameTimingRecord
|
||||
uint64_t readyTime;
|
||||
uint64_t holdTime;
|
||||
uint64_t readyLeadTime;
|
||||
uint64_t receiveTime;
|
||||
uint64_t providerPrepareTime;
|
||||
uint64_t importTime;
|
||||
uint64_t importWaitTime;
|
||||
uint64_t dispatchTime;
|
||||
@@ -319,6 +349,11 @@ static void frameTimingReset(void)
|
||||
LG_RENDERER_FRAME_TOKEN_NONE, memory_order_release);
|
||||
}
|
||||
|
||||
static uint64_t frameTimingAdd(uint64_t lhs, uint64_t rhs)
|
||||
{
|
||||
return rhs > UINT64_MAX - lhs ? UINT64_MAX : lhs + rhs;
|
||||
}
|
||||
|
||||
static struct FrameTimingRecord * frameTimingRecord(
|
||||
LG_RendererFrameToken token)
|
||||
{
|
||||
@@ -326,7 +361,7 @@ static struct FrameTimingRecord * frameTimingRecord(
|
||||
(token - 1) % FRAME_TIMING_RECORD_COUNT];
|
||||
}
|
||||
|
||||
void app_handleFramePresented(uint64_t frameToken, uint64_t presentTime,
|
||||
void main_framePresented(uint64_t frameToken, uint64_t presentTime,
|
||||
bool valid)
|
||||
{
|
||||
if (!frameToken)
|
||||
@@ -386,7 +421,8 @@ static void frameTimingQueue(LG_RendererFrameToken token, uint64_t frameSerial,
|
||||
{
|
||||
const uint64_t elapsed = queueStart > dispatchStart ?
|
||||
queueStart - dispatchStart : 0;
|
||||
const uint64_t accounted = importTime + importWaitTime;
|
||||
const uint64_t accounted =
|
||||
frameTimingAdd(importTime, importWaitTime);
|
||||
record->importTime = importTime;
|
||||
record->importWaitTime = importWaitTime;
|
||||
record->dispatchTime = elapsed > accounted ?
|
||||
@@ -433,6 +469,9 @@ static void frameTimingFinishFrame(LG_RendererFrameToken token,
|
||||
record->readyTime = timing->readyTime;
|
||||
record->holdTime = timing->holdTime;
|
||||
record->readyLeadTime = timing->readyLeadTime;
|
||||
record->providerValid = timing->providerValid;
|
||||
record->receiveTime = timing->receiveTime;
|
||||
record->providerPrepareTime = timing->prepareTime;
|
||||
if (record->timestamp < timestamp)
|
||||
record->timestamp = timestamp;
|
||||
record->readyMask |= FRAME_TIMING_FRAME_READY;
|
||||
@@ -569,8 +608,13 @@ static void frameTimingPublishReady(void)
|
||||
const uint64_t queueTime =
|
||||
record->prepareStart > record->queueStart ?
|
||||
record->prepareStart - record->queueStart : 0;
|
||||
const uint64_t providerAccounted =
|
||||
record->providerValid ?
|
||||
frameTimingAdd(
|
||||
record->receiveTime, record->providerPrepareTime) : 0;
|
||||
const uint64_t transportAccounted =
|
||||
record->importTime + record->dispatchTime + queueTime;
|
||||
frameTimingAdd(frameTimingAdd(frameTimingAdd(providerAccounted,
|
||||
record->importTime), record->dispatchTime), queueTime);
|
||||
const uint64_t transportTime =
|
||||
record->readyLeadTime > transportAccounted ?
|
||||
record->readyLeadTime - transportAccounted : 0;
|
||||
@@ -580,6 +624,8 @@ static void frameTimingPublishReady(void)
|
||||
validMask &= ~OVERLAY_FRAME_TIMING_VALID_PRODUCER;
|
||||
if (!record->producerValid || !record->transportValid)
|
||||
validMask &= ~OVERLAY_FRAME_TIMING_VALID_TRANSPORT;
|
||||
if (!record->providerValid)
|
||||
validMask &= ~OVERLAY_FRAME_TIMING_VALID_PROVIDER;
|
||||
if (!record->presentValid)
|
||||
validMask &= ~OVERLAY_FRAME_TIMING_VALID_PRESENT;
|
||||
|
||||
@@ -592,8 +638,10 @@ static void frameTimingPublishReady(void)
|
||||
.ready = record->readyTime * 1e-6f,
|
||||
.hold = record->holdTime * 1e-6f,
|
||||
.transport = transportTime * 1e-6f,
|
||||
.import = (record->importTime +
|
||||
(record->producerValid ? 0 : record->importWaitTime)) * 1e-6f,
|
||||
.receive = record->receiveTime * 1e-6f,
|
||||
.providerPrepare = record->providerPrepareTime * 1e-6f,
|
||||
.import = frameTimingAdd(record->importTime,
|
||||
record->producerValid ? 0 : record->importWaitTime) * 1e-6f,
|
||||
.dispatch = record->dispatchTime * 1e-6f,
|
||||
.queue = queueTime * 1e-6f,
|
||||
.prepare = record->prepareTime * 1e-6f,
|
||||
@@ -752,13 +800,15 @@ static bool queueCursorRedraw(void)
|
||||
const int y = state->cursorY;
|
||||
const int hx = state->cursorHX;
|
||||
const int hy = state->cursorHY;
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
renderQueue_sourceCursorState(renderQueueSource(source), generation,
|
||||
visible, x, y, hx, hy);
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1109,8 +1159,8 @@ static int renderThread(void * unused)
|
||||
else if (g_state.videoOps &&
|
||||
g_state.videoOps->type == LG_VIDEO_TYPE_SW_SURFACE)
|
||||
{
|
||||
g_state.videoOps->swSurface->setActive(
|
||||
g_state.transport.handle, false);
|
||||
lgSwSurface_setActive(g_state.videoOps->swSurface,
|
||||
g_state.transport.handle, false, NULL, NULL);
|
||||
g_state.videoOps->swSurface->detach(g_state.transport.handle);
|
||||
}
|
||||
|
||||
@@ -1125,9 +1175,6 @@ static int renderThread(void * unused)
|
||||
int main_cursorThread(void * unused)
|
||||
{
|
||||
LG_RendererCursor cursorType = LG_CURSOR_COLOR;
|
||||
const uint64_t sourceGeneration = atomic_load_explicit(
|
||||
&g_state.videoSource[LG_VIDEO_SOURCE_PRIMARY].generation,
|
||||
memory_order_acquire);
|
||||
struct VideoSourceState * source =
|
||||
&g_state.videoSource[LG_VIDEO_SOURCE_PRIMARY];
|
||||
|
||||
@@ -1143,7 +1190,10 @@ int main_cursorThread(void * unused)
|
||||
g_state.transport.handle, &pointer);
|
||||
if (status != LG_TRANSPORT_OK)
|
||||
{
|
||||
if (status == LG_TRANSPORT_TIMEOUT || status == LG_TRANSPORT_UNAVAILABLE)
|
||||
if (videoStatusRetry(status,
|
||||
g_state.videoOps->frame->setStatusListener,
|
||||
atomic_load_explicit(
|
||||
&g_state.videoPointerAvailable, memory_order_acquire)))
|
||||
{
|
||||
if (!atomic_load_explicit(
|
||||
&g_state.stopVideoThreads, memory_order_acquire) &&
|
||||
@@ -1168,11 +1218,22 @@ int main_cursorThread(void * unused)
|
||||
}
|
||||
|
||||
const bool inputAvailable = lgInput_available();
|
||||
if (!videoPayloadAccept(
|
||||
&g_state.videoPointerAvailable, &g_state.videoPointerEpoch,
|
||||
pointer.epoch,
|
||||
g_state.videoOps->frame->setStatusListener != NULL, 0))
|
||||
{
|
||||
g_state.videoOps->frame->releasePointer(
|
||||
g_state.transport.handle, &pointer);
|
||||
continue;
|
||||
}
|
||||
LG_LOCK(g_state.videoSourceLock);
|
||||
if (atomic_load_explicit(&source->generation, memory_order_acquire) !=
|
||||
sourceGeneration)
|
||||
const uint64_t sourceGeneration = atomic_load_explicit(
|
||||
&source->generation, memory_order_acquire);
|
||||
if (!sourceGeneration)
|
||||
{
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
videoPayloadRelease();
|
||||
g_state.videoOps->frame->releasePointer(
|
||||
g_state.transport.handle, &pointer);
|
||||
continue;
|
||||
@@ -1198,6 +1259,7 @@ int main_cursorThread(void * unused)
|
||||
case CURSOR_TYPE_MASKED_COLOR: cursorType = LG_CURSOR_MASKED_COLOR; break;
|
||||
default:
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
videoPayloadRelease();
|
||||
DEBUG_ERROR("Invalid cursor type");
|
||||
g_state.videoOps->frame->releasePointer(
|
||||
g_state.transport.handle, &pointer);
|
||||
@@ -1260,8 +1322,6 @@ int main_cursorThread(void * unused)
|
||||
g_cursor.guest.hy = hy;
|
||||
g_cursor.guest.valid = valid;
|
||||
}
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
|
||||
if (pointer.flags & LG_TRANSPORT_POINTER_SHAPE)
|
||||
renderQueue_sourceCursorImage(RENDER_QUEUE_SOURCE_PRIMARY,
|
||||
sourceGeneration, cursorType, pointer.width, pointer.height,
|
||||
@@ -1278,6 +1338,7 @@ int main_cursorThread(void * unused)
|
||||
if (valid)
|
||||
renderQueue_sourceCursorState(RENDER_QUEUE_SOURCE_PRIMARY,
|
||||
sourceGeneration, visible, x, y, hx, hy);
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
|
||||
if (sourceApplied)
|
||||
{
|
||||
@@ -1298,6 +1359,7 @@ int main_cursorThread(void * unused)
|
||||
(g_params.mouseRedraw || contentChanged))))
|
||||
cursorRepaintRequest();
|
||||
|
||||
videoPayloadRelease();
|
||||
g_state.videoOps->frame->releasePointer(
|
||||
g_state.transport.handle, &pointer);
|
||||
}
|
||||
@@ -1313,9 +1375,7 @@ int main_frameThread(void * unused)
|
||||
uint64_t frameSerial = 0;
|
||||
uint32_t formatVersion = 0;
|
||||
LG_RendererFormat rendererFormat;
|
||||
const uint64_t sourceGeneration = atomic_load_explicit(
|
||||
&g_state.videoSource[LG_VIDEO_SOURCE_PRIMARY].generation,
|
||||
memory_order_acquire);
|
||||
uint64_t sourceGeneration = 0;
|
||||
|
||||
if (g_state.useDMA)
|
||||
DEBUG_INFO("Using DMA buffer support");
|
||||
@@ -1337,7 +1397,10 @@ int main_frameThread(void * unused)
|
||||
g_state.transport.handle, g_state.useDMA, &frame);
|
||||
if (status != LG_TRANSPORT_OK)
|
||||
{
|
||||
if (status == LG_TRANSPORT_TIMEOUT || status == LG_TRANSPORT_UNAVAILABLE)
|
||||
if (videoStatusRetry(status,
|
||||
g_state.videoOps->frame->setStatusListener,
|
||||
atomic_load_explicit(
|
||||
&g_state.videoFrameAvailable, memory_order_acquire)))
|
||||
{
|
||||
if (g_state.lgr->ops.onFramePoll)
|
||||
RENDERER(onFramePoll);
|
||||
@@ -1360,24 +1423,30 @@ int main_frameThread(void * unused)
|
||||
break;
|
||||
}
|
||||
|
||||
if (frame.serial == frameSerial && g_state.formatValid &&
|
||||
!frame.scheduleOwner)
|
||||
const uint64_t currentGeneration = atomic_load_explicit(
|
||||
&g_state.videoSource[LG_VIDEO_SOURCE_PRIMARY].generation,
|
||||
memory_order_acquire);
|
||||
bool sourceChanged;
|
||||
if (!videoFramePayloadAccept(&frame, currentGeneration,
|
||||
g_state.videoOps->frame->setStatusListener != NULL,
|
||||
&sourceGeneration, &frameSerial, &formatVersion, &sourceChanged))
|
||||
{
|
||||
g_state.videoOps->frame->releaseFrame(g_state.transport.handle, &frame);
|
||||
g_state.videoOps->frame->releaseFrame(
|
||||
g_state.transport.handle, &frame);
|
||||
continue;
|
||||
}
|
||||
frameSerial = frame.serial;
|
||||
const uint64_t dispatchStart = nanotime();
|
||||
|
||||
const LG_TransportFrameFormat * format = frame.format;
|
||||
if (!format)
|
||||
{
|
||||
videoPayloadRelease();
|
||||
DEBUG_ERROR("Transport returned a frame without format metadata");
|
||||
g_state.videoOps->frame->releaseFrame(g_state.transport.handle, &frame);
|
||||
app_setState(APP_STATE_SHUTDOWN);
|
||||
break;
|
||||
}
|
||||
const bool formatChanged =
|
||||
const bool formatChanged = sourceChanged ||
|
||||
!g_state.formatValid || format->version != formatVersion;
|
||||
bool rendererSupportsNativeHDR = false;
|
||||
if (formatChanged)
|
||||
@@ -1455,6 +1524,7 @@ int main_frameThread(void * unused)
|
||||
|
||||
if (invalid)
|
||||
{
|
||||
videoPayloadRelease();
|
||||
DEBUG_ERROR("Unsupported frame type");
|
||||
g_state.videoOps->frame->releaseFrame(
|
||||
g_state.transport.handle, &frame);
|
||||
@@ -1462,6 +1532,9 @@ int main_frameThread(void * unused)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (formatChanged)
|
||||
{
|
||||
DEBUG_INFO("Format: %s %ux%u (%ux%u) stride:%u pitch:%u rotation:%d hdr:%d pq:%d sdrWhite:%u nits",
|
||||
FrameTypeStr[format->type], format->frameWidth, format->frameHeight,
|
||||
format->dataWidth, format->dataHeight, format->stride, format->pitch,
|
||||
@@ -1472,6 +1545,7 @@ int main_frameThread(void * unused)
|
||||
if (!RENDERER(onFrameFormat, rendererFormat))
|
||||
{
|
||||
LG_UNLOCK(g_state.lgrLock);
|
||||
videoPayloadRelease();
|
||||
DEBUG_ERROR("Renderer failed to configure format");
|
||||
g_state.videoOps->frame->releaseFrame(
|
||||
g_state.transport.handle, &frame);
|
||||
@@ -1523,13 +1597,13 @@ int main_frameThread(void * unused)
|
||||
{
|
||||
if (formatChanged)
|
||||
LG_UNLOCK(g_state.lgrLock);
|
||||
videoPayloadRelease();
|
||||
frameTimingCancel(frameToken);
|
||||
g_state.videoOps->frame->releaseFrame(g_state.transport.handle, &frame);
|
||||
DEBUG_ERROR("Renderer onFrame returned failure");
|
||||
app_setState(APP_STATE_SHUTDOWN);
|
||||
break;
|
||||
}
|
||||
|
||||
/* A DMA snapshot can complete on the render thread immediately after it
|
||||
* is signalled below, so sample producer timing while this lease is still
|
||||
* unambiguously owned by the frame thread. */
|
||||
@@ -1563,10 +1637,17 @@ int main_frameThread(void * unused)
|
||||
memory_order_release);
|
||||
#endif
|
||||
|
||||
atomic_store_explicit(
|
||||
&g_state.videoSource[LG_VIDEO_SOURCE_PRIMARY].ready, true,
|
||||
memory_order_release);
|
||||
app_useVideoSource(LG_VIDEO_SOURCE_PRIMARY);
|
||||
if (atomic_load_explicit(
|
||||
&g_state.videoSource[LG_VIDEO_SOURCE_PRIMARY].generation,
|
||||
memory_order_acquire) == sourceGeneration &&
|
||||
atomic_load_explicit(
|
||||
&g_state.videoFrameAvailable, memory_order_acquire))
|
||||
{
|
||||
atomic_store_explicit(
|
||||
&g_state.videoSource[LG_VIDEO_SOURCE_PRIMARY].ready, true,
|
||||
memory_order_release);
|
||||
app_useVideoSource(LG_VIDEO_SOURCE_PRIMARY);
|
||||
}
|
||||
|
||||
if (formatChanged)
|
||||
{
|
||||
@@ -1602,6 +1683,7 @@ int main_frameThread(void * unused)
|
||||
}
|
||||
|
||||
frameTimingFinishFrame(frameToken, &timing);
|
||||
videoPayloadRelease();
|
||||
|
||||
if (!rendererOwnsFrame)
|
||||
g_state.videoOps->frame->releaseFrame(g_state.transport.handle, &frame);
|
||||
@@ -1741,9 +1823,251 @@ static void videoSourceClearCursor(LG_VideoSource source)
|
||||
if (atomic_load_explicit(
|
||||
&g_state.videoSourceApplied, memory_order_acquire) == source)
|
||||
g_cursor.guest.valid = false;
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
|
||||
renderQueue_sourceClearCursor(renderQueueSource(source));
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
}
|
||||
|
||||
static bool videoComponentStatusChanged(atomic_bool * available,
|
||||
atomic_uint_least64_t * epoch, atomic_int * reason,
|
||||
const LG_VideoComponentStatus * status)
|
||||
{
|
||||
/* Close the component before changing epochs so a consumer can never pair
|
||||
* a newly available state with a payload from the prior endpoint. */
|
||||
const bool oldAvailable = atomic_exchange_explicit(
|
||||
available, false, memory_order_acq_rel);
|
||||
const uint64_t oldEpoch = atomic_exchange_explicit(
|
||||
epoch, status->epoch, memory_order_acq_rel);
|
||||
atomic_store_explicit(reason, status->reason, memory_order_release);
|
||||
atomic_store_explicit(
|
||||
available, status->available, memory_order_release);
|
||||
return oldAvailable != status->available ||
|
||||
oldEpoch != status->epoch;
|
||||
}
|
||||
|
||||
static bool videoComponentPayloadCurrent(const atomic_bool * available,
|
||||
const atomic_uint_least64_t * epoch, uint64_t payloadEpoch,
|
||||
bool trackEpoch)
|
||||
{
|
||||
if (!atomic_load_explicit(available, memory_order_acquire))
|
||||
return false;
|
||||
return !trackEpoch || (payloadEpoch && atomic_load_explicit(
|
||||
epoch, memory_order_acquire) == payloadEpoch);
|
||||
}
|
||||
|
||||
/* Success retains the payload gate. The caller must release it after every
|
||||
* externally visible effect derived from the accepted payload is complete. */
|
||||
static bool videoPayloadAccept(const atomic_bool * available,
|
||||
const atomic_uint_least64_t * epoch, uint64_t payloadEpoch,
|
||||
bool trackEpoch, uint64_t sourceGeneration)
|
||||
{
|
||||
LG_LOCK(g_state.videoPayloadLock);
|
||||
const bool current = videoComponentPayloadCurrent(
|
||||
available, epoch, payloadEpoch, trackEpoch) &&
|
||||
(!sourceGeneration || atomic_load_explicit(
|
||||
&g_state.videoSource[LG_VIDEO_SOURCE_PRIMARY].generation,
|
||||
memory_order_acquire) == sourceGeneration);
|
||||
if (!current)
|
||||
LG_UNLOCK(g_state.videoPayloadLock);
|
||||
return current;
|
||||
}
|
||||
|
||||
static void videoPayloadRelease(void)
|
||||
{
|
||||
LG_UNLOCK(g_state.videoPayloadLock);
|
||||
}
|
||||
|
||||
static bool videoFramePayloadAccept(const LG_TransportFrame * frame,
|
||||
uint64_t currentGeneration, bool trackEpoch,
|
||||
uint64_t * sourceGeneration, uint64_t * frameSerial,
|
||||
uint32_t * formatVersion, bool * sourceChanged)
|
||||
{
|
||||
if (!currentGeneration || !videoPayloadAccept(
|
||||
&g_state.videoFrameAvailable, &g_state.videoFrameEpoch,
|
||||
frame->epoch, trackEpoch, currentGeneration))
|
||||
return false;
|
||||
|
||||
*sourceChanged = *sourceGeneration != currentGeneration;
|
||||
if (*sourceChanged)
|
||||
{
|
||||
*sourceGeneration = currentGeneration;
|
||||
*frameSerial = 0;
|
||||
*formatVersion = 0;
|
||||
}
|
||||
|
||||
if (!*sourceChanged && frame->serial == *frameSerial &&
|
||||
g_state.formatValid && !frame->scheduleOwner)
|
||||
{
|
||||
videoPayloadRelease();
|
||||
return false;
|
||||
}
|
||||
|
||||
*frameSerial = frame->serial;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void primaryVideoStatusChanged(void * opaque,
|
||||
const LG_VideoStatus * status)
|
||||
{
|
||||
(void)opaque;
|
||||
if (!status || !atomic_load_explicit(
|
||||
&g_state.videoStatusActive, memory_order_acquire) ||
|
||||
app_getState() != APP_STATE_RUNNING)
|
||||
return;
|
||||
|
||||
LG_LOCK(g_state.videoPayloadLock);
|
||||
if (!atomic_load_explicit(
|
||||
&g_state.videoStatusActive, memory_order_acquire) ||
|
||||
app_getState() != APP_STATE_RUNNING)
|
||||
{
|
||||
LG_UNLOCK(g_state.videoPayloadLock);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool frameWasAvailable = atomic_load_explicit(
|
||||
&g_state.videoFrameAvailable, memory_order_acquire);
|
||||
const bool first = !atomic_exchange_explicit(
|
||||
&g_state.videoStatusKnown, true, memory_order_acq_rel);
|
||||
const bool frameChanged = videoComponentStatusChanged(
|
||||
&g_state.videoFrameAvailable, &g_state.videoFrameEpoch,
|
||||
&g_state.videoFrameReason, &status->frame) || first;
|
||||
const bool pointerChanged = videoComponentStatusChanged(
|
||||
&g_state.videoPointerAvailable, &g_state.videoPointerEpoch,
|
||||
&g_state.videoPointerReason, &status->pointer) || first;
|
||||
|
||||
if (pointerChanged)
|
||||
videoSourceClearCursor(LG_VIDEO_SOURCE_PRIMARY);
|
||||
|
||||
if (frameChanged)
|
||||
{
|
||||
const uint64_t sourceGeneration = atomic_load_explicit(
|
||||
&g_state.videoSource[LG_VIDEO_SOURCE_PRIMARY].generation,
|
||||
memory_order_acquire);
|
||||
if (!status->frame.available || frameWasAvailable || !sourceGeneration)
|
||||
{
|
||||
videoSourceInvalidate(LG_VIDEO_SOURCE_PRIMARY);
|
||||
/* Keep an inactive generation while frames are unavailable so an
|
||||
* independent pointer component can continue updating its retained
|
||||
* state. */
|
||||
videoSourceBegin(LG_VIDEO_SOURCE_PRIMARY);
|
||||
}
|
||||
}
|
||||
LG_UNLOCK(g_state.videoPayloadLock);
|
||||
|
||||
if (frameChanged)
|
||||
g_state.videoOps->frame->cancelFrameWait(g_state.transport.handle);
|
||||
if (pointerChanged && g_state.videoOps->frame->nextPointer)
|
||||
g_state.videoOps->frame->cancelPointerWait(g_state.transport.handle);
|
||||
|
||||
if (!frameChanged)
|
||||
return;
|
||||
|
||||
if (status->frame.available)
|
||||
return;
|
||||
|
||||
if (atomic_load_explicit(&g_state.stopVideo, memory_order_acquire))
|
||||
return;
|
||||
|
||||
const bool fallbackReady = fallbackRequestVideo();
|
||||
if (atomic_load_explicit(&g_state.stopVideo, memory_order_acquire))
|
||||
{
|
||||
if (g_state.fallback)
|
||||
lgTransportFallback_requestVideoActive(g_state.fallback, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fallbackReady)
|
||||
videoSourceShowSplashIfNeeded();
|
||||
}
|
||||
|
||||
static void primaryVideoStatusStart(void)
|
||||
{
|
||||
if (!g_state.videoOps || g_state.videoOps->type != LG_VIDEO_TYPE_FRAME)
|
||||
return;
|
||||
|
||||
atomic_store_explicit(
|
||||
&g_state.videoFrameAvailable, false, memory_order_relaxed);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoPointerAvailable, false, memory_order_relaxed);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoStatusKnown, false, memory_order_relaxed);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoFrameEpoch, 0, memory_order_relaxed);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoPointerEpoch, 0, memory_order_relaxed);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoFrameReason, LG_TRANSPORT_UNAVAILABLE,
|
||||
memory_order_relaxed);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoPointerReason, LG_TRANSPORT_UNAVAILABLE,
|
||||
memory_order_relaxed);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoStatusActive, true, memory_order_release);
|
||||
|
||||
if (g_state.videoOps->frame->setStatusListener)
|
||||
{
|
||||
g_state.videoOps->frame->setStatusListener(g_state.transport.handle,
|
||||
primaryVideoStatusChanged, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
const LG_VideoStatus status =
|
||||
{
|
||||
.frame =
|
||||
{
|
||||
.available = true,
|
||||
.epoch = 1,
|
||||
.reason = LG_TRANSPORT_OK,
|
||||
},
|
||||
.pointer =
|
||||
{
|
||||
.available = g_state.videoOps->frame->nextPointer != NULL,
|
||||
.epoch = g_state.videoOps->frame->nextPointer ? 1 : 0,
|
||||
.reason = g_state.videoOps->frame->nextPointer ?
|
||||
LG_TRANSPORT_OK : LG_TRANSPORT_UNAVAILABLE,
|
||||
},
|
||||
};
|
||||
primaryVideoStatusChanged(NULL, &status);
|
||||
}
|
||||
|
||||
static void primaryVideoStatusStop(void)
|
||||
{
|
||||
if (!atomic_exchange_explicit(
|
||||
&g_state.videoStatusActive, false, memory_order_acq_rel))
|
||||
return;
|
||||
|
||||
if (g_state.videoOps && g_state.videoOps->type == LG_VIDEO_TYPE_FRAME &&
|
||||
g_state.videoOps->frame->setStatusListener)
|
||||
g_state.videoOps->frame->setStatusListener(
|
||||
g_state.transport.handle, NULL, NULL);
|
||||
|
||||
LG_LOCK(g_state.videoPayloadLock);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoFrameAvailable, false, memory_order_release);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoPointerAvailable, false, memory_order_release);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoFrameReason, LG_TRANSPORT_DISCONNECTED,
|
||||
memory_order_release);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoPointerReason, LG_TRANSPORT_DISCONNECTED,
|
||||
memory_order_release);
|
||||
atomic_store_explicit(
|
||||
&g_state.videoStatusKnown, false, memory_order_release);
|
||||
LG_UNLOCK(g_state.videoPayloadLock);
|
||||
}
|
||||
|
||||
void main_videoStreamEnabled(void)
|
||||
{
|
||||
if (!g_state.videoOps || g_state.videoOps->type != LG_VIDEO_TYPE_FRAME ||
|
||||
!g_state.videoOps->frame->setStatusListener ||
|
||||
!atomic_load_explicit(
|
||||
&g_state.videoStatusKnown, memory_order_acquire) ||
|
||||
atomic_load_explicit(
|
||||
&g_state.videoFrameAvailable, memory_order_acquire))
|
||||
return;
|
||||
|
||||
if (!fallbackRequestVideo())
|
||||
videoSourceShowSplashIfNeeded();
|
||||
}
|
||||
|
||||
static void videoSourceReject(void * opaque, RenderQueueSource queueSource,
|
||||
@@ -1914,10 +2238,10 @@ static void videoSourceApplied(void * opaque, RenderQueueSource queueSource,
|
||||
app_refreshVideoSource();
|
||||
}
|
||||
|
||||
static bool swSurfaceEventAdmitted(LG_VideoSource source)
|
||||
static bool swSurfaceEventAccepted(LG_VideoSource source)
|
||||
{
|
||||
return source != LG_VIDEO_SOURCE_FALLBACK ||
|
||||
lgTransportFallback_admitted(g_state.fallback);
|
||||
lgTransportFallback_acceptsVideoEvents(g_state.fallback);
|
||||
}
|
||||
|
||||
static void swSurfaceConfigure(LG_VideoSource source,
|
||||
@@ -1926,7 +2250,7 @@ static void swSurfaceConfigure(LG_VideoSource source,
|
||||
struct VideoSourceState * state = &g_state.videoSource[source];
|
||||
uint64_t generation;
|
||||
LG_LOCK(g_state.videoSourceLock);
|
||||
if (!swSurfaceEventAdmitted(source))
|
||||
if (!swSurfaceEventAccepted(source))
|
||||
{
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
return;
|
||||
@@ -1984,12 +2308,12 @@ static void swSurfaceDrawFill(LG_VideoSource source, int x, int y,
|
||||
int width, int height, uint32_t color)
|
||||
{
|
||||
LG_LOCK(g_state.videoSourceLock);
|
||||
const bool admitted = swSurfaceEventAdmitted(source);
|
||||
const uint64_t generation = admitted ? atomic_load_explicit(
|
||||
const bool accepted = swSurfaceEventAccepted(source);
|
||||
const uint64_t generation = accepted ? atomic_load_explicit(
|
||||
&g_state.videoSource[source].generation, memory_order_acquire) : 0;
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
|
||||
if (!admitted)
|
||||
if (!accepted)
|
||||
return;
|
||||
|
||||
renderQueue_sourceSwSurfaceDrawFill(renderQueueSource(source), generation,
|
||||
@@ -2000,12 +2324,12 @@ static void swSurfaceDrawBitmap(LG_VideoSource source, bool topDown,
|
||||
int x, int y, int width, int height, int stride, const void * data)
|
||||
{
|
||||
LG_LOCK(g_state.videoSourceLock);
|
||||
const bool admitted = swSurfaceEventAdmitted(source);
|
||||
const uint64_t generation = admitted ? atomic_load_explicit(
|
||||
const bool accepted = swSurfaceEventAccepted(source);
|
||||
const uint64_t generation = accepted ? atomic_load_explicit(
|
||||
&g_state.videoSource[source].generation, memory_order_acquire) : 0;
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
|
||||
if (!admitted)
|
||||
if (!accepted)
|
||||
return;
|
||||
|
||||
renderQueue_sourceSwSurfaceDrawBitmap(renderQueueSource(source), generation,
|
||||
@@ -2039,7 +2363,7 @@ static void swSurfacePointer(LG_VideoSource source,
|
||||
const bool drawCursor = source != LG_VIDEO_SOURCE_PRIMARY ||
|
||||
g_cursor.draw || !lgInput_available();
|
||||
LG_LOCK(g_state.videoSourceLock);
|
||||
if (!swSurfaceEventAdmitted(source))
|
||||
if (!swSurfaceEventAccepted(source))
|
||||
{
|
||||
LG_UNLOCK(g_state.videoSourceLock);
|
||||
return;
|
||||
@@ -2163,7 +2487,9 @@ static void swSurfaceEventConfigure(void * opaque,
|
||||
|
||||
static void swSurfaceEventDestroy(void * opaque)
|
||||
{
|
||||
swSurfaceDestroy(swSurfaceSource(opaque));
|
||||
const LG_VideoSource source = swSurfaceSource(opaque);
|
||||
if (swSurfaceEventAccepted(source))
|
||||
swSurfaceDestroy(source);
|
||||
}
|
||||
|
||||
static void swSurfaceEventDrawFill(void * opaque, int x, int y,
|
||||
@@ -2196,14 +2522,29 @@ static const LG_SwSurfaceEventOps swSurfaceEvents =
|
||||
|
||||
static bool fallbackRequestVideo(void)
|
||||
{
|
||||
if (!g_state.fallback)
|
||||
if (!g_state.fallback || atomic_load_explicit(
|
||||
&g_state.stopVideo, memory_order_acquire))
|
||||
return false;
|
||||
|
||||
lgTransportFallback_requestVideoActive(g_state.fallback, true);
|
||||
if (!lgTransportFallback_admitted(g_state.fallback))
|
||||
if (atomic_load_explicit(&g_state.stopVideo, memory_order_acquire))
|
||||
{
|
||||
lgTransportFallback_requestVideoActive(g_state.fallback, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lgTransportFallback_usable(g_state.fallback) ||
|
||||
!lgTransportFallback_ready(g_state.fallback))
|
||||
return false;
|
||||
|
||||
return app_useVideoSource(LG_VIDEO_SOURCE_FALLBACK);
|
||||
const bool active = app_useVideoSource(LG_VIDEO_SOURCE_FALLBACK);
|
||||
if (atomic_load_explicit(&g_state.stopVideo, memory_order_acquire))
|
||||
{
|
||||
lgTransportFallback_requestVideoActive(g_state.fallback, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
static void fallbackConnected(void * opaque,
|
||||
@@ -2235,6 +2576,23 @@ static void fallbackDisconnected(void * opaque)
|
||||
videoSourceShowSplashIfNeeded();
|
||||
}
|
||||
|
||||
static void fallbackVideoStateChanged(void * opaque, bool ready)
|
||||
{
|
||||
(void)opaque;
|
||||
if (app_getState() == APP_STATE_SHUTDOWN)
|
||||
return;
|
||||
|
||||
if (ready && lgTransportFallback_videoRequested(g_state.fallback))
|
||||
{
|
||||
fallbackRequestVideo();
|
||||
return;
|
||||
}
|
||||
|
||||
swSurfaceDestroy(LG_VIDEO_SOURCE_FALLBACK);
|
||||
videoSourceClearCursor(LG_VIDEO_SOURCE_FALLBACK);
|
||||
app_refreshVideoSource();
|
||||
}
|
||||
|
||||
static void fallbackLost(void * opaque)
|
||||
{
|
||||
(void)opaque;
|
||||
@@ -2248,14 +2606,14 @@ static void fallbackLost(void * opaque)
|
||||
}
|
||||
}
|
||||
|
||||
static void fallbackUUIDMismatch(void * opaque, const uint8_t primary[16],
|
||||
static void fallbackEndpointMismatch(void * opaque, const uint8_t primary[16],
|
||||
const uint8_t fallback[16])
|
||||
{
|
||||
(void)opaque;
|
||||
(void)primary;
|
||||
(void)fallback;
|
||||
atomic_store_explicit(
|
||||
&g_state.fallbackUUIDMismatch, true, memory_order_release);
|
||||
&g_state.fallbackEndpointMismatch, true, memory_order_release);
|
||||
app_invalidateWindow(false);
|
||||
}
|
||||
|
||||
@@ -2264,7 +2622,8 @@ static const LG_TransportFallbackEventOps fallbackEvents =
|
||||
.connected = fallbackConnected,
|
||||
.lost = fallbackLost,
|
||||
.disconnected = fallbackDisconnected,
|
||||
.uuidMismatch = fallbackUUIDMismatch,
|
||||
.videoStateChanged = fallbackVideoStateChanged,
|
||||
.endpointMismatch = fallbackEndpointMismatch,
|
||||
};
|
||||
|
||||
static void primaryLost(void)
|
||||
@@ -2276,7 +2635,7 @@ static void primaryLost(void)
|
||||
for (;;)
|
||||
{
|
||||
unsigned int next = lost | TRANSPORT_LOST_PRIMARY;
|
||||
if (lgTransportFallback_ready(g_state.fallback))
|
||||
if (lgTransportFallback_usable(g_state.fallback))
|
||||
next &= ~TRANSPORT_LOST_FALLBACK;
|
||||
if (atomic_compare_exchange_weak_explicit(&g_state.transportLost,
|
||||
&lost, next, memory_order_acq_rel, memory_order_acquire))
|
||||
@@ -2319,7 +2678,7 @@ static void fallbackStop(void)
|
||||
static void fallbackHandleEvents(void)
|
||||
{
|
||||
if (!atomic_exchange_explicit(
|
||||
&g_state.fallbackUUIDMismatch, false, memory_order_acq_rel))
|
||||
&g_state.fallbackEndpointMismatch, false, memory_order_acq_rel))
|
||||
return;
|
||||
|
||||
app_msgBox(
|
||||
@@ -2572,6 +2931,12 @@ static const char * recoveryErrorText(LG_RecoveryError error)
|
||||
case LG_RECOVERY_ERR_NO_FALLBACK_DISPLAY:
|
||||
return "No fallback display became active";
|
||||
|
||||
case LG_RECOVERY_ERR_BUSY:
|
||||
return "A conflicting recovery request is already in progress";
|
||||
|
||||
case LG_RECOVERY_ERR_CAPACITY:
|
||||
return "Too many recovery requests are pending";
|
||||
|
||||
case LG_RECOVERY_ERR_UNSUPPORTED:
|
||||
return "Recovery is not supported by the guest driver";
|
||||
|
||||
@@ -2919,17 +3284,34 @@ static MsgBoxHandle showSpiceInputHelp(void)
|
||||
|
||||
struct TransportSessionProbe
|
||||
{
|
||||
LG_Transport * transport;
|
||||
LG_Transport * transport;
|
||||
const LG_TransportOps * ops;
|
||||
LG_TransportSession session;
|
||||
LG_TransportStatus status;
|
||||
atomic_bool done;
|
||||
LG_TransportCancelledFn cancelled;
|
||||
void * cancelOpaque;
|
||||
LG_TransportSession session;
|
||||
LG_TransportStatus status;
|
||||
atomic_bool done;
|
||||
};
|
||||
|
||||
static bool transportSessionCancelled(void * opaque)
|
||||
{
|
||||
(void)opaque;
|
||||
return app_getState() != APP_STATE_RUNNING;
|
||||
}
|
||||
|
||||
static bool primarySurfaceActivationCancelled(void * opaque)
|
||||
{
|
||||
(void)opaque;
|
||||
return app_getState() != APP_STATE_RUNNING ||
|
||||
atomic_load_explicit(&g_state.stopVideo, memory_order_acquire);
|
||||
}
|
||||
|
||||
static int transportSessionProbe(void * opaque)
|
||||
{
|
||||
struct TransportSessionProbe * probe = opaque;
|
||||
probe->status = probe->ops->connect(probe->transport, &probe->session);
|
||||
probe->status = probe->ops->connectCancellable(
|
||||
probe->transport, &probe->session,
|
||||
probe->cancelled, probe->cancelOpaque);
|
||||
atomic_store_explicit(&probe->done, true, memory_order_release);
|
||||
return 0;
|
||||
}
|
||||
@@ -3059,18 +3441,26 @@ static int lg_run(void)
|
||||
LG_RecoveryInfo initialRecovery = { 0 };
|
||||
const bool initialRecoveryValid = recoveryGetInfo(&initialRecovery);
|
||||
|
||||
g_state.videoOps =
|
||||
g_state.transport.ops->getVideoOps(g_state.transport.handle);
|
||||
g_state.videoOps = g_state.transport.ops->getVideoOps ?
|
||||
g_state.transport.ops->getVideoOps(g_state.transport.handle) : NULL;
|
||||
if (!g_state.videoOps ||
|
||||
(g_state.videoOps->type != LG_VIDEO_TYPE_FRAME &&
|
||||
g_state.videoOps->type != LG_VIDEO_TYPE_SW_SURFACE) ||
|
||||
(g_state.videoOps->type == LG_VIDEO_TYPE_FRAME &&
|
||||
!g_state.videoOps->frame) ||
|
||||
(!g_state.videoOps->frame ||
|
||||
!g_state.videoOps->frame->nextFrame ||
|
||||
!g_state.videoOps->frame->releaseFrame ||
|
||||
!g_state.videoOps->frame->cancelFrameWait ||
|
||||
((g_state.videoOps->frame->nextPointer != NULL) !=
|
||||
(g_state.videoOps->frame->releasePointer != NULL)) ||
|
||||
(g_state.videoOps->frame->nextPointer &&
|
||||
!g_state.videoOps->frame->cancelPointerWait))) ||
|
||||
(g_state.videoOps->type == LG_VIDEO_TYPE_SW_SURFACE &&
|
||||
(!g_state.videoOps->swSurface ||
|
||||
!g_state.videoOps->swSurface->attach ||
|
||||
!g_state.videoOps->swSurface->detach ||
|
||||
!g_state.videoOps->swSurface->setActive)))
|
||||
!g_state.videoOps->swSurface->setActive ||
|
||||
!g_state.videoOps->swSurface->cancelPending)))
|
||||
{
|
||||
DEBUG_ERROR("Transport does not provide a usable video source");
|
||||
return -1;
|
||||
@@ -3092,6 +3482,7 @@ static int lg_run(void)
|
||||
}
|
||||
|
||||
//setup the render command queue
|
||||
LG_LOCK_INIT(g_state.videoPayloadLock);
|
||||
LG_LOCK_INIT(g_state.videoSourceLock);
|
||||
LG_LOCK_INIT(g_state.videoSplashLock);
|
||||
renderQueue_init();
|
||||
@@ -3296,9 +3687,11 @@ restart:
|
||||
}
|
||||
|
||||
struct TransportSessionProbe probe = {
|
||||
.transport = g_state.transport.handle,
|
||||
.ops = g_state.transport.ops,
|
||||
.done = false,
|
||||
.transport = g_state.transport.handle,
|
||||
.ops = g_state.transport.ops,
|
||||
.cancelled = transportSessionCancelled,
|
||||
.cancelOpaque = NULL,
|
||||
.done = false,
|
||||
};
|
||||
LGThread * probeThread;
|
||||
if (!lgCreateThread("transportSession", transportSessionProbe, &probe,
|
||||
@@ -3490,15 +3883,19 @@ restart:
|
||||
|
||||
if (g_state.videoOps->type == LG_VIDEO_TYPE_FRAME)
|
||||
{
|
||||
videoSourceBegin(LG_VIDEO_SOURCE_PRIMARY);
|
||||
primaryVideoStatusStart();
|
||||
if (!atomic_load_explicit(&g_state.stopVideo, memory_order_acquire) &&
|
||||
!core_startVideoThreads())
|
||||
{
|
||||
primaryVideoStatusStop();
|
||||
return recoveryExit(&recoveryPrompt, -1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!g_state.videoOps->swSurface->setActive(
|
||||
g_state.transport.handle, true))
|
||||
if (!lgSwSurface_setActive(g_state.videoOps->swSurface,
|
||||
g_state.transport.handle, true,
|
||||
primarySurfaceActivationCancelled, NULL))
|
||||
{
|
||||
DEBUG_ERROR("Failed to activate the primary software surface");
|
||||
return recoveryExit(&recoveryPrompt, -1);
|
||||
@@ -3509,7 +3906,6 @@ restart:
|
||||
while(likely(app_getState() == APP_STATE_RUNNING))
|
||||
{
|
||||
fallbackHandleEvents();
|
||||
|
||||
if (unlikely(!g_state.transport.ops->sessionValid(
|
||||
g_state.transport.handle)))
|
||||
{
|
||||
@@ -3525,6 +3921,7 @@ restart:
|
||||
}
|
||||
|
||||
frameScheduler_stop();
|
||||
primaryVideoStatusStop();
|
||||
|
||||
if (app_getState() == APP_STATE_RESTART)
|
||||
{
|
||||
@@ -3538,8 +3935,8 @@ restart:
|
||||
if (g_state.videoOps->type == LG_VIDEO_TYPE_FRAME)
|
||||
core_stopVideoThreads();
|
||||
else
|
||||
g_state.videoOps->swSurface->setActive(
|
||||
g_state.transport.handle, false);
|
||||
lgSwSurface_setActive(g_state.videoOps->swSurface,
|
||||
g_state.transport.handle, false, NULL, NULL);
|
||||
videoSourceInvalidate(LG_VIDEO_SOURCE_PRIMARY);
|
||||
videoSourceClearCursor(LG_VIDEO_SOURCE_PRIMARY);
|
||||
lgInput_dropTransport();
|
||||
@@ -3651,6 +4048,7 @@ static void lg_shutdown(void)
|
||||
|
||||
renderQueue_setSourceFns(NULL, NULL, NULL, NULL);
|
||||
renderQueue_free();
|
||||
LG_LOCK_FREE(g_state.videoPayloadLock);
|
||||
LG_LOCK_FREE(g_state.videoSourceLock);
|
||||
LG_LOCK_FREE(g_state.videoSplashLock);
|
||||
frameScheduler_free();
|
||||
|
||||
@@ -101,11 +101,20 @@ struct AppState
|
||||
atomic_int videoSourceRequested;
|
||||
atomic_int videoSourceApplied;
|
||||
atomic_uint_least64_t videoSourceAppliedGeneration;
|
||||
atomic_bool videoStatusActive;
|
||||
atomic_bool videoStatusKnown;
|
||||
atomic_bool videoFrameAvailable;
|
||||
atomic_bool videoPointerAvailable;
|
||||
atomic_uint_least64_t videoFrameEpoch;
|
||||
atomic_uint_least64_t videoPointerEpoch;
|
||||
atomic_int videoFrameReason;
|
||||
atomic_int videoPointerReason;
|
||||
LG_Lock videoPayloadLock;
|
||||
LG_Lock videoSourceLock;
|
||||
LG_Lock videoSplashLock;
|
||||
bool videoGeometryDirty;
|
||||
|
||||
atomic_bool fallbackUUIDMismatch;
|
||||
atomic_bool fallbackEndpointMismatch;
|
||||
atomic_uint transportLost;
|
||||
|
||||
uint8_t guestUUID[16];
|
||||
@@ -357,5 +366,8 @@ extern struct AppParams g_params;
|
||||
|
||||
int main_cursorThread(void * unused);
|
||||
int main_frameThread(void * unused);
|
||||
void main_videoStreamEnabled(void);
|
||||
void main_framePresented(uint64_t frameToken, uint64_t presentTime,
|
||||
bool valid);
|
||||
|
||||
#define RENDERER(fn, ...) g_state.lgr->ops.fn(g_state.lgr, ##__VA_ARGS__)
|
||||
|
||||
@@ -238,6 +238,8 @@ static const char * const frameTimingLabels[FRAME_TIMING_STAGE_COUNT] = {
|
||||
"Ready",
|
||||
"Hold",
|
||||
"Transport",
|
||||
"Receive",
|
||||
"Provider Prep",
|
||||
"Import",
|
||||
"Dispatch",
|
||||
"Queue",
|
||||
@@ -310,6 +312,8 @@ static bool accumulateFrameTimingSample(int index, void * value_, void * udata_)
|
||||
timing->ready,
|
||||
timing->hold,
|
||||
timing->transport,
|
||||
timing->receive,
|
||||
timing->providerPrepare,
|
||||
timing->import,
|
||||
timing->dispatch,
|
||||
timing->queue,
|
||||
|
||||
@@ -54,6 +54,8 @@ typedef struct OverlayFrameTiming
|
||||
float ready;
|
||||
float hold;
|
||||
float transport;
|
||||
float receive;
|
||||
float providerPrepare;
|
||||
float import;
|
||||
float dispatch;
|
||||
float queue;
|
||||
@@ -75,6 +77,8 @@ enum OverlayFrameTimingStage
|
||||
OVERLAY_FRAME_TIMING_READY,
|
||||
OVERLAY_FRAME_TIMING_HOLD,
|
||||
OVERLAY_FRAME_TIMING_TRANSPORT,
|
||||
OVERLAY_FRAME_TIMING_RECEIVE,
|
||||
OVERLAY_FRAME_TIMING_PROVIDER_PREPARE,
|
||||
OVERLAY_FRAME_TIMING_IMPORT,
|
||||
OVERLAY_FRAME_TIMING_DISPATCH,
|
||||
OVERLAY_FRAME_TIMING_QUEUE,
|
||||
@@ -94,6 +98,9 @@ enum OverlayFrameTimingStage
|
||||
((1U << OVERLAY_FRAME_TIMING_TRANSPORT) - 1U)
|
||||
#define OVERLAY_FRAME_TIMING_VALID_TRANSPORT \
|
||||
(1U << OVERLAY_FRAME_TIMING_TRANSPORT)
|
||||
#define OVERLAY_FRAME_TIMING_VALID_PROVIDER \
|
||||
((1U << OVERLAY_FRAME_TIMING_RECEIVE) | \
|
||||
(1U << OVERLAY_FRAME_TIMING_PROVIDER_PREPARE))
|
||||
#define OVERLAY_FRAME_TIMING_VALID_PRESENT \
|
||||
(1U << OVERLAY_FRAME_TIMING_PRESENT)
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ typedef struct RenderCommand
|
||||
|
||||
RenderQueueSource source;
|
||||
uint64_t generation;
|
||||
uint64_t cursorGeneration;
|
||||
uint64_t transitionSerial;
|
||||
|
||||
enum
|
||||
@@ -47,6 +48,7 @@ typedef struct RenderCommand
|
||||
CURSOR_OP_IMAGE,
|
||||
CURSOR_OP_COLOR_TRANSFORM,
|
||||
CURSOR_OP_WHITE_LEVEL,
|
||||
CURSOR_OP_CLEAR,
|
||||
SOURCE_OP_TRANSITION,
|
||||
}
|
||||
op;
|
||||
@@ -172,6 +174,7 @@ static bool l_rendererSupportsNativeHDR;
|
||||
static LG_RendererFormat l_surfaceFormat;
|
||||
|
||||
static _Atomic(uint64_t) l_sourceGeneration[RENDER_QUEUE_SOURCE_COUNT];
|
||||
static _Atomic(uint64_t) l_cursorGeneration[RENDER_QUEUE_SOURCE_COUNT];
|
||||
static _Atomic(uint64_t) l_transitionSerial;
|
||||
static LG_Lock l_sourceLock;
|
||||
static LG_Lock l_transitionLock;
|
||||
@@ -257,6 +260,15 @@ static bool transitionCommand(const RenderCommand * cmd)
|
||||
cmd->op == SW_SURFACE_OP_CONFIGURE_TRANSITION;
|
||||
}
|
||||
|
||||
static bool cursorCommand(const RenderCommand * cmd)
|
||||
{
|
||||
return cmd->op == CURSOR_OP_STATE ||
|
||||
cmd->op == CURSOR_OP_IMAGE ||
|
||||
cmd->op == CURSOR_OP_COLOR_TRANSFORM ||
|
||||
cmd->op == CURSOR_OP_WHITE_LEVEL ||
|
||||
cmd->op == CURSOR_OP_CLEAR;
|
||||
}
|
||||
|
||||
static bool commandValid(const RenderCommand * cmd)
|
||||
{
|
||||
if (transitionCommand(cmd) &&
|
||||
@@ -266,8 +278,15 @@ static bool commandValid(const RenderCommand * cmd)
|
||||
|
||||
if (cmd->source == RENDER_QUEUE_SOURCE_NONE)
|
||||
return cmd->op == SOURCE_OP_TRANSITION;
|
||||
if (!sourceValid(cmd->source))
|
||||
return false;
|
||||
|
||||
return generationValid(cmd->source, cmd->generation);
|
||||
const bool cursorCurrent = !cursorCommand(cmd) || atomic_load_explicit(
|
||||
&l_cursorGeneration[cmd->source], memory_order_acquire) ==
|
||||
cmd->cursorGeneration;
|
||||
if (cmd->op == CURSOR_OP_CLEAR)
|
||||
return cursorCurrent;
|
||||
return generationValid(cmd->source, cmd->generation) && cursorCurrent;
|
||||
}
|
||||
|
||||
static uint64_t swSurfaceDamageArea(const FrameDamageRect * rect)
|
||||
@@ -469,6 +488,8 @@ static void setCommandSource(RenderCommand * cmd, RenderQueueSource source,
|
||||
{
|
||||
cmd->source = source;
|
||||
cmd->generation = generation;
|
||||
cmd->cursorGeneration = sourceValid(source) ? atomic_load_explicit(
|
||||
&l_cursorGeneration[source], memory_order_acquire) : 0;
|
||||
}
|
||||
|
||||
static bool copyCursorImage(RenderCommand * cmd, const void * data)
|
||||
@@ -567,6 +588,7 @@ void renderQueue_init(void)
|
||||
for (int i = 0; i < RENDER_QUEUE_SOURCE_COUNT; ++i)
|
||||
{
|
||||
atomic_store_explicit(&l_sourceGeneration[i], 0, memory_order_relaxed);
|
||||
atomic_store_explicit(&l_cursorGeneration[i], 1, memory_order_relaxed);
|
||||
LG_LOCK_INIT(l_swSurface[i].lock);
|
||||
}
|
||||
atomic_store_explicit(&l_transitionSerial, 0, memory_order_relaxed);
|
||||
@@ -686,9 +708,26 @@ void renderQueue_sourceClearCursor(RenderQueueSource source)
|
||||
return;
|
||||
|
||||
LG_LOCK(l_sourceLock);
|
||||
uint64_t cursorGeneration = atomic_load_explicit(
|
||||
&l_cursorGeneration[source], memory_order_relaxed) + 1;
|
||||
if (!cursorGeneration)
|
||||
++cursorGeneration;
|
||||
atomic_store_explicit(&l_cursorGeneration[source], cursorGeneration,
|
||||
memory_order_release);
|
||||
free(l_cursor[source].data);
|
||||
memset(&l_cursor[source], 0, sizeof(l_cursor[source]));
|
||||
const uint64_t sourceGeneration = atomic_load_explicit(
|
||||
&l_sourceGeneration[source], memory_order_acquire);
|
||||
RenderCommand * cmd = malloc(sizeof(*cmd));
|
||||
bool wake = false;
|
||||
if (cmd)
|
||||
{
|
||||
setCommandSource(cmd, source, sourceGeneration);
|
||||
cmd->op = CURSOR_OP_CLEAR;
|
||||
wake = queueCommand(cmd, RENDER_QUEUE_INVALIDATE_PARTIAL);
|
||||
}
|
||||
LG_UNLOCK(l_sourceLock);
|
||||
wakeQueue(wake);
|
||||
}
|
||||
|
||||
static bool configureSwSurface(RenderQueueSource source,
|
||||
@@ -1454,6 +1493,19 @@ bool renderQueue_process(void)
|
||||
break;
|
||||
}
|
||||
|
||||
case CURSOR_OP_CLEAR:
|
||||
if (l_appliedSource == cmd->source)
|
||||
{
|
||||
RENDERER(onMouseEvent, false, 0, 0, 0, 0);
|
||||
if (g_state.lgr->ops.onMouseColorTransform)
|
||||
g_state.lgr->ops.onMouseColorTransform(
|
||||
g_state.lgr, &l_identityColorTransform);
|
||||
if (g_state.lgr->ops.onMouseWhiteLevel)
|
||||
g_state.lgr->ops.onMouseWhiteLevel(
|
||||
g_state.lgr, LG_SDR_WHITE_LEVEL_DEFAULT);
|
||||
}
|
||||
break;
|
||||
|
||||
case SOURCE_OP_TRANSITION:
|
||||
{
|
||||
const bool swSurface =
|
||||
|
||||
84
client/src/sw_surface.c
Normal file
84
client/src/sw_surface.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include "sw_surface.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/time.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
struct SwSurfaceCall
|
||||
{
|
||||
const LG_SwSurfaceOps * ops;
|
||||
LG_Transport * transport;
|
||||
bool active;
|
||||
bool result;
|
||||
atomic_bool done;
|
||||
};
|
||||
|
||||
static int swSurfaceCallThread(void * opaque)
|
||||
{
|
||||
struct SwSurfaceCall * call = opaque;
|
||||
call->result = call->ops->setActive(call->transport, call->active);
|
||||
atomic_store_explicit(&call->done, true, memory_order_release);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool lgSwSurface_setActive(const LG_SwSurfaceOps * ops,
|
||||
LG_Transport * transport, bool active,
|
||||
LG_TransportCancelledFn cancelled, void * opaque)
|
||||
{
|
||||
if (!ops || !ops->setActive || !ops->cancelPending || !transport)
|
||||
return false;
|
||||
|
||||
struct SwSurfaceCall call =
|
||||
{
|
||||
.ops = ops,
|
||||
.transport = transport,
|
||||
.active = active,
|
||||
};
|
||||
atomic_init(&call.done, false);
|
||||
|
||||
LGThread * thread;
|
||||
if (!lgCreateThread("surfaceState", swSurfaceCallThread, &call, &thread))
|
||||
return false;
|
||||
|
||||
bool cancelRequested = false;
|
||||
while (!atomic_load_explicit(&call.done, memory_order_acquire))
|
||||
{
|
||||
if (active && cancelled && cancelled(opaque))
|
||||
{
|
||||
cancelRequested = true;
|
||||
ops->cancelPending(transport);
|
||||
}
|
||||
nsleep(1000000U);
|
||||
}
|
||||
|
||||
if (active && cancelled && cancelled(opaque))
|
||||
cancelRequested = true;
|
||||
|
||||
if (!lgJoinThread(thread, NULL))
|
||||
{
|
||||
DEBUG_ERROR("Failed to join software surface state worker");
|
||||
return false;
|
||||
}
|
||||
if (active && cancelRequested)
|
||||
{
|
||||
/* The backend may complete successfully at the same instant cancellation
|
||||
* is observed. Leave it inactive before reporting the cancelled result. */
|
||||
if (!ops->setActive(transport, false))
|
||||
DEBUG_ERROR("Failed to deactivate a cancelled software surface");
|
||||
return false;
|
||||
}
|
||||
return call.result;
|
||||
}
|
||||
24
client/src/sw_surface.h
Normal file
24
client/src/sw_surface.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_CLIENT_SW_SURFACE_
|
||||
#define _H_LG_CLIENT_SW_SURFACE_
|
||||
|
||||
#include "interface/transport.h"
|
||||
|
||||
/* Runs setActive on a worker and joins it before returning. Cancellation is
|
||||
* observed only while activating; deactivation is allowed to complete unless
|
||||
* an owner invokes cancelPending from another thread. */
|
||||
bool lgSwSurface_setActive(const LG_SwSurfaceOps * ops,
|
||||
LG_Transport * transport, bool active,
|
||||
LG_TransportCancelledFn cancelled, void * opaque);
|
||||
|
||||
#endif
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "interface/transport.h"
|
||||
#include "dynamic/transports.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void lgTransport_setup(void)
|
||||
@@ -51,10 +53,39 @@ bool lgTransport_create(const char * name, LG_TransportInstance * instance)
|
||||
if (strcmp(LG_Transports[i]->name, name) != 0)
|
||||
continue;
|
||||
|
||||
if (!LG_Transports[i]->create(&instance->handle))
|
||||
const LG_TransportOps * ops = LG_Transports[i];
|
||||
if (!ops->create || !ops->destroy || !ops->connectCancellable ||
|
||||
!ops->disconnect || !ops->sessionValid)
|
||||
{
|
||||
DEBUG_ERROR("Transport %s has an incomplete session interface", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ops->create(&instance->handle))
|
||||
return false;
|
||||
|
||||
instance->ops = LG_Transports[i];
|
||||
const LG_VideoOps * video = ops->getVideoOps ?
|
||||
ops->getVideoOps(instance->handle) : NULL;
|
||||
const bool badFrame = video && video->type == LG_VIDEO_TYPE_FRAME &&
|
||||
(!video->frame || !video->frame->nextFrame ||
|
||||
!video->frame->releaseFrame || !video->frame->cancelFrameWait ||
|
||||
((video->frame->nextPointer != NULL) !=
|
||||
(video->frame->releasePointer != NULL)) ||
|
||||
(video->frame->nextPointer && !video->frame->cancelPointerWait));
|
||||
const bool badSurface = video && video->type == LG_VIDEO_TYPE_SW_SURFACE &&
|
||||
(!video->swSurface || !video->swSurface->attach ||
|
||||
!video->swSurface->detach || !video->swSurface->setActive ||
|
||||
!video->swSurface->cancelPending);
|
||||
if (video && (badFrame || badSurface ||
|
||||
(video->type != LG_VIDEO_TYPE_FRAME &&
|
||||
video->type != LG_VIDEO_TYPE_SW_SURFACE)))
|
||||
{
|
||||
DEBUG_ERROR("Transport %s has an incomplete video interface", name);
|
||||
ops->destroy(&instance->handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
instance->ops = ops;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "audio.h"
|
||||
#include "clipboard.h"
|
||||
#include "input.h"
|
||||
#include "sw_surface.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/event.h"
|
||||
@@ -44,6 +45,7 @@ struct LG_TransportFallback
|
||||
|
||||
LG_SwSurfaceEventOps surfaceEvents;
|
||||
void * surfaceOpaque;
|
||||
bool surfaceRequested;
|
||||
LG_TransportFallbackEventOps eventOps;
|
||||
void * eventOpaque;
|
||||
|
||||
@@ -55,7 +57,8 @@ struct LG_TransportFallback
|
||||
|
||||
atomic_bool stop;
|
||||
atomic_bool ready;
|
||||
atomic_bool admitted;
|
||||
atomic_bool usable;
|
||||
atomic_bool videoAcceptingEvents;
|
||||
|
||||
LG_TransportInstance transport;
|
||||
LG_TransportSession session;
|
||||
@@ -80,28 +83,48 @@ struct LG_TransportFallback
|
||||
|
||||
static bool applyVideoRequest(LG_TransportFallback * fallback);
|
||||
|
||||
struct VideoCallCancellation
|
||||
{
|
||||
LG_TransportFallback * fallback;
|
||||
uint64_t connectionSerial;
|
||||
bool requested;
|
||||
};
|
||||
|
||||
static bool connectCancelled(void * opaque)
|
||||
{
|
||||
const LG_TransportFallback * fallback = opaque;
|
||||
return atomic_load_explicit(&fallback->stop, memory_order_acquire);
|
||||
}
|
||||
|
||||
static bool uuidMismatchLocked(const LG_TransportFallback * fallback)
|
||||
static bool videoCallCancelled(void * opaque)
|
||||
{
|
||||
const struct VideoCallCancellation * cancellation = opaque;
|
||||
LG_TransportFallback * fallback = cancellation->fallback;
|
||||
LG_LOCK_SHARED(fallback->lock);
|
||||
const bool cancelled = atomic_load_explicit(
|
||||
&fallback->stop, memory_order_acquire) || fallback->closing ||
|
||||
fallback->connectionSerial != cancellation->connectionSerial ||
|
||||
fallback->videoRequested != cancellation->requested;
|
||||
LG_UNLOCK_SHARED(fallback->lock);
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
static bool endpointMismatchLocked(const LG_TransportFallback * fallback)
|
||||
{
|
||||
return fallback->primaryUUIDValid && fallback->session.uuidValid &&
|
||||
memcmp(fallback->primaryUUID, fallback->session.uuid,
|
||||
sizeof(fallback->primaryUUID)) != 0;
|
||||
}
|
||||
|
||||
static bool uuidAdmittedLocked(const LG_TransportFallback * fallback)
|
||||
static bool endpointMatchesLocked(const LG_TransportFallback * fallback)
|
||||
{
|
||||
return !uuidMismatchLocked(fallback);
|
||||
return !endpointMismatchLocked(fallback);
|
||||
}
|
||||
|
||||
static bool recordMismatchLocked(LG_TransportFallback * fallback,
|
||||
uint8_t primary[16], uint8_t remote[16])
|
||||
{
|
||||
if (!uuidMismatchLocked(fallback))
|
||||
if (!endpointMismatchLocked(fallback))
|
||||
return false;
|
||||
|
||||
const bool duplicate = fallback->mismatchReported &&
|
||||
@@ -133,9 +156,8 @@ static void notifyConnected(LG_TransportFallback * fallback)
|
||||
LG_LOCK(fallback->eventLock);
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
const bool report =
|
||||
atomic_load_explicit(&fallback->ready, memory_order_acquire) &&
|
||||
atomic_load_explicit(&fallback->admitted, memory_order_acquire) &&
|
||||
uuidAdmittedLocked(fallback) && !fallback->closing &&
|
||||
atomic_load_explicit(&fallback->usable, memory_order_acquire) &&
|
||||
endpointMatchesLocked(fallback) && !fallback->closing &&
|
||||
!fallback->disconnectedReported;
|
||||
fallback->connectedReported = report;
|
||||
const LG_TransportSession session = fallback->session;
|
||||
@@ -174,6 +196,26 @@ static void notifyLost(LG_TransportFallback * fallback, bool requested)
|
||||
LG_UNLOCK(fallback->eventLock);
|
||||
}
|
||||
|
||||
static void notifyVideoState(LG_TransportFallback * fallback,
|
||||
uint64_t connectionSerial, bool ready)
|
||||
{
|
||||
if (!fallback->eventOps.videoStateChanged)
|
||||
return;
|
||||
|
||||
LG_LOCK(fallback->eventLock);
|
||||
LG_LOCK_SHARED(fallback->lock);
|
||||
const bool current = fallback->connectionSerial == connectionSerial &&
|
||||
atomic_load_explicit(&fallback->ready, memory_order_acquire) == ready &&
|
||||
(!ready || (atomic_load_explicit(
|
||||
&fallback->usable, memory_order_acquire) &&
|
||||
!fallback->closing && endpointMatchesLocked(fallback) &&
|
||||
fallback->videoRequested));
|
||||
LG_UNLOCK_SHARED(fallback->lock);
|
||||
if (current)
|
||||
fallback->eventOps.videoStateChanged(fallback->eventOpaque, ready);
|
||||
LG_UNLOCK(fallback->eventLock);
|
||||
}
|
||||
|
||||
static void unpublishProviders(LG_TransportFallback * fallback, bool live)
|
||||
{
|
||||
LG_LOCK(fallback->providerLock);
|
||||
@@ -199,11 +241,13 @@ static void unpublishProviders(LG_TransportFallback * fallback, bool live)
|
||||
LG_UNLOCK(fallback->providerLock);
|
||||
}
|
||||
|
||||
static void closeVideoAdmission(LG_TransportFallback * fallback)
|
||||
static void closeConnection(LG_TransportFallback * fallback)
|
||||
{
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
atomic_store_explicit(&fallback->ready, false, memory_order_release);
|
||||
atomic_store_explicit(&fallback->admitted, false, memory_order_release);
|
||||
atomic_store_explicit(&fallback->usable, false, memory_order_release);
|
||||
atomic_store_explicit(
|
||||
&fallback->videoAcceptingEvents, false, memory_order_release);
|
||||
fallback->closing = true;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
}
|
||||
@@ -211,7 +255,7 @@ static void closeVideoAdmission(LG_TransportFallback * fallback)
|
||||
static bool cleanupConnection(LG_TransportFallback * fallback,
|
||||
bool knownDead)
|
||||
{
|
||||
closeVideoAdmission(fallback);
|
||||
closeConnection(fallback);
|
||||
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
const bool reportDisconnected = fallback->connectedReported;
|
||||
@@ -224,9 +268,11 @@ static bool cleanupConnection(LG_TransportFallback * fallback,
|
||||
if (fallback->attached)
|
||||
{
|
||||
if (live && fallback->videoActive)
|
||||
fallback->videoOps->swSurface->setActive(
|
||||
fallback->transport.handle, false);
|
||||
lgSwSurface_setActive(fallback->videoOps->swSurface,
|
||||
fallback->transport.handle, false, NULL, NULL);
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
fallback->videoActive = false;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
fallback->videoOps->swSurface->detach(fallback->transport.handle);
|
||||
fallback->attached = false;
|
||||
}
|
||||
@@ -249,10 +295,10 @@ static bool publishProviders(LG_TransportFallback * fallback)
|
||||
{
|
||||
LG_LOCK(fallback->providerLock);
|
||||
LG_LOCK_SHARED(fallback->lock);
|
||||
const bool admitted = atomic_load_explicit(
|
||||
&fallback->admitted, memory_order_acquire) &&
|
||||
uuidAdmittedLocked(fallback) && !fallback->closing;
|
||||
if (!admitted)
|
||||
const bool usable = atomic_load_explicit(
|
||||
&fallback->usable, memory_order_acquire) &&
|
||||
endpointMatchesLocked(fallback) && !fallback->closing;
|
||||
if (!usable)
|
||||
{
|
||||
LG_UNLOCK_SHARED(fallback->lock);
|
||||
LG_UNLOCK(fallback->providerLock);
|
||||
@@ -283,75 +329,47 @@ static bool publishProviders(LG_TransportFallback * fallback)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool admitConnection(LG_TransportFallback * fallback,
|
||||
static bool markConnectionUsable(LG_TransportFallback * fallback,
|
||||
bool * reportMismatch, uint8_t primaryUUID[16],
|
||||
uint8_t fallbackUUID[16])
|
||||
{
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
const bool reject = uuidMismatchLocked(fallback);
|
||||
if (reject)
|
||||
const bool mismatch = endpointMismatchLocked(fallback);
|
||||
if (mismatch)
|
||||
*reportMismatch = recordMismatchLocked(
|
||||
fallback, primaryUUID, fallbackUUID);
|
||||
const bool stop = atomic_load_explicit(
|
||||
&fallback->stop, memory_order_acquire);
|
||||
const bool admitted = !reject && !stop && !fallback->closing;
|
||||
if (admitted)
|
||||
const bool usable = !mismatch && !stop && !fallback->closing;
|
||||
if (usable)
|
||||
atomic_store_explicit(
|
||||
&fallback->admitted, true, memory_order_release);
|
||||
&fallback->usable, true, memory_order_release);
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
return admitted;
|
||||
return usable;
|
||||
}
|
||||
|
||||
static bool publishConnection(LG_TransportFallback * fallback,
|
||||
bool * reportMismatch, uint8_t primaryUUID[16],
|
||||
uint8_t fallbackUUID[16])
|
||||
{
|
||||
if (!admitConnection(fallback, reportMismatch,
|
||||
if (!markConnectionUsable(fallback, reportMismatch,
|
||||
primaryUUID, fallbackUUID))
|
||||
return false;
|
||||
|
||||
if (!publishProviders(fallback))
|
||||
return false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
const bool reject = uuidMismatchLocked(fallback);
|
||||
if (reject)
|
||||
*reportMismatch = recordMismatchLocked(
|
||||
fallback, primaryUUID, fallbackUUID);
|
||||
const bool admitted = uuidAdmittedLocked(fallback);
|
||||
const bool stop = atomic_load_explicit(
|
||||
&fallback->stop, memory_order_acquire);
|
||||
const bool closing = fallback->closing;
|
||||
const bool requested = fallback->videoRequested;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
|
||||
if (reject || !admitted || stop || closing)
|
||||
return false;
|
||||
if (requested == fallback->videoActive)
|
||||
break;
|
||||
|
||||
if (!fallback->videoOps->swSurface->setActive(
|
||||
fallback->transport.handle, requested))
|
||||
break;
|
||||
fallback->videoActive = requested;
|
||||
}
|
||||
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
const bool finalReject = uuidMismatchLocked(fallback);
|
||||
if (finalReject)
|
||||
const bool finalMismatch = endpointMismatchLocked(fallback);
|
||||
if (finalMismatch)
|
||||
*reportMismatch = recordMismatchLocked(
|
||||
fallback, primaryUUID, fallbackUUID);
|
||||
const bool finalAdmitted = uuidAdmittedLocked(fallback);
|
||||
const bool finalMatches = endpointMatchesLocked(fallback);
|
||||
const bool finalStop = atomic_load_explicit(
|
||||
&fallback->stop, memory_order_acquire);
|
||||
if (!finalReject && finalAdmitted && !finalStop && !fallback->closing)
|
||||
{
|
||||
if (!finalMismatch && finalMatches && !finalStop && !fallback->closing)
|
||||
fallback->mismatchReported = false;
|
||||
atomic_store_explicit(&fallback->ready, true, memory_order_release);
|
||||
}
|
||||
const bool published = !finalReject && finalAdmitted && !finalStop &&
|
||||
const bool published = !finalMismatch && finalMatches && !finalStop &&
|
||||
!fallback->closing;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
return published;
|
||||
@@ -367,37 +385,13 @@ static bool connectFallback(LG_TransportFallback * fallback)
|
||||
return false;
|
||||
}
|
||||
|
||||
const LG_VideoOps * videoOps = transport.ops->getVideoOps ?
|
||||
transport.ops->getVideoOps(transport.handle) : NULL;
|
||||
if (!videoOps || videoOps->type != LG_VIDEO_TYPE_SW_SURFACE ||
|
||||
!videoOps->swSurface || !videoOps->swSurface->attach ||
|
||||
!videoOps->swSurface->detach || !videoOps->swSurface->setActive)
|
||||
{
|
||||
DEBUG_ERROR("Fallback transport %s does not provide a software surface",
|
||||
fallback->transportName);
|
||||
lgTransport_destroy(&transport);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!videoOps->swSurface->attach(transport.handle,
|
||||
&fallback->surfaceEvents, fallback->surfaceOpaque))
|
||||
{
|
||||
DEBUG_ERROR("Failed to attach fallback software surface");
|
||||
lgTransport_destroy(&transport);
|
||||
return false;
|
||||
}
|
||||
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
fallback->transport = transport;
|
||||
fallback->videoOps = videoOps;
|
||||
fallback->attached = true;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
|
||||
LG_TransportSession session = { 0 };
|
||||
const LG_TransportStatus status = transport.ops->connectCancellable ?
|
||||
transport.ops->connectCancellable(transport.handle, &session,
|
||||
connectCancelled, fallback) :
|
||||
transport.ops->connect(transport.handle, &session);
|
||||
const LG_TransportStatus status = transport.ops->connectCancellable(
|
||||
transport.handle, &session, connectCancelled, fallback);
|
||||
|
||||
if (status != LG_TRANSPORT_OK)
|
||||
{
|
||||
@@ -415,6 +409,26 @@ static bool connectFallback(LG_TransportFallback * fallback)
|
||||
++fallback->connectionSerial;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
|
||||
const LG_VideoOps * videoOps = transport.ops->getVideoOps ?
|
||||
transport.ops->getVideoOps(transport.handle) : NULL;
|
||||
bool attached = false;
|
||||
if (fallback->surfaceRequested && videoOps &&
|
||||
videoOps->type == LG_VIDEO_TYPE_SW_SURFACE &&
|
||||
videoOps->swSurface && videoOps->swSurface->attach &&
|
||||
videoOps->swSurface->detach && videoOps->swSurface->setActive &&
|
||||
videoOps->swSurface->cancelPending)
|
||||
{
|
||||
attached = videoOps->swSurface->attach(transport.handle,
|
||||
&fallback->surfaceEvents, fallback->surfaceOpaque);
|
||||
if (!attached)
|
||||
DEBUG_WARN("Failed to attach fallback video source; continuing without it");
|
||||
}
|
||||
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
fallback->videoOps = attached ? videoOps : NULL;
|
||||
fallback->attached = attached;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
|
||||
bool reportMismatch = false;
|
||||
uint8_t primaryUUID[16];
|
||||
uint8_t fallbackUUID[16];
|
||||
@@ -422,13 +436,14 @@ static bool connectFallback(LG_TransportFallback * fallback)
|
||||
primaryUUID, fallbackUUID))
|
||||
{
|
||||
cleanupConnection(fallback, false);
|
||||
if (reportMismatch && fallback->eventOps.uuidMismatch)
|
||||
fallback->eventOps.uuidMismatch(
|
||||
if (reportMismatch && fallback->eventOps.endpointMismatch)
|
||||
fallback->eventOps.endpointMismatch(
|
||||
fallback->eventOpaque, primaryUUID, fallbackUUID);
|
||||
return false;
|
||||
}
|
||||
|
||||
notifyConnected(fallback);
|
||||
applyVideoRequest(fallback);
|
||||
|
||||
bool lost = false;
|
||||
while (!atomic_load_explicit(&fallback->stop, memory_order_acquire))
|
||||
@@ -439,14 +454,14 @@ static bool connectFallback(LG_TransportFallback * fallback)
|
||||
applyVideoRequest(fallback);
|
||||
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
const bool reject = uuidMismatchLocked(fallback);
|
||||
if (reject)
|
||||
const bool mismatch = endpointMismatchLocked(fallback);
|
||||
if (mismatch)
|
||||
reportMismatch = recordMismatchLocked(
|
||||
fallback, primaryUUID, fallbackUUID);
|
||||
const bool admitted = uuidAdmittedLocked(fallback);
|
||||
const bool matches = endpointMatchesLocked(fallback);
|
||||
const bool closing = fallback->closing;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
if (reject || !admitted || closing)
|
||||
if (mismatch || !matches || closing)
|
||||
break;
|
||||
if (!sessionLive(fallback))
|
||||
{
|
||||
@@ -458,8 +473,8 @@ static bool connectFallback(LG_TransportFallback * fallback)
|
||||
|
||||
const bool reportDisconnect = cleanupConnection(fallback, lost);
|
||||
|
||||
if (reportMismatch && fallback->eventOps.uuidMismatch)
|
||||
fallback->eventOps.uuidMismatch(
|
||||
if (reportMismatch && fallback->eventOps.endpointMismatch)
|
||||
fallback->eventOps.endpointMismatch(
|
||||
fallback->eventOpaque, primaryUUID, fallbackUUID);
|
||||
const bool reportLost = lost && reportDisconnect && !atomic_load_explicit(
|
||||
&fallback->stop, memory_order_acquire);
|
||||
@@ -500,7 +515,7 @@ bool lgTransportFallback_start(const char * transportName,
|
||||
return false;
|
||||
|
||||
*result = NULL;
|
||||
if (!transportName || !*transportName || !surfaceEvents ||
|
||||
if (!transportName || !*transportName ||
|
||||
!lgTransport_isValid(transportName))
|
||||
return false;
|
||||
|
||||
@@ -513,13 +528,18 @@ bool lgTransportFallback_start(const char * transportName,
|
||||
LG_LOCK_INIT(fallback->eventLock);
|
||||
atomic_init(&fallback->stop, false);
|
||||
atomic_init(&fallback->ready, false);
|
||||
atomic_init(&fallback->admitted, false);
|
||||
atomic_init(&fallback->usable, false);
|
||||
atomic_init(&fallback->videoAcceptingEvents, false);
|
||||
|
||||
fallback->transportName = strdup(transportName);
|
||||
if (!fallback->transportName)
|
||||
goto fail;
|
||||
|
||||
fallback->surfaceEvents = *surfaceEvents;
|
||||
if (surfaceEvents)
|
||||
{
|
||||
fallback->surfaceEvents = *surfaceEvents;
|
||||
fallback->surfaceRequested = true;
|
||||
}
|
||||
fallback->surfaceOpaque = surfaceOpaque;
|
||||
if (eventOps)
|
||||
fallback->eventOps = *eventOps;
|
||||
@@ -587,10 +607,17 @@ bool lgTransportFallback_ready(const LG_TransportFallback * fallback)
|
||||
&fallback->ready, memory_order_acquire);
|
||||
}
|
||||
|
||||
bool lgTransportFallback_admitted(const LG_TransportFallback * fallback)
|
||||
bool lgTransportFallback_usable(const LG_TransportFallback * fallback)
|
||||
{
|
||||
return fallback && atomic_load_explicit(
|
||||
&fallback->admitted, memory_order_acquire);
|
||||
&fallback->usable, memory_order_acquire);
|
||||
}
|
||||
|
||||
bool lgTransportFallback_acceptsVideoEvents(
|
||||
const LG_TransportFallback * fallback)
|
||||
{
|
||||
return fallback && atomic_load_explicit(
|
||||
&fallback->videoAcceptingEvents, memory_order_acquire);
|
||||
}
|
||||
|
||||
bool lgTransportFallback_videoRequested(
|
||||
@@ -615,8 +642,8 @@ static bool applyVideoRequest(LG_TransportFallback * fallback)
|
||||
bool requested;
|
||||
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
if (!atomic_load_explicit(&fallback->ready, memory_order_acquire) ||
|
||||
fallback->closing)
|
||||
if (!atomic_load_explicit(&fallback->usable, memory_order_acquire) ||
|
||||
!fallback->attached || !fallback->videoOps || fallback->closing)
|
||||
{
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
return false;
|
||||
@@ -632,19 +659,49 @@ static bool applyVideoRequest(LG_TransportFallback * fallback)
|
||||
connectionSerial = fallback->connectionSerial;
|
||||
transport = fallback->transport.handle;
|
||||
surfaceOps = fallback->videoOps->swSurface;
|
||||
atomic_store_explicit(
|
||||
&fallback->ready, false, memory_order_release);
|
||||
atomic_store_explicit(&fallback->videoAcceptingEvents,
|
||||
requested, memory_order_release);
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
|
||||
const bool result = surfaceOps->setActive(transport, requested);
|
||||
const struct VideoCallCancellation cancellation =
|
||||
{
|
||||
.fallback = fallback,
|
||||
.connectionSerial = connectionSerial,
|
||||
.requested = requested,
|
||||
};
|
||||
const bool result = lgSwSurface_setActive(surfaceOps, transport,
|
||||
requested, videoCallCancelled, (void *)&cancellation);
|
||||
|
||||
LG_LOCK_EXCLUSIVE(fallback->lock);
|
||||
const bool current = fallback->connectionSerial == connectionSerial;
|
||||
if (current && result)
|
||||
fallback->videoActive = requested;
|
||||
const bool accepted = current && !fallback->closing && result;
|
||||
const bool desiredCurrent = current &&
|
||||
fallback->videoRequested == requested;
|
||||
const bool usable = desiredCurrent && result &&
|
||||
atomic_load_explicit(&fallback->usable, memory_order_acquire) &&
|
||||
!atomic_load_explicit(&fallback->stop, memory_order_acquire) &&
|
||||
!fallback->closing && endpointMatchesLocked(fallback);
|
||||
if (current)
|
||||
{
|
||||
if (result)
|
||||
fallback->videoActive = requested;
|
||||
else if (requested)
|
||||
fallback->videoActive = false;
|
||||
const bool ready = usable && requested;
|
||||
atomic_store_explicit(
|
||||
&fallback->ready, ready, memory_order_release);
|
||||
atomic_store_explicit(&fallback->videoAcceptingEvents,
|
||||
ready, memory_order_release);
|
||||
}
|
||||
const bool accepted = usable;
|
||||
const bool retry = current && !fallback->closing &&
|
||||
fallback->videoRequested != requested;
|
||||
LG_UNLOCK_EXCLUSIVE(fallback->lock);
|
||||
|
||||
if (current)
|
||||
notifyVideoState(fallback, connectionSerial, accepted && requested);
|
||||
|
||||
if (!retry)
|
||||
return accepted;
|
||||
}
|
||||
@@ -662,16 +719,19 @@ void lgTransportFallback_requestVideoActive(
|
||||
lgSignalEvent(fallback->wakeEvent);
|
||||
}
|
||||
|
||||
static bool revokeAdmissionLocked(LG_TransportFallback * fallback)
|
||||
static bool clearUsableLocked(LG_TransportFallback * fallback)
|
||||
{
|
||||
atomic_store_explicit(&fallback->ready, false, memory_order_release);
|
||||
atomic_store_explicit(&fallback->admitted, false, memory_order_release);
|
||||
atomic_store_explicit(&fallback->usable, false, memory_order_release);
|
||||
atomic_store_explicit(
|
||||
&fallback->videoAcceptingEvents, false, memory_order_release);
|
||||
if (!fallback->connected)
|
||||
return false;
|
||||
|
||||
const bool reportDisconnected = fallback->connectedReported;
|
||||
fallback->connectedReported = false;
|
||||
fallback->closing = true;
|
||||
return true;
|
||||
return reportDisconnected;
|
||||
}
|
||||
|
||||
void lgTransportFallback_setPrimaryUUID(
|
||||
@@ -680,7 +740,7 @@ void lgTransportFallback_setPrimaryUUID(
|
||||
if (!fallback || !uuid)
|
||||
return;
|
||||
|
||||
bool revoked = false;
|
||||
bool mismatchTransition = false;
|
||||
bool notifyDisconnect = false;
|
||||
bool reportMismatch = false;
|
||||
uint8_t primaryUUID[16];
|
||||
@@ -694,10 +754,10 @@ void lgTransportFallback_setPrimaryUUID(
|
||||
{
|
||||
memcpy(fallback->primaryUUID, uuid, sizeof(fallback->primaryUUID));
|
||||
fallback->primaryUUIDValid = true;
|
||||
if (uuidMismatchLocked(fallback))
|
||||
if (endpointMismatchLocked(fallback))
|
||||
{
|
||||
revoked = true;
|
||||
notifyDisconnect = revokeAdmissionLocked(fallback);
|
||||
mismatchTransition = true;
|
||||
notifyDisconnect = clearUsableLocked(fallback);
|
||||
reportMismatch = recordMismatchLocked(
|
||||
fallback, primaryUUID, fallbackUUID);
|
||||
}
|
||||
@@ -706,11 +766,11 @@ void lgTransportFallback_setPrimaryUUID(
|
||||
|
||||
if (changed)
|
||||
lgSignalEvent(fallback->wakeEvent);
|
||||
if (revoked)
|
||||
if (mismatchTransition)
|
||||
unpublishProviders(fallback, false);
|
||||
notifyDisconnected(fallback, notifyDisconnect);
|
||||
if (reportMismatch && fallback->eventOps.uuidMismatch)
|
||||
fallback->eventOps.uuidMismatch(
|
||||
if (reportMismatch && fallback->eventOps.endpointMismatch)
|
||||
fallback->eventOps.endpointMismatch(
|
||||
fallback->eventOpaque, primaryUUID, fallbackUUID);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,10 +32,12 @@ typedef struct LG_TransportFallbackEventOps
|
||||
{
|
||||
/* The session is valid only for the duration of this callback. */
|
||||
void (*connected)(void * opaque, const LG_TransportSession * session);
|
||||
/* Called only when an admitted session is unexpectedly lost. */
|
||||
/* Called only when a usable session is unexpectedly lost. */
|
||||
void (*lost)(void * opaque);
|
||||
void (*disconnected)(void * opaque);
|
||||
void (*uuidMismatch)(void * opaque, const uint8_t primary[16],
|
||||
/* Reports completion of an optional video state request. */
|
||||
void (*videoStateChanged)(void * opaque, bool ready);
|
||||
void (*endpointMismatch)(void * opaque, const uint8_t primary[16],
|
||||
const uint8_t fallback[16]);
|
||||
}
|
||||
LG_TransportFallbackEventOps;
|
||||
@@ -47,8 +49,13 @@ bool lgTransportFallback_start(const char * transportName,
|
||||
LG_TransportFallback ** result);
|
||||
void lgTransportFallback_stop(LG_TransportFallback ** fallback);
|
||||
|
||||
/* ready reports optional video availability; usable reports session services. */
|
||||
bool lgTransportFallback_ready(const LG_TransportFallback * fallback);
|
||||
bool lgTransportFallback_admitted(const LG_TransportFallback * fallback);
|
||||
bool lgTransportFallback_usable(const LG_TransportFallback * fallback);
|
||||
/* True while video callbacks belong to the current activation attempt or
|
||||
* confirmed active state. */
|
||||
bool lgTransportFallback_acceptsVideoEvents(
|
||||
const LG_TransportFallback * fallback);
|
||||
bool lgTransportFallback_videoRequested(
|
||||
LG_TransportFallback * fallback);
|
||||
/* The requested state is retained across reconnects. */
|
||||
|
||||
Reference in New Issue
Block a user