mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-09 16:18:20 +00:00
[host] windows: handle graceful shutdown on user switch
This commit is contained in:
parent
6a72633674
commit
545e736389
@ -37,7 +37,7 @@
|
||||
int app_main(int argc, char * argv[]);
|
||||
bool app_init(void);
|
||||
void app_shutdown(void);
|
||||
void app_quit(void);
|
||||
void app_quit(int exitcode);
|
||||
|
||||
// these must be implemented for each OS
|
||||
const char * os_getExecutable(void);
|
||||
|
@ -62,7 +62,7 @@ int main(int argc, char * argv[])
|
||||
void sigHandler(int signo)
|
||||
{
|
||||
DEBUG_INFO("SIGINT");
|
||||
app_quit();
|
||||
app_quit(LG_HOST_EXIT_USER);
|
||||
}
|
||||
|
||||
bool app_init(void)
|
||||
|
@ -246,7 +246,7 @@ LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
NULL
|
||||
);
|
||||
|
||||
if (clicked == ID_MENU_EXIT ) app_quit();
|
||||
if (clicked == ID_MENU_EXIT ) app_quit(LG_HOST_EXIT_USER);
|
||||
else if (clicked == ID_MENU_SHOW_LOG)
|
||||
{
|
||||
const char * logFile = option_get_string("os", "logFile");
|
||||
@ -259,6 +259,14 @@ LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_WTSSESSION_CHANGE:
|
||||
if (wParam == WTS_CONSOLE_DISCONNECT)
|
||||
{
|
||||
DEBUG_INFO("Console disconnected, shutting down");
|
||||
app_quit(LG_HOST_EXIT_CAPTURE);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (msg == app.trayRestartMsg)
|
||||
RegisterTrayIcon();
|
||||
@ -440,6 +448,9 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
AppendMenu(app.trayMenu, MF_SEPARATOR, 0 , NULL );
|
||||
AppendMenu(app.trayMenu, MF_STRING , ID_MENU_EXIT , "Exit" );
|
||||
|
||||
if (!WTSRegisterSessionNotification(app.messageWnd, NOTIFY_FOR_THIS_SESSION))
|
||||
DEBUG_WINERROR("WTSRegisterSessionNotification failed", GetLastError());
|
||||
|
||||
// create the application thread
|
||||
LGThread * thread;
|
||||
if (!lgCreateThread("appThread", appThread, NULL, &thread))
|
||||
@ -470,6 +481,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
}
|
||||
|
||||
shutdown:
|
||||
WTSUnRegisterSessionNotification(app.messageWnd);
|
||||
|
||||
DestroyMenu(app.trayMenu);
|
||||
app_shutdown();
|
||||
UnregisterWait(app.exitWait);
|
||||
|
327
host/src/app.c
327
host/src/app.c
@ -68,8 +68,8 @@ enum AppState
|
||||
{
|
||||
APP_STATE_RUNNING,
|
||||
APP_STATE_IDLE,
|
||||
APP_STATE_RESTART,
|
||||
APP_STATE_REINIT,
|
||||
APP_STATE_TRANSITION_TO_IDLE,
|
||||
APP_STATE_REINIT_LGMP,
|
||||
APP_STATE_SHUTDOWN
|
||||
};
|
||||
|
||||
@ -103,10 +103,12 @@ struct app
|
||||
uint32_t frameSerial;
|
||||
|
||||
CaptureInterface * iface;
|
||||
bool captureStarted;
|
||||
|
||||
enum AppState state;
|
||||
enum AppState state, lastState;
|
||||
LGTimer * lgmpTimer;
|
||||
LGThread * frameThread;
|
||||
bool threadsStarted;
|
||||
};
|
||||
|
||||
static struct app app;
|
||||
@ -156,6 +158,14 @@ static struct Option options[] =
|
||||
{0}
|
||||
};
|
||||
|
||||
inline static void setAppState(enum AppState state)
|
||||
{
|
||||
if (app.state == APP_STATE_SHUTDOWN)
|
||||
return;
|
||||
app.lastState = app.state;
|
||||
app.state = state;
|
||||
}
|
||||
|
||||
static bool lgmpTimer(void * opaque)
|
||||
{
|
||||
LGMP_STATUS status;
|
||||
@ -166,12 +176,12 @@ static bool lgmpTimer(void * opaque)
|
||||
{
|
||||
DEBUG_ERROR("LGMP reported the shared memory has been corrrupted, "
|
||||
"attempting to recover");
|
||||
app.state = APP_STATE_REINIT;
|
||||
setAppState(APP_STATE_REINIT_LGMP);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_ERROR("lgmpHostProcess Failed: %s", lgmpStatusString(status));
|
||||
app.state = APP_STATE_SHUTDOWN;
|
||||
setAppState(APP_STATE_SHUTDOWN);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -196,7 +206,7 @@ static bool lgmpTimer(void * opaque)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sendFrame(CaptureResult result)
|
||||
static bool sendFrame(CaptureResult result, bool * restart)
|
||||
{
|
||||
CaptureFrame frame = { 0 };
|
||||
bool repeatFrame = false;
|
||||
@ -225,13 +235,14 @@ static bool sendFrame(CaptureResult result)
|
||||
|
||||
case CAPTURE_RESULT_REINIT:
|
||||
{
|
||||
app.state = APP_STATE_RESTART;
|
||||
*restart = true;
|
||||
DEBUG_INFO("Frame thread reinit");
|
||||
return false;
|
||||
}
|
||||
|
||||
case CAPTURE_RESULT_ERROR:
|
||||
{
|
||||
*restart = false;
|
||||
DEBUG_ERROR("Failed to get the frame");
|
||||
return false;
|
||||
}
|
||||
@ -363,39 +374,43 @@ static int frameThread(void * opaque)
|
||||
|
||||
while(app.state == APP_STATE_RUNNING)
|
||||
{
|
||||
if (!sendFrame(CAPTURE_RESULT_OK))
|
||||
bool restart = false;
|
||||
if (!sendFrame(CAPTURE_RESULT_OK, &restart))
|
||||
{
|
||||
if (restart)
|
||||
setAppState(APP_STATE_TRANSITION_TO_IDLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUG_INFO("Frame thread stopped");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool startThreads(void)
|
||||
{
|
||||
app.state = APP_STATE_RUNNING;
|
||||
if (!app.iface->asyncCapture)
|
||||
if (app.threadsStarted)
|
||||
return true;
|
||||
|
||||
if (!lgCreateThread("FrameThread", frameThread, NULL, &app.frameThread))
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the frame thread");
|
||||
return false;
|
||||
}
|
||||
if (app.iface->asyncCapture)
|
||||
if (!lgCreateThread("FrameThread", frameThread, NULL, &app.frameThread))
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the frame thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
app.threadsStarted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stopThreads(void)
|
||||
{
|
||||
app.iface->stop();
|
||||
if (app.state != APP_STATE_SHUTDOWN &&
|
||||
app.state != APP_STATE_REINIT)
|
||||
app.state = APP_STATE_IDLE;
|
||||
|
||||
if (!app.iface->asyncCapture)
|
||||
if (!app.threadsStarted)
|
||||
return true;
|
||||
|
||||
if (app.frameThread)
|
||||
app.iface->stop();
|
||||
|
||||
if (app.iface->asyncCapture && app.frameThread)
|
||||
{
|
||||
if (!lgJoinThread(app.frameThread, NULL))
|
||||
{
|
||||
@ -406,32 +421,37 @@ bool stopThreads(void)
|
||||
app.frameThread = NULL;
|
||||
}
|
||||
|
||||
app.threadsStarted = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool captureStart(void)
|
||||
{
|
||||
if (app.state == APP_STATE_IDLE)
|
||||
{
|
||||
if (!app.iface->init(app.ivshmemBase, &app.alignSize))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the capture device");
|
||||
return false;
|
||||
}
|
||||
if (app.captureStarted)
|
||||
return true;
|
||||
|
||||
if (app.iface->start && !app.iface->start())
|
||||
{
|
||||
DEBUG_ERROR("Failed to start the capture device");
|
||||
return false;
|
||||
}
|
||||
if (!app.iface->init(app.ivshmemBase, &app.alignSize))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the capture device");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (app.iface->start && !app.iface->start())
|
||||
{
|
||||
DEBUG_ERROR("Failed to start the capture device");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_INFO("==== [ Capture Start ] ====");
|
||||
app.captureStarted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool captureStop(void)
|
||||
{
|
||||
if (!app.captureStarted)
|
||||
return true;
|
||||
|
||||
DEBUG_INFO("==== [ Capture Stop ] ====");
|
||||
|
||||
if (!app.iface->deinit())
|
||||
@ -441,6 +461,7 @@ static bool captureStop(void)
|
||||
}
|
||||
|
||||
app.frameValid = false;
|
||||
app.captureStarted = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -790,7 +811,8 @@ int app_main(int argc, char * argv[])
|
||||
// make sure rng is actually seeded for LGMP
|
||||
srand((unsigned)time(NULL));
|
||||
|
||||
app.state = APP_STATE_RUNNING;
|
||||
app.lastState = APP_STATE_RUNNING;
|
||||
app.state = APP_STATE_RUNNING;
|
||||
ivshmemOptionsInit();
|
||||
|
||||
// register capture interface options
|
||||
@ -887,7 +909,8 @@ int app_main(int argc, char * argv[])
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iface->init(app.ivshmemBase, &app.alignSize))
|
||||
app.iface = iface;
|
||||
if (captureStart())
|
||||
break;
|
||||
|
||||
iface->free();
|
||||
@ -896,6 +919,8 @@ int app_main(int argc, char * argv[])
|
||||
|
||||
if (!iface)
|
||||
{
|
||||
app.iface = NULL;
|
||||
|
||||
if (*ifaceName)
|
||||
DEBUG_ERROR("Specified capture interface not supported");
|
||||
else
|
||||
@ -907,8 +932,6 @@ int app_main(int argc, char * argv[])
|
||||
DEBUG_INFO("Using : %s", iface->getName());
|
||||
DEBUG_INFO("Capture Method : %s", iface->asyncCapture ?
|
||||
"Asynchronous" : "Synchronous");
|
||||
|
||||
app.iface = iface;
|
||||
}
|
||||
|
||||
if (!lgmpSetup(&shmDev))
|
||||
@ -919,141 +942,129 @@ int app_main(int argc, char * argv[])
|
||||
|
||||
LG_LOCK_INIT(app.pointerLock);
|
||||
|
||||
if (app.iface->start && !app.iface->start())
|
||||
do
|
||||
{
|
||||
DEBUG_ERROR("Failed to start the capture interface");
|
||||
exitcode = LG_HOST_EXIT_FATAL;
|
||||
goto fail_lgmp;
|
||||
}
|
||||
|
||||
while(app.state != APP_STATE_SHUTDOWN)
|
||||
{
|
||||
if (app.state == APP_STATE_REINIT)
|
||||
switch(app.state)
|
||||
{
|
||||
DEBUG_INFO("Performing LGMP reinitialization");
|
||||
lgmpShutdown();
|
||||
app.state = APP_STATE_RUNNING;
|
||||
if (!lgmpSetup(&shmDev))
|
||||
goto fail_lgmp;
|
||||
}
|
||||
|
||||
if (app.state == APP_STATE_IDLE)
|
||||
{
|
||||
if(lgmpHostQueueHasSubs(app.pointerQueue) ||
|
||||
lgmpHostQueueHasSubs(app.frameQueue))
|
||||
{
|
||||
if (!captureStart())
|
||||
{
|
||||
exitcode = LG_HOST_EXIT_FAILED;
|
||||
goto fail_capture;
|
||||
}
|
||||
|
||||
if (!startThreads())
|
||||
{
|
||||
exitcode = LG_HOST_EXIT_FAILED;
|
||||
goto fail_threads;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
usleep(100000);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
while(likely(app.state != APP_STATE_SHUTDOWN && (
|
||||
lgmpHostQueueHasSubs(app.pointerQueue) ||
|
||||
lgmpHostQueueHasSubs(app.frameQueue))))
|
||||
{
|
||||
if (unlikely(
|
||||
app.state == APP_STATE_RESTART ||
|
||||
app.state == APP_STATE_REINIT))
|
||||
case APP_STATE_REINIT_LGMP:
|
||||
DEBUG_INFO("Performing LGMP reinitialization");
|
||||
lgmpShutdown();
|
||||
setAppState(app.lastState);
|
||||
if (!lgmpSetup(&shmDev))
|
||||
goto fail_lgmp;
|
||||
break;
|
||||
|
||||
if (unlikely(lgmpHostQueueNewSubs(app.pointerQueue) > 0))
|
||||
{
|
||||
LG_LOCK(app.pointerLock);
|
||||
sendPointer(true);
|
||||
LG_UNLOCK(app.pointerLock);
|
||||
}
|
||||
|
||||
const uint64_t delta = microtime() - previousFrameTime;
|
||||
if (delta < throttleUs)
|
||||
{
|
||||
const uint64_t us = throttleUs - delta;
|
||||
// only delay if the time is reasonable
|
||||
if (us > 1000)
|
||||
nsleep(us * 1000);
|
||||
}
|
||||
|
||||
const uint64_t captureStart = microtime();
|
||||
|
||||
const CaptureResult result = app.iface->capture(
|
||||
app.captureIndex, app.frameBuffer[app.captureIndex]);
|
||||
|
||||
if (likely(result == CAPTURE_RESULT_OK))
|
||||
previousFrameTime = captureStart;
|
||||
else if (likely(result == CAPTURE_RESULT_TIMEOUT))
|
||||
{
|
||||
if (!app.iface->asyncCapture)
|
||||
if (unlikely(app.frameValid &&
|
||||
lgmpHostQueueNewSubs(app.frameQueue) > 0))
|
||||
{
|
||||
LGMP_STATUS status;
|
||||
if ((status = lgmpHostQueuePost(app.frameQueue, 0,
|
||||
app.frameMemory[app.readIndex])) != LGMP_OK)
|
||||
DEBUG_ERROR("%s", lgmpStatusString(status));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(result)
|
||||
case APP_STATE_IDLE:
|
||||
// if there are no clients subscribed, just remain idle
|
||||
if (!lgmpHostQueueHasSubs(app.pointerQueue) &&
|
||||
!lgmpHostQueueHasSubs(app.frameQueue))
|
||||
{
|
||||
case CAPTURE_RESULT_REINIT:
|
||||
app.state = APP_STATE_RESTART;
|
||||
continue;
|
||||
|
||||
case CAPTURE_RESULT_ERROR:
|
||||
DEBUG_ERROR("Capture interface reported a fatal error");
|
||||
exitcode = LG_HOST_EXIT_FAILED;
|
||||
goto fail_capture;
|
||||
|
||||
default:
|
||||
DEBUG_ASSERT("Invalid capture result");
|
||||
usleep(100000);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!app.iface->asyncCapture)
|
||||
sendFrame(result);
|
||||
}
|
||||
// clients subscribed, start the capture
|
||||
if (!captureStart() || !startThreads())
|
||||
{
|
||||
exitcode = LG_HOST_EXIT_FAILED;
|
||||
goto fail;
|
||||
}
|
||||
setAppState(APP_STATE_RUNNING);
|
||||
break;
|
||||
|
||||
if (app.state != APP_STATE_SHUTDOWN)
|
||||
{
|
||||
if (!stopThreads())
|
||||
case APP_STATE_TRANSITION_TO_IDLE:
|
||||
if (!stopThreads() || !captureStop())
|
||||
{
|
||||
exitcode = LG_HOST_EXIT_FAILED;
|
||||
goto fail;
|
||||
}
|
||||
setAppState(APP_STATE_IDLE);
|
||||
break;
|
||||
|
||||
case APP_STATE_RUNNING:
|
||||
{
|
||||
exitcode = LG_HOST_EXIT_FAILED;
|
||||
goto fail_threads;
|
||||
// if there are no clients subscribed, go idle
|
||||
if (!lgmpHostQueueHasSubs(app.pointerQueue) &&
|
||||
!lgmpHostQueueHasSubs(app.frameQueue))
|
||||
{
|
||||
setAppState(APP_STATE_TRANSITION_TO_IDLE);
|
||||
break;
|
||||
}
|
||||
|
||||
// if there is a brand new client, send them the pointer
|
||||
if (unlikely(lgmpHostQueueNewSubs(app.pointerQueue) > 0))
|
||||
{
|
||||
LG_LOCK(app.pointerLock);
|
||||
sendPointer(true);
|
||||
LG_UNLOCK(app.pointerLock);
|
||||
}
|
||||
|
||||
const uint64_t delta = microtime() - previousFrameTime;
|
||||
if (delta < throttleUs)
|
||||
{
|
||||
const uint64_t us = throttleUs - delta;
|
||||
// only delay if the time is reasonable
|
||||
if (us > 1000)
|
||||
nsleep(us * 1000);
|
||||
}
|
||||
|
||||
const uint64_t captureStartTime = microtime();
|
||||
|
||||
const CaptureResult result = app.iface->capture(
|
||||
app.captureIndex, app.frameBuffer[app.captureIndex]);
|
||||
|
||||
if (likely(result == CAPTURE_RESULT_OK))
|
||||
previousFrameTime = captureStartTime;
|
||||
else if (likely(result == CAPTURE_RESULT_TIMEOUT))
|
||||
{
|
||||
if (!app.iface->asyncCapture)
|
||||
if (unlikely(app.frameValid &&
|
||||
lgmpHostQueueNewSubs(app.frameQueue) > 0))
|
||||
{
|
||||
LGMP_STATUS status;
|
||||
if ((status = lgmpHostQueuePost(app.frameQueue, 0,
|
||||
app.frameMemory[app.readIndex])) != LGMP_OK)
|
||||
DEBUG_ERROR("%s", lgmpStatusString(status));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(result)
|
||||
{
|
||||
case CAPTURE_RESULT_REINIT:
|
||||
setAppState(APP_STATE_TRANSITION_TO_IDLE);
|
||||
continue;
|
||||
|
||||
case CAPTURE_RESULT_ERROR:
|
||||
DEBUG_ERROR("Capture interface reported a fatal error");
|
||||
exitcode = LG_HOST_EXIT_FAILED;
|
||||
goto fail;
|
||||
|
||||
default:
|
||||
DEBUG_ASSERT("Invalid capture result");
|
||||
}
|
||||
}
|
||||
|
||||
if (!app.iface->asyncCapture)
|
||||
{
|
||||
bool restart = false;
|
||||
if (!sendFrame(result, &restart) && restart)
|
||||
setAppState(APP_STATE_TRANSITION_TO_IDLE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!captureStop())
|
||||
{
|
||||
exitcode = LG_HOST_EXIT_FAILED;
|
||||
goto fail_capture;
|
||||
}
|
||||
|
||||
continue;
|
||||
case APP_STATE_SHUTDOWN:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
while(app.state != APP_STATE_SHUTDOWN);
|
||||
|
||||
exitcode = app.exitcode;
|
||||
|
||||
fail:
|
||||
stopThreads();
|
||||
|
||||
fail_threads:
|
||||
captureStop();
|
||||
|
||||
fail_capture:
|
||||
app.iface->free();
|
||||
LG_LOCK_FREE(app.pointerLock);
|
||||
|
||||
@ -1072,7 +1083,7 @@ void app_shutdown(void)
|
||||
app.state = APP_STATE_SHUTDOWN;
|
||||
}
|
||||
|
||||
void app_quit(void)
|
||||
void app_quit(int exitcode)
|
||||
{
|
||||
if (app.state == APP_STATE_SHUTDOWN)
|
||||
{
|
||||
@ -1080,6 +1091,6 @@ void app_quit(void)
|
||||
exit(LG_HOST_EXIT_USER);
|
||||
}
|
||||
|
||||
app.exitcode = LG_HOST_EXIT_USER;
|
||||
app.exitcode = exitcode;
|
||||
app_shutdown();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user