Compare commits

..

1 Commits

Author SHA1 Message Date
Geoffrey McRae
655c993c5b [client] main: add new option for integer only upscaling
The new option `win:intUpscale` will limit upscaling to integer sizes
only if enabled.
2022-05-09 14:05:04 +10:00
121 changed files with 2187 additions and 8672 deletions

4
.github/CODEOWNERS vendored
View File

@@ -1,4 +0,0 @@
# Jonathan Rubenstein (JJRcop)
# - Primary documentation manager. Does not require direct approval for every
# - change, but should be consulted for large additions and changes.
docs/ jrubcop@gmail.com

View File

@@ -12,7 +12,7 @@ jobs:
wayland_shell: [xdg-shell, libdecor]
build_type: [Release, Debug]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v1
with:
submodules: recursive
- name: Install libdecor PPA
@@ -62,7 +62,7 @@ jobs:
module:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v1
with:
submodules: recursive
- name: Build kernel module
@@ -73,7 +73,7 @@ jobs:
host-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v1
with:
submodules: recursive
- name: Install PipeWire repository
@@ -99,7 +99,7 @@ jobs:
host-windows-cross:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v1
with:
submodules: recursive
- name: Update apt
@@ -130,7 +130,7 @@ jobs:
host-windows-native:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v1
with:
submodules: recursive
- name: Configure Windows host for native MinGW-w64
@@ -153,7 +153,7 @@ jobs:
matrix:
cc: [gcc, clang]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v1
with:
submodules: recursive
- name: Update apt

View File

@@ -60,8 +60,3 @@ Johnathon Paul Weaver <weaver123_johnathon@hotmail.com> (8BallBomBom)
Chris Spencer <spencercw@gmail.com> (spencercw)
Mark Boorer <markboo99@gmail.com> (Shootfast)
babbaj <babbaj45@gmail.com> (Babbaj)
Matthew McMullin <matthew@mcmullin.one> (matthewjmc)
Leonard Fricke <leonard.fricke98@gmail.com> (Leo1998)
David Meier <meier_david_91@hotmail.com> (Kenny.ch)
Daniel Cordero <looking-glass@0xdc.io> (0xdc)
esi <git@esibun.net> (esibun)

View File

@@ -4,13 +4,13 @@ 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"
)
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/")
@@ -66,7 +66,7 @@ add_compile_options(
"-Wextra"
"-Wno-sign-compare"
"-Wno-unused-parameter"
"$<$<COMPILE_LANGUAGE:C>:-Wstrict-prototypes>"
"-Wstrict-prototypes"
"$<$<C_COMPILER_ID:GNU>:-Wimplicit-fallthrough=2>"
"-Werror"
"-Wfatal-errors"
@@ -101,57 +101,52 @@ add_definitions(-D ATOMIC_LOCKING)
add_definitions(-D GL_GLEXT_PROTOTYPES)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/version.c
${CMAKE_BINARY_DIR}/_version.c
COMMAND ${CMAKE_COMMAND} -D PROJECT_TOP=${PROJECT_TOP} -P
${PROJECT_TOP}/version.cmake
OUTPUT ${CMAKE_BINARY_DIR}/version.c
${CMAKE_BINARY_DIR}/_version.c
COMMAND ${CMAKE_COMMAND} -D PROJECT_TOP=${PROJECT_TOP} -P
${PROJECT_TOP}/version.cmake
)
include_directories(
${PROJECT_TOP}
${PROJECT_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}
${CMAKE_BINARY_DIR}/include
${PROJECT_TOP}/repos/nanosvg/src
${PROJECT_TOP}
${PROJECT_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
)
link_libraries(
${CMAKE_DL_LIBS}
rt
m
${CMAKE_DL_LIBS}
rt
m
)
set(SOURCES
${CMAKE_BINARY_DIR}/version.c
src/main.c
src/core.c
src/app.c
src/audio.c
src/config.c
src/keybind.c
src/util.c
src/clipboard.c
src/kb.c
src/gl_dynprocs.c
src/egl_dynprocs.c
src/eglutil.c
src/overlay_utils.c
src/render_queue.c
${CMAKE_BINARY_DIR}/version.c
src/main.c
src/core.c
src/app.c
src/audio.c
src/config.c
src/keybind.c
src/util.c
src/clipboard.c
src/kb.c
src/gl_dynprocs.c
src/egl_dynprocs.c
src/eglutil.c
src/overlay_utils.c
src/overlay/splash.c
src/overlay/alert.c
src/overlay/fps.c
src/overlay/graphs.c
src/overlay/help.c
src/overlay/config.c
src/overlay/msg.c
src/overlay/status.c
src/overlay/alert.c
src/overlay/fps.c
src/overlay/graphs.c
src/overlay/help.c
src/overlay/config.c
src/overlay/msg.c
src/overlay/record.c
)
# Force cimgui to build as a static library.
set(IMGUI_STATIC "yes" CACHE STRING "Build as a static library")
add_subdirectory("${PROJECT_TOP}/resources" "${CMAKE_BINARY_DIR}/resources")
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common" )
add_subdirectory("${PROJECT_TOP}/repos/LGMP/lgmp" "${CMAKE_BINARY_DIR}/LGMP" )
add_subdirectory("${PROJECT_TOP}/repos/PureSpice" "${CMAKE_BINARY_DIR}/PureSpice")
@@ -165,29 +160,28 @@ add_executable(looking-glass-client ${SOURCES})
target_compile_definitions(looking-glass-client PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1)
target_link_libraries(looking-glass-client
${EXE_FLAGS}
PkgConfig::FONTCONFIG
lg_resources
lg_common
displayservers
lgmp
purespice
renderers
cimgui
${EXE_FLAGS}
PkgConfig::FONTCONFIG
lg_common
displayservers
lgmp
purespice
renderers
cimgui
)
if (ENABLE_PIPEWIRE OR ENABLE_PULSEAUDIO)
add_definitions(-D ENABLE_AUDIO)
add_subdirectory(audiodevs)
pkg_check_modules(SAMPLERATE REQUIRED IMPORTED_TARGET samplerate)
target_link_libraries(looking-glass-client
PkgConfig::SAMPLERATE
audiodevs
)
add_definitions(-D ENABLE_AUDIO)
add_subdirectory(audiodevs)
pkg_check_modules(SAMPLERATE REQUIRED IMPORTED_TARGET samplerate)
target_link_libraries(looking-glass-client
PkgConfig::SAMPLERATE
audiodevs
)
endif()
install(TARGETS looking-glass-client
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT binary)
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT binary)
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)

View File

@@ -3,19 +3,19 @@ project(audiodev_PipeWire LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(AUDIODEV_PipeWire REQUIRED IMPORTED_TARGET
libpipewire-0.3
libpipewire-0.3
)
add_library(audiodev_PipeWire STATIC
pipewire.c
pipewire.c
)
target_link_libraries(audiodev_PipeWire
PkgConfig::AUDIODEV_PipeWire
lg_common
PkgConfig::AUDIODEV_PipeWire
lg_common
)
target_include_directories(audiodev_PipeWire
PRIVATE
src
PRIVATE
src
)

View File

@@ -3,19 +3,19 @@ project(audiodev_PulseAudio LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(AUDIODEV_PulseAudio REQUIRED IMPORTED_TARGET
libpulse
libpulse
)
add_library(audiodev_PulseAudio STATIC
pulseaudio.c
pulseaudio.c
)
target_link_libraries(audiodev_PulseAudio
PkgConfig::AUDIODEV_PulseAudio
lg_common
PkgConfig::AUDIODEV_PulseAudio
lg_common
)
target_include_directories(audiodev_PulseAudio
PRIVATE
src
PRIVATE
src
)

View File

@@ -3,99 +3,99 @@ project(displayserver_Wayland LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(DISPLAYSERVER_Wayland REQUIRED IMPORTED_TARGET
wayland-client
wayland-cursor
xkbcommon
wayland-client
wayland-cursor
xkbcommon
)
set(DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES "")
set(displayserver_Wayland_SHELL_SRC "")
if (ENABLE_LIBDECOR)
pkg_check_modules(DISPLAYSERVER_Wayland_LIBDECOR REQUIRED IMPORTED_TARGET
libdecor-0
)
list(APPEND DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES PkgConfig::DISPLAYSERVER_Wayland_LIBDECOR)
list(APPEND displayserver_Wayland_SHELL_SRC shell_libdecor.c)
add_compile_definitions(ENABLE_LIBDECOR)
pkg_check_modules(DISPLAYSERVER_Wayland_LIBDECOR REQUIRED IMPORTED_TARGET
libdecor-0
)
list(APPEND DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES PkgConfig::DISPLAYSERVER_Wayland_LIBDECOR)
list(APPEND displayserver_Wayland_SHELL_SRC shell_libdecor.c)
add_compile_definitions(ENABLE_LIBDECOR)
else()
list(APPEND displayserver_Wayland_SHELL_SRC shell_xdg.c)
list(APPEND displayserver_Wayland_SHELL_SRC shell_xdg.c)
endif()
add_library(displayserver_Wayland STATIC
activation.c
clipboard.c
cursor.c
gl.c
idle.c
input.c
output.c
poll.c
presentation.c
state.c
registry.c
wayland.c
window.c
${displayserver_Wayland_SHELL_SRC}
activation.c
clipboard.c
cursor.c
gl.c
idle.c
input.c
output.c
poll.c
presentation.c
state.c
registry.c
wayland.c
window.c
${displayserver_Wayland_SHELL_SRC}
)
target_link_libraries(displayserver_Wayland
PkgConfig::DISPLAYSERVER_Wayland
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
lg_common
PkgConfig::DISPLAYSERVER_Wayland
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
lg_common
)
target_include_directories(displayserver_Wayland
PRIVATE
src
PRIVATE
src
)
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
macro(wayland_generate protocol_file output_file)
add_custom_command(OUTPUT "${output_file}.h"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" client-header "${protocol_file}" "${output_file}.h"
DEPENDS "${protocol_file}"
VERBATIM)
add_custom_command(OUTPUT "${output_file}.h"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" client-header "${protocol_file}" "${output_file}.h"
DEPENDS "${protocol_file}"
VERBATIM)
add_custom_command(OUTPUT "${output_file}.c"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c"
DEPENDS "${protocol_file}"
VERBATIM)
add_custom_command(OUTPUT "${output_file}.c"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c"
DEPENDS "${protocol_file}"
VERBATIM)
target_sources(displayserver_Wayland PRIVATE "${output_file}.h" "${output_file}.c")
target_sources(displayserver_Wayland PRIVATE "${output_file}.h" "${output_file}.c")
endmacro()
set(WAYLAND_PROTOCOLS_BASE "${PROJECT_TOP}/repos/wayland-protocols")
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/wayland")
include_directories("${CMAKE_BINARY_DIR}/wayland")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol")
"${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol")
"${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol")
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-relative-pointer-unstable-v1-client-protocol")
"${WAYLAND_PROTOCOLS_BASE}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-relative-pointer-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-pointer-constraints-unstable-v1-client-protocol")
"${WAYLAND_PROTOCOLS_BASE}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-pointer-constraints-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol")
"${WAYLAND_PROTOCOLS_BASE}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
"${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol")
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol")
"${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol")

View File

@@ -512,13 +512,6 @@ error:
free(data);
}
static void dataSourceHandleTarget(void * data, struct wl_data_source * source,
const char * mimetype)
{
// Certain Wayland clients send this for copy-paste operations even though
// it only makes sense for drag-and-drop. We just do nothing.
}
static void dataSourceHandleSend(void * data, struct wl_data_source * source,
const char * mimetype, int fd)
{
@@ -554,8 +547,7 @@ static void dataSourceHandleCancelled(void * data,
}
static const struct wl_data_source_listener dataSourceListener = {
.target = dataSourceHandleTarget,
.send = dataSourceHandleSend,
.send = dataSourceHandleSend,
.cancelled = dataSourceHandleCancelled,
};

View File

@@ -88,13 +88,10 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
if (wlWm.needsResize)
{
bool skipResize = false;
wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(wlWm.width * wlWm.scale),
wl_fixed_to_int(wlWm.height * wlWm.scale), 0, 0);
if (wlWm.width == 0 || wlWm.height == 0)
skipResize = true;
else if (wlWm.fractionalScale)
if (wlWm.fractionalScale)
{
wl_surface_set_buffer_scale(wlWm.surface, 1);
if (!wlWm.viewport)
@@ -128,7 +125,7 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
(struct Border) {0, 0, 0, 0});
app_invalidateWindow(true);
waylandStopWaitFrame();
wlWm.needsResize = skipResize;
wlWm.needsResize = false;
}
waylandShellAckConfigureIfNeeded();

View File

@@ -209,17 +209,6 @@ done:
close(fd);
}
static int getCharcode(uint32_t key)
{
key += 8; // xkb scancode is evdev scancode + 8
xkb_keysym_t sym = xkb_state_key_get_one_sym(wlWm.xkbState, key);
if (sym == XKB_KEY_NoSymbol)
return 0;
sym = xkb_keysym_to_upper(sym);
return xkb_keysym_to_utf32(sym);
}
static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
uint32_t serial, struct wl_surface * surface, struct wl_array * keys)
{
@@ -232,7 +221,7 @@ static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
uint32_t * key;
wl_array_for_each(key, keys)
app_handleKeyPress(*key, getCharcode(*key));
app_handleKeyPress(*key);
}
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
@@ -253,9 +242,9 @@ static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard,
return;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
app_handleKeyPress(key, getCharcode(key));
app_handleKeyPress(key);
else
app_handleKeyRelease(key, getCharcode(key));
app_handleKeyRelease(key);
if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED)
return;
@@ -465,7 +454,7 @@ void waylandGrabPointer(void)
INTERLOCKED_SECTION(wlWm.confineLock,
{
if (!wlWm.confinedPointer && !wlWm.lockedPointer)
if (!wlWm.confinedPointer)
{
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,

View File

@@ -154,7 +154,6 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
struct LG_DisplayServerOps LGDS_Wayland =
{
.name = "Wayland",
.setup = waylandSetup,
.probe = waylandProbe,
.earlyInit = waylandEarlyInit,

View File

@@ -3,30 +3,29 @@ project(displayserver_X11 LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(DISPLAYSERVER_X11 REQUIRED IMPORTED_TARGET
x11
xi
xfixes
xscrnsaver
xinerama
xcursor
xpresent
xkbcommon
x11
xi
xfixes
xscrnsaver
xinerama
xcursor
xpresent
)
add_library(displayserver_X11 STATIC
x11.c
atoms.c
clipboard.c
x11.c
atoms.c
clipboard.c
)
add_definitions(-D GLX_GLXEXT_PROTOTYPES)
target_link_libraries(displayserver_X11
PkgConfig::DISPLAYSERVER_X11
lg_common
PkgConfig::DISPLAYSERVER_X11
lg_common
)
target_include_directories(displayserver_X11
PRIVATE
src
PRIVATE
src
)

View File

@@ -38,7 +38,6 @@
DEF_ATOM(_NET_WM_WINDOW_TYPE, True) \
DEF_ATOM(_NET_WM_WINDOW_TYPE_NORMAL, True) \
DEF_ATOM(_NET_WM_WINDOW_TYPE_UTILITY, True) \
DEF_ATOM(_NET_WM_PID, True) \
DEF_ATOM(WM_DELETE_WINDOW, True) \
DEF_ATOM(_MOTIF_WM_HINTS, True) \
\

View File

@@ -36,8 +36,6 @@
#include <X11/extensions/Xpresent.h>
#include <X11/Xcursor/Xcursor.h>
#include <xkbcommon/xkbcommon.h>
#include <GL/glx.h>
#include <GL/glxext.h>
@@ -192,14 +190,14 @@ static void x11CheckEWMHSupport(void)
if (XGetWindowProperty(x11.display, DefaultRootWindow(x11.display),
x11atoms._NET_SUPPORTING_WM_CHECK, 0, ~0L, False, XA_WINDOW,
&type, &fmt, &num, &bytes, &data) != Success || !data)
&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 || !data)
&type, &fmt, &num, &bytes, &data) != Success)
goto out_root;
Window * windowFromChild = (Window *)data;
@@ -208,7 +206,7 @@ static void x11CheckEWMHSupport(void)
if (XGetWindowProperty(x11.display, DefaultRootWindow(x11.display),
x11atoms._NET_SUPPORTED, 0, ~0L, False, AnyPropertyType,
&type, &fmt, &num, &bytes, &data) != Success || !data)
&type, &fmt, &num, &bytes, &data) != Success)
goto out_child;
Atom * supported = (Atom *)data;
@@ -216,7 +214,7 @@ static void x11CheckEWMHSupport(void)
if (XGetWindowProperty(x11.display, *windowFromRoot,
x11atoms._NET_WM_NAME, 0, ~0L, False, AnyPropertyType,
&type, &fmt, &num, &bytes, &data) != Success || !data)
&type, &fmt, &num, &bytes, &data) != Success)
goto out_supported;
char * wmName = (char *)data;
@@ -350,21 +348,6 @@ static bool x11Init(const LG_DSInitParams params)
// check for Extended Window Manager Hints support
x11CheckEWMHSupport();
if (x11atoms._NET_WM_PID)
{
pid_t pid = getpid();
XChangeProperty(
x11.display,
x11.window,
x11atoms._NET_WM_PID,
XA_CARDINAL,
32,
PropModeReplace,
(unsigned char *)&pid,
1
);
}
if (params.borderless)
{
if (x11atoms._MOTIF_WM_HINTS)
@@ -415,14 +398,23 @@ static bool x11Init(const LG_DSInitParams params)
);
}
Atom wmState[3] = {0};
int wmStateCount = 0;
if (params.fullscreen)
{
x11.fullscreen = true;
wmState[wmStateCount++] = x11atoms._NET_WM_STATE_FULLSCREEN;
}
if (params.maximize)
{
Atom wmState[2] =
{
x11atoms._NET_WM_STATE_MAXIMIZED_HORZ,
x11atoms._NET_WM_STATE_MAXIMIZED_VERT
};
wmState[wmStateCount++] = x11atoms._NET_WM_STATE_MAXIMIZED_HORZ;
wmState[wmStateCount++] = x11atoms._NET_WM_STATE_MAXIMIZED_VERT;
}
if (wmStateCount)
{
XChangeProperty(
x11.display,
x11.window,
@@ -431,7 +423,7 @@ static bool x11Init(const LG_DSInitParams params)
32,
PropModeReplace,
(unsigned char *)&wmState,
2
wmStateCount
);
}
@@ -531,10 +523,6 @@ static bool x11Init(const LG_DSInitParams params)
goto fail_window;
}
XDisplayKeycodes(x11.display, &x11.minKeycode, &x11.maxKeycode);
x11.keysyms = XGetKeyboardMapping(x11.display, x11.minKeycode,
x11.maxKeycode - x11.minKeycode, &x11.symsPerKeycode);
XIFreeDeviceInfo(devinfo);
XQueryExtension(x11.display, "XInputExtension", &x11.xinputOp, &event, &error);
@@ -676,9 +664,6 @@ static bool x11Init(const LG_DSInitParams params)
if (!params.center)
XMoveWindow(x11.display, x11.window, params.x, params.y);
if (params.fullscreen)
x11SetFullscreen(true);
XSetLocaleModifiers(""); // Load XMODIFIERS
x11.xim = XOpenIM(x11.display, 0, 0, 0);
@@ -759,9 +744,6 @@ static void x11Free(void)
if (x11.cursors[i])
XFreeCursor(x11.display, x11.cursors[i]);
if (x11.keysyms)
XFree(x11.keysyms);
XCloseDisplay(x11.display);
}
@@ -1059,17 +1041,6 @@ static void setFocus(bool focused, double x, double y)
app_handleFocusEvent(focused);
}
static int getCharcode(int detail)
{
if (detail < x11.minKeycode || detail > x11.maxKeycode)
return 0;
KeySym sym = x11.keysyms[(detail - x11.minKeycode) *
x11.symsPerKeycode];
sym = xkb_keysym_to_upper(sym);
return xkb_keysym_to_utf32(sym);
}
static void x11XInputEvent(XGenericEventCookie *cookie)
{
static int button_state = 0;
@@ -1161,8 +1132,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIDeviceEvent *device = cookie->data;
app_handleKeyPress(device->detail - x11.minKeycode,
getCharcode(device->detail));
app_handleKeyPress(device->detail - 8);
if (!x11.xic || !app_isOverlayMode())
return;
@@ -1212,8 +1182,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIDeviceEvent *device = cookie->data;
app_handleKeyRelease(device->detail - x11.minKeycode,
getCharcode(device->detail));
app_handleKeyRelease(device->detail - 8);
if (!x11.xic || !app_isOverlayMode())
return;
@@ -1242,8 +1211,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIRawEvent *raw = cookie->data;
app_handleKeyPress(raw->detail - x11.minKeycode,
getCharcode(raw->detail));
app_handleKeyPress(raw->detail - 8);
return;
}
@@ -1253,8 +1221,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIRawEvent *raw = cookie->data;
app_handleKeyRelease(raw->detail - x11.minKeycode,
getCharcode(raw->detail));
app_handleKeyRelease(raw->detail - 8);
return;
}
@@ -1937,7 +1904,6 @@ static void x11Minimize(void)
struct LG_DisplayServerOps LGDS_X11 =
{
.name = "X11",
.setup = x11Setup,
.probe = x11Probe,
.earlyInit = x11EarlyInit,

View File

@@ -54,10 +54,6 @@ struct X11DSState
Window window;
XVisualInfo * visual;
int minKeycode, maxKeycode;
int symsPerKeycode;
KeySym * keysyms;
//Extended Window Manager Hints
//ref: https://specifications.freedesktop.org/wm-spec/latest/
bool ewmhSupport;

View File

@@ -58,9 +58,9 @@ void app_resyncMouseBasic(void);
void app_handleButtonPress(int button);
void app_handleButtonRelease(int button);
void app_handleWheelMotion(double motion);
void app_handleKeyPress(int scancode);
void app_handleKeyRelease(int scancode);
void app_handleKeyboardTyped(const char * typed);
void app_handleKeyPress(int scancode, int charcode);
void app_handleKeyRelease(int scancode, int charcode);
void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super);
void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock);
void app_handleEnterEvent(bool entered);
@@ -155,14 +155,12 @@ void app_showRecord(bool show);
/**
* Register a handler for the <super>+<key> combination
* @param sc The scancode to register
* @param charcode The charcode to register (used instead of sc if non zero)
* @param callback The function to be called when the combination is pressed
* @param opaque A pointer to be passed to the callback, may be NULL
* @retval A handle for the binding or NULL on failure.
* The caller is required to release the handle via `app_releaseKeybind` when it is no longer required
*/
KeybindHandle app_registerKeybind(int sc, int charcode, KeybindFn callback,
void * opaque, const char * description);
KeybindHandle app_registerKeybind(int sc, KeybindFn callback, void * opaque, const char * description);
/**
* Release an existing key binding
@@ -181,14 +179,5 @@ bool app_guestIsOSX(void);
bool app_guestIsBSD(void);
bool app_guestIsOther(void);
/**
* Enable/disable the LG display
*/
void app_stopVideo(bool stop);
/**
* Enable/disable the spice display
*/
bool app_useSpiceDisplay(bool enable);
#endif

View File

@@ -110,8 +110,6 @@ typedef struct LGEvent LGEvent;
struct LG_DisplayServerOps
{
const char * name;
/* called before options are parsed, useful for registering options */
void (*setup)(void);

View File

@@ -25,8 +25,6 @@
#include "common/types.h"
#define TICK_RATE 25
struct LG_OverlayOps
{
/* internal name of the overlay for debugging */
@@ -65,7 +63,7 @@ struct LG_OverlayOps
int (*render)(void * udata, bool interactive, struct Rect * windowRects,
int maxRects);
/* called TICK_RATE times a second by the application
/* called 25 times a second by the application
*
* Note: This may not run in the same context as `render`!
*

View File

@@ -27,22 +27,17 @@
#include "common/framebuffer.h"
#define IS_LG_RENDERER_VALID(x) \
((x)->getName && \
(x)->create && \
(x)->initialize && \
(x)->deinitialize && \
(x)->onRestart && \
(x)->onResize && \
(x)->onMouseShape && \
(x)->onMouseEvent && \
(x)->renderStartup && \
(x)->render && \
(x)->createTexture && \
(x)->freeTexture && \
(x)->spiceConfigure && \
(x)->spiceDrawFill && \
(x)->spiceDrawBitmap && \
(x)->spiceShow)
((x)->getName && \
(x)->create && \
(x)->initialize && \
(x)->deinitialize && \
(x)->onRestart && \
(x)->onResize && \
(x)->onMouseShape && \
(x)->onMouseEvent && \
(x)->renderStartup && \
(x)->needsRender && \
(x)->render)
typedef struct LG_RendererParams
{
@@ -163,36 +158,15 @@ typedef struct LG_RendererOps
* Context: renderThread */
bool (*renderStartup)(LG_Renderer * renderer, bool useDMA);
/* returns if the render method must be called even if nothing has changed.
* Context: renderThread */
bool (*needsRender)(LG_Renderer * renderer);
/* called to render the scene
* Context: renderThread */
bool (*render)(LG_Renderer * renderer, LG_RendererRotate rotate,
const bool newFrame, const bool invalidateWindow,
void (*preSwap)(void * udata), void * udata);
/* called to create a texture from the specified 32-bit RGB image data. This
* method is for use with Dear ImGui
* Context: renderThread */
void * (*createTexture)(LG_Renderer * renderer,
int width, int height, uint8_t * data);
/* called to free a texture previously created by createTexture. This method
* is for use with Dear ImGui
* Context: renderThread */
void (*freeTexture)(LG_Renderer * renderer, void * texture);
/* setup the spice display */
void (*spiceConfigure)(LG_Renderer * renderer, int width, int height);
/* draw a filled rect on the spice display with the specified color */
void (*spiceDrawFill)(LG_Renderer * renderer, int x, int y, int width,
int height, uint32_t color);
/* draw an image on the spice display, data is RGBA32 */
void (*spiceDrawBitmap)(LG_Renderer * renderer, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown);
/* show the spice display */
void (*spiceShow)(LG_Renderer * renderer, bool show);
}
LG_RendererOps;

View File

@@ -27,23 +27,9 @@
typedef struct ImVec2 ImVec2;
typedef struct
{
void * tex;
int width;
int height;
}
OverlayImage;
void overlayGetImGuiRect(struct Rect * rect);
ImVec2 * overlayGetScreenSize(void);
void overlayTextURL(const char * url, const char * text);
void overlayTextMaybeURL(const char * text, bool wrapped);
// create a texture from a SVG and scale it to fit the supplied width & height
bool overlayLoadSVG(const char * data, unsigned int size, OverlayImage * image,
int width, int height);
void overlayFreeImage(OverlayImage * image);
#endif

View File

@@ -3,114 +3,120 @@ project(renderer_EGL LANGUAGES C CXX)
find_package(PkgConfig)
pkg_check_modules(RENDERER_EGL REQUIRED IMPORTED_TARGET
egl
gl
egl
gl
)
pkg_check_modules(RENDERER_EGL_OPT IMPORTED_TARGET
wayland-egl
wayland-egl
)
find_program(AWK NAMES gawk mawk original-awk awk)
if(AWK MATCHES ".+-NOTFOUND")
message(FATAL_ERROR "FATAL: some known version of awk couldn't be found (${AWK}).")
message(FATAL_ERROR "FATAL: some known version of awk couldn't be found (${AWK}).")
else()
message(STATUS "Using awk: ${AWK}")
message(STATUS "Using awk: ${AWK}")
endif()
include(MakeObject)
function(build_shaders header_dir)
file(GLOB headers "${header_dir}/*.h")
set(EGL_SHADER_PROCESSED)
foreach(shader ${ARGN})
set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${shader}")
add_custom_command(OUTPUT "${out_f}"
COMMAND "${AWK}" -f "${CMAKE_CURRENT_SOURCE_DIR}/glsl.include.awk"
"${CMAKE_CURRENT_SOURCE_DIR}/${shader}" > "${out_f}"
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/${shader}"
DEPENDS ${headers}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/shader"
COMMENT "Preprocessing shader ${shader}"
VERBATIM
)
endforeach()
file(GLOB headers "${header_dir}/*.h")
set(EGL_SHADER_PROCESSED)
foreach(shader ${ARGN})
set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${shader}")
add_custom_command(OUTPUT "${out_f}"
COMMAND "${AWK}" -f "${CMAKE_CURRENT_SOURCE_DIR}/glsl.include.awk"
"${CMAKE_CURRENT_SOURCE_DIR}/${shader}" > "${out_f}"
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/${shader}"
DEPENDS ${headers}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/shader"
COMMENT "Preprocessing shader ${shader}"
VERBATIM
)
endforeach()
set(CMAKE_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
make_object(
EGL_SHADER
${ARGN}
)
set(CMAKE_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
make_object(
EGL_SHADER
${ARGN}
)
set(EGL_SHADER_OBJS "${EGL_SHADER_OBJS}" PARENT_SCOPE)
set(EGL_SHADER_INCS "${EGL_SHADER_INCS}" PARENT_SCOPE)
set(EGL_SHADER_OBJS "${EGL_SHADER_OBJS}" PARENT_SCOPE)
set(EGL_SHADER_INCS "${EGL_SHADER_INCS}" PARENT_SCOPE)
endfunction()
build_shaders(
shader
shader/desktop.vert
shader/desktop_rgb.frag
shader/cursor.vert
shader/cursor_rgb.frag
shader/cursor_mono.frag
shader/damage.vert
shader/damage.frag
shader/basic.vert
shader/ffx_cas.frag
shader/ffx_fsr1_easu.frag
shader/ffx_fsr1_rcas.frag
shader/downscale.frag
shader/downscale_lanczos2.frag
shader/downscale_linear.frag
shader
shader/desktop.vert
shader/desktop_rgb.frag
shader/cursor.vert
shader/cursor_rgb.frag
shader/cursor_mono.frag
shader/damage.vert
shader/damage.frag
shader/splash_bg.vert
shader/splash_bg.frag
shader/splash_logo.vert
shader/splash_logo.frag
shader/basic.vert
shader/ffx_cas.frag
shader/ffx_fsr1_easu.frag
shader/ffx_fsr1_rcas.frag
shader/downscale.frag
shader/downscale_lanczos2.frag
shader/downscale_linear.frag
)
make_defines(
"${CMAKE_CURRENT_SOURCE_DIR}/shader/desktop_rgb.frag"
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
"${CMAKE_CURRENT_SOURCE_DIR}/shader/desktop_rgb.frag"
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
)
add_library(renderer_EGL STATIC
egl.c
egldebug.c
shader.c
texture_util.c
texture.c
texture_buffer.c
texture_framebuffer.c
texture_dmabuf.c
model.c
desktop.c
desktop_rects.c
cursor.c
damage.c
framebuffer.c
postprocess.c
ffx.c
filter.c
filter_ffx_cas.c
filter_ffx_fsr1.c
filter_downscale.c
${EGL_SHADER_OBJS}
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl3.cpp
egl.c
egldebug.c
shader.c
texture_util.c
texture.c
texture_buffer.c
texture_framebuffer.c
texture_dmabuf.c
model.c
desktop.c
desktop_rects.c
cursor.c
draw.c
splash.c
damage.c
framebuffer.c
postprocess.c
ffx.c
filter.c
filter_ffx_cas.c
filter_ffx_fsr1.c
filter_downscale.c
${EGL_SHADER_OBJS}
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl3.cpp
)
target_compile_definitions(renderer_EGL PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1 IMGUI_IMPL_OPENGL_ES3)
target_link_libraries(renderer_EGL
PkgConfig::RENDERER_EGL
lg_common
PkgConfig::RENDERER_EGL
lg_common
cimgui
cimgui
)
if(RENDERER_EGL_OPT_FOUND)
target_link_libraries(renderer_EGL
PkgConfig::RENDERER_EGL_OPT
)
target_link_libraries(renderer_EGL
PkgConfig::RENDERER_EGL_OPT
)
endif()
target_include_directories(renderer_EGL
PRIVATE
src
${EGL_SHADER_INCS}
PRIVATE
src
${EGL_SHADER_INCS}
)

View File

@@ -87,7 +87,7 @@ static bool cursorTexInit(struct CursorTex * t,
const char * vertex_code , size_t vertex_size,
const char * fragment_code, size_t fragment_size)
{
if (!egl_textureInit(&t->texture, NULL, EGL_TEXTYPE_BUFFER))
if (!egl_textureInit(&t->texture, NULL, EGL_TEXTYPE_BUFFER, false))
{
DEBUG_ERROR("Failed to initialize the cursor texture");
return false;
@@ -278,7 +278,7 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
cursor->width, cursor->height, sizeof(xor[0]));
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor);
}
// fall through
@@ -286,7 +286,7 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
{
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
cursor->width, cursor->height, cursor->stride);
egl_textureUpdate(cursor->norm.texture, data, true);
egl_textureUpdate(cursor->norm.texture, data);
break;
}
@@ -314,8 +314,8 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
cursor->width, cursor->height, sizeof(and[0]));
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
cursor->width, cursor->height, sizeof(xor[0]));
egl_textureUpdate(cursor->norm.texture, (uint8_t *)and, true);
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
egl_textureUpdate(cursor->norm.texture, (uint8_t *)and);
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor);
break;
}
}

View File

@@ -65,10 +65,6 @@ struct EGL_Desktop
int width, height;
LG_RendererRotate rotate;
bool useSpice;
int spiceWidth, spiceHeight;
EGL_Texture * spiceTexture;
// scale algorithm
int scaleAlgo;
@@ -129,7 +125,7 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
desktop->display = display;
if (!egl_textureInit(&desktop->texture, display,
useDMA ? EGL_TEXTYPE_DMABUF : EGL_TEXTYPE_FRAMEBUFFER))
useDMA ? EGL_TEXTYPE_DMABUF : EGL_TEXTYPE_FRAMEBUFFER, true))
{
DEBUG_ERROR("Failed to initialize the desktop texture");
return false;
@@ -157,7 +153,7 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
return false;
}
app_registerKeybind(0, 'N', toggleNV, desktop,
app_registerKeybind(KEY_N, toggleNV, desktop,
"Toggle night vision mode");
desktop->nvMax = option_get_int("egl", "nvGainMax");
@@ -206,7 +202,6 @@ void egl_desktopFree(EGL_Desktop ** desktop)
return;
egl_textureFree (&(*desktop)->texture );
egl_textureFree (&(*desktop)->spiceTexture );
egl_shaderFree (&(*desktop)->shader.shader);
egl_desktopRectsFree(&(*desktop)->mesh );
countedBufferRelease(&(*desktop)->matrix );
@@ -334,7 +329,7 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dma
egl_textureFree(&desktop->texture);
if (!egl_textureInit(&desktop->texture, desktop->display,
EGL_TEXTYPE_FRAMEBUFFER))
EGL_TEXTYPE_FRAMEBUFFER, true))
{
DEBUG_ERROR("Failed to initialize the desktop texture");
return false;
@@ -364,27 +359,11 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
LG_RendererRotate rotate, const struct DamageRects * rects)
{
EGL_Texture * tex;
int width, height;
if (desktop->useSpice)
{
tex = desktop->spiceTexture;
width = desktop->spiceWidth;
height = desktop->spiceHeight;
}
else
{
tex = desktop->texture;
width = desktop->width;
height = desktop->height;
}
if (outputWidth == 0 && outputHeight == 0)
DEBUG_FATAL("outputWidth || outputHeight == 0");
enum EGL_TexStatus status;
if ((status = egl_textureProcess(tex)) != EGL_TEX_STATUS_OK)
if ((status = egl_textureProcess(desktop->texture)) != EGL_TEX_STATUS_OK)
{
if (status != EGL_TEX_STATUS_NOTREADY)
DEBUG_ERROR("Failed to process the desktop texture");
@@ -393,13 +372,13 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
int scaleAlgo = EGL_SCALE_NEAREST;
egl_desktopRectsMatrix((float *)desktop->matrix->data,
width, height, x, y, scaleX, scaleY, rotate);
egl_desktopRectsUpdate(desktop->mesh, rects, width, height);
desktop->width, desktop->height, x, y, scaleX, scaleY, rotate);
egl_desktopRectsUpdate(desktop->mesh, rects, desktop->width, desktop->height);
if (atomic_exchange(&desktop->processFrame, false) ||
egl_postProcessConfigModified(desktop->pp))
egl_postProcessRun(desktop->pp, tex, desktop->mesh,
width, height, outputWidth, outputHeight);
egl_postProcessRun(desktop->pp, desktop->texture, desktop->mesh,
desktop->width, desktop->height, outputWidth, outputHeight);
unsigned int finalSizeX, finalSizeY;
GLuint texture = egl_postProcessGetOutput(desktop->pp,
@@ -410,9 +389,9 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, tex->sampler);
glBindSampler(0, desktop->texture->sampler);
if (finalSizeX > width || finalSizeY > height)
if (finalSizeX > desktop->width || finalSizeY > desktop->height)
scaleType = EGL_DESKTOP_DOWNSCALE;
switch (desktop->scaleAlgo)
@@ -446,7 +425,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
{
.type = EGL_UNIFORM_TYPE_2F,
.location = shader->uDesktopSize,
.f = { width, height },
.f = { desktop->width, desktop->height },
},
{
.type = EGL_UNIFORM_TYPE_M3x2FV,
@@ -472,59 +451,3 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height)
{
if (!desktop->spiceTexture)
if (!egl_textureInit(&desktop->spiceTexture, desktop->display,
EGL_TEXTYPE_BUFFER_MAP))
{
DEBUG_ERROR("Failed to initialize the spice desktop texture");
return;
}
if (!egl_textureSetup(
desktop->spiceTexture,
EGL_PF_BGRA,
width,
height,
width * 4
))
{
DEBUG_ERROR("Failed to setup the spice desktop texture");
return;
}
desktop->spiceWidth = width;
desktop->spiceHeight = height;
}
void egl_desktopSpiceDrawFill(EGL_Desktop * desktop, int x, int y, int width,
int height, uint32_t color)
{
/* this is a fairly hacky way to do this, but since it's only for the fallback
* spice display it's not really an issue */
uint32_t line[width];
for(int x = 0; x < width; ++x)
line[x] = color;
for(; y < height; ++y)
egl_textureUpdateRect(desktop->spiceTexture,
x, y, width, 1, sizeof(line), (uint8_t *)line, false);
atomic_store(&desktop->processFrame, true);
}
void egl_desktopSpiceDrawBitmap(EGL_Desktop * desktop, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown)
{
egl_textureUpdateRect(desktop->spiceTexture,
x, y, width, height, stride, data, topDown);
atomic_store(&desktop->processFrame, true);
}
void egl_desktopSpiceShow(EGL_Desktop * desktop, bool show)
{
desktop->useSpice = show;
}

View File

@@ -50,10 +50,3 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
unsigned int outputHeight, const float x, const float y,
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
LG_RendererRotate rotate, const struct DamageRects * rects);
void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height);
void egl_desktopSpiceDrawFill(EGL_Desktop * desktop, int x, int y, int width,
int height, uint32_t color);
void egl_desktopSpiceDrawBitmap(EGL_Desktop * desktop, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown);
void egl_desktopSpiceShow(EGL_Desktop * desktop, bool show);

View File

@@ -32,10 +32,6 @@
struct EGL_DesktopRects
{
GLfloat * lastVertices;
int lastVerticesCount;
int lastVerticesSize;
GLuint buffers[2];
GLuint vao;
int count;
@@ -92,7 +88,6 @@ void egl_desktopRectsFree(EGL_DesktopRects ** rects_)
glDeleteVertexArrays(1, &rects->vao);
glDeleteBuffers(2, rects->buffers);
free(rects->lastVertices);
free(rects);
*rects_ = NULL;
}
@@ -118,8 +113,7 @@ void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects *
return;
}
const int count = (!data || data->count < 0 ? 1 : data->count) * 8;
GLfloat vertices[count];
GLfloat vertices[(!data || data->count < 0 ? 1 : data->count) * 8];
if (!data || data->count < 0)
{
FrameDamageRect full = {
@@ -137,30 +131,6 @@ void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects *
rectToVertices(vertices + i * 8, data->rects + i);
}
// check if the value actually changed and needs updating
if (count == rects->lastVerticesCount &&
memcmp(rects->lastVertices, vertices, sizeof(GLfloat) * count) == 0)
return;
// ensure the local storage is large enough
if (count > rects->lastVerticesSize)
{
if (rects->lastVertices)
free(rects->lastVertices);
rects->lastVertices = malloc(sizeof(GLfloat) * count);
if (!rects->lastVertices)
{
DEBUG_ERROR("out of memory");
return;
}
rects->lastVerticesSize = count;
}
// copy the last value for later comparison
rects->lastVerticesCount = count;
memcpy(rects->lastVertices, vertices, sizeof(GLfloat) * count);
glBindBuffer(GL_ARRAY_BUFFER, rects->buffers[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, rects->count * 8 * sizeof(GLfloat), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);

View File

@@ -0,0 +1,69 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "draw.h"
#include <stdlib.h>
#include <math.h>
void egl_drawTorus(EGL_Model * model, unsigned int pts, float x, float y,
float inner, float outer)
{
GLfloat * v = malloc(sizeof(*v) * (pts + 1) * 6);
GLfloat * dst = v;
for(unsigned int i = 0; i <= pts; ++i)
{
const float angle = (i / (float)pts) * M_PI * 2.0f;
const float c = cos(angle);
const float s = sin(angle);
*dst++ = x + (inner * c);
*dst++ = y + (inner * s);
*dst++ = 0.0f;
*dst++ = x + (outer * c);
*dst++ = y + (outer * s);
*dst++ = 0.0f;
}
egl_modelAddVerts(model, v, NULL, (pts + 1) * 2);
free(v);
}
void egl_drawTorusArc(EGL_Model * model, unsigned int pts, float x, float y,
float inner, float outer, float s, float e)
{
GLfloat * v = malloc(sizeof(*v) * (pts + 1) * 6);
GLfloat * dst = v;
for(unsigned int i = 0; i <= pts; ++i)
{
const float angle = s + ((i / (float)pts) * e);
const float c = cos(angle);
const float s = sin(angle);
*dst++ = x + (inner * c);
*dst++ = y + (inner * s);
*dst++ = 0.0f;
*dst++ = x + (outer * c);
*dst++ = y + (outer * s);
*dst++ = 0.0f;
}
egl_modelAddVerts(model, v, NULL, (pts + 1) * 2);
free(v);
}

View File

@@ -0,0 +1,29 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include "model.h"
void egl_drawTorus(EGL_Model * model, unsigned int pts, float x, float y,
float inner, float outer);
void egl_drawTorusArc(EGL_Model * model, unsigned int pts, float x, float y,
float inner, float outer, float s, float e);

View File

@@ -45,9 +45,11 @@
#include "damage.h"
#include "desktop.h"
#include "cursor.h"
#include "splash.h"
#include "postprocess.h"
#include "util.h"
#define SPLASH_FADE_TIME 1000000
#define MAX_BUFFER_AGE 3
#define DESKTOP_DAMAGE_COUNT 4
#define MAX_ACCUMULATED_DAMAGE ((KVMFR_MAX_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2) * MAX_BUFFER_AGE)
@@ -75,11 +77,15 @@ struct Inst
EGL_Desktop * desktop; // the desktop
EGL_Cursor * cursor; // the mouse cursor
EGL_Splash * splash; // the splash screen
EGL_Damage * damage; // the damage display
bool imgui; // if imgui was initialized
LG_RendererFormat format;
bool formatValid;
bool start;
uint64_t waitFadeTime;
bool waitDone;
int width, height;
float uiScale;
@@ -117,9 +123,6 @@ struct Inst
RingBuffer importTimings;
GraphHandle importGraph;
bool showSpice;
int spiceWidth, spiceHeight;
};
static struct Option egl_options[] =
@@ -275,10 +278,12 @@ static void egl_deinitialize(LG_Renderer * renderer)
if (this->imgui)
ImGui_ImplOpenGL3_Shutdown();
app_unregisterGraph(this->importGraph);
ringbuffer_free(&this->importTimings);
egl_desktopFree(&this->desktop);
egl_cursorFree (&this->cursor);
egl_splashFree (&this->splash);
egl_damageFree (&this->damage);
LG_LOCK_FREE(this->lock);
@@ -317,6 +322,7 @@ static void egl_onRestart(LG_Renderer * renderer)
eglDestroyContext(this->display, this->frameContext);
this->frameContext = NULL;
this->start = false;
INTERLOCKED_SECTION(this->desktopDamageLock, {
this->desktopDamage[this->desktopDamageIdx].count = -1;
@@ -325,17 +331,6 @@ static void egl_onRestart(LG_Renderer * renderer)
static void egl_calc_mouse_size(struct Inst * this)
{
if (this->showSpice)
{
this->mouseScaleX = 2.0f / this->spiceWidth;
this->mouseScaleY = 2.0f / this->spiceHeight;
egl_cursorSetSize(this->cursor,
(this->mouseWidth * (1.0f / this->spiceWidth )) * this->scaleX,
(this->mouseHeight * (1.0f / this->spiceHeight)) * this->scaleY
);
return;
}
if (!this->formatValid)
return;
@@ -385,19 +380,6 @@ static void egl_calc_mouse_size(struct Inst * this)
static void egl_calc_mouse_state(struct Inst * this)
{
if (this->showSpice)
{
egl_cursorSetState(
this->cursor,
this->cursorVisible,
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY,
((float)this->cursorHX * this->mouseScaleX) * this->scaleX,
((float)this->cursorHY * this->mouseScaleY) * this->scaleY
);
return;
}
if (!this->formatValid)
return;
@@ -480,8 +462,8 @@ static void egl_onResize(LG_Renderer * renderer, const int width, const int heig
if (destRect.valid)
{
this->translateX = -1.0f + (((this->destRect.w / 2) + this->destRect.x) * 2) / (float)this->width;
this->translateY = 1.0f - (((this->destRect.h / 2) + this->destRect.y) * 2) / (float)this->height;
this->translateX = 1.0f - (((this->destRect.w / 2) + this->destRect.x) * 2) / (float)this->width;
this->translateY = 1.0f - (((this->destRect.h / 2) + this->destRect.y) * 2) / (float)this->height;
this->scaleX = (float)this->destRect.w / (float)this->width;
this->scaleY = (float)this->destRect.h / (float)this->height;
this->viewportWidth = this->destRect.w;
@@ -609,6 +591,8 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int d
}
ringbuffer_push(this->importTimings, &(float){ (nanotime() - start) * 1e-6f });
this->start = true;
INTERLOCKED_SECTION(this->desktopDamageLock, {
struct DesktopDamage * damage = this->desktopDamage + this->desktopDamageIdx;
if (damage->count == -1 || damageRectsCount == 0 ||
@@ -943,6 +927,12 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
return false;
}
if (!egl_splashInit(&this->splash))
{
DEBUG_ERROR("Failed to initialize the splash screen");
return false;
}
if (!egl_damageInit(&this->damage))
{
DEBUG_ERROR("Failed to initialize the damage display");
@@ -961,6 +951,12 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
return true;
}
static bool egl_needsRender(LG_Renderer * renderer)
{
struct Inst * this = UPCAST(struct Inst, renderer);
return !this->waitDone;
}
inline static EGLint egl_bufferAge(struct Inst * this)
{
if (!this->hasBufferAge)
@@ -987,26 +983,21 @@ inline static void renderLetterBox(struct Inst * this)
if (hLB)
{
// left
glScissor(0, 0, this->destRect.x, this->height);
glScissor(0.0f, 0.0f, this->destRect.x + 0.5f, this->height + 0.5f);
glClear(GL_COLOR_BUFFER_BIT);
// right
float x2 = this->destRect.x + this->destRect.w;
glScissor(x2, 0, this->width - x2, this->height);
glScissor(x2 - 0.5f, 0.0f, this->width - x2 + 1.0f, this->height + 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
if (vLB)
{
// top
glScissor(0, this->height - this->destRect.y,
this->width, this->destRect.y);
glScissor(0.0f, 0.0f, this->width + 0.5f, this->destRect.y + 0.5f);
glClear(GL_COLOR_BUFFER_BIT);
// bottom
int y2 = this->destRect.y + this->destRect.h;
glScissor(0, 0, this->width, this->height - y2);
float y2 = this->destRect.y + this->destRect.h;
glScissor(0.0f, y2 - 0.5f, this->width + 1.0f, this->height - y2 + 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
@@ -1020,9 +1011,8 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
{
struct Inst * this = UPCAST(struct Inst, renderer);
EGLint bufferAge = egl_bufferAge(this);
bool renderAll = invalidateWindow || this->hadOverlay ||
bufferAge <= 0 || bufferAge > MAX_BUFFER_AGE ||
this->showSpice;
bool renderAll = invalidateWindow || !this->start || this->hadOverlay ||
bufferAge <= 0 || bufferAge > MAX_BUFFER_AGE;
bool hasOverlay = false;
struct CursorState cursorState = { .visible = false };
@@ -1097,7 +1087,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
}
++this->overlayHistoryIdx;
if (this->destRect.w > 0 && this->destRect.h > 0)
if (this->start && this->destRect.w > 0 && this->destRect.h > 0)
{
if (egl_desktopRender(this->desktop,
this->destRect.w, this->destRect.h,
@@ -1105,6 +1095,14 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
this->scaleX , this->scaleY ,
this->scaleType , rotate, renderAll ? NULL : accumulated))
{
if (!this->waitFadeTime)
{
if (!this->params.quickSplash)
this->waitFadeTime = microtime() + SPLASH_FADE_TIME;
else
this->waitDone = true;
}
cursorState = egl_cursorRender(this->cursor,
(this->format.rotate + rotate) % LG_ROTATE_MAX,
this->width, this->height);
@@ -1115,6 +1113,35 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
renderLetterBox(this);
if (!this->waitDone)
{
float a = 1.0f;
if (!this->waitFadeTime)
a = 1.0f;
else
{
uint64_t t = microtime();
if (t > this->waitFadeTime)
this->waitDone = true;
else
{
uint64_t delta = this->waitFadeTime - t;
a = 1.0f / SPLASH_FADE_TIME * delta;
}
}
if (!this->waitDone)
{
egl_splashRender(this->splash, a, this->splashRatio);
hasOverlay = true;
}
}
else if (!this->start)
{
egl_splashRender(this->splash, 1.0f, this->splashRatio);
hasOverlay = true;
}
hasOverlay |= egl_damageRender(this->damage, rotate, newFrame ? desktopDamage : NULL);
hasOverlay |= invalidateWindow;
@@ -1180,72 +1207,6 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
return true;
}
static void * egl_createTexture(LG_Renderer * renderer,
int width, int height, uint8_t * data)
{
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
data);
glBindTexture(GL_TEXTURE_2D, 0);
return (void*)(intptr_t)tex;
}
static void egl_freeTexture(LG_Renderer * renderer, void * texture)
{
GLuint tex = (GLuint)(intptr_t)texture;
glDeleteTextures(1, &tex);
}
static void egl_spiceConfigure(LG_Renderer * renderer, int width, int height)
{
struct Inst * this = UPCAST(struct Inst, renderer);
this->spiceWidth = width;
this->spiceHeight = height;
egl_desktopSpiceConfigure(this->desktop, width, height);
}
static void egl_spiceDrawFill(LG_Renderer * renderer, int x, int y, int width,
int height, uint32_t color)
{
struct Inst * this = UPCAST(struct Inst, renderer);
egl_desktopSpiceDrawFill(this->desktop, x, y, width, height, color);
}
static void egl_spiceDrawBitmap(LG_Renderer * renderer, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown)
{
struct Inst * this = UPCAST(struct Inst, renderer);
egl_desktopSpiceDrawBitmap(this->desktop, x, y, width, height, stride,
data, topDown);
}
static void egl_spiceShow(LG_Renderer * renderer, bool show)
{
struct Inst * this = UPCAST(struct Inst, renderer);
this->showSpice = show;
egl_calc_mouse_size(this);
egl_desktopSpiceShow(this->desktop, show);
}
struct LG_RendererOps LGR_EGL =
{
.getName = egl_getName,
@@ -1261,12 +1222,6 @@ struct LG_RendererOps LGR_EGL =
.onFrameFormat = egl_onFrameFormat,
.onFrame = egl_onFrame,
.renderStartup = egl_renderStartup,
.render = egl_render,
.createTexture = egl_createTexture,
.freeTexture = egl_freeTexture,
.spiceConfigure = egl_spiceConfigure,
.spiceDrawFill = egl_spiceDrawFill,
.spiceDrawBitmap = egl_spiceDrawBitmap,
.spiceShow = egl_spiceShow
.needsRender = egl_needsRender,
.render = egl_render
};

View File

@@ -25,8 +25,6 @@
typedef enum EGL_TexType
{
EGL_TEXTYPE_BUFFER,
EGL_TEXTYPE_BUFFER_MAP,
EGL_TEXTYPE_BUFFER_STREAM,
EGL_TEXTYPE_FRAMEBUFFER,
EGL_TEXTYPE_DMABUF
}

View File

@@ -40,7 +40,7 @@ bool egl_framebufferInit(EGL_Framebuffer ** fb)
return false;
}
if (!egl_textureInit(&this->tex, NULL, EGL_TEXTYPE_BUFFER))
if (!egl_textureInit(&this->tex, NULL, EGL_TEXTYPE_BUFFER, false))
{
DEBUG_ERROR("Failed to initialize the texture");
return false;

View File

@@ -527,7 +527,7 @@ static void configUI(void * opaque, int * id)
if (redraw)
{
atomic_store(&this->modified, true);
app_invalidateWindow(true);
app_invalidateWindow(false);
}
}

View File

@@ -747,12 +747,12 @@ AF1 sharpness){
// Immediate constants for peak range.
AF2 peakC=AF2(1.0,-1.0*4.0);
// Limiters, these need to be high precision RCPs.
AF1 hitMinR=min(mn4R,eR)*ARcpF1(AF1_(4.0)*mx4R);
AF1 hitMinG=min(mn4G,eG)*ARcpF1(AF1_(4.0)*mx4G);
AF1 hitMinB=min(mn4B,eB)*ARcpF1(AF1_(4.0)*mx4B);
AF1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpF1(AF1_(4.0)*mn4R+peakC.y);
AF1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpF1(AF1_(4.0)*mn4G+peakC.y);
AF1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpF1(AF1_(4.0)*mn4B+peakC.y);
AF1 hitMinR=mn4R*ARcpF1(AF1_(4.0)*mx4R);
AF1 hitMinG=mn4G*ARcpF1(AF1_(4.0)*mx4G);
AF1 hitMinB=mn4B*ARcpF1(AF1_(4.0)*mx4B);
AF1 hitMaxR=(peakC.x-mx4R)*ARcpF1(AF1_(4.0)*mn4R+peakC.y);
AF1 hitMaxG=(peakC.x-mx4G)*ARcpF1(AF1_(4.0)*mn4G+peakC.y);
AF1 hitMaxB=(peakC.x-mx4B)*ARcpF1(AF1_(4.0)*mn4B+peakC.y);
AF1 lobeR=max(-hitMinR,hitMaxR);
AF1 lobeG=max(-hitMinG,hitMaxG);
AF1 lobeB=max(-hitMinB,hitMaxB);
@@ -845,12 +845,12 @@ AF1 sharpness){
// Immediate constants for peak range.
AH2 peakC=AH2(1.0,-1.0*4.0);
// Limiters, these need to be high precision RCPs.
AH1 hitMinR=min(mn4R,eR)*ARcpH1(AH1_(4.0)*mx4R);
AH1 hitMinG=min(mn4G,eG)*ARcpH1(AH1_(4.0)*mx4G);
AH1 hitMinB=min(mn4B,eB)*ARcpH1(AH1_(4.0)*mx4B);
AH1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH1(AH1_(4.0)*mn4R+peakC.y);
AH1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH1(AH1_(4.0)*mn4G+peakC.y);
AH1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH1(AH1_(4.0)*mn4B+peakC.y);
AH1 hitMinR=mn4R*ARcpH1(AH1_(4.0)*mx4R);
AH1 hitMinG=mn4G*ARcpH1(AH1_(4.0)*mx4G);
AH1 hitMinB=mn4B*ARcpH1(AH1_(4.0)*mx4B);
AH1 hitMaxR=(peakC.x-mx4R)*ARcpH1(AH1_(4.0)*mn4R+peakC.y);
AH1 hitMaxG=(peakC.x-mx4G)*ARcpH1(AH1_(4.0)*mn4G+peakC.y);
AH1 hitMaxB=(peakC.x-mx4B)*ARcpH1(AH1_(4.0)*mn4B+peakC.y);
AH1 lobeR=max(-hitMinR,hitMaxR);
AH1 lobeG=max(-hitMinG,hitMaxG);
AH1 lobeB=max(-hitMinB,hitMaxB);
@@ -963,12 +963,12 @@ AF1 sharpness){
// Immediate constants for peak range.
AH2 peakC=AH2(1.0,-1.0*4.0);
// Limiters, these need to be high precision RCPs.
AH2 hitMinR=min(mn4R,eR)*ARcpH2(AH2_(4.0)*mx4R);
AH2 hitMinG=min(mn4G,eG)*ARcpH2(AH2_(4.0)*mx4G);
AH2 hitMinB=min(mn4B,eB)*ARcpH2(AH2_(4.0)*mx4B);
AH2 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH2(AH2_(4.0)*mn4R+peakC.y);
AH2 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH2(AH2_(4.0)*mn4G+peakC.y);
AH2 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH2(AH2_(4.0)*mn4B+peakC.y);
AH2 hitMinR=mn4R*ARcpH2(AH2_(4.0)*mx4R);
AH2 hitMinG=mn4G*ARcpH2(AH2_(4.0)*mx4G);
AH2 hitMinB=mn4B*ARcpH2(AH2_(4.0)*mx4B);
AH2 hitMaxR=(peakC.x-mx4R)*ARcpH2(AH2_(4.0)*mn4R+peakC.y);
AH2 hitMaxG=(peakC.x-mx4G)*ARcpH2(AH2_(4.0)*mn4G+peakC.y);
AH2 hitMaxB=(peakC.x-mx4B)*ARcpH2(AH2_(4.0)*mn4B+peakC.y);
AH2 lobeR=max(-hitMinR,hitMaxR);
AH2 lobeG=max(-hitMinG,hitMaxG);
AH2 lobeB=max(-hitMinB,hitMaxB);

View File

@@ -0,0 +1,13 @@
#version 300 es
precision mediump float;
in vec3 pos;
out vec4 color;
uniform sampler2D sampler1;
void main()
{
highp float d = 1.0 - 0.5 * length(pos.xy);
color = vec4(0.234375 * d, 0.015625 * d, 0.425781 * d, 1.0);
}

View File

@@ -0,0 +1,14 @@
#version 300 es
precision mediump float;
layout(location = 0) in vec3 vertexPosition_modelspace;
out vec3 pos;
void main()
{
gl_Position.xyz = vertexPosition_modelspace;
gl_Position.w = 1.0;
pos = vertexPosition_modelspace;
}

View File

@@ -0,0 +1,11 @@
#version 300 es
precision mediump float;
out vec4 color;
uniform sampler2D sampler1;
void main()
{
color = vec4(1.0, 1.0, 1.0, 1.0);
}

View File

@@ -0,0 +1,12 @@
#version 300 es
layout(location = 0) in vec3 vertexPosition_modelspace;
uniform float scale;
void main()
{
gl_Position.xyz = vertexPosition_modelspace;
gl_Position.y *= scale;
gl_Position.w = 1.0;
}

View File

@@ -0,0 +1,178 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "splash.h"
#include "common/debug.h"
#include "draw.h"
#include "texture.h"
#include "shader.h"
#include "model.h"
#include <GLES3/gl3.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
// these headers are auto generated by cmake
#include "splash_bg.vert.h"
#include "splash_bg.frag.h"
#include "splash_logo.vert.h"
#include "splash_logo.frag.h"
struct EGL_Splash
{
EGL_Shader * bgShader;
EGL_Model * bg;
EGL_Shader * logoShader;
EGL_Model * logo;
// uniforms
GLint uScale;
};
bool egl_splashInit(EGL_Splash ** splash)
{
*splash = malloc(sizeof(**splash));
if (!*splash)
{
DEBUG_ERROR("Failed to malloc EGL_Splash");
return false;
}
memset(*splash, 0, sizeof(**splash));
if (!egl_shaderInit(&(*splash)->bgShader))
{
DEBUG_ERROR("Failed to initialize the splash bgShader");
return false;
}
if (!egl_shaderCompile((*splash)->bgShader,
b_shader_splash_bg_vert, b_shader_splash_bg_vert_size,
b_shader_splash_bg_frag, b_shader_splash_bg_frag_size))
{
DEBUG_ERROR("Failed to compile the splash bgShader");
return false;
}
if (!egl_modelInit(&(*splash)->bg))
{
DEBUG_ERROR("Failed to intiailize the splash bg model");
return false;
}
egl_modelSetDefault((*splash)->bg, false);
if (!egl_shaderInit(&(*splash)->logoShader))
{
DEBUG_ERROR("Failed to initialize the splash logoShader");
return false;
}
if (!egl_shaderCompile((*splash)->logoShader,
b_shader_splash_logo_vert, b_shader_splash_logo_vert_size,
b_shader_splash_logo_frag, b_shader_splash_logo_frag_size))
{
DEBUG_ERROR("Failed to compile the splash logoShader");
return false;
}
(*splash)->uScale = egl_shaderGetUniform((*splash)->logoShader, "scale");
if (!egl_modelInit(&(*splash)->logo))
{
DEBUG_ERROR("Failed to intiailize the splash model");
return false;
}
/* build the splash model */
#define P(x) ((1.0f/800.0f)*(float)(x))
egl_drawTorusArc((*splash)->logo, 30, P( 0 ), P(0), P(102), P(98), 0.0f, -M_PI);
egl_drawTorus ((*splash)->logo, 30, P(-100), P(8), P(8 ), P(4 ));
egl_drawTorus ((*splash)->logo, 30, P( 100), P(8), P(8 ), P(4 ));
egl_drawTorus ((*splash)->logo, 60, P(0), P(0), P(83), P(79));
egl_drawTorus ((*splash)->logo, 60, P(0), P(0), P(67), P(63));
static const GLfloat lines[][12] =
{
{
P( -2), P(-140), 0.0f,
P( -2), P(-100), 0.0f,
P( 2), P(-140), 0.0f,
P( 2), P(-100), 0.0f
},
{
P(-26), P(-144), 0.0f,
P(-26), P(-140), 0.0f,
P( 26), P(-144), 0.0f,
P( 26), P(-140), 0.0f
},
{
P(-40), P(-156), 0.0f,
P(-40), P(-152), 0.0f,
P( 40), P(-156), 0.0f,
P( 40), P(-152), 0.0f
}
};
egl_modelAddVerts((*splash)->logo, lines[0], NULL, 4);
egl_modelAddVerts((*splash)->logo, lines[1], NULL, 4);
egl_modelAddVerts((*splash)->logo, lines[2], NULL, 4);
egl_drawTorusArc((*splash)->logo, 10, P(-26), P(-154), P(10), P(14), M_PI , -M_PI / 2.0);
egl_drawTorusArc((*splash)->logo, 10, P( 26), P(-154), P(10), P(14), M_PI / 2.0f, -M_PI / 2.0);
#undef P
return true;
}
void egl_splashFree(EGL_Splash ** splash)
{
if (!*splash)
return;
egl_modelFree(&(*splash)->bg );
egl_modelFree(&(*splash)->logo);
egl_shaderFree(&(*splash)->bgShader );
egl_shaderFree(&(*splash)->logoShader);
free(*splash);
*splash = NULL;
}
void egl_splashRender(EGL_Splash * splash, float alpha, float scaleY)
{
glEnable(GL_BLEND);
glBlendColor(0, 0, 0, alpha);
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
egl_shaderUse(splash->bgShader);
egl_modelRender(splash->bg);
egl_shaderUse(splash->logoShader);
glUniform1f(splash->uScale, scaleY);
egl_modelRender(splash->logo);
glDisable(GL_BLEND);
}

View File

@@ -0,0 +1,30 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stdbool.h>
typedef struct EGL_Splash EGL_Splash;
bool egl_splashInit(EGL_Splash ** splash);
void egl_splashFree(EGL_Splash ** splash);
void egl_splashRender(EGL_Splash * splash, float alpha, float scaleY);

View File

@@ -37,26 +37,23 @@ extern const EGL_TextureOps EGL_TextureFrameBuffer;
extern const EGL_TextureOps EGL_TextureDMABUF;
bool egl_textureInit(EGL_Texture ** texture_, EGLDisplay * display,
EGL_TexType type)
EGL_TexType type, bool streaming)
{
const EGL_TextureOps * ops;
switch(type)
{
case EGL_TEXTYPE_BUFFER:
ops = &EGL_TextureBuffer;
break;
case EGL_TEXTYPE_BUFFER_MAP:
case EGL_TEXTYPE_BUFFER_STREAM:
ops = &EGL_TextureBufferStream;
ops = streaming ? &EGL_TextureBufferStream : &EGL_TextureBuffer;
break;
case EGL_TEXTYPE_FRAMEBUFFER:
DEBUG_ASSERT(streaming);
ops = &EGL_TextureFrameBuffer;
break;
case EGL_TEXTYPE_DMABUF:
DEBUG_ASSERT(streaming);
ops = &EGL_TextureDMABUF;
break;
@@ -65,7 +62,7 @@ bool egl_textureInit(EGL_Texture ** texture_, EGLDisplay * display,
}
*texture_ = NULL;
if (!ops->init(texture_, type, display))
if (!ops->init(texture_, display))
return false;
EGL_Texture * this = *texture_;
@@ -82,8 +79,6 @@ bool egl_textureInit(EGL_Texture ** texture_, EGLDisplay * display,
void egl_textureFree(EGL_Texture ** tex)
{
EGL_Texture * this = *tex;
if (!this)
return;
glDeleteSamplers(1, &this->sampler);
@@ -108,47 +103,12 @@ bool egl_textureSetup(EGL_Texture * this, enum EGL_PixelFormat pixFmt,
return this->ops.setup(this, &setup);
}
bool egl_textureUpdate(EGL_Texture * this, const uint8_t * buffer, bool topDown)
bool egl_textureUpdate(EGL_Texture * this, const uint8_t * buffer)
{
const struct EGL_TexUpdate update =
{
.type = EGL_TEXTYPE_BUFFER,
.x = 0,
.y = 0,
.width = this->format.width,
.height = this->format.height,
.pitch = this->format.pitch,
.stride = this->format.stride,
.topDown = topDown,
.buffer = buffer
};
return this->ops.update(this, &update);
}
bool egl_textureUpdateRect(EGL_Texture * this,
int x, int y, int width, int height, int stride,
const uint8_t * buffer, bool topDown)
{
x = clamp(x , 0, this->format.width );
y = clamp(y , 0, this->format.height );
width = clamp(width , 0, this->format.width - x);
height = clamp(height, 0, this->format.height - y);
if (!width || !height)
return true;
const struct EGL_TexUpdate update =
{
.type = EGL_TEXTYPE_BUFFER,
.x = x,
.y = y,
.width = width,
.height = height,
.pitch = stride / this->format.bpp,
.stride = stride,
.topDown = topDown,
.buffer = buffer
.type = EGL_TEXTYPE_BUFFER,
.buffer = buffer
};
return this->ops.update(this, &update);
@@ -161,12 +121,6 @@ bool egl_textureUpdateFromFrame(EGL_Texture * this,
const struct EGL_TexUpdate update =
{
.type = EGL_TEXTYPE_FRAMEBUFFER,
.x = 0,
.y = 0,
.width = this->format.width,
.height = this->format.height,
.pitch = this->format.pitch,
.stride = this->format.stride,
.frame = frame,
.rects = damageRects,
.rectCount = damageRectsCount,
@@ -180,14 +134,8 @@ bool egl_textureUpdateFromDMA(EGL_Texture * this,
{
const struct EGL_TexUpdate update =
{
.type = EGL_TEXTYPE_DMABUF,
.x = 0,
.y = 0,
.width = this->format.width,
.height = this->format.height,
.pitch = this->format.pitch,
.stride = this->format.stride,
.dmaFD = dmaFd
.type = EGL_TEXTYPE_DMABUF,
.dmaFD = dmaFd
};
/* wait for completion */

View File

@@ -42,23 +42,12 @@ typedef struct EGL_TexUpdate
/* the type of this update */
EGL_TexType type;
int x, y, width, height;
//pitch = row length in pixels
//stride = row length in bytes
int pitch, stride;
union
{
/* EGL_TEXTYPE_BUFFER */
struct
{
// true if row 0 is the top of the image
bool topDown;
const uint8_t * buffer;
};
/* EGL_TEXTURE_BUFFER */
const uint8_t * buffer;
/* EGL_TEXTYPE_FRAMEBUFFER */
/* EGL_TEXTURE_FRAMEBUFFER */
struct
{
const FrameBuffer * frame;
@@ -66,7 +55,7 @@ typedef struct EGL_TexUpdate
int rectCount;
};
/* EGL_TEXTYPE_DMABUF */
/* EGL_TEXTURE_DMABUF */
int dmaFD;
};
}
@@ -77,7 +66,7 @@ typedef struct EGL_Texture EGL_Texture;
typedef struct EGL_TextureOps
{
/* allocate & initialize an EGL_Texture */
bool (*init)(EGL_Texture ** texture, EGL_TexType type, EGLDisplay * display);
bool (*init)(EGL_Texture ** texture, EGLDisplay * display);
/* free the EGL_Texture */
void (*free)(EGL_Texture * texture);
@@ -99,25 +88,19 @@ EGL_TextureOps;
struct EGL_Texture
{
struct EGL_TextureOps ops;
EGL_TexType type;
GLuint sampler;
EGL_TexFormat format;
};
bool egl_textureInit(EGL_Texture ** texture, EGLDisplay * display,
EGL_TexType type);
EGL_TexType type, bool streaming);
void egl_textureFree(EGL_Texture ** tex);
bool egl_textureSetup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt,
size_t width, size_t height, size_t stride);
bool egl_textureUpdate(EGL_Texture * texture, const uint8_t * buffer,
bool topDown);
bool egl_textureUpdateRect(EGL_Texture * texture,
int x, int y, int width, int height, int stride,
const uint8_t * buffer, bool topDown);
bool egl_textureUpdate(EGL_Texture * texture, const uint8_t * buffer);
bool egl_textureUpdateFromFrame(EGL_Texture * texture,
const FrameBuffer * frame, const FrameDamageRect * damageRects,

View File

@@ -46,8 +46,7 @@ static void egl_texBuffer_cleanup(TextureBuffer * this)
// common functions
bool egl_texBufferInit(EGL_Texture ** texture, EGL_TexType type,
EGLDisplay * display)
bool egl_texBufferInit(EGL_Texture ** texture, EGLDisplay * display)
{
TextureBuffer * this;
if (!*texture)
@@ -112,13 +111,11 @@ static bool egl_texBufferUpdate(EGL_Texture * texture, const EGL_TexUpdate * upd
DEBUG_ASSERT(update->type == EGL_TEXTYPE_BUFFER);
glBindTexture(GL_TEXTURE_2D, this->tex[0]);
glPixelStorei(GL_UNPACK_ROW_LENGTH, update->pitch);
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->format.pitch);
glTexSubImage2D(GL_TEXTURE_2D,
0,
update->x,
update->y,
update->width,
update->height,
0, 0, 0,
texture->format.width,
texture->format.height,
texture->format.format,
texture->format.dataType,
update->buffer);
@@ -141,30 +138,14 @@ EGL_TexStatus egl_texBufferGet(EGL_Texture * texture, GLuint * tex)
// streaming functions
bool egl_texBufferStreamInit(EGL_Texture ** texture, EGL_TexType type,
EGLDisplay * display)
bool egl_texBufferStreamInit(EGL_Texture ** texture, EGLDisplay * display)
{
if (!egl_texBufferInit(texture, type, display))
if (!egl_texBufferInit(texture, display))
return false;
TextureBuffer * this = UPCAST(TextureBuffer, *texture);
switch(type)
{
case EGL_TEXTYPE_BUFFER_STREAM:
case EGL_TEXTYPE_FRAMEBUFFER:
case EGL_TEXTYPE_DMABUF:
this->texCount = 2;
break;
case EGL_TEXTYPE_BUFFER_MAP:
this->texCount = 1;
break;
default:
DEBUG_UNREACHABLE();
}
this->texCount = 2;
LG_LOCK_INIT(this->copyLock);
return true;
}
@@ -185,32 +166,8 @@ static bool egl_texBufferStreamUpdate(EGL_Texture * texture,
DEBUG_ASSERT(update->type == EGL_TEXTYPE_BUFFER);
LG_LOCK(this->copyLock);
uint8_t * dst = this->buf[this->bufIndex].map +
texture->format.stride * update->y +
update->x * texture->format.bpp;
if (update->topDown)
{
const uint8_t * src = update->buffer;
for(int y = 0; y < update->height; ++y)
{
memcpy(dst, src, update->stride);
dst += texture->format.stride;
src += update->stride;
}
}
else
{
const uint8_t * src = update->buffer + update->stride * update->height;
for(int y = 0; y < update->height; ++y)
{
src -= update->stride;
memcpy(dst, src, update->stride);
dst += texture->format.stride;
}
}
memcpy(this->buf[this->bufIndex].map, update->buffer,
texture->format.bufferSize);
this->buf[this->bufIndex].updated = true;
LG_UNLOCK(this->copyLock);

View File

@@ -42,15 +42,13 @@ typedef struct TextureBuffer
}
TextureBuffer;
bool egl_texBufferInit(EGL_Texture ** texture_, EGL_TexType type,
EGLDisplay * display);
bool egl_texBufferInit(EGL_Texture ** texture_, EGLDisplay * display);
void egl_texBufferFree(EGL_Texture * texture_);
bool egl_texBufferSetup(EGL_Texture * texture_, const EGL_TexSetup * setup);
EGL_TexStatus egl_texBufferProcess(EGL_Texture * texture_);
EGL_TexStatus egl_texBufferGet(EGL_Texture * texture_, GLuint * tex);
bool egl_texBufferStreamInit(EGL_Texture ** texture_, EGL_TexType type,
EGLDisplay * display);
bool egl_texBufferStreamInit(EGL_Texture ** texture_, EGLDisplay * display);
bool egl_texBufferStreamSetup(EGL_Texture * texture_,
const EGL_TexSetup * setup);
EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture_);

View File

@@ -20,7 +20,6 @@
#include "texture.h"
#include "texture_buffer.h"
#include "util.h"
#include "common/vector.h"
#include "egl_dynprocs.h"
@@ -37,7 +36,6 @@ typedef struct TexDMABUF
TextureBuffer base;
EGLDisplay display;
bool hasImportModifiers;
Vector images;
}
TexDMABUF;
@@ -56,8 +54,7 @@ static void egl_texDMABUFCleanup(TexDMABUF * this)
// dmabuf functions
static bool egl_texDMABUFInit(EGL_Texture ** texture, EGL_TexType type,
EGLDisplay * display)
static bool egl_texDMABUFInit(EGL_Texture ** texture, EGLDisplay * display)
{
TexDMABUF * this = calloc(1, sizeof(*this));
*texture = &this->base.base;
@@ -70,7 +67,7 @@ static bool egl_texDMABUFInit(EGL_Texture ** texture, EGL_TexType type,
}
EGL_Texture * parent = &this->base.base;
if (!egl_texBufferStreamInit(&parent, type, display))
if (!egl_texBufferStreamInit(&parent, display))
{
vector_destroy(&this->images);
free(this);
@@ -79,11 +76,6 @@ static bool egl_texDMABUFInit(EGL_Texture ** texture, EGL_TexType type,
}
this->display = display;
const char * client_exts = eglQueryString(this->display, EGL_EXTENSIONS);
this->hasImportModifiers =
util_hasGLExt(client_exts, "EGL_EXT_image_dma_buf_import_modifiers");
return true;
}
@@ -129,24 +121,17 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture,
if (image == EGL_NO_IMAGE)
{
const uint64_t modifier = DRM_FORMAT_MOD_LINEAR;
EGLAttrib attribs[] =
EGLAttrib const attribs[] =
{
EGL_WIDTH , texture->format.width,
EGL_HEIGHT , texture->format.height,
EGL_LINUX_DRM_FOURCC_EXT , texture->format.fourcc,
EGL_DMA_BUF_PLANE0_FD_EXT , update->dmaFD,
EGL_DMA_BUF_PLANE0_OFFSET_EXT , 0,
EGL_DMA_BUF_PLANE0_PITCH_EXT , texture->format.stride,
EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, (modifier & 0xffffffff),
EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, (modifier >> 32),
EGL_NONE , EGL_NONE
EGL_WIDTH , texture->format.width,
EGL_HEIGHT , texture->format.height,
EGL_LINUX_DRM_FOURCC_EXT , texture->format.fourcc,
EGL_DMA_BUF_PLANE0_FD_EXT , update->dmaFD,
EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
EGL_DMA_BUF_PLANE0_PITCH_EXT , texture->format.stride,
EGL_NONE , EGL_NONE
};
if (!this->hasImportModifiers)
attribs[12] = attribs[13] =
attribs[14] = attribs[15] = EGL_NONE;
image = g_egl_dynProcs.eglCreateImage(
this->display,
EGL_NO_CONTEXT,

View File

@@ -38,14 +38,13 @@ typedef struct TexFB
}
TexFB;
static bool egl_texFBInit(EGL_Texture ** texture, EGL_TexType type,
EGLDisplay * display)
static bool egl_texFBInit(EGL_Texture ** texture, EGLDisplay * display)
{
TexFB * this = calloc(1, sizeof(*this));
*texture = &this->base.base;
EGL_Texture * parent = &this->base.base;
if (!egl_texBufferStreamInit(&parent, type, display))
if (!egl_texBufferStreamInit(&parent, display))
{
free(this);
*texture = NULL;

View File

@@ -28,6 +28,17 @@
#include "egldebug.h"
#include "egl_dynprocs.h"
/**
* the following comes from drm_fourcc.h and is included here to avoid the
* external dependency for the few simple defines we need
*/
#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4')
#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4')
#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0')
#define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H')
bool egl_texUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt)
{
switch(setup->pixFmt)

View File

@@ -35,7 +35,7 @@ typedef struct EGL_TexFormat
unsigned int fourcc;
size_t bufferSize;
size_t width , height;
size_t width, height;
size_t stride, pitch;
}
EGL_TexFormat;
@@ -55,20 +55,3 @@ bool egl_texUtilGenBuffers(const EGL_TexFormat * fmt, EGL_TexBuffer * buffers,
void egl_texUtilFreeBuffers(EGL_TexBuffer * buffers, int count);
bool egl_texUtilMapBuffer(EGL_TexBuffer * buffer);
void egl_texUtilUnmapBuffer(EGL_TexBuffer * buffer);
/**
* the following comes from drm_fourcc.h and is included here to avoid the
* external dependency for the few simple defines we need
*/
#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4')
#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4')
#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0')
#define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H')
#define DRM_FORMAT_MOD_VENDOR_NONE 0
#define fourcc_mod_code(vendor, val) \
((((uint64_t)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | \
((val) & 0x00ffffffffffffffULL))
#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0)

View File

@@ -3,24 +3,24 @@ project(renderer_Opengl LANGUAGES C CXX)
find_package(PkgConfig)
pkg_check_modules(RENDERER_OPENGL REQUIRED IMPORTED_TARGET
gl
gl
)
add_library(renderer_OpenGL STATIC
opengl.c
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl2.cpp
opengl.c
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl2.cpp
)
target_compile_definitions(renderer_OpenGL PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1)
target_link_libraries(renderer_OpenGL
PkgConfig::RENDERER_OPENGL
lg_common
PkgConfig::RENDERER_OPENGL
lg_common
cimgui
cimgui
)
target_include_directories(renderer_OpenGL
PRIVATE
src
PRIVATE
src
)

View File

@@ -42,8 +42,9 @@
#define FPS_TEXTURE 0
#define MOUSE_TEXTURE 1
#define SPICE_TEXTURE 2
#define TEXTURE_COUNT 3
#define TEXTURE_COUNT 2
#define FADE_TIME 1000000
static struct Option opengl_options[] =
{
@@ -135,16 +136,17 @@ struct Inst
int texWIndex, texRIndex;
int texList;
int mouseList;
int spiceList;
LG_RendererRect destRect;
struct IntPoint spiceSize;
bool spiceShow;
bool hasTextures, hasFrames;
GLuint frames[BUFFER_COUNT];
GLsync fences[BUFFER_COUNT];
GLuint textures[TEXTURE_COUNT];
bool waiting;
uint64_t waitFadeTime;
bool waitDone;
LG_Lock mouseLock;
LG_RendererCursor mouseCursor;
int mouseWidth;
@@ -172,9 +174,10 @@ enum ConfigStatus
static void deconfigure(struct Inst * this);
static enum ConfigStatus configure(struct Inst * this);
static void updateMouseShape(struct Inst * this);
static void updateMouseShape(struct Inst * this, bool * newShape);
static bool drawFrame(struct Inst * this);
static void drawMouse(struct Inst * this);
static void renderWait(struct Inst * this);
const char * opengl_getName(void)
{
@@ -204,6 +207,7 @@ bool opengl_create(LG_Renderer ** renderer, const LG_RendererParams params,
this->opt.preventBuffer = option_get_bool("opengl", "preventBuffer");
this->opt.amdPinnedMem = option_get_bool("opengl", "amdPinnedMem" );
LG_LOCK_INIT(this->formatLock);
LG_LOCK_INIT(this->frameLock );
LG_LOCK_INIT(this->mouseLock );
@@ -214,7 +218,10 @@ bool opengl_create(LG_Renderer ** renderer, const LG_RendererParams params,
bool opengl_initialize(LG_Renderer * renderer)
{
// struct Inst * this = UPCAST(struct Inst, renderer);
struct Inst * this = UPCAST(struct Inst, renderer);
this->waiting = true;
this->waitDone = false;
return true;
}
@@ -228,7 +235,6 @@ void opengl_deinitialize(LG_Renderer * renderer)
glDeleteLists(this->texList , BUFFER_COUNT);
glDeleteLists(this->mouseList, 1);
glDeleteLists(this->spiceList, 1);
}
deconfigure(this);
@@ -257,34 +263,9 @@ void opengl_deinitialize(LG_Renderer * renderer)
void opengl_onRestart(LG_Renderer * renderer)
{
// struct Inst * this = UPCAST(struct Inst, renderer);
}
struct Inst * this = UPCAST(struct Inst, renderer);
static void setupModelView(struct Inst * this)
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (!this->destRect.valid)
return;
int fw, fh;
if (this->spiceShow)
{
fw = this->spiceSize.x;
fh = this->spiceSize.y;
}
else
{
fw = this->format.frameWidth;
fh = this->format.frameHeight;
}
glTranslatef(this->destRect.x, this->destRect.y, 0.0f);
glScalef(
(float)this->destRect.w / (float)fw,
(float)this->destRect.h / (float)fh,
1.0f
);
this->waiting = true;
}
void opengl_onResize(LG_Renderer * renderer, const int width, const int height, const double scale,
@@ -311,6 +292,19 @@ void opengl_onResize(LG_Renderer * renderer, const int width, const int height,
glLoadIdentity();
glOrtho(0, this->window.x, this->window.y, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (this->destRect.valid)
{
glTranslatef(this->destRect.x, this->destRect.y, 0.0f);
glScalef(
(float)this->destRect.w / (float)this->format.frameWidth,
(float)this->destRect.h / (float)this->format.frameHeight,
1.0f
);
}
// this is needed to refresh the font atlas texture
ImGui_ImplOpenGL2_Shutdown();
ImGui_ImplOpenGL2_Init();
@@ -387,6 +381,18 @@ bool opengl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int dmaFd
atomic_store_explicit(&this->frameUpdate, true, memory_order_release);
LG_UNLOCK(this->frameLock);
if (this->waiting)
{
this->waiting = false;
if (!this->params.quickSplash)
this->waitFadeTime = microtime() + FADE_TIME;
else
{
glDisable(GL_MULTISAMPLE);
this->waitDone = true;
}
}
return true;
}
@@ -450,7 +456,6 @@ bool opengl_renderStartup(LG_Renderer * renderer, bool useDMA)
// generate lists for drawing
this->texList = glGenLists(BUFFER_COUNT);
this->mouseList = glGenLists(1);
this->spiceList = glGenLists(1);
// create the overlay textures
glGenTextures(TEXTURE_COUNT, this->textures);
@@ -473,13 +478,17 @@ bool opengl_renderStartup(LG_Renderer * renderer, bool useDMA)
return true;
}
static bool opengl_needsRender(LG_Renderer * renderer)
{
struct Inst * this = UPCAST(struct Inst, renderer);
return !this->waitDone;
}
bool opengl_render(LG_Renderer * renderer, LG_RendererRotate rotate, const bool newFrame,
const bool invalidateWindow, void (*preSwap)(void * udata), void * udata)
{
struct Inst * this = UPCAST(struct Inst, renderer);
setupModelView(this);
switch(configure(this))
{
case CONFIG_STATUS_ERROR:
@@ -495,12 +504,18 @@ bool opengl_render(LG_Renderer * renderer, LG_RendererRotate rotate, const bool
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
updateMouseShape(this);
if (this->spiceShow)
glCallList(this->spiceList);
if (this->waiting)
renderWait(this);
else
{
bool newShape;
updateMouseShape(this, &newShape);
glCallList(this->texList + this->texRIndex);
drawMouse(this);
drawMouse(this);
if (!this->waitDone)
renderWait(this);
}
if (app_renderOverlay(NULL, 0) != 0)
{
@@ -521,154 +536,100 @@ bool opengl_render(LG_Renderer * renderer, LG_RendererRotate rotate, const bool
return true;
}
static void * opengl_createTexture(LG_Renderer * renderer,
int width, int height, uint8_t * data)
static void drawTorus(float x, float y, float inner, float outer,
unsigned int pts)
{
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
data);
glBindTexture(GL_TEXTURE_2D, 0);
return (void*)(intptr_t)tex;
}
static void opengl_freeTexture(LG_Renderer * renderer, void * texture)
{
GLuint tex = (GLuint)(intptr_t)texture;
glDeleteTextures(1, &tex);
}
static void opengl_spiceConfigure(LG_Renderer * renderer, int width, int height)
{
struct Inst * this = UPCAST(struct Inst, renderer);
this->spiceSize.x = width;
this->spiceSize.y = height;
glBindTexture(GL_TEXTURE_2D, this->textures[SPICE_TEXTURE]);
glTexImage2D
(
GL_TEXTURE_2D,
0 ,
GL_RGBA,
width ,
height ,
0 ,
GL_BGRA,
GL_UNSIGNED_BYTE,
0
);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
// create the display lists
glNewList(this->spiceList, GL_COMPILE);
glBindTexture(GL_TEXTURE_2D, this->textures[SPICE_TEXTURE]);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0);
glTexCoord2f(1.0f, 0.0f); glVertex2i(this->spiceSize.x, 0);
glTexCoord2f(0.0f, 1.0f); glVertex2i(0, this->spiceSize.y);
glTexCoord2f(1.0f, 1.0f);
glVertex2i(this->spiceSize.x, this->spiceSize.y);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glEndList();
}
static void opengl_spiceDrawFill(LG_Renderer * renderer, int x, int y, int width,
int height, uint32_t color)
{
struct Inst * this = UPCAST(struct Inst, renderer);
/* this is a fairly hacky way to do this, but since it's only for the fallback
* spice display it's not really an issue */
uint32_t line[width];
for(int x = 0; x < width; ++x)
line[x] = color;
glBindTexture(GL_TEXTURE_2D, this->textures[SPICE_TEXTURE]);
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
for(; y < height; ++y)
glTexSubImage2D
(
GL_TEXTURE_2D,
0 ,
x ,
y ,
width ,
1 ,
GL_BGRA,
GL_UNSIGNED_BYTE,
line
);
glBindTexture(GL_TEXTURE_2D, 0);
}
static void opengl_spiceDrawBitmap(LG_Renderer * renderer, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown)
{
struct Inst * this = UPCAST(struct Inst, renderer);
if (!topDown)
glBegin(GL_QUAD_STRIP);
for (unsigned int i = 0; i <= pts; ++i)
{
// this is non-optimal but as spice is a fallback it's not too critical
uint8_t line[stride];
for(int y = 0; y < height / 2; ++y)
float angle = (i / (float)pts) * M_PI * 2.0f;
glVertex2f(x + (inner * cos(angle)), y + (inner * sin(angle)));
glVertex2f(x + (outer * cos(angle)), y + (outer * sin(angle)));
}
glEnd();
}
static void drawTorusArc(float x, float y, float inner, float outer,
unsigned int pts, float s, float e)
{
glBegin(GL_QUAD_STRIP);
for (unsigned int i = 0; i <= pts; ++i)
{
float angle = s + ((i / (float)pts) * e);
glVertex2f(x + (inner * cos(angle)), y + (inner * sin(angle)));
glVertex2f(x + (outer * cos(angle)), y + (outer * sin(angle)));
}
glEnd();
}
static void renderWait(struct Inst * this)
{
float a;
if (this->waiting)
a = 1.0f;
else
{
uint64_t t = microtime();
if (t > this->waitFadeTime)
{
uint8_t * top = data + y * stride;
uint8_t * btm = data + (height - y - 1) * stride;
memcpy(line, top , stride);
memcpy(top , btm , stride);
memcpy(btm , line, stride);
glDisable(GL_MULTISAMPLE);
this->waitDone = true;
return;
}
uint64_t delta = this->waitFadeTime - t;
a = 1.0f / FADE_TIME * delta;
}
glBindTexture(GL_TEXTURE_2D, this->textures[SPICE_TEXTURE]);
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride / 4);
glTexSubImage2D
(
GL_TEXTURE_2D,
0 ,
x ,
y ,
width ,
height ,
GL_BGRA,
GL_UNSIGNED_BYTE,
data
);
glBindTexture(GL_TEXTURE_2D, 0);
}
glEnable(GL_BLEND);
glPushMatrix();
glLoadIdentity();
glTranslatef(this->window.x / 2.0f, this->window.y / 2.0f, 0.0f);
static void opengl_spiceShow(LG_Renderer * renderer, bool show)
{
struct Inst * this = UPCAST(struct Inst, renderer);
this->spiceShow = show;
//draw the background gradient
glBegin(GL_TRIANGLE_FAN);
glColor4f(0.234375f, 0.015625f, 0.425781f, a);
glVertex2f(0, 0);
glColor4f(0, 0, 0, a);
for (unsigned int i = 0; i <= 100; ++i)
{
float angle = (i / (float)100) * M_PI * 2.0f;
glVertex2f(cos(angle) * this->window.x, sin(angle) * this->window.y);
}
glEnd();
// draw the logo
glColor4f(1.0f, 1.0f, 1.0f, a);
glScalef (2.0f, 2.0f, 1.0f);
drawTorus ( 0, 0, 40, 42, 60);
drawTorus ( 0, 0, 32, 34, 60);
drawTorus (-50, -3, 2, 4, 30);
drawTorus ( 50, -3, 2, 4, 30);
drawTorusArc( 0, 0, 51, 49, 60, 0.0f, M_PI);
glBegin(GL_QUADS);
glVertex2f(-1 , 50);
glVertex2f(-1 , 76);
glVertex2f( 1 , 76);
glVertex2f( 1 , 50);
glVertex2f(-14, 76);
glVertex2f(-14, 78);
glVertex2f( 14, 78);
glVertex2f( 14, 76);
glVertex2f(-21, 83);
glVertex2f(-21, 85);
glVertex2f( 21, 85);
glVertex2f( 21, 83);
glEnd();
drawTorusArc(-14, 83, 5, 7, 10, M_PI , M_PI / 2.0f);
drawTorusArc( 14, 83, 5, 7, 10, M_PI * 1.5f, M_PI / 2.0f);
//FIXME: draw the diagnoal marks on the circle
glPopMatrix();
glDisable(GL_BLEND);
}
const LG_RendererOps LGR_OpenGL =
@@ -686,14 +647,8 @@ const LG_RendererOps LGR_OpenGL =
.onFrameFormat = opengl_onFrameFormat,
.onFrame = opengl_onFrame,
.renderStartup = opengl_renderStartup,
.render = opengl_render,
.createTexture = opengl_createTexture,
.freeTexture = opengl_freeTexture,
.spiceConfigure = opengl_spiceConfigure,
.spiceDrawFill = opengl_spiceDrawFill,
.spiceDrawBitmap = opengl_spiceDrawBitmap,
.spiceShow = opengl_spiceShow
.needsRender = opengl_needsRender,
.render = opengl_render
};
static bool _checkGLError(unsigned int line, const char * name)
@@ -960,9 +915,10 @@ static void deconfigure(struct Inst * this)
this->configured = false;
}
static void updateMouseShape(struct Inst * this)
static void updateMouseShape(struct Inst * this, bool * newShape)
{
LG_LOCK(this->mouseLock);
*newShape = this->newShape;
if (!this->newShape)
{
LG_UNLOCK(this->mouseLock);

View File

@@ -24,7 +24,6 @@
#include "core.h"
#include "util.h"
#include "clipboard.h"
#include "render_queue.h"
#include "kb.h"
@@ -38,7 +37,6 @@
#include <stdarg.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
bool app_isRunning(void)
{
@@ -116,7 +114,7 @@ void app_handleFocusEvent(bool focused)
if (g_params.releaseKeysOnFocusLoss)
for (int key = 0; key < KEY_MAX; key++)
if (g_state.keyDown[key])
app_handleKeyRelease(key, 0);
app_handleKeyRelease(key);
g_state.escapeActive = false;
@@ -305,7 +303,7 @@ void app_handleWheelMotion(double motion)
g_state.io->MouseWheel -= motion;
}
void app_handleKeyPress(int sc, int charcode)
void app_handleKeyPress(int sc)
{
if (!app_isOverlayMode() || !g_state.io->WantCaptureKeyboard)
{
@@ -320,16 +318,9 @@ void app_handleKeyPress(int sc, int charcode)
if (g_state.escapeActive)
{
g_state.escapeAction = sc;
KeybindHandle handle;
ll_forEachNL(g_state.bindings, item, handle)
{
if ((handle->sc && handle->sc == sc ) ||
(handle->charcode && handle->charcode == charcode))
{
handle->callback(sc, handle->opaque);
break;
}
}
KeybindHandle handle = g_state.bindings[sc];
if (handle)
handle->callback(sc, handle->opaque);
return;
}
}
@@ -365,7 +356,7 @@ void app_handleKeyPress(int sc, int charcode)
}
}
void app_handleKeyRelease(int sc, int charcode)
void app_handleKeyRelease(int sc)
{
if (g_state.escapeActive)
{
@@ -543,7 +534,7 @@ void app_invalidateWindow(bool full)
if (full)
atomic_store(&g_state.invalidateWindow, true);
if (g_state.dsInitialized && g_state.jitRender && g_state.ds->stopWaitFrame)
if (g_state.jitRender && g_state.ds->stopWaitFrame)
g_state.ds->stopWaitFrame();
lgSignalEvent(g_state.frameEvent);
@@ -684,51 +675,31 @@ void app_msgBoxClose(MsgBoxHandle handle)
void app_showRecord(bool show)
{
overlayStatus_set(LG_USER_STATUS_RECORDING, show);
overlayRecord_show(show);
}
KeybindHandle app_registerKeybind(int sc, int charcode, KeybindFn callback,
void * opaque, const char * description)
KeybindHandle app_registerKeybind(int sc, KeybindFn callback, void * opaque, const char * description)
{
if (charcode != 0 && sc != 0)
{
DEBUG_ERROR("invalid keybind, one of scancode or charcode must be 0");
return NULL;
}
if (charcode && islower(charcode))
{
DEBUG_ERROR("invalid keybind, charcode must be uppercase");
return NULL;
}
KeybindHandle handle;
// don't allow duplicate binds
ll_forEachNL(g_state.bindings, item, handle)
if (g_state.bindings[sc])
{
if ((sc && handle->sc == sc ) ||
(charcode && handle->charcode == charcode))
{
DEBUG_INFO("Key already bound");
return NULL;
}
DEBUG_INFO("Key already bound");
return NULL;
}
handle = malloc(sizeof(*handle));
KeybindHandle handle = malloc(sizeof(*handle));
if (!handle)
{
DEBUG_ERROR("out of memory");
return NULL;
}
handle->sc = sc;
handle->charcode = charcode;
handle->callback = callback;
handle->description = description;
handle->opaque = opaque;
handle->sc = sc;
handle->callback = callback;
handle->opaque = opaque;
ll_push(g_state.bindings, handle);
g_state.bindings[sc] = handle;
g_state.keyDescription[sc] = description;
return handle;
}
@@ -737,16 +708,19 @@ void app_releaseKeybind(KeybindHandle * handle)
if (!*handle)
return;
ll_removeData(g_state.bindings, *handle);
g_state.bindings[(*handle)->sc] = NULL;
free(*handle);
*handle = NULL;
}
void app_releaseAllKeybinds(void)
{
KeybindHandle * handle;
while(ll_shift(g_state.bindings, (void **)&handle))
free(handle);
for(int i = 0; i < KEY_MAX; ++i)
if (g_state.bindings[i])
{
free(g_state.bindings[i]);
g_state.bindings[i] = NULL;
}
}
GraphHandle app_registerGraph(const char * name, RingBuffer buffer,
@@ -887,13 +861,16 @@ render_again:
igNewFrame();
const bool overlayMode = app_isOverlayMode();
if (overlayMode && g_params.overlayDim)
if (overlayMode)
{
totalDamage = true;
ImDrawList_AddRectFilled(igGetBackgroundDrawList_Nil(), (ImVec2) { 0.0f , 0.0f },
g_state.io->DisplaySize,
igGetColorU32_Col(ImGuiCol_ModalWindowDimBg, 1.0f),
0, 0);
// bool test;
// igShowDemoWindow(&test);
}
const bool msgModal = overlayMsg_modal();
@@ -1002,9 +979,6 @@ void app_overlayConfigRegisterTab(const char * title,
void app_invalidateOverlay(bool renderTwice)
{
if (g_state.state == APP_STATE_SHUTDOWN)
return;
if (renderTwice)
g_state.renderImGuiTwice = true;
app_invalidateWindow(false);
@@ -1034,69 +1008,3 @@ bool app_guestIsOther(void)
{
return g_state.guestOS == KVMFR_OS_OTHER;
}
void app_stopVideo(bool stop)
{
if (g_state.stopVideo == stop)
return;
// do not change the state if the host app is not connected
if (!g_state.lgHostConnected)
return;
g_state.stopVideo = stop;
app_alert(
LG_ALERT_INFO,
stop ? "Video Stream Disabled" : "Video Stream Enabled"
);
if (stop)
{
core_stopCursorThread();
core_stopFrameThread();
}
else
{
core_startCursorThread();
core_startFrameThread();
}
}
bool app_useSpiceDisplay(bool enable)
{
static bool lastState = false;
if (!g_params.useSpice || lastState == enable)
return g_params.useSpice && lastState;
// if spice is not yet ready, flag the state we want for when it is
if (!g_state.spiceReady)
{
g_state.initialSpiceDisplay = enable;
return false;
}
if (!purespice_hasChannel(PS_CHANNEL_DISPLAY))
return false;
// do not allow stopping of the host app if not connected
if (!enable && !g_state.lgHostConnected)
return false;
lastState = enable;
if (enable)
{
purespice_connectChannel(PS_CHANNEL_DISPLAY);
purespice_connectChannel(PS_CHANNEL_CURSOR);
renderQueue_spiceShow(true);
}
else
{
renderQueue_spiceShow(false);
purespice_disconnectChannel(PS_CHANNEL_DISPLAY);
purespice_disconnectChannel(PS_CHANNEL_CURSOR);
}
overlayStatus_set(LG_USER_STATUS_SPICE, enable);
return enable;
}

View File

@@ -118,16 +118,12 @@ typedef struct
struct
{
bool requested;
bool started;
int volumeChannels;
uint16_t volume[8];
bool mute;
int stride;
uint32_t time;
int lastChannels;
int lastSampleRate;
PSAudioFormat lastFormat;
MsgBoxHandle confirmHandle;
int confirmChannels;
int confirmSampleRate;
@@ -807,16 +803,12 @@ void audio_recordStart(int channels, int sampleRate, PSAudioFormat format)
return;
}
audio.record.requested = true;
audio.record.lastChannels = channels;
audio.record.lastSampleRate = sampleRate;
audio.record.lastFormat = format;
lastChannels = channels;
lastSampleRate = sampleRate;
if (audio.record.started)
realRecordStart(channels, sampleRate, format);
else if (g_state.micDefaultState == MIC_DEFAULT_DENY)
DEBUG_INFO("Microphone access denied by default");
else if (g_state.micDefaultState == MIC_DEFAULT_ALLOW)
else if (g_params.micAlwaysAllow)
{
DEBUG_INFO("Microphone access granted by default");
realRecordStart(channels, sampleRate, format);
@@ -836,8 +828,12 @@ void audio_recordStart(int channels, int sampleRate, PSAudioFormat format)
}
}
static void realRecordStop(void)
void audio_recordStop(void)
{
if (!audio.audioDev || !audio.record.started)
return;
DEBUG_INFO("Microphone recording stopped");
audio.audioDev->record.stop();
audio.record.started = false;
@@ -845,43 +841,6 @@ static void realRecordStop(void)
app_showRecord(false);
}
void audio_recordStop(void)
{
audio.record.requested = false;
if (!audio.audioDev || !audio.record.started)
return;
DEBUG_INFO("Microphone recording stopped");
realRecordStop();
}
void audio_recordToggleKeybind(int sc, void * opaque)
{
if (!audio.audioDev)
return;
if (!audio.record.requested)
{
app_alert(LG_ALERT_WARNING,
"No application is requesting microphone access.");
return;
}
if (audio.record.started)
{
app_alert(LG_ALERT_INFO, "Microphone disabled");
DEBUG_INFO("Microphone recording stopped by user");
realRecordStop();
}
else
{
app_alert(LG_ALERT_INFO, "Microphone enabled");
DEBUG_INFO("Microphone recording started by user");
realRecordStart(audio.record.lastChannels, audio.record.lastSampleRate,
audio.record.lastFormat);
}
}
void audio_recordVolume(int channels, const uint16_t volume[])
{
if (!audio.audioDev || !audio.audioDev->record.volume)

View File

@@ -36,7 +36,6 @@ void audio_playbackData(uint8_t * data, size_t size);
bool audio_supportsRecord(void);
void audio_recordStart(int channels, int sampleRate, PSAudioFormat format);
void audio_recordToggleKeybind(int sc, void * opaque);
void audio_recordStop(void);
void audio_recordVolume(int channels, const uint16_t volume[]);
void audio_recordMute(bool mute);

View File

@@ -33,23 +33,18 @@
#include <string.h>
// forwards
static bool optRendererParse (struct Option * opt, const char * str);
static StringList optRendererValues (struct Option * opt);
static char * optRendererToString (struct Option * opt);
static bool optPosParse (struct Option * opt, const char * str);
static StringList optPosValues (struct Option * opt);
static char * optPosToString (struct Option * opt);
static bool optSizeParse (struct Option * opt, const char * str);
static StringList optSizeValues (struct Option * opt);
static char * optSizeToString (struct Option * opt);
static bool optScancodeParse (struct Option * opt, const char * str);
static StringList optScancodeValues (struct Option * opt);
static bool optScancodeValidate (struct Option * opt, const char ** error);
static char * optScancodeToString (struct Option * opt);
static bool optRotateValidate (struct Option * opt, const char ** error);
static bool optMicDefaultParse (struct Option * opt, const char * str);
static StringList optMicDefaultValues (struct Option * opt);
static char * optMicDefaultToString(struct Option * opt);
static bool optRendererParse (struct Option * opt, const char * str);
static StringList optRendererValues (struct Option * opt);
static char * optRendererToString(struct Option * opt);
static bool optPosParse (struct Option * opt, const char * str);
static StringList optPosValues (struct Option * opt);
static char * optPosToString (struct Option * opt);
static bool optSizeParse (struct Option * opt, const char * str);
static StringList optSizeValues (struct Option * opt);
static char * optSizeToString (struct Option * opt);
static bool optScancodeValidate(struct Option * opt, const char ** error);
static char * optScancodeToString(struct Option * opt);
static bool optRotateValidate (struct Option * opt, const char ** error);
static void doLicense(void);
@@ -259,13 +254,6 @@ static struct Option options[] =
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
},
{
.module = "win",
.name = "overlayDimsDesktop",
.description = "Dim the desktop when in interactive overlay mode",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
{
.module = "win",
.name = "rotate",
@@ -322,12 +310,10 @@ static struct Option options[] =
{
.module = "input",
.name = "escapeKey",
.description = "Specify the escape/menu key to use (use \"help\" to see valid values)",
.description = "Specify the escape key, see <linux/input-event-codes.h> for valid values",
.shortopt = 'm',
.type = OPTION_TYPE_INT,
.value.x_int = KEY_SCROLLLOCK,
.parser = optScancodeParse,
.getValues = optScancodeValues,
.validator = optScancodeValidate,
.toString = optScancodeToString,
},
@@ -503,12 +489,10 @@ static struct Option options[] =
},
{
.module = "audio",
.name = "micDefault",
.description = "Default action when an application opens the microphone (prompt, allow, deny)",
.type = OPTION_TYPE_CUSTOM,
.parser = optMicDefaultParse,
.getValues = optMicDefaultValues,
.toString = optMicDefaultToString
.name = "micAlwaysAllow",
.description = "Always allow guest attempts to access the microphone",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{
.module = "audio",
@@ -620,27 +604,26 @@ bool config_load(int argc, char * argv[])
g_params.framePollInterval = option_get_int ("app" , "framePollInterval" );
g_params.allowDMA = option_get_bool ("app" , "allowDMA" );
g_params.windowTitle = option_get_string("win", "title" );
g_params.autoResize = option_get_bool ("win", "autoResize" );
g_params.allowResize = option_get_bool ("win", "allowResize" );
g_params.keepAspect = option_get_bool ("win", "keepAspect" );
g_params.forceAspect = option_get_bool ("win", "forceAspect" );
g_params.dontUpscale = option_get_bool ("win", "dontUpscale" );
g_params.intUpscale = option_get_bool ("win", "intUpscale" );
g_params.shrinkOnUpscale = option_get_bool ("win", "shrinkOnUpscale" );
g_params.borderless = option_get_bool ("win", "borderless" );
g_params.fullscreen = option_get_bool ("win", "fullScreen" );
g_params.maximize = option_get_bool ("win", "maximize" );
g_params.fpsMin = option_get_int ("win", "fpsMin" );
g_params.ignoreQuit = option_get_bool ("win", "ignoreQuit" );
g_params.noScreensaver = option_get_bool ("win", "noScreensaver" );
g_params.autoScreensaver = option_get_bool ("win", "autoScreensaver" );
g_params.showAlerts = option_get_bool ("win", "alerts" );
g_params.quickSplash = option_get_bool ("win", "quickSplash" );
g_params.overlayDim = option_get_bool ("win", "overlayDimsDesktop");
g_params.uiFont = option_get_string("win", "uiFont" );
g_params.uiSize = option_get_int ("win", "uiSize" );
g_params.jitRender = option_get_bool ("win", "jitRender" );
g_params.windowTitle = option_get_string("win", "title" );
g_params.autoResize = option_get_bool ("win", "autoResize" );
g_params.allowResize = option_get_bool ("win", "allowResize" );
g_params.keepAspect = option_get_bool ("win", "keepAspect" );
g_params.forceAspect = option_get_bool ("win", "forceAspect" );
g_params.dontUpscale = option_get_bool ("win", "dontUpscale" );
g_params.intUpscale = option_get_bool ("win", "intUpscale" );
g_params.shrinkOnUpscale = option_get_bool ("win", "shrinkOnUpscale");
g_params.borderless = option_get_bool ("win", "borderless" );
g_params.fullscreen = option_get_bool ("win", "fullScreen" );
g_params.maximize = option_get_bool ("win", "maximize" );
g_params.fpsMin = option_get_int ("win", "fpsMin" );
g_params.ignoreQuit = option_get_bool ("win", "ignoreQuit" );
g_params.noScreensaver = option_get_bool ("win", "noScreensaver" );
g_params.autoScreensaver = option_get_bool ("win", "autoScreensaver");
g_params.showAlerts = option_get_bool ("win", "alerts" );
g_params.quickSplash = option_get_bool ("win", "quickSplash" );
g_params.uiFont = option_get_string("win" , "uiFont" );
g_params.uiSize = option_get_int ("win" , "uiSize" );
g_params.jitRender = option_get_bool ("win" , "jitRender" );
if (g_params.noScreensaver && g_params.autoScreensaver)
{
@@ -680,7 +663,7 @@ bool config_load(int argc, char * argv[])
g_params.minimizeOnFocusLoss = option_get_bool("win", "minimizeOnFocusLoss");
if ((g_params.useSpice = option_get_bool("spice", "enable")))
if (option_get_bool("spice", "enable"))
{
g_params.spiceHost = option_get_string("spice", "host");
g_params.spicePort = option_get_int ("spice", "port");
@@ -709,6 +692,7 @@ bool config_load(int argc, char * argv[])
g_params.audioPeriodSize = option_get_int("audio", "periodSize");
g_params.audioBufferLatency = option_get_int("audio", "bufferLatency");
g_params.micAlwaysAllow = option_get_bool("audio", "micAlwaysAllow");
g_params.micShowIndicator = option_get_bool("audio", "micShowIndicator");
return true;
@@ -878,20 +862,6 @@ static char * optSizeToString(struct Option * opt)
return str;
}
static StringList optScancodeValues(struct Option * opt)
{
StringList sl = stringlist_new(false);
if (!sl)
return NULL;
// this typecast is safe as the stringlist doesn't own the values
for(unsigned int i = 0; i < KEY_MAX; ++i)
if (linux_to_str[i])
stringlist_push(sl, (char *)linux_to_str[i]);
return sl;
}
static bool optScancodeValidate(struct Option * opt, const char ** error)
{
if (opt->value.x_int >= 0 && opt->value.x_int < KEY_MAX)
@@ -901,29 +871,6 @@ static bool optScancodeValidate(struct Option * opt, const char ** error)
return false;
}
static bool optScancodeParse(struct Option * opt, const char * str)
{
for(unsigned int i = 0; i < KEY_MAX; ++i)
{
if (!linux_to_str[i])
continue;
if (strcasecmp(linux_to_str[i], str) == 0)
{
opt->value.x_int = i;
return true;
}
}
// fallback for pre-keycode name support
char * end;
opt->value.x_int = strtol(str, &end, 10);
if (*end)
return false;
return true;
}
static char * optScancodeToString(struct Option * opt)
{
char * str;
@@ -946,47 +893,3 @@ static bool optRotateValidate(struct Option * opt, const char ** error)
*error = "Rotation angle must be one of 0, 90, 180 or 270";
return false;
}
static bool optMicDefaultParse(struct Option * opt, const char * str)
{
if (!str)
return false;
if (strcasecmp(str, "prompt") == 0)
g_params.micDefaultState = MIC_DEFAULT_PROMPT;
else if (strcasecmp(str, "allow") == 0)
g_params.micDefaultState = MIC_DEFAULT_ALLOW;
else if (strcasecmp(str, "deny") == 0)
g_params.micDefaultState = MIC_DEFAULT_DENY;
else
return false;
return true;
}
static StringList optMicDefaultValues(struct Option * opt)
{
StringList sl = stringlist_new(false);
if (!sl)
return NULL;
stringlist_push(sl, (char *)"prompt");
stringlist_push(sl, (char *)"allow");
stringlist_push(sl, (char *)"deny");
return sl;
}
static char * optMicDefaultToString(struct Option * opt)
{
switch (g_params.micDefaultState)
{
case MIC_DEFAULT_PROMPT:
return strdup("prompt");
case MIC_DEFAULT_ALLOW:
return strdup("allow");
case MIC_DEFAULT_DENY:
return strdup("deny");
}
return NULL;
}

View File

@@ -22,7 +22,6 @@
#include "main.h"
#include "app.h"
#include "audio.h"
#include "core.h"
#include "kb.h"
@@ -36,7 +35,22 @@ static void bind_fullscreen(int sc, void * opaque)
static void bind_video(int sc, void * opaque)
{
app_stopVideo(!g_state.stopVideo);
g_state.stopVideo = !g_state.stopVideo;
app_alert(
LG_ALERT_INFO,
g_state.stopVideo ? "Video Stream Disabled" : "Video Stream Enabled"
);
if (g_state.stopVideo)
{
core_stopCursorThread();
core_stopFrameThread();
}
else
{
core_startCursorThread();
core_startFrameThread();
}
}
static void bind_rotate(int sc, void * opaque)
@@ -127,75 +141,44 @@ static void bind_toggleKey(int sc, void * opaque)
void keybind_commonRegister(void)
{
app_registerKeybind(0, 'F', bind_fullscreen , NULL,
app_registerKeybind(KEY_F, bind_fullscreen , NULL,
"Full screen toggle");
app_registerKeybind(0, 'V', bind_video , NULL,
app_registerKeybind(KEY_V, bind_video , NULL,
"Video stream toggle");
app_registerKeybind(0, 'R', bind_rotate , NULL,
app_registerKeybind(KEY_R, bind_rotate , NULL,
"Rotate the output clockwise by 90° increments");
app_registerKeybind(0, 'Q', bind_quit , NULL,
app_registerKeybind(KEY_Q, bind_quit , NULL,
"Quit");
app_registerKeybind(0, 'O', bind_toggleOverlay, NULL,
app_registerKeybind(KEY_O, bind_toggleOverlay, NULL,
"Toggle overlay");
}
#if ENABLE_AUDIO
static void bind_toggleMicDefault(int sc, void * opaque)
{
g_state.micDefaultState = (g_state.micDefaultState + 1) % MIC_DEFAULT_MAX;
switch (g_state.micDefaultState)
{
case MIC_DEFAULT_PROMPT:
app_alert(LG_ALERT_INFO, "Microphone access will prompt");
break;
case MIC_DEFAULT_ALLOW:
app_alert(LG_ALERT_INFO, "Microphone access allowed by default");
break;
case MIC_DEFAULT_DENY:
app_alert(LG_ALERT_INFO, "Microphone access denied by default");
}
}
#endif
void keybind_spiceRegister(void)
{
/* register the common keybinds for spice */
static bool firstTime = true;
if (firstTime)
{
app_registerKeybind(0, 'I', bind_input, NULL,
app_registerKeybind(KEY_I, bind_input, NULL,
"Spice keyboard & mouse toggle");
app_registerKeybind(KEY_INSERT, 0, bind_mouseSens, (void *) true ,
"Increase mouse sensitivity 0, in capture mode");
app_registerKeybind(KEY_DELETE, 0, bind_mouseSens, (void *) false,
app_registerKeybind(KEY_INSERT, bind_mouseSens, (void *) true ,
"Increase mouse sensitivity in capture mode");
app_registerKeybind(KEY_DELETE, bind_mouseSens, (void *) false,
"Descrease mouse sensitivity in capture mode");
app_registerKeybind(KEY_UP , 0 , bind_toggleKey, (void *) PS2_VOLUME_UP ,
app_registerKeybind(KEY_UP , bind_toggleKey, (void *) PS2_VOLUME_UP ,
"Send volume up to the guest");
app_registerKeybind(KEY_DOWN, 0 , bind_toggleKey, (void *) PS2_VOLUME_DOWN,
app_registerKeybind(KEY_DOWN, bind_toggleKey, (void *) PS2_VOLUME_DOWN,
"Send volume down to the guest");
app_registerKeybind(0 , 'M', bind_toggleKey, (void *) PS2_MUTE ,
app_registerKeybind(KEY_M , bind_toggleKey, (void *) PS2_MUTE ,
"Send mute to the guest");
app_registerKeybind(KEY_LEFTMETA , 0, bind_passthrough, NULL,
app_registerKeybind(KEY_LEFTMETA , bind_passthrough, NULL,
"Send LWin to the guest");
app_registerKeybind(KEY_RIGHTMETA, 0, bind_passthrough, NULL,
app_registerKeybind(KEY_RIGHTMETA, bind_passthrough, NULL,
"Send RWin to the guest");
#if ENABLE_AUDIO
if (audio_supportsRecord())
{
app_registerKeybind(0, 'E', audio_recordToggleKeybind, NULL,
"Toggle audio recording");
app_registerKeybind(0, 'C', bind_toggleMicDefault, NULL,
"Cycle audio recording default");
}
#endif
firstTime = false;
}
@@ -206,32 +189,33 @@ void keybind_spiceRegister(void)
app_releaseKeybind(&handles[i]);
handleCount = 0;
/* register OS based keybinds */
if (app_guestIsLinux())
{
handles[handleCount++] = app_registerKeybind(KEY_F1 , 0, bind_ctrlAltFn, NULL,
handles[handleCount++] = app_registerKeybind(KEY_F1 , bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F1 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F2 , 0, bind_ctrlAltFn, NULL,
handles[handleCount++] = app_registerKeybind(KEY_F2 , bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F2 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F3 , 0, bind_ctrlAltFn, NULL,
handles[handleCount++] = app_registerKeybind(KEY_F3 , bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F3 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F4 , 0, bind_ctrlAltFn, NULL,
handles[handleCount++] = app_registerKeybind(KEY_F4 , bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F4 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F5 , 0, bind_ctrlAltFn, NULL,
handles[handleCount++] = app_registerKeybind(KEY_F5 , bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F5 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F6 , 0, bind_ctrlAltFn, NULL,
handles[handleCount++] = app_registerKeybind(KEY_F6 , bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F6 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F7 , 0, bind_ctrlAltFn, NULL,
handles[handleCount++] = app_registerKeybind(KEY_F7 , bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F7 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F8 , 0, bind_ctrlAltFn, NULL,
handles[handleCount++] = app_registerKeybind(KEY_F8 , bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F8 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F9 , 0, bind_ctrlAltFn, NULL,
handles[handleCount++] = app_registerKeybind(KEY_F9 , bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F9 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F10, 0, bind_ctrlAltFn, NULL,
handles[handleCount++] = app_registerKeybind(KEY_F10, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F10 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F11, 0, bind_ctrlAltFn, NULL,
handles[handleCount++] = app_registerKeybind(KEY_F11, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F11 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F12, 0, bind_ctrlAltFn, NULL,
handles[handleCount++] = app_registerKeybind(KEY_F12, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F12 to the guest");
}
}

View File

@@ -50,7 +50,6 @@
#include "common/version.h"
#include "common/paths.h"
#include "common/cpuinfo.h"
#include "common/ll.h"
#include "core.h"
#include "app.h"
@@ -63,13 +62,11 @@
#include "overlays.h"
#include "overlay_utils.h"
#include "util.h"
#include "render_queue.h"
// forwards
static int renderThread(void * unused);
static LGEvent *e_startup = NULL;
static LGEvent *e_spice = NULL;
static LGThread *t_spice = NULL;
static LGThread *t_render = NULL;
@@ -192,9 +189,8 @@ static int renderThread(void * unused)
return 1;
}
app_initOverlays();
LGTimer * tickTimer;
if (!lgCreateTimer(1000 / TICK_RATE, tickTimerFn, NULL, &tickTimer))
if (!lgCreateTimer(40, tickTimerFn, NULL, &tickTimer))
{
lgTimerDestroy(fpsTimer);
DEBUG_ERROR("Failed to create the tick timer");
@@ -223,7 +219,8 @@ static int renderThread(void * unused)
if (!lgResetEvent(g_state.frameEvent)
&& !forceRender
&& !pending
&& !app_overlayNeedsRender())
&& !app_overlayNeedsRender()
&& !RENDERER(needsRender))
{
if (g_state.ds->skipFrame)
g_state.ds->skipFrame();
@@ -283,9 +280,6 @@ static int renderThread(void * unused)
const uint64_t renderStart = nanotime();
LG_LOCK(g_state.lgrLock);
renderQueue_process();
if (!RENDERER(render, g_params.winRotate, newFrame, invalidate,
preSwapCallback, (void *)&renderStart))
{
@@ -323,13 +317,6 @@ static int renderThread(void * unused)
g_state.state = APP_STATE_SHUTDOWN;
if (g_state.overlays)
{
app_freeOverlays();
ll_free(g_state.overlays);
g_state.overlays = NULL;
}
lgTimerDestroy(tickTimer);
lgTimerDestroy(fpsTimer);
@@ -755,7 +742,7 @@ int main_frameThread(void * unused)
if (dma->fd == -1)
{
const uintptr_t pos = (uintptr_t)msg.mem - (uintptr_t)g_state.shm.mem;
const uintptr_t offset = (uintptr_t)frame->offset + sizeof(FrameBuffer);
const uintptr_t offset = (uintptr_t)frame->offset + FrameBufferStructSize;
dma->dataSize = dataSize;
dma->fd = ivshmemGetDMABuf(&g_state.shm, pos + offset, dataSize);
@@ -778,8 +765,6 @@ int main_frameThread(void * unused)
break;
}
overlaySplash_show(false);
if (frame->flags & FRAME_FLAG_REQUEST_ACTIVATION)
g_state.ds->requestActivation();
@@ -812,21 +797,11 @@ int main_frameThread(void * unused)
lgSignalEvent(g_state.frameEvent);
lgmpClientMessageDone(queue);
// switch over to the LG stream
app_useSpiceDisplay(false);
}
lgmpClientUnsubscribe(&queue);
RENDERER(onRestart);
if (g_state.state != APP_STATE_SHUTDOWN)
{
if (!app_useSpiceDisplay(true))
overlaySplash_show(true);
}
if (g_state.useDMA)
{
for(int i = 0; i < ARRAY_LENGTH(dmaInfo); ++i)
@@ -834,6 +809,7 @@ int main_frameThread(void * unused)
close(dmaInfo[i].fd);
}
return 0;
}
@@ -858,10 +834,6 @@ static void checkUUID(void)
void spiceReady(void)
{
g_state.spiceReady = true;
if (g_state.initialSpiceDisplay)
app_useSpiceDisplay(true);
// set the intial mouse mode
purespice_mouseMode(true);
@@ -877,79 +849,17 @@ void spiceReady(void)
break;
}
if (uuidValid)
{
memcpy(g_state.spiceUUID, info.uuid, sizeof(g_state.spiceUUID));
checkUUID();
}
purespice_freeServerInfo(&info);
if (!uuidValid)
return;
memcpy(g_state.spiceUUID, info.uuid, sizeof(g_state.spiceUUID));
g_state.spiceReady = true;
checkUUID();
if (g_params.useSpiceInput)
keybind_spiceRegister();
lgSignalEvent(e_spice);
}
static void spice_surfaceCreate(unsigned int surfaceId, PSSurfaceFormat format,
unsigned int width, unsigned int height)
{
DEBUG_INFO("Create SPICE surface: id: %d, size: %dx%d",
surfaceId, width, height);
g_state.srcSize.x = width;
g_state.srcSize.y = height;
g_state.haveSrcSize = true;
core_updatePositionInfo();
renderQueue_spiceConfigure(width, height);
renderQueue_spiceDrawFill(0, 0, width, height, 0x0);
}
static void spice_surfaceDestroy(unsigned int surfaceId)
{
DEBUG_INFO("Destroy spice surface %d", surfaceId);
app_useSpiceDisplay(false);
}
static void spice_drawFill(unsigned int surfaceId, int x, int y, int width,
int height, uint32_t color)
{
renderQueue_spiceDrawFill(x, y, width, height, color);
}
static void spice_drawBitmap(unsigned int surfaceId, PSBitmapFormat format,
bool topDown, int x, int y, int width, int height, int stride, void * data)
{
renderQueue_spiceDrawBitmap(x, y, width, height, stride, data, topDown);
}
static void spice_setCursorRGBAImage(int width, int height, int hx, int hy,
const void * data)
{
g_state.spiceHotX = hx;
g_state.spiceHotY = hy;
void * copied = malloc(width * height * 4);
memcpy(copied, data, width * height * 4);
renderQueue_cursorImage(false, width, height, width * 4, copied);
}
static void spice_setCursorMonoImage(int width, int height, int hx, int hy,
const void * xorMask, const void * andMask)
{
g_state.spiceHotX = hx;
g_state.spiceHotY = hy;
int stride = (width + 7) / 8;
uint8_t * buffer = malloc(stride * height * 2);
memcpy(buffer, xorMask, stride * height);
memcpy(buffer + stride * height, andMask, stride * height);
renderQueue_cursorImage(true, width, height * 2, stride, buffer);
}
static void spice_setCursorState(bool visible, int x, int y)
{
renderQueue_cursorState(visible, x, y, g_state.spiceHotX, g_state.spiceHotY);
purespice_freeServerInfo(&info);
}
int spiceThread(void * arg)
@@ -963,11 +873,6 @@ int spiceThread(void * arg)
.port = g_params.spicePort,
.password = "",
.ready = spiceReady,
.inputs =
{
.enable = g_params.useSpiceInput,
.autoConnect = true
},
.clipboard =
{
.enable = g_params.useSpiceClipboard,
@@ -976,42 +881,23 @@ int spiceThread(void * arg)
.release = cb_spiceRelease,
.request = cb_spiceRequest
},
.display =
{
.enable = true,
.autoConnect = false,
.surfaceCreate = spice_surfaceCreate,
.surfaceDestroy = spice_surfaceDestroy,
.drawFill = spice_drawFill,
.drawBitmap = spice_drawBitmap
},
.cursor =
{
.enable = true,
.autoConnect = false,
.setRGBAImage = spice_setCursorRGBAImage,
.setMonoImage = spice_setCursorMonoImage,
.setState = spice_setCursorState,
},
#if ENABLE_AUDIO
.playback =
{
.enable = audio_supportsPlayback(),
.autoConnect = true,
.start = audio_playbackStart,
.volume = audio_playbackVolume,
.mute = audio_playbackMute,
.stop = audio_playbackStop,
.data = audio_playbackData
.enable = audio_supportsPlayback(),
.start = audio_playbackStart,
.volume = audio_playbackVolume,
.mute = audio_playbackMute,
.stop = audio_playbackStop,
.data = audio_playbackData
},
.record =
{
.enable = audio_supportsRecord(),
.autoConnect = true,
.start = audio_recordStart,
.volume = audio_recordVolume,
.mute = audio_recordMute,
.stop = audio_recordStop
.enable = audio_supportsRecord(),
.start = audio_recordStart,
.volume = audio_recordVolume,
.mute = audio_recordMute,
.stop = audio_recordStop
}
#endif
};
@@ -1019,7 +905,6 @@ int spiceThread(void * arg)
if (!purespice_connect(&config))
{
DEBUG_ERROR("Failed to connect to spice server");
lgSignalEvent(e_spice);
goto end;
}
@@ -1035,19 +920,6 @@ int spiceThread(void * arg)
}
}
// send key up events for any pressed keys
if (g_params.useSpiceInput)
{
for(int scancode = 0; scancode < KEY_MAX; ++scancode)
if (g_state.keyDown[scancode])
{
g_state.keyDown[scancode] = false;
purespice_keyUp(scancode);
}
}
purespice_disconnect();
end:
audio_free();
@@ -1057,7 +929,6 @@ end:
if (!g_state.spiceClose)
g_state.state = APP_STATE_SHUTDOWN;
lgSignalEvent(e_spice);
return 0;
}
@@ -1173,6 +1044,8 @@ static int lg_run(void)
ImFontGlyphRangesBuilder_BuildRanges(rangeBuilder, &g_state.fontRange);
ImFontGlyphRangesBuilder_destroy(rangeBuilder);
app_initOverlays();
// initialize metrics ringbuffers
g_state.renderTimings = ringbuffer_new(256, sizeof(float));
g_state.uploadTimings = ringbuffer_new(256, sizeof(float));
@@ -1194,14 +1067,7 @@ static int lg_run(void)
break;
}
if (!g_state.ds)
{
DEBUG_ERROR("No display servers available, tried:");
for (int i = 0; i < LG_DISPLAYSERVER_COUNT; ++i)
DEBUG_ERROR("* %s", LG_DisplayServers[i]->name);
return -1;
}
DEBUG_ASSERT(g_state.ds);
ASSERT_LG_DS_VALID(g_state.ds);
if (g_params.jitRender)
@@ -1231,30 +1097,6 @@ static int lg_run(void)
return -1;
}
// setup the spice startup condition
if (!(e_spice = lgCreateEvent(false, 0)))
{
DEBUG_ERROR("failed to create the spice startup event");
return -1;
}
// setup the startup condition
if (!(e_startup = lgCreateEvent(false, 0)))
{
DEBUG_ERROR("failed to create the startup event");
return -1;
}
// setup the new frame event
if (!(g_state.frameEvent = lgCreateEvent(!g_state.jitRender, 0)))
{
DEBUG_ERROR("failed to create the frame event");
return -1;
}
//setup the render command queue
renderQueue_init();
const PSInit psInit =
{
.log =
@@ -1266,8 +1108,6 @@ static int lg_run(void)
};
purespice_init(&psInit);
g_state.micDefaultState = g_params.micDefaultState;
if (g_params.useSpiceInput ||
g_params.useSpiceClipboard ||
g_params.useSpiceAudio)
@@ -1277,10 +1117,6 @@ static int lg_run(void)
DEBUG_ERROR("spice create thread failed");
return -1;
}
lgWaitEvent(e_spice, TIMEOUT_INFINITE);
if (!g_state.spiceReady)
return -1;
}
// select and init a renderer
@@ -1370,6 +1206,20 @@ static int lg_run(void)
keybind_commonRegister();
// setup the startup condition
if (!(e_startup = lgCreateEvent(false, 0)))
{
DEBUG_ERROR("failed to create the startup event");
return -1;
}
// setup the new frame event
if (!(g_state.frameEvent = lgCreateEvent(!g_state.jitRender, 0)))
{
DEBUG_ERROR("failed to create the frame event");
return -1;
}
if (g_state.jitRender)
DEBUG_INFO("Using JIT render mode");
@@ -1420,24 +1270,12 @@ restart:
msgsCount = 0;
memset(msgs, 0, sizeof(msgs));
uint64_t initialSpiceEnable = microtime() + 1000 * 1000;
while(g_state.state == APP_STATE_RUNNING)
{
if (initialSpiceEnable && microtime() > initialSpiceEnable)
{
/* use the spice display until we get frames from the LG host application
* it is safe to call this before connect as it will be delayed until
* spiceReady is called */
app_useSpiceDisplay(true);
initialSpiceEnable = 0;
}
status = lgmpClientSessionInit(g_state.lgmp, &udataSize, (uint8_t **)&udata);
switch(status)
{
case LGMP_OK:
initialSpiceEnable = 0;
break;
case LGMP_ERR_INVALID_VERSION:
@@ -1657,10 +1495,7 @@ restart:
checkUUID();
if (g_state.state == APP_STATE_RUNNING)
{
DEBUG_INFO("Starting session");
g_state.lgHostConnected = true;
}
g_state.kvmfrFeatures = udata->features;
@@ -1675,9 +1510,8 @@ restart:
{
if (!lgmpClientSessionValid(g_state.lgmp))
{
g_state.lgHostConnected = false;
DEBUG_INFO("Waiting for the host to restart...");
g_state.state = APP_STATE_RESTART;
DEBUG_INFO("Waiting for the host to restart...");
break;
}
g_state.ds->wait(100);
@@ -1691,8 +1525,11 @@ restart:
core_stopFrameThread();
core_stopCursorThread();
g_state.state = APP_STATE_RUNNING;
lgInit();
RENDERER(onRestart);
goto restart;
}
@@ -1703,10 +1540,6 @@ restart:
static void lg_shutdown(void)
{
g_state.state = APP_STATE_SHUTDOWN;
if (t_spice)
lgJoinThread(t_spice, NULL);
if (t_render)
{
if (g_state.jitRender && g_state.ds->stopWaitFrame)
@@ -1730,10 +1563,19 @@ static void lg_shutdown(void)
e_startup = NULL;
}
if (e_spice)
// if spice is still connected send key up events for any pressed keys
if (g_params.useSpiceInput)
{
lgFreeEvent(e_spice);
e_startup = NULL;
for(int scancode = 0; scancode < KEY_MAX; ++scancode)
if (g_state.keyDown[scancode])
{
g_state.keyDown[scancode] = false;
purespice_keyUp(scancode);
}
purespice_disconnect();
if (t_spice)
lgJoinThread(t_spice, NULL);
}
if (g_state.ds)
@@ -1746,14 +1588,18 @@ static void lg_shutdown(void)
}
app_releaseAllKeybinds();
ll_free(g_state.bindings);
if (g_state.dsInitialized)
g_state.ds->free();
ivshmemClose(&g_state.shm);
if (g_state.overlays)
{
app_freeOverlays();
ll_free(g_state.overlays);
g_state.overlays = NULL;
}
renderQueue_free();
ivshmemClose(&g_state.shm);
// free metrics ringbuffers
ringbuffer_free(&g_state.renderTimings);
@@ -1796,17 +1642,15 @@ int main(int argc, char * argv[])
egl_dynProcsInit();
gl_dynProcsInit();
g_state.bindings = ll_new();
g_state.overlays = ll_new();
app_registerOverlay(&LGOverlaySplash, NULL);
app_registerOverlay(&LGOverlayConfig, NULL);
app_registerOverlay(&LGOverlayAlert , NULL);
app_registerOverlay(&LGOverlayFPS , NULL);
app_registerOverlay(&LGOverlayGraphs, NULL);
app_registerOverlay(&LGOverlayHelp , NULL);
app_registerOverlay(&LGOverlayMsg , NULL);
app_registerOverlay(&LGOverlayStatus, NULL);
app_registerOverlay(&LGOverlayRecord, NULL);
// early renderer setup for option registration
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)

View File

@@ -45,13 +45,6 @@ enum RunState
APP_STATE_SHUTDOWN
};
enum MicDefaultState {
MIC_DEFAULT_PROMPT,
MIC_DEFAULT_ALLOW,
MIC_DEFAULT_DENY
};
#define MIC_DEFAULT_MAX (MIC_DEFAULT_DENY + 1)
struct AppState
{
enum RunState state;
@@ -78,20 +71,18 @@ struct AppState
uint8_t spiceUUID[16];
bool spiceReady;
bool initialSpiceDisplay;
uint8_t guestUUID[16];
bool guestUUIDValid;
KVMFROS guestOS;
bool lgHostConnected;
bool stopVideo;
bool ignoreInput;
bool escapeActive;
uint64_t escapeTime;
int escapeAction;
bool escapeHelp;
struct ll * bindings;
KeybindHandle bindings[KEY_MAX];
const char * keyDescription[KEY_MAX];
bool keyDown[KEY_MAX];
bool haveSrcSize;
@@ -148,78 +139,72 @@ struct AppState
bool resizeDone;
bool autoIdleInhibitState;
enum MicDefaultState micDefaultState;
int spiceHotX, spiceHotY;
};
struct AppParams
{
bool autoResize;
bool allowResize;
bool keepAspect;
bool forceAspect;
bool dontUpscale;
bool intUpscale;
bool shrinkOnUpscale;
bool borderless;
bool fullscreen;
bool maximize;
bool minimizeOnFocusLoss;
bool center;
int x, y;
unsigned int w, h;
int fpsMin;
LG_RendererRotate winRotate;
bool useSpice;
bool useSpiceInput;
bool useSpiceClipboard;
bool useSpiceAudio;
const char * spiceHost;
unsigned int spicePort;
bool clipboardToVM;
bool clipboardToLocal;
bool scaleMouseInput;
bool hideMouse;
bool ignoreQuit;
bool noScreensaver;
bool autoScreensaver;
bool grabKeyboard;
bool grabKeyboardOnFocus;
int escapeKey;
bool ignoreWindowsKeys;
bool releaseKeysOnFocusLoss;
bool showAlerts;
bool captureOnStart;
bool quickSplash;
bool overlayDim;
bool alwaysShowCursor;
uint64_t helpMenuDelayUs;
const char * uiFont;
int uiSize;
bool jitRender;
bool autoResize;
bool allowResize;
bool keepAspect;
bool forceAspect;
bool dontUpscale;
bool intUpscale;
bool shrinkOnUpscale;
bool borderless;
bool fullscreen;
bool maximize;
bool minimizeOnFocusLoss;
bool center;
int x, y;
unsigned int w, h;
int fpsMin;
LG_RendererRotate winRotate;
bool useSpiceInput;
bool useSpiceClipboard;
bool useSpiceAudio;
const char * spiceHost;
unsigned int spicePort;
bool clipboardToVM;
bool clipboardToLocal;
bool scaleMouseInput;
bool hideMouse;
bool ignoreQuit;
bool noScreensaver;
bool autoScreensaver;
bool grabKeyboard;
bool grabKeyboardOnFocus;
int escapeKey;
bool ignoreWindowsKeys;
bool releaseKeysOnFocusLoss;
bool showAlerts;
bool captureOnStart;
bool quickSplash;
bool alwaysShowCursor;
uint64_t helpMenuDelayUs;
const char * uiFont;
int uiSize;
bool jitRender;
unsigned int cursorPollInterval;
unsigned int framePollInterval;
bool allowDMA;
unsigned int cursorPollInterval;
unsigned int framePollInterval;
bool allowDMA;
bool forceRenderer;
unsigned int forceRendererIndex;
bool forceRenderer;
unsigned int forceRendererIndex;
const char * windowTitle;
bool mouseRedraw;
int mouseSens;
bool mouseSmoothing;
bool rawMouse;
bool autoCapture;
bool captureInputOnly;
bool showCursorDot;
const char * windowTitle;
bool mouseRedraw;
int mouseSens;
bool mouseSmoothing;
bool rawMouse;
bool autoCapture;
bool captureInputOnly;
bool showCursorDot;
int audioPeriodSize;
int audioBufferLatency;
bool micShowIndicator;
enum MicDefaultState micDefaultState;
int audioPeriodSize;
int audioBufferLatency;
bool micAlwaysAllow;
bool micShowIndicator;
};
struct CBRequest
@@ -231,11 +216,9 @@ struct CBRequest
struct KeybindHandle
{
int sc;
int charcode;
KeybindFn callback;
const char * description;
void * opaque;
int sc;
KeybindFn callback;
void * opaque;
};
enum WarpState

View File

@@ -46,14 +46,16 @@ OverlayConfig;
static OverlayConfig cfg = { 0 };
static void config_earlyInit(void)
static bool config_init(void ** udata, const void * params)
{
cfg.callbacks = ll_new();
cfg.tabCallbacks = ll_new();
}
if (!cfg.callbacks)
{
DEBUG_ERROR("failed to allocate ram");
return false;
}
static bool config_init(void ** udata, const void * params)
{
return true;
}
@@ -75,19 +77,14 @@ static void config_renderLGTab(void)
{
const float fontSize = igGetFontSize();
if (igCollapsingHeader_BoolPtr("Donations", NULL,
if (igCollapsingHeader_BoolPtr("About", NULL,
ImGuiTreeNodeFlags_DefaultOpen))
{
igTextWrapped(LG_DONATION_STR);
igBeginTable("split", 2, 0, (ImVec2){}, 0.0f);
igTableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, fontSize, 0);
igTableNextColumn();
igBulletText("");
igTableNextColumn();
overlayTextURL(LG_DONATION_URL, NULL);
igEndTable();
igText(LG_COPYRIGHT_STR);
overlayTextURL(LG_WEBSITE_STR, NULL);
igText(LG_VERSION_STR);
igSeparator();
igTextWrapped(LG_LICENSE_STR);
}
if (igCollapsingHeader_BoolPtr("Help & Support", NULL,
@@ -105,7 +102,7 @@ static void config_renderLGTab(void)
igEndTable();
}
if (igCollapsingHeader_BoolPtr("The Looking Glass Team", NULL,
if (igCollapsingHeader_BoolPtr("The Looking Glass Team / Donations", NULL,
ImGuiTreeNodeFlags_DefaultOpen))
{
for(const struct LGTeamMember * member = LG_TEAM; member->name; ++member)
@@ -141,15 +138,6 @@ static void config_renderLGTab(void)
}
}
static void config_renderLicenseTab(void)
{
igText(LG_COPYRIGHT_STR);
overlayTextURL(LG_WEBSITE_URL, NULL);
igText(LG_VERSION_STR);
igSeparator();
igTextWrapped(LG_LICENSE_STR);
}
static int config_render(void * udata, bool interactive, struct Rect * windowRects,
int maxRects)
{
@@ -183,7 +171,7 @@ static int config_render(void * udata, bool interactive, struct Rect * windowRec
igBeginTabBar("Configuration#tabs", 0);
if (igBeginTabItem("About", NULL, 0))
if (igBeginTabItem("Looking Glass", NULL, 0))
{
config_renderLGTab();
igEndTabItem();
@@ -220,12 +208,6 @@ static int config_render(void * udata, bool interactive, struct Rect * windowRec
}
ll_unlock(cfg.tabCallbacks);
if (igBeginTabItem("License", NULL, 0))
{
config_renderLicenseTab();
igEndTabItem();
}
igEndTabBar();
overlayGetImGuiRect(windowRects);
@@ -237,7 +219,6 @@ static int config_render(void * udata, bool interactive, struct Rect * windowRec
struct LG_OverlayOps LGOverlayConfig =
{
.name = "Config",
.earlyInit = config_earlyInit,
.init = config_init,
.free = config_free,
.render = config_render

View File

@@ -53,7 +53,7 @@ static void fps_earlyInit(void)
static bool fps_init(void ** udata, const void * params)
{
app_registerKeybind(0, 'D', showFPSKeybind, NULL, "FPS display toggle");
app_registerKeybind(KEY_D, showFPSKeybind, NULL, "FPS display toggle");
showFPS = option_get_bool("win", "showFPS");
return true;
}

View File

@@ -70,15 +70,11 @@ static void showTimingKeybind(int sc, void * opaque)
app_invalidateWindow(false);
}
static void graphs_earlyInit(void)
{
gs.graphs = ll_new();
}
static bool graphs_init(void ** udata, const void * params)
{
gs.graphs = ll_new();
app_overlayConfigRegister("Performance Metrics", configCallback, NULL);
app_registerKeybind(0, 'T', showTimingKeybind, NULL,
app_registerKeybind(KEY_T, showTimingKeybind, NULL,
"Show frame timing information");
return true;
}
@@ -89,7 +85,6 @@ static void graphs_free(void * udata)
while(ll_shift(gs.graphs, (void **)&graph))
free(graph);
ll_free(gs.graphs);
gs.graphs = NULL;
}
struct BufferMetrics
@@ -212,7 +207,6 @@ static int graphs_render(void * udata, bool interactive,
struct LG_OverlayOps LGOverlayGraphs =
{
.name = "Graphs",
.earlyInit = graphs_earlyInit,
.init = graphs_init,
.free = graphs_free,
.render = graphs_render
@@ -240,9 +234,6 @@ GraphHandle overlayGraph_register(const char * name, RingBuffer buffer,
void overlayGraph_unregister(GraphHandle handle)
{
if (!gs.graphs)
return;
ll_removeData(gs.graphs, handle);
free(handle);

View File

@@ -63,20 +63,14 @@ static int help_render(void * udata, bool interactive, struct Rect * windowRects
igTableNextColumn();
igText("Toggle capture mode");
KeybindHandle handle;
ll_forEachNL(g_state.bindings, item, handle)
{
if (!handle->description)
continue;
igTableNextColumn();
if (handle->sc)
igText("%s+%s", escapeName, linux_to_display[handle->sc]);
else
igText("%s+%c", escapeName, handle->charcode);
igTableNextColumn();
igText(handle->description);
}
for (int i = 0; i < KEY_MAX; ++i)
if (g_state.keyDescription[i])
{
igTableNextColumn();
igText("%s+%s", escapeName, linux_to_display[i]);
igTableNextColumn();
igText(g_state.keyDescription[i]);
}
igEndTable();
}

View File

@@ -46,13 +46,9 @@ struct MsgState
struct MsgState l_msg = { 0 };
static void msg_earlyInit(void)
{
l_msg.messages = ll_new();
}
static bool msg_init(void ** udata, const void * params)
{
l_msg.messages = ll_new();
return true;
}
@@ -172,7 +168,6 @@ static int msg_render(void * udata, bool interactive, struct Rect * windowRects,
struct LG_OverlayOps LGOverlayMsg =
{
.name = "msg",
.earlyInit = msg_earlyInit,
.init = msg_init,
.free = msg_free,
.needs_overlay = msg_needsOverlay,

View File

@@ -0,0 +1,87 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "interface/overlay.h"
#include "cimgui.h"
#include "overlay_utils.h"
#include "common/stringutils.h"
#include "../main.h"
static bool recordShow = false;
static bool recordToggle = false;
static unsigned long long lastTick = 0;
static bool record_init(void ** udata, const void * params)
{
return true;
}
static void record_free(void * udata)
{}
static int record_render(void * udata, bool interactive, struct Rect * windowRects,
int maxRects)
{
if (!recordShow || !recordToggle)
return 0;
ImVec2 * screen = overlayGetScreenSize();
ImDrawList_AddCircleFilled(igGetBackgroundDrawList_Nil(),
(ImVec2) { screen->x - 20.0f, 20.0f },
5.0f, 0xFF0000FF, 0
);
*windowRects = (struct Rect) {
.x = screen->x - 26, .y = 14, .w = 12, .h = 12
};
return 1;
}
static bool record_tick(void * udata, unsigned long long tickCount)
{
if (tickCount - lastTick >= 25)
{
recordToggle = !recordToggle;
lastTick = tickCount;
return true;
}
return false;
}
struct LG_OverlayOps LGOverlayRecord =
{
.name = "record",
.init = record_init,
.free = record_free,
.render = record_render,
.tick = record_tick,
};
void overlayRecord_show(bool show)
{
if (show == recordShow)
return;
recordShow = show;
if (g_state.state != APP_STATE_SHUTDOWN)
app_invalidateOverlay(true);
}

View File

@@ -1,264 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "interface/overlay.h"
#include "cimgui.h"
#include "../overlays.h"
#include "../main.h"
#include "overlay_utils.h"
#include "common/array.h"
#include "version.h"
#include "common/appstrings.h"
#include "common/stringlist.h"
#include "common/stringutils.h"
#include "resources/lg-logo.svg.h"
#include <string.h>
#include <math.h>
#define SEGMENTS 12
static bool l_show;
static bool l_fadeDone;
static float l_alpha;
static OverlayImage l_logo;
static float l_vectors[SEGMENTS][2];
static StringList l_tagline;
static StringList l_footline;
static void calcRadialVectors(float vectors[][2], int segments)
{
for (unsigned int i = 0; i < segments; ++i)
{
float angle = (i / (float)(segments - 1)) * M_PI * 2.0f;
vectors[i][0] = cos(angle);
vectors[i][1] = sin(angle);
}
}
static void drawRadialGradient(ImDrawList * list, int x, int y, int w, int h,
ImU32 innerColor, ImU32 outerColor,
float vectors[0][2], int segments)
{
const ImVec2 uv = list->_Data->TexUvWhitePixel;
ImDrawList_PrimReserve(list, (segments - 1) * 3, segments + 1);
for(int i = 0; i < segments - 1; ++i)
{
ImDrawList_PrimWriteIdx(list, list->_VtxCurrentIdx);
ImDrawList_PrimWriteIdx(list, list->_VtxCurrentIdx + i + 1);
ImDrawList_PrimWriteIdx(list, list->_VtxCurrentIdx + i + 2);
}
ImDrawList_PrimWriteVtx(list,
(ImVec2){x, y},
uv,
innerColor);
for (unsigned int i = 0; i < segments; ++i)
ImDrawList_PrimWriteVtx(list,
(ImVec2){
x + vectors[i][0] * w,
y + vectors[i][1] * h
},
uv,
outerColor);
}
static bool splash_init(void ** udata, const void * params)
{
l_show = true;
l_fadeDone = false;
l_alpha = 1.0f;
overlayLoadSVG(b_lg_logo_svg, b_lg_logo_svg_size, &l_logo, 200, 200);
calcRadialVectors(l_vectors, ARRAY_LENGTH(l_vectors));
l_tagline = stringlist_new(false);
l_footline = stringlist_new(false);
stringlist_push(l_tagline, "Looking Glass");
stringlist_push(l_tagline, (char *)LG_WEBSITE_URL);
stringlist_push(l_footline, (char *)LG_VERSION_STR);
stringlist_push(l_footline, (char *)LG_COPYRIGHT_STR);
return true;
}
static void splash_free(void * udata)
{
overlayFreeImage(&l_logo);
stringlist_free(&l_tagline );
stringlist_free(&l_footline);
}
static void renderText(ImDrawList * list, int x, int y, ImU32 color,
StringList lines, bool topAlign)
{
static float textHeight = 0.0f;
ImVec2 size;
if (textHeight == 0.0f)
{
const char * tmp = "W";
igCalcTextSize(&size, tmp, tmp + 1, false, 0.0f);
textHeight = size.y;
}
float fy = y;
const unsigned int count = stringlist_count(lines);
for(int i = 0; i < count; ++i)
{
const char * text = stringlist_at(lines, topAlign ? i : count - i - 1);
const int len = strlen(text);
igCalcTextSize(&size, text, text + len, false, 0.0f);
ImDrawList_AddText_Vec2(
list,
(ImVec2){
x - size.x / 2,
topAlign ? fy : fy - size.y
},
color,
text,
text + len
);
if (topAlign)
fy += textHeight;
else
fy -= textHeight;
}
}
static int splash_render(void * udata, bool interactive, struct Rect * windowRects,
int maxRects)
{
if (!l_show && l_fadeDone)
return 0;
const float alpha = l_fadeDone ? 1.0f : l_alpha;
ImVec2 * screen = overlayGetScreenSize();
ImDrawList * list = igGetBackgroundDrawList_Nil();
struct Rect rect = {
.x = 0,
.y = 0,
.w = screen->x,
.h = screen->y
};
struct Rect logoRect = {
.x = screen->x / 2 - l_logo.width / 2,
.y = screen->y / 2 - l_logo.height / 2,
.w = l_logo.width,
.h = l_logo.height
};
const ImU32 innerColor = igColorConvertFloat4ToU32((ImVec4){
0.234375f, 0.015625f, 0.425781f, alpha});
const ImU32 outerColor = igColorConvertFloat4ToU32((ImVec4){
0.0f, 0.0f, 0.0f, alpha});
const ImU32 imageColor = igColorConvertFloat4ToU32((ImVec4){
1.0f, 1.0f, 1.0f, alpha});
const ImU32 fontColor = igColorConvertFloat4ToU32((ImVec4){
0.8f, 0.8f, 0.8f, alpha});
drawRadialGradient(list,
screen->x / 2, screen->y / 2,
screen->x , screen->y ,
innerColor,
outerColor,
l_vectors,
ARRAY_LENGTH(l_vectors));
ImDrawList_AddImage(
list,
l_logo.tex,
(ImVec2){
logoRect.x,
logoRect.y
},
(ImVec2){
logoRect.x + logoRect.w,
logoRect.y + logoRect.h
},
(ImVec2){ 0, 0 },
(ImVec2){ 1, 1 },
imageColor);
renderText(list,
screen->x / 2,
logoRect.y + logoRect.h + 10,
fontColor,
l_tagline,
true);
renderText(list,
screen->x / 2,
screen->y - 10,
fontColor,
l_footline,
false);
*windowRects = rect;
return 1;
}
static bool splash_tick(void * udata, unsigned long long tickCount)
{
if (!l_show && !l_fadeDone)
{
if (g_params.quickSplash)
{
l_fadeDone = true;
return true;
}
l_alpha -= 1.0f / TICK_RATE;
if (l_alpha <= 0.0f)
l_fadeDone = true;
return true;
}
return false;
}
struct LG_OverlayOps LGOverlaySplash =
{
.name = "splash",
.init = splash_init,
.free = splash_free,
.render = splash_render,
.tick = splash_tick,
};
void overlaySplash_show(bool show)
{
if (l_show == show)
return;
l_show = show;
app_invalidateOverlay(true);
}

View File

@@ -1,155 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "interface/overlay.h"
#include "math.h"
#include "cimgui.h"
#include "../overlays.h"
#include "../main.h"
#include "overlay_utils.h"
#include "resources/status/recording.svg.h"
#include "resources/status/spice.svg.h"
//TODO: Make this user configurable?
#define ICON_SIZE 32
static bool l_state[LG_USER_STATUS_MAX] = { 0 };
static OverlayImage l_image[LG_USER_STATUS_MAX] = { 0 };
static bool l_recordToggle;
static double l_scale = 1.0;
static void status_loadImage(const char * data, unsigned int size,
OverlayImage * image, int width, int height)
{
overlayFreeImage(image);
overlayLoadSVG(data, size, image, width, height);
}
static void status_loadIcons(double scale)
{
int iconSize = ceil(scale * ICON_SIZE);
status_loadImage(b_status_recording_svg, b_status_recording_svg_size,
&l_image[LG_USER_STATUS_RECORDING], iconSize, iconSize);
status_loadImage(b_status_spice_svg, b_status_spice_svg_size,
&l_image[LG_USER_STATUS_SPICE], iconSize, iconSize);
}
static bool status_init(void ** udata, const void * params)
{
status_loadIcons(l_scale);
return true;
}
static void status_free(void * udata)
{
for(int i = 0; i < LG_USER_STATUS_MAX; ++i)
overlayFreeImage(&l_image[i]);
}
static int status_render(void * udata, bool interactive, struct Rect * windowRects,
int maxRects)
{
const int marginX = 10;
const int marginY = 10;
const int gapX = 5;
if (g_state.windowScale > l_scale)
{
l_scale = g_state.windowScale;
status_loadIcons(l_scale);
}
ImVec2 * screen = overlayGetScreenSize();
struct Rect rect = {
.x = screen->x - LG_USER_STATUS_MAX * (ICON_SIZE + gapX) - marginX,
.y = marginY,
.w = LG_USER_STATUS_MAX * (ICON_SIZE + gapX),
.h = ICON_SIZE
};
int xPos = screen->x - marginX;
for(int i = 0; i < LG_USER_STATUS_MAX; ++i)
{
OverlayImage * img = &l_image[i];
if (!l_state[i] || !img->tex)
continue;
// if the recording indicator is off, don't draw but reserve space
if (i == LG_USER_STATUS_RECORDING && !l_recordToggle)
goto next;
ImDrawList_AddImage(
igGetBackgroundDrawList_Nil(),
img->tex,
(ImVec2){
xPos,
marginY
},
(ImVec2){
xPos - ICON_SIZE,
img->height / l_scale + marginY
},
(ImVec2){ 0, 0 },
(ImVec2){ 1, 1 },
0xFFFFFFFF);
next:
xPos -= ICON_SIZE + gapX;
}
*windowRects = rect;
return 1;
}
static bool status_tick(void * udata, unsigned long long tickCount)
{
static unsigned long long lastTick = 0;
if (tickCount - lastTick >= 25)
{
l_recordToggle = !l_recordToggle;
lastTick = tickCount;
return true;
}
return false;
}
struct LG_OverlayOps LGOverlayStatus =
{
.name = "status",
.init = status_init,
.free = status_free,
.render = status_render,
.tick = status_tick,
};
void overlayStatus_set(LGUserStatus status, bool value)
{
if (l_state[status] == value)
return;
l_state[status] = value;
app_invalidateOverlay(true);
};

View File

@@ -26,11 +26,6 @@
#include "cimgui.h"
#include "main.h"
#define NANOSVG_IMPLEMENTATION
#define NANOSVG_ALL_COLOR_KEYWORDS
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvgrast.h"
void overlayGetImGuiRect(struct Rect * rect)
{
ImVec2 size;
@@ -83,93 +78,3 @@ void overlayTextMaybeURL(const char * text, bool wrapped)
else
igText(text);
}
bool overlayLoadSVG(const char * data, unsigned int size, OverlayImage * image,
int width, int height)
{
image->tex = NULL;
//nsvgParse alters the data, we need to make a copy and null terminate it
char * svg = malloc(size + 1);
if (!svg)
{
DEBUG_ERROR("out of ram");
goto err;
}
memcpy(svg, data, size);
svg[size] = 0;
NSVGimage * nvi = nsvgParse(svg, "px", 96.0);
if (!nvi)
{
free(svg);
DEBUG_ERROR("nvsgParseFromData failed");
goto err;
}
free(svg);
NSVGrasterizer * rast = nsvgCreateRasterizer();
if (!rast)
{
DEBUG_ERROR("nsvgCreateRasterizer failed");
goto err_image;
}
double srcAspect = nvi->width / nvi->height;
double dstAspect = (double)width / (double)height;
float scale;
if (dstAspect > srcAspect)
{
image->width = (double)height * srcAspect;
image->height = height;
scale = (float)image->width / nvi->width;
}
else
{
image->width = width;
image->height = (double)width / srcAspect;
scale = (float)image->height / nvi->height;
}
uint8_t * img = malloc(image->width * image->height * 4);
if (!img)
{
DEBUG_ERROR("out of ram");
goto err_rast;
}
nsvgRasterize(rast, nvi,
0.0f, 0.0f,
scale,
img,
image->width,
image->height,
image->width * 4);
image->tex = RENDERER(createTexture, image->width, image->height, img);
free(img);
if (!image->tex)
{
DEBUG_ERROR("renderer failed to create the texture");
goto err_rast;
}
err_rast:
nsvgDeleteRasterizer(rast);
err_image:
nsvgDelete(nvi);
err:
return image->tex != NULL;
}
void overlayFreeImage(OverlayImage * image)
{
if (!image->tex)
return;
RENDERER(freeTexture, image->tex);
}

View File

@@ -33,14 +33,13 @@ struct Overlay
struct Rect lastRects[MAX_OVERLAY_RECTS];
};
extern struct LG_OverlayOps LGOverlaySplash;
extern struct LG_OverlayOps LGOverlayAlert;
extern struct LG_OverlayOps LGOverlayFPS;
extern struct LG_OverlayOps LGOverlayGraphs;
extern struct LG_OverlayOps LGOverlayHelp;
extern struct LG_OverlayOps LGOverlayConfig;
extern struct LG_OverlayOps LGOverlayMsg;
extern struct LG_OverlayOps LGOverlayStatus;
extern struct LG_OverlayOps LGOverlayRecord;
void overlayAlert_show(LG_MsgAlert type, const char * fmt, va_list args);
@@ -57,15 +56,6 @@ void overlayConfig_register(const char * title,
void overlayConfig_registerTab(const char * title,
void (*callback)(void * udata, int * id), void * udata);
typedef enum LG_UserStatus
{
LG_USER_STATUS_SPICE,
LG_USER_STATUS_RECORDING,
LG_USER_STATUS_MAX
}
LGUserStatus;
void overlaySplash_show(bool show);
void overlayStatus_set(LGUserStatus, bool value);
void overlayRecord_show(bool show);
#endif

View File

@@ -1,179 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "render_queue.h"
#include <string.h>
#include "common/ll.h"
#include "main.h"
#include "overlays.h"
struct ll * l_renderQueue = NULL;
void renderQueue_init(void)
{
l_renderQueue = ll_new();
}
void renderQueue_free(void)
{
if (!l_renderQueue)
return;
renderQueue_clear();
ll_free(l_renderQueue);
}
void renderQueue_clear(void)
{
RenderCommand * cmd;
while(ll_shift(l_renderQueue, (void **)&cmd))
{
if (cmd->op == SPICE_OP_DRAW_BITMAP)
free(cmd->spiceDrawBitmap.data);
free(cmd);
}
}
void renderQueue_spiceConfigure(int width, int height)
{
RenderCommand * cmd = malloc(sizeof(*cmd));
cmd->op = SPICE_OP_CONFIGURE;
cmd->spiceConfigure.width = width;
cmd->spiceConfigure.height = height;
ll_push(l_renderQueue, cmd);
app_invalidateWindow(true);
}
void renderQueue_spiceDrawFill(int x, int y, int width, int height,
uint32_t color)
{
RenderCommand * cmd = malloc(sizeof(*cmd));
cmd->op = SPICE_OP_DRAW_FILL;
cmd->spiceFillRect.x = x;
cmd->spiceFillRect.y = y;
cmd->spiceFillRect.width = width;
cmd->spiceFillRect.height = height;
cmd->spiceFillRect.color = color;
ll_push(l_renderQueue, cmd);
app_invalidateWindow(true);
}
void renderQueue_spiceDrawBitmap(int x, int y, int width, int height, int stride,
void * data, bool topDown)
{
RenderCommand * cmd = malloc(sizeof(*cmd));
cmd->op = SPICE_OP_DRAW_BITMAP;
cmd->spiceDrawBitmap.x = x;
cmd->spiceDrawBitmap.y = y;
cmd->spiceDrawBitmap.width = width;
cmd->spiceDrawBitmap.height = height;
cmd->spiceDrawBitmap.stride = stride;
cmd->spiceDrawBitmap.data = malloc(height * stride);
cmd->spiceDrawBitmap.topDown = topDown;
memcpy(cmd->spiceDrawBitmap.data, data, height * stride);
ll_push(l_renderQueue, cmd);
app_invalidateWindow(true);
}
void renderQueue_spiceShow(bool show)
{
RenderCommand * cmd = malloc(sizeof(*cmd));
cmd->op = SPICE_OP_SHOW;
cmd->spiceShow.show = show;
ll_push(l_renderQueue, cmd);
app_invalidateWindow(true);
}
void renderQueue_cursorState(bool visible, int x, int y, int hx, int hy)
{
RenderCommand * cmd = malloc(sizeof(*cmd));
cmd->op = CURSOR_OP_STATE;
cmd->cursorState.visible = visible;
cmd->cursorState.x = x;
cmd->cursorState.y = y;
cmd->cursorState.hx = hx;
cmd->cursorState.hy = hy;
ll_push(l_renderQueue, cmd);
}
void renderQueue_cursorImage(bool monochrome, int width, int height, int pitch,
uint8_t * data)
{
RenderCommand * cmd = malloc(sizeof(*cmd));
cmd->op = CURSOR_OP_IMAGE;
cmd->cursorImage.monochrome = monochrome;
cmd->cursorImage.width = width;
cmd->cursorImage.height = height;
cmd->cursorImage.pitch = pitch;
cmd->cursorImage.data = data;
ll_push(l_renderQueue, cmd);
}
void renderQueue_process(void)
{
RenderCommand * cmd;
while(ll_shift(l_renderQueue, (void **)&cmd))
{
switch(cmd->op)
{
case SPICE_OP_CONFIGURE:
RENDERER(spiceConfigure,
cmd->spiceConfigure.width, cmd->spiceConfigure.height);
break;
case SPICE_OP_DRAW_FILL:
RENDERER(spiceDrawFill,
cmd->spiceFillRect.x , cmd->spiceFillRect.y,
cmd->spiceFillRect.width, cmd->spiceFillRect.height,
cmd->spiceFillRect.color);
break;
case SPICE_OP_DRAW_BITMAP:
RENDERER(spiceDrawBitmap,
cmd->spiceDrawBitmap.x , cmd->spiceDrawBitmap.y,
cmd->spiceDrawBitmap.width , cmd->spiceDrawBitmap.height,
cmd->spiceDrawBitmap.stride, cmd->spiceDrawBitmap.data,
cmd->spiceDrawBitmap.topDown);
free(cmd->spiceDrawBitmap.data);
break;
case SPICE_OP_SHOW:
RENDERER(spiceShow, cmd->spiceShow.show);
if (cmd->spiceShow.show)
overlaySplash_show(false);
break;
case CURSOR_OP_STATE:
RENDERER(onMouseEvent, cmd->cursorState.visible, cmd->cursorState.x,
cmd->cursorState.y, cmd->cursorState.hx, cmd->cursorState.hy);
break;
case CURSOR_OP_IMAGE:
RENDERER(onMouseShape,
cmd->cursorImage.monochrome ? LG_CURSOR_MONOCHROME : LG_CURSOR_COLOR,
cmd->cursorImage.width, cmd->cursorImage.height,
cmd->cursorImage.pitch, cmd->cursorImage.data);
free(cmd->cursorImage.data);
}
free(cmd);
}
}

View File

@@ -1,109 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "common/ll.h"
typedef struct
{
enum
{
SPICE_OP_CONFIGURE,
SPICE_OP_DRAW_FILL,
SPICE_OP_DRAW_BITMAP,
SPICE_OP_SHOW,
CURSOR_OP_STATE,
CURSOR_OP_IMAGE,
}
op;
union
{
struct
{
int width, height;
}
spiceConfigure;
struct
{
int x, y;
int width, height;
uint32_t color;
}
spiceFillRect;
struct
{
int x , y;
int width, height;
int stride;
uint8_t * data;
bool topDown;
}
spiceDrawBitmap;
struct
{
bool show;
}
spiceShow;
struct
{
bool visible;
int x;
int y;
int hx;
int hy;
}
cursorState;
struct
{
bool monochrome;
int width;
int height;
int pitch;
uint8_t * data;
}
cursorImage;
};
}
RenderCommand;
void renderQueue_init(void);
void renderQueue_free(void);
void renderQueue_clear(void);
void renderQueue_process(void);
void renderQueue_spiceConfigure(int width, int height);
void renderQueue_spiceDrawFill(int x, int y, int width, int height,
uint32_t color);
void renderQueue_spiceDrawBitmap(int x, int y, int width, int height, int stride,
void * data, bool topDown);
void renderQueue_spiceShow(bool show);
void renderQueue_cursorState(bool visible, int x, int y, int hx, int hy);
void renderQueue_cursorImage(bool monochrome, int width, int height, int pitch,
uint8_t * data);

View File

@@ -4,7 +4,7 @@ project(lg_common LANGUAGES C)
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
include_directories(
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/include
)
add_definitions(-D_GNU_SOURCE)
@@ -42,8 +42,8 @@ add_library(lg_common STATIC ${COMMON_SOURCES})
target_link_libraries(lg_common lg_common_platform)
target_include_directories(lg_common
INTERFACE
include
PRIVATE
src
INTERFACE
include
PRIVATE
src
)

View File

@@ -21,10 +21,8 @@
#include "common/types.h"
extern const char * LG_COPYRIGHT_STR;
extern const char * LG_WEBSITE_URL;
extern const char * LG_WEBSITE_STR;
extern const char * LG_LICENSE_STR;
extern const char * LG_DONATION_STR;
extern const char * LG_DONATION_URL;
extern const StringPair LG_HELP_LINKS[];
struct LGTeamMember

View File

@@ -24,21 +24,16 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdatomic.h>
#define FB_CHUNK_SIZE 1048576 // 1MB
#define FB_SPIN_LIMIT 10000 // 10ms
#define FB_WP_TYPE atomic_uint_least32_t
#define FB_WP_SIZE sizeof(FB_WP_TYPE)
typedef struct stFrameBuffer
{
FB_WP_TYPE wp;
uint8_t data[0];
} FrameBuffer;
typedef struct stFrameBuffer FrameBuffer;
typedef bool (*FrameBufferReadFn)(void * opaque, const void * src, size_t size);
/**
* The size of the FrameBuffer struct
*/
extern const size_t FrameBufferStructSize;
/**
* Wait for the framebuffer to fill to the specified size
*/

View File

@@ -31,10 +31,6 @@
_a > _b ? _a : _b; })
#endif
#ifndef clamp
#define clamp(v,a,b) min(max(v, a), b)
#endif
#define UPCAST(type, x) \
(type *)((uintptr_t)(x) - offsetof(type, base))

View File

@@ -20,18 +20,10 @@
#include "common/appstrings.h"
const char * LG_DONATION_STR =
"Looking Glass is written and supported by a small group of developers in our "
"free time and has been given to the world free of charge. If you find this "
"program useful please consider supporting our efforts through the following "
"address:";
const char * LG_DONATION_URL = "https://looking-glass.io/donations";
const char * LG_COPYRIGHT_STR =
"Copyright © 2017-2022 The Looking Glass Authors";
const char * LG_WEBSITE_URL =
const char * LG_WEBSITE_STR =
"https://looking-glass.io";
const char * LG_LICENSE_STR =
@@ -80,7 +72,30 @@ const struct LGTeamMember LG_TEAM[] =
"and direction of the project as a whole, focusing on the X11 "
"platform as well as maintaining the Looking Glass community on "
"Discord and the Level1Tech forums.",
.donate = { { 0 } }
.donate =
{
{
.name = "GitHub Sponsors",
.value = "https://github.com/sponsors/gnif"
},
{
.name = "Patreon",
.value = "https://www.patreon.com/gnif"
},
{
.name = "Ko-Fi",
.value = "https://ko-fi.com/lookingglass"
},
{
.name = "Bitcoin (BTC)",
.value = "14ZFcYjsKPiVreHqcaekvHGL846u3ZuT13"
},
{
.name = "Ethereum (ETH)",
.value = "0x6f8aEe454384122bF9ed28f025FBCe2Bce98db85"
},
{ 0 }
}
},
{
.name = "Guanzhong Chen (quantum)",
@@ -101,10 +116,7 @@ const struct LGTeamMember LG_TEAM[] =
},
{
.name = "Jonathan Rubenstein (JJRcop)",
.blurb =
"Documentation Guru and Discord Community Manager. Takes around four "
"or five tries and weeks of delay to turn ideas and spitballing into "
"tangible work, but tries to make the result look beautiful.",
.blurb = "Documentation Guru and Discord Community Manager",
.donate = { { 0 } }
},
{

View File

@@ -27,10 +27,22 @@
#endif
#include <string.h>
#include <stdatomic.h>
#include <emmintrin.h>
#include <smmintrin.h>
#include <unistd.h>
#define FB_CHUNK_SIZE 1048576 // 1MB
#define FB_SPIN_LIMIT 10000 // 10ms
struct stFrameBuffer
{
atomic_uint_least32_t wp;
uint8_t data[0];
};
const size_t FrameBufferStructSize = sizeof(FrameBuffer);
bool framebuffer_wait(const FrameBuffer * frame, size_t size)
{
while(atomic_load_explicit(&frame->wp, memory_order_acquire) < size)

View File

@@ -2,21 +2,21 @@ cmake_minimum_required(VERSION 3.0)
project(lg_common_platform_code LANGUAGES C)
include_directories(
${PROJECT_SOURCE_DIR}/include
${PROJECT_TOP}
${PROJECT_SOURCE_DIR}/include
${PROJECT_TOP}
)
add_library(lg_common_platform_code STATIC
debug.c
crash.c
sysinfo.c
thread.c
event.c
ivshmem.c
time.c
paths.c
open.c
cpuinfo.c
debug.c
crash.c
sysinfo.c
thread.c
event.c
ivshmem.c
time.c
paths.c
open.c
cpuinfo.c
)
if(ENABLE_BACKTRACE)
@@ -25,7 +25,7 @@ if(ENABLE_BACKTRACE)
endif()
target_link_libraries(lg_common_platform_code
lg_common
pthread
rt
lg_common
pthread
rt
)

View File

@@ -2,49 +2,49 @@ cmake_minimum_required(VERSION 3.0)
project(lg_common_platform_code LANGUAGES C)
include_directories(
${PROJECT_TOP}/vendor/ivshmem
${PROJECT_TOP}/vendor/ivshmem
)
# allow use of functions for Windows 7 or later
add_compile_definitions(WINVER=0x0601 _WIN32_WINNT=0x0601)
if (MINGW)
# Build our own ntdll.dll import library
# This tricks MinGW into not linking stuff like memcpy from ntdll.dll instead of mscvrt.dll
if(NOT CMAKE_DLLTOOL)
# cmake older than 3.16 doesn't know how to find dlltool
find_program(CMAKE_DLLTOOL NAMES "x86_64-w64-mingw32-dlltool" "dlltool.exe" DOC "dlltool executable")
endif()
add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/ntdll.a"
COMMAND "${CMAKE_DLLTOOL}" -d "${PROJECT_SOURCE_DIR}/ntdll.def" -l "${PROJECT_BINARY_DIR}/ntdll.a"
MAIN_DEPENDENCY "${PROJECT_SOURCE_DIR}/ntdll.def"
COMMENT "Building import library ntdll.a"
VERBATIM
)
add_custom_target(ntdll_target DEPENDS "${PROJECT_BINARY_DIR}/ntdll.a")
add_library(ntdll STATIC IMPORTED GLOBAL)
add_dependencies(ntdll ntdll_target)
set_target_properties(ntdll PROPERTIES IMPORTED_LOCATION "${PROJECT_BINARY_DIR}/ntdll.a")
# Build our own ntdll.dll import library
# This tricks MinGW into not linking stuff like memcpy from ntdll.dll instead of mscvrt.dll
if(NOT CMAKE_DLLTOOL)
# cmake older than 3.16 doesn't know how to find dlltool
find_program(CMAKE_DLLTOOL NAMES "x86_64-w64-mingw32-dlltool" "dlltool.exe" DOC "dlltool executable")
endif()
add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/ntdll.a"
COMMAND "${CMAKE_DLLTOOL}" -d "${PROJECT_SOURCE_DIR}/ntdll.def" -l "${PROJECT_BINARY_DIR}/ntdll.a"
MAIN_DEPENDENCY "${PROJECT_SOURCE_DIR}/ntdll.def"
COMMENT "Building import library ntdll.a"
VERBATIM
)
add_custom_target(ntdll_target DEPENDS "${PROJECT_BINARY_DIR}/ntdll.a")
add_library(ntdll STATIC IMPORTED GLOBAL)
add_dependencies(ntdll ntdll_target)
set_target_properties(ntdll PROPERTIES IMPORTED_LOCATION "${PROJECT_BINARY_DIR}/ntdll.a")
endif()
add_library(lg_common_platform_code STATIC
debug.c
crash.c
sysinfo.c
thread.c
event.c
windebug.c
ivshmem.c
time.c
cpuinfo.c
debug.c
crash.c
sysinfo.c
thread.c
event.c
windebug.c
ivshmem.c
time.c
cpuinfo.c
)
target_link_libraries(lg_common_platform_code
lg_common
setupapi
ntdll
lg_common
setupapi
ntdll
)
if (ENABLE_BACKTRACE)
target_link_libraries(lg_common_platform_code dbghelp)
target_link_libraries(lg_common_platform_code dbghelp)
endif()

View File

@@ -42,7 +42,7 @@ Client
.. _installing_build_dependencies:
Installing build dependencies
Installing Build Dependencies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These required libraries and tools should be installed first.
@@ -56,7 +56,7 @@ These required libraries and tools should be installed first.
.. _client_dependencies:
Required dependencies
Required Dependencies
^^^^^^^^^^^^^^^^^^^^^
..
@@ -66,17 +66,17 @@ Required dependencies
Depends: or Recommends: from another listed package is not allowed.
All required packages must be listed.
- ``cmake``
- ``gcc``, ``g++`` \| ``clang``
- ``libegl-dev``
- ``libgl-dev``
- ``libgles-dev``
- ``libfontconfig-dev``
- ``libgmp-dev``
- ``libspice-protocol-dev``
- ``make``
- ``nettle-dev``
- ``pkg-config``
- cmake
- gcc, g++ \| clang
- libegl-dev
- libgl-dev
- libgles-dev
- libfontconfig-dev
- libgmp-dev
- libspice-protocol-dev
- make
- nettle-dev
- pkg-config
.. _client_deps_may_be_disabled:
@@ -88,41 +88,41 @@ feature is disabled when running :ref:`cmake <client_building>`.
- Disable with ``cmake -DENABLE_BACKTRACE=no ..``
- ``binutils-dev``
- binutils-dev
- Disable with ``cmake -DENABLE_X11=no ..``
- ``libx11-dev``
- ``libxfixes-dev``
- ``libxi-dev``
- ``libxinerama-dev``
- ``libxss-dev``
- ``libxcursor-dev``
- ``libxpresent-dev``
- libx11-dev
- libxfixes-dev
- libxi-dev
- libxinerama-dev
- libxss-dev
- libxcursor-dev
- libxpresent-dev
- Disable with ``cmake -DENABLE_WAYLAND=no ..``
- ``libxkbcommon-dev``
- ``libwayland-bin``
- ``libwayland-dev``
- ``wayland-protocols``
- libxkbcommon-dev
- libwayland-bin
- libwayland-dev
- wayland-protocols
- Disable with ``cmake -DENABLE_PIPEWIRE=no ..``
- ``libpipewire-0.3-dev``
- ``libsamplerate0-dev``
- libpipewire-0.3-dev
- libsamplerate0-dev
- Disable with ``cmake -DENABLE_PULSEAUDIO=no ..``
- ``libpulse-dev``
- ``libsamplerate0-dev``
- libpulse-dev
- libsamplerate0-dev
.. _client_deps_recommended:
Recommended
<<<<<<<<<<<
- ``fonts-dejavu-core`` (This is the default UI font, but a random font will
- fonts-dejavu-core (This is the default UI font, but a random font will
be chosen if not available).
.. _client_fetching_with_apt:
@@ -140,7 +140,7 @@ You can fetch these dependencies with the following command:
libxpresent-dev libxss-dev libxkbcommon-dev libwayland-dev wayland-protocols \
libpipewire-0.3-dev libpulse-dev libsamplerate0-dev
You may omit some dependencies if you disable the feature which requires them
You may omit some dependencies, if you disable the feature which requires them
when running :ref:`cmake <client_building>`.
(See :ref:`client_deps_may_be_disabled`)
@@ -160,43 +160,27 @@ into the *LookingGlass* directory.
cmake ../
make
This will build the ``looking-glass-client`` binary, which is used to display
This will build the **looking-glass-client** binary, which is used to display
frames from the guest.
You can then :ref:`continue installing Looking Glass <client_install>`, or run
it directly from the build directory:
.. code:: bash
./looking-glass-client
.. seealso::
- :ref:`Client installation <client_install>`
- :ref:`Client usage <client_usage>`
.. note::
For users running GNOME on Wayland, you may want to enable ``libdecor`` when
building.
.. code:: bash
cmake -DENABLE_LIBDECOR=ON ../
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>`.
.. seealso::
- :ref:`Installing the Client <client_install>`
- :ref:`Client Usage <client_usage>`
.. note::
The most common compile error is related to backtrace support. Try disabling
this when building:
.. code:: bash
cmake -DENABLE_BACKTRACE=0 ../
If you disable this and need support for a crash, use ``gdb`` to obtain a
backtrace manually.
The most common compile error is related to backtrace support. This can be
disabled by adding the following option to the cmake command:
``-DENABLE_BACKTRACE=0``. However, if you disable this and need support for
a crash, use ``gdb`` to obtain a backtrace manually.
.. _host_building:

View File

@@ -22,7 +22,7 @@ from lgrelease import release
# -- Project information -----------------------------------------------------
project = 'Looking Glass'
copyright = '2022, Looking Glass team'
copyright = '2021, Looking Glass team'
author = 'Geoffrey McRae and the Looking Glass team'
rst_prolog = """
@@ -90,12 +90,12 @@ html_favicon = '../resources/icon.ico'
html_logo = '../resources/icon-128x128.png'
html_css_files = [
'lg-custom.css',
'center-rtd.css',
]
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = [
'css/',
'css/center-rtd.css',
]

View File

@@ -13,7 +13,6 @@ Developers
* gnif (Geoffrey McRae)
* quantum (Guanzhong Chen)
* xyene (Tudor Brindus)
* spencercw (Chris Spencer)
.. _lg_documentation_guys:

17
doc/css/center-rtd.css Normal file
View File

@@ -0,0 +1,17 @@
@media screen and (min-width:1100px) {
.wy-nav-content-wrap {
margin-left: auto;
}
.wy-nav-content {
margin: auto;
}
}
@media screen and (min-width:769px) and (max-width:1420px) {
.wy-nav-content-wrap {
margin-left: 300px;
}
.wy-nav-content {
margin: 0;
}
}

View File

@@ -1,34 +0,0 @@
/* Center content in the RTD theme */
@media screen and (min-width: 1100px) {
.wy-nav-content-wrap {
margin-left: auto;
}
.wy-nav-content {
margin: auto;
}
}
@media screen and (min-width:769px) and (max-width:1420px) {
.wy-nav-content-wrap {
margin-left: 300px;
}
.wy-nav-content {
margin: 0;
}
}
/* Make <kbd> look like a key */
kbd {
background-color: #eee;
border-radius: 3px;
border: 1px solid #b4b4b4;
box-shadow: 0 1px 1px rgba(0, 0, 0, .2), 0 2px 0 0 rgba(255, 255, 255, .7) inset;
color: #333;
display: inline-block;
font-size: .85em;
line-height: 1;
padding: 2px 4px;
white-space: nowrap;
}

View File

@@ -1,4 +1,4 @@
Frequently asked questions
Frequently Asked Questions
##########################
General
@@ -144,7 +144,13 @@ does not support them.
Audio
-----
As of B6 Looking Glass supports audio input and output via SPICE.
Looking Glass does not support audio routing. The preferred
solution is to pass through QEMU's audio to your host's audio system.
Another popular solution is to use
`Scream <https://github.com/duncanthrax/scream>`_, a virtual sound card which
pipes audio through the network. A guide for setting up scream is available on
the wiki: https://looking-glass.io/wiki/Using_Scream_over_LAN
.. _faq_win:

View File

@@ -10,9 +10,7 @@ systems for legacy programs that require high-performance graphics.
.. toctree::
:maxdepth: 2
requirements
install
usage
build
troubleshooting
obs
@@ -42,4 +40,3 @@ Donate:
* `PayPal <https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ESQ72XUPGKXRY>`_
* BTC - 14ZFcYjsKPiVreHqcaekvHGL846u3ZuT13
* ETH - 0x6f8aEe454384122bF9ed28f025FBCe2Bce98db85
* XMR - 47xM4zG7b2tEj4mnSywHve4ydZzn3wzhf22snDRB7aSEcXrgUBpoT2Z4phTnyFMi1sMyQtHbdufMYRQ2PzMn3PGUJAE1dpc

View File

@@ -3,30 +3,29 @@
Installation
############
.. _libvirt:
.. _client_install:
libvirt/QEMU configuration
--------------------------
Client
------
The Looking Glass Client receives frames from the :ref:`Host <host_install>` to
display on your screen. It also handles input, and can optionally share the
system clipboard with your guest OS through Spice.
First you must build the client from source code, see :ref:`building`.
.. _client_libvirt_configuration:
libvirt Configuration
~~~~~~~~~~~~~~~~~~~~~
This article assumes you already have a fully functional libvirt domain with
PCI passthrough working.
PCI passthrough working on a dedicated monitor.
If you use virt-manager, this guide also applies to you, since virt-manager uses
libvirt as its back-end.
.. _libvirt_ivshmem:
IVSHMEM
^^^^^^^
Configuration
~~~~~~~~~~~~~
.. note::
If your host GPU is either AMD or Intel it is better to set this up using the
KVMFR kernel module as this will allow you to make use of DMA transfers to
offload some of the memory transfers to the GPU.
See `VM->host` in :ref:`kernel_module`.
**If you are using QEMU directly, this does not apply to you.**
Add the following to your libvirt machine configuration inside the
'devices' section by running ``virsh edit <VM>`` where ``<VM>`` is the name of
@@ -39,172 +38,139 @@ your virtual machine.
<size unit='M'>32</size>
</shmem>
.. note::
If you are using QEMU directly without libvirt the following arguments are
required instead.
Add the following to the commands to your QEMU command line, adjusting
the ``bus`` parameter to suit your particular configuration:
.. code:: bash
-device ivshmem-plain,memdev=ivshmem,bus=pcie.0 \
-object memory-backend-file,id=ivshmem,share=on,mem-path=/dev/shm/looking-glass,size=32M
The memory size (show as 32 in the example above) may need to be
adjusted as per the :ref:`Determining memory <libvirt_determining_memory>`
section.
adjusted as per the :ref:`Determining Memory <client_determining_memory>` section.
.. warning::
If you change the size of this after starting your virtual machine you may
need to remove the file `/dev/shm/looking-glass` to allow QEMU to re-create
it with the correct size. If you do this the permissions of the file may be
incorrect for your user to be able to access it and you will need to correct
this. See :ref:`libvirt_shmfile_permissions`
.. _client_spice_server:
.. _libvirt_determining_memory:
Spice Server
^^^^^^^^^^^^
Determining memory
If you would like to use Spice to give you keyboard and mouse input
along with clipboard sync support, make sure you have a
``<graphics type='spice'>`` device, then:
- Find your ``<video>`` device, and set ``<model type='none'/>``
- If you can't find it, make sure you have a ``<graphics>``
device, save and edit again
- On older libvirt versions, just disable the device in Windows
Device Manager
- Remove the ``<input type='tablet'/>`` device, if you have one
- Create an ``<input type='mouse'/>`` device, if you don't already have one
- Create an ``<input type='keyboard' bus='virtio'/>`` device to improve
keyboard usage
- This requires the *vioinput* driver from
`virtio-win <https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/>`_
to be installed in the guest
If you want clipboard synchronization please see
:ref:`client_clipboard_synchronization`
.. _client_apparmor:
AppArmor
^^^^^^^^
For libvirt versions before **5.10.0**, if you are using AppArmor, you
need to add permissions for QEMU to access the shared memory file. This
can be done by adding the following to
``/etc/apparmor.d/local/abstractions/libvirt-qemu``::
/dev/shm/looking-glass rw,
then, restart AppArmor.
.. code:: bash
sudo systemctl restart apparmor
.. _client_memballoon_tweak:
Memballoon
^^^^^^^^^^
The VirtIO memballoon device enables the host to dynamically reclaim memory
from your VM by growing the balloon inside the guest, reserving reclaimed
memory. Libvirt adds this device to guests by default.
However, this device causes major performance issues with VFIO passthrough
setups, and should be disabled.
Find the ``<memballoon>`` tag and set its type to ``none``:
.. code:: xml
<memballoon model="none"/>
.. _client_qemu_commands:
QEMU Commands
~~~~~~~~~~~~~
**If you are using libvirt/virt-manager, then this does not apply to you.**
Add the following to the commands to your QEMU command line, adjusting
the ``bus`` parameter to suit your particular configuration:
.. code:: bash
-device ivshmem-plain,memdev=ivshmem,bus=pcie.0 \
-object memory-backend-file,id=ivshmem,share=on,mem-path=/dev/shm/looking-glass,size=32M
The memory size (shown as 32M in the example above) may need to be
adjusted as per :ref:`Determining Memory <client_determining_memory>` section.
.. _client_determining_memory:
Determining Memory
~~~~~~~~~~~~~~~~~~
You will need to adjust the memory size to be suitable for your desired maximum
resolution, with the following formula:
You will need to adjust the memory size to be suitable for
your desired maximum resolution, with the following formula:
.. code:: text
width x height x pixel size x 2 = frame bytes
``width x height x 4 x 2 = total bytes``
frame bytes / 1024 / 1024 = frame megabytes
frame megabytes + 10 MiB = total megabytes
Where `pixel size` is 4 for 32-bit RGB (SDR) or 8 for 64-bit
(HDR :ref:`* <libvirt_determining_memory_hdr>`).
Failure to do so will cause Looking Glass to truncate the bottom of the screen
and will trigger a message popup to inform you of the size you need to increase
the value to.
``total bytes / 1024 / 1024 = total megabytes + 10``
For example, for a resolution of 1920x1080 (1080p):
.. code:: text
``1920 x 1080 x 4 x 2 = 16,588,800 bytes``
1920 x 1080 x 4 x 2 = 16,588,800 bytes
16,588,800 / 1024 / 1024 = 15.82 MiB
15.82 MiB + 10 MiB = 25.82 MiB
``16,588,800 / 1024 / 1024 = 15.82 MB + 10 = 25.82 MB``
You must round this value up to the nearest power of two, which for the
provided example is 32 MiB.
provided example is 32MB.
.. note::
Increasing this value beyond what you need does not yield any performance
improvements, it simply will block access to that RAM making it unusable by
your system.
.. _client_shmfile_permissions:
.. list-table:: Common Values
:widths: 50 25 25
:header-rows: 1
* - Resolution
- Standard Dynamic Range
- High Dynamic Range (HDR) :ref:`* <libvirt_determining_memory_hdr>`
* - 1920x1080 (1080p)
- 32
- 64
* - 1920x1200 (1200p)
- 32
- 64
* - 1920x1440 (1440p)
- 32
- 64
* - 3840x2160 (2160p/4K)
- 128
- 256
.. _libvirt_determining_memory_hdr:
.. warning::
While Looking Glass can capture and display HDR, at the time of writing
neither Xorg or Wayland can make use of it and it will be converted by the
GPU drivers/hardware to SDR. Additionally using HDR doubles the amount of
memory, bandwidth, and CPU load and should generally not be used unless you
have a special reason to do so.
.. _libvirt_shmfile_permissions:
Permissions
~~~~~~~~~~~
Shared Memory File Permissions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The shared memory file used by IVSHMEM is found in ``/dev/shm/looking-glass``.
By default, it is owned by QEMU, and does not give read/write permissions to
your user, which are required for Looking Glass to run properly.
You can use ``systemd-tmpfiles`` to create the file before running your VM,
You can use `systemd-tmpfiles` to create the file before running your VM,
granting the necessary permissions which allow Looking Glass to use the file
properly.
Create a new file ``/etc/tmpfiles.d/10-looking-glass.conf``, and populate it
with the following::
# Type Path Mode UID GID Age Argument
#Type Path Mode UID GID Age Argument
f /dev/shm/looking-glass 0660 user kvm -
Change ``UID`` to the user name you will run Looking Glass with, usually your
own.
.. _libvirt_spice_server:
.. _client_clipboard_synchronization:
Keyboard/mouse/display/audio
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Looking Glass makes use of the SPICE protocol to provide keyboard and mouse
input, audio input and output, and display fallback.
.. note::
The default configuration that libvirt uses is not optimal and must be
adjusted. Failure to perform these changes will cause input issues along
with failure to support 5 button mice.
If you would like to use SPICE to give you keyboard and mouse input
along with clipboard sync support, make sure you have a
``<graphics type='spice'>`` device, then:
- Find your ``<video>`` device, and set ``<model type='vga'/>``
- If you can't find it, make sure you have a ``<graphics>``
device, save and edit again.
- Remove the ``<input type='tablet'/>`` device, if you have one.
- Create an ``<input type='mouse' bus='virtio'/>`` device, if you don't
already have one.
- Create an ``<input type='keyboard' bus='virtio'/>`` device to improve
keyboard usage.
.. note::
Be sure to install the the *vioinput* driver from
`virtio-win <https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/>`_
in the guest
To enable audio support add a standard Intel HDA audio device to your
configuration as per below:
.. code:: xml
<sound model='ich9'>
<audio id='1'/>
</sound>
<audio id='1' type='spice'/>
If you also want clipboard synchronization please see
:ref:`libvirt_clipboard_synchronization`
.. _libvirt_clipboard_synchronization:
Clipboard synchronization
^^^^^^^^^^^^^^^^^^^^^^^^^
Clipboard Synchronization
~~~~~~~~~~~~~~~~~~~~~~~~~
Looking Glass can synchronize the clipboard between the host and guest using
the SPICE guest agent.
@@ -232,90 +198,397 @@ https://www.spice-space.org/download.html#windows-binaries.
</channel>
<!-- No need to add a VirtIO Serial device, it will be added automatically -->
.. _libvirt_apparmor:
.. _client_usage:
AppArmor
^^^^^^^^
Usage
-----
For libvirt versions before **5.10.0**, if you are using AppArmor, you
need to add permissions for QEMU to access the shared memory file. This
can be done by adding the following to
``/etc/apparmor.d/local/abstractions/libvirt-qemu``::
The client command is the binary file: **looking-glass-client**. This
command should run after the Windows Host Application has started.
/dev/shm/looking-glass rw,
You may run the client directly from the build directory. Alternatively, to
install the client for all users, you can run
::
then, restart AppArmor.
make install
.. code:: bash
To install for the local user only, run
::
sudo systemctl restart apparmor
cmake -DCMAKE_INSTALL_PREFIX=~/.local .. && make install
.. _libvirt_memballoon_tweak:
.. _client_key_bindings:
Memballoon
^^^^^^^^^^
Default Key Bindings
~~~~~~~~~~~~~~~~~~~~
The VirtIO memballoon device enables the host to dynamically reclaim memory
from your VM by growing the balloon inside the guest, reserving reclaimed
memory. Libvirt adds this device to guests by default.
By default, Looking Glass uses the :kbd:`Scroll Lock` key as the escape key
for commands, as well as the input :kbd:`capture` mode toggle; this can be
changed using the ``-m`` switch if you desire a different key. Below are
a list of current key bindings:
However, this device causes major performance issues with VFIO passthrough
setups, and should be disabled.
============================ =======================================================
Command Description
============================ =======================================================
:kbd:`ScrLk` Toggle capture mode
:kbd:`ScrLk` + :kbd:`Q` Quit
:kbd:`ScrLk` + :kbd:`R` Rotate the output clockwise by 90° increments
:kbd:`ScrLk` + :kbd:`T` Show frame timing information
:kbd:`ScrLk` + :kbd:`I` Spice keyboard & mouse enable toggle
:kbd:`ScrLk` + :kbd:`O` Toggle overlay
:kbd:`ScrLk` + :kbd:`D` FPS display toggle
:kbd:`ScrLk` + :kbd:`F` Full screen toggle
:kbd:`ScrLk` + :kbd:`V` Video stream toggle
:kbd:`ScrLk` + :kbd:`N` Toggle night vision mode
:kbd:`ScrLk` + :kbd:`F1` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F1` to the guest
:kbd:`ScrLk` + :kbd:`F2` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F2` to the guest
:kbd:`ScrLk` + :kbd:`F3` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F3` to the guest
:kbd:`ScrLk` + :kbd:`F4` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F4` to the guest
:kbd:`ScrLk` + :kbd:`F5` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F5` to the guest
:kbd:`ScrLk` + :kbd:`F6` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F6` to the guest
:kbd:`ScrLk` + :kbd:`F7` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F7` to the guest
:kbd:`ScrLk` + :kbd:`F8` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F8` to the guest
:kbd:`ScrLk` + :kbd:`F9` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F9` to the guest
:kbd:`ScrLk` + :kbd:`F10` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F10` to the guest
:kbd:`ScrLk` + :kbd:`F11` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F11` to the guest
:kbd:`ScrLk` + :kbd:`F12` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F12` to the guest
:kbd:`ScrLk` + :kbd:`Insert` Increase mouse sensitivity in capture mode
:kbd:`ScrLk` + :kbd:`Del` Decrease mouse sensitivity in capture mode
:kbd:`ScrLk` + :kbd:`LWin` Send :kbd:`LWin` to the guest
:kbd:`ScrLk` + :kbd:`RWin` Send :kbd:`RWin` to the guest
============================ =======================================================
Find the ``<memballoon>`` tag and set its type to ``none``:
You can also find this list at any time by holding down :kbd:`Scroll Lock`.
.. code:: xml
.. _client_cli_options:
<memballoon model="none"/>
Command Line Options
~~~~~~~~~~~~~~~~~~~~
A full list of command line options is available with the ``--help`` or ``-h``
options.
Example: ``looking-glass-client --help``
Common options include ``-s`` for disabling spice, ``-S`` for disabling the
screen saver, and ``-F`` to automatically enter full screen.
Options may be provided with a short form, if available, or long form.
Boolean options may be specified without a parameter to toggle their
state.
Examples:
- ``looking-glass-client -F`` (short)
- ``looking-glass-client win:fullScreen`` (long)
- ``looking-glass-client -f /dev/shm/my-lg-shmem`` (short with parameter)
- ``looking-glass-client app:shmFile=/dev/shm/my-lg-shmem`` (long with parameter)
.. _client_config_options_file:
Configuration Files
~~~~~~~~~~~~~~~~~~~
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)
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.
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
[egl]
nvGain=1
Command line arguments will override any options loaded from config
files.
.. _client_overlay_mode:
Overlay Mode
~~~~~~~~~~~~
The Overlay Mode lets you configure various runtime options for Looking Glass.
These include:
- EGL filters
- Performance metrics options
- Debug frame damage display
(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
widget has multiple tabs that allow setting a variety of modes and
parameters for Looking Glass at runtime.
Settings tab
^^^^^^^^^^^^
- 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
their default values when the client is restarted.
EGL Filters tab
^^^^^^^^^^^^^^^
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 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.
- 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::
Please refrain from modifying any files under the ``presets`` folder.
Those files are meant to be modified only by the Looking-Glass client.
.. note::
Although presets are persistent, the client will not remember which
preset was used last session, so a preset needs to be recalled once
the client starts.
.. _client_full_command_options:
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 |
+=========================+=======+========================+======================================================================+
| win:title | | Looking Glass (client) | The window title |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:position | | center | Initial window position at startup |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:size | | 1024x768 | Initial window size at startup |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:autoResize | -a | no | Auto resize the window to the guest |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:allowResize | -n | yes | Allow the window to be manually resized |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:keepAspect | -r | yes | Maintain the correct aspect ratio |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:forceAspect | | yes | Force the window to maintain the aspect ratio |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:dontUpscale | | no | Never try to upscale the window |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:shrinkOnUpscale | | no | Limit the window dimensions when dontUpscale is enabled |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:borderless | -d | no | Borderless mode |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:fullScreen | -F | no | Launch in fullscreen borderless mode |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:maximize | -T | no | Launch window maximized |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:minimizeOnFocusLoss | | no | Minimize window on focus loss |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| 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:noScreensaver | -S | no | Prevent the screensaver from starting |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:autoScreensaver | | no | Prevent the screensaver from starting when guest requests it |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:alerts | -q | yes | Show on screen alert messages |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:quickSplash | | no | Skip fading out the splash screen when a connection is established |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:rotate | | 0 | Rotate the displayed image (0, 90, 180, 270) |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:uiFont | | DejaVu Sans Mono | The font to use when rendering on-screen UI |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:uiSize | | 14 | The font size to use when rendering on-screen UI |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:jitRender | | no | Enable just-in-time rendering |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:showFPS | -k | no | Enable the FPS & UPS display |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| Long | Short | Value | Description |
+==============================+=======+=====================+==================================================================================+
| input:grabKeyboard | -G | yes | Grab the keyboard in capture mode |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:grabKeyboardOnFocus | | no | Grab the keyboard when focused |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:releaseKeysOnFocusLoss | | yes | On focus loss, send key up events to guest for all held keys |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:escapeKey | -m | 70 = KEY_SCROLLLOCK | Specify the escape key, see <linux/input-event-codes.h> for valid values |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:ignoreWindowsKeys | | no | Do not pass events for the windows keys to the guest |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:hideCursor | -M | yes | Hide the local mouse cursor |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:mouseSens | | 0 | Initial mouse sensitivity when in capture mode (-9 to 9) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:mouseSmoothing | | yes | Apply simple mouse smoothing when rawMouse is not in use (helps reduce aliasing) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:rawMouse | | no | Use RAW mouse input when in capture mode (good for gaming) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:mouseRedraw | | yes | Mouse movements trigger redraws (ignores FPS minimum) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:autoCapture | | no | Try to keep the mouse captured when needed |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:captureOnly | | no | Only enable input via SPICE if in capture mode |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:helpMenuDelay | | 200 | Show help menu after holding down the escape key for this many milliseconds |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
+------------------------+-------+-----------+---------------------------------------------------------------------+
| Long | Short | Value | Description |
+========================+=======+===========+=====================================================================+
| spice:enable | -s | yes | Enable the built in SPICE client for input and/or clipboard support |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:host | -c | 127.0.0.1 | The SPICE server host or UNIX socket |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:port | -p | 5900 | The SPICE server port (0 = unix socket) |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:input | | yes | Use SPICE to send keyboard and mouse input events to the guest |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:clipboard | | yes | Use SPICE to synchronize the clipboard contents with the guest |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:clipboardToVM | | yes | Allow the clipboard to be synchronized TO the VM |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:clipboardToLocal | | yes | Allow the clipboard to be synchronized FROM the VM |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:scaleCursor | -j | yes | Scale cursor input position to screen size when up/down scaled |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:captureOnStart | | no | Capture mouse and keyboard on start |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:alwaysShowCursor | | no | Always show host cursor |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:showCursorDot | | yes | Use a "dot" cursor when the window does not have focus |
+------------------------+-------+-----------+---------------------------------------------------------------------+
+------------------+-------+-------+---------------------------------------------------------------------------+
| Long | Short | Value | Description |
+==================+=======+=======+===========================================================================+
| egl:vsync | | no | Enable vsync |
+------------------+-------+-------+---------------------------------------------------------------------------+
| egl:doubleBuffer | | no | Enable double buffering |
+------------------+-------+-------+---------------------------------------------------------------------------+
| egl:multisample | | yes | Enable Multisampling |
+------------------+-------+-------+---------------------------------------------------------------------------+
| egl:nvGainMax | | 1 | The maximum night vision gain |
+------------------+-------+-------+---------------------------------------------------------------------------+
| egl:nvGain | | 0 | The initial night vision gain at startup |
+------------------+-------+-------+---------------------------------------------------------------------------+
| egl:cbMode | | 0 | Color Blind Mode (0 = Off, 1 = Protanope, 2 = Deuteranope, 3 = Tritanope) |
+------------------+-------+-------+---------------------------------------------------------------------------+
| egl:scale | | 0 | Set the scale algorithm (0 = auto, 1 = nearest, 2 = linear) |
+------------------+-------+-------+---------------------------------------------------------------------------+
| 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 |
+======================+=======+=======+=============================================+
| opengl:mipmap | | yes | Enable mipmapping |
+----------------------+-------+-------+---------------------------------------------+
| opengl:vsync | | no | Enable vsync |
+----------------------+-------+-------+---------------------------------------------+
| opengl:preventBuffer | | yes | Prevent the driver from buffering frames |
+----------------------+-------+-------+---------------------------------------------+
| opengl:amdPinnedMem | | yes | Use GL_AMD_pinned_memory if it is available |
+----------------------+-------+-------+---------------------------------------------+
+-----------------------+-------+-------+-------------------------+
| Long | Short | Value | Description |
+=======================+=======+=======+=========================+
| wayland:warpSupport | | yes | Enable cursor warping |
+-----------------------+-------+-------+-------------------------+
| wayland:fractionScale | | yes | Enable fractional scale |
+-----------------------+-------+-------+-------------------------+
.. _host_install:
Additional tuning
^^^^^^^^^^^^^^^^^
Host
----
Looking Glass is latency sensitive and as such it may suffer microstutters if
you have not properly tuned your virtual machine. The physical display output
of your GPU will usually not show such issues due to the nature of the hardware
but be sure that if you are experiencing issues the following tuning is
required to obtain optimal performance.
1. Do not assign all your CPU cores to your guest VM, you must at minimum
reserve two CPU cores (4 threads) for your host system to use. For example,
if you have a 6 core CPU, only assign 4 cores (8 threads) to the guest.
2. Ensure you correctly pin your VMs vCPU threads to the correct cores for your
CPU architecture.
3. If you are on a NUMA architecture (dual CPU, or early Threadripper) be sure
that you pin the vCPU threads to the physical CPU/die attached to your GPU.
4. Just because your GPU is in a slot that is physically x16 in size, does not
mean your GPU is running at x16, this is dependent on how your motherboard
is physically wired and the physical slot may be limited to x4 or x8.
5. Be sure to set your CPU model type to `host-passthrough` so that your guest
operating system is aware of the acceleration features of your CPU and can
make full use of them.
6. AMD users be sure that you have the CPU feature flag `topoext` enabled or
your guest operating system will not be aware of which CPU cores are
hyper-thread pairs.
7. NVIDIA users may want to enable NvFBC as an alternative capture API in the
guest. Note that NvFBC is officially available on professional cards only
and methods to enable NvFBC on non-supported GPUs is against the NVIDIA
Capture API SDK License Agreement even though GeForce Experience and
Steam make use of it on any NVIDIA GPU.
How to perform these changes is left as an exercise to the reader.
Host application
----------------
The Looking Glass Host application captures frames from the guest OS using a
capture API, and sends them to the
:ref:`client <client_install>`—be it on the host OS (hypervisor) or another
Virtual Machine—through a low-latency transfer protocol over shared memory.
The Looking Glass Host captures frames from the guest OS using a capture API,
and sends them to the :ref:`Client <client_install>`—be it on the host OS (hypervisor)
or another Virtual Machine—through a low-latency transfer protocol over shared
memory.
You can get the host program in two ways:
@@ -325,46 +598,40 @@ You can get the host program in two ways:
- Download the source code as described in :ref:`building`, then
:ref:`build the host <host_building>`.
.. _host_install_linux:
For Linux
^^^^^^^^^
While the host application can be compiled and is somewhat functional for Linux
it is currently considered incomplete and not ready for usage. As such use at
your own risk and do not ask for support.
.. _host_install_osx:
For OSX
^^^^^^^
Currently there is no support or plans for support for OSX due to technical
limitations.
.. _host_install_windows:
For Windows
^^^^^^^^^^^
Windows
~~~~~~~
To begin, you must first run the Windows VM with the changes noted above in
either the :ref:`libvirt` section.
either the :ref:`client_libvirt_configuration` or :ref:`client_qemu_commands`
sections.
.. _installing_the_ivshmem_driver:
Installing the IVSHMEM driver
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Installing the IVSHMEM Driver
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Since B6 the host installer available on the official Looking Glass website
comes with the IVSHMEM driver and will install this for you. If you are running
an older version of Looking Glass please refer to the documentation for your
version.
Windows will not prompt for a driver for the IVSHMEM device, instead, it
will use a default null (do nothing) driver for the device. To install
the IVSHMEM driver you will need to go into the device manager and
update the driver for the device "PCI standard RAM Controller" under the
"System Devices" node.
A signed Windows 10 driver can be obtained from Red Hat for this device
from the below address:
https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/upstream-virtio/
Please note that you must obtain version 0.1.161 or later.
If you encounter warnings or errors about driver signatures, ensure secure boot
is turned off in the bios/UEFI settings of your virtual machine.
.. _host_install_service:
Installing the Looking Glass service
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Installing the Looking Glass Service
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
After installing your IVSHMEM driver, we can now install the Looking Glass Host
onto our Windows Virtual Machine.
@@ -388,21 +655,3 @@ Command line users can run ``looking-glass-host-setup.exe /S`` to execute a
silent install with default options selected. Further configuration from the
command line can be done with flags. You can list all available flags by
running ``looking-glass-host-setup.exe /?``.
.. _client_install:
Client application
------------------
The Looking Glass client receives frames from the :ref:`host <host_install>` to
display on your screen. It also handles input, and can optionally share the
system clipboard with your guest OS through SPICE.
First you must build the client from source, see :ref:`building`. Once you have
built the client, you can install it. Run the following as root::
make install
To install for the local user only, run::
cmake -DCMAKE_INSTALL_PREFIX=~/.local .. && make install

View File

@@ -10,7 +10,7 @@ reoption = re.compile(r'^[a-z]+:\w+$')
recamel = re.compile(r'^[A-Za-z]+[A-Z]\w+$')
repackage = re.compile(r'^[\w-]+-(?:dev|bin)$|^fonts-[\w-]+-ttf$|^virt-manager$')
repath = re.compile(r'^/dev/|.*\.\w+$')
recrypto = re.compile(r'^[13][A-Za-z0-9]{25,34}$|^0x[0-9a-fA-F]{40}|^4([0-9]|[A-B])(.){93}$')
recrypto = re.compile(r'^[13][A-Za-z0-9]{25,34}$|^0x[0-9a-fA-F]{40}$')
class AcronymFilter(Filter):

View File

@@ -1,18 +1,18 @@
.. _kernel_module:
Kernel module
Kernel Module
#############
This kernel module implements a basic interface to the IVSHMEM device
for Looking Glass in VMVM mode.
for Looking Glass in VM->VM mode.
Additionally in VMhost mode, it can be used to generate a shared
Additionally in VM->host mode, it can be used to generate a shared
memory device on the host machine that supports dmabuf.
Prerequisites
-------------
The Linux kernel headers for your kernel version are required for building.
The linux kernel headers for your kernel version are required for building.
Install them with ``apt-get``
.. code:: bash
@@ -27,7 +27,7 @@ Then switch to the ``module/`` directory
.. _module_dkms:
Using DKMS (recommended)
Using DKMS (Recommended)
------------------------
You can use the kernel's DKMS feature to keep the module across upgrades.
@@ -53,11 +53,11 @@ To install the module into DKMS, run
Loading
~~~~~~~
For VMVM, simply ``modprobe`` the module::
For VM->VM, simply modprobe the module::
modprobe kvmfr
For VMhost with dmabuf, ``modprobe`` with the parameter
For VM->host with dmabuf, modprobe with the parameter
``static_size_mb``:
.. code:: bash
@@ -70,7 +70,7 @@ with commas.
.. _module_manual:
Compiling & loading (manual)
Compiling & Loading (Manual)
----------------------------
To compile the module manually, run ``make`` in the module directory.
@@ -80,15 +80,15 @@ To compile the module manually, run ``make`` in the module directory.
Loading
~~~~~~~
For VMVM mode, run:
For VM->VM mode, run:
.. code:: bash
insmod kvmfr.ko
For VMhost mode with dmabuf, instead of creating a shared memory file,
For VM->host mode with dmabuf, instead of creating a shared memory file,
load this module with the parameter ``static_size_mb``. For example, a
32 MiB shared memory device can be created with:
32 MB shared memory device can be created with:
.. code:: bash
@@ -96,7 +96,7 @@ load this module with the parameter ``static_size_mb``. For example, a
Multiple devices can be created by separating the sizes with commas. For
example, ``static_size_mb=128,64`` would create two kvmfr devices:
``kvmfr0`` would be 128 MB and ``kvmfr1`` would be 64 MiB.
``kvmfr0`` would be 128 MB and ``kvmfr1`` would be 64 MB.
.. note::
@@ -145,10 +145,10 @@ You may also use a config file: ``~/.looking-glass-client.ini``, or
.. _module_vm_to_host:
VMHost
~~~~~~~~~~~~
VM->Host
~~~~~~~~
In VMhost mode, use this device in place of the shared memory file.
In VM->host mode, use this device in place of the shared memory file.
QEMU
^^^^
@@ -166,8 +166,8 @@ Add the following arguments to your ``qemu`` command line::
libvirt
^^^^^^^
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,
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
@@ -181,9 +181,9 @@ should use this XML block to configure their VM for kvmfr:
.. note::
- The ``"size"`` tag represents the size of the shared memory device in
- 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 <libvirt_determining_memory>`, use the figure you
: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``
@@ -202,7 +202,7 @@ legacy syntax for IVSHMEM setup:
.. note::
- Using the legacy syntax on QEMU 6.2/libvirt 7.9 may cause QEMU to
- 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``"
@@ -238,12 +238,12 @@ We can use the ``systemd-modules-load.service(8)`` service for this task.
Create the file ``/etc/modules-load.d/kvmfr.conf`` with the following
contents::
# KVMFR Looking Glass module
#KVMFR Looking Glass module
kvmfr
This will now run the next time you start your machine.
If you are running in VMhost mode, you must additionally create another file
If you are running in VM->host mode, you must additionally create another file
``/etc/modprobe.d/kvmfr.conf`` to properly set the size. It should have the
following contents::

View File

@@ -9,7 +9,7 @@ with a Screen or Window Capture source.
This may help improve your viewers' watching experience, and
allows you to use your host privately.
Build instructions
Build Instructions
~~~~~~~~~~~~~~~~~~
The OBS plugin is included in the main source tree of Looking Glass. The
@@ -23,7 +23,7 @@ The OBS plugin requires the following extra dependencies alongside the
:ref:`client's build
dependencies <installing_build_dependencies>`.
- ``libobs-dev``
- libobs-dev
Install this package with ``apt-get``

View File

@@ -1,82 +0,0 @@
.. _requrements:
Requirements
############
.. _minimum_:
Minimum
-------
The most basic requirement to make use of Looking Glass is to have a system
with two GPUs, the following configurations are valid:
* Two discrete GPUs (dGPU)
* A discrete GPU and an integrated (iGPU) such as is common in laptops.
* A discrete GPU or iGPU and a virtual GPU (vGPU) as supported by some
hardware.
.. note::
Please be aware that iGPU users may be limited in the resolution and refresh
rate possible with Looking Glass due to the memory bandwidth limitations
imposed due to the iGPU sharing system RAM for GPU usage.
Looking Glass aims to achieve the lowest possible latency and as such it
is important that you do not overload your system. The minimum recommended CPU
to obtain a decent experience with Looking Glass is 6 cores or more, with
Hyper-threading (>= 12 threads).
PCIe bandwidth can also be a limiting factor, as such both GPUs should have a
minimum of 8 lanes (x8) at PCIe3 speeds, or 4 lanes (x4) at PCIe4 speeds.
The GPU used for the guest virtual machine must have either a physical monitor
attached to it, or a cheap dummy plug. The guest operating system (most notably
Windows) will disable the GPU output if there is nothing attached to it and
Looking Glass will not be able to function. If you are using a vGPU the virtual
device should already have a virtual monitor attached to it negating this
requirement.
.. _recommended_:
Recommended
-----------
At this time the recommended configuration is as follows:
* CPU 8 cores (16 threads) or better @ 3.0GHz or faster (full cores, not
efficiency cores).
* Two discrete GPUs consisting of:
* AMD or Intel brand GPU for the client application (usually your host system).
* NVIDIA brand GPU for the guest system (virtual machine).
The reason for these recommendations are as follows:
AMD or Intel for the client
^^^^^^^^^^^^^^^^^^^^^^^^^^^
AMD and Intel both support the `DMABUF` feature which enables offloading memory
transfers to the GPU hardware. Please note that making use of this feature
requires :doc:`loading the KVMFR kernel module <module>`.
Additionally AMD GPUs suffer stability issues when operating as a passthrough
device and as such we do not recommend their usage for such purposes. Models of
note that have issues include but are not limited to the entire Polaris, Vega,
Navi and BigNavi GPU series. Vega and Navi are notably the worst and should be
avoided for virtualization usage.
NVIDIA for the guest
^^^^^^^^^^^^^^^^^^^^
NVIDIA unlike AMD do not seem to suffer from the same stability issues as AMD
GPUs when operating as a passthrough GPU, however due to the closed source
nature of their drivers NVIDIA can not make use of the DMABUF feature in the
Linux kernel and as such it is not recommended for use as the host GPU.
`It has been said <https://github.com/NVIDIA/open-gpu-kernel-modules/discussions/243#discussioncomment-3283415>`_
that the open-source NVIDIA drivers as of release 525 will enable this support
and we have made changes to `an experiemental branch <https://github.com/gnif/LookingGlass/tree/dmabuf-test>`_
to support this, however until NVIDIA release this version and we can test these
changes against the driver, support will not be included in the release builds
of Looking Glass.

View File

@@ -6,7 +6,7 @@ Glass. Below is a list of known issues with potential solutions:
.. _when_launching_looking_glass_the_desktop_doesnt_appear:
When launching Looking Glass the desktop doesn't appear
When Launching Looking Glass the Desktop Doesn't Appear
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **Using an AMD GPU?**
@@ -21,14 +21,14 @@ When launching Looking Glass the desktop doesn't appear
.. _the_clipboard_is_not_working:
The clipboard is not working
The Clipboard is not Working
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **Is clipboard synchronization enabled?**
- Before you can copy or paste content between the guest and host,
- Before you can copy or paste content between the Guest and Host,
:ref:`clipboard
synchronization <libvirt_clipboard_synchronization>`
synchronization <client_clipboard_synchronization>`
must be enabled.
- **Did you install the Spice Guest Tools?**

View File

@@ -1,525 +0,0 @@
.. _client_usage:
Client usage
------------
.. raw:: html
<p><code class="literal"><b>looking-glass-client</b> [--help] [-f] [-F] [-s] [-S] [options...]</code></p>
.. _client_cli_options:
Command line options
~~~~~~~~~~~~~~~~~~~~
A full list of command line options is available with the ``--help`` or ``-h``
options.
Example: ``looking-glass-client --help``
Common options are listed below:
================ ===========================================
Short option Description
================ ===========================================
``-f shmFile`` use ``shmFile`` for IVSHMEM shared memory
``-F`` automatically enter full screen
``-s`` disable spice
``-S`` disable host screensaver
================ ===========================================
Options may be provided in short form when available, or long form.
Boolean options may be specified without a parameter to toggle their
state.
Examples:
- ``looking-glass-client -F`` (short)
- ``looking-glass-client win:fullScreen`` (long)
- ``looking-glass-client -f /dev/shm/my-lg-shmem`` (short with parameter)
- ``looking-glass-client app:shmFile=/dev/shm/my-lg-shmem`` (long with parameter)
.. seealso::
:ref:`client_full_command_options`
.. _client_key_bindings:
Default key bindings
~~~~~~~~~~~~~~~~~~~~
By default, Looking Glass uses the :kbd:`ScrLk` key as the escape key
for commands, as well as the input :kbd:`capture` mode toggle; this can be
changed using the ``-m`` switch if you desire a different key. Below are
a list of current key bindings:
============================ =======================================================
Command Description
============================ =======================================================
:kbd:`ScrLk` Toggle capture mode
:kbd:`ScrLk` + :kbd:`Q` Quit
:kbd:`ScrLk` + :kbd:`E` Toggle audio recording
:kbd:`ScrLk` + :kbd:`R` Rotate the output clockwise by 90° increments
:kbd:`ScrLk` + :kbd:`T` Show frame timing information
:kbd:`ScrLk` + :kbd:`I` Spice keyboard & mouse enable toggle
:kbd:`ScrLk` + :kbd:`O` Toggle overlay
:kbd:`ScrLk` + :kbd:`D` FPS display toggle
:kbd:`ScrLk` + :kbd:`F` Full screen toggle
:kbd:`ScrLk` + :kbd:`V` Video stream toggle
:kbd:`ScrLk` + :kbd:`N` Toggle night vision mode
:kbd:`ScrLk` + :kbd:`F1` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F1` to the guest
:kbd:`ScrLk` + :kbd:`F2` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F2` to the guest
:kbd:`ScrLk` + :kbd:`F3` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F3` to the guest
:kbd:`ScrLk` + :kbd:`F4` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F4` to the guest
:kbd:`ScrLk` + :kbd:`F5` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F5` to the guest
:kbd:`ScrLk` + :kbd:`F6` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F6` to the guest
:kbd:`ScrLk` + :kbd:`F7` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F7` to the guest
:kbd:`ScrLk` + :kbd:`F8` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F8` to the guest
:kbd:`ScrLk` + :kbd:`F9` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F9` to the guest
:kbd:`ScrLk` + :kbd:`F10` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F10` to the guest
:kbd:`ScrLk` + :kbd:`F11` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F11` to the guest
:kbd:`ScrLk` + :kbd:`F12` Send :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F12` to the guest
:kbd:`ScrLk` + :kbd:`M` Send mute to the guest
:kbd:`ScrLk` + :kbd:`↑` Send volume up to the guest
:kbd:`ScrLk` + :kbd:`↓` Send volume down to the guest
:kbd:`ScrLk` + :kbd:`Insert` Increase mouse sensitivity in capture mode
:kbd:`ScrLk` + :kbd:`Del` Decrease mouse sensitivity in capture mode
:kbd:`ScrLk` + :kbd:`LWin` Send :kbd:`LWin` to the guest
:kbd:`ScrLk` + :kbd:`RWin` Send :kbd:`RWin` to the guest
============================ =======================================================
You can also find this list at any time by holding down :kbd:`ScrLk`.
.. _client_config_options_file:
Configuration files
~~~~~~~~~~~~~~~~~~~
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``)
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.
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:
.. code-block:: ini
[win]
fullScreen=yes
[egl]
nvGain=1
; this is a comment
Command line arguments will override any options loaded from config
files.
.. _client_overlay_mode:
Overlay mode
~~~~~~~~~~~~
The Overlay Mode lets you configure various runtime options for Looking Glass.
These include:
- EGL filters
- Performance metrics options
- Debug frame damage display
(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
widget has multiple tabs that allow setting a variety of modes and
parameters for Looking Glass at runtime.
Settings tab
^^^^^^^^^^^^
- *Performance Metrics*: A toggle for the performance metrics widget.
Multiple graphs are available, and they will stack vertically.
- *EGL*: Modify EGL settings, such as the algorithm used for scaling, and
night vision mode.
Changes in the settings tab are not persistent, and will be reset back to
their default values when the client is restarted.
EGL filters tab
^^^^^^^^^^^^^^^
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 -- for example,
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 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.
- *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::
Please refrain from modifying any files under the ``presets`` folder.
Those files are meant to be modified only by the Looking-Glass client.
.. note::
Although presets are persistent, the client will not remember which
preset was used last session, so a preset needs to be recalled once
the client starts.
.. _client_full_command_options:
All command line options
~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block::
+------------------------+-------+------------------------+-----------------------------------------------------------------------------------------+
| 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 |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:title | | Looking Glass (client) | The window title |
| win:position | | center | Initial window position at startup |
| win:size | | 1024x768 | Initial window size at startup |
| win:autoResize | -a | no | Auto resize the window to the guest |
| win:allowResize | -n | yes | Allow the window to be manually resized |
| win:keepAspect | -r | yes | Maintain the correct aspect ratio |
| win:forceAspect | | yes | Force the window to maintain the aspect ratio |
| win:dontUpscale | | no | Never try to upscale the window |
| win:intUpscale | | no | Allow only integer upscaling |
| win:shrinkOnUpscale | | no | Limit the window dimensions when dontUpscale is enabled |
| win:borderless | -d | no | Borderless mode |
| win:fullScreen | -F | no | Launch in fullscreen borderless mode |
| win:maximize | -T | no | Launch window maximized |
| win:minimizeOnFocusLoss | | no | Minimize window on focus loss |
| 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:noScreensaver | -S | no | Prevent the screensaver from starting |
| win:autoScreensaver | | no | Prevent the screensaver from starting when guest requests it |
| win:alerts | -q | yes | Show on screen alert messages |
| win:quickSplash | | no | Skip fading out the splash screen when a connection is established |
| win:overlayDimsDesktop | | yes | Dim the desktop when in interactive overlay mode |
| win:rotate | | 0 | Rotate the displayed image (0, 90, 180, 270) |
| win:uiFont | | DejaVu Sans Mono | The font to use when rendering on-screen UI |
| win:uiSize | | 14 | The font size to use when rendering on-screen UI |
| win:jitRender | | no | Enable just-in-time rendering |
| win:showFPS | -k | no | Enable the FPS & UPS display |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| Long | Short | Value | Description |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:grabKeyboard | -G | yes | Grab the keyboard in capture mode |
| input:grabKeyboardOnFocus | | no | Grab the keyboard when focused |
| input:releaseKeysOnFocusLoss | | yes | On focus loss, send key up events to guest for all held keys |
| input:escapeKey | -m | 70 = KEY_SCROLLLOCK | Specify the escape/menu key to use (use "help" to see valid values) |
| input:ignoreWindowsKeys | | no | Do not pass events for the windows keys to the guest |
| input:hideCursor | -M | yes | Hide the local mouse cursor |
| input:mouseSens | | 0 | Initial mouse sensitivity when in capture mode (-9 to 9) |
| input:mouseSmoothing | | yes | Apply simple mouse smoothing when rawMouse is not in use (helps reduce aliasing) |
| input:rawMouse | | no | Use RAW mouse input when in capture mode (good for gaming) |
| input:mouseRedraw | | yes | Mouse movements trigger redraws (ignores FPS minimum) |
| input:autoCapture | | no | Try to keep the mouse captured when needed |
| input:captureOnly | | no | Only enable input via SPICE if in capture mode |
| input:helpMenuDelay | | 200 | Show help menu after holding down the escape key for this many milliseconds |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
+------------------------+-------+-----------+---------------------------------------------------------------------+
| Long | Short | Value | Description |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:enable | -s | yes | Enable the built in SPICE client for input and/or clipboard support |
| spice:host | -c | 127.0.0.1 | The SPICE server host or UNIX socket |
| spice:port | -p | 5900 | The SPICE server port (0 = unix socket) |
| spice:input | | yes | Use SPICE to send keyboard and mouse input events to the guest |
| spice:clipboard | | yes | Use SPICE to synchronize the clipboard contents with the guest |
| spice:clipboardToVM | | yes | Allow the clipboard to be synchronized TO the VM |
| spice:clipboardToLocal | | yes | Allow the clipboard to be synchronized FROM the VM |
| spice:audio | | yes | Enable SPICE audio support |
| spice:scaleCursor | -j | yes | Scale cursor input position to screen size when up/down scaled |
| spice:captureOnStart | | no | Capture mouse and keyboard on start |
| spice:alwaysShowCursor | | no | Always show host cursor |
| spice:showCursorDot | | yes | Use a "dot" cursor when the window does not have focus |
+------------------------+-------+-----------+---------------------------------------------------------------------+
+------------------------+-------+--------+-------------------------------------------------------------------------------+
| Long | Short | Value | Description |
+------------------------+-------+--------+-------------------------------------------------------------------------------+
| audio:periodSize | | 2048 | Requested audio device period size in samples |
| audio:bufferLatency | | 13 | Additional buffer latency in milliseconds |
| audio:micDefault | | prompt | Default action when an application opens the microphone (prompt, allow, deny) |
| audio:micShowIndicator | | yes | Display microphone usage indicator |
+------------------------+-------+--------+-------------------------------------------------------------------------------+
+------------------+-------+-------+---------------------------------------------------------------------------+
| Long | Short | Value | Description |
+------------------+-------+-------+---------------------------------------------------------------------------+
| egl:vsync | | no | Enable vsync |
| egl:doubleBuffer | | no | Enable double buffering |
| egl:multisample | | yes | Enable Multisampling |
| egl:nvGainMax | | 1 | The maximum night vision gain |
| egl:nvGain | | 0 | The initial night vision gain at startup |
| egl:cbMode | | 0 | Color Blind Mode (0 = Off, 1 = Protanope, 2 = Deuteranope, 3 = Tritanope) |
| egl:scale | | 0 | Set the scale algorithm (0 = auto, 1 = nearest, 2 = linear) |
| egl:debug | | no | Enable debug output |
| egl:noBufferAge | | no | Disable partial rendering based on buffer age |
| egl:noSwapDamage | | no | Disable swapping with damage |
| egl:scalePointer | | yes | Keep the pointer size 1:1 when downscaling |
| egl:preset | | NULL | The initial filter preset to load |
+------------------+-------+-------+---------------------------------------------------------------------------+
+----------------------+-------+-------+---------------------------------------------+
| Long | Short | Value | Description |
+----------------------+-------+-------+---------------------------------------------+
| opengl:mipmap | | yes | Enable mipmapping |
| opengl:vsync | | no | Enable vsync |
| opengl:preventBuffer | | yes | Prevent the driver from buffering frames |
| opengl:amdPinnedMem | | yes | Use GL_AMD_pinned_memory if it is available |
+----------------------+-------+-------+---------------------------------------------+
+-----------------------+-------+-------+-------------------------+
| Long | Short | Value | Description |
+-----------------------+-------+-------+-------------------------+
| wayland:warpSupport | | yes | Enable cursor warping |
| wayland:fractionScale | | yes | Enable fractional scale |
+-----------------------+-------+-------+-------------------------+
.. _host_usage:
Host usage
----------
By default the host application will simply work however there are some
configurable options available. While the host application will accept command
line arguments just as the client will it is more convenient to create the
``looking-glass-host.ini`` file with the desired configuration options.
This file must be placed in the same directory that the Looking Glass host
application was installed for it to be found and used by the application
.. _host_capture:
Capture interface
~~~~~~~~~~~~~~~~~
.. note::
Currently we only provide support for the Windows host application, Linux
options are not currently documented.
Currently under windows there are two capture interfaces available for use,
by default the most compatible and commonly supported interface is selected
however this can be changed via the ini file with the following configuration:
.. code:: ini
[app]
capture=<INTERFACE>
Where ``<INTERFACE>`` is one of ``dxgi`` or ``nvfbc``
.. _host_capture_dxgi:
Microsoft DXGI Desktop Duplication
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This interface (DXGI) is the default and most compatible capture interface for
windows, unfortunately though it does suffer from several drawbacks over other
options. DXGI capture can operate in two modes, DirectX 11 (default) or the
experimental and unofficial DirectX 12 mode.
Due to the design of Microsoft's DXGI API and the decision made to roll
hardware cursor updates into the capture stream this interface can suffer from
microstutters when the mouse is being moved/updated. This issue only affects
guest applications that make use of the hardware cursor instead of compositing
the cursor directly, as such titles that do not use a mouse (most FPV games)
are not affected.
The other drawback of this API is the overall system overhead, however this can
be mitigated by using the DirectX 12 backend. Please be aware though that this
backend is not experimental because it's new, but rather it's a slight
abuse/misuse of the DXGI API and allows us to bypass some Windows internals.
To enable the DirectX 12 backend the following configuration needs to be added
to the ``looking-glass-host.ini`` configuration:
.. code:: ini
[app]
capture=dxgi
[dxgi]
copyBackend=d3d12
d3d12CopySleep=5
disableDamage=false
The option ``d3d12CopySleep`` is to work around the lack of locking this misuse
of the API allows and you will need to tune this value to what suits your
hardware best. The default value is 5ms as this should work for most, lowing
it below 2ms is doubtful to be of practical use to anyone. If this value is too
low you may see screen corruption which is usually most evident while dragging
a window around on the Windows desktop.
.. note::
Lowering d3d12CopySleep can improve the UPS however the UPS metric makes
little sense when using the d3d12 backend as if this value is too low
unchanged frames will be doubled up.
The ``disableDamage`` option may be needed to avoid screen corruption. Note
that this will increase the bandwidth required and in turn the overall load on
your system.
The DXGI capture interface also offers a feature that allows downsampling the
captured frames in the guest GPU before transferring them to shared memory.
This feature is very useful if you are super scaling for better picture quality
and wish to reduce system memory pressure.
The configuration for this is fairly straightforward and is defined as set of
rules to determine when to perform this downsampling. The format is as follows:
.. code:: ini
[dxgi]
downsample=RULE1,RULE2,RULE3
The rules are written as follows:
.. code::
(>|>=)(WIDTH)x(HEIGHT):(LEVEL)
The ``LEVEL`` is the fractional scale level where 1 = 50%, 2 = 25%, 3 = 12.5%.
**Examples:**
.. code:: ini
[dxgi]
; Downsample anything greater then 1920x1080 to 50% of it's original size
downsample=>1920:1080:1
; Downsample exactly 1920x1080 to 25% of it's original size, and anything greater
; then 1920x1080 to 50% of it's original size.
downsample=1920x1080:1,>1920x1080:2
; Downsample anything greater or equal to 1920x1080 to 50% of it's original size
downsample=>=1920x1080:1
.. _host_capture_nvfbc:
NVIDIA Frame Buffer Capture
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Due to the NVIDIA SDK License agreement this GPU feature is only available on
professional/workstation GPUs such as the Quadro series. It is known however
that **all** NVIDIA GPUs are capable of this as both GeForce Experience and
Steam are able to make use of it.
If you are able to make use/enable this this feature it offers lower overall
system load and lower latency capture, and does not suffer from the mouse
motion stutter issues that DXGI suffers from.
To enable it's usage use the following configuration in the
``looking-glass-host.ini`` file:
.. code:: ini
[app]
capture=nvfbc
If this feature is unavailable to you the host application will fail to start
and the host log will contain an error stating that the feature is not
available.
The NVFBC capture interface also offers a feature much like DXGI to allow
downsampling the captured frames in the guest GPU before transferring them to
shared memory. However unlike DXGI which is limited to fractional scaling,
NvFBC is able to scale to any arbitrary resolution.
The configuration for this is fairly straight forward and is defined as set of
rules to determine when to perform this downsampling. The format is as follows:
.. code:: ini
[nvfbc]
downsample=RULE1,RULE2,RULE3
The rules are written as follows:
.. code::
(>|>=)(WIDTH)x(HEIGHT):(TARGET WIDTH)x(TARGET HEIGHT)
**Examples:**
.. code:: ini
[nvfbc]
; Downsample exactly 3840x2160 to 1920x1080
downsample=3840x2160:1920x1080
; Downsample anything greater then 1920x1080 to 1920x1080
downsample=>1920x1080:1920x1080
; Downsample 3840x2160 to 1920x1080, or 3840x2400 to 1920x1200
downsample=3840x2160:1920x1080,3840x2400:1920x1200
This capture interface also looks for and reads the value of the system
environment variable ``NVFBC_PRIV_DATA`` if it has been set, documentation on
its usage however is unavailable.

View File

@@ -1,23 +1,18 @@
backend
backtrace
borderless
BigNavi
cgroups
clang
cmake
config
CopySleep
Debian
dejavu
deuteranope
dev
dir
DirectX
distros
dmabuf
downscaler
downscaling
downsampling
fullscreen
gcc
gnif
@@ -32,14 +27,11 @@ libvirt
linux
LookingGlass
memballoon
MiB
microstutters
mingw
mipmapping
modprobe
msys
multisampling
Navi
Nvidia
overlayed
pacman
@@ -47,7 +39,6 @@ passthrough
pre
protanope
QEMU
Quadro
radeon
realtime
renderer
@@ -55,9 +46,7 @@ repo
runtime
submodule
submodules
spencercw
systemd
Threadripper
toolchain
tritanope
tunable
@@ -75,5 +64,4 @@ VM
vsync
wayland
xdg
Xorg
xyene

View File

@@ -4,13 +4,13 @@ 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"
)
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/")
@@ -62,21 +62,20 @@ if(ENABLE_UBSAN)
endif()
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/version.c
OUTPUT ${CMAKE_BINARY_DIR}/version.c
${CMAKE_BINARY_DIR}/include/version.h
${CMAKE_BINARY_DIR}/_version.c
COMMAND ${CMAKE_COMMAND} -D PROJECT_TOP=${PROJECT_TOP} -P
${PROJECT_TOP}/version.cmake
${CMAKE_BINARY_DIR}/_version.c
COMMAND ${CMAKE_COMMAND} -D PROJECT_TOP=${PROJECT_TOP} -P
${PROJECT_TOP}/version.cmake
)
include_directories(
${PROJECT_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
${PROJECT_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
)
link_libraries(
${CMAKE_DL_LIBS}
)
#link_libraries(
#)
if (ENABLE_ASAN OR ENABLE_UBSAN)
link_directories("c:/msys64/clang64/lib/clang/12.0.0/lib/windows")
@@ -85,8 +84,8 @@ endif()
set(SOURCES
${CMAKE_BINARY_DIR}/version.c
src/app.c
${CMAKE_BINARY_DIR}/version.c
src/app.c
)
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common")
@@ -105,9 +104,9 @@ else()
add_executable(looking-glass-host ${SOURCES})
endif()
target_link_libraries(looking-glass-host
lg_common
platform
lgmp
lg_common
platform
lgmp
)
if(WIN32)
@@ -117,7 +116,7 @@ else()
endif()
install(TARGETS looking-glass-host
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT binary)
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT binary)
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)

View File

@@ -22,15 +22,8 @@
#include <stdbool.h>
#include <stdint.h>
#include "common/KVMFR.h"
#ifdef __cplusplus
/* using common/framebuffer.h breaks compatibillity with C++ due to it's usage
* of stdatomic.h, so we need to forward declare the structure here */
typedef struct stFrameBuffer FrameBuffer;
#else
#include "common/framebuffer.h"
#endif
#include "common/KVMFR.h"
typedef enum CaptureResult
{

View File

@@ -2,22 +2,22 @@ cmake_minimum_required(VERSION 3.0)
project(platform_Linux LANGUAGES C)
include_directories(
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/include
)
add_library(platform_Linux STATIC
src/platform.c
src/platform.c
)
add_subdirectory("capture")
target_link_libraries(platform_Linux
m
capture
pthread
m
capture
pthread
)
target_include_directories(platform_Linux
PRIVATE
src
PRIVATE
src
)

View File

@@ -2,17 +2,17 @@ cmake_minimum_required(VERSION 3.0)
project(capture_XCB LANGUAGES C)
add_library(capture_XCB STATIC
src/xcb.c
src/xcb.c
)
target_link_libraries(capture_XCB
lg_common
xcb
xcb-shm
xcb-xfixes
lg_common
xcb
xcb-shm
xcb-xfixes
)
target_include_directories(capture_XCB
PRIVATE
src
PRIVATE
src
)

View File

@@ -20,7 +20,6 @@
#include "interface/capture.h"
#include "interface/platform.h"
#include "common/util.h"
#include "common/option.h"
#include "common/debug.h"
#include "common/event.h"
@@ -274,8 +273,8 @@ static CaptureResult xcb_waitFrame(CaptureFrame * frame,
frame->screenWidth = this->width;
frame->screenHeight = this->height;
frame->frameWidth = this->width;
frame->frameHeight = min(maxHeight, this->height);
frame->truncated = maxHeight < this->height;
frame->frameHeight = maxHeight > this->height ? this->height : maxHeight;
frame->truncated = maxHeight > this->height;
frame->pitch = this->width * 4;
frame->stride = this->width;
frame->format = CAPTURE_FMT_BGRA;

View File

@@ -3,23 +3,23 @@ project(capture_pipewire LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(CAPTURE_PIPEWIRE REQUIRED IMPORTED_TARGET
gio-2.0
gio-unix-2.0
libpipewire-0.3
libspa-0.2
gio-2.0
gio-unix-2.0
libpipewire-0.3
libspa-0.2
)
add_library(capture_pipewire STATIC
src/pipewire.c
src/portal.c
src/pipewire.c
src/portal.c
)
target_link_libraries(capture_pipewire
PkgConfig::CAPTURE_PIPEWIRE
lg_common
PkgConfig::CAPTURE_PIPEWIRE
lg_common
)
target_include_directories(capture_pipewire
PRIVATE
src
PRIVATE
src
)

View File

@@ -21,7 +21,6 @@
#include "portal.h"
#include "interface/capture.h"
#include "interface/platform.h"
#include "common/util.h"
#include "common/debug.h"
#include "common/stringutils.h"
#include <string.h>
@@ -429,8 +428,8 @@ static CaptureResult pipewire_waitFrame(CaptureFrame * frame,
frame->screenWidth = this->width;
frame->screenHeight = this->height;
frame->frameWidth = this->width;
frame->frameHeight = min(maxHeight, this->height);
frame->truncated = maxHeight < this->height;
frame->frameHeight = maxHeight > this->height ? this->height : maxHeight;
frame->truncated = maxHeight > this->height;
frame->pitch = this->width * bpp;
frame->stride = this->width;
frame->rotation = CAPTURE_ROT_0;

View File

@@ -2,14 +2,14 @@ cmake_minimum_required(VERSION 3.0)
project(platform_Windows LANGUAGES C)
include_directories(
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/include
)
add_library(platform_Windows STATIC
src/platform.c
src/service.c
src/mousehook.c
src/force_compose.c
src/platform.c
src/service.c
src/mousehook.c
src/force_compose.c
)
# allow use of functions for Windows 7 or later
@@ -18,21 +18,21 @@ add_compile_definitions(WINVER=0x0601 _WIN32_WINNT=0x0601)
add_subdirectory("capture")
target_link_libraries(platform_Windows
lg_common
capture
lg_common
capture
userenv
wtsapi32
psapi
shlwapi
powrprof
rpcrt4
avrt
userenv
wtsapi32
psapi
shlwapi
powrprof
rpcrt4
avrt
)
target_include_directories(platform_Windows
PRIVATE
src
PRIVATE
src
)
# these are for the nsis installer generator

View File

@@ -2,24 +2,24 @@ cmake_minimum_required(VERSION 3.0)
project(capture_DXGI LANGUAGES C)
add_library(capture_DXGI STATIC
src/dxgi.c
src/d3d11.c
src/d3d12.c
src/ods_capture.c
src/util.c
src/dxgi.c
src/d3d11.c
src/d3d12.c
src/ods_capture.c
src/util.c
)
add_definitions("-DCOBJMACROS -DINITGUID")
target_link_libraries(capture_DXGI
lg_common
d3d11
dxgi
dwmapi
lg_common
d3d11
dxgi
dwmapi
)
target_include_directories(capture_DXGI
PRIVATE
src
"${PROJECT_TOP}/vendor/directx"
PRIVATE
src
"${PROJECT_TOP}/vendor/directx"
)

Some files were not shown because too many files have changed in this diff Show More