mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-02-18 00:29:48 +00:00
Compare commits
1 Commits
B6-rc1
...
host-downs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
655c993c5b |
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@@ -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
|
||||
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -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
|
||||
|
||||
5
AUTHORS
5
AUTHORS
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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) \
|
||||
\
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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`!
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
69
client/renderers/EGL/draw.c
Normal file
69
client/renderers/EGL/draw.c
Normal 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);
|
||||
}
|
||||
29
client/renderers/EGL/draw.h
Normal file
29
client/renderers/EGL/draw.h
Normal 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);
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -527,7 +527,7 @@ static void configUI(void * opaque, int * id)
|
||||
if (redraw)
|
||||
{
|
||||
atomic_store(&this->modified, true);
|
||||
app_invalidateWindow(true);
|
||||
app_invalidateWindow(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
13
client/renderers/EGL/shader/splash_bg.frag
Normal file
13
client/renderers/EGL/shader/splash_bg.frag
Normal 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);
|
||||
}
|
||||
14
client/renderers/EGL/shader/splash_bg.vert
Normal file
14
client/renderers/EGL/shader/splash_bg.vert
Normal 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;
|
||||
}
|
||||
11
client/renderers/EGL/shader/splash_logo.frag
Normal file
11
client/renderers/EGL/shader/splash_logo.frag
Normal 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);
|
||||
}
|
||||
12
client/renderers/EGL/shader/splash_logo.vert
Normal file
12
client/renderers/EGL/shader/splash_logo.vert
Normal 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;
|
||||
}
|
||||
178
client/renderers/EGL/splash.c
Normal file
178
client/renderers/EGL/splash.c
Normal 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);
|
||||
}
|
||||
30
client/renderers/EGL/splash.h
Normal file
30
client/renderers/EGL/splash.h
Normal 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);
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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);
|
||||
|
||||
150
client/src/app.c
150
client/src/app.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
87
client/src/overlay/record.c
Normal file
87
client/src/overlay/record.c
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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 } }
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
102
doc/build.rst
102
doc/build.rst
@@ -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:
|
||||
|
||||
|
||||
@@ -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',
|
||||
]
|
||||
|
||||
@@ -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
17
doc/css/center-rtd.css
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
10
doc/faq.rst
10
doc/faq.rst
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
775
doc/install.rst
775
doc/install.rst
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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 VM → VM mode.
|
||||
for Looking Glass in VM->VM mode.
|
||||
|
||||
Additionally in VM → host 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 VM → VM, simply ``modprobe`` the module::
|
||||
For VM->VM, simply modprobe the module::
|
||||
|
||||
modprobe kvmfr
|
||||
|
||||
For VM → host 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 VM → VM mode, run:
|
||||
For VM->VM mode, run:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
insmod kvmfr.ko
|
||||
|
||||
For VM → host 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:
|
||||
|
||||
VM → Host
|
||||
~~~~~~~~~~~~
|
||||
VM->Host
|
||||
~~~~~~~~
|
||||
|
||||
In VM → host 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 VM → host 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::
|
||||
|
||||
|
||||
@@ -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``
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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?**
|
||||
|
||||
525
doc/usage.rst
525
doc/usage.rst
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user