[client] transport: encapsulate transport instances

This commit is contained in:
Geoffrey McRae
2026-08-11 14:53:29 +10:00
parent 77b661194a
commit eb6bb78db9
6 changed files with 91 additions and 67 deletions

View File

@@ -292,9 +292,16 @@ typedef struct LG_TransportOps
} }
LG_TransportOps; LG_TransportOps;
typedef struct LG_TransportInstance
{
LG_Transport * handle;
const LG_TransportOps * ops;
}
LG_TransportInstance;
void lgTransport_setup(void); void lgTransport_setup(void);
bool lgTransport_isValid(const char * name); bool lgTransport_isValid(const char * name);
bool lgTransport_create(const char * name, LG_Transport ** transport, bool lgTransport_create(const char * name, LG_TransportInstance * instance);
const LG_TransportOps ** ops); void lgTransport_destroy(LG_TransportInstance * instance);
#endif #endif

View File

@@ -402,7 +402,7 @@ bool core_warpPointer(int x, int y, bool exiting)
void core_onWindowSizeChanged(unsigned width, unsigned height) void core_onWindowSizeChanged(unsigned width, unsigned height)
{ {
if (!g_state.transport || if (!g_state.transport.handle ||
!(g_state.transportFeatures & LG_TRANSPORT_FEATURE_WINDOW_SIZE)) !(g_state.transportFeatures & LG_TRANSPORT_FEATURE_WINDOW_SIZE))
return; return;
@@ -416,8 +416,8 @@ void core_onWindowSizeChanged(unsigned width, unsigned height)
}; };
LG_TransportControlToken token; LG_TransportControlToken token;
const LG_TransportStatus status = g_state.transportOps->sendControl( const LG_TransportStatus status = g_state.transport.ops->sendControl(
g_state.transport, &control, &token); g_state.transport.handle, &control, &token);
if (status != LG_TRANSPORT_OK && status != LG_TRANSPORT_UNAVAILABLE) if (status != LG_TRANSPORT_OK && status != LG_TRANSPORT_UNAVAILABLE)
DEBUG_WARN("Window-size control failed with status %d", status); DEBUG_WARN("Window-size control failed with status %d", status);
} }
@@ -940,8 +940,8 @@ void core_handleMouseNormal(double ex, double ey)
}; };
LG_TransportControlToken token; LG_TransportControlToken token;
LG_TransportStatus status = g_state.transportOps->sendControl( LG_TransportStatus status = g_state.transport.ops->sendControl(
g_state.transport, &control, &token); g_state.transport.handle, &control, &token);
if (status != LG_TRANSPORT_OK) if (status != LG_TRANSPORT_OK)
{ {
DEBUG_WARN("Cursor-position control failed with status %d", status); DEBUG_WARN("Cursor-position control failed with status %d", status);
@@ -954,8 +954,8 @@ void core_handleMouseNormal(double ex, double ey)
unsigned timeout = 200; unsigned timeout = 200;
do do
{ {
status = g_state.transportOps->controlStatus(g_state.transport, status = g_state.transport.ops->controlStatus(
token); g_state.transport.handle, token);
if (status == LG_TRANSPORT_DISCONNECTED) if (status == LG_TRANSPORT_DISCONNECTED)
{ {
g_cursor.realigning = false; g_cursor.realigning = false;

View File

@@ -96,8 +96,8 @@ static bool controlReady(uint64_t now)
if (now < l_frameScheduler.nextControlCheck) if (now < l_frameScheduler.nextControlCheck)
return false; return false;
const LG_TransportStatus status = g_state.transportOps->controlStatus( const LG_TransportStatus status = g_state.transport.ops->controlStatus(
g_state.transport, l_frameScheduler.controlToken); g_state.transport.handle, l_frameScheduler.controlToken);
if (status != LG_TRANSPORT_OK) if (status != LG_TRANSPORT_OK)
{ {
if (status != LG_TRANSPORT_UNAVAILABLE && if (status != LG_TRANSPORT_UNAVAILABLE &&
@@ -155,8 +155,8 @@ static bool sendSchedule(LG_TransportFrameScheduleFlags flags,
}, },
}; };
const LG_TransportStatus status = g_state.transportOps->sendControl( const LG_TransportStatus status = g_state.transport.ops->sendControl(
g_state.transport, &control, &l_frameScheduler.controlToken); g_state.transport.handle, &control, &l_frameScheduler.controlToken);
if (status != LG_TRANSPORT_OK) if (status != LG_TRANSPORT_OK)
{ {
if (status != LG_TRANSPORT_UNAVAILABLE && if (status != LG_TRANSPORT_UNAVAILABLE &&

View File

@@ -1027,8 +1027,8 @@ static int renderThread(void * unused)
lgTimerDestroy(tickTimer); lgTimerDestroy(tickTimer);
lgTimerDestroy(fpsTimer); lgTimerDestroy(fpsTimer);
if (g_state.transport && if (g_state.transport.handle &&
g_state.transportOps->sessionValid(g_state.transport)) g_state.transport.ops->sessionValid(g_state.transport.handle))
{ {
lgInput_setTransport(NULL, NULL); lgInput_setTransport(NULL, NULL);
lgAudio_setTransport(NULL, NULL); lgAudio_setTransport(NULL, NULL);
@@ -1044,8 +1044,8 @@ static int renderThread(void * unused)
core_stopCursorThread(); core_stopCursorThread();
core_stopFrameThread(); core_stopFrameThread();
if (g_state.transportOps && g_state.transportOps->detachRenderer) if (g_state.transport.ops && g_state.transport.ops->detachRenderer)
g_state.transportOps->detachRenderer(g_state.transport); g_state.transport.ops->detachRenderer(g_state.transport.handle);
RENDERER(deinitialize); RENDERER(deinitialize);
g_state.lgr = NULL; g_state.lgr = NULL;
@@ -1066,8 +1066,8 @@ int main_cursorThread(void * unused)
while(app_getState() == APP_STATE_RUNNING && !g_state.stopVideo) while(app_getState() == APP_STATE_RUNNING && !g_state.stopVideo)
{ {
LG_TransportPointer pointer; LG_TransportPointer pointer;
const LG_TransportStatus status = g_state.transportOps->nextPointer( const LG_TransportStatus status = g_state.transport.ops->nextPointer(
g_state.transport, &pointer); g_state.transport.handle, &pointer);
if (status != LG_TRANSPORT_OK) if (status != LG_TRANSPORT_OK)
{ {
if (status == LG_TRANSPORT_TIMEOUT || status == LG_TRANSPORT_UNAVAILABLE) if (status == LG_TRANSPORT_TIMEOUT || status == LG_TRANSPORT_UNAVAILABLE)
@@ -1119,7 +1119,8 @@ int main_cursorThread(void * unused)
case CURSOR_TYPE_MASKED_COLOR: cursorType = LG_CURSOR_MASKED_COLOR; break; case CURSOR_TYPE_MASKED_COLOR: cursorType = LG_CURSOR_MASKED_COLOR; break;
default: default:
DEBUG_ERROR("Invalid cursor type"); DEBUG_ERROR("Invalid cursor type");
g_state.transportOps->releasePointer(g_state.transport, &pointer); g_state.transport.ops->releasePointer(
g_state.transport.handle, &pointer);
continue; continue;
} }
@@ -1129,7 +1130,8 @@ int main_cursorThread(void * unused)
pointer.pitch, pointer.shape)) pointer.pitch, pointer.shape))
{ {
DEBUG_ERROR("Failed to update mouse shape"); DEBUG_ERROR("Failed to update mouse shape");
g_state.transportOps->releasePointer(g_state.transport, &pointer); g_state.transport.ops->releasePointer(
g_state.transport.handle, &pointer);
continue; continue;
} }
g_cursor.guest.hx = pointer.hx; g_cursor.guest.hx = pointer.hx;
@@ -1190,11 +1192,11 @@ int main_cursorThread(void * unused)
(g_params.mouseRedraw || contentChanged)))) (g_params.mouseRedraw || contentChanged))))
cursorRepaintRequest(); cursorRepaintRequest();
g_state.transportOps->releasePointer(g_state.transport, &pointer); g_state.transport.ops->releasePointer(g_state.transport.handle, &pointer);
} }
if (g_state.transportOps->stopPointer) if (g_state.transport.ops->stopPointer)
g_state.transportOps->stopPointer(g_state.transport); g_state.transport.ops->stopPointer(g_state.transport.handle);
return 0; return 0;
} }
@@ -1211,16 +1213,16 @@ int main_frameThread(void * unused)
lgWaitEvent(e_startup, TIMEOUT_INFINITE); lgWaitEvent(e_startup, TIMEOUT_INFINITE);
if (app_getState() != APP_STATE_RUNNING) if (app_getState() != APP_STATE_RUNNING)
{ {
if (g_state.transportOps->stopFrame) if (g_state.transport.ops->stopFrame)
g_state.transportOps->stopFrame(g_state.transport); g_state.transport.ops->stopFrame(g_state.transport.handle);
return 0; return 0;
} }
while(app_getState() == APP_STATE_RUNNING && !g_state.stopVideo) while(app_getState() == APP_STATE_RUNNING && !g_state.stopVideo)
{ {
LG_TransportFrame frame; LG_TransportFrame frame;
const LG_TransportStatus status = g_state.transportOps->nextFrame( const LG_TransportStatus status = g_state.transport.ops->nextFrame(
g_state.transport, g_state.useDMA, &frame); g_state.transport.handle, g_state.useDMA, &frame);
if (status != LG_TRANSPORT_OK) if (status != LG_TRANSPORT_OK)
{ {
if (status == LG_TRANSPORT_TIMEOUT || status == LG_TRANSPORT_UNAVAILABLE) if (status == LG_TRANSPORT_TIMEOUT || status == LG_TRANSPORT_UNAVAILABLE)
@@ -1249,7 +1251,7 @@ int main_frameThread(void * unused)
if (frame.serial == frameSerial && g_state.formatValid && if (frame.serial == frameSerial && g_state.formatValid &&
!frame.scheduleOwner) !frame.scheduleOwner)
{ {
g_state.transportOps->releaseFrame(g_state.transport, &frame); g_state.transport.ops->releaseFrame(g_state.transport.handle, &frame);
continue; continue;
} }
frameSerial = frame.serial; frameSerial = frame.serial;
@@ -1259,7 +1261,7 @@ int main_frameThread(void * unused)
if (!format) if (!format)
{ {
DEBUG_ERROR("Transport returned a frame without format metadata"); DEBUG_ERROR("Transport returned a frame without format metadata");
g_state.transportOps->releaseFrame(g_state.transport, &frame); g_state.transport.ops->releaseFrame(g_state.transport.handle, &frame);
app_setState(APP_STATE_SHUTDOWN); app_setState(APP_STATE_SHUTDOWN);
break; break;
} }
@@ -1341,7 +1343,7 @@ int main_frameThread(void * unused)
if (invalid) if (invalid)
{ {
DEBUG_ERROR("Unsupported frame type"); DEBUG_ERROR("Unsupported frame type");
g_state.transportOps->releaseFrame(g_state.transport, &frame); g_state.transport.ops->releaseFrame(g_state.transport.handle, &frame);
app_setState(APP_STATE_SHUTDOWN); app_setState(APP_STATE_SHUTDOWN);
break; break;
} }
@@ -1363,7 +1365,7 @@ int main_frameThread(void * unused)
{ {
LG_UNLOCK(g_state.lgrLock); LG_UNLOCK(g_state.lgrLock);
DEBUG_ERROR("Renderer failed to configure format"); DEBUG_ERROR("Renderer failed to configure format");
g_state.transportOps->releaseFrame(g_state.transport, &frame); g_state.transport.ops->releaseFrame(g_state.transport.handle, &frame);
app_setState(APP_STATE_SHUTDOWN); app_setState(APP_STATE_SHUTDOWN);
break; break;
} }
@@ -1416,7 +1418,7 @@ int main_frameThread(void * unused)
rendererOwnsFrame ? frame.releaseHandle : 0)) rendererOwnsFrame ? frame.releaseHandle : 0))
{ {
frameTimingCancel(frameToken); frameTimingCancel(frameToken);
g_state.transportOps->releaseFrame(g_state.transport, &frame); g_state.transport.ops->releaseFrame(g_state.transport.handle, &frame);
DEBUG_ERROR("Renderer onFrame returned failure"); DEBUG_ERROR("Renderer onFrame returned failure");
app_setState(APP_STATE_SHUTDOWN); app_setState(APP_STATE_SHUTDOWN);
break; break;
@@ -1426,9 +1428,9 @@ int main_frameThread(void * unused)
* is signalled below, so sample producer timing while this lease is still * is signalled below, so sample producer timing while this lease is still
* unambiguously owned by the frame thread. */ * unambiguously owned by the frame thread. */
LG_TransportFrameTiming timing = {}; LG_TransportFrameTiming timing = {};
if (g_state.transportOps->getFrameTiming) if (g_state.transport.ops->getFrameTiming)
g_state.transportOps->getFrameTiming( g_state.transport.ops->getFrameTiming(
g_state.transport, &frame, &timing); g_state.transport.handle, &frame, &timing);
const uint64_t queueStart = nanotime(); const uint64_t queueStart = nanotime();
atomic_fetch_add_explicit(&g_state.frameCount, 1, memory_order_relaxed); atomic_fetch_add_explicit(&g_state.frameCount, 1, memory_order_relaxed);
@@ -1472,7 +1474,7 @@ int main_frameThread(void * unused)
frameTimingFinishFrame(frameToken, &timing); frameTimingFinishFrame(frameToken, &timing);
if (!rendererOwnsFrame) if (!rendererOwnsFrame)
g_state.transportOps->releaseFrame(g_state.transport, &frame); g_state.transport.ops->releaseFrame(g_state.transport.handle, &frame);
app_useSpiceDisplay(false); app_useSpiceDisplay(false);
} }
@@ -1489,8 +1491,8 @@ int main_frameThread(void * unused)
/* Renderer reset requests release for every asynchronous DMA snapshot. /* Renderer reset requests release for every asynchronous DMA snapshot.
* Drain those requests before the transport unsubscribes or reconnects. */ * Drain those requests before the transport unsubscribes or reconnects. */
if (g_state.transportOps->stopFrame) if (g_state.transport.ops->stopFrame)
g_state.transportOps->stopFrame(g_state.transport); g_state.transport.ops->stopFrame(g_state.transport.handle);
return 0; return 0;
} }
@@ -2117,13 +2119,12 @@ static int lg_run(void)
signal(SIGINT , intHandler); signal(SIGINT , intHandler);
signal(SIGTERM, intHandler); signal(SIGTERM, intHandler);
if (!lgTransport_create(g_params.transport, &g_state.transport, if (!lgTransport_create(g_params.transport, &g_state.transport))
&g_state.transportOps))
{ {
DEBUG_ERROR("Failed to create transport: %s", g_params.transport); DEBUG_ERROR("Failed to create transport: %s", g_params.transport);
return -1; return -1;
} }
DEBUG_INFO("Using Transport: %s", g_state.transportOps->name); DEBUG_INFO("Using Transport: %s", g_state.transport.ops->name);
// setup the spice startup condition // setup the spice startup condition
if (!(e_spice = lgCreateEvent(false, 0))) if (!(e_spice = lgCreateEvent(false, 0)))
@@ -2206,7 +2207,7 @@ static int lg_run(void)
return -1; return -1;
} }
g_state.useDMA = g_state.transportOps->supportsDMA(g_state.transport); g_state.useDMA = g_state.transport.ops->supportsDMA(g_state.transport.handle);
// initialize the window dimensions at init for renderers // initialize the window dimensions at init for renderers
g_state.windowW = g_params.w; g_state.windowW = g_params.w;
@@ -2304,8 +2305,9 @@ static int lg_run(void)
if (g_state.lgr->ops.getInterop && if (g_state.lgr->ops.getInterop &&
g_state.lgr->ops.getInterop(g_state.lgr, &interop)) g_state.lgr->ops.getInterop(g_state.lgr, &interop))
interopPtr = &interop; interopPtr = &interop;
if (g_state.transportOps->attachRenderer && if (g_state.transport.ops->attachRenderer &&
!g_state.transportOps->attachRenderer(g_state.transport, interopPtr)) !g_state.transport.ops->attachRenderer(
g_state.transport.handle, interopPtr))
{ {
DEBUG_ERROR("Failed to attach the renderer to the transport"); DEBUG_ERROR("Failed to attach the renderer to the transport");
return -1; return -1;
@@ -2340,8 +2342,8 @@ restart:
} }
struct TransportSessionProbe probe = { struct TransportSessionProbe probe = {
.transport = g_state.transport, .transport = g_state.transport.handle,
.ops = g_state.transportOps, .ops = g_state.transport.ops,
.done = false, .done = false,
}; };
LGThread * probeThread; LGThread * probeThread;
@@ -2475,20 +2477,22 @@ restart:
frameScheduler_start(session.features); frameScheduler_start(session.features);
void * inputOpaque = NULL; void * inputOpaque = NULL;
const LG_InputOps * inputOps = g_state.transportOps->getInputOps ? const LG_InputOps * inputOps = g_state.transport.ops->getInputOps ?
g_state.transportOps->getInputOps(g_state.transport, &inputOpaque) : NULL; g_state.transport.ops->getInputOps(
g_state.transport.handle, &inputOpaque) : NULL;
lgInput_setTransport(inputOps, inputOpaque); lgInput_setTransport(inputOps, inputOpaque);
void * audioOpaque = NULL; void * audioOpaque = NULL;
const LG_AudioOps * audioOps = g_state.transportOps->getAudioOps ? const LG_AudioOps * audioOps = g_state.transport.ops->getAudioOps ?
g_state.transportOps->getAudioOps(g_state.transport, &audioOpaque) : NULL; g_state.transport.ops->getAudioOps(
g_state.transport.handle, &audioOpaque) : NULL;
lgAudio_setTransport(audioOps, audioOpaque); lgAudio_setTransport(audioOps, audioOpaque);
void * clipboardOpaque = NULL; void * clipboardOpaque = NULL;
const LG_ClipboardOps * clipboardOps = const LG_ClipboardOps * clipboardOps =
g_state.transportOps->getClipboardOps ? g_state.transport.ops->getClipboardOps ?
g_state.transportOps->getClipboardOps( g_state.transport.ops->getClipboardOps(
g_state.transport, &clipboardOpaque) : NULL; g_state.transport.handle, &clipboardOpaque) : NULL;
lgClipboard_setTransport(clipboardOps, clipboardOpaque); lgClipboard_setTransport(clipboardOps, clipboardOpaque);
if (inputOps || lgInput_available()) if (inputOps || lgInput_available())
@@ -2505,7 +2509,8 @@ restart:
while(likely(app_getState() == APP_STATE_RUNNING)) while(likely(app_getState() == APP_STATE_RUNNING))
{ {
if (unlikely(!g_state.transportOps->sessionValid(g_state.transport))) if (unlikely(!g_state.transport.ops->sessionValid(
g_state.transport.handle)))
{ {
lgInput_dropTransport(); lgInput_dropTransport();
lgAudio_dropTransport(); lgAudio_dropTransport();
@@ -2533,7 +2538,7 @@ restart:
lgInput_dropTransport(); lgInput_dropTransport();
lgAudio_dropTransport(); lgAudio_dropTransport();
lgClipboard_dropTransport(); lgClipboard_dropTransport();
g_state.transportOps->disconnect(g_state.transport); g_state.transport.ops->disconnect(g_state.transport.handle);
app_setState(APP_STATE_RUNNING); app_setState(APP_STATE_RUNNING);
lgInit(); lgInit();
@@ -2576,11 +2581,11 @@ static void lg_shutdown(void)
} }
LG_LOCK_FREE(l_cursorRepaint.lock); LG_LOCK_FREE(l_cursorRepaint.lock);
if (g_state.transportOps) if (g_state.transport.ops)
{ {
lgInput_dropTransport(); lgInput_dropTransport();
if (g_state.transport && if (g_state.transport.handle &&
g_state.transportOps->sessionValid(g_state.transport)) g_state.transport.ops->sessionValid(g_state.transport.handle))
{ {
lgAudio_setTransport(NULL, NULL); lgAudio_setTransport(NULL, NULL);
lgClipboard_setTransport(NULL, NULL); lgClipboard_setTransport(NULL, NULL);
@@ -2590,7 +2595,7 @@ static void lg_shutdown(void)
lgAudio_dropTransport(); lgAudio_dropTransport();
lgClipboard_dropTransport(); lgClipboard_dropTransport();
} }
g_state.transportOps->destroy(&g_state.transport); lgTransport_destroy(&g_state.transport);
} }
lgInput_free(); lgInput_free();

View File

@@ -123,8 +123,7 @@ struct AppState
LG_Lock lgrLock; LG_Lock lgrLock;
bool useDMA; bool useDMA;
LG_Transport * transport; LG_TransportInstance transport;
const LG_TransportOps * transportOps;
LG_TransportFeatureFlags transportFeatures; LG_TransportFeatureFlags transportFeatures;
LGThread * cursorThread; LGThread * cursorThread;

View File

@@ -40,20 +40,33 @@ bool lgTransport_isValid(const char * name)
return false; return false;
} }
bool lgTransport_create(const char * name, LG_Transport ** transport, bool lgTransport_create(const char * name, LG_TransportInstance * instance)
const LG_TransportOps ** ops)
{ {
if (!instance)
return false;
*instance = (LG_TransportInstance) { 0 };
for (unsigned i = 0; i < LG_TRANSPORT_COUNT; ++i) for (unsigned i = 0; i < LG_TRANSPORT_COUNT; ++i)
{ {
if (strcmp(LG_Transports[i]->name, name) != 0) if (strcmp(LG_Transports[i]->name, name) != 0)
continue; continue;
if (!LG_Transports[i]->create(transport)) if (!LG_Transports[i]->create(&instance->handle))
return false; return false;
*ops = LG_Transports[i]; instance->ops = LG_Transports[i];
return true; return true;
} }
return false; return false;
} }
void lgTransport_destroy(LG_TransportInstance * instance)
{
if (!instance)
return;
if (instance->ops)
instance->ops->destroy(&instance->handle);
*instance = (LG_TransportInstance) { 0 };
}