Compare commits

..

1 Commits

Author SHA1 Message Date
Geoffrey McRae
3df7d30cd5 [client] core: don't try to send LGMP messages if the video is stopped
If the video stream is stopped the LGMP session is not valid, so we
can't send messages to the client.
2021-12-27 09:56:53 +11:00
283 changed files with 3612 additions and 28224 deletions

4
.github/CODEOWNERS vendored
View File

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

View File

@@ -12,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
View File

@@ -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
View File

@@ -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)

View File

@@ -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.

View File

@@ -4,13 +4,13 @@ project(looking-glass-client C CXX)
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(FATAL_ERROR
"\n"
"In-source builds are not supported\n"
"See build instructions provided in: "
"${PROJECT_TOP}/doc/build.rst\n"
"Refusing to continue"
)
message(FATAL_ERROR
"\n"
"In-source builds are not supported\n"
"See build instructions provided in: "
"${PROJECT_TOP}/doc/build.rst\n"
"Refusing to continue"
)
endif()
list(APPEND CMAKE_MODULE_PATH "${PROJECT_TOP}/cmake/" "${PROJECT_SOURCE_DIR}/cmake/")
@@ -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)

View File

@@ -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})

View File

@@ -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
)

View File

@@ -1,617 +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"
#include "common/option.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 struct Option pipewire_options[] =
{
{
.module = "pipewire",
.name = "outDevice",
.description = "The default playback device to use",
.type = OPTION_TYPE_STRING
},
{
.module = "pipewire",
.name = "recDevice",
.description = "The default record device to use",
.type = OPTION_TYPE_STRING
},
{0}
};
static void pipewire_earlyInit(void)
{
option_register(pipewire_options);
}
static bool pipewire_init(void)
{
pw_init(NULL, NULL);
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);
struct pw_properties * props =
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
);
const char * device = option_get_string("pipewire", "outDevice");
if (device)
{
#ifdef PW_KEY_TARGET_OBJECT
pw_properties_set(props, PW_KEY_TARGET_OBJECT, device);
#else
pw_properties_set(props, PW_KEY_NODE_TARGET, device);
#endif
}
pw.playback.stream = pw_stream_new_simple(
pw.loop,
"Looking Glass",
props,
&events,
NULL
);
// 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;
struct pw_properties * props =
pw_properties_new(
PW_KEY_NODE_NAME , "Looking Glass",
PW_KEY_MEDIA_TYPE , "Audio",
PW_KEY_MEDIA_CATEGORY, "Capture",
PW_KEY_MEDIA_ROLE , "Music",
NULL
);
const char * device = option_get_string("pipewire", "recDevice");
if (device)
{
#ifdef PW_KEY_TARGET_OBJECT
pw_properties_set(props, PW_KEY_TARGET_OBJECT, device);
#else
pw_properties_set(props, PW_KEY_NODE_TARGET, device);
#endif
}
pw_thread_loop_lock(pw.thread);
pw.record.stream = pw_stream_new_simple(
pw.loop,
"Looking Glass",
props,
&events,
NULL
);
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",
.earlyInit = pipewire_earlyInit,
.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
}
};

View File

@@ -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
)

View File

@@ -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
}
};

View File

@@ -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")

View File

@@ -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);
}

View File

@@ -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,
};

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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,

View File

@@ -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

View File

@@ -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.
}

View File

@@ -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

View File

@@ -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
@@ -71,16 +71,12 @@ static bool waylandProbe(void)
static bool waylandInit(const LG_DSInitParams params)
{
memset(&wlWm, 0, sizeof(wlWm));
wlWm.display = wl_display_connect(NULL);
if (!wlWm.display)
return false;
wl_list_init(&wlWm.surfaceOutputs);
wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale");
wlWm.display = wl_display_connect(NULL);
wlWm.width = params.w;
wlWm.height = params.h;
@@ -93,9 +89,6 @@ static bool waylandInit(const LG_DSInitParams params)
if (!waylandRegistryInit())
return false;
if (!waylandActivationInit())
return false;
if (!waylandIdleInit())
return false;
@@ -158,7 +151,6 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
struct LG_DisplayServerOps LGDS_Wayland =
{
.name = "Wayland",
.setup = waylandSetup,
.probe = waylandProbe,
.earlyInit = waylandEarlyInit,
@@ -195,7 +187,6 @@ struct LG_DisplayServerOps LGDS_Wayland =
.warpPointer = waylandWarpPointer,
.realignPointer = waylandRealignPointer,
.isValidPointerPos = waylandIsValidPointerPos,
.requestActivation = waylandActivationRequestActivation,
.inhibitIdle = waylandInhibitIdle,
.uninhibitIdle = waylandUninhibitIdle,
.wait = waylandWait,

View File

@@ -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);

View File

@@ -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();

View File

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

View File

@@ -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

View File

@@ -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) \
\

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;
@@ -242,31 +239,12 @@ out:
return;
}
static int x11ErrorHandler(Display * display, XErrorEvent * error)
{
char errorText[1024];
XGetErrorText(display, error->error_code, errorText, sizeof(errorText));
DEBUG_ERROR("X11 Error: %s", errorText);
DEBUG_PRINT_BACKTRACE();
return 0;
}
static int x11IOErrorHandler(Display * display)
{
DEBUG_FATAL("Fatal X11 IO Error");
return 0;
}
static bool x11Init(const LG_DSInitParams params)
{
XIDeviceInfo *devinfo;
int count;
int event, error;
XSetErrorHandler(x11ErrorHandler);
XSetIOErrorHandler(x11IOErrorHandler);
memset(&x11, 0, sizeof(x11));
x11.xValuator = -1;
x11.yValuator = -1;
@@ -369,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)
@@ -434,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,
@@ -450,7 +422,7 @@ static bool x11Init(const LG_DSInitParams params)
32,
PropModeReplace,
(unsigned char *)&wmState,
2
wmStateCount
);
}
@@ -550,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);
@@ -564,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 );
@@ -594,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 };
@@ -695,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);
@@ -778,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);
}
@@ -1068,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;
@@ -1097,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;
}
@@ -1171,46 +1097,6 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
app_updateCursorPos(xie->event_x, xie->event_y);
app_handleEnterEvent(false);
x11.entered = false;
/**
* Because there is a race with the pointer ungrab the enter event for the
* next window is sometimes sent with the mode NotifyUngrab, unfortunatly
* some window managers such as i3 will ignore these which breaks focus
* follows mouse mode. To correct this we generate and send a normal
* EnterNotify event.
*/
int root_x, root_y, win_x, win_y;
Window root_win, child_win;
unsigned int mask;
XQueryPointer(x11.display, DefaultRootWindow(x11.display), &root_win,
&child_win, &root_x, &root_y, &win_x, &win_y, &mask);
int target_x, target_y;
Window target_root;
XTranslateCoordinates(x11.display, DefaultRootWindow(x11.display),
child_win, root_x, root_y, &target_x, &target_y, &target_root);
XEvent event;
memset(&event, 0, sizeof(event));
event.type = EnterNotify;
event.xcrossing.serial = 0;
event.xcrossing.send_event = True;
event.xcrossing.display = x11.display;
event.xcrossing.window = child_win;
event.xcrossing.root = root_win;
event.xcrossing.subwindow = child_win;
event.xcrossing.time = CurrentTime;
event.xcrossing.mode = NotifyNormal;
event.xcrossing.detail = NotifyNonlinear;
event.xcrossing.same_screen = True;
event.xcrossing.focus = False;
event.xcrossing.x = target_x;
event.xcrossing.y = target_y;
event.xcrossing.x_root = root_x;
event.xcrossing.y_root = root_y;
XSendEvent(x11.display, child_win, True, EnterWindowMask, &event);
XFlush(x11.display);
return;
}
@@ -1220,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;
@@ -1271,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;
@@ -1301,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;
}
@@ -1312,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;
}
@@ -1917,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);
@@ -1996,7 +1856,6 @@ static void x11Minimize(void)
struct LG_DisplayServerOps LGDS_X11 =
{
.name = "X11",
.setup = x11Setup,
.probe = x11Probe,
.earlyInit = x11EarlyInit,
@@ -2030,7 +1889,6 @@ struct LG_DisplayServerOps LGDS_X11 =
.warpPointer = x11WarpPointer,
.realignPointer = x11RealignPointer,
.isValidPointerPos = x11IsValidPointerPos,
.requestActivation = x11RequestActivation,
.inhibitIdle = x11InhibitIdle,
.uninhibitIdle = x11UninhibitIdle,
.wait = x11Wait,

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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 */
};

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

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

View File

@@ -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;
@@ -100,28 +97,34 @@ static bool cursorTexInit(struct CursorTex * t,
}
if (!egl_shaderCompile(t->shader,
vertex_code, vertex_size, fragment_code, fragment_size, false))
vertex_code, vertex_size, fragment_code, fragment_size))
{
DEBUG_ERROR("Failed to compile the cursor shader");
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;
}

View File

@@ -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);

View File

@@ -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
@@ -76,8 +76,7 @@ bool egl_damageInit(EGL_Damage ** damage)
if (!egl_shaderCompile((*damage)->shader,
b_shader_damage_vert, b_shader_damage_vert_size,
b_shader_damage_frag, b_shader_damage_frag_size,
false))
b_shader_damage_frag, b_shader_damage_frag_size))
{
DEBUG_ERROR("Failed to compile the damage shader");
return false;

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-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

View File

@@ -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,7 +46,6 @@ struct DesktopShader
EGL_Shader * shader;
GLint uTransform;
GLint uDesktopSize;
GLint uSamplerType;
GLint uScaleAlgo;
GLint uNVGain;
GLint uCBMode;
@@ -58,7 +57,8 @@ struct EGL_Desktop
EGLDisplay * display;
EGL_Texture * texture;
struct DesktopShader dmaShader, shader;
GLuint sampler;
struct DesktopShader shader;
EGL_DesktopRects * mesh;
CountedBuffer * matrix;
@@ -66,10 +66,6 @@ struct EGL_Desktop
int width, height;
LG_RendererRotate rotate;
bool useSpice;
int spiceWidth, spiceHeight;
EGL_Texture * spiceTexture;
// scale algorithm
int scaleAlgo;
@@ -93,8 +89,7 @@ void toggleNV(int key, void * opaque);
static bool egl_initDesktopShader(
struct DesktopShader * shader,
const char * vertex_code , size_t vertex_size,
const char * fragment_code, size_t fragment_size,
bool useDMA
const char * fragment_code, size_t fragment_size
)
{
if (!egl_shaderInit(&shader->shader))
@@ -102,8 +97,7 @@ static bool egl_initDesktopShader(
if (!egl_shaderCompile(shader->shader,
vertex_code , vertex_size,
fragment_code, fragment_size,
useDMA))
fragment_code, fragment_size))
{
return false;
}
@@ -132,12 +126,21 @@ 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;
}
if (!egl_initDesktopShader(
&desktop->shader,
b_shader_desktop_vert , b_shader_desktop_vert_size,
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size))
{
DEBUG_ERROR("Failed to initialize the generic desktop shader");
return false;
}
if (!egl_desktopRectsInit(&desktop->mesh, maxRects))
{
DEBUG_ERROR("Failed to initialize the desktop mesh");
@@ -151,28 +154,7 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
return false;
}
if (!egl_initDesktopShader(
&desktop->shader,
b_shader_desktop_vert , b_shader_desktop_vert_size,
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size,
false))
{
DEBUG_ERROR("Failed to initialize the desktop shader");
return false;
}
if (useDMA)
if (!egl_initDesktopShader(
&desktop->dmaShader,
b_shader_desktop_vert , b_shader_desktop_vert_size,
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size,
true))
{
DEBUG_ERROR("Failed to initialize the desktop DMA shader");
return false;
}
app_registerKeybind(0, 'N', toggleNV, desktop,
app_registerKeybind(KEY_N, toggleNV, desktop,
"Toggle night vision mode");
desktop->nvMax = option_get_int("egl", "nvGainMax");
@@ -220,12 +202,10 @@ void egl_desktopFree(EGL_Desktop ** desktop)
if (!*desktop)
return;
egl_textureFree (&(*desktop)->texture );
egl_textureFree (&(*desktop)->spiceTexture );
egl_shaderFree (&(*desktop)->shader .shader);
egl_shaderFree (&(*desktop)->dmaShader.shader);
egl_desktopRectsFree(&(*desktop)->mesh );
countedBufferRelease(&(*desktop)->matrix );
egl_textureFree (&(*desktop)->texture );
egl_shaderFree (&(*desktop)->shader.shader);
egl_desktopRectsFree(&(*desktop)->mesh );
countedBufferRelease(&(*desktop)->matrix );
egl_postProcessFree(&(*desktop)->pp);
@@ -248,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();
@@ -301,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
))
{
@@ -316,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;
}
@@ -350,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;
@@ -380,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");
@@ -409,25 +378,26 @@ 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, desktop->useDMA);
egl_postProcessRun(desktop->pp, desktop->texture, desktop->mesh,
desktop->width, desktop->height, outputWidth, outputHeight);
unsigned int finalSizeX, finalSizeY;
EGL_Texture * texture = egl_postProcessGetOutput(desktop->pp,
GLuint texture = egl_postProcessGetOutput(desktop->pp,
&finalSizeX, &finalSizeY);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
egl_resetViewport(desktop->egl);
glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, desktop->sampler);
if (finalSizeX > width || finalSizeY > height)
if (finalSizeX > desktop->width || finalSizeY > desktop->height)
scaleType = EGL_DESKTOP_DOWNSCALE;
switch (desktop->scaleAlgo)
@@ -450,10 +420,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
scaleAlgo = desktop->scaleAlgo;
}
const struct DesktopShader * shader =
desktop->useDMA && texture == desktop->texture ?
&desktop->dmaShader : &desktop->shader;
const struct DesktopShader * shader = &desktop->shader;
EGL_Uniform uniforms[] =
{
{
@@ -464,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,
@@ -490,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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View 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);
}

View File

@@ -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);

View File

@@ -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
};

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
@@ -68,11 +68,9 @@ typedef struct EGL_FilterOps
/* reads filter state from options */
void (*loadState)(EGL_Filter * filter);
/* set the input format of the filter
* useDMA will be true if the texture provided needs to use samplerExternalOES
*/
/* set the input format of the filter */
bool (*setup)(EGL_Filter * filter, enum EGL_PixelFormat pixFmt,
unsigned int width, unsigned int height, bool useDMA);
unsigned int width, unsigned int height);
/* set the output resolution hint for the filter
* this is optional and only a hint */
@@ -89,8 +87,8 @@ typedef struct EGL_FilterOps
/* runs the filter on the provided texture
* returns the processed texture as the output */
EGL_Texture * (*run)(EGL_Filter * filter, EGL_FilterRects * rects,
EGL_Texture * texture);
GLuint (*run)(EGL_Filter * filter, EGL_FilterRects * rects,
GLuint texture);
/* called when the filter output is no loger needed so it can release memory
* this is optional */
@@ -135,10 +133,9 @@ static inline void egl_filterLoadState(EGL_Filter * filter)
}
static inline bool egl_filterSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
bool useDMA)
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
return filter->ops.setup(filter, pixFmt, width, height, useDMA);
return filter->ops.setup(filter, pixFmt, width, height);
}
static inline void egl_filterSetOutputResHint(EGL_Filter * filter,
@@ -159,8 +156,8 @@ static inline bool egl_filterPrepare(EGL_Filter * filter)
return filter->ops.prepare(filter);
}
static inline EGL_Texture * egl_filterRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
static inline GLuint egl_filterRun(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
{
return filter->ops.run(filter, rects, texture);
}

View File

@@ -1,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
@@ -60,7 +60,6 @@ typedef struct EGL_FilterDownscale
EGL_Shader * lanczos2;
DownscaleFilter filter;
int useDMA;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
float pixelSize;
@@ -158,26 +157,55 @@ static bool egl_filterDownscaleInit(EGL_Filter ** filter)
return false;
}
this->useDMA = -1;
if (!egl_shaderInit(&this->nearest))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->nearest,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_downscale_frag, b_shader_downscale_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
if (!egl_shaderInit(&this->linear))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->linear,
b_shader_basic_vert, b_shader_basic_vert_size,
b_shader_downscale_linear_frag, b_shader_downscale_linear_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
if (!egl_shaderInit(&this->lanczos2))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->lanczos2,
b_shader_basic_vert, b_shader_basic_vert_size,
b_shader_downscale_lanczos2_frag, b_shader_downscale_lanczos2_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
this->uNearest.type = EGL_UNIFORM_TYPE_3F;
this->uNearest.location =
egl_shaderGetUniform(this->nearest, "uConfig");
if (!egl_framebufferInit(&this->fb))
{
DEBUG_ERROR("Failed to initialize the framebuffer");
@@ -240,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;
@@ -298,8 +326,7 @@ static bool egl_filterDownscaleImguiConfig(EGL_Filter * filter)
}
static bool egl_filterDownscaleSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
bool useDMA)
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
@@ -309,48 +336,6 @@ static bool egl_filterDownscaleSetup(EGL_Filter * filter,
if (!this->enable)
return false;
if (this->useDMA != useDMA)
{
if (!egl_shaderCompile(this->nearest,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_downscale_frag,
b_shader_downscale_frag_size,
useDMA)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
if (!egl_shaderCompile(this->linear,
b_shader_basic_vert, b_shader_basic_vert_size,
b_shader_downscale_linear_frag,
b_shader_downscale_linear_frag_size,
useDMA)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
if (!egl_shaderCompile(this->lanczos2,
b_shader_basic_vert, b_shader_basic_vert_size,
b_shader_downscale_lanczos2_frag,
b_shader_downscale_lanczos2_frag_size,
useDMA)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
this->uNearest.type = EGL_UNIFORM_TYPE_3F;
this->uNearest.location =
egl_shaderGetUniform(this->nearest, "uConfig");
this->useDMA = useDMA;
}
if (this->prepared &&
pixFmt == this->pixFmt &&
this->width == width &&
@@ -400,15 +385,15 @@ static bool egl_filterDownscalePrepare(EGL_Filter * filter)
return true;
}
static EGL_Texture * egl_filterDownscaleRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
static GLuint egl_filterDownscaleRun(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
egl_framebufferBind(this->fb);
glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture);
glBindTexture(GL_TEXTURE_2D, texture);
EGL_Shader * shader;

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-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,7 +37,6 @@ typedef struct EGL_FilterFFXCAS
EGL_Shader * shader;
bool enable;
int useDMA;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
float sharpness;
@@ -107,14 +106,21 @@ static bool egl_filterFFXCASInit(EGL_Filter ** filter)
return false;
}
this->useDMA = -1;
if (!egl_shaderInit(&this->shader))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->shader,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_cas_frag, b_shader_ffx_cas_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
this->consts = countedBufferNew(8 * sizeof(GLuint));
if (!this->consts)
{
@@ -122,6 +128,12 @@ static bool egl_filterFFXCASInit(EGL_Filter ** filter)
goto error_shader;
}
egl_shaderSetUniforms(this->shader, &(EGL_Uniform) {
.type = EGL_UNIFORM_TYPE_4UIV,
.location = egl_shaderGetUniform(this->shader, "uConsts"),
.v = this->consts,
}, 1);
egl_filterFFXCASLoadState(&this->base);
if (!egl_framebufferInit(&this->fb))
@@ -208,35 +220,13 @@ static bool egl_filterFFXCASImguiConfig(EGL_Filter * filter)
}
static bool egl_filterFFXCASSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
bool useDMA)
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
if (!this->enable)
return false;
if (this->useDMA != useDMA)
{
if (!egl_shaderCompile(this->shader,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_cas_frag, b_shader_ffx_cas_frag_size,
useDMA)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
egl_shaderSetUniforms(this->shader, &(EGL_Uniform) {
.type = EGL_UNIFORM_TYPE_4UIV,
.location = egl_shaderGetUniform(this->shader, "uConsts"),
.v = this->consts,
}, 1);
this->useDMA = useDMA;
}
if (pixFmt == this->pixFmt && this->width == width && this->height == height)
return true;
@@ -272,15 +262,15 @@ static bool egl_filterFFXCASPrepare(EGL_Filter * filter)
return true;
}
static EGL_Texture * egl_filterFFXCASRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
static GLuint egl_filterFFXCASRun(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
egl_framebufferBind(this->fb);
glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->shader);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-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 @@ typedef struct EGL_FilterFFXFSR1
CountedBuffer * consts;
EGL_Uniform easuUniform[2], rcasUniform;
int useDMA;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
unsigned int inWidth, inHeight;
@@ -110,8 +109,6 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
return false;
}
this->useDMA = -1;
if (!egl_shaderInit(&this->easu))
{
DEBUG_ERROR("Failed to initialize the Easu shader");
@@ -124,10 +121,18 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
goto error_esau;
}
if (!egl_shaderCompile(this->easu,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_easu_frag, b_shader_ffx_fsr1_easu_frag_size)
)
{
DEBUG_ERROR("Failed to compile the Easu shader");
goto error_rcas;
}
if (!egl_shaderCompile(this->rcas,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_rcas_frag, b_shader_ffx_fsr1_rcas_frag_size,
false)
b_shader_ffx_fsr1_rcas_frag, b_shader_ffx_fsr1_rcas_frag_size)
)
{
DEBUG_ERROR("Failed to compile the Rcas shader");
@@ -143,6 +148,14 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
egl_filterFFXFSR1LoadState(&this->base);
this->easuUniform[0].type = EGL_UNIFORM_TYPE_4UIV;
this->easuUniform[0].location =
egl_shaderGetUniform(this->easu, "uConsts");
this->easuUniform[0].v = this->consts;
this->easuUniform[1].type = EGL_UNIFORM_TYPE_2F;
this->easuUniform[1].location =
egl_shaderGetUniform(this->easu, "uOutRes");
this->rcasUniform.type = EGL_UNIFORM_TYPE_4UI;
this->rcasUniform.location = egl_shaderGetUniform(this->rcas, "uConsts");
rcasUpdateUniform(this);
@@ -322,37 +335,13 @@ static void egl_filterFFXFSR1SetOutputResHint(EGL_Filter * filter,
}
static bool egl_filterFFXFSR1Setup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
bool useDMA)
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
if (!this->enable)
return false;
if (this->useDMA != useDMA)
{
if (!egl_shaderCompile(this->easu,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_easu_frag, b_shader_ffx_fsr1_easu_frag_size,
useDMA)
)
{
DEBUG_ERROR("Failed to compile the Easu shader");
return false;
}
this->easuUniform[0].type = EGL_UNIFORM_TYPE_4UIV;
this->easuUniform[0].location =
egl_shaderGetUniform(this->easu, "uConsts");
this->easuUniform[0].v = this->consts;
this->easuUniform[1].type = EGL_UNIFORM_TYPE_2F;
this->easuUniform[1].location =
egl_shaderGetUniform(this->easu, "uOutRes");
this->useDMA = useDMA;
}
this->active = this->width > width && this->height > height;
if (!this->active)
return false;
@@ -406,15 +395,15 @@ static bool egl_filterFFXFSR1Prepare(EGL_Filter * filter)
return true;
}
static EGL_Texture * egl_filterFFXFSR1Run(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
static GLuint egl_filterFFXFSR1Run(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
// pass 1, Easu
egl_framebufferBind(this->easuFb);
glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->easu);
egl_filterRectsRender(this->easu, rects);
@@ -423,7 +412,7 @@ static EGL_Texture * egl_filterFFXFSR1Run(EGL_Filter * filter,
// pass 2, Rcas
egl_framebufferBind(this->rcasFb);
glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->rcas);
egl_filterRectsRender(this->rcas, rects);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-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

View File

@@ -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;
@@ -100,7 +100,9 @@ void egl_framebufferBind(EGL_Framebuffer * this)
glViewport(0, 0, this->tex->format.width, this->tex->format.height);
}
EGL_Texture * egl_framebufferGetTexture(EGL_Framebuffer * this)
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this)
{
return this->tex;
GLuint output;
egl_textureGet(this->tex, &output, NULL, NULL);
return output;
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-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,4 +32,4 @@ bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt,
void egl_framebufferBind(EGL_Framebuffer * this);
EGL_Texture * egl_framebufferGetTexture(EGL_Framebuffer * this);
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this);

View File

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

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-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);

View File

@@ -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

View File

@@ -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
@@ -49,7 +49,7 @@ static const EGL_FilterOps * EGL_Filters[] =
struct EGL_PostProcess
{
Vector filters;
EGL_Texture * output;
GLuint output;
unsigned int outputX, outputY;
_Atomic(bool) modified;
@@ -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);
}
}
@@ -606,7 +583,7 @@ bool egl_postProcessConfigModified(EGL_PostProcess * this)
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
unsigned int targetX, unsigned int targetY, bool useDMA)
unsigned int targetX, unsigned int targetY)
{
if (targetX == 0 && targetY == 0)
DEBUG_FATAL("targetX || targetY == 0");
@@ -614,9 +591,8 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
EGL_Filter * lastFilter = NULL;
unsigned int sizeX, sizeY;
//TODO: clean this up
GLuint _unused;
if (egl_textureGet(tex, &_unused, &sizeX, &sizeY) != EGL_TEX_STATUS_OK)
GLuint texture;
if (egl_textureGet(tex, &texture, &sizeX, &sizeY) != EGL_TEX_STATUS_OK)
return false;
if (atomic_exchange(&this->modified, false))
@@ -637,12 +613,11 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
};
EGL_Filter * filter;
EGL_Texture * texture = tex;
vector_forEach(filter, &this->filters)
{
egl_filterSetOutputResHint(filter, targetX, targetY);
if (!egl_filterSetup(filter, tex->format.pixFmt, sizeX, sizeY, useDMA) ||
if (!egl_filterSetup(filter, tex->format.pixFmt, sizeX, sizeY) ||
!egl_filterPrepare(filter))
continue;
@@ -653,9 +628,6 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
egl_filterRelease(lastFilter);
lastFilter = filter;
// the first filter to run will convert to a normal texture
useDMA = false;
}
this->output = texture;
@@ -664,7 +636,7 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
return true;
}
EGL_Texture * egl_postProcessGetOutput(EGL_PostProcess * this,
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
unsigned int * outputX, unsigned int * outputY)
{
*outputX = this->outputX;

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-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
@@ -41,7 +41,7 @@ bool egl_postProcessConfigModified(EGL_PostProcess * this);
* targetX/Y is the final target output dimension hint if scalers are present */
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
unsigned int targetX, unsigned int targetY, bool useDMA);
unsigned int targetX, unsigned int targetY);
EGL_Texture * egl_postProcessGetOutput(EGL_PostProcess * this,
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
unsigned int * outputX, unsigned int * outputY);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-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
@@ -64,8 +64,7 @@ void egl_shaderFree(EGL_Shader ** shader)
*shader = NULL;
}
bool egl_shaderLoad(EGL_Shader * this,
const char * vertex_file, const char * fragment_file, bool useDMA)
bool egl_shaderLoad(EGL_Shader * this, const char * vertex_file, const char * fragment_file)
{
char * vertex_code, * fragment_code;
size_t vertex_size, fragment_size;
@@ -87,16 +86,13 @@ bool egl_shaderLoad(EGL_Shader * this,
DEBUG_INFO("Loaded fragment shader: %s", fragment_file);
bool ret = egl_shaderCompile(this,
vertex_code, vertex_size, fragment_code, fragment_size,
useDMA);
bool ret = egl_shaderCompile(this, vertex_code, vertex_size, fragment_code, fragment_size);
free(vertex_code);
free(fragment_code);
return ret;
}
static bool shaderCompile(EGL_Shader * this, const char * vertex_code,
bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
size_t vertex_size, const char * fragment_code, size_t fragment_size)
{
if (this->hasShader)
@@ -123,15 +119,10 @@ static bool 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);
@@ -154,15 +145,10 @@ static bool 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);
@@ -208,64 +194,6 @@ static bool shaderCompile(EGL_Shader * this, const char * vertex_code,
return true;
}
bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
size_t vertex_size, const char * fragment_code, size_t fragment_size,
bool useDMA)
{
if (useDMA)
{
const char * search = "sampler2D";
const char * replace = "samplerExternalOES";
const char * src = fragment_code;
int instances = 0;
while((src = strstr(src, search)))
{
++instances;
src += strlen(search);
}
const int diff = (strlen(replace) - strlen(search)) * instances;
char * newCode = malloc(fragment_size + diff);
if (!newCode)
{
DEBUG_ERROR("Out of memory");
return false;
}
src = fragment_code;
char * dst = newCode;
for(int i = 0; i < instances; ++i)
{
const char * pos = strstr(src, search);
const int offset = pos - src;
memcpy(dst, src, offset);
dst += offset;
src = pos + strlen(search);
memcpy(dst, replace, strlen(replace));
dst += strlen(replace);
}
const int final = fragment_size - (src - fragment_code);
memcpy(dst, src, final);
dst[final] = 0;
bool result = shaderCompile(
this,
vertex_code, vertex_size,
newCode , fragment_size + diff);
free(newCode);
return result;
}
return shaderCompile(this,
vertex_code , vertex_size,
fragment_code, fragment_size);
}
void egl_shaderSetUniforms(EGL_Shader * this, EGL_Uniform * uniforms, int count)
{
egl_shaderFreeUniforms(this);
@@ -273,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;
}

View File

@@ -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
@@ -97,11 +97,10 @@ bool egl_shaderInit(EGL_Shader ** shader);
void egl_shaderFree(EGL_Shader ** shader);
bool egl_shaderLoad(EGL_Shader * model, const char * vertex_file,
const char * fragment_file, bool useDMA);
const char * fragment_file);
bool egl_shaderCompile(EGL_Shader * model, const char * vertex_code,
size_t vertex_size, const char * fragment_code, size_t fragment_size,
bool useDMA);
size_t vertex_size, const char * fragment_code, size_t fragment_size);
void egl_shaderSetUniforms(EGL_Shader * shader, EGL_Uniform * uniforms,
int count);

View File

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

View File

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

View File

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

View File

@@ -1,29 +1,15 @@
#version 300 es
precision highp float;
precision mediump float;
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;
}

View File

@@ -1,28 +1,18 @@
#version 300 es
precision highp float;
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);

View File

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

View File

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

View File

@@ -1,7 +1,5 @@
#version 300 es
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision mediump float;
#define EGL_SCALE_AUTO 0
#define EGL_SCALE_NEAREST 1
@@ -25,17 +23,13 @@ void main()
switch (scaleAlgo)
{
case EGL_SCALE_NEAREST:
{
vec2 ts = vec2(textureSize(sampler1, 0));
color = texelFetch(sampler1, ivec2(uv * ts), 0);
break;
}
case EGL_SCALE_LINEAR:
{
color = texture(sampler1, uv);
break;
}
}
if (cbMode > 0)
@@ -43,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;
}

View File

@@ -1,12 +1,10 @@
#version 300 es
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision mediump float;
in vec2 fragCoord;
out vec4 fragColor;
uniform sampler2D sampler1;
uniform sampler2D texture;
uniform vec3 uConfig;
void main()
@@ -15,7 +13,7 @@ void main()
float vOffset = uConfig.y;
float hOffset = uConfig.z;
vec2 inRes = vec2(textureSize(sampler1, 0));
vec2 inRes = vec2(textureSize(texture, 0));
ivec2 point = ivec2(
(floor((fragCoord * inRes) / pixelSize) * pixelSize) +
pixelSize / 2.0f
@@ -24,5 +22,5 @@ void main()
point.x += int(pixelSize * hOffset);
point.y += int(pixelSize * vOffset);
fragColor = texelFetch(sampler1, point, 0);
fragColor = texelFetch(texture, point, 0);
}

View File

@@ -1,14 +1,12 @@
#version 300 es
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision mediump float;
#define PI 3.141592653589793
in vec2 fragCoord;
out vec4 fragColor;
uniform sampler2D sampler1;
uniform sampler2D texture;
float sinc(float x)
{
return x == 0.0 ? 1.0 : sin(x * PI) / (x * PI);
@@ -26,7 +24,7 @@ float lanczos(vec2 v)
void main()
{
vec2 size = vec2(textureSize(sampler1, 0));
vec2 size = vec2(textureSize(texture, 0));
vec2 pos = fragCoord * size;
vec2 invSize = 1.0 / size;
vec2 uvc = floor(pos) + vec2(0.5, 0.5);
@@ -56,7 +54,7 @@ void main()
vec3 color = vec3(0.0);
for (int i = 0; i < 9; ++i)
color += texture(sampler1, uvs[i] * invSize).rgb * factors[i];
color += texture2D(texture, uvs[i] * invSize).rgb * factors[i];
fragColor = vec4(color, 1.0);
}

View File

@@ -1,14 +1,12 @@
#version 300 es
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision mediump float;
in vec2 fragCoord;
out vec4 fragColor;
uniform sampler2D sampler1;
uniform sampler2D texture;
void main()
{
fragColor = texture(sampler1, fragCoord);
fragColor = texture2D(texture, fragCoord);
}

View File

@@ -1,14 +1,12 @@
#version 300 es
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision mediump float;
#include "compat.h"
in vec2 fragCoord;
out vec4 fragColor;
uniform sampler2D sampler1;
uniform sampler2D texture;
uniform uvec4 uConsts[2];
#define A_GPU 1
@@ -18,7 +16,7 @@ uniform uvec4 uConsts[2];
vec3 imageLoad(ivec2 point)
{
return texelFetch(sampler1, point, 0).rgb;
return texelFetch(texture, point, 0).rgb;
}
AF3 CasLoad(ASU2 p)
@@ -32,7 +30,7 @@ void CasInput(inout AF1 r,inout AF1 g,inout AF1 b) {}
void main()
{
vec2 res = vec2(textureSize(sampler1, 0));
vec2 res = vec2(textureSize(texture, 0));
uvec2 point = uvec2(fragCoord * res);
CasFilter(

View File

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

View File

@@ -1,14 +1,12 @@
#version 300 es
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision mediump float;
#include "compat.h"
in vec2 fragCoord;
out vec4 fragColor;
uniform sampler2D sampler1;
uniform sampler2D texture;
uniform vec2 uOutRes;
uniform uvec4 uConsts[4];
@@ -20,9 +18,20 @@ uniform uvec4 uConsts[4];
#define FSR_EASU_F 1
AF4 FsrEasuRF(AF2 p){return AF4(textureGather(sampler1, p, 0));}
AF4 FsrEasuGF(AF2 p){return AF4(textureGather(sampler1, p, 1));}
AF4 FsrEasuBF(AF2 p){return AF4(textureGather(sampler1, p, 2));}
vec4 _textureGather(sampler2D tex, vec2 uv, int comp)
{
vec2 res = vec2(textureSize(tex, 0));
ivec2 p = ivec2((uv * res) - 0.5f);
vec4 c0 = texelFetchOffset(tex, p, 0, ivec2(0,1));
vec4 c1 = texelFetchOffset(tex, p, 0, ivec2(1,1));
vec4 c2 = texelFetchOffset(tex, p, 0, ivec2(1,0));
vec4 c3 = texelFetchOffset(tex, p, 0, ivec2(0,0));
return vec4(c0[comp], c1[comp], c2[comp],c3[comp]);
}
AF4 FsrEasuRF(AF2 p){return AF4(_textureGather(texture, p, 0));}
AF4 FsrEasuGF(AF2 p){return AF4(_textureGather(texture, p, 1));}
AF4 FsrEasuBF(AF2 p){return AF4(_textureGather(texture, p, 2));}
#include "ffx_fsr1.h"

View File

@@ -1,12 +1,12 @@
#version 300 es
precision highp float;
precision mediump float;
#include "compat.h"
in vec2 fragCoord;
out vec4 fragColor;
uniform sampler2D sampler1;
uniform sampler2D texture;
uniform uvec4 uConsts;
#define A_GPU 1
@@ -15,7 +15,7 @@ uniform uvec4 uConsts;
#include "ffx_a.h"
AF4 FsrRcasLoadF(ASU2 p) { return texelFetch(sampler1, ASU2(p), 0); }
AF4 FsrRcasLoadF(ASU2 p) { return texelFetch(texture, ASU2(p), 0); }
void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
#define FSR_RCAS_F 1
@@ -24,7 +24,7 @@ void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
void main()
{
vec2 inRes = vec2(textureSize(sampler1, 0));
vec2 inRes = vec2(textureSize(texture, 0));
uvec2 point = uvec2(fragCoord * (inRes + 0.5f));
FsrRcasF(fragColor.r, fragColor.g, fragColor.b, point, uConsts);

View File

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

View File

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

View File

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

View File

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

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