Compare commits

..

1 Commits

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

4
.github/CODEOWNERS vendored
View File

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

View File

@@ -1,9 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Looking Glass on Level1Tech's Forum
url: https://forum.level1techs.com/c/software/lookingGlass/142
about: Ask for help by creating a New Topic on the Level1Tech's forum
- name: Looking Glass Discord Server
url: https://discord.gg/52SMupxkvt
about: Ask for help in the Looking Glass discord server

View File

@@ -1,9 +1,3 @@
---
name: Bug report
about: Report bugs in Looking Glass (only confirmed bugs and feature requests please)
---
### Issues are for Bug Reports and Feature Requests Only! ### Issues are for Bug Reports and Feature Requests Only!
If you are looking for help or support please use one of the following methods If you are looking for help or support please use one of the following methods
@@ -70,4 +64,3 @@ https://www.youtube.com/watch?v=EqxxJK9Yo64
``` ```
PASTE FULL BACKTRACE HERE PASTE FULL BACKTRACE HERE
``` ```

View File

@@ -2,7 +2,7 @@ name: build
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
client: client:
runs-on: ubuntu-latest runs-on: ubuntu-20.04
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -12,9 +12,15 @@ jobs:
wayland_shell: [xdg-shell, libdecor] wayland_shell: [xdg-shell, libdecor]
build_type: [Release, Debug] build_type: [Release, Debug]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v1
with: with:
submodules: recursive 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 - name: Update apt
run: | run: |
sudo apt-get update sudo apt-get update
@@ -26,7 +32,6 @@ jobs:
libgl-dev libgles-dev \ libgl-dev libgles-dev \
libx11-dev libxss-dev libxi-dev libxinerama-dev libxcursor-dev libxpresent-dev \ libx11-dev libxss-dev libxi-dev libxinerama-dev libxcursor-dev libxpresent-dev \
libwayland-dev libxkbcommon-dev \ libwayland-dev libxkbcommon-dev \
libfontconfig-dev \
libsamplerate0-dev libpipewire-0.3-dev libpulse-dev \ libsamplerate0-dev libpipewire-0.3-dev libpulse-dev \
$([ '${{ matrix.wayland_shell }}' = libdecor ] && echo 'libdecor-0-dev libdbus-1-dev') \ $([ '${{ matrix.wayland_shell }}' = libdecor ] && echo 'libdecor-0-dev libdbus-1-dev') \
$([ '${{ matrix.compiler.cc }}' = clang ] && echo 'clang-tools') $([ '${{ matrix.compiler.cc }}' = clang ] && echo 'clang-tools')
@@ -57,7 +62,7 @@ jobs:
module: module:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v1
with: with:
submodules: recursive submodules: recursive
- name: Build kernel module - name: Build kernel module
@@ -68,16 +73,19 @@ jobs:
host-linux: host-linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v1
with: with:
submodules: recursive 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 - name: Update apt
run: | run: |
sudo apt-get update sudo apt-get update
- name: Install Linux host dependencies - name: Install Linux host dependencies
run: | run: |
sudo apt-get install binutils-dev libglib2.0-dev libxcb-xfixes0-dev \ sudo apt-get install binutils-dev libxcb-xfixes0-dev \
libpipewire-0.3-dev libxcb-shm0-dev libpipewire-0.3-dev
- name: Configure Linux host - name: Configure Linux host
run: | run: |
mkdir host/build mkdir host/build
@@ -89,9 +97,9 @@ jobs:
make -j$(nproc) make -j$(nproc)
host-windows-cross: host-windows-cross:
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v1
with: with:
submodules: recursive submodules: recursive
- name: Update apt - name: Update apt
@@ -120,33 +128,11 @@ jobs:
makensis -DIVSHMEM platform/Windows/installer.nsi makensis -DIVSHMEM platform/Windows/installer.nsi
host-windows-native: host-windows-native:
runs-on: windows-2025 runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v1
with: with:
submodules: recursive submodules: recursive
- name: Install NSIS
shell: powershell
run: |
winget install -e --id NSIS.NSIS --silent --accept-source-agreements --accept-package-agreements
$paths = @(
(Join-Path ${env:ProgramFiles} 'NSIS\Bin'),
(Join-Path ${env:ProgramFiles(x86)} 'NSIS\Bin')
) | Where-Object { Test-Path $_ }
if ($paths.Count -eq 0) {
Write-Error "NSIS 'Bin' folder not found after install."
exit 1
}
$paths | ForEach-Object {
Add-Content -Path $env:GITHUB_PATH -Value $_
}
Write-Host "makensis location(s): $($paths -join ', ')"
- name: Test NSIS
run: |
makensis /VERSION
- name: Configure Windows host for native MinGW-w64 - name: Configure Windows host for native MinGW-w64
run: | run: |
mkdir host\build mkdir host\build
@@ -161,43 +147,13 @@ jobs:
cd host\build cd host\build
makensis -DBUILD_32BIT platform\Windows\installer.nsi makensis -DBUILD_32BIT platform\Windows\installer.nsi
idd:
runs-on: windows-2022
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2
with:
msbuild-architecture: x64
- name: Install NuGet packages
run: |
cd idd
nuget restore LGIdd.sln
- name: Build IDD
run: |
cd idd
msbuild.exe LGIdd.sln /t:Build /p:Configuration=Release /p:Platform=x64
- name: Build NSIS Installer
run: |
cd idd\x64\Release\LGIdd
makensis -DBUILD_32BIT installer.nsi
- name: Build NSIS installer with IVSHMEM drivers
run: |
cd idd\x64\Release
Invoke-WebRequest https://dl.quantum2.xyz/ivshmem.tar.gz -OutFile ivshmem.tar.gz
tar -xzvf ivshmem.tar.gz
cd LGIdd
makensis -DBUILD_32BIT -DIVSHMEM installer.nsi
obs: obs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
cc: [gcc, clang] cc: [gcc, clang]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v1
with: with:
submodules: recursive submodules: recursive
- name: Update apt - name: Update apt
@@ -227,8 +183,8 @@ jobs:
sudo apt-get update sudo apt-get update
- name: Install docs dependencies - name: Install docs dependencies
run: | run: |
sudo apt-get install python3-sphinx python3-sphinx-rtd-theme \ sudo apt-get install python3-sphinx python3-sphinx-rtd-theme
python3-sphinxcontrib.spelling sudo pip3 install sphinxcontrib-spelling
- name: Build docs - name: Build docs
run: | run: |
cd doc cd doc

View File

@@ -3,36 +3,16 @@ on: pull_request
jobs: jobs:
authors: authors:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
pull-requests: write
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- name: Check AUTHORS file - name: Check AUTHORS file
id: check-authors
run: | 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)" 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..." echo "Checking if GitHub user $user is in AUTHORS file..."
if grep -q -E '> \('"$user"'\)' AUTHORS; then if grep -q -E '> \('"$user"'\)' AUTHORS; then
echo "$user found in AUTHORS file, all good!" echo "$user found in AUTHORS file, all good!"
else else
echo "$user not found in AUTHORS file." echo "$user not found in AUTHORS file."
echo "Please add yourself to the AUTHORS file and try again." echo "Please add yourself to the AUTHORS file and try again."
echo "not-found=yes" >> "$GITHUB_OUTPUT" exit 1
fi 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

9
.gitignore vendored
View File

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

5
.gitmodules vendored
View File

@@ -9,7 +9,4 @@
url = https://github.com/cimgui/cimgui.git url = https://github.com/cimgui/cimgui.git
[submodule "repos/wayland-protocols"] [submodule "repos/wayland-protocols"]
path = repos/wayland-protocols path = repos/wayland-protocols
url = https://github.com/quantum5/wayland-protocols.git url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git
[submodule "repos/nanosvg"]
path = repos/nanosvg
url = https://github.com/memononen/nanosvg.git

View File

@@ -1,34 +0,0 @@
---
labels:
platform: windows/amd64
matrix:
BUILD_TYPE:
- Debug
- Release
when:
- event: push
branch: master
repo: gnif/LookingGlass
clone:
- name: clone
image: woodpeckerci/plugin-git
pull: false
settings:
tags: true
steps:
- name: idd
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)

16
AUTHORS
View File

@@ -9,7 +9,7 @@ arcnmx <arcnmx@users.noreply.github.com> (arcnmx)
TheCakeIsNaOH <TheCakeIsNaOH@gmail.com> (TheCakeIsNaOH) TheCakeIsNaOH <TheCakeIsNaOH@gmail.com> (TheCakeIsNaOH)
NamoDev <namodev@users.noreply.github.com> (NamoDev) NamoDev <namodev@users.noreply.github.com> (NamoDev)
feltcat <58396817+feltcat@users.noreply.github.com> (feltcat) feltcat <58396817+feltcat@users.noreply.github.com> (feltcat)
Ali Abdel-Qader <abdelqaderali@protonmail.com> (thrifty-txt) Ali Abdel-Qader <abdelqaderali@protonmail.com>
Jack Karamanian <karamanian.jack@gmail.com> Jack Karamanian <karamanian.jack@gmail.com>
Mikko Rasa <tdb@tdb.fi> (DataBeaver) Mikko Rasa <tdb@tdb.fi> (DataBeaver)
Omar Pakker <Omar007@users.noreply.github.com> (Omar007) Omar Pakker <Omar007@users.noreply.github.com> (Omar007)
@@ -60,17 +60,3 @@ Johnathon Paul Weaver <weaver123_johnathon@hotmail.com> (8BallBomBom)
Chris Spencer <spencercw@gmail.com> (spencercw) Chris Spencer <spencercw@gmail.com> (spencercw)
Mark Boorer <markboo99@gmail.com> (Shootfast) Mark Boorer <markboo99@gmail.com> (Shootfast)
babbaj <babbaj45@gmail.com> (Babbaj) babbaj <babbaj45@gmail.com> (Babbaj)
Matthew McMullin <matthew@mcmullin.one> (matthewjmc)
Leonard Fricke <leonard.fricke98@gmail.com> (Leo1998)
David Meier <meier_david_91@hotmail.com> (Kenny.ch)
Daniel Cordero <looking-glass@0xdc.io> (0xdc)
esi <git@esibun.net> (esibun)
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)
Marco Rodolfi <marco.rodolfi@tuta.io> (RodoMa92)
Stewart Borle <stewi1014@gmail.com> (stewi1014)

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.0)
project(looking-glass-client C CXX) project(looking-glass-client C CXX)
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE) get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
@@ -42,18 +42,31 @@ add_feature_info(ENABLE_ASAN ENABLE_ASAN "AddressSanitizer support.")
option(ENABLE_UBSAN "Build with UndefinedBehaviorSanitizer" OFF) option(ENABLE_UBSAN "Build with UndefinedBehaviorSanitizer" OFF)
add_feature_info(ENABLE_UBSAN ENABLE_UBSAN "UndefinedBehaviorSanitizer support.") 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) option(ENABLE_PIPEWIRE "Build with PipeWire audio output support" ON)
add_feature_info(ENABLE_PIPEWIRE ENABLE_PIPEWIRE "PipeWire audio support.") add_feature_info(ENABLE_PIPEWIRE ENABLE_PIPEWIRE "PipeWire audio support.")
option(ENABLE_PULSEAUDIO "Build with PulseAudio audio output support" ON) option(ENABLE_PULSEAUDIO "Build with PulseAudio audio output support" ON)
add_feature_info(ENABLE_PULSEAUDIO ENABLE_PULSEAUDIO "PulseAudio audio support.") 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( add_compile_options(
"-Wall" "-Wall"
"-Wextra" "-Wextra"
"-Wno-sign-compare" "-Wno-sign-compare"
"-Wno-unused-parameter" "-Wno-unused-parameter"
"$<$<COMPILE_LANGUAGE:C>:-Wstrict-prototypes>" "-Wstrict-prototypes"
"$<$<C_COMPILER_ID:GNU>:-Wimplicit-fallthrough=2>" "$<$<C_COMPILER_ID:GNU>:-Wimplicit-fallthrough=2>"
"-Werror" "-Werror"
"-Wfatal-errors" "-Wfatal-errors"
@@ -97,9 +110,7 @@ add_custom_command(
include_directories( include_directories(
${PROJECT_TOP} ${PROJECT_TOP}
${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}
${CMAKE_BINARY_DIR}/include ${CMAKE_BINARY_DIR}/include
${PROJECT_TOP}/repos/nanosvg/src
) )
link_libraries( link_libraries(
@@ -113,7 +124,6 @@ set(SOURCES
src/main.c src/main.c
src/core.c src/core.c
src/app.c src/app.c
src/message.c
src/audio.c src/audio.c
src/config.c src/config.c
src/keybind.c src/keybind.c
@@ -124,25 +134,19 @@ set(SOURCES
src/egl_dynprocs.c src/egl_dynprocs.c
src/eglutil.c src/eglutil.c
src/overlay_utils.c src/overlay_utils.c
src/render_queue.c
src/evdev.c
src/overlay/splash.c
src/overlay/alert.c src/overlay/alert.c
src/overlay/fps.c src/overlay/fps.c
src/overlay/graphs.c src/overlay/graphs.c
src/overlay/help.c src/overlay/help.c
src/overlay/config.c src/overlay/config.c
src/overlay/msg.c src/overlay/msg.c
src/overlay/status.c src/overlay/record.c
) )
# Force cimgui to build as a static library. # Force cimgui to build as a static library.
set(IMGUI_STATIC "yes" CACHE STRING "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" ) add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common" )
add_subdirectory("${PROJECT_TOP}/repos/LGMP/lgmp" "${CMAKE_BINARY_DIR}/LGMP" ) add_subdirectory("${PROJECT_TOP}/repos/LGMP/lgmp" "${CMAKE_BINARY_DIR}/LGMP" )
add_subdirectory("${PROJECT_TOP}/repos/PureSpice" "${CMAKE_BINARY_DIR}/PureSpice") add_subdirectory("${PROJECT_TOP}/repos/PureSpice" "${CMAKE_BINARY_DIR}/PureSpice")
@@ -151,7 +155,6 @@ add_subdirectory("${PROJECT_TOP}/repos/cimgui" "${CMAKE_BINARY_DIR}/cimgui" E
add_subdirectory(displayservers) add_subdirectory(displayservers)
add_subdirectory(renderers) 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}) add_executable(looking-glass-client ${SOURCES})
target_compile_definitions(looking-glass-client PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1) target_compile_definitions(looking-glass-client PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1)
@@ -159,7 +162,6 @@ target_compile_definitions(looking-glass-client PRIVATE CIMGUI_DEFINE_ENUMS_AND_
target_link_libraries(looking-glass-client target_link_libraries(looking-glass-client
${EXE_FLAGS} ${EXE_FLAGS}
PkgConfig::FONTCONFIG PkgConfig::FONTCONFIG
lg_resources
lg_common lg_common
displayservers displayservers
lgmp lgmp
@@ -182,10 +184,4 @@ install(TARGETS looking-glass-client
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT binary) 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) 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) project(audiodevs LANGUAGES C)
set(AUDIODEV_H "${CMAKE_BINARY_DIR}/include/dynamic/audiodev.h") 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) project(audiodev_PipeWire LANGUAGES C)
find_package(PkgConfig) find_package(PkgConfig)

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -28,7 +28,6 @@
#include "common/debug.h" #include "common/debug.h"
#include "common/stringutils.h" #include "common/stringutils.h"
#include "common/util.h" #include "common/util.h"
#include "common/option.h"
typedef enum typedef enum
{ {
@@ -48,7 +47,6 @@ struct PipeWire
{ {
struct pw_stream * stream; struct pw_stream * stream;
struct spa_io_rate_match * rateMatch; struct spa_io_rate_match * rateMatch;
struct pw_time time;
int channels; int channels;
int sampleRate; int sampleRate;
@@ -92,14 +90,6 @@ static void pipewire_onPlaybackProcess(void * userdata)
{ {
struct pw_buffer * pbuf; 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))) if (!(pbuf = pw_stream_dequeue_buffer(pw.playback.stream)))
{ {
DEBUG_WARN("out of buffers"); DEBUG_WARN("out of buffers");
@@ -124,7 +114,6 @@ static void pipewire_onPlaybackProcess(void * userdata)
return; return;
} }
pbuf->size = frames;
sbuf->datas[0].chunk->offset = 0; sbuf->datas[0].chunk->offset = 0;
sbuf->datas[0].chunk->stride = pw.playback.stride; sbuf->datas[0].chunk->stride = pw.playback.stride;
sbuf->datas[0].chunk->size = frames * pw.playback.stride; sbuf->datas[0].chunk->size = frames * pw.playback.stride;
@@ -140,40 +129,11 @@ static void pipewire_onPlaybackDrained(void * userdata)
pw_thread_loop_unlock(pw.thread); 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) static bool pipewire_init(void)
{ {
pw_init(NULL, NULL); pw_init(NULL, NULL);
pw.loop = pw_loop_new(NULL); pw.loop = pw_loop_new(NULL);
#if PW_CHECK_VERSION(1, 3, 81)
pw.context = pw_context_new(
pw.loop,
NULL,
0);
#else
pw.context = pw_context_new( pw.context = pw_context_new(
pw.loop, pw.loop,
pw_properties_new( pw_properties_new(
@@ -182,7 +142,6 @@ static bool pipewire_init(void)
NULL NULL
), ),
0); 0);
#endif
if (!pw.context) if (!pw.context)
{ {
DEBUG_ERROR("Failed to create a context"); DEBUG_ERROR("Failed to create a context");
@@ -262,32 +221,17 @@ static void pipewire_playbackSetup(int channels, int sampleRate,
pw.playback.pullFn = pullFn; pw.playback.pullFn = pullFn;
pw_thread_loop_lock(pw.thread); pw_thread_loop_lock(pw.thread);
pw.playback.stream = pw_stream_new_simple(
struct pw_properties * props = pw.loop,
"Looking Glass",
pw_properties_new( pw_properties_new(
PW_KEY_APP_NAME , "Looking Glass",
PW_KEY_NODE_NAME , "Looking Glass", PW_KEY_NODE_NAME , "Looking Glass",
PW_KEY_MEDIA_TYPE , "Audio", PW_KEY_MEDIA_TYPE , "Audio",
PW_KEY_MEDIA_CATEGORY, "Playback", PW_KEY_MEDIA_CATEGORY, "Playback",
PW_KEY_MEDIA_ROLE , "Music", PW_KEY_MEDIA_ROLE , "Music",
PW_KEY_NODE_LATENCY , requestedNodeLatency, PW_KEY_NODE_LATENCY , requestedNodeLatency,
NULL 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, &events,
NULL NULL
); );
@@ -419,32 +363,20 @@ static void pipewire_playbackMute(bool mute)
pw_thread_loop_unlock(pw.thread); 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_CHECK_VERSION(0, 3, 50)
if (pw.playback.time.rate.num == 0) if (pw_stream_get_time_n(pw.playback.stream, &time, sizeof(time)) < 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);
#else #else
return pw.playback.time.delay + pw.playback.time.queued / pw.playback.stride; if (pw_stream_get_time(pw.playback.stream, &time) < 0)
#endif #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) static void pipewire_recordStopStream(void)
@@ -517,30 +449,17 @@ static void pipewire_recordStart(int channels, int sampleRate,
pw.record.stride = sizeof(uint16_t) * channels; pw.record.stride = sizeof(uint16_t) * channels;
pw.record.pushFn = pushFn; 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_properties_new(
PW_KEY_NODE_NAME , "Looking Glass", PW_KEY_NODE_NAME , "Looking Glass",
PW_KEY_MEDIA_TYPE , "Audio", PW_KEY_MEDIA_TYPE , "Audio",
PW_KEY_MEDIA_CATEGORY, "Capture", PW_KEY_MEDIA_CATEGORY, "Capture",
PW_KEY_MEDIA_ROLE , "Music", PW_KEY_MEDIA_ROLE , "Music",
NULL 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, &events,
NULL NULL
); );
@@ -625,7 +544,6 @@ static void pipewire_free(void)
struct LG_AudioDevOps LGAD_PipeWire = struct LG_AudioDevOps LGAD_PipeWire =
{ {
.name = "PipeWire", .name = "PipeWire",
.earlyInit = pipewire_earlyInit,
.init = pipewire_init, .init = pipewire_init,
.free = pipewire_free, .free = pipewire_free,
.playback = .playback =

View File

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

View File

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

View File

@@ -4,20 +4,17 @@ function(make_object out_var)
foreach(in_f ${ARGN}) foreach(in_f ${ARGN})
file(RELATIVE_PATH out_f ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}") file(RELATIVE_PATH out_f ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}")
set(out_h "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.h") set(out_h "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.h")
set(out_f "${out_f}.o") set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.o")
string(REGEX REPLACE "[/.-]" "_" sym_in "${in_f}") string(REGEX REPLACE "[/.-]" "_" sym_in "${in_f}")
add_custom_command(OUTPUT ${out_f} add_custom_command(OUTPUT ${out_f}
COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/${in_f} ${out_f}.tmp COMMAND ${CMAKE_LINKER} -r -b binary -o ${out_f} "${in_f}"
COMMAND dd if=/dev/zero bs=1 count=1 of=${out_f}.tmp oflag=append conv=notrunc status=none
COMMAND ${CMAKE_LINKER} -r -b binary -o ${out_f} ${out_f}.tmp
COMMAND ${CMAKE_OBJCOPY} --rename-section .data=.rodata,CONTENTS,ALLOC,LOAD,READONLY,DATA ${out_f} ${out_f} COMMAND ${CMAKE_OBJCOPY} --rename-section .data=.rodata,CONTENTS,ALLOC,LOAD,READONLY,DATA ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_o_tmp_start=b_${sym_in} ${out_f} ${out_f} COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_start=b_${sym_in} ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_o_tmp_end=b_${sym_in}_end ${out_f} ${out_f} COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_end=b_${sym_in}_end ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_o_tmp_size=b_${sym_in}_size ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --strip-symbol _binary_${sym_in}_size ${out_f} ${out_f} COMMAND ${CMAKE_OBJCOPY} --strip-symbol _binary_${sym_in}_size ${out_f} ${out_f}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Creating object from ${in_f}" COMMENT "Creating object from ${in_f}"
VERBATIM VERBATIM
) )
@@ -37,10 +34,9 @@ function(make_object out_var)
endfunction() endfunction()
function(make_defines in_file out_file) function(make_defines in_file out_file)
file(RELATIVE_PATH rel_f ${CMAKE_CURRENT_SOURCE_DIR} "${in_file}")
add_custom_command(OUTPUT ${out_file} add_custom_command(OUTPUT ${out_file}
COMMAND grep "^#define" "${in_file}" > "${out_file}" COMMAND grep "^#define" "${in_file}" > "${out_file}"
DEPENDS ${in_file} DEPENDS ${in_file}
COMMENT "Creating #defines from ${rel_f}" COMMENT "Creating #defines from ${in_file}"
) )
endfunction() endfunction()

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.0)
project(displayservers LANGUAGES C) project(displayservers LANGUAGES C)
set(DISPLAYSERVER_H "${CMAKE_BINARY_DIR}/include/dynamic/displayservers.h") set(DISPLAYSERVER_H "${CMAKE_BINARY_DIR}/include/dynamic/displayservers.h")
@@ -18,16 +18,6 @@ function(add_displayserver name)
add_subdirectory(${name}) add_subdirectory(${name})
endfunction() 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! # Add/remove displayservers here!
if (ENABLE_WAYLAND) if (ENABLE_WAYLAND)
add_displayserver(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) project(displayserver_Wayland LANGUAGES C)
find_package(PkgConfig) find_package(PkgConfig)
@@ -8,7 +8,19 @@ pkg_check_modules(DISPLAYSERVER_Wayland REQUIRED IMPORTED_TARGET
xkbcommon 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 add_library(displayserver_Wayland STATIC
activation.c activation.c
@@ -24,19 +36,66 @@ add_library(displayserver_Wayland STATIC
registry.c registry.c
wayland.c wayland.c
window.c window.c
${displayserver_Wayland_SHELL_SRC}
) )
add_subdirectory(protocol)
add_subdirectory(desktops)
target_link_libraries(displayserver_Wayland target_link_libraries(displayserver_Wayland
PkgConfig::DISPLAYSERVER_Wayland PkgConfig::DISPLAYSERVER_Wayland
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
lg_common lg_common
wayland_protocol
wayland_desktops
) )
target_include_directories(displayserver_Wayland target_include_directories(displayserver_Wayland
PRIVATE 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 * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * 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); close(data->fd);
free(data->buf); free(data->buf);
free(data); free(data);
return;
} }
wlCb.currentRead = data; wlCb.currentRead = data;
@@ -513,13 +512,6 @@ error:
free(data); free(data);
} }
static void dataSourceHandleTarget(void * data, struct wl_data_source * source,
const char * mimetype)
{
// Certain Wayland clients send this for copy-paste operations even though
// it only makes sense for drag-and-drop. We just do nothing.
}
static void dataSourceHandleSend(void * data, struct wl_data_source * source, static void dataSourceHandleSend(void * data, struct wl_data_source * source,
const char * mimetype, int fd) const char * mimetype, int fd)
{ {
@@ -555,7 +547,6 @@ static void dataSourceHandleCancelled(void * data,
} }
static const struct wl_data_source_listener dataSourceListener = { static const struct wl_data_source_listener dataSourceListener = {
.target = dataSourceHandleTarget,
.send = dataSourceHandleSend, .send = dataSourceHandleSend,
.cancelled = dataSourceHandleCancelled, .cancelled = dataSourceHandleCancelled,
}; };

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * 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,281 +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;
bool borderless;
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 (borderless)
libdecor_frame_set_visibility(state.libdecorFrame, false);
state.borderless = borderless;
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);
if (!state.borderless)
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,322 +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;
bool resizable;
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 (!resizable)
{
xdg_toplevel_set_min_size(state.toplevel, state.width, state.height);
xdg_toplevel_set_max_size(state.toplevel, state.width, state.height);
}
state.resizable = resizable;
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 || !state.resizable)
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 * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -88,26 +88,16 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
if (wlWm.needsResize) 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; if (wlWm.fractionalScale)
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)
skipResize = true;
else if (wlWm.fractionalScale)
{ {
wl_surface_set_buffer_scale(wlWm.surface, 1); wl_surface_set_buffer_scale(wlWm.surface, 1);
if (!wlWm.viewport) if (!wlWm.viewport)
wlWm.viewport = wp_viewporter_get_viewport(wlWm.viewporter, wlWm.surface); wlWm.viewport = wp_viewporter_get_viewport(wlWm.viewporter, wlWm.surface);
wp_viewport_set_source( wp_viewport_set_source(wlWm.viewport, 0, 0, wlWm.width * wlWm.scale, wlWm.height * wlWm.scale);
wlWm.viewport, wp_viewport_set_destination(wlWm.viewport, wlWm.width, wlWm.height);
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);
} }
else else
{ {
@@ -127,18 +117,18 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
} }
struct wl_region * region = wl_compositor_create_region(wlWm.compositor); 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_surface_set_opaque_region(wlWm.surface, region);
wl_region_destroy(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}); (struct Border) {0, 0, 0, 0});
app_invalidateWindow(true); app_invalidateWindow(true);
waylandStopWaitFrame(); waylandStopWaitFrame();
wlWm.needsResize = skipResize; wlWm.needsResize = false;
} }
wlWm.desktop->shellAckConfigureIfNeeded(); waylandShellAckConfigureIfNeeded();
} }
#endif #endif

View File

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

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -209,17 +209,6 @@ done:
close(fd); close(fd);
} }
int waylandGetCharCode(int key)
{
key += 8; // xkb scancode is evdev scancode + 8
xkb_keysym_t sym = xkb_state_key_get_one_sym(wlWm.xkbState, key);
if (sym == XKB_KEY_NoSymbol)
return 0;
sym = xkb_keysym_to_upper(sym);
return xkb_keysym_to_utf32(sym);
}
static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard, static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
uint32_t serial, struct wl_surface * surface, struct wl_array * keys) uint32_t serial, struct wl_surface * surface, struct wl_array * keys)
{ {
@@ -465,7 +454,7 @@ void waylandGrabPointer(void)
INTERLOCKED_SECTION(wlWm.confineLock, INTERLOCKED_SECTION(wlWm.confineLock,
{ {
if (!wlWm.confinedPointer && !wlWm.lockedPointer) if (!wlWm.confinedPointer)
{ {
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer( wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL, wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
@@ -590,13 +579,10 @@ void waylandWarpPointer(int x, int y, bool exiting)
return; return;
} }
int width, height;
wlWm.desktop->getSize(&width, &height);
if (x < 0) x = 0; 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; 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); struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
wl_region_add(region, x, y, 1, 1); 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 * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -30,6 +30,23 @@
#include "common/debug.h" #include "common/debug.h"
#include "common/locking.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) bool waylandPollInit(void)
{ {
wlWm.epollFd = epoll_create1(EPOLL_CLOEXEC); wlWm.epollFd = epoll_create1(EPOLL_CLOEXEC);
@@ -44,12 +61,58 @@ bool waylandPollInit(void)
LG_LOCK_INIT(wlWm.pollLock); LG_LOCK_INIT(wlWm.pollLock);
LG_LOCK_INIT(wlWm.pollFreeLock); 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) 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, INTERLOCKED_SECTION(wlWm.pollFreeLock,
{ {
struct WaylandPoll * node; struct WaylandPoll * node;

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -109,17 +109,10 @@ void waylandPresentationFrame(void)
return; return;
struct FrameData * data = malloc(sizeof(*data)); struct FrameData * data = malloc(sizeof(*data));
if (!data)
{
DEBUG_ERROR("out of memory");
return;
}
if (clock_gettime(wlWm.clkId, &data->sent)) if (clock_gettime(wlWm.clkId, &data->sent))
{ {
DEBUG_ERROR("clock_gettime failed: %s\n", strerror(errno)); DEBUG_ERROR("clock_gettime failed: %s\n", strerror(errno));
free(data); free(data);
return;
} }
struct wp_presentation_feedback * feedback = wp_presentation_feedback(wlWm.presentation, wlWm.surface); 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 * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * 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, wlWm.compositor = wl_registry_bind(wlWm.registry, name,
// we only need v3 to run, but v4 can use eglSwapBuffersWithDamageKHR // we only need v3 to run, but v4 can use eglSwapBuffersWithDamageKHR
&wl_compositor_interface, version > 4 ? 4 : version); &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)) else if (!strcmp(interface, wp_presentation_interface.name))
wlWm.presentation = wl_registry_bind(wlWm.registry, name, wlWm.presentation = wl_registry_bind(wlWm.registry, name,
&wp_presentation_interface, 1); &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)) else if (!strcmp(interface, xdg_activation_v1_interface.name))
wlWm.xdgActivation = wl_registry_bind(wlWm.registry, name, wlWm.xdgActivation = wl_registry_bind(wlWm.registry, name,
&xdg_activation_v1_interface, 1); &xdg_activation_v1_interface, 1);
else if (wlWm.desktop->registryGlobalHandler(
data, registry, name, interface, version))
return;
} }
static void registryGlobalRemoveHandler(void * data, 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,160 @@
/**
* 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;
enum xdg_toplevel_state * state;
wl_array_for_each(state, states)
{
if (*state == XDG_TOPLEVEL_STATE_FULLSCREEN)
wlWm.fullscreen = true;
}
}
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)
{
//TODO: Implement resize for XDG.
}

View File

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

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -24,13 +24,10 @@
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <sys/socket.h>
#include "common/debug.h" #include "common/debug.h"
#include "common/option.h" #include "common/option.h"
#include "dynamic/wayland_desktops.h"
static struct Option waylandOptions[] = static struct Option waylandOptions[] =
{ {
{ {
@@ -71,69 +68,18 @@ static bool waylandProbe(void)
return getenv("WAYLAND_DISPLAY") != NULL; 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) static bool waylandInit(const LG_DSInitParams params)
{ {
memset(&wlWm, 0, sizeof(wlWm)); 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); wl_list_init(&wlWm.surfaceOutputs);
wlWm.warpSupport = option_get_bool("wayland", "warpSupport"); wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale"); wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale");
wlWm.display = wl_display_connect(NULL);
wlWm.width = params.w;
wlWm.height = params.h;
if (!waylandPollInit()) if (!waylandPollInit())
return false; return false;
@@ -158,9 +104,7 @@ static bool waylandInit(const LG_DSInitParams params)
if (!waylandInputInit()) if (!waylandInputInit())
return false; return false;
wlWm.desktop->setSize(params.w, params.h); if (!waylandWindowInit(params.title, params.fullscreen, params.maximize, params.borderless, params.resizable))
if (!waylandWindowInit(params.title, params.appId, params.fullscreen, params.maximize,
params.borderless, params.resizable))
return false; return false;
if (!waylandEGLInit(params.w, params.h)) if (!waylandEGLInit(params.w, params.h))
@@ -171,6 +115,9 @@ static bool waylandInit(const LG_DSInitParams params)
return false; return false;
#endif #endif
wlWm.width = params.w;
wlWm.height = params.h;
return true; return true;
} }
@@ -205,31 +152,8 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
return false; 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 = struct LG_DisplayServerOps LGDS_Wayland =
{ {
.name = "Wayland",
.setup = waylandSetup, .setup = waylandSetup,
.probe = waylandProbe, .probe = waylandProbe,
.earlyInit = waylandEarlyInit, .earlyInit = waylandEarlyInit,
@@ -263,7 +187,6 @@ struct LG_DisplayServerOps LGDS_Wayland =
.uncapturePointer = waylandUncapturePointer, .uncapturePointer = waylandUncapturePointer,
.grabKeyboard = waylandGrabKeyboard, .grabKeyboard = waylandGrabKeyboard,
.ungrabKeyboard = waylandUngrabKeyboard, .ungrabKeyboard = waylandUngrabKeyboard,
.getCharCode = waylandGetCharCode,
.warpPointer = waylandWarpPointer, .warpPointer = waylandWarpPointer,
.realignPointer = waylandRealignPointer, .realignPointer = waylandRealignPointer,
.isValidPointerPos = waylandIsValidPointerPos, .isValidPointerPos = waylandIsValidPointerPos,

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -37,10 +37,11 @@
#include "common/countedbuffer.h" #include "common/countedbuffer.h"
#include "common/ringbuffer.h" #include "common/ringbuffer.h"
#include "interface/displayserver.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-presentation-time-client-protocol.h"
#include "wayland-viewporter-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-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h" #include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
#include "wayland-relative-pointer-unstable-v1-client-protocol.h" #include "wayland-relative-pointer-unstable-v1-client-protocol.h"
@@ -99,8 +100,6 @@ struct WaylandDSState
bool pointerInSurface; bool pointerInSurface;
bool focusedOnSurface; bool focusedOnSurface;
WL_DesktopOps * desktop;
struct wl_display * display; struct wl_display * display;
struct wl_surface * surface; struct wl_surface * surface;
struct wl_registry * registry; struct wl_registry * registry;
@@ -108,9 +107,12 @@ struct WaylandDSState
struct wl_shm * shm; struct wl_shm * shm;
struct wl_compositor * compositor; struct wl_compositor * compositor;
int32_t width, height;
wl_fixed_t scale; wl_fixed_t scale;
bool fractionalScale; bool fractionalScale;
bool needsResize; bool needsResize;
bool fullscreen;
uint32_t resizeSerial;
bool configured; bool configured;
bool warpSupport; bool warpSupport;
double cursorX, cursorY; double cursorX, cursorY;
@@ -131,6 +133,17 @@ struct WaylandDSState
RingBuffer photonTimings; RingBuffer photonTimings;
GraphHandle photonGraph; 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; const char * cursorThemeName;
int cursorSize; int cursorSize;
int cursorScale; int cursorScale;
@@ -277,7 +290,6 @@ void waylandUncapturePointer(void);
void waylandRealignPointer(void); void waylandRealignPointer(void);
void waylandWarpPointer(int x, int y, bool exiting); void waylandWarpPointer(int x, int y, bool exiting);
void waylandGuestPointerUpdated(double x, double y, double localX, double localY); void waylandGuestPointerUpdated(double x, double y, double localX, double localY);
int waylandGetCharCode(int key);
// output module // output module
bool waylandOutputInit(void); bool waylandOutputInit(void);
@@ -301,8 +313,16 @@ void waylandPresentationFree(void);
bool waylandRegistryInit(void); bool waylandRegistryInit(void);
void waylandRegistryFree(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 // 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 waylandWindowFree(void);
void waylandWindowUpdateScale(void); void waylandWindowUpdateScale(void);
void waylandSetWindowSize(int x, int y); void waylandSetWindowSize(int x, int y);
@@ -310,4 +330,3 @@ bool waylandIsValidPointerPos(int x, int y);
bool waylandWaitFrame(void); bool waylandWaitFrame(void);
void waylandSkipFrame(void); void waylandSkipFrame(void);
void waylandStopWaitFrame(void); void waylandStopWaitFrame(void);
void waylandNeedsResize(void);

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * 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, .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); 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); wl_surface_add_listener(wlWm.surface, &wlSurfaceListener, NULL);
if (!wlWm.desktop->shellInit(wlWm.display, wlWm.surface, if (!waylandShellInit(title, fullscreen, maximize, borderless, resizable))
title, appId, fullscreen, maximize, borderless, resizable))
return false; return false;
wl_surface_commit(wlWm.surface); wl_surface_commit(wlWm.surface);
@@ -128,14 +127,12 @@ void waylandWindowFree(void)
void waylandSetWindowSize(int x, int y) void waylandSetWindowSize(int x, int y)
{ {
wlWm.desktop->shellResize(x, y); waylandShellResize(x, y);
} }
bool waylandIsValidPointerPos(int x, int y) bool waylandIsValidPointerPos(int x, int y)
{ {
int width, height; return x >= 0 && x < wlWm.width && y >= 0 && y < wlWm.height;
wlWm.desktop->getSize(&width, &height);
return x >= 0 && x < width && y >= 0 && y < height;
} }
static void frameHandler(void * opaque, struct wl_callback * callback, unsigned int data) 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) project(displayserver_X11 LANGUAGES C)
find_package(PkgConfig) find_package(PkgConfig)
@@ -10,17 +10,12 @@ pkg_check_modules(DISPLAYSERVER_X11 REQUIRED IMPORTED_TARGET
xinerama xinerama
xcursor xcursor
xpresent xpresent
xkbcommon
) )
add_library(displayserver_X11 STATIC add_library(displayserver_X11 STATIC
x11.c x11.c
atoms.c atoms.c
clipboard.c clipboard.c
cursor.c
wm/default.c
wm/i3.c
) )
add_definitions(-D GLX_GLXEXT_PROTOTYPES) add_definitions(-D GLX_GLXEXT_PROTOTYPES)
@@ -28,10 +23,9 @@ add_definitions(-D GLX_GLXEXT_PROTOTYPES)
target_link_libraries(displayserver_X11 target_link_libraries(displayserver_X11
PkgConfig::DISPLAYSERVER_X11 PkgConfig::DISPLAYSERVER_X11
lg_common lg_common
lg_resources
) )
target_include_directories(displayserver_X11 target_include_directories(displayserver_X11
PRIVATE PRIVATE
. src
) )

View File

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

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -38,7 +38,6 @@
DEF_ATOM(_NET_WM_WINDOW_TYPE, True) \ DEF_ATOM(_NET_WM_WINDOW_TYPE, True) \
DEF_ATOM(_NET_WM_WINDOW_TYPE_NORMAL, True) \ DEF_ATOM(_NET_WM_WINDOW_TYPE_NORMAL, True) \
DEF_ATOM(_NET_WM_WINDOW_TYPE_UTILITY, True) \ DEF_ATOM(_NET_WM_WINDOW_TYPE_UTILITY, True) \
DEF_ATOM(_NET_WM_PID, True) \
DEF_ATOM(WM_DELETE_WINDOW, True) \ DEF_ATOM(WM_DELETE_WINDOW, True) \
DEF_ATOM(_MOTIF_WM_HINTS, True) \ DEF_ATOM(_MOTIF_WM_HINTS, True) \
\ \

View File

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

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -26,7 +26,7 @@
#include "interface/displayserver.h" #include "interface/displayserver.h"
bool x11CBEventThread(const XEvent * xe); bool x11CBEventThread(const XEvent xe);
bool x11CBInit(void); bool x11CBInit(void);
void x11CBNotice(LG_ClipboardData type); 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 * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -23,11 +23,7 @@
#include "x11.h" #include "x11.h"
#include "atoms.h" #include "atoms.h"
#include "clipboard.h" #include "clipboard.h"
#include "cursor.h"
#include "resources/icondata.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 <string.h>
#include <unistd.h> #include <unistd.h>
@@ -40,8 +36,6 @@
#include <X11/extensions/Xpresent.h> #include <X11/extensions/Xpresent.h>
#include <X11/Xcursor/Xcursor.h> #include <X11/Xcursor/Xcursor.h>
#include <xkbcommon/xkbcommon.h>
#include <GL/glx.h> #include <GL/glx.h>
#include <GL/glxext.h> #include <GL/glxext.h>
@@ -57,6 +51,10 @@
#include "common/event.h" #include "common/event.h"
#include "util.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 X11DSState x11;
struct MwmHints struct MwmHints
@@ -166,8 +164,6 @@ static void x11DoPresent(uint64_t msc)
static void x11Setup(void) static void x11Setup(void)
{ {
X11WM_Default.setup();
X11WM_i3 .setup();
} }
static bool x11Probe(void) static bool x11Probe(void)
@@ -194,14 +190,14 @@ static void x11CheckEWMHSupport(void)
if (XGetWindowProperty(x11.display, DefaultRootWindow(x11.display), if (XGetWindowProperty(x11.display, DefaultRootWindow(x11.display),
x11atoms._NET_SUPPORTING_WM_CHECK, 0, ~0L, False, XA_WINDOW, x11atoms._NET_SUPPORTING_WM_CHECK, 0, ~0L, False, XA_WINDOW,
&type, &fmt, &num, &bytes, &data) != Success || !data) &type, &fmt, &num, &bytes, &data) != Success)
goto out; goto out;
Window * windowFromRoot = (Window *)data; Window * windowFromRoot = (Window *)data;
if (XGetWindowProperty(x11.display, *windowFromRoot, if (XGetWindowProperty(x11.display, *windowFromRoot,
x11atoms._NET_SUPPORTING_WM_CHECK, 0, ~0L, False, XA_WINDOW, x11atoms._NET_SUPPORTING_WM_CHECK, 0, ~0L, False, XA_WINDOW,
&type, &fmt, &num, &bytes, &data) != Success || !data) &type, &fmt, &num, &bytes, &data) != Success)
goto out_root; goto out_root;
Window * windowFromChild = (Window *)data; Window * windowFromChild = (Window *)data;
@@ -210,7 +206,7 @@ static void x11CheckEWMHSupport(void)
if (XGetWindowProperty(x11.display, DefaultRootWindow(x11.display), if (XGetWindowProperty(x11.display, DefaultRootWindow(x11.display),
x11atoms._NET_SUPPORTED, 0, ~0L, False, AnyPropertyType, x11atoms._NET_SUPPORTED, 0, ~0L, False, AnyPropertyType,
&type, &fmt, &num, &bytes, &data) != Success || !data) &type, &fmt, &num, &bytes, &data) != Success)
goto out_child; goto out_child;
Atom * supported = (Atom *)data; Atom * supported = (Atom *)data;
@@ -218,7 +214,7 @@ static void x11CheckEWMHSupport(void)
if (XGetWindowProperty(x11.display, *windowFromRoot, if (XGetWindowProperty(x11.display, *windowFromRoot,
x11atoms._NET_WM_NAME, 0, ~0L, False, AnyPropertyType, x11atoms._NET_WM_NAME, 0, ~0L, False, AnyPropertyType,
&type, &fmt, &num, &bytes, &data) != Success || !data) &type, &fmt, &num, &bytes, &data) != Success)
goto out_supported; goto out_supported;
char * wmName = (char *)data; char * wmName = (char *)data;
@@ -229,11 +225,9 @@ static void x11CheckEWMHSupport(void)
x11.ewmhHasFocusEvent = true; x11.ewmhHasFocusEvent = true;
} }
DEBUG_INFO("EWMH-compliant window manager detected: %s", wmName); DEBUG_INFO("EWMH-complient window manager detected: %s", wmName);
x11.ewmhSupport = true; x11.ewmhSupport = true;
if (strcmp(wmName, "i3") == 0)
x11.wm = &X11WM_i3;
XFree(wmName); XFree(wmName);
out_supported: out_supported:
@@ -246,37 +240,17 @@ out:
return; 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) static bool x11Init(const LG_DSInitParams params)
{ {
XIDeviceInfo *devinfo; XIDeviceInfo *devinfo;
int count; int count;
int event, error; int event, error;
XSetErrorHandler(x11ErrorHandler);
XSetIOErrorHandler(x11IOErrorHandler);
memset(&x11, 0, sizeof(x11)); memset(&x11, 0, sizeof(x11));
x11.xValuator = -1; x11.xValuator = -1;
x11.yValuator = -1; x11.yValuator = -1;
x11.display = XOpenDisplay(NULL); x11.display = XOpenDisplay(NULL);
x11.jitRender = params.jitRender; x11.jitRender = params.jitRender;
x11.wm = &X11WM_Default;
XSetWindowAttributes swa = XSetWindowAttributes swa =
{ {
@@ -343,7 +317,7 @@ static bool x11Init(const LG_DSInitParams params)
XClassHint hint = XClassHint hint =
{ {
.res_name = strdup(params.title), .res_name = strdup(params.title),
.res_class = strdup(params.appId) .res_class = strdup("looking-glass-client")
}; };
XSetClassHint(x11.display, x11.window, &hint); XSetClassHint(x11.display, x11.window, &hint);
free(hint.res_name); free(hint.res_name);
@@ -374,31 +348,6 @@ static bool x11Init(const LG_DSInitParams params)
// check for Extended Window Manager Hints support // check for Extended Window Manager Hints support
x11CheckEWMHSupport(); 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();
XChangeProperty(
x11.display,
x11.window,
x11atoms._NET_WM_PID,
XA_CARDINAL,
32,
PropModeReplace,
(unsigned char *)&pid,
1
);
}
if (params.borderless) if (params.borderless)
{ {
if (x11atoms._MOTIF_WM_HINTS) if (x11atoms._MOTIF_WM_HINTS)
@@ -449,14 +398,23 @@ static bool x11Init(const LG_DSInitParams params)
); );
} }
Atom wmState[3] = {0};
int wmStateCount = 0;
if (params.fullscreen)
{
x11.fullscreen = true;
wmState[wmStateCount++] = x11atoms._NET_WM_STATE_FULLSCREEN;
}
if (params.maximize) if (params.maximize)
{ {
Atom wmState[2] = wmState[wmStateCount++] = x11atoms._NET_WM_STATE_MAXIMIZED_HORZ;
{ wmState[wmStateCount++] = x11atoms._NET_WM_STATE_MAXIMIZED_VERT;
x11atoms._NET_WM_STATE_MAXIMIZED_HORZ, }
x11atoms._NET_WM_STATE_MAXIMIZED_VERT
};
if (wmStateCount)
{
XChangeProperty( XChangeProperty(
x11.display, x11.display,
x11.window, x11.window,
@@ -465,7 +423,7 @@ static bool x11Init(const LG_DSInitParams params)
32, 32,
PropModeReplace, PropModeReplace,
(unsigned char *)&wmState, (unsigned char *)&wmState,
2 wmStateCount
); );
} }
@@ -491,7 +449,7 @@ static bool x11Init(const LG_DSInitParams params)
if (XIQueryVersion(x11.display, &major, &minor) != Success) if (XIQueryVersion(x11.display, &major, &minor) != Success)
{ {
DEBUG_ERROR("Failed to query the XInput version"); DEBUG_ERROR("Failed to query the XInput version");
goto fail_window; return false;
} }
DEBUG_INFO("X11 XInput %d.%d in use", major, minor); DEBUG_INFO("X11 XInput %d.%d in use", major, minor);
@@ -565,10 +523,6 @@ static bool x11Init(const LG_DSInitParams params)
goto fail_window; goto fail_window;
} }
XDisplayKeycodes(x11.display, &x11.minKeycode, &x11.maxKeycode);
x11.keysyms = XGetKeyboardMapping(x11.display, x11.minKeycode,
x11.maxKeycode - x11.minKeycode, &x11.symsPerKeycode);
XIFreeDeviceInfo(devinfo); XIFreeDeviceInfo(devinfo);
XQueryExtension(x11.display, "XInputExtension", &x11.xinputOp, &event, &error); XQueryExtension(x11.display, "XInputExtension", &x11.xinputOp, &event, &error);
@@ -630,17 +584,29 @@ static bool x11Init(const LG_DSInitParams params)
XFreePixmap(x11.display, temp); XFreePixmap(x11.display, temp);
} }
XcursorImages * images; /* create the square cursor */
if (params.largeCursorDot) {
images = x11cursor_load(b_no_input_cursor_32_xcur, static char data[] = { 0x07, 0x05, 0x07 };
b_no_input_cursor_32_xcur_size); static char mask[] = { 0xff, 0xff, 0xff };
else
images = x11cursor_load(b_no_input_cursor_16_xcur,
b_no_input_cursor_16_xcur_size);
x11.cursors[LG_POINTER_SQUARE] = Colormap cmap = DefaultColormap(x11.display, DefaultScreen(x11.display));
XcursorImagesLoadCursor(x11.display, images); XColor colors[2] =
XcursorImagesDestroy(images); {
{ .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 */ /* initialize the rest of the cursors */
const char * cursorLookup[LG_POINTER_COUNT] = { const char * cursorLookup[LG_POINTER_COUNT] = {
@@ -698,9 +664,6 @@ static bool x11Init(const LG_DSInitParams params)
if (!params.center) if (!params.center)
XMoveWindow(x11.display, x11.window, params.x, params.y); XMoveWindow(x11.display, x11.window, params.x, params.y);
if (params.fullscreen)
x11.doFullscreenOnExpose = true;
XSetLocaleModifiers(""); // Load XMODIFIERS XSetLocaleModifiers(""); // Load XMODIFIERS
x11.xim = XOpenIM(x11.display, 0, 0, 0); x11.xim = XOpenIM(x11.display, 0, 0, 0);
@@ -781,10 +744,6 @@ static void x11Free(void)
if (x11.cursors[i]) if (x11.cursors[i])
XFreeCursor(x11.display, x11.cursors[i]); XFreeCursor(x11.display, x11.cursors[i]);
if (x11.keysyms)
XFree(x11.keysyms);
x11.wm->deinit();
XCloseDisplay(x11.display); XCloseDisplay(x11.display);
} }
@@ -897,7 +856,7 @@ static int x11EventThread(void * unused)
XNextEvent(x11.display, &xe); XNextEvent(x11.display, &xe);
// call the clipboard handling code // call the clipboard handling code
if (x11CBEventThread(&xe)) if (x11CBEventThread(xe))
continue; continue;
switch(xe.type) switch(xe.type)
@@ -946,11 +905,6 @@ static int x11EventThread(void * unused)
{ {
atomic_store(&x11.lastWMEvent, microtime()); atomic_store(&x11.lastWMEvent, microtime());
x11.invalidateAll = true; x11.invalidateAll = true;
if (x11.doFullscreenOnExpose)
{
x11SetFullscreen(true);
x11.doFullscreenOnExpose = false;
}
break; break;
} }
@@ -1087,18 +1041,6 @@ static void setFocus(bool focused, double x, double y)
app_handleFocusEvent(focused); app_handleFocusEvent(focused);
} }
static int x11GetCharCode(int detail)
{
detail += x11.minKeycode;
if (detail < x11.minKeycode || detail > x11.maxKeycode)
return 0;
KeySym sym = x11.keysyms[(detail - x11.minKeycode) *
x11.symsPerKeycode];
sym = xkb_keysym_to_upper(sym);
return xkb_keysym_to_utf32(sym);
}
static void x11XInputEvent(XGenericEventCookie *cookie) static void x11XInputEvent(XGenericEventCookie *cookie)
{ {
static int button_state = 0; static int button_state = 0;
@@ -1181,46 +1123,6 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
app_updateCursorPos(xie->event_x, xie->event_y); app_updateCursorPos(xie->event_x, xie->event_y);
app_handleEnterEvent(false); app_handleEnterEvent(false);
x11.entered = 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; return;
} }
@@ -1230,7 +1132,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return; return;
XIDeviceEvent *device = cookie->data; XIDeviceEvent *device = cookie->data;
app_handleKeyPress(device->detail - x11.minKeycode); app_handleKeyPress(device->detail - 8);
if (!x11.xic || !app_isOverlayMode()) if (!x11.xic || !app_isOverlayMode())
return; return;
@@ -1280,7 +1182,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return; return;
XIDeviceEvent *device = cookie->data; XIDeviceEvent *device = cookie->data;
app_handleKeyRelease(device->detail - x11.minKeycode); app_handleKeyRelease(device->detail - 8);
if (!x11.xic || !app_isOverlayMode()) if (!x11.xic || !app_isOverlayMode())
return; return;
@@ -1309,7 +1211,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return; return;
XIRawEvent *raw = cookie->data; XIRawEvent *raw = cookie->data;
app_handleKeyPress(raw->detail - x11.minKeycode); app_handleKeyPress(raw->detail - 8);
return; return;
} }
@@ -1319,7 +1221,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return; return;
XIRawEvent *raw = cookie->data; XIRawEvent *raw = cookie->data;
app_handleKeyRelease(raw->detail - x11.minKeycode); app_handleKeyRelease(raw->detail - 8);
return; return;
} }
@@ -1970,7 +1872,24 @@ static void x11SetFullscreen(bool fs)
if (x11.fullscreen == fs) if (x11.fullscreen == fs)
return; 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) static bool x11GetFullscreen(void)
@@ -1985,7 +1904,6 @@ static void x11Minimize(void)
struct LG_DisplayServerOps LGDS_X11 = struct LG_DisplayServerOps LGDS_X11 =
{ {
.name = "X11",
.setup = x11Setup, .setup = x11Setup,
.probe = x11Probe, .probe = x11Probe,
.earlyInit = x11EarlyInit, .earlyInit = x11EarlyInit,
@@ -2014,7 +1932,6 @@ struct LG_DisplayServerOps LGDS_X11 =
.ungrabPointer = x11UngrabPointer, .ungrabPointer = x11UngrabPointer,
.capturePointer = x11CapturePointer, .capturePointer = x11CapturePointer,
.uncapturePointer = x11UncapturePointer, .uncapturePointer = x11UncapturePointer,
.getCharCode = x11GetCharCode,
.grabKeyboard = x11GrabKeyboard, .grabKeyboard = x11GrabKeyboard,
.ungrabKeyboard = x11UngrabKeyboard, .ungrabKeyboard = x11UngrabKeyboard,
.warpPointer = x11WarpPointer, .warpPointer = x11WarpPointer,

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -33,7 +33,6 @@
#include "interface/displayserver.h" #include "interface/displayserver.h"
#include "common/thread.h" #include "common/thread.h"
#include "common/types.h" #include "common/types.h"
#include "wm.h"
enum Modifiers enum Modifiers
{ {
@@ -49,20 +48,11 @@ enum Modifiers
#define MOD_COUNT (MOD_SUPER_RIGHT + 1) #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 struct X11DSState
{ {
Display * display; Display * display;
Window window; Window window;
XVisualInfo * visual; XVisualInfo * visual;
X11WM * wm;
int minKeycode, maxKeycode;
int symsPerKeycode;
KeySym * keysyms;
//Extended Window Manager Hints //Extended Window Manager Hints
//ref: https://specifications.freedesktop.org/wm-spec/latest/ //ref: https://specifications.freedesktop.org/wm-spec/latest/
@@ -71,7 +61,6 @@ struct X11DSState
_Atomic(uint64_t) lastWMEvent; _Atomic(uint64_t) lastWMEvent;
bool invalidateAll; bool invalidateAll;
bool doFullscreenOnExpose;
int xpresentOp; int xpresentOp;
bool jitRender; bool jitRender;

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -84,14 +84,12 @@ LG_DSPointer;
typedef struct LG_DSInitParams typedef struct LG_DSInitParams
{ {
const char * title; const char * title;
const char * appId;
int x, y, w, h; int x, y, w, h;
bool center; bool center;
bool fullscreen; bool fullscreen;
bool resizable; bool resizable;
bool borderless; bool borderless;
bool maximize; bool maximize;
bool largeCursorDot;
// if true the renderer requires an OpenGL context // if true the renderer requires an OpenGL context
bool opengl; bool opengl;
@@ -112,8 +110,6 @@ typedef struct LGEvent LGEvent;
struct LG_DisplayServerOps struct LG_DisplayServerOps
{ {
const char * name;
/* called before options are parsed, useful for registering options */ /* called before options are parsed, useful for registering options */
void (*setup)(void); void (*setup)(void);
@@ -183,9 +179,6 @@ struct LG_DisplayServerOps
void (*capturePointer)(void); void (*capturePointer)(void);
void (*uncapturePointer)(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 */ /* exiting = true if the warp is to leave the window */
void (*warpPointer)(int x, int y, bool exiting); void (*warpPointer)(int x, int y, bool exiting);
@@ -256,7 +249,6 @@ struct LG_DisplayServerOps
DEBUG_ASSERT((x)->ungrabPointer ); \ DEBUG_ASSERT((x)->ungrabPointer ); \
DEBUG_ASSERT((x)->capturePointer ); \ DEBUG_ASSERT((x)->capturePointer ); \
DEBUG_ASSERT((x)->uncapturePointer ); \ DEBUG_ASSERT((x)->uncapturePointer ); \
DEBUG_ASSERT((x)->getCharCode ); \
DEBUG_ASSERT((x)->warpPointer ); \ DEBUG_ASSERT((x)->warpPointer ); \
DEBUG_ASSERT((x)->realignPointer ); \ DEBUG_ASSERT((x)->realignPointer ); \
DEBUG_ASSERT((x)->isValidPointerPos ); \ DEBUG_ASSERT((x)->isValidPointerPos ); \

View File

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

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -25,8 +25,6 @@
#include "common/types.h" #include "common/types.h"
#define TICK_RATE 25
struct LG_OverlayOps struct LG_OverlayOps
{ {
/* internal name of the overlay for debugging */ /* internal name of the overlay for debugging */
@@ -65,7 +63,7 @@ struct LG_OverlayOps
int (*render)(void * udata, bool interactive, struct Rect * windowRects, int (*render)(void * udata, bool interactive, struct Rect * windowRects,
int maxRects); int maxRects);
/* called TICK_RATE times a second by the application /* called 25 times a second by the application
* *
* Note: This may not run in the same context as `render`! * Note: This may not run in the same context as `render`!
* *

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -36,13 +36,8 @@
(x)->onMouseShape && \ (x)->onMouseShape && \
(x)->onMouseEvent && \ (x)->onMouseEvent && \
(x)->renderStartup && \ (x)->renderStartup && \
(x)->render && \ (x)->needsRender && \
(x)->createTexture && \ (x)->render)
(x)->freeTexture && \
(x)->spiceConfigure && \
(x)->spiceDrawFill && \
(x)->spiceDrawBitmap && \
(x)->spiceShow)
typedef struct LG_RendererParams typedef struct LG_RendererParams
{ {
@@ -72,12 +67,8 @@ LG_RendererRotate;
typedef struct LG_RendererFormat typedef struct LG_RendererFormat
{ {
FrameType type; // frame type 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 screenWidth; // actual width of the host
unsigned int screenHeight; // actual height 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 frameWidth; // width of frame transmitted
unsigned int frameHeight; // height of frame transmitted unsigned int frameHeight; // height of frame transmitted
unsigned int stride; // scanline width (zero if compresed) unsigned int stride; // scanline width (zero if compresed)
@@ -167,36 +158,15 @@ typedef struct LG_RendererOps
* Context: renderThread */ * Context: renderThread */
bool (*renderStartup)(LG_Renderer * renderer, bool useDMA); bool (*renderStartup)(LG_Renderer * renderer, bool useDMA);
/* returns if the render method must be called even if nothing has changed.
* Context: renderThread */
bool (*needsRender)(LG_Renderer * renderer);
/* called to render the scene /* called to render the scene
* Context: renderThread */ * Context: renderThread */
bool (*render)(LG_Renderer * renderer, LG_RendererRotate rotate, bool (*render)(LG_Renderer * renderer, LG_RendererRotate rotate,
const bool newFrame, const bool invalidateWindow, const bool newFrame, const bool invalidateWindow,
void (*preSwap)(void * udata), void * udata); void (*preSwap)(void * udata), void * udata);
/* called to create a texture from the specified 32-bit RGB image data. This
* method is for use with Dear ImGui
* Context: renderThread */
void * (*createTexture)(LG_Renderer * renderer,
int width, int height, uint8_t * data);
/* called to free a texture previously created by createTexture. This method
* is for use with Dear ImGui
* Context: renderThread */
void (*freeTexture)(LG_Renderer * renderer, void * texture);
/* setup the spice display */
void (*spiceConfigure)(LG_Renderer * renderer, int width, int height);
/* draw a filled rect on the spice display with the specified color */
void (*spiceDrawFill)(LG_Renderer * renderer, int x, int y, int width,
int height, uint32_t color);
/* draw an image on the spice display, data is RGBA32 */
void (*spiceDrawBitmap)(LG_Renderer * renderer, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown);
/* show the spice display */
void (*spiceShow)(LG_Renderer * renderer, bool show);
} }
LG_RendererOps; LG_RendererOps;

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -27,23 +27,9 @@
typedef struct ImVec2 ImVec2; typedef struct ImVec2 ImVec2;
typedef struct
{
void * tex;
int width;
int height;
}
OverlayImage;
void overlayGetImGuiRect(struct Rect * rect); void overlayGetImGuiRect(struct Rect * rect);
ImVec2 * overlayGetScreenSize(void); ImVec2 * overlayGetScreenSize(void);
void overlayTextURL(const char * url, const char * text); void overlayTextURL(const char * url, const char * text);
void overlayTextMaybeURL(const char * text, bool wrapped); void overlayTextMaybeURL(const char * text, bool wrapped);
// create a texture from a SVG and scale it to fit the supplied width & height
bool overlayLoadSVG(const char * data, unsigned int size, OverlayImage * image,
int width, int height);
void overlayFreeImage(OverlayImage * image);
#endif #endif

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * 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) project(renderers LANGUAGES C)
set(RENDERER_H "${CMAKE_BINARY_DIR}/include/dynamic/renderers.h") 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) project(renderer_EGL LANGUAGES C CXX)
find_package(PkgConfig) find_package(PkgConfig)
@@ -55,8 +55,11 @@ build_shaders(
shader/cursor_mono.frag shader/cursor_mono.frag
shader/damage.vert shader/damage.vert
shader/damage.frag shader/damage.frag
shader/splash_bg.vert
shader/splash_bg.frag
shader/splash_logo.vert
shader/splash_logo.frag
shader/basic.vert shader/basic.vert
shader/convert_24bit.frag
shader/ffx_cas.frag shader/ffx_cas.frag
shader/ffx_fsr1_easu.frag shader/ffx_fsr1_easu.frag
shader/ffx_fsr1_rcas.frag shader/ffx_fsr1_rcas.frag
@@ -83,12 +86,13 @@ add_library(renderer_EGL STATIC
desktop.c desktop.c
desktop_rects.c desktop_rects.c
cursor.c cursor.c
draw.c
splash.c
damage.c damage.c
framebuffer.c framebuffer.c
postprocess.c postprocess.c
ffx.c ffx.c
filter.c filter.c
filter_24bit.c
filter_ffx_cas.c filter_ffx_cas.c
filter_ffx_fsr1.c filter_ffx_fsr1.c
filter_downscale.c filter_downscale.c

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -46,14 +46,9 @@ struct DesktopShader
EGL_Shader * shader; EGL_Shader * shader;
GLint uTransform; GLint uTransform;
GLint uDesktopSize; GLint uDesktopSize;
GLint uSamplerType;
GLint uScaleAlgo; GLint uScaleAlgo;
GLint uNVGain; GLint uNVGain;
GLint uCBMode; GLint uCBMode;
GLint uIsHDR;
GLint uMapHDRtoSDR;
GLint uMapHDRGain;
GLint uMapHDRPQ;
}; };
struct EGL_Desktop struct EGL_Desktop
@@ -62,20 +57,14 @@ struct EGL_Desktop
EGLDisplay * display; EGLDisplay * display;
EGL_Texture * texture; EGL_Texture * texture;
struct DesktopShader dmaShader, shader; struct DesktopShader shader;
EGL_DesktopRects * mesh; EGL_DesktopRects * mesh;
CountedBuffer * matrix; CountedBuffer * matrix;
// internals // internals
int width, height; int width, height;
bool hdr;
bool hdrPQ;
LG_RendererRotate rotate; LG_RendererRotate rotate;
bool useSpice;
int spiceWidth, spiceHeight;
EGL_Texture * spiceTexture;
// scale algorithm // scale algorithm
int scaleAlgo; int scaleAlgo;
@@ -89,11 +78,6 @@ struct EGL_Desktop
bool useDMA; bool useDMA;
LG_RendererFormat format; LG_RendererFormat format;
// map HDR content to SDR
bool mapHDRtoSDR;
int peakLuminance;
int maxCLL;
EGL_PostProcess * pp; EGL_PostProcess * pp;
_Atomic(bool) processFrame; _Atomic(bool) processFrame;
}; };
@@ -104,8 +88,7 @@ void toggleNV(int key, void * opaque);
static bool egl_initDesktopShader( static bool egl_initDesktopShader(
struct DesktopShader * shader, struct DesktopShader * shader,
const char * vertex_code , size_t vertex_size, const char * vertex_code , size_t vertex_size,
const char * fragment_code, size_t fragment_size, const char * fragment_code, size_t fragment_size
bool useDMA
) )
{ {
if (!egl_shaderInit(&shader->shader)) if (!egl_shaderInit(&shader->shader))
@@ -113,21 +96,16 @@ static bool egl_initDesktopShader(
if (!egl_shaderCompile(shader->shader, if (!egl_shaderCompile(shader->shader,
vertex_code , vertex_size, vertex_code , vertex_size,
fragment_code, fragment_size, fragment_code, fragment_size))
useDMA, NULL))
{ {
return false; return false;
} }
shader->uDesktopSize = egl_shaderGetUniform(shader->shader, "desktopSize" );
shader->uTransform = egl_shaderGetUniform(shader->shader, "transform" ); shader->uTransform = egl_shaderGetUniform(shader->shader, "transform" );
shader->uDesktopSize = egl_shaderGetUniform(shader->shader, "desktopSize");
shader->uScaleAlgo = egl_shaderGetUniform(shader->shader, "scaleAlgo" ); shader->uScaleAlgo = egl_shaderGetUniform(shader->shader, "scaleAlgo" );
shader->uNVGain = egl_shaderGetUniform(shader->shader, "nvGain" ); shader->uNVGain = egl_shaderGetUniform(shader->shader, "nvGain" );
shader->uCBMode = egl_shaderGetUniform(shader->shader, "cbMode" ); 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; return true;
} }
@@ -147,12 +125,21 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
desktop->display = display; desktop->display = display;
if (!egl_textureInit(&desktop->texture, display, if (!egl_textureInit(&desktop->texture, display,
useDMA ? EGL_TEXTYPE_DMABUF : EGL_TEXTYPE_FRAMEBUFFER)) useDMA ? EGL_TEXTYPE_DMABUF : EGL_TEXTYPE_FRAMEBUFFER, true))
{ {
DEBUG_ERROR("Failed to initialize the desktop texture"); DEBUG_ERROR("Failed to initialize the desktop texture");
return false; 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)) if (!egl_desktopRectsInit(&desktop->mesh, maxRects))
{ {
DEBUG_ERROR("Failed to initialize the desktop mesh"); DEBUG_ERROR("Failed to initialize the desktop mesh");
@@ -166,28 +153,7 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
return false; return false;
} }
if (!egl_initDesktopShader( app_registerKeybind(KEY_N, toggleNV, desktop,
&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"); "Toggle night vision mode");
desktop->nvMax = option_get_int("egl", "nvGainMax"); desktop->nvMax = option_get_int("egl", "nvGainMax");
@@ -196,19 +162,12 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
desktop->scaleAlgo = option_get_int("egl", "scale" ); desktop->scaleAlgo = option_get_int("egl", "scale" );
desktop->useDMA = useDMA; 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)) if (!egl_postProcessInit(&desktop->pp))
{ {
DEBUG_ERROR("Failed to initialize the post process manager"); DEBUG_ERROR("Failed to initialize the post process manager");
return false; return false;
} }
// this MUST be first
egl_postProcessAdd(desktop->pp, &egl_filter24bitOps);
egl_postProcessAdd(desktop->pp, &egl_filterDownscaleOps); egl_postProcessAdd(desktop->pp, &egl_filterDownscaleOps);
egl_postProcessAdd(desktop->pp, &egl_filterFFXCASOps ); egl_postProcessAdd(desktop->pp, &egl_filterFFXCASOps );
egl_postProcessAdd(desktop->pp, &egl_filterFFXFSR1Ops ); egl_postProcessAdd(desktop->pp, &egl_filterFFXFSR1Ops );
@@ -243,9 +202,7 @@ void egl_desktopFree(EGL_Desktop ** desktop)
return; return;
egl_textureFree (&(*desktop)->texture ); egl_textureFree (&(*desktop)->texture );
egl_textureFree (&(*desktop)->spiceTexture ); egl_shaderFree (&(*desktop)->shader.shader);
egl_shaderFree (&(*desktop)->shader .shader);
egl_shaderFree (&(*desktop)->dmaShader.shader);
egl_desktopRectsFree(&(*desktop)->mesh ); egl_desktopRectsFree(&(*desktop)->mesh );
countedBufferRelease(&(*desktop)->matrix ); countedBufferRelease(&(*desktop)->matrix );
@@ -293,28 +250,6 @@ void egl_desktopConfigUI(EGL_Desktop * desktop)
} }
igSliderInt("##nvgain", &desktop->nvGain, 0, desktop->nvMax, format, 0); igSliderInt("##nvgain", &desktop->nvGain, 0, desktop->nvMax, format, 0);
igPopItemWidth(); 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) bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
@@ -340,14 +275,6 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
pixFmt = EGL_PF_RGBA16F; pixFmt = EGL_PF_RGBA16F;
break; break;
case FRAME_TYPE_BGR_32:
pixFmt = EGL_PF_BGR_32;
break;
case FRAME_TYPE_RGB_24:
pixFmt = EGL_PF_RGB_24;
break;
default: default:
DEBUG_ERROR("Unsupported frame format"); DEBUG_ERROR("Unsupported frame format");
return false; return false;
@@ -355,16 +282,13 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
desktop->width = format.frameWidth; desktop->width = format.frameWidth;
desktop->height = format.frameHeight; desktop->height = format.frameHeight;
desktop->hdr = format.hdr;
desktop->hdrPQ = format.hdrPQ;
if (!egl_textureSetup( if (!egl_textureSetup(
desktop->texture, desktop->texture,
pixFmt, pixFmt,
desktop->format.dataWidth, format.frameWidth,
desktop->format.dataHeight, format.frameHeight,
desktop->format.stride, format.pitch
desktop->format.pitch
)) ))
{ {
DEBUG_ERROR("Failed to setup the desktop texture"); DEBUG_ERROR("Failed to setup the desktop texture");
@@ -377,9 +301,9 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd, bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
const FrameDamageRect * damageRects, int damageRectsCount) 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); atomic_store(&desktop->processFrame, true);
return true; return true;
@@ -405,7 +329,7 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dma
egl_textureFree(&desktop->texture); egl_textureFree(&desktop->texture);
if (!egl_textureInit(&desktop->texture, desktop->display, if (!egl_textureInit(&desktop->texture, desktop->display,
EGL_TEXTYPE_FRAMEBUFFER)) EGL_TEXTYPE_FRAMEBUFFER, true))
{ {
DEBUG_ERROR("Failed to initialize the desktop texture"); DEBUG_ERROR("Failed to initialize the desktop texture");
return false; return false;
@@ -415,8 +339,8 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dma
return false; return false;
} }
if (likely(egl_textureUpdateFromFrame(desktop->texture, frame, if (egl_textureUpdateFromFrame(desktop->texture, frame,
damageRects, damageRectsCount))) damageRects, damageRectsCount))
{ {
atomic_store(&desktop->processFrame, true); atomic_store(&desktop->processFrame, true);
return true; return true;
@@ -435,30 +359,11 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType, const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
LG_RendererRotate rotate, const struct DamageRects * rects) LG_RendererRotate rotate, const struct DamageRects * rects)
{ {
EGL_Texture * tex; if (outputWidth == 0 && outputHeight == 0)
int width, height;
bool dma;
if (unlikely(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))
DEBUG_FATAL("outputWidth || outputHeight == 0"); DEBUG_FATAL("outputWidth || outputHeight == 0");
enum EGL_TexStatus status; enum EGL_TexStatus status;
if (unlikely((status = egl_textureProcess(tex)) != EGL_TEX_STATUS_OK)) if ((status = egl_textureProcess(desktop->texture)) != EGL_TEX_STATUS_OK)
{ {
if (status != EGL_TEX_STATUS_NOTREADY) if (status != EGL_TEX_STATUS_NOTREADY)
DEBUG_ERROR("Failed to process the desktop texture"); DEBUG_ERROR("Failed to process the desktop texture");
@@ -467,32 +372,26 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
int scaleAlgo = EGL_SCALE_NEAREST; int scaleAlgo = EGL_SCALE_NEAREST;
egl_desktopRectsMatrix((float *)desktop->matrix->data, egl_desktopRectsMatrix((float *)desktop->matrix->data,
width, height, x, y, scaleX, scaleY, rotate); desktop->width, desktop->height, x, y, scaleX, scaleY, rotate);
egl_desktopRectsUpdate(desktop->mesh, rects, width, height); egl_desktopRectsUpdate(desktop->mesh, rects, desktop->width, desktop->height);
if (atomic_exchange(&desktop->processFrame, false) || if (atomic_exchange(&desktop->processFrame, false) ||
egl_postProcessConfigModified(desktop->pp)) egl_postProcessConfigModified(desktop->pp))
egl_postProcessRun(desktop->pp, tex, desktop->mesh, egl_postProcessRun(desktop->pp, desktop->texture, desktop->mesh,
width, height, outputWidth, outputHeight, dma); desktop->width, desktop->height, outputWidth, outputHeight);
unsigned int finalSizeX, finalSizeY; unsigned int finalSizeX, finalSizeY;
EGL_Texture * texture = egl_postProcessGetOutput(desktop->pp, GLuint texture = egl_postProcessGetOutput(desktop->pp,
&finalSizeX, &finalSizeY); &finalSizeX, &finalSizeY);
if (unlikely(!texture))
{
texture = tex;
finalSizeX = width;
finalSizeY = height;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
egl_resetViewport(desktop->egl); egl_resetViewport(desktop->egl);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture); glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, desktop->texture->sampler);
if (finalSizeX > width || finalSizeY > height) if (finalSizeX > desktop->width || finalSizeY > desktop->height)
scaleType = EGL_DESKTOP_DOWNSCALE; scaleType = EGL_DESKTOP_DOWNSCALE;
switch (desktop->scaleAlgo) switch (desktop->scaleAlgo)
@@ -515,13 +414,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
scaleAlgo = desktop->scaleAlgo; scaleAlgo = desktop->scaleAlgo;
} }
const struct DesktopShader * shader = const struct DesktopShader * shader = &desktop->shader;
desktop->useDMA && texture == desktop->texture ?
&desktop->dmaShader : &desktop->shader;
const float mapHDRGain =
(float)desktop->maxCLL / desktop->peakLuminance;
EGL_Uniform uniforms[] = EGL_Uniform uniforms[] =
{ {
{ {
@@ -532,7 +425,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
{ {
.type = EGL_UNIFORM_TYPE_2F, .type = EGL_UNIFORM_TYPE_2F,
.location = shader->uDesktopSize, .location = shader->uDesktopSize,
.f = { width, height }, .f = { desktop->width, desktop->height },
}, },
{ {
.type = EGL_UNIFORM_TYPE_M3x2FV, .type = EGL_UNIFORM_TYPE_M3x2FV,
@@ -549,26 +442,6 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
.type = EGL_UNIFORM_TYPE_1I, .type = EGL_UNIFORM_TYPE_1I,
.location = shader->uCBMode, .location = shader->uCBMode,
.f = { desktop->cbMode } .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 }
} }
}; };
@@ -578,61 +451,3 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
return true; return true;
} }
void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height)
{
if (!desktop->spiceTexture)
if (!egl_textureInit(&desktop->spiceTexture, desktop->display,
EGL_TEXTYPE_BUFFER_MAP))
{
DEBUG_ERROR("Failed to initialize the spice desktop texture");
return;
}
if (!egl_textureSetup(
desktop->spiceTexture,
EGL_PF_BGRA,
width,
height,
width,
width * 4
))
{
DEBUG_ERROR("Failed to setup the spice desktop texture");
return;
}
desktop->spiceWidth = width;
desktop->spiceHeight = height;
}
void egl_desktopSpiceDrawFill(EGL_Desktop * desktop, int x, int y, int width,
int height, uint32_t color)
{
/* this is a fairly hacky way to do this, but since it's only for the fallback
* spice display it's not really an issue */
uint32_t line[width];
for(int x = 0; x < width; ++x)
line[x] = color;
for(int dy = 0; dy < height; ++dy)
egl_textureUpdateRect(desktop->spiceTexture,
x, y + dy, width, 1, width, sizeof(line), (uint8_t *)line, false);
atomic_store(&desktop->processFrame, true);
}
void egl_desktopSpiceDrawBitmap(EGL_Desktop * desktop, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown)
{
egl_textureUpdateRect(desktop->spiceTexture,
x, y, width, height, width, 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 * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -50,10 +50,3 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
unsigned int outputHeight, const float x, const float y, unsigned int outputHeight, const float x, const float y,
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType, const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
LG_RendererRotate rotate, const struct DamageRects * rects); LG_RendererRotate rotate, const struct DamageRects * rects);
void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height);
void egl_desktopSpiceDrawFill(EGL_Desktop * desktop, int x, int y, int width,
int height, uint32_t color);
void egl_desktopSpiceDrawBitmap(EGL_Desktop * desktop, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown);
void egl_desktopSpiceShow(EGL_Desktop * desktop, bool show);

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -32,10 +32,6 @@
struct EGL_DesktopRects struct EGL_DesktopRects
{ {
GLfloat * lastVertices;
int lastVerticesCount;
int lastVerticesSize;
GLuint buffers[2]; GLuint buffers[2];
GLuint vao; GLuint vao;
int count; int count;
@@ -92,7 +88,6 @@ void egl_desktopRectsFree(EGL_DesktopRects ** rects_)
glDeleteVertexArrays(1, &rects->vao); glDeleteVertexArrays(1, &rects->vao);
glDeleteBuffers(2, rects->buffers); glDeleteBuffers(2, rects->buffers);
free(rects->lastVertices);
free(rects); free(rects);
*rects_ = NULL; *rects_ = NULL;
} }
@@ -118,8 +113,7 @@ void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects *
return; return;
} }
const int count = (!data || data->count < 0 ? 1 : data->count) * 8; GLfloat vertices[(!data || data->count < 0 ? 1 : data->count) * 8];
GLfloat vertices[count];
if (!data || data->count < 0) if (!data || data->count < 0)
{ {
FrameDamageRect full = { FrameDamageRect full = {
@@ -137,30 +131,6 @@ void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects *
rectToVertices(vertices + i * 8, data->rects + i); rectToVertices(vertices + i * 8, data->rects + i);
} }
// check if the value actually changed and needs updating
if (count == rects->lastVerticesCount &&
memcmp(rects->lastVertices, vertices, sizeof(GLfloat) * count) == 0)
return;
// ensure the local storage is large enough
if (count > rects->lastVerticesSize)
{
if (rects->lastVertices)
free(rects->lastVertices);
rects->lastVertices = malloc(sizeof(GLfloat) * count);
if (!rects->lastVertices)
{
DEBUG_ERROR("out of memory");
return;
}
rects->lastVerticesSize = count;
}
// copy the last value for later comparison
rects->lastVerticesCount = count;
memcpy(rects->lastVertices, vertices, sizeof(GLfloat) * count);
glBindBuffer(GL_ARRAY_BUFFER, rects->buffers[0]); glBindBuffer(GL_ARRAY_BUFFER, rects->buffers[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, rects->count * 8 * sizeof(GLfloat), vertices); glBufferSubData(GL_ARRAY_BUFFER, 0, rects->count * 8 * sizeof(GLfloat), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
@@ -296,7 +266,7 @@ bool egl_screenToDesktop(struct FrameDamageRect * output, const double matrix[6]
void egl_desktopRectsRender(EGL_DesktopRects * rects) void egl_desktopRectsRender(EGL_DesktopRects * rects)
{ {
if (unlikely(!rects->count)) if (!rects->count)
return; return;
glBindVertexArray(rects->vao); glBindVertexArray(rects->vao);

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -18,16 +18,12 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA * Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include <windows.h> #pragma once
#include <wdf.h>
#include <initguid.h>
#include "device.h" #include "model.h"
#include "trace.h"
EXTERN_C_START void egl_drawTorus(EGL_Model * model, unsigned int pts, float x, float y,
DRIVER_INITIALIZE DriverEntry; float inner, float outer);
EXTERN_C_END
EVT_WDF_DRIVER_DEVICE_ADD LGIddEvtDeviceAdd; void egl_drawTorusArc(EGL_Model * model, unsigned int pts, float x, float y,
EVT_WDF_OBJECT_CONTEXT_CLEANUP LGIddEvtDriverContextCleanup; float inner, float outer, float s, float e);

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -20,7 +20,6 @@
#include "interface/renderer.h" #include "interface/renderer.h"
#include "common/util.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/KVMFR.h" #include "common/KVMFR.h"
#include "common/option.h" #include "common/option.h"
@@ -46,9 +45,11 @@
#include "damage.h" #include "damage.h"
#include "desktop.h" #include "desktop.h"
#include "cursor.h" #include "cursor.h"
#include "splash.h"
#include "postprocess.h" #include "postprocess.h"
#include "util.h" #include "util.h"
#define SPLASH_FADE_TIME 1000000
#define MAX_BUFFER_AGE 3 #define MAX_BUFFER_AGE 3
#define DESKTOP_DAMAGE_COUNT 4 #define DESKTOP_DAMAGE_COUNT 4
#define MAX_ACCUMULATED_DAMAGE ((KVMFR_MAX_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2) * MAX_BUFFER_AGE) #define MAX_ACCUMULATED_DAMAGE ((KVMFR_MAX_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2) * MAX_BUFFER_AGE)
@@ -76,11 +77,15 @@ struct Inst
EGL_Desktop * desktop; // the desktop EGL_Desktop * desktop; // the desktop
EGL_Cursor * cursor; // the mouse cursor EGL_Cursor * cursor; // the mouse cursor
EGL_Splash * splash; // the splash screen
EGL_Damage * damage; // the damage display EGL_Damage * damage; // the damage display
bool imgui; // if imgui was initialized bool imgui; // if imgui was initialized
LG_RendererFormat format; LG_RendererFormat format;
bool formatValid; bool formatValid;
bool start;
uint64_t waitFadeTime;
bool waitDone;
int width, height; int width, height;
float uiScale; float uiScale;
@@ -118,9 +123,6 @@ struct Inst
RingBuffer importTimings; RingBuffer importTimings;
GraphHandle importGraph; GraphHandle importGraph;
bool showSpice;
int spiceWidth, spiceHeight;
}; };
static struct Option egl_options[] = static struct Option egl_options[] =
@@ -203,27 +205,6 @@ static struct Option egl_options[] =
.type = OPTION_TYPE_BOOL, .type = OPTION_TYPE_BOOL,
.value.x_bool = true .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} {0}
}; };
@@ -297,10 +278,12 @@ static void egl_deinitialize(LG_Renderer * renderer)
if (this->imgui) if (this->imgui)
ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown();
app_unregisterGraph(this->importGraph);
ringbuffer_free(&this->importTimings); ringbuffer_free(&this->importTimings);
egl_desktopFree(&this->desktop); egl_desktopFree(&this->desktop);
egl_cursorFree (&this->cursor); egl_cursorFree (&this->cursor);
egl_splashFree (&this->splash);
egl_damageFree (&this->damage); egl_damageFree (&this->damage);
LG_LOCK_FREE(this->lock); LG_LOCK_FREE(this->lock);
@@ -339,6 +322,7 @@ static void egl_onRestart(LG_Renderer * renderer)
eglDestroyContext(this->display, this->frameContext); eglDestroyContext(this->display, this->frameContext);
this->frameContext = NULL; this->frameContext = NULL;
this->start = false;
INTERLOCKED_SECTION(this->desktopDamageLock, { INTERLOCKED_SECTION(this->desktopDamageLock, {
this->desktopDamage[this->desktopDamageIdx].count = -1; this->desktopDamage[this->desktopDamageIdx].count = -1;
@@ -347,17 +331,6 @@ static void egl_onRestart(LG_Renderer * renderer)
static void egl_calc_mouse_size(struct Inst * this) static void egl_calc_mouse_size(struct Inst * this)
{ {
if (this->showSpice)
{
this->mouseScaleX = 2.0f / this->spiceWidth;
this->mouseScaleY = 2.0f / this->spiceHeight;
egl_cursorSetSize(this->cursor,
(this->mouseWidth * (1.0f / this->spiceWidth )) * this->scaleX,
(this->mouseHeight * (1.0f / this->spiceHeight)) * this->scaleY
);
return;
}
if (!this->formatValid) if (!this->formatValid)
return; return;
@@ -407,19 +380,6 @@ static void egl_calc_mouse_size(struct Inst * this)
static void egl_calc_mouse_state(struct Inst * this) static void egl_calc_mouse_state(struct Inst * this)
{ {
if (this->showSpice)
{
egl_cursorSetState(
this->cursor,
this->cursorVisible,
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY,
((float)this->cursorHX * this->mouseScaleX) * this->scaleX,
((float)this->cursorHY * this->mouseScaleY) * this->scaleY
);
return;
}
if (!this->formatValid) if (!this->formatValid)
return; return;
@@ -502,7 +462,7 @@ static void egl_onResize(LG_Renderer * renderer, const int width, const int heig
if (destRect.valid) if (destRect.valid)
{ {
this->translateX = -1.0f + (((this->destRect.w / 2) + this->destRect.x) * 2) / (float)this->width; this->translateX = 1.0f - (((this->destRect.w / 2) + this->destRect.x) * 2) / (float)this->width;
this->translateY = 1.0f - (((this->destRect.h / 2) + this->destRect.y) * 2) / (float)this->height; this->translateY = 1.0f - (((this->destRect.h / 2) + this->destRect.y) * 2) / (float)this->height;
this->scaleX = (float)this->destRect.w / (float)this->width; this->scaleX = (float)this->destRect.w / (float)this->width;
this->scaleY = (float)this->destRect.h / (float)this->height; this->scaleY = (float)this->destRect.h / (float)this->height;
@@ -581,7 +541,7 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
this->formatValid = true; this->formatValid = true;
/* this event runs in a second thread so we need to init it here */ /* this event runs in a second thread so we need to init it here */
if (unlikely(!this->frameContext)) if (!this->frameContext)
{ {
static EGLint attrs[] = { static EGLint attrs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_CONTEXT_CLIENT_VERSION, 2,
@@ -601,7 +561,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); float scale = max(1.0f, (float)format.screenWidth / this->width);
egl_cursorSetScale(this->cursor, scale); egl_cursorSetScale(this->cursor, scale);
@@ -624,27 +584,23 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int d
struct Inst * this = UPCAST(struct Inst, renderer); struct Inst * this = UPCAST(struct Inst, renderer);
uint64_t start = nanotime(); uint64_t start = nanotime();
if (unlikely(!egl_desktopUpdate( if (!egl_desktopUpdate(this->desktop, frame, dmaFd, damageRects, damageRectsCount))
this->desktop, frame, dmaFd, damageRects, damageRectsCount)))
{ {
DEBUG_INFO("Failed to to update the desktop"); DEBUG_INFO("Failed to to update the desktop");
return false; return false;
} }
ringbuffer_push(this->importTimings, &(float){ (nanotime() - start) * 1e-6f }); ringbuffer_push(this->importTimings, &(float){ (nanotime() - start) * 1e-6f });
this->start = true;
INTERLOCKED_SECTION(this->desktopDamageLock, { INTERLOCKED_SECTION(this->desktopDamageLock, {
struct DesktopDamage * damage = this->desktopDamage + this->desktopDamageIdx; struct DesktopDamage * damage = this->desktopDamage + this->desktopDamageIdx;
if (unlikely( if (damage->count == -1 || damageRectsCount == 0 ||
damage->count == -1 || damage->count + damageRectsCount >= KVMFR_MAX_DAMAGE_RECTS)
damageRectsCount == 0 ||
damage->count + damageRectsCount >= KVMFR_MAX_DAMAGE_RECTS))
{
damage->count = -1; damage->count = -1;
}
else else
{ {
memcpy(damage->rects + damage->count, damageRects, memcpy(damage->rects + damage->count, damageRects, damageRectsCount * sizeof(FrameDamageRect));
damageRectsCount * sizeof(FrameDamageRect));
damage->count += damageRectsCount; damage->count += damageRectsCount;
} }
}); });
@@ -776,7 +732,7 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
EGLint attr[] = EGLint attr[] =
{ {
EGL_BUFFER_SIZE , 30, EGL_BUFFER_SIZE , 24,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SAMPLE_BUFFERS , maxSamples > 0 ? 1 : 0, EGL_SAMPLE_BUFFERS , maxSamples > 0 ? 1 : 0,
EGL_SAMPLES , maxSamples, EGL_SAMPLES , maxSamples,
@@ -971,6 +927,12 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
return false; return false;
} }
if (!egl_splashInit(&this->splash))
{
DEBUG_ERROR("Failed to initialize the splash screen");
return false;
}
if (!egl_damageInit(&this->damage)) if (!egl_damageInit(&this->damage))
{ {
DEBUG_ERROR("Failed to initialize the damage display"); DEBUG_ERROR("Failed to initialize the damage display");
@@ -989,6 +951,12 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
return true; return true;
} }
static bool egl_needsRender(LG_Renderer * renderer)
{
struct Inst * this = UPCAST(struct Inst, renderer);
return !this->waitDone;
}
inline static EGLint egl_bufferAge(struct Inst * this) inline static EGLint egl_bufferAge(struct Inst * this)
{ {
if (!this->hasBufferAge) if (!this->hasBufferAge)
@@ -1015,26 +983,21 @@ inline static void renderLetterBox(struct Inst * this)
if (hLB) if (hLB)
{ {
// left glScissor(0.0f, 0.0f, this->destRect.x + 0.5f, this->height + 0.5f);
glScissor(0, 0, this->destRect.x, this->height);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
// right
float x2 = this->destRect.x + this->destRect.w; float x2 = this->destRect.x + this->destRect.w;
glScissor(x2, 0, this->width - x2, this->height); glScissor(x2 - 0.5f, 0.0f, this->width - x2 + 1.0f, this->height + 1.0f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
} }
if (vLB) if (vLB)
{ {
// top glScissor(0.0f, 0.0f, this->width + 0.5f, this->destRect.y + 0.5f);
glScissor(0, this->height - this->destRect.y,
this->width, this->destRect.y);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
// bottom float y2 = this->destRect.y + this->destRect.h;
int y2 = this->destRect.y + this->destRect.h; glScissor(0.0f, y2 - 0.5f, this->width + 1.0f, this->height - y2 + 1.0f);
glScissor(0, 0, this->width, this->height - y2);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
} }
@@ -1048,9 +1011,8 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
{ {
struct Inst * this = UPCAST(struct Inst, renderer); struct Inst * this = UPCAST(struct Inst, renderer);
EGLint bufferAge = egl_bufferAge(this); EGLint bufferAge = egl_bufferAge(this);
bool renderAll = invalidateWindow || this->hadOverlay || bool renderAll = invalidateWindow || !this->start || this->hadOverlay ||
bufferAge <= 0 || bufferAge > MAX_BUFFER_AGE || bufferAge <= 0 || bufferAge > MAX_BUFFER_AGE;
this->showSpice;
bool hasOverlay = false; bool hasOverlay = false;
struct CursorState cursorState = { .visible = false }; struct CursorState cursorState = { .visible = false };
@@ -1063,14 +1025,14 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
accumulated->count = 0; accumulated->count = 0;
INTERLOCKED_SECTION(this->desktopDamageLock, { INTERLOCKED_SECTION(this->desktopDamageLock, {
if (likely(!renderAll)) if (!renderAll)
{ {
for (int i = 0; i < bufferAge; ++i) for (int i = 0; i < bufferAge; ++i)
{ {
struct DesktopDamage * damage = this->desktopDamage + struct DesktopDamage * damage = this->desktopDamage +
IDX_AGO(this->desktopDamageIdx, i, DESKTOP_DAMAGE_COUNT); IDX_AGO(this->desktopDamageIdx, i, DESKTOP_DAMAGE_COUNT);
if (unlikely(damage->count < 0)) if (damage->count < 0)
{ {
renderAll = true; renderAll = true;
break; break;
@@ -1094,7 +1056,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
this->desktopDamage[this->desktopDamageIdx].count = 0; this->desktopDamage[this->desktopDamageIdx].count = 0;
}); });
if (likely(!renderAll)) if (!renderAll)
{ {
double matrix[6]; double matrix[6];
egl_screenToDesktopMatrix(matrix, egl_screenToDesktopMatrix(matrix,
@@ -1108,7 +1070,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
int count = this->overlayHistoryCount[idx]; int count = this->overlayHistoryCount[idx];
struct Rect * damage = this->overlayHistory[idx]; struct Rect * damage = this->overlayHistory[idx];
if (unlikely(count < 0)) if (count < 0)
{ {
renderAll = true; renderAll = true;
break; break;
@@ -1121,12 +1083,11 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
); );
} }
accumulated->count = rectsMergeOverlapping(accumulated->rects, accumulated->count = rectsMergeOverlapping(accumulated->rects, accumulated->count);
accumulated->count);
} }
++this->overlayHistoryIdx; ++this->overlayHistoryIdx;
if (likely(this->destRect.w > 0 && this->destRect.h > 0)) if (this->start && this->destRect.w > 0 && this->destRect.h > 0)
{ {
if (egl_desktopRender(this->desktop, if (egl_desktopRender(this->desktop,
this->destRect.w, this->destRect.h, this->destRect.w, this->destRect.h,
@@ -1134,6 +1095,14 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
this->scaleX , this->scaleY , this->scaleX , this->scaleY ,
this->scaleType , rotate, renderAll ? NULL : accumulated)) this->scaleType , rotate, renderAll ? NULL : accumulated))
{ {
if (!this->waitFadeTime)
{
if (!this->params.quickSplash)
this->waitFadeTime = microtime() + SPLASH_FADE_TIME;
else
this->waitDone = true;
}
cursorState = egl_cursorRender(this->cursor, cursorState = egl_cursorRender(this->cursor,
(this->format.rotate + rotate) % LG_ROTATE_MAX, (this->format.rotate + rotate) % LG_ROTATE_MAX,
this->width, this->height); this->width, this->height);
@@ -1144,17 +1113,49 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
renderLetterBox(this); renderLetterBox(this);
hasOverlay |= if (!this->waitDone)
egl_damageRender(this->damage, rotate, newFrame ? desktopDamage : NULL) | {
invalidateWindow; float a = 1.0f;
if (!this->waitFadeTime)
a = 1.0f;
else
{
uint64_t t = microtime();
if (t > this->waitFadeTime)
this->waitDone = true;
else
{
uint64_t delta = this->waitFadeTime - t;
a = 1.0f / SPLASH_FADE_TIME * delta;
}
}
if (!this->waitDone)
{
egl_splashRender(this->splash, a, this->splashRatio);
hasOverlay = true;
}
}
else if (!this->start)
{
egl_splashRender(this->splash, 1.0f, this->splashRatio);
hasOverlay = true;
}
hasOverlay |= egl_damageRender(this->damage, rotate, newFrame ? desktopDamage : NULL);
hasOverlay |= invalidateWindow;
struct Rect damage[KVMFR_MAX_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2]; struct Rect damage[KVMFR_MAX_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2];
int damageIdx = app_renderOverlay(damage, MAX_OVERLAY_RECTS); 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_NewFrame();
ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData()); ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData());
@@ -1162,21 +1163,20 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
damage[i].y = this->height - damage[i].y - damage[i].h; 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; damage[damageIdx++] = cursorState.rect;
int overlayHistoryIdx = this->overlayHistoryIdx % DESKTOP_DAMAGE_COUNT; int overlayHistoryIdx = this->overlayHistoryIdx % DESKTOP_DAMAGE_COUNT;
if (unlikely(hasOverlay)) if (hasOverlay)
this->overlayHistoryCount[overlayHistoryIdx] = -1; this->overlayHistoryCount[overlayHistoryIdx] = -1;
else else
{ {
if (unlikely(damageIdx > 0)) if (damageIdx > 0)
memcpy(this->overlayHistory[overlayHistoryIdx], memcpy(this->overlayHistory[overlayHistoryIdx], damage, damageIdx * sizeof(struct Rect));
damage, damageIdx * sizeof(struct Rect));
this->overlayHistoryCount[overlayHistoryIdx] = damageIdx; this->overlayHistoryCount[overlayHistoryIdx] = damageIdx;
} }
if (unlikely(!hasOverlay && !this->hadOverlay)) if (!hasOverlay && !this->hadOverlay)
{ {
if (this->cursorLast.visible) if (this->cursorLast.visible)
damage[damageIdx++] = this->cursorLast.rect; damage[damageIdx++] = this->cursorLast.rect;
@@ -1203,78 +1203,10 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
this->cursorLast = cursorState; this->cursorLast = cursorState;
preSwap(udata); preSwap(udata);
app_eglSwapBuffers(this->display, this->surface, damage, app_eglSwapBuffers(this->display, this->surface, damage, this->noSwapDamage ? 0 : damageIdx);
this->noSwapDamage ? 0 : damageIdx);
return true; return true;
} }
static void * egl_createTexture(LG_Renderer * renderer,
int width, int height, uint8_t * data)
{
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
data);
glBindTexture(GL_TEXTURE_2D, 0);
return (void*)(intptr_t)tex;
}
static void egl_freeTexture(LG_Renderer * renderer, void * texture)
{
GLuint tex = (GLuint)(intptr_t)texture;
glDeleteTextures(1, &tex);
}
static void egl_spiceConfigure(LG_Renderer * renderer, int width, int height)
{
struct Inst * this = UPCAST(struct Inst, renderer);
this->spiceWidth = width;
this->spiceHeight = height;
egl_desktopSpiceConfigure(this->desktop, width, height);
}
static void egl_spiceDrawFill(LG_Renderer * renderer, int x, int y, int width,
int height, uint32_t color)
{
struct Inst * this = UPCAST(struct Inst, renderer);
egl_desktopSpiceDrawFill(this->desktop, x, y, width, height, color);
}
static void egl_spiceDrawBitmap(LG_Renderer * renderer, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown)
{
struct Inst * this = UPCAST(struct Inst, renderer);
egl_desktopSpiceDrawBitmap(this->desktop, x, y, width, height, stride,
data, topDown);
}
static void egl_spiceShow(LG_Renderer * renderer, bool show)
{
struct Inst * this = UPCAST(struct Inst, renderer);
this->showSpice = show;
egl_calc_mouse_size(this);
egl_desktopSpiceShow(this->desktop, show);
}
struct LG_RendererOps LGR_EGL = struct LG_RendererOps LGR_EGL =
{ {
.getName = egl_getName, .getName = egl_getName,
@@ -1290,12 +1222,6 @@ struct LG_RendererOps LGR_EGL =
.onFrameFormat = egl_onFrameFormat, .onFrameFormat = egl_onFrameFormat,
.onFrame = egl_onFrame, .onFrame = egl_onFrame,
.renderStartup = egl_renderStartup, .renderStartup = egl_renderStartup,
.render = egl_render, .needsRender = egl_needsRender,
.createTexture = egl_createTexture, .render = egl_render
.freeTexture = egl_freeTexture,
.spiceConfigure = egl_spiceConfigure,
.spiceDrawFill = egl_spiceDrawFill,
.spiceDrawBitmap = egl_spiceDrawBitmap,
.spiceShow = egl_spiceShow
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * 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 */ /* reads filter state from options */
void (*loadState)(EGL_Filter * filter); void (*loadState)(EGL_Filter * filter);
/* set the input format of the filter /* set the input format of the filter */
* useDMA will be true if the texture provided needs to use samplerExternalOES
*/
bool (*setup)(EGL_Filter * filter, enum EGL_PixelFormat pixFmt, bool (*setup)(EGL_Filter * filter, enum EGL_PixelFormat pixFmt,
unsigned int width, unsigned int height, unsigned int width, unsigned int height);
unsigned int desktopWidth, unsigned int desktopHeight, bool useDMA);
/* set the output resolution hint for the filter /* set the output resolution hint for the filter
* this is optional and only a hint */ * this is optional and only a hint */
void (*setOutputResHint)(EGL_Filter * filter, void (*setOutputResHint)(EGL_Filter * filter,
unsigned int x, unsigned int y); 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, 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 /* prepare the shader for use
* A filter can return false to bypass it */ * A filter can return false to bypass it */
@@ -90,8 +87,8 @@ typedef struct EGL_FilterOps
/* runs the filter on the provided texture /* runs the filter on the provided texture
* returns the processed texture as the output */ * returns the processed texture as the output */
EGL_Texture * (*run)(EGL_Filter * filter, EGL_FilterRects * rects, GLuint (*run)(EGL_Filter * filter, EGL_FilterRects * rects,
EGL_Texture * texture); GLuint texture);
/* called when the filter output is no loger needed so it can release memory /* called when the filter output is no loger needed so it can release memory
* this is optional */ * this is optional */
@@ -105,12 +102,6 @@ typedef struct EGL_Filter
} }
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) static inline bool egl_filterInit(const EGL_FilterOps * ops, EGL_Filter ** filter)
{ {
if (!ops->init(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) static inline bool egl_filterImguiConfig(EGL_Filter * filter)
{ {
if (filter->ops.imguiConfig)
return filter->ops.imguiConfig(filter); return filter->ops.imguiConfig(filter);
return false;
} }
static inline void egl_filterSaveState(EGL_Filter * filter) static inline void egl_filterSaveState(EGL_Filter * filter)
{ {
if (filter->ops.saveState)
filter->ops.saveState(filter); filter->ops.saveState(filter);
} }
static inline void egl_filterLoadState(EGL_Filter * filter) static inline void egl_filterLoadState(EGL_Filter * filter)
{ {
if (filter->ops.loadState)
filter->ops.loadState(filter); filter->ops.loadState(filter);
} }
static inline bool egl_filterSetup(EGL_Filter * filter, static inline bool egl_filterSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height, enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
unsigned int desktopWidth, unsigned int desktopHeight,
bool useDMA)
{ {
return filter->ops.setup(filter, pixFmt, width, height, return filter->ops.setup(filter, pixFmt, width, height);
desktopWidth, desktopHeight, useDMA);
} }
static inline void egl_filterSetOutputResHint(EGL_Filter * filter, 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, 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) 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); return filter->ops.prepare(filter);
} }
static inline EGL_Texture * egl_filterRun(EGL_Filter * filter, static inline GLuint egl_filterRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture) EGL_FilterRects * rects, GLuint texture)
{ {
return filter->ops.run(filter, rects, 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 * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -60,7 +60,6 @@ typedef struct EGL_FilterDownscale
EGL_Shader * lanczos2; EGL_Shader * lanczos2;
DownscaleFilter filter; DownscaleFilter filter;
int useDMA;
enum EGL_PixelFormat pixFmt; enum EGL_PixelFormat pixFmt;
unsigned int width, height; unsigned int width, height;
float pixelSize; float pixelSize;
@@ -158,26 +157,55 @@ static bool egl_filterDownscaleInit(EGL_Filter ** filter)
return false; return false;
} }
this->useDMA = -1;
if (!egl_shaderInit(&this->nearest)) if (!egl_shaderInit(&this->nearest))
{ {
DEBUG_ERROR("Failed to initialize the shader"); DEBUG_ERROR("Failed to initialize the shader");
goto error_this; 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)) if (!egl_shaderInit(&this->linear))
{ {
DEBUG_ERROR("Failed to initialize the shader"); DEBUG_ERROR("Failed to initialize the shader");
goto error_this; 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)) if (!egl_shaderInit(&this->lanczos2))
{ {
DEBUG_ERROR("Failed to initialize the shader"); DEBUG_ERROR("Failed to initialize the shader");
goto error_this; 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)) if (!egl_framebufferInit(&this->fb))
{ {
DEBUG_ERROR("Failed to initialize the framebuffer"); 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, static bool egl_filterDownscaleSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height, enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
unsigned int desktopWidth, unsigned int desktopHeight,
bool useDMA)
{ {
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter); EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
@@ -310,48 +336,6 @@ static bool egl_filterDownscaleSetup(EGL_Filter * filter,
if (!this->enable) if (!this->enable)
return false; 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 && if (this->prepared &&
pixFmt == this->pixFmt && pixFmt == this->pixFmt &&
this->width == width && this->width == width &&
@@ -370,12 +354,11 @@ static bool egl_filterDownscaleSetup(EGL_Filter * filter,
} }
static void egl_filterDownscaleGetOutputRes(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); EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
*width = this->width; *width = this->width;
*height = this->height; *height = this->height;
*pixFmt = this->pixFmt;
} }
static bool egl_filterDownscalePrepare(EGL_Filter * filter) static bool egl_filterDownscalePrepare(EGL_Filter * filter)
@@ -402,15 +385,15 @@ static bool egl_filterDownscalePrepare(EGL_Filter * filter)
return true; return true;
} }
static EGL_Texture * egl_filterDownscaleRun(EGL_Filter * filter, static GLuint egl_filterDownscaleRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture) EGL_FilterRects * rects, GLuint texture)
{ {
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter); EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
egl_framebufferBind(this->fb); egl_framebufferBind(this->fb);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture); glBindTexture(GL_TEXTURE_2D, texture);
EGL_Shader * shader; EGL_Shader * shader;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/** /**
* Looking Glass * Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors * Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -20,7 +20,6 @@
#include "shader.h" #include "shader.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/stringutils.h"
#include "util.h" #include "util.h"
#include <stdlib.h> #include <stdlib.h>
@@ -65,9 +64,7 @@ void egl_shaderFree(EGL_Shader ** shader)
*shader = NULL; *shader = NULL;
} }
bool egl_shaderLoad(EGL_Shader * this, bool egl_shaderLoad(EGL_Shader * this, const char * vertex_file, const char * fragment_file)
const char * vertex_file, const char * fragment_file, bool useDMA,
const EGL_ShaderDefine * defines)
{ {
char * vertex_code, * fragment_code; char * vertex_code, * fragment_code;
size_t vertex_size, fragment_size; size_t vertex_size, fragment_size;
@@ -89,16 +86,13 @@ bool egl_shaderLoad(EGL_Shader * this,
DEBUG_INFO("Loaded fragment shader: %s", fragment_file); DEBUG_INFO("Loaded fragment shader: %s", fragment_file);
bool ret = egl_shaderCompile(this, bool ret = egl_shaderCompile(this, vertex_code, vertex_size, fragment_code, fragment_size);
vertex_code, vertex_size, fragment_code, fragment_size,
useDMA, defines);
free(vertex_code); free(vertex_code);
free(fragment_code); free(fragment_code);
return ret; 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) size_t vertex_size, const char * fragment_code, size_t fragment_size)
{ {
if (this->hasShader) if (this->hasShader)
@@ -210,149 +204,6 @@ static bool shaderCompile(EGL_Shader * this, const char * vertex_code,
return true; 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) void egl_shaderSetUniforms(EGL_Shader * this, EGL_Uniform * uniforms, int count)
{ {
egl_shaderFreeUniforms(this); egl_shaderFreeUniforms(this);

View File

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

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