When a new cursor shape is provided by the capture interface we need to
retain a copy of it incase a new client connects which will not yet have
the cursor shape. The logic here was flawed causing the wrong shape to
be sent to a new client in some instances.
This change adds an average function to time how long it takes the GPU
to copy and map the texture, and then uses this average to sleep for 80%
of this average lowering CPU usage and potentially decreasing lock
contention.
It has been detemined that a failure to init NvFBC causes a 20-30%
performance penalty on non NvFBC supported hardware (GeForce) when using
DXGI, as such reverse the order and default to using DXGI as our first
option.
If NvFBC is still desired, pr #500 added the option `app:capture` which
can be used to force NvFBC.
One of the most common issues reported in the support channels is the
IVSHMEM size being too small. This change adds a calculation to
determine an optimal size and uses the new `os_showMessage` platform
method to display a message box to the user with the error.
Since we now let the mouse hook linger until the process is killed, the
cursor event that the hook signals may now be null, as the capture could
have stopped. If the hook fires during this time, a crash occurs.
Instead of converting every SID to string with ConvertSidToStringSidA
and compare it with the magical SID string for local system with strcmp,
we could instead create the local system SID and compare directly with
EqualSid.
We don't actually have any handles that should be inherited, so specifying
TRUE for bInheritHandles to CreateProcessAsUserA is pointless.
Furthermore, according to MSDN, "[y]ou cannot inherit handles across
sessions," and we are spawning the host in a different session, so this
is even more pointless.
Instead of doing ShellExecute from the service, we instead get the token
of the currently logged in user, and do CreateProcessAsUserA to run
notepad with that token. This should be safe.
Also for failure to parse command line. For these errors, restarting
with exponential backoff will not help: no amount of restarting the
service could possibly make the ivshmem device exist or larger, so
we shouldn't try.
Certain users of Radeon cards have observed that the host fails to start
at boot, with D3D11CreateDevice failing with HSTATUS 0x887a0004, which
translates to "The specified device interface or feature level is not
supported on this system."
This failure results in a LG_HOST_EXIT_FAILED exit code, which the service
does not attempt to restart. The user has to manually restart the service
for the host application to work.
These users reported that the host application started fine on
B2. This strongly suggests that the fix to enable capturing the login
screen made the host application start too early during the boot process,
and the graphics driver did not have time to initialize fully.
This PR allows the service to retry a few times on LG_HOST_EXIT_FAILED,
with exponential backoff, before giving up. This should cover this bug
and other similar bugs related to the early initialization which I do not
have logs for.
This commit introduces a new option, app:capture, which can be set to
either DXGI or NvFBC to force the host application to use that backend.
This is very useful for testing DXGI on Quadro cards, which would default
to running with NvFBC.
This is needed for proper cleanup.
Freeing the capture interface also avoids a crash when using the NvFBC
backend. This is because we moved the mouse hook removal to nvfbc_free.
If nvfbc_free is not called before we start freeing LGMP memory, the
mouse hook would end up writing the cursor position into an invalid
memory address, causing an access violation.
This will allow us to add an option to disable the screensaver on the client
when an application in the guest requests it. This behaviour may be useful
when the guest is doing media playback.
The mouse hook code is very fragile, and we would like to avoid unhooking
and re-hooking as much as possible.
After this commit, this is done only once, and the hook and 1x1 window is
only destroyed upon exit. This, of course, comes with the downside of
the slight performance penalty if the guest machine is used directly while
the host is running and the client is not running.
Moving NvFBCToSysSetup to nvfbc_init means that when the pointer thread
fails to be created, NvFBCToSysRelease needs to be called.
To resolve such cleanup issues in the future, we instead call nvfbc_deinit,
which should cleanup everything that needs to be cleaned up. fails.
When NvFBCToSysCapture reports recreation is required, we return
CAPTURE_RESULT_REINIT, which eventually calls nvfbc_deinit and then
nvfbc_init.
However, the NvFBC object is actually created in nvfbc_create, which
means the NvFBC object is never actually recreated. The result is an
endless cycle of NvFBC asking for recreation. This commonly manifests
as the client waiting endlessly for the host when the guest machine
reboots.
In this commit, the NvFBC object creation is moved into nvfbc_init,
and when recreation is required, it will actually be recreated.
mouseHook_install and dwmForceComposition both create threads, but these
are only freed in nvfbc_deinit which is not called if nvfbc_init fails.
These should be freed if the pointer thread fails to be created, as
nothing else could be cleaning it up.
Instead of using %windir%\Temp, which is not accessible by default and
contains a lot of unrelated files, as the location for our log files,
this commit moves it to %ProgramData%\Looking Glass (host), which will
be a dedicated directory just for the LG host log files. This applies
to both the host application logs and the service logs.
Also, we now switched to using PathCombineA from shlwapi.dll instead
of using snprintf, which greatly simplifies the code. PathCombineA
guarantees that the path would not overflow a buffer of MAX_PATH.
Before this commit, the NvFBC backend only generated the first cursor
position update when the mouse moves. Therefore, if the user does
not move the mouse, the cursor will be shown at (0, 0), which is not
ideal.
This commit changes this behaviour to unconditionally generate a
cursor update when the mouse hook initializes.
Nehalem is the minimum requirement for the host application as it makes
use of SSE4.1 instructions, as such we should default to compling with
it instead of `-march=native` so that when the binary is distributed it
will operate on foreign systems.
Fixed#416
Before this change, the log is buffered, so if the host application exits
for any reason, it usually would not show up in the log file immediately,
and the service has to be restarted for the logs to be flushed.
This commit disables the buffering so that any log entries shows up
immediately.
This is because we keep track of the top-left corner of the cursor, not
the location of the hotspot. When the cursor shape changes, the hotspot
location may also change. When it does, the position of the top-left
corner changes and requires an update.
In the case that we do not have the current cursor position, which
happens on startup, we do not generate this update.
NvFBC is unable to capture certain applications that bypasses the DWM
compositor, for example, Firefox playing video in full screen. This
has been a known issue for a long time with Nvidia's ShadowPlay, see:
* https://www.nvidia.com/en-us/geforce/forums/geforce-experience/14/233709/
* https://crbug.com/609857
Nvidia won't fix this, but there are workarounds. For example, we
create a transparent 1x1 layered window, which forces desktop composition
to be enabled.
Note that SetLayeredWindowAttributes also supports alpha-based transparency,
but setting transparency to 0 will cause DWM to skip composition. We could
use a transparency of 1, but this ruins the image by the slightest bit,
which is unacceptable. Therefore, we must use chroma key-based
transparency, which tricks DWM into compositing despite being fully
transparent.
Basically, this creates a separate thread for the mouse events, and this
thread detects that the desktop has changed (say to the secure desktop),
and unhooks, switches to the new desktop, and then rehooks.
This allows the cursor location to be updated while using NvFBC on secure
desktop and the login screen.
WTSGetActiveConsoleSessionId will return a session even if it's not logged in,
unlike our old GetInteractiveSessionID function. Launching looking glass on
such a console session will allow the login screen to be captured.
Note that WTSGetActiveConsoleSessionId() will return 0xFFFFFFFF if there are
no sessions attached.
To quote MSDN documentation:
> The lpApplicationName parameter can be NULL, in which case the executable
> name must be the first white space–delimited string in lpCommandLine. If
> the executable or path name has a space in it, there is a risk that a
> different executable could be run because of the way the function parses
> spaces. The following example is dangerous because the function will
> attempt to run "Program.exe", if it exists, instead of "MyApp.exe".
>
> LPTSTR szCmdline[] = _tcsdup(TEXT("C:\\Program Files\\MyApp"));
> CreateProcessAsUser(hToken, NULL, szCmdline, /*...*/ );
>
> If a malicious user were to create an application called "Program.exe" on
> a system, any program that incorrectly calls CreateProcessAsUser using the
> Program Files directory will run this application instead of the intended
> application.
>
> To avoid this problem, do not pass NULL for lpApplicationName.
So instead, we pass the executable to lpApplicationName instead, which avoids
the issue. MSDN says:
> The lpCommandLine parameter can be NULL. In that case, the function uses
> the string pointed to by lpApplicationName as the command line.
This also avoids the strdup since lpApplicationName is LPCSTR unlike
lpCommandLine which is LPSTR.
It shouldn't have any effect, since the host application is created with
the token, and there is no need for the service itself to impersonate.
In practice, removal doesn't appear to have any effect on the ability to
capture privileged things like secure desktop.
Use the process handle returned by CreateProcessAsUserA to wait on the
process. This results in faster response times and less polling.
For example, it now restarts instantly when UAC is activated.
This also removes the call to OpenProcess and rendering the mutex unnecessary.
As a bonus, it should fix#298.
The host process will be changed to return these codes, from which the
service process could decide whether to exit or restart the process and log.
Note that on Windows, return values are 32-bit unlike POSIX which is only 8.
This makes it a compile-time error to call a function that semantically
takes no parameters with a nonzero number of arguments.
Previously, such code would still compile, but risk blowing up the stack
if a compiler chose to use something other than caller-cleanup calling
conventions.
There is no need to allocate a buffer for each message as the client is
only required to show the latest version of the cursor. Whie the logic
should prevent cursor corruption, it's not guaranteed, however this is
not a problem as this can only happen if the client is lagging behind
and as such when it gets another update message it will re-read the
now new shape anyway.
This commit bumps the KVMFR protocol version as it adds additional
hotspot x & y fields to the KVMFRCursor struct. This corrects the issue
of invalid alignment of the local mouse when the shape has an offset
such as the 'I' beam.
Now that it's recommended to run LG as the `SYSTEM` user, launching an
application to read the log file is dangerous as it will be launched
with the same access rights (`SYSTEM`). Instead so as Microsoft
recommends and only present a message box with the information.
Experimental, use at your own peril!
This commit adds the ability for the LG host to install and launch with
Windows as a system service.
To install simply run `looking-glass-host.exe InstallService` or
conversely to uninstall `looking-glass-host.exe UninstallService`.
SafeRelease was really useless, derefencing the smart pointers through
the use of & releases the value before SafeRelease get's to it. Instead
either allow the destructor to handle it's release, or explicityly
release it by assigning NULL
Contrary to the MS documentation, benchmarking shows a substantial
increase in performance when releasing the captured frame as soon
as possible. This change makes it possible to achieve 60FPS at 4K
resolutions.
Pass -DUNICODE as a CFLAG. The visual studio project asks for
a unicode (wide-string) build, but the unix makefile did not.
This fixes the build on msys2.
* [host] rename min() to LG_MIN()
* [host] format string type fixes, %Ix doesn't exist in mingw
* [host] DXGI minor fixes
* [host] mingw lacks media foundation api headers and QISearch
Writing to shared memory is much faster then reading as the shared
memory is not cached, this change ensures we are using a local copy
of the header performing the final update all in one go.
This will help frame delivery to the host arrive on time, as well as
allow lower latency with a higher host refresh rate.
Data offset addresses are now also pre-calculated rather then
updated on every frame.
NvFBC will wait forever for a change if nothing has changed, instead use
a timeout so that if the guest has re-connected it doesn't stall waiting
for a guest update.
For this to function correctly the display scaling in the nvidia control
panel must be set to "No scaling", failure to do so will cause incorrect
window cropping. This is due to the inability to capture a non-scaled
image using NvFBC.
DXGI doesn't like to restart too fast, com exceptions are logged and the
duplication device fails to create with an E_ACCESS_DENIED error. Adding
a 200ms sleep between teardown and re-init resolves this issue.
Note: This class will be entirely re-written when I find some time,
it is very poorly implemented, full of assumptions and creates a new
texture for every single frame 🤦.