diff --git a/c-host/app.c b/c-host/app.c index 87533695..7af6646f 100644 --- a/c-host/app.c +++ b/c-host/app.c @@ -51,19 +51,67 @@ static struct app app; static int cursorThread(void * opaque) { + DEBUG_INFO("Cursor thread started"); + while(app.running) usleep(10000); + + DEBUG_INFO("Cursor thread stopped"); return 0; } static int frameThread(void * opaque) { + DEBUG_INFO("Frame thread started"); + while(app.running) usleep(10000); + + DEBUG_INFO("Frame thread stopped"); return 0; } -int app_main() +bool startThreads() +{ + app.running = true; + if (!os_createThread("CursorThread", cursorThread, NULL, &app.cursorThread)) + { + DEBUG_ERROR("Failed to create the cursor thread"); + return false; + } + + if (!os_createThread("FrameThread", frameThread, NULL, &app.frameThread)) + { + DEBUG_ERROR("Failed to create the frame thread"); + return false; + } + + return true; +} + +bool stopThreads() +{ + bool ok = true; + + app.running = false; + if (app.frameThread && !os_joinThread(app.frameThread, NULL)) + { + DEBUG_WARN("Failed to join the frame thread"); + ok = false; + } + app.frameThread = NULL; + + if (app.cursorThread && !os_joinThread(app.cursorThread, NULL)) + { + DEBUG_WARN("Failed to join the cursor thread"); + ok = false; + } + app.cursorThread = NULL; + + return ok; +} + +int app_main(bool * termSignal) { unsigned int shmemSize = os_shmemSize(); uint8_t * shmemMap = NULL; @@ -128,32 +176,57 @@ int app_main() } DEBUG_INFO("Capture Size : %u MiB (%u)", maxFrameSize / 1048576, maxFrameSize); - if (!os_createThread("CursorThread", cursorThread, NULL, &app.cursorThread)) + DEBUG_INFO("==== [ Capture Start ] ===="); + + if (!startThreads()) { - DEBUG_ERROR("Failed to create the cursor thread"); exitcode = -1; - goto exit; + goto finish; } - if (!os_createThread("FrameThread", frameThread, NULL, &app.frameThread)) + while(!*termSignal) { - DEBUG_ERROR("Failed to create the frame thread"); - exitcode = -1; - goto exit_cursor_thread; + bool hasFrameUpdate = false; + bool hasPointerUpdate = false; + switch(iface->capture(&hasFrameUpdate, &hasPointerUpdate)) + { + case CAPTURE_RESULT_OK: + break; + + case CAPTURE_RESULT_TIMEOUT: + continue; + + case CAPTURE_RESULT_REINIT: + DEBUG_INFO("==== [ Capture Reinit ] ===="); + if (!stopThreads()) + { + exitcode = -1; + goto finish; + } + + if (true || !iface->deinit() || !iface->init()) + { + DEBUG_ERROR("Failed to reinitialize the capture device"); + exitcode = -1; + goto finish; + } + + if (!startThreads()) + { + exitcode = -1; + goto finish; + } + break; + + case CAPTURE_RESULT_ERROR: + DEBUG_ERROR("Capture interface reported a fatal error"); + exitcode = -1; + goto finish; + } } - iface->capture(); - iface->capture(); - iface->capture(); - -//finish: - app.running = false; - if (!os_joinThread(app.frameThread, NULL)) - DEBUG_WARN("Failed to join the cursor thread"); -exit_cursor_thread: - app.running = false; - if (!os_joinThread(app.cursorThread, NULL)) - DEBUG_WARN("Failed to join the cursor thread"); +finish: + stopThreads(); exit: iface->deinit(); iface->free(); diff --git a/c-host/app.h b/c-host/app.h index 46302ed2..721d3fd3 100644 --- a/c-host/app.h +++ b/c-host/app.h @@ -21,7 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include -int app_main(); +int app_main(bool * termSignal); // these must be implemented for each OS diff --git a/c-host/capture/interface.h b/c-host/capture/interface.h index 64e39ea6..8831eb28 100644 --- a/c-host/capture/interface.h +++ b/c-host/capture/interface.h @@ -39,5 +39,8 @@ struct CaptureInterface bool (*deinit )(); void (*free )(); unsigned int (*getMaxFrameSize)(); - CaptureResult (*capture )(); + + CaptureResult (*capture)( + bool * hasFrameUpdate, + bool * hasPointerUpdate); }; \ No newline at end of file diff --git a/c-host/windows/capture/dxgi.c b/c-host/windows/capture/dxgi.c index 99076083..daaa64e1 100644 --- a/c-host/windows/capture/dxgi.c +++ b/c-host/windows/capture/dxgi.c @@ -41,7 +41,6 @@ struct iface IDXGIOutputDuplication * dup; ID3D11Texture2D * texture; bool hasFrame; - bool retryAcquire; unsigned int width; unsigned int height; @@ -93,7 +92,7 @@ static bool dxgi_init() fn = (User32_SetProcessDpiAwarenessContext)GetProcAddress(user32, "SetProcessDpiAwarenessContext"); if (fn) fn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - CloseHandle(user32); + FreeLibrary(user32); dpiDone = true; } @@ -260,6 +259,10 @@ static bool dxgi_init() if (SUCCEEDED(status)) break; + // if access is denied we just keep trying until it isn't + if (status == E_ACCESSDENIED) + --i; + Sleep(200); } @@ -392,7 +395,7 @@ static unsigned int dxgi_getMaxFrameSize() return this->height * this->pitch; } -static CaptureResult dxgi_capture() +static CaptureResult dxgi_capture(bool * hasFrameUpdate, bool * hasPointerUpdate) { assert(this); assert(this->initialized); @@ -418,14 +421,6 @@ static CaptureResult dxgi_capture() case WAIT_ABANDONED: case DXGI_ERROR_ACCESS_LOST: - if (this->retryAcquire) - { - DEBUG_WINERROR("Unable to acquire next frame, giving up", status); - this->retryAcquire = false; - return CAPTURE_RESULT_ERROR; - } - - this->retryAcquire = true; return CAPTURE_RESULT_REINIT; default: @@ -444,6 +439,11 @@ static CaptureResult dxgi_capture() ID3D11DeviceContext_CopyResource(this->deviceContext, (ID3D11Resource *)this->texture, (ID3D11Resource *)src); + *hasFrameUpdate = true; + + if (frameInfo.PointerShapeBufferSize > 0) + *hasPointerUpdate = true; + return CAPTURE_RESULT_OK; } @@ -465,7 +465,10 @@ static CaptureResult dxgi_releaseFrame() case WAIT_ABANDONED: case DXGI_ERROR_ACCESS_LOST: + { + this->hasFrame = false; return CAPTURE_RESULT_REINIT; + } default: DEBUG_WINERROR("ReleaseFrame failed", status); diff --git a/c-host/windows/platform.c b/c-host/windows/platform.c index 8bc85a3e..b4e1b28c 100644 --- a/c-host/windows/platform.c +++ b/c-host/windows/platform.c @@ -28,6 +28,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA static HANDLE shmemHandle = INVALID_HANDLE_VALUE; static bool shmemOwned = false; static IVSHMEM_MMAP shmemMap = {0}; +static bool termSignal = false; +static HWND messageWnd; struct osThreadHandle { @@ -39,12 +41,54 @@ struct osThreadHandle int resultCode; }; -int WINAPI WinMain(HINSTANCE hInstnace, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch(msg) + { + case WM_CLOSE: + DestroyWindow(hwnd); + break; + + case WM_DESTROY: + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + return 0; +} + +static int appThread(void * opaque) +{ + int result = app_main(&termSignal); + SendMessage(messageWnd, WM_CLOSE, 0, 0); + return result; +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + int result = 0; HDEVINFO deviceInfoSet; PSP_DEVICE_INTERFACE_DETAIL_DATA infData = NULL; SP_DEVICE_INTERFACE_DATA deviceInterfaceData; +#if 0 + // redirect stderr to a file + { + char tempPath[MAX_PATH+1]; + GetTempPathA(sizeof(tempPath), tempPath); + int len = snprintf(NULL, 0, "%slooking-glass-host.txt", tempPath); + char * path = malloc(len + 1); + sprintf(path, "%slooking-glass-host.txt", tempPath); + freopen(path, "a", stderr); + free(path); + } +#endif + + // always flush stderr + setbuf(stderr, NULL); + deviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE); memset(&deviceInterfaceData, 0, sizeof(SP_DEVICE_INTERFACE_DATA)); deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); @@ -55,11 +99,13 @@ int WINAPI WinMain(HINSTANCE hInstnace, HINSTANCE hPrevInstance, LPSTR lpCmdLine if (error == ERROR_NO_MORE_ITEMS) { DEBUG_WINERROR("Unable to enumerate the device, is it attached?", error); - return -1; + result = -1; + goto finish; } DEBUG_WINERROR("SetupDiEnumDeviceInterfaces failed", error); - return -1; + result = -1; + goto finish; } DWORD reqSize = 0; @@ -67,7 +113,8 @@ int WINAPI WinMain(HINSTANCE hInstnace, HINSTANCE hPrevInstance, LPSTR lpCmdLine if (!reqSize) { DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError()); - return -1; + result = -1; + goto finish; } infData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)calloc(reqSize, 1); @@ -76,7 +123,8 @@ int WINAPI WinMain(HINSTANCE hInstnace, HINSTANCE hPrevInstance, LPSTR lpCmdLine { free(infData); DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError()); - return -1; + result = -1; + goto finish; } shmemHandle = CreateFile(infData->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, 0); @@ -85,28 +133,66 @@ int WINAPI WinMain(HINSTANCE hInstnace, HINSTANCE hPrevInstance, LPSTR lpCmdLine SetupDiDestroyDeviceInfoList(deviceInfoSet); free(infData); DEBUG_WINERROR("CreateFile returned INVALID_HANDLE_VALUE", GetLastError()); - return -1; + result = -1; + goto finish; } free(infData); SetupDiDestroyDeviceInfoList(deviceInfoSet); - int result = app_main(); - - os_shmemUnmap(); - CloseHandle(shmemHandle); - - if (result != 0) + // create a message window so that our message pump works + WNDCLASSEX wx = {}; + wx.cbSize = sizeof(WNDCLASSEX); + wx.lpfnWndProc = DummyWndProc; + wx.hInstance = hInstance; + wx.lpszClassName = "DUMMY_CLASS"; + if (!RegisterClassEx(&wx)) { - MessageBoxA( - NULL, - "The Looking Glass host has terminated due to an error.\r\n" - "\r\n" - "For more information run the application in a command prompt.", - "Looking Glass Host", - MB_ICONERROR); + DEBUG_ERROR("Failed to register message window class"); + result = -1; + goto finish_shmem; + } + messageWnd = CreateWindowEx(0, "DUMMY_CLASS", "DUMMY_NAME", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); + + osThreadHandle * thread; + if (!os_createThread("appThread", appThread, NULL, &thread)) + { + DEBUG_ERROR("Failed to create the main application thread"); + result = -1; + goto finish_shmem; } + while(!termSignal) + { + MSG msg; + BOOL bRet = GetMessage(&msg, NULL, 0, 0); + if (bRet > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else if (bRet < 0) + { + DEBUG_ERROR("Unknown error from GetMessage"); + result = -1; + goto shutdown; + } + + DEBUG_INFO("Platform shutdown"); + break; + } + +shutdown: + termSignal = true; + if (!os_joinThread(thread, &result)) + { + DEBUG_ERROR("Failed to join the main application thread"); + result = -1; + } +finish_shmem: + os_shmemUnmap(); + CloseHandle(shmemHandle); +finish: return result; }