diff --git a/VERSION b/VERSION index 5f4d0e5a..e28124f0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -a12-135-gd6805cfa0f+1 \ No newline at end of file +a12-142-g3f13485ced+1 \ No newline at end of file diff --git a/c-host/include/interface/capture.h b/c-host/include/interface/capture.h index 69bc9768..9a48892e 100644 --- a/c-host/include/interface/capture.h +++ b/c-host/include/interface/capture.h @@ -76,6 +76,7 @@ typedef struct CaptureInterface const char * (*getName )(); bool (*create )(); bool (*init )(void * pointerShape, const unsigned int pointerSize); + void (*stop )(); bool (*deinit )(); void (*free )(); unsigned int (*getMaxFrameSize)(); diff --git a/c-host/include/interface/platform.h b/c-host/include/interface/platform.h index 6064f344..ec65349c 100644 --- a/c-host/include/interface/platform.h +++ b/c-host/include/interface/platform.h @@ -47,5 +47,6 @@ typedef struct osEventHandle osEventHandle; osEventHandle * os_createEvent(bool autoReset); void os_freeEvent (osEventHandle * handle); bool os_waitEvent (osEventHandle * handle, unsigned int timeout); +bool os_waitEvents (osEventHandle * handles[], int count, bool waitAll, unsigned int timeout); bool os_signalEvent(osEventHandle * handle); bool os_resetEvent (osEventHandle * handle); \ No newline at end of file diff --git a/c-host/looking-glass-host b/c-host/looking-glass-host deleted file mode 100755 index e71e951f..00000000 Binary files a/c-host/looking-glass-host and /dev/null differ diff --git a/c-host/platform/Windows/CMakeLists.txt b/c-host/platform/Windows/CMakeLists.txt index e8dff3fe..aca29196 100644 --- a/c-host/platform/Windows/CMakeLists.txt +++ b/c-host/platform/Windows/CMakeLists.txt @@ -7,12 +7,21 @@ include_directories( add_library(platform_Windows STATIC src/platform.c - src/windebug.c + src/windebug.c + src/mousehook.c ) add_subdirectory("capture") +FIND_PROGRAM(WINDRES_EXECUTABLE NAMES "windres.exe" DOC "windres executable") +ADD_CUSTOM_COMMAND(TARGET platform_Windows POST_BUILD + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + COMMAND ${WINDRES_EXECUTABLE} -i resource.rc -o "${PROJECT_BINARY_DIR}/resource.o" + VERBATIM +) + target_link_libraries(platform_Windows + "${PROJECT_BINARY_DIR}/resource.o" capture setupapi ) diff --git a/c-host/platform/Windows/app.manifest b/c-host/platform/Windows/app.manifest new file mode 100644 index 00000000..b157af14 --- /dev/null +++ b/c-host/platform/Windows/app.manifest @@ -0,0 +1,12 @@ + + + + Hello World + + + + + + + + diff --git a/c-host/platform/Windows/capture/DXGI/src/dxgi.c b/c-host/platform/Windows/capture/DXGI/src/dxgi.c index 11d5a1f3..32be656f 100644 --- a/c-host/platform/Windows/capture/DXGI/src/dxgi.c +++ b/c-host/platform/Windows/capture/DXGI/src/dxgi.c @@ -55,7 +55,7 @@ Pointer; struct iface { bool initialized; - bool reinit; + bool stop; IDXGIFactory1 * factory; IDXGIAdapter1 * adapter; IDXGIOutput * output; @@ -158,7 +158,7 @@ static bool dxgi_init(void * pointerShape, const unsigned int pointerSize) this->pointerSize = pointerSize; this->pointerUsed = 0; - this->reinit = false; + this->stop = false; this->texRIndex = 0; this->texWIndex = 0; os_resetEvent(this->frameEvent); @@ -402,6 +402,13 @@ fail: return false; } +static void dxgi_stop() +{ + this->stop = true; + os_signalEvent(this->frameEvent ); + os_signalEvent(this->pointerEvent); +} + static bool dxgi_deinit() { assert(this); @@ -497,7 +504,7 @@ static unsigned int dxgi_getMaxFrameSize() return this->height * this->pitch; } -inline static CaptureResult dxgi_capture_int() +static CaptureResult dxgi_capture() { assert(this); assert(this->initialized); @@ -641,36 +648,18 @@ inline static CaptureResult dxgi_capture_int() return CAPTURE_RESULT_OK; } -static CaptureResult dxgi_capture(bool * hasFrameUpdate, bool * hasPointerUpdate) -{ - CaptureResult result = dxgi_capture_int(hasFrameUpdate, hasPointerUpdate); - - // signal pending events if the result was any form of failure or reinit - if (result != CAPTURE_RESULT_OK && result != CAPTURE_RESULT_TIMEOUT) - { - this->reinit = true; - os_signalEvent(this->frameEvent ); - os_signalEvent(this->pointerEvent); - } - - return result; -} - static CaptureResult dxgi_getFrame(CaptureFrame * frame) { assert(this); assert(this->initialized); - if (this->reinit) - return CAPTURE_RESULT_REINIT; - if (!os_waitEvent(this->frameEvent, TIMEOUT_INFINITE)) { DEBUG_ERROR("Failed to wait on the frame event"); return CAPTURE_RESULT_ERROR; } - if (this->reinit) + if (this->stop) return CAPTURE_RESULT_REINIT; Texture * tex = &this->texture[this->texRIndex]; @@ -695,16 +684,13 @@ static CaptureResult dxgi_getPointer(CapturePointer * pointer) assert(this); assert(this->initialized); - if (this->reinit) - return CAPTURE_RESULT_REINIT; - if (!os_waitEvent(this->pointerEvent, TIMEOUT_INFINITE)) { DEBUG_ERROR("Failed to wait on the pointer event"); return CAPTURE_RESULT_ERROR; } - if (this->reinit) + if (this->stop) return CAPTURE_RESULT_REINIT; Pointer p; @@ -761,6 +747,7 @@ struct CaptureInterface Capture_DXGI = .getName = dxgi_getName, .create = dxgi_create, .init = dxgi_init, + .stop = dxgi_stop, .deinit = dxgi_deinit, .free = dxgi_free, .getMaxFrameSize = dxgi_getMaxFrameSize, diff --git a/c-host/platform/Windows/capture/NVFBC/CMakeLists.txt b/c-host/platform/Windows/capture/NVFBC/CMakeLists.txt index e3d0e607..7977ac60 100644 --- a/c-host/platform/Windows/capture/NVFBC/CMakeLists.txt +++ b/c-host/platform/Windows/capture/NVFBC/CMakeLists.txt @@ -12,3 +12,7 @@ target_include_directories(capture_NVFBC PRIVATE src ) + +target_link_libraries(capture_NVFBC + platform_Windows +) diff --git a/c-host/platform/Windows/capture/NVFBC/src/nvfbc.c b/c-host/platform/Windows/capture/NVFBC/src/nvfbc.c index c4c0a356..2921699b 100644 --- a/c-host/platform/Windows/capture/NVFBC/src/nvfbc.c +++ b/c-host/platform/Windows/capture/NVFBC/src/nvfbc.c @@ -19,8 +19,9 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "interface/capture.h" #include "interface/platform.h" -#include "debug.h" +#include "windows/platform.h" #include "windows/windebug.h" +#include "windows/mousehook.h" #include #include #include @@ -30,7 +31,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA struct iface { - bool reinit; + bool stop; NvFBCHandle nvfbc; void * pointerShape; @@ -43,7 +44,10 @@ struct iface NvFBCFrameGrabInfo grabInfo; osEventHandle * frameEvent; - HANDLE cursorEvent; + osEventHandle * cursorEvents[2]; + + int mouseX, mouseY, mouseHotX, mouseHotY; + bool mouseVisible; }; static struct iface * this = NULL; @@ -64,6 +68,13 @@ static void getDesktopSize(unsigned int * width, unsigned int * height) *height = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top; } +static void on_mouseMove(int x, int y) +{ + this->mouseX = x; + this->mouseY = y; + os_signalEvent(this->cursorEvents[1]); +} + static const char * nvfbc_getName() { return "NVFBC (NVidia Frame Buffer Capture)"; @@ -95,13 +106,14 @@ static bool nvfbc_create() static bool nvfbc_init(void * pointerShape, const unsigned int pointerSize) { - this->reinit = false; + this->stop = false; this->pointerShape = pointerShape; this->pointerSize = pointerSize; getDesktopSize(&this->width, &this->height); os_resetEvent(this->frameEvent); + HANDLE event; if (!NvFBCToSysSetup( this->nvfbc, BUFFER_FMT_ARGB10, @@ -111,18 +123,30 @@ static bool nvfbc_init(void * pointerShape, const unsigned int pointerSize) 0, (void **)&this->frameBuffer, NULL, - &this->cursorEvent + &event )) { return false; } + this->cursorEvents[0] = os_wrapEvent(event); + this->cursorEvents[1] = os_createEvent(true); + mouseHook_install(on_mouseMove); + Sleep(100); return true; } +static void nvfbc_stop() +{ + this->stop = true; + os_signalEvent(this->cursorEvents[1]); + os_signalEvent(this->frameEvent); +} + static bool nvfbc_deinit() { + mouseHook_remove(); return true; } @@ -172,7 +196,7 @@ static CaptureResult nvfbc_getFrame(CaptureFrame * frame) return CAPTURE_RESULT_ERROR; } - if (this->reinit) + if (this->stop) return CAPTURE_RESULT_REINIT; frame->width = this->grabInfo.dwWidth; @@ -195,37 +219,33 @@ static CaptureResult nvfbc_getFrame(CaptureFrame * frame) static CaptureResult nvfbc_getPointer(CapturePointer * pointer) { - while(true) + osEventHandle * events[2]; + memcpy(&events, &this->cursorEvents, sizeof(osEventHandle *) * 2); + if (!os_waitEvents(events, 2, false, TIMEOUT_INFINITE)) { - bool sig = false; - switch(WaitForSingleObject((HANDLE)this->cursorEvent, INFINITE)) - { - case WAIT_OBJECT_0: - sig = true; - break; - - case WAIT_ABANDONED: - continue; - - case WAIT_TIMEOUT: - continue; - - case WAIT_FAILED: - DEBUG_WINERROR("Wait for cursor event failed", GetLastError()); - return CAPTURE_RESULT_ERROR; - } - - if (sig) - break; - - DEBUG_ERROR("Unknown wait event return code"); + DEBUG_ERROR("Failed to wait on the cursor events"); return CAPTURE_RESULT_ERROR; } - if (this->reinit) + if (this->stop) return CAPTURE_RESULT_REINIT; - return NvFBCToSysGetCursor(this->nvfbc, pointer, this->pointerShape, this->pointerSize); + CaptureResult result; + pointer->shapeUpdate = false; + if (events[0]) + { + result = NvFBCToSysGetCursor(this->nvfbc, pointer, this->pointerShape, this->pointerSize); + this->mouseVisible = pointer->visible; + this->mouseHotX = pointer->x; + this->mouseHotY = pointer->y; + if (result != CAPTURE_RESULT_OK) + return result; + } + + pointer->visible = this->mouseVisible; + pointer->x = this->mouseX - this->mouseHotX; + pointer->y = this->mouseY - this->mouseHotY; + return CAPTURE_RESULT_OK; } struct CaptureInterface Capture_NVFBC = @@ -233,6 +253,7 @@ struct CaptureInterface Capture_NVFBC = .getName = nvfbc_getName, .create = nvfbc_create, .init = nvfbc_init, + .stop = nvfbc_stop, .deinit = nvfbc_deinit, .free = nvfbc_free, .getMaxFrameSize = nvfbc_getMaxFrameSize, diff --git a/c-host/platform/Windows/include/windows/mousehook.h b/c-host/platform/Windows/include/windows/mousehook.h new file mode 100644 index 00000000..ef71a3e1 --- /dev/null +++ b/c-host/platform/Windows/include/windows/mousehook.h @@ -0,0 +1,23 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017-2019 Geoffrey McRae +https://looking-glass.hostfission.com + +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. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +typedef void (*MouseHookFn)(int x, int y); + +void mouseHook_install(MouseHookFn callback); +void mouseHook_remove(); \ No newline at end of file diff --git a/c-host/platform/Windows/include/windows/platform.h b/c-host/platform/Windows/include/windows/platform.h new file mode 100644 index 00000000..1a098324 --- /dev/null +++ b/c-host/platform/Windows/include/windows/platform.h @@ -0,0 +1,23 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017-2019 Geoffrey McRae +https://looking-glass.hostfission.com + +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. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "interface/platform.h" +#include + +osEventHandle * os_wrapEvent(HANDLE event); \ No newline at end of file diff --git a/c-host/platform/Windows/resource.rc b/c-host/platform/Windows/resource.rc new file mode 100644 index 00000000..8af0ed12 --- /dev/null +++ b/c-host/platform/Windows/resource.rc @@ -0,0 +1,3 @@ +#include "winuser.h" + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "app.manifest" diff --git a/c-host/platform/Windows/src/mousehook.c b/c-host/platform/Windows/src/mousehook.c new file mode 100644 index 00000000..a3da302a --- /dev/null +++ b/c-host/platform/Windows/src/mousehook.c @@ -0,0 +1,98 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017-2019 Geoffrey McRae +https://looking-glass.hostfission.com + +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. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "windows/mousehook.h" +#include "windows/windebug.h" +#include "platform.h" + +#include +#include + +struct mouseHook +{ + bool installed; + HHOOK hook; + MouseHookFn callback; +}; + +static struct mouseHook mouseHook = { 0 }; + +// forwards +static LRESULT WINAPI mouseHook_hook(int nCode, WPARAM wParam, LPARAM lParam); +static LRESULT msg_callback(WPARAM wParam, LPARAM lParam); + +void mouseHook_install(MouseHookFn callback) +{ + struct MSG_CALL_FUNCTION cf; + cf.fn = msg_callback; + cf.wParam = 1; + cf.lParam = (LPARAM)callback; + sendAppMessage(WM_CALL_FUNCTION, 0, (LPARAM)&cf); +} + +void mouseHook_remove() +{ + struct MSG_CALL_FUNCTION cf; + cf.fn = msg_callback; + cf.wParam = 0; + cf.lParam = 0; + sendAppMessage(WM_CALL_FUNCTION, 0, (LPARAM)&cf); +} + +static LRESULT msg_callback(WPARAM wParam, LPARAM lParam) +{ + if (wParam) + { + if (mouseHook.installed) + { + DEBUG_WARN("Mouse hook already installed"); + return 0; + } + + mouseHook.hook = SetWindowsHookEx(WH_MOUSE_LL, mouseHook_hook, NULL, 0); + if (!mouseHook.hook) + { + DEBUG_WINERROR("Failed to install the mouse hook", GetLastError()); + return 0; + } + + mouseHook.installed = true; + mouseHook.callback = (MouseHookFn)lParam; + } + else + { + if (!mouseHook.installed) + return 0; + + UnhookWindowsHookEx(mouseHook.hook); + mouseHook.installed = false; + } + + return 0; +} + +static LRESULT WINAPI mouseHook_hook(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (nCode == HC_ACTION && wParam == WM_MOUSEMOVE) + { + MSLLHOOKSTRUCT *msg = (MSLLHOOKSTRUCT *)lParam; + mouseHook.callback(msg->pt.x, msg->pt.y); + } + return CallNextHookEx(mouseHook.hook, nCode, wParam, lParam); +} \ No newline at end of file diff --git a/c-host/platform/Windows/src/platform.c b/c-host/platform/Windows/src/platform.c index 3a139fa4..bdaa736b 100644 --- a/c-host/platform/Windows/src/platform.c +++ b/c-host/platform/Windows/src/platform.c @@ -17,6 +17,9 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "platform.h" +#include "windows/platform.h" +#include "windows/mousehook.h" #include #include @@ -37,6 +40,7 @@ struct osThreadHandle void * opaque; HANDLE handle; DWORD threadID; + int resultCode; }; @@ -45,6 +49,7 @@ LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) switch(msg) { case WM_CLOSE: + mouseHook_remove(); DestroyWindow(hwnd); break; @@ -52,6 +57,12 @@ LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) PostQuitMessage(0); break; + case WM_CALL_FUNCTION: + { + struct MSG_CALL_FUNCTION * cf = (struct MSG_CALL_FUNCTION *)lParam; + return cf->fn(cf->wParam, cf->lParam); + } + default: return DefWindowProc(hwnd, msg, wParam, lParam); } @@ -65,6 +76,22 @@ static int appThread(void * opaque) return result; } +LRESULT sendAppMessage(UINT Msg, WPARAM wParam, LPARAM lParam) +{ + return SendMessage(messageWnd, Msg, wParam, lParam); +} + +static BOOL WINAPI CtrlHandler(DWORD dwCtrlType) +{ + if (dwCtrlType == CTRL_C_EVENT) + { + SendMessage(messageWnd, WM_CLOSE, 0, 0); + return TRUE; + } + + return FALSE; +} + int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int result = 0; @@ -139,6 +166,9 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine free(infData); SetupDiDestroyDeviceInfoList(deviceInfoSet); + // setup a handler for ctrl+c + SetConsoleCtrlHandler(CtrlHandler, TRUE); + // create a message window so that our message pump works WNDCLASSEX wx = {}; wx.cbSize = sizeof(WNDCLASSEX); @@ -169,6 +199,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine { TranslateMessage(&msg); DispatchMessage(&msg); + continue; } else if (bRet < 0) { @@ -312,6 +343,11 @@ osEventHandle * os_createEvent(bool autoReset) return (osEventHandle*)event; } +osEventHandle * os_wrapEvent(HANDLE event) +{ + return (osEventHandle*)event; +} + void os_freeEvent(osEventHandle * handle) { CloseHandle((HANDLE)handle); @@ -346,6 +382,42 @@ bool os_waitEvent(osEventHandle * handle, unsigned int timeout) } } +bool os_waitEvents(osEventHandle * handles[], int count, bool waitAll, unsigned int timeout) +{ + const DWORD to = (timeout == TIMEOUT_INFINITE) ? INFINITE : (DWORD)timeout; + while(true) + { + DWORD result = WaitForMultipleObjects(count, (HANDLE*)handles, waitAll, to); + if (result >= WAIT_OBJECT_0 && result < count) + { + // null non signalled events from the handle list + for(int i = 0; i < count; ++i) + if (i != result && !os_waitEvent(handles[i], 0)) + handles[i] = NULL; + return true; + } + + if (result >= WAIT_ABANDONED_0 && result - WAIT_ABANDONED_0 < count) + continue; + + switch(result) + { + case WAIT_TIMEOUT: + if (timeout == TIMEOUT_INFINITE) + continue; + + return false; + + case WAIT_FAILED: + DEBUG_WINERROR("Wait for event failed", GetLastError()); + return false; + } + + DEBUG_ERROR("Unknown wait event return code"); + return false; + } +} + bool os_signalEvent(osEventHandle * handle) { return SetEvent((HANDLE)handle); diff --git a/c-host/platform/Windows/src/platform.h b/c-host/platform/Windows/src/platform.h new file mode 100644 index 00000000..4d14b224 --- /dev/null +++ b/c-host/platform/Windows/src/platform.h @@ -0,0 +1,32 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017-2019 Geoffrey McRae +https://looking-glass.hostfission.com + +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. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include + +#define WM_CALL_FUNCTION (WM_USER+1) + +typedef LRESULT (*CallFunction)(WPARAM wParam, LPARAM lParam); +struct MSG_CALL_FUNCTION +{ + CallFunction fn; + WPARAM wParam; + LPARAM lParam; +}; + +LRESULT sendAppMessage(UINT Msg, WPARAM wParam, LPARAM lParam); \ No newline at end of file diff --git a/c-host/src/app.c b/c-host/src/app.c index 44759dbe..9ff2f632 100644 --- a/c-host/src/app.c +++ b/c-host/src/app.c @@ -368,6 +368,7 @@ int app_main() } finish: + iface->stop(); stopThreads(); exit: