Compare commits

..

No commits in common. "master" and "B6" have entirely different histories.
master ... B6

377 changed files with 12379 additions and 39238 deletions

2
.github/CODEOWNERS vendored
View File

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

View File

@ -2,7 +2,7 @@ name: build
on: [push, pull_request]
jobs:
client:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
@ -15,6 +15,12 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install libdecor PPA
run: sudo add-apt-repository ppa:christianrauch/libdecoration
if: ${{ matrix.wayland_shell == 'libdecor' }}
- name: Install PipeWire repository
run: |
echo 'deb [trusted=yes] https://pipewire-ubuntu.quantum5.workers.dev ./' | sudo tee /etc/apt/sources.list.d/pipewire.list
- name: Update apt
run: |
sudo apt-get update
@ -26,7 +32,6 @@ jobs:
libgl-dev libgles-dev \
libx11-dev libxss-dev libxi-dev libxinerama-dev libxcursor-dev libxpresent-dev \
libwayland-dev libxkbcommon-dev \
libfontconfig-dev \
libsamplerate0-dev libpipewire-0.3-dev libpulse-dev \
$([ '${{ matrix.wayland_shell }}' = libdecor ] && echo 'libdecor-0-dev libdbus-1-dev') \
$([ '${{ matrix.compiler.cc }}' = clang ] && echo 'clang-tools')
@ -71,13 +76,16 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install PipeWire repository
run: |
echo 'deb [trusted=yes] https://pipewire-ubuntu.quantum5.workers.dev ./' | sudo tee /etc/apt/sources.list.d/pipewire.list
- name: Update apt
run: |
sudo apt-get update
- name: Install Linux host dependencies
run: |
sudo apt-get install binutils-dev libglib2.0-dev libxcb-xfixes0-dev \
libpipewire-0.3-dev libxcb-shm0-dev
sudo apt-get install binutils-dev libxcb-xfixes0-dev \
libpipewire-0.3-dev
- name: Configure Linux host
run: |
mkdir host/build
@ -89,7 +97,7 @@ jobs:
make -j$(nproc)
host-windows-cross:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
@ -175,8 +183,8 @@ jobs:
sudo apt-get update
- name: Install docs dependencies
run: |
sudo apt-get install python3-sphinx python3-sphinx-rtd-theme \
python3-sphinxcontrib.spelling
sudo apt-get install python3-sphinx python3-sphinx-rtd-theme
sudo pip3 install sphinxcontrib-spelling
- name: Build docs
run: |
cd doc

View File

@ -3,36 +3,16 @@ on: pull_request
jobs:
authors:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v1
- name: Check AUTHORS file
id: check-authors
run: |
user="$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' -s https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }} | jq -r .user.login)"
echo "user=$user" >> "$GITHUB_OUTPUT"
echo "Checking if GitHub user $user is in AUTHORS file..."
if grep -q -E '> \('"$user"'\)' AUTHORS; then
echo "$user found in AUTHORS file, all good!"
else
echo "$user not found in AUTHORS file."
echo "Please add yourself to the AUTHORS file and try again."
echo "not-found=yes" >> "$GITHUB_OUTPUT"
exit 1
fi
- name: 'Not found: Create review requesting changes'
if: ${{ steps.check-authors.outputs.not-found }}
uses: actions/github-script@v7
with:
script: |
github.rest.pulls.createReview({
owner: context.issue.owner,
repo: context.issue.repo,
pull_number: context.issue.number,
event: "REQUEST_CHANGES",
body: "@${{ steps.check-authors.outputs.user }} not found in AUTHORS file.\n" +
"Please add yourself to the AUTHORS file and try again."
});
- name: 'Not found: Fail job'
if: ${{ steps.check-authors.outputs.not-found }}
run: exit 1

8
.gitignore vendored
View File

@ -10,11 +10,3 @@ module/modules.order
*/build
__pycache__
*.py[co]
*/.vs
*.user
idd/Debug
idd/x64
idd/*/x64
idd/*/Debug
idd/*/VersionInfo.h
idd/packages

View File

@ -1,32 +0,0 @@
---
labels:
platform: windows/amd64
matrix:
BUILD_TYPE:
- Debug
- Release
clone:
- name: clone
image: woodpeckerci/plugin-git
pull: false
settings:
tags: true
steps:
- name: idd
when:
- branch: master
event: [push, manual]
image: lg-vs2022:latest
pull: true
environment:
VS_PATH: "\"C:\\\\Program Files (x86)\\\\Microsoft Visual Studio\\\\2022\\\\\""
entrypoint:
- cmd
- /C
- >
%VS_PATH%\BuildTools\Common7\Tools\VsDevCmd.bat -arch=amd64 &&
msbuild /restore idd\LGIdd.sln /p:Configuration=${BUILD_TYPE} /p:RestorePackagesConfig=true /p:Platform=x64 /p:SignMode=Off /m &&
IF EXIST C:\artifacts\build.cmd (cmd /C C:\artifacts\build.cmd)

View File

@ -65,10 +65,3 @@ 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)
MakiseKurisu <saberconer@gmail.com> (MakiseKurisu)
Zenithal <i@zenithal.me> (ZenithalHourlyRate)
Kamplom <6284968128@protonmail.ch> (kamplom)
Jacob McNamee <jacob@jacobmcnamee.com> (jacobmcnamee)
Marco Antonio J. Costa <marco.antonio.costa@gmail.com> (majcosta)
rs189 <35667100+rs189@users.noreply.github.com> (rs189)
Jérôme Poulin <jeromepoulin@gmail.com> (ticpu)

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.0)
project(looking-glass-client C CXX)
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
@ -42,12 +42,25 @@ add_feature_info(ENABLE_ASAN ENABLE_ASAN "AddressSanitizer support.")
option(ENABLE_UBSAN "Build with UndefinedBehaviorSanitizer" OFF)
add_feature_info(ENABLE_UBSAN ENABLE_UBSAN "UndefinedBehaviorSanitizer support.")
option(ENABLE_X11 "Build with X11 support" ON)
add_feature_info(ENABLE_X11 ENABLE_X11 "X11 support.")
option(ENABLE_WAYLAND "Build with Wayland support" ON)
add_feature_info(ENABLE_WAYLAND ENABLE_WAYLAND "Wayland support.")
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
option(ENABLE_PIPEWIRE "Build with PipeWire audio output support" ON)
add_feature_info(ENABLE_PIPEWIRE ENABLE_PIPEWIRE "PipeWire audio support.")
option(ENABLE_PULSEAUDIO "Build with PulseAudio audio output support" ON)
add_feature_info(ENABLE_PULSEAUDIO ENABLE_PULSEAUDIO "PulseAudio audio support.")
if (NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
message(FATAL_ERROR "Either ENABLE_X11 or ENABLE_WAYLAND must be on")
endif()
add_compile_options(
"-Wall"
"-Wextra"
@ -113,7 +126,6 @@ set(SOURCES
src/main.c
src/core.c
src/app.c
src/message.c
src/audio.c
src/config.c
src/keybind.c
@ -125,7 +137,6 @@ set(SOURCES
src/eglutil.c
src/overlay_utils.c
src/render_queue.c
src/evdev.c
src/overlay/splash.c
src/overlay/alert.c
@ -139,8 +150,6 @@ set(SOURCES
# Force cimgui to build as a static library.
set(IMGUI_STATIC "yes" CACHE STRING "Build as a static library")
add_definitions("-DCIMGUI_USE_OPENGL2=1")
add_definitions("-DCIMGUI_USE_OPENGL3=1")
add_subdirectory("${PROJECT_TOP}/resources" "${CMAKE_BINARY_DIR}/resources")
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common" )
@ -151,7 +160,6 @@ add_subdirectory("${PROJECT_TOP}/repos/cimgui" "${CMAKE_BINARY_DIR}/cimgui" E
add_subdirectory(displayservers)
add_subdirectory(renderers)
configure_file("${PROJECT_TOP}/resources/looking-glass-client.desktop.in" "${CMAKE_BINARY_DIR}/resources/looking-glass-client.desktop" @ONLY)
add_executable(looking-glass-client ${SOURCES})
target_compile_definitions(looking-glass-client PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1)
@ -182,10 +190,4 @@ install(TARGETS looking-glass-client
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT binary)
install(FILES "${CMAKE_BINARY_DIR}/resources/looking-glass-client.desktop"
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications")
install(FILES "${PROJECT_TOP}/resources/lg-logo.svg"
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps"
RENAME "looking-glass.svg")
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.0)
project(audiodevs LANGUAGES C)
set(AUDIODEV_H "${CMAKE_BINARY_DIR}/include/dynamic/audiodev.h")

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.0)
project(audiodev_PipeWire LANGUAGES C)
find_package(PkgConfig)

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -28,7 +28,6 @@
#include "common/debug.h"
#include "common/stringutils.h"
#include "common/util.h"
#include "common/option.h"
typedef enum
{
@ -48,7 +47,6 @@ struct PipeWire
{
struct pw_stream * stream;
struct spa_io_rate_match * rateMatch;
struct pw_time time;
int channels;
int sampleRate;
@ -92,14 +90,6 @@ static void pipewire_onPlaybackProcess(void * userdata)
{
struct pw_buffer * pbuf;
#if PW_CHECK_VERSION(0, 3, 50)
if (pw_stream_get_time_n(pw.playback.stream, &pw.playback.time,
sizeof(pw.playback.time)) < 0)
#else
if (pw_stream_get_time(pw.playback.stream, &pw.playback.time) < 0)
#endif
DEBUG_ERROR("pw_stream_get_time failed");
if (!(pbuf = pw_stream_dequeue_buffer(pw.playback.stream)))
{
DEBUG_WARN("out of buffers");
@ -124,7 +114,6 @@ static void pipewire_onPlaybackProcess(void * userdata)
return;
}
pbuf->size = frames;
sbuf->datas[0].chunk->offset = 0;
sbuf->datas[0].chunk->stride = pw.playback.stride;
sbuf->datas[0].chunk->size = frames * pw.playback.stride;
@ -140,29 +129,6 @@ static void pipewire_onPlaybackDrained(void * userdata)
pw_thread_loop_unlock(pw.thread);
}
static struct Option pipewire_options[] =
{
{
.module = "pipewire",
.name = "outDevice",
.description = "The default playback device to use",
.type = OPTION_TYPE_STRING
},
{
.module = "pipewire",
.name = "recDevice",
.description = "The default record device to use",
.type = OPTION_TYPE_STRING
},
{0}
};
static void pipewire_earlyInit(void)
{
option_register(pipewire_options);
}
static bool pipewire_init(void)
{
pw_init(NULL, NULL);
@ -255,32 +221,17 @@ static void pipewire_playbackSetup(int channels, int sampleRate,
pw.playback.pullFn = pullFn;
pw_thread_loop_lock(pw.thread);
struct pw_properties * props =
pw.playback.stream = pw_stream_new_simple(
pw.loop,
"Looking Glass",
pw_properties_new(
PW_KEY_APP_NAME , "Looking Glass",
PW_KEY_NODE_NAME , "Looking Glass",
PW_KEY_MEDIA_TYPE , "Audio",
PW_KEY_MEDIA_CATEGORY, "Playback",
PW_KEY_MEDIA_ROLE , "Music",
PW_KEY_NODE_LATENCY , requestedNodeLatency,
NULL
);
const char * device = option_get_string("pipewire", "outDevice");
if (device)
{
#ifdef PW_KEY_TARGET_OBJECT
pw_properties_set(props, PW_KEY_TARGET_OBJECT, device);
#else
pw_properties_set(props, PW_KEY_NODE_TARGET, device);
#endif
}
pw.playback.stream = pw_stream_new_simple(
pw.loop,
"Looking Glass",
props,
),
&events,
NULL
);
@ -412,32 +363,20 @@ static void pipewire_playbackMute(bool mute)
pw_thread_loop_unlock(pw.thread);
}
static uint64_t pipewire_playbackLatency(void)
static size_t pipewire_playbackLatency(void)
{
struct pw_time time = { 0 };
pw_thread_loop_lock(pw.thread);
#if PW_CHECK_VERSION(0, 3, 50)
if (pw.playback.time.rate.num == 0)
return 0;
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
// diff in ns
int64_t diff = SPA_TIMESPEC_TO_NSEC(&ts) - pw.playback.time.now;
// elapsed frames
int64_t elapsed =
(pw.playback.time.rate.denom * diff) /
(pw.playback.time.rate.num * SPA_NSEC_PER_SEC);
const int64_t buffered = pw.playback.time.buffered + pw.playback.time.queued;
int64_t latency = (buffered * 1000 / pw.playback.sampleRate) +
((pw.playback.time.delay - elapsed) * 1000 *
pw.playback.time.rate.num / pw.playback.time.rate.denom);
return max(0, -latency);
if (pw_stream_get_time_n(pw.playback.stream, &time, sizeof(time)) < 0)
#else
return pw.playback.time.delay + pw.playback.time.queued / pw.playback.stride;
if (pw_stream_get_time(pw.playback.stream, &time) < 0)
#endif
DEBUG_ERROR("pw_stream_get_time failed");
pw_thread_loop_unlock(pw.thread);
return time.delay + time.queued / pw.playback.stride;
}
static void pipewire_recordStopStream(void)
@ -510,30 +449,17 @@ static void pipewire_recordStart(int channels, int sampleRate,
pw.record.stride = sizeof(uint16_t) * channels;
pw.record.pushFn = pushFn;
struct pw_properties * props =
pw_thread_loop_lock(pw.thread);
pw.record.stream = pw_stream_new_simple(
pw.loop,
"Looking Glass",
pw_properties_new(
PW_KEY_NODE_NAME , "Looking Glass",
PW_KEY_MEDIA_TYPE , "Audio",
PW_KEY_MEDIA_CATEGORY, "Capture",
PW_KEY_MEDIA_ROLE , "Music",
NULL
);
const char * device = option_get_string("pipewire", "recDevice");
if (device)
{
#ifdef PW_KEY_TARGET_OBJECT
pw_properties_set(props, PW_KEY_TARGET_OBJECT, device);
#else
pw_properties_set(props, PW_KEY_NODE_TARGET, device);
#endif
}
pw_thread_loop_lock(pw.thread);
pw.record.stream = pw_stream_new_simple(
pw.loop,
"Looking Glass",
props,
),
&events,
NULL
);
@ -618,7 +544,6 @@ static void pipewire_free(void)
struct LG_AudioDevOps LGAD_PipeWire =
{
.name = "PipeWire",
.earlyInit = pipewire_earlyInit,
.init = pipewire_init,
.free = pipewire_free,
.playback =

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.0)
project(audiodev_PulseAudio LANGUAGES C)
find_package(PkgConfig)

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.0)
project(displayservers LANGUAGES C)
set(DISPLAYSERVER_H "${CMAKE_BINARY_DIR}/include/dynamic/displayservers.h")
@ -18,16 +18,6 @@ function(add_displayserver name)
add_subdirectory(${name})
endfunction()
option(ENABLE_X11 "Build with X11 support" ON)
add_feature_info(ENABLE_X11 ENABLE_X11 "X11 support.")
option(ENABLE_WAYLAND "Build with Wayland support" ON)
add_feature_info(ENABLE_WAYLAND ENABLE_WAYLAND "Wayland support.")
if (NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
message(FATAL_ERROR "Either ENABLE_X11 or ENABLE_WAYLAND must be on")
endif()
# Add/remove displayservers here!
if (ENABLE_WAYLAND)
add_displayserver(Wayland)

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.0)
project(displayserver_Wayland LANGUAGES C)
find_package(PkgConfig)
@ -8,7 +8,19 @@ pkg_check_modules(DISPLAYSERVER_Wayland REQUIRED IMPORTED_TARGET
xkbcommon
)
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
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)
else()
list(APPEND displayserver_Wayland_SHELL_SRC shell_xdg.c)
endif()
add_library(displayserver_Wayland STATIC
activation.c
@ -24,19 +36,66 @@ add_library(displayserver_Wayland STATIC
registry.c
wayland.c
window.c
${displayserver_Wayland_SHELL_SRC}
)
add_subdirectory(protocol)
add_subdirectory(desktops)
target_link_libraries(displayserver_Wayland
PkgConfig::DISPLAYSERVER_Wayland
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
lg_common
wayland_protocol
wayland_desktops
)
target_include_directories(displayserver_Wayland
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}.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")
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_generate(
"${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_generate(
"${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_generate(
"${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_generate(
"${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_generate(
"${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol")

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -475,7 +475,6 @@ void waylandCBRequest(LG_ClipboardData type)
close(data->fd);
free(data->buf);
free(data);
return;
}
wlCb.currentRead = data;

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,58 +0,0 @@
cmake_minimum_required(VERSION 3.10)
project(wayland_desktops LANGUAGES C)
set(DESKTOP_H "${CMAKE_BINARY_DIR}/include/dynamic/wayland_desktops.h")
set(DESKTOP_C "${CMAKE_BINARY_DIR}/src/wayland_desktops.c")
file(WRITE ${DESKTOP_H} "#include \"interface/desktop.h\"\n\n")
file(APPEND ${DESKTOP_H} "extern struct WL_DesktopOps * WL_Desktops[];\n\n")
file(WRITE ${DESKTOP_C} "#include \"interface/desktop.h\"\n\n")
file(APPEND ${DESKTOP_C} "#include <stddef.h>\n\n")
set(DESKTOPS "_")
set(DESKTOPS_LINK "_")
function(add_desktop name)
set(DESKTOPS "${DESKTOPS};${name}" PARENT_SCOPE)
set(DESKTOPS_LINK "${DESKTOPS_LINK};wayland_desktop_${name}" PARENT_SCOPE)
add_subdirectory(${name})
endfunction()
# Add/remove desktops here!
# the first entry here is the default
add_desktop(xdg)
pkg_check_modules(LIBDECOR IMPORTED_TARGET libdecor-0)
if(LIBDECOR_FOUND)
option(ENABLE_LIBDECOR "Build with libdecor support" ON)
else()
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
endif()
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
if (ENABLE_LIBDECOR)
add_desktop(libdecor)
endif()
list(REMOVE_AT DESKTOPS 0)
list(REMOVE_AT DESKTOPS_LINK 0)
list(LENGTH DESKTOPS DESKTOP_COUNT)
file(APPEND ${DESKTOP_H} "#define WL_DESKTOP_COUNT ${DESKTOP_COUNT}\n")
foreach(desktop ${DESKTOPS})
file(APPEND ${DESKTOP_C} "extern struct WL_DesktopOps WLD_${desktop};\n")
endforeach()
file(APPEND ${DESKTOP_C} "\nconst struct WL_DesktopOps * WL_Desktops[] =\n{\n")
foreach(desktop ${DESKTOPS})
file(APPEND ${DESKTOP_C} " &WLD_${desktop},\n")
endforeach()
file(APPEND ${DESKTOP_C} " NULL\n};")
add_library(wayland_desktops STATIC ${DESKTOP_C})
target_link_libraries(wayland_desktops ${DESKTOPS_LINK})
target_include_directories(wayland_desktops
PRIVATE
../
)

View File

@ -1,16 +0,0 @@
cmake_minimum_required(VERSION 3.10)
project(wayland_desktop_libdecor LANGUAGES C)
add_library(wayland_desktop_libdecor STATIC
libdecor.c
)
target_link_libraries(wayland_desktop_libdecor
lg_common
wayland_protocol
PkgConfig::LIBDECOR
)
include_directories(
"../../"
)

View File

@ -1,275 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2025 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/desktop.h"
#include "wayland-xdg-shell-client-protocol.h"
#include "wayland.h"
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/epoll.h>
#include <libdecor.h>
#include <wayland-client.h>
#include "app.h"
#include "common/debug.h"
// Maximum number of fds we can process at once in waylandWait
#define MAX_EPOLL_EVENTS 10
typedef struct LibDecorState
{
bool configured;
struct libdecor * libdecor;
struct libdecor_frame * libdecorFrame;
int32_t width, height;
bool needsResize;
bool fullscreen;
uint32_t resizeSerial;
}
LibDecorState;
static LibDecorState state = {0};
struct libdecor_configuration
{
uint32_t serial;
bool has_window_state;
enum libdecor_window_state window_state;
bool has_size;
int window_width;
int window_height;
};
static void libdecorHandleError(struct libdecor * context, enum libdecor_error error,
const char *message)
{
DEBUG_ERROR("Got libdecor error (%d): %s", error, message);
}
static void libdecorFrameConfigure(struct libdecor_frame * frame,
struct libdecor_configuration * configuration, void * opaque)
{
if (!state.configured)
{
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(frame), configuration->serial);
state.configured = true;
return;
}
int width, height;
if (libdecor_configuration_get_content_size(configuration, frame, &width, &height))
{
state.width = width;
state.height = height;
struct libdecor_state * s = libdecor_state_new(width, height);
libdecor_frame_commit(state.libdecorFrame, s, NULL);
libdecor_state_free(s);
}
enum libdecor_window_state windowState;
if (libdecor_configuration_get_window_state(configuration, &windowState))
state.fullscreen = windowState & LIBDECOR_WINDOW_STATE_FULLSCREEN;
state.resizeSerial = configuration->serial;
waylandNeedsResize();
}
static void libdecorFrameClose(struct libdecor_frame * frame, void * opaque)
{
app_handleCloseEvent();
}
static void libdecorFrameCommit(struct libdecor_frame * frame, void * opaque)
{
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
static struct libdecor_interface libdecorListener =
{
libdecorHandleError,
};
static struct libdecor_frame_interface libdecorFrameListener =
{
libdecorFrameConfigure,
libdecorFrameClose,
libdecorFrameCommit,
};
#pragma GCC diagnostic pop
static void libdecorCallback(uint32_t events, void * opaque)
{
libdecor_dispatch(state.libdecor, 0);
}
static bool libdecor_shellInit(
struct wl_display * display, struct wl_surface * surface,
const char * title, const char * appId, bool fullscreen,
bool maximize, bool borderless, bool resizable)
{
state.libdecor = libdecor_new(display, &libdecorListener);
state.libdecorFrame = libdecor_decorate(state.libdecor, surface,
&libdecorFrameListener, NULL);
libdecor_frame_set_app_id(state.libdecorFrame, appId);
libdecor_frame_set_title(state.libdecorFrame, title);
libdecor_frame_map(state.libdecorFrame);
if (fullscreen)
libdecor_frame_set_fullscreen(state.libdecorFrame, NULL);
if (maximize)
libdecor_frame_set_maximized(state.libdecorFrame);
if (resizable)
libdecor_frame_set_capabilities(state.libdecorFrame,
LIBDECOR_ACTION_RESIZE);
else
libdecor_frame_unset_capabilities(state.libdecorFrame,
LIBDECOR_ACTION_RESIZE);
while (!state.configured)
libdecor_dispatch(state.libdecor, 0);
if (!waylandPollRegister(libdecor_get_fd(state.libdecor),
libdecorCallback, NULL, EPOLLIN))
{
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
return false;
}
return true;
}
static void libdecor_shellAckConfigureIfNeeded(void)
{
if (state.resizeSerial)
{
xdg_surface_ack_configure(
libdecor_frame_get_xdg_surface(state.libdecorFrame), state.resizeSerial);
state.resizeSerial = 0;
}
}
static void libdecor_setFullscreen(bool fs)
{
if (fs)
libdecor_frame_set_fullscreen(state.libdecorFrame, NULL);
else
libdecor_frame_unset_fullscreen(state.libdecorFrame);
libdecor_frame_set_visibility(state.libdecorFrame, !fs);
}
static bool libdecor_getFullscreen(void)
{
return state.fullscreen;
}
static void libdecor_minimize(void)
{
libdecor_frame_set_minimized(state.libdecorFrame);
}
static void libdecor_shellResize(int w, int h)
{
if (!libdecor_frame_is_floating(state.libdecorFrame))
return;
state.width = w;
state.height = h;
struct libdecor_state * s = libdecor_state_new(w, h);
libdecor_frame_commit(state.libdecorFrame, s, NULL);
libdecor_state_free(s);
waylandNeedsResize();
}
static void libdecor_setSize(int w, int h)
{
state.width = w;
state.height = h;
}
static void libdecor_getSize(int * w, int * h)
{
*w = state.width;
*h = state.height;
}
static bool libdecor_registryGlobalHandler(void * data,
struct wl_registry * registry, uint32_t name, const char * interface,
uint32_t version)
{
return false;
}
bool libdecor_pollInit(struct wl_display * display)
{
return true;
}
void libdecor_pollWait(struct wl_display * display, int epollFd,
unsigned int time)
{
libdecor_dispatch(state.libdecor, 0);
struct epoll_event events[MAX_EPOLL_EVENTS];
int count;
if ((count = epoll_wait(epollFd, events, MAX_EPOLL_EVENTS, time)) < 0)
{
if (errno != EINTR)
DEBUG_INFO("epoll failed: %s", strerror(errno));
return;
}
for (int i = 0; i < count; ++i)
{
struct WaylandPoll * poll = events[i].data.ptr;
if (!poll->removed)
poll->callback(events[i].events, poll->opaque);
}
}
WL_DesktopOps WLD_libdecor =
{
.name = "libdecor",
.compositor = "gnome-shell",
.shellInit = libdecor_shellInit,
.shellAckConfigureIfNeeded = libdecor_shellAckConfigureIfNeeded,
.setFullscreen = libdecor_setFullscreen,
.getFullscreen = libdecor_getFullscreen,
.minimize = libdecor_minimize,
.shellResize = libdecor_shellResize,
.setSize = libdecor_setSize,
.getSize = libdecor_getSize,
.registryGlobalHandler = libdecor_registryGlobalHandler,
.pollInit = libdecor_pollInit,
.pollWait = libdecor_pollWait
};

View File

@ -1,15 +0,0 @@
cmake_minimum_required(VERSION 3.10)
project(wayland_desktop_xdg LANGUAGES C)
add_library(wayland_desktop_xdg STATIC
xdg.c
)
target_link_libraries(wayland_desktop_xdg
lg_common
wayland_protocol
)
include_directories(
"../../"
)

View File

@ -1,314 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2025 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 "wayland.h"
#include "wayland-xdg-shell-client-protocol.h"
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
#include <stdbool.h>
#include <string.h>
#include <sys/epoll.h>
#include <errno.h>
#include <wayland-client.h>
#include "app.h"
#include "common/debug.h"
// Maximum number of fds we can process at once in waylandWait
#define MAX_EPOLL_EVENTS 10
typedef struct XDGState
{
bool configured;
struct xdg_wm_base * wmBase;
struct xdg_surface * surface;
struct xdg_toplevel * toplevel;
struct zxdg_decoration_manager_v1 * decorationManager;
struct zxdg_toplevel_decoration_v1 * toplevelDecoration;
int32_t width, height;
uint32_t resizeSerial;
bool fullscreen;
bool floating;
int displayFd;
}
XDGState;
static XDGState state = {0};
// XDG WM base listeners.
static void xdgWmBasePing(void * data, struct xdg_wm_base * xdgWmBase, uint32_t serial)
{
xdg_wm_base_pong(xdgWmBase, serial);
}
static const struct xdg_wm_base_listener xdgWmBaseListener = {
.ping = xdgWmBasePing,
};
// XDG Surface listeners.
static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface,
uint32_t serial)
{
if (state.configured)
{
state.resizeSerial = serial;
waylandNeedsResize();
}
else
{
xdg_surface_ack_configure(xdgSurface, serial);
state.configured = true;
}
}
static const struct xdg_surface_listener xdgSurfaceListener = {
.configure = xdgSurfaceConfigure,
};
// XDG Toplevel listeners.
static void xdgToplevelConfigure(void * data, struct xdg_toplevel * xdgToplevel,
int32_t width, int32_t height, struct wl_array * states)
{
state.width = width;
state.height = height;
state.fullscreen = false;
state.floating = true;
enum xdg_toplevel_state * s;
wl_array_for_each(s, states)
{
switch (*s)
{
case XDG_TOPLEVEL_STATE_FULLSCREEN:
state.fullscreen = true;
// fallthrough
case XDG_TOPLEVEL_STATE_MAXIMIZED:
case XDG_TOPLEVEL_STATE_TILED_LEFT:
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
case XDG_TOPLEVEL_STATE_TILED_TOP:
case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
state.floating = false;
break;
default:
break;
}
}
}
static void xdgToplevelClose(void * data, struct xdg_toplevel * xdgToplevel)
{
app_handleCloseEvent();
}
static const struct xdg_toplevel_listener xdgToplevelListener = {
.configure = xdgToplevelConfigure,
.close = xdgToplevelClose,
};
bool xdg_shellInit(struct wl_display * display, struct wl_surface * surface,
const char * title, const char * appId, bool fullscreen, bool maximize, bool borderless,
bool resizable)
{
if (!state.wmBase)
{
DEBUG_ERROR("Compositor missing xdg_wm_base, will not proceed");
return false;
}
xdg_wm_base_add_listener(state.wmBase, &xdgWmBaseListener, NULL);
state.surface = xdg_wm_base_get_xdg_surface(state.wmBase, surface);
xdg_surface_add_listener(state.surface, &xdgSurfaceListener, NULL);
state.toplevel = xdg_surface_get_toplevel(state.surface);
xdg_toplevel_add_listener(state.toplevel, &xdgToplevelListener, NULL);
xdg_toplevel_set_title(state.toplevel, title);
xdg_toplevel_set_app_id(state.toplevel, appId);
if (fullscreen)
xdg_toplevel_set_fullscreen(state.toplevel, NULL);
if (maximize)
xdg_toplevel_set_maximized(state.toplevel);
if (state.decorationManager)
{
state.toplevelDecoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
state.decorationManager, state.toplevel);
if (state.toplevelDecoration)
{
zxdg_toplevel_decoration_v1_set_mode(state.toplevelDecoration,
borderless ?
ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
}
return true;
}
static void xdg_shellAckConfigureIfNeeded(void)
{
if (state.resizeSerial)
{
xdg_surface_ack_configure(state.surface, state.resizeSerial);
state.resizeSerial = 0;
}
}
static void xdg_setFullscreen(bool fs)
{
if (fs)
xdg_toplevel_set_fullscreen(state.toplevel, NULL);
else
xdg_toplevel_unset_fullscreen(state.toplevel);
}
static bool xdg_getFullscreen(void)
{
return state.fullscreen;
}
static void xdg_minimize(void)
{
xdg_toplevel_set_minimized(state.toplevel);
}
static void xdg_shellResize(int w, int h)
{
if (!state.floating)
return;
state.width = w;
state.height = h;
xdg_surface_set_window_geometry(state.surface, 0, 0, w, h);
waylandNeedsResize();
}
static void xdg_setSize(int w, int h)
{
state.width = w;
state.height = h;
}
static void xdg_getSize(int * w, int * h)
{
*w = state.width;
*h = state.height;
}
static bool xdg_registryGlobalHandler(void * data,
struct wl_registry * registry, uint32_t name, const char * interface,
uint32_t version)
{
if (!strcmp(interface, xdg_wm_base_interface.name))
{
state.wmBase = wl_registry_bind(registry, name,
&xdg_wm_base_interface, 1);
return true;
}
if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name))
{
state.decorationManager = wl_registry_bind(registry, name,
&zxdg_decoration_manager_v1_interface, 1);
return true;
}
return false;
}
static void waylandDisplayCallback(uint32_t events, void * opaque)
{
struct wl_display * display = (struct wl_display *)opaque;
if (events & EPOLLERR)
wl_display_cancel_read(display);
else
wl_display_read_events(display);
wl_display_dispatch_pending(display);
}
static bool xdg_pollInit(struct wl_display * display)
{
state.displayFd = wl_display_get_fd(display);
if (!waylandPollRegister(state.displayFd, waylandDisplayCallback,
display, EPOLLIN))
{
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
return false;
}
return true;
}
void xdg_pollWait(struct wl_display * display, int epollFd,
unsigned int time)
{
while (wl_display_prepare_read(display))
wl_display_dispatch_pending(display);
wl_display_flush(display);
struct epoll_event events[MAX_EPOLL_EVENTS];
int count;
if ((count = epoll_wait(epollFd, events, MAX_EPOLL_EVENTS, time)) < 0)
{
if (errno != EINTR)
DEBUG_INFO("epoll failed: %s", strerror(errno));
wl_display_cancel_read(display);
return;
}
bool sawDisplay = false;
for (int i = 0; i < count; ++i) {
struct WaylandPoll * poll = events[i].data.ptr;
if (!poll->removed)
poll->callback(events[i].events, poll->opaque);
if (poll->fd == state.displayFd)
sawDisplay = true;
}
if (!sawDisplay)
wl_display_cancel_read(display);
}
WL_DesktopOps WLD_xdg =
{
.name = "xdg",
.compositor = "",
.shellInit = xdg_shellInit,
.shellAckConfigureIfNeeded = xdg_shellAckConfigureIfNeeded,
.setFullscreen = xdg_setFullscreen,
.getFullscreen = xdg_getFullscreen,
.minimize = xdg_minimize,
.shellResize = xdg_shellResize,
.setSize = xdg_setSize,
.getSize = xdg_getSize,
.registryGlobalHandler = xdg_registryGlobalHandler,
.pollInit = xdg_pollInit,
.pollWait = xdg_pollWait
};

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -89,25 +89,18 @@ 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);
int width, height;
wlWm.desktop->getSize(&width, &height);
wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(width * wlWm.scale),
wl_fixed_to_int(height * wlWm.scale), 0, 0);
if (width == 0 || height == 0)
if (wlWm.width == 0 || wlWm.height == 0)
skipResize = true;
else if (wlWm.fractionalScale)
{
wl_surface_set_buffer_scale(wlWm.surface, 1);
if (!wlWm.viewport)
wlWm.viewport = wp_viewporter_get_viewport(wlWm.viewporter, wlWm.surface);
wp_viewport_set_source(
wlWm.viewport,
wl_fixed_from_int(-1), wl_fixed_from_int(-1),
wl_fixed_from_int(-1), wl_fixed_from_int(-1)
);
wp_viewport_set_destination(wlWm.viewport, width, height);
wp_viewport_set_source(wlWm.viewport, 0, 0, wlWm.width * wlWm.scale, wlWm.height * wlWm.scale);
wp_viewport_set_destination(wlWm.viewport, wlWm.width, wlWm.height);
}
else
{
@ -127,18 +120,18 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
}
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
wl_region_add(region, 0, 0, width, height);
wl_region_add(region, 0, 0, wlWm.width, wlWm.height);
wl_surface_set_opaque_region(wlWm.surface, region);
wl_region_destroy(region);
app_handleResizeEvent(width, height, wl_fixed_to_double(wlWm.scale),
app_handleResizeEvent(wlWm.width, wlWm.height, wl_fixed_to_double(wlWm.scale),
(struct Border) {0, 0, 0, 0});
app_invalidateWindow(true);
waylandStopWaitFrame();
wlWm.needsResize = skipResize;
}
wlWm.desktop->shellAckConfigureIfNeeded();
waylandShellAckConfigureIfNeeded();
}
#endif

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -209,7 +209,7 @@ done:
close(fd);
}
int waylandGetCharCode(int key)
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);
@ -232,7 +232,7 @@ static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
uint32_t * key;
wl_array_for_each(key, keys)
app_handleKeyPress(*key);
app_handleKeyPress(*key, getCharcode(*key));
}
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
@ -253,9 +253,9 @@ static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard,
return;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
app_handleKeyPress(key);
app_handleKeyPress(key, getCharcode(key));
else
app_handleKeyRelease(key);
app_handleKeyRelease(key, getCharcode(key));
if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED)
return;
@ -590,13 +590,10 @@ void waylandWarpPointer(int x, int y, bool exiting)
return;
}
int width, height;
wlWm.desktop->getSize(&width, &height);
if (x < 0) x = 0;
else if (x >= width) x = width - 1;
else if (x >= wlWm.width) x = wlWm.width - 1;
if (y < 0) y = 0;
else if (y >= height) y = height - 1;
else if (y >= wlWm.height) y = wlWm.height - 1;
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
wl_region_add(region, x, y, 1, 1);

View File

@ -1,64 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2025 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
*/
#ifndef _H_WAYLAND_DESKTOP_H_
#define _H_WAYLAND_DESKTOP_H_
#include <stdbool.h>
#include <wayland-client.h>
typedef struct WL_DesktopOps
{
// the friendly name
const char * name;
// the compositor process name to match
const char * compositor;
bool (*shellInit)(
struct wl_display * display, struct wl_surface * surface,
const char * title, const char * appId, bool fullscreen, bool maximize,
bool borderless, bool resizable);
void (*shellAckConfigureIfNeeded)(void);
void (*setFullscreen)(bool fs);
bool (*getFullscreen)(void);
void (*minimize)(void);
void (*shellResize)(int w, int h);
void (*setSize)(int w, int h);
void (*getSize)(int * w, int * h);
bool (*registryGlobalHandler)(
void * data, struct wl_registry * registry,
uint32_t name, const char * interface, uint32_t version);
bool (*pollInit)(struct wl_display * display);
void (*pollWait)(struct wl_display * display, int epollFd, unsigned int time);
}
WL_DesktopOps;
#endif

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -30,6 +30,23 @@
#include "common/debug.h"
#include "common/locking.h"
#ifdef ENABLE_LIBDECOR
#include <libdecor.h>
#endif
#define EPOLL_EVENTS 10 // Maximum number of fds we can process at once in waylandWait
#ifndef ENABLE_LIBDECOR
static void waylandDisplayCallback(uint32_t events, void * opaque)
{
if (events & EPOLLERR)
wl_display_cancel_read(wlWm.display);
else
wl_display_read_events(wlWm.display);
wl_display_dispatch_pending(wlWm.display);
}
#endif
bool waylandPollInit(void)
{
wlWm.epollFd = epoll_create1(EPOLL_CLOEXEC);
@ -44,12 +61,58 @@ bool waylandPollInit(void)
LG_LOCK_INIT(wlWm.pollLock);
LG_LOCK_INIT(wlWm.pollFreeLock);
return wlWm.desktop->pollInit(wlWm.display);
#ifndef ENABLE_LIBDECOR
wlWm.displayFd = wl_display_get_fd(wlWm.display);
if (!waylandPollRegister(wlWm.displayFd, waylandDisplayCallback, NULL, EPOLLIN))
{
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
return false;
}
#endif
return true;
}
void waylandWait(unsigned int time)
{
wlWm.desktop->pollWait(wlWm.display, wlWm.epollFd, time);
#ifdef ENABLE_LIBDECOR
libdecor_dispatch(wlWm.libdecor, 0);
#else
while (wl_display_prepare_read(wlWm.display))
wl_display_dispatch_pending(wlWm.display);
wl_display_flush(wlWm.display);
#endif
struct epoll_event events[EPOLL_EVENTS];
int count;
if ((count = epoll_wait(wlWm.epollFd, events, EPOLL_EVENTS, time)) < 0)
{
if (errno != EINTR)
DEBUG_INFO("epoll failed: %s", strerror(errno));
#ifndef ENABLE_LIBDECOR
wl_display_cancel_read(wlWm.display);
#endif
return;
}
#ifndef ENABLE_LIBDECOR
bool sawDisplay = false;
#endif
for (int i = 0; i < count; ++i) {
struct WaylandPoll * poll = events[i].data.ptr;
if (!poll->removed)
poll->callback(events[i].events, poll->opaque);
#ifndef ENABLE_LIBDECOR
if (poll->fd == wlWm.displayFd)
sawDisplay = true;
#endif
}
#ifndef ENABLE_LIBDECOR
if (!sawDisplay)
wl_display_cancel_read(wlWm.display);
#endif
INTERLOCKED_SECTION(wlWm.pollFreeLock,
{
struct WaylandPoll * node;

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -109,17 +109,10 @@ void waylandPresentationFrame(void)
return;
struct FrameData * data = malloc(sizeof(*data));
if (!data)
{
DEBUG_ERROR("out of memory");
return;
}
if (clock_gettime(wlWm.clkId, &data->sent))
{
DEBUG_ERROR("clock_gettime failed: %s\n", strerror(errno));
free(data);
return;
}
struct wp_presentation_feedback * feedback = wp_presentation_feedback(wlWm.presentation, wlWm.surface);

View File

@ -1,71 +0,0 @@
cmake_minimum_required(VERSION 3.10)
project(wayland_protocol LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(WAYLAND REQUIRED IMPORTED_TARGET
wayland-client
wayland-cursor
xkbcommon
)
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
add_library(wayland_protocol STATIC
)
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}.c"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c"
DEPENDS "${protocol_file}"
VERBATIM)
target_sources(wayland_protocol 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_generate(
"${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_generate(
"${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_generate(
"${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_generate(
"${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_generate(
"${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol")
target_link_libraries(wayland_protocol
PkgConfig::WAYLAND
)
target_include_directories(wayland_protocol
PUBLIC
"${CMAKE_BINARY_DIR}/wayland"
)

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -40,6 +40,13 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
wlWm.compositor = wl_registry_bind(wlWm.registry, name,
// we only need v3 to run, but v4 can use eglSwapBuffersWithDamageKHR
&wl_compositor_interface, version > 4 ? 4 : version);
#ifndef ENABLE_LIBDECOR
else if (!strcmp(interface, xdg_wm_base_interface.name))
wlWm.xdgWmBase = wl_registry_bind(wlWm.registry, name, &xdg_wm_base_interface, 1);
else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name))
wlWm.xdgDecorationManager = wl_registry_bind(wlWm.registry, name,
&zxdg_decoration_manager_v1_interface, 1);
#endif
else if (!strcmp(interface, wp_presentation_interface.name))
wlWm.presentation = wl_registry_bind(wlWm.registry, name,
&wp_presentation_interface, 1);
@ -68,9 +75,6 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
else if (!strcmp(interface, xdg_activation_v1_interface.name))
wlWm.xdgActivation = wl_registry_bind(wlWm.registry, name,
&xdg_activation_v1_interface, 1);
else if (wlWm.desktop->registryGlobalHandler(
data, registry, name, interface, version))
return;
}
static void registryGlobalRemoveHandler(void * data,

View File

@ -0,0 +1,178 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/epoll.h>
#include <libdecor.h>
#include <wayland-client.h>
#include "app.h"
#include "common/debug.h"
struct libdecor_configuration {
uint32_t serial;
bool has_window_state;
enum libdecor_window_state window_state;
bool has_size;
int window_width;
int window_height;
};
static void libdecorHandleError(struct libdecor * context, enum libdecor_error error,
const char *message)
{
DEBUG_ERROR("Got libdecor error (%d): %s", error, message);
}
static void libdecorFrameConfigure(struct libdecor_frame * frame,
struct libdecor_configuration * configuration, void * opaque)
{
if (!wlWm.configured)
{
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(frame), configuration->serial);
wlWm.configured = true;
return;
}
int width, height;
if (libdecor_configuration_get_content_size(configuration, frame, &width, &height))
{
wlWm.width = width;
wlWm.height = height;
struct libdecor_state * state = libdecor_state_new(wlWm.width, wlWm.height);
libdecor_frame_commit(wlWm.libdecorFrame, state, NULL);
libdecor_state_free(state);
}
enum libdecor_window_state windowState;
if (libdecor_configuration_get_window_state(configuration, &windowState))
wlWm.fullscreen = windowState & LIBDECOR_WINDOW_STATE_FULLSCREEN;
wlWm.needsResize = true;
wlWm.resizeSerial = configuration->serial;
app_invalidateWindow(true);
waylandStopWaitFrame();
}
static void libdecorFrameClose(struct libdecor_frame * frame, void * opaque)
{
app_handleCloseEvent();
}
static void libdecorFrameCommit(struct libdecor_frame * frame, void * opaque)
{
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
static struct libdecor_interface libdecorListener = {
libdecorHandleError,
};
static struct libdecor_frame_interface libdecorFrameListener = {
libdecorFrameConfigure,
libdecorFrameClose,
libdecorFrameCommit,
};
#pragma GCC diagnostic pop
static void libdecorCallback(uint32_t events, void * opaque)
{
libdecor_dispatch(wlWm.libdecor, 0);
}
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
{
wlWm.libdecor = libdecor_new(wlWm.display, &libdecorListener);
wlWm.libdecorFrame = libdecor_decorate(wlWm.libdecor, wlWm.surface, &libdecorFrameListener, NULL);
libdecor_frame_set_app_id(wlWm.libdecorFrame, "looking-glass-client");
libdecor_frame_set_title(wlWm.libdecorFrame, title);
libdecor_frame_map(wlWm.libdecorFrame);
if (resizable)
libdecor_frame_set_capabilities(wlWm.libdecorFrame, LIBDECOR_ACTION_RESIZE);
else
libdecor_frame_unset_capabilities(wlWm.libdecorFrame, LIBDECOR_ACTION_RESIZE);
while (!wlWm.configured)
libdecor_dispatch(wlWm.libdecor, 0);
if (!waylandPollRegister(libdecor_get_fd(wlWm.libdecor), libdecorCallback, NULL, EPOLLIN))
{
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
return false;
}
return true;
}
void waylandShellAckConfigureIfNeeded(void)
{
if (wlWm.resizeSerial)
{
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(wlWm.libdecorFrame), wlWm.resizeSerial);
wlWm.resizeSerial = 0;
}
}
void waylandSetFullscreen(bool fs)
{
if (fs)
libdecor_frame_set_fullscreen(wlWm.libdecorFrame, NULL);
else
libdecor_frame_unset_fullscreen(wlWm.libdecorFrame);
libdecor_frame_set_visibility(wlWm.libdecorFrame, !fs);
}
bool waylandGetFullscreen(void)
{
return wlWm.fullscreen;
}
void waylandMinimize(void)
{
libdecor_frame_set_minimized(wlWm.libdecorFrame);
}
void waylandShellResize(int w, int h)
{
if (!libdecor_frame_is_floating(wlWm.libdecorFrame))
return;
wlWm.width = w;
wlWm.height = h;
struct libdecor_state * state = libdecor_state_new(w, h);
libdecor_frame_commit(wlWm.libdecorFrame, state, NULL);
libdecor_state_free(state);
wlWm.needsResize = true;
app_invalidateWindow(true);
waylandStopWaitFrame();
}

View File

@ -0,0 +1,184 @@
/**
* 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 "wayland.h"
#include <stdbool.h>
#include <wayland-client.h>
#include "app.h"
#include "common/debug.h"
// XDG WM base listeners.
static void xdgWmBasePing(void * data, struct xdg_wm_base * xdgWmBase, uint32_t serial)
{
xdg_wm_base_pong(xdgWmBase, serial);
}
static const struct xdg_wm_base_listener xdgWmBaseListener = {
.ping = xdgWmBasePing,
};
// XDG Surface listeners.
static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface,
uint32_t serial)
{
if (wlWm.configured)
{
wlWm.needsResize = true;
wlWm.resizeSerial = serial;
app_invalidateWindow(true);
waylandStopWaitFrame();
}
else
{
xdg_surface_ack_configure(xdgSurface, serial);
wlWm.configured = true;
}
}
static const struct xdg_surface_listener xdgSurfaceListener = {
.configure = xdgSurfaceConfigure,
};
// XDG Toplevel listeners.
static void xdgToplevelConfigure(void * data, struct xdg_toplevel * xdgToplevel,
int32_t width, int32_t height, struct wl_array * states)
{
wlWm.width = width;
wlWm.height = height;
wlWm.fullscreen = false;
wlWm.floating = true;
enum xdg_toplevel_state * state;
wl_array_for_each(state, states)
{
switch (*state)
{
case XDG_TOPLEVEL_STATE_FULLSCREEN:
wlWm.fullscreen = true;
// fallthrough
case XDG_TOPLEVEL_STATE_MAXIMIZED:
case XDG_TOPLEVEL_STATE_TILED_LEFT:
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
case XDG_TOPLEVEL_STATE_TILED_TOP:
case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
wlWm.floating = false;
break;
default:
break;
}
}
}
static void xdgToplevelClose(void * data, struct xdg_toplevel * xdgToplevel)
{
app_handleCloseEvent();
}
static const struct xdg_toplevel_listener xdgToplevelListener = {
.configure = xdgToplevelConfigure,
.close = xdgToplevelClose,
};
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
{
if (!wlWm.xdgWmBase)
{
DEBUG_ERROR("Compositor missing xdg_wm_base, will not proceed");
return false;
}
xdg_wm_base_add_listener(wlWm.xdgWmBase, &xdgWmBaseListener, NULL);
wlWm.xdgSurface = xdg_wm_base_get_xdg_surface(wlWm.xdgWmBase, wlWm.surface);
xdg_surface_add_listener(wlWm.xdgSurface, &xdgSurfaceListener, NULL);
wlWm.xdgToplevel = xdg_surface_get_toplevel(wlWm.xdgSurface);
xdg_toplevel_add_listener(wlWm.xdgToplevel, &xdgToplevelListener, NULL);
xdg_toplevel_set_title(wlWm.xdgToplevel, title);
xdg_toplevel_set_app_id(wlWm.xdgToplevel, "looking-glass-client");
if (fullscreen)
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
if (maximize)
xdg_toplevel_set_maximized(wlWm.xdgToplevel);
if (wlWm.xdgDecorationManager)
{
wlWm.xdgToplevelDecoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
wlWm.xdgDecorationManager, wlWm.xdgToplevel);
if (wlWm.xdgToplevelDecoration)
{
zxdg_toplevel_decoration_v1_set_mode(wlWm.xdgToplevelDecoration,
borderless ?
ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
}
return true;
}
void waylandShellAckConfigureIfNeeded(void)
{
if (wlWm.resizeSerial)
{
xdg_surface_ack_configure(wlWm.xdgSurface, wlWm.resizeSerial);
wlWm.resizeSerial = 0;
}
}
void waylandSetFullscreen(bool fs)
{
if (fs)
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
else
xdg_toplevel_unset_fullscreen(wlWm.xdgToplevel);
}
bool waylandGetFullscreen(void)
{
return wlWm.fullscreen;
}
void waylandMinimize(void)
{
xdg_toplevel_set_minimized(wlWm.xdgToplevel);
}
void waylandShellResize(int w, int h)
{
if (!wlWm.floating)
return;
wlWm.width = w;
wlWm.height = h;
xdg_surface_set_window_geometry(wlWm.xdgSurface, 0, 0, w, h);
wlWm.needsResize = true;
app_invalidateWindow(true);
waylandStopWaitFrame();
}

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -24,13 +24,10 @@
#include <signal.h>
#include <string.h>
#include <wayland-client.h>
#include <sys/socket.h>
#include "common/debug.h"
#include "common/option.h"
#include "dynamic/wayland_desktops.h"
static struct Option waylandOptions[] =
{
{
@ -71,69 +68,18 @@ static bool waylandProbe(void)
return getenv("WAYLAND_DISPLAY") != NULL;
}
static bool getCompositor(char * dst, size_t size)
{
int fd = wl_display_get_fd(wlWm.display);
struct ucred ucred;
socklen_t len = sizeof(struct ucred);
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1)
{
DEBUG_ERROR("Failed to get the pid of the socket");
return false;
}
char path[64];
snprintf(path, sizeof(path), "/proc/%d/comm", ucred.pid);
FILE *fp = fopen(path, "r");
if (!fp)
{
DEBUG_ERROR("Failed to open %s", path);
return false;
}
if (!fgets(dst, size, fp))
{
DEBUG_ERROR("Failed to read %s", path);
fclose(fp);
return false;
}
fclose(fp);
dst[strlen(dst) - 1] = 0;
return true;
}
static bool waylandInit(const LG_DSInitParams params)
{
memset(&wlWm, 0, sizeof(wlWm));
wlWm.desktop = WL_Desktops[0];
wlWm.display = wl_display_connect(NULL);
if (!wlWm.display)
return false;
// select the desktop interface based on the compositor process name
char compositor[1024];
if (getCompositor(compositor, sizeof(compositor)))
{
DEBUG_INFO("Compositor: %s", compositor);
for(int i = 0; i < WL_DESKTOP_COUNT; ++i)
if (strcmp(WL_Desktops[i]->compositor, compositor) == 0)
{
wlWm.desktop = WL_Desktops[i];
break;
}
}
else
DEBUG_WARN("Compositor: UNKNOWN");
DEBUG_INFO("Selected : %s", wlWm.desktop->name);
wl_list_init(&wlWm.surfaceOutputs);
wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale");
wlWm.display = wl_display_connect(NULL);
wlWm.width = params.w;
wlWm.height = params.h;
if (!waylandPollInit())
return false;
@ -158,9 +104,7 @@ static bool waylandInit(const LG_DSInitParams params)
if (!waylandInputInit())
return false;
wlWm.desktop->setSize(params.w, params.h);
if (!waylandWindowInit(params.title, params.appId, params.fullscreen, params.maximize,
params.borderless, params.resizable))
if (!waylandWindowInit(params.title, params.fullscreen, params.maximize, params.borderless, params.resizable))
return false;
if (!waylandEGLInit(params.w, params.h))
@ -171,6 +115,9 @@ static bool waylandInit(const LG_DSInitParams params)
return false;
#endif
wlWm.width = params.w;
wlWm.height = params.h;
return true;
}
@ -205,28 +152,6 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
return false;
}
void waylandNeedsResize(void)
{
wlWm.needsResize = true;
app_invalidateWindow(true);
waylandStopWaitFrame();
}
static void waylandSetFullscreen(bool fs)
{
wlWm.desktop->setFullscreen(fs);
}
static bool waylandGetFullscreen(void)
{
return wlWm.desktop->getFullscreen();
}
static void waylandMinimize(void)
{
wlWm.desktop->minimize();
}
struct LG_DisplayServerOps LGDS_Wayland =
{
.name = "Wayland",
@ -263,7 +188,6 @@ struct LG_DisplayServerOps LGDS_Wayland =
.uncapturePointer = waylandUncapturePointer,
.grabKeyboard = waylandGrabKeyboard,
.ungrabKeyboard = waylandUngrabKeyboard,
.getCharCode = waylandGetCharCode,
.warpPointer = waylandWarpPointer,
.realignPointer = waylandRealignPointer,
.isValidPointerPos = waylandIsValidPointerPos,

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -37,10 +37,11 @@
#include "common/countedbuffer.h"
#include "common/ringbuffer.h"
#include "interface/displayserver.h"
#include "interface/desktop.h"
#include "wayland-xdg-shell-client-protocol.h"
#include "wayland-presentation-time-client-protocol.h"
#include "wayland-viewporter-client-protocol.h"
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
#include "wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
@ -99,8 +100,6 @@ struct WaylandDSState
bool pointerInSurface;
bool focusedOnSurface;
WL_DesktopOps * desktop;
struct wl_display * display;
struct wl_surface * surface;
struct wl_registry * registry;
@ -108,9 +107,13 @@ struct WaylandDSState
struct wl_shm * shm;
struct wl_compositor * compositor;
int32_t width, height;
wl_fixed_t scale;
bool fractionalScale;
bool needsResize;
bool fullscreen;
bool floating;
uint32_t resizeSerial;
bool configured;
bool warpSupport;
double cursorX, cursorY;
@ -131,6 +134,17 @@ struct WaylandDSState
RingBuffer photonTimings;
GraphHandle photonGraph;
#ifdef ENABLE_LIBDECOR
struct libdecor * libdecor;
struct libdecor_frame * libdecorFrame;
#else
struct xdg_wm_base * xdgWmBase;
struct xdg_surface * xdgSurface;
struct xdg_toplevel * xdgToplevel;
struct zxdg_decoration_manager_v1 * xdgDecorationManager;
struct zxdg_toplevel_decoration_v1 * xdgToplevelDecoration;
#endif
const char * cursorThemeName;
int cursorSize;
int cursorScale;
@ -277,7 +291,6 @@ void waylandUncapturePointer(void);
void waylandRealignPointer(void);
void waylandWarpPointer(int x, int y, bool exiting);
void waylandGuestPointerUpdated(double x, double y, double localX, double localY);
int waylandGetCharCode(int key);
// output module
bool waylandOutputInit(void);
@ -301,8 +314,16 @@ void waylandPresentationFree(void);
bool waylandRegistryInit(void);
void waylandRegistryFree(void);
// shell module
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
void waylandShellAckConfigureIfNeeded(void);
void waylandSetFullscreen(bool fs);
bool waylandGetFullscreen(void);
void waylandMinimize(void);
void waylandShellResize(int w, int h);
// window module
bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, bool maximize, bool borderless, bool resizable);
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
void waylandWindowFree(void);
void waylandWindowUpdateScale(void);
void waylandSetWindowSize(int x, int y);
@ -310,4 +331,3 @@ bool waylandIsValidPointerPos(int x, int y);
bool waylandWaitFrame(void);
void waylandSkipFrame(void);
void waylandStopWaitFrame(void);
void waylandNeedsResize(void);

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -85,7 +85,7 @@ static const struct wl_surface_listener wlSurfaceListener = {
.leave = wlSurfaceLeaveHandler,
};
bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, bool maximize, bool borderless, bool resizable)
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
{
wlWm.scale = wl_fixed_from_int(1);
@ -112,8 +112,7 @@ bool waylandWindowInit(const char * title, const char * appId, bool fullscreen,
wl_surface_add_listener(wlWm.surface, &wlSurfaceListener, NULL);
if (!wlWm.desktop->shellInit(wlWm.display, wlWm.surface,
title, appId, fullscreen, maximize, borderless, resizable))
if (!waylandShellInit(title, fullscreen, maximize, borderless, resizable))
return false;
wl_surface_commit(wlWm.surface);
@ -128,14 +127,12 @@ void waylandWindowFree(void)
void waylandSetWindowSize(int x, int y)
{
wlWm.desktop->shellResize(x, y);
waylandShellResize(x, y);
}
bool waylandIsValidPointerPos(int x, int y)
{
int width, height;
wlWm.desktop->getSize(&width, &height);
return x >= 0 && x < width && y >= 0 && y < height;
return x >= 0 && x < wlWm.width && y >= 0 && y < wlWm.height;
}
static void frameHandler(void * opaque, struct wl_callback * callback, unsigned int data)

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.0)
project(displayserver_X11 LANGUAGES C)
find_package(PkgConfig)
@ -17,10 +17,6 @@ add_library(displayserver_X11 STATIC
x11.c
atoms.c
clipboard.c
cursor.c
wm/default.c
wm/i3.c
)
add_definitions(-D GLX_GLXEXT_PROTOTYPES)
@ -28,10 +24,9 @@ add_definitions(-D GLX_GLXEXT_PROTOTYPES)
target_link_libraries(displayserver_X11
PkgConfig::DISPLAYSERVER_X11
lg_common
lg_resources
)
target_include_directories(displayserver_X11
PRIVATE
.
src
)

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -60,40 +60,40 @@ static void x11CBSelectionIncr(const XPropertyEvent e);
static void x11CBSelectionNotify(const XSelectionEvent e);
static void x11CBXFixesSelectionNotify(const XFixesSelectionNotifyEvent e);
bool x11CBEventThread(const XEvent * xe)
bool x11CBEventThread(const XEvent xe)
{
switch(xe->type)
switch(xe.type)
{
case SelectionRequest:
x11CBSelectionRequest(xe->xselectionrequest);
x11CBSelectionRequest(xe.xselectionrequest);
return true;
case SelectionClear:
x11CBSelectionClear(xe->xselectionclear);
x11CBSelectionClear(xe.xselectionclear);
return true;
case SelectionNotify:
x11CBSelectionNotify(xe->xselection);
x11CBSelectionNotify(xe.xselection);
return true;
case PropertyNotify:
if (xe->xproperty.state != PropertyNewValue)
if (xe.xproperty.state != PropertyNewValue)
break;
if (xe->xproperty.atom == x11atoms.SEL_DATA)
if (xe.xproperty.atom == x11atoms.SEL_DATA)
{
if (x11cb.lowerBound == 0)
return true;
x11CBSelectionIncr(xe->xproperty);
x11CBSelectionIncr(xe.xproperty);
return true;
}
break;
default:
if (xe->type == x11.eventBase + XFixesSelectionNotify)
if (xe.type == x11.eventBase + XFixesSelectionNotify)
{
XFixesSelectionNotifyEvent * sne = (XFixesSelectionNotifyEvent *)xe;
XFixesSelectionNotifyEvent * sne = (XFixesSelectionNotifyEvent *)&xe;
x11CBXFixesSelectionNotify(*sne);
return true;
}

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -26,7 +26,7 @@
#include "interface/displayserver.h"
bool x11CBEventThread(const XEvent * xe);
bool x11CBEventThread(const XEvent xe);
bool x11CBInit(void);
void x11CBNotice(LG_ClipboardData type);

View File

@ -1,105 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2025 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 "cursor.h"
#include "common/util.h"
#include <string.h>
#include <errno.h>
struct MemFile
{
const char * data;
int size;
int pos;
};
static int x11cursor_read(XcursorFile *file, unsigned char * buf, int len)
{
struct MemFile * f = (struct MemFile *)file->closure;
if (f->pos == f->size)
return 0;
len = min(f->size - f->pos, len);
memcpy(buf, f->data + f->pos, len);
f->pos += len;
return len;
}
static int x11cursor_write(XcursorFile *file, unsigned char * buf, int len)
{
errno = -EINVAL;
return -1;
}
static int x11cursor_seek(XcursorFile *file, long offset, int whence)
{
struct MemFile * f = (struct MemFile *)file->closure;
long target;
switch(whence)
{
case SEEK_SET:
target = offset;
break;
case SEEK_CUR:
target = f->pos + offset;
break;
case SEEK_END:
target = f->size + offset;
break;
default:
errno = -EINVAL;
return -1;
}
if (target < 0 || target > f->size)
{
errno = -EINVAL;
return -1;
}
f->pos = target;
return target;
}
XcursorImages * x11cursor_load(const char * cursor, int size)
{
struct MemFile closure =
{
.data = cursor,
.size = size,
.pos = 0
};
XcursorFile f =
{
.closure = &closure,
.read = x11cursor_read,
.write = x11cursor_write,
.seek = x11cursor_seek
};
return XcursorXcFileLoadAllImages(&f);
}

View File

@ -1,29 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2025 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
*/
#ifndef _H_X11DS_CURSOR_
#define _H_X11DS_CURSOR_
#include <X11/Xlib.h>
#include <X11/Xcursor/Xcursor.h>
XcursorImages * x11cursor_load(const char * cursor, int size);
#endif

View File

@ -1,39 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2025 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
*/
#ifndef _H_X11DS_WM_
#define _H_X11DS_WM_
#include <stdint.h>
#include <stdbool.h>
typedef struct X11WM
{
void (*setup)(void);
bool (*init)(void);
void (*deinit)(void);
void (*setFullscreen)(bool enable);
}
X11WM;
extern X11WM X11WM_Default;
extern X11WM X11WM_i3;
#endif

View File

@ -1,71 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2025 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
*/
#ifndef _H_X11DS_WM_DEFAULT_
#define _H_X11DS_WM_DEFAULT_
#include "wm.h"
#include "x11.h"
#include "atoms.h"
static void wm_default_setup(void)
{
}
static bool wm_default_init(void)
{
return true;
}
static void wm_default_deinit(void)
{
}
static void wm_default_setFullscreen(bool enable)
{
XEvent e =
{
.xclient = {
.type = ClientMessage,
.send_event = true,
.message_type = x11atoms._NET_WM_STATE,
.format = 32,
.window = x11.window,
.data.l = {
enable ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
x11atoms._NET_WM_STATE_FULLSCREEN,
0
}
}
};
XSendEvent(x11.display, DefaultRootWindow(x11.display), False,
SubstructureNotifyMask | SubstructureRedirectMask, &e);
};
X11WM X11WM_Default =
{
.setup = wm_default_setup,
.init = wm_default_init,
.deinit = wm_default_deinit,
.setFullscreen = wm_default_setFullscreen
};
#endif

View File

@ -1,193 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2025 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
*/
#ifndef _H_X11DS_WM_DEFAULT_
#define _H_X11DS_WM_DEFAULT_
#include "wm.h"
#include "x11.h"
#include "atoms.h"
#include "common/debug.h"
#include "common/option.h"
#include "common/util.h"
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
static struct Option options[] =
{
// app options
{
.module = "i3",
.name = "globalFullScreen",
.description = "Use i3's global full screen feature (spans all monitors)",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
},
{0}
};
struct i3state
{
int sock;
bool globalFullScreen;
};
static struct i3state i3;
static void wm_i3_setup(void)
{
option_register(options);
}
static bool wm_i3_init(void)
{
memset(&i3, 0, sizeof(i3));
i3.globalFullScreen = option_get_bool("i3", "globalFullScreen");
FILE * fd = popen("i3 --get-socketpath", "r");
if (!fd)
return false;
struct sockaddr_un addr = { .sun_family = AF_UNIX };
char * path = (char *)&addr.sun_path;
int pathLen;
if ((pathLen = fread(path, 1, sizeof(addr.sun_path), fd)) <= 0)
{
pclose(fd);
return false;
}
pclose(fd);
if(path[pathLen-1] == '\n')
--pathLen;
path[pathLen] = '\0';
i3.sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (i3.sock < 0)
{
DEBUG_ERROR("Failed to create socket for i3 IPC");
return false;
}
if (connect(i3.sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
DEBUG_ERROR("Failed to connect to the i3 IPC socket");
perror("connect");
goto err_socket;
}
DEBUG_INFO("i3 IPC Connected");
return true;
err_socket:
close(i3.sock);
return false;
}
static void wm_i3_deinit(void)
{
close(i3.sock);
}
static void wm_i3_setFullscreen(bool enable)
{
if (!i3.globalFullScreen)
{
X11WM_Default.setFullscreen(enable);
return;
}
struct i3Msg
{
char magic[6];
uint32_t length;
uint32_t type;
char payload[0];
}
__attribute__((packed));
#define I3_IPC_TYPE_RUN_COMMAND 0
char cmd[128];
int cmdLen = snprintf(cmd, sizeof(cmd),
"[id=%lu] fullscreen toggle global",
x11.window);
struct i3Msg *msg = alloca(sizeof(struct i3Msg) + cmdLen);
memcpy(msg->magic, "i3-ipc", 6);
msg->length = cmdLen;
msg->type = I3_IPC_TYPE_RUN_COMMAND;
memcpy(msg->payload, cmd, cmdLen);
int msgSize = sizeof(*msg) + msg->length;
char * buf = (char *)msg;
while(msgSize)
{
int wrote = write(i3.sock, buf, msgSize);
if (wrote <= 0)
{
DEBUG_WARN("i3 IPC communication failure");
return;
}
buf += wrote;
msgSize -= wrote;
}
if ((msgSize = read(i3.sock, msg, sizeof(*msg))) < 0)
{
DEBUG_WARN("i3 IPC read failure");
return;
}
if (memcmp(msg->magic, "i3-ipc", 6) != 0 ||
msg->type != I3_IPC_TYPE_RUN_COMMAND)
{
DEBUG_WARN("i3 IPC unexpected reply");
return;
}
// read and discard the payload
while(msg->length)
{
int len = read(i3.sock, cmd, min(msg->length, sizeof(cmd)));
if (len <= 0)
{
DEBUG_WARN("i3 IPC failed to read payload");
return;
}
msg->length -= len;
}
};
X11WM X11WM_i3 =
{
.setup = wm_i3_setup,
.init = wm_i3_init,
.deinit = wm_i3_deinit,
.setFullscreen = wm_i3_setFullscreen
};
#endif

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -23,11 +23,7 @@
#include "x11.h"
#include "atoms.h"
#include "clipboard.h"
#include "cursor.h"
#include "resources/icondata.h"
#include "resources/no-input-cursor/16.xcur.h"
#include "resources/no-input-cursor/32.xcur.h"
#include <string.h>
#include <unistd.h>
@ -57,6 +53,10 @@
#include "common/event.h"
#include "util.h"
#define _NET_WM_STATE_REMOVE 0
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_TOGGLE 2
struct X11DSState x11;
struct MwmHints
@ -166,8 +166,6 @@ static void x11DoPresent(uint64_t msc)
static void x11Setup(void)
{
X11WM_Default.setup();
X11WM_i3 .setup();
}
static bool x11Probe(void)
@ -229,11 +227,9 @@ static void x11CheckEWMHSupport(void)
x11.ewmhHasFocusEvent = true;
}
DEBUG_INFO("EWMH-compliant window manager detected: %s", wmName);
DEBUG_INFO("EWMH-complient window manager detected: %s", wmName);
x11.ewmhSupport = true;
if (strcmp(wmName, "i3") == 0)
x11.wm = &X11WM_i3;
XFree(wmName);
out_supported:
@ -246,37 +242,17 @@ out:
return;
}
static int x11ErrorHandler(Display * display, XErrorEvent * error)
{
char errorText[1024];
XGetErrorText(display, error->error_code, errorText, sizeof(errorText));
DEBUG_ERROR("X11 Error: %s", errorText);
DEBUG_PRINT_BACKTRACE();
return 0;
}
static int x11IOErrorHandler(Display * display)
{
DEBUG_FATAL("Fatal X11 IO Error");
return 0;
}
static bool x11Init(const LG_DSInitParams params)
{
XIDeviceInfo *devinfo;
int count;
int event, error;
XSetErrorHandler(x11ErrorHandler);
XSetIOErrorHandler(x11IOErrorHandler);
memset(&x11, 0, sizeof(x11));
x11.xValuator = -1;
x11.yValuator = -1;
x11.display = XOpenDisplay(NULL);
x11.jitRender = params.jitRender;
x11.wm = &X11WM_Default;
XSetWindowAttributes swa =
{
@ -343,7 +319,7 @@ static bool x11Init(const LG_DSInitParams params)
XClassHint hint =
{
.res_name = strdup(params.title),
.res_class = strdup(params.appId)
.res_class = strdup("looking-glass-client")
};
XSetClassHint(x11.display, x11.window, &hint);
free(hint.res_name);
@ -374,16 +350,6 @@ static bool x11Init(const LG_DSInitParams params)
// check for Extended Window Manager Hints support
x11CheckEWMHSupport();
if (!x11.wm->init())
{
x11.wm = &X11WM_Default;
if (!x11.wm->init())
{
DEBUG_ERROR("Failed to initialize the X11 window manager subsystem");
goto fail_window;
}
}
if (x11atoms._NET_WM_PID)
{
pid_t pid = getpid();
@ -491,7 +457,7 @@ static bool x11Init(const LG_DSInitParams params)
if (XIQueryVersion(x11.display, &major, &minor) != Success)
{
DEBUG_ERROR("Failed to query the XInput version");
goto fail_window;
return false;
}
DEBUG_INFO("X11 XInput %d.%d in use", major, minor);
@ -630,17 +596,29 @@ static bool x11Init(const LG_DSInitParams params)
XFreePixmap(x11.display, temp);
}
XcursorImages * images;
if (params.largeCursorDot)
images = x11cursor_load(b_no_input_cursor_32_xcur,
b_no_input_cursor_32_xcur_size);
else
images = x11cursor_load(b_no_input_cursor_16_xcur,
b_no_input_cursor_16_xcur_size);
/* create the square cursor */
{
static char data[] = { 0x07, 0x05, 0x07 };
static char mask[] = { 0xff, 0xff, 0xff };
x11.cursors[LG_POINTER_SQUARE] =
XcursorImagesLoadCursor(x11.display, images);
XcursorImagesDestroy(images);
Colormap cmap = DefaultColormap(x11.display, DefaultScreen(x11.display));
XColor colors[2] =
{
{ .pixel = BlackPixelOfScreen(DefaultScreenOfDisplay(x11.display)) },
{ .pixel = WhitePixelOfScreen(DefaultScreenOfDisplay(x11.display)) }
};
XQueryColors(x11.display, cmap, colors, 2);
Pixmap img = XCreateBitmapFromData(x11.display, x11.window, data, 3, 3);
Pixmap msk = XCreateBitmapFromData(x11.display, x11.window, mask, 3, 3);
x11.cursors[LG_POINTER_SQUARE] = XCreatePixmapCursor(x11.display, img, msk,
&colors[0], &colors[1], 1, 1);
XFreePixmap(x11.display, img);
XFreePixmap(x11.display, msk);
}
/* initialize the rest of the cursors */
const char * cursorLookup[LG_POINTER_COUNT] = {
@ -699,7 +677,7 @@ static bool x11Init(const LG_DSInitParams params)
XMoveWindow(x11.display, x11.window, params.x, params.y);
if (params.fullscreen)
x11.doFullscreenOnExpose = true;
x11SetFullscreen(true);
XSetLocaleModifiers(""); // Load XMODIFIERS
x11.xim = XOpenIM(x11.display, 0, 0, 0);
@ -784,7 +762,6 @@ static void x11Free(void)
if (x11.keysyms)
XFree(x11.keysyms);
x11.wm->deinit();
XCloseDisplay(x11.display);
}
@ -897,7 +874,7 @@ static int x11EventThread(void * unused)
XNextEvent(x11.display, &xe);
// call the clipboard handling code
if (x11CBEventThread(&xe))
if (x11CBEventThread(xe))
continue;
switch(xe.type)
@ -946,11 +923,6 @@ static int x11EventThread(void * unused)
{
atomic_store(&x11.lastWMEvent, microtime());
x11.invalidateAll = true;
if (x11.doFullscreenOnExpose)
{
x11SetFullscreen(true);
x11.doFullscreenOnExpose = false;
}
break;
}
@ -1087,9 +1059,8 @@ static void setFocus(bool focused, double x, double y)
app_handleFocusEvent(focused);
}
static int x11GetCharCode(int detail)
static int getCharcode(int detail)
{
detail += x11.minKeycode;
if (detail < x11.minKeycode || detail > x11.maxKeycode)
return 0;
@ -1181,46 +1152,6 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
app_updateCursorPos(xie->event_x, xie->event_y);
app_handleEnterEvent(false);
x11.entered = false;
/**
* Because there is a race with the pointer ungrab the enter event for the
* next window is sometimes sent with the mode NotifyUngrab, unfortunatly
* some window managers such as i3 will ignore these which breaks focus
* follows mouse mode. To correct this we generate and send a normal
* EnterNotify event.
*/
int root_x, root_y, win_x, win_y;
Window root_win, child_win;
unsigned int mask;
XQueryPointer(x11.display, DefaultRootWindow(x11.display), &root_win,
&child_win, &root_x, &root_y, &win_x, &win_y, &mask);
int target_x, target_y;
Window target_root;
XTranslateCoordinates(x11.display, DefaultRootWindow(x11.display),
child_win, root_x, root_y, &target_x, &target_y, &target_root);
XEvent event;
memset(&event, 0, sizeof(event));
event.type = EnterNotify;
event.xcrossing.serial = 0;
event.xcrossing.send_event = True;
event.xcrossing.display = x11.display;
event.xcrossing.window = child_win;
event.xcrossing.root = root_win;
event.xcrossing.subwindow = child_win;
event.xcrossing.time = CurrentTime;
event.xcrossing.mode = NotifyNormal;
event.xcrossing.detail = NotifyNonlinear;
event.xcrossing.same_screen = True;
event.xcrossing.focus = False;
event.xcrossing.x = target_x;
event.xcrossing.y = target_y;
event.xcrossing.x_root = root_x;
event.xcrossing.y_root = root_y;
XSendEvent(x11.display, child_win, True, EnterWindowMask, &event);
XFlush(x11.display);
return;
}
@ -1230,7 +1161,8 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIDeviceEvent *device = cookie->data;
app_handleKeyPress(device->detail - x11.minKeycode);
app_handleKeyPress(device->detail - x11.minKeycode,
getCharcode(device->detail));
if (!x11.xic || !app_isOverlayMode())
return;
@ -1280,7 +1212,8 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIDeviceEvent *device = cookie->data;
app_handleKeyRelease(device->detail - x11.minKeycode);
app_handleKeyRelease(device->detail - x11.minKeycode,
getCharcode(device->detail));
if (!x11.xic || !app_isOverlayMode())
return;
@ -1309,7 +1242,8 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIRawEvent *raw = cookie->data;
app_handleKeyPress(raw->detail - x11.minKeycode);
app_handleKeyPress(raw->detail - x11.minKeycode,
getCharcode(raw->detail));
return;
}
@ -1319,7 +1253,8 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIRawEvent *raw = cookie->data;
app_handleKeyRelease(raw->detail - x11.minKeycode);
app_handleKeyRelease(raw->detail - x11.minKeycode,
getCharcode(raw->detail));
return;
}
@ -1970,7 +1905,24 @@ static void x11SetFullscreen(bool fs)
if (x11.fullscreen == fs)
return;
x11.wm->setFullscreen(fs);
XEvent e =
{
.xclient = {
.type = ClientMessage,
.send_event = true,
.message_type = x11atoms._NET_WM_STATE,
.format = 32,
.window = x11.window,
.data.l = {
fs ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
x11atoms._NET_WM_STATE_FULLSCREEN,
0
}
}
};
XSendEvent(x11.display, DefaultRootWindow(x11.display), False,
SubstructureNotifyMask | SubstructureRedirectMask, &e);
}
static bool x11GetFullscreen(void)
@ -2014,7 +1966,6 @@ struct LG_DisplayServerOps LGDS_X11 =
.ungrabPointer = x11UngrabPointer,
.capturePointer = x11CapturePointer,
.uncapturePointer = x11UncapturePointer,
.getCharCode = x11GetCharCode,
.grabKeyboard = x11GrabKeyboard,
.ungrabKeyboard = x11UngrabKeyboard,
.warpPointer = x11WarpPointer,

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -33,7 +33,6 @@
#include "interface/displayserver.h"
#include "common/thread.h"
#include "common/types.h"
#include "wm.h"
enum Modifiers
{
@ -49,16 +48,11 @@ enum Modifiers
#define MOD_COUNT (MOD_SUPER_RIGHT + 1)
#define _NET_WM_STATE_REMOVE 0
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_TOGGLE 2
struct X11DSState
{
Display * display;
Window window;
XVisualInfo * visual;
X11WM * wm;
int minKeycode, maxKeycode;
int symsPerKeycode;
@ -71,7 +65,6 @@ struct X11DSState
_Atomic(uint64_t) lastWMEvent;
bool invalidateAll;
bool doFullscreenOnExpose;
int xpresentOp;
bool jitRender;

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -59,8 +59,8 @@ void app_handleButtonPress(int button);
void app_handleButtonRelease(int button);
void app_handleWheelMotion(double motion);
void app_handleKeyboardTyped(const char * typed);
void app_handleKeyPress(int scancode);
void app_handleKeyRelease(int scancode);
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);

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,51 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2025 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 <stdbool.h>
/**
* initialize configuration options
*/
void evdev_earlyInit(void);
/**
* start the evdev layer
*/
bool evdev_start(void);
/**
* stop the evdev layer
*/
void evdev_stop(void);
/**
* grab the keyboard for exclusive access
*/
void evdev_grabKeyboard(void);
/**
* ungrab the keyboard
*/
void evdev_ungrabKeyboard(void);
/**
* returns true if input should only be processed by evdev
*/
bool evdev_isExclusive(void);

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -84,14 +84,12 @@ LG_DSPointer;
typedef struct LG_DSInitParams
{
const char * title;
const char * appId;
int x, y, w, h;
bool center;
bool fullscreen;
bool resizable;
bool borderless;
bool maximize;
bool largeCursorDot;
// if true the renderer requires an OpenGL context
bool opengl;
@ -183,9 +181,6 @@ struct LG_DisplayServerOps
void (*capturePointer)(void);
void (*uncapturePointer)(void);
/* get the character code for the provided scancode */
int (*getCharCode)(int sc);
/* exiting = true if the warp is to leave the window */
void (*warpPointer)(int x, int y, bool exiting);
@ -256,7 +251,6 @@ struct LG_DisplayServerOps
DEBUG_ASSERT((x)->ungrabPointer ); \
DEBUG_ASSERT((x)->capturePointer ); \
DEBUG_ASSERT((x)->uncapturePointer ); \
DEBUG_ASSERT((x)->getCharCode ); \
DEBUG_ASSERT((x)->warpPointer ); \
DEBUG_ASSERT((x)->realignPointer ); \
DEBUG_ASSERT((x)->isValidPointerPos ); \

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -72,12 +72,8 @@ LG_RendererRotate;
typedef struct LG_RendererFormat
{
FrameType type; // frame type
bool hdr; // if the frame is HDR or not
bool hdrPQ; // if the HDR content is PQ mapped
unsigned int screenWidth; // actual width of the host
unsigned int screenHeight; // actual height of the host
unsigned int dataWidth; // the width of the packed data
unsigned int dataHeight; // the height of the packed data
unsigned int frameWidth; // width of frame transmitted
unsigned int frameHeight; // height of frame transmitted
unsigned int stride; // scanline width (zero if compresed)

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.0)
project(renderers LANGUAGES C)
set(RENDERER_H "${CMAKE_BINARY_DIR}/include/dynamic/renderers.h")

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.0)
project(renderer_EGL LANGUAGES C CXX)
find_package(PkgConfig)
@ -56,7 +56,6 @@ build_shaders(
shader/damage.vert
shader/damage.frag
shader/basic.vert
shader/convert_24bit.frag
shader/ffx_cas.frag
shader/ffx_fsr1_easu.frag
shader/ffx_fsr1_rcas.frag
@ -88,7 +87,6 @@ add_library(renderer_EGL STATIC
postprocess.c
ffx.c
filter.c
filter_24bit.c
filter_ffx_cas.c
filter_ffx_fsr1.c
filter_downscale.c

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -100,7 +100,7 @@ static bool cursorTexInit(struct CursorTex * t,
}
if (!egl_shaderCompile(t->shader,
vertex_code, vertex_size, fragment_code, fragment_size, false, NULL))
vertex_code, vertex_size, fragment_code, fragment_size))
{
DEBUG_ERROR("Failed to compile the cursor shader");
return false;
@ -277,7 +277,7 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
}
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
cursor->width, cursor->height, cursor->width, sizeof(xor[0]));
cursor->width, cursor->height, sizeof(xor[0]));
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
}
// fall through
@ -285,7 +285,7 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
case LG_CURSOR_COLOR:
{
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
cursor->width, cursor->height, cursor->width, cursor->stride);
cursor->width, cursor->height, cursor->stride);
egl_textureUpdate(cursor->norm.texture, data, true);
break;
}
@ -311,9 +311,9 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
}
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
cursor->width, cursor->height, cursor->width, sizeof(and[0]));
cursor->width, cursor->height, sizeof(and[0]));
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
cursor->width, cursor->height, cursor->width, sizeof(xor[0]));
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);
break;

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -76,8 +76,7 @@ bool egl_damageInit(EGL_Damage ** damage)
if (!egl_shaderCompile((*damage)->shader,
b_shader_damage_vert, b_shader_damage_vert_size,
b_shader_damage_frag, b_shader_damage_frag_size,
false, NULL))
b_shader_damage_frag, b_shader_damage_frag_size))
{
DEBUG_ERROR("Failed to compile the damage shader");
return false;

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -46,14 +46,9 @@ struct DesktopShader
EGL_Shader * shader;
GLint uTransform;
GLint uDesktopSize;
GLint uSamplerType;
GLint uScaleAlgo;
GLint uNVGain;
GLint uCBMode;
GLint uIsHDR;
GLint uMapHDRtoSDR;
GLint uMapHDRGain;
GLint uMapHDRPQ;
};
struct EGL_Desktop
@ -62,14 +57,12 @@ struct EGL_Desktop
EGLDisplay * display;
EGL_Texture * texture;
struct DesktopShader dmaShader, shader;
struct DesktopShader shader;
EGL_DesktopRects * mesh;
CountedBuffer * matrix;
// internals
int width, height;
bool hdr;
bool hdrPQ;
LG_RendererRotate rotate;
bool useSpice;
@ -89,11 +82,6 @@ struct EGL_Desktop
bool useDMA;
LG_RendererFormat format;
// map HDR content to SDR
bool mapHDRtoSDR;
int peakLuminance;
int maxCLL;
EGL_PostProcess * pp;
_Atomic(bool) processFrame;
};
@ -104,8 +92,7 @@ void toggleNV(int key, void * opaque);
static bool egl_initDesktopShader(
struct DesktopShader * shader,
const char * vertex_code , size_t vertex_size,
const char * fragment_code, size_t fragment_size,
bool useDMA
const char * fragment_code, size_t fragment_size
)
{
if (!egl_shaderInit(&shader->shader))
@ -113,21 +100,16 @@ static bool egl_initDesktopShader(
if (!egl_shaderCompile(shader->shader,
vertex_code , vertex_size,
fragment_code, fragment_size,
useDMA, NULL))
fragment_code, fragment_size))
{
return false;
}
shader->uDesktopSize = egl_shaderGetUniform(shader->shader, "desktopSize" );
shader->uTransform = egl_shaderGetUniform(shader->shader, "transform" );
shader->uDesktopSize = egl_shaderGetUniform(shader->shader, "desktopSize");
shader->uScaleAlgo = egl_shaderGetUniform(shader->shader, "scaleAlgo" );
shader->uNVGain = egl_shaderGetUniform(shader->shader, "nvGain" );
shader->uCBMode = egl_shaderGetUniform(shader->shader, "cbMode" );
shader->uIsHDR = egl_shaderGetUniform(shader->shader, "isHDR" );
shader->uMapHDRtoSDR = egl_shaderGetUniform(shader->shader, "mapHDRtoSDR" );
shader->uMapHDRGain = egl_shaderGetUniform(shader->shader, "mapHDRGain" );
shader->uMapHDRPQ = egl_shaderGetUniform(shader->shader, "mapHDRPQ" );
return true;
}
@ -153,6 +135,15 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
return false;
}
if (!egl_initDesktopShader(
&desktop->shader,
b_shader_desktop_vert , b_shader_desktop_vert_size,
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size))
{
DEBUG_ERROR("Failed to initialize the generic desktop shader");
return false;
}
if (!egl_desktopRectsInit(&desktop->mesh, maxRects))
{
DEBUG_ERROR("Failed to initialize the desktop mesh");
@ -166,27 +157,6 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
return false;
}
if (!egl_initDesktopShader(
&desktop->shader,
b_shader_desktop_vert , b_shader_desktop_vert_size,
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size,
false))
{
DEBUG_ERROR("Failed to initialize the desktop shader");
return false;
}
if (useDMA)
if (!egl_initDesktopShader(
&desktop->dmaShader,
b_shader_desktop_vert , b_shader_desktop_vert_size,
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size,
true))
{
DEBUG_ERROR("Failed to initialize the desktop DMA shader");
return false;
}
app_registerKeybind(0, 'N', toggleNV, desktop,
"Toggle night vision mode");
@ -196,19 +166,12 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
desktop->scaleAlgo = option_get_int("egl", "scale" );
desktop->useDMA = useDMA;
desktop->mapHDRtoSDR = option_get_bool("egl", "mapHDRtoSDR" );
desktop->peakLuminance = option_get_int ("egl", "peakLuminance");
desktop->maxCLL = option_get_int ("egl", "maxCLL" );
if (!egl_postProcessInit(&desktop->pp))
{
DEBUG_ERROR("Failed to initialize the post process manager");
return false;
}
// this MUST be first
egl_postProcessAdd(desktop->pp, &egl_filter24bitOps);
egl_postProcessAdd(desktop->pp, &egl_filterDownscaleOps);
egl_postProcessAdd(desktop->pp, &egl_filterFFXCASOps );
egl_postProcessAdd(desktop->pp, &egl_filterFFXFSR1Ops );
@ -245,7 +208,6 @@ void egl_desktopFree(EGL_Desktop ** desktop)
egl_textureFree (&(*desktop)->texture );
egl_textureFree (&(*desktop)->spiceTexture );
egl_shaderFree (&(*desktop)->shader.shader);
egl_shaderFree (&(*desktop)->dmaShader.shader);
egl_desktopRectsFree(&(*desktop)->mesh );
countedBufferRelease(&(*desktop)->matrix );
@ -293,28 +255,6 @@ void egl_desktopConfigUI(EGL_Desktop * desktop)
}
igSliderInt("##nvgain", &desktop->nvGain, 0, desktop->nvMax, format, 0);
igPopItemWidth();
bool mapHDRtoSDR = desktop->mapHDRtoSDR;
int peakLuminance = desktop->peakLuminance;
int maxCLL = desktop->maxCLL;
igSeparator();
igCheckbox("Map HDR content to SDR", &mapHDRtoSDR);
igSliderInt("Peak Luminance", &peakLuminance, 1, 10000,
"%d nits",
ImGuiInputTextFlags_CharsDecimal);
igSliderInt("Max content light level", &maxCLL, 1, 10000,
"%d nits", ImGuiInputTextFlags_CharsDecimal);
if (mapHDRtoSDR != desktop->mapHDRtoSDR ||
peakLuminance != desktop->peakLuminance ||
maxCLL != desktop->maxCLL)
{
desktop->mapHDRtoSDR = mapHDRtoSDR;
desktop->peakLuminance = max(1, peakLuminance);
desktop->maxCLL = max(1, maxCLL);
app_invalidateWindow(true);
}
}
bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
@ -340,14 +280,6 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
pixFmt = EGL_PF_RGBA16F;
break;
case FRAME_TYPE_BGR_32:
pixFmt = EGL_PF_BGR_32;
break;
case FRAME_TYPE_RGB_24:
pixFmt = EGL_PF_RGB_24;
break;
default:
DEBUG_ERROR("Unsupported frame format");
return false;
@ -355,16 +287,13 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
desktop->width = format.frameWidth;
desktop->height = format.frameHeight;
desktop->hdr = format.hdr;
desktop->hdrPQ = format.hdrPQ;
if (!egl_textureSetup(
desktop->texture,
pixFmt,
desktop->format.dataWidth,
desktop->format.dataHeight,
desktop->format.stride,
desktop->format.pitch
format.frameWidth,
format.frameHeight,
format.pitch
))
{
DEBUG_ERROR("Failed to setup the desktop texture");
@ -377,9 +306,9 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
const FrameDamageRect * damageRects, int damageRectsCount)
{
if (likely(desktop->useDMA && dmaFd >= 0))
if (desktop->useDMA && dmaFd >= 0)
{
if (likely(egl_textureUpdateFromDMA(desktop->texture, frame, dmaFd)))
if (egl_textureUpdateFromDMA(desktop->texture, frame, dmaFd))
{
atomic_store(&desktop->processFrame, true);
return true;
@ -415,8 +344,8 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dma
return false;
}
if (likely(egl_textureUpdateFromFrame(desktop->texture, frame,
damageRects, damageRectsCount)))
if (egl_textureUpdateFromFrame(desktop->texture, frame,
damageRects, damageRectsCount))
{
atomic_store(&desktop->processFrame, true);
return true;
@ -437,28 +366,25 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
{
EGL_Texture * tex;
int width, height;
bool dma;
if (unlikely(desktop->useSpice))
if (desktop->useSpice)
{
tex = desktop->spiceTexture;
width = desktop->spiceWidth;
height = desktop->spiceHeight;
dma = false;
}
else
{
tex = desktop->texture;
width = desktop->width;
height = desktop->height;
dma = desktop->useDMA;
}
if (unlikely(outputWidth == 0 || outputHeight == 0))
if (outputWidth == 0 && outputHeight == 0)
DEBUG_FATAL("outputWidth || outputHeight == 0");
enum EGL_TexStatus status;
if (unlikely((status = egl_textureProcess(tex)) != EGL_TEX_STATUS_OK))
if ((status = egl_textureProcess(tex)) != EGL_TEX_STATUS_OK)
{
if (status != EGL_TEX_STATUS_NOTREADY)
DEBUG_ERROR("Failed to process the desktop texture");
@ -473,17 +399,18 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
if (atomic_exchange(&desktop->processFrame, false) ||
egl_postProcessConfigModified(desktop->pp))
egl_postProcessRun(desktop->pp, tex, desktop->mesh,
width, height, outputWidth, outputHeight, dma);
width, height, outputWidth, outputHeight);
unsigned int finalSizeX, finalSizeY;
EGL_Texture * texture = egl_postProcessGetOutput(desktop->pp,
GLuint texture = egl_postProcessGetOutput(desktop->pp,
&finalSizeX, &finalSizeY);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
egl_resetViewport(desktop->egl);
glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, tex->sampler);
if (finalSizeX > width || finalSizeY > height)
scaleType = EGL_DESKTOP_DOWNSCALE;
@ -508,13 +435,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
scaleAlgo = desktop->scaleAlgo;
}
const struct DesktopShader * shader =
desktop->useDMA && texture == desktop->texture ?
&desktop->dmaShader : &desktop->shader;
const float mapHDRGain =
(float)desktop->maxCLL / desktop->peakLuminance;
const struct DesktopShader * shader = &desktop->shader;
EGL_Uniform uniforms[] =
{
{
@ -542,26 +463,6 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
.type = EGL_UNIFORM_TYPE_1I,
.location = shader->uCBMode,
.f = { desktop->cbMode }
},
{
.type = EGL_UNIFORM_TYPE_1I,
.location = shader->uIsHDR,
.i = { desktop->hdr }
},
{
.type = EGL_UNIFORM_TYPE_1I,
.location = shader->uMapHDRtoSDR,
.i = { desktop->mapHDRtoSDR }
},
{
.type = EGL_UNIFORM_TYPE_1F,
.location = shader->uMapHDRGain,
.f = { mapHDRGain }
},
{
.type = EGL_UNIFORM_TYPE_1I,
.location = shader->uMapHDRPQ,
.f = { desktop->hdrPQ }
}
};
@ -587,7 +488,6 @@ void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height)
EGL_PF_BGRA,
width,
height,
width,
width * 4
))
{
@ -609,9 +509,9 @@ void egl_desktopSpiceDrawFill(EGL_Desktop * desktop, int x, int y, int width,
for(int x = 0; x < width; ++x)
line[x] = color;
for(int dy = 0; dy < height; ++dy)
for(; y < height; ++y)
egl_textureUpdateRect(desktop->spiceTexture,
x, y + dy, width, 1, width, sizeof(line), (uint8_t *)line, false);
x, y, width, 1, sizeof(line), (uint8_t *)line, false);
atomic_store(&desktop->processFrame, true);
}
@ -620,12 +520,11 @@ 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, width, stride, data, topDown);
x, y, width, height, stride, data, topDown);
atomic_store(&desktop->processFrame, true);
}
void egl_desktopSpiceShow(EGL_Desktop * desktop, bool show)
{
desktop->useSpice = show;
atomic_store(&desktop->processFrame, true);
}

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -296,7 +296,7 @@ bool egl_screenToDesktop(struct FrameDamageRect * output, const double matrix[6]
void egl_desktopRectsRender(EGL_DesktopRects * rects)
{
if (unlikely(!rects->count))
if (!rects->count)
return;
glBindVertexArray(rects->vao);

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -20,7 +20,6 @@
#include "interface/renderer.h"
#include "common/util.h"
#include "common/debug.h"
#include "common/KVMFR.h"
#include "common/option.h"
@ -203,27 +202,6 @@ static struct Option egl_options[] =
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "egl",
.name = "mapHDRtoSDR",
.description = "Map HDR content to the SDR color space",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "egl",
.name = "peakLuminance",
.description = "The peak luminance level in nits for HDR to SDR mapping",
.type = OPTION_TYPE_INT,
.value.x_int = 250,
},
{
.module = "egl",
.name = "maxCLL",
.description = "Maximum content light level in nits for HDR to SDR mapping",
.type = OPTION_TYPE_INT,
.value.x_int = 10000,
},
{0}
};
@ -581,7 +559,7 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
this->formatValid = true;
/* this event runs in a second thread so we need to init it here */
if (unlikely(!this->frameContext))
if (!this->frameContext)
{
static EGLint attrs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
@ -601,7 +579,7 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
}
}
if (likely(this->scalePointer))
if (this->scalePointer)
{
float scale = max(1.0f, (float)format.screenWidth / this->width);
egl_cursorSetScale(this->cursor, scale);
@ -624,8 +602,7 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int d
struct Inst * this = UPCAST(struct Inst, renderer);
uint64_t start = nanotime();
if (unlikely(!egl_desktopUpdate(
this->desktop, frame, dmaFd, damageRects, damageRectsCount)))
if (!egl_desktopUpdate(this->desktop, frame, dmaFd, damageRects, damageRectsCount))
{
DEBUG_INFO("Failed to to update the desktop");
return false;
@ -634,17 +611,12 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int d
INTERLOCKED_SECTION(this->desktopDamageLock, {
struct DesktopDamage * damage = this->desktopDamage + this->desktopDamageIdx;
if (unlikely(
damage->count == -1 ||
damageRectsCount == 0 ||
damage->count + damageRectsCount >= KVMFR_MAX_DAMAGE_RECTS))
{
if (damage->count == -1 || damageRectsCount == 0 ||
damage->count + damageRectsCount >= KVMFR_MAX_DAMAGE_RECTS)
damage->count = -1;
}
else
{
memcpy(damage->rects + damage->count, damageRects,
damageRectsCount * sizeof(FrameDamageRect));
memcpy(damage->rects + damage->count, damageRects, damageRectsCount * sizeof(FrameDamageRect));
damage->count += damageRectsCount;
}
});
@ -776,7 +748,7 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
EGLint attr[] =
{
EGL_BUFFER_SIZE , 30,
EGL_BUFFER_SIZE , 24,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SAMPLE_BUFFERS , maxSamples > 0 ? 1 : 0,
EGL_SAMPLES , maxSamples,
@ -1063,14 +1035,14 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
accumulated->count = 0;
INTERLOCKED_SECTION(this->desktopDamageLock, {
if (likely(!renderAll))
if (!renderAll)
{
for (int i = 0; i < bufferAge; ++i)
{
struct DesktopDamage * damage = this->desktopDamage +
IDX_AGO(this->desktopDamageIdx, i, DESKTOP_DAMAGE_COUNT);
if (unlikely(damage->count < 0))
if (damage->count < 0)
{
renderAll = true;
break;
@ -1094,7 +1066,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
this->desktopDamage[this->desktopDamageIdx].count = 0;
});
if (likely(!renderAll))
if (!renderAll)
{
double matrix[6];
egl_screenToDesktopMatrix(matrix,
@ -1108,7 +1080,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
int count = this->overlayHistoryCount[idx];
struct Rect * damage = this->overlayHistory[idx];
if (unlikely(count < 0))
if (count < 0)
{
renderAll = true;
break;
@ -1121,12 +1093,11 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
);
}
accumulated->count = rectsMergeOverlapping(accumulated->rects,
accumulated->count);
accumulated->count = rectsMergeOverlapping(accumulated->rects, accumulated->count);
}
++this->overlayHistoryIdx;
if (likely(this->destRect.w > 0 && this->destRect.h > 0))
if (this->destRect.w > 0 && this->destRect.h > 0)
{
if (egl_desktopRender(this->desktop,
this->destRect.w, this->destRect.h,
@ -1144,17 +1115,20 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
renderLetterBox(this);
hasOverlay |=
egl_damageRender(this->damage, rotate, newFrame ? desktopDamage : NULL) |
invalidateWindow;
hasOverlay |= egl_damageRender(this->damage, rotate, newFrame ? desktopDamage : NULL);
hasOverlay |= invalidateWindow;
struct Rect damage[KVMFR_MAX_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2];
int damageIdx = app_renderOverlay(damage, MAX_OVERLAY_RECTS);
if (unlikely(damageIdx != 0))
{
if (damageIdx == -1)
hasOverlay = true;
switch (damageIdx)
{
case 0: // no overlay
break;
case -1: // full damage
hasOverlay = true;
// fallthrough
default:
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData());
@ -1162,21 +1136,20 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
damage[i].y = this->height - damage[i].y - damage[i].h;
}
if (likely(damageIdx >= 0 && cursorState.visible))
if (damageIdx >= 0 && cursorState.visible)
damage[damageIdx++] = cursorState.rect;
int overlayHistoryIdx = this->overlayHistoryIdx % DESKTOP_DAMAGE_COUNT;
if (unlikely(hasOverlay))
if (hasOverlay)
this->overlayHistoryCount[overlayHistoryIdx] = -1;
else
{
if (unlikely(damageIdx > 0))
memcpy(this->overlayHistory[overlayHistoryIdx],
damage, damageIdx * sizeof(struct Rect));
if (damageIdx > 0)
memcpy(this->overlayHistory[overlayHistoryIdx], damage, damageIdx * sizeof(struct Rect));
this->overlayHistoryCount[overlayHistoryIdx] = damageIdx;
}
if (unlikely(!hasOverlay && !this->hadOverlay))
if (!hasOverlay && !this->hadOverlay)
{
if (this->cursorLast.visible)
damage[damageIdx++] = this->cursorLast.rect;
@ -1203,9 +1176,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
this->cursorLast = cursorState;
preSwap(udata);
app_eglSwapBuffers(this->display, this->surface, damage,
this->noSwapDamage ? 0 : damageIdx);
app_eglSwapBuffers(this->display, this->surface, damage, this->noSwapDamage ? 0 : damageIdx);
return true;
}

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -37,10 +37,7 @@ typedef enum EGL_PixelFormat
EGL_PF_RGBA,
EGL_PF_BGRA,
EGL_PF_RGBA10,
EGL_PF_RGBA16F,
EGL_PF_BGR_32,
EGL_PF_RGB_24,
EGL_PF_RGB_24_32
EGL_PF_RGBA16F
}
EGL_PixelFormat;
@ -63,17 +60,13 @@ typedef struct EGL_TexSetup
/* the height of the texture in pixels */
size_t height;
/* the row length of the texture in pixels */
/* the stide of the texture in bytes */
size_t stride;
/* the row length of the texture in bytes */
size_t pitch;
}
EGL_TexSetup;
typedef enum EGL_FilterType
{
EGL_FILTER_TYPE_INTERNAL,
EGL_FILTER_TYPE_EFFECT,
EGL_FILTER_TYPE_UPSCALE,
EGL_FILTER_TYPE_DOWNSCALE

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -68,21 +68,18 @@ typedef struct EGL_FilterOps
/* reads filter state from options */
void (*loadState)(EGL_Filter * filter);
/* set the input format of the filter
* useDMA will be true if the texture provided needs to use samplerExternalOES
*/
/* set the input format of the filter */
bool (*setup)(EGL_Filter * filter, enum EGL_PixelFormat pixFmt,
unsigned int width, unsigned int height,
unsigned int desktopWidth, unsigned int desktopHeight, bool useDMA);
unsigned int width, unsigned int height);
/* set the output resolution hint for the filter
* this is optional and only a hint */
void (*setOutputResHint)(EGL_Filter * filter,
unsigned int x, unsigned int y);
/* returns the output resolution and pixel format of the filter */
/* returns the output resolution of the filter */
void (*getOutputRes)(EGL_Filter * filter,
unsigned int *x, unsigned int *y, enum EGL_PixelFormat *pixFmt);
unsigned int *x, unsigned int *y);
/* prepare the shader for use
* A filter can return false to bypass it */
@ -90,8 +87,8 @@ typedef struct EGL_FilterOps
/* runs the filter on the provided texture
* returns the processed texture as the output */
EGL_Texture * (*run)(EGL_Filter * filter, EGL_FilterRects * rects,
EGL_Texture * texture);
GLuint (*run)(EGL_Filter * filter, EGL_FilterRects * rects,
GLuint texture);
/* called when the filter output is no loger needed so it can release memory
* this is optional */
@ -105,12 +102,6 @@ typedef struct EGL_Filter
}
EGL_Filter;
static inline void egl_filterEarlyInit(const EGL_FilterOps * ops)
{
if (ops->earlyInit)
ops->earlyInit();
}
static inline bool egl_filterInit(const EGL_FilterOps * ops, EGL_Filter ** filter)
{
if (!ops->init(filter))
@ -128,30 +119,23 @@ static inline void egl_filterFree(EGL_Filter ** filter)
static inline bool egl_filterImguiConfig(EGL_Filter * filter)
{
if (filter->ops.imguiConfig)
return filter->ops.imguiConfig(filter);
return false;
}
static inline void egl_filterSaveState(EGL_Filter * filter)
{
if (filter->ops.saveState)
filter->ops.saveState(filter);
}
static inline void egl_filterLoadState(EGL_Filter * filter)
{
if (filter->ops.loadState)
filter->ops.loadState(filter);
}
static inline bool egl_filterSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
unsigned int desktopWidth, unsigned int desktopHeight,
bool useDMA)
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
return filter->ops.setup(filter, pixFmt, width, height,
desktopWidth, desktopHeight, useDMA);
return filter->ops.setup(filter, pixFmt, width, height);
}
static inline void egl_filterSetOutputResHint(EGL_Filter * filter,
@ -162,9 +146,9 @@ static inline void egl_filterSetOutputResHint(EGL_Filter * filter,
}
static inline void egl_filterGetOutputRes(EGL_Filter * filter,
unsigned int *x, unsigned int *y, enum EGL_PixelFormat *pixFmt)
unsigned int *x, unsigned int *y)
{
return filter->ops.getOutputRes(filter, x, y, pixFmt);
return filter->ops.getOutputRes(filter, x, y);
}
static inline bool egl_filterPrepare(EGL_Filter * filter)
@ -172,8 +156,8 @@ static inline bool egl_filterPrepare(EGL_Filter * filter)
return filter->ops.prepare(filter);
}
static inline EGL_Texture * egl_filterRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
static inline GLuint egl_filterRun(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
{
return filter->ops.run(filter, rects, texture);
}

View File

@ -1,221 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2025 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 "filter.h"
#include "framebuffer.h"
#include <math.h>
#include "common/array.h"
#include "common/debug.h"
#include "common/option.h"
#include "cimgui.h"
#include "basic.vert.h"
#include "convert_24bit.frag.h"
typedef struct EGL_Filter24bit
{
EGL_Filter base;
bool enable;
EGL_PixelFormat format;
int useDMA;
unsigned int width, height;
unsigned int desktopWidth, desktopHeight;
bool prepared;
EGL_Uniform uOutputSize;
EGL_Shader * shader;
EGL_Framebuffer * fb;
GLuint sampler[2];
}
EGL_Filter24bit;
static bool egl_filter24bitInit(EGL_Filter ** filter)
{
EGL_Filter24bit * this = calloc(1, sizeof(*this));
if (!this)
{
DEBUG_ERROR("Failed to allocate ram");
return false;
}
this->useDMA = -1;
if (!egl_shaderInit(&this->shader))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_framebufferInit(&this->fb))
{
DEBUG_ERROR("Failed to initialize the framebuffer");
goto error_shader;
}
glGenSamplers(ARRAY_LENGTH(this->sampler), this->sampler);
glSamplerParameteri(this->sampler[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glSamplerParameteri(this->sampler[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->sampler[1], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(this->sampler[1], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
*filter = &this->base;
return true;
error_shader:
egl_shaderFree(&this->shader);
error_this:
free(this);
return false;
}
static void egl_filter24bitFree(EGL_Filter * filter)
{
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
egl_shaderFree(&this->shader);
egl_framebufferFree(&this->fb);
glDeleteSamplers(ARRAY_LENGTH(this->sampler), this->sampler);
free(this);
}
static bool egl_filter24bitSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
unsigned int desktopWidth, unsigned int desktopHeight,
bool useDMA)
{
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
if (pixFmt != EGL_PF_BGR_32 && pixFmt != EGL_PF_RGB_24_32)
return false;
if (this->useDMA != useDMA || this->format != pixFmt)
{
EGL_ShaderDefine defines[] =
{
{"OUTPUT", pixFmt == EGL_PF_BGR_32 ? "fragColor.bgra" : "fragColor.rgba" },
{0}
};
if (!egl_shaderCompile(this->shader,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_convert_24bit_frag, b_shader_convert_24bit_frag_size,
useDMA, defines)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
this->uOutputSize.type = EGL_UNIFORM_TYPE_2F;
this->uOutputSize.location =
egl_shaderGetUniform(this->shader, "outputSize");
this->useDMA = useDMA;
this->prepared = false;
}
if (this->prepared &&
this->width == width &&
this->height == height &&
this->desktopWidth == desktopWidth &&
this->desktopHeight == desktopHeight)
return true;
if (!egl_framebufferSetup(this->fb, EGL_PF_BGRA, desktopWidth, desktopHeight))
return false;
this->format = pixFmt;
this->width = width;
this->height = height;
this->desktopWidth = desktopWidth;
this->desktopHeight = desktopHeight;
this->prepared = false;
return true;
}
static void egl_filter24bitGetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height, enum EGL_PixelFormat *pixFmt)
{
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
*width = this->desktopWidth;
*height = this->desktopHeight;
*pixFmt = EGL_PF_BGRA;
}
static bool egl_filter24bitPrepare(EGL_Filter * filter)
{
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
if (this->prepared)
return true;
this->uOutputSize.f[0] = this->desktopWidth;
this->uOutputSize.f[1] = this->desktopHeight;
egl_shaderSetUniforms(this->shader, &this->uOutputSize, 1);
this->prepared = true;
return true;
}
static EGL_Texture * egl_filter24bitRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
{
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
egl_framebufferBind(this->fb);
glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture);
glBindSampler(0, this->sampler[0]);
egl_shaderUse(this->shader);
egl_filterRectsRender(this->shader, rects);
return egl_framebufferGetTexture(this->fb);
}
EGL_FilterOps egl_filter24bitOps =
{
.id = "24bit",
.name = "24bit",
.type = EGL_FILTER_TYPE_INTERNAL,
.earlyInit = NULL,
.init = egl_filter24bitInit,
.free = egl_filter24bitFree,
.imguiConfig = NULL,
.saveState = NULL,
.loadState = NULL,
.setup = egl_filter24bitSetup,
.getOutputRes = egl_filter24bitGetOutputRes,
.prepare = egl_filter24bitPrepare,
.run = egl_filter24bitRun
};

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -60,7 +60,6 @@ typedef struct EGL_FilterDownscale
EGL_Shader * lanczos2;
DownscaleFilter filter;
int useDMA;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
float pixelSize;
@ -158,26 +157,55 @@ static bool egl_filterDownscaleInit(EGL_Filter ** filter)
return false;
}
this->useDMA = -1;
if (!egl_shaderInit(&this->nearest))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->nearest,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_downscale_frag, b_shader_downscale_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
if (!egl_shaderInit(&this->linear))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->linear,
b_shader_basic_vert, b_shader_basic_vert_size,
b_shader_downscale_linear_frag, b_shader_downscale_linear_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
if (!egl_shaderInit(&this->lanczos2))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->lanczos2,
b_shader_basic_vert, b_shader_basic_vert_size,
b_shader_downscale_lanczos2_frag, b_shader_downscale_lanczos2_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
this->uNearest.type = EGL_UNIFORM_TYPE_3F;
this->uNearest.location =
egl_shaderGetUniform(this->nearest, "uConfig");
if (!egl_framebufferInit(&this->fb))
{
DEBUG_ERROR("Failed to initialize the framebuffer");
@ -298,9 +326,7 @@ static bool egl_filterDownscaleImguiConfig(EGL_Filter * filter)
}
static bool egl_filterDownscaleSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
unsigned int desktopWidth, unsigned int desktopHeight,
bool useDMA)
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
@ -310,48 +336,6 @@ static bool egl_filterDownscaleSetup(EGL_Filter * filter,
if (!this->enable)
return false;
if (this->useDMA != useDMA)
{
if (!egl_shaderCompile(this->nearest,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_downscale_frag,
b_shader_downscale_frag_size,
useDMA, NULL)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
if (!egl_shaderCompile(this->linear,
b_shader_basic_vert, b_shader_basic_vert_size,
b_shader_downscale_linear_frag,
b_shader_downscale_linear_frag_size,
useDMA, NULL)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
if (!egl_shaderCompile(this->lanczos2,
b_shader_basic_vert, b_shader_basic_vert_size,
b_shader_downscale_lanczos2_frag,
b_shader_downscale_lanczos2_frag_size,
useDMA, NULL)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
this->uNearest.type = EGL_UNIFORM_TYPE_3F;
this->uNearest.location =
egl_shaderGetUniform(this->nearest, "uConfig");
this->useDMA = useDMA;
}
if (this->prepared &&
pixFmt == this->pixFmt &&
this->width == width &&
@ -370,12 +354,11 @@ static bool egl_filterDownscaleSetup(EGL_Filter * filter,
}
static void egl_filterDownscaleGetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height, enum EGL_PixelFormat *pixFmt)
unsigned int *width, unsigned int *height)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
*width = this->width;
*height = this->height;
*pixFmt = this->pixFmt;
}
static bool egl_filterDownscalePrepare(EGL_Filter * filter)
@ -402,15 +385,15 @@ static bool egl_filterDownscalePrepare(EGL_Filter * filter)
return true;
}
static EGL_Texture * egl_filterDownscaleRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
static GLuint egl_filterDownscaleRun(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
egl_framebufferBind(this->fb);
glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture);
glBindTexture(GL_TEXTURE_2D, texture);
EGL_Shader * shader;

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -37,7 +37,6 @@ typedef struct EGL_FilterFFXCAS
EGL_Shader * shader;
bool enable;
int useDMA;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
float sharpness;
@ -107,14 +106,21 @@ static bool egl_filterFFXCASInit(EGL_Filter ** filter)
return false;
}
this->useDMA = -1;
if (!egl_shaderInit(&this->shader))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->shader,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_cas_frag, b_shader_ffx_cas_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
this->consts = countedBufferNew(8 * sizeof(GLuint));
if (!this->consts)
{
@ -122,6 +128,12 @@ static bool egl_filterFFXCASInit(EGL_Filter ** filter)
goto error_shader;
}
egl_shaderSetUniforms(this->shader, &(EGL_Uniform) {
.type = EGL_UNIFORM_TYPE_4UIV,
.location = egl_shaderGetUniform(this->shader, "uConsts"),
.v = this->consts,
}, 1);
egl_filterFFXCASLoadState(&this->base);
if (!egl_framebufferInit(&this->fb))
@ -208,36 +220,13 @@ static bool egl_filterFFXCASImguiConfig(EGL_Filter * filter)
}
static bool egl_filterFFXCASSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
unsigned int desktopWidth, unsigned int desktopHeight,
bool useDMA)
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
if (!this->enable)
return false;
if (this->useDMA != useDMA)
{
if (!egl_shaderCompile(this->shader,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_cas_frag, b_shader_ffx_cas_frag_size,
useDMA, NULL)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
egl_shaderSetUniforms(this->shader, &(EGL_Uniform) {
.type = EGL_UNIFORM_TYPE_4UIV,
.location = egl_shaderGetUniform(this->shader, "uConsts"),
.v = this->consts,
}, 1);
this->useDMA = useDMA;
}
if (pixFmt == this->pixFmt && this->width == width && this->height == height)
return true;
@ -254,12 +243,11 @@ static bool egl_filterFFXCASSetup(EGL_Filter * filter,
}
static void egl_filterFFXCASGetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height, enum EGL_PixelFormat *pixFmt)
unsigned int *width, unsigned int *height)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
*width = this->width;
*height = this->height;
*pixFmt = this->pixFmt;
}
static bool egl_filterFFXCASPrepare(EGL_Filter * filter)
@ -274,15 +262,15 @@ static bool egl_filterFFXCASPrepare(EGL_Filter * filter)
return true;
}
static EGL_Texture * egl_filterFFXCASRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
static GLuint egl_filterFFXCASRun(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
egl_framebufferBind(this->fb);
glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->shader);

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -42,7 +42,6 @@ typedef struct EGL_FilterFFXFSR1
CountedBuffer * consts;
EGL_Uniform easuUniform[2], rcasUniform;
int useDMA;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
unsigned int inWidth, inHeight;
@ -110,8 +109,6 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
return false;
}
this->useDMA = -1;
if (!egl_shaderInit(&this->easu))
{
DEBUG_ERROR("Failed to initialize the Easu shader");
@ -124,10 +121,18 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
goto error_esau;
}
if (!egl_shaderCompile(this->easu,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_easu_frag, b_shader_ffx_fsr1_easu_frag_size)
)
{
DEBUG_ERROR("Failed to compile the Easu shader");
goto error_rcas;
}
if (!egl_shaderCompile(this->rcas,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_rcas_frag, b_shader_ffx_fsr1_rcas_frag_size,
false, NULL)
b_shader_ffx_fsr1_rcas_frag, b_shader_ffx_fsr1_rcas_frag_size)
)
{
DEBUG_ERROR("Failed to compile the Rcas shader");
@ -143,6 +148,14 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
egl_filterFFXFSR1LoadState(&this->base);
this->easuUniform[0].type = EGL_UNIFORM_TYPE_4UIV;
this->easuUniform[0].location =
egl_shaderGetUniform(this->easu, "uConsts");
this->easuUniform[0].v = this->consts;
this->easuUniform[1].type = EGL_UNIFORM_TYPE_2F;
this->easuUniform[1].location =
egl_shaderGetUniform(this->easu, "uOutRes");
this->rcasUniform.type = EGL_UNIFORM_TYPE_4UI;
this->rcasUniform.location = egl_shaderGetUniform(this->rcas, "uConsts");
rcasUpdateUniform(this);
@ -322,38 +335,13 @@ static void egl_filterFFXFSR1SetOutputResHint(EGL_Filter * filter,
}
static bool egl_filterFFXFSR1Setup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
unsigned int desktopWidth, unsigned int desktopHeight,
bool useDMA)
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
if (!this->enable)
return false;
if (this->useDMA != useDMA)
{
if (!egl_shaderCompile(this->easu,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_easu_frag, b_shader_ffx_fsr1_easu_frag_size,
useDMA, NULL)
)
{
DEBUG_ERROR("Failed to compile the Easu shader");
return false;
}
this->easuUniform[0].type = EGL_UNIFORM_TYPE_4UIV;
this->easuUniform[0].location =
egl_shaderGetUniform(this->easu, "uConsts");
this->easuUniform[0].v = this->consts;
this->easuUniform[1].type = EGL_UNIFORM_TYPE_2F;
this->easuUniform[1].location =
egl_shaderGetUniform(this->easu, "uOutRes");
this->useDMA = useDMA;
}
this->active = this->width > width && this->height > height;
if (!this->active)
return false;
@ -383,12 +371,11 @@ static bool egl_filterFFXFSR1Setup(EGL_Filter * filter,
}
static void egl_filterFFXFSR1GetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height, enum EGL_PixelFormat *pixFmt)
unsigned int *width, unsigned int *height)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
*width = this->width;
*height = this->height;
*pixFmt = this->pixFmt;
}
static bool egl_filterFFXFSR1Prepare(EGL_Filter * filter)
@ -408,15 +395,15 @@ static bool egl_filterFFXFSR1Prepare(EGL_Filter * filter)
return true;
}
static EGL_Texture * egl_filterFFXFSR1Run(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
static GLuint egl_filterFFXFSR1Run(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
// pass 1, Easu
egl_framebufferBind(this->easuFb);
glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->easu);
egl_filterRectsRender(this->easu, rects);
@ -425,7 +412,7 @@ static EGL_Texture * egl_filterFFXFSR1Run(EGL_Filter * filter,
// pass 2, Rcas
egl_framebufferBind(this->rcasFb);
glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->rcas);
egl_filterRectsRender(this->rcas, rects);

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -20,7 +20,6 @@
#pragma once
extern EGL_FilterOps egl_filter24bitOps;
extern EGL_FilterOps egl_filterDownscaleOps;
extern EGL_FilterOps egl_filterFFXCASOps;
extern EGL_FilterOps egl_filterFFXFSR1Ops;

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -43,7 +43,6 @@ bool egl_framebufferInit(EGL_Framebuffer ** fb)
if (!egl_textureInit(&this->tex, NULL, EGL_TEXTYPE_BUFFER))
{
DEBUG_ERROR("Failed to initialize the texture");
free(this);
return false;
}
@ -65,14 +64,14 @@ void egl_framebufferFree(EGL_Framebuffer ** fb)
bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt,
unsigned int width, unsigned int height)
{
if (!egl_textureSetup(this->tex, pixFmt, width, height, 0, 0))
if (!egl_textureSetup(this->tex, pixFmt, width, height, 0))
{
DEBUG_ERROR("Failed to setup the texture");
return false;
}
GLuint tex;
egl_textureGet(this->tex, &tex, NULL, NULL, NULL);
egl_textureGet(this->tex, &tex, NULL, NULL);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@ -101,7 +100,9 @@ void egl_framebufferBind(EGL_Framebuffer * this)
glViewport(0, 0, this->tex->format.width, this->tex->format.height);
}
EGL_Texture * egl_framebufferGetTexture(EGL_Framebuffer * this)
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this)
{
return this->tex;
GLuint output;
egl_textureGet(this->tex, &output, NULL, NULL);
return output;
}

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -32,4 +32,4 @@ bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt,
void egl_framebufferBind(EGL_Framebuffer * this);
EGL_Texture * egl_framebufferGetTexture(EGL_Framebuffer * this);
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this);

View File

@ -10,4 +10,4 @@ function process(line, second) {
}
}
{ process($0, $2) } END { print "\0"; }
{ process($0, $2) }

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -48,8 +48,8 @@ static const EGL_FilterOps * EGL_Filters[] =
struct EGL_PostProcess
{
Vector filters, internalFilters;
EGL_Texture * output;
Vector filters;
GLuint output;
unsigned int outputX, outputY;
_Atomic(bool) modified;
@ -85,7 +85,7 @@ void egl_postProcessEarlyInit(void)
option_register(options);
for (int i = 0; i < ARRAY_LENGTH(EGL_Filters); ++i)
egl_filterEarlyInit(EGL_Filters[i]);
EGL_Filters[i]->earlyInit();
}
static void loadPreset(struct EGL_PostProcess * this, const char * name);
@ -464,7 +464,6 @@ static void configUI(void * opaque, int * id)
static size_t mouseIdx = -1;
static bool moving = false;
static size_t moveIdx = 0;
bool doMove = false;
ImVec2 window, pos;
@ -519,16 +518,9 @@ static void configUI(void * opaque, int * id)
{
EGL_Filter * tmp = filters[moveIdx];
if (mouseIdx > moveIdx) // moving down
memmove(
filters + moveIdx,
filters + moveIdx + 1,
(mouseIdx - moveIdx) * sizeof(EGL_Filter *));
memmove(filters + moveIdx, filters + moveIdx + 1, (mouseIdx - moveIdx) * sizeof(EGL_Filter *));
else // moving up
memmove(
filters + mouseIdx + 1,
filters + mouseIdx,
(moveIdx - mouseIdx) * sizeof(EGL_Filter *));
memmove(filters + mouseIdx + 1, filters + mouseIdx, (moveIdx - mouseIdx) * sizeof(EGL_Filter *));
filters[mouseIdx] = tmp;
}
@ -548,24 +540,16 @@ bool egl_postProcessInit(EGL_PostProcess ** pp)
return false;
}
if (!vector_create(&this->filters,
sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters)))
if (!vector_create(&this->filters, sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters)))
{
DEBUG_ERROR("Failed to allocate the filter list");
goto error_this;
}
if (!vector_create(&this->internalFilters,
sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters)))
{
DEBUG_ERROR("Failed to allocate the filter list");
goto error_filters;
}
if (!egl_desktopRectsInit(&this->rects, 1))
{
DEBUG_ERROR("Failed to initialize the desktop rects");
goto error_internal;
goto error_filters;
}
loadPresetList(this);
@ -575,9 +559,6 @@ bool egl_postProcessInit(EGL_PostProcess ** pp)
*pp = this;
return true;
error_internal:
vector_destroy(&this->internalFilters);
error_filters:
vector_destroy(&this->filters);
@ -598,10 +579,6 @@ void egl_postProcessFree(EGL_PostProcess ** pp)
egl_filterFree(filter);
vector_destroy(&this->filters);
vector_forEachRef(filter, &this->internalFilters)
egl_filterFree(filter);
vector_destroy(&this->internalFilters);
free(this->presetDir);
if (this->presets)
stringlist_free(&this->presets);
@ -618,9 +595,6 @@ bool egl_postProcessAdd(EGL_PostProcess * this, const EGL_FilterOps * ops)
if (!egl_filterInit(ops, &filter))
return false;
if (ops->type == EGL_FILTER_TYPE_INTERNAL)
vector_push(&this->internalFilters, &filter);
else
vector_push(&this->filters, &filter);
return true;
}
@ -632,7 +606,7 @@ bool egl_postProcessConfigModified(EGL_PostProcess * this)
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
unsigned int targetX, unsigned int targetY, bool useDMA)
unsigned int targetX, unsigned int targetY)
{
if (targetX == 0 && targetY == 0)
DEBUG_FATAL("targetX || targetY == 0");
@ -640,11 +614,8 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
EGL_Filter * lastFilter = NULL;
unsigned int sizeX, sizeY;
//TODO: clean this up
GLuint _unused;
EGL_PixelFormat pixFmt;
if (egl_textureGet(tex, &_unused,
&sizeX, &sizeY, &pixFmt) != EGL_TEX_STATUS_OK)
GLuint texture;
if (egl_textureGet(tex, &texture, &sizeX, &sizeY) != EGL_TEX_STATUS_OK)
return false;
if (atomic_exchange(&this->modified, false))
@ -665,35 +636,21 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
};
EGL_Filter * filter;
EGL_Texture * texture = tex;
const Vector * lists[] =
{
&this->internalFilters,
&this->filters,
NULL
};
for(const Vector ** filters = lists; *filters; ++filters)
vector_forEach(filter, *filters)
vector_forEach(filter, &this->filters)
{
egl_filterSetOutputResHint(filter, targetX, targetY);
if (!egl_filterSetup(filter, pixFmt, sizeX, sizeY,
desktopWidth, desktopHeight, useDMA) ||
if (!egl_filterSetup(filter, tex->format.pixFmt, sizeX, sizeY) ||
!egl_filterPrepare(filter))
continue;
texture = egl_filterRun(filter, &filterRects, texture);
egl_filterGetOutputRes(filter, &sizeX, &sizeY, &pixFmt);
egl_filterGetOutputRes(filter, &sizeX, &sizeY);
if (lastFilter)
egl_filterRelease(lastFilter);
lastFilter = filter;
// the first filter to run will convert to a normal texture
useDMA = false;
}
this->output = texture;
@ -702,7 +659,7 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
return true;
}
EGL_Texture * egl_postProcessGetOutput(EGL_PostProcess * this,
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
unsigned int * outputX, unsigned int * outputY)
{
*outputX = this->outputX;

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -41,7 +41,7 @@ bool egl_postProcessConfigModified(EGL_PostProcess * this);
* targetX/Y is the final target output dimension hint if scalers are present */
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
unsigned int targetX, unsigned int targetY, bool useDMA);
unsigned int targetX, unsigned int targetY);
EGL_Texture * egl_postProcessGetOutput(EGL_PostProcess * this,
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
unsigned int * outputX, unsigned int * outputY);

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -20,7 +20,6 @@
#include "shader.h"
#include "common/debug.h"
#include "common/stringutils.h"
#include "util.h"
#include <stdlib.h>
@ -65,9 +64,7 @@ void egl_shaderFree(EGL_Shader ** shader)
*shader = NULL;
}
bool egl_shaderLoad(EGL_Shader * this,
const char * vertex_file, const char * fragment_file, bool useDMA,
const EGL_ShaderDefine * defines)
bool egl_shaderLoad(EGL_Shader * this, const char * vertex_file, const char * fragment_file)
{
char * vertex_code, * fragment_code;
size_t vertex_size, fragment_size;
@ -89,16 +86,13 @@ bool egl_shaderLoad(EGL_Shader * this,
DEBUG_INFO("Loaded fragment shader: %s", fragment_file);
bool ret = egl_shaderCompile(this,
vertex_code, vertex_size, fragment_code, fragment_size,
useDMA, defines);
bool ret = egl_shaderCompile(this, vertex_code, vertex_size, fragment_code, fragment_size);
free(vertex_code);
free(fragment_code);
return ret;
}
static bool shaderCompile(EGL_Shader * this, const char * vertex_code,
bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
size_t vertex_size, const char * fragment_code, size_t fragment_size)
{
if (this->hasShader)
@ -210,149 +204,6 @@ static bool shaderCompile(EGL_Shader * this, const char * vertex_code,
return true;
}
bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
size_t vertex_size, const char * fragment_code, size_t fragment_size,
bool useDMA, const EGL_ShaderDefine * defines)
{
bool result = false;
char * processed = NULL;
char * newCode = NULL;
if (useDMA)
{
const char search[] = "sampler2D";
const char replace[] = "samplerExternalOES";
const char * offset = NULL;
int instances = 0;
while((offset = memsearch(
fragment_code, fragment_size,
search , sizeof(search)-1,
offset)))
{
++instances;
offset += sizeof(search)-1;
}
const int diff = (sizeof(replace) - sizeof(search)) * instances;
const int newLen = fragment_size + diff;
newCode = malloc(newLen + 1);
if (!newCode)
{
DEBUG_ERROR("Out of memory");
goto exit;
}
const char * src = fragment_code;
char * dst = newCode;
for(int i = 0; i < instances; ++i)
{
const char * pos = strstr(src, search);
const int offset = pos - src;
memcpy(dst, src, offset);
dst += offset;
src = pos + sizeof(search)-1;
memcpy(dst, replace, sizeof(replace)-1);
dst += sizeof(replace)-1;
}
const int final = fragment_size - (src - fragment_code);
memcpy(dst, src, final);
dst[final] = '\0';
fragment_code = newCode;
fragment_size = newLen;
}
if (defines)
{
// find the end of any existing lines starting with #
bool newLine = true;
bool skip = false;
int insertPos = 0;
for(int i = 0; i < fragment_size; ++i)
{
if (skip)
{
if (fragment_code[i] == '\n')
skip = false;
continue;
}
switch(fragment_code[i])
{
case '\n':
newLine = true;
continue;
case ' ':
case '\t':
case '\r':
continue;
case '#':
if (newLine)
{
skip = true;
continue;
}
//fallthrough
default:
newLine = false;
break;
}
if (!newLine)
{
insertPos = i;
if (insertPos > 0)
--insertPos;
break;
}
}
int processedLen = fragment_size;
const char * defineFormat = "#define %s %s\n";
for(const EGL_ShaderDefine * define = defines; define->name; ++define)
processedLen += snprintf(NULL, 0, defineFormat, define->name, define->value);
processed = malloc(processedLen);
if (!processed)
{
DEBUG_ERROR("Out of memory");
goto exit;
}
memcpy(processed, fragment_code, insertPos);
int offset = insertPos;
for(const EGL_ShaderDefine * define = defines; define->name; ++define)
offset += sprintf(processed + offset, defineFormat,
define->name, define->value);
memcpy(
processed + offset,
fragment_code + insertPos,
fragment_size - insertPos);
fragment_code = processed;
fragment_size = processedLen;
}
result = shaderCompile(this,
vertex_code , vertex_size,
fragment_code, fragment_size);
exit:
free(processed);
free(newCode);
return result;
}
void egl_shaderSetUniforms(EGL_Shader * this, EGL_Uniform * uniforms, int count)
{
egl_shaderFreeUniforms(this);

View File

@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@ -93,22 +93,14 @@ typedef struct EGL_Uniform
}
EGL_Uniform;
typedef struct EGL_ShaderDefine
{
const char * name;
const char * value;
}
EGL_ShaderDefine;
bool egl_shaderInit(EGL_Shader ** shader);
void egl_shaderFree(EGL_Shader ** shader);
bool egl_shaderLoad(EGL_Shader * model, const char * vertex_file,
const char * fragment_file, bool useDMA, const EGL_ShaderDefine * defines);
const char * fragment_file);
bool egl_shaderCompile(EGL_Shader * model, const char * vertex_code,
size_t vertex_size, const char * fragment_code, size_t fragment_size,
bool useDMA, const EGL_ShaderDefine * defines);
size_t vertex_size, const char * fragment_code, size_t fragment_size);
void egl_shaderSetUniforms(EGL_Shader * shader, EGL_Uniform * uniforms,
int count);

View File

@ -1,5 +1,5 @@
#version 300 es
precision highp float;
precision mediump float;
layout(location = 0) in vec2 vertex;
out vec2 fragCoord;

View File

@ -1,21 +1,16 @@
#if __VERSION__ == 300
vec4 textureGather(sampler2D tex, vec2 uv, int comp)
{
vec2 res = vec2(textureSize(tex, 0));
ivec2 p = ivec2((uv * res) - 0.5f);
// NOTE: we can't use texelFecthOffset because sampler2D may actually be samplerExternalOES
vec4 c0 = texelFetch(tex, p+ivec2(0,1), 0);
vec4 c1 = texelFetch(tex, p+ivec2(1,1), 0);
vec4 c2 = texelFetch(tex, p+ivec2(1,0), 0);
vec4 c3 = texelFetch(tex, p+ivec2(0,0), 0);
vec4 c0 = textureOffset(tex, uv, ivec2(0,1));
vec4 c1 = textureOffset(tex, uv, ivec2(1,1));
vec4 c2 = textureOffset(tex, uv, ivec2(1,0));
vec4 c3 = textureOffset(tex, uv, ivec2(0,0));
return vec4(c0[comp], c1[comp], c2[comp],c3[comp]);
}
#elif __VERSION__ < 300
vec4 textureGather(sampler2D tex, vec2 uv, int comp)
{
vec4 c3 = texture(tex, uv);
vec4 c3 = texture2D(tex, uv);
return vec4(c3[comp], c3[comp], c3[comp],c3[comp]);
}
#endif

View File

@ -1,32 +0,0 @@
#version 300 es
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
in vec2 fragCoord;
out vec4 fragColor;
uniform sampler2D sampler1;
uniform vec2 outputSize;
void main()
{
uvec2 inputSize = uvec2(textureSize(sampler1, 0));
uvec2 outputPos = uvec2(fragCoord * outputSize);
uint fst = outputPos.x * 3u / 4u;
vec4 color_0 = texelFetch(sampler1, ivec2(fst, outputPos.y), 0);
uint snd = (outputPos.x * 3u + 1u) / 4u;
vec4 color_1 = texelFetch(sampler1, ivec2(snd, outputPos.y), 0);
uint trd = (outputPos.x * 3u + 2u) / 4u;
vec4 color_2 = texelFetch(sampler1, ivec2(trd, outputPos.y), 0);
OUTPUT = vec4(
color_0.barg[outputPos.x % 4u],
color_1.gbar[outputPos.x % 4u],
color_2.rgba[outputPos.x % 4u],
1.0
);
}

View File

@ -1,5 +1,5 @@
#version 300 es
precision highp float;
precision mediump float;
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;

View File

@ -1,5 +1,5 @@
#version 300 es
precision highp float;
precision mediump float;
in vec2 uv;
out vec4 color;

View File

@ -1,5 +1,5 @@
#version 300 es
precision highp float;
precision mediump float;
#include "color_blind.h"

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