mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-01-16 16:52:28 +00:00
Compare commits
1 Commits
B6
...
Release/B5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3df7d30cd5 |
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@@ -1,4 +0,0 @@
|
||||
# Jonathan Rubenstein (JJRcop)
|
||||
# - Primary documentation manager. Does not require direct approval for every
|
||||
# - change, but should be consulted for large additions and changes.
|
||||
docs/ jrubcop@gmail.com
|
||||
35
.github/workflows/build.yml
vendored
35
.github/workflows/build.yml
vendored
@@ -12,15 +12,12 @@ jobs:
|
||||
wayland_shell: [xdg-shell, libdecor]
|
||||
build_type: [Release, Debug]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install libdecor PPA
|
||||
run: sudo add-apt-repository ppa:christianrauch/libdecoration
|
||||
if: ${{ matrix.wayland_shell == 'libdecor' }}
|
||||
- name: Install PipeWire repository
|
||||
run: |
|
||||
echo 'deb [trusted=yes] https://pipewire-ubuntu.quantum5.workers.dev ./' | sudo tee /etc/apt/sources.list.d/pipewire.list
|
||||
- name: Update apt
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@@ -31,9 +28,8 @@ jobs:
|
||||
libspice-protocol-dev nettle-dev \
|
||||
libgl-dev libgles-dev \
|
||||
libx11-dev libxss-dev libxi-dev libxinerama-dev libxcursor-dev libxpresent-dev \
|
||||
libwayland-dev libxkbcommon-dev \
|
||||
libsamplerate0-dev libpipewire-0.3-dev libpulse-dev \
|
||||
$([ '${{ matrix.wayland_shell }}' = libdecor ] && echo 'libdecor-0-dev libdbus-1-dev') \
|
||||
libwayland-dev wayland-protocols libxkbcommon-dev \
|
||||
$([ '${{ matrix.wayland_shell }}' = libdecor ] && echo 'libdecor-dev libdbus-1-dev') \
|
||||
$([ '${{ matrix.compiler.cc }}' = clang ] && echo 'clang-tools')
|
||||
sudo pip3 install pyenchant
|
||||
- name: Configure client
|
||||
@@ -47,7 +43,7 @@ jobs:
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
|
||||
-DCMAKE_LINKER:FILEPATH=/usr/bin/ld \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DENABLE_LIBDECOR=${{ matrix.wayland_shell == 'libdecor' }} \
|
||||
-DENABLE_LIBDECOR=${{ matrix.wayland_shell == 'libdecor' }} \
|
||||
..
|
||||
- name: Build client
|
||||
run: |
|
||||
@@ -62,7 +58,7 @@ jobs:
|
||||
module:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build kernel module
|
||||
@@ -73,19 +69,15 @@ jobs:
|
||||
host-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install PipeWire repository
|
||||
run: |
|
||||
echo 'deb [trusted=yes] https://pipewire-ubuntu.quantum5.workers.dev ./' | sudo tee /etc/apt/sources.list.d/pipewire.list
|
||||
- name: Update apt
|
||||
run: |
|
||||
sudo apt-get update
|
||||
- name: Install Linux host dependencies
|
||||
run: |
|
||||
sudo apt-get install binutils-dev libxcb-xfixes0-dev \
|
||||
libpipewire-0.3-dev
|
||||
sudo apt-get install binutils-dev libgl1-mesa-dev libxcb-xfixes0-dev
|
||||
- name: Configure Linux host
|
||||
run: |
|
||||
mkdir host/build
|
||||
@@ -99,7 +91,7 @@ jobs:
|
||||
host-windows-cross:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Update apt
|
||||
@@ -121,16 +113,11 @@ jobs:
|
||||
run: |
|
||||
cd host/build
|
||||
makensis platform/Windows/installer.nsi
|
||||
- name: Build Windows host installer with IVSHMEM drivers
|
||||
run: |
|
||||
cd host/build
|
||||
curl https://dl.quantum2.xyz/ivshmem.tar.gz | tar xz
|
||||
makensis -DIVSHMEM platform/Windows/installer.nsi
|
||||
|
||||
host-windows-native:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Configure Windows host for native MinGW-w64
|
||||
@@ -153,7 +140,7 @@ jobs:
|
||||
matrix:
|
||||
cc: [gcc, clang]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Update apt
|
||||
@@ -183,7 +170,7 @@ jobs:
|
||||
sudo apt-get update
|
||||
- name: Install docs dependencies
|
||||
run: |
|
||||
sudo apt-get install python3-sphinx python3-sphinx-rtd-theme
|
||||
sudo apt-get install python3-sphinx
|
||||
sudo pip3 install sphinxcontrib-spelling
|
||||
- name: Build docs
|
||||
run: |
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -7,9 +7,3 @@
|
||||
[submodule "repos/cimgui"]
|
||||
path = repos/cimgui
|
||||
url = https://github.com/cimgui/cimgui.git
|
||||
[submodule "repos/wayland-protocols"]
|
||||
path = repos/wayland-protocols
|
||||
url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git
|
||||
[submodule "repos/nanosvg"]
|
||||
path = repos/nanosvg
|
||||
url = https://github.com/memononen/nanosvg.git
|
||||
|
||||
13
AUTHORS
13
AUTHORS
@@ -9,7 +9,7 @@ arcnmx <arcnmx@users.noreply.github.com> (arcnmx)
|
||||
TheCakeIsNaOH <TheCakeIsNaOH@gmail.com> (TheCakeIsNaOH)
|
||||
NamoDev <namodev@users.noreply.github.com> (NamoDev)
|
||||
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>
|
||||
Mikko Rasa <tdb@tdb.fi> (DataBeaver)
|
||||
Omar Pakker <Omar007@users.noreply.github.com> (Omar007)
|
||||
@@ -54,14 +54,3 @@ thejavascriptman <thejavascriptman@outlook.com> (thejavascriptman)
|
||||
vroad <396351+vroad@users.noreply.github.com> (vroad)
|
||||
williamvds <w.vigolodasilva@gmail.com> (williamvds)
|
||||
SytheZN <sythe.zn@gmail.com> (SytheZN)
|
||||
RTXUX <wyf@rtxux.xyz> (RTXUX)
|
||||
Vincent LaRocca <vincentmlarocca@gmail.com> (VMFortress)
|
||||
Johnathon Paul Weaver <weaver123_johnathon@hotmail.com> (8BallBomBom)
|
||||
Chris Spencer <spencercw@gmail.com> (spencercw)
|
||||
Mark Boorer <markboo99@gmail.com> (Shootfast)
|
||||
babbaj <babbaj45@gmail.com> (Babbaj)
|
||||
Matthew McMullin <matthew@mcmullin.one> (matthewjmc)
|
||||
Leonard Fricke <leonard.fricke98@gmail.com> (Leo1998)
|
||||
David Meier <meier_david_91@hotmail.com> (Kenny.ch)
|
||||
Daniel Cordero <looking-glass@0xdc.io> (0xdc)
|
||||
esi <git@esibun.net> (esibun)
|
||||
|
||||
@@ -19,4 +19,4 @@ https://looking-glass.io/downloads
|
||||
Source code for the documentation can be found in the `/doc` directory.
|
||||
|
||||
You may view this locally as HTML by running `make html` with `python3-sphinx`
|
||||
and `python3-sphinx-rtd-theme` installed.
|
||||
installed.
|
||||
|
||||
@@ -4,13 +4,13 @@ project(looking-glass-client C CXX)
|
||||
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
|
||||
|
||||
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
|
||||
message(FATAL_ERROR
|
||||
"\n"
|
||||
"In-source builds are not supported\n"
|
||||
"See build instructions provided in: "
|
||||
"${PROJECT_TOP}/doc/build.rst\n"
|
||||
"Refusing to continue"
|
||||
)
|
||||
message(FATAL_ERROR
|
||||
"\n"
|
||||
"In-source builds are not supported\n"
|
||||
"See build instructions provided in: "
|
||||
"${PROJECT_TOP}/doc/build.rst\n"
|
||||
"Refusing to continue"
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_TOP}/cmake/" "${PROJECT_SOURCE_DIR}/cmake/")
|
||||
@@ -51,12 +51,6 @@ add_feature_info(ENABLE_WAYLAND ENABLE_WAYLAND "Wayland support.")
|
||||
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
|
||||
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
|
||||
|
||||
option(ENABLE_PIPEWIRE "Build with PipeWire audio output support" ON)
|
||||
add_feature_info(ENABLE_PIPEWIRE ENABLE_PIPEWIRE "PipeWire audio support.")
|
||||
|
||||
option(ENABLE_PULSEAUDIO "Build with PulseAudio audio output support" ON)
|
||||
add_feature_info(ENABLE_PULSEAUDIO ENABLE_PULSEAUDIO "PulseAudio audio support.")
|
||||
|
||||
if (NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
|
||||
message(FATAL_ERROR "Either ENABLE_X11 or ENABLE_WAYLAND must be on")
|
||||
endif()
|
||||
@@ -66,7 +60,6 @@ add_compile_options(
|
||||
"-Wextra"
|
||||
"-Wno-sign-compare"
|
||||
"-Wno-unused-parameter"
|
||||
"$<$<COMPILE_LANGUAGE:C>:-Wstrict-prototypes>"
|
||||
"$<$<C_COMPILER_ID:GNU>:-Wimplicit-fallthrough=2>"
|
||||
"-Werror"
|
||||
"-Wfatal-errors"
|
||||
@@ -101,57 +94,49 @@ add_definitions(-D ATOMIC_LOCKING)
|
||||
add_definitions(-D GL_GLEXT_PROTOTYPES)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/version.c
|
||||
${CMAKE_BINARY_DIR}/_version.c
|
||||
COMMAND ${CMAKE_COMMAND} -D PROJECT_TOP=${PROJECT_TOP} -P
|
||||
${PROJECT_TOP}/version.cmake
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/version.c
|
||||
${CMAKE_BINARY_DIR}/_version.c
|
||||
COMMAND ${CMAKE_COMMAND} -D PROJECT_TOP=${PROJECT_TOP} -P
|
||||
${PROJECT_TOP}/version.cmake
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${PROJECT_TOP}
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
${PROJECT_TOP}/repos/nanosvg/src
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
)
|
||||
|
||||
link_libraries(
|
||||
${CMAKE_DL_LIBS}
|
||||
rt
|
||||
m
|
||||
${CMAKE_DL_LIBS}
|
||||
rt
|
||||
m
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
${CMAKE_BINARY_DIR}/version.c
|
||||
src/main.c
|
||||
src/core.c
|
||||
src/app.c
|
||||
src/audio.c
|
||||
src/config.c
|
||||
src/keybind.c
|
||||
src/util.c
|
||||
src/clipboard.c
|
||||
src/kb.c
|
||||
src/gl_dynprocs.c
|
||||
src/egl_dynprocs.c
|
||||
src/eglutil.c
|
||||
src/overlay_utils.c
|
||||
src/render_queue.c
|
||||
${CMAKE_BINARY_DIR}/version.c
|
||||
src/main.c
|
||||
src/core.c
|
||||
src/app.c
|
||||
src/config.c
|
||||
src/keybind.c
|
||||
src/ll.c
|
||||
src/util.c
|
||||
src/clipboard.c
|
||||
src/kb.c
|
||||
src/gl_dynprocs.c
|
||||
src/egl_dynprocs.c
|
||||
src/eglutil.c
|
||||
src/overlay_utils.c
|
||||
|
||||
src/overlay/splash.c
|
||||
src/overlay/alert.c
|
||||
src/overlay/fps.c
|
||||
src/overlay/graphs.c
|
||||
src/overlay/help.c
|
||||
src/overlay/config.c
|
||||
src/overlay/msg.c
|
||||
src/overlay/status.c
|
||||
src/overlay/alert.c
|
||||
src/overlay/fps.c
|
||||
src/overlay/graphs.c
|
||||
src/overlay/help.c
|
||||
src/overlay/config.c
|
||||
)
|
||||
|
||||
# Force cimgui to build as a static library.
|
||||
set(IMGUI_STATIC "yes" CACHE STRING "Build as a static library")
|
||||
|
||||
add_subdirectory("${PROJECT_TOP}/resources" "${CMAKE_BINARY_DIR}/resources")
|
||||
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common" )
|
||||
add_subdirectory("${PROJECT_TOP}/repos/LGMP/lgmp" "${CMAKE_BINARY_DIR}/LGMP" )
|
||||
add_subdirectory("${PROJECT_TOP}/repos/PureSpice" "${CMAKE_BINARY_DIR}/PureSpice")
|
||||
@@ -165,29 +150,18 @@ add_executable(looking-glass-client ${SOURCES})
|
||||
target_compile_definitions(looking-glass-client PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1)
|
||||
|
||||
target_link_libraries(looking-glass-client
|
||||
${EXE_FLAGS}
|
||||
PkgConfig::FONTCONFIG
|
||||
lg_resources
|
||||
lg_common
|
||||
displayservers
|
||||
lgmp
|
||||
purespice
|
||||
renderers
|
||||
cimgui
|
||||
${EXE_FLAGS}
|
||||
PkgConfig::FONTCONFIG
|
||||
lg_common
|
||||
displayservers
|
||||
lgmp
|
||||
purespice
|
||||
renderers
|
||||
cimgui
|
||||
)
|
||||
|
||||
if (ENABLE_PIPEWIRE OR ENABLE_PULSEAUDIO)
|
||||
add_definitions(-D ENABLE_AUDIO)
|
||||
add_subdirectory(audiodevs)
|
||||
pkg_check_modules(SAMPLERATE REQUIRED IMPORTED_TARGET samplerate)
|
||||
target_link_libraries(looking-glass-client
|
||||
PkgConfig::SAMPLERATE
|
||||
audiodevs
|
||||
)
|
||||
endif()
|
||||
|
||||
install(TARGETS looking-glass-client
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
COMPONENT binary)
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
COMPONENT binary)
|
||||
|
||||
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(audiodevs LANGUAGES C)
|
||||
|
||||
set(AUDIODEV_H "${CMAKE_BINARY_DIR}/include/dynamic/audiodev.h")
|
||||
set(AUDIODEV_C "${CMAKE_BINARY_DIR}/src/audiodev.c")
|
||||
|
||||
file(WRITE ${AUDIODEV_H} "#include \"interface/audiodev.h\"\n\n")
|
||||
file(APPEND ${AUDIODEV_H} "extern struct LG_AudioDevOps * LG_AudioDevs[];\n\n")
|
||||
|
||||
file(WRITE ${AUDIODEV_C} "#include \"interface/audiodev.h\"\n\n")
|
||||
file(APPEND ${AUDIODEV_C} "#include <stddef.h>\n\n")
|
||||
|
||||
set(AUDIODEVS "_")
|
||||
set(AUDIODEVS_LINK "_")
|
||||
function(add_audiodev name)
|
||||
set(AUDIODEVS "${AUDIODEVS};${name}" PARENT_SCOPE)
|
||||
set(AUDIODEVS_LINK "${AUDIODEVS_LINK};audiodev_${name}" PARENT_SCOPE)
|
||||
add_subdirectory(${name})
|
||||
endfunction()
|
||||
|
||||
# Add/remove audiodevs here!
|
||||
if(ENABLE_PIPEWIRE)
|
||||
add_audiodev(PipeWire)
|
||||
endif()
|
||||
if(ENABLE_PULSEAUDIO)
|
||||
add_audiodev(PulseAudio)
|
||||
endif()
|
||||
|
||||
list(REMOVE_AT AUDIODEVS 0)
|
||||
list(REMOVE_AT AUDIODEVS_LINK 0)
|
||||
|
||||
list(LENGTH AUDIODEVS AUDIODEV_COUNT)
|
||||
file(APPEND ${AUDIODEV_H} "#define LG_AUDIODEV_COUNT ${AUDIODEV_COUNT}\n")
|
||||
|
||||
foreach(audiodev ${AUDIODEVS})
|
||||
file(APPEND ${AUDIODEV_C} "extern struct LG_AudioDevOps LGAD_${audiodev};\n")
|
||||
endforeach()
|
||||
|
||||
file(APPEND ${AUDIODEV_C} "\nconst struct LG_AudioDevOps * LG_AudioDevs[] =\n{\n")
|
||||
foreach(audiodev ${AUDIODEVS})
|
||||
file(APPEND ${AUDIODEV_C} " &LGAD_${audiodev},\n")
|
||||
endforeach()
|
||||
file(APPEND ${AUDIODEV_C} " NULL\n};")
|
||||
|
||||
add_library(audiodevs STATIC ${AUDIODEV_C})
|
||||
target_link_libraries(audiodevs ${AUDIODEVS_LINK})
|
||||
@@ -1,21 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(audiodev_PipeWire LANGUAGES C)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(AUDIODEV_PipeWire REQUIRED IMPORTED_TARGET
|
||||
libpipewire-0.3
|
||||
)
|
||||
|
||||
add_library(audiodev_PipeWire STATIC
|
||||
pipewire.c
|
||||
)
|
||||
|
||||
target_link_libraries(audiodev_PipeWire
|
||||
PkgConfig::AUDIODEV_PipeWire
|
||||
lg_common
|
||||
)
|
||||
|
||||
target_include_directories(audiodev_PipeWire
|
||||
PRIVATE
|
||||
src
|
||||
)
|
||||
@@ -1,565 +0,0 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "interface/audiodev.h"
|
||||
|
||||
#include <spa/param/audio/format-utils.h>
|
||||
#include <spa/param/props.h>
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/stringutils.h"
|
||||
#include "common/util.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
STREAM_STATE_INACTIVE,
|
||||
STREAM_STATE_ACTIVE,
|
||||
STREAM_STATE_DRAINING
|
||||
}
|
||||
StreamState;
|
||||
|
||||
struct PipeWire
|
||||
{
|
||||
struct pw_loop * loop;
|
||||
struct pw_context * context;
|
||||
struct pw_thread_loop * thread;
|
||||
|
||||
struct
|
||||
{
|
||||
struct pw_stream * stream;
|
||||
struct spa_io_rate_match * rateMatch;
|
||||
|
||||
int channels;
|
||||
int sampleRate;
|
||||
int stride;
|
||||
LG_AudioPullFn pullFn;
|
||||
int maxPeriodFrames;
|
||||
int startFrames;
|
||||
|
||||
StreamState state;
|
||||
}
|
||||
playback;
|
||||
|
||||
struct
|
||||
{
|
||||
struct pw_stream * stream;
|
||||
|
||||
int channels;
|
||||
int sampleRate;
|
||||
int stride;
|
||||
LG_AudioPushFn pushFn;
|
||||
|
||||
bool active;
|
||||
}
|
||||
record;
|
||||
};
|
||||
|
||||
static struct PipeWire pw = {0};
|
||||
|
||||
static void pipewire_onPlaybackIoChanged(void * userdata, uint32_t id,
|
||||
void * data, uint32_t size)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case SPA_IO_RateMatch:
|
||||
pw.playback.rateMatch = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void pipewire_onPlaybackProcess(void * userdata)
|
||||
{
|
||||
struct pw_buffer * pbuf;
|
||||
|
||||
if (!(pbuf = pw_stream_dequeue_buffer(pw.playback.stream)))
|
||||
{
|
||||
DEBUG_WARN("out of buffers");
|
||||
return;
|
||||
}
|
||||
|
||||
struct spa_buffer * sbuf = pbuf->buffer;
|
||||
uint8_t * dst;
|
||||
|
||||
if (!(dst = sbuf->datas[0].data))
|
||||
return;
|
||||
|
||||
int frames = sbuf->datas[0].maxsize / pw.playback.stride;
|
||||
if (pw.playback.rateMatch && pw.playback.rateMatch->size > 0)
|
||||
frames = min(frames, pw.playback.rateMatch->size);
|
||||
|
||||
frames = pw.playback.pullFn(dst, frames);
|
||||
if (!frames)
|
||||
{
|
||||
sbuf->datas[0].chunk->size = 0;
|
||||
pw_stream_queue_buffer(pw.playback.stream, pbuf);
|
||||
return;
|
||||
}
|
||||
|
||||
sbuf->datas[0].chunk->offset = 0;
|
||||
sbuf->datas[0].chunk->stride = pw.playback.stride;
|
||||
sbuf->datas[0].chunk->size = frames * pw.playback.stride;
|
||||
|
||||
pw_stream_queue_buffer(pw.playback.stream, pbuf);
|
||||
}
|
||||
|
||||
static void pipewire_onPlaybackDrained(void * userdata)
|
||||
{
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
pw_stream_set_active(pw.playback.stream, false);
|
||||
pw.playback.state = STREAM_STATE_INACTIVE;
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
}
|
||||
|
||||
static bool pipewire_init(void)
|
||||
{
|
||||
pw_init(NULL, NULL);
|
||||
|
||||
pw.loop = pw_loop_new(NULL);
|
||||
pw.context = pw_context_new(
|
||||
pw.loop,
|
||||
pw_properties_new(
|
||||
// Request real-time priority on the PipeWire threads
|
||||
PW_KEY_CONFIG_NAME, "client-rt.conf",
|
||||
NULL
|
||||
),
|
||||
0);
|
||||
if (!pw.context)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create a context");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* this is just to test for PipeWire availabillity */
|
||||
struct pw_core * core = pw_context_connect(pw.context, NULL, 0);
|
||||
if (!core)
|
||||
goto err_context;
|
||||
|
||||
/* PipeWire is available so create the loop thread and start it */
|
||||
pw.thread = pw_thread_loop_new_full(pw.loop, "PipeWire", NULL);
|
||||
if (!pw.thread)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the thread loop");
|
||||
goto err_context;
|
||||
}
|
||||
|
||||
pw_thread_loop_start(pw.thread);
|
||||
return true;
|
||||
|
||||
err_context:
|
||||
pw_context_destroy(pw.context);
|
||||
|
||||
err:
|
||||
pw_loop_destroy(pw.loop);
|
||||
pw_deinit();
|
||||
return false;
|
||||
}
|
||||
|
||||
static void pipewire_playbackStopStream(void)
|
||||
{
|
||||
if (!pw.playback.stream)
|
||||
return;
|
||||
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
pw_stream_destroy(pw.playback.stream);
|
||||
pw.playback.stream = NULL;
|
||||
pw.playback.rateMatch = NULL;
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
}
|
||||
|
||||
static void pipewire_playbackSetup(int channels, int sampleRate,
|
||||
int requestedPeriodFrames, int * maxPeriodFrames, int * startFrames,
|
||||
LG_AudioPullFn pullFn)
|
||||
{
|
||||
const struct spa_pod * params[1];
|
||||
uint8_t buffer[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
static const struct pw_stream_events events =
|
||||
{
|
||||
.version = PW_VERSION_STREAM_EVENTS,
|
||||
.io_changed = pipewire_onPlaybackIoChanged,
|
||||
.process = pipewire_onPlaybackProcess,
|
||||
.drained = pipewire_onPlaybackDrained
|
||||
};
|
||||
|
||||
if (pw.playback.stream &&
|
||||
pw.playback.channels == channels &&
|
||||
pw.playback.sampleRate == sampleRate)
|
||||
{
|
||||
*maxPeriodFrames = pw.playback.maxPeriodFrames;
|
||||
*startFrames = pw.playback.startFrames;
|
||||
return;
|
||||
}
|
||||
|
||||
pipewire_playbackStopStream();
|
||||
|
||||
char requestedNodeLatency[32];
|
||||
snprintf(requestedNodeLatency, sizeof(requestedNodeLatency), "%d/%d",
|
||||
requestedPeriodFrames, sampleRate);
|
||||
|
||||
pw.playback.channels = channels;
|
||||
pw.playback.sampleRate = sampleRate;
|
||||
pw.playback.stride = sizeof(float) * channels;
|
||||
pw.playback.pullFn = pullFn;
|
||||
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
pw.playback.stream = pw_stream_new_simple(
|
||||
pw.loop,
|
||||
"Looking Glass",
|
||||
pw_properties_new(
|
||||
PW_KEY_NODE_NAME , "Looking Glass",
|
||||
PW_KEY_MEDIA_TYPE , "Audio",
|
||||
PW_KEY_MEDIA_CATEGORY, "Playback",
|
||||
PW_KEY_MEDIA_ROLE , "Music",
|
||||
PW_KEY_NODE_LATENCY , requestedNodeLatency,
|
||||
NULL
|
||||
),
|
||||
&events,
|
||||
NULL
|
||||
);
|
||||
|
||||
// The user can override the default node latency with the PIPEWIRE_LATENCY
|
||||
// environment variable, so get the actual node latency value from the stream.
|
||||
// The actual quantum size may be lower than this value depending on what else
|
||||
// is using the audio device, but we can treat this value as a maximum
|
||||
const struct pw_properties * properties =
|
||||
pw_stream_get_properties(pw.playback.stream);
|
||||
const char *actualNodeLatency =
|
||||
pw_properties_get(properties, PW_KEY_NODE_LATENCY);
|
||||
DEBUG_ASSERT(actualNodeLatency != NULL);
|
||||
|
||||
unsigned num, denom;
|
||||
if (sscanf(actualNodeLatency, "%u/%u", &num, &denom) != 2 ||
|
||||
denom != sampleRate)
|
||||
{
|
||||
DEBUG_WARN(
|
||||
"PIPEWIRE_LATENCY value '%s' is invalid or does not match stream sample "
|
||||
"rate; using %d/%d", actualNodeLatency, requestedPeriodFrames,
|
||||
sampleRate);
|
||||
|
||||
struct spa_dict_item items[] = {
|
||||
{ PW_KEY_NODE_LATENCY, requestedNodeLatency }
|
||||
};
|
||||
pw_stream_update_properties(pw.playback.stream,
|
||||
&SPA_DICT_INIT_ARRAY(items));
|
||||
|
||||
pw.playback.maxPeriodFrames = requestedPeriodFrames;
|
||||
}
|
||||
else
|
||||
pw.playback.maxPeriodFrames = num;
|
||||
|
||||
// If the previous quantum size was very small, PipeWire can request two full
|
||||
// periods almost immediately at the start of playback
|
||||
pw.playback.startFrames = pw.playback.maxPeriodFrames * 2;
|
||||
|
||||
*maxPeriodFrames = pw.playback.maxPeriodFrames;
|
||||
*startFrames = pw.playback.startFrames;
|
||||
|
||||
if (!pw.playback.stream)
|
||||
{
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
DEBUG_ERROR("Failed to create the stream");
|
||||
return;
|
||||
}
|
||||
|
||||
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
|
||||
&SPA_AUDIO_INFO_RAW_INIT(
|
||||
.format = SPA_AUDIO_FORMAT_F32,
|
||||
.channels = channels,
|
||||
.rate = sampleRate
|
||||
));
|
||||
|
||||
pw_stream_connect(
|
||||
pw.playback.stream,
|
||||
PW_DIRECTION_OUTPUT,
|
||||
PW_ID_ANY,
|
||||
PW_STREAM_FLAG_AUTOCONNECT |
|
||||
PW_STREAM_FLAG_MAP_BUFFERS |
|
||||
PW_STREAM_FLAG_RT_PROCESS |
|
||||
PW_STREAM_FLAG_INACTIVE,
|
||||
params, 1);
|
||||
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
}
|
||||
|
||||
static void pipewire_playbackStart(void)
|
||||
{
|
||||
if (!pw.playback.stream)
|
||||
return;
|
||||
|
||||
if (pw.playback.state != STREAM_STATE_ACTIVE)
|
||||
{
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
|
||||
switch (pw.playback.state)
|
||||
{
|
||||
case STREAM_STATE_INACTIVE:
|
||||
pw_stream_set_active(pw.playback.stream, true);
|
||||
pw.playback.state = STREAM_STATE_ACTIVE;
|
||||
break;
|
||||
|
||||
case STREAM_STATE_DRAINING:
|
||||
// We are in the middle of draining the PipeWire buffers; we need to
|
||||
// wait for this to complete before allowing the new playback to start
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_UNREACHABLE();
|
||||
}
|
||||
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
}
|
||||
}
|
||||
|
||||
static void pipewire_playbackStop(void)
|
||||
{
|
||||
if (pw.playback.state != STREAM_STATE_ACTIVE)
|
||||
return;
|
||||
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
pw_stream_flush(pw.playback.stream, true);
|
||||
pw.playback.state = STREAM_STATE_DRAINING;
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
}
|
||||
|
||||
static void pipewire_playbackVolume(int channels, const uint16_t volume[])
|
||||
{
|
||||
if (channels != pw.playback.channels)
|
||||
return;
|
||||
|
||||
float param[channels];
|
||||
for(int i = 0; i < channels; ++i)
|
||||
param[i] = 9.3234e-7 * pow(1.000211902, volume[i]) - 0.000172787;
|
||||
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
pw_stream_set_control(pw.playback.stream, SPA_PROP_channelVolumes,
|
||||
channels, param, 0);
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
}
|
||||
|
||||
static void pipewire_playbackMute(bool mute)
|
||||
{
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
float val = mute ? 1.0f : 0.0f;
|
||||
pw_stream_set_control(pw.playback.stream, SPA_PROP_mute, 1, &val, 0);
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
}
|
||||
|
||||
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_stream_get_time_n(pw.playback.stream, &time, sizeof(time)) < 0)
|
||||
#else
|
||||
if (pw_stream_get_time(pw.playback.stream, &time) < 0)
|
||||
#endif
|
||||
DEBUG_ERROR("pw_stream_get_time failed");
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
|
||||
return time.delay + time.queued / pw.playback.stride;
|
||||
}
|
||||
|
||||
static void pipewire_recordStopStream(void)
|
||||
{
|
||||
if (!pw.record.stream)
|
||||
return;
|
||||
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
pw_stream_destroy(pw.record.stream);
|
||||
pw.record.stream = NULL;
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
}
|
||||
|
||||
static void pipewire_onRecordProcess(void * userdata)
|
||||
{
|
||||
struct pw_buffer * pbuf;
|
||||
|
||||
if (!(pbuf = pw_stream_dequeue_buffer(pw.record.stream)))
|
||||
{
|
||||
DEBUG_WARN("out of buffers");
|
||||
return;
|
||||
}
|
||||
|
||||
struct spa_buffer * sbuf = pbuf->buffer;
|
||||
uint8_t * dst;
|
||||
|
||||
if (!(dst = sbuf->datas[0].data))
|
||||
return;
|
||||
|
||||
dst += sbuf->datas[0].chunk->offset;
|
||||
pw.record.pushFn(dst,
|
||||
min(
|
||||
sbuf->datas[0].chunk->size,
|
||||
sbuf->datas[0].maxsize - sbuf->datas[0].chunk->offset) / pw.record.stride
|
||||
);
|
||||
|
||||
pw_stream_queue_buffer(pw.record.stream, pbuf);
|
||||
}
|
||||
|
||||
static void pipewire_recordStart(int channels, int sampleRate,
|
||||
LG_AudioPushFn pushFn)
|
||||
{
|
||||
const struct spa_pod * params[1];
|
||||
uint8_t buffer[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
static const struct pw_stream_events events =
|
||||
{
|
||||
.version = PW_VERSION_STREAM_EVENTS,
|
||||
.process = pipewire_onRecordProcess
|
||||
};
|
||||
|
||||
if (pw.record.stream &&
|
||||
pw.record.channels == channels &&
|
||||
pw.record.sampleRate == sampleRate)
|
||||
{
|
||||
if (!pw.record.active)
|
||||
{
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
pw_stream_set_active(pw.record.stream, true);
|
||||
pw.record.active = true;
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
pipewire_recordStopStream();
|
||||
|
||||
pw.record.channels = channels;
|
||||
pw.record.sampleRate = sampleRate;
|
||||
pw.record.stride = sizeof(uint16_t) * channels;
|
||||
pw.record.pushFn = pushFn;
|
||||
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
pw.record.stream = pw_stream_new_simple(
|
||||
pw.loop,
|
||||
"Looking Glass",
|
||||
pw_properties_new(
|
||||
PW_KEY_NODE_NAME , "Looking Glass",
|
||||
PW_KEY_MEDIA_TYPE , "Audio",
|
||||
PW_KEY_MEDIA_CATEGORY, "Capture",
|
||||
PW_KEY_MEDIA_ROLE , "Music",
|
||||
NULL
|
||||
),
|
||||
&events,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (!pw.record.stream)
|
||||
{
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
DEBUG_ERROR("Failed to create the stream");
|
||||
return;
|
||||
}
|
||||
|
||||
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
|
||||
&SPA_AUDIO_INFO_RAW_INIT(
|
||||
.format = SPA_AUDIO_FORMAT_S16,
|
||||
.channels = channels,
|
||||
.rate = sampleRate
|
||||
));
|
||||
|
||||
pw_stream_connect(
|
||||
pw.record.stream,
|
||||
PW_DIRECTION_INPUT,
|
||||
PW_ID_ANY,
|
||||
PW_STREAM_FLAG_AUTOCONNECT |
|
||||
PW_STREAM_FLAG_MAP_BUFFERS |
|
||||
PW_STREAM_FLAG_RT_PROCESS,
|
||||
params, 1);
|
||||
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
pw.record.active = true;
|
||||
}
|
||||
|
||||
static void pipewire_recordStop(void)
|
||||
{
|
||||
if (!pw.record.active)
|
||||
return;
|
||||
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
pw_stream_set_active(pw.record.stream, false);
|
||||
pw.record.active = false;
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
}
|
||||
|
||||
static void pipewire_recordVolume(int channels, const uint16_t volume[])
|
||||
{
|
||||
if (channels != pw.record.channels)
|
||||
return;
|
||||
|
||||
float param[channels];
|
||||
for(int i = 0; i < channels; ++i)
|
||||
param[i] = 9.3234e-7 * pow(1.000211902, volume[i]) - 0.000172787;
|
||||
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
pw_stream_set_control(pw.record.stream, SPA_PROP_channelVolumes,
|
||||
channels, param, 0);
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
}
|
||||
|
||||
static void pipewire_recordMute(bool mute)
|
||||
{
|
||||
pw_thread_loop_lock(pw.thread);
|
||||
float val = mute ? 1.0f : 0.0f;
|
||||
pw_stream_set_control(pw.record.stream, SPA_PROP_mute, 1, &val, 0);
|
||||
pw_thread_loop_unlock(pw.thread);
|
||||
}
|
||||
|
||||
static void pipewire_free(void)
|
||||
{
|
||||
pipewire_playbackStopStream();
|
||||
pipewire_recordStopStream();
|
||||
pw_thread_loop_stop(pw.thread);
|
||||
pw_thread_loop_destroy(pw.thread);
|
||||
pw_context_destroy(pw.context);
|
||||
pw_loop_destroy(pw.loop);
|
||||
|
||||
pw.loop = NULL;
|
||||
pw.context = NULL;
|
||||
pw.thread = NULL;
|
||||
|
||||
pw_deinit();
|
||||
}
|
||||
|
||||
struct LG_AudioDevOps LGAD_PipeWire =
|
||||
{
|
||||
.name = "PipeWire",
|
||||
.init = pipewire_init,
|
||||
.free = pipewire_free,
|
||||
.playback =
|
||||
{
|
||||
.setup = pipewire_playbackSetup,
|
||||
.start = pipewire_playbackStart,
|
||||
.stop = pipewire_playbackStop,
|
||||
.volume = pipewire_playbackVolume,
|
||||
.mute = pipewire_playbackMute,
|
||||
.latency = pipewire_playbackLatency
|
||||
},
|
||||
.record =
|
||||
{
|
||||
.start = pipewire_recordStart,
|
||||
.stop = pipewire_recordStop,
|
||||
.volume = pipewire_recordVolume,
|
||||
.mute = pipewire_recordMute
|
||||
}
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(audiodev_PulseAudio LANGUAGES C)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(AUDIODEV_PulseAudio REQUIRED IMPORTED_TARGET
|
||||
libpulse
|
||||
)
|
||||
|
||||
add_library(audiodev_PulseAudio STATIC
|
||||
pulseaudio.c
|
||||
)
|
||||
|
||||
target_link_libraries(audiodev_PulseAudio
|
||||
PkgConfig::AUDIODEV_PulseAudio
|
||||
lg_common
|
||||
)
|
||||
|
||||
target_include_directories(audiodev_PulseAudio
|
||||
PRIVATE
|
||||
src
|
||||
)
|
||||
@@ -1,393 +0,0 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "interface/audiodev.h"
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
struct PulseAudio
|
||||
{
|
||||
pa_threaded_mainloop * loop;
|
||||
pa_mainloop_api * api;
|
||||
pa_context * context;
|
||||
pa_operation * contextSub;
|
||||
|
||||
pa_stream * sink;
|
||||
int sinkIndex;
|
||||
bool sinkCorked;
|
||||
bool sinkMuted;
|
||||
bool sinkStarting;
|
||||
int sinkMaxPeriodFrames;
|
||||
int sinkStartFrames;
|
||||
int sinkSampleRate;
|
||||
int sinkChannels;
|
||||
int sinkStride;
|
||||
LG_AudioPullFn sinkPullFn;
|
||||
};
|
||||
|
||||
static struct PulseAudio pa = {0};
|
||||
|
||||
static void pulseaudio_sink_input_cb(pa_context *c, const pa_sink_input_info *i,
|
||||
int eol, void *userdata)
|
||||
{
|
||||
if (eol < 0 || eol == 1)
|
||||
return;
|
||||
|
||||
pa.sinkIndex = i->index;
|
||||
}
|
||||
|
||||
static void pulseaudio_subscribe_cb(pa_context *c,
|
||||
pa_subscription_event_type_t t, uint32_t index, void *userdata)
|
||||
{
|
||||
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)
|
||||
{
|
||||
case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
|
||||
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
|
||||
pa.sinkIndex = 0;
|
||||
else
|
||||
{
|
||||
pa_operation *o = pa_context_get_sink_input_info(c, index,
|
||||
pulseaudio_sink_input_cb, NULL);
|
||||
pa_operation_unref(o);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void pulseaudio_ctx_state_change_cb(pa_context * c, void * userdata)
|
||||
{
|
||||
switch (pa_context_get_state(c))
|
||||
{
|
||||
case PA_CONTEXT_CONNECTING:
|
||||
case PA_CONTEXT_AUTHORIZING:
|
||||
case PA_CONTEXT_SETTING_NAME:
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_READY:
|
||||
DEBUG_INFO("Connected to PulseAudio server");
|
||||
pa_context_set_subscribe_callback(c, pulseaudio_subscribe_cb, NULL);
|
||||
pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL);
|
||||
pa_threaded_mainloop_signal(pa.loop, 0);
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
if (pa.contextSub)
|
||||
{
|
||||
pa_operation_unref(pa.contextSub);
|
||||
pa.contextSub = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_FAILED:
|
||||
default:
|
||||
DEBUG_ERROR("context error: %s", pa_strerror(pa_context_errno(c)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool pulseaudio_init(void)
|
||||
{
|
||||
pa.loop = pa_threaded_mainloop_new();
|
||||
if (!pa.loop)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the main loop");
|
||||
goto err;
|
||||
}
|
||||
|
||||
pa.api = pa_threaded_mainloop_get_api(pa.loop);
|
||||
if (pa_signal_init(pa.api) != 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to init signals");
|
||||
goto err_loop;
|
||||
}
|
||||
|
||||
if (pa_threaded_mainloop_start(pa.loop) < 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to start the main loop");
|
||||
goto err_loop;
|
||||
}
|
||||
|
||||
pa_proplist * propList = pa_proplist_new();
|
||||
if (!propList)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the proplist");
|
||||
goto err_thread;
|
||||
}
|
||||
pa_proplist_sets(propList, PA_PROP_MEDIA_ROLE, "video");
|
||||
|
||||
pa_threaded_mainloop_lock(pa.loop);
|
||||
pa.context = pa_context_new_with_proplist(
|
||||
pa.api,
|
||||
"Looking Glass",
|
||||
propList);
|
||||
if (!pa.context)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the context");
|
||||
goto err_context;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback(pa.context,
|
||||
pulseaudio_ctx_state_change_cb, NULL);
|
||||
|
||||
if (pa_context_connect(pa.context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to connect to the context server");
|
||||
goto err_context;
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
pa_context_state_t state = pa_context_get_state(pa.context);
|
||||
if(!PA_CONTEXT_IS_GOOD(state))
|
||||
{
|
||||
DEBUG_ERROR("Context is bad");
|
||||
goto err_context;
|
||||
}
|
||||
|
||||
if (state == PA_CONTEXT_READY)
|
||||
break;
|
||||
|
||||
pa_threaded_mainloop_wait(pa.loop);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pa.loop);
|
||||
pa_proplist_free(propList);
|
||||
return true;
|
||||
|
||||
err_context:
|
||||
pa_threaded_mainloop_unlock(pa.loop);
|
||||
pa_proplist_free(propList);
|
||||
|
||||
err_thread:
|
||||
pa_threaded_mainloop_stop(pa.loop);
|
||||
|
||||
err_loop:
|
||||
pa_threaded_mainloop_free(pa.loop);
|
||||
|
||||
err:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void pulseaudio_sink_close_nl(void)
|
||||
{
|
||||
if (!pa.sink)
|
||||
return;
|
||||
|
||||
pa_stream_set_write_callback(pa.sink, NULL, NULL);
|
||||
pa_stream_flush(pa.sink, NULL, NULL);
|
||||
pa_stream_unref(pa.sink);
|
||||
pa.sink = NULL;
|
||||
}
|
||||
|
||||
static void pulseaudio_free(void)
|
||||
{
|
||||
pa_threaded_mainloop_lock(pa.loop);
|
||||
|
||||
pulseaudio_sink_close_nl();
|
||||
|
||||
pa_context_set_state_callback(pa.context, NULL, NULL);
|
||||
pa_context_set_subscribe_callback(pa.context, NULL, NULL);
|
||||
pa_context_disconnect(pa.context);
|
||||
pa_context_unref(pa.context);
|
||||
|
||||
if (pa.contextSub)
|
||||
{
|
||||
pa_operation_unref(pa.contextSub);
|
||||
pa.contextSub = NULL;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pa.loop);
|
||||
}
|
||||
|
||||
static void pulseaudio_state_cb(pa_stream * p, void * userdata)
|
||||
{
|
||||
if (pa.sinkStarting && pa_stream_get_state(pa.sink) == PA_STREAM_READY)
|
||||
{
|
||||
pa_stream_cork(pa.sink, 0, NULL, NULL);
|
||||
pa.sinkCorked = false;
|
||||
pa.sinkStarting = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void pulseaudio_write_cb(pa_stream * p, size_t nbytes, void * userdata)
|
||||
{
|
||||
// PulseAudio tries to pull data from the stream as soon as it is created for
|
||||
// some reason, even though it is corked
|
||||
if (pa.sinkCorked)
|
||||
return;
|
||||
|
||||
uint8_t * dst;
|
||||
|
||||
pa_stream_begin_write(p, (void **)&dst, &nbytes);
|
||||
|
||||
int frames = nbytes / pa.sinkStride;
|
||||
frames = pa.sinkPullFn(dst, frames);
|
||||
|
||||
pa_stream_write(p, dst, frames * pa.sinkStride, NULL, 0, PA_SEEK_RELATIVE);
|
||||
}
|
||||
|
||||
static void pulseaudio_underflow_cb(pa_stream * p, void * userdata)
|
||||
{
|
||||
DEBUG_WARN("Underflow");
|
||||
}
|
||||
|
||||
static void pulseaudio_overflow_cb(pa_stream * p, void * userdata)
|
||||
{
|
||||
DEBUG_WARN("Overflow");
|
||||
}
|
||||
|
||||
static void pulseaudio_setup(int channels, int sampleRate,
|
||||
int requestedPeriodFrames, int * maxPeriodFrames, int * startFrames,
|
||||
LG_AudioPullFn pullFn)
|
||||
{
|
||||
if (pa.sink && pa.sinkChannels == channels && pa.sinkSampleRate == sampleRate)
|
||||
{
|
||||
*maxPeriodFrames = pa.sinkMaxPeriodFrames;
|
||||
*startFrames = pa.sinkStartFrames;
|
||||
return;
|
||||
}
|
||||
|
||||
pa_sample_spec spec = {
|
||||
.format = PA_SAMPLE_FLOAT32,
|
||||
.rate = sampleRate,
|
||||
.channels = channels
|
||||
};
|
||||
|
||||
int stride = channels * sizeof(float);
|
||||
int bufferSize = requestedPeriodFrames * 2 * stride;
|
||||
pa_buffer_attr attribs =
|
||||
{
|
||||
.maxlength = -1,
|
||||
.tlength = bufferSize,
|
||||
.prebuf = 0,
|
||||
.minreq = (uint32_t)-1
|
||||
};
|
||||
|
||||
pa_threaded_mainloop_lock(pa.loop);
|
||||
pulseaudio_sink_close_nl();
|
||||
|
||||
pa.sinkChannels = channels;
|
||||
pa.sinkSampleRate = sampleRate;
|
||||
|
||||
pa.sink = pa_stream_new(pa.context, "Looking Glass", &spec, NULL);
|
||||
pa_stream_set_state_callback (pa.sink, pulseaudio_state_cb , NULL);
|
||||
pa_stream_set_write_callback (pa.sink, pulseaudio_write_cb , NULL);
|
||||
pa_stream_set_underflow_callback(pa.sink, pulseaudio_underflow_cb, NULL);
|
||||
pa_stream_set_overflow_callback (pa.sink, pulseaudio_overflow_cb , NULL);
|
||||
|
||||
pa_stream_connect_playback(pa.sink, NULL, &attribs, PA_STREAM_START_CORKED,
|
||||
NULL, NULL);
|
||||
|
||||
pa.sinkStride = stride;
|
||||
pa.sinkPullFn = pullFn;
|
||||
pa.sinkMaxPeriodFrames = requestedPeriodFrames;
|
||||
pa.sinkCorked = true;
|
||||
pa.sinkStarting = false;
|
||||
|
||||
// If something else is, or was recently using a small latency value,
|
||||
// PulseAudio can request way more data at startup than is reasonable
|
||||
pa.sinkStartFrames = requestedPeriodFrames * 4;
|
||||
|
||||
*maxPeriodFrames = requestedPeriodFrames;
|
||||
*startFrames = pa.sinkStartFrames;
|
||||
|
||||
pa_threaded_mainloop_unlock(pa.loop);
|
||||
}
|
||||
|
||||
static void pulseaudio_start(void)
|
||||
{
|
||||
if (!pa.sink)
|
||||
return;
|
||||
|
||||
pa_threaded_mainloop_lock(pa.loop);
|
||||
|
||||
pa_stream_state_t state = pa_stream_get_state(pa.sink);
|
||||
if (state == PA_STREAM_CREATING)
|
||||
pa.sinkStarting = true;
|
||||
else
|
||||
{
|
||||
pa_stream_cork(pa.sink, 0, NULL, NULL);
|
||||
pa.sinkCorked = false;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pa.loop);
|
||||
}
|
||||
|
||||
static void pulseaudio_stop(void)
|
||||
{
|
||||
if (!pa.sink)
|
||||
return;
|
||||
|
||||
bool needLock = !pa_threaded_mainloop_in_thread(pa.loop);
|
||||
if (needLock)
|
||||
pa_threaded_mainloop_lock(pa.loop);
|
||||
|
||||
pa_stream_cork(pa.sink, 1, NULL, NULL);
|
||||
pa.sinkCorked = true;
|
||||
pa.sinkStarting = false;
|
||||
|
||||
if (needLock)
|
||||
pa_threaded_mainloop_unlock(pa.loop);
|
||||
}
|
||||
|
||||
static void pulseaudio_volume(int channels, const uint16_t volume[])
|
||||
{
|
||||
if (!pa.sink || !pa.sinkIndex)
|
||||
return;
|
||||
|
||||
struct pa_cvolume v = { .channels = channels };
|
||||
for(int i = 0; i < channels; ++i)
|
||||
v.values[i] = pa_sw_volume_from_linear(
|
||||
9.3234e-7 * pow(1.000211902, volume[i]) - 0.000172787);
|
||||
|
||||
pa_threaded_mainloop_lock(pa.loop);
|
||||
pa_context_set_sink_input_volume(pa.context, pa.sinkIndex, &v, NULL, NULL);
|
||||
pa_threaded_mainloop_unlock(pa.loop);
|
||||
}
|
||||
|
||||
static void pulseaudio_mute(bool mute)
|
||||
{
|
||||
if (!pa.sink || !pa.sinkIndex || pa.sinkMuted == mute)
|
||||
return;
|
||||
|
||||
pa.sinkMuted = mute;
|
||||
pa_threaded_mainloop_lock(pa.loop);
|
||||
pa_context_set_sink_input_mute(pa.context, pa.sinkIndex, mute, NULL, NULL);
|
||||
pa_threaded_mainloop_unlock(pa.loop);
|
||||
}
|
||||
|
||||
struct LG_AudioDevOps LGAD_PulseAudio =
|
||||
{
|
||||
.name = "PulseAudio",
|
||||
.init = pulseaudio_init,
|
||||
.free = pulseaudio_free,
|
||||
.playback =
|
||||
{
|
||||
.setup = pulseaudio_setup,
|
||||
.start = pulseaudio_start,
|
||||
.stop = pulseaudio_stop,
|
||||
.volume = pulseaudio_volume,
|
||||
.mute = pulseaudio_mute
|
||||
}
|
||||
};
|
||||
@@ -3,99 +3,96 @@ project(displayserver_Wayland LANGUAGES C)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(DISPLAYSERVER_Wayland REQUIRED IMPORTED_TARGET
|
||||
wayland-client
|
||||
wayland-cursor
|
||||
xkbcommon
|
||||
wayland-client
|
||||
wayland-cursor
|
||||
xkbcommon
|
||||
)
|
||||
|
||||
set(DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES "")
|
||||
set(displayserver_Wayland_SHELL_SRC "")
|
||||
|
||||
if (ENABLE_LIBDECOR)
|
||||
pkg_check_modules(DISPLAYSERVER_Wayland_LIBDECOR REQUIRED IMPORTED_TARGET
|
||||
libdecor-0
|
||||
)
|
||||
list(APPEND DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES PkgConfig::DISPLAYSERVER_Wayland_LIBDECOR)
|
||||
list(APPEND displayserver_Wayland_SHELL_SRC shell_libdecor.c)
|
||||
add_compile_definitions(ENABLE_LIBDECOR)
|
||||
pkg_check_modules(DISPLAYSERVER_Wayland_LIBDECOR REQUIRED IMPORTED_TARGET
|
||||
libdecor-0
|
||||
)
|
||||
list(APPEND DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES PkgConfig::DISPLAYSERVER_Wayland_LIBDECOR)
|
||||
list(APPEND displayserver_Wayland_SHELL_SRC shell_libdecor.c)
|
||||
add_compile_definitions(ENABLE_LIBDECOR)
|
||||
else()
|
||||
list(APPEND displayserver_Wayland_SHELL_SRC shell_xdg.c)
|
||||
list(APPEND displayserver_Wayland_SHELL_SRC shell_xdg.c)
|
||||
endif()
|
||||
|
||||
add_library(displayserver_Wayland STATIC
|
||||
activation.c
|
||||
clipboard.c
|
||||
cursor.c
|
||||
gl.c
|
||||
idle.c
|
||||
input.c
|
||||
output.c
|
||||
poll.c
|
||||
presentation.c
|
||||
state.c
|
||||
registry.c
|
||||
wayland.c
|
||||
window.c
|
||||
${displayserver_Wayland_SHELL_SRC}
|
||||
clipboard.c
|
||||
cursor.c
|
||||
gl.c
|
||||
idle.c
|
||||
input.c
|
||||
output.c
|
||||
poll.c
|
||||
presentation.c
|
||||
state.c
|
||||
registry.c
|
||||
wayland.c
|
||||
window.c
|
||||
${displayserver_Wayland_SHELL_SRC}
|
||||
)
|
||||
|
||||
target_link_libraries(displayserver_Wayland
|
||||
PkgConfig::DISPLAYSERVER_Wayland
|
||||
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
|
||||
lg_common
|
||||
PkgConfig::DISPLAYSERVER_Wayland
|
||||
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
|
||||
lg_common
|
||||
)
|
||||
|
||||
target_include_directories(displayserver_Wayland
|
||||
PRIVATE
|
||||
src
|
||||
PRIVATE
|
||||
src
|
||||
)
|
||||
|
||||
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
|
||||
pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols>=1.15)
|
||||
pkg_get_variable(WAYLAND_PROTOCOLS_BASE wayland-protocols pkgdatadir)
|
||||
|
||||
macro(wayland_generate protocol_file output_file)
|
||||
add_custom_command(OUTPUT "${output_file}.h"
|
||||
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" client-header "${protocol_file}" "${output_file}.h"
|
||||
DEPENDS "${protocol_file}"
|
||||
VERBATIM)
|
||||
add_custom_command(OUTPUT "${output_file}.h"
|
||||
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" client-header "${protocol_file}" "${output_file}.h"
|
||||
DEPENDS "${protocol_file}"
|
||||
VERBATIM)
|
||||
|
||||
add_custom_command(OUTPUT "${output_file}.c"
|
||||
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c"
|
||||
DEPENDS "${protocol_file}"
|
||||
VERBATIM)
|
||||
add_custom_command(OUTPUT "${output_file}.c"
|
||||
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c"
|
||||
DEPENDS "${protocol_file}"
|
||||
VERBATIM)
|
||||
|
||||
target_sources(displayserver_Wayland PRIVATE "${output_file}.h" "${output_file}.c")
|
||||
target_sources(displayserver_Wayland PRIVATE "${output_file}.h" "${output_file}.c")
|
||||
endmacro()
|
||||
|
||||
set(WAYLAND_PROTOCOLS_BASE "${PROJECT_TOP}/repos/wayland-protocols")
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/wayland")
|
||||
include_directories("${CMAKE_BINARY_DIR}/wayland")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
|
||||
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol")
|
||||
"${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol")
|
||||
"${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol")
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-relative-pointer-unstable-v1-client-protocol")
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-relative-pointer-unstable-v1-client-protocol")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-pointer-constraints-unstable-v1-client-protocol")
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-pointer-constraints-unstable-v1-client-protocol")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol")
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol")
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol")
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
bool waylandActivationInit(void)
|
||||
{
|
||||
if (!wlWm.xdgActivation)
|
||||
DEBUG_WARN("xdg_activation_v1 not exported by compositor, will not be able "
|
||||
"to request host focus on behalf of guest applications");
|
||||
return true;
|
||||
}
|
||||
|
||||
void waylandActivationFree(void)
|
||||
{
|
||||
if (wlWm.xdgActivation)
|
||||
{
|
||||
xdg_activation_v1_destroy(wlWm.xdgActivation);
|
||||
}
|
||||
}
|
||||
|
||||
static void activationTokenDone(void * data,
|
||||
struct xdg_activation_token_v1 * xdgToken, const char * token)
|
||||
{
|
||||
xdg_activation_v1_activate(wlWm.xdgActivation, token, wlWm.surface);
|
||||
xdg_activation_token_v1_destroy(xdgToken);
|
||||
}
|
||||
|
||||
static const struct xdg_activation_token_v1_listener activationTokenListener = {
|
||||
.done = &activationTokenDone,
|
||||
};
|
||||
|
||||
void waylandActivationRequestActivation(void)
|
||||
{
|
||||
if (!wlWm.xdgActivation) return;
|
||||
|
||||
struct xdg_activation_token_v1 * token =
|
||||
xdg_activation_v1_get_activation_token(wlWm.xdgActivation);
|
||||
|
||||
if (!token)
|
||||
{
|
||||
DEBUG_ERROR("failed to retrieve XDG activation token");
|
||||
return;
|
||||
}
|
||||
|
||||
xdg_activation_token_v1_add_listener(token, &activationTokenListener, NULL);
|
||||
xdg_activation_token_v1_set_surface(token, wlWm.surface);
|
||||
xdg_activation_token_v1_commit(token);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -512,13 +512,6 @@ error:
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void dataSourceHandleTarget(void * data, struct wl_data_source * source,
|
||||
const char * mimetype)
|
||||
{
|
||||
// Certain Wayland clients send this for copy-paste operations even though
|
||||
// it only makes sense for drag-and-drop. We just do nothing.
|
||||
}
|
||||
|
||||
static void dataSourceHandleSend(void * data, struct wl_data_source * source,
|
||||
const char * mimetype, int fd)
|
||||
{
|
||||
@@ -554,8 +547,7 @@ static void dataSourceHandleCancelled(void * data,
|
||||
}
|
||||
|
||||
static const struct wl_data_source_listener dataSourceListener = {
|
||||
.target = dataSourceHandleTarget,
|
||||
.send = dataSourceHandleSend,
|
||||
.send = dataSourceHandleSend,
|
||||
.cancelled = dataSourceHandleCancelled,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -88,13 +88,10 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
|
||||
|
||||
if (wlWm.needsResize)
|
||||
{
|
||||
bool skipResize = false;
|
||||
wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(wlWm.width * wlWm.scale),
|
||||
wl_fixed_to_int(wlWm.height * wlWm.scale), 0, 0);
|
||||
|
||||
if (wlWm.width == 0 || wlWm.height == 0)
|
||||
skipResize = true;
|
||||
else if (wlWm.fractionalScale)
|
||||
if (wlWm.fractionalScale)
|
||||
{
|
||||
wl_surface_set_buffer_scale(wlWm.surface, 1);
|
||||
if (!wlWm.viewport)
|
||||
@@ -128,7 +125,7 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
|
||||
(struct Border) {0, 0, 0, 0});
|
||||
app_invalidateWindow(true);
|
||||
waylandStopWaitFrame();
|
||||
wlWm.needsResize = skipResize;
|
||||
wlWm.needsResize = false;
|
||||
}
|
||||
|
||||
waylandShellAckConfigureIfNeeded();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -209,17 +209,6 @@ done:
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static int getCharcode(uint32_t key)
|
||||
{
|
||||
key += 8; // xkb scancode is evdev scancode + 8
|
||||
xkb_keysym_t sym = xkb_state_key_get_one_sym(wlWm.xkbState, key);
|
||||
if (sym == XKB_KEY_NoSymbol)
|
||||
return 0;
|
||||
|
||||
sym = xkb_keysym_to_upper(sym);
|
||||
return xkb_keysym_to_utf32(sym);
|
||||
}
|
||||
|
||||
static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
|
||||
uint32_t serial, struct wl_surface * surface, struct wl_array * keys)
|
||||
{
|
||||
@@ -232,7 +221,7 @@ static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
|
||||
|
||||
uint32_t * key;
|
||||
wl_array_for_each(key, keys)
|
||||
app_handleKeyPress(*key, getCharcode(*key));
|
||||
app_handleKeyPress(*key);
|
||||
}
|
||||
|
||||
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
|
||||
@@ -253,9 +242,9 @@ static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard,
|
||||
return;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
app_handleKeyPress(key, getCharcode(key));
|
||||
app_handleKeyPress(key);
|
||||
else
|
||||
app_handleKeyRelease(key, getCharcode(key));
|
||||
app_handleKeyRelease(key);
|
||||
|
||||
if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
return;
|
||||
@@ -465,7 +454,7 @@ void waylandGrabPointer(void)
|
||||
|
||||
INTERLOCKED_SECTION(wlWm.confineLock,
|
||||
{
|
||||
if (!wlWm.confinedPointer && !wlWm.lockedPointer)
|
||||
if (!wlWm.confinedPointer)
|
||||
{
|
||||
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
|
||||
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
|
||||
@@ -627,10 +616,7 @@ void waylandRealignPointer(void)
|
||||
|
||||
void waylandGuestPointerUpdated(double x, double y, double localX, double localY)
|
||||
{
|
||||
if ( !wlWm.pointer ||
|
||||
!wlWm.warpSupport ||
|
||||
!wlWm.pointerInSurface ||
|
||||
wlWm.lockedPointer )
|
||||
if (!wlWm.warpSupport || !wlWm.pointerInSurface || wlWm.lockedPointer)
|
||||
return;
|
||||
|
||||
waylandWarpPointer((int) localX, (int) localY, false);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -86,8 +86,7 @@ bool waylandPresentationInit(void)
|
||||
if (wlWm.presentation)
|
||||
{
|
||||
wlWm.photonTimings = ringbuffer_new(256, sizeof(float));
|
||||
wlWm.photonGraph = app_registerGraph("PHOTON", wlWm.photonTimings,
|
||||
0.0f, 30.0f, NULL);
|
||||
wlWm.photonGraph = app_registerGraph("PHOTON", wlWm.photonTimings, 0.0f, 30.0f);
|
||||
wp_presentation_add_listener(wlWm.presentation, &presentationListener, NULL);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -72,9 +72,6 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
|
||||
wlWm.xdgOutputManager = wl_registry_bind(wlWm.registry, name,
|
||||
// we only need v2 to run, but v3 saves a callback
|
||||
&zxdg_output_manager_v1_interface, version > 3 ? 3 : version);
|
||||
else if (!strcmp(interface, xdg_activation_v1_interface.name))
|
||||
wlWm.xdgActivation = wl_registry_bind(wlWm.registry, name,
|
||||
&xdg_activation_v1_interface, 1);
|
||||
}
|
||||
|
||||
static void registryGlobalRemoveHandler(void * data,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -65,30 +65,15 @@ static const struct xdg_surface_listener xdgSurfaceListener = {
|
||||
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.width = width;
|
||||
wlWm.height = height;
|
||||
wlWm.fullscreen = false;
|
||||
wlWm.floating = true;
|
||||
|
||||
enum xdg_toplevel_state * state;
|
||||
wl_array_for_each(state, states)
|
||||
{
|
||||
switch (*state)
|
||||
{
|
||||
case XDG_TOPLEVEL_STATE_FULLSCREEN:
|
||||
if (*state == XDG_TOPLEVEL_STATE_FULLSCREEN)
|
||||
wlWm.fullscreen = true;
|
||||
// fallthrough
|
||||
case XDG_TOPLEVEL_STATE_MAXIMIZED:
|
||||
case XDG_TOPLEVEL_STATE_TILED_LEFT:
|
||||
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
|
||||
case XDG_TOPLEVEL_STATE_TILED_TOP:
|
||||
case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
|
||||
wlWm.floating = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,14 +156,5 @@ void waylandMinimize(void)
|
||||
|
||||
void waylandShellResize(int w, int h)
|
||||
{
|
||||
if (!wlWm.floating)
|
||||
return;
|
||||
|
||||
wlWm.width = w;
|
||||
wlWm.height = h;
|
||||
xdg_surface_set_window_geometry(wlWm.xdgSurface, 0, 0, w, h);
|
||||
|
||||
wlWm.needsResize = true;
|
||||
app_invalidateWindow(true);
|
||||
waylandStopWaitFrame();
|
||||
//TODO: Implement resize for XDG.
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -89,9 +89,6 @@ static bool waylandInit(const LG_DSInitParams params)
|
||||
if (!waylandRegistryInit())
|
||||
return false;
|
||||
|
||||
if (!waylandActivationInit())
|
||||
return false;
|
||||
|
||||
if (!waylandIdleInit())
|
||||
return false;
|
||||
|
||||
@@ -154,7 +151,6 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
|
||||
|
||||
struct LG_DisplayServerOps LGDS_Wayland =
|
||||
{
|
||||
.name = "Wayland",
|
||||
.setup = waylandSetup,
|
||||
.probe = waylandProbe,
|
||||
.earlyInit = waylandEarlyInit,
|
||||
@@ -191,7 +187,6 @@ struct LG_DisplayServerOps LGDS_Wayland =
|
||||
.warpPointer = waylandWarpPointer,
|
||||
.realignPointer = waylandRealignPointer,
|
||||
.isValidPointerPos = waylandIsValidPointerPos,
|
||||
.requestActivation = waylandActivationRequestActivation,
|
||||
.inhibitIdle = waylandInhibitIdle,
|
||||
.uninhibitIdle = waylandUninhibitIdle,
|
||||
.wait = waylandWait,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -47,7 +47,6 @@
|
||||
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
||||
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "wayland-xdg-output-unstable-v1-client-protocol.h"
|
||||
#include "wayland-xdg-activation-v1-client-protocol.h"
|
||||
|
||||
typedef void (*WaylandPollCallback)(uint32_t events, void * opaque);
|
||||
|
||||
@@ -112,7 +111,6 @@ struct WaylandDSState
|
||||
bool fractionalScale;
|
||||
bool needsResize;
|
||||
bool fullscreen;
|
||||
bool floating;
|
||||
uint32_t resizeSerial;
|
||||
bool configured;
|
||||
bool warpSupport;
|
||||
@@ -182,8 +180,6 @@ struct WaylandDSState
|
||||
struct zwp_idle_inhibit_manager_v1 * idleInhibitManager;
|
||||
struct zwp_idle_inhibitor_v1 * idleInhibitor;
|
||||
|
||||
struct xdg_activation_v1 * xdgActivation;
|
||||
|
||||
struct wp_viewporter * viewporter;
|
||||
struct wp_viewport * viewport;
|
||||
struct zxdg_output_manager_v1 * xdgOutputManager;
|
||||
@@ -235,11 +231,6 @@ struct WCBState
|
||||
extern struct WaylandDSState wlWm;
|
||||
extern struct WCBState wlCb;
|
||||
|
||||
// activation module
|
||||
bool waylandActivationInit(void);
|
||||
void waylandActivationFree(void);
|
||||
void waylandActivationRequestActivation(void);
|
||||
|
||||
// clipboard module
|
||||
bool waylandCBInit(void);
|
||||
void waylandCBRequest(LG_ClipboardData type);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -57,12 +57,6 @@ void waylandWindowUpdateScale(void)
|
||||
static void wlSurfaceEnterHandler(void * data, struct wl_surface * surface, struct wl_output * output)
|
||||
{
|
||||
struct SurfaceOutput * node = malloc(sizeof(*node));
|
||||
if (!node)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return;
|
||||
}
|
||||
|
||||
node->output = output;
|
||||
wl_list_insert(&wlWm.surfaceOutputs, &node->link);
|
||||
waylandWindowUpdateScale();
|
||||
|
||||
@@ -3,30 +3,29 @@ project(displayserver_X11 LANGUAGES C)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(DISPLAYSERVER_X11 REQUIRED IMPORTED_TARGET
|
||||
x11
|
||||
xi
|
||||
xfixes
|
||||
xscrnsaver
|
||||
xinerama
|
||||
xcursor
|
||||
xpresent
|
||||
xkbcommon
|
||||
x11
|
||||
xi
|
||||
xfixes
|
||||
xscrnsaver
|
||||
xinerama
|
||||
xcursor
|
||||
xpresent
|
||||
)
|
||||
|
||||
add_library(displayserver_X11 STATIC
|
||||
x11.c
|
||||
atoms.c
|
||||
clipboard.c
|
||||
x11.c
|
||||
atoms.c
|
||||
clipboard.c
|
||||
)
|
||||
|
||||
add_definitions(-D GLX_GLXEXT_PROTOTYPES)
|
||||
|
||||
target_link_libraries(displayserver_X11
|
||||
PkgConfig::DISPLAYSERVER_X11
|
||||
lg_common
|
||||
PkgConfig::DISPLAYSERVER_X11
|
||||
lg_common
|
||||
)
|
||||
|
||||
target_include_directories(displayserver_X11
|
||||
PRIVATE
|
||||
src
|
||||
PRIVATE
|
||||
src
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -28,17 +28,14 @@
|
||||
DEF_ATOM(_NET_REQUEST_FRAME_EXTENTS, True) \
|
||||
DEF_ATOM(_NET_FRAME_EXTENTS, True) \
|
||||
DEF_ATOM(_NET_WM_BYPASS_COMPOSITOR, False) \
|
||||
DEF_ATOM(_NET_WM_ICON, True) \
|
||||
DEF_ATOM(_NET_WM_STATE, True) \
|
||||
DEF_ATOM(_NET_WM_STATE_FULLSCREEN, True) \
|
||||
DEF_ATOM(_NET_WM_STATE_FOCUSED, True) \
|
||||
DEF_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ, True) \
|
||||
DEF_ATOM(_NET_WM_STATE_MAXIMIZED_VERT, True) \
|
||||
DEF_ATOM(_NET_WM_STATE_DEMANDS_ATTENTION, True) \
|
||||
DEF_ATOM(_NET_WM_WINDOW_TYPE, True) \
|
||||
DEF_ATOM(_NET_WM_WINDOW_TYPE_NORMAL, True) \
|
||||
DEF_ATOM(_NET_WM_WINDOW_TYPE_UTILITY, True) \
|
||||
DEF_ATOM(_NET_WM_PID, True) \
|
||||
DEF_ATOM(WM_DELETE_WINDOW, True) \
|
||||
DEF_ATOM(_MOTIF_WM_HINTS, True) \
|
||||
\
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -103,7 +103,7 @@ bool x11CBEventThread(const XEvent xe)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool x11CBInit(void)
|
||||
bool x11CBInit()
|
||||
{
|
||||
x11cb.aCurSelection = BadValue;
|
||||
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
@@ -123,6 +123,8 @@ bool x11CBInit(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
XFixesSelectSelectionInput(x11.display, x11.window,
|
||||
XA_PRIMARY, XFixesSetSelectionOwnerNotifyMask);
|
||||
XFixesSelectSelectionInput(x11.display, x11.window,
|
||||
x11atoms.CLIPBOARD, XFixesSetSelectionOwnerNotifyMask);
|
||||
|
||||
@@ -152,12 +154,6 @@ static void x11CBReplyFn(void * opaque, LG_ClipboardData type,
|
||||
static void x11CBSelectionRequest(const XSelectionRequestEvent e)
|
||||
{
|
||||
XEvent * s = malloc(sizeof(*s));
|
||||
if (!s)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return;
|
||||
}
|
||||
|
||||
s->xselection.type = SelectionNotify;
|
||||
s->xselection.requestor = e.requestor;
|
||||
s->xselection.selection = e.selection;
|
||||
@@ -209,7 +205,7 @@ send:
|
||||
|
||||
static void x11CBSelectionClear(const XSelectionClearEvent e)
|
||||
{
|
||||
if (e.selection != x11atoms.CLIPBOARD)
|
||||
if (e.selection != XA_PRIMARY && e.selection != x11atoms.CLIPBOARD)
|
||||
return;
|
||||
|
||||
x11cb.aCurSelection = BadValue;
|
||||
@@ -295,7 +291,7 @@ out:
|
||||
static void x11CBXFixesSelectionNotify(const XFixesSelectionNotifyEvent e)
|
||||
{
|
||||
// check if the selection is valid and it isn't ourself
|
||||
if (e.selection != x11atoms.CLIPBOARD ||
|
||||
if ((e.selection != XA_PRIMARY && e.selection != x11atoms.CLIPBOARD) ||
|
||||
e.owner == x11.window || e.owner == 0)
|
||||
{
|
||||
return;
|
||||
@@ -400,6 +396,7 @@ void x11CBNotice(LG_ClipboardData type)
|
||||
{
|
||||
x11cb.haveRequest = true;
|
||||
x11cb.type = type;
|
||||
XSetSelectionOwner(x11.display, XA_PRIMARY , x11.window, CurrentTime);
|
||||
XSetSelectionOwner(x11.display, x11atoms.CLIPBOARD, x11.window, CurrentTime);
|
||||
XFlush(x11.display);
|
||||
}
|
||||
@@ -407,6 +404,7 @@ void x11CBNotice(LG_ClipboardData type)
|
||||
void x11CBRelease(void)
|
||||
{
|
||||
x11cb.haveRequest = false;
|
||||
XSetSelectionOwner(x11.display, XA_PRIMARY , None, CurrentTime);
|
||||
XSetSelectionOwner(x11.display, x11atoms.CLIPBOARD, None, CurrentTime);
|
||||
XFlush(x11.display);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
bool x11CBEventThread(const XEvent xe);
|
||||
|
||||
bool x11CBInit(void);
|
||||
bool x11CBInit();
|
||||
void x11CBNotice(LG_ClipboardData type);
|
||||
void x11CBRelease(void);
|
||||
void x11CBRequest(LG_ClipboardData type);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "x11.h"
|
||||
#include "atoms.h"
|
||||
#include "clipboard.h"
|
||||
#include "resources/icondata.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@@ -36,8 +35,6 @@
|
||||
#include <X11/extensions/Xpresent.h>
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
#include <GL/glx.h>
|
||||
#include <GL/glxext.h>
|
||||
|
||||
@@ -192,14 +189,14 @@ static void x11CheckEWMHSupport(void)
|
||||
|
||||
if (XGetWindowProperty(x11.display, DefaultRootWindow(x11.display),
|
||||
x11atoms._NET_SUPPORTING_WM_CHECK, 0, ~0L, False, XA_WINDOW,
|
||||
&type, &fmt, &num, &bytes, &data) != Success || !data)
|
||||
&type, &fmt, &num, &bytes, &data) != Success)
|
||||
goto out;
|
||||
|
||||
Window * windowFromRoot = (Window *)data;
|
||||
|
||||
if (XGetWindowProperty(x11.display, *windowFromRoot,
|
||||
x11atoms._NET_SUPPORTING_WM_CHECK, 0, ~0L, False, XA_WINDOW,
|
||||
&type, &fmt, &num, &bytes, &data) != Success || !data)
|
||||
&type, &fmt, &num, &bytes, &data) != Success)
|
||||
goto out_root;
|
||||
|
||||
Window * windowFromChild = (Window *)data;
|
||||
@@ -208,7 +205,7 @@ static void x11CheckEWMHSupport(void)
|
||||
|
||||
if (XGetWindowProperty(x11.display, DefaultRootWindow(x11.display),
|
||||
x11atoms._NET_SUPPORTED, 0, ~0L, False, AnyPropertyType,
|
||||
&type, &fmt, &num, &bytes, &data) != Success || !data)
|
||||
&type, &fmt, &num, &bytes, &data) != Success)
|
||||
goto out_child;
|
||||
|
||||
Atom * supported = (Atom *)data;
|
||||
@@ -216,7 +213,7 @@ static void x11CheckEWMHSupport(void)
|
||||
|
||||
if (XGetWindowProperty(x11.display, *windowFromRoot,
|
||||
x11atoms._NET_WM_NAME, 0, ~0L, False, AnyPropertyType,
|
||||
&type, &fmt, &num, &bytes, &data) != Success || !data)
|
||||
&type, &fmt, &num, &bytes, &data) != Success)
|
||||
goto out_supported;
|
||||
|
||||
char * wmName = (char *)data;
|
||||
@@ -350,21 +347,6 @@ static bool x11Init(const LG_DSInitParams params)
|
||||
// check for Extended Window Manager Hints support
|
||||
x11CheckEWMHSupport();
|
||||
|
||||
if (x11atoms._NET_WM_PID)
|
||||
{
|
||||
pid_t pid = getpid();
|
||||
XChangeProperty(
|
||||
x11.display,
|
||||
x11.window,
|
||||
x11atoms._NET_WM_PID,
|
||||
XA_CARDINAL,
|
||||
32,
|
||||
PropModeReplace,
|
||||
(unsigned char *)&pid,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
if (params.borderless)
|
||||
{
|
||||
if (x11atoms._MOTIF_WM_HINTS)
|
||||
@@ -415,14 +397,23 @@ static bool x11Init(const LG_DSInitParams params)
|
||||
);
|
||||
}
|
||||
|
||||
Atom wmState[3] = {0};
|
||||
int wmStateCount = 0;
|
||||
|
||||
if (params.fullscreen)
|
||||
{
|
||||
x11.fullscreen = true;
|
||||
wmState[wmStateCount++] = x11atoms._NET_WM_STATE_FULLSCREEN;
|
||||
}
|
||||
|
||||
if (params.maximize)
|
||||
{
|
||||
Atom wmState[2] =
|
||||
{
|
||||
x11atoms._NET_WM_STATE_MAXIMIZED_HORZ,
|
||||
x11atoms._NET_WM_STATE_MAXIMIZED_VERT
|
||||
};
|
||||
wmState[wmStateCount++] = x11atoms._NET_WM_STATE_MAXIMIZED_HORZ;
|
||||
wmState[wmStateCount++] = x11atoms._NET_WM_STATE_MAXIMIZED_VERT;
|
||||
}
|
||||
|
||||
if (wmStateCount)
|
||||
{
|
||||
XChangeProperty(
|
||||
x11.display,
|
||||
x11.window,
|
||||
@@ -431,7 +422,7 @@ static bool x11Init(const LG_DSInitParams params)
|
||||
32,
|
||||
PropModeReplace,
|
||||
(unsigned char *)&wmState,
|
||||
2
|
||||
wmStateCount
|
||||
);
|
||||
}
|
||||
|
||||
@@ -531,10 +522,6 @@ static bool x11Init(const LG_DSInitParams params)
|
||||
goto fail_window;
|
||||
}
|
||||
|
||||
XDisplayKeycodes(x11.display, &x11.minKeycode, &x11.maxKeycode);
|
||||
x11.keysyms = XGetKeyboardMapping(x11.display, x11.minKeycode,
|
||||
x11.maxKeycode - x11.minKeycode, &x11.symsPerKeycode);
|
||||
|
||||
XIFreeDeviceInfo(devinfo);
|
||||
|
||||
XQueryExtension(x11.display, "XInputExtension", &x11.xinputOp, &event, &error);
|
||||
@@ -545,8 +532,11 @@ static bool x11Init(const LG_DSInitParams params)
|
||||
eventmask.mask_len = sizeof(mask);
|
||||
eventmask.mask = mask;
|
||||
|
||||
XISetMask(mask, XI_FocusIn );
|
||||
XISetMask(mask, XI_FocusOut);
|
||||
if (!x11.ewmhHasFocusEvent)
|
||||
{
|
||||
XISetMask(mask, XI_FocusIn );
|
||||
XISetMask(mask, XI_FocusOut);
|
||||
}
|
||||
|
||||
XISetMask(mask, XI_Enter );
|
||||
XISetMask(mask, XI_Leave );
|
||||
@@ -575,17 +565,6 @@ static bool x11Init(const LG_DSInitParams params)
|
||||
1
|
||||
);
|
||||
|
||||
XChangeProperty(
|
||||
x11.display,
|
||||
x11.window,
|
||||
x11atoms._NET_WM_ICON,
|
||||
XA_CARDINAL,
|
||||
32,
|
||||
PropModeReplace,
|
||||
(unsigned char *)icondata,
|
||||
sizeof(icondata) / sizeof(icondata[0])
|
||||
);
|
||||
|
||||
/* create the blank cursor */
|
||||
{
|
||||
static char data[] = { 0x00 };
|
||||
@@ -676,9 +655,6 @@ static bool x11Init(const LG_DSInitParams params)
|
||||
if (!params.center)
|
||||
XMoveWindow(x11.display, x11.window, params.x, params.y);
|
||||
|
||||
if (params.fullscreen)
|
||||
x11SetFullscreen(true);
|
||||
|
||||
XSetLocaleModifiers(""); // Load XMODIFIERS
|
||||
x11.xim = XOpenIM(x11.display, 0, 0, 0);
|
||||
|
||||
@@ -759,9 +735,6 @@ static void x11Free(void)
|
||||
if (x11.cursors[i])
|
||||
XFreeCursor(x11.display, x11.cursors[i]);
|
||||
|
||||
if (x11.keysyms)
|
||||
XFree(x11.keysyms);
|
||||
|
||||
XCloseDisplay(x11.display);
|
||||
}
|
||||
|
||||
@@ -1049,27 +1022,6 @@ static void updateModifiers(void)
|
||||
);
|
||||
}
|
||||
|
||||
static void setFocus(bool focused, double x, double y)
|
||||
{
|
||||
if (x11.focused == focused)
|
||||
return;
|
||||
|
||||
x11.focused = focused;
|
||||
app_updateCursorPos(x, y);
|
||||
app_handleFocusEvent(focused);
|
||||
}
|
||||
|
||||
static int getCharcode(int detail)
|
||||
{
|
||||
if (detail < x11.minKeycode || detail > x11.maxKeycode)
|
||||
return 0;
|
||||
|
||||
KeySym sym = x11.keysyms[(detail - x11.minKeycode) *
|
||||
x11.symsPerKeycode];
|
||||
sym = xkb_keysym_to_upper(sym);
|
||||
return xkb_keysym_to_utf32(sym);
|
||||
}
|
||||
|
||||
static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
{
|
||||
static int button_state = 0;
|
||||
@@ -1078,50 +1030,43 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
{
|
||||
case XI_FocusIn:
|
||||
{
|
||||
XIFocusOutEvent *xie = cookie->data;
|
||||
if (x11.ewmhHasFocusEvent)
|
||||
{
|
||||
// if meta ungrab for move/resize
|
||||
if (xie->mode == XINotifyUngrab)
|
||||
setFocus(true, xie->event_x, xie->event_y);
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_store(&x11.lastWMEvent, microtime());
|
||||
if (x11.focused)
|
||||
return;
|
||||
|
||||
XIFocusOutEvent *xie = cookie->data;
|
||||
if (xie->mode != XINotifyNormal &&
|
||||
xie->mode != XINotifyWhileGrabbed &&
|
||||
xie->mode != XINotifyUngrab)
|
||||
return;
|
||||
|
||||
|
||||
setFocus(true, xie->event_x, xie->event_y);
|
||||
x11.focused = true;
|
||||
app_updateCursorPos(xie->event_x, xie->event_y);
|
||||
app_handleFocusEvent(true);
|
||||
return;
|
||||
}
|
||||
|
||||
case XI_FocusOut:
|
||||
{
|
||||
XIFocusOutEvent *xie = cookie->data;
|
||||
if (x11.ewmhHasFocusEvent)
|
||||
{
|
||||
// if meta grab for move/resize
|
||||
if (xie->mode == XINotifyGrab)
|
||||
setFocus(false, xie->event_x, xie->event_y);
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_store(&x11.lastWMEvent, microtime());
|
||||
if (!x11.focused)
|
||||
return;
|
||||
|
||||
XIFocusOutEvent *xie = cookie->data;
|
||||
if (xie->mode != XINotifyNormal &&
|
||||
xie->mode != XINotifyWhileGrabbed &&
|
||||
xie->mode != XINotifyGrab)
|
||||
return;
|
||||
|
||||
setFocus(false, xie->event_x, xie->event_y);
|
||||
app_updateCursorPos(xie->event_x, xie->event_y);
|
||||
app_handleFocusEvent(false);
|
||||
x11.focused = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1161,8 +1106,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
return;
|
||||
|
||||
XIDeviceEvent *device = cookie->data;
|
||||
app_handleKeyPress(device->detail - x11.minKeycode,
|
||||
getCharcode(device->detail));
|
||||
app_handleKeyPress(device->detail - 8);
|
||||
|
||||
if (!x11.xic || !app_isOverlayMode())
|
||||
return;
|
||||
@@ -1212,8 +1156,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
return;
|
||||
|
||||
XIDeviceEvent *device = cookie->data;
|
||||
app_handleKeyRelease(device->detail - x11.minKeycode,
|
||||
getCharcode(device->detail));
|
||||
app_handleKeyRelease(device->detail - 8);
|
||||
|
||||
if (!x11.xic || !app_isOverlayMode())
|
||||
return;
|
||||
@@ -1242,8 +1185,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
return;
|
||||
|
||||
XIRawEvent *raw = cookie->data;
|
||||
app_handleKeyPress(raw->detail - x11.minKeycode,
|
||||
getCharcode(raw->detail));
|
||||
app_handleKeyPress(raw->detail - 8);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1253,8 +1195,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
return;
|
||||
|
||||
XIRawEvent *raw = cookie->data;
|
||||
app_handleKeyRelease(raw->detail - x11.minKeycode,
|
||||
getCharcode(raw->detail));
|
||||
app_handleKeyRelease(raw->detail - 8);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1858,28 +1799,6 @@ static bool x11IsValidPointerPos(int x, int y)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void x11RequestActivation(void)
|
||||
{
|
||||
XEvent e =
|
||||
{
|
||||
.xclient = {
|
||||
.type = ClientMessage,
|
||||
.send_event = true,
|
||||
.message_type = x11atoms._NET_WM_STATE,
|
||||
.format = 32,
|
||||
.window = x11.window,
|
||||
.data.l = {
|
||||
_NET_WM_STATE_ADD,
|
||||
x11atoms._NET_WM_STATE_DEMANDS_ATTENTION,
|
||||
0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
XSendEvent(x11.display, DefaultRootWindow(x11.display), False,
|
||||
SubstructureNotifyMask | SubstructureRedirectMask, &e);
|
||||
}
|
||||
|
||||
static void x11InhibitIdle(void)
|
||||
{
|
||||
XScreenSaverSuspend(x11.display, true);
|
||||
@@ -1937,7 +1856,6 @@ static void x11Minimize(void)
|
||||
|
||||
struct LG_DisplayServerOps LGDS_X11 =
|
||||
{
|
||||
.name = "X11",
|
||||
.setup = x11Setup,
|
||||
.probe = x11Probe,
|
||||
.earlyInit = x11EarlyInit,
|
||||
@@ -1971,7 +1889,6 @@ struct LG_DisplayServerOps LGDS_X11 =
|
||||
.warpPointer = x11WarpPointer,
|
||||
.realignPointer = x11RealignPointer,
|
||||
.isValidPointerPos = x11IsValidPointerPos,
|
||||
.requestActivation = x11RequestActivation,
|
||||
.inhibitIdle = x11InhibitIdle,
|
||||
.uninhibitIdle = x11UninhibitIdle,
|
||||
.wait = x11Wait,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -54,10 +54,6 @@ struct X11DSState
|
||||
Window window;
|
||||
XVisualInfo * visual;
|
||||
|
||||
int minKeycode, maxKeycode;
|
||||
int symsPerKeycode;
|
||||
KeySym * keysyms;
|
||||
|
||||
//Extended Window Manager Hints
|
||||
//ref: https://specifications.freedesktop.org/wm-spec/latest/
|
||||
bool ewmhSupport;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* 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_handleButtonRelease(int button);
|
||||
void app_handleWheelMotion(double motion);
|
||||
void app_handleKeyPress(int scancode);
|
||||
void app_handleKeyRelease(int scancode);
|
||||
void app_handleKeyboardTyped(const char * typed);
|
||||
void app_handleKeyPress(int scancode, int charcode);
|
||||
void app_handleKeyRelease(int scancode, int charcode);
|
||||
void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super);
|
||||
void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock);
|
||||
void app_handleEnterEvent(bool entered);
|
||||
@@ -102,21 +102,11 @@ int app_renderOverlay(struct Rect * rects, int maxRects);
|
||||
|
||||
void app_freeOverlays(void);
|
||||
|
||||
/**
|
||||
* invalidate the window to update the overlay, if renderTwice is set the imgui
|
||||
* render code will run twice so that auto sized windows are calculated correctly
|
||||
*/
|
||||
void app_invalidateOverlay(bool renderTwice);
|
||||
|
||||
struct OverlayGraph;
|
||||
typedef struct OverlayGraph * GraphHandle;
|
||||
typedef const char * (*GraphFormatFn)(const char * name,
|
||||
float min, float max, float avg, float freq, float last);
|
||||
|
||||
GraphHandle app_registerGraph(const char * name, RingBuffer buffer,
|
||||
float min, float max, GraphFormatFn formatFn);
|
||||
GraphHandle app_registerGraph(const char * name, RingBuffer buffer, float min, float max);
|
||||
void app_unregisterGraph(GraphHandle handle);
|
||||
void app_invalidateGraph(GraphHandle handle);
|
||||
|
||||
void app_overlayConfigRegister(const char * title,
|
||||
void (*callback)(void * udata, int * id), void * udata);
|
||||
@@ -138,31 +128,18 @@ void app_clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque);
|
||||
*/
|
||||
void app_alert(LG_MsgAlert type, const char * fmt, ...);
|
||||
|
||||
typedef struct MsgBoxHandle * MsgBoxHandle;
|
||||
MsgBoxHandle app_msgBox(const char * caption, const char * fmt, ...);
|
||||
|
||||
typedef void (*MsgBoxConfirmCallback)(bool yes, void * opaque);
|
||||
MsgBoxHandle app_confirmMsgBox(const char * caption,
|
||||
MsgBoxConfirmCallback callback, void * opaque, const char * fmt, ...);
|
||||
|
||||
void app_msgBoxClose(MsgBoxHandle handle);
|
||||
|
||||
typedef struct KeybindHandle * KeybindHandle;
|
||||
typedef void (*KeybindFn)(int sc, void * opaque);
|
||||
|
||||
void app_showRecord(bool show);
|
||||
|
||||
/**
|
||||
* Register a handler for the <super>+<key> combination
|
||||
* @param sc The scancode to register
|
||||
* @param charcode The charcode to register (used instead of sc if non zero)
|
||||
* @param callback The function to be called when the combination is pressed
|
||||
* @param opaque A pointer to be passed to the callback, may be NULL
|
||||
* @retval A handle for the binding or NULL on failure.
|
||||
* The caller is required to release the handle via `app_releaseKeybind` when it is no longer required
|
||||
*/
|
||||
KeybindHandle app_registerKeybind(int sc, int charcode, KeybindFn callback,
|
||||
void * opaque, const char * description);
|
||||
KeybindHandle app_registerKeybind(int sc, KeybindFn callback, void * opaque, const char * description);
|
||||
|
||||
/**
|
||||
* Release an existing key binding
|
||||
@@ -175,20 +152,4 @@ void app_releaseKeybind(KeybindHandle * handle);
|
||||
*/
|
||||
void app_releaseAllKeybinds(void);
|
||||
|
||||
bool app_guestIsLinux(void);
|
||||
bool app_guestIsWindows(void);
|
||||
bool app_guestIsOSX(void);
|
||||
bool app_guestIsBSD(void);
|
||||
bool app_guestIsOther(void);
|
||||
|
||||
/**
|
||||
* Enable/disable the LG display
|
||||
*/
|
||||
void app_stopVideo(bool stop);
|
||||
|
||||
/**
|
||||
* Enable/disable the spice display
|
||||
*/
|
||||
bool app_useSpiceDisplay(bool enable);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_I_AUDIODEV_
|
||||
#define _H_I_AUDIODEV_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef int (*LG_AudioPullFn)(uint8_t * dst, int frames);
|
||||
typedef void (*LG_AudioPushFn)(uint8_t * src, int frames);
|
||||
|
||||
struct LG_AudioDevOps
|
||||
{
|
||||
/* internal name of the audio for debugging */
|
||||
const char * name;
|
||||
|
||||
/* called very early to allow for option registration, optional */
|
||||
void (*earlyInit)(void);
|
||||
|
||||
/* called to initialize the audio backend */
|
||||
bool (*init)(void);
|
||||
|
||||
/* final free */
|
||||
void (*free)(void);
|
||||
|
||||
struct
|
||||
{
|
||||
/* setup the stream for playback but don't start it yet
|
||||
* Note: the pull function returns f32 samples
|
||||
*/
|
||||
void (*setup)(int channels, int sampleRate, int requestedPeriodFrames,
|
||||
int * maxPeriodFrames, int * startFrames, LG_AudioPullFn pullFn);
|
||||
|
||||
/* called when there is data available to start playback */
|
||||
void (*start)(void);
|
||||
|
||||
/* called when SPICE reports the audio stream has stopped */
|
||||
void (*stop)(void);
|
||||
|
||||
/* [optional] called to set the volume of the channels */
|
||||
void (*volume)(int channels, const uint16_t volume[]);
|
||||
|
||||
/* [optional] called to set muting of the output */
|
||||
void (*mute)(bool mute);
|
||||
|
||||
/* return the current total playback latency in microseconds */
|
||||
uint64_t (*latency)(void);
|
||||
}
|
||||
playback;
|
||||
|
||||
struct
|
||||
{
|
||||
/* start the record stream
|
||||
* Note: currently SPICE only supports S16 samples so always assume so
|
||||
*/
|
||||
void (*start)(int channels, int sampleRate, LG_AudioPushFn pushFn);
|
||||
|
||||
/* called when SPICE reports the audio stream has stopped */
|
||||
void (*stop)(void);
|
||||
|
||||
/* [optional] called to set the volume of the channels */
|
||||
void (*volume)(int channels, const uint16_t volume[]);
|
||||
|
||||
/* [optional] called to set muting of the input */
|
||||
void (*mute)(bool mute);
|
||||
}
|
||||
record;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -110,8 +110,6 @@ typedef struct LGEvent LGEvent;
|
||||
|
||||
struct LG_DisplayServerOps
|
||||
{
|
||||
const char * name;
|
||||
|
||||
/* called before options are parsed, useful for registering options */
|
||||
void (*setup)(void);
|
||||
|
||||
@@ -125,13 +123,13 @@ struct LG_DisplayServerOps
|
||||
bool (*init)(const LG_DSInitParams params);
|
||||
|
||||
/* called at startup after window creation, renderer and SPICE is ready */
|
||||
void (*startup)(void);
|
||||
void (*startup)();
|
||||
|
||||
/* called just before final window destruction, before final free */
|
||||
void (*shutdown)(void);
|
||||
void (*shutdown)();
|
||||
|
||||
/* final free */
|
||||
void (*free)(void);
|
||||
void (*free)();
|
||||
|
||||
/*
|
||||
* return a system specific property, returns false if unsupported or failure
|
||||
@@ -172,14 +170,14 @@ struct LG_DisplayServerOps
|
||||
/* dm specific cursor implementations */
|
||||
void (*guestPointerUpdated)(double x, double y, double localX, double localY);
|
||||
void (*setPointer)(LG_DSPointer pointer);
|
||||
void (*grabKeyboard)(void);
|
||||
void (*ungrabKeyboard)(void);
|
||||
void (*grabKeyboard)();
|
||||
void (*ungrabKeyboard)();
|
||||
/* (un)grabPointer is used to toggle cursor tracking/confine in normal mode */
|
||||
void (*grabPointer)(void);
|
||||
void (*ungrabPointer)(void);
|
||||
void (*grabPointer)();
|
||||
void (*ungrabPointer)();
|
||||
/* (un)capturePointer is used do toggle special cursor tracking in capture mode */
|
||||
void (*capturePointer)(void);
|
||||
void (*uncapturePointer)(void);
|
||||
void (*capturePointer)();
|
||||
void (*uncapturePointer)();
|
||||
|
||||
/* exiting = true if the warp is to leave the window */
|
||||
void (*warpPointer)(int x, int y, bool exiting);
|
||||
@@ -187,17 +185,14 @@ struct LG_DisplayServerOps
|
||||
/* called when the client needs to realign the pointer. This should simply
|
||||
* call the appropriate app_handleMouse* method for the platform with zero
|
||||
* deltas */
|
||||
void (*realignPointer)(void);
|
||||
void (*realignPointer)();
|
||||
|
||||
/* returns true if the position specified is actually valid */
|
||||
bool (*isValidPointerPos)(int x, int y);
|
||||
|
||||
/* called to disable/enable the screensaver */
|
||||
void (*inhibitIdle)(void);
|
||||
void (*uninhibitIdle)(void);
|
||||
|
||||
/* called to request activation */
|
||||
void (*requestActivation)(void);
|
||||
void (*inhibitIdle)();
|
||||
void (*uninhibitIdle)();
|
||||
|
||||
/* wait for the specified time without blocking UI processing/event loops */
|
||||
void (*wait)(unsigned int time);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -25,8 +25,6 @@
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
#define TICK_RATE 25
|
||||
|
||||
struct LG_OverlayOps
|
||||
{
|
||||
/* internal name of the overlay for debugging */
|
||||
@@ -45,10 +43,6 @@ struct LG_OverlayOps
|
||||
* optional, if omitted assumes false */
|
||||
bool (*needs_render)(void * udata, bool interactive);
|
||||
|
||||
/* return true if the overlay currently requires overlay mode
|
||||
* optional, if omitted assumes false */
|
||||
bool (*needs_overlay)(void * udata);
|
||||
|
||||
/* perform the actual drawing/rendering
|
||||
*
|
||||
* `interactive` is true if the application is currently in overlay interaction
|
||||
@@ -65,15 +59,6 @@ struct LG_OverlayOps
|
||||
int (*render)(void * udata, bool interactive, struct Rect * windowRects,
|
||||
int maxRects);
|
||||
|
||||
/* called TICK_RATE times a second by the application
|
||||
*
|
||||
* Note: This may not run in the same context as `render`!
|
||||
*
|
||||
* return true if the frame needs to be rendered
|
||||
* optional, if omitted assumes false
|
||||
*/
|
||||
bool (*tick)(void * udata, unsigned long long tickCount);
|
||||
|
||||
/* TODO: add load/save settings capabillity */
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -27,22 +27,17 @@
|
||||
#include "common/framebuffer.h"
|
||||
|
||||
#define IS_LG_RENDERER_VALID(x) \
|
||||
((x)->getName && \
|
||||
(x)->create && \
|
||||
(x)->initialize && \
|
||||
(x)->deinitialize && \
|
||||
(x)->onRestart && \
|
||||
(x)->onResize && \
|
||||
(x)->onMouseShape && \
|
||||
(x)->onMouseEvent && \
|
||||
(x)->renderStartup && \
|
||||
(x)->render && \
|
||||
(x)->createTexture && \
|
||||
(x)->freeTexture && \
|
||||
(x)->spiceConfigure && \
|
||||
(x)->spiceDrawFill && \
|
||||
(x)->spiceDrawBitmap && \
|
||||
(x)->spiceShow)
|
||||
((x)->getName && \
|
||||
(x)->create && \
|
||||
(x)->initialize && \
|
||||
(x)->deinitialize && \
|
||||
(x)->onRestart && \
|
||||
(x)->onResize && \
|
||||
(x)->onMouseShape && \
|
||||
(x)->onMouseEvent && \
|
||||
(x)->renderStartup && \
|
||||
(x)->needsRender && \
|
||||
(x)->render)
|
||||
|
||||
typedef struct LG_RendererParams
|
||||
{
|
||||
@@ -71,11 +66,9 @@ LG_RendererRotate;
|
||||
|
||||
typedef struct LG_RendererFormat
|
||||
{
|
||||
FrameType type; // frame type
|
||||
unsigned int screenWidth; // actual width of the host
|
||||
unsigned int screenHeight; // actual height of the host
|
||||
unsigned int frameWidth; // width of frame transmitted
|
||||
unsigned int frameHeight; // height of frame transmitted
|
||||
FrameType type; // frame type
|
||||
unsigned int width; // image width
|
||||
unsigned int height; // image height
|
||||
unsigned int stride; // scanline width (zero if compresed)
|
||||
unsigned int pitch; // scanline bytes (or compressed size)
|
||||
unsigned int bpp; // bits per pixel (zero if compressed)
|
||||
@@ -146,8 +139,8 @@ typedef struct LG_RendererOps
|
||||
|
||||
/* called when the mouse has moved or changed visibillity
|
||||
* Context: cursorThread */
|
||||
bool (*onMouseEvent)(LG_Renderer * renderer, const bool visible, int x, int y,
|
||||
const int hx, const int hy);
|
||||
bool (*onMouseEvent)(LG_Renderer * renderer, const bool visible, const int x,
|
||||
const int y);
|
||||
|
||||
/* called when the frame format has changed
|
||||
* Context: frameThread */
|
||||
@@ -163,36 +156,15 @@ typedef struct LG_RendererOps
|
||||
* Context: renderThread */
|
||||
bool (*renderStartup)(LG_Renderer * renderer, bool useDMA);
|
||||
|
||||
/* returns if the render method must be called even if nothing has changed.
|
||||
* Context: renderThread */
|
||||
bool (*needsRender)(LG_Renderer * renderer);
|
||||
|
||||
/* called to render the scene
|
||||
* Context: renderThread */
|
||||
bool (*render)(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
const bool newFrame, const bool invalidateWindow,
|
||||
void (*preSwap)(void * udata), void * udata);
|
||||
|
||||
/* called to create a texture from the specified 32-bit RGB image data. This
|
||||
* method is for use with Dear ImGui
|
||||
* Context: renderThread */
|
||||
void * (*createTexture)(LG_Renderer * renderer,
|
||||
int width, int height, uint8_t * data);
|
||||
|
||||
/* called to free a texture previously created by createTexture. This method
|
||||
* is for use with Dear ImGui
|
||||
* Context: renderThread */
|
||||
void (*freeTexture)(LG_Renderer * renderer, void * texture);
|
||||
|
||||
/* setup the spice display */
|
||||
void (*spiceConfigure)(LG_Renderer * renderer, int width, int height);
|
||||
|
||||
/* draw a filled rect on the spice display with the specified color */
|
||||
void (*spiceDrawFill)(LG_Renderer * renderer, int x, int y, int width,
|
||||
int height, uint32_t color);
|
||||
|
||||
/* draw an image on the spice display, data is RGBA32 */
|
||||
void (*spiceDrawBitmap)(LG_Renderer * renderer, int x, int y, int width,
|
||||
int height, int stride, uint8_t * data, bool topDown);
|
||||
|
||||
/* show the spice display */
|
||||
void (*spiceShow)(LG_Renderer * renderer, bool show);
|
||||
}
|
||||
LG_RendererOps;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -18,24 +18,16 @@
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_COMMON_UTIL_
|
||||
#define _H_LG_COMMON_UTIL_
|
||||
#include <stdbool.h>
|
||||
struct ll;
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); \
|
||||
_a < _b ? _a : _b; })
|
||||
#endif
|
||||
struct ll * ll_new();
|
||||
void ll_free (struct ll * list);
|
||||
void ll_push (struct ll * list, void * data);
|
||||
bool ll_shift (struct ll * list, void ** data);
|
||||
bool ll_peek_head(struct ll * list, void ** data);
|
||||
bool ll_peek_tail(struct ll * list, void ** data);
|
||||
unsigned int ll_count (struct ll * list);
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; })
|
||||
#endif
|
||||
|
||||
#ifndef clamp
|
||||
#define clamp(v,a,b) min(max(v, a), b)
|
||||
#endif
|
||||
|
||||
#define UPCAST(type, x) \
|
||||
(type *)((uintptr_t)(x) - offsetof(type, base))
|
||||
|
||||
#endif
|
||||
void ll_reset (struct ll * list);
|
||||
bool ll_walk (struct ll * list, void ** data);
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -27,23 +27,9 @@
|
||||
|
||||
typedef struct ImVec2 ImVec2;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void * tex;
|
||||
int width;
|
||||
int height;
|
||||
}
|
||||
OverlayImage;
|
||||
|
||||
void overlayGetImGuiRect(struct Rect * rect);
|
||||
ImVec2 * overlayGetScreenSize(void);
|
||||
void overlayTextURL(const char * url, const char * text);
|
||||
void overlayTextMaybeURL(const char * text, bool wrapped);
|
||||
|
||||
// create a texture from a SVG and scale it to fit the supplied width & height
|
||||
bool overlayLoadSVG(const char * data, unsigned int size, OverlayImage * image,
|
||||
int width, int height);
|
||||
|
||||
void overlayFreeImage(OverlayImage * image);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -24,7 +24,12 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "common/types.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#define min(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })
|
||||
#define max(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; })
|
||||
|
||||
#define UPCAST(type, x) \
|
||||
(type *)((uintptr_t)(x) - offsetof(type, base))
|
||||
|
||||
// reads the specified file into a new buffer
|
||||
// the callee must free the buffer
|
||||
|
||||
@@ -3,114 +3,120 @@ project(renderer_EGL LANGUAGES C CXX)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(RENDERER_EGL REQUIRED IMPORTED_TARGET
|
||||
egl
|
||||
gl
|
||||
egl
|
||||
gl
|
||||
)
|
||||
|
||||
pkg_check_modules(RENDERER_EGL_OPT IMPORTED_TARGET
|
||||
wayland-egl
|
||||
wayland-egl
|
||||
)
|
||||
|
||||
find_program(AWK NAMES gawk mawk original-awk awk)
|
||||
|
||||
if(AWK MATCHES ".+-NOTFOUND")
|
||||
message(FATAL_ERROR "FATAL: some known version of awk couldn't be found (${AWK}).")
|
||||
message(FATAL_ERROR "FATAL: some known version of awk couldn't be found (${AWK}).")
|
||||
else()
|
||||
message(STATUS "Using awk: ${AWK}")
|
||||
message(STATUS "Using awk: ${AWK}")
|
||||
endif()
|
||||
|
||||
include(MakeObject)
|
||||
function(build_shaders header_dir)
|
||||
file(GLOB headers "${header_dir}/*.h")
|
||||
set(EGL_SHADER_PROCESSED)
|
||||
foreach(shader ${ARGN})
|
||||
set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${shader}")
|
||||
add_custom_command(OUTPUT "${out_f}"
|
||||
COMMAND "${AWK}" -f "${CMAKE_CURRENT_SOURCE_DIR}/glsl.include.awk"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${shader}" > "${out_f}"
|
||||
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/${shader}"
|
||||
DEPENDS ${headers}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/shader"
|
||||
COMMENT "Preprocessing shader ${shader}"
|
||||
VERBATIM
|
||||
)
|
||||
endforeach()
|
||||
file(GLOB headers "${header_dir}/*.h")
|
||||
set(EGL_SHADER_PROCESSED)
|
||||
foreach(shader ${ARGN})
|
||||
set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${shader}")
|
||||
add_custom_command(OUTPUT "${out_f}"
|
||||
COMMAND "${AWK}" -f "${CMAKE_CURRENT_SOURCE_DIR}/glsl.include.awk"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${shader}" > "${out_f}"
|
||||
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/${shader}"
|
||||
DEPENDS ${headers}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/shader"
|
||||
COMMENT "Preprocessing shader ${shader}"
|
||||
VERBATIM
|
||||
)
|
||||
endforeach()
|
||||
|
||||
set(CMAKE_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
make_object(
|
||||
EGL_SHADER
|
||||
${ARGN}
|
||||
)
|
||||
set(CMAKE_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
make_object(
|
||||
EGL_SHADER
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
set(EGL_SHADER_OBJS "${EGL_SHADER_OBJS}" PARENT_SCOPE)
|
||||
set(EGL_SHADER_INCS "${EGL_SHADER_INCS}" PARENT_SCOPE)
|
||||
set(EGL_SHADER_OBJS "${EGL_SHADER_OBJS}" PARENT_SCOPE)
|
||||
set(EGL_SHADER_INCS "${EGL_SHADER_INCS}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
build_shaders(
|
||||
shader
|
||||
shader/desktop.vert
|
||||
shader/desktop_rgb.frag
|
||||
shader/cursor.vert
|
||||
shader/cursor_rgb.frag
|
||||
shader/cursor_mono.frag
|
||||
shader/damage.vert
|
||||
shader/damage.frag
|
||||
shader/basic.vert
|
||||
shader/ffx_cas.frag
|
||||
shader/ffx_fsr1_easu.frag
|
||||
shader/ffx_fsr1_rcas.frag
|
||||
shader/downscale.frag
|
||||
shader/downscale_lanczos2.frag
|
||||
shader/downscale_linear.frag
|
||||
shader
|
||||
shader/desktop.vert
|
||||
shader/desktop_rgb.frag
|
||||
shader/cursor.vert
|
||||
shader/cursor_rgb.frag
|
||||
shader/cursor_mono.frag
|
||||
shader/damage.vert
|
||||
shader/damage.frag
|
||||
shader/splash_bg.vert
|
||||
shader/splash_bg.frag
|
||||
shader/splash_logo.vert
|
||||
shader/splash_logo.frag
|
||||
shader/basic.vert
|
||||
shader/ffx_cas.frag
|
||||
shader/ffx_fsr1_easu.frag
|
||||
shader/ffx_fsr1_rcas.frag
|
||||
shader/downscale.frag
|
||||
shader/downscale_lanczos2.frag
|
||||
shader/downscale_linear.frag
|
||||
)
|
||||
|
||||
make_defines(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shader/desktop_rgb.frag"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shader/desktop_rgb.frag"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
|
||||
)
|
||||
|
||||
add_library(renderer_EGL STATIC
|
||||
egl.c
|
||||
egldebug.c
|
||||
shader.c
|
||||
texture_util.c
|
||||
texture.c
|
||||
texture_buffer.c
|
||||
texture_framebuffer.c
|
||||
texture_dmabuf.c
|
||||
model.c
|
||||
desktop.c
|
||||
desktop_rects.c
|
||||
cursor.c
|
||||
damage.c
|
||||
framebuffer.c
|
||||
postprocess.c
|
||||
ffx.c
|
||||
filter.c
|
||||
filter_ffx_cas.c
|
||||
filter_ffx_fsr1.c
|
||||
filter_downscale.c
|
||||
${EGL_SHADER_OBJS}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
|
||||
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl3.cpp
|
||||
egl.c
|
||||
egldebug.c
|
||||
shader.c
|
||||
texture_util.c
|
||||
texture.c
|
||||
texture_buffer.c
|
||||
texture_framebuffer.c
|
||||
texture_dmabuf.c
|
||||
model.c
|
||||
desktop.c
|
||||
desktop_rects.c
|
||||
cursor.c
|
||||
draw.c
|
||||
splash.c
|
||||
damage.c
|
||||
framebuffer.c
|
||||
postprocess.c
|
||||
ffx.c
|
||||
filter.c
|
||||
filter_ffx_cas.c
|
||||
filter_ffx_fsr1.c
|
||||
filter_downscale.c
|
||||
${EGL_SHADER_OBJS}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
|
||||
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl3.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(renderer_EGL PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1 IMGUI_IMPL_OPENGL_ES3)
|
||||
|
||||
target_link_libraries(renderer_EGL
|
||||
PkgConfig::RENDERER_EGL
|
||||
lg_common
|
||||
PkgConfig::RENDERER_EGL
|
||||
lg_common
|
||||
|
||||
cimgui
|
||||
cimgui
|
||||
)
|
||||
if(RENDERER_EGL_OPT_FOUND)
|
||||
target_link_libraries(renderer_EGL
|
||||
PkgConfig::RENDERER_EGL_OPT
|
||||
)
|
||||
target_link_libraries(renderer_EGL
|
||||
PkgConfig::RENDERER_EGL_OPT
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(renderer_EGL
|
||||
PRIVATE
|
||||
src
|
||||
${EGL_SHADER_INCS}
|
||||
PRIVATE
|
||||
src
|
||||
${EGL_SHADER_INCS}
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -42,7 +42,6 @@ struct CursorTex
|
||||
struct EGL_Texture * texture;
|
||||
struct EGL_Shader * shader;
|
||||
GLuint uMousePos;
|
||||
GLuint uScale;
|
||||
GLuint uRotate;
|
||||
GLuint uCBMode;
|
||||
};
|
||||
@@ -74,9 +73,7 @@ struct EGL_Cursor
|
||||
int cbMode;
|
||||
|
||||
_Atomic(struct CursorPos) pos;
|
||||
_Atomic(struct CursorPos) hs;
|
||||
_Atomic(struct CursorSize) size;
|
||||
_Atomic(float) scale;
|
||||
|
||||
struct CursorTex norm;
|
||||
struct CursorTex mono;
|
||||
@@ -87,7 +84,7 @@ static bool cursorTexInit(struct CursorTex * t,
|
||||
const char * vertex_code , size_t vertex_size,
|
||||
const char * fragment_code, size_t fragment_size)
|
||||
{
|
||||
if (!egl_textureInit(&t->texture, NULL, EGL_TEXTYPE_BUFFER))
|
||||
if (!egl_textureInit(&t->texture, NULL, EGL_TEXTYPE_BUFFER, false))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the cursor texture");
|
||||
return false;
|
||||
@@ -106,22 +103,28 @@ static bool cursorTexInit(struct CursorTex * t,
|
||||
return false;
|
||||
}
|
||||
|
||||
t->uMousePos = egl_shaderGetUniform(t->shader, "mouse" );
|
||||
t->uScale = egl_shaderGetUniform(t->shader, "scale" );
|
||||
t->uRotate = egl_shaderGetUniform(t->shader, "rotate" );
|
||||
t->uCBMode = egl_shaderGetUniform(t->shader, "cbMode" );
|
||||
t->uMousePos = egl_shaderGetUniform(t->shader, "mouse" );
|
||||
t->uRotate = egl_shaderGetUniform(t->shader, "rotate");
|
||||
t->uCBMode = egl_shaderGetUniform(t->shader, "cbMode");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void setCursorTexUniforms(EGL_Cursor * cursor,
|
||||
struct CursorTex * t, bool mono, float x, float y,
|
||||
float w, float h, float scale)
|
||||
struct CursorTex * t, bool mono, float x, float y, float w, float h)
|
||||
{
|
||||
glUniform4f(t->uMousePos, x, y, w, mono ? h / 2 : h);
|
||||
glUniform1f(t->uScale , scale);
|
||||
glUniform1i(t->uRotate , cursor->rotate);
|
||||
glUniform1i(t->uCBMode , cursor->cbMode);
|
||||
if (mono)
|
||||
{
|
||||
glUniform4f(t->uMousePos, x, y, w, h / 2);
|
||||
glUniform1i(t->uRotate , cursor->rotate);
|
||||
glUniform1i(t->uCBMode , cursor->cbMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
glUniform4f(t->uMousePos, x, y, w, h);
|
||||
glUniform1i(t->uRotate , cursor->rotate);
|
||||
glUniform1i(t->uCBMode , cursor->cbMode);
|
||||
}
|
||||
}
|
||||
|
||||
static void cursorTexFree(struct CursorTex * t)
|
||||
@@ -163,12 +166,9 @@ bool egl_cursorInit(EGL_Cursor ** cursor)
|
||||
(*cursor)->cbMode = option_get_int("egl", "cbMode");
|
||||
|
||||
struct CursorPos pos = { .x = 0, .y = 0 };
|
||||
struct CursorPos hs = { .x = 0, .y = 0 };
|
||||
struct CursorSize size = { .w = 0, .h = 0 };
|
||||
atomic_init(&(*cursor)->pos , pos );
|
||||
atomic_init(&(*cursor)->hs , hs );
|
||||
atomic_init(&(*cursor)->size , size);
|
||||
atomic_init(&(*cursor)->scale, 1.0f);
|
||||
atomic_init(&(*cursor)->pos, pos);
|
||||
atomic_init(&(*cursor)->size, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -229,19 +229,11 @@ void egl_cursorSetSize(EGL_Cursor * cursor, const float w, const float h)
|
||||
atomic_store(&cursor->size, size);
|
||||
}
|
||||
|
||||
void egl_cursorSetScale(EGL_Cursor * cursor, const float scale)
|
||||
{
|
||||
atomic_store(&cursor->scale, scale);
|
||||
}
|
||||
|
||||
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible,
|
||||
const float x, const float y, const float hx, const float hy)
|
||||
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible, const float x, const float y)
|
||||
{
|
||||
cursor->visible = visible;
|
||||
struct CursorPos pos = { .x = x , .y = y };
|
||||
struct CursorPos hs = { .x = hx, .y = hy };
|
||||
struct CursorPos pos = { .x = x, .y = y};
|
||||
atomic_store(&cursor->pos, pos);
|
||||
atomic_store(&cursor->hs , hs);
|
||||
}
|
||||
|
||||
struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
@@ -260,43 +252,22 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
switch(cursor->type)
|
||||
{
|
||||
case LG_CURSOR_MASKED_COLOR:
|
||||
{
|
||||
uint32_t xor[cursor->height][cursor->width];
|
||||
for(int y = 0; y < cursor->height; ++y)
|
||||
for(int x = 0; x < cursor->width; ++x)
|
||||
{
|
||||
uint32_t * src = (uint32_t *)(data + (cursor->stride * y) + x * 4);
|
||||
const bool masked = (*src & 0xFF000000) != 0;
|
||||
if (masked)
|
||||
*src = xor[y][x] = *src & 0x00FFFFFF;
|
||||
else
|
||||
{
|
||||
xor[y][x] = 0xFF000000;
|
||||
*src |= 0xFF000000;
|
||||
}
|
||||
}
|
||||
|
||||
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
|
||||
cursor->width, cursor->height, sizeof(xor[0]));
|
||||
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
|
||||
}
|
||||
// fall through
|
||||
// fall through
|
||||
|
||||
case LG_CURSOR_COLOR:
|
||||
{
|
||||
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
|
||||
cursor->width, cursor->height, cursor->stride);
|
||||
egl_textureUpdate(cursor->norm.texture, data, true);
|
||||
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride);
|
||||
egl_textureUpdate(cursor->norm.texture, data);
|
||||
egl_modelSetTexture(cursor->model, cursor->norm.texture);
|
||||
break;
|
||||
}
|
||||
|
||||
case LG_CURSOR_MONOCHROME:
|
||||
{
|
||||
uint32_t and[cursor->height][cursor->width];
|
||||
uint32_t xor[cursor->height][cursor->width];
|
||||
uint32_t and[cursor->width * cursor->height];
|
||||
uint32_t xor[cursor->width * cursor->height];
|
||||
|
||||
for(int y = 0; y < cursor->height; ++y)
|
||||
{
|
||||
for(int x = 0; x < cursor->width; ++x)
|
||||
{
|
||||
const uint8_t * srcAnd = data + (cursor->stride * y) + (x / 8);
|
||||
@@ -305,17 +276,14 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
const uint32_t andMask = (*srcAnd & mask) ? 0xFFFFFFFF : 0xFF000000;
|
||||
const uint32_t xorMask = (*srcXor & mask) ? 0x00FFFFFF : 0x00000000;
|
||||
|
||||
and[y][x] = andMask;
|
||||
xor[y][x] = xorMask;
|
||||
and[y * cursor->width + x] = andMask;
|
||||
xor[y * cursor->width + x] = xorMask;
|
||||
}
|
||||
}
|
||||
|
||||
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
|
||||
cursor->width, cursor->height, sizeof(and[0]));
|
||||
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
|
||||
cursor->width, cursor->height, sizeof(xor[0]));
|
||||
egl_textureUpdate(cursor->norm.texture, (uint8_t *)and, true);
|
||||
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
|
||||
egl_textureSetup (cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4);
|
||||
egl_textureSetup (cursor->mono.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4);
|
||||
egl_textureUpdate(cursor->norm.texture, (uint8_t *)and);
|
||||
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -324,15 +292,8 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
|
||||
cursor->rotate = rotate;
|
||||
|
||||
struct CursorPos pos = atomic_load(&cursor->pos );
|
||||
float scale = atomic_load(&cursor->scale);
|
||||
struct CursorPos hs = atomic_load(&cursor->hs );
|
||||
struct CursorSize size = atomic_load(&cursor->size );
|
||||
|
||||
pos.x -= hs.x * scale;
|
||||
pos.y -= hs.y * scale;
|
||||
size.w *= scale;
|
||||
size.h *= scale;
|
||||
struct CursorPos pos = atomic_load(&cursor->pos);
|
||||
struct CursorSize size = atomic_load(&cursor->size);
|
||||
|
||||
struct CursorState state = {
|
||||
.visible = true,
|
||||
@@ -381,33 +342,13 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
case LG_CURSOR_MONOCHROME:
|
||||
{
|
||||
egl_shaderUse(cursor->norm.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->norm, true, pos.x, pos.y,
|
||||
size.w, size.h, scale);
|
||||
setCursorTexUniforms(cursor, &cursor->norm, true, pos.x, pos.y, size.w, size.h);
|
||||
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
|
||||
egl_modelSetTexture(cursor->model, cursor->norm.texture);
|
||||
egl_modelRender(cursor->model);
|
||||
|
||||
egl_shaderUse(cursor->mono.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->mono, true, pos.x, pos.y,
|
||||
size.w, size.h, scale);
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
||||
egl_modelSetTexture(cursor->model, cursor->mono.texture);
|
||||
egl_modelRender(cursor->model);
|
||||
break;
|
||||
}
|
||||
|
||||
case LG_CURSOR_MASKED_COLOR:
|
||||
{
|
||||
egl_shaderUse(cursor->norm.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y,
|
||||
size.w, size.h, scale);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
egl_modelSetTexture(cursor->model, cursor->norm.texture);
|
||||
egl_modelRender(cursor->model);
|
||||
|
||||
egl_shaderUse(cursor->mono.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->mono, false, pos.x, pos.y,
|
||||
size.w, size.h, scale);
|
||||
setCursorTexUniforms(cursor, &cursor->mono, true, pos.x, pos.y, size.w, size.h);
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
||||
egl_modelSetTexture(cursor->model, cursor->mono.texture);
|
||||
egl_modelRender(cursor->model);
|
||||
@@ -417,10 +358,17 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
case LG_CURSOR_COLOR:
|
||||
{
|
||||
egl_shaderUse(cursor->norm.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y,
|
||||
size.w, size.h, scale);
|
||||
setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y, size.w, size.h);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
egl_modelSetTexture(cursor->model, cursor->norm.texture);
|
||||
egl_modelRender(cursor->model);
|
||||
break;
|
||||
}
|
||||
|
||||
case LG_CURSOR_MASKED_COLOR:
|
||||
{
|
||||
egl_shaderUse(cursor->mono.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->mono, false, pos.x, pos.y, size.w, size.h);
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
||||
egl_modelRender(cursor->model);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -45,10 +45,8 @@ bool egl_cursorSetShape(
|
||||
|
||||
void egl_cursorSetSize(EGL_Cursor * cursor, const float x, const float y);
|
||||
|
||||
void egl_cursorSetScale(EGL_Cursor * cursor, const float scale);
|
||||
|
||||
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible,
|
||||
const float x, const float y, const float hx, const float hy);
|
||||
const float x, const float y);
|
||||
|
||||
struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
LG_RendererRotate rotate, int width, int height);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -57,6 +57,7 @@ struct EGL_Desktop
|
||||
EGLDisplay * display;
|
||||
|
||||
EGL_Texture * texture;
|
||||
GLuint sampler;
|
||||
struct DesktopShader shader;
|
||||
EGL_DesktopRects * mesh;
|
||||
CountedBuffer * matrix;
|
||||
@@ -65,10 +66,6 @@ struct EGL_Desktop
|
||||
int width, height;
|
||||
LG_RendererRotate rotate;
|
||||
|
||||
bool useSpice;
|
||||
int spiceWidth, spiceHeight;
|
||||
EGL_Texture * spiceTexture;
|
||||
|
||||
// scale algorithm
|
||||
int scaleAlgo;
|
||||
|
||||
@@ -129,7 +126,7 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
|
||||
desktop->display = display;
|
||||
|
||||
if (!egl_textureInit(&desktop->texture, display,
|
||||
useDMA ? EGL_TEXTYPE_DMABUF : EGL_TEXTYPE_FRAMEBUFFER))
|
||||
useDMA ? EGL_TEXTYPE_DMABUF : EGL_TEXTYPE_FRAMEBUFFER, true))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the desktop texture");
|
||||
return false;
|
||||
@@ -157,7 +154,7 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
|
||||
return false;
|
||||
}
|
||||
|
||||
app_registerKeybind(0, 'N', toggleNV, desktop,
|
||||
app_registerKeybind(KEY_N, toggleNV, desktop,
|
||||
"Toggle night vision mode");
|
||||
|
||||
desktop->nvMax = option_get_int("egl", "nvGainMax");
|
||||
@@ -206,7 +203,6 @@ void egl_desktopFree(EGL_Desktop ** desktop)
|
||||
return;
|
||||
|
||||
egl_textureFree (&(*desktop)->texture );
|
||||
egl_textureFree (&(*desktop)->spiceTexture );
|
||||
egl_shaderFree (&(*desktop)->shader.shader);
|
||||
egl_desktopRectsFree(&(*desktop)->mesh );
|
||||
countedBufferRelease(&(*desktop)->matrix );
|
||||
@@ -232,8 +228,7 @@ void egl_desktopConfigUI(EGL_Desktop * desktop)
|
||||
for (int i = 0; i < EGL_SCALE_MAX; ++i)
|
||||
{
|
||||
bool selected = i == desktop->scaleAlgo;
|
||||
if (igSelectable_Bool(algorithmNames[i], selected, 0,
|
||||
(ImVec2) { 0.0f, 0.0f }))
|
||||
if (igSelectableBool(algorithmNames[i], selected, 0, (ImVec2) { 0.0f, 0.0f }))
|
||||
desktop->scaleAlgo = i;
|
||||
if (selected)
|
||||
igSetItemDefaultFocus();
|
||||
@@ -285,14 +280,14 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
|
||||
return false;
|
||||
}
|
||||
|
||||
desktop->width = format.frameWidth;
|
||||
desktop->height = format.frameHeight;
|
||||
desktop->width = format.width;
|
||||
desktop->height = format.height;
|
||||
|
||||
if (!egl_textureSetup(
|
||||
desktop->texture,
|
||||
pixFmt,
|
||||
format.frameWidth,
|
||||
format.frameHeight,
|
||||
format.width,
|
||||
format.height,
|
||||
format.pitch
|
||||
))
|
||||
{
|
||||
@@ -300,6 +295,12 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
|
||||
return false;
|
||||
}
|
||||
|
||||
glGenSamplers(1, &desktop->sampler);
|
||||
glSamplerParameteri(desktop->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(desktop->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(desktop->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(desktop->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -334,7 +335,7 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dma
|
||||
|
||||
egl_textureFree(&desktop->texture);
|
||||
if (!egl_textureInit(&desktop->texture, desktop->display,
|
||||
EGL_TEXTYPE_FRAMEBUFFER))
|
||||
EGL_TEXTYPE_FRAMEBUFFER, true))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the desktop texture");
|
||||
return false;
|
||||
@@ -364,27 +365,11 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
|
||||
LG_RendererRotate rotate, const struct DamageRects * rects)
|
||||
{
|
||||
EGL_Texture * tex;
|
||||
int width, height;
|
||||
|
||||
if (desktop->useSpice)
|
||||
{
|
||||
tex = desktop->spiceTexture;
|
||||
width = desktop->spiceWidth;
|
||||
height = desktop->spiceHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
tex = desktop->texture;
|
||||
width = desktop->width;
|
||||
height = desktop->height;
|
||||
}
|
||||
|
||||
if (outputWidth == 0 && outputHeight == 0)
|
||||
DEBUG_FATAL("outputWidth || outputHeight == 0");
|
||||
|
||||
enum EGL_TexStatus status;
|
||||
if ((status = egl_textureProcess(tex)) != EGL_TEX_STATUS_OK)
|
||||
if ((status = egl_textureProcess(desktop->texture)) != EGL_TEX_STATUS_OK)
|
||||
{
|
||||
if (status != EGL_TEX_STATUS_NOTREADY)
|
||||
DEBUG_ERROR("Failed to process the desktop texture");
|
||||
@@ -393,13 +378,13 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
int scaleAlgo = EGL_SCALE_NEAREST;
|
||||
|
||||
egl_desktopRectsMatrix((float *)desktop->matrix->data,
|
||||
width, height, x, y, scaleX, scaleY, rotate);
|
||||
egl_desktopRectsUpdate(desktop->mesh, rects, width, height);
|
||||
desktop->width, desktop->height, x, y, scaleX, scaleY, rotate);
|
||||
egl_desktopRectsUpdate(desktop->mesh, rects, desktop->width, desktop->height);
|
||||
|
||||
if (atomic_exchange(&desktop->processFrame, false) ||
|
||||
egl_postProcessConfigModified(desktop->pp))
|
||||
egl_postProcessRun(desktop->pp, tex, desktop->mesh,
|
||||
width, height, outputWidth, outputHeight);
|
||||
egl_postProcessRun(desktop->pp, desktop->texture, desktop->mesh,
|
||||
desktop->width, desktop->height, outputWidth, outputHeight);
|
||||
|
||||
unsigned int finalSizeX, finalSizeY;
|
||||
GLuint texture = egl_postProcessGetOutput(desktop->pp,
|
||||
@@ -410,9 +395,9 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glBindSampler(0, tex->sampler);
|
||||
glBindSampler(0, desktop->sampler);
|
||||
|
||||
if (finalSizeX > width || finalSizeY > height)
|
||||
if (finalSizeX > desktop->width || finalSizeY > desktop->height)
|
||||
scaleType = EGL_DESKTOP_DOWNSCALE;
|
||||
|
||||
switch (desktop->scaleAlgo)
|
||||
@@ -446,7 +431,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
{
|
||||
.type = EGL_UNIFORM_TYPE_2F,
|
||||
.location = shader->uDesktopSize,
|
||||
.f = { width, height },
|
||||
.f = { desktop->width, desktop->height },
|
||||
},
|
||||
{
|
||||
.type = EGL_UNIFORM_TYPE_M3x2FV,
|
||||
@@ -472,59 +457,3 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height)
|
||||
{
|
||||
if (!desktop->spiceTexture)
|
||||
if (!egl_textureInit(&desktop->spiceTexture, desktop->display,
|
||||
EGL_TEXTYPE_BUFFER_MAP))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the spice desktop texture");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!egl_textureSetup(
|
||||
desktop->spiceTexture,
|
||||
EGL_PF_BGRA,
|
||||
width,
|
||||
height,
|
||||
width * 4
|
||||
))
|
||||
{
|
||||
DEBUG_ERROR("Failed to setup the spice desktop texture");
|
||||
return;
|
||||
}
|
||||
|
||||
desktop->spiceWidth = width;
|
||||
desktop->spiceHeight = height;
|
||||
}
|
||||
|
||||
void egl_desktopSpiceDrawFill(EGL_Desktop * desktop, int x, int y, int width,
|
||||
int height, uint32_t color)
|
||||
{
|
||||
/* this is a fairly hacky way to do this, but since it's only for the fallback
|
||||
* spice display it's not really an issue */
|
||||
|
||||
uint32_t line[width];
|
||||
for(int x = 0; x < width; ++x)
|
||||
line[x] = color;
|
||||
|
||||
for(; y < height; ++y)
|
||||
egl_textureUpdateRect(desktop->spiceTexture,
|
||||
x, y, width, 1, sizeof(line), (uint8_t *)line, false);
|
||||
|
||||
atomic_store(&desktop->processFrame, true);
|
||||
}
|
||||
|
||||
void egl_desktopSpiceDrawBitmap(EGL_Desktop * desktop, int x, int y, int width,
|
||||
int height, int stride, uint8_t * data, bool topDown)
|
||||
{
|
||||
egl_textureUpdateRect(desktop->spiceTexture,
|
||||
x, y, width, height, stride, data, topDown);
|
||||
atomic_store(&desktop->processFrame, true);
|
||||
}
|
||||
|
||||
void egl_desktopSpiceShow(EGL_Desktop * desktop, bool show)
|
||||
{
|
||||
desktop->useSpice = show;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* 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,
|
||||
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
|
||||
LG_RendererRotate rotate, const struct DamageRects * rects);
|
||||
|
||||
void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height);
|
||||
void egl_desktopSpiceDrawFill(EGL_Desktop * desktop, int x, int y, int width,
|
||||
int height, uint32_t color);
|
||||
void egl_desktopSpiceDrawBitmap(EGL_Desktop * desktop, int x, int y, int width,
|
||||
int height, int stride, uint8_t * data, bool topDown);
|
||||
void egl_desktopSpiceShow(EGL_Desktop * desktop, bool show);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -32,10 +32,6 @@
|
||||
|
||||
struct EGL_DesktopRects
|
||||
{
|
||||
GLfloat * lastVertices;
|
||||
int lastVerticesCount;
|
||||
int lastVerticesSize;
|
||||
|
||||
GLuint buffers[2];
|
||||
GLuint vao;
|
||||
int count;
|
||||
@@ -92,7 +88,6 @@ void egl_desktopRectsFree(EGL_DesktopRects ** rects_)
|
||||
|
||||
glDeleteVertexArrays(1, &rects->vao);
|
||||
glDeleteBuffers(2, rects->buffers);
|
||||
free(rects->lastVertices);
|
||||
free(rects);
|
||||
*rects_ = NULL;
|
||||
}
|
||||
@@ -118,8 +113,7 @@ void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects *
|
||||
return;
|
||||
}
|
||||
|
||||
const int count = (!data || data->count < 0 ? 1 : data->count) * 8;
|
||||
GLfloat vertices[count];
|
||||
GLfloat vertices[(!data || data->count < 0 ? 1 : data->count) * 8];
|
||||
if (!data || data->count < 0)
|
||||
{
|
||||
FrameDamageRect full = {
|
||||
@@ -137,30 +131,6 @@ void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects *
|
||||
rectToVertices(vertices + i * 8, data->rects + i);
|
||||
}
|
||||
|
||||
// check if the value actually changed and needs updating
|
||||
if (count == rects->lastVerticesCount &&
|
||||
memcmp(rects->lastVertices, vertices, sizeof(GLfloat) * count) == 0)
|
||||
return;
|
||||
|
||||
// ensure the local storage is large enough
|
||||
if (count > rects->lastVerticesSize)
|
||||
{
|
||||
if (rects->lastVertices)
|
||||
free(rects->lastVertices);
|
||||
|
||||
rects->lastVertices = malloc(sizeof(GLfloat) * count);
|
||||
if (!rects->lastVertices)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return;
|
||||
}
|
||||
rects->lastVerticesSize = count;
|
||||
}
|
||||
|
||||
// copy the last value for later comparison
|
||||
rects->lastVerticesCount = count;
|
||||
memcpy(rects->lastVertices, vertices, sizeof(GLfloat) * count);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, rects->buffers[0]);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, rects->count * 8 * sizeof(GLfloat), vertices);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
69
client/renderers/EGL/draw.c
Normal file
69
client/renderers/EGL/draw.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 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;
|
||||
*dst = y + (inner * s); ++dst;
|
||||
*dst = 0.0f; ++dst;
|
||||
*dst = x + (outer * c); ++dst;
|
||||
*dst = y + (outer * s); ++dst;
|
||||
*dst = 0.0f; ++dst;
|
||||
}
|
||||
|
||||
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;
|
||||
*dst = y + (inner * s); ++dst;
|
||||
*dst = 0.0f; ++dst;
|
||||
*dst = x + (outer * c); ++dst;
|
||||
*dst = y + (outer * s); ++dst;
|
||||
*dst = 0.0f; ++dst;
|
||||
}
|
||||
|
||||
egl_modelAddVerts(model, v, NULL, (pts + 1) * 2);
|
||||
free(v);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -18,20 +18,12 @@
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_OVERLAY_MSG_H
|
||||
#define _H_OVERLAY_MSG_H
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include "model.h"
|
||||
|
||||
#include "app.h"
|
||||
void egl_drawTorus(EGL_Model * model, unsigned int pts, float x, float y,
|
||||
float inner, float outer);
|
||||
|
||||
bool overlayMsg_modal(void);
|
||||
|
||||
MsgBoxHandle overlayMsg_show(
|
||||
const char * caption, MsgBoxConfirmCallback confirm, void * opaque,
|
||||
const char * fmt, va_list args);
|
||||
|
||||
void overlayMsg_close(MsgBoxHandle handle);
|
||||
|
||||
#endif
|
||||
void egl_drawTorusArc(EGL_Model * model, unsigned int pts, float x, float y,
|
||||
float inner, float outer, float s, float e);
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -39,15 +39,18 @@
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "app.h"
|
||||
#include "egl_dynprocs.h"
|
||||
#include "model.h"
|
||||
#include "shader.h"
|
||||
#include "damage.h"
|
||||
#include "desktop.h"
|
||||
#include "cursor.h"
|
||||
#include "splash.h"
|
||||
#include "postprocess.h"
|
||||
#include "util.h"
|
||||
|
||||
#define SPLASH_FADE_TIME 1000000
|
||||
#define MAX_BUFFER_AGE 3
|
||||
#define DESKTOP_DAMAGE_COUNT 4
|
||||
#define MAX_ACCUMULATED_DAMAGE ((KVMFR_MAX_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2) * MAX_BUFFER_AGE)
|
||||
@@ -75,11 +78,15 @@ struct Inst
|
||||
|
||||
EGL_Desktop * desktop; // the desktop
|
||||
EGL_Cursor * cursor; // the mouse cursor
|
||||
EGL_Splash * splash; // the splash screen
|
||||
EGL_Damage * damage; // the damage display
|
||||
bool imgui; // if imgui was initialized
|
||||
|
||||
LG_RendererFormat format;
|
||||
bool formatValid;
|
||||
bool start;
|
||||
uint64_t waitFadeTime;
|
||||
bool waitDone;
|
||||
|
||||
int width, height;
|
||||
float uiScale;
|
||||
@@ -96,11 +103,9 @@ struct Inst
|
||||
|
||||
bool cursorVisible;
|
||||
int cursorX , cursorY;
|
||||
int cursorHX , cursorHY;
|
||||
float mouseWidth , mouseHeight;
|
||||
float mouseScaleX, mouseScaleY;
|
||||
bool showDamage;
|
||||
bool scalePointer;
|
||||
|
||||
struct CursorState cursorLast;
|
||||
|
||||
@@ -117,9 +122,6 @@ struct Inst
|
||||
|
||||
RingBuffer importTimings;
|
||||
GraphHandle importGraph;
|
||||
|
||||
bool showSpice;
|
||||
int spiceWidth, spiceHeight;
|
||||
};
|
||||
|
||||
static struct Option egl_options[] =
|
||||
@@ -195,13 +197,6 @@ static struct Option egl_options[] =
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false
|
||||
},
|
||||
{
|
||||
.module = "egl",
|
||||
.name = "scalePointer",
|
||||
.description = "Keep the pointer size 1:1 when downscaling",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
|
||||
{0}
|
||||
};
|
||||
@@ -254,8 +249,7 @@ static bool egl_create(LG_Renderer ** renderer, const LG_RendererParams params,
|
||||
this->desktopDamage[0].count = -1;
|
||||
|
||||
this->importTimings = ringbuffer_new(256, sizeof(float));
|
||||
this->importGraph = app_registerGraph("IMPORT", this->importTimings,
|
||||
0.0f, 5.0f, NULL);
|
||||
this->importGraph = app_registerGraph("IMPORT", this->importTimings, 0.0f, 5.0f);
|
||||
|
||||
*needsOpenGL = false;
|
||||
return true;
|
||||
@@ -275,10 +269,12 @@ static void egl_deinitialize(LG_Renderer * renderer)
|
||||
if (this->imgui)
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
|
||||
app_unregisterGraph(this->importGraph);
|
||||
ringbuffer_free(&this->importTimings);
|
||||
|
||||
egl_desktopFree(&this->desktop);
|
||||
egl_cursorFree (&this->cursor);
|
||||
egl_splashFree (&this->splash);
|
||||
egl_damageFree (&this->damage);
|
||||
|
||||
LG_LOCK_FREE(this->lock);
|
||||
@@ -317,6 +313,7 @@ static void egl_onRestart(LG_Renderer * renderer)
|
||||
|
||||
eglDestroyContext(this->display, this->frameContext);
|
||||
this->frameContext = NULL;
|
||||
this->start = false;
|
||||
|
||||
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
||||
this->desktopDamage[this->desktopDamageIdx].count = -1;
|
||||
@@ -325,17 +322,6 @@ static void egl_onRestart(LG_Renderer * renderer)
|
||||
|
||||
static void egl_calc_mouse_size(struct Inst * this)
|
||||
{
|
||||
if (this->showSpice)
|
||||
{
|
||||
this->mouseScaleX = 2.0f / this->spiceWidth;
|
||||
this->mouseScaleY = 2.0f / this->spiceHeight;
|
||||
egl_cursorSetSize(this->cursor,
|
||||
(this->mouseWidth * (1.0f / this->spiceWidth )) * this->scaleX,
|
||||
(this->mouseHeight * (1.0f / this->spiceHeight)) * this->scaleY
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->formatValid)
|
||||
return;
|
||||
|
||||
@@ -345,18 +331,18 @@ static void egl_calc_mouse_size(struct Inst * this)
|
||||
{
|
||||
case LG_ROTATE_0:
|
||||
case LG_ROTATE_180:
|
||||
this->mouseScaleX = 2.0f / this->format.screenWidth;
|
||||
this->mouseScaleY = 2.0f / this->format.screenHeight;
|
||||
w = this->format.screenWidth;
|
||||
h = this->format.screenHeight;
|
||||
this->mouseScaleX = 2.0f / this->format.width;
|
||||
this->mouseScaleY = 2.0f / this->format.height;
|
||||
w = this->format.width;
|
||||
h = this->format.height;
|
||||
break;
|
||||
|
||||
case LG_ROTATE_90:
|
||||
case LG_ROTATE_270:
|
||||
this->mouseScaleX = 2.0f / this->format.screenHeight;
|
||||
this->mouseScaleY = 2.0f / this->format.screenWidth;
|
||||
w = this->format.screenHeight;
|
||||
h = this->format.screenWidth;
|
||||
this->mouseScaleX = 2.0f / this->format.height;
|
||||
this->mouseScaleY = 2.0f / this->format.width;
|
||||
w = this->format.height;
|
||||
h = this->format.width;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -385,19 +371,6 @@ static void egl_calc_mouse_size(struct Inst * this)
|
||||
|
||||
static void egl_calc_mouse_state(struct Inst * this)
|
||||
{
|
||||
if (this->showSpice)
|
||||
{
|
||||
egl_cursorSetState(
|
||||
this->cursor,
|
||||
this->cursorVisible,
|
||||
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
|
||||
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY,
|
||||
((float)this->cursorHX * this->mouseScaleX) * this->scaleX,
|
||||
((float)this->cursorHY * this->mouseScaleY) * this->scaleY
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->formatValid)
|
||||
return;
|
||||
|
||||
@@ -408,10 +381,8 @@ static void egl_calc_mouse_state(struct Inst * this)
|
||||
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
|
||||
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
|
||||
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -420,10 +391,8 @@ static void egl_calc_mouse_state(struct Inst * this)
|
||||
egl_cursorSetState(
|
||||
this->cursor,
|
||||
this->cursorVisible,
|
||||
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleY,
|
||||
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleX,
|
||||
((float)this->cursorHX * this->mouseScaleX) * this->scaleY,
|
||||
((float)this->cursorHY * this->mouseScaleY) * this->scaleX
|
||||
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleY,
|
||||
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleX
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -437,14 +406,14 @@ static void egl_update_scale_type(struct Inst * this)
|
||||
{
|
||||
case LG_ROTATE_0:
|
||||
case LG_ROTATE_180:
|
||||
width = this->format.frameWidth;
|
||||
height = this->format.frameHeight;
|
||||
width = this->format.width;
|
||||
height = this->format.height;
|
||||
break;
|
||||
|
||||
case LG_ROTATE_90:
|
||||
case LG_ROTATE_270:
|
||||
width = this->format.frameHeight;
|
||||
height = this->format.frameWidth;
|
||||
width = this->format.height;
|
||||
height = this->format.width;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -480,8 +449,8 @@ static void egl_onResize(LG_Renderer * renderer, const int width, const int heig
|
||||
|
||||
if (destRect.valid)
|
||||
{
|
||||
this->translateX = -1.0f + (((this->destRect.w / 2) + this->destRect.x) * 2) / (float)this->width;
|
||||
this->translateY = 1.0f - (((this->destRect.h / 2) + this->destRect.y) * 2) / (float)this->height;
|
||||
this->translateX = 1.0f - (((this->destRect.w / 2) + this->destRect.x) * 2) / (float)this->width;
|
||||
this->translateY = 1.0f - (((this->destRect.h / 2) + this->destRect.y) * 2) / (float)this->height;
|
||||
this->scaleX = (float)this->destRect.w / (float)this->width;
|
||||
this->scaleY = (float)this->destRect.h / (float)this->height;
|
||||
this->viewportWidth = this->destRect.w;
|
||||
@@ -496,16 +465,6 @@ static void egl_onResize(LG_Renderer * renderer, const int width, const int heig
|
||||
this->screenScaleY = 1.0f / this->height;
|
||||
|
||||
egl_calc_mouse_state(this);
|
||||
if (this->scalePointer)
|
||||
{
|
||||
float scale = max(1.0f,
|
||||
this->formatValid ?
|
||||
max(
|
||||
(float)this->format.screenWidth / this->width,
|
||||
(float)this->format.screenHeight / this->height)
|
||||
: 1.0f);
|
||||
egl_cursorSetScale(this->cursor, scale);
|
||||
}
|
||||
|
||||
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
||||
this->desktopDamage[this->desktopDamageIdx].count = -1;
|
||||
@@ -513,7 +472,6 @@ static void egl_onResize(LG_Renderer * renderer, const int width, const int heig
|
||||
|
||||
// this is needed to refresh the font atlas texture
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplOpenGL3_Init("#version 300 es");
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
|
||||
egl_damageResize(this->damage, this->translateX, this->translateY, this->scaleX, this->scaleY);
|
||||
@@ -539,15 +497,12 @@ static bool egl_onMouseShape(LG_Renderer * renderer, const LG_RendererCursor cur
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool egl_onMouseEvent(LG_Renderer * renderer, const bool visible,
|
||||
int x, int y, const int hx, const int hy)
|
||||
static bool egl_onMouseEvent(LG_Renderer * renderer, const bool visible, const int x, const int y)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
this->cursorVisible = visible;
|
||||
this->cursorX = x + hx;
|
||||
this->cursorY = y + hy;
|
||||
this->cursorHX = hx;
|
||||
this->cursorHY = hy;
|
||||
this->cursorX = x;
|
||||
this->cursorY = y;
|
||||
egl_calc_mouse_state(this);
|
||||
return true;
|
||||
}
|
||||
@@ -579,14 +534,8 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
|
||||
}
|
||||
}
|
||||
|
||||
if (this->scalePointer)
|
||||
{
|
||||
float scale = max(1.0f, (float)format.screenWidth / this->width);
|
||||
egl_cursorSetScale(this->cursor, scale);
|
||||
}
|
||||
|
||||
egl_update_scale_type(this);
|
||||
egl_damageSetup(this->damage, format.frameWidth, format.frameHeight);
|
||||
egl_damageSetup(this->damage, format.width, format.height);
|
||||
|
||||
/* we need full screen damage when the format changes */
|
||||
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
||||
@@ -609,6 +558,8 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int d
|
||||
}
|
||||
ringbuffer_push(this->importTimings, &(float){ (nanotime() - start) * 1e-6f });
|
||||
|
||||
this->start = true;
|
||||
|
||||
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
||||
struct DesktopDamage * damage = this->desktopDamage + this->desktopDamageIdx;
|
||||
if (damage->count == -1 || damageRectsCount == 0 ||
|
||||
@@ -785,13 +736,7 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
}
|
||||
|
||||
const char * client_exts = eglQueryString(this->display, EGL_EXTENSIONS);
|
||||
if (!client_exts)
|
||||
{
|
||||
DEBUG_ERROR("Failed to query EGL_EXTENSIONS");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool debug = option_get_bool("egl", "debug");
|
||||
bool debugContext = option_get_bool("egl", "debug");
|
||||
EGLint ctxattr[5];
|
||||
int ctxidx = 0;
|
||||
|
||||
@@ -801,17 +746,17 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
if (maj > 1 || (maj == 1 && min >= 5))
|
||||
{
|
||||
ctxattr[ctxidx++] = EGL_CONTEXT_OPENGL_DEBUG;
|
||||
ctxattr[ctxidx++] = debug ? EGL_TRUE : EGL_FALSE;
|
||||
ctxattr[ctxidx++] = debugContext ? EGL_TRUE : EGL_FALSE;
|
||||
}
|
||||
else if (util_hasGLExt(client_exts, "EGL_KHR_create_context"))
|
||||
{
|
||||
ctxattr[ctxidx++] = EGL_CONTEXT_FLAGS_KHR;
|
||||
ctxattr[ctxidx++] = debug ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0;
|
||||
ctxattr[ctxidx++] = debugContext ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0;
|
||||
}
|
||||
else if (debug)
|
||||
else if (debugContext)
|
||||
DEBUG_WARN("Cannot create debug contexts before EGL 1.5 without EGL_KHR_create_context");
|
||||
|
||||
ctxattr[ctxidx] = EGL_NONE;
|
||||
ctxattr[ctxidx++] = EGL_NONE;
|
||||
|
||||
this->context = eglCreateContext(this->display, this->configs, EGL_NO_CONTEXT, ctxattr);
|
||||
if (this->context == EGL_NO_CONTEXT)
|
||||
@@ -839,30 +784,15 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
|
||||
eglMakeCurrent(this->display, this->surface, this->surface, this->context);
|
||||
const char * gl_exts = (const char *)glGetString(GL_EXTENSIONS);
|
||||
if (!gl_exts)
|
||||
{
|
||||
DEBUG_ERROR("Failed to query GL_EXTENSIONS");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char * vendor = (const char *)glGetString(GL_VENDOR);
|
||||
if (!vendor)
|
||||
{
|
||||
DEBUG_ERROR("Failed to query GL_VENDOR");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_INFO("EGL : %d.%d", maj, min);
|
||||
DEBUG_INFO("Vendor : %s", vendor);
|
||||
DEBUG_INFO("Renderer: %s", glGetString(GL_RENDERER));
|
||||
DEBUG_INFO("Version : %s", glGetString(GL_VERSION ));
|
||||
DEBUG_INFO("EGL APIs: %s", eglQueryString(this->display, EGL_CLIENT_APIS));
|
||||
|
||||
if (debug)
|
||||
{
|
||||
DEBUG_INFO("EGL Exts: %s", client_exts);
|
||||
DEBUG_INFO("GL Exts : %s", gl_exts);
|
||||
}
|
||||
DEBUG_INFO("EGL Exts: %s", client_exts);
|
||||
DEBUG_INFO("GL Exts : %s", gl_exts);
|
||||
|
||||
GLint esMaj, esMin;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &esMaj);
|
||||
@@ -888,8 +818,6 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
if (this->noSwapDamage)
|
||||
DEBUG_WARN("egl:noSwapDamage specified, disabling swap buffers with damage.");
|
||||
|
||||
this->scalePointer = option_get_bool("egl", "scalePointer");
|
||||
|
||||
if (!g_egl_dynProcs.glEGLImageTargetTexture2DOES)
|
||||
DEBUG_INFO("glEGLImageTargetTexture2DOES unavilable, DMA support disabled");
|
||||
else if (!g_egl_dynProcs.eglCreateImage || !g_egl_dynProcs.eglDestroyImage)
|
||||
@@ -911,7 +839,7 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
}
|
||||
}
|
||||
|
||||
if (debug)
|
||||
if (debugContext)
|
||||
{
|
||||
if ((esMaj > 3 || (esMaj == 3 && esMin >= 2)) && g_egl_dynProcs.glDebugMessageCallback)
|
||||
{
|
||||
@@ -943,6 +871,12 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_splashInit(&this->splash))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the splash screen");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_damageInit(&this->damage))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the damage display");
|
||||
@@ -961,6 +895,12 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool egl_needsRender(LG_Renderer * renderer)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
return !this->waitDone;
|
||||
}
|
||||
|
||||
inline static EGLint egl_bufferAge(struct Inst * this)
|
||||
{
|
||||
if (!this->hasBufferAge)
|
||||
@@ -987,26 +927,21 @@ inline static void renderLetterBox(struct Inst * this)
|
||||
|
||||
if (hLB)
|
||||
{
|
||||
// left
|
||||
glScissor(0, 0, this->destRect.x, this->height);
|
||||
glScissor(0.0f, 0.0f, this->destRect.x + 0.5f, this->height + 0.5f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// right
|
||||
float x2 = this->destRect.x + this->destRect.w;
|
||||
glScissor(x2, 0, this->width - x2, this->height);
|
||||
glScissor(x2 - 0.5f, 0.0f, this->width - x2 + 1.0f, this->height + 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
if (vLB)
|
||||
{
|
||||
// top
|
||||
glScissor(0, this->height - this->destRect.y,
|
||||
this->width, this->destRect.y);
|
||||
glScissor(0.0f, 0.0f, this->width + 0.5f, this->destRect.y + 0.5f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// bottom
|
||||
int y2 = this->destRect.y + this->destRect.h;
|
||||
glScissor(0, 0, this->width, this->height - y2);
|
||||
float y2 = this->destRect.y + this->destRect.h;
|
||||
glScissor(0.0f, y2 - 0.5f, this->width + 1.0f, this->height - y2 + 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
@@ -1020,9 +955,8 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
EGLint bufferAge = egl_bufferAge(this);
|
||||
bool renderAll = invalidateWindow || this->hadOverlay ||
|
||||
bufferAge <= 0 || bufferAge > MAX_BUFFER_AGE ||
|
||||
this->showSpice;
|
||||
bool renderAll = invalidateWindow || !this->start || this->hadOverlay ||
|
||||
bufferAge <= 0 || bufferAge > MAX_BUFFER_AGE;
|
||||
|
||||
bool hasOverlay = false;
|
||||
struct CursorState cursorState = { .visible = false };
|
||||
@@ -1055,8 +989,8 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
int y = rect->y > 0 ? rect->y - 1 : 0;
|
||||
accumulated->rects[accumulated->count++] = (struct FrameDamageRect) {
|
||||
.x = x, .y = y,
|
||||
.width = min(this->format.frameWidth - x, rect->width + 2),
|
||||
.height = min(this->format.frameHeight - y, rect->height + 2),
|
||||
.width = min(this->format.width - x, rect->width + 2),
|
||||
.height = min(this->format.height - y, rect->height + 2),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1069,8 +1003,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
if (!renderAll)
|
||||
{
|
||||
double matrix[6];
|
||||
egl_screenToDesktopMatrix(matrix,
|
||||
this->format.frameWidth, this->format.frameHeight,
|
||||
egl_screenToDesktopMatrix(matrix, this->format.width, this->format.height,
|
||||
this->translateX, this->translateY, this->scaleX, this->scaleY, rotate,
|
||||
this->width, this->height);
|
||||
|
||||
@@ -1089,7 +1022,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
for (int j = 0; j < count; ++j)
|
||||
accumulated->count += egl_screenToDesktop(
|
||||
accumulated->rects + accumulated->count, matrix, damage + j,
|
||||
this->format.frameWidth, this->format.frameHeight
|
||||
this->format.width, this->format.height
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1097,7 +1030,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
}
|
||||
++this->overlayHistoryIdx;
|
||||
|
||||
if (this->destRect.w > 0 && this->destRect.h > 0)
|
||||
if (this->start && this->destRect.w > 0 && this->destRect.h > 0)
|
||||
{
|
||||
if (egl_desktopRender(this->desktop,
|
||||
this->destRect.w, this->destRect.h,
|
||||
@@ -1105,6 +1038,14 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
this->scaleX , this->scaleY ,
|
||||
this->scaleType , rotate, renderAll ? NULL : accumulated))
|
||||
{
|
||||
if (!this->waitFadeTime)
|
||||
{
|
||||
if (!this->params.quickSplash)
|
||||
this->waitFadeTime = microtime() + SPLASH_FADE_TIME;
|
||||
else
|
||||
this->waitDone = true;
|
||||
}
|
||||
|
||||
cursorState = egl_cursorRender(this->cursor,
|
||||
(this->format.rotate + rotate) % LG_ROTATE_MAX,
|
||||
this->width, this->height);
|
||||
@@ -1115,6 +1056,35 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
|
||||
renderLetterBox(this);
|
||||
|
||||
if (!this->waitDone)
|
||||
{
|
||||
float a = 1.0f;
|
||||
if (!this->waitFadeTime)
|
||||
a = 1.0f;
|
||||
else
|
||||
{
|
||||
uint64_t t = microtime();
|
||||
if (t > this->waitFadeTime)
|
||||
this->waitDone = true;
|
||||
else
|
||||
{
|
||||
uint64_t delta = this->waitFadeTime - t;
|
||||
a = 1.0f / SPLASH_FADE_TIME * delta;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->waitDone)
|
||||
{
|
||||
egl_splashRender(this->splash, a, this->splashRatio);
|
||||
hasOverlay = true;
|
||||
}
|
||||
}
|
||||
else if (!this->start)
|
||||
{
|
||||
egl_splashRender(this->splash, 1.0f, this->splashRatio);
|
||||
hasOverlay = true;
|
||||
}
|
||||
|
||||
hasOverlay |= egl_damageRender(this->damage, rotate, newFrame ? desktopDamage : NULL);
|
||||
hasOverlay |= invalidateWindow;
|
||||
|
||||
@@ -1160,8 +1130,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
else
|
||||
{
|
||||
double matrix[6];
|
||||
egl_desktopToScreenMatrix(matrix,
|
||||
this->format.frameWidth, this->format.frameHeight,
|
||||
egl_desktopToScreenMatrix(matrix, this->format.width, this->format.height,
|
||||
this->translateX, this->translateY, this->scaleX, this->scaleY, rotate,
|
||||
this->width, this->height);
|
||||
|
||||
@@ -1180,72 +1149,6 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void * egl_createTexture(LG_Renderer * renderer,
|
||||
int width, int height, uint8_t * data)
|
||||
{
|
||||
GLuint tex;
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
data);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
return (void*)(intptr_t)tex;
|
||||
}
|
||||
|
||||
static void egl_freeTexture(LG_Renderer * renderer, void * texture)
|
||||
{
|
||||
GLuint tex = (GLuint)(intptr_t)texture;
|
||||
glDeleteTextures(1, &tex);
|
||||
}
|
||||
|
||||
static void egl_spiceConfigure(LG_Renderer * renderer, int width, int height)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
this->spiceWidth = width;
|
||||
this->spiceHeight = height;
|
||||
egl_desktopSpiceConfigure(this->desktop, width, height);
|
||||
}
|
||||
|
||||
static void egl_spiceDrawFill(LG_Renderer * renderer, int x, int y, int width,
|
||||
int height, uint32_t color)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
egl_desktopSpiceDrawFill(this->desktop, x, y, width, height, color);
|
||||
}
|
||||
|
||||
static void egl_spiceDrawBitmap(LG_Renderer * renderer, int x, int y, int width,
|
||||
int height, int stride, uint8_t * data, bool topDown)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
egl_desktopSpiceDrawBitmap(this->desktop, x, y, width, height, stride,
|
||||
data, topDown);
|
||||
}
|
||||
|
||||
static void egl_spiceShow(LG_Renderer * renderer, bool show)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
this->showSpice = show;
|
||||
egl_calc_mouse_size(this);
|
||||
egl_desktopSpiceShow(this->desktop, show);
|
||||
}
|
||||
|
||||
struct LG_RendererOps LGR_EGL =
|
||||
{
|
||||
.getName = egl_getName,
|
||||
@@ -1261,12 +1164,6 @@ struct LG_RendererOps LGR_EGL =
|
||||
.onFrameFormat = egl_onFrameFormat,
|
||||
.onFrame = egl_onFrame,
|
||||
.renderStartup = egl_renderStartup,
|
||||
.render = egl_render,
|
||||
.createTexture = egl_createTexture,
|
||||
.freeTexture = egl_freeTexture,
|
||||
|
||||
.spiceConfigure = egl_spiceConfigure,
|
||||
.spiceDrawFill = egl_spiceDrawFill,
|
||||
.spiceDrawBitmap = egl_spiceDrawBitmap,
|
||||
.spiceShow = egl_spiceShow
|
||||
.needsRender = egl_needsRender,
|
||||
.render = egl_render
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -25,8 +25,6 @@
|
||||
typedef enum EGL_TexType
|
||||
{
|
||||
EGL_TEXTYPE_BUFFER,
|
||||
EGL_TEXTYPE_BUFFER_MAP,
|
||||
EGL_TEXTYPE_BUFFER_STREAM,
|
||||
EGL_TEXTYPE_FRAMEBUFFER,
|
||||
EGL_TEXTYPE_DMABUF
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -268,7 +268,7 @@ static bool egl_filterDownscaleImguiConfig(EGL_Filter * filter)
|
||||
for (int i = 0; i < DOWNSCALE_COUNT; ++i)
|
||||
{
|
||||
bool selected = i == this->filter;
|
||||
if (igSelectable_Bool(filterNames[i], selected, 0, (ImVec2) { 0.0f, 0.0f }))
|
||||
if (igSelectableBool(filterNames[i], selected, 0, (ImVec2) { 0.0f, 0.0f }))
|
||||
{
|
||||
redraw = true;
|
||||
this->filter = i;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -40,7 +40,7 @@ bool egl_framebufferInit(EGL_Framebuffer ** fb)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_textureInit(&this->tex, NULL, EGL_TEXTYPE_BUFFER))
|
||||
if (!egl_textureInit(&this->tex, NULL, EGL_TEXTYPE_BUFFER, false))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the texture");
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "texture.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/ll.h"
|
||||
#include "ll.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -124,31 +124,10 @@ void egl_modelSetDefault(EGL_Model * model, bool flipped)
|
||||
void egl_modelAddVerts(EGL_Model * model, const GLfloat * verticies, const GLfloat * uvs, const size_t count)
|
||||
{
|
||||
struct FloatList * fl = malloc(sizeof(*fl));
|
||||
if (!fl)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return;
|
||||
}
|
||||
|
||||
fl->count = count;
|
||||
|
||||
fl->v = malloc(sizeof(GLfloat) * count * 3);
|
||||
if (!fl->v)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
free(fl);
|
||||
return;
|
||||
}
|
||||
|
||||
fl->u = malloc(sizeof(GLfloat) * count * 2);
|
||||
if (!fl->u)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
free(fl->v);
|
||||
free(fl);
|
||||
return;
|
||||
}
|
||||
|
||||
fl->v = malloc(sizeof(GLfloat) * count * 3);
|
||||
fl->u = malloc(sizeof(GLfloat) * count * 2);
|
||||
memcpy(fl->v, verticies, sizeof(GLfloat) * count * 3);
|
||||
|
||||
if (uvs)
|
||||
@@ -185,20 +164,18 @@ void egl_modelRender(EGL_Model * model)
|
||||
|
||||
/* buffer the verticies */
|
||||
struct FloatList * fl;
|
||||
ll_lock(model->verticies);
|
||||
ll_forEachNL(model->verticies, item, fl)
|
||||
for(ll_reset(model->verticies); ll_walk(model->verticies, (void **)&fl);)
|
||||
{
|
||||
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(GLfloat) * fl->count * 3, fl->v);
|
||||
offset += sizeof(GLfloat) * fl->count * 3;
|
||||
}
|
||||
|
||||
/* buffer the uvs */
|
||||
ll_forEachNL(model->verticies, item, fl)
|
||||
for(ll_reset(model->verticies); ll_walk(model->verticies, (void **)&fl);)
|
||||
{
|
||||
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(GLfloat) * fl->count * 2, fl->u);
|
||||
offset += sizeof(GLfloat) * fl->count * 2;
|
||||
}
|
||||
ll_unlock(model->verticies);
|
||||
|
||||
/* set up vertex arrays in the VAO */
|
||||
glEnableVertexAttribArray(0);
|
||||
@@ -222,13 +199,11 @@ void egl_modelRender(EGL_Model * model)
|
||||
/* draw the arrays */
|
||||
GLint offset = 0;
|
||||
struct FloatList * fl;
|
||||
ll_lock(model->verticies);
|
||||
ll_forEachNL(model->verticies, item, fl)
|
||||
for(ll_reset(model->verticies); ll_walk(model->verticies, (void **)&fl);)
|
||||
{
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, offset, fl->count);
|
||||
offset += fl->count;
|
||||
}
|
||||
ll_unlock(model->verticies);
|
||||
|
||||
/* unbind and cleanup */
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -74,12 +74,6 @@ void egl_postProcessEarlyInit(void)
|
||||
.type = OPTION_TYPE_STRING,
|
||||
.value.x_string = ""
|
||||
},
|
||||
{
|
||||
.module = "egl",
|
||||
.name = "preset",
|
||||
.description = "The initial filter preset to load",
|
||||
.type = OPTION_TYPE_STRING
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
option_register(options);
|
||||
@@ -88,8 +82,6 @@ void egl_postProcessEarlyInit(void)
|
||||
EGL_Filters[i]->earlyInit();
|
||||
}
|
||||
|
||||
static void loadPreset(struct EGL_PostProcess * this, const char * name);
|
||||
|
||||
static void loadPresetList(struct EGL_PostProcess * this)
|
||||
{
|
||||
DIR * dir = NULL;
|
||||
@@ -122,9 +114,6 @@ static void loadPresetList(struct EGL_PostProcess * this)
|
||||
}
|
||||
|
||||
struct dirent * entry;
|
||||
const char * preset = option_get_string("egl", "preset");
|
||||
this->activePreset = -1;
|
||||
|
||||
while ((entry = readdir(dir)) != NULL)
|
||||
{
|
||||
if (entry->d_type != DT_REG)
|
||||
@@ -138,21 +127,10 @@ static void loadPresetList(struct EGL_PostProcess * this)
|
||||
goto fail;
|
||||
}
|
||||
stringlist_push(this->presets, name);
|
||||
|
||||
if (preset && strcmp(preset, name) == 0)
|
||||
this->activePreset = stringlist_count(this->presets) - 1;
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
|
||||
if (preset)
|
||||
{
|
||||
if (this->activePreset > -1)
|
||||
loadPreset(this, preset);
|
||||
else
|
||||
DEBUG_WARN("egl:preset '%s' does not exist", preset);
|
||||
}
|
||||
|
||||
this->activePreset = -1;
|
||||
return;
|
||||
|
||||
fail:
|
||||
@@ -356,8 +334,7 @@ static bool presetsUI(struct EGL_PostProcess * this)
|
||||
for (unsigned i = 0; i < stringlist_count(this->presets); ++i)
|
||||
{
|
||||
bool selected = i == this->activePreset;
|
||||
if (igSelectable_Bool(stringlist_at(this->presets, i), selected, 0,
|
||||
(ImVec2) { 0.0f, 0.0f }))
|
||||
if (igSelectableBool(stringlist_at(this->presets, i), selected, 0, (ImVec2) { 0.0f, 0.0f }))
|
||||
{
|
||||
this->activePreset = i;
|
||||
redraw = true;
|
||||
@@ -388,7 +365,7 @@ static bool presetsUI(struct EGL_PostProcess * this)
|
||||
if (igButton("Save preset as...", (ImVec2) { 0.0f, 0.0f }))
|
||||
{
|
||||
this->presetEdit[0] = '\0';
|
||||
igOpenPopup_Str("Save preset as...", ImGuiPopupFlags_None);
|
||||
igOpenPopup("Save preset as...", ImGuiPopupFlags_None);
|
||||
}
|
||||
|
||||
igSameLine(0.0f, -1.0f);
|
||||
@@ -424,7 +401,7 @@ static bool presetsUI(struct EGL_PostProcess * this)
|
||||
}
|
||||
|
||||
if (this->presetError)
|
||||
igOpenPopup_Str("Preset error", ImGuiPopupFlags_None);
|
||||
igOpenPopup("Preset error", ImGuiPopupFlags_None);
|
||||
|
||||
if (igBeginPopupModal("Preset error", NULL, ImGuiWindowFlags_AlwaysAutoResize))
|
||||
{
|
||||
@@ -448,7 +425,7 @@ static bool presetsUI(struct EGL_PostProcess * this)
|
||||
|
||||
static void drawDropTarget(void)
|
||||
{
|
||||
igPushStyleColor_Vec4(ImGuiCol_Separator, (ImVec4) { 1.0f, 1.0f, 0.0f, 1.0f });
|
||||
igPushStyleColorVec4(ImGuiCol_Separator, (ImVec4) { 1.0f, 1.0f, 0.0f, 1.0f });
|
||||
igSeparator();
|
||||
igPopStyleColor(1);
|
||||
}
|
||||
@@ -479,8 +456,8 @@ static void configUI(void * opaque, int * id)
|
||||
if (moving && mouseIdx < moveIdx && i == mouseIdx)
|
||||
drawDropTarget();
|
||||
|
||||
igPushID_Ptr(filter);
|
||||
bool draw = igCollapsingHeader_BoolPtr(filter->ops.name, NULL, 0);
|
||||
igPushIDPtr(filter);
|
||||
bool draw = igCollapsingHeaderBoolPtr(filter->ops.name, NULL, 0);
|
||||
if (igIsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
|
||||
mouseIdx = i;
|
||||
|
||||
@@ -527,7 +504,7 @@ static void configUI(void * opaque, int * id)
|
||||
if (redraw)
|
||||
{
|
||||
atomic_store(&this->modified, true);
|
||||
app_invalidateWindow(true);
|
||||
app_invalidateWindow(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -119,15 +119,10 @@ bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
|
||||
if (logLength > 0)
|
||||
{
|
||||
char *log = malloc(logLength + 1);
|
||||
if (!log)
|
||||
DEBUG_ERROR("out of memory");
|
||||
else
|
||||
{
|
||||
glGetShaderInfoLog(vertexShader, logLength, NULL, log);
|
||||
log[logLength] = 0;
|
||||
DEBUG_ERROR("%s", log);
|
||||
free(log);
|
||||
}
|
||||
glGetShaderInfoLog(vertexShader, logLength, NULL, log);
|
||||
log[logLength] = 0;
|
||||
DEBUG_ERROR("%s", log);
|
||||
free(log);
|
||||
}
|
||||
|
||||
glDeleteShader(vertexShader);
|
||||
@@ -150,15 +145,10 @@ bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
|
||||
if (logLength > 0)
|
||||
{
|
||||
char *log = malloc(logLength + 1);
|
||||
if (!log)
|
||||
DEBUG_ERROR("out of memory");
|
||||
else
|
||||
{
|
||||
glGetShaderInfoLog(fragmentShader, logLength, NULL, log);
|
||||
log[logLength] = 0;
|
||||
DEBUG_ERROR("%s", log);
|
||||
free(log);
|
||||
}
|
||||
glGetShaderInfoLog(fragmentShader, logLength, NULL, log);
|
||||
log[logLength] = 0;
|
||||
DEBUG_ERROR("%s", log);
|
||||
free(log);
|
||||
}
|
||||
|
||||
glDeleteShader(fragmentShader);
|
||||
@@ -211,12 +201,6 @@ void egl_shaderSetUniforms(EGL_Shader * this, EGL_Uniform * uniforms, int count)
|
||||
{
|
||||
free(this->uniforms);
|
||||
this->uniforms = malloc(sizeof(*this->uniforms) * count);
|
||||
if (!this->uniforms)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return;
|
||||
}
|
||||
|
||||
this->uniformCount = count;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
||||
@@ -5,25 +5,11 @@ in vec2 uv;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
uniform float scale;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tmp;
|
||||
if (scale > 1.0)
|
||||
{
|
||||
vec2 ts = vec2(textureSize(sampler1, 0));
|
||||
vec2 px = (uv - (0.5 / ts)) * ts;
|
||||
if (px.x < 0.0 || px.y < 0.0)
|
||||
discard;
|
||||
|
||||
tmp = texelFetch(sampler1, ivec2(px), 0);
|
||||
}
|
||||
else
|
||||
tmp = texture(sampler1, uv);
|
||||
|
||||
vec4 tmp = texture(sampler1, uv);
|
||||
if (tmp.rgb == vec3(0.0, 0.0, 0.0))
|
||||
discard;
|
||||
|
||||
color = tmp;
|
||||
}
|
||||
|
||||
@@ -3,26 +3,16 @@ precision mediump float;
|
||||
|
||||
#include "color_blind.h"
|
||||
|
||||
in vec2 uv;
|
||||
out vec4 color;
|
||||
in vec2 uv;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
uniform float scale;
|
||||
uniform int cbMode;
|
||||
|
||||
uniform int cbMode;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (scale > 1.0)
|
||||
{
|
||||
vec2 ts = vec2(textureSize(sampler1, 0));
|
||||
vec2 px = (uv - (0.5 / ts)) * ts;
|
||||
if (px.x < 0.0 || px.y < 0.0)
|
||||
discard;
|
||||
|
||||
color = texelFetch(sampler1, ivec2(px), 0);
|
||||
}
|
||||
else
|
||||
color = texture(sampler1, uv);
|
||||
color = texture(sampler1, uv);
|
||||
|
||||
if (cbMode > 0)
|
||||
color = cbTransform(color, cbMode);
|
||||
|
||||
@@ -37,9 +37,8 @@ void main()
|
||||
|
||||
if (nvGain > 0.0)
|
||||
{
|
||||
highp float lumi = (0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b);
|
||||
if (lumi < 0.5)
|
||||
color *= atanh((1.0 - lumi) * 2.0 - 1.0) + 1.0;
|
||||
highp float lumi = 1.0 - (0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b);
|
||||
color *= 1.0 + lumi;
|
||||
color *= nvGain;
|
||||
}
|
||||
|
||||
|
||||
@@ -747,12 +747,12 @@ AF1 sharpness){
|
||||
// Immediate constants for peak range.
|
||||
AF2 peakC=AF2(1.0,-1.0*4.0);
|
||||
// Limiters, these need to be high precision RCPs.
|
||||
AF1 hitMinR=min(mn4R,eR)*ARcpF1(AF1_(4.0)*mx4R);
|
||||
AF1 hitMinG=min(mn4G,eG)*ARcpF1(AF1_(4.0)*mx4G);
|
||||
AF1 hitMinB=min(mn4B,eB)*ARcpF1(AF1_(4.0)*mx4B);
|
||||
AF1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpF1(AF1_(4.0)*mn4R+peakC.y);
|
||||
AF1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpF1(AF1_(4.0)*mn4G+peakC.y);
|
||||
AF1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpF1(AF1_(4.0)*mn4B+peakC.y);
|
||||
AF1 hitMinR=mn4R*ARcpF1(AF1_(4.0)*mx4R);
|
||||
AF1 hitMinG=mn4G*ARcpF1(AF1_(4.0)*mx4G);
|
||||
AF1 hitMinB=mn4B*ARcpF1(AF1_(4.0)*mx4B);
|
||||
AF1 hitMaxR=(peakC.x-mx4R)*ARcpF1(AF1_(4.0)*mn4R+peakC.y);
|
||||
AF1 hitMaxG=(peakC.x-mx4G)*ARcpF1(AF1_(4.0)*mn4G+peakC.y);
|
||||
AF1 hitMaxB=(peakC.x-mx4B)*ARcpF1(AF1_(4.0)*mn4B+peakC.y);
|
||||
AF1 lobeR=max(-hitMinR,hitMaxR);
|
||||
AF1 lobeG=max(-hitMinG,hitMaxG);
|
||||
AF1 lobeB=max(-hitMinB,hitMaxB);
|
||||
@@ -845,12 +845,12 @@ AF1 sharpness){
|
||||
// Immediate constants for peak range.
|
||||
AH2 peakC=AH2(1.0,-1.0*4.0);
|
||||
// Limiters, these need to be high precision RCPs.
|
||||
AH1 hitMinR=min(mn4R,eR)*ARcpH1(AH1_(4.0)*mx4R);
|
||||
AH1 hitMinG=min(mn4G,eG)*ARcpH1(AH1_(4.0)*mx4G);
|
||||
AH1 hitMinB=min(mn4B,eB)*ARcpH1(AH1_(4.0)*mx4B);
|
||||
AH1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH1(AH1_(4.0)*mn4R+peakC.y);
|
||||
AH1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH1(AH1_(4.0)*mn4G+peakC.y);
|
||||
AH1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH1(AH1_(4.0)*mn4B+peakC.y);
|
||||
AH1 hitMinR=mn4R*ARcpH1(AH1_(4.0)*mx4R);
|
||||
AH1 hitMinG=mn4G*ARcpH1(AH1_(4.0)*mx4G);
|
||||
AH1 hitMinB=mn4B*ARcpH1(AH1_(4.0)*mx4B);
|
||||
AH1 hitMaxR=(peakC.x-mx4R)*ARcpH1(AH1_(4.0)*mn4R+peakC.y);
|
||||
AH1 hitMaxG=(peakC.x-mx4G)*ARcpH1(AH1_(4.0)*mn4G+peakC.y);
|
||||
AH1 hitMaxB=(peakC.x-mx4B)*ARcpH1(AH1_(4.0)*mn4B+peakC.y);
|
||||
AH1 lobeR=max(-hitMinR,hitMaxR);
|
||||
AH1 lobeG=max(-hitMinG,hitMaxG);
|
||||
AH1 lobeB=max(-hitMinB,hitMaxB);
|
||||
@@ -963,12 +963,12 @@ AF1 sharpness){
|
||||
// Immediate constants for peak range.
|
||||
AH2 peakC=AH2(1.0,-1.0*4.0);
|
||||
// Limiters, these need to be high precision RCPs.
|
||||
AH2 hitMinR=min(mn4R,eR)*ARcpH2(AH2_(4.0)*mx4R);
|
||||
AH2 hitMinG=min(mn4G,eG)*ARcpH2(AH2_(4.0)*mx4G);
|
||||
AH2 hitMinB=min(mn4B,eB)*ARcpH2(AH2_(4.0)*mx4B);
|
||||
AH2 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH2(AH2_(4.0)*mn4R+peakC.y);
|
||||
AH2 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH2(AH2_(4.0)*mn4G+peakC.y);
|
||||
AH2 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH2(AH2_(4.0)*mn4B+peakC.y);
|
||||
AH2 hitMinR=mn4R*ARcpH2(AH2_(4.0)*mx4R);
|
||||
AH2 hitMinG=mn4G*ARcpH2(AH2_(4.0)*mx4G);
|
||||
AH2 hitMinB=mn4B*ARcpH2(AH2_(4.0)*mx4B);
|
||||
AH2 hitMaxR=(peakC.x-mx4R)*ARcpH2(AH2_(4.0)*mn4R+peakC.y);
|
||||
AH2 hitMaxG=(peakC.x-mx4G)*ARcpH2(AH2_(4.0)*mn4G+peakC.y);
|
||||
AH2 hitMaxB=(peakC.x-mx4B)*ARcpH2(AH2_(4.0)*mn4B+peakC.y);
|
||||
AH2 lobeR=max(-hitMinR,hitMaxR);
|
||||
AH2 lobeG=max(-hitMinG,hitMaxG);
|
||||
AH2 lobeB=max(-hitMinB,hitMaxB);
|
||||
|
||||
13
client/renderers/EGL/shader/splash_bg.frag
Normal file
13
client/renderers/EGL/shader/splash_bg.frag
Normal file
@@ -0,0 +1,13 @@
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
in vec3 pos;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp float d = 1.0 - 0.5 * length(pos.xy);
|
||||
color = vec4(0.234375 * d, 0.015625 * d, 0.425781 * d, 1.0);
|
||||
}
|
||||
14
client/renderers/EGL/shader/splash_bg.vert
Normal file
14
client/renderers/EGL/shader/splash_bg.vert
Normal file
@@ -0,0 +1,14 @@
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;
|
||||
|
||||
out vec3 pos;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position.xyz = vertexPosition_modelspace;
|
||||
gl_Position.w = 1.0;
|
||||
|
||||
pos = vertexPosition_modelspace;
|
||||
}
|
||||
11
client/renderers/EGL/shader/splash_logo.frag
Normal file
11
client/renderers/EGL/shader/splash_logo.frag
Normal file
@@ -0,0 +1,11 @@
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
12
client/renderers/EGL/shader/splash_logo.vert
Normal file
12
client/renderers/EGL/shader/splash_logo.vert
Normal file
@@ -0,0 +1,12 @@
|
||||
#version 300 es
|
||||
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;
|
||||
|
||||
uniform float scale;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position.xyz = vertexPosition_modelspace;
|
||||
gl_Position.y *= scale;
|
||||
gl_Position.w = 1.0;
|
||||
}
|
||||
178
client/renderers/EGL/splash.c
Normal file
178
client/renderers/EGL/splash.c
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "splash.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "draw.h"
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
#include "model.h"
|
||||
|
||||
#include <GLES3/gl3.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
// these headers are auto generated by cmake
|
||||
#include "splash_bg.vert.h"
|
||||
#include "splash_bg.frag.h"
|
||||
#include "splash_logo.vert.h"
|
||||
#include "splash_logo.frag.h"
|
||||
|
||||
struct EGL_Splash
|
||||
{
|
||||
EGL_Shader * bgShader;
|
||||
EGL_Model * bg;
|
||||
|
||||
EGL_Shader * logoShader;
|
||||
EGL_Model * logo;
|
||||
|
||||
// uniforms
|
||||
GLint uScale;
|
||||
};
|
||||
|
||||
bool egl_splashInit(EGL_Splash ** splash)
|
||||
{
|
||||
*splash = malloc(sizeof(**splash));
|
||||
if (!*splash)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_Splash");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(*splash, 0, sizeof(**splash));
|
||||
|
||||
if (!egl_shaderInit(&(*splash)->bgShader))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the splash bgShader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shaderCompile((*splash)->bgShader,
|
||||
b_shader_splash_bg_vert, b_shader_splash_bg_vert_size,
|
||||
b_shader_splash_bg_frag, b_shader_splash_bg_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the splash bgShader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_modelInit(&(*splash)->bg))
|
||||
{
|
||||
DEBUG_ERROR("Failed to intiailize the splash bg model");
|
||||
return false;
|
||||
}
|
||||
|
||||
egl_modelSetDefault((*splash)->bg, false);
|
||||
|
||||
if (!egl_shaderInit(&(*splash)->logoShader))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the splash logoShader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shaderCompile((*splash)->logoShader,
|
||||
b_shader_splash_logo_vert, b_shader_splash_logo_vert_size,
|
||||
b_shader_splash_logo_frag, b_shader_splash_logo_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the splash logoShader");
|
||||
return false;
|
||||
}
|
||||
|
||||
(*splash)->uScale = egl_shaderGetUniform((*splash)->logoShader, "scale");
|
||||
|
||||
if (!egl_modelInit(&(*splash)->logo))
|
||||
{
|
||||
DEBUG_ERROR("Failed to intiailize the splash model");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* build the splash model */
|
||||
#define P(x) ((1.0f/800.0f)*(float)(x))
|
||||
egl_drawTorusArc((*splash)->logo, 30, P( 0 ), P(0), P(102), P(98), 0.0f, -M_PI);
|
||||
egl_drawTorus ((*splash)->logo, 30, P(-100), P(8), P(8 ), P(4 ));
|
||||
egl_drawTorus ((*splash)->logo, 30, P( 100), P(8), P(8 ), P(4 ));
|
||||
|
||||
egl_drawTorus ((*splash)->logo, 60, P(0), P(0), P(83), P(79));
|
||||
egl_drawTorus ((*splash)->logo, 60, P(0), P(0), P(67), P(63));
|
||||
|
||||
static const GLfloat lines[][12] =
|
||||
{
|
||||
{
|
||||
P( -2), P(-140), 0.0f,
|
||||
P( -2), P(-100), 0.0f,
|
||||
P( 2), P(-140), 0.0f,
|
||||
P( 2), P(-100), 0.0f
|
||||
},
|
||||
{
|
||||
P(-26), P(-144), 0.0f,
|
||||
P(-26), P(-140), 0.0f,
|
||||
P( 26), P(-144), 0.0f,
|
||||
P( 26), P(-140), 0.0f
|
||||
},
|
||||
{
|
||||
P(-40), P(-156), 0.0f,
|
||||
P(-40), P(-152), 0.0f,
|
||||
P( 40), P(-156), 0.0f,
|
||||
P( 40), P(-152), 0.0f
|
||||
}
|
||||
};
|
||||
|
||||
egl_modelAddVerts((*splash)->logo, lines[0], NULL, 4);
|
||||
egl_modelAddVerts((*splash)->logo, lines[1], NULL, 4);
|
||||
egl_modelAddVerts((*splash)->logo, lines[2], NULL, 4);
|
||||
|
||||
egl_drawTorusArc((*splash)->logo, 10, P(-26), P(-154), P(10), P(14), M_PI , -M_PI / 2.0);
|
||||
egl_drawTorusArc((*splash)->logo, 10, P( 26), P(-154), P(10), P(14), M_PI / 2.0f, -M_PI / 2.0);
|
||||
#undef P
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_splashFree(EGL_Splash ** splash)
|
||||
{
|
||||
if (!*splash)
|
||||
return;
|
||||
|
||||
egl_modelFree(&(*splash)->bg );
|
||||
egl_modelFree(&(*splash)->logo);
|
||||
|
||||
egl_shaderFree(&(*splash)->bgShader );
|
||||
egl_shaderFree(&(*splash)->logoShader);
|
||||
|
||||
free(*splash);
|
||||
*splash = NULL;
|
||||
}
|
||||
|
||||
void egl_splashRender(EGL_Splash * splash, float alpha, float scaleY)
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
glBlendColor(0, 0, 0, alpha);
|
||||
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
|
||||
|
||||
egl_shaderUse(splash->bgShader);
|
||||
egl_modelRender(splash->bg);
|
||||
|
||||
egl_shaderUse(splash->logoShader);
|
||||
glUniform1f(splash->uScale, scaleY);
|
||||
egl_modelRender(splash->logo);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
@@ -18,6 +18,13 @@
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#pragma once
|
||||
|
||||
void captureOutputDebugString(void);
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct EGL_Splash EGL_Splash;
|
||||
|
||||
bool egl_splashInit(EGL_Splash ** splash);
|
||||
void egl_splashFree(EGL_Splash ** splash);
|
||||
|
||||
void egl_splashRender(EGL_Splash * splash, float alpha, float scaleY);
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -37,26 +37,23 @@ extern const EGL_TextureOps EGL_TextureFrameBuffer;
|
||||
extern const EGL_TextureOps EGL_TextureDMABUF;
|
||||
|
||||
bool egl_textureInit(EGL_Texture ** texture_, EGLDisplay * display,
|
||||
EGL_TexType type)
|
||||
EGL_TexType type, bool streaming)
|
||||
{
|
||||
const EGL_TextureOps * ops;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case EGL_TEXTYPE_BUFFER:
|
||||
ops = &EGL_TextureBuffer;
|
||||
break;
|
||||
|
||||
case EGL_TEXTYPE_BUFFER_MAP:
|
||||
case EGL_TEXTYPE_BUFFER_STREAM:
|
||||
ops = &EGL_TextureBufferStream;
|
||||
ops = streaming ? &EGL_TextureBufferStream : &EGL_TextureBuffer;
|
||||
break;
|
||||
|
||||
case EGL_TEXTYPE_FRAMEBUFFER:
|
||||
DEBUG_ASSERT(streaming);
|
||||
ops = &EGL_TextureFrameBuffer;
|
||||
break;
|
||||
|
||||
case EGL_TEXTYPE_DMABUF:
|
||||
DEBUG_ASSERT(streaming);
|
||||
ops = &EGL_TextureDMABUF;
|
||||
break;
|
||||
|
||||
@@ -65,7 +62,7 @@ bool egl_textureInit(EGL_Texture ** texture_, EGLDisplay * display,
|
||||
}
|
||||
|
||||
*texture_ = NULL;
|
||||
if (!ops->init(texture_, type, display))
|
||||
if (!ops->init(texture_, display))
|
||||
return false;
|
||||
|
||||
EGL_Texture * this = *texture_;
|
||||
@@ -82,8 +79,6 @@ bool egl_textureInit(EGL_Texture ** texture_, EGLDisplay * display,
|
||||
void egl_textureFree(EGL_Texture ** tex)
|
||||
{
|
||||
EGL_Texture * this = *tex;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
glDeleteSamplers(1, &this->sampler);
|
||||
|
||||
@@ -108,47 +103,12 @@ bool egl_textureSetup(EGL_Texture * this, enum EGL_PixelFormat pixFmt,
|
||||
return this->ops.setup(this, &setup);
|
||||
}
|
||||
|
||||
bool egl_textureUpdate(EGL_Texture * this, const uint8_t * buffer, bool topDown)
|
||||
bool egl_textureUpdate(EGL_Texture * this, const uint8_t * buffer)
|
||||
{
|
||||
const struct EGL_TexUpdate update =
|
||||
{
|
||||
.type = EGL_TEXTYPE_BUFFER,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = this->format.width,
|
||||
.height = this->format.height,
|
||||
.pitch = this->format.pitch,
|
||||
.stride = this->format.stride,
|
||||
.topDown = topDown,
|
||||
.buffer = buffer
|
||||
};
|
||||
|
||||
return this->ops.update(this, &update);
|
||||
}
|
||||
|
||||
bool egl_textureUpdateRect(EGL_Texture * this,
|
||||
int x, int y, int width, int height, int stride,
|
||||
const uint8_t * buffer, bool topDown)
|
||||
{
|
||||
x = clamp(x , 0, this->format.width );
|
||||
y = clamp(y , 0, this->format.height );
|
||||
width = clamp(width , 0, this->format.width - x);
|
||||
height = clamp(height, 0, this->format.height - y);
|
||||
|
||||
if (!width || !height)
|
||||
return true;
|
||||
|
||||
const struct EGL_TexUpdate update =
|
||||
{
|
||||
.type = EGL_TEXTYPE_BUFFER,
|
||||
.x = x,
|
||||
.y = y,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.pitch = stride / this->format.bpp,
|
||||
.stride = stride,
|
||||
.topDown = topDown,
|
||||
.buffer = buffer
|
||||
.type = EGL_TEXTYPE_BUFFER,
|
||||
.buffer = buffer
|
||||
};
|
||||
|
||||
return this->ops.update(this, &update);
|
||||
@@ -161,12 +121,6 @@ bool egl_textureUpdateFromFrame(EGL_Texture * this,
|
||||
const struct EGL_TexUpdate update =
|
||||
{
|
||||
.type = EGL_TEXTYPE_FRAMEBUFFER,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = this->format.width,
|
||||
.height = this->format.height,
|
||||
.pitch = this->format.pitch,
|
||||
.stride = this->format.stride,
|
||||
.frame = frame,
|
||||
.rects = damageRects,
|
||||
.rectCount = damageRectsCount,
|
||||
@@ -180,14 +134,8 @@ bool egl_textureUpdateFromDMA(EGL_Texture * this,
|
||||
{
|
||||
const struct EGL_TexUpdate update =
|
||||
{
|
||||
.type = EGL_TEXTYPE_DMABUF,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = this->format.width,
|
||||
.height = this->format.height,
|
||||
.pitch = this->format.pitch,
|
||||
.stride = this->format.stride,
|
||||
.dmaFD = dmaFd
|
||||
.type = EGL_TEXTYPE_DMABUF,
|
||||
.dmaFD = dmaFd
|
||||
};
|
||||
|
||||
/* wait for completion */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "common/types.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "ll.h"
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
@@ -42,23 +43,12 @@ typedef struct EGL_TexUpdate
|
||||
/* the type of this update */
|
||||
EGL_TexType type;
|
||||
|
||||
int x, y, width, height;
|
||||
|
||||
//pitch = row length in pixels
|
||||
//stride = row length in bytes
|
||||
int pitch, stride;
|
||||
|
||||
union
|
||||
{
|
||||
/* EGL_TEXTYPE_BUFFER */
|
||||
struct
|
||||
{
|
||||
// true if row 0 is the top of the image
|
||||
bool topDown;
|
||||
const uint8_t * buffer;
|
||||
};
|
||||
/* EGL_TEXTURE_BUFFER */
|
||||
const uint8_t * buffer;
|
||||
|
||||
/* EGL_TEXTYPE_FRAMEBUFFER */
|
||||
/* EGL_TEXTURE_FRAMEBUFFER */
|
||||
struct
|
||||
{
|
||||
const FrameBuffer * frame;
|
||||
@@ -66,7 +56,7 @@ typedef struct EGL_TexUpdate
|
||||
int rectCount;
|
||||
};
|
||||
|
||||
/* EGL_TEXTYPE_DMABUF */
|
||||
/* EGL_TEXTURE_DMABUF */
|
||||
int dmaFD;
|
||||
};
|
||||
}
|
||||
@@ -77,7 +67,7 @@ typedef struct EGL_Texture EGL_Texture;
|
||||
typedef struct EGL_TextureOps
|
||||
{
|
||||
/* allocate & initialize an EGL_Texture */
|
||||
bool (*init)(EGL_Texture ** texture, EGL_TexType type, EGLDisplay * display);
|
||||
bool (*init)(EGL_Texture ** texture, EGLDisplay * display);
|
||||
|
||||
/* free the EGL_Texture */
|
||||
void (*free)(EGL_Texture * texture);
|
||||
@@ -99,25 +89,19 @@ EGL_TextureOps;
|
||||
struct EGL_Texture
|
||||
{
|
||||
struct EGL_TextureOps ops;
|
||||
EGL_TexType type;
|
||||
GLuint sampler;
|
||||
|
||||
EGL_TexFormat format;
|
||||
};
|
||||
|
||||
bool egl_textureInit(EGL_Texture ** texture, EGLDisplay * display,
|
||||
EGL_TexType type);
|
||||
EGL_TexType type, bool streaming);
|
||||
void egl_textureFree(EGL_Texture ** tex);
|
||||
|
||||
bool egl_textureSetup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt,
|
||||
size_t width, size_t height, size_t stride);
|
||||
|
||||
bool egl_textureUpdate(EGL_Texture * texture, const uint8_t * buffer,
|
||||
bool topDown);
|
||||
|
||||
bool egl_textureUpdateRect(EGL_Texture * texture,
|
||||
int x, int y, int width, int height, int stride,
|
||||
const uint8_t * buffer, bool topDown);
|
||||
bool egl_textureUpdate(EGL_Texture * texture, const uint8_t * buffer);
|
||||
|
||||
bool egl_textureUpdateFromFrame(EGL_Texture * texture,
|
||||
const FrameBuffer * frame, const FrameDamageRect * damageRects,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -46,8 +46,7 @@ static void egl_texBuffer_cleanup(TextureBuffer * this)
|
||||
|
||||
// common functions
|
||||
|
||||
bool egl_texBufferInit(EGL_Texture ** texture, EGL_TexType type,
|
||||
EGLDisplay * display)
|
||||
bool egl_texBufferInit(EGL_Texture ** texture, EGLDisplay * display)
|
||||
{
|
||||
TextureBuffer * this;
|
||||
if (!*texture)
|
||||
@@ -112,13 +111,11 @@ static bool egl_texBufferUpdate(EGL_Texture * texture, const EGL_TexUpdate * upd
|
||||
DEBUG_ASSERT(update->type == EGL_TEXTYPE_BUFFER);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, this->tex[0]);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, update->pitch);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->format.pitch);
|
||||
glTexSubImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
update->x,
|
||||
update->y,
|
||||
update->width,
|
||||
update->height,
|
||||
0, 0, 0,
|
||||
texture->format.width,
|
||||
texture->format.height,
|
||||
texture->format.format,
|
||||
texture->format.dataType,
|
||||
update->buffer);
|
||||
@@ -141,30 +138,14 @@ EGL_TexStatus egl_texBufferGet(EGL_Texture * texture, GLuint * tex)
|
||||
|
||||
// streaming functions
|
||||
|
||||
bool egl_texBufferStreamInit(EGL_Texture ** texture, EGL_TexType type,
|
||||
EGLDisplay * display)
|
||||
bool egl_texBufferStreamInit(EGL_Texture ** texture, EGLDisplay * display)
|
||||
{
|
||||
if (!egl_texBufferInit(texture, type, display))
|
||||
if (!egl_texBufferInit(texture, display))
|
||||
return false;
|
||||
|
||||
TextureBuffer * this = UPCAST(TextureBuffer, *texture);
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case EGL_TEXTYPE_BUFFER_STREAM:
|
||||
case EGL_TEXTYPE_FRAMEBUFFER:
|
||||
case EGL_TEXTYPE_DMABUF:
|
||||
this->texCount = 2;
|
||||
break;
|
||||
|
||||
case EGL_TEXTYPE_BUFFER_MAP:
|
||||
this->texCount = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_UNREACHABLE();
|
||||
}
|
||||
|
||||
this->texCount = 2;
|
||||
LG_LOCK_INIT(this->copyLock);
|
||||
return true;
|
||||
}
|
||||
@@ -185,32 +166,8 @@ static bool egl_texBufferStreamUpdate(EGL_Texture * texture,
|
||||
DEBUG_ASSERT(update->type == EGL_TEXTYPE_BUFFER);
|
||||
|
||||
LG_LOCK(this->copyLock);
|
||||
|
||||
uint8_t * dst = this->buf[this->bufIndex].map +
|
||||
texture->format.stride * update->y +
|
||||
update->x * texture->format.bpp;
|
||||
|
||||
if (update->topDown)
|
||||
{
|
||||
const uint8_t * src = update->buffer;
|
||||
for(int y = 0; y < update->height; ++y)
|
||||
{
|
||||
memcpy(dst, src, update->stride);
|
||||
dst += texture->format.stride;
|
||||
src += update->stride;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint8_t * src = update->buffer + update->stride * update->height;
|
||||
for(int y = 0; y < update->height; ++y)
|
||||
{
|
||||
src -= update->stride;
|
||||
memcpy(dst, src, update->stride);
|
||||
dst += texture->format.stride;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(this->buf[this->bufIndex].map, update->buffer,
|
||||
texture->format.bufferSize);
|
||||
this->buf[this->bufIndex].updated = true;
|
||||
LG_UNLOCK(this->copyLock);
|
||||
|
||||
@@ -268,7 +225,7 @@ EGL_TexStatus egl_texBufferStreamGet(EGL_Texture * texture, GLuint * tex)
|
||||
|
||||
if (this->sync)
|
||||
{
|
||||
switch(glClientWaitSync(this->sync, 0, 40000000)) // 40ms
|
||||
switch(glClientWaitSync(this->sync, 0, 20000000)) // 20ms
|
||||
{
|
||||
case GL_ALREADY_SIGNALED:
|
||||
case GL_CONDITION_SATISFIED:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -33,6 +33,7 @@ typedef struct TextureBuffer
|
||||
|
||||
int texCount;
|
||||
GLuint tex[EGL_TEX_BUFFER_MAX];
|
||||
GLuint sampler;
|
||||
EGL_TexBuffer buf[EGL_TEX_BUFFER_MAX];
|
||||
int bufFree;
|
||||
GLsync sync;
|
||||
@@ -42,15 +43,13 @@ typedef struct TextureBuffer
|
||||
}
|
||||
TextureBuffer;
|
||||
|
||||
bool egl_texBufferInit(EGL_Texture ** texture_, EGL_TexType type,
|
||||
EGLDisplay * display);
|
||||
bool egl_texBufferInit(EGL_Texture ** texture_, EGLDisplay * display);
|
||||
void egl_texBufferFree(EGL_Texture * texture_);
|
||||
bool egl_texBufferSetup(EGL_Texture * texture_, const EGL_TexSetup * setup);
|
||||
EGL_TexStatus egl_texBufferProcess(EGL_Texture * texture_);
|
||||
EGL_TexStatus egl_texBufferGet(EGL_Texture * texture_, GLuint * tex);
|
||||
|
||||
bool egl_texBufferStreamInit(EGL_Texture ** texture_, EGL_TexType type,
|
||||
EGLDisplay * display);
|
||||
bool egl_texBufferStreamInit(EGL_Texture ** texture_, EGLDisplay * display);
|
||||
bool egl_texBufferStreamSetup(EGL_Texture * texture_,
|
||||
const EGL_TexSetup * setup);
|
||||
EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture_);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "texture.h"
|
||||
#include "texture_buffer.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "common/vector.h"
|
||||
#include "egl_dynprocs.h"
|
||||
@@ -37,7 +36,6 @@ typedef struct TexDMABUF
|
||||
TextureBuffer base;
|
||||
|
||||
EGLDisplay display;
|
||||
bool hasImportModifiers;
|
||||
Vector images;
|
||||
}
|
||||
TexDMABUF;
|
||||
@@ -56,8 +54,7 @@ static void egl_texDMABUFCleanup(TexDMABUF * this)
|
||||
|
||||
// dmabuf functions
|
||||
|
||||
static bool egl_texDMABUFInit(EGL_Texture ** texture, EGL_TexType type,
|
||||
EGLDisplay * display)
|
||||
static bool egl_texDMABUFInit(EGL_Texture ** texture, EGLDisplay * display)
|
||||
{
|
||||
TexDMABUF * this = calloc(1, sizeof(*this));
|
||||
*texture = &this->base.base;
|
||||
@@ -70,7 +67,7 @@ static bool egl_texDMABUFInit(EGL_Texture ** texture, EGL_TexType type,
|
||||
}
|
||||
|
||||
EGL_Texture * parent = &this->base.base;
|
||||
if (!egl_texBufferStreamInit(&parent, type, display))
|
||||
if (!egl_texBufferStreamInit(&parent, display))
|
||||
{
|
||||
vector_destroy(&this->images);
|
||||
free(this);
|
||||
@@ -79,11 +76,6 @@ static bool egl_texDMABUFInit(EGL_Texture ** texture, EGL_TexType type,
|
||||
}
|
||||
|
||||
this->display = display;
|
||||
|
||||
const char * client_exts = eglQueryString(this->display, EGL_EXTENSIONS);
|
||||
this->hasImportModifiers =
|
||||
util_hasGLExt(client_exts, "EGL_EXT_image_dma_buf_import_modifiers");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -129,24 +121,17 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture,
|
||||
|
||||
if (image == EGL_NO_IMAGE)
|
||||
{
|
||||
const uint64_t modifier = DRM_FORMAT_MOD_LINEAR;
|
||||
EGLAttrib attribs[] =
|
||||
EGLAttrib const attribs[] =
|
||||
{
|
||||
EGL_WIDTH , texture->format.width,
|
||||
EGL_HEIGHT , texture->format.height,
|
||||
EGL_LINUX_DRM_FOURCC_EXT , texture->format.fourcc,
|
||||
EGL_DMA_BUF_PLANE0_FD_EXT , update->dmaFD,
|
||||
EGL_DMA_BUF_PLANE0_OFFSET_EXT , 0,
|
||||
EGL_DMA_BUF_PLANE0_PITCH_EXT , texture->format.stride,
|
||||
EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, (modifier & 0xffffffff),
|
||||
EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, (modifier >> 32),
|
||||
EGL_NONE , EGL_NONE
|
||||
EGL_WIDTH , texture->format.width,
|
||||
EGL_HEIGHT , texture->format.height,
|
||||
EGL_LINUX_DRM_FOURCC_EXT , texture->format.fourcc,
|
||||
EGL_DMA_BUF_PLANE0_FD_EXT , update->dmaFD,
|
||||
EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
|
||||
EGL_DMA_BUF_PLANE0_PITCH_EXT , texture->format.stride,
|
||||
EGL_NONE , EGL_NONE
|
||||
};
|
||||
|
||||
if (!this->hasImportModifiers)
|
||||
attribs[12] = attribs[13] =
|
||||
attribs[14] = attribs[15] = EGL_NONE;
|
||||
|
||||
image = g_egl_dynProcs.eglCreateImage(
|
||||
this->display,
|
||||
EGL_NO_CONTEXT,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -38,14 +38,13 @@ typedef struct TexFB
|
||||
}
|
||||
TexFB;
|
||||
|
||||
static bool egl_texFBInit(EGL_Texture ** texture, EGL_TexType type,
|
||||
EGLDisplay * display)
|
||||
static bool egl_texFBInit(EGL_Texture ** texture, EGLDisplay * display)
|
||||
{
|
||||
TexFB * this = calloc(1, sizeof(*this));
|
||||
*texture = &this->base.base;
|
||||
|
||||
EGL_Texture * parent = &this->base.base;
|
||||
if (!egl_texBufferStreamInit(&parent, type, display))
|
||||
if (!egl_texBufferStreamInit(&parent, display))
|
||||
{
|
||||
free(this);
|
||||
*texture = NULL;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -28,6 +28,17 @@
|
||||
#include "egldebug.h"
|
||||
#include "egl_dynprocs.h"
|
||||
|
||||
/**
|
||||
* the following comes from drm_fourcc.h and is included here to avoid the
|
||||
* external dependency for the few simple defines we need
|
||||
*/
|
||||
#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
|
||||
((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
|
||||
#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4')
|
||||
#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4')
|
||||
#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0')
|
||||
#define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H')
|
||||
|
||||
bool egl_texUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt)
|
||||
{
|
||||
switch(setup->pixFmt)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -35,7 +35,7 @@ typedef struct EGL_TexFormat
|
||||
unsigned int fourcc;
|
||||
size_t bufferSize;
|
||||
|
||||
size_t width , height;
|
||||
size_t width, height;
|
||||
size_t stride, pitch;
|
||||
}
|
||||
EGL_TexFormat;
|
||||
@@ -55,20 +55,3 @@ bool egl_texUtilGenBuffers(const EGL_TexFormat * fmt, EGL_TexBuffer * buffers,
|
||||
void egl_texUtilFreeBuffers(EGL_TexBuffer * buffers, int count);
|
||||
bool egl_texUtilMapBuffer(EGL_TexBuffer * buffer);
|
||||
void egl_texUtilUnmapBuffer(EGL_TexBuffer * buffer);
|
||||
|
||||
/**
|
||||
* the following comes from drm_fourcc.h and is included here to avoid the
|
||||
* external dependency for the few simple defines we need
|
||||
*/
|
||||
#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
|
||||
((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
|
||||
#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4')
|
||||
#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4')
|
||||
#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0')
|
||||
#define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H')
|
||||
|
||||
#define DRM_FORMAT_MOD_VENDOR_NONE 0
|
||||
#define fourcc_mod_code(vendor, val) \
|
||||
((((uint64_t)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | \
|
||||
((val) & 0x00ffffffffffffffULL))
|
||||
#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0)
|
||||
|
||||
@@ -3,24 +3,24 @@ project(renderer_Opengl LANGUAGES C CXX)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(RENDERER_OPENGL REQUIRED IMPORTED_TARGET
|
||||
gl
|
||||
gl
|
||||
)
|
||||
|
||||
add_library(renderer_OpenGL STATIC
|
||||
opengl.c
|
||||
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl2.cpp
|
||||
opengl.c
|
||||
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl2.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(renderer_OpenGL PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1)
|
||||
|
||||
target_link_libraries(renderer_OpenGL
|
||||
PkgConfig::RENDERER_OPENGL
|
||||
lg_common
|
||||
PkgConfig::RENDERER_OPENGL
|
||||
lg_common
|
||||
|
||||
cimgui
|
||||
cimgui
|
||||
)
|
||||
|
||||
target_include_directories(renderer_OpenGL
|
||||
PRIVATE
|
||||
src
|
||||
PRIVATE
|
||||
src
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2022 The Looking Glass Authors
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -36,14 +36,16 @@
|
||||
#include "common/framebuffer.h"
|
||||
#include "common/locking.h"
|
||||
#include "gl_dynprocs.h"
|
||||
#include "ll.h"
|
||||
#include "util.h"
|
||||
|
||||
#define BUFFER_COUNT 2
|
||||
|
||||
#define FPS_TEXTURE 0
|
||||
#define MOUSE_TEXTURE 1
|
||||
#define SPICE_TEXTURE 2
|
||||
#define TEXTURE_COUNT 3
|
||||
#define TEXTURE_COUNT 2
|
||||
|
||||
#define FADE_TIME 1000000
|
||||
|
||||
static struct Option opengl_options[] =
|
||||
{
|
||||
@@ -135,16 +137,17 @@ struct Inst
|
||||
int texWIndex, texRIndex;
|
||||
int texList;
|
||||
int mouseList;
|
||||
int spiceList;
|
||||
LG_RendererRect destRect;
|
||||
struct IntPoint spiceSize;
|
||||
bool spiceShow;
|
||||
|
||||
bool hasTextures, hasFrames;
|
||||
GLuint frames[BUFFER_COUNT];
|
||||
GLsync fences[BUFFER_COUNT];
|
||||
GLuint textures[TEXTURE_COUNT];
|
||||
|
||||
bool waiting;
|
||||
uint64_t waitFadeTime;
|
||||
bool waitDone;
|
||||
|
||||
LG_Lock mouseLock;
|
||||
LG_RendererCursor mouseCursor;
|
||||
int mouseWidth;
|
||||
@@ -172,9 +175,10 @@ enum ConfigStatus
|
||||
|
||||
static void deconfigure(struct Inst * this);
|
||||
static enum ConfigStatus configure(struct Inst * this);
|
||||
static void updateMouseShape(struct Inst * this);
|
||||
static void updateMouseShape(struct Inst * this, bool * newShape);
|
||||
static bool drawFrame(struct Inst * this);
|
||||
static void drawMouse(struct Inst * this);
|
||||
static void renderWait(struct Inst * this);
|
||||
|
||||
const char * opengl_getName(void)
|
||||
{
|
||||
@@ -204,6 +208,7 @@ bool opengl_create(LG_Renderer ** renderer, const LG_RendererParams params,
|
||||
this->opt.preventBuffer = option_get_bool("opengl", "preventBuffer");
|
||||
this->opt.amdPinnedMem = option_get_bool("opengl", "amdPinnedMem" );
|
||||
|
||||
|
||||
LG_LOCK_INIT(this->formatLock);
|
||||
LG_LOCK_INIT(this->frameLock );
|
||||
LG_LOCK_INIT(this->mouseLock );
|
||||
@@ -214,7 +219,10 @@ bool opengl_create(LG_Renderer ** renderer, const LG_RendererParams params,
|
||||
|
||||
bool opengl_initialize(LG_Renderer * renderer)
|
||||
{
|
||||
// struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
|
||||
this->waiting = true;
|
||||
this->waitDone = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -228,7 +236,6 @@ void opengl_deinitialize(LG_Renderer * renderer)
|
||||
|
||||
glDeleteLists(this->texList , BUFFER_COUNT);
|
||||
glDeleteLists(this->mouseList, 1);
|
||||
glDeleteLists(this->spiceList, 1);
|
||||
}
|
||||
|
||||
deconfigure(this);
|
||||
@@ -257,34 +264,9 @@ void opengl_deinitialize(LG_Renderer * renderer)
|
||||
|
||||
void opengl_onRestart(LG_Renderer * renderer)
|
||||
{
|
||||
// struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
}
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
|
||||
static void setupModelView(struct Inst * this)
|
||||
{
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
if (!this->destRect.valid)
|
||||
return;
|
||||
|
||||
int fw, fh;
|
||||
if (this->spiceShow)
|
||||
{
|
||||
fw = this->spiceSize.x;
|
||||
fh = this->spiceSize.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
fw = this->format.frameWidth;
|
||||
fh = this->format.frameHeight;
|
||||
}
|
||||
glTranslatef(this->destRect.x, this->destRect.y, 0.0f);
|
||||
glScalef(
|
||||
(float)this->destRect.w / (float)fw,
|
||||
(float)this->destRect.h / (float)fh,
|
||||
1.0f
|
||||
);
|
||||
this->waiting = true;
|
||||
}
|
||||
|
||||
void opengl_onResize(LG_Renderer * renderer, const int width, const int height, const double scale,
|
||||
@@ -311,9 +293,21 @@ void opengl_onResize(LG_Renderer * renderer, const int width, const int height,
|
||||
glLoadIdentity();
|
||||
glOrtho(0, this->window.x, this->window.y, 0, -1, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
if (this->destRect.valid)
|
||||
{
|
||||
glTranslatef(this->destRect.x, this->destRect.y, 0.0f);
|
||||
glScalef(
|
||||
(float)this->destRect.w / (float)this->format.width,
|
||||
(float)this->destRect.h / (float)this->format.height,
|
||||
1.0f
|
||||
);
|
||||
}
|
||||
|
||||
// this is needed to refresh the font atlas texture
|
||||
ImGui_ImplOpenGL2_Shutdown();
|
||||
ImGui_ImplOpenGL2_Init();
|
||||
ImGui_ImplOpenGL2_NewFrame();
|
||||
}
|
||||
|
||||
@@ -333,14 +327,7 @@ bool opengl_onMouseShape(LG_Renderer * renderer, const LG_RendererCursor cursor,
|
||||
{
|
||||
if (this->mouseData)
|
||||
free(this->mouseData);
|
||||
|
||||
this->mouseData = malloc(size);
|
||||
if (!this->mouseData)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
this->mouseData = malloc(size);
|
||||
this->mouseDataSize = size;
|
||||
}
|
||||
|
||||
@@ -351,8 +338,7 @@ bool opengl_onMouseShape(LG_Renderer * renderer, const LG_RendererCursor cursor,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool opengl_onMouseEvent(LG_Renderer * renderer, const bool visible,
|
||||
int x, int y, const int hx, const int hy)
|
||||
bool opengl_onMouseEvent(LG_Renderer * renderer, const bool visible, const int x, const int y)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
|
||||
@@ -387,6 +373,18 @@ bool opengl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int dmaFd
|
||||
atomic_store_explicit(&this->frameUpdate, true, memory_order_release);
|
||||
LG_UNLOCK(this->frameLock);
|
||||
|
||||
if (this->waiting)
|
||||
{
|
||||
this->waiting = false;
|
||||
if (!this->params.quickSplash)
|
||||
this->waitFadeTime = microtime() + FADE_TIME;
|
||||
else
|
||||
{
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
this->waitDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -450,7 +448,6 @@ bool opengl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
// generate lists for drawing
|
||||
this->texList = glGenLists(BUFFER_COUNT);
|
||||
this->mouseList = glGenLists(1);
|
||||
this->spiceList = glGenLists(1);
|
||||
|
||||
// create the overlay textures
|
||||
glGenTextures(TEXTURE_COUNT, this->textures);
|
||||
@@ -473,13 +470,17 @@ bool opengl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool opengl_needsRender(LG_Renderer * renderer)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
return !this->waitDone;
|
||||
}
|
||||
|
||||
bool opengl_render(LG_Renderer * renderer, LG_RendererRotate rotate, const bool newFrame,
|
||||
const bool invalidateWindow, void (*preSwap)(void * udata), void * udata)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
|
||||
setupModelView(this);
|
||||
|
||||
switch(configure(this))
|
||||
{
|
||||
case CONFIG_STATUS_ERROR:
|
||||
@@ -495,12 +496,18 @@ bool opengl_render(LG_Renderer * renderer, LG_RendererRotate rotate, const bool
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
updateMouseShape(this);
|
||||
if (this->spiceShow)
|
||||
glCallList(this->spiceList);
|
||||
if (this->waiting)
|
||||
renderWait(this);
|
||||
else
|
||||
{
|
||||
bool newShape;
|
||||
updateMouseShape(this, &newShape);
|
||||
glCallList(this->texList + this->texRIndex);
|
||||
drawMouse(this);
|
||||
drawMouse(this);
|
||||
|
||||
if (!this->waitDone)
|
||||
renderWait(this);
|
||||
}
|
||||
|
||||
if (app_renderOverlay(NULL, 0) != 0)
|
||||
{
|
||||
@@ -521,154 +528,98 @@ bool opengl_render(LG_Renderer * renderer, LG_RendererRotate rotate, const bool
|
||||
return true;
|
||||
}
|
||||
|
||||
static void * opengl_createTexture(LG_Renderer * renderer,
|
||||
int width, int height, uint8_t * data)
|
||||
void drawTorus(float x, float y, float inner, float outer, unsigned int pts)
|
||||
{
|
||||
GLuint tex;
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
data);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
return (void*)(intptr_t)tex;
|
||||
}
|
||||
|
||||
static void opengl_freeTexture(LG_Renderer * renderer, void * texture)
|
||||
{
|
||||
GLuint tex = (GLuint)(intptr_t)texture;
|
||||
glDeleteTextures(1, &tex);
|
||||
}
|
||||
|
||||
static void opengl_spiceConfigure(LG_Renderer * renderer, int width, int height)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
this->spiceSize.x = width;
|
||||
this->spiceSize.y = height;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, this->textures[SPICE_TEXTURE]);
|
||||
glTexImage2D
|
||||
(
|
||||
GL_TEXTURE_2D,
|
||||
0 ,
|
||||
GL_RGBA,
|
||||
width ,
|
||||
height ,
|
||||
0 ,
|
||||
GL_BGRA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
0
|
||||
);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// create the display lists
|
||||
glNewList(this->spiceList, GL_COMPILE);
|
||||
glBindTexture(GL_TEXTURE_2D, this->textures[SPICE_TEXTURE]);
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0);
|
||||
glTexCoord2f(1.0f, 0.0f); glVertex2i(this->spiceSize.x, 0);
|
||||
glTexCoord2f(0.0f, 1.0f); glVertex2i(0, this->spiceSize.y);
|
||||
glTexCoord2f(1.0f, 1.0f);
|
||||
glVertex2i(this->spiceSize.x, this->spiceSize.y);
|
||||
glEnd();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glEndList();
|
||||
}
|
||||
|
||||
static void opengl_spiceDrawFill(LG_Renderer * renderer, int x, int y, int width,
|
||||
int height, uint32_t color)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
|
||||
/* this is a fairly hacky way to do this, but since it's only for the fallback
|
||||
* spice display it's not really an issue */
|
||||
|
||||
uint32_t line[width];
|
||||
for(int x = 0; x < width; ++x)
|
||||
line[x] = color;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, this->textures[SPICE_TEXTURE]);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
|
||||
for(; y < height; ++y)
|
||||
glTexSubImage2D
|
||||
(
|
||||
GL_TEXTURE_2D,
|
||||
0 ,
|
||||
x ,
|
||||
y ,
|
||||
width ,
|
||||
1 ,
|
||||
GL_BGRA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
line
|
||||
);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
static void opengl_spiceDrawBitmap(LG_Renderer * renderer, int x, int y, int width,
|
||||
int height, int stride, uint8_t * data, bool topDown)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
|
||||
if (!topDown)
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (unsigned int i = 0; i <= pts; ++i)
|
||||
{
|
||||
// this is non-optimal but as spice is a fallback it's not too critical
|
||||
uint8_t line[stride];
|
||||
for(int y = 0; y < height / 2; ++y)
|
||||
float angle = (i / (float)pts) * M_PI * 2.0f;
|
||||
glVertex2f(x + (inner * cos(angle)), y + (inner * sin(angle)));
|
||||
glVertex2f(x + (outer * cos(angle)), y + (outer * sin(angle)));
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void drawTorusArc(float x, float y, float inner, float outer, unsigned int pts, float s, float e)
|
||||
{
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (unsigned int i = 0; i <= pts; ++i)
|
||||
{
|
||||
float angle = s + ((i / (float)pts) * e);
|
||||
glVertex2f(x + (inner * cos(angle)), y + (inner * sin(angle)));
|
||||
glVertex2f(x + (outer * cos(angle)), y + (outer * sin(angle)));
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
static void renderWait(struct Inst * this)
|
||||
{
|
||||
float a;
|
||||
if (this->waiting)
|
||||
a = 1.0f;
|
||||
else
|
||||
{
|
||||
uint64_t t = microtime();
|
||||
if (t > this->waitFadeTime)
|
||||
{
|
||||
uint8_t * top = data + y * stride;
|
||||
uint8_t * btm = data + (height - y - 1) * stride;
|
||||
memcpy(line, top , stride);
|
||||
memcpy(top , btm , stride);
|
||||
memcpy(btm , line, stride);
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
this->waitDone = true;
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t delta = this->waitFadeTime - t;
|
||||
a = 1.0f / FADE_TIME * delta;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, this->textures[SPICE_TEXTURE]);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride / 4);
|
||||
glTexSubImage2D
|
||||
(
|
||||
GL_TEXTURE_2D,
|
||||
0 ,
|
||||
x ,
|
||||
y ,
|
||||
width ,
|
||||
height ,
|
||||
GL_BGRA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
data
|
||||
);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
glEnable(GL_BLEND);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glTranslatef(this->window.x / 2.0f, this->window.y / 2.0f, 0.0f);
|
||||
|
||||
static void opengl_spiceShow(LG_Renderer * renderer, bool show)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
this->spiceShow = show;
|
||||
//draw the background gradient
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glColor4f(0.234375f, 0.015625f, 0.425781f, a);
|
||||
glVertex2f(0, 0);
|
||||
glColor4f(0, 0, 0, a);
|
||||
for (unsigned int i = 0; i <= 100; ++i)
|
||||
{
|
||||
float angle = (i / (float)100) * M_PI * 2.0f;
|
||||
glVertex2f(cos(angle) * this->window.x, sin(angle) * this->window.y);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
// draw the logo
|
||||
glColor4f(1.0f, 1.0f, 1.0f, a);
|
||||
glScalef (2.0f, 2.0f, 1.0f);
|
||||
|
||||
drawTorus ( 0, 0, 40, 42, 60);
|
||||
drawTorus ( 0, 0, 32, 34, 60);
|
||||
drawTorus (-50, -3, 2, 4, 30);
|
||||
drawTorus ( 50, -3, 2, 4, 30);
|
||||
drawTorusArc( 0, 0, 51, 49, 60, 0.0f, M_PI);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(-1 , 50);
|
||||
glVertex2f(-1 , 76);
|
||||
glVertex2f( 1 , 76);
|
||||
glVertex2f( 1 , 50);
|
||||
glVertex2f(-14, 76);
|
||||
glVertex2f(-14, 78);
|
||||
glVertex2f( 14, 78);
|
||||
glVertex2f( 14, 76);
|
||||
glVertex2f(-21, 83);
|
||||
glVertex2f(-21, 85);
|
||||
glVertex2f( 21, 85);
|
||||
glVertex2f( 21, 83);
|
||||
glEnd();
|
||||
|
||||
drawTorusArc(-14, 83, 5, 7, 10, M_PI , M_PI / 2.0f);
|
||||
drawTorusArc( 14, 83, 5, 7, 10, M_PI * 1.5f, M_PI / 2.0f);
|
||||
|
||||
//FIXME: draw the diagnoal marks on the circle
|
||||
|
||||
glPopMatrix();
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
const LG_RendererOps LGR_OpenGL =
|
||||
@@ -686,14 +637,8 @@ const LG_RendererOps LGR_OpenGL =
|
||||
.onFrameFormat = opengl_onFrameFormat,
|
||||
.onFrame = opengl_onFrame,
|
||||
.renderStartup = opengl_renderStartup,
|
||||
.render = opengl_render,
|
||||
.createTexture = opengl_createTexture,
|
||||
.freeTexture = opengl_freeTexture,
|
||||
|
||||
.spiceConfigure = opengl_spiceConfigure,
|
||||
.spiceDrawFill = opengl_spiceDrawFill,
|
||||
.spiceDrawBitmap = opengl_spiceDrawBitmap,
|
||||
.spiceShow = opengl_spiceShow
|
||||
.needsRender = opengl_needsRender,
|
||||
.render = opengl_render
|
||||
};
|
||||
|
||||
static bool _checkGLError(unsigned int line, const char * name)
|
||||
@@ -783,7 +728,7 @@ static enum ConfigStatus configure(struct Inst * this)
|
||||
}
|
||||
|
||||
// calculate the texture size in bytes
|
||||
this->texSize = this->format.frameHeight * this->format.pitch;
|
||||
this->texSize = this->format.height * this->format.pitch;
|
||||
this->texPos = 0;
|
||||
|
||||
g_gl_dynProcs.glGenBuffers(BUFFER_COUNT, this->vboID);
|
||||
@@ -880,8 +825,8 @@ static enum ConfigStatus configure(struct Inst * this)
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
this->intFormat,
|
||||
this->format.frameWidth,
|
||||
this->format.frameHeight,
|
||||
this->format.width,
|
||||
this->format.height,
|
||||
0,
|
||||
this->vboFormat,
|
||||
this->dataFormat,
|
||||
@@ -904,11 +849,10 @@ static enum ConfigStatus configure(struct Inst * this)
|
||||
glBindTexture(GL_TEXTURE_2D, this->frames[i]);
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0);
|
||||
glTexCoord2f(1.0f, 0.0f); glVertex2i(this->format.frameWidth, 0);
|
||||
glTexCoord2f(0.0f, 1.0f); glVertex2i(0, this->format.frameHeight);
|
||||
glTexCoord2f(1.0f, 1.0f);
|
||||
glVertex2i(this->format.frameWidth, this->format.frameHeight);
|
||||
glTexCoord2f(0.0f, 0.0f); glVertex2i(0 , 0 );
|
||||
glTexCoord2f(1.0f, 0.0f); glVertex2i(this->format.width, 0 );
|
||||
glTexCoord2f(0.0f, 1.0f); glVertex2i(0 , this->format.height);
|
||||
glTexCoord2f(1.0f, 1.0f); glVertex2i(this->format.width, this->format.height);
|
||||
glEnd();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glEndList();
|
||||
@@ -960,9 +904,10 @@ static void deconfigure(struct Inst * this)
|
||||
this->configured = false;
|
||||
}
|
||||
|
||||
static void updateMouseShape(struct Inst * this)
|
||||
static void updateMouseShape(struct Inst * this, bool * newShape)
|
||||
{
|
||||
LG_LOCK(this->mouseLock);
|
||||
*newShape = this->newShape;
|
||||
if (!this->newShape)
|
||||
{
|
||||
LG_UNLOCK(this->mouseLock);
|
||||
@@ -1164,14 +1109,14 @@ static bool drawFrame(struct Inst * this)
|
||||
|
||||
const int bpp = this->format.bpp / 8;
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT , bpp);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, this->format.frameWidth);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, this->format.width);
|
||||
|
||||
this->texPos = 0;
|
||||
|
||||
framebuffer_read_fn(
|
||||
this->frame,
|
||||
this->format.frameHeight,
|
||||
this->format.frameWidth,
|
||||
this->format.height,
|
||||
this->format.width,
|
||||
bpp,
|
||||
this->format.pitch,
|
||||
opengl_bufferFn,
|
||||
@@ -1186,8 +1131,8 @@ static bool drawFrame(struct Inst * this)
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
this->format.frameWidth ,
|
||||
this->format.frameHeight,
|
||||
this->format.width ,
|
||||
this->format.height,
|
||||
this->vboFormat,
|
||||
this->dataFormat,
|
||||
(void*)0
|
||||
@@ -1195,8 +1140,7 @@ static bool drawFrame(struct Inst * this)
|
||||
if (check_gl_error("glTexSubImage2D"))
|
||||
{
|
||||
DEBUG_ERROR("texWIndex: %u, width: %u, height: %u, vboFormat: %x, texSize: %lu",
|
||||
this->texWIndex, this->format.frameWidth, this->format.frameHeight,
|
||||
this->vboFormat, this->texSize
|
||||
this->texWIndex, this->format.width, this->format.height, this->vboFormat, this->texSize
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1204,8 +1148,8 @@ static bool drawFrame(struct Inst * this)
|
||||
g_gl_dynProcs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
const bool mipmap = this->opt.mipmap && (
|
||||
(this->format.frameWidth > this->destRect.w) ||
|
||||
(this->format.frameHeight > this->destRect.h));
|
||||
(this->format.width > this->destRect.w) ||
|
||||
(this->format.height > this->destRect.h));
|
||||
|
||||
if (mipmap)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user