Compare commits

..

21 Commits

Author SHA1 Message Date
Jonathan Rubenstein
25c88a1c6c [doc] faq: Correct misinformed question about mouse warp issue 2021-07-17 15:20:22 +10:00
Geoffrey McRae
7decb58bf7 [host] windows: fix build on Linux due to case sensitive filenames 2021-07-17 15:05:32 +10:00
Geoffrey McRae
d1ec19b30b [host] windows: fix delayExecution order of magnitude bug 2021-07-17 15:02:58 +10:00
Geoffrey McRae
74468cf799 [host] windows: remove accidental addition of some junk 2021-07-17 15:02:36 +10:00
Geoffrey McRae
411a6b1e49 [host] windows: add delayExecution function for more accurate sleeps
This change not only exposes and allows use of NtDelayExecution, but
also moves the code to set the system timer resolution.
2021-07-17 14:55:22 +10:00
Geoffrey McRae
e228165ff9 [host] windows: fix system timer resolution message units
ZwSetTimerResolution works in units of 100ns
2021-07-17 14:31:31 +10:00
Geoffrey McRae
d615514799 [host] windows: do not callback from the mouse hook context
The windows hook WH_MOUSE_LL is called in such a way that any delay in
processing causes a system wide stall. This change spawns an extra
thread which waits on an event set by the hook which is then used to
call the callback with an artifical limit of 1000Hz.
2021-07-17 14:03:52 +10:00
Jonathan Rubenstein
ed717351cf [doc] faq: Partial revert of fa871e9 to include registry hack
Expected to be fixed in B5
2021-07-16 00:47:22 +10:00
Jonathan Rubenstein
4658244686 [doc] build: Revise client build dependencies for B4 2021-07-16 00:47:22 +10:00
Quantum
48ae5c69f4 [client] wayland: fix typo in warp usage
The unwanted ! was introduced in 4b99bba200.
This basically caused warp to never be used.
2021-07-15 08:40:36 +10:00
Geoffrey McRae
4d065d577b [obs] call debug_init() to initialize debug print
Fixes a segfault reported in discord, DEBUG_* macros are not available
until `debug_init()` has been called as of commit
1effd5fddc
2021-07-13 07:42:25 +10:00
Geoffrey McRae
789ee70674 [host] dxgi: print out the adapter details earlier
Before we try and perhaps fail to init DXGI, we should print out what
the device is so that when there is an error report we can immediately
see if the user has the QXL device attached still.
2021-07-12 19:28:13 +10:00
Geoffrey McRae
3c0616bab7 [host] dxgi: print out the output device name to aid with support 2021-07-12 19:03:02 +10:00
Geoffrey McRae
3ce3b573a3 [host] app: fix infinate loop introduced in the last commit 2021-07-12 17:35:16 +10:00
Geoffrey McRae
ce459c24ce [host] app: wait for space in the frame queue
We must always wait for space in the frame queue so that we do not
overwrite memory that is already queued and may be in use by a client.
2021-07-12 17:30:06 +10:00
Geoffrey McRae
7d0b9711bd [host] nvfbc: remove the frameEvent event and associated code
Now that the host application can run the capture interface in
synchronous mode, and NVFBC uses this mode there is no longer need for
the frameEvent.
2021-07-12 17:01:23 +10:00
Geoffrey McRae
e477663a7e [host] app: allow the capture interface to select async or sync mode
While it's correct for DXGI to use a asyncronous waitFrame model, other
capture interfaces such as NvFBC it is not correct. This change allows
the capture interface to specify which is more correct for it and moves
the waitFrame/post into the main thread if async is not desired.
2021-07-12 16:57:22 +10:00
Quantum
eb01efe0cb [host] nvfbc: do not crash when protected content is playing
We return a timeout, so that when protected content finishes playing, we
can immediately resume capture.
2021-07-11 17:54:23 +10:00
Geoffrey McRae
8db4b65dee [host] app: allocate LGMP memory for KVMFRCursor updates without shapes
This changes the host to use a seperate pool of LGMP memory for cursor
positionl updates without shape information helping to prevent
corruption of the shape entries if they are still pending. While this is
not a perfect solution it resolves the issue without making major
changes to LGMP during the RC phase we are currently in.
2021-07-11 12:52:18 +10:00
Quantum
501b270890 [host] nvfbc: optimize change detection loop
Before, we only break out of the current row when a change is detected,
and all subsequent rows are still scanned. Now we break out of the entire
loop. This should make change detection ever so slightly faster.
2021-07-11 10:15:12 +10:00
Quantum
fd8f8b2b28 [host] dxgi: correctly mention AcquireNextFrame in help text
Also fix some formatting issues.

Co-Authored-By: Tudor Brindus <me@tbrindus.ca>
2021-07-11 10:15:12 +10:00
15 changed files with 406 additions and 213 deletions

View File

@@ -444,7 +444,7 @@ void waylandWarpPointer(int x, int y, bool exiting)
INTERLOCKED_SECTION(wlWm.confineLock, INTERLOCKED_SECTION(wlWm.confineLock,
{ {
if (!wlWm.lockedPointer) if (wlWm.lockedPointer)
{ {
LG_UNLOCK(wlWm.confineLock); LG_UNLOCK(wlWm.confineLock);
return; return;

View File

@@ -59,14 +59,12 @@ Required Dependencies
- libgl-dev - libgl-dev
- libfontconfig1-dev - libfontconfig1-dev
- libgmp-dev - libgmp-dev
- libsdl2-dev
- libsdl2-ttf-dev
- libspice-protocol-dev - libspice-protocol-dev
- make - make
- nettle-dev - nettle-dev
- pkg-config - pkg-config
.. _may_be_disabled: .. _client_deps_may_be_disabled:
May be disabled May be disabled
<<<<<<<<<<<<<<< <<<<<<<<<<<<<<<
@@ -83,6 +81,7 @@ feature is disabled when running :ref:`cmake <client_building>`.
- libx11-dev - libx11-dev
- libxfixes-dev - libxfixes-dev
- libxi-dev - libxi-dev
- libxinerama-dev
- libxss-dev - libxss-dev
- Disable with ``cmake -DENABLE_WAYLAND=no ..`` - Disable with ``cmake -DENABLE_WAYLAND=no ..``
@@ -91,11 +90,33 @@ feature is disabled when running :ref:`cmake <client_building>`.
- libwayland-dev - libwayland-dev
- wayland-protocols - wayland-protocols
You can fetch these dependencies on Debian systems with the following command: .. _client_deps_deprecated:
Deprecated
<<<<<<<<<<
These dependencies may be used, but are not required, and will be removed
in the future.
- Enable with ``cmake -DENABLE_SDL=yes ..``
- libsdl2-dev
- libsdl2-ttf-dev
.. _client_fetching_with_apt:
Fetching with APT
^^^^^^^^^^^^^^^^^
You can fetch these dependencies with the following command:
``apt-get install binutils-dev cmake fonts-freefont-ttf libfontconfig1-dev ``apt-get install binutils-dev cmake fonts-freefont-ttf libfontconfig1-dev
libsdl2-dev libsdl2-ttf-dev libspice-protocol-dev libx11-dev nettle-dev libegl-dev libspice-protocol-dev nettle-dev libx11-dev libxi-dev libxinerama-dev
wayland-protocols`` libxss-dev libwayland-dev wayland-protocols``
You may omit some dependencies, if you disable the feature which requires them
when running :ref:`cmake <client_building>`.
(See :ref:`client_deps_may_be_disabled`)
.. _client_building: .. _client_building:

View File

@@ -88,13 +88,20 @@ The mouse is jumpy, slow, laggy when using SPICE
Please be sure to install the SPICE guest tools from Please be sure to install the SPICE guest tools from
https://www.spice-space.org/download.html#windows-binaries. https://www.spice-space.org/download.html#windows-binaries.
.. _the_mouse_doesnt_stay_aligned_with_the_host.: .. _mouse_desync_when_entering:
The mouse doesn't stay aligned with the host. The mouse position is wrong when entering the window
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is intentional. The host's mouse no longer interacts with your operating This is due to windows mouse acceleration, it can be disabled by
system, and is completely captured by Looking Glass. following one of these methods:
- Disabling pointer precision (Control Panel > Mouse > Pointer Options
> Uncheck Enhance pointer precision)
- By changing the acceleration behavior with the following registry
magic:
http://donewmouseaccel.blogspot.com.au/2010/03/markc-windows-7-mouse-acceleration-fix.html
(Contrary to the title this works just fine on Windows 10)
.. _the_cursor_position_doesnt_update_until_i_click: .. _the_cursor_position_doesnt_update_until_i_click:

View File

@@ -91,9 +91,10 @@ typedef void (*CapturePostPointerBuffer)(CapturePointer pointer);
typedef struct CaptureInterface typedef struct CaptureInterface
{ {
const char *shortName; const char * shortName;
const char * (*getName )(); const bool asyncCapture;
void (*initOptions )(); const char * (*getName )();
void (*initOptions )();
bool(*create)( bool(*create)(
CaptureGetPointerBuffer getPointerBufferFn, CaptureGetPointerBuffer getPointerBufferFn,

View File

@@ -236,6 +236,7 @@ static CaptureResult xcb_getFrame(FrameBuffer * frame, const unsigned int height
struct CaptureInterface Capture_XCB = struct CaptureInterface Capture_XCB =
{ {
.shortName = "XCB", .shortName = "XCB",
.asyncCapture = true,
.getName = xcb_getName, .getName = xcb_getName,
.create = xcb_create, .create = xcb_create,
.init = xcb_init, .init = xcb_init,

View File

@@ -10,6 +10,7 @@ add_library(platform_Windows STATIC
src/service.c src/service.c
src/mousehook.c src/mousehook.c
src/force_compose.c src/force_compose.c
src/delay.c
) )
# allow use of functions for Windows Vista or later # allow use of functions for Windows Vista or later

View File

@@ -142,7 +142,7 @@ static void dxgi_initOptions(void)
{ {
.module = "dxgi", .module = "dxgi",
.name = "useAcquireLock", .name = "useAcquireLock",
.description = "Enable locking around `AcquireFrame` (EXPERIMENTAL, leave enabled if you're not sure!)", .description = "Enable locking around `AcquireNextFrame` (EXPERIMENTAL, leave enabled if you're not sure!)",
.type = OPTION_TYPE_BOOL, .type = OPTION_TYPE_BOOL,
.value.x_bool = true .value.x_bool = true
}, },
@@ -230,7 +230,7 @@ static bool dxgi_init(void)
const char * optAdapter = option_get_string("dxgi", "adapter"); const char * optAdapter = option_get_string("dxgi", "adapter");
const char * optOutput = option_get_string("dxgi", "output" ); const char * optOutput = option_get_string("dxgi", "output" );
for(int i = 0; IDXGIFactory1_EnumAdapters1(this->factory, i, &this->adapter) != DXGI_ERROR_NOT_FOUND; ++i) for (int i = 0; IDXGIFactory1_EnumAdapters1(this->factory, i, &this->adapter) != DXGI_ERROR_NOT_FOUND; ++i)
{ {
if (optAdapter) if (optAdapter)
{ {
@@ -259,7 +259,7 @@ static bool dxgi_init(void)
DEBUG_INFO("Adapter matched, trying: %ls", adapterDesc.Description); DEBUG_INFO("Adapter matched, trying: %ls", adapterDesc.Description);
} }
for(int n = 0; IDXGIAdapter1_EnumOutputs(this->adapter, n, &this->output) != DXGI_ERROR_NOT_FOUND; ++n) for (int n = 0; IDXGIAdapter1_EnumOutputs(this->adapter, n, &this->output) != DXGI_ERROR_NOT_FOUND; ++n)
{ {
IDXGIOutput_GetDesc(this->output, &outputDesc); IDXGIOutput_GetDesc(this->output, &outputDesc);
if (optOutput) if (optOutput)
@@ -302,6 +302,16 @@ static bool dxgi_init(void)
goto fail; goto fail;
} }
DXGI_ADAPTER_DESC1 adapterDesc;
IDXGIAdapter1_GetDesc1(this->adapter, &adapterDesc);
DEBUG_INFO("Device Name : %ls" , outputDesc.DeviceName);
DEBUG_INFO("Device Descripion: %ls" , adapterDesc.Description);
DEBUG_INFO("Device Vendor ID : 0x%x" , adapterDesc.VendorId);
DEBUG_INFO("Device Device ID : 0x%x" , adapterDesc.DeviceId);
DEBUG_INFO("Device Video Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedVideoMemory / 1048576));
DEBUG_INFO("Device Sys Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedSystemMemory / 1048576));
DEBUG_INFO("Shared Sys Mem : %u MiB" , (unsigned)(adapterDesc.SharedSystemMemory / 1048576));
static const D3D_FEATURE_LEVEL win8[] = static const D3D_FEATURE_LEVEL win8[] =
{ {
D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_1,
@@ -368,9 +378,6 @@ static bool dxgi_init(void)
goto fail; goto fail;
} }
DXGI_ADAPTER_DESC1 adapterDesc;
IDXGIAdapter1_GetDesc1(this->adapter, &adapterDesc);
switch(outputDesc.Rotation) switch(outputDesc.Rotation)
{ {
case DXGI_MODE_ROTATION_ROTATE90: case DXGI_MODE_ROTATION_ROTATE90:
@@ -411,12 +418,6 @@ static bool dxgi_init(void)
this->dpi = monitor_dpi(outputDesc.Monitor); this->dpi = monitor_dpi(outputDesc.Monitor);
++this->formatVer; ++this->formatVer;
DEBUG_INFO("Device Descripion: %ls" , adapterDesc.Description);
DEBUG_INFO("Device Vendor ID : 0x%x" , adapterDesc.VendorId);
DEBUG_INFO("Device Device ID : 0x%x" , adapterDesc.DeviceId);
DEBUG_INFO("Device Video Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedVideoMemory / 1048576));
DEBUG_INFO("Device Sys Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedSystemMemory / 1048576));
DEBUG_INFO("Shared Sys Mem : %u MiB" , (unsigned)(adapterDesc.SharedSystemMemory / 1048576));
DEBUG_INFO("Feature Level : 0x%x" , this->featureLevel); DEBUG_INFO("Feature Level : 0x%x" , this->featureLevel);
DEBUG_INFO("Capture Size : %u x %u", this->width, this->height); DEBUG_INFO("Capture Size : %u x %u", this->width, this->height);
DEBUG_INFO("AcquireLock : %s" , this->useAcquireLock ? "enabled" : "disabled"); DEBUG_INFO("AcquireLock : %s" , this->useAcquireLock ? "enabled" : "disabled");
@@ -542,7 +543,7 @@ static bool dxgi_init(void)
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
texDesc.MiscFlags = 0; texDesc.MiscFlags = 0;
for(int i = 0; i < this->maxTextures; ++i) for (int i = 0; i < this->maxTextures; ++i)
{ {
status = ID3D11Device_CreateTexture2D(this->device, &texDesc, NULL, &this->texture[i].tex); status = ID3D11Device_CreateTexture2D(this->device, &texDesc, NULL, &this->texture[i].tex);
if (FAILED(status)) if (FAILED(status))
@@ -583,7 +584,7 @@ static bool dxgi_deinit(void)
{ {
assert(this); assert(this);
for(int i = 0; i < this->maxTextures; ++i) for (int i = 0; i < this->maxTextures; ++i)
{ {
this->texture[i].state = TEXTURE_STATE_UNUSED; this->texture[i].state = TEXTURE_STATE_UNUSED;
@@ -764,7 +765,7 @@ static CaptureResult dxgi_capture(void)
uint32_t bufferSize; uint32_t bufferSize;
if (frameInfo.PointerShapeBufferSize > 0) if (frameInfo.PointerShapeBufferSize > 0)
{ {
if(!this->getPointerBufferFn(&pointerShape, &bufferSize)) if (!this->getPointerBufferFn(&pointerShape, &bufferSize))
DEBUG_WARN("Failed to obtain a buffer for the pointer shape"); DEBUG_WARN("Failed to obtain a buffer for the pointer shape");
else else
copyPointer = true; copyPointer = true;
@@ -881,13 +882,13 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame, const size_t maxFrameS
assert(this->initialized); assert(this->initialized);
// NOTE: the event may be signaled when there are no frames available // NOTE: the event may be signaled when there are no frames available
if(atomic_load_explicit(&this->texReady, memory_order_acquire) == 0) if (atomic_load_explicit(&this->texReady, memory_order_acquire) == 0)
{ {
if (!lgWaitEvent(this->frameEvent, 1000)) if (!lgWaitEvent(this->frameEvent, 1000))
return CAPTURE_RESULT_TIMEOUT; return CAPTURE_RESULT_TIMEOUT;
// the count will still be zero if we are stopping // the count will still be zero if we are stopping
if(atomic_load_explicit(&this->texReady, memory_order_acquire) == 0) if (atomic_load_explicit(&this->texReady, memory_order_acquire) == 0)
return CAPTURE_RESULT_TIMEOUT; return CAPTURE_RESULT_TIMEOUT;
} }
@@ -996,6 +997,7 @@ static CaptureResult dxgi_releaseFrame(void)
struct CaptureInterface Capture_DXGI = struct CaptureInterface Capture_DXGI =
{ {
.shortName = "DXGI", .shortName = "DXGI",
.asyncCapture = true,
.getName = dxgi_getName, .getName = dxgi_getName,
.initOptions = dxgi_initOptions, .initOptions = dxgi_initOptions,
.create = dxgi_create, .create = dxgi_create,

View File

@@ -57,7 +57,6 @@ struct iface
NvFBCFrameGrabInfo grabInfo; NvFBCFrameGrabInfo grabInfo;
LGEvent * frameEvent;
LGEvent * cursorEvent; LGEvent * cursorEvent;
int mouseX, mouseY, mouseHotX, mouseHotY; int mouseX, mouseY, mouseHotX, mouseHotY;
@@ -134,13 +133,6 @@ static bool nvfbc_create(
return false; return false;
this = (struct iface *)calloc(sizeof(struct iface), 1); this = (struct iface *)calloc(sizeof(struct iface), 1);
this->frameEvent = lgCreateEvent(true, 17);
if (!this->frameEvent)
{
DEBUG_ERROR("failed to create the frame event");
nvfbc_free();
return false;
}
this->seperateCursor = option_get_bool("nvfbc", "decoupleCursor"); this->seperateCursor = option_get_bool("nvfbc", "decoupleCursor");
this->getPointerBufferFn = getPointerBufferFn; this->getPointerBufferFn = getPointerBufferFn;
@@ -165,7 +157,7 @@ static bool nvfbc_init(void)
privDataLen = (bufferLen - 1) / 2; privDataLen = (bufferLen - 1) / 2;
privData = (uint8_t *)malloc(privDataLen); privData = (uint8_t *)malloc(privDataLen);
char hex[3] = {0}; char hex[3] = {0};
for(int i = 0; i < privDataLen; ++i) for (int i = 0; i < privDataLen; ++i)
{ {
memcpy(hex, &buffer[i*2], 2); memcpy(hex, &buffer[i*2], 2);
privData[i] = (uint8_t)strtoul(hex, NULL, 16); privData[i] = (uint8_t)strtoul(hex, NULL, 16);
@@ -186,7 +178,6 @@ static bool nvfbc_init(void)
free(privData); free(privData);
getDesktopSize(&this->width, &this->height, &this->dpi); getDesktopSize(&this->width, &this->height, &this->dpi);
lgResetEvent(this->frameEvent);
HANDLE event; HANDLE event;
if (!NvFBCToSysSetup( if (!NvFBCToSysSetup(
@@ -239,7 +230,6 @@ static void nvfbc_stop(void)
this->stop = true; this->stop = true;
lgSignalEvent(this->cursorEvent); lgSignalEvent(this->cursorEvent);
lgSignalEvent(this->frameEvent);
if (this->pointerThread) if (this->pointerThread)
{ {
@@ -262,9 +252,6 @@ static bool nvfbc_deinit(void)
static void nvfbc_free(void) static void nvfbc_free(void)
{ {
if (this->frameEvent)
lgFreeEvent(this->frameEvent);
if (this->mouseHookCreated) if (this->mouseHookCreated)
mouseHook_remove(); mouseHook_remove();
@@ -300,28 +287,25 @@ static CaptureResult nvfbc_capture(void)
bool changed = false; bool changed = false;
const unsigned int h = (this->height + 127) / 128; const unsigned int h = (this->height + 127) / 128;
const unsigned int w = (this->width + 127) / 128; const unsigned int w = (this->width + 127) / 128;
for(unsigned int y = 0; y < h; ++y) for (unsigned int y = 0; y < h; ++y)
for(unsigned int x = 0; x < w; ++x) for (unsigned int x = 0; x < w; ++x)
if (this->diffMap[(y*w)+x]) if (this->diffMap[(y*w)+x])
{ {
changed = true; changed = true;
break; goto done;
} }
done:
if (!changed) if (!changed)
return CAPTURE_RESULT_TIMEOUT; return CAPTURE_RESULT_TIMEOUT;
memcpy(&this->grabInfo, &grabInfo, sizeof(grabInfo)); memcpy(&this->grabInfo, &grabInfo, sizeof(grabInfo));
lgSignalEvent(this->frameEvent);
return CAPTURE_RESULT_OK; return CAPTURE_RESULT_OK;
} }
static CaptureResult nvfbc_waitFrame(CaptureFrame * frame, static CaptureResult nvfbc_waitFrame(CaptureFrame * frame,
const size_t maxFrameSize) const size_t maxFrameSize)
{ {
if (!lgWaitEvent(this->frameEvent, 1000))
return CAPTURE_RESULT_TIMEOUT;
if (this->stop) if (this->stop)
return CAPTURE_RESULT_REINIT; return CAPTURE_RESULT_REINIT;
@@ -423,6 +407,7 @@ static int pointerThread(void * unused)
struct CaptureInterface Capture_NVFBC = struct CaptureInterface Capture_NVFBC =
{ {
.shortName = "NvFBC", .shortName = "NvFBC",
.asyncCapture = false,
.getName = nvfbc_getName, .getName = nvfbc_getName,
.initOptions = nvfbc_initOptions, .initOptions = nvfbc_initOptions,

View File

@@ -253,6 +253,11 @@ CaptureResult NvFBCToSysCapture(
handle->retry = 0; handle->retry = 0;
break; break;
case NVFBC_ERROR_PROTECTED_CONTENT:
DEBUG_WARN("Protected content is playing, can't capture");
Sleep(100);
return CAPTURE_RESULT_TIMEOUT;
case NVFBC_ERROR_INVALID_PARAM: case NVFBC_ERROR_INVALID_PARAM:
if (handle->retry < 2) if (handle->retry < 2)
{ {

View File

@@ -0,0 +1,34 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 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.
*
* 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.h>
typedef NTSTATUS (__stdcall *ZwSetTimerResolution_t)(ULONG RequestedResolution,
BOOLEAN Set, PULONG ActualResolution);
extern ZwSetTimerResolution_t ZwSetTimerResolution;
typedef NTSTATUS (__stdcall *NtDelayExecution_t)(BOOL Alertable,
PLARGE_INTEGER DelayInterval);
extern NtDelayExecution_t NtDelayExecution;
void delayInit(void);
// like sleep but more accurate
void delayExecution(float ms);

View File

@@ -0,0 +1,49 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 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.
*
* 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/delay.h"
#include "common/debug.h"
NtDelayExecution_t NtDelayExecution;
ZwSetTimerResolution_t ZwSetTimerResolution;
void delayInit(void)
{
HMODULE ntdll = GetModuleHandle("ntdll.dll");
NtDelayExecution = (NtDelayExecution_t)
GetProcAddress(ntdll, "NtDelayExecution");
// Increase the timer resolution
ZwSetTimerResolution = (ZwSetTimerResolution_t)
GetProcAddress(ntdll, "ZwSetTimerResolution");
if (ZwSetTimerResolution)
{
ULONG actualResolution;
ZwSetTimerResolution(1, true, &actualResolution);
DEBUG_INFO("System timer resolution: %lu ns", actualResolution * 100);
}
}
void delayExecution(float ms)
{
LARGE_INTEGER interval = { .QuadPart = -1 * (int)(ms * 10000.0f) };
NtDelayExecution(FALSE, &interval);
}

View File

@@ -19,6 +19,7 @@
*/ */
#include "windows/mousehook.h" #include "windows/mousehook.h"
#include "windows/delay.h"
#include "common/windebug.h" #include "common/windebug.h"
#include "platform.h" #include "platform.h"
@@ -31,8 +32,8 @@ struct mouseHook
HHOOK hook; HHOOK hook;
MouseHookFn callback; MouseHookFn callback;
int x, y; int x, y;
HANDLE event; HANDLE event , updateEvent;
HANDLE thread; HANDLE thread, updateThread;
}; };
static struct mouseHook mouseHook = { 0 }; static struct mouseHook mouseHook = { 0 };
@@ -85,6 +86,31 @@ static VOID WINAPI winEventProc(HWINEVENTHOOK hWinEventHook, DWORD event,
} }
} }
static DWORD WINAPI updateThreadProc(LPVOID lParam)
{
HANDLE events[2] = {
mouseHook.event,
mouseHook.updateEvent
};
while(true)
{
switch(WaitForMultipleObjects(2, events, FALSE, INFINITE))
{
case WAIT_OBJECT_0:
DEBUG_INFO("Mouse hook update thread received quit request");
return 0;
case WAIT_OBJECT_0 + 1:
mouseHook.callback(mouseHook.x, mouseHook.y);
// limit this to 1000Hz, who has a mouse that updates faster anyway?
delayExecution(1.0f);
break;
}
}
}
static DWORD WINAPI threadProc(LPVOID lParam) { static DWORD WINAPI threadProc(LPVOID lParam) {
if (mouseHook.installed) if (mouseHook.installed)
{ {
@@ -143,11 +169,28 @@ void mouseHook_install(MouseHookFn callback)
mouseHook.event = CreateEventA(NULL, FALSE, FALSE, NULL); mouseHook.event = CreateEventA(NULL, FALSE, FALSE, NULL);
if (!mouseHook.event) if (!mouseHook.event)
{ {
DEBUG_WINERROR("Failed to create mouse hook uninstall event", GetLastError()); DEBUG_WINERROR("Failed to create mouse hook uninstall event",
GetLastError());
return; return;
} }
} }
mouseHook.thread = CreateThread(NULL, 0, threadProc, callback, 0, NULL);
if (!mouseHook.updateEvent)
{
mouseHook.updateEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
if (!mouseHook.event)
{
DEBUG_WINERROR("Failed to create mouse hook update event",
GetLastError());
return;
}
}
mouseHook.thread =
CreateThread(NULL, 0, threadProc, callback, 0, NULL);
mouseHook.updateThread =
CreateThread(NULL, 0, updateThreadProc, 0, 0, NULL);
} }
void mouseHook_remove(void) void mouseHook_remove(void)
@@ -156,8 +199,10 @@ void mouseHook_remove(void)
return; return;
SetEvent(mouseHook.event); SetEvent(mouseHook.event);
WaitForSingleObject(mouseHook.thread, INFINITE); WaitForSingleObject(mouseHook.thread , INFINITE);
WaitForSingleObject(mouseHook.updateThread, INFINITE);
CloseHandle(mouseHook.thread); CloseHandle(mouseHook.thread);
CloseHandle(mouseHook.updateThread);
} }
static LRESULT WINAPI mouseHook_hook(int nCode, WPARAM wParam, LPARAM lParam) static LRESULT WINAPI mouseHook_hook(int nCode, WPARAM wParam, LPARAM lParam)
@@ -169,7 +214,7 @@ static LRESULT WINAPI mouseHook_hook(int nCode, WPARAM wParam, LPARAM lParam)
{ {
mouseHook.x = msg->pt.x; mouseHook.x = msg->pt.x;
mouseHook.y = msg->pt.y; mouseHook.y = msg->pt.y;
mouseHook.callback(msg->pt.x, msg->pt.y); SetEvent(mouseHook.updateEvent);
} }
} }
return CallNextHookEx(mouseHook.hook, nCode, wParam, lParam); return CallNextHookEx(mouseHook.hook, nCode, wParam, lParam);

View File

@@ -20,6 +20,7 @@
#include "platform.h" #include "platform.h"
#include "service.h" #include "service.h"
#include "windows/delay.h"
#include "windows/mousehook.h" #include "windows/mousehook.h"
#include <windows.h> #include <windows.h>
@@ -61,10 +62,6 @@ struct AppState
static struct AppState app = {0}; static struct AppState app = {0};
HWND MessageHWND; HWND MessageHWND;
// undocumented API to adjust the system timer resolution (yes, its a nasty hack)
typedef NTSTATUS (__stdcall *ZwSetTimerResolution_t)(ULONG RequestedResolution, BOOLEAN Set, PULONG ActualResolution);
static ZwSetTimerResolution_t ZwSetTimerResolution = NULL;
// linux mingw64 is missing this // linux mingw64 is missing this
#ifndef MSGFLT_RESET #ifndef MSGFLT_RESET
#define MSGFLT_RESET (0) #define MSGFLT_RESET (0)
@@ -504,7 +501,7 @@ void boostPriority(void)
bool app_init(void) bool app_init(void)
{ {
const char * logFile = option_get_string("os", "logFile" ); const char * logFile = option_get_string("os", "logFile");
// redirect stderr to a file // redirect stderr to a file
if (logFile && strcmp(logFile, "stderr") != 0) if (logFile && strcmp(logFile, "stderr") != 0)
@@ -513,14 +510,7 @@ bool app_init(void)
// always flush stderr // always flush stderr
setbuf(stderr, NULL); setbuf(stderr, NULL);
// Increase the timer resolution delayInit();
ZwSetTimerResolution = (ZwSetTimerResolution_t)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetTimerResolution");
if (ZwSetTimerResolution)
{
ULONG actualResolution;
ZwSetTimerResolution(1, true, &actualResolution);
DEBUG_INFO("System timer resolution: %.2f ns", (float)actualResolution / 100.0f);
}
// get the performance frequency for spinlocks // get the performance frequency for spinlocks
QueryPerformanceFrequency(&app.perfFreq); QueryPerformanceFrequency(&app.perfFreq);

View File

@@ -79,17 +79,21 @@ struct app
PLGMPHost lgmp; PLGMPHost lgmp;
PLGMPHostQueue pointerQueue; PLGMPHostQueue pointerQueue;
PLGMPMemory pointerMemory[POINTER_SHAPE_BUFFERS]; PLGMPMemory pointerMemory[LGMP_Q_POINTER_LEN];
PLGMPMemory pointerShapeMemory[POINTER_SHAPE_BUFFERS];
LG_Lock pointerLock; LG_Lock pointerLock;
CapturePointer pointerInfo; CapturePointer pointerInfo;
PLGMPMemory pointerShape; PLGMPMemory pointerShape;
bool pointerShapeValid; bool pointerShapeValid;
unsigned int pointerIndex; unsigned int pointerIndex;
unsigned int pointerShapeIndex;
long pageSize;
size_t maxFrameSize; size_t maxFrameSize;
PLGMPHostQueue frameQueue; PLGMPHostQueue frameQueue;
PLGMPMemory frameMemory[LGMP_Q_FRAME_LEN]; PLGMPMemory frameMemory[LGMP_Q_FRAME_LEN];
unsigned int frameIndex; unsigned int frameIndex;
bool frameValid;
CaptureInterface * iface; CaptureInterface * iface;
@@ -138,118 +142,127 @@ static bool lgmpTimer(void * opaque)
return true; return true;
} }
static bool sendFrame(void)
{
CaptureFrame frame = { 0 };
bool repeatFrame = false;
//wait until there is room in the queue
while(app.state == APP_STATE_RUNNING &&
lgmpHostQueuePending(app.frameQueue) == LGMP_Q_FRAME_LEN)
{
usleep(1);
continue;
}
if (app.state != APP_STATE_RUNNING)
return false;
switch(app.iface->waitFrame(&frame, app.maxFrameSize))
{
case CAPTURE_RESULT_OK:
break;
case CAPTURE_RESULT_REINIT:
{
app.state = APP_STATE_RESTART;
DEBUG_INFO("Frame thread reinit");
return false;
}
case CAPTURE_RESULT_ERROR:
{
DEBUG_ERROR("Failed to get the frame");
return false;
}
case CAPTURE_RESULT_TIMEOUT:
{
if (app.frameValid && lgmpHostQueueNewSubs(app.frameQueue) > 0)
{
// resend the last frame
repeatFrame = true;
break;
}
return true;
}
}
LGMP_STATUS status;
// if we are repeating a frame just send the last frame again
if (repeatFrame)
{
if ((status = lgmpHostQueuePost(app.frameQueue, 0, app.frameMemory[app.frameIndex])) != LGMP_OK)
DEBUG_ERROR("%s", lgmpStatusString(status));
return true;
}
// we increment the index first so that if we need to repeat a frame
// the index still points to the latest valid frame
if (++app.frameIndex == LGMP_Q_FRAME_LEN)
app.frameIndex = 0;
KVMFRFrame * fi = lgmpHostMemPtr(app.frameMemory[app.frameIndex]);
switch(frame.format)
{
case CAPTURE_FMT_BGRA : fi->type = FRAME_TYPE_BGRA ; break;
case CAPTURE_FMT_RGBA : fi->type = FRAME_TYPE_RGBA ; break;
case CAPTURE_FMT_RGBA10 : fi->type = FRAME_TYPE_RGBA10 ; break;
case CAPTURE_FMT_RGBA16F: fi->type = FRAME_TYPE_RGBA16F; break;
default:
DEBUG_ERROR("Unsupported frame format %d, skipping frame", frame.format);
return true;
}
switch(frame.rotation)
{
case CAPTURE_ROT_0 : fi->rotation = FRAME_ROT_0 ; break;
case CAPTURE_ROT_90 : fi->rotation = FRAME_ROT_90 ; break;
case CAPTURE_ROT_180: fi->rotation = FRAME_ROT_180; break;
case CAPTURE_ROT_270: fi->rotation = FRAME_ROT_270; break;
default:
DEBUG_WARN("Unsupported frame rotation %d", frame.rotation);
fi->rotation = FRAME_ROT_0;
break;
}
fi->formatVer = frame.formatVer;
fi->width = frame.width;
fi->height = frame.height;
fi->realHeight = frame.realHeight;
fi->stride = frame.stride;
fi->pitch = frame.pitch;
fi->offset = app.pageSize - FrameBufferStructSize;
fi->mouseScalePercent = app.iface->getMouseScale();
fi->blockScreensaver = os_blockScreensaver();
app.frameValid = true;
// put the framebuffer on the border of the next page
// this is to allow for aligned DMA transfers by the receiver
FrameBuffer * fb = (FrameBuffer *)(((uint8_t*)fi) + fi->offset);
framebuffer_prepare(fb);
/* we post and then get the frame, this is intentional! */
if ((status = lgmpHostQueuePost(app.frameQueue, 0, app.frameMemory[app.frameIndex])) != LGMP_OK)
{
DEBUG_ERROR("%s", lgmpStatusString(status));
return true;
}
app.iface->getFrame(fb, frame.height);
return true;
}
static int frameThread(void * opaque) static int frameThread(void * opaque)
{ {
DEBUG_INFO("Frame thread started"); DEBUG_INFO("Frame thread started");
bool frameValid = false;
bool repeatFrame = false;
CaptureFrame frame = { 0 };
const long pageSize = sysinfo_getPageSize();
while(app.state == APP_STATE_RUNNING) while(app.state == APP_STATE_RUNNING)
{ {
//wait until there is room in the queue if (!sendFrame())
if(lgmpHostQueuePending(app.frameQueue) == LGMP_Q_FRAME_LEN) break;
{
usleep(1);
continue;
}
switch(app.iface->waitFrame(&frame, app.maxFrameSize))
{
case CAPTURE_RESULT_OK:
repeatFrame = false;
break;
case CAPTURE_RESULT_REINIT:
{
app.state = APP_STATE_RESTART;
DEBUG_INFO("Frame thread reinit");
return 0;
}
case CAPTURE_RESULT_ERROR:
{
DEBUG_ERROR("Failed to get the frame");
return 0;
}
case CAPTURE_RESULT_TIMEOUT:
{
if (frameValid && lgmpHostQueueNewSubs(app.frameQueue) > 0)
{
// resend the last frame
repeatFrame = true;
break;
}
continue;
}
}
LGMP_STATUS status;
// if we are repeating a frame just send the last frame again
if (repeatFrame)
{
if ((status = lgmpHostQueuePost(app.frameQueue, 0, app.frameMemory[app.frameIndex])) != LGMP_OK)
DEBUG_ERROR("%s", lgmpStatusString(status));
continue;
}
// we increment the index first so that if we need to repeat a frame
// the index still points to the latest valid frame
if (++app.frameIndex == LGMP_Q_FRAME_LEN)
app.frameIndex = 0;
KVMFRFrame * fi = lgmpHostMemPtr(app.frameMemory[app.frameIndex]);
switch(frame.format)
{
case CAPTURE_FMT_BGRA : fi->type = FRAME_TYPE_BGRA ; break;
case CAPTURE_FMT_RGBA : fi->type = FRAME_TYPE_RGBA ; break;
case CAPTURE_FMT_RGBA10 : fi->type = FRAME_TYPE_RGBA10 ; break;
case CAPTURE_FMT_RGBA16F: fi->type = FRAME_TYPE_RGBA16F; break;
default:
DEBUG_ERROR("Unsupported frame format %d, skipping frame", frame.format);
continue;
}
switch(frame.rotation)
{
case CAPTURE_ROT_0 : fi->rotation = FRAME_ROT_0 ; break;
case CAPTURE_ROT_90 : fi->rotation = FRAME_ROT_90 ; break;
case CAPTURE_ROT_180: fi->rotation = FRAME_ROT_180; break;
case CAPTURE_ROT_270: fi->rotation = FRAME_ROT_270; break;
default:
DEBUG_WARN("Unsupported frame rotation %d", frame.rotation);
fi->rotation = FRAME_ROT_0;
break;
}
fi->formatVer = frame.formatVer;
fi->width = frame.width;
fi->height = frame.height;
fi->realHeight = frame.realHeight;
fi->stride = frame.stride;
fi->pitch = frame.pitch;
fi->offset = pageSize - FrameBufferStructSize;
fi->mouseScalePercent = app.iface->getMouseScale();
fi->blockScreensaver = os_blockScreensaver();
frameValid = true;
// put the framebuffer on the border of the next page
// this is to allow for aligned DMA transfers by the receiver
FrameBuffer * fb = (FrameBuffer *)(((uint8_t*)fi) + fi->offset);
framebuffer_prepare(fb);
/* we post and then get the frame, this is intentional! */
if ((status = lgmpHostQueuePost(app.frameQueue, 0, app.frameMemory[app.frameIndex])) != LGMP_OK)
{
DEBUG_ERROR("%s", lgmpStatusString(status));
continue;
}
app.iface->getFrame(fb, frame.height);
} }
DEBUG_INFO("Frame thread stopped"); DEBUG_INFO("Frame thread stopped");
return 0; return 0;
@@ -258,6 +271,9 @@ static int frameThread(void * opaque)
bool startThreads(void) bool startThreads(void)
{ {
app.state = APP_STATE_RUNNING; app.state = APP_STATE_RUNNING;
if (!app.iface->asyncCapture)
return true;
if (!lgCreateThread("FrameThread", frameThread, NULL, &app.frameThread)) if (!lgCreateThread("FrameThread", frameThread, NULL, &app.frameThread))
{ {
DEBUG_ERROR("Failed to create the frame thread"); DEBUG_ERROR("Failed to create the frame thread");
@@ -269,20 +285,25 @@ bool startThreads(void)
bool stopThreads(void) bool stopThreads(void)
{ {
bool ok = true;
app.iface->stop(); app.iface->stop();
if (app.state != APP_STATE_SHUTDOWN) if (app.state != APP_STATE_SHUTDOWN)
app.state = APP_STATE_IDLE; app.state = APP_STATE_IDLE;
if (app.frameThread && !lgJoinThread(app.frameThread, NULL)) if (!app.iface->asyncCapture)
{ return true;
DEBUG_WARN("Failed to join the frame thread");
ok = false;
}
app.frameThread = NULL;
return ok; if (app.frameThread)
{
if (!lgJoinThread(app.frameThread, NULL))
{
DEBUG_WARN("Failed to join the frame thread");
app.frameThread = NULL;
return false;
}
app.frameThread = NULL;
}
return true;
} }
static bool captureStart(void) static bool captureStart(void)
@@ -315,8 +336,8 @@ static bool captureStop(void)
bool captureGetPointerBuffer(void ** data, uint32_t * size) bool captureGetPointerBuffer(void ** data, uint32_t * size)
{ {
PLGMPMemory mem = app.pointerMemory[app.pointerIndex]; PLGMPMemory mem = app.pointerShapeMemory[app.pointerShapeIndex];
*data = ((uint8_t*)lgmpHostMemPtr(mem)) + sizeof(KVMFRCursor); *data = (uint8_t*)lgmpHostMemPtr(mem) + sizeof(KVMFRCursor);
*size = MAX_POINTER_SIZE - sizeof(KVMFRCursor); *size = MAX_POINTER_SIZE - sizeof(KVMFRCursor);
return true; return true;
} }
@@ -342,8 +363,18 @@ static void sendPointer(bool newClient)
// new clients need the last known shape and current position // new clients need the last known shape and current position
if (newClient) if (newClient)
{ {
PLGMPMemory mem;
if (app.pointerShapeValid)
mem = app.pointerShape;
else
{
mem = app.pointerMemory[app.pointerIndex];
if (++app.pointerIndex == LGMP_Q_POINTER_LEN)
app.pointerIndex = 0;
}
// update the saved details with the current cursor position // update the saved details with the current cursor position
KVMFRCursor *cursor = lgmpHostMemPtr(app.pointerShape); KVMFRCursor *cursor = lgmpHostMemPtr(mem);
cursor->x = app.pointerInfo.x; cursor->x = app.pointerInfo.x;
cursor->y = app.pointerInfo.y; cursor->y = app.pointerInfo.y;
@@ -351,12 +382,24 @@ static void sendPointer(bool newClient)
(app.pointerShapeValid ? CURSOR_FLAG_SHAPE : 0) | (app.pointerShapeValid ? CURSOR_FLAG_SHAPE : 0) |
(app.pointerInfo.visible ? CURSOR_FLAG_VISIBLE : 0); (app.pointerInfo.visible ? CURSOR_FLAG_VISIBLE : 0);
postPointer(flags, app.pointerShape); postPointer(flags, mem);
return; return;
} }
uint32_t flags = 0; uint32_t flags = 0;
PLGMPMemory mem = app.pointerMemory[app.pointerIndex]; PLGMPMemory mem;
if (app.pointerInfo.shapeUpdate)
{
mem = app.pointerShapeMemory[app.pointerShapeIndex];
if (++app.pointerShapeIndex == POINTER_SHAPE_BUFFERS)
app.pointerShapeIndex = 0;
}
else
{
mem = app.pointerMemory[app.pointerIndex];
if (++app.pointerIndex == LGMP_Q_POINTER_LEN)
app.pointerIndex = 0;
}
KVMFRCursor *cursor = lgmpHostMemPtr(mem); KVMFRCursor *cursor = lgmpHostMemPtr(mem);
if (app.pointerInfo.positionUpdate || newClient) if (app.pointerInfo.positionUpdate || newClient)
@@ -390,16 +433,9 @@ static void sendPointer(bool newClient)
app.pointerShapeValid = true; app.pointerShapeValid = true;
flags |= CURSOR_FLAG_SHAPE; flags |= CURSOR_FLAG_SHAPE;
// retain this memory for new clients
app.pointerMemory[app.pointerIndex] = app.pointerShape;
app.pointerShape = mem; app.pointerShape = mem;
} }
// only advance if the pointer shape was not swapped out of the list
if ((flags & CURSOR_FLAG_SHAPE) == 0)
if (++app.pointerIndex == POINTER_SHAPE_BUFFERS)
app.pointerIndex = 0;
postPointer(flags, mem); postPointer(flags, mem);
} }
@@ -525,34 +561,41 @@ int app_main(int argc, char * argv[])
goto fail_lgmp; goto fail_lgmp;
} }
for(int i = 0; i < POINTER_SHAPE_BUFFERS; ++i) for(int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
{ {
if ((status = lgmpHostMemAlloc(app.lgmp, MAX_POINTER_SIZE, &app.pointerMemory[i])) != LGMP_OK) if ((status = lgmpHostMemAlloc(app.lgmp, sizeof(KVMFRCursor), &app.pointerMemory[i])) != LGMP_OK)
{ {
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer): %s", lgmpStatusString(status)); DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer): %s", lgmpStatusString(status));
exitcode = LG_HOST_EXIT_FATAL; exitcode = LG_HOST_EXIT_FATAL;
goto fail_lgmp; goto fail_lgmp;
} }
memset(lgmpHostMemPtr(app.pointerMemory[i]), 0, MAX_POINTER_SIZE); memset(lgmpHostMemPtr(app.pointerMemory[i]), 0, sizeof(KVMFRCursor));
} }
app.pointerShapeValid = false; for(int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
if ((status = lgmpHostMemAlloc(app.lgmp, MAX_POINTER_SIZE, &app.pointerShape)) != LGMP_OK)
{ {
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer Shape): %s", lgmpStatusString(status)); if ((status = lgmpHostMemAlloc(app.lgmp, MAX_POINTER_SIZE, &app.pointerShapeMemory[i])) != LGMP_OK)
exitcode = LG_HOST_EXIT_FATAL; {
goto fail_lgmp; DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer Shapes): %s", lgmpStatusString(status));
exitcode = LG_HOST_EXIT_FATAL;
goto fail_lgmp;
}
memset(lgmpHostMemPtr(app.pointerShapeMemory[i]), 0, MAX_POINTER_SIZE);
} }
const long sz = sysinfo_getPageSize(); app.pageSize = sysinfo_getPageSize();
app.frameValid = false;
app.pointerShapeValid = false;
app.maxFrameSize = lgmpHostMemAvail(app.lgmp); app.maxFrameSize = lgmpHostMemAvail(app.lgmp);
app.maxFrameSize = (app.maxFrameSize - (sz - 1)) & ~(sz - 1); app.maxFrameSize = (app.maxFrameSize - (app.pageSize - 1)) & ~(app.pageSize - 1);
app.maxFrameSize /= LGMP_Q_FRAME_LEN; app.maxFrameSize /= LGMP_Q_FRAME_LEN;
DEBUG_INFO("Max Frame Size : %u MiB", (unsigned int)(app.maxFrameSize / 1048576LL)); DEBUG_INFO("Max Frame Size : %u MiB", (unsigned int)(app.maxFrameSize / 1048576LL));
for(int i = 0; i < LGMP_Q_FRAME_LEN; ++i) for(int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
{ {
if ((status = lgmpHostMemAllocAligned(app.lgmp, app.maxFrameSize, sz, &app.frameMemory[i])) != LGMP_OK) if ((status = lgmpHostMemAllocAligned(app.lgmp, app.maxFrameSize,
app.pageSize, &app.frameMemory[i])) != LGMP_OK)
{ {
DEBUG_ERROR("lgmpHostMemAlloc Failed (Frame): %s", lgmpStatusString(status)); DEBUG_ERROR("lgmpHostMemAlloc Failed (Frame): %s", lgmpStatusString(status));
exitcode = LG_HOST_EXIT_FATAL; exitcode = LG_HOST_EXIT_FATAL;
@@ -594,6 +637,8 @@ int app_main(int argc, char * argv[])
} }
DEBUG_INFO("Using : %s", iface->getName()); DEBUG_INFO("Using : %s", iface->getName());
DEBUG_INFO("Capture Method : %s", iface->asyncCapture ?
"Asynchronous" : "Synchronous");
app.state = APP_STATE_RUNNING; app.state = APP_STATE_RUNNING;
app.iface = iface; app.iface = iface;
@@ -688,6 +733,9 @@ int app_main(int argc, char * argv[])
exitcode = LG_HOST_EXIT_FAILED; exitcode = LG_HOST_EXIT_FAILED;
goto fail_capture; goto fail_capture;
} }
if (!iface->asyncCapture)
sendFrame();
} }
if (app.state != APP_STATE_SHUTDOWN) if (app.state != APP_STATE_SHUTDOWN)
@@ -728,9 +776,10 @@ fail_timer:
fail_lgmp: fail_lgmp:
for(int i = 0; i < LGMP_Q_FRAME_LEN; ++i) for(int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
lgmpHostMemFree(&app.frameMemory[i]); lgmpHostMemFree(&app.frameMemory[i]);
for(int i = 0; i < POINTER_SHAPE_BUFFERS; ++i) for(int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
lgmpHostMemFree(&app.pointerMemory[i]); lgmpHostMemFree(&app.pointerMemory[i]);
lgmpHostMemFree(&app.pointerShape); for(int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
lgmpHostMemFree(&app.pointerShapeMemory[i]);
lgmpHostFree(&app.lgmp); lgmpHostFree(&app.lgmp);
fail_ivshmem: fail_ivshmem:

View File

@@ -22,6 +22,8 @@
#include <common/version.h> #include <common/version.h>
#include <stdio.h> #include <stdio.h>
#include "common/debug.h"
#ifdef _WIN32 #ifdef _WIN32
#undef EXPORT #undef EXPORT
#define EXPORT __declspec(dllexport) #define EXPORT __declspec(dllexport)
@@ -38,6 +40,7 @@ extern struct obs_source_info lg_source;
MODULE_EXPORT bool obs_module_load(void) MODULE_EXPORT bool obs_module_load(void)
{ {
debug_init();
printf("Looking Glass OBS Client (%s)\n", BUILD_VERSION); printf("Looking Glass OBS Client (%s)\n", BUILD_VERSION);
obs_register_source(&lg_source); obs_register_source(&lg_source);
return true; return true;