mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-12-16 13:08:13 +00:00
Compare commits
33 Commits
B5-rc1
...
Release/B5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3df7d30cd5 | ||
|
|
74444f8eed | ||
|
|
6c43650cd3 | ||
|
|
181ee2b4f5 | ||
|
|
5bef733647 | ||
|
|
22cef47bc4 | ||
|
|
5b25e20a2e | ||
|
|
bb5c7a222c | ||
|
|
39ea6b0587 | ||
|
|
ddc6cb5277 | ||
|
|
b13a79880b | ||
|
|
53fdc2e148 | ||
|
|
9872d2e407 | ||
|
|
3ccf6de868 | ||
|
|
12461196c3 | ||
|
|
15ec80e80d | ||
|
|
d6eb72331c | ||
|
|
eea0ced627 | ||
|
|
94684324f4 | ||
|
|
194afa2d75 | ||
|
|
d96b2ef1fb | ||
|
|
ad40ea4195 | ||
|
|
65948034dd | ||
|
|
9d47ca4f12 | ||
|
|
27c7054505 | ||
|
|
02b59ba8f7 | ||
|
|
a5727262cd | ||
|
|
43545a4e17 | ||
|
|
adbe333414 | ||
|
|
5f80ce91e8 | ||
|
|
b6fa296d5a | ||
|
|
2e170ad06f | ||
|
|
a6720db749 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -8,3 +8,5 @@ module/modules.order
|
|||||||
*.o
|
*.o
|
||||||
*.exe
|
*.exe
|
||||||
*/build
|
*/build
|
||||||
|
__pycache__
|
||||||
|
*.py[co]
|
||||||
|
|||||||
@@ -2,6 +2,17 @@ cmake_minimum_required(VERSION 3.0)
|
|||||||
project(looking-glass-client C CXX)
|
project(looking-glass-client C CXX)
|
||||||
|
|
||||||
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
|
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/")
|
list(APPEND CMAKE_MODULE_PATH "${PROJECT_TOP}/cmake/" "${PROJECT_SOURCE_DIR}/cmake/")
|
||||||
|
|
||||||
include(CheckSubmodule)
|
include(CheckSubmodule)
|
||||||
|
|||||||
@@ -217,5 +217,6 @@ void waylandSetPointer(LG_DSPointer pointer)
|
|||||||
wlWm.cursor = wlWm.cursors[pointer];
|
wlWm.cursor = wlWm.cursors[pointer];
|
||||||
wlWm.cursorHotX = wlWm.cursorHot[pointer].x;
|
wlWm.cursorHotX = wlWm.cursorHot[pointer].x;
|
||||||
wlWm.cursorHotY = wlWm.cursorHot[pointer].y;
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,20 +290,53 @@ static const struct wl_keyboard_listener keyboardListener = {
|
|||||||
.modifiers = keyboardModifiersHandler,
|
.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.
|
// Seat-handling listeners.
|
||||||
|
|
||||||
static void handlePointerCapability(uint32_t capabilities)
|
static void handlePointerCapability(uint32_t capabilities)
|
||||||
{
|
{
|
||||||
bool hasPointer = capabilities & WL_SEAT_CAPABILITY_POINTER;
|
bool hasPointer = capabilities & WL_SEAT_CAPABILITY_POINTER;
|
||||||
if (!hasPointer && wlWm.pointer)
|
if (!hasPointer && wlWm.pointer)
|
||||||
{
|
waylandCleanUpPointer();
|
||||||
wl_pointer_destroy(wlWm.pointer);
|
|
||||||
wlWm.pointer = NULL;
|
|
||||||
}
|
|
||||||
else if (hasPointer && !wlWm.pointer)
|
else if (hasPointer && !wlWm.pointer)
|
||||||
{
|
{
|
||||||
wlWm.pointer = wl_seat_get_pointer(wlWm.seat);
|
wlWm.pointer = wl_seat_get_pointer(wlWm.seat);
|
||||||
wl_pointer_add_listener(wlWm.pointer, &pointerListener, NULL);
|
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_seat_add_listener(wlWm.seat, &seatListener, NULL);
|
||||||
wl_display_roundtrip(wlWm.display);
|
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);
|
LG_LOCK_INIT(wlWm.confineLock);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -393,8 +417,15 @@ void waylandInputFree(void)
|
|||||||
{
|
{
|
||||||
waylandUngrabPointer();
|
waylandUngrabPointer();
|
||||||
LG_LOCK_FREE(wlWm.confineLock);
|
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);
|
wl_seat_destroy(wlWm.seat);
|
||||||
|
|
||||||
if (wlWm.xkbState)
|
if (wlWm.xkbState)
|
||||||
@@ -508,7 +539,7 @@ void waylandUncapturePointer(void)
|
|||||||
*/
|
*/
|
||||||
if (!wlWm.warpSupport || !app_isFormatValid() || app_isCaptureOnlyMode())
|
if (!wlWm.warpSupport || !app_isFormatValid() || app_isCaptureOnlyMode())
|
||||||
internalUngrabPointer(false);
|
internalUngrabPointer(false);
|
||||||
else
|
else if (wlWm.pointer)
|
||||||
{
|
{
|
||||||
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
|
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
|
||||||
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
|
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
|
||||||
|
|||||||
@@ -95,6 +95,9 @@ static bool waylandInit(const LG_DSInitParams params)
|
|||||||
if (!waylandPresentationInit())
|
if (!waylandPresentationInit())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (!waylandCursorInit())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!waylandInputInit())
|
if (!waylandInputInit())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -104,9 +107,6 @@ static bool waylandInit(const LG_DSInitParams params)
|
|||||||
if (!waylandEGLInit(params.w, params.h))
|
if (!waylandEGLInit(params.w, params.h))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!waylandCursorInit())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#ifdef ENABLE_OPENGL
|
#ifdef ENABLE_OPENGL
|
||||||
if (params.opengl && !waylandOpenGLInit())
|
if (params.opengl && !waylandOpenGLInit())
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -22,11 +22,15 @@
|
|||||||
#define _H_X11DS_ATOMS_
|
#define _H_X11DS_ATOMS_
|
||||||
|
|
||||||
#define DEF_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_REQUEST_FRAME_EXTENTS, True) \
|
||||||
DEF_ATOM(_NET_FRAME_EXTENTS, True) \
|
DEF_ATOM(_NET_FRAME_EXTENTS, True) \
|
||||||
DEF_ATOM(_NET_WM_BYPASS_COMPOSITOR, False) \
|
DEF_ATOM(_NET_WM_BYPASS_COMPOSITOR, False) \
|
||||||
DEF_ATOM(_NET_WM_STATE, True) \
|
DEF_ATOM(_NET_WM_STATE, True) \
|
||||||
DEF_ATOM(_NET_WM_STATE_FULLSCREEN, 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_HORZ, True) \
|
||||||
DEF_ATOM(_NET_WM_STATE_MAXIMIZED_VERT, True) \
|
DEF_ATOM(_NET_WM_STATE_MAXIMIZED_VERT, True) \
|
||||||
DEF_ATOM(_NET_WM_WINDOW_TYPE, True) \
|
DEF_ATOM(_NET_WM_WINDOW_TYPE, True) \
|
||||||
|
|||||||
@@ -82,8 +82,9 @@ static void x11SetFullscreen(bool fs);
|
|||||||
static int x11EventThread(void * unused);
|
static int x11EventThread(void * unused);
|
||||||
static void x11XInputEvent(XGenericEventCookie *cookie);
|
static void x11XInputEvent(XGenericEventCookie *cookie);
|
||||||
static void x11XPresentEvent(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;
|
static bool startup = true;
|
||||||
if (startup)
|
if (startup)
|
||||||
@@ -113,11 +114,19 @@ static void x11DoPresent(void)
|
|||||||
|
|
||||||
static bool first = true;
|
static bool first = true;
|
||||||
static uint64_t lastMsc = 0;
|
static uint64_t lastMsc = 0;
|
||||||
uint64_t msc = atomic_load(&x11.presentMsc);
|
|
||||||
|
|
||||||
uint64_t refill;
|
uint64_t refill;
|
||||||
if (!first)
|
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
|
else
|
||||||
{
|
{
|
||||||
refill = 50;
|
refill = 50;
|
||||||
@@ -167,6 +176,69 @@ static bool x11EarlyInit(void)
|
|||||||
return true;
|
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)
|
static bool x11Init(const LG_DSInitParams params)
|
||||||
{
|
{
|
||||||
XIDeviceInfo *devinfo;
|
XIDeviceInfo *devinfo;
|
||||||
@@ -272,6 +344,9 @@ static bool x11Init(const LG_DSInitParams params)
|
|||||||
X11AtomsInit();
|
X11AtomsInit();
|
||||||
XSetWMProtocols(x11.display, x11.window, &x11atoms.WM_DELETE_WINDOW, 1);
|
XSetWMProtocols(x11.display, x11.window, &x11atoms.WM_DELETE_WINDOW, 1);
|
||||||
|
|
||||||
|
// check for Extended Window Manager Hints support
|
||||||
|
x11CheckEWMHSupport();
|
||||||
|
|
||||||
if (params.borderless)
|
if (params.borderless)
|
||||||
{
|
{
|
||||||
if (x11atoms._MOTIF_WM_HINTS)
|
if (x11atoms._MOTIF_WM_HINTS)
|
||||||
@@ -457,8 +532,12 @@ static bool x11Init(const LG_DSInitParams params)
|
|||||||
eventmask.mask_len = sizeof(mask);
|
eventmask.mask_len = sizeof(mask);
|
||||||
eventmask.mask = mask;
|
eventmask.mask = mask;
|
||||||
|
|
||||||
XISetMask(mask, XI_FocusIn );
|
if (!x11.ewmhHasFocusEvent)
|
||||||
XISetMask(mask, XI_FocusOut );
|
{
|
||||||
|
XISetMask(mask, XI_FocusIn );
|
||||||
|
XISetMask(mask, XI_FocusOut);
|
||||||
|
}
|
||||||
|
|
||||||
XISetMask(mask, XI_Enter );
|
XISetMask(mask, XI_Enter );
|
||||||
XISetMask(mask, XI_Leave );
|
XISetMask(mask, XI_Leave );
|
||||||
XISetMask(mask, XI_Motion );
|
XISetMask(mask, XI_Motion );
|
||||||
@@ -615,7 +694,7 @@ static bool x11Init(const LG_DSInitParams params)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (x11.jitRender)
|
if (x11.jitRender)
|
||||||
x11DoPresent();
|
x11DoPresent(0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -839,6 +918,7 @@ static int x11EventThread(void * unused)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case PropertyNotify:
|
case PropertyNotify:
|
||||||
|
|
||||||
// ignore property events that are not for us
|
// ignore property events that are not for us
|
||||||
if (xe.xproperty.display != x11.display ||
|
if (xe.xproperty.display != x11.display ||
|
||||||
xe.xproperty.window != x11.window ||
|
xe.xproperty.window != x11.window ||
|
||||||
@@ -860,11 +940,20 @@ static int x11EventThread(void * unused)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
bool fullscreen = false;
|
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];
|
Atom prop = ((Atom *)data)[i];
|
||||||
if (prop == x11atoms._NET_WM_STATE_FULLSCREEN)
|
if (prop == x11atoms._NET_WM_STATE_FULLSCREEN)
|
||||||
fullscreen = true;
|
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;
|
x11.fullscreen = fullscreen;
|
||||||
@@ -941,6 +1030,9 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
|||||||
{
|
{
|
||||||
case XI_FocusIn:
|
case XI_FocusIn:
|
||||||
{
|
{
|
||||||
|
if (x11.ewmhHasFocusEvent)
|
||||||
|
return;
|
||||||
|
|
||||||
atomic_store(&x11.lastWMEvent, microtime());
|
atomic_store(&x11.lastWMEvent, microtime());
|
||||||
if (x11.focused)
|
if (x11.focused)
|
||||||
return;
|
return;
|
||||||
@@ -959,6 +1051,9 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
|||||||
|
|
||||||
case XI_FocusOut:
|
case XI_FocusOut:
|
||||||
{
|
{
|
||||||
|
if (x11.ewmhHasFocusEvent)
|
||||||
|
return;
|
||||||
|
|
||||||
atomic_store(&x11.lastWMEvent, microtime());
|
atomic_store(&x11.lastWMEvent, microtime());
|
||||||
if (!x11.focused)
|
if (!x11.focused)
|
||||||
return;
|
return;
|
||||||
@@ -1251,10 +1346,10 @@ static void x11XPresentEvent(XGenericEventCookie *cookie)
|
|||||||
case PresentCompleteNotify:
|
case PresentCompleteNotify:
|
||||||
{
|
{
|
||||||
XPresentCompleteNotifyEvent * e = cookie->data;
|
XPresentCompleteNotifyEvent * e = cookie->data;
|
||||||
|
x11DoPresent(e->msc);
|
||||||
atomic_store(&x11.presentMsc, e->msc);
|
atomic_store(&x11.presentMsc, e->msc);
|
||||||
atomic_store(&x11.presentUst, e->ust);
|
atomic_store(&x11.presentUst, e->ust);
|
||||||
lgSignalEvent(x11.frameEvent);
|
lgSignalEvent(x11.frameEvent);
|
||||||
x11DoPresent();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1556,7 +1651,10 @@ static void x11GrabPointer(void)
|
|||||||
XISetMask(mask.mask, XI_Enter );
|
XISetMask(mask.mask, XI_Enter );
|
||||||
XISetMask(mask.mask, XI_Leave );
|
XISetMask(mask.mask, XI_Leave );
|
||||||
|
|
||||||
Status ret = XIGrabDevice(
|
Status ret;
|
||||||
|
for(int retry = 0; retry < 10; ++retry)
|
||||||
|
{
|
||||||
|
ret = XIGrabDevice(
|
||||||
x11.display,
|
x11.display,
|
||||||
x11.pointerDev,
|
x11.pointerDev,
|
||||||
x11.window,
|
x11.window,
|
||||||
@@ -1567,6 +1665,18 @@ static void x11GrabPointer(void)
|
|||||||
XINoOwnerEvents,
|
XINoOwnerEvents,
|
||||||
&mask);
|
&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)
|
if (ret != Success)
|
||||||
{
|
{
|
||||||
x11PrintGrabError("pointer", x11.pointerDev, ret);
|
x11PrintGrabError("pointer", x11.pointerDev, ret);
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ struct X11DSState
|
|||||||
Window window;
|
Window window;
|
||||||
XVisualInfo * visual;
|
XVisualInfo * visual;
|
||||||
|
|
||||||
|
//Extended Window Manager Hints
|
||||||
|
//ref: https://specifications.freedesktop.org/wm-spec/latest/
|
||||||
|
bool ewmhSupport;
|
||||||
|
bool ewmhHasFocusEvent;
|
||||||
|
|
||||||
_Atomic(uint64_t) lastWMEvent;
|
_Atomic(uint64_t) lastWMEvent;
|
||||||
bool invalidateAll;
|
bool invalidateAll;
|
||||||
|
|
||||||
|
|||||||
@@ -326,6 +326,13 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dma
|
|||||||
|
|
||||||
desktop->useDMA = false;
|
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);
|
egl_textureFree(&desktop->texture);
|
||||||
if (!egl_textureInit(&desktop->texture, desktop->display,
|
if (!egl_textureInit(&desktop->texture, desktop->display,
|
||||||
EGL_TEXTYPE_FRAMEBUFFER, true))
|
EGL_TEXTYPE_FRAMEBUFFER, true))
|
||||||
|
|||||||
@@ -798,12 +798,6 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
|||||||
glGetIntegerv(GL_MAJOR_VERSION, &esMaj);
|
glGetIntegerv(GL_MAJOR_VERSION, &esMaj);
|
||||||
glGetIntegerv(GL_MINOR_VERSION, &esMin);
|
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"))
|
if (!util_hasGLExt(gl_exts, "GL_EXT_texture_format_BGRA8888"))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("GL_EXT_texture_format_BGRA8888 is needed to use EGL backend");
|
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;
|
this->dmaSupport = true;
|
||||||
|
|
||||||
if (!this->dmaSupport)
|
if (!this->dmaSupport)
|
||||||
|
{
|
||||||
useDMA = false;
|
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)
|
if (debugContext)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -268,6 +268,19 @@ void core_updatePositionInfo(void)
|
|||||||
{
|
{
|
||||||
g_state.posInfoValid = true;
|
g_state.posInfoValid = true;
|
||||||
g_state.ds->realignPointer();
|
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:
|
done:
|
||||||
@@ -435,7 +448,8 @@ void core_handleMouseNormal(double ex, double ey)
|
|||||||
struct DoublePoint guest;
|
struct DoublePoint guest;
|
||||||
util_localCurToGuest(&guest);
|
util_localCurToGuest(&guest);
|
||||||
|
|
||||||
if (g_state.kvmfrFeatures & KVMFR_FEATURE_SETCURSORPOS)
|
if (!g_state.stopVideo &&
|
||||||
|
g_state.kvmfrFeatures & KVMFR_FEATURE_SETCURSORPOS)
|
||||||
{
|
{
|
||||||
const KVMFRSetCursorPos msg = {
|
const KVMFRSetCursorPos msg = {
|
||||||
.msg.type = KVMFR_MESSAGE_SETCURSORPOS,
|
.msg.type = KVMFR_MESSAGE_SETCURSORPOS,
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ frames from the guest.
|
|||||||
|
|
||||||
.. note::
|
.. 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 ../``.
|
``-DENABLE_LIBDECOR=ON`` to ``cmake``, i.e. run ``cmake -DENABLE_LIBDECOR=ON ../``.
|
||||||
|
|
||||||
For details, see :ref:`the FAQ <gnome_wayland_decorations>`.
|
For details, see :ref:`the FAQ <gnome_wayland_decorations>`.
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ master_doc = 'index'
|
|||||||
html_theme = 'alabaster'
|
html_theme = 'alabaster'
|
||||||
|
|
||||||
html_theme_options = {
|
html_theme_options = {
|
||||||
'logo': 'icon-128x128.png',
|
'logo': 'icon-128x128.png',
|
||||||
'fixed_sidebar': 'true',
|
'fixed_sidebar': 'true',
|
||||||
}
|
}
|
||||||
|
|
||||||
html_sidebars = {
|
html_sidebars = {
|
||||||
|
|||||||
29
doc/faq.rst
29
doc/faq.rst
@@ -79,26 +79,29 @@ well.
|
|||||||
|
|
||||||
.. _gnome_wayland_decorations:
|
.. _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
|
This happens because GNOME on Wayland doesn't support the `standard protocol`_
|
||||||
protocol`_ for server-side decorations. As a result, the window decorations
|
for server-side decorations, and Looking Glass doesn't implement its own
|
||||||
do not appear.
|
decorations.
|
||||||
|
|
||||||
The easy solution is to use `libdecor`_. This is currently not packaged in
|
The easiest solution is to build Looking Glass with `libdecor`_ support.
|
||||||
most distros, so you will need to build it from the linked git repository.
|
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
|
You can then build the the client with libdecor support by passing
|
||||||
support by passing ``-DENABLE_LIBDECOR=ON`` to ``cmake``.
|
``-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.
|
|
||||||
|
|
||||||
An alternative solution is to hold down the Super key (Windows key on most
|
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.
|
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
|
.. _standard protocol: https://wayland.app/protocols/xdg-decoration-unstable-v1
|
||||||
.. _libdecor: https://gitlab.gnome.org/jadahl/libdecor
|
.. _libdecor: https://gitlab.gnome.org/jadahl/libdecor
|
||||||
|
|
||||||
|
|||||||
167
doc/install.rst
167
doc/install.rst
@@ -289,21 +289,23 @@ Examples:
|
|||||||
Configuration Files
|
Configuration Files
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
By default, the application will look for and load the config files in
|
By default, Looking Glass will load config files from
|
||||||
the following locations and order:
|
the following locations:
|
||||||
|
|
||||||
- /etc/looking-glass-client.ini
|
- /etc/looking-glass-client.ini
|
||||||
- ~/.looking-glass-client.ini
|
- ~/.looking-glass-client.ini
|
||||||
- $XDG_CONFIG_HOME/looking-glass/client.ini (usually ~/.config/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
|
All config files are loaded in order. Duplicate entries override earlier ones.
|
||||||
than one file will be overridden by the latest loaded one (E.g. an option
|
This means you can set a system-wide configuration in
|
||||||
appearing in ~/.config/looking-glass/client.ini will override the same
|
``/etc/looking-glass-client.ini``, and override specific options for just
|
||||||
option appearing in ~/.looking-glass-client.ini). When first launched,
|
your user in ``~/.looking-glass-client.ini``, which is overlayed on top of
|
||||||
the Looking-Glass client will create the folder $XDG_CONFIG_HOME/looking-glass/
|
the system-wide configuration.
|
||||||
if it does not yet exist.
|
|
||||||
|
|
||||||
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]
|
[win]
|
||||||
fullScreen=yes
|
fullScreen=yes
|
||||||
@@ -311,7 +313,7 @@ The format of this file is the commonly known INI format, for example::
|
|||||||
[egl]
|
[egl]
|
||||||
nvGain=1
|
nvGain=1
|
||||||
|
|
||||||
Command line arguments will override any options loaded from the config
|
Command line arguments will override any options loaded from config
|
||||||
files.
|
files.
|
||||||
|
|
||||||
.. _client_overlay_mode:
|
.. _client_overlay_mode:
|
||||||
@@ -319,82 +321,77 @@ files.
|
|||||||
Overlay Mode
|
Overlay Mode
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
Looking-Glass is utilizing an overlay layer to draw various widgets (such
|
The Overlay Mode lets you configure various runtime options for Looking Glass.
|
||||||
as the FPS display). An "Overlay Mode" can be toggled (see
|
These include:
|
||||||
:ref:`client_key_bindings`) that allows editing and modifying widgets
|
|
||||||
properties, such as their position and size. To use the Overlay Mode:
|
|
||||||
|
|
||||||
- Enable one or more widgets (such as the FPS display)
|
- EGL filters
|
||||||
- Enter Overlay Mode by using its keybinding (the screen will turn
|
- Performance metrics options
|
||||||
darker to indicate Overlay Mode is active)
|
- Debug frame damage display
|
||||||
- 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
|
|
||||||
|
|
||||||
Modifications done in Overlay Mode are persistent and are stored
|
(see :ref:`client_config_widget`)
|
||||||
in a special config file $XDG_CONFIG_HOME/looking-glass/imgui.ini.
|
|
||||||
Please refrain from editing this file manually as your changes might
|
You can also reposition and resize enabled widgets, like the FPS/UPS Display,
|
||||||
be overwritten by the client.
|
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:
|
.. _client_config_widget:
|
||||||
|
|
||||||
Configuration 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
|
widget has multiple tabs that allow setting a variety of modes and
|
||||||
parameters for the application.
|
parameters for Looking Glass at runtime.
|
||||||
|
|
||||||
Settings tab
|
Settings tab
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
- Performance Metrics: Enabling this will turn on the Metrics Graphs
|
- Performance Metrics: A toggle for the Performance Metrics Widget.
|
||||||
Widget. Multiple graphs can be enabled and they will stack vertically.
|
Multiple graphs are available, and they will stack vertically.
|
||||||
- EGL: Modify EGL features such as the algorithm used for scaling and
|
- EGL: Modify EGL features, such as the algorithm used for scaling, and
|
||||||
enabling night vision mode.
|
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.
|
their default values when the client is restarted.
|
||||||
|
|
||||||
EGL Filters tab
|
EGL Filters tab
|
||||||
^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
The EGL Filters tab is a GUI for enabling, configuring and ordering
|
The EGL Filters tab contains options for toggling, configuring, and ordering
|
||||||
the post-processing filter stack. Each filter can be expanded to open
|
post-processing filters. Each filter can be expanded to open its settings.
|
||||||
its settings. Filters can also be re-ordered by dragging them up or down.
|
Filters can also be re-ordered by dragging them up or down. Filters are applied
|
||||||
Filters application is cumulative, and order is important (e.g. applying CAS
|
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
|
before FSR might have different results than the reverse. Users are encouraged
|
||||||
encouraged to experiment with the order and parameters to achieve optimal
|
to experiment with the order and parameters to achieve optimal results. The
|
||||||
results. The currently available filters include:
|
currently available filters include:
|
||||||
|
|
||||||
- Downscaler: Filter for downscaling resolution. Can be used to undo the
|
- Downscaler: Filter for downscaling the host resolution. Can be used to undo
|
||||||
poor upscaling that some games implement such that it can then
|
poor upscaling on the VM to better utilize AMD FSR (see below). The filter
|
||||||
be better upscaled using FSR (see below). The filter has a pixel-size setting
|
has a pixel-size setting that is used to set the effective downscaling ratio,
|
||||||
that is used to set the effective downscaling ratio and can be set to use
|
and a configurable interpolation algorithm.
|
||||||
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.
|
|
||||||
|
|
||||||
The filter stack settings and order can be saved to presets so that it
|
- AMD FidelityFX Super Resolution (FSR): Spatial upscaling filter that works
|
||||||
can be conveniently recalled at a later time. As filter settings are
|
on low resolution frames from the guest VM and intelligently upscales to a
|
||||||
usually application specific, multiple presets can be defined for each
|
higher resolution. The filter sharpness is tunable, and displays the
|
||||||
case scenario. To save a preset, click on "Save preset as..." and enter
|
equivalent AMD quality mode based on the resolution difference.
|
||||||
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 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``.
|
``$XDG_CONFIG_HOME/looking-glass/presets``.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
@@ -413,23 +410,23 @@ Full Command Line Options
|
|||||||
|
|
||||||
The following is a complete list of options accepted by this application
|
The following is a complete list of options accepted by this application
|
||||||
|
|
||||||
+------------------------+-------+------------------------+------------------------------------------------------------------------------------------+
|
+------------------------+-------+------------------------+-----------------------------------------------------------------------------------------+
|
||||||
| Long | Short | Value | Description |
|
| Long | Short | Value | Description |
|
||||||
+========================+=======+========================+==========================================================================================+
|
+========================+=======+========================+=========================================================================================+
|
||||||
| app:configFile | -C | NULL | A file to read additional configuration from |
|
| app:configFile | -C | NULL | A file to read additional configuration from |
|
||||||
+------------------------+-------+------------------------+------------------------------------------------------------------------------------------+
|
+------------------------+-------+------------------------+-----------------------------------------------------------------------------------------+
|
||||||
| app:renderer | -g | auto | Specify the renderer to use |
|
| app:renderer | -g | auto | Specify the renderer to use |
|
||||||
+------------------------+-------+------------------------+------------------------------------------------------------------------------------------+
|
+------------------------+-------+------------------------+-----------------------------------------------------------------------------------------+
|
||||||
| app:license | -l | no | Show the license for this application and then terminate |
|
| 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: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: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: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 |
|
| 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 |
|
| 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: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 |
|
| 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: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 |
|
| Long | Short | Value | Description |
|
||||||
|
|||||||
@@ -166,7 +166,30 @@ Add the following arguments to your ``qemu`` command line::
|
|||||||
libvirt
|
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
|
.. code:: xml
|
||||||
|
|
||||||
@@ -179,8 +202,12 @@ Create the following XML block in your domain:
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Remember to add ``xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'``
|
- Using the legacy syntax on QEMU 6.2/libvirt 7.9 may cause QEMU to
|
||||||
to the ``<domain>`` tag.
|
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
|
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
|
block the VM from running. These policies must be amended to allow the VM
|
||||||
|
|||||||
10
doc/obs.rst
10
doc/obs.rst
@@ -47,7 +47,7 @@ These instructions are similar to building the
|
|||||||
cmake -DUSER_INSTALL=1 ../
|
cmake -DUSER_INSTALL=1 ../
|
||||||
make
|
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
|
.. code:: bash
|
||||||
|
|
||||||
@@ -59,15 +59,19 @@ Alternatively if you would like to install the plugin for all users.
|
|||||||
Installation
|
Installation
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
You can install the plugin for by running ``make install``.
|
You can install the plugin by running ``make install``.
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
make install
|
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``.
|
this command as ``root``.
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
sudo make install
|
||||||
|
|
||||||
Setup
|
Setup
|
||||||
~~~~~
|
~~~~~
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ modprobe
|
|||||||
msys
|
msys
|
||||||
multisampling
|
multisampling
|
||||||
Nvidia
|
Nvidia
|
||||||
|
overlayed
|
||||||
pacman
|
pacman
|
||||||
passthrough
|
passthrough
|
||||||
pre
|
pre
|
||||||
@@ -38,11 +39,13 @@ radeon
|
|||||||
realtime
|
realtime
|
||||||
renderer
|
renderer
|
||||||
repo
|
repo
|
||||||
|
runtime
|
||||||
submodule
|
submodule
|
||||||
submodules
|
submodules
|
||||||
systemd
|
systemd
|
||||||
toolchain
|
toolchain
|
||||||
tritanope
|
tritanope
|
||||||
|
tunable
|
||||||
udev
|
udev
|
||||||
UEFI
|
UEFI
|
||||||
uncheck
|
uncheck
|
||||||
@@ -51,6 +54,7 @@ Unigine
|
|||||||
unix
|
unix
|
||||||
upscaled
|
upscaled
|
||||||
upscaler
|
upscaler
|
||||||
|
upscales
|
||||||
upscaling
|
upscaling
|
||||||
VM
|
VM
|
||||||
vsync
|
vsync
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(looking-glass-host C)
|
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_C_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG" CACHE STRING "compiler flags" FORCE)
|
||||||
set(CMAKE_CXX_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(CheckSubmodule)
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
include(CheckCCompilerFlag)
|
include(CheckCCompilerFlag)
|
||||||
|
|||||||
@@ -319,13 +319,13 @@ static bool dxgi_init(void)
|
|||||||
|
|
||||||
DXGI_ADAPTER_DESC1 adapterDesc;
|
DXGI_ADAPTER_DESC1 adapterDesc;
|
||||||
IDXGIAdapter1_GetDesc1(this->adapter, &adapterDesc);
|
IDXGIAdapter1_GetDesc1(this->adapter, &adapterDesc);
|
||||||
DEBUG_INFO("Device Name : %ls" , outputDesc.DeviceName);
|
DEBUG_INFO("Device Name : %ls" , outputDesc.DeviceName);
|
||||||
DEBUG_INFO("Device Descripion: %ls" , adapterDesc.Description);
|
DEBUG_INFO("Device Description: %ls" , adapterDesc.Description);
|
||||||
DEBUG_INFO("Device Vendor ID : 0x%x" , adapterDesc.VendorId);
|
DEBUG_INFO("Device Vendor ID : 0x%x" , adapterDesc.VendorId);
|
||||||
DEBUG_INFO("Device Device ID : 0x%x" , adapterDesc.DeviceId);
|
DEBUG_INFO("Device Device ID : 0x%x" , adapterDesc.DeviceId);
|
||||||
DEBUG_INFO("Device Video Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedVideoMemory / 1048576));
|
DEBUG_INFO("Device Video Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedVideoMemory / 1048576));
|
||||||
DEBUG_INFO("Device Sys Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedSystemMemory / 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("Shared Sys Mem : %u MiB" , (unsigned)(adapterDesc.SharedSystemMemory / 1048576));
|
||||||
|
|
||||||
static const D3D_FEATURE_LEVEL win8[] =
|
static const D3D_FEATURE_LEVEL win8[] =
|
||||||
{
|
{
|
||||||
@@ -432,9 +432,9 @@ static bool dxgi_init(void)
|
|||||||
|
|
||||||
++this->formatVer;
|
++this->formatVer;
|
||||||
|
|
||||||
DEBUG_INFO("Feature Level : 0x%x" , this->featureLevel);
|
DEBUG_INFO("Feature Level : 0x%x" , this->featureLevel);
|
||||||
DEBUG_INFO("Capture Size : %u x %u", this->width, this->height);
|
DEBUG_INFO("Capture Size : %u x %u", this->width, this->height);
|
||||||
DEBUG_INFO("AcquireLock : %s" , this->useAcquireLock ? "enabled" : "disabled");
|
DEBUG_INFO("AcquireLock : %s" , this->useAcquireLock ? "enabled" : "disabled");
|
||||||
|
|
||||||
// try to reduce the latency
|
// try to reduce the latency
|
||||||
{
|
{
|
||||||
@@ -524,7 +524,7 @@ static bool dxgi_init(void)
|
|||||||
|
|
||||||
DXGI_OUTDUPL_DESC dupDesc;
|
DXGI_OUTDUPL_DESC dupDesc;
|
||||||
IDXGIOutputDuplication_GetDesc(this->dup, &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;
|
uint8_t bpp = 4;
|
||||||
switch(dupDesc.ModeDesc.Format)
|
switch(dupDesc.ModeDesc.Format)
|
||||||
|
|||||||
@@ -394,8 +394,8 @@ static void updateDamageRects(CaptureFrame * frame)
|
|||||||
for(unsigned int i = 0; i < ARRAY_LENGTH(ds); ++i)
|
for(unsigned int i = 0; i < ARRAY_LENGTH(ds); ++i)
|
||||||
ds[i].use = (bool)this->diffMap[i];
|
ds[i].use = (bool)this->diffMap[i];
|
||||||
|
|
||||||
// reduce the number of reusuting rectangles by filling in holes and merging
|
// reduce the number of resulting rectangles by filling in holes and merging
|
||||||
// iiregular shapes into contiguous rectangles
|
// irregular shapes into contiguous rectangles
|
||||||
bool resolve;
|
bool resolve;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@@ -447,8 +447,8 @@ static void updateDamageRects(CaptureFrame * frame)
|
|||||||
bool ok = true;
|
bool ok = true;
|
||||||
if (x > 0 && ds[l].id != ds[u].id)
|
if (x > 0 && ds[l].id != ds[u].id)
|
||||||
{
|
{
|
||||||
// no need to use dsFind as the search order ensures that the id has
|
// no need to use dsFind as the search order ensures that the id of
|
||||||
// been fully resolved of the block above
|
// the block above has been fully resolved
|
||||||
for(unsigned int j = ds[ds[u].id].x1; j <= ds[ds[u].id].x2; ++j)
|
for(unsigned int j = ds[ds[u].id].x1; j <= ds[ds[u].id].x2; ++j)
|
||||||
if (!ds[y * w + j].use)
|
if (!ds[y * w + j].use)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user