X11 needs to calibrate to get the best possible latency, as such it
needs the scene to render so that the render time of the scene can be
accounted for in the delay calculation.
The way things were handled in EGLTexture is not only very hard to
follow, but broken. This change set breaks up EGLTexture into a modular
design making it easier to implement the various versions.
Note that DMABUF is currently broken and needs to be re-implemented.
Without configuring Wayland compositors to send frame callbacks as late as
possible, JIT rendering can increase latency by more than one frame.
For example, by default, sway asks applications to render right after a
vblank, and does its own composition right after a vblank, resulting in
~2 frame's worth of latency. If max_render_time is set on the output,
it composes that many milliseconds before the vblank, losing ~1 frame's
worth of latency. If max_render_time is set on the window also, the frame
callback is sent that many milliseconds before composition, and we achieve
perfectly low latency.
Therefore, out of the box, JIT rendering should not be enabled, as manual
compositor configuration is required for optimal results.
For reference, the following sway settings results in the best latency:
output <insert output name> max_render_time 1
for_window [app_id="looking-glass-client"] max_render_time 1
This reverts commit 3baed05728.
When requested, JIT render mode will be used if the display server supports it.
Otherwise, a warning is generated instead.
This essentially uses the signalNextFrame logic for imgui, but for everything.
We automatically enable this mode when overlay is on.
Currently, this exposes some damage tracking bugs in the EGL renderer.
We look for the client config in $XDG_CONFIG_HOME/looking-glass/client.ini.
This is done because it's more conventional, and also allows us to add
additional configuration files, e.g. for the host.
We fallback to $HOME/.config as is standard, and then as a last resort use
getpwuid(getuid())->pw_dir. This is also recommended by the getpwuid manpage:
> An application that wants to determine its user's home directory should
> inspect the value of HOME (rather than the value getpwuid(getuid())->pw_dir)
> since this allows the user to modify their notion of "the home directory"
> during a login session.
Currently, we load /etc/looking-glass-client.ini and/or
~/.config/looking-glass-client.ini as long as they exist, even if they are
not files. We should only load them if they are files.
We don't want to encourage craziness of people making the client suid to
bypass permission issues on the shm file.
Note: I see no evidence of this happening in the wild, but let's be
proactive.
The refresh-copyright script now automatically updates the copyright string
embedded in config.c. In order to achieve this, refresh-copyright gained the
ability to reflow text as the situation needs.
We now give ImGui the true logical size of the window and tell it to scale
the framebuffer. To fix the blurry fonts, we continue to load fonts at the
scale necessary for the DPI and use FontGlobalScale to shrink the fonts back
to the logical size. The font rectangle is then expanded by the framebuffer
scaling, resulting in good text rendering.
This method has the advantage of not messing up the sizes of resizable
overlays when moving across monitors.
The default of [0, 50] makes sense for FPS/UPS graphs, but does not for
things like the import graph. The latter should not take more than 5 ms
for sure.
This commit allows the min/max y-axis value to be specified when registering
the graph.
Now that we are drawing with damage rects, when the window is hidden and
then exposed the window may not get fully redrawn. This provides
`app_invalidateWindow` for the display server backend to call when the
screen needs a full redraw.
When a new client connects to our session the host will repeat the last
valid frame for the new client. This change will detect this and skip
the duplicated frame.
This is necessary in case overlays change size. When this happens, we must
damage the larger of the overlays' rectangles this frame and last frame.
This erases the overlay from where it is no longer appears.
In order to do this, we must keep track of the rectangles for every overlay
with no exception. We cannot short-circuit the generation of rectangles if
we run out of buffer space, and we must allocate space for MAX_OVERLAY_RECTS
rectangles for every frame. Otherwise, we will not know where to erase the
overlay if it disappears.
While the renderer can internally track this it would be better to
simply provide this information to the renderer directly so it can make
better decisions on how best to update the screen.
If the guest is not sending frames at a constant rate, the minimum FPS
timeout may expire drawing an additional frame. This change calculates
the average ups frame time over the past 100ms and adds this to the
timeout value allowing this value to be dynamic.
The accumulated time is not the best way to do this as the timer
function callback may not be exactly every 1000ms, by using the
monotonic clock we will get more accurate results.
This commit creates a new utility library, eglutil.h, which contains code
to detect and use EGL_KHR_swap_buffers_with_damage or its EXT equivalent.
This logic used to be duplicated between the X11 and Wayland display servers,
which is not ideal.
The renderer may take time to process the cursor update due to various
internal factors, as such it's best we copy the data and mark the
message as done ASAP. This prevents the host from filling up the queue
as easily when a high dpi mouse is in use.
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.
Conversion from the float values srcW/srcH to the int values for the client window dimensions would sometimes round down, causing the client to scale instead of matching the host's resolution.
A resolution switch could cause the renderer state to become invalid as
the texture format may change while it's being rendered. This fixes this
by adding a lock around the format change and render calls to the
renderer.
This fixes a regression caused by the move from SDL2 which handled this
itself. We should only minimize when focus is lost if the application
was in full screen mode.
util_guestCurToLocal may not be able to provide the local position if
we do not yet know where the guest cursor is, or the destination render
rect dimensions. Acting on this when this information is unknown causes
undefined behaivour.
Instead of damaging the entire surface when rendering a cursor move,
we can use the EGL_KHR_swap_buffers_with_damage extension to only
damage the part of the window covered by the cursor. This should
reduce the cursor movement latency on Wayland.
We previously used strstr, which can be prone to false positives when
the name of one extension is a substring of another extension.
This commit creates the helper function util_hasGLExt, which asserts
that the substring found in extension list is bounded by either spaces
or the beginning/end of the string.
Using util_cursorToInt messes with the error tracking for normal movements,
and is not necessary since we are computing an absolute position on the
client window.
Instead, we should pass doubles directly to display servers and let them
decide how to best handle them. For example, XIWarpPointer accepts doubles
directly.
Currently, (un)grabPointer is used both for tracking/confining the mouse
in normal mode, as well as entering/exiting capture mode. This makes it
impossible to use separate cursor logic for capture mode, which is needed
to deal with overlapping windows for the Wayland backend.
This commit creates separate (un)capturePointer for entering/exiting
capture mode. There should be no behaviour changes.
This adds a new method to the display server interface to allow the
application to notify the ds when there is a guest cursor position
update along with the translated local guest cursor position. This makes
it possible for the display server to keep the local cursor position in
sync with the guest cursor so that window leave events can be detected
when the cursor would move into an overlapping window.
Wayland currently just has a stub for this, and the X11 implementation
still needs some minor tweaking.
This option controls the time period (in ms) after which the help menu
appears when holding down the escape key. After this time period,
capture mode is no longer toggled.
This fixes#527.
When users press escapeKey for a long time, they probably want to
see the help text instead of actually toggling capture. Therefore,
if the key is held down for more than 500 ms, we assume the user
wants to look at the help text and do not toggle capture mode.
500 ms seems to be a decent compromise, allowing slow presses, but
is not enough time for the user to have looked at the help text.
During the refactor/rebase period with B3-next the conditional was
accidentally reversed. This would cause the cursor to be ungrabbed
simply when toggling capture mode instead of waiting for the cursor to
exit the window.
Mouse move deltas greater then 10 are rare, let alone the 20 this code
now uses. Any movements that exceed 20 pixels will disable the exit
detection code path preventing rapid movements in FPV games from causing
the cursor to exit the window if autoCapture is enabled.
As we now are using our own backends instead of SDL, there is no longer
any need to warp back to the center of the window when in autoCapture
mode. This breaks the SDL ds backend behaviour, however as SDL is
planned to be removed this is not an issue.
This is enabled on default. Specify wayland:warpSupport=no to disable it,
which may be useful on certain compositors that do not warp when the
pointer is confined.
This commit converts the output of ds->getProp(LG_DS_WARP_SUPPORT) to
an enum containing three items:
* LG_DS_WARP_NONE: warp is not supported at all
* LG_DS_WARP_SURFACE: warp is possible, but only inside the window
* LG_DS_WARP_SCREEN: warp is possible anywhere on the screen
LG_DS_WARP_NONE corresponds to the old false return value, and
LG_DS_WARP_SCREEN corresponds to the old true return value.
LG_DS_WARP_SURFACE is designed for Wayland, where warping is possible,
but only in our window. In this case, since we cannot warp outside
the window, we can warp the cursor to the edge when we attempt to exit.
If the cursor leaves, the normal leave routine gets called, and the
cursor disappears. If the cursor does not end up leaving, we grab it
again.
This makes dealing with window manager shortcuts that overlap with guest
keys more pleasant, while retaining the previous functionality for users
who prefer it.
For instance, previously, using Alt+Tab (or $mod as Alt in i3/sway
movement commands) would result in the guest retaining Alt as pressed.
When the guest regained focus, it would continue thinking Alt is
pressed, leading to accidentally triggering obscure shortcuts. One had
to remember to press Alt again to "unstick" things, which was
suboptimal.
This commit adds a new option, win:autoScreensaver, which when set to yes,
automatically disables the screensaver when requested by an application
running in the guest, and enables it when the application no longer wants
it disabled.
This is useful when doing media playback in the guest.
It appears that the keyboard should only be grabbed if the client is
focused and the cursor is in the view. However, the relevant logic was
missing from core_setCursorInView, and the keyboard was never actually
grabbed.
This commit adds the call to g_state.ds->grabKeyboard(), allowing grabbing
to work.
Before, if you want to see the FPS, you need to close the client and
restart it with the -k switch to see the FPS. This is annoying.
This PR introduces a new keybind, ScrollLock+D, which, when pressed,
toggles the display of the FPS.
This is implemented for both EGL and OpenGL backends.
One of the major issues with the old tracking code is a data race
between the cursor thread updating g_cursor.guest and the
app_handleMouseBasic function. Specifically, the latter may have
sent mouse input via spice that has not been processed by the guest
and updated g_cursor.guest, but the guest may overwrite g_cursor.guest
to a previous state before the input is processed. This causes some
movements to be doubled. Eventually, the cursor positions will
synchronize, but this nevertheless causes a lot of jitter.
In this commit, we introduce a new field g_cursor.projected, which
is unambiguously the position of the cursor after taking into account
all the input already sent via spice. This is synced up to the guest
cursor upon entering the window and when the host restarts. Afterwards,
all mouse movements will be based on this position. This eliminates
all cursor jitter as far as I could tell.
Also, the cursor is now synced to the host position when exiting
capture mode.
A downside of this commit is that if the 1:1 movement patch is not
correctly applied, the cursor position would be wildly off instead
of simply jittering, but that is an unsupported configuration and
should not matter.
Also unsupported is when an application in guest moves the cursor
programmatically and bypassing spice. When using those applications,
capture mode must be on. Before this commit, we try to move the guest
cursor back to where it should be, but it's inherently fragile and
may lead to scenarios such as wild movements in first-person shooters.
Using a macro ENABLE_OPENGL just like ENABLE_EGL to optionally remove
OpenGL implementation code. This is mostly because on Wayland it's just
a rehash of the EGL code (as EGL is the only way to create OpenGL
contexts on Wayland).
As the window manager may change our mode to full screen without our
request we must ask the ds backend for the current state when we want to
toggle the mode.
When input:grabKeyboardOnFocus=no, exiting capture mode should ungrab
the keyboard. Otherwise, focusing the window doesn't grab the keyboard,
but toggling capture mode would leave the keyboard stuck in a grabbed
state until defocused.
While a compositor will never send us 0-delta motion events, they can
still end up as 0-deltas post-projection, consuming QEMU buffer space
for no reason.
This should help with mouse skipping issues.
If the renderer fails to start it sets the run state to stopped, having
lgInit where it was causes this to be reset to running triggering
invalid usage of g_state.lgmp.
Under some circumstances, Looking Glass can hang when SIGINT'd, for
instance, if it's stuck waiting on spice I/O that won't complete because
the guest is misbehaving.
This commit provides an escape hatch for such cases, so one doesn't have
to reach for `kill -9 $(pidof looking-glass-client)`.
It does not make sense to accumulate fractional error in non-capture mode
as you know exactly where the cursor is supposed to be, at least on Wayland.
On Wayland, we base movements on the current guest position and desired
target position, and the accumulated errors only skew our movements.
Build failed with _FORTIFY_SOURCE enabled because the compiler couldn't
ensure the switch statements didn't hit the default arm and thus wouldn't
define the variables. Adding a statically failing assert makes sure that
all code paths either define the variables or fail early.
$ cd client
$ env CFLAGS='-O1 -D_FORTIFY_SOURCE=1' cmake -B build/
$ make -C build
[...]
client/renderers/EGL/egl.c: In function ‘egl_calc_mouse_size’:
client/renderers/EGL/egl.c:299:36: error: ‘h’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
299 | (this->mouseHeight * (1.0f / h)) * this->scaleY
| ~~~~~~^~~~
When using the meta resize feautre the cursor is over the client window,
and as such the application continues to receive motion events. This
causes the window size to spaz out.
This is an ugly hack for now that will get us over the line for Beta 3,
after which will need to be addressed and people will need to be
informed that their configured escape key will have changed.
If the guest has it's output rotated (ie, landscape) we must rotate and
translate the pointer draw location, as well as all the translations of
cursor coordinate spaces based on the rotation, along with any local
rotations that may also be applied.
Unless the corresponding mouse down event was on our surface, we should
not be receiving the mouse up.
This is always the case on Wayland. On some other platforms,
SDL_CaptureMouse can be used to obtain input that happens outside the
Looking Glass surface, but Looking Glass does not make use of that
function.
We may want to process a mouse up if the corresponding mouse down
initiated a drag (e.g., of a window) that was released slightly outside
of the Looking Glass surface. Previously, Looking Glass would ignore the
mouse up, and the guest would be confused into thinking the button had
never been released, not ending the drag.
We are actually getting mouse events directly from Wayland instead of going
through SDL, so we call app_updateCursorPos in pointer motion handlers and
swallow the SDL event.
Also removed parameters for app_handleMouseBasic as it relies exclusively on
absolute positions provided by app_updateCursorPos. Wayland does not give
you relative movements at all unless grabbed and passing absolute movements
is semantically incorrect.
Note that when the cursor is grabbed, movements are handled entirely through
relativePointerMotionHandler in wayland.c and does not go through
app_handleMouseBasic at all.
If the guest cursor state & position is unknown we can not rely on the
information to detect edge crossings. As such only allow cursor input if
LG is operating in capture mode.
Platforms such as Wayland have no abillity to warp the cursor, as such
can not operate in an always relative mode. This property allows
platforms to report the lack of warp support and prevent LG from
grabbing the pointer.
Some platforms such as Wayland need to set environment vairables before
SDL is initialized, as such this change detects the display server
before SDL has started and calls the new `earlyInit` method providing
the implementation an opportunity to set things up.
When capture mode is set if the cursor is not already in the view area
we need to force it to the state it would be if it were in view as
capture mode overrides all.
The pointer may not yet be in the view area so we should defer drawing
it until the mouse move handler determines that it's inside the view
area and turn it on itself.
As LG always operates in relitive mode, the actual pointer grab/ungrab
is managed by the move handler, as such setGrabQuiet should not alter
the grab/ungrab state of the local pointer.
The prototype for abs is int abs (int n), which implicitly casts floating
point values to integers. The correct function is fabs.
This commit allows the client to compile under clang.
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.
When input:grabKeyboardOnFocus is set (default), entering capture mode grabs
the keyboard a second time. This commit makes the second grab a no-op on
Wayland to avoid a crash.
Previously, main.c would segfault at runtime if clipboards were disabled
via cmake flags, as the clipboards array would be empty but still
indexed during initialization.
Co-authored-by: Quantum <quantum2048@gmail.com>
Otherwise, a badly-behaving client causes Looking Glass to receive a
SIGPIPE during Wayland copy operations. Handle EPIPE at call-sites
instead.
Co-authored-by: Quantum <quantum2048@gmail.com>
The normal logic does not work due to Wayland not supporting mouse warp.
We use a simple logic that works for the desktop with 1:1 mouse patch and
require capture mode for all other cases.
=================================================================
==7680==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000ec010 at pc 0x5622fcf9f386 bp 0x7f36084ff680 sp 0x7f36084ff678
WRITE of size 4 at 0x6020000ec010 thread T1
#0 0x5622fcf9f385 in clipboardRequest /code/LookingGlass/client/src/main.c:707
#1 0x5622fd0036c9 in wayland_cb_notice /code/LookingGlass/client/clipboards/Wayland/src/wayland.c:521
#2 0x5622fcf9f4dc in spiceClipboardNotice /code/LookingGlass/client/src/main.c:724
#3 0x5622fcfc4d59 in spice_agent_process /code/LookingGlass/repos/PureSpice/src/spice.c:1106
#4 0x5622fcfc16d6 in spice_on_main_channel_read /code/LookingGlass/repos/PureSpice/src/spice.c:655
#5 0x5622fcfbee4f in spice_process /code/LookingGlass/repos/PureSpice/src/spice.c:361
#6 0x5622fcf9e3a2 in spiceThread /code/LookingGlass/client/src/main.c:598
#7 0x5622fd006b5e in threadWrapper /code/LookingGlass/common/src/platform/linux/thread.c:39
#8 0x7f3614b2bf26 in start_thread /build/glibc-WZtAaN/glibc-2.30/nptl/pthread_create.c:479
#9 0x7f3614a4c2ee in __clone (/lib/x86_64-linux-gnu/libc.so.6+0xfd2ee)
0x6020000ec011 is located 0 bytes to the right of 1-byte region [0x6020000ec010,0x6020000ec011)
allocated by thread T1 here:
#0 0x7f36156f9628 in malloc (/lib/x86_64-linux-gnu/libasan.so.5+0x107628)
#1 0x5622fcf9f33f in clipboardRequest /code/LookingGlass/client/src/main.c:705
#2 0x5622fd0036c9 in wayland_cb_notice /code/LookingGlass/client/clipboards/Wayland/src/wayland.c:521
#3 0x5622fcf9f4dc in spiceClipboardNotice /code/LookingGlass/client/src/main.c:724
#4 0x5622fcfc4d59 in spice_agent_process /code/LookingGlass/repos/PureSpice/src/spice.c:1106
#5 0x5622fcfc16d6 in spice_on_main_channel_read /code/LookingGlass/repos/PureSpice/src/spice.c:655
#6 0x5622fcfbee4f in spice_process /code/LookingGlass/repos/PureSpice/src/spice.c:361
#7 0x5622fcf9e3a2 in spiceThread /code/LookingGlass/client/src/main.c:598
#8 0x5622fd006b5e in threadWrapper /code/LookingGlass/common/src/platform/linux/thread.c:39
#9 0x7f3614b2bf26 in start_thread /build/glibc-WZtAaN/glibc-2.30/nptl/pthread_create.c:479
Thread T1 created by T0 here:
#0 0x7f361562b9b2 in pthread_create (/lib/x86_64-linux-gnu/libasan.so.5+0x399b2)
#1 0x5622fd006cd0 in lgCreateThread /code/LookingGlass/common/src/platform/linux/thread.c:50
#2 0x5622fcfa5a7d in lg_run /code/LookingGlass/client/src/main.c:1615
#3 0x5622fcface28 in main /code/LookingGlass/client/src/main.c:2035
#4 0x7f3614975e0a in __libc_start_main ../csu/libc-start.c:308
SUMMARY: AddressSanitizer: heap-buffer-overflow /code/LookingGlass/client/src/main.c:707 in clipboardRequest
Shadow bytes around the buggy address:
0x0c04800157b0: fa fa 00 00 fa fa fd fa fa fa fd fa fa fa fd fd
0x0c04800157c0: fa fa fd fd fa fa fd fa fa fa 00 fa fa fa 00 fa
0x0c04800157d0: fa fa 00 fa fa fa fd fa fa fa fd fd fa fa fa fa
0x0c04800157e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c04800157f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c0480015800: fa fa[01]fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0480015810: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0480015820: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0480015830: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0480015840: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0480015850: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==7680==ABORTING
On Wayland, SDL_WINDOWEVENT_CLOSE is sent even when exiting with keyboard
shortcuts. This meant that the client is still closed even with -Q.
We now swallow SDL_WINDOWEVENT_CLOSE if the cursor is inside the VM. This
should prevent keyboard shortcuts from closing the client, while still
allowing the window to be closed by clicking X with the mouse per #138.
Rapid movements in games can cause large detas that may cause the client
to allow the mouse to exit when this is not desired. This change
attempts to limit this by ignoring movements large movements when using
this mode.
This new mode if enabled (disabled by default) will cause the client to
attempt to hold onto the mouse when a title/game has it captured in the
guest. This is best effort and is not a replacement for full capture
mode.