Compare commits

..

21 Commits

Author SHA1 Message Date
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
Jonathan Rubenstein
7083b73720 [doc] faq: B4-rc2 touchups 2021-07-10 13:13:23 +10:00
Jonathan Rubenstein
511adbba68 [doc] module: dkms example has explicit "."
This works in dash, so should be portable to most shells.
Will hopefully help users not misinterpret the period as a mistake.
2021-07-10 12:43:10 +10:00
Geoffrey McRae
78b8e2a73c [host] windows: make D3DKMTSetProcessSchedulingPriorityClass global
Testing shows that `D3DKMTSetProcessSchedulingPriorityClass` has a
positive performance impact for NvFBC as well as DXGI, as such always
try to boost the priority for the windows host.
2021-07-10 12:27:30 +10:00
Geoffrey McRae
041b95507d [host] windows/nvfbc/common: strip out broken "enhanced" event logic
This so called "enhanced" event logic is completely flawed and can never
work correctly, better to strip it out and put our faith in windows to
handle the events for us.

And yes, I am fully aware I wrote the utter trash in the first place :)
2021-07-09 10:22:03 +10:00
Geoffrey McRae
bfb47a0ae4 [common] windows: update event fast path to use atomics
Due to a failure to understand atomics when this code was originally
written it has a critical flaw with the fast path where an event could
be signalled when it should not be. This change set corrects this issue
by using atomic operations.
2021-07-09 04:50:14 +10:00
Geoffrey McRae
59efa6f0ad [client] config: default minimizeOnFocusLoss to off 2021-07-08 08:49:00 +10:00
Geoffrey McRae
0acce0737f [common] debug: output to stdout and reset the the color at the end 2021-07-08 07:18:06 +10:00
Jonathan Rubenstein
e96a80dd20 [doc] faq: Correct log file tray operation 2021-07-07 23:19:54 +10:00
Jonathan Rubenstein
c626385845 [doc] index: Nuke trello and add ethereum address 2021-07-07 23:19:54 +10:00
Geoffrey McRae
54d0dc351b [common] debug: remove missed debug_print forward 2021-07-07 23:08:47 +10:00
Geoffrey McRae
1effd5fddc [common] debug: add color support to debug messages
People often miss the warnings about invalid arguments in their command
line, this last minute patch attempts to address this by making
warnings, errors, fixme's and fatal errors stand out if stdout is a TTY.
2021-07-07 23:05:46 +10:00
20 changed files with 460 additions and 446 deletions

View File

@@ -198,7 +198,7 @@ static struct Option options[] =
.name = "minimizeOnFocusLoss",
.description = "Minimize window on focus loss",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
.value.x_bool = false,
},
{
.module = "win",

View File

@@ -1052,6 +1052,9 @@ static void lg_shutdown(void)
int main(int argc, char * argv[])
{
// initialize for DEBUG_* macros
debug_init();
if (getuid() == 0)
{
DEBUG_ERROR("Do not run looking glass as root!");

View File

@@ -30,6 +30,20 @@
#include <inttypes.h>
#include "time.h"
enum DebugLevel
{
DEBUG_LEVEL_NONE,
DEBUG_LEVEL_INFO,
DEBUG_LEVEL_WARN,
DEBUG_LEVEL_ERROR,
DEBUG_LEVEL_FIXME,
DEBUG_LEVEL_FATAL
};
extern const char ** debug_lookup;
void debug_init(void);
#ifdef ENABLE_BACKTRACE
void printBacktrace(void);
#define DEBUG_PRINT_BACKTRACE() printBacktrace()
@@ -65,19 +79,20 @@ void printBacktrace(void);
sizeof(s) > 20 && (s)[sizeof(s)-21] == DIRECTORY_SEPARATOR ? (s) + sizeof(s) - 20 : \
sizeof(s) > 21 && (s)[sizeof(s)-22] == DIRECTORY_SEPARATOR ? (s) + sizeof(s) - 21 : (s))
#define DEBUG_PRINT(type, fmt, ...) do { \
fprintf(stderr, "%12" PRId64 " " type " %20s:%-4u | %-30s | " fmt "\n", \
microtime(), STRIPPATH(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__);\
#define DEBUG_PRINT(level, fmt, ...) do { \
fprintf(stderr, "%s%12" PRId64 "%20s:%-4u | %-30s | " fmt "%s\n", \
debug_lookup[level], microtime(), STRIPPATH(__FILE__), \
__LINE__, __FUNCTION__, ##__VA_ARGS__, debug_lookup[DEBUG_LEVEL_NONE]); \
} while (0)
#define DEBUG_BREAK() DEBUG_PRINT("[ ]", "================================================================================")
#define DEBUG_INFO(fmt, ...) DEBUG_PRINT("[I]", fmt, ##__VA_ARGS__)
#define DEBUG_WARN(fmt, ...) DEBUG_PRINT("[W]", fmt, ##__VA_ARGS__)
#define DEBUG_ERROR(fmt, ...) DEBUG_PRINT("[E]", fmt, ##__VA_ARGS__)
#define DEBUG_FIXME(fmt, ...) DEBUG_PRINT("[F]", fmt, ##__VA_ARGS__)
#define DEBUG_BREAK() DEBUG_PRINT(DEBUG_LEVEL_INFO, "================================================================================")
#define DEBUG_INFO(fmt, ...) DEBUG_PRINT(DEBUG_LEVEL_INFO, fmt, ##__VA_ARGS__)
#define DEBUG_WARN(fmt, ...) DEBUG_PRINT(DEBUG_LEVEL_WARN, fmt, ##__VA_ARGS__)
#define DEBUG_ERROR(fmt, ...) DEBUG_PRINT(DEBUG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
#define DEBUG_FIXME(fmt, ...) DEBUG_PRINT(DEBUG_LEVEL_FIXME, fmt, ##__VA_ARGS__)
#define DEBUG_FATAL(fmt, ...) do { \
DEBUG_BREAK(); \
DEBUG_PRINT("[!]", fmt, ##__VA_ARGS__); \
DEBUG_PRINT(DEBUG_LEVEL_FATAL, fmt, ##__VA_ARGS__); \
DEBUG_PRINT_BACKTRACE(); \
abort(); \
} while(0)

View File

@@ -31,7 +31,6 @@ typedef struct LGEvent LGEvent;
LGEvent * lgCreateEvent(bool autoReset, unsigned int msSpinTime);
void lgFreeEvent (LGEvent * handle);
bool lgWaitEvent (LGEvent * handle, unsigned int timeout);
bool lgWaitEvents (LGEvent * handles[], int count, bool waitAll, unsigned int timeout);
bool lgSignalEvent(LGEvent * handle);
bool lgResetEvent (LGEvent * handle);

View File

@@ -7,6 +7,7 @@ include_directories(
)
add_library(lg_common_platform_code STATIC
debug.c
crash.c
sysinfo.c
thread.c

View File

@@ -0,0 +1,56 @@
/**
* 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 "common/debug.h"
#include <unistd.h>
#define COLOR_RESET "\033[0m"
#define COLOR_YELLOW "\033[0;33m"
#define COLOR_RED "\033[0;31m"
#define COLOR_CYAN "\033[0;36m"
#define COLOR_WHITE "\033[0;37m"
const char ** debug_lookup = NULL;
void debug_init(void)
{
static const char * colorLookup[] =
{
COLOR_RESET , // DEBUG_LEVEL_NONE
COLOR_RESET "[I] ", // DEBUG_LEVEL_INFO
COLOR_YELLOW "[W] ", // DEBUG_LEVEL_WARN
COLOR_RED "[E] ", // DEBUG_LEVEL_ERROR
COLOR_CYAN "[F] ", // DEBUG_LEVEL_FIXME
COLOR_WHITE "[!] " // DEBUG_LEVEL_FATAL
};
static const char * plainLookup[] =
{
"" , // DEBUG_LEVEL_NONE
"[I] ", // DEBUG_LEVEL_INFO
"[W] ", // DEBUG_LEVEL_WARN
"[E] ", // DEBUG_LEVEL_ERROR
"[F] ", // DEBUG_LEVEL_FIXME
"[!] " // DEBUG_LEVEL_FATAL
};
debug_lookup = (isatty(STDERR_FILENO) == 1) ? colorLookup : plainLookup;
}

View File

@@ -6,6 +6,7 @@ include_directories(
)
add_library(lg_common_platform_code STATIC
debug.c
crash.c
dpi.c
sysinfo.c

View File

@@ -0,0 +1,38 @@
/**
* 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 "common/debug.h"
const char ** debug_lookup = NULL;
void debug_init(void)
{
static const char * plainLookup[] =
{
"" , // DEBUG_LEVEL_NONE
"[I] ", // DEBUG_LEVEL_INFO
"[W] ", // DEBUG_LEVEL_WARN
"[E] ", // DEBUG_LEVEL_ERROR
"[F] ", // DEBUG_LEVEL_FIXME
"[!] " // DEBUG_LEVEL_FATAL
};
debug_lookup = plainLookup;
}

View File

@@ -23,126 +23,38 @@
#include "common/time.h"
#include <windows.h>
struct LGEvent
{
volatile int lock;
bool reset;
HANDLE handle;
bool wrapped;
unsigned int msSpinTime;
volatile bool signaled;
};
#include <stdatomic.h>
LGEvent * lgCreateEvent(bool autoReset, unsigned int msSpinTime)
{
LGEvent * event = (LGEvent *)malloc(sizeof(LGEvent));
if (!event)
{
DEBUG_ERROR("out of ram");
return NULL;
}
event->lock = 0;
event->reset = autoReset;
event->handle = CreateEvent(NULL, autoReset ? FALSE : TRUE, FALSE, NULL);
event->wrapped = false;
event->msSpinTime = msSpinTime;
event->signaled = false;
if (!event->handle)
HANDLE handle = CreateEvent(NULL, autoReset ? FALSE : TRUE, FALSE, NULL);
if (!handle)
{
DEBUG_WINERROR("Failed to create the event", GetLastError());
free(event);
return NULL;
}
return event;
return (LGEvent *)handle;
}
LGEvent * lgWrapEvent(void * handle)
{
LGEvent * event = (LGEvent *)malloc(sizeof(LGEvent));
if (!event)
{
DEBUG_ERROR("out of ram");
return NULL;
}
event->lock = 0;
event->reset = false;
event->handle = (HANDLE)handle;
event->wrapped = true;
event->msSpinTime = 0;
event->signaled = false;
return event;
return (LGEvent *)handle;
}
void lgFreeEvent(LGEvent * event)
{
CloseHandle(event->handle);
CloseHandle((HANDLE)event);
}
bool lgWaitEvent(LGEvent * event, unsigned int timeout)
{
// wrapped events can't be enahnced
if (!event->wrapped)
{
if (event->signaled)
{
if (event->reset)
event->signaled = false;
return true;
}
if (timeout == 0)
{
bool ret = event->signaled;
if (event->reset)
event->signaled = false;
return ret;
}
if (event->msSpinTime)
{
unsigned int spinTime = event->msSpinTime;
if (timeout != TIMEOUT_INFINITE)
{
if (timeout > event->msSpinTime)
timeout -= event->msSpinTime;
else
{
spinTime -= timeout;
timeout = 0;
}
}
uint64_t now = microtime();
uint64_t end = now + spinTime * 1000;
while(!event->signaled)
{
now = microtime();
if (now >= end)
break;
}
if (event->signaled)
{
if (event->reset)
event->signaled = false;
return true;
}
}
}
const DWORD to = (timeout == TIMEOUT_INFINITE) ? INFINITE : (DWORD)timeout;
while(true)
do
{
switch(WaitForSingleObject(event->handle, to))
switch(WaitForSingleObject((HANDLE)event, to))
{
case WAIT_OBJECT_0:
if (!event->reset)
event->signaled = true;
return true;
case WAIT_ABANDONED:
@@ -151,7 +63,6 @@ bool lgWaitEvent(LGEvent * event, unsigned int timeout)
case WAIT_TIMEOUT:
if (timeout == TIMEOUT_INFINITE)
continue;
return false;
case WAIT_FAILED:
@@ -160,63 +71,18 @@ bool lgWaitEvent(LGEvent * event, unsigned int timeout)
}
DEBUG_ERROR("Unknown wait event return code");
return false;
}
}
while(0);
bool lgWaitEvents(LGEvent * events[], int count, bool waitAll, unsigned int timeout)
{
const DWORD to = (timeout == TIMEOUT_INFINITE) ? INFINITE : (DWORD)timeout;
HANDLE * handles = (HANDLE *)malloc(sizeof(HANDLE) * count);
for(int i = 0; i < count; ++i)
handles[i] = events[i]->handle;
while(true)
{
DWORD result = WaitForMultipleObjects(count, handles, waitAll, to);
if (result >= WAIT_OBJECT_0 && result < count)
{
// null non signaled events from the handle list
for(int i = 0; i < count; ++i)
if (i != result && !lgWaitEvent(events[i], 0))
events[i] = NULL;
free(handles);
return true;
}
if (result >= WAIT_ABANDONED_0 && result - WAIT_ABANDONED_0 < count)
continue;
switch(result)
{
case WAIT_TIMEOUT:
if (timeout == TIMEOUT_INFINITE)
continue;
free(handles);
return false;
case WAIT_FAILED:
DEBUG_WINERROR("Wait for event failed", GetLastError());
free(handles);
return false;
}
DEBUG_ERROR("Unknown wait event return code");
free(handles);
return false;
}
return false;
}
bool lgSignalEvent(LGEvent * event)
{
event->signaled = true;
return SetEvent(event->handle);
return SetEvent((HANDLE)event);
}
bool lgResetEvent(LGEvent * event)
{
event->signaled = false;
return ResetEvent(event->handle);
return ResetEvent((HANDLE)event);
}

View File

@@ -31,13 +31,6 @@ There are several reasons why this can happen, the most common are your
capture resolution, or refresh rate. The windows capture methods currently
struggle to capture high resolutions under certain circumstances.
Another cause can be how the game or application you are running is
configured. Because of the way windows integrate with the WDM (Windows
Desktop Manager), running applications in "Full Screen" mode may—in some
cases—cause a large performance penalty. Try switching to windowed
full-screen mode, the difference in performance can be like night and
day.
Some titles do some strange things at early initialization that cause
capture performance issues. One such title is the Unigine Valley
benchmark where the capture rate is limited to 1/2 the actual rate. For
@@ -137,6 +130,16 @@ NvFBC (NVIDIA Capture API) doesn't work
NvFBC is only supported on professional-grade GPUs, and will not function on
consumer-grade cards like those from the GeForce series.
If you have a supported card, you can enable NVFBC by adding the following
to the host ini file, found at
``%ProgramFiles%\Looking Glass (host)\looking-glass-host.ini``
(create one if it doesn't exist):
.. code:: INI
[app]
capture=nvfbc
.. _the_screen_stops_updating_when_left_idle_for_a_time:
The screen stops updating when left idle for a time
@@ -157,8 +160,9 @@ The log file for the host application is located at::
%ProgramData%\Looking Glass (host)\looking-glass-host.txt
You can also find out where the file is by right clicking on the tray
icon and selecting "Log File Location".
You can also open the log file by right clicking on the Looking Glass
system tray icon, then clicking *Open Log File*. This opens the log
file in Notepad.
The log file for the looking glass service is located at::
@@ -204,8 +208,9 @@ This is intentional for several reasons.
position as NVIDIA decided to not provide this as part of the cursor
updates.
2. NvFBC requires administrator level access to enable the interface in
the first place. (WIP)
3. DXGI performance can be improved if we have this. (WIP)
the first place.
3. General capture performance is boosted by taking advantage of high priority
scheduling with SYSTEM level privileges.
NvFBC (NVIDIA Frame Buffer Capture)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -216,8 +221,6 @@ Why can't I compile NvFBC support into the host?
You must download and install the NVidia Capture SDK. Please note that
by doing so you will be agreeing to NVIDIA's SDK License agreement.
*-Geoff*
.. _a_note_about_ivshmem_and_scream_audio:
Why doesn't Looking Glass work with Scream over IVSHMEM?
@@ -235,9 +238,10 @@ a highly optimized memory copy anyway so there is no need to make another one.
If you insist on using IVSHMEM for Scream—despite its inferiority to the
default network implementation—the Windows Host Application can be told
what device to use. Create a ``looking-glass-host.ini`` file in the same
directory as the looking-glass-host.exe file. In it, you can use the
``os:shmDevice`` option like so:
what device to use. Edit the file
``%ProgramFiles%\Looking Glass (host)\looking-glass-host.ini``,
(create one if it doesn't exist)
then, you can add the ``os:shmDevice`` option like so:
.. code:: INI

View File

@@ -31,7 +31,6 @@ More information:
* `Forum <https://forum.level1techs.com/c/software/lookingglass/142>`_
* `Community wiki <https://looking-glass.io/wiki/>`_
* IRC - #LookingGlass on `libera.chat <https://libera.chat/>`_
* `Trello <https://trello.com/b/tI1Xbwsg/looking-glass>`_
Donate:
@@ -40,3 +39,4 @@ Donate:
* `Patreon <https://www.patreon.com/gnif>`_
* `PayPal <https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ESQ72XUPGKXRY>`_
* BTC - 14ZFcYjsKPiVreHqcaekvHGL846u3ZuT13
* ETH - 0x6f8aEe454384122bF9ed28f025FBCe2Bce98db85

View File

@@ -76,7 +76,7 @@ To install the module into DKMS, run
.. code:: bash
dkms install .
dkms install "."
.. _module_dkms_loading:

View File

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

View File

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

View File

@@ -41,6 +41,9 @@ struct app app = { 0 };
int main(int argc, char * argv[])
{
// initialize for DEBUG_* macros
debug_init();
app.executable = argv[0];
struct passwd * pw = getpwuid(getuid());

View File

@@ -37,18 +37,6 @@
#include "dxgi_extra.h"
typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS
{
D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE,
D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL,
D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL,
D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL,
D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH,
D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME
}
D3DKMT_SCHEDULINGPRIORITYCLASS;
typedef NTSTATUS WINAPI (*PD3DKMTSetProcessSchedulingPriorityClass)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS);
#define LOCKED(x) INTERLOCKED_SECTION(this->deviceContextLock, x)
enum TextureState
@@ -154,7 +142,7 @@ static void dxgi_initOptions(void)
{
.module = "dxgi",
.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,
.value.x_bool = true
},
@@ -242,7 +230,7 @@ static bool dxgi_init(void)
const char * optAdapter = option_get_string("dxgi", "adapter");
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)
{
@@ -271,7 +259,7 @@ static bool dxgi_init(void)
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);
if (optOutput)
@@ -314,6 +302,16 @@ static bool dxgi_init(void)
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[] =
{
D3D_FEATURE_LEVEL_11_1,
@@ -380,9 +378,6 @@ static bool dxgi_init(void)
goto fail;
}
DXGI_ADAPTER_DESC1 adapterDesc;
IDXGIAdapter1_GetDesc1(this->adapter, &adapterDesc);
switch(outputDesc.Rotation)
{
case DXGI_MODE_ROTATION_ROTATE90:
@@ -423,49 +418,10 @@ static bool dxgi_init(void)
this->dpi = monitor_dpi(outputDesc.Monitor);
++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("Capture Size : %u x %u", this->width, this->height);
DEBUG_INFO("AcquireLock : %s" , this->useAcquireLock ? "enabled" : "disabled");
// bump up our priority
{
HMODULE gdi32 = GetModuleHandleA("GDI32");
if (gdi32)
{
PD3DKMTSetProcessSchedulingPriorityClass fn =
(PD3DKMTSetProcessSchedulingPriorityClass)GetProcAddress(gdi32, "D3DKMTSetProcessSchedulingPriorityClass");
if (fn)
{
status = fn(GetCurrentProcess(), D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME);
if (FAILED(status))
{
DEBUG_WARN("Failed to set realtime GPU priority.");
DEBUG_INFO("This is not a failure, please do not report this as an issue.");
DEBUG_INFO("To fix this, install and run the Looking Glass host as a service.");
DEBUG_INFO("looking-glass-host.exe InstallService");
}
}
}
IDXGIDevice * dxgi;
status = ID3D11Device_QueryInterface(this->device, &IID_IDXGIDevice, (void **)&dxgi);
if (FAILED(status))
{
DEBUG_WINERROR("failed to query DXGI interface from device", status);
goto fail;
}
IDXGIDevice_SetGPUThreadPriority(dxgi, 7);
IDXGIDevice_Release(dxgi);
}
// try to reduce the latency
{
IDXGIDevice1 * dxgi;
@@ -587,7 +543,7 @@ static bool dxgi_init(void)
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
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);
if (FAILED(status))
@@ -628,7 +584,7 @@ static bool dxgi_deinit(void)
{
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;
@@ -809,7 +765,7 @@ static CaptureResult dxgi_capture(void)
uint32_t bufferSize;
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");
else
copyPointer = true;
@@ -926,13 +882,13 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame, const size_t maxFrameS
assert(this->initialized);
// 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))
return CAPTURE_RESULT_TIMEOUT;
// 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;
}
@@ -1041,6 +997,7 @@ static CaptureResult dxgi_releaseFrame(void)
struct CaptureInterface Capture_DXGI =
{
.shortName = "DXGI",
.asyncCapture = true,
.getName = dxgi_getName,
.initOptions = dxgi_initOptions,
.create = dxgi_create,

View File

@@ -57,8 +57,7 @@ struct iface
NvFBCFrameGrabInfo grabInfo;
LGEvent * frameEvent;
LGEvent * cursorEvents[2];
LGEvent * cursorEvent;
int mouseX, mouseY, mouseHotX, mouseHotY;
bool mouseVisible, hasMousePosition;
@@ -92,8 +91,16 @@ static void on_mouseMove(int x, int y)
this->hasMousePosition = true;
this->mouseX = x;
this->mouseY = y;
if (this->cursorEvents[0])
lgSignalEvent(this->cursorEvents[0]);
const CapturePointer pointer =
{
.positionUpdate = true,
.visible = this->mouseVisible,
.x = x - this->mouseHotX,
.y = y - this->mouseHotY
};
this->postPointerBufferFn(pointer);
}
static const char * nvfbc_getName(void)
@@ -126,13 +133,6 @@ static bool nvfbc_create(
return false;
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->getPointerBufferFn = getPointerBufferFn;
@@ -157,7 +157,7 @@ static bool nvfbc_init(void)
privDataLen = (bufferLen - 1) / 2;
privData = (uint8_t *)malloc(privDataLen);
char hex[3] = {0};
for(int i = 0; i < privDataLen; ++i)
for (int i = 0; i < privDataLen; ++i)
{
memcpy(hex, &buffer[i*2], 2);
privData[i] = (uint8_t)strtoul(hex, NULL, 16);
@@ -178,7 +178,6 @@ static bool nvfbc_init(void)
free(privData);
getDesktopSize(&this->width, &this->height, &this->dpi);
lgResetEvent(this->frameEvent);
HANDLE event;
if (!NvFBCToSysSetup(
@@ -196,10 +195,8 @@ static bool nvfbc_init(void)
return false;
}
this->cursorEvents[0] = lgCreateEvent(true, 10);
if (this->seperateCursor)
this->cursorEvents[1] = lgWrapEvent(event);
this->cursorEvent = lgWrapEvent(event);
if (!this->mouseHookCreated)
{
@@ -232,8 +229,7 @@ static void nvfbc_stop(void)
{
this->stop = true;
lgSignalEvent(this->cursorEvents[0]);
lgSignalEvent(this->frameEvent);
lgSignalEvent(this->cursorEvent);
if (this->pointerThread)
{
@@ -244,12 +240,7 @@ static void nvfbc_stop(void)
static bool nvfbc_deinit(void)
{
if (this->cursorEvents[0])
{
lgFreeEvent(this->cursorEvents[0]);
this->cursorEvents[0] = NULL;
}
this->cursorEvent = NULL;
if (this->nvfbc)
{
NvFBCToSysRelease(&this->nvfbc);
@@ -261,9 +252,6 @@ static bool nvfbc_deinit(void)
static void nvfbc_free(void)
{
if (this->frameEvent)
lgFreeEvent(this->frameEvent);
if (this->mouseHookCreated)
mouseHook_remove();
@@ -299,28 +287,25 @@ static CaptureResult nvfbc_capture(void)
bool changed = false;
const unsigned int h = (this->height + 127) / 128;
const unsigned int w = (this->width + 127) / 128;
for(unsigned int y = 0; y < h; ++y)
for(unsigned int x = 0; x < w; ++x)
for (unsigned int y = 0; y < h; ++y)
for (unsigned int x = 0; x < w; ++x)
if (this->diffMap[(y*w)+x])
{
changed = true;
break;
goto done;
}
done:
if (!changed)
return CAPTURE_RESULT_TIMEOUT;
memcpy(&this->grabInfo, &grabInfo, sizeof(grabInfo));
lgSignalEvent(this->frameEvent);
return CAPTURE_RESULT_OK;
}
static CaptureResult nvfbc_waitFrame(CaptureFrame * frame,
const size_t maxFrameSize)
{
if (!lgWaitEvent(this->frameEvent, 1000))
return CAPTURE_RESULT_TIMEOUT;
if (this->stop)
return CAPTURE_RESULT_REINIT;
@@ -378,13 +363,9 @@ static CaptureResult nvfbc_getFrame(FrameBuffer * frame,
static int pointerThread(void * unused)
{
lgSignalEvent(this->cursorEvents[1]);
while(!this->stop)
{
LGEvent * events[2];
memcpy(&events, &this->cursorEvents, sizeof(LGEvent *) * 2);
if (!lgWaitEvents(events, this->seperateCursor ? 2 : 1, false, 1000))
if (!lgWaitEvent(this->cursorEvent, 1000))
continue;
if (this->stop)
@@ -392,39 +373,31 @@ static int pointerThread(void * unused)
CaptureResult result;
CapturePointer pointer = { 0 };
bool hotspotUpdated = false;
if (this->seperateCursor && events[1])
void * data;
uint32_t size;
if (!this->getPointerBufferFn(&data, &size))
{
void * data;
uint32_t size;
if (!this->getPointerBufferFn(&data, &size))
{
DEBUG_WARN("failed to get a pointer buffer");
continue;
}
result = NvFBCToSysGetCursor(this->nvfbc, &pointer, data, size);
if (result != CAPTURE_RESULT_OK)
{
DEBUG_WARN("NvFBCToSysGetCursor failed");
continue;
}
this->mouseVisible = pointer.visible;
this->mouseHotX = pointer.hx;
this->mouseHotY = pointer.hy;
hotspotUpdated = true;
DEBUG_WARN("failed to get a pointer buffer");
continue;
}
if (events[0] || (hotspotUpdated && this->hasMousePosition))
result = NvFBCToSysGetCursor(this->nvfbc, &pointer, data, size);
if (result != CAPTURE_RESULT_OK)
{
pointer.positionUpdate = true;
pointer.visible = this->mouseVisible;
pointer.x = this->mouseX - this->mouseHotX;
pointer.y = this->mouseY - this->mouseHotY;
DEBUG_WARN("NvFBCToSysGetCursor failed");
continue;
}
this->mouseVisible = pointer.visible;
this->mouseHotX = pointer.hx;
this->mouseHotY = pointer.hy;
pointer.positionUpdate = true;
pointer.visible = this->mouseVisible;
pointer.x = this->mouseX - pointer.hx;
pointer.y = this->mouseY - pointer.hy;
this->postPointerBufferFn(pointer);
}
@@ -434,6 +407,7 @@ static int pointerThread(void * unused)
struct CaptureInterface Capture_NVFBC =
{
.shortName = "NvFBC",
.asyncCapture = false,
.getName = nvfbc_getName,
.initOptions = nvfbc_initOptions,

View File

@@ -253,6 +253,11 @@ CaptureResult NvFBCToSysCapture(
handle->retry = 0;
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:
if (handle->retry < 2)
{

View File

@@ -314,6 +314,9 @@ fail:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// initialize for DEBUG_* macros
debug_init();
// convert the command line to the standard argc and argv
LPWSTR * wargv = CommandLineToArgvW(GetCommandLineW(), &app.argc);
app.argv = malloc(sizeof(char *) * app.argc);
@@ -464,6 +467,41 @@ finish:
return result;
}
void boostPriority(void)
{
typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS
{
D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE,
D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL,
D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL,
D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL,
D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH,
D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME
}
D3DKMT_SCHEDULINGPRIORITYCLASS;
typedef NTSTATUS WINAPI (*PD3DKMTSetProcessSchedulingPriorityClass)
(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS);
HMODULE gdi32 = GetModuleHandleA("GDI32");
if (!gdi32)
return;
PD3DKMTSetProcessSchedulingPriorityClass fn =
(PD3DKMTSetProcessSchedulingPriorityClass)
GetProcAddress(gdi32, "D3DKMTSetProcessSchedulingPriorityClass");
if (!fn)
return;
if (FAILED(fn(GetCurrentProcess(), D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME)))
{
DEBUG_WARN("Failed to set realtime GPU priority.");
DEBUG_INFO("This is not a failure, please do not report this as an issue.");
DEBUG_INFO("To fix this, install and run the Looking Glass host as a service.");
DEBUG_INFO("looking-glass-host.exe InstallService");
}
}
bool app_init(void)
{
const char * logFile = option_get_string("os", "logFile" );
@@ -487,6 +525,9 @@ bool app_init(void)
// get the performance frequency for spinlocks
QueryPerformanceFrequency(&app.perfFreq);
// try to boost the scheduler priority
boostPriority();
return true;
}

View File

@@ -79,17 +79,21 @@ struct app
PLGMPHost lgmp;
PLGMPHostQueue pointerQueue;
PLGMPMemory pointerMemory[POINTER_SHAPE_BUFFERS];
PLGMPMemory pointerMemory[LGMP_Q_POINTER_LEN];
PLGMPMemory pointerShapeMemory[POINTER_SHAPE_BUFFERS];
LG_Lock pointerLock;
CapturePointer pointerInfo;
PLGMPMemory pointerShape;
bool pointerShapeValid;
unsigned int pointerIndex;
unsigned int pointerShapeIndex;
long pageSize;
size_t maxFrameSize;
PLGMPHostQueue frameQueue;
PLGMPMemory frameMemory[LGMP_Q_FRAME_LEN];
unsigned int frameIndex;
bool frameValid;
CaptureInterface * iface;
@@ -138,118 +142,127 @@ static bool lgmpTimer(void * opaque)
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)
{
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)
{
//wait until there is room in the queue
if(lgmpHostQueuePending(app.frameQueue) == LGMP_Q_FRAME_LEN)
{
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);
if (!sendFrame())
break;
}
DEBUG_INFO("Frame thread stopped");
return 0;
@@ -258,6 +271,9 @@ static int frameThread(void * opaque)
bool startThreads(void)
{
app.state = APP_STATE_RUNNING;
if (!app.iface->asyncCapture)
return true;
if (!lgCreateThread("FrameThread", frameThread, NULL, &app.frameThread))
{
DEBUG_ERROR("Failed to create the frame thread");
@@ -269,20 +285,25 @@ bool startThreads(void)
bool stopThreads(void)
{
bool ok = true;
app.iface->stop();
if (app.state != APP_STATE_SHUTDOWN)
app.state = APP_STATE_IDLE;
if (app.frameThread && !lgJoinThread(app.frameThread, NULL))
{
DEBUG_WARN("Failed to join the frame thread");
ok = false;
}
app.frameThread = NULL;
if (!app.iface->asyncCapture)
return true;
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)
@@ -315,8 +336,8 @@ static bool captureStop(void)
bool captureGetPointerBuffer(void ** data, uint32_t * size)
{
PLGMPMemory mem = app.pointerMemory[app.pointerIndex];
*data = ((uint8_t*)lgmpHostMemPtr(mem)) + sizeof(KVMFRCursor);
PLGMPMemory mem = app.pointerShapeMemory[app.pointerShapeIndex];
*data = (uint8_t*)lgmpHostMemPtr(mem) + sizeof(KVMFRCursor);
*size = MAX_POINTER_SIZE - sizeof(KVMFRCursor);
return true;
}
@@ -342,8 +363,18 @@ static void sendPointer(bool newClient)
// new clients need the last known shape and current position
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
KVMFRCursor *cursor = lgmpHostMemPtr(app.pointerShape);
KVMFRCursor *cursor = lgmpHostMemPtr(mem);
cursor->x = app.pointerInfo.x;
cursor->y = app.pointerInfo.y;
@@ -351,12 +382,24 @@ static void sendPointer(bool newClient)
(app.pointerShapeValid ? CURSOR_FLAG_SHAPE : 0) |
(app.pointerInfo.visible ? CURSOR_FLAG_VISIBLE : 0);
postPointer(flags, app.pointerShape);
postPointer(flags, mem);
return;
}
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);
if (app.pointerInfo.positionUpdate || newClient)
@@ -390,16 +433,9 @@ static void sendPointer(bool newClient)
app.pointerShapeValid = true;
flags |= CURSOR_FLAG_SHAPE;
// retain this memory for new clients
app.pointerMemory[app.pointerIndex] = app.pointerShape;
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);
}
@@ -525,34 +561,41 @@ int app_main(int argc, char * argv[])
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));
exitcode = LG_HOST_EXIT_FATAL;
goto fail_lgmp;
}
memset(lgmpHostMemPtr(app.pointerMemory[i]), 0, MAX_POINTER_SIZE);
memset(lgmpHostMemPtr(app.pointerMemory[i]), 0, sizeof(KVMFRCursor));
}
app.pointerShapeValid = false;
if ((status = lgmpHostMemAlloc(app.lgmp, MAX_POINTER_SIZE, &app.pointerShape)) != LGMP_OK)
for(int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
{
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer Shape): %s", lgmpStatusString(status));
exitcode = LG_HOST_EXIT_FATAL;
goto fail_lgmp;
if ((status = lgmpHostMemAlloc(app.lgmp, MAX_POINTER_SIZE, &app.pointerShapeMemory[i])) != LGMP_OK)
{
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 = (app.maxFrameSize - (sz - 1)) & ~(sz - 1);
app.maxFrameSize = (app.maxFrameSize - (app.pageSize - 1)) & ~(app.pageSize - 1);
app.maxFrameSize /= LGMP_Q_FRAME_LEN;
DEBUG_INFO("Max Frame Size : %u MiB", (unsigned int)(app.maxFrameSize / 1048576LL));
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));
exitcode = LG_HOST_EXIT_FATAL;
@@ -594,6 +637,8 @@ int app_main(int argc, char * argv[])
}
DEBUG_INFO("Using : %s", iface->getName());
DEBUG_INFO("Capture Method : %s", iface->asyncCapture ?
"Asynchronous" : "Synchronous");
app.state = APP_STATE_RUNNING;
app.iface = iface;
@@ -688,6 +733,9 @@ int app_main(int argc, char * argv[])
exitcode = LG_HOST_EXIT_FAILED;
goto fail_capture;
}
if (!iface->asyncCapture)
sendFrame();
}
if (app.state != APP_STATE_SHUTDOWN)
@@ -728,9 +776,10 @@ fail_timer:
fail_lgmp:
for(int i = 0; i < LGMP_Q_FRAME_LEN; ++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.pointerShape);
for(int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
lgmpHostMemFree(&app.pointerShapeMemory[i]);
lgmpHostFree(&app.lgmp);
fail_ivshmem: