Compare commits

...

33 Commits

Author SHA1 Message Date
Geoffrey McRae
3df7d30cd5 [client] core: don't try to send LGMP messages if the video is stopped
If the video stream is stopped the LGMP session is not valid, so we
can't send messages to the client.
2021-12-27 09:56:53 +11:00
Jonathan Rubenstein
74444f8eed [docs] Minor semantic spit and polish
Co-authored-by: Guanzhong Chen <quantum2048@gmail.com>
Co-authored-by: Netboy3 <1472804+netboy3@users.noreply.github.com>
2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
6c43650cd3 [doc] spelling fixes 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
181ee2b4f5 [doc] module: Kernel Module polish 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
5bef733647 [doc] install: Update command line options 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
22cef47bc4 [doc] install: Overlay Filter config polish 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
5b25e20a2e [doc] install: Overlay Mode polish 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
bb5c7a222c [doc] conf: Whitespace 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
39ea6b0587 [doc] install: Config file loading polish 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
ddc6cb5277 [doc] obs: Install polish 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
b13a79880b [doc] faq: GNOME on Wayland polish 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
53fdc2e148 [docs] build: Better flow for Wayland tip 2021-12-26 10:02:07 +11:00
Tudor Brindus
9872d2e407 [host] dxgi: fix typo in debug log message 2021-12-26 09:49:03 +11:00
Quantum
3ccf6de868 [all] gitignore: ignore __pycache__ and *.py[co]
This prevents accidental commits of the compiled spellchecker.
2021-12-26 09:46:31 +11:00
Quantum
12461196c3 [host] nvfbc: fix comments in updateDamageRects 2021-12-24 15:30:04 +11:00
Quantum
15ec80e80d [client] input: fix race between window size and guest cursor
g_state.posInfoValid could become valid after the guest reports the
cursor position, in which case we did not show the cursor until another
update occurs.

This commit eliminates the race by performing the update when
g_state.posInfoValid becomes true.
2021-12-24 13:16:52 +11:00
Netboy3
d6eb72331c [doc] module: Bring back older style XML
Older libvirt and QEMU require older style config
so put it back with version instructions.
2021-12-24 10:24:57 +11:00
Quantum
eea0ced627 [client] wayland: handle NULL wl_keyboard on destruction 2021-12-24 10:22:23 +11:00
Quantum
94684324f4 [client] wayland: don't create confines on uncapture without wl_pointer 2021-12-24 10:17:16 +11:00
Quantum
194afa2d75 [client] wayland: create wl_relative_pointer when creating wl_pointer 2021-12-24 10:17:16 +11:00
Quantum
d96b2ef1fb [client] wayland: clean up objects when wl_pointer disappears 2021-12-24 10:17:16 +11:00
Geoffrey McRae
ad40ea4195 [client] x11: don't override the focus state if no EWMH focus support 2021-12-22 11:25:54 +11:00
Geoffrey McRae
65948034dd [client] x11: be more agressive grabbing the pointer 2021-12-21 21:51:43 +11:00
Netboy3
9d47ca4f12 [doc] module: Modify libvirt commandline block to use JSON
Latest versions of QEMU are running out of PCIe mapping
through libvirt as the commandline device is mapping
ivshmem before other root devices.
Use JSON style configuration in the commandline block.
JSON arguments are loaded after all regular ones
and will guarantee to get loaded after all other libvirt mapped
PCIe devices.
2021-12-21 15:38:28 +11:00
Geoffrey McRae
27c7054505 [client] x11: protect against msc overflow with jitRender 2021-12-21 10:47:55 +11:00
Geoffrey McRae
02b59ba8f7 [client] x11: don't use the atomic msc value when we already have it 2021-12-21 10:47:11 +11:00
Quantum
a5727262cd [client] wayland: make cursor change work without wl_pointer 2021-12-16 11:36:39 +11:00
Geoffrey McRae
43545a4e17 [all] cmake: refuse to perform in-source builds 2021-12-15 10:56:27 +11:00
Geoffrey McRae
adbe333414 [client] egl: dmabuf can be used without GL_EXT_buffer_storage support 2021-12-15 06:10:30 +11:00
Geoffrey McRae
5f80ce91e8 [client] x11: fix broken grab retry logic 2021-12-15 01:43:09 +11:00
Geoffrey McRae
b6fa296d5a [client] x11: work around issue with desktop switch on i3
For an unknwon reason when LG is on another desktop (hidden) and the
user switches to that desktop, the first attempt to grab the pointer
results in a GrabFrozen result. This adds some simple retry logic to
attempt again after a short (100ms) delay which seems to resolve the
issue.
2021-12-15 00:23:44 +11:00
Geoffrey McRae
2e170ad06f [client] x11: properly detect WMEH support for focus events 2021-12-15 00:17:33 +11:00
jonpas
a6720db749 [docs] obs: fix installation instructions grammar
regression in bc022c77f4
2021-12-14 21:19:25 +11:00
21 changed files with 395 additions and 159 deletions

2
.gitignore vendored
View File

@@ -8,3 +8,5 @@ module/modules.order
*.o
*.exe
*/build
__pycache__
*.py[co]

View File

@@ -2,6 +2,17 @@ cmake_minimum_required(VERSION 3.0)
project(looking-glass-client C CXX)
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(FATAL_ERROR
"\n"
"In-source builds are not supported\n"
"See build instructions provided in: "
"${PROJECT_TOP}/doc/build.rst\n"
"Refusing to continue"
)
endif()
list(APPEND CMAKE_MODULE_PATH "${PROJECT_TOP}/cmake/" "${PROJECT_SOURCE_DIR}/cmake/")
include(CheckSubmodule)

View File

@@ -217,5 +217,6 @@ void waylandSetPointer(LG_DSPointer pointer)
wlWm.cursor = wlWm.cursors[pointer];
wlWm.cursorHotX = wlWm.cursorHot[pointer].x;
wlWm.cursorHotY = wlWm.cursorHot[pointer].y;
wl_pointer_set_cursor(wlWm.pointer, wlWm.pointerEnterSerial, wlWm.cursor, wlWm.cursorHotX, wlWm.cursorHotY);
if (wlWm.pointer)
wl_pointer_set_cursor(wlWm.pointer, wlWm.pointerEnterSerial, wlWm.cursor, wlWm.cursorHotX, wlWm.cursorHotY);
}

View File

@@ -290,20 +290,53 @@ static const struct wl_keyboard_listener keyboardListener = {
.modifiers = keyboardModifiersHandler,
};
static void waylandCleanUpPointer(void)
{
INTERLOCKED_SECTION(wlWm.confineLock, {
if (wlWm.lockedPointer)
{
zwp_locked_pointer_v1_destroy(wlWm.lockedPointer);
wlWm.lockedPointer = NULL;
}
if (wlWm.confinedPointer)
{
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
wlWm.confinedPointer = NULL;
}
});
if (wlWm.relativePointer)
{
zwp_relative_pointer_v1_destroy(wlWm.relativePointer);
wlWm.relativePointer = NULL;
}
wl_pointer_destroy(wlWm.pointer);
wlWm.pointer = NULL;
}
// Seat-handling listeners.
static void handlePointerCapability(uint32_t capabilities)
{
bool hasPointer = capabilities & WL_SEAT_CAPABILITY_POINTER;
if (!hasPointer && wlWm.pointer)
{
wl_pointer_destroy(wlWm.pointer);
wlWm.pointer = NULL;
}
waylandCleanUpPointer();
else if (hasPointer && !wlWm.pointer)
{
wlWm.pointer = wl_seat_get_pointer(wlWm.seat);
wl_pointer_add_listener(wlWm.pointer, &pointerListener, NULL);
waylandSetPointer(wlWm.cursorId);
if (wlWm.warpSupport)
{
wlWm.relativePointer =
zwp_relative_pointer_manager_v1_get_relative_pointer(
wlWm.relativePointerManager, wlWm.pointer);
zwp_relative_pointer_v1_add_listener(wlWm.relativePointer,
&relativePointerListener, NULL);
}
}
}
@@ -375,15 +408,6 @@ bool waylandInputInit(void)
wl_seat_add_listener(wlWm.seat, &seatListener, NULL);
wl_display_roundtrip(wlWm.display);
if (wlWm.warpSupport)
{
wlWm.relativePointer =
zwp_relative_pointer_manager_v1_get_relative_pointer(
wlWm.relativePointerManager, wlWm.pointer);
zwp_relative_pointer_v1_add_listener(wlWm.relativePointer,
&relativePointerListener, NULL);
}
LG_LOCK_INIT(wlWm.confineLock);
return true;
@@ -393,8 +417,15 @@ void waylandInputFree(void)
{
waylandUngrabPointer();
LG_LOCK_FREE(wlWm.confineLock);
wl_pointer_destroy(wlWm.pointer);
wl_keyboard_destroy(wlWm.keyboard);
if (wlWm.pointer)
waylandCleanUpPointer();
// The only legal way the keyboard can be null is if it never existed.
// When unplugged, the compositor must have an inert object.
if (wlWm.keyboard)
wl_keyboard_destroy(wlWm.keyboard);
wl_seat_destroy(wlWm.seat);
if (wlWm.xkbState)
@@ -508,7 +539,7 @@ void waylandUncapturePointer(void)
*/
if (!wlWm.warpSupport || !app_isFormatValid() || app_isCaptureOnlyMode())
internalUngrabPointer(false);
else
else if (wlWm.pointer)
{
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,

View File

@@ -95,6 +95,9 @@ static bool waylandInit(const LG_DSInitParams params)
if (!waylandPresentationInit())
return false;
if (!waylandCursorInit())
return false;
if (!waylandInputInit())
return false;
@@ -104,9 +107,6 @@ static bool waylandInit(const LG_DSInitParams params)
if (!waylandEGLInit(params.w, params.h))
return false;
if (!waylandCursorInit())
return false;
#ifdef ENABLE_OPENGL
if (params.opengl && !waylandOpenGLInit())
return false;

View File

@@ -22,11 +22,15 @@
#define _H_X11DS_ATOMS_
#define DEF_ATOMS() \
DEF_ATOM(_NET_SUPPORTING_WM_CHECK, True) \
DEF_ATOM(_NET_SUPPORTED, True) \
DEF_ATOM(_NET_WM_NAME, True) \
DEF_ATOM(_NET_REQUEST_FRAME_EXTENTS, True) \
DEF_ATOM(_NET_FRAME_EXTENTS, True) \
DEF_ATOM(_NET_WM_BYPASS_COMPOSITOR, False) \
DEF_ATOM(_NET_WM_STATE, True) \
DEF_ATOM(_NET_WM_STATE_FULLSCREEN, True) \
DEF_ATOM(_NET_WM_STATE_FOCUSED, True) \
DEF_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ, True) \
DEF_ATOM(_NET_WM_STATE_MAXIMIZED_VERT, True) \
DEF_ATOM(_NET_WM_WINDOW_TYPE, True) \

View File

@@ -82,8 +82,9 @@ static void x11SetFullscreen(bool fs);
static int x11EventThread(void * unused);
static void x11XInputEvent(XGenericEventCookie *cookie);
static void x11XPresentEvent(XGenericEventCookie *cookie);
static void x11GrabPointer(void);
static void x11DoPresent(void)
static void x11DoPresent(uint64_t msc)
{
static bool startup = true;
if (startup)
@@ -113,11 +114,19 @@ static void x11DoPresent(void)
static bool first = true;
static uint64_t lastMsc = 0;
uint64_t msc = atomic_load(&x11.presentMsc);
uint64_t refill;
if (!first)
refill = 50 - (lastMsc - msc);
{
const uint64_t delta = (lastMsc >= msc) ?
lastMsc - msc :
~0ULL - msc + lastMsc;
if (delta > 50)
return;
refill = 50 - delta;
}
else
{
refill = 50;
@@ -167,6 +176,69 @@ static bool x11EarlyInit(void)
return true;
}
static void x11CheckEWMHSupport(void)
{
if (x11atoms._NET_SUPPORTING_WM_CHECK == None ||
x11atoms._NET_SUPPORTED == None)
return;
Atom type;
int fmt;
unsigned long num, bytes;
unsigned char *data;
if (XGetWindowProperty(x11.display, DefaultRootWindow(x11.display),
x11atoms._NET_SUPPORTING_WM_CHECK, 0, ~0L, False, XA_WINDOW,
&type, &fmt, &num, &bytes, &data) != Success)
goto out;
Window * windowFromRoot = (Window *)data;
if (XGetWindowProperty(x11.display, *windowFromRoot,
x11atoms._NET_SUPPORTING_WM_CHECK, 0, ~0L, False, XA_WINDOW,
&type, &fmt, &num, &bytes, &data) != Success)
goto out_root;
Window * windowFromChild = (Window *)data;
if (*windowFromChild != *windowFromRoot)
goto out_child;
if (XGetWindowProperty(x11.display, DefaultRootWindow(x11.display),
x11atoms._NET_SUPPORTED, 0, ~0L, False, AnyPropertyType,
&type, &fmt, &num, &bytes, &data) != Success)
goto out_child;
Atom * supported = (Atom *)data;
unsigned long supportedCount = num;
if (XGetWindowProperty(x11.display, *windowFromRoot,
x11atoms._NET_WM_NAME, 0, ~0L, False, AnyPropertyType,
&type, &fmt, &num, &bytes, &data) != Success)
goto out_supported;
char * wmName = (char *)data;
for(unsigned long i = 0; i < supportedCount; ++i)
{
if (supported[i] == x11atoms._NET_WM_STATE_FOCUSED)
x11.ewmhHasFocusEvent = true;
}
DEBUG_INFO("EWMH-complient window manager detected: %s", wmName);
x11.ewmhSupport = true;
XFree(wmName);
out_supported:
XFree(supported);
out_child:
XFree(windowFromChild);
out_root:
XFree(windowFromRoot);
out:
return;
}
static bool x11Init(const LG_DSInitParams params)
{
XIDeviceInfo *devinfo;
@@ -272,6 +344,9 @@ static bool x11Init(const LG_DSInitParams params)
X11AtomsInit();
XSetWMProtocols(x11.display, x11.window, &x11atoms.WM_DELETE_WINDOW, 1);
// check for Extended Window Manager Hints support
x11CheckEWMHSupport();
if (params.borderless)
{
if (x11atoms._MOTIF_WM_HINTS)
@@ -457,8 +532,12 @@ static bool x11Init(const LG_DSInitParams params)
eventmask.mask_len = sizeof(mask);
eventmask.mask = mask;
XISetMask(mask, XI_FocusIn );
XISetMask(mask, XI_FocusOut );
if (!x11.ewmhHasFocusEvent)
{
XISetMask(mask, XI_FocusIn );
XISetMask(mask, XI_FocusOut);
}
XISetMask(mask, XI_Enter );
XISetMask(mask, XI_Leave );
XISetMask(mask, XI_Motion );
@@ -615,7 +694,7 @@ static bool x11Init(const LG_DSInitParams params)
}
if (x11.jitRender)
x11DoPresent();
x11DoPresent(0);
return true;
@@ -839,6 +918,7 @@ static int x11EventThread(void * unused)
}
case PropertyNotify:
// ignore property events that are not for us
if (xe.xproperty.display != x11.display ||
xe.xproperty.window != x11.window ||
@@ -860,11 +940,20 @@ static int x11EventThread(void * unused)
break;
bool fullscreen = false;
for(int i = 0; i < num; ++i)
bool focused = false;
for(unsigned long i = 0; i < num; ++i)
{
Atom prop = ((Atom *)data)[i];
if (prop == x11atoms._NET_WM_STATE_FULLSCREEN)
fullscreen = true;
else if (prop == x11atoms._NET_WM_STATE_FOCUSED)
focused = true;
}
if (x11.ewmhHasFocusEvent && x11.focused != focused)
{
x11.focused = focused;
app_handleFocusEvent(focused);
}
x11.fullscreen = fullscreen;
@@ -941,6 +1030,9 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
{
case XI_FocusIn:
{
if (x11.ewmhHasFocusEvent)
return;
atomic_store(&x11.lastWMEvent, microtime());
if (x11.focused)
return;
@@ -959,6 +1051,9 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
case XI_FocusOut:
{
if (x11.ewmhHasFocusEvent)
return;
atomic_store(&x11.lastWMEvent, microtime());
if (!x11.focused)
return;
@@ -1251,10 +1346,10 @@ static void x11XPresentEvent(XGenericEventCookie *cookie)
case PresentCompleteNotify:
{
XPresentCompleteNotifyEvent * e = cookie->data;
x11DoPresent(e->msc);
atomic_store(&x11.presentMsc, e->msc);
atomic_store(&x11.presentUst, e->ust);
lgSignalEvent(x11.frameEvent);
x11DoPresent();
break;
}
}
@@ -1556,7 +1651,10 @@ static void x11GrabPointer(void)
XISetMask(mask.mask, XI_Enter );
XISetMask(mask.mask, XI_Leave );
Status ret = XIGrabDevice(
Status ret;
for(int retry = 0; retry < 10; ++retry)
{
ret = XIGrabDevice(
x11.display,
x11.pointerDev,
x11.window,
@@ -1567,6 +1665,18 @@ static void x11GrabPointer(void)
XINoOwnerEvents,
&mask);
// on some WMs (i3) for an unknown reason the first grab attempt when
// switching to a desktop that has LG on it fails with GrabFrozen, however
// adding as short delay seems to resolve the issue.
if (ret == GrabFrozen && retry < 9)
{
usleep(100000);
continue;
}
break;
}
if (ret != Success)
{
x11PrintGrabError("pointer", x11.pointerDev, ret);

View File

@@ -54,6 +54,11 @@ struct X11DSState
Window window;
XVisualInfo * visual;
//Extended Window Manager Hints
//ref: https://specifications.freedesktop.org/wm-spec/latest/
bool ewmhSupport;
bool ewmhHasFocusEvent;
_Atomic(uint64_t) lastWMEvent;
bool invalidateAll;

View File

@@ -326,6 +326,13 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dma
desktop->useDMA = false;
const char * gl_exts = (const char *)glGetString(GL_EXTENSIONS);
if (!util_hasGLExt(gl_exts, "GL_EXT_buffer_storage"))
{
DEBUG_ERROR("GL_EXT_buffer_storage is needed to use EGL backend");
return false;
}
egl_textureFree(&desktop->texture);
if (!egl_textureInit(&desktop->texture, desktop->display,
EGL_TEXTYPE_FRAMEBUFFER, true))

View File

@@ -798,12 +798,6 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
glGetIntegerv(GL_MAJOR_VERSION, &esMaj);
glGetIntegerv(GL_MINOR_VERSION, &esMin);
if (!util_hasGLExt(gl_exts, "GL_EXT_buffer_storage"))
{
DEBUG_ERROR("GL_EXT_buffer_storage is needed to use EGL backend");
return false;
}
if (!util_hasGLExt(gl_exts, "GL_EXT_texture_format_BGRA8888"))
{
DEBUG_ERROR("GL_EXT_texture_format_BGRA8888 is needed to use EGL backend");
@@ -836,7 +830,14 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
this->dmaSupport = true;
if (!this->dmaSupport)
{
useDMA = false;
if (!util_hasGLExt(gl_exts, "GL_EXT_buffer_storage"))
{
DEBUG_ERROR("GL_EXT_buffer_storage is needed to use EGL backend");
return false;
}
}
if (debugContext)
{

View File

@@ -268,6 +268,19 @@ void core_updatePositionInfo(void)
{
g_state.posInfoValid = true;
g_state.ds->realignPointer();
// g_cursor.guest.valid could have become true in the meantime.
if (g_cursor.guest.valid)
{
// Since posInfoValid was false, core_handleGuestMouseUpdate becomes a
// noop when called on the cursor thread, which means we need to call it
// again in order for the cursor to show up.
core_handleGuestMouseUpdate();
// Similarly, the position needs to be valid before the initial mouse
// move, otherwise we wouldn't know if the cursor is in the viewport.
app_handleMouseRelative(0.0, 0.0, 0.0, 0.0);
}
}
done:
@@ -435,7 +448,8 @@ void core_handleMouseNormal(double ex, double ey)
struct DoublePoint guest;
util_localCurToGuest(&guest);
if (g_state.kvmfrFeatures & KVMFR_FEATURE_SETCURSORPOS)
if (!g_state.stopVideo &&
g_state.kvmfrFeatures & KVMFR_FEATURE_SETCURSORPOS)
{
const KVMFRSetCursorPos msg = {
.msg.type = KVMFR_MESSAGE_SETCURSORPOS,

View File

@@ -140,7 +140,7 @@ frames from the guest.
.. note::
If you are using GNOME on Wayland, you likely want to pass
For users running GNOME on Wayland, you likely want to pass
``-DENABLE_LIBDECOR=ON`` to ``cmake``, i.e. run ``cmake -DENABLE_LIBDECOR=ON ../``.
For details, see :ref:`the FAQ <gnome_wayland_decorations>`.

View File

@@ -71,8 +71,8 @@ master_doc = 'index'
html_theme = 'alabaster'
html_theme_options = {
'logo': 'icon-128x128.png',
'fixed_sidebar': 'true',
'logo': 'icon-128x128.png',
'fixed_sidebar': 'true',
}
html_sidebars = {

View File

@@ -79,26 +79,29 @@ well.
.. _gnome_wayland_decorations:
Why is there no title bar? / Why can't I resize the window?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Why is there no title bar on GNOME? / Why can't I resize the window on GNOME?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This happens on GNOME Wayland because GNOME doesn't support the `standard
protocol`_ for server-side decorations. As a result, the window decorations
do not appear.
This happens because GNOME on Wayland doesn't support the `standard protocol`_
for server-side decorations, and Looking Glass doesn't implement its own
decorations.
The easy solution is to use `libdecor`_. This is currently not packaged in
most distros, so you will need to build it from the linked git repository.
The easiest solution is to build Looking Glass with `libdecor`_ support.
If your distribution lacks a ``libdecor`` package, you must build it from
`source code <libdecor_>`_.
Once you have built it, you can then build the the client with libdecor
support by passing ``-DENABLE_LIBDECOR=ON`` to ``cmake``.
Please be aware that libdecor comes with overheads, which is why it is not
used by default. You should avoid it if possible.
You can then build the the client with libdecor support by passing
``-DENABLE_LIBDECOR=ON`` to ``cmake``.
An alternative solution is to hold down the Super key (Windows key on most
keyboards), and then right click on the window. This should bring up a menu,
keyboards), then right click Looking Glass. This should bring up a menu,
which will allow you to move the window and resize it.
.. warning::
Libdecor support is provided for the convenience of our Wayland users on
GNOME, however it is not a priority feature and may break, please seek
alternatives if you require stable operation.
.. _standard protocol: https://wayland.app/protocols/xdg-decoration-unstable-v1
.. _libdecor: https://gitlab.gnome.org/jadahl/libdecor

View File

@@ -289,21 +289,23 @@ Examples:
Configuration Files
~~~~~~~~~~~~~~~~~~~
By default, the application will look for and load the config files in
the following locations and order:
By default, Looking Glass will load config files from
the following locations:
- /etc/looking-glass-client.ini
- ~/.looking-glass-client.ini
- $XDG_CONFIG_HOME/looking-glass/client.ini (usually ~/.config/looking-glass/client.ini)
Config options are merged from all files. Same options appearing in more
than one file will be overridden by the latest loaded one (E.g. an option
appearing in ~/.config/looking-glass/client.ini will override the same
option appearing in ~/.looking-glass-client.ini). When first launched,
the Looking-Glass client will create the folder $XDG_CONFIG_HOME/looking-glass/
if it does not yet exist.
All config files are loaded in order. Duplicate entries override earlier ones.
This means you can set a system-wide configuration in
``/etc/looking-glass-client.ini``, and override specific options for just
your user in ``~/.looking-glass-client.ini``, which is overlayed on top of
the system-wide configuration.
The format of this file is the commonly known INI format, for example::
When first launched, the Looking-Glass client will create the folder
$XDG_CONFIG_HOME/looking-glass/ if it does not yet exist.
The format of config files is the commonly known INI format, for example::
[win]
fullScreen=yes
@@ -311,7 +313,7 @@ The format of this file is the commonly known INI format, for example::
[egl]
nvGain=1
Command line arguments will override any options loaded from the config
Command line arguments will override any options loaded from config
files.
.. _client_overlay_mode:
@@ -319,82 +321,77 @@ files.
Overlay Mode
~~~~~~~~~~~~
Looking-Glass is utilizing an overlay layer to draw various widgets (such
as the FPS display). An "Overlay Mode" can be toggled (see
:ref:`client_key_bindings`) that allows editing and modifying widgets
properties, such as their position and size. To use the Overlay Mode:
The Overlay Mode lets you configure various runtime options for Looking Glass.
These include:
- Enable one or more widgets (such as the FPS display)
- Enter Overlay Mode by using its keybinding (the screen will turn
darker to indicate Overlay Mode is active)
- Drag the widgets you enabled to your preferred position
- Change the widget size (if the widget allows you) by dragging the
lower right edge
- Change any other properties a widget has and are modifiable
- Exit Overlay Mode by pressing :kbd:`ESC` or using the Overlay-Mode
keybinding again
- EGL filters
- Performance metrics options
- Debug frame damage display
Modifications done in Overlay Mode are persistent and are stored
in a special config file $XDG_CONFIG_HOME/looking-glass/imgui.ini.
Please refrain from editing this file manually as your changes might
be overwritten by the client.
(see :ref:`client_config_widget`)
You can also reposition and resize enabled widgets, like the FPS/UPS Display,
and Performance Metrics.
Enter and exit Overlay Mode with :kbd:`ScrLk` + :kbd:`O`.
:kbd:`ESC` can also be used to exit. (see :ref:`client_key_bindings`)
Modifications done to widgets in Overlay Mode are stored in
``$XDG_CONFIG_HOME/looking-glass/imgui.ini``.
Please do not manually edit this file while Looking Glass is running,
as your changes may be discarded.
.. _client_config_widget:
Configuration Widget
~~~~~~~~~~~~~~~~~~~~
The Configuration Widget is accessible through the Overlay mode. The
The Configuration Widget is accessible through the Overlay Mode. The
widget has multiple tabs that allow setting a variety of modes and
parameters for the application.
parameters for Looking Glass at runtime.
Settings tab
^^^^^^^^^^^^
- Performance Metrics: Enabling this will turn on the Metrics Graphs
Widget. Multiple graphs can be enabled and they will stack vertically.
- EGL: Modify EGL features such as the algorithm used for scaling and
enabling night vision mode.
- Performance Metrics: A toggle for the Performance Metrics Widget.
Multiple graphs are available, and they will stack vertically.
- EGL: Modify EGL features, such as the algorithm used for scaling, and
night vision mode.
Changes in the Settings tab are not persistent and will change back to
Changes in the Settings tab are not persistent, and will change back to
their default values when the client is restarted.
EGL Filters tab
^^^^^^^^^^^^^^^
The EGL Filters tab is a GUI for enabling, configuring and ordering
the post-processing filter stack. Each filter can be expanded to open
its settings. Filters can also be re-ordered by dragging them up or down.
Filters application is cumulative, and order is important (e.g. applying CAS
before FSR might have different results than the reverse). Users are
encouraged to experiment with the order and parameters to achieve optimal
results. The currently available filters include:
The EGL Filters tab contains options for toggling, configuring, and ordering
post-processing filters. Each filter can be expanded to open its settings.
Filters can also be re-ordered by dragging them up or down. Filters are applied
from top to bottom, keep this in mind when ordering them, e.g applying CAS
before FSR might have different results than the reverse. Users are encouraged
to experiment with the order and parameters to achieve optimal results. The
currently available filters include:
- Downscaler: Filter for downscaling resolution. Can be used to undo the
poor upscaling that some games implement such that it can then
be better upscaled using FSR (see below). The filter has a pixel-size setting
that is used to set the effective downscaling ratio and can be set to use
different downscaling algorithms.
- AMD FidelityFX Super Resolution (FSR): Filter implementing a rendering
technique for upscaling resolution. FSR works by accepting the original
frames (on the guest) at lower resolution, then applying a spatial upscaling
algorithm in the client to make the final result look as though it is
rendered in high-resolution. The FSR filter interface allows for fine
tuning the sharpness factor of the algorithm and shows the equivalent
quality mode based on the ratio between the original frame resolution
and the client resolution.
- AMD FidelityFX Contrast Adaptive Sharpening (CAS): Filter that
increases visual quality by applying a sharpening algorithm to the
frame. CAS can sometimes restore detail lost in a typical upscaling
application. The CAS filter interface has an adjustable sharpness
control.
- Downscaler: Filter for downscaling the host resolution. Can be used to undo
poor upscaling on the VM to better utilize AMD FSR (see below). The filter
has a pixel-size setting that is used to set the effective downscaling ratio,
and a configurable interpolation algorithm.
The filter stack settings and order can be saved to presets so that it
can be conveniently recalled at a later time. As filter settings are
usually application specific, multiple presets can be defined for each
case scenario. To save a preset, click on "Save preset as..." and enter
a preset name. Presets are recalled by selecting them in the "Preset name"
pull down. Presets are persistent and are stored on disk at
- AMD FidelityFX Super Resolution (FSR): Spatial upscaling filter that works
on low resolution frames from the guest VM and intelligently upscales to a
higher resolution. The filter sharpness is tunable, and displays the
equivalent AMD quality mode based on the resolution difference.
- AMD FidelityFX Contrast Adaptive Sharpening (CAS): Filter that
increases visual quality by applying a sharpening algorithm to the
video. CAS can sometimes restore detail lost in a typical upscaling
application. Has adjustable sharpness setting.
The filter settings and order can be saved to presets so that it can be restored
at a later time. As filter settings are usually application specific, multiple
presets can be defined for each case scenario. To save a preset, click on "Save
preset as..." and enter a preset name. Presets are loaded by selecting them in
the "Preset name" pull down. Presets are persistent and are stored on disk at
``$XDG_CONFIG_HOME/looking-glass/presets``.
.. warning::
@@ -413,23 +410,23 @@ Full Command Line Options
The following is a complete list of options accepted by this application
+------------------------+-------+------------------------+------------------------------------------------------------------------------------------+
| Long | Short | Value | Description |
+========================+=======+========================+==========================================================================================+
| app:configFile | -C | NULL | A file to read additional configuration from |
+------------------------+-------+------------------------+------------------------------------------------------------------------------------------+
| app:renderer | -g | auto | Specify the renderer to use |
+------------------------+-------+------------------------+------------------------------------------------------------------------------------------+
| app:license | -l | no | Show the license for this application and then terminate |
+------------------------+-------+------------------------+------------------------------------------------------------------------------------------+
| app:cursorPollInterval | | 1000 | How often to check for a cursor update in microseconds |
+------------------------+-------+------------------------+------------------------------------------------------------------------------------------+
| app:framePollInterval | | 1000 | How often to check for a frame update in microseconds |
+------------------------+-------+------------------------+------------------------------------------------------------------------------------------+
| app:allowDMA | | yes | Allow direct DMA transfers if supported (see `README.md` in the `module` dir) |
+------------------------+-------+------------------------+------------------------------------------------------------------------------------------+
| app:shmFile | -f | /dev/shm/looking-glass | The path to the shared memory file, or the name of the kvmfr device to use, e.g.: kvmfr0 |
+------------------------+-------+------------------------+------------------------------------------------------------------------------------------+
+------------------------+-------+------------------------+-----------------------------------------------------------------------------------------+
| Long | Short | Value | Description |
+========================+=======+========================+=========================================================================================+
| app:configFile | -C | NULL | A file to read additional configuration from |
+------------------------+-------+------------------------+-----------------------------------------------------------------------------------------+
| app:renderer | -g | auto | Specify the renderer to use |
+------------------------+-------+------------------------+-----------------------------------------------------------------------------------------+
| app:license | -l | no | Show the license for this application and then terminate |
+------------------------+-------+------------------------+-----------------------------------------------------------------------------------------+
| app:cursorPollInterval | | 1000 | How often to check for a cursor update in microseconds |
+------------------------+-------+------------------------+-----------------------------------------------------------------------------------------+
| app:framePollInterval | | 1000 | How often to check for a frame update in microseconds |
+------------------------+-------+------------------------+-----------------------------------------------------------------------------------------+
| app:allowDMA | | yes | Allow direct DMA transfers if supported (see `README.md` in the `module` dir) |
+------------------------+-------+------------------------+-----------------------------------------------------------------------------------------+
| app:shmFile | -f | /dev/shm/looking-glass | The path to the shared memory file, or the name of the kvmfr device to use, e.g. kvmfr0 |
+------------------------+-------+------------------------+-----------------------------------------------------------------------------------------+
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| Long | Short | Value | Description |
@@ -462,7 +459,7 @@ The following is a complete list of options accepted by this application
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:fpsMin | -K | -1 | Frame rate minimum (0 = disable - not recommended, -1 = auto detect) |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:ignoreQuit | -Q | no | Ignore requests to quit (i.e.: Alt+F4) |
| win:ignoreQuit | -Q | no | Ignore requests to quit (i.e. Alt+F4) |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:noScreensaver | -S | no | Prevent the screensaver from starting |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
@@ -558,6 +555,10 @@ The following is a complete list of options accepted by this application
+------------------+-------+-------+---------------------------------------------------------------------------+
| egl:debug | | no | Enable debug output |
+------------------+-------+-------+---------------------------------------------------------------------------+
| egl:noBufferAge | | no | Disable partial rendering based on buffer age |
+------------------+-------+-------+---------------------------------------------------------------------------+
| egl:noSwapDamage | | no | Disable swapping with damage |
+------------------+-------+-------+---------------------------------------------------------------------------+
+----------------------+-------+-------+---------------------------------------------+
| Long | Short | Value | Description |

View File

@@ -166,7 +166,30 @@ Add the following arguments to your ``qemu`` command line::
libvirt
^^^^^^^
Create the following XML block in your domain:
Starting with QEMU 6.2 and libvirt 7.9, JSON style QEMU configuration is the
default syntax. Users running QEMU 6.2 or later **and** libvirt 7.9 or later,
should use this XML block to configure their VM for kvmfr:
.. code:: xml
<qemu:commandline>
<qemu:arg value='-device'/>
<qemu:arg value='{"driver":"ivshmem-plain","id":"shmem0","memdev":"looking-glass"}'/>
<qemu:arg value='-object'/>
<qemu:arg value='{"qom-type":"memory-backend-file","id":"looking-glass","mem-path":"/dev/kvmfr0","size":33554432,"share":true}'/>
</qemu:commandline>
.. note::
- The ``"size"`` tag represents the size of the shared memory device in
bytes. Once you determine the proper size of the device as per
:ref:`Determining Memory <client_determining_memory>`, use the figure you
got to calculate the size in bytes:
``size_in_MB x 1024 x 1024 = size_in_bytes``
If you are running QEMU older than 6.2 or libvirt older than 7.9, please use
legacy syntax for IVSHMEM setup:
.. code:: xml
@@ -179,8 +202,12 @@ Create the following XML block in your domain:
.. note::
Remember to add ``xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'``
to the ``<domain>`` tag.
- Using the legacy syntax on QEMU 6.2/libvirt 7.9 may cause QEMU to
abort with the following error message:
"``error: internal error: ... PCI: slot 1 function 0 not available for pcie-root-port, in use by ivshmem-plain``"
- Remember to add ``xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'``
to the ``<domain>`` tag.
Running libvirt this way violates AppArmor and cgroups policies, which will
block the VM from running. These policies must be amended to allow the VM

View File

@@ -47,7 +47,7 @@ These instructions are similar to building the
cmake -DUSER_INSTALL=1 ../
make
Alternatively if you would like to install the plugin for all users.
Alternatively, if you would like to install the plugin for all users:
.. code:: bash
@@ -59,15 +59,19 @@ Alternatively if you would like to install the plugin for all users.
Installation
~~~~~~~~~~~~
You can install the plugin for by running ``make install``.
You can install the plugin by running ``make install``.
.. code:: bash
make install
If you opted to install the plugin for all users you will need to run
If you've opted to install the plugin for all users, you will need to run
this command as ``root``.
.. code:: bash
sudo make install
Setup
~~~~~

View File

@@ -29,6 +29,7 @@ modprobe
msys
multisampling
Nvidia
overlayed
pacman
passthrough
pre
@@ -38,11 +39,13 @@ radeon
realtime
renderer
repo
runtime
submodule
submodules
systemd
toolchain
tritanope
tunable
udev
UEFI
uncheck
@@ -51,6 +54,7 @@ Unigine
unix
upscaled
upscaler
upscales
upscaling
VM
vsync

View File

@@ -1,12 +1,23 @@
cmake_minimum_required(VERSION 3.0)
project(looking-glass-host C)
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(FATAL_ERROR
"\n"
"In-source builds are not supported\n"
"See build instructions provided in: "
"${PROJECT_TOP}/doc/build.rst\n"
"Refusing to continue"
)
endif()
list(APPEND CMAKE_MODULE_PATH "${PROJECT_TOP}/cmake/" "${PROJECT_SOURCE_DIR}/cmake/")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG" CACHE STRING "compiler flags" FORCE)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG" CACHE STRING "compiler flags" FORCE)
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
list(APPEND CMAKE_MODULE_PATH "${PROJECT_TOP}/cmake/" "${PROJECT_SOURCE_DIR}/cmake/")
include(CheckSubmodule)
include(GNUInstallDirs)
include(CheckCCompilerFlag)

View File

@@ -319,13 +319,13 @@ static bool dxgi_init(void)
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));
DEBUG_INFO("Device Name : %ls" , outputDesc.DeviceName);
DEBUG_INFO("Device Description: %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[] =
{
@@ -432,9 +432,9 @@ static bool dxgi_init(void)
++this->formatVer;
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");
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");
// try to reduce the latency
{
@@ -524,7 +524,7 @@ static bool dxgi_init(void)
DXGI_OUTDUPL_DESC dupDesc;
IDXGIOutputDuplication_GetDesc(this->dup, &dupDesc);
DEBUG_INFO("Source Format : %s", GetDXGIFormatStr(dupDesc.ModeDesc.Format));
DEBUG_INFO("Source Format : %s", GetDXGIFormatStr(dupDesc.ModeDesc.Format));
uint8_t bpp = 4;
switch(dupDesc.ModeDesc.Format)

View File

@@ -394,8 +394,8 @@ static void updateDamageRects(CaptureFrame * frame)
for(unsigned int i = 0; i < ARRAY_LENGTH(ds); ++i)
ds[i].use = (bool)this->diffMap[i];
// reduce the number of reusuting rectangles by filling in holes and merging
// iiregular shapes into contiguous rectangles
// reduce the number of resulting rectangles by filling in holes and merging
// irregular shapes into contiguous rectangles
bool resolve;
do
{
@@ -447,8 +447,8 @@ static void updateDamageRects(CaptureFrame * frame)
bool ok = true;
if (x > 0 && ds[l].id != ds[u].id)
{
// no need to use dsFind as the search order ensures that the id has
// been fully resolved of the block above
// no need to use dsFind as the search order ensures that the id of
// the block above has been fully resolved
for(unsigned int j = ds[ds[u].id].x1; j <= ds[ds[u].id].x2; ++j)
if (!ds[y * w + j].use)
{