mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-12-16 21:18:13 +00:00
Compare commits
21 Commits
B4-rc2
...
Release/B4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25c88a1c6c | ||
|
|
7decb58bf7 | ||
|
|
d1ec19b30b | ||
|
|
74468cf799 | ||
|
|
411a6b1e49 | ||
|
|
e228165ff9 | ||
|
|
d615514799 | ||
|
|
ed717351cf | ||
|
|
4658244686 | ||
|
|
48ae5c69f4 | ||
|
|
4d065d577b | ||
|
|
789ee70674 | ||
|
|
3c0616bab7 | ||
|
|
3ce3b573a3 | ||
|
|
ce459c24ce | ||
|
|
7d0b9711bd | ||
|
|
e477663a7e | ||
|
|
eb01efe0cb | ||
|
|
8db4b65dee | ||
|
|
501b270890 | ||
|
|
fd8f8b2b28 |
@@ -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;
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
17
doc/faq.rst
17
doc/faq.rst
@@ -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:
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
34
host/platform/Windows/include/windows/delay.h
Normal file
34
host/platform/Windows/include/windows/delay.h
Normal 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);
|
||||||
49
host/platform/Windows/src/delay.c
Normal file
49
host/platform/Windows/src/delay.c
Normal 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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
331
host/src/app.c
331
host/src/app.c
@@ -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:
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user