From 141782b22bea23b52caae514b6dedadee4631c11 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Thu, 29 Aug 2024 21:57:25 +0000 Subject: [PATCH 01/38] BufferMemo: Add debug logging to buffer memos --- src/BufferMemo.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/BufferMemo.cpp b/src/BufferMemo.cpp index 0eac3c4e47..bd892c484a 100644 --- a/src/BufferMemo.cpp +++ b/src/BufferMemo.cpp @@ -4,6 +4,8 @@ namespace gamescope { + static LogScope memo_log{ "BufferMemo" }; + ///////////////// // CBufferMemo ///////////////// @@ -49,6 +51,8 @@ namespace gamescope void CBufferMemoizer::MemoizeBuffer( wlr_buffer *pBuffer, OwningRc pTexture ) { + memo_log.debugf( "Memoizing new buffer: wlr_buffer %p -> texture: %p", pBuffer, pTexture.get() ); + // Can't hold m_mutBufferMemos while we finalize link from pMemo to buffer // as we can't have wlserver_lock held otherwise we can deadlock when // adding the wl_signal. @@ -70,6 +74,8 @@ namespace gamescope void CBufferMemoizer::UnmemoizeBuffer( wlr_buffer *pBuffer ) { + memo_log.debugf( "Unmemoizing buffer: wlr_buffer %p", pBuffer ); + std::scoped_lock lock{ m_mutBufferMemos }; auto iter = m_BufferMemos.find( pBuffer ); assert( iter != m_BufferMemos.end() ); From d4ca0b979732ad6433fe4d0c4a29af7647777ae7 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Thu, 29 Aug 2024 23:06:42 +0000 Subject: [PATCH 02/38] pipewire: cmpxchng in_buffer + out_buffer to nullptr if they equal to buffer --- src/pipewire.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pipewire.cpp b/src/pipewire.cpp index c9252eb466..52ca0f48c9 100644 --- a/src/pipewire.cpp +++ b/src/pipewire.cpp @@ -52,7 +52,14 @@ static void destroy_buffer(struct pipewire_buffer *buffer) { break; // nothing to do default: assert(false); // unreachable - } + } + + // If out_buffer == buffer, then set it to nullptr. + // We don't care about the result. + struct pipewire_buffer *buffer1 = buffer; + out_buffer.compare_exchange_strong(buffer1, nullptr); + struct pipewire_buffer *buffer2 = buffer; + in_buffer.compare_exchange_strong(buffer2, nullptr); delete buffer; } From b1886b8bb7c92a4aa045a79c5e3a76c8bf83a55e Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 18 Jun 2024 12:34:13 +0300 Subject: [PATCH 03/38] steamcompmgr: Advertise UTF8_STRING in clipboard TARGETS. Software respecting ICCCM will ask for TARGETS first before asking for one of the supported formats. Advertising only STRING will break with any non-ANSI characters, especially since handle_selection_notify() process UTF8 only and gamescope only returns UTF8 strings. This fixes non-ANSI text copied from Firefox / Chrome running on a Steam deck into any Wine app being a garbled mess. --- src/steamcompmgr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index a8d959f78a..8055e880be 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -4891,11 +4891,11 @@ handle_selection_request(xwayland_ctx_t *ctx, XSelectionRequestEvent *ev) { Atom targetList[] = { ctx->atoms.targets, - XA_STRING, + ctx->atoms.utf8StringAtom, }; XChangeProperty(ctx->dpy, ev->requestor, ev->property, XA_ATOM, 32, PropModeReplace, - (unsigned char *)&targetList, 2); + (unsigned char *)&targetList, sizeof(targetList) / sizeof(targetList[0])); response.xselection.property = ev->property; response.xselection.target = ev->target; } From 7bc3c6772570c2c14d797c331f56531b03777a74 Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Thu, 5 Sep 2024 23:45:22 -0100 Subject: [PATCH 04/38] drm: fix wrong degamma/shaper TFs on NV12 For BT.709, the AMD driver-specific color management properties define the inverse OETF for plane degamma and blend predefined TFs and the OETF for plane shaper and CRTC regamma TFs. By correctly setting degamma and shaper TFs to NV12 planes, we can bring degamma and shaper+3D LUT back and remove the workaround made in [1]. [1] 3b69847e15c8 ("drm: Add workaround for NV12 scanout on SteamOS 3.6") --- src/Backends/DRMBackend.cpp | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index f2951a3931..1f87effb3f 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -70,16 +70,6 @@ gamescope::ConVar cv_drm_debug_disable_color_range( "drm_debug_disable_col gamescope::ConVar cv_drm_debug_disable_explicit_sync( "drm_debug_disable_explicit_sync", false, "Force disable explicit sync on the DRM backend." ); gamescope::ConVar cv_drm_debug_disable_in_fence_fd( "drm_debug_disable_in_fence_fd", false, "Force disable IN_FENCE_FD being set to avoid over-synchronization on the DRM backend." ); -// HACK: -// Workaround for AMDGPU bug on SteamOS 3.6 right now. -// Using a Shaper or 3D LUT results in the commit failing, and we really want -// NV12 direct scanout so we can get GFXOFF. -// The compromise here is that colors may look diff to when we composite due to -// lack of 3D LUT, etc. -// TODO: Come back to me on the kernel side after figuring out what broke -// since we moved to the upstream properites and a bunch of work happened. -gamescope::ConVar cv_drm_hack_nv12_color_mgmt_fix( "drm_hack_nv12_color_mgmt_fix", true, "If using NV12, disable explicit degamma + shaper + 3D LUT" ); - namespace gamescope { std::tuple GetKernelVersion() @@ -2457,23 +2447,17 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo // // Doing LINEAR/DEFAULT here introduces banding so... this is the best way. // (sRGB DEGAMMA does NOT work on YUV planes!) - degamma_tf = AMDGPU_TRANSFER_FUNCTION_BT709_OETF; - shaper_tf = AMDGPU_TRANSFER_FUNCTION_BT709_INV_OETF; + degamma_tf = AMDGPU_TRANSFER_FUNCTION_BT709_INV_OETF; + shaper_tf = AMDGPU_TRANSFER_FUNCTION_BT709_OETF; } bool bUseDegamma = !cv_drm_debug_disable_degamma_tf; - if ( bYCbCr && cv_drm_hack_nv12_color_mgmt_fix ) - bUseDegamma = false; - if ( bUseDegamma ) liftoff_layer_set_property( drm->lo_layers[ i ], "AMD_PLANE_DEGAMMA_TF", degamma_tf ); else liftoff_layer_set_property( drm->lo_layers[ i ], "AMD_PLANE_DEGAMMA_TF", 0 ); bool bUseShaperAnd3DLUT = !cv_drm_debug_disable_shaper_and_3dlut; - if ( bYCbCr && cv_drm_hack_nv12_color_mgmt_fix ) - bUseShaperAnd3DLUT = false; - if ( bUseShaperAnd3DLUT ) { liftoff_layer_set_property( drm->lo_layers[ i ], "AMD_PLANE_SHAPER_LUT", drm->pending.shaperlut_id[ ColorSpaceToEOTFIndex( entry.layerState[i].colorspace ) ]->GetBlobValue() ); From dfd84796fdd6caa1bd9c2c9644f5f20abffed9d2 Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Sat, 31 Aug 2024 15:47:39 -0700 Subject: [PATCH 05/38] build: add compiler information to gamescope_version Seems helpful for diagnosing issues that comes up in one build that may not be present in the same build with a different compiler. --- src/meson.build | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/meson.build b/src/meson.build index da811e8362..49f207945c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -161,10 +161,22 @@ foreach feat : required_wlroots_features endif endforeach -gamescope_version = vcs_tag( - command : ['git', 'describe', '--always', '--tags', '--dirty=+'], - input : 'GamescopeVersion.h.in', - output : 'GamescopeVersion.h') +cc = meson.get_compiler('c') +compiler_name = cc.get_id() +compiler_version = cc.version() + +vcs_tag_cmd = ['git', 'describe', '--always', '--tags', '--dirty=+'] +vcs_tag = run_command(vcs_tag_cmd).stdout().strip() +version_tag = vcs_tag + ' (' + compiler_name + ' ' + compiler_version + ')' + +gamescope_version_conf = configuration_data() +gamescope_version_conf.set('VCS_TAG', version_tag) + +gamescope_version = configure_file( + input : 'GamescopeVersion.h.in', + output : 'GamescopeVersion.h', + configuration : gamescope_version_conf +) executable( 'gamescope', From 7bbc2cddbe50e54488440b123a724633dfbaf964 Mon Sep 17 00:00:00 2001 From: Wayne Heaney <42350981+wheaney@users.noreply.github.com> Date: Sat, 7 Sep 2024 08:13:05 -0700 Subject: [PATCH 06/38] Add gamescope-reshade Wayland interface (#1495) * Add gamescope-reshade Wayland interface --- protocol/gamescope-reshade.xml | 68 +++++++++++++ protocol/meson.build | 1 + src/WaylandServer/Reshade.h | 58 +++++++++++ src/WaylandServer/WaylandDecls.h | 3 + src/reshade_effect_manager.cpp | 162 ++++++++++++++++++++++++++++++- src/reshade_effect_manager.hpp | 6 ++ src/steamcompmgr.cpp | 25 ++++- src/steamcompmgr.hpp | 1 + src/wlserver.cpp | 9 ++ 9 files changed, 330 insertions(+), 3 deletions(-) create mode 100644 protocol/gamescope-reshade.xml create mode 100644 src/WaylandServer/Reshade.h diff --git a/protocol/gamescope-reshade.xml b/protocol/gamescope-reshade.xml new file mode 100644 index 0000000000..401b1a0df3 --- /dev/null +++ b/protocol/gamescope-reshade.xml @@ -0,0 +1,68 @@ + + + + + Copyright © 2024 Wayne Heaney + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol allows applications to load and interact with a reshade FX shader in gamescope. + + + + + + + + The effect will be disabled to allow an opportunity to set uniform variables before enabling it. + + + + + + + This event alerts the client when an effect has been enabled. + + + + + + + Enables the effect that was previously loaded by set_effect. + + + + + + Set the value of a uniform variable. Can be called before or after enabling the effect. + + + + + + + + Disables the effect that was previously enabled by enable_effect. + + + + diff --git a/protocol/meson.build b/protocol/meson.build index ddb98885a8..dbce92edce 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -33,6 +33,7 @@ protocols = [ 'gamescope-pipewire.xml', 'gamescope-input-method.xml', 'gamescope-control.xml', + 'gamescope-reshade.xml', 'gamescope-swapchain.xml', 'gamescope-private.xml', diff --git a/src/WaylandServer/Reshade.h b/src/WaylandServer/Reshade.h new file mode 100644 index 0000000000..39e638c164 --- /dev/null +++ b/src/WaylandServer/Reshade.h @@ -0,0 +1,58 @@ +#pragma once + +#include "WaylandProtocol.h" + +#include "gamescope-reshade-protocol.h" +#include "reshade_effect_manager.hpp" + +#include + +namespace gamescope::WaylandServer +{ + class CReshadeManager : public CWaylandResource + { + public: + WL_PROTO_DEFINE( gamescope_reshade, 1 ); + WL_PROTO_DEFAULT_CONSTRUCTOR(); + + void EffectReadyCallback( const char* path ) + { + gamescope_reshade_send_effect_ready(GetResource(), path); + } + + void SetEffect( const char *path ) + { + reshade_effect_manager_set_effect( path, + [this]( const char* callbackPath ) { EffectReadyCallback( callbackPath ); } + ); + } + + void EnableEffect() + { + reshade_effect_manager_enable_effect(); + } + + void SetUniformVariable( const char *key, struct wl_array *value ) + { + uint8_t* data_copy = new uint8_t[value->size]; + std::memcpy(data_copy, value->data, value->size); + reshade_effect_manager_set_uniform_variable( key, data_copy ); + } + + void DisableEffect() + { + reshade_effect_manager_disable_effect(); + } + + }; + + const struct gamescope_reshade_interface CReshadeManager::Implementation = + { + .destroy = WL_PROTO_DESTROY(), + .set_effect = WL_PROTO( CReshadeManager, SetEffect ), + .enable_effect = WL_PROTO( CReshadeManager, EnableEffect ), + .set_uniform_variable = WL_PROTO( CReshadeManager, SetUniformVariable ), + .disable_effect = WL_PROTO( CReshadeManager, DisableEffect ) + }; + +} diff --git a/src/WaylandServer/WaylandDecls.h b/src/WaylandServer/WaylandDecls.h index ff1557c075..e8fd934319 100644 --- a/src/WaylandServer/WaylandDecls.h +++ b/src/WaylandServer/WaylandDecls.h @@ -12,4 +12,7 @@ namespace gamescope::WaylandServer class CLinuxDrmSyncobjTimeline; using CLinuxDrmSyncobj = CWaylandProtocol; + class CReshadeManager; + using CReshade = CWaylandProtocol; + } diff --git a/src/reshade_effect_manager.cpp b/src/reshade_effect_manager.cpp index fa37a5cc4e..87f886b140 100644 --- a/src/reshade_effect_manager.cpp +++ b/src/reshade_effect_manager.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "reshade_effect_manager.hpp" #include "log.hpp" @@ -9,16 +10,29 @@ #include "effect_parser.hpp" #include "effect_codegen.hpp" #include "effect_preprocessor.hpp" +#include "gamescope-reshade-protocol.h" #include "reshade_api_format.hpp" +#include "convar.h" #include #define STB_IMAGE_RESIZE_IMPLEMENTATION #include +#include #include #include #include +#include + +// This is based on wl_array_for_each from `wayland-util.h` in the Wayland client library. +#define uint8_array_for_each(pos, data, size) \ + for (pos = (decltype(pos))data; (const char *)pos < ((const char *)data + size); (pos)++) + +static char* g_reshadeEffectPath = nullptr; +static std::function g_effectReadyCallback = nullptr; +static auto g_runtimeUniforms = std::unordered_map(); +static std::mutex g_runtimeUniformsMutex; const char *homedir; @@ -172,6 +186,21 @@ class DepthUniform : public ReshadeUniform virtual ~DepthUniform(); }; +class RuntimeUniform : public ReshadeUniform +{ +public: + RuntimeUniform(reshadefx::uniform_info uniformInfo); + void virtual update(void* mappedBuffer) override; + virtual ~RuntimeUniform(); + +private: + uint32_t offset; + uint32_t size; + std::string name; + reshadefx::type type; + std::variant, std::vector, std::vector> defaultValue; +}; + class DataUniform : public ReshadeUniform { public: @@ -470,6 +499,100 @@ DepthUniform::~DepthUniform() { } +////////////////////////////////////////////////////////////////////////////////////////////////////////// +RuntimeUniform::RuntimeUniform(reshadefx::uniform_info uniformInfo) + : ReshadeUniform(uniformInfo) +{ + offset = uniformInfo.offset; + size = uniformInfo.size; + type = uniformInfo.type; + name = std::find_if(uniformInfo.annotations.begin(), uniformInfo.annotations.end(), [](const auto& a) { return a.name == "source"; })->value.string_data; + + if (auto defaultValueAnnotation = + std::find_if(uniformInfo.annotations.begin(), uniformInfo.annotations.end(), [](const auto& a) { return a.name == "defaultValue"; }); + defaultValueAnnotation != uniformInfo.annotations.end()) + { + reshadefx::constant value = defaultValueAnnotation->value; + if (type.is_floating_point()) { + defaultValue = std::vector(value.as_float, value.as_float + type.components()); + reshade_log.debugf("Found float* runtime uniform %s of size %d\n", name.c_str(), type.components()); + } else if (type.is_boolean()) { + defaultValue = std::vector(value.as_uint, value.as_uint + type.components()); + reshade_log.debugf("Found bool* runtime uniform %s of size %d\n", name.c_str(), type.components()); + } else if (type.is_numeric()) { + if (type.is_signed()) { + defaultValue = std::vector(value.as_int, value.as_int + type.components()); + reshade_log.debugf("Found int32_t* runtime uniform %s of size %d\n", name.c_str(), type.components()); + } else { + defaultValue = std::vector(value.as_uint, value.as_uint + type.components()); + reshade_log.debugf("Found uint32_t* runtime uniform %s of size %d\n", name.c_str(), type.components()); + } + } else { + reshade_log.errorf("Tried to create a runtime uniform variable of an unsupported type\n"); + } + } +} +void RuntimeUniform::update(void* mappedBuffer) +{ + std::variant, std::vector, std::vector> value; + uint8_t* wl_value = nullptr; + + std::lock_guard lock(g_runtimeUniformsMutex); + auto it = g_runtimeUniforms.find(name); + if (it != g_runtimeUniforms.end()) { + wl_value = it->second; + } + + if (wl_value) { + if (type.is_floating_point()) { + value = std::vector(); + float *float_value = nullptr; + uint8_array_for_each(float_value, wl_value, type.components() * sizeof(float)) { + std::get>(value).push_back(*float_value); + } + } else if (type.is_boolean()) { + // convert to a uint32_t vector, that's how the reshade uniform code understands booleans + value = std::vector(); + uint8_t *bool_value = nullptr; + uint8_array_for_each(bool_value, wl_value, type.components() * sizeof(uint8_t)) { + std::get>(value).push_back(*bool_value); + } + } else if (type.is_numeric()) { + if (type.is_signed()) { + value = std::vector(); + int32_t *int_value = nullptr; + uint8_array_for_each(int_value, wl_value, type.components() * sizeof(int32_t)) { + std::get>(value).push_back(*int_value); + } + } else { + value = std::vector(); + uint32_t *uint_value = nullptr; + uint8_array_for_each(uint_value, wl_value, type.components() * sizeof(uint32_t)) { + std::get>(value).push_back(*uint_value); + } + } + } + } + + if (std::holds_alternative(value)) { + value = defaultValue; + } + + if (std::holds_alternative>(value)) { + std::vector& vec = std::get>(value); + std::memcpy((uint8_t*) mappedBuffer + offset, vec.data(), vec.size() * sizeof(float)); + } else if (std::holds_alternative>(value)) { + std::vector& vec = std::get>(value); + std::memcpy((uint8_t*) mappedBuffer + offset, vec.data(), vec.size() * sizeof(int32_t)); + } else if (std::holds_alternative>(value)) { + std::vector& vec = std::get>(value); + std::memcpy((uint8_t*) mappedBuffer + offset, vec.data(), vec.size() * sizeof(uint32_t)); + } +} +RuntimeUniform::~RuntimeUniform() +{ +} + ////////////////////////////////////////////////////////////////////////////////////////////////////////// DataUniform::DataUniform(reshadefx::uniform_info uniformInfo) : ReshadeUniform(uniformInfo) @@ -541,9 +664,9 @@ static std::vector> createReshadeUniforms(const { uniforms.push_back(std::make_shared(uniform)); } - else + else if (!source.empty()) { - reshade_log.errorf("Unknown uniform source: %s", source.c_str()); + uniforms.push_back(std::make_shared(uniform)); } } } @@ -1534,6 +1657,11 @@ bool ReshadeEffectPipeline::init(CVulkanDevice *device, const ReshadeEffectKey & void ReshadeEffectPipeline::update() { + if (g_effectReadyCallback && g_reshadeEffectPath) { + g_effectReadyCallback(g_reshadeEffectPath); + g_effectReadyCallback = nullptr; + } + for (auto& uniform : m_uniforms) uniform->update(m_mappedPtr); } @@ -1793,3 +1921,33 @@ ReshadeEffectPipeline* ReshadeEffectManager::pipeline(const ReshadeEffectKey &ke ReshadeEffectManager g_reshadeManager; +void reshade_effect_manager_set_uniform_variable(const char *key, uint8_t* value) +{ + std::lock_guard lock(g_runtimeUniformsMutex); + + auto it = g_runtimeUniforms.find(key); + if (it != g_runtimeUniforms.end()) { + delete[] it->second; + } + + g_runtimeUniforms[std::string(key)] = value; + force_repaint(); +} + +void reshade_effect_manager_set_effect(const char *path, std::function callback) +{ + g_runtimeUniforms.clear(); + if (g_reshadeEffectPath) free(g_reshadeEffectPath); + g_reshadeEffectPath = strdup(path); + g_effectReadyCallback = callback; +} + +void reshade_effect_manager_enable_effect() +{ + if (g_reshadeEffectPath) gamescope_set_reshade_effect(g_reshadeEffectPath); +} + +void reshade_effect_manager_disable_effect() +{ + gamescope_set_reshade_effect(nullptr); +} \ No newline at end of file diff --git a/src/reshade_effect_manager.hpp b/src/reshade_effect_manager.hpp index d2bf967525..55f280285d 100644 --- a/src/reshade_effect_manager.hpp +++ b/src/reshade_effect_manager.hpp @@ -94,3 +94,9 @@ class ReshadeEffectManager }; extern ReshadeEffectManager g_reshadeManager; + + +void reshade_effect_manager_set_uniform_variable(const char *key, uint8_t* value); +void reshade_effect_manager_set_effect(const char *path, std::function callback); +void reshade_effect_manager_enable_effect(); +void reshade_effect_manager_disable_effect(); \ No newline at end of file diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 8055e880be..df12794a9b 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -2946,6 +2946,23 @@ std::string get_string_prop( xwayland_ctx_t *ctx, Window win, Atom prop ) return value; } +void set_string_prop( xwayland_ctx_t *ctx, Window win, Atom prop, const std::string &value ) +{ + if ( value.empty() ) + XDeleteProperty( ctx->dpy, win, prop ); + else { + XTextProperty text_property = + { + .value = ( unsigned char * )value.c_str(), + .encoding = ctx->atoms.utf8StringAtom, + .format = 8, + .nitems = strlen( value.c_str() ) + }; + XSetTextProperty( ctx->dpy, ctx->root, &text_property, prop); + } + XFlush( ctx->dpy ); +} + static bool win_has_game_id( steamcompmgr_win_t *w ) { @@ -4867,6 +4884,12 @@ void gamescope_set_selection(std::string contents, GamescopeSelection eSelection } } +void gamescope_set_reshade_effect(std::string effect_path) +{ + gamescope_xwayland_server_t *server = wlserver_get_xwayland_server(0); + set_string_prop(server->ctx.get(), server->ctx->ourWindow, server->ctx->atoms.gamescopeReshadeEffect, effect_path); +} + static void handle_selection_request(xwayland_ctx_t *ctx, XSelectionRequestEvent *ev) { @@ -7821,7 +7844,7 @@ steamcompmgr_main(int argc, char **argv) // If we are running behind, allow tearing. - const bool bForceRepaint = g_bForceRepaint.exchange(false); + const bool bForceRepaint = vblank && g_bForceRepaint.exchange(false); const bool bForceSyncFlip = bForceRepaint || is_fading_out(); // If we are compositing, always force sync flips because we currently wait diff --git a/src/steamcompmgr.hpp b/src/steamcompmgr.hpp index 91b911b63f..8c76db90a8 100644 --- a/src/steamcompmgr.hpp +++ b/src/steamcompmgr.hpp @@ -147,6 +147,7 @@ extern pid_t focusWindow_pid; void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_server); void gamescope_set_selection(std::string contents, GamescopeSelection eSelection); +void gamescope_set_reshade_effect(std::string effect_path); MouseCursor *steamcompmgr_get_current_cursor(); MouseCursor *steamcompmgr_get_server_cursor(uint32_t serverId); diff --git a/src/wlserver.cpp b/src/wlserver.cpp index ccbd512485..5178c7e4d4 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -19,6 +19,7 @@ #include "WaylandServer/WaylandResource.h" #include "WaylandServer/WaylandProtocol.h" #include "WaylandServer/LinuxDrmSyncobj.h" +#include "WaylandServer/Reshade.h" #include "wlr_begin.hpp" #include @@ -57,6 +58,7 @@ #include "steamcompmgr.hpp" #include "log.hpp" #include "ime.hpp" +#include "reshade_effect_manager.hpp" #include "xwayland_ctx.hpp" #include "refresh_rate.h" #include "InputEmulation.h" @@ -1178,6 +1180,11 @@ static void create_explicit_sync() new gamescope::WaylandServer::CLinuxDrmSyncobj( wlserver.display ); } +static void create_reshade() +{ + new gamescope::WaylandServer::CReshade( wlserver.display ); +} + //////////////////////// // presentation-time @@ -1737,6 +1744,8 @@ bool wlserver_init( void ) { create_ime_manager( &wlserver ); + create_reshade(); + create_gamescope_xwayland(); create_gamescope_swapchain_factory_v2(); From 6eaee770db73e031ff6011e7286b29c0408647f5 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Sat, 7 Sep 2024 23:26:29 +0100 Subject: [PATCH 07/38] backend, rendervulkan: Use DRM format for output format on backend Fixes running on my Thinkpad X13s --- src/Backends/DRMBackend.cpp | 6 +++--- src/Backends/HeadlessBackend.cpp | 6 +++--- src/Backends/OpenVRBackend.cpp | 6 +++--- src/Backends/SDLBackend.cpp | 8 +++---- src/Backends/WaylandBackend.cpp | 36 ++++++++++++++++++++----------- src/backend.h | 2 +- src/rendervulkan.cpp | 37 ++++++++++++++++---------------- src/rendervulkan.hpp | 4 ++-- 8 files changed, 58 insertions(+), 47 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 1f87effb3f..c62635f190 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -3174,10 +3174,10 @@ namespace gamescope // thus: newLayout is ignored. return VK_IMAGE_LAYOUT_GENERAL; } - virtual void GetPreferredOutputFormat( VkFormat *pPrimaryPlaneFormat, VkFormat *pOverlayPlaneFormat ) const override + virtual void GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const override { - *pPrimaryPlaneFormat = DRMFormatToVulkan( g_nDRMFormat, false ); - *pOverlayPlaneFormat = DRMFormatToVulkan( g_nDRMFormatOverlay, false ); + *pPrimaryPlaneFormat = g_nDRMFormat; + *pOverlayPlaneFormat = g_nDRMFormatOverlay; } virtual bool ValidPhysicalDevice( VkPhysicalDevice pVkPhysicalDevice ) const override { diff --git a/src/Backends/HeadlessBackend.cpp b/src/Backends/HeadlessBackend.cpp index ddf8c10529..49987f69fc 100644 --- a/src/Backends/HeadlessBackend.cpp +++ b/src/Backends/HeadlessBackend.cpp @@ -147,10 +147,10 @@ namespace gamescope { return VK_IMAGE_LAYOUT_GENERAL; } - virtual void GetPreferredOutputFormat( VkFormat *pPrimaryPlaneFormat, VkFormat *pOverlayPlaneFormat ) const override + virtual void GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const override { - *pPrimaryPlaneFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32; - *pOverlayPlaneFormat = VK_FORMAT_B8G8R8A8_UNORM; + *pPrimaryPlaneFormat = VulkanFormatToDRM( VK_FORMAT_A2B10G10R10_UNORM_PACK32 ); + *pOverlayPlaneFormat = VulkanFormatToDRM( VK_FORMAT_B8G8R8A8_UNORM ); } virtual bool ValidPhysicalDevice( VkPhysicalDevice pVkPhysicalDevice ) const override { diff --git a/src/Backends/OpenVRBackend.cpp b/src/Backends/OpenVRBackend.cpp index c4de4aca83..f03a65d2b0 100644 --- a/src/Backends/OpenVRBackend.cpp +++ b/src/Backends/OpenVRBackend.cpp @@ -532,10 +532,10 @@ namespace gamescope { return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; } - virtual void GetPreferredOutputFormat( VkFormat *pPrimaryPlaneFormat, VkFormat *pOverlayPlaneFormat ) const override + virtual void GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const override { - *pPrimaryPlaneFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32; - *pOverlayPlaneFormat = VK_FORMAT_B8G8R8A8_UNORM; + *pPrimaryPlaneFormat = VulkanFormatToDRM( VK_FORMAT_A2B10G10R10_UNORM_PACK32 ); + *pOverlayPlaneFormat = VulkanFormatToDRM( VK_FORMAT_B8G8R8A8_UNORM ); } virtual bool ValidPhysicalDevice( VkPhysicalDevice pVkPhysicalDevice ) const override { diff --git a/src/Backends/SDLBackend.cpp b/src/Backends/SDLBackend.cpp index c2821a1235..8b36464202 100644 --- a/src/Backends/SDLBackend.cpp +++ b/src/Backends/SDLBackend.cpp @@ -122,7 +122,7 @@ namespace gamescope virtual std::span GetInstanceExtensions() const override; virtual std::span GetDeviceExtensions( VkPhysicalDevice pVkPhysicalDevice ) const override; virtual VkImageLayout GetPresentLayout() const override; - virtual void GetPreferredOutputFormat( VkFormat *pPrimaryPlaneFormat, VkFormat *pOverlayPlaneFormat ) const override; + virtual void GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const override; virtual bool ValidPhysicalDevice( VkPhysicalDevice pVkPhysicalDevice ) const override; virtual int Present( const FrameInfo_t *pFrameInfo, bool bAsync ) override; @@ -351,10 +351,10 @@ namespace gamescope return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; } - void CSDLBackend::GetPreferredOutputFormat( VkFormat *pPrimaryPlaneFormat, VkFormat *pOverlayPlaneFormat ) const + void CSDLBackend::GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const { - *pPrimaryPlaneFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32; - *pOverlayPlaneFormat = VK_FORMAT_B8G8R8A8_UNORM; + *pPrimaryPlaneFormat = VulkanFormatToDRM( VK_FORMAT_A2B10G10R10_UNORM_PACK32 ); + *pOverlayPlaneFormat = VulkanFormatToDRM( VK_FORMAT_B8G8R8A8_UNORM ); } bool CSDLBackend::ValidPhysicalDevice( VkPhysicalDevice pVkPhysicalDevice ) const diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp index 6498f3b491..bb8bd6f642 100644 --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -504,7 +504,7 @@ namespace gamescope virtual std::span GetInstanceExtensions() const override; virtual std::span GetDeviceExtensions( VkPhysicalDevice pVkPhysicalDevice ) const override; virtual VkImageLayout GetPresentLayout() const override; - virtual void GetPreferredOutputFormat( VkFormat *pPrimaryPlaneFormat, VkFormat *pOverlayPlaneFormat ) const override; + virtual void GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const override; virtual bool ValidPhysicalDevice( VkPhysicalDevice pVkPhysicalDevice ) const override; virtual int Present( const FrameInfo_t *pFrameInfo, bool bAsync ) override; @@ -1542,23 +1542,33 @@ namespace gamescope return VK_IMAGE_LAYOUT_GENERAL; } - void CWaylandBackend::GetPreferredOutputFormat( VkFormat *pPrimaryPlaneFormat, VkFormat *pOverlayPlaneFormat ) const + void CWaylandBackend::GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const { - VkFormat u8BitFormat = VK_FORMAT_UNDEFINED; - if ( SupportsFormat( DRM_FORMAT_ARGB8888 ) ) - u8BitFormat = VK_FORMAT_B8G8R8A8_UNORM; - else if ( SupportsFormat( DRM_FORMAT_ABGR8888 ) ) - u8BitFormat = VK_FORMAT_R8G8B8A8_UNORM; + // Prefer opaque for composition on the Wayland backend. - VkFormat u10BitFormat = VK_FORMAT_UNDEFINED; - if ( SupportsFormat( DRM_FORMAT_ABGR2101010 ) ) - u10BitFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32; + uint32_t u8BitFormat = DRM_FORMAT_INVALID; + if ( SupportsFormat( DRM_FORMAT_XRGB8888 ) ) + u8BitFormat = DRM_FORMAT_XRGB8888; + else if ( SupportsFormat( DRM_FORMAT_XBGR8888 ) ) + u8BitFormat = DRM_FORMAT_XBGR8888; + else if ( SupportsFormat( DRM_FORMAT_ARGB8888 ) ) + u8BitFormat = DRM_FORMAT_ARGB8888; + else if ( SupportsFormat( DRM_FORMAT_ABGR8888 ) ) + u8BitFormat = DRM_FORMAT_ABGR8888; + + uint32_t u10BitFormat = DRM_FORMAT_INVALID; + if ( SupportsFormat( DRM_FORMAT_XBGR2101010 ) ) + u10BitFormat = DRM_FORMAT_XBGR2101010; + else if ( SupportsFormat( DRM_FORMAT_XRGB2101010 ) ) + u10BitFormat = DRM_FORMAT_XRGB2101010; + else if ( SupportsFormat( DRM_FORMAT_ABGR2101010 ) ) + u10BitFormat = DRM_FORMAT_ABGR2101010; else if ( SupportsFormat( DRM_FORMAT_ARGB2101010 ) ) - u10BitFormat = VK_FORMAT_A2R10G10B10_UNORM_PACK32; + u10BitFormat = DRM_FORMAT_ARGB2101010; - assert( u8BitFormat != VK_FORMAT_UNDEFINED ); + assert( u8BitFormat != DRM_FORMAT_INVALID ); - *pPrimaryPlaneFormat = u10BitFormat != VK_FORMAT_UNDEFINED ? u10BitFormat : u8BitFormat; + *pPrimaryPlaneFormat = u10BitFormat != DRM_FORMAT_INVALID ? u10BitFormat : u8BitFormat; *pOverlayPlaneFormat = u8BitFormat; } diff --git a/src/backend.h b/src/backend.h index 7d9fb465dd..341e9d783b 100644 --- a/src/backend.h +++ b/src/backend.h @@ -178,7 +178,7 @@ namespace gamescope virtual std::span GetInstanceExtensions() const = 0; virtual std::span GetDeviceExtensions( VkPhysicalDevice pVkPhysicalDevice ) const = 0; virtual VkImageLayout GetPresentLayout() const = 0; - virtual void GetPreferredOutputFormat( VkFormat *pPrimaryPlaneFormat, VkFormat *pOverlayPlaneFormat ) const = 0; + virtual void GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const = 0; virtual bool ValidPhysicalDevice( VkPhysicalDevice pVkPhysicalDevice ) const = 0; virtual int Present( const FrameInfo_t *pFrameInfo, bool bAsync ) = 0; diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp index 63d34b3ca9..97e6fbe226 100644 --- a/src/rendervulkan.cpp +++ b/src/rendervulkan.cpp @@ -3112,12 +3112,13 @@ bool vulkan_make_swapchain( VulkanOutput_t *pOutput ) if ( surfaceFormat == formatCount ) return false; - pOutput->outputFormat = pOutput->surfaceFormats[ surfaceFormat ].format; + VkFormat eVkFormat = pOutput->surfaceFormats[ surfaceFormat ].format; + pOutput->uOutputFormat = VulkanFormatToDRM( pOutput->surfaceFormats[ surfaceFormat ].format ); VkFormat formats[2] = { - ToSrgbVulkanFormat( pOutput->outputFormat ), - ToLinearVulkanFormat( pOutput->outputFormat ), + ToSrgbVulkanFormat( eVkFormat ), + ToLinearVulkanFormat( eVkFormat ), }; VkImageFormatListCreateInfo usageListInfo = { @@ -3126,7 +3127,7 @@ bool vulkan_make_swapchain( VulkanOutput_t *pOutput ) .pViewFormats = formats, }; - vk_log.infof("Creating Gamescope nested swapchain with format %u and colorspace %u", pOutput->outputFormat, pOutput->surfaceFormats[surfaceFormat].colorSpace); + vk_log.infof("Creating Gamescope nested swapchain with format %u and colorspace %u", eVkFormat, pOutput->surfaceFormats[surfaceFormat].colorSpace); VkSwapchainCreateInfoKHR createInfo = { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, @@ -3134,7 +3135,7 @@ bool vulkan_make_swapchain( VulkanOutput_t *pOutput ) .flags = formats[0] != formats[1] ? VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR : (VkSwapchainCreateFlagBitsKHR )0, .surface = pOutput->surface, .minImageCount = imageCount, - .imageFormat = pOutput->outputFormat, + .imageFormat = eVkFormat, .imageColorSpace = pOutput->surfaceFormats[surfaceFormat].colorSpace, .imageExtent = { .width = g_nOutputWidth, @@ -3163,7 +3164,7 @@ bool vulkan_make_swapchain( VulkanOutput_t *pOutput ) { pOutput->outputImages[i] = new CVulkanTexture(); - if ( !pOutput->outputImages[i]->BInitFromSwapchain(swapchainImages[i], g_nOutputWidth, g_nOutputHeight, pOutput->outputFormat)) + if ( !pOutput->outputImages[i]->BInitFromSwapchain(swapchainImages[i], g_nOutputWidth, g_nOutputHeight, eVkFormat)) return false; } @@ -3220,10 +3221,10 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) pOutput->outputImagesPartialOverlay[1] = nullptr; pOutput->outputImagesPartialOverlay[2] = nullptr; - VkFormat format = pOutput->outputFormat; + uint32_t uDRMFormat = pOutput->uOutputFormat; pOutput->outputImages[0] = new CVulkanTexture(); - bool bSuccess = pOutput->outputImages[0]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, VulkanFormatToDRM(format, false), outputImageflags ); + bool bSuccess = pOutput->outputImages[0]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uDRMFormat, outputImageflags ); if ( bSuccess != true ) { vk_log.errorf( "failed to allocate buffer for KMS" ); @@ -3231,7 +3232,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) } pOutput->outputImages[1] = new CVulkanTexture(); - bSuccess = pOutput->outputImages[1]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, VulkanFormatToDRM(format, false), outputImageflags ); + bSuccess = pOutput->outputImages[1]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uDRMFormat, outputImageflags ); if ( bSuccess != true ) { vk_log.errorf( "failed to allocate buffer for KMS" ); @@ -3239,7 +3240,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) } pOutput->outputImages[2] = new CVulkanTexture(); - bSuccess = pOutput->outputImages[2]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, VulkanFormatToDRM(format, false), outputImageflags ); + bSuccess = pOutput->outputImages[2]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uDRMFormat, outputImageflags ); if ( bSuccess != true ) { vk_log.errorf( "failed to allocate buffer for KMS" ); @@ -3249,12 +3250,12 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) // Oh no. pOutput->temporaryHackyBlankImage = vulkan_create_debug_blank_texture(); - if ( pOutput->outputFormatOverlay != VK_FORMAT_UNDEFINED && !kDisablePartialComposition ) + if ( pOutput->uOutputFormatOverlay != VK_FORMAT_UNDEFINED && !kDisablePartialComposition ) { - VkFormat partialFormat = pOutput->outputFormatOverlay; + uint32_t uPartialDRMFormat = pOutput->uOutputFormatOverlay; pOutput->outputImagesPartialOverlay[0] = new CVulkanTexture(); - bool bSuccess = pOutput->outputImagesPartialOverlay[0]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, VulkanFormatToDRM(partialFormat), outputImageflags, nullptr, 0, 0, pOutput->outputImages[0].get() ); + bool bSuccess = pOutput->outputImagesPartialOverlay[0]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[0].get() ); if ( bSuccess != true ) { vk_log.errorf( "failed to allocate buffer for KMS" ); @@ -3262,7 +3263,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) } pOutput->outputImagesPartialOverlay[1] = new CVulkanTexture(); - bSuccess = pOutput->outputImagesPartialOverlay[1]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, VulkanFormatToDRM(partialFormat), outputImageflags, nullptr, 0, 0, pOutput->outputImages[1].get() ); + bSuccess = pOutput->outputImagesPartialOverlay[1]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[1].get() ); if ( bSuccess != true ) { vk_log.errorf( "failed to allocate buffer for KMS" ); @@ -3270,7 +3271,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) } pOutput->outputImagesPartialOverlay[2] = new CVulkanTexture(); - bSuccess = pOutput->outputImagesPartialOverlay[2]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, VulkanFormatToDRM(partialFormat), outputImageflags, nullptr, 0, 0, pOutput->outputImages[2].get() ); + bSuccess = pOutput->outputImagesPartialOverlay[2]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[2].get() ); if ( bSuccess != true ) { vk_log.errorf( "failed to allocate buffer for KMS" ); @@ -3356,15 +3357,15 @@ bool vulkan_make_output() } else { - GetBackend()->GetPreferredOutputFormat( &pOutput->outputFormat, &pOutput->outputFormatOverlay ); + GetBackend()->GetPreferredOutputFormat( &pOutput->uOutputFormat, &pOutput->uOutputFormatOverlay ); - if ( pOutput->outputFormat == VK_FORMAT_UNDEFINED ) + if ( pOutput->uOutputFormat == DRM_FORMAT_INVALID ) { vk_log.errorf( "failed to find Vulkan format suitable for KMS" ); return false; } - if ( pOutput->outputFormatOverlay == VK_FORMAT_UNDEFINED ) + if ( pOutput->uOutputFormatOverlay == DRM_FORMAT_INVALID ) { vk_log.errorf( "failed to find Vulkan format suitable for KMS partial overlays" ); return false; diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp index f5fef302b5..b967e849fe 100644 --- a/src/rendervulkan.hpp +++ b/src/rendervulkan.hpp @@ -508,8 +508,8 @@ struct VulkanOutput_t std::vector> outputImagesPartialOverlay; gamescope::OwningRc temporaryHackyBlankImage; - VkFormat outputFormat = VK_FORMAT_UNDEFINED; - VkFormat outputFormatOverlay = VK_FORMAT_UNDEFINED; + uint32_t uOutputFormat = DRM_FORMAT_INVALID; + uint32_t uOutputFormatOverlay = DRM_FORMAT_INVALID; std::array, 2> pScreenshotImages; From 1bd5bed42cf9139b5dd6ecc457e3349a13e8ddc3 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Tue, 10 Sep 2024 04:43:24 +0100 Subject: [PATCH 08/38] pipewire: Fix truncation of SPA_FORMAT_VIDEO_gamescope_focus_appid No longer needed with pipewire as of a63aa6329bf3b9a7c6a13a511617472980e9e238 --- src/pipewire.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipewire.cpp b/src/pipewire.cpp index 52ca0f48c9..2f06f7cf2c 100644 --- a/src/pipewire.cpp +++ b/src/pipewire.cpp @@ -105,7 +105,7 @@ static void build_format_params(struct spa_pod_builder *builder, spa_video_forma SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&size), SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&framerate), SPA_FORMAT_VIDEO_requested_size, SPA_POD_CHOICE_RANGE_Rectangle( &min_requested_size, &min_requested_size, &max_requested_size ), - SPA_FORMAT_VIDEO_gamescope_focus_appid, SPA_POD_CHOICE_RANGE_Long( 0ll, 0ll, INT32_MAX ), + SPA_FORMAT_VIDEO_gamescope_focus_appid, SPA_POD_CHOICE_RANGE_Long( 0ll, INT64_MIN, INT64_MAX ), 0); if (format == SPA_VIDEO_FORMAT_NV12) { spa_pod_builder_add(builder, @@ -134,7 +134,7 @@ static void build_format_params(struct spa_pod_builder *builder, spa_video_forma SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&size), SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&framerate), SPA_FORMAT_VIDEO_requested_size, SPA_POD_CHOICE_RANGE_Rectangle( &min_requested_size, &min_requested_size, &max_requested_size ), - SPA_FORMAT_VIDEO_gamescope_focus_appid, SPA_POD_CHOICE_RANGE_Long( 0ll, 0ll, INT32_MAX ), + SPA_FORMAT_VIDEO_gamescope_focus_appid, SPA_POD_CHOICE_RANGE_Long( 0ll, INT64_MIN, INT64_MAX ), 0); if (format == SPA_VIDEO_FORMAT_NV12) { spa_pod_builder_add(builder, From 7d4ff16b179312b8ab2298a218e7b7f8b24013b8 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Tue, 10 Sep 2024 04:43:41 +0100 Subject: [PATCH 09/38] steamcompmgr: Add logging for when pipewire focus appid changes --- src/steamcompmgr.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index df12794a9b..c5556fbb23 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -2151,8 +2151,17 @@ static void paint_pipewire() focus_t *pFocus = nullptr; if ( ulFocusAppId ) { + static uint64_t s_ulLastFocusAppId = 0; + + bool bAppIdChange = ulFocusAppId != s_ulLastFocusAppId; + if ( bAppIdChange ) + { + xwm_log.infof( "Exposing appid %lu (%u 32-bit) focus-wise on pipewire stream.", ulFocusAppId, uint32_t( ulFocusAppId ) ); + s_ulLastFocusAppId = ulFocusAppId; + } + static focus_t s_PipewireFocus{}; - if ( s_PipewireFocus.IsDirty() ) + if ( s_PipewireFocus.IsDirty() || bAppIdChange ) { std::vector vecPossibleFocusWindows = GetGlobalPossibleFocusWindows(); From 6f516c3022830452d85abe39d7e163d47e6e652e Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Tue, 10 Sep 2024 23:47:15 +0100 Subject: [PATCH 10/38] layer: Send VK_ERROR_OUT_OF_DATE when window size changes on X11 As currentExtent will have changed... --- layer/VkLayer_FROG_gamescope_wsi.cpp | 24 +++++++- layer/vulkan_operators.hpp | 90 ++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 layer/vulkan_operators.hpp diff --git a/layer/VkLayer_FROG_gamescope_wsi.cpp b/layer/VkLayer_FROG_gamescope_wsi.cpp index 2baea4f0ca..8da45deafe 100644 --- a/layer/VkLayer_FROG_gamescope_wsi.cpp +++ b/layer/VkLayer_FROG_gamescope_wsi.cpp @@ -3,6 +3,7 @@ #define VK_USE_PLATFORM_XLIB_KHR #include "vkroots.h" #include "xcb_helpers.hpp" +#include "vulkan_operators.hpp" #include "gamescope-swapchain-client-protocol.h" #include "../src/color_helpers.h" #include "../src/layer_defines.h" @@ -209,7 +210,11 @@ namespace GamescopeWSILayer { GamescopeLayerClient::Flags flags; bool hdrOutput; + // Cached for comparison. + std::optional cachedWindowRect; + bool isWayland() const { + // Is native Wayland? return connection == nullptr; } @@ -222,7 +227,7 @@ namespace GamescopeWSILayer { return hdrOutput && hdrAllowed; } - bool canBypassXWayland() const { + bool canBypassXWayland() { if (isWayland()) return true; @@ -234,6 +239,8 @@ namespace GamescopeWSILayer { return false; } + cachedWindowRect = *rect; + auto toplevelRect = xcb::getWindowRect(connection, *toplevelWindow); if (!toplevelRect) { fprintf(stderr, "[Gamescope WSI] canBypassXWayland: failed to get window info for window 0x%x.\n", window); @@ -294,6 +301,7 @@ namespace GamescopeWSILayer { bool isBypassingXWayland; bool forceFifo; VkPresentModeKHR presentMode; + VkExtent2D extent; uint32_t serverId = 0; bool retired = false; @@ -1046,6 +1054,7 @@ namespace GamescopeWSILayer { .isBypassingXWayland = canBypass, .forceFifo = gamescopeIsForcingFifo(), // Were we forcing fifo when this swapchain was made? .presentMode = pCreateInfo->presentMode, // The new present mode. + .extent = pCreateInfo->imageExtent, .serverId = serverId, }); gamescopeSwapchain->pastPresentTimings.reserve(MaxPastPresentationTimes); @@ -1238,6 +1247,19 @@ namespace GamescopeWSILayer { const bool canBypass = gamescopeSurface->canBypassXWayland(); if (canBypass != gamescopeSwapchain->isBypassingXWayland) UpdateSwapchainResult(canBypass ? VK_SUBOPTIMAL_KHR : VK_ERROR_OUT_OF_DATE_KHR); + + // Emulate behaviour when currentExtent changes in X11 swapchain. + if (!gamescopeSurface->isWayland()) { + // gamescopeSurface->cachedWindowSize is set by canBypassXWayland. + // TODO: Rename that to be some update cached vars thing, then read back canBypassXWayland. + if (gamescopeSurface->cachedWindowRect) { + const bool windowSizeChanged = gamescopeSurface->cachedWindowRect->extent != gamescopeSwapchain->extent; + if (windowSizeChanged) + UpdateSwapchainResult(VK_ERROR_OUT_OF_DATE_KHR); + } else { + fprintf(stderr, "[Gamescope WSI] QueuePresentKHR: Failed to get cached window size for swapchain %u\n", i); + } + } } } diff --git a/layer/vulkan_operators.hpp b/layer/vulkan_operators.hpp new file mode 100644 index 0000000000..ab313dd6c8 --- /dev/null +++ b/layer/vulkan_operators.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include + +inline bool operator == ( + const VkImageSubresourceRange& a, + const VkImageSubresourceRange& b) { + return a.aspectMask == b.aspectMask + && a.baseMipLevel == b.baseMipLevel + && a.levelCount == b.levelCount + && a.baseArrayLayer == b.baseArrayLayer + && a.layerCount == b.layerCount; +} + + +inline bool operator != ( + const VkImageSubresourceRange& a, + const VkImageSubresourceRange& b) { + return !operator == (a, b); +} + + +inline bool operator == ( + const VkImageSubresourceLayers& a, + const VkImageSubresourceLayers& b) { + return a.aspectMask == b.aspectMask + && a.mipLevel == b.mipLevel + && a.baseArrayLayer == b.baseArrayLayer + && a.layerCount == b.layerCount; +} + + +inline bool operator != ( + const VkImageSubresourceLayers& a, + const VkImageSubresourceLayers& b) { + return !operator == (a, b); +} + + +inline bool operator == (VkExtent3D a, VkExtent3D b) { + return a.width == b.width + && a.height == b.height + && a.depth == b.depth; +} + + +inline bool operator != (VkExtent3D a, VkExtent3D b) { + return a.width != b.width + || a.height != b.height + || a.depth != b.depth; +} + + +inline bool operator == (VkExtent2D a, VkExtent2D b) { + return a.width == b.width + && a.height == b.height; +} + + +inline bool operator != (VkExtent2D a, VkExtent2D b) { + return a.width != b.width + || a.height != b.height; +} + + +inline bool operator == (VkOffset3D a, VkOffset3D b) { + return a.x == b.x + && a.y == b.y + && a.z == b.z; +} + + +inline bool operator != (VkOffset3D a, VkOffset3D b) { + return a.x != b.x + || a.y != b.y + || a.z != b.z; +} + + +inline bool operator == (VkOffset2D a, VkOffset2D b) { + return a.x == b.x + && a.y == b.y; +} + + +inline bool operator != (VkOffset2D a, VkOffset2D b) { + return a.x != b.x + || a.y != b.y; +} + From 52981b8a29f2fca75722cc9db2d806c3c5edff12 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Wed, 11 Sep 2024 01:10:32 +0100 Subject: [PATCH 11/38] steamcompmgr: Add some logic for picking commits if we have an override w/ WSI layer --- src/steamcompmgr.cpp | 17 +++++++++++++++-- src/steamcompmgr_shared.hpp | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index c5556fbb23..16d515a58e 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -6360,9 +6360,22 @@ void update_wayland_res(CommitDoneList_t *doneCommits, steamcompmgr_win_t *w, Re return; } - // If we have an override surface, make sure this commit is for the current surface. + // If we ever use HDR on the surface, only ever accept flip commits from the WSI layer. + if ( reslistentry.feedback && reslistentry.feedback->vk_colorspace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR ) + { + w->bHasHadNonSRGBColorSpace = true; + } + + // If there are random commits that are really thin/small when we have the WSI layer active ever, + // let's just ignore these as they are probably bogus commits from glamor. + bool bPossiblyBogus = reslistentry.buf->width <= 2 || reslistentry.buf->height <= 2; + + // If we have an override surface, make sure this commit is for the current surface + // or if the commit is probably bogus. + bool bOnlyCurrentSurface = w->bHasHadNonSRGBColorSpace || bPossiblyBogus; + bool for_current_surface = !w->override_surface() || w->current_surface() == reslistentry.surf; - if (!for_current_surface) + if ( bOnlyCurrentSurface && !for_current_surface ) { wlserver_lock(); wlr_buffer_unlock( buf ); diff --git a/src/steamcompmgr_shared.hpp b/src/steamcompmgr_shared.hpp index 9252a7eda5..095694e493 100644 --- a/src/steamcompmgr_shared.hpp +++ b/src/steamcompmgr_shared.hpp @@ -132,6 +132,8 @@ struct steamcompmgr_win_t { bool hasHwndStyleEx = false; uint32_t hwndStyleEx = 0; + bool bHasHadNonSRGBColorSpace = false; + bool nudged = false; bool ignoreOverrideRedirect = false; From 0c5cf2b66565c34c03ca4c646528534eb6ac3650 Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Sat, 7 Sep 2024 22:16:08 -0700 Subject: [PATCH 12/38] build: fix meson compiler warning --- src/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meson.build b/src/meson.build index 49f207945c..5174de6700 100644 --- a/src/meson.build +++ b/src/meson.build @@ -166,7 +166,7 @@ compiler_name = cc.get_id() compiler_version = cc.version() vcs_tag_cmd = ['git', 'describe', '--always', '--tags', '--dirty=+'] -vcs_tag = run_command(vcs_tag_cmd).stdout().strip() +vcs_tag = run_command(vcs_tag_cmd, check: false).stdout().strip() version_tag = vcs_tag + ' (' + compiler_name + ' ' + compiler_version + ')' gamescope_version_conf = configuration_data() From 861cf7938df0e77cadd838b49493cb8c4eab1d22 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Wed, 11 Sep 2024 20:33:05 +0100 Subject: [PATCH 13/38] steamcompmgr: Disregard XWayland commits if we have an override surface and there was no damage --- src/steamcompmgr.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 16d515a58e..8dc24ef111 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -6345,7 +6345,7 @@ static TempUpscaleImage_t *GetTempUpscaleImage( uint32_t uWidth, uint32_t uHeigh return ℑ } - +gamescope::ConVar cv_surface_update_force_only_current_surface( "surface_update_force_only_current_surface", false ); void update_wayland_res(CommitDoneList_t *doneCommits, steamcompmgr_win_t *w, ResListEntry_t& reslistentry) { @@ -6370,11 +6370,21 @@ void update_wayland_res(CommitDoneList_t *doneCommits, steamcompmgr_win_t *w, Re // let's just ignore these as they are probably bogus commits from glamor. bool bPossiblyBogus = reslistentry.buf->width <= 2 || reslistentry.buf->height <= 2; + // If the buffer has no damage, always prefer our override surface. + bool bHasDamage = ( reslistentry.surf->buffer_damage.extents.x2 - reslistentry.surf->buffer_damage.extents.x1 ) > 2 && + ( reslistentry.surf->buffer_damage.extents.y2 - reslistentry.surf->buffer_damage.extents.y1 ) > 2; + // If we have an override surface, make sure this commit is for the current surface // or if the commit is probably bogus. - bool bOnlyCurrentSurface = w->bHasHadNonSRGBColorSpace || bPossiblyBogus; + bool bOnlyCurrentSurface = w->bHasHadNonSRGBColorSpace || bPossiblyBogus || !bHasDamage || cv_surface_update_force_only_current_surface; bool for_current_surface = !w->override_surface() || w->current_surface() == reslistentry.surf; + + if ( !for_current_surface ) + { + xwm_log.debugf( "Got commit not for current surface." ); + } + if ( bOnlyCurrentSurface && !for_current_surface ) { wlserver_lock(); From 99bed333881c97693335033467dada358e63a8f4 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Wed, 11 Sep 2024 20:49:47 +0100 Subject: [PATCH 14/38] steamcompmgr: Ensure forward progress for surfaces without windows when we discard commits Otherwise they can get stuck in WaitForPresent, etc. --- src/steamcompmgr.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 8dc24ef111..411eaca117 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -6356,6 +6356,15 @@ void update_wayland_res(CommitDoneList_t *doneCommits, steamcompmgr_win_t *w, Re wlserver_lock(); wlr_buffer_unlock( buf ); wlserver_unlock(); + + // Make sure to send the discarded event if we hit this + // to ensure forward progress. + if (!reslistentry.presentation_feedbacks.empty()) + { + wlserver_presentation_feedback_discard( reslistentry.surf, reslistentry.presentation_feedbacks ); + // presentation_feedbacks cleared by wlserver_presentation_feedback_discard + } + xwm_log.errorf( "waylandres but no win" ); return; } From 8045786067f7a7fd9fd3fff98c29588f314218a9 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Wed, 11 Sep 2024 21:40:06 +0100 Subject: [PATCH 15/38] layer: Add hacks for Talos Principle and Serious Sam's bad swapchain usage These broke under Gamescope WSI, workaround them being broken. --- layer/VkLayer_FROG_gamescope_wsi.cpp | 134 ++++++++++++++++++++++++++- src/layer_defines.h | 3 + 2 files changed, 134 insertions(+), 3 deletions(-) diff --git a/layer/VkLayer_FROG_gamescope_wsi.cpp b/layer/VkLayer_FROG_gamescope_wsi.cpp index 8da45deafe..cbb71e26d4 100644 --- a/layer/VkLayer_FROG_gamescope_wsi.cpp +++ b/layer/VkLayer_FROG_gamescope_wsi.cpp @@ -91,6 +91,80 @@ namespace GamescopeWSILayer { return atoi(appid); } + // Taken from Mesa, licensed under MIT. + // + // No real reason to rewrite this code, + // it works :) + static char * + __getProgramName() + { + char * arg = strrchr(program_invocation_name, '/'); + if (arg) { + char *program_name = NULL; + /* If the / character was found this is likely a linux path or + * an invocation path for a 64-bit wine program. + * + * However, some programs pass command line arguments into argv[0]. + * Strip these arguments out by using the realpath only if it was + * a prefix of the invocation name. + */ + char *path = realpath("/proc/self/exe", NULL); + + if (path && strncmp(path, program_invocation_name, strlen(path)) == 0) { + /* This shouldn't be null because path is a a prefix, + * but check it anyway since path is static. */ + char * name = strrchr(path, '/'); + if (name) + program_name = strdup(name + 1); + } + if (path) { + free(path); + } + if (!program_name) { + program_name = strdup(arg+1); + } + return program_name; + } + + /* If there was no '/' at all we likely have a windows like path from + * a wine application. + */ + arg = strrchr(program_invocation_name, '\\'); + if (arg) + return strdup(arg+1); + + return strdup(program_invocation_name); + } + + std::string_view getExecutableName() { + static std::string s_execName = []() -> std::string + { + const char *mesaExecutableEnv = getenv("MESA_DRICONF_EXECUTABLE_OVERRIDE"); + if (mesaExecutableEnv && *mesaExecutableEnv) { + fprintf(stderr, "[Gamescope WSI] Executable name overriden by MESA_DRICONF_EXECUTABLE_OVERRIDE: %s\n", mesaExecutableEnv); + return mesaExecutableEnv; + } + + const char *mesaProcessName = getenv("MESA_PROCESS_NAME"); + if (mesaProcessName && *mesaProcessName) { + fprintf(stderr, "[Gamescope WSI] Executable name overriden by MESA_PROCESS_NAME: %s\n", mesaExecutableEnv); + return mesaProcessName; + } + + std::string name; + { + char *programNameCStr = __getProgramName(); + name = programNameCStr; + free(programNameCStr); + } + + fprintf(stderr, "[Gamescope WSI] Executable name: %s\n", name.c_str()); + return name; + }(); + + return s_execName; + } + static GamescopeLayerClient::Flags defaultLayerClientFlags(const VkApplicationInfo *pApplicationInfo, uint32_t appid) { GamescopeLayerClient::Flags flags = 0; @@ -116,6 +190,40 @@ namespace GamescopeWSILayer { } } + std::string_view executable = getExecutableName(); + + // Work around various Croteam games not handling + // suboptimal and swapchain extent correctly. + if (executable == "Talos"sv || + executable == "Talos_Unrestricted"sv || + executable == "Talos_VR"sv || + executable == "Talos_Unrestricted_VR"sv || + executable == "Sam2017"sv || + executable == "Sam2017_Unrestricted"sv) { + flags |= GamescopeLayerClient::Flag::ForceSwapchainExtent; + flags |= GamescopeLayerClient::Flag::NoSuboptimal; + } + + { + const char *forceSwapchainExtentEnvVar = getenv("vk_wsi_force_swapchain_to_current_extent"); + if (forceSwapchainExtentEnvVar && *forceSwapchainExtentEnvVar) { + if (forceSwapchainExtentEnvVar == "true"sv) + flags |= GamescopeLayerClient::Flag::ForceSwapchainExtent; + else + flags &= ~GamescopeLayerClient::Flag::ForceSwapchainExtent; + } + } + + { + const char *ignoreSuboptimalEnvVar = getenv("vk_x11_ignore_suboptimal"); + if (ignoreSuboptimalEnvVar && *ignoreSuboptimalEnvVar) { + if (ignoreSuboptimalEnvVar == "true"sv) + flags |= GamescopeLayerClient::Flag::NoSuboptimal; + else + flags &= ~GamescopeLayerClient::Flag::NoSuboptimal; + } + } + return flags; } @@ -415,6 +523,10 @@ namespace GamescopeWSILayer { setenv("DXVK_HDR", "0", 1); } + // Work around the Mesa implementation of this being broken. + // ( https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/31134 ) + setenv("vk_wsi_force_swapchain_to_current_extent", "false", 0); + return result; } @@ -961,6 +1073,16 @@ namespace GamescopeWSILayer { VkSwapchainCreateInfoKHR swapchainInfo = *pCreateInfo; + if (gamescopeSurface->flags & GamescopeLayerClient::Flag::ForceSwapchainExtent) { + if (!gamescopeSurface->isWayland()) { + auto rect = xcb::getWindowRect(gamescopeSurface->connection, gamescopeSurface->window); + if (!rect) + return VK_ERROR_SURFACE_LOST_KHR; + + swapchainInfo.imageExtent = rect->extent; + } + } + const bool canBypass = gamescopeSurface->canBypassXWayland(); // If we can't flip, fallback to the regular XCB surface on the XCB window. if (!canBypass) @@ -1245,11 +1367,17 @@ namespace GamescopeWSILayer { } const bool canBypass = gamescopeSurface->canBypassXWayland(); - if (canBypass != gamescopeSwapchain->isBypassingXWayland) - UpdateSwapchainResult(canBypass ? VK_SUBOPTIMAL_KHR : VK_ERROR_OUT_OF_DATE_KHR); + if (canBypass != gamescopeSwapchain->isBypassingXWayland) { + if (canBypass) { + if (!(gamescopeSurface->flags & GamescopeLayerClient::Flag::NoSuboptimal)) + UpdateSwapchainResult(VK_SUBOPTIMAL_KHR); + } else { + UpdateSwapchainResult(VK_ERROR_OUT_OF_DATE_KHR); + } + } // Emulate behaviour when currentExtent changes in X11 swapchain. - if (!gamescopeSurface->isWayland()) { + if (!gamescopeSurface->isWayland() && !(gamescopeSurface->flags & GamescopeLayerClient::Flag::ForceSwapchainExtent)) { // gamescopeSurface->cachedWindowSize is set by canBypassXWayland. // TODO: Rename that to be some update cached vars thing, then read back canBypassXWayland. if (gamescopeSurface->cachedWindowRect) { diff --git a/src/layer_defines.h b/src/layer_defines.h index a77fb13808..6ebfd3221d 100644 --- a/src/layer_defines.h +++ b/src/layer_defines.h @@ -10,6 +10,9 @@ namespace GamescopeLayerClient static constexpr uint32_t DisableHDR = 1u << 0; static constexpr uint32_t ForceBypass = 1u << 1; static constexpr uint32_t FrameLimiterAware = 1u << 2; + + static constexpr uint32_t NoSuboptimal = 1u << 3; + static constexpr uint32_t ForceSwapchainExtent = 1u << 4; } using Flags = uint32_t; } \ No newline at end of file From cc28cd7e2ed1c254db2787be7fff01d0028e6007 Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Thu, 12 Sep 2024 11:39:21 -0700 Subject: [PATCH 16/38] convars, steamcompmgr: clean up descriptions and HDR launch options --- src/main.cpp | 4 ++-- src/steamcompmgr.cpp | 8 ++++---- src/wlserver.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index ca4001249f..a9cdaa2e21 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -132,7 +132,7 @@ const struct option *gamescope_options = (struct option[]){ { "sdr-gamut-wideness", required_argument, nullptr, 0 }, { "hdr-enabled", no_argument, nullptr, 0 }, { "hdr-sdr-content-nits", required_argument, nullptr, 0 }, - { "hdr-itm-enable", no_argument, nullptr, 0 }, + { "hdr-itm-enabled", no_argument, nullptr, 0 }, { "hdr-itm-sdr-nits", required_argument, nullptr, 0 }, { "hdr-itm-target-nits", required_argument, nullptr, 0 }, { "hdr-debug-force-support", no_argument, nullptr, 0 }, @@ -194,7 +194,7 @@ const char usage[] = " If this is not set, and there is a HDR client, it will be tonemapped SDR.\n" " --sdr-gamut-wideness Set the 'wideness' of the gamut for SDR comment. 0 - 1.\n" " --hdr-sdr-content-nits set the luminance of SDR content in nits. Default: 400 nits.\n" - " --hdr-itm-enable enable SDR->HDR inverse tone mapping. only works for SDR input.\n" + " --hdr-itm-enabled enable SDR->HDR inverse tone mapping. only works for SDR input.\n" " --hdr-itm-sdr-nits set the luminance of SDR content in nits used as the input for the inverse tone mapping process.\n" " Default: 100 nits, Max: 1000 nits\n" " --hdr-itm-target-nits set the target luminace of the inverse tone mapping process.\n" diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 411eaca117..8c9920e315 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -195,7 +195,7 @@ update_runtime_info(); gamescope::ConVar cv_adaptive_sync( "adaptive_sync", false, "Whether or not adaptive sync is enabled if available." ); gamescope::ConVar cv_adaptive_sync_ignore_overlay( "adaptive_sync_ignore_overlay", false, "Whether or not to ignore overlay planes for pushing commits with adaptive sync." ); -gamescope::ConVar cv_adaptive_sync_overlay_cycles( "adaptive_sync_overlay_cycles", 1, "" ); +gamescope::ConVar cv_adaptive_sync_overlay_cycles( "adaptive_sync_overlay_cycles", 1, "Number of vblank cycles to ignore overlay repaints before forcing a commit with adaptive sync." ); uint64_t g_SteamCompMgrLimitedAppRefreshCycle = 16'666'666; uint64_t g_SteamCompMgrAppRefreshCycle = 16'666'666; @@ -6345,7 +6345,7 @@ static TempUpscaleImage_t *GetTempUpscaleImage( uint32_t uWidth, uint32_t uHeigh return ℑ } -gamescope::ConVar cv_surface_update_force_only_current_surface( "surface_update_force_only_current_surface", false ); +gamescope::ConVar cv_surface_update_force_only_current_surface( "surface_update_force_only_current_surface", false, "Force updates to apply only to the current surface, ignoring commits for other surfaces." ); void update_wayland_res(CommitDoneList_t *doneCommits, steamcompmgr_win_t *w, ResListEntry_t& reslistentry) { @@ -7434,13 +7434,13 @@ steamcompmgr_main(int argc, char **argv) g_FadeOutDuration = atoi(optarg); } else if (strcmp(opt_name, "force-windows-fullscreen") == 0) { bForceWindowsFullscreen = true; - } else if (strcmp(opt_name, "hdr-enabled") == 0) { + } else if (strcmp(opt_name, "hdr-enabled") == 0 || strcmp(opt_name, "hdr-enable") == 0) { cv_hdr_enabled = true; } else if (strcmp(opt_name, "hdr-debug-force-support") == 0) { g_bForceHDRSupportDebug = true; } else if (strcmp(opt_name, "hdr-debug-force-output") == 0) { g_bForceHDR10OutputDebug = true; - } else if (strcmp(opt_name, "hdr-itm-enable") == 0) { + } else if (strcmp(opt_name, "hdr-itm-enabled") == 0 || strcmp(opt_name, "hdr-itm-enable") == 0) { g_bHDRItmEnable = true; } else if (strcmp(opt_name, "sdr-gamut-wideness") == 0) { g_ColorMgmt.pending.sdrGamutWideness = atof(optarg); diff --git a/src/wlserver.cpp b/src/wlserver.cpp index 5178c7e4d4..670cf6896d 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -123,7 +123,7 @@ std::vector& gamescope_xwayland_server_t::retrieve_commits() return commits; } -gamescope::ConVar cv_drm_debug_syncobj_force_wait_on_commit( "drm_debug_syncobj_force_wait_on_commit", false ); +gamescope::ConVar cv_drm_debug_syncobj_force_wait_on_commit( "drm_debug_syncobj_force_wait_on_commit", false, "Force a wait on DRM sync objects before committing buffers" ); std::optional PrepareCommit( struct wlr_surface *surf, struct wlr_buffer *buf ) { From 7a273743368aaa6368cb0d31d4786d78b4b00dc6 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Mon, 16 Sep 2024 05:42:00 +0100 Subject: [PATCH 17/38] rendervulkan: Fix scanout-able check --- src/rendervulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp index 97e6fbe226..93dbfc54fd 100644 --- a/src/rendervulkan.cpp +++ b/src/rendervulkan.cpp @@ -2159,7 +2159,7 @@ bool CVulkanTexture::BInit( uint32_t width, uint32_t height, uint32_t depth, uin imageInfo.tiling = tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; } - if ( GetBackend()->UsesModifiers() && flags.bFlippable == true && tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT ) + if ( flags.bFlippable == true && tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT ) { // We want to scan-out the image wsiImageCreateInfo = { From 01101093445af20ea25ad386fd872d5f92266c2c Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Mon, 16 Sep 2024 06:01:23 +0100 Subject: [PATCH 18/38] rendervulkan: Attempt at fixing modifierless devices having issues --- src/Backends/WaylandBackend.cpp | 34 ++++++------ src/backend.h | 6 +++ src/rendervulkan.cpp | 95 +++++++++++++++------------------ 3 files changed, 65 insertions(+), 70 deletions(-) diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp index bb8bd6f642..5e2b0191d8 100644 --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -577,8 +577,6 @@ namespace gamescope void SetFullscreen( bool bFullscreen ); // Thread safe, can be called from the input thread. void UpdateFullscreenState(); - bool SupportsFormat( uint32_t uDRMFormat ) const; - bool HostCompositorIsCurrentlyVRR() const { return m_bHostCompositorIsCurrentlyVRR; } void SetHostCompositorIsCurrentlyVRR( bool bActive ) { m_bHostCompositorIsCurrentlyVRR = bActive; } @@ -672,8 +670,8 @@ namespace gamescope zwp_locked_pointer_v1 *m_pLockedPointer = nullptr; zwp_relative_pointer_v1 *m_pRelativePointer = nullptr; + bool m_bCanUseModifiers = false; std::unordered_map> m_FormatModifiers; - std::unordered_set m_ModifierlessFormats; std::unordered_map m_ImportedFbs; uint32_t m_uPointerEnterSerial = 0; @@ -1770,13 +1768,10 @@ namespace gamescope if ( !cv_wayland_use_modifiers ) return false; - return !m_FormatModifiers.empty(); + return m_bCanUseModifiers; } std::span CWaylandBackend::GetSupportedModifiers( uint32_t uDrmFormat ) const { - if ( !UsesModifiers() ) - return std::span{}; - auto iter = m_FormatModifiers.find( uDrmFormat ); if ( iter == m_FormatModifiers.end() ) return std::span{}; @@ -2069,13 +2064,6 @@ namespace gamescope } } - bool CWaylandBackend::SupportsFormat( uint32_t uDRMFormat ) const - { - return UsesModifiers() - ? m_FormatModifiers.contains( uDRMFormat ) - : m_ModifierlessFormats.contains( uDRMFormat ); - } - ///////////////////// // Wayland Callbacks ///////////////////// @@ -2169,11 +2157,21 @@ namespace gamescope void CWaylandBackend::Wayland_Modifier( zwp_linux_dmabuf_v1 *pDmabuf, uint32_t uFormat, uint32_t uModifierHi, uint32_t uModifierLo ) { uint64_t ulModifier = ( uint64_t( uModifierHi ) << 32 ) | uModifierLo; - //xdg_log.infof( "Modifier: %s (0x%" PRIX32 ") %lx", drmGetFormatName( uFormat ), uFormat, ulModifier ); + +#if 0 + const char *pszExtraModifierName = ""; + if ( ulModifier == DRM_FORMAT_MOD_INVALID ) + pszExtraModifierName = " (Invalid)"; + if ( ulModifier == DRM_FORMAT_MOD_LINEAR ) + pszExtraModifierName = " (Invalid)"; + + xdg_log.infof( "Modifier: %s (0x%" PRIX32 ") %lx%s", drmGetFormatName( uFormat ), uFormat, ulModifier, pszExtraModifierName ); +#endif + if ( ulModifier != DRM_FORMAT_MOD_INVALID ) - m_FormatModifiers[uFormat].emplace_back( ulModifier ); - else - m_ModifierlessFormats.emplace( uFormat ); + m_bCanUseModifiers = true; + + m_FormatModifiers[uFormat].emplace_back( ulModifier ); } // Output diff --git a/src/backend.h b/src/backend.h index 341e9d783b..d09e11dd5f 100644 --- a/src/backend.h +++ b/src/backend.h @@ -6,6 +6,8 @@ #include "Timeline.h" #include "convar.h" #include "rc.h" +#include "drm_include.h" +#include "Utils/Algorithm.h" #include #include @@ -203,6 +205,10 @@ namespace gamescope virtual bool UsesModifiers() const = 0; virtual std::span GetSupportedModifiers( uint32_t uDrmFormat ) const = 0; + inline bool SupportsFormat( uint32_t uDrmFormat ) const + { + return Algorithm::Contains( this->GetSupportedModifiers( uDrmFormat ), DRM_FORMAT_MOD_INVALID ); + } virtual IBackendConnector *GetCurrentConnector() = 0; virtual IBackendConnector *GetConnector( GamescopeScreenType eScreenType ) = 0; diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp index 93dbfc54fd..d61fd73ea6 100644 --- a/src/rendervulkan.cpp +++ b/src/rendervulkan.cpp @@ -12,6 +12,7 @@ #include #include #include "vulkan_include.h" +#include "Utils/Algorithm.h" #if defined(__linux__) #include @@ -122,12 +123,6 @@ VulkanOutput_t g_output; uint32_t g_uCompositeDebug = 0u; gamescope::ConVar cv_composite_debug{ "composite_debug", 0, "Debug composition flags" }; -template -static bool Contains( const std::span x, T value ) -{ - return std::ranges::any_of( x, std::bind_front(std::equal_to{}, value) ); -} - static std::map< VkFormat, std::map< uint64_t, VkDrmFormatModifierPropertiesEXT > > DRMModifierProps = {}; static struct wlr_drm_format_set sampledShmFormats = {}; static struct wlr_drm_format_set sampledDRMFormats = {}; @@ -2064,7 +2059,7 @@ bool CVulkanTexture::BInit( uint32_t width, uint32_t height, uint32_t depth, uin assert( drmFormat == pDMA->format ); } - if ( g_device.supportsModifiers() && pDMA && pDMA->modifier != DRM_FORMAT_MOD_INVALID ) + if ( GetBackend()->UsesModifiers() && g_device.supportsModifiers() && pDMA && pDMA->modifier != DRM_FORMAT_MOD_INVALID ) { VkExternalImageFormatProperties externalImageProperties = { .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES, @@ -2754,66 +2749,62 @@ bool vulkan_init_format(VkFormat format, uint32_t drmFormat) wlr_drm_format_set_add( &sampledShmFormats, drmFormat, DRM_FORMAT_MOD_LINEAR ); - if ( !g_device.supportsModifiers() ) + if ( g_device.supportsModifiers() ) { - if ( GetBackend()->UsesModifiers() ) - { - if ( !Contains( GetBackend()->GetSupportedModifiers( drmFormat ), DRM_FORMAT_MOD_INVALID ) ) - return false; - } - - wlr_drm_format_set_add( &sampledDRMFormats, drmFormat, DRM_FORMAT_MOD_INVALID ); - return false; - } - - // Then, collect the list of modifiers supported for sampled usage - VkDrmFormatModifierPropertiesListEXT modifierPropList = { - .sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT, - }; - VkFormatProperties2 formatProps = { - .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, - .pNext = &modifierPropList, - }; - - g_device.vk.GetPhysicalDeviceFormatProperties2( g_device.physDev(), format, &formatProps ); + // Then, collect the list of modifiers supported for sampled usage + VkDrmFormatModifierPropertiesListEXT modifierPropList = { + .sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT, + }; + VkFormatProperties2 formatProps = { + .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, + .pNext = &modifierPropList, + }; - if ( modifierPropList.drmFormatModifierCount == 0 ) - { - vk_errorf( res, "vkGetPhysicalDeviceFormatProperties2 returned zero modifiers for DRM format 0x%" PRIX32, drmFormat ); - return false; - } + g_device.vk.GetPhysicalDeviceFormatProperties2( g_device.physDev(), format, &formatProps ); - std::vector modifierProps(modifierPropList.drmFormatModifierCount); - modifierPropList.pDrmFormatModifierProperties = modifierProps.data(); - g_device.vk.GetPhysicalDeviceFormatProperties2( g_device.physDev(), format, &formatProps ); + if ( modifierPropList.drmFormatModifierCount == 0 ) + { + vk_errorf( res, "vkGetPhysicalDeviceFormatProperties2 returned zero modifiers for DRM format 0x%" PRIX32, drmFormat ); + return false; + } - std::map< uint64_t, VkDrmFormatModifierPropertiesEXT > map = {}; + std::vector modifierProps(modifierPropList.drmFormatModifierCount); + modifierPropList.pDrmFormatModifierProperties = modifierProps.data(); + g_device.vk.GetPhysicalDeviceFormatProperties2( g_device.physDev(), format, &formatProps ); - for ( size_t j = 0; j < modifierProps.size(); j++ ) - { - map[ modifierProps[j].drmFormatModifier ] = modifierProps[j]; + std::map< uint64_t, VkDrmFormatModifierPropertiesEXT > map = {}; - uint64_t modifier = modifierProps[j].drmFormatModifier; + for ( size_t j = 0; j < modifierProps.size(); j++ ) + { + map[ modifierProps[j].drmFormatModifier ] = modifierProps[j]; - if ( !is_image_format_modifier_supported( format, drmFormat, modifier ) ) - continue; + uint64_t modifier = modifierProps[j].drmFormatModifier; - if ( ( modifierProps[j].drmFormatModifierTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT ) == 0 ) - { + if ( !is_image_format_modifier_supported( format, drmFormat, modifier ) ) continue; - } - if ( GetBackend()->UsesModifiers() ) - { - if ( !Contains( GetBackend()->GetSupportedModifiers( drmFormat ), modifier ) ) + if ( ( modifierProps[j].drmFormatModifierTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT ) == 0 ) + { + continue; + } + + if ( !gamescope::Algorithm::Contains( GetBackend()->GetSupportedModifiers( drmFormat ), modifier ) ) continue; + + wlr_drm_format_set_add( &sampledDRMFormats, drmFormat, modifier ); } - wlr_drm_format_set_add( &sampledDRMFormats, drmFormat, modifier ); + DRMModifierProps[ format ] = map; + return true; } + else + { + if ( !GetBackend()->SupportsFormat( drmFormat ) ) + return false; - DRMModifierProps[ format ] = map; - return true; + wlr_drm_format_set_add( &sampledDRMFormats, drmFormat, DRM_FORMAT_MOD_INVALID ); + return false; + } } bool vulkan_init_formats() From a2b45e15009f80ca1ad346840f2574bbe060fa94 Mon Sep 17 00:00:00 2001 From: wheaney <42350981+wheaney@users.noreply.github.com> Date: Fri, 13 Sep 2024 23:42:15 -0700 Subject: [PATCH 19/38] Fix how reshade effects are cleared --- README.md | 2 +- src/reshade_effect_manager.cpp | 2 +- src/steamcompmgr.cpp | 35 ++++++++++++++++++++-------------- src/steamcompmgr.hpp | 1 + src/wlserver.cpp | 1 - 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index a56075a91b..8aa0227bf8 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ Gamescope supports a subset of Reshade effects/shaders using the `--reshade-effe This provides an easy way to do shader effects (ie. CRT shader, film grain, debugging HDR with histograms, etc) on top of whatever is being displayed in Gamescope without having to hook into the underlying process. -There is currently no way to set the value of uniforms/options, they will just be their initializer values currently. +Uniform/shader options can be modified programmatically via the `gamescope-reshade` wayland interface. Otherwise, they will just use their initializer values. Using Reshade effects will increase latency as there will be work performed on the general gfx + compute queue as opposed to only using the realtime async compute queue which can run in tandem with the game's gfx work. diff --git a/src/reshade_effect_manager.cpp b/src/reshade_effect_manager.cpp index 87f886b140..0ba5985dba 100644 --- a/src/reshade_effect_manager.cpp +++ b/src/reshade_effect_manager.cpp @@ -1949,5 +1949,5 @@ void reshade_effect_manager_enable_effect() void reshade_effect_manager_disable_effect() { - gamescope_set_reshade_effect(nullptr); + gamescope_clear_reshade_effect(); } \ No newline at end of file diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 8c9920e315..376770b9f3 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -2955,20 +2955,22 @@ std::string get_string_prop( xwayland_ctx_t *ctx, Window win, Atom prop ) return value; } -void set_string_prop( xwayland_ctx_t *ctx, Window win, Atom prop, const std::string &value ) +void set_string_prop( xwayland_ctx_t *ctx, Atom prop, const std::string &value ) { - if ( value.empty() ) - XDeleteProperty( ctx->dpy, win, prop ); - else { - XTextProperty text_property = - { - .value = ( unsigned char * )value.c_str(), - .encoding = ctx->atoms.utf8StringAtom, - .format = 8, - .nitems = strlen( value.c_str() ) - }; - XSetTextProperty( ctx->dpy, ctx->root, &text_property, prop); - } + XTextProperty text_property = + { + .value = ( unsigned char * )value.c_str(), + .encoding = ctx->atoms.utf8StringAtom, + .format = 8, + .nitems = strlen( value.c_str() ) + }; + XSetTextProperty( ctx->dpy, ctx->root, &text_property, prop); + XFlush( ctx->dpy ); +} + +void clear_prop( xwayland_ctx_t *ctx, Atom prop ) +{ + XDeleteProperty( ctx->dpy, ctx->root, prop ); XFlush( ctx->dpy ); } @@ -4896,7 +4898,12 @@ void gamescope_set_selection(std::string contents, GamescopeSelection eSelection void gamescope_set_reshade_effect(std::string effect_path) { gamescope_xwayland_server_t *server = wlserver_get_xwayland_server(0); - set_string_prop(server->ctx.get(), server->ctx->ourWindow, server->ctx->atoms.gamescopeReshadeEffect, effect_path); + set_string_prop(server->ctx.get(), server->ctx->atoms.gamescopeReshadeEffect, effect_path); +} + +void gamescope_clear_reshade_effect() { + gamescope_xwayland_server_t *server = wlserver_get_xwayland_server(0); + clear_prop(server->ctx.get(), server->ctx->atoms.gamescopeReshadeEffect); } static void diff --git a/src/steamcompmgr.hpp b/src/steamcompmgr.hpp index 8c76db90a8..9f384c461c 100644 --- a/src/steamcompmgr.hpp +++ b/src/steamcompmgr.hpp @@ -148,6 +148,7 @@ extern pid_t focusWindow_pid; void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_server); void gamescope_set_selection(std::string contents, GamescopeSelection eSelection); void gamescope_set_reshade_effect(std::string effect_path); +void gamescope_clear_reshade_effect(); MouseCursor *steamcompmgr_get_current_cursor(); MouseCursor *steamcompmgr_get_server_cursor(uint32_t serverId); diff --git a/src/wlserver.cpp b/src/wlserver.cpp index 670cf6896d..8cd58dd6a0 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -58,7 +58,6 @@ #include "steamcompmgr.hpp" #include "log.hpp" #include "ime.hpp" -#include "reshade_effect_manager.hpp" #include "xwayland_ctx.hpp" #include "refresh_rate.h" #include "InputEmulation.h" From d87f6d9b9d98ea8064e4063c01d483895fef87f0 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Fri, 2 Aug 2024 13:33:57 -0500 Subject: [PATCH 20/38] Add clipboard support from SDL backend Looks like as part of the backend refactor, copy and paste was partially broken with the SDL backend. This fixes that broken functionality and lays some groundwork for copy and paste in the Wayland Backend. --- src/Backends/OpenVRBackend.cpp | 6 +++++- src/Backends/SDLBackend.cpp | 12 ++++++++++-- src/Backends/WaylandBackend.cpp | 5 +++++ src/backend.h | 1 + src/steamcompmgr.cpp | 6 ++++-- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Backends/OpenVRBackend.cpp b/src/Backends/OpenVRBackend.cpp index f03a65d2b0..c39caa54b7 100644 --- a/src/Backends/OpenVRBackend.cpp +++ b/src/Backends/OpenVRBackend.cpp @@ -893,7 +893,11 @@ namespace gamescope vr::VROverlay()->ClearOverlayTexture( GetPrimaryPlane()->GetOverlayThumbnail() ); } } - virtual std::shared_ptr GetHostCursor() override + virtual void SetSelection( std::shared_ptr szContents, GamescopeSelection eSelection ) override + { + // Do nothing. + } + virtual std::shared_ptr GetHostCursor() override { return nullptr; } diff --git a/src/Backends/SDLBackend.cpp b/src/Backends/SDLBackend.cpp index 8b36464202..6d50f8d34e 100644 --- a/src/Backends/SDLBackend.cpp +++ b/src/Backends/SDLBackend.cpp @@ -11,6 +11,7 @@ #include "SDL_clipboard.h" #include "SDL_events.h" +#include "gamescope_shared.h" #include "main.hpp" #include "wlserver.hpp" #include @@ -162,7 +163,8 @@ namespace gamescope virtual void SetVisible( bool bVisible ) override; virtual void SetTitle( std::shared_ptr szTitle ) override; virtual void SetIcon( std::shared_ptr> uIconPixels ) override; - virtual std::shared_ptr GetHostCursor() override; + virtual void SetSelection( std::shared_ptr szContents, GamescopeSelection eSelection ) override; + virtual std::shared_ptr GetHostCursor() override; protected: virtual void OnBackendBlobDestroyed( BackendBlob *pBlob ) override; private: @@ -496,7 +498,13 @@ namespace gamescope m_pApplicationIcon = uIconPixels; PushUserEvent( GAMESCOPE_SDL_EVENT_ICON ); } - + void CSDLBackend::SetSelection( std::shared_ptr szContents, GamescopeSelection eSelection ) + { + if (eSelection == GAMESCOPE_SELECTION_CLIPBOARD) + SDL_SetClipboardText(szContents->c_str()); + else if (eSelection == GAMESCOPE_SELECTION_PRIMARY) + SDL_SetPrimarySelectionText(szContents->c_str()); + } std::shared_ptr CSDLBackend::GetHostCursor() { return GetX11HostCursor(); diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp index 5e2b0191d8..c53bf14df1 100644 --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -545,6 +545,7 @@ namespace gamescope virtual void SetVisible( bool bVisible ) override; virtual void SetTitle( std::shared_ptr szTitle ) override; virtual void SetIcon( std::shared_ptr> uIconPixels ) override; + virtual void SetSelection( std::shared_ptr szContents, GamescopeSelection eSelection ) override; virtual std::shared_ptr GetHostCursor() override; protected: virtual void OnBackendBlobDestroyed( BackendBlob *pBlob ) override; @@ -1976,6 +1977,10 @@ namespace gamescope xdg_toplevel_icon_manager_v1_set_icon( m_pToplevelIconManager, m_Planes[0].GetXdgToplevel(), nullptr ); } } + void CWaylandBackend::SetSelection( std::shared_ptr szContents, GamescopeSelection eSelection ) + { + // Do nothing + } std::shared_ptr CWaylandBackend::GetHostCursor() { diff --git a/src/backend.h b/src/backend.h index d09e11dd5f..dd295f4ac0 100644 --- a/src/backend.h +++ b/src/backend.h @@ -130,6 +130,7 @@ namespace gamescope virtual void SetVisible( bool bVisible ) = 0; virtual void SetTitle( std::shared_ptr szTitle ) = 0; virtual void SetIcon( std::shared_ptr> uIconPixels ) = 0; + virtual void SetSelection( std::shared_ptr szContents, GamescopeSelection eSelection ) = 0; virtual std::shared_ptr GetHostCursor() = 0; }; diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 376770b9f3..047a930536 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -29,6 +29,7 @@ * says above. Not that I can really do anything about it */ +#include "gamescope_shared.h" #include "xwayland_ctx.hpp" #include #include @@ -4979,13 +4980,14 @@ handle_selection_notify(xwayland_ctx_t *ctx, XSelectionEvent *ev) &actual_type, &actual_format, &nitems, &bytes_after, &data); if (data) { const char *contents = (const char *) data; + auto szContents = std::make_shared(contents); defer( XFree( data ); ); if (ev->selection == ctx->atoms.clipboard) { if ( GetBackend()->GetNestedHints() ) { - //GetBackend()->GetNestedHints()->SetSelection() + GetBackend()->GetNestedHints()->SetSelection( szContents, GAMESCOPE_SELECTION_CLIPBOARD ); } else { @@ -4996,7 +4998,7 @@ handle_selection_notify(xwayland_ctx_t *ctx, XSelectionEvent *ev) { if ( GetBackend()->GetNestedHints() ) { - //GetBackend()->GetNestedHints()->SetSelection() + GetBackend()->GetNestedHints()->SetSelection( szContents, GAMESCOPE_SELECTION_PRIMARY ); } else { From 03e5dd03bffcb6a369ab8627b3f779d2672b42e5 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Thu, 19 Sep 2024 09:33:29 +0100 Subject: [PATCH 21/38] steamcompmgr: Fix crash when using magnifier and game recording --- src/steamcompmgr.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 047a930536..02148f423c 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -1972,7 +1972,7 @@ paint_window_commit( const gamescope::Rc &lastCommit, steamcompmgr_win drawYOffset += w->GetGeometry().nY * currentScaleRatio_y; } - if ( zoomScaleRatio != 1.0 ) + if ( cursor && zoomScaleRatio != 1.0 ) { drawXOffset += (((int)sourceWidth / 2) - cursor->x()) * currentScaleRatio_x; drawYOffset += (((int)sourceHeight / 2) - cursor->y()) * currentScaleRatio_y; @@ -2198,10 +2198,10 @@ static void paint_pipewire() s_ulLastOverrideCommitId = ulOverrideCommitId; // Paint the windows we have onto the Pipewire stream. - paint_window( pFocus->focusWindow, pFocus->focusWindow, &frameInfo, nullptr, 0, 1.0f, pFocus->overrideWindow ); + paint_window( pFocus->focusWindow, pFocus->focusWindow, &frameInfo, global_focus.cursor, 0, 1.0f, pFocus->overrideWindow ); if ( pFocus->overrideWindow && !pFocus->focusWindow->isSteamStreamingClient ) - paint_window( pFocus->overrideWindow, pFocus->focusWindow, &frameInfo, nullptr, PaintWindowFlag::NoFilter, 1.0f, pFocus->overrideWindow ); + paint_window( pFocus->overrideWindow, pFocus->focusWindow, &frameInfo, global_focus.cursor, PaintWindowFlag::NoFilter, 1.0f, pFocus->overrideWindow ); gamescope::Rc pRGBTexture = s_pPipewireBuffer->texture->isYcbcr() ? vulkan_acquire_screenshot_texture( g_nOutputWidth, g_nOutputHeight, false, DRM_FORMAT_XRGB2101010 ) From 4be5215641a4b8d6078590518c9bb0e95dcd5f35 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Thu, 19 Sep 2024 13:44:58 +0100 Subject: [PATCH 22/38] layer: Print application info at instance creation time --- layer/VkLayer_FROG_gamescope_wsi.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/layer/VkLayer_FROG_gamescope_wsi.cpp b/layer/VkLayer_FROG_gamescope_wsi.cpp index cbb71e26d4..c817880f18 100644 --- a/layer/VkLayer_FROG_gamescope_wsi.cpp +++ b/layer/VkLayer_FROG_gamescope_wsi.cpp @@ -507,6 +507,19 @@ namespace GamescopeWSILayer { fprintf(stderr, "[Gamescope WSI] Failed to connect to gamescope socket: %s. Bypass layer will be unavailable.\n", gamescopeWaylandSocket()); return result; } + + { + if (pCreateInfo->pApplicationInfo) { + fprintf(stderr, "[Gamescope WSI] Application info:\n"); + fprintf(stderr, " pApplicationName: %s\n", pCreateInfo->pApplicationInfo->pApplicationName); + fprintf(stderr, " applicationVersion: %u\n", pCreateInfo->pApplicationInfo->applicationVersion); + fprintf(stderr, " pEngineName: %s\n", pCreateInfo->pApplicationInfo->pEngineName); + fprintf(stderr, " engineVersion: %u\n", pCreateInfo->pApplicationInfo->engineVersion); + fprintf(stderr, " apiVersion: %u\n", pCreateInfo->pApplicationInfo->apiVersion); + } else { + fprintf(stderr, "[Gamescope WSI] No application info given.\n"); + } + } { uint32_t appId = clientAppId(); From ddf0d76dd6a48ca35e580cda31b2bf4284dab5a3 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Thu, 19 Sep 2024 13:45:23 +0100 Subject: [PATCH 23/38] WaylandResource: Fix deleting resource `delete this` doesn't work on non-virtual base classes. Derp. Probably impacts #1520 --- src/WaylandServer/WaylandResource.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/WaylandServer/WaylandResource.h b/src/WaylandServer/WaylandResource.h index 05b41e5df2..0a5994a0c2 100644 --- a/src/WaylandServer/WaylandResource.h +++ b/src/WaylandServer/WaylandResource.h @@ -46,15 +46,6 @@ namespace gamescope::WaylandServer { } - void OnResourceDestroy() - { - m_pResource = nullptr; - m_pClient = nullptr; - m_uVersion = 0; - - delete this; - } - template static bool CheckAllocation( const T &object, wl_client *pClient ) { @@ -101,7 +92,7 @@ namespace gamescope::WaylandServer []( wl_resource *pResource ) { T *pObject = CWaylandResource::FromWlResource( pResource ); - pObject->OnResourceDestroy(); + delete pObject; }); return pThing; From ad02559210eedb57a985ddd30b477f9c8b87b261 Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Tue, 1 Oct 2024 16:27:15 -0700 Subject: [PATCH 24/38] repository: add issue templates --- .github/ISSUE_TEMPLATE/bug_report.yml | 88 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 9 +++ 2 files changed, 97 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..e994df75e3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,88 @@ +name: Issue report +description: File an issue report +body: +- type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues + required: true +- type: checkboxes + attributes: + label: Are you using any gamescope patches or a forked version of gamescope? + description: Please confirm any issues occur on upstream gamescope without any patches before filing an issue here. + options: + - label: The issue occurs on upstream gamescope without any modifications + required: true +- type: textarea + attributes: + label: Current Behavior + description: A concise description of the issue you're experiencing. + validations: + required: false +- type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the issue. + placeholder: | + 1. Launch Dota 2 from Steam with the gamescope launch command `gamescope -f -r 120 -- %command%`... + 2. Enter a bot match + 3. Move the cursor around + validations: + required: false +- type: textarea + attributes: + label: Hardware information + description: | + examples: + - **Distro**: SteamOS 3.6.15 (`cat /etc/os-release`) + - **CPU**: 32-core AMD Ryzen Threadripper 7970X (`inxi` or `cat /proc/cpuinfo`) + - **GPU**: Advanced Micro Devices [AMD/ATI] Navi 31 [Radeon RX 7900 XT/7900 XTX/7900M] (`lspci -nn | grep VGA` or `lshw -C display -numeric` or `vulkaninfo --summary | grep deviceName` + - **Driver Version**: Mesa 24.2.3 or NVIDIA 560.35.03 (`vulkaninfo --summary | grep driverInfo` or `nvidia-smi`) + value: | + - Distro: + - CPU: + - GPU: + - Driver Version: + render: markdown + validations: + required: false +- type: textarea + attributes: + label: Software information + description: | + examples: + - **Desktop environment**: KDE 6.1.5 + - **Session type**: wayland (`echo $XDG_SESSION_TYPE`) + - **Gamescope version**: gamescope version 3.15.9-8-gddf0d76 (gcc 14.2.1) (find this with `gamescope --version`) + - **Gamescope launch command(s)**: `gamescope -f -h 2160 -w 7680 -r 120 -- %command%` + value: | + - Desktop environment: + - Session type: + - Gamescope version: + - Gamescope launch command(s): + render: markdown + validations: + required: false +- type: checkboxes + id: backend + attributes: + label: Which gamescope backends have the issue you are reporting? + description: You may select more than one. + options: + - label: Wayland (default for nested gamescope) + - label: DRM (default for embedded gamescope, i.e. gamescope-session) + - label: SDL + - label: OpenVR + validations: + required: true +- type: textarea + attributes: + label: Logging, screenshots, or anything else + description: | + Please include any relevant logging or screenshots that will give us more context about the issue you are reporting. + + Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..7d1a9f05f8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,9 @@ +name: Feature request +description: Share ideas for new features +body: +- type: textarea + attributes: + label: Feature request + description: Share your idea for a new feature within gamescope + validations: + required: false From ca7bf9ff4a1987f358a27c173d46fad7a4645658 Mon Sep 17 00:00:00 2001 From: laurirasanen Date: Sun, 29 Sep 2024 17:58:42 +0300 Subject: [PATCH 25/38] readme: Fix Meson deprecated command warning --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8aa0227bf8..97dea45b3e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ If running RadeonSI clients with older cards (GFX8 and below), currently have to ``` git submodule update --init -meson build/ +meson setup build/ ninja -C build/ build/gamescope -- ``` From 69fe1d77009ea327134b8b8cb675cf9a296a11a8 Mon Sep 17 00:00:00 2001 From: William Allen Date: Mon, 7 Oct 2024 12:21:05 -0500 Subject: [PATCH 26/38] ci: Update actions/checkout to v4 (#1544) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d9755f113d..a6b0efc5ee 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,7 +19,7 @@ jobs: libxtst libxkbcommon libdrm libinput wayland-protocols benchmark \ xorg-xwayland pipewire cmake \ libavif libheif aom rav1e libdecor libxdamage - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive - name: Build with gcc From a493c58773c774c832ef426b5712b6f20629ee12 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Thu, 5 Sep 2024 07:00:14 +0100 Subject: [PATCH 27/38] script: Add beginnings of config + script system Port our known display configs to this system, and expose some basic features such as logging and printing convars. Very early work. See README.md in the scripts folder for more info. --- .github/workflows/main.yml | 3 +- default_scripts_install.sh | 6 + meson.build | 4 + scripts/00-gamescope/common/inspect.lua | 359 + scripts/00-gamescope/common/modegen.lua | 36 + scripts/00-gamescope/common/util.lua | 19 + .../displays/deckhd.steamdeck.deckhd-lcd.lua | 50 + .../displays/valve.steamdeck.lcd.lua | 73 + .../displays/valve.steamdeck.oled.lua | 100 + scripts/README.md | 75 + src/Backends/DRMBackend.cpp | 237 +- src/Script/Script.cpp | 288 + src/Script/Script.h | 184 + src/Utils/Dict.h | 23 + src/convar.cpp | 19 + src/convar.h | 81 +- src/gamescope_shared.h | 9 - src/log.cpp | 17 +- src/log.hpp | 3 +- src/main.cpp | 7 + src/meson.build | 10 +- src/modegen.cpp | 118 +- src/modegen.hpp | 2 +- src/reshade_effect_manager.cpp | 18 +- src/steamcompmgr.cpp | 5 + src/wlserver.cpp | 7 +- thirdparty/sol/config.hpp | 55 + thirdparty/sol/forward.hpp | 1340 + thirdparty/sol/sol.hpp | 29208 ++++++++++++++++ 29 files changed, 32080 insertions(+), 276 deletions(-) create mode 100755 default_scripts_install.sh create mode 100644 scripts/00-gamescope/common/inspect.lua create mode 100644 scripts/00-gamescope/common/modegen.lua create mode 100644 scripts/00-gamescope/common/util.lua create mode 100644 scripts/00-gamescope/displays/deckhd.steamdeck.deckhd-lcd.lua create mode 100644 scripts/00-gamescope/displays/valve.steamdeck.lcd.lua create mode 100644 scripts/00-gamescope/displays/valve.steamdeck.oled.lua create mode 100644 scripts/README.md create mode 100644 src/Script/Script.cpp create mode 100644 src/Script/Script.h create mode 100644 src/Utils/Dict.h create mode 100644 thirdparty/sol/config.hpp create mode 100644 thirdparty/sol/forward.hpp create mode 100644 thirdparty/sol/sol.hpp diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a6b0efc5ee..7b128d1ceb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,8 @@ jobs: sdl2 vulkan-headers libx11 libxmu libxcomposite libxrender libxres \ libxtst libxkbcommon libdrm libinput wayland-protocols benchmark \ xorg-xwayland pipewire cmake \ - libavif libheif aom rav1e libdecor libxdamage + libavif libheif aom rav1e libdecor libxdamage \ + luajit - uses: actions/checkout@v4 with: submodules: recursive diff --git a/default_scripts_install.sh b/default_scripts_install.sh new file mode 100755 index 0000000000..11751167e6 --- /dev/null +++ b/default_scripts_install.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +# Remove old Gamescope default configs and add our own. +mkdir -p "${DESTDIR}/${MESON_INSTALL_PREFIX}/share/gamescope" +rm -rf "${DESTDIR}/${MESON_INSTALL_PREFIX}/share/gamescope/scripts" || true +cp -r "${MESON_SOURCE_ROOT}/scripts" "${DESTDIR}/${MESON_INSTALL_PREFIX}/share/gamescope/scripts" diff --git a/meson.build b/meson.build index b562ec3512..c4924c7afb 100644 --- a/meson.build +++ b/meson.build @@ -38,6 +38,7 @@ add_project_arguments(cppc.get_supported_arguments([ ]), language: 'cpp') add_project_arguments(cppc.get_supported_arguments([ + '-fno-exceptions', '-ffast-math', ]), language: 'cpp') @@ -94,3 +95,6 @@ endif if get_option('enable_gamescope') subdir('src') endif + +# Handle default script/config stuff +meson.add_install_script('default_scripts_install.sh') diff --git a/scripts/00-gamescope/common/inspect.lua b/scripts/00-gamescope/common/inspect.lua new file mode 100644 index 0000000000..774c4c55a1 --- /dev/null +++ b/scripts/00-gamescope/common/inspect.lua @@ -0,0 +1,359 @@ +-- Slightly modified from https://github.com/kikito/inspect.lua +-- MIT license. + +local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local math = _tl_compat and _tl_compat.math or math; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table + +inspect = {Options = {}, } + +inspect._VERSION = 'inspect.lua 3.1.0' +inspect._URL = 'http://github.com/kikito/inspect.lua' +inspect._DESCRIPTION = 'human-readable representations of tables' +inspect._LICENSE = [[ + MIT LICENSE + + Copyright (c) 2022 Enrique García Cota + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] +inspect.KEY = setmetatable({}, { __tostring = function() return 'inspect.KEY' end }) +inspect.METATABLE = setmetatable({}, { __tostring = function() return 'inspect.METATABLE' end }) + +local tostring = tostring +local rep = string.rep +local match = string.match +local char = string.char +local gsub = string.gsub +local fmt = string.format + +local _rawget +if rawget then + _rawget = rawget +else + _rawget = function(t, k) return t[k] end +end + +local function rawpairs(t) + return next, t, nil +end + + + +local function smartQuote(str) + if match(str, '"') and not match(str, "'") then + return "'" .. str .. "'" + end + return '"' .. gsub(str, '"', '\\"') .. '"' +end + + +local shortControlCharEscapes = { + ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", + ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v", ["\127"] = "\\127", +} +local longControlCharEscapes = { ["\127"] = "\127" } +for i = 0, 31 do + local ch = char(i) + if not shortControlCharEscapes[ch] then + shortControlCharEscapes[ch] = "\\" .. i + longControlCharEscapes[ch] = fmt("\\%03d", i) + end +end + +local function escape(str) + return (gsub(gsub(gsub(str, "\\", "\\\\"), + "(%c)%f[0-9]", longControlCharEscapes), + "%c", shortControlCharEscapes)) +end + +local luaKeywords = { + ['and'] = true, + ['break'] = true, + ['do'] = true, + ['else'] = true, + ['elseif'] = true, + ['end'] = true, + ['false'] = true, + ['for'] = true, + ['function'] = true, + ['goto'] = true, + ['if'] = true, + ['in'] = true, + ['local'] = true, + ['nil'] = true, + ['not'] = true, + ['or'] = true, + ['repeat'] = true, + ['return'] = true, + ['then'] = true, + ['true'] = true, + ['until'] = true, + ['while'] = true, +} + +local function isIdentifier(str) + return type(str) == "string" and + not not str:match("^[_%a][_%a%d]*$") and + not luaKeywords[str] +end + +local flr = math.floor +local function isSequenceKey(k, sequenceLength) + return type(k) == "number" and + flr(k) == k and + 1 <= (k) and + k <= sequenceLength +end + +local defaultTypeOrders = { + ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4, + ['function'] = 5, ['userdata'] = 6, ['thread'] = 7, +} + +local function sortKeys(a, b) + local ta, tb = type(a), type(b) + + + if ta == tb and (ta == 'string' or ta == 'number') then + return (a) < (b) + end + + local dta = defaultTypeOrders[ta] or 100 + local dtb = defaultTypeOrders[tb] or 100 + + + return dta == dtb and ta < tb or dta < dtb +end + +local function getKeys(t) + + local seqLen = 1 + while _rawget(t, seqLen) ~= nil do + seqLen = seqLen + 1 + end + seqLen = seqLen - 1 + + local keys, keysLen = {}, 0 + for k in rawpairs(t) do + if not isSequenceKey(k, seqLen) then + keysLen = keysLen + 1 + keys[keysLen] = k + end + end + table.sort(keys, sortKeys) + return keys, keysLen, seqLen +end + +local function countCycles(x, cycles) + if type(x) == "table" then + if cycles[x] then + cycles[x] = cycles[x] + 1 + else + cycles[x] = 1 + for k, v in rawpairs(x) do + countCycles(k, cycles) + countCycles(v, cycles) + end + countCycles(getmetatable(x), cycles) + end + end +end + +local function makePath(path, a, b) + local newPath = {} + local len = #path + for i = 1, len do newPath[i] = path[i] end + + newPath[len + 1] = a + newPath[len + 2] = b + + return newPath +end + + +local function processRecursive(process, + item, + path, + visited) + if item == nil then return nil end + if visited[item] then return visited[item] end + + local processed = process(item, path) + if type(processed) == "table" then + local processedCopy = {} + visited[item] = processedCopy + local processedKey + + for k, v in rawpairs(processed) do + processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) + if processedKey ~= nil then + processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) + end + end + + local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) + if type(mt) ~= 'table' then mt = nil end + setmetatable(processedCopy, mt) + processed = processedCopy + end + return processed +end + +local function puts(buf, str) + buf.n = buf.n + 1 + buf[buf.n] = str +end + + + +local Inspector = {} + + + + + + + + + + +local Inspector_mt = { __index = Inspector } + +local function tabify(inspector) + puts(inspector.buf, inspector.newline .. rep(inspector.indent, inspector.level)) +end + +function Inspector:getId(v) + local id = self.ids[v] + local ids = self.ids + if not id then + local tv = type(v) + id = (ids[tv] or 0) + 1 + ids[v], ids[tv] = id, id + end + return tostring(id) +end + +function Inspector:putValue(v) + local buf = self.buf + local tv = type(v) + if tv == 'string' then + puts(buf, smartQuote(escape(v))) + elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or + tv == 'cdata' or tv == 'ctype' then + puts(buf, tostring(v)) + elseif tv == 'table' and not self.ids[v] then + local t = v + + if t == inspect.KEY or t == inspect.METATABLE then + puts(buf, tostring(t)) + elseif self.level >= self.depth then + puts(buf, '{...}') + else + if self.cycles[t] > 1 then puts(buf, fmt('<%d>', self:getId(t))) end + + local keys, keysLen, seqLen = getKeys(t) + + puts(buf, '{') + self.level = self.level + 1 + + for i = 1, seqLen + keysLen do + if i > 1 then puts(buf, ',') end + if i <= seqLen then + puts(buf, ' ') + self:putValue(t[i]) + else + local k = keys[i - seqLen] + tabify(self) + if isIdentifier(k) then + puts(buf, k) + else + puts(buf, "[") + self:putValue(k) + puts(buf, "]") + end + puts(buf, ' = ') + self:putValue(t[k]) + end + end + + local mt = getmetatable(t) + if type(mt) == 'table' then + if seqLen + keysLen > 0 then puts(buf, ',') end + tabify(self) + puts(buf, ' = ') + self:putValue(mt) + end + + self.level = self.level - 1 + + if keysLen > 0 or type(mt) == 'table' then + tabify(self) + elseif seqLen > 0 then + puts(buf, ' ') + end + + puts(buf, '}') + end + + else + puts(buf, fmt('<%s %d>', tv, self:getId(v))) + end +end + + + + +function inspect.inspect(root, options) + options = options or {} + + local depth = options.depth or (math.huge) + local newline = options.newline or '\n' + local indent = options.indent or ' ' + local process = options.process + + if process then + root = processRecursive(process, root, {}, {}) + end + + local cycles = {} + countCycles(root, cycles) + + local inspector = setmetatable({ + buf = { n = 0 }, + ids = {}, + cycles = cycles, + depth = depth, + level = 0, + newline = newline, + indent = indent, + }, Inspector_mt) + + inspector:putValue(root) + + return table.concat(inspector.buf) +end + +setmetatable(inspect, { + __call = function(_, root, options) + return inspect.inspect(root, options) + end, +}) + +return inspect diff --git a/scripts/00-gamescope/common/modegen.lua b/scripts/00-gamescope/common/modegen.lua new file mode 100644 index 0000000000..90db2ed7aa --- /dev/null +++ b/scripts/00-gamescope/common/modegen.lua @@ -0,0 +1,36 @@ +gamescope.modegen = {} + +gamescope.modegen.adjust_front_porch = function(mode, vfp) + local vsync = mode.vsync_end - mode.vsync_start + local vbp = mode.vtotal - mode.vsync_end + + mode.vsync_start = mode.vdisplay + vfp + mode.vsync_end = mode.vsync_start + vsync + mode.vtotal = mode.vsync_end + vbp +end + +gamescope.modegen.set_h_timings = function(mode, hfp, hsync, hbp) + mode.hsync_start = mode.hdisplay + hfp + mode.hsync_end = mode.hsync_start + hsync + mode.htotal = mode.hsync_end + hbp +end + +gamescope.modegen.set_v_timings = function(mode, vfp, vsync, vbp) + mode.vsync_start = mode.vdisplay + vfp + mode.vsync_end = mode.vsync_start + vsync + mode.vtotal = mode.vsync_end + vbp +end + +gamescope.modegen.set_resolution = function(mode, width, height) + mode.hdisplay = width + mode.vdisplay = height +end + +gamescope.modegen.calc_max_clock = function(mode, refresh) + -- LuaJIT does not have // operator, sad face. + return math.floor( ( ( mode.htotal * mode.vtotal * refresh ) + 999 ) / 1000 ) +end + +gamescope.modegen.calc_vrefresh = function(mode, refresh) + return math.floor( (1000 * mode.clock) / (mode.htotal * mode.vtotal) ) +end diff --git a/scripts/00-gamescope/common/util.lua b/scripts/00-gamescope/common/util.lua new file mode 100644 index 0000000000..98cf66b480 --- /dev/null +++ b/scripts/00-gamescope/common/util.lua @@ -0,0 +1,19 @@ +function zero_index(index) + return index + 1 +end + +function error(text) + gamescope.log(gamescope.log_priority.error, text) +end + +function warn(text) + gamescope.log(gamescope.log_priority.warning, text) +end + +function info(text) + gamescope.log(gamescope.log_priority.info, text) +end + +function debug(text) + gamescope.log(gamescope.log_priority.debug, text) +end diff --git a/scripts/00-gamescope/displays/deckhd.steamdeck.deckhd-lcd.lua b/scripts/00-gamescope/displays/deckhd.steamdeck.deckhd-lcd.lua new file mode 100644 index 0000000000..b23990e862 --- /dev/null +++ b/scripts/00-gamescope/displays/deckhd.steamdeck.deckhd-lcd.lua @@ -0,0 +1,50 @@ +gamescope.config.known_displays.steamdeck_deckhd_lcd = { + pretty_name = "Steam Deck DeckHD - Unofficial Screen Replacement", + dynamic_refresh_rates = { + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60 + }, + hdr = { + -- Setup some fallbacks or undocking with HDR + -- for this display. + supported = false, + force_enabled = false, + eotf = gamescope.eotf.gamma22, + max_content_light_level = 400, + max_frame_average_luminance = 400, + min_content_light_level = 0.5 + }, + -- No colorimetry was provided in the original PR, + -- and I have no idea if their BIOS mod/whatever has colorimetry + -- in their edid. + -- Someone that works on this or uses this should go fix that. + dynamic_modegen = function(base_mode, refresh) + debug("Generating mode "..refresh.."Hz for DeckHD") + local mode = base_mode + + -- These are only tuned for 1200x1920. + gamescope.modegen.set_resolution(mode, 1200, 1920) + + -- hfp, hsync, hbp + gamescope.modegen.set_h_timings(mode, 40, 20, 40) + -- vfp, vsync, vbp + gamescope.modegen.set_v_timings(mode, 18, 2, 20) + + mode.clock = gamescope.modegen.calc_max_clock(mode, refresh) + mode.vrefresh = gamescope.modegen.calc_vrefresh(mode) + + --debug(inspect(mode)) + return mode + end, + matches = function(display) + if display.vendor == "DHD" and display.model == "DeckHD-1200p" and display.product == 0x4001 then + debug("[steamdeck_deckhd_lcd] Matched vendor: "..value.vendor.." model: "..value.model.." product:"..value.product) + return 5000 + end + + return -1 + end +} +debug("Registered Steam Deck DeckHD - Unofficial Screen Replacement as a known display") +--debug(inspect(gamescope.config.known_displays.steamdeck_deckhd_lcd)) diff --git a/scripts/00-gamescope/displays/valve.steamdeck.lcd.lua b/scripts/00-gamescope/displays/valve.steamdeck.lcd.lua new file mode 100644 index 0000000000..5e97c4e4a8 --- /dev/null +++ b/scripts/00-gamescope/displays/valve.steamdeck.lcd.lua @@ -0,0 +1,73 @@ +-- TODO: Make a vec2 class so we can alias .x and indices. + +local steamdeck_lcd_colorimetry_spec = { + r = { x = 0.602, y = 0.355 }, + g = { x = 0.340, y = 0.574 }, + b = { x = 0.164, y = 0.121 }, + w = { x = 0.3070, y = 0.3220 } +} + +local steamdeck_lcd_colorimetry_measured = { + r = { x = 0.603, y = 0.349 }, + g = { x = 0.335, y = 0.571 }, + b = { x = 0.163, y = 0.115 }, + w = { x = 0.296, y = 0.307 } +} + +gamescope.config.known_displays.steamdeck_lcd = { + pretty_name = "Steam Deck LCD", + dynamic_refresh_rates = { + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60 + }, + hdr = { + -- Setup some fallbacks or undocking with HDR + -- for this display. + supported = false, + force_enabled = false, + eotf = gamescope.eotf.gamma22, + max_content_light_level = 500, + max_frame_average_luminance = 500, + min_content_light_level = 0.5 + }, + -- Use measured colorimetry instead. + --colorimetry = steamdeck_lcd_colorimetry_spec, + colorimetry = steamdeck_lcd_colorimetry_measured, + dynamic_modegen = function(base_mode, refresh) + debug("Generating mode "..refresh.."Hz for Steam Deck LCD") + local mode = base_mode + + -- These are only tuned for 800x1280. + gamescope.modegen.set_resolution(mode, 800, 1280) + + -- hfp, hsync, hbp + gamescope.modegen.set_h_timings(mode, 40, 4, 40) + -- vfp, vsync, vbp + gamescope.modegen.set_v_timings(mode, 30, 4, 8) + mode.clock = gamescope.modegen.calc_max_clock(mode, refresh) + mode.vrefresh = gamescope.modegen.calc_vrefresh(mode) + + --debug(inspect(mode)) + return mode + end, + matches = function(display) + local lcd_types = { + { vendor = "WLC", model = "ANX7530 U" }, + { vendor = "ANX", model = "ANX7530 U" }, + { vendor = "VLV", model = "ANX7530 U" }, + { vendor = "VLV", model = "Jupiter" }, + } + + for index, value in ipairs(lcd_types) do + if value.vendor == display.vendor and value.model == display.model then + debug("[steamdeck_lcd] Matched vendor: "..value.vendor.." model: "..value.model) + return 5000 + end + end + + return -1 + end +} +debug("Registered Steam Deck LCD as a known display") +--debug(inspect(gamescope.config.known_displays.steamdeck_lcd)) diff --git a/scripts/00-gamescope/displays/valve.steamdeck.oled.lua b/scripts/00-gamescope/displays/valve.steamdeck.oled.lua new file mode 100644 index 0000000000..8abc2218cb --- /dev/null +++ b/scripts/00-gamescope/displays/valve.steamdeck.oled.lua @@ -0,0 +1,100 @@ +local steamdeck_oled_hdr = { + supported = true, + force_enabled = true, + eotf = gamescope.eotf.gamma22, + max_content_light_level = 1000, + max_frame_average_luminance = 800, + min_content_light_level = 0 +} + +local steamdeck_oled_refresh_rates = { + 45, 47, 48, 49, + 50, 51, 53, 55, 56, 59, + 60, 62, 64, 65, 66, 68, + 72, 73, 76, 77, 78, + 80, 81, 82, 84, 85, 86, 87, 88, + 90 +} + +gamescope.config.known_displays.steamdeck_oled_sdc = { + pretty_name = "Steam Deck OLED (SDC)", + hdr = steamdeck_oled_hdr, + dynamic_refresh_rates = steamdeck_oled_refresh_rates, + dynamic_modegen = function(base_mode, refresh) + debug("Generating mode "..refresh.."Hz for Steam Deck OLED (SDC)") + local vfps = { + 1321, 1264, 1209, 1157, 1106, + 1058, 993, 967, 925, 883, 829, 805, 768, 732, 698, + 665, 632, 601, 571, 542, 501, 486, 459, 433, 408, + 383, 360, 337, 314, 292, 271, 250, 230, 210, 191, + 173, 154, 137, 119, 102, 86, 70, 54, 38, 23, + 9 + } + local vfp = vfps[zero_index(refresh - 45)] + if vfp == nil then + warn("Couldn't do refresh "..refresh.." on Steam Deck OLED (SDC)") + return base_mode + end + + local mode = base_mode + + gamescope.modegen.adjust_front_porch(mode, vfp) + mode.vrefresh = gamescope.modegen.calc_vrefresh(mode) + + --debug(inspect(mode)) + return mode + end, + matches = function(display) + if display.vendor == "VLV" and display.product == 0x3003 then + debug("[steamdeck_oled_sdc] Matched VLV and product 0x3003") + -- Higher priorty than LCD. + return 5100 + end + + return -1 + end +} +debug("Registered Steam Deck OLED (SDC) as a known display") +--debug(inspect(gamescope.config.known_displays.steamdeck_oled_sdc)) + +gamescope.config.known_displays.steamdeck_oled_boe = { + pretty_name = "Steam Deck OLED (BOE)", + hdr = steamdeck_oled_hdr, + dynamic_refresh_rates = steamdeck_oled_refresh_rates, + dynamic_modegen = function(base_mode, refresh) + debug("Generating mode for "..refresh.." for Steam Deck OLED (BOE)") + + local vfps = { + 1320, 1272, 1216, 1156, 1112, + 1064, 992, 972, 928, 888, 828, 808, 772, 736, 700, + 664, 636, 604, 572, 544, 500, 488, 460, 436, 408, + 384, 360, 336, 316, 292, 272, 252, 228, 212, 192, + 172, 152, 136, 120, 100, 84, 68, 52, 36, 20, + 8 + } + local vfp = vfps[zero_index(refresh - 45)] + if vfp == nil then + warn("Couldn't do refresh "..refresh.." on Steam Deck OLED (BOE)") + return base_mode + end + + local mode = base_mode + + gamescope.modegen.adjust_front_porch(mode, vfp) + mode.vrefresh = gamescope.modegen.calc_vrefresh(mode) + + --debug(inspect(mode)) + return mode + end, + matches = function(display) + if display.vendor == "VLV" and display.product == 0x3004 then + debug("[steamdeck_oled_boe] Matched VLV and product 0x3004") + -- Higher priorty than LCD. + return 5100 + end + + return -1 + end +} +debug("Registered Steam Deck OLED (BOE) as a known display") +--debug(inspect(gamescope.config.known_displays.steamdeck_oled_boe)) diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000000..337719b9c4 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,75 @@ +# Gamescope Script/Config Files + +## ⚠️ Health Warning ⚠️ + +Gamescope scripting/configuration is currently experimental and subject to change massively. + +Scripts and configs working between revisions is not guaranteed to work, it should at least not crash... probably. + +## The Basics + +Gamescope uses Lua for it's configuration and scripting system. + +Scripts ending in `.lua` are executed recursively in alphabetical order from the following directories: + - `/usr/share/gamescope` + - `/etc/gamescope` + - `$XDG_CONFIG_DIR/gamescope` + +You can develop easily without overriding your installation by setting `script_use_local_scripts` which will eliminate `/usr/share/gamescope` and `/etc/gamescope` from being read, and instead read from `../config` of where Gamescope is run instead of those. + +When errors are encountered, it will simply output that to the terminal. There is no visual indicator of this currently. + +Things should mostly fail-safe, unless you actually made an egregious mistake in your config like setting the refresh rate to 0 or the colorimetry to all 0, 0 or something. + +# Making modifications as a user + +If you wish to make modifications that will persist as a user, simply make a new `.lua` file in `$XDG_CONFIG_DIR/gamescope` which is usually `$HOME/.config/gamescope` with what you want to change. + +For example, to make the Steam Deck LCD use spec colorimetry instead of the measured colorimetry you could create the following file `~/.config/gamescope/my_deck_lcd_colorimetry.lua` with the following contents: + +```lua +local steamdeck_lcd_colorimetry_spec = { + r = { x = 0.602, y = 0.355 }, + g = { x = 0.340, y = 0.574 }, + b = { x = 0.164, y = 0.121 }, + w = { x = 0.3070, y = 0.3220 } +} + +gamescope.config.known_displays.steamdeck_lcd.colorimetry = steamdeck_lcd_colorimetry_spec +``` + +and it would override that. + +You could also place this in `/etc/gamescope` if you really want it to apply to all users/system-wide, but that would need root privelages. + +# Features + +Being able to set known displays (`gamescope.config.known_displays`) + +The ability to set convars. + +Hooks + +# Examples + +A script that will enable composite debug and force composition on and off every 60 frames. + +```lua +my_counter = 0 + +gamescope.convars.composite_debug.value = 3 + +gamescope.hook("OnPostPaint", function() + my_counter = my_counter + 1 + + if my_counter > 60 then + gamescope.convars.composite_force.value = not gamescope.convars.composite_force.value + my_counter = 0 + warn("Changed composite_force to "..tostring(gamescope.convars.composite_force.value)..".") + end +end) +``` + +# Hot Reloading? + +Coming soon... diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index c62635f190..3a996af72b 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -1,5 +1,7 @@ // DRM output stuff +#include "Script/Script.h" + #include #include #include @@ -287,7 +289,6 @@ namespace gamescope const char *GetModel() const override { return m_Mutable.szModel; } uint32_t GetPossibleCRTCMask() const { return m_Mutable.uPossibleCRTCMask; } std::span GetValidDynamicRefreshRates() const override { return m_Mutable.ValidDynamicRefreshRates; } - GamescopeKnownDisplays GetKnownDisplayType() const { return m_Mutable.eKnownDisplay; } const displaycolorimetry_t& GetDisplayColorimetry() const { return m_Mutable.DisplayColorimetry; } std::span GetRawEDID() const override { return std::span{ m_Mutable.EdidData.begin(), m_Mutable.EdidData.end() }; } @@ -372,11 +373,15 @@ namespace gamescope void UpdateEffectiveOrientation( const drmModeModeInfo *pMode ); + using DRMModeGenerator = std::function; + const DRMModeGenerator &GetModeGenerator() const + { + return m_Mutable.fnDynamicModeGenerator; + } + private: void ParseEDID(); - static std::optional GetKnownDisplayHDRInfo( GamescopeKnownDisplays eKnownDisplay ); - CAutoDeletePtr m_pConnector; struct MutableConnectorState @@ -388,8 +393,8 @@ namespace gamescope char szMakePNP[4]{}; char szModel[16]{}; const char *pszMake = ""; // Not owned, no free. This is a pointer to pnp db or szMakePNP. - GamescopeKnownDisplays eKnownDisplay = GAMESCOPE_KNOWN_DISPLAY_UNKNOWN; - std::span ValidDynamicRefreshRates{}; + std::vector ValidDynamicRefreshRates{}; + DRMModeGenerator fnDynamicModeGenerator; std::vector EdidData; // Raw, unmodified. std::vector BackendModes; @@ -2111,71 +2116,113 @@ namespace gamescope drm_log.infof("Connector %s -> %s - %s", m_Mutable.szName, m_Mutable.szMakePNP, m_Mutable.szModel ); - const bool bIsDeckHDUnofficial = ( m_Mutable.szMakePNP == "DHD"sv && m_Mutable.szModel == "DeckHD-1200p"sv ); + bool bHasKnownColorimetry = false; + bool bHasKnownHDRInfo = false; - const bool bSteamDeckDisplay = - ( m_Mutable.szMakePNP == "WLC"sv && m_Mutable.szModel == "ANX7530 U"sv ) || - ( m_Mutable.szMakePNP == "ANX"sv && m_Mutable.szModel == "ANX7530 U"sv ) || - ( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "ANX7530 U"sv ) || - ( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "Jupiter"sv ) || - ( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "Galileo"sv ); - - if ( bSteamDeckDisplay ) { - static constexpr uint32_t kPIDGalileoSDC = 0x3003; - static constexpr uint32_t kPIDGalileoBOE = 0x3004; + CScriptScopedLock script; - if ( pProduct->product == kPIDGalileoSDC ) - { - m_Mutable.eKnownDisplay = GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_SDC; - m_Mutable.ValidDynamicRefreshRates = std::span( s_kSteamDeckOLEDRates ); - } - else if ( pProduct->product == kPIDGalileoBOE ) - { - m_Mutable.eKnownDisplay = GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_BOE; - m_Mutable.ValidDynamicRefreshRates = std::span( s_kSteamDeckOLEDRates ); - } - else + auto oKnownDisplay = script.Manager().Gamescope().Config.LookupDisplay( script, m_Mutable.szMakePNP, pProduct->product, m_Mutable.szModel ); + if ( oKnownDisplay ) { - m_Mutable.eKnownDisplay = GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD; - m_Mutable.ValidDynamicRefreshRates = std::span( s_kSteamDeckLCDRates ); - } - } + sol::table tTable = oKnownDisplay->second; - if ( bIsDeckHDUnofficial ) - { - static constexpr uint32_t kPIDJupiterDHD = 0x4001; + std::string_view psvPrettyName = tTable.get_or( "pretty_name", std::string_view{ "Untitled Display" } ); + drm_log.infof( "Got known display: %.*s (%.*s)", + (int)oKnownDisplay->first.size(), oKnownDisplay->first.data(), + (int)psvPrettyName.size(), psvPrettyName.data() ); - if ( pProduct->product == kPIDJupiterDHD ) - { - m_Mutable.eKnownDisplay = GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD_DHD; - m_Mutable.ValidDynamicRefreshRates = std::span( s_kSteamDeckLCDRates ); - } - } + m_Mutable.fnDynamicModeGenerator = nullptr; + m_Mutable.ValidDynamicRefreshRates.clear(); - // Colorimetry - const char *pszColorOverride = getenv( "GAMESCOPE_INTERNAL_COLORIMETRY_OVERRIDE" ); - if ( pszColorOverride && *pszColorOverride && GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL ) - { - if ( sscanf( pszColorOverride, "%f %f %f %f %f %f %f %f", - &m_Mutable.DisplayColorimetry.primaries.r.x, &m_Mutable.DisplayColorimetry.primaries.r.y, - &m_Mutable.DisplayColorimetry.primaries.g.x, &m_Mutable.DisplayColorimetry.primaries.g.y, - &m_Mutable.DisplayColorimetry.primaries.b.x, &m_Mutable.DisplayColorimetry.primaries.b.y, - &m_Mutable.DisplayColorimetry.white.x, &m_Mutable.DisplayColorimetry.white.y ) == 8 ) - { - drm_log.infof( "[colorimetry]: GAMESCOPE_INTERNAL_COLORIMETRY_OVERRIDE detected" ); - } - else - { - drm_log.errorf( "[colorimetry]: GAMESCOPE_INTERNAL_COLORIMETRY_OVERRIDE specified, but could not parse \"rx ry gx gy bx by wx wy\"" ); + sol::optional otDynamicRefreshRates = tTable["dynamic_refresh_rates"]; + sol::optional ofnDynamicModegen = tTable["dynamic_modegen"]; + + if ( otDynamicRefreshRates && ofnDynamicModegen ) + { + m_Mutable.ValidDynamicRefreshRates = TableToVector( *otDynamicRefreshRates ); + + m_Mutable.fnDynamicModeGenerator = [ fnDynamicModegen = *ofnDynamicModegen ]( const drmModeModeInfo *pBaseMode, int nRefreshHz ) -> drmModeModeInfo + { + CScriptScopedLock script; + + sol::table tInMode = script->create_table(); + tInMode["clock"] = pBaseMode->clock; + + tInMode["hdisplay"] = pBaseMode->hdisplay; + tInMode["hsync_start"] = pBaseMode->hsync_start; + tInMode["hsync_end"] = pBaseMode->hsync_end; + tInMode["htotal"] = pBaseMode->htotal; + + tInMode["vdisplay"] = pBaseMode->vdisplay; + tInMode["vsync_start"] = pBaseMode->vsync_start; + tInMode["vsync_end"] = pBaseMode->vsync_end; + tInMode["vtotal"] = pBaseMode->vtotal; + + tInMode["vrefresh"] = pBaseMode->vrefresh; + + sol::function_result ret = fnDynamicModegen(tInMode, nRefreshHz); + if ( !ret.valid() || !ret.get() ) + return *pBaseMode; + + sol::table tOutMode = ret; + + drmModeModeInfo outMode = *pBaseMode; + outMode.clock = tOutMode["clock"]; + + outMode.hdisplay = tOutMode["hdisplay"]; + outMode.hsync_start = tOutMode["hsync_start"]; + outMode.hsync_end = tOutMode["hsync_end"]; + outMode.htotal = tOutMode["htotal"]; + + outMode.vdisplay = tOutMode["vdisplay"]; + outMode.vsync_start = tOutMode["vsync_start"]; + outMode.vsync_end = tOutMode["vsync_end"]; + outMode.vtotal = tOutMode["vtotal"]; + + outMode.vrefresh = tOutMode["vrefresh"]; + + snprintf( outMode.name, sizeof( outMode.name ), "%dx%d@%d.00", outMode.hdisplay, outMode.vdisplay, nRefreshHz ); + + return outMode; + }; + } + + if ( sol::optional otColorimetry = tTable["colorimetry"] ) + { + sol::table tColorimetry = *otColorimetry; + + // TODO: Add a vec2 + colorimetry type? + sol::optional otR = tColorimetry["r"]; + sol::optional otG = tColorimetry["g"]; + sol::optional otB = tColorimetry["b"]; + sol::optional otW = tColorimetry["w"]; + + if ( otR && otG && otB && otW ) + { + m_Mutable.DisplayColorimetry.primaries.r = TableToVec( *otR ); + m_Mutable.DisplayColorimetry.primaries.g = TableToVec( *otG ); + m_Mutable.DisplayColorimetry.primaries.b = TableToVec( *otB ); + m_Mutable.DisplayColorimetry.white = TableToVec( *otW ); + + bHasKnownColorimetry = true; + } + } + + if ( sol::optional otHDRInfo = tTable["hdr"] ) + { + m_Mutable.HDR.bExposeHDRSupport = otHDRInfo->get_or( "supported", false ); + m_Mutable.HDR.eOutputEncodingEOTF = otHDRInfo->get_or( "eotf", EOTF_Gamma22 ); + m_Mutable.HDR.uMaxContentLightLevel = nits_to_u16( otHDRInfo->get_or( "max_content_light_level", 400.0f ) ); + m_Mutable.HDR.uMaxFrameAverageLuminance = nits_to_u16( otHDRInfo->get_or( "max_frame_average_luminance", 400.0f ) ); + m_Mutable.HDR.uMinContentLightLevel = nits_to_u16_dark( otHDRInfo->get_or( "min_content_light_level", 0.1f ) ); + + bHasKnownHDRInfo = true; + } } } - else if ( m_Mutable.eKnownDisplay == GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD ) - { - drm_log.infof( "[colorimetry]: Steam Deck LCD detected. Using known colorimetry" ); - m_Mutable.DisplayColorimetry = displaycolorimetry_steamdeck_measured; - } - else + + if ( !bHasKnownColorimetry ) { // Steam Deck OLED has calibrated chromaticity coordinates in the EDID // for each unit. @@ -2191,6 +2238,11 @@ namespace gamescope .white = { pChroma->white_x, pChroma->white_y }, }; } + else + { + // Assume 709 if we have no data at all. + m_Mutable.DisplayColorimetry = displaycolorimetry_709; + } } drm_log.infof( "[colorimetry]: r %f %f", m_Mutable.DisplayColorimetry.primaries.r.x, m_Mutable.DisplayColorimetry.primaries.r.y ); @@ -2201,12 +2253,7 @@ namespace gamescope ///////////////////// // Parse HDR stuff. ///////////////////// - std::optional oKnownHDRInfo = GetKnownDisplayHDRInfo( m_Mutable.eKnownDisplay ); - if ( oKnownHDRInfo ) - { - m_Mutable.HDR = *oKnownHDRInfo; - } - else + if ( !bHasKnownHDRInfo ) { const di_cta_hdr_static_metadata_block *pHDRStaticMetadata = nullptr; const di_cta_colorimetry_block *pColorimetry = nullptr; @@ -2289,38 +2336,6 @@ namespace gamescope } } - /*static*/ std::optional CDRMConnector::GetKnownDisplayHDRInfo( GamescopeKnownDisplays eKnownDisplay ) - { - if ( eKnownDisplay == GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_BOE || eKnownDisplay == GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_SDC ) - { - // The stuff in the EDID for the HDR metadata does not fully - // reflect what we can achieve on the display by poking at more - // things out-of-band. - return BackendConnectorHDRInfo - { - .bExposeHDRSupport = true, - .eOutputEncodingEOTF = EOTF_Gamma22, - .uMaxContentLightLevel = nits_to_u16( 1000.0f ), - .uMaxFrameAverageLuminance = nits_to_u16( 800.0f ), // Full-frame sustained. - .uMinContentLightLevel = nits_to_u16_dark( 0 ), - }; - } - else if ( eKnownDisplay == GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD || eKnownDisplay == GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD_DHD ) - { - // Set up some HDR fallbacks for undocking - return BackendConnectorHDRInfo - { - .bExposeHDRSupport = false, - .eOutputEncodingEOTF = EOTF_Gamma22, - .uMaxContentLightLevel = nits_to_u16( 500.0f ), - .uMaxFrameAverageLuminance = nits_to_u16( 500.0f ), - .uMinContentLightLevel = nits_to_u16_dark( 0.5f ), - }; - } - - return std::nullopt; - } - ///////////////////////// // CDRMFb ///////////////////////// @@ -3015,17 +3030,25 @@ bool drm_set_refresh( struct drm_t *drm, int refresh ) } else { - /* TODO: check refresh is within the EDID limits */ - switch ( g_eGamescopeModeGeneration ) + if ( g_DRM.pConnector && g_DRM.pConnector->GetModeGenerator() ) { - case gamescope::GAMESCOPE_MODE_GENERATE_CVT: - generate_cvt_mode( &mode, width, height, refresh, true, false ); - break; - case gamescope::GAMESCOPE_MODE_GENERATE_FIXED: + const drmModeModeInfo *preferred_mode = find_mode(connector, 0, 0, 0); + mode = g_DRM.pConnector->GetModeGenerator()( preferred_mode, refresh ); + } + else + { + /* TODO: check refresh is within the EDID limits */ + switch ( g_eGamescopeModeGeneration ) { - const drmModeModeInfo *preferred_mode = find_mode(connector, 0, 0, 0); - generate_fixed_mode( &mode, preferred_mode, refresh, drm->pConnector->GetKnownDisplayType() ); + case gamescope::GAMESCOPE_MODE_GENERATE_CVT: + generate_cvt_mode( &mode, width, height, refresh, true, false ); break; + case gamescope::GAMESCOPE_MODE_GENERATE_FIXED: + { + const drmModeModeInfo *preferred_mode = find_mode(connector, 0, 0, 0); + generate_fixed_mode( &mode, preferred_mode, refresh ); + break; + } } } } diff --git a/src/Script/Script.cpp b/src/Script/Script.cpp new file mode 100644 index 0000000000..a104ee993b --- /dev/null +++ b/src/Script/Script.cpp @@ -0,0 +1,288 @@ +#include "Script.h" +#include "convar.h" +#include "color_helpers.h" +#include "../log.hpp" + +#include +#include + +std::string_view GetHomeDir(); + +namespace gamescope +{ + using namespace std::literals; + + static LogScope s_ScriptLog{ "script" }; + static LogScope s_ScriptMgrLog{ "scriptmgr" }; + + static ConVar cv_script_use_local_scripts{ "script_use_local_scripts", false, "Whether or not to use the local scripts (../config) as opposed to the ones in /etc/gamescope.d" }; + static ConVar cv_script_use_user_scripts{ "script_use_user_scripts", true, "Whether or not to use user config scripts ($XDG_CONFIG_DIR/gamescope) at all." }; + + static std::string_view GetConfigDir() + { + static std::string s_sConfigDir = []() -> std::string + { + const char *pszConfigHome = getenv( "XDG_CONFIG_HOME" ); + if ( pszConfigHome && *pszConfigHome ) + return pszConfigHome; + + return std::string{ GetHomeDir() } + "/.config"; + }(); + + return s_sConfigDir; + } + + static inline void PanicFunction( sol::optional oMsg ) + { + s_ScriptLog.errorf( "Lua is in a panic state and will now abort() the application" ); + if ( oMsg ) + { + s_ScriptLog.errorf( "\tError Message: %s", oMsg->c_str() ); + } + + abort(); + } + + static inline int ExceptionFunction( lua_State* pState, sol::optional oException, sol::string_view psvDescription ) + { + // L is the lua state, which you can wrap in a state_view if necessary + // maybe_exception will contain exception, if it exists + // description will either be the what() of the exception or a description saying that we hit the general-case catch(...) + + s_ScriptLog.errorf( "An exception occurred:\n %.*s", + (int)psvDescription.length(), psvDescription.data() ); + + // you must push 1 element onto the stack to be + // transported through as the error object in Lua + // note that Lua -- and 99.5% of all Lua users and libraries -- expects a string + // so we push a single string (in our case, the description of the error) + return sol::stack::push( pState, psvDescription ); + } + + static inline void LuaErrorHandler( const std::string &msg ) + { + s_ScriptLog.errorf( "An error occurred:\n %.*s", + (int)msg.length(), msg.data() ); + } + + int32_t CScriptManager::s_nNextScriptId = 0; + + CScriptManager &CScriptManager::GlobalScriptScope() + { + static CScriptManager s_State; + return s_State; + } + + CScriptManager::CScriptManager() + { + m_State.open_libraries(); + + static bool s_bSetDefaultHandler = false; + if ( !s_bSetDefaultHandler ) + { + m_State["_gamescope_error_handler"] = LuaErrorHandler; + + sol::protected_function::set_default_handler( m_State["_gamescope_error_handler"] ); + s_bSetDefaultHandler = true; + } + + m_State.set_panic( sol::c_call ); + m_State.set_exception_handler( &ExceptionFunction ); + + m_Gamescope.Base = m_State.create_named_table( "gamescope" ); + m_Gamescope.Base["hook"] = [this]( std::string_view svName, sol::function fnFunc ) + { + m_Hooks.emplace( std::make_pair( svName, Hook_t{ std::move( fnFunc ), m_nCurrentScriptId } ) ); + }; + m_Gamescope.Base.new_enum( "eotf", + { + { "gamma22", EOTF_Gamma22 }, + { "pq", EOTF_PQ }, + { "count", EOTF_Count }, + } + ); + m_Gamescope.Base.new_enum( "log_priority", + { + { "silent", LOG_SILENT }, + { "error", LOG_ERROR }, + { "warning", LOG_WARNING }, + { "info", LOG_INFO }, + { "debug", LOG_DEBUG }, + } + ); + m_Gamescope.Base["log"] = []( LogPriority ePriority, std::string_view svText ) { s_ScriptLog.log( ePriority, svText ); }; + + m_Gamescope.Convars.Base = m_State.create_table(); + m_Gamescope.Base.set( "convars", m_Gamescope.Convars.Base ); + + m_Gamescope.Config.Base = m_State.create_table(); + m_Gamescope.Base.set( "config", m_Gamescope.Config.Base ); + + m_Gamescope.Config.KnownDisplays = m_State.create_table(); + m_Gamescope.Config.Base.set( "known_displays", m_Gamescope.Config.KnownDisplays ); + } + + void CScriptManager::RunDefaultScripts() + { + if ( cv_script_use_local_scripts ) + { + RunFolder( "../scripts", true ); + } + else + { + RunFolder( "/usr/share/gamescope/scripts", true ); + RunFolder( "/etc/gamescope/scripts", true ); + } + + if ( cv_script_use_user_scripts ) + { + std::string sUserConfigs = std::string{ GetConfigDir() } + "/gamescope/scripts"; + RunFolder( sUserConfigs, true ); + } + } + + void CScriptManager::RunScriptText( std::string_view svContents ) + { + uint32_t uScriptId = s_nNextScriptId++; + + { + int32_t nPreviousScriptId = m_nCurrentScriptId; + + m_nCurrentScriptId = uScriptId; + State().script( svContents ); + m_nCurrentScriptId = nPreviousScriptId; + } + } + void CScriptManager::RunFile( std::string_view svPath ) + { + uint32_t uScriptId = s_nNextScriptId++; + + s_ScriptMgrLog.infof( "Running script file '%.*s' (id: %u)", + int( svPath.length() ), svPath.data(), + uScriptId ); + + std::string sPath = std::string( svPath ); + + { + int32_t nPreviousScriptId = m_nCurrentScriptId; + + m_nCurrentScriptId = uScriptId; + State().script_file( std::move( sPath ) ); + m_nCurrentScriptId = nPreviousScriptId; + } + } + bool CScriptManager::RunFolder( std::string_view svDirectory, bool bRecursive ) + { + s_ScriptMgrLog.infof( "Loading scripts from: '%.*s'", + int( svDirectory.size() ), svDirectory.data() ); + + std::filesystem::path dirConfig = std::filesystem::path{ svDirectory }; + if ( !std::filesystem::is_directory( dirConfig ) ) + { + s_ScriptMgrLog.warnf( "Directory '%.*s' does not exist", + int( svDirectory.size() ), svDirectory.data() ); + return false; + } + + if ( access( dirConfig.c_str(), R_OK | X_OK ) != 0 ) + { + s_ScriptMgrLog.warnf( "Cannot open directory '%.*s'", + int( svDirectory.size() ), svDirectory.data() ); + return false; + } + + std::vector sFiles; + std::vector sDirectories; + for ( const auto &iter : std::filesystem::directory_iterator( dirConfig ) ) + { + const std::filesystem::path &path = iter.path(); + // XXX: is_regular_file -> What about symlinks? + if ( std::filesystem::is_regular_file( iter.status() ) && path.extension() == ".lua"sv ) + { + sFiles.push_back( path ); + } + + if ( bRecursive && std::filesystem::is_directory( iter.status() ) ) + { + sDirectories.push_back( path ); + } + } + + std::sort( sFiles.begin(), sFiles.end() ); + std::sort( sDirectories.begin(), sDirectories.end() ); + + for ( const auto &sPath : sFiles ) + { + RunFile( sPath ); + } + + if ( bRecursive ) + { + for ( const auto &sPath : sDirectories ) + { + RunFolder( sPath, bRecursive ); + } + } + + return true; + } + + void CScriptManager::InvalidateAllHooks() + { + m_Hooks.clear(); + } + + void CScriptManager::InvalidateHooksForScript( int32_t nScriptId ) + { + if ( nScriptId < 0 ) + return; + + std::erase_if( m_Hooks, [ nScriptId ]( const auto &iter ) -> bool + { + return iter.second.nScriptId == nScriptId; + }); + } + + // + // GamescopeScript_t + // + + std::optional> GamescopeScript_t::Config_t::LookupDisplay( CScriptScopedLock &script, std::string_view psvVendor, uint16_t uProduct, std::string_view psvModel ) + { + int nMaxPrority = -1; + std::optional> oOutDisplay; + + sol::table tDisplay = script->create_table(); + tDisplay["vendor"] = psvVendor; + tDisplay["product"] = uProduct; + tDisplay["model"] = psvModel; + + for ( auto iter : KnownDisplays ) + { + sol::optional otTable = iter.second.as>(); + if ( !otTable ) + continue; + sol::table tTable = *otTable; + + sol::optional ofnMatches = tTable["matches"]; + if ( !ofnMatches ) + continue; + + sol::function fnMatches = *ofnMatches; + if ( !fnMatches ) + continue; + + int nPriority = fnMatches( tDisplay); + if ( nPriority <= nMaxPrority ) + continue; + + std::string_view psvKey = iter.first.as(); + + nMaxPrority = nPriority; + oOutDisplay = std::make_pair( psvKey, tTable ); + } + + return oOutDisplay; + } + +} diff --git a/src/Script/Script.h b/src/Script/Script.h new file mode 100644 index 0000000000..6eebb66a9f --- /dev/null +++ b/src/Script/Script.h @@ -0,0 +1,184 @@ +#pragma once + +#include "../Utils/Dict.h" + +#include + +#if HAVE_SCRIPTING + +#include + +namespace gamescope +{ + class CScriptScopedLock; + + struct GamescopeScript_t + { + // Stores table entries, and caches sol::table + // handles for things we use frequently. + + sol::table Base; + + struct ConVars_t + { + sol::table Base; + } Convars; + + struct Config_t + { + sol::table Base; + + sol::table KnownDisplays; + + std::optional> LookupDisplay( CScriptScopedLock &script, std::string_view psvVendor, uint16_t uProduct, std::string_view psvModel ); + } Config; + }; + + class CScriptManager + { + public: + CScriptManager(); + + template + void CallHook( std::string_view svName, Args&&... args ) + { + auto range = m_Hooks.equal_range( svName ); + for ( auto iter = range.first; iter != range.second; iter++ ) + { + int32_t nPreviousScriptId = m_nCurrentScriptId; + + Hook_t *pHook = &iter->second; + + m_nCurrentScriptId = pHook->nScriptId; + iter->second.fnCallback( std::forward( args )... ); + m_nCurrentScriptId = nPreviousScriptId; + } + } + + void RunDefaultScripts(); + + void RunScriptText( std::string_view svContents ); + + void RunFile( std::string_view svPath ); + bool RunFolder( std::string_view svPath, bool bRecursive = false ); + + void InvalidateAllHooks(); + void InvalidateHooksForScript( int32_t nScriptId ); + + sol::state *operator->() { return &m_State; } + + sol::state &State() { return m_State; } + GamescopeScript_t &Gamescope() { return m_Gamescope; } + + std::mutex &Mutex() { return m_mutMutex; } + + protected: + static CScriptManager &GlobalScriptScope(); + friend CScriptScopedLock; + private: + mutable std::mutex m_mutMutex; + + sol::state m_State; + GamescopeScript_t m_Gamescope; + + struct Hook_t + { + sol::function fnCallback; + int32_t nScriptId = -1; + }; + + MultiDict m_Hooks; + + int32_t m_nCurrentScriptId = -1; + + static int32_t s_nNextScriptId; + }; + + class CScriptScopedLock + { + public: + CScriptScopedLock() + : CScriptScopedLock{ CScriptManager::GlobalScriptScope() } + { + } + + CScriptScopedLock( CScriptManager &manager ) + : m_Lock{ manager.Mutex() } + , m_ScriptManager{ manager } + { + } + + ~CScriptScopedLock() + { + } + + CScriptManager &Manager() { return m_ScriptManager; } + sol::state *State() { return &m_ScriptManager.State(); } + + sol::state *operator ->() { return State(); } + private: + std::scoped_lock m_Lock; + CScriptManager &m_ScriptManager; + }; + + template + T TableToVec( const sol::table &table ) + { + if ( !table ) + return T{}; + + T out{}; + for ( int i = 0; i < T::length(); i++ ) + { + std::array ppsvIndices + { + "x", "y", "z", "w" + }; + + sol::optional ofValue = table[ppsvIndices[i]]; + out[i] = ofValue ? *ofValue : 0; + } + return out; + } + + template + std::vector TableToVector( const sol::table &table ) + { + std::vector out; + + if ( table ) + { + for ( auto &iter : table ) + { + sol::optional oValue = iter.second.as>(); + if ( oValue ) + out.emplace_back( *oValue ); + } + } + + return out; + } + + #define SCRIPTDESC_TEMPLATE( type ) template + #define DECLARE_SCRIPTDESC( type ) \ + static sol::usertype< type > s_ScriptType; \ + class CEnsureScriptTypeInstantiation { public: CEnsureScriptTypeInstantiation() { (void) type :: s_ScriptType; } } m_EnsureTemplateInstantiation_ScriptDesc; + + #define START_SCRIPTDESC( type, lua_name ) \ + inline sol::usertype type::s_ScriptType = CScriptScopedLock()->new_usertype( lua_name + #define START_SCRIPTDESC_ANON( type ) \ + inline sol::usertype type::s_ScriptType = CScriptScopedLock()->new_usertype( typeid( type ).name() + #define SCRIPTDESC( x, y ) , x, y + #define END_SCRIPTDESC() ); +} + +#else + + #define SCRIPTDESC_TEMPLATE( type ) + #define DECLARE_SCRIPTDESC( x ) + #define START_SCRIPTDESC( type, lua_name ) + #define START_SCRIPTDESC_ANON( type ) + #define SCRIPTDESC( x, y ) + #define END_SCRIPTDESC() + +#endif \ No newline at end of file diff --git a/src/Utils/Dict.h b/src/Utils/Dict.h new file mode 100644 index 0000000000..c17efd77c6 --- /dev/null +++ b/src/Utils/Dict.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +namespace gamescope +{ + struct StringHash + { + using is_transparent = void; + [[nodiscard]] size_t operator()( const char *string ) const { return std::hash{}( string ); } + [[nodiscard]] size_t operator()( std::string_view string ) const { return std::hash{}( string ); } + [[nodiscard]] size_t operator()( const std::string &string ) const { return std::hash{}( string ); } + }; + + template + using Dict = std::unordered_map>; + + template + using MultiDict = std::unordered_multimap>; +} diff --git a/src/convar.cpp b/src/convar.cpp index ed2d0b125c..7fdc8a450e 100644 --- a/src/convar.cpp +++ b/src/convar.cpp @@ -8,6 +8,25 @@ extern void PrintGamescopeVersion(); namespace gamescope { + ConCommand::ConCommand( std::string_view pszName, std::string_view pszDescription, ConCommandFunc func, bool bRegisterScript ) + : m_pszName{ pszName } + , m_pszDescription{ pszDescription } + , m_Func{ func } + { + assert( !GetCommands().contains( pszName ) ); + GetCommands()[ std::string( pszName ) ] = this; + +#if HAVE_SCRIPTING + if ( bRegisterScript ) + CScriptScopedLock().Manager().Gamescope().Convars.Base[pszName] = this; +#endif + } + + ConCommand::~ConCommand() + { + GetCommands().erase( GetCommands().find( m_pszName ) ); + } + bool ConCommand::Exec( std::span args ) { if ( args.size() < 1 ) diff --git a/src/convar.h b/src/convar.h index 0bcdcff7d8..5a90bfbdcd 100644 --- a/src/convar.h +++ b/src/convar.h @@ -12,6 +12,9 @@ #include #include +#include "Script/Script.h" +#include "Utils/Dict.h" + #include "log.hpp" extern LogScope console_log; @@ -62,10 +65,8 @@ namespace gamescope return false; } - inline std::vector Split( std::string_view string, std::string_view delims = " " ) + inline void Split( std::vector &tokens, std::string_view string, std::string_view delims = " " ) { - std::vector tokens; - size_t end = 0; for ( size_t start = 0; start < string.size() && end != std::string_view::npos; start = end + 1 ) { @@ -74,39 +75,24 @@ namespace gamescope if ( start != end ) tokens.emplace_back( string.substr( start, end-start ) ); } - - return tokens; } - struct StringHash + inline std::vector Split( std::string_view string, std::string_view delims = " " ) { - using is_transparent = void; - [[nodiscard]] size_t operator()( const char *string ) const { return std::hash{}( string ); } - [[nodiscard]] size_t operator()( std::string_view string ) const { return std::hash{}( string ); } - [[nodiscard]] size_t operator()( const std::string &string ) const { return std::hash{}( string ); } - }; - - template - using Dict = std::unordered_map>; + std::vector tokens; + Split( tokens, string, delims ); + return tokens; + } class ConCommand { using ConCommandFunc = std::function )>; public: - ConCommand( std::string_view pszName, std::string_view pszDescription, ConCommandFunc func ) - : m_pszName{ pszName } - , m_pszDescription{ pszDescription } - , m_Func{ func } - { - assert( !GetCommands().contains( pszName ) ); - GetCommands()[ std::string( pszName ) ] = this; - } + DECLARE_SCRIPTDESC( ConCommand ); - ~ConCommand() - { - GetCommands().erase( GetCommands().find( m_pszName ) ); - } + ConCommand( std::string_view pszName, std::string_view pszDescription, ConCommandFunc func, bool bRegisterScript = true ); + ~ConCommand(); void Invoke( std::span args ) { @@ -114,6 +100,16 @@ namespace gamescope m_Func( args ); } + // Calls it with space separated args. + void CallWithArgString( std::string_view args ) + { + std::vector sArgs; + sArgs.push_back( m_pszName ); + Split( sArgs, args, " " ); + + Invoke( sArgs ); + } + static bool Exec( std::span args ); std::string_view GetName() const { return m_pszName; } @@ -126,13 +122,21 @@ namespace gamescope ConCommandFunc m_Func; }; + START_SCRIPTDESC( ConCommand, "concommand" ) + SCRIPTDESC( "name", &ConCommand::m_pszName ) + SCRIPTDESC( "description", &ConCommand::m_pszDescription ) + SCRIPTDESC( "call", &ConCommand::CallWithArgString ) + END_SCRIPTDESC() + template class ConVar : public ConCommand { using ConVarCallbackFunc = std::function &)>; public: - ConVar( std::string_view pszName, T defaultValue = T{}, std::string_view pszDescription = "", ConVarCallbackFunc func = nullptr, bool bRunCallbackAtStartup = false ) - : ConCommand( pszName, pszDescription, [this]( std::span pArgs ){ this->InvokeFunc( pArgs ); } ) + DECLARE_SCRIPTDESC( ConVar ); + + ConVar( std::string_view pszName, T defaultValue = T{}, std::string_view pszDescription = "", ConVarCallbackFunc func = nullptr, bool bRunCallbackAtStartup = false, bool bRegisterScript = true ) + : ConCommand( pszName, pszDescription, [this]( std::span pArgs ){ this->InvokeFunc( pArgs ); }, false ) , m_Value{ defaultValue } , m_Callback{ func } { @@ -140,6 +144,13 @@ namespace gamescope { RunCallback(); } + +#if HAVE_SCRIPTING + if ( bRegisterScript ) + { + CScriptScopedLock().Manager().Gamescope().Convars.Base[pszName] = this; + } +#endif } const T& Get() const @@ -177,6 +188,10 @@ namespace gamescope template bool operator != ( const J &other ) const { return m_Value != other; } template auto operator <=>( const J &other ) const { return m_Value <=> other; } + template bool operator == ( const ConVar &other ) const { return *this == other.Get(); } + template bool operator != ( const ConVar &other ) const { return *this != other.Get(); } + template auto operator <=>( const ConVar &other ) const { return *this <=> other.Get(); } + T operator | (T other) { return m_Value | other; } T &operator |=(T other) { return m_Value |= other; } T operator & (T other) { return m_Value & other; } @@ -221,4 +236,14 @@ namespace gamescope ConVarCallbackFunc m_Callback; bool m_bInCallback; }; + + SCRIPTDESC_TEMPLATE( T ) + START_SCRIPTDESC_ANON( ConVar ) + SCRIPTDESC( "name", &ConVar::m_pszName ) + SCRIPTDESC( "description", &ConVar::m_pszDescription ) + SCRIPTDESC( "call", &ConVar::CallWithArgString ) + + SCRIPTDESC( "value", &ConVar::m_Value ) + END_SCRIPTDESC() + } diff --git a/src/gamescope_shared.h b/src/gamescope_shared.h index 5ce859123c..1ff54bdcff 100644 --- a/src/gamescope_shared.h +++ b/src/gamescope_shared.h @@ -6,15 +6,6 @@ namespace gamescope { class BackendBlob; - enum GamescopeKnownDisplays - { - GAMESCOPE_KNOWN_DISPLAY_UNKNOWN, - GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD, // Jupiter - GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD_DHD, // Jupiter Deck HD - GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_SDC, // Galileo SDC - GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_BOE, // Galileo BOE - }; - enum GamescopeModeGeneration { GAMESCOPE_MODE_GENERATE_CVT, diff --git a/src/log.cpp b/src/log.cpp index 50e5bb48c3..7b502a4ae1 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -104,18 +104,27 @@ void LogScope::vlogf(enum LogPriority priority, const char *fmt, va_list args) return; defer( free(buf); ); + std::string_view svBuf = buf; + log(priority, svBuf); +} + +void LogScope::log(enum LogPriority priority, std::string_view psvText) +{ + if ( !Enabled( priority ) ) + return; + for (auto& listener : m_LoggingListeners) - listener.second( priority, m_psvPrefix, buf ); + listener.second( priority, m_psvPrefix, psvText ); std::string_view psvLogName = GetLogPriorityText( priority ); if ( bPrefixEnabled ) - fprintf(stderr, "[%s] %.*s \e[0;37m%.*s:\e[0m %s\n", + fprintf(stderr, "[%s] %.*s \e[0;37m%.*s:\e[0m %.*s\n", gamescope::Process::GetProcessName(), (int)psvLogName.size(), psvLogName.data(), (int)this->m_psvPrefix.size(), this->m_psvPrefix.data(), - buf); + (int)psvText.size(), psvText.data()); else - fprintf(stderr, "%s\n", buf); + fprintf(stderr, "%.*s\n", (int)psvText.size(), psvText.data()); } void LogScope::logf(enum LogPriority priority, const char *fmt, ...) { diff --git a/src/log.hpp b/src/log.hpp index 5d28da6106..8225b0038e 100644 --- a/src/log.hpp +++ b/src/log.hpp @@ -35,6 +35,7 @@ class LogScope void SetPriority( LogPriority ePriority ) { m_eMaxPriority = ePriority; } void vlogf(enum LogPriority priority, const char *fmt, va_list args) ATTRIB_PRINTF(3, 0); + void log(enum LogPriority priority, std::string_view psvText); void warnf(const char *fmt, ...) ATTRIB_PRINTF(2, 3); void errorf(const char *fmt, ...) ATTRIB_PRINTF(2, 3); @@ -45,7 +46,7 @@ class LogScope bool bPrefixEnabled = true; - using LoggingListenerFunc = std::function; + using LoggingListenerFunc = std::function; std::unordered_map m_LoggingListeners; private: diff --git a/src/main.cpp b/src/main.cpp index a9cdaa2e21..c074f82e2e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,5 @@ +#include "Script/Script.h" + #include #include @@ -808,6 +810,11 @@ int main(int argc, char **argv) fprintf( stderr, "Tracing is enabled\n"); } + { + gamescope::CScriptScopedLock script; + script.Manager().RunDefaultScripts(); + } + XInitThreads(); g_mainThread = pthread_self(); diff --git a/src/meson.build b/src/meson.build index 5174de6700..74fc0334d4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -90,6 +90,8 @@ reshade_include = include_directories([ '../thirdparty/SPIRV-Headers/include/spirv/unified1' ]) +sol2_include = include_directories(['../thirdparty']) + required_wlroots_features = ['xwayland'] src = [ @@ -98,6 +100,7 @@ src = [ 'Utils/TempFiles.cpp', 'Utils/Version.cpp', 'Utils/Process.cpp', + 'Script/Script.cpp', 'BufferMemo.cpp', 'steamcompmgr.cpp', 'convar.cpp', @@ -118,6 +121,8 @@ src = [ 'InputEmulation.cpp', ] +luajit_dep = dependency( 'luajit' ) + gamescope_cpp_args = [] if drm_dep.found() src += 'Backends/DRMBackend.cpp' @@ -142,6 +147,7 @@ gamescope_cpp_args += '-DHAVE_SDL2=@0@'.format(sdl2_dep.found().to_int()) gamescope_cpp_args += '-DHAVE_AVIF=@0@'.format(avif_dep.found().to_int()) gamescope_cpp_args += '-DHAVE_LIBCAP=@0@'.format(cap_dep.found().to_int()) gamescope_cpp_args += '-DHAVE_LIBEIS=@0@'.format(eis_dep.found().to_int()) +gamescope_cpp_args += '-DHAVE_SCRIPTING=1' src += spirv_shaders src += protocols_server_src @@ -181,14 +187,14 @@ gamescope_version = configure_file( executable( 'gamescope', src, reshade_src, gamescope_version, - include_directories : [reshade_include], + include_directories : [reshade_include, sol2_include], dependencies: [ dep_wayland, dep_x11, dep_xdamage, dep_xcomposite, dep_xrender, dep_xext, dep_xfixes, dep_xxf86vm, dep_xres, glm_dep, drm_dep, wayland_server, xkbcommon, thread_dep, sdl2_dep, wlroots_dep, vulkan_dep, liftoff_dep, dep_xtst, dep_xmu, cap_dep, epoll_dep, pipewire_dep, librt_dep, stb_dep, displayinfo_dep, openvr_dep, dep_xcursor, avif_dep, dep_xi, - libdecor_dep, eis_dep, + libdecor_dep, eis_dep, luajit_dep ], install: true, cpp_args: gamescope_cpp_args, diff --git a/src/modegen.cpp b/src/modegen.cpp index 5dd113697c..1cf317c6c9 100644 --- a/src/modegen.cpp +++ b/src/modegen.cpp @@ -268,127 +268,13 @@ void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay, } } -// galielo SDC rev B ds.10 spec'd rates -// fps 45 48 51 55 60 65 72 80 90 -// VFP 1321 1157 993 829 665 501 337 173 9 - -// galielo BOE spec'd rates -// fps 45 48 51 55 60 65 72 80 90 -// VFP 1320 1156 992 828 664 500 336 172 8 - -// SDC VFP values for 45 Hz to 90 Hz -unsigned int galileo_sdc_vfp[] = -{ - 1321,1264,1209,1157,1106,1058,993,967,925,883,829,805,768,732,698, - 665,632,601,571,542,501,486,459,433,408,383,360,337,314,292,271,250,230,210,191, - 173,154,137,119,102,86,70,54,38,23,9 -}; - -// BOE VFP values for 45 Hz to 90 Hz -// BOE Vtotal must be a multiple of 4 -unsigned int galileo_boe_vfp[] = -{ - 1320,1272,1216,1156,1112,1064,992,972,928,888,828,808,772,736,700, - 664,636,604,572,544,500,488,460,436,408,384,360,336,316,292,272,252,228,212,192, - 172,152,136,120,100,84,68,52,36,20,8 -}; - -//SD LCD Stock Timings -#define JUPITER_BOE_PID 0x3001 -#define JUPITER_B_PID 0x3002 -#define JUPITER_HFP 40 -#define JUPITER_HSYNC 4 -#define JUPITER_HBP 40 -#define JUPITER_VFP 30 -#define JUPITER_VSYNC 4 -#define JUPITER_VBP 8 -//SD LCD DeckHD Timings -#define JUPITER_DHD_PID 0x4001 -#define JUPITER_DHD_HFP 40 -#define JUPITER_DHD_HSYNC 20 -#define JUPITER_DHD_HBP 40 -#define JUPITER_DHD_VFP 18 -#define JUPITER_DHD_VSYNC 2 -#define JUPITER_DHD_VBP 20 -//SD OLED SDC Timings -#define GALILEO_SDC_PID 0x3003 -#define GALILEO_SDC_VSYNC 1 -#define GALILEO_SDC_VBP 22 -//SD OLED BOE Timings -#define GALILEO_BOE_PID 0x3004 -#define GALILEO_BOE_VSYNC 2 -#define GALILEO_BOE_VBP 30 -#define GALILEO_MIN_REFRESH 45 -#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) - -unsigned int get_galileo_vfp( int vrefresh, unsigned int * vfp_array, unsigned int num_rates ) -{ - for ( unsigned int i = 0; i < num_rates; i++ ) { - if ( i+GALILEO_MIN_REFRESH == (unsigned int)vrefresh ) { - return vfp_array[i]; - } - } - return 0; -} - -void generate_fixed_mode(drmModeModeInfo *mode, const drmModeModeInfo *base, int vrefresh, gamescope::GamescopeKnownDisplays eKnownDisplay ) +void generate_fixed_mode(drmModeModeInfo *mode, const drmModeModeInfo *base, int vrefresh ) { *mode = *base; if (!vrefresh) vrefresh = 60; - if ( eKnownDisplay == gamescope::GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_SDC || - eKnownDisplay == gamescope::GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_BOE ) { - unsigned int vfp = 0, vsync = 0, vbp = 0; - if ( eKnownDisplay == gamescope::GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_SDC ) - { - vfp = get_galileo_vfp( vrefresh, galileo_sdc_vfp, ARRAY_SIZE(galileo_sdc_vfp) ); - // if we did not find a matching rate then we default to 60 Hz - if ( !vfp ) { - vrefresh = 60; - vfp = 665; - } - vsync = GALILEO_SDC_VSYNC; - vbp = GALILEO_SDC_VBP; - } else if ( eKnownDisplay == gamescope::GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_BOE ) { // BOE Panel - vfp = get_galileo_vfp( vrefresh, galileo_boe_vfp, ARRAY_SIZE(galileo_boe_vfp) ); - // if we did not find a matching rate then we default to 60 Hz - if ( !vfp ) { - vrefresh = 60; - vfp = 664; - } - vsync = GALILEO_BOE_VSYNC; - vbp = GALILEO_BOE_VBP; - } - mode->vsync_start = mode->vdisplay + vfp; - mode->vsync_end = mode->vsync_start + vsync; - mode->vtotal = mode->vsync_end + vbp; - } else { - if ( eKnownDisplay == gamescope::GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD_DHD ) { - mode->hdisplay = 1200; - mode->hsync_start = mode->hdisplay + JUPITER_DHD_HFP; - mode->hsync_end = mode->hsync_start + JUPITER_DHD_HSYNC; - mode->htotal = mode->hsync_end + JUPITER_DHD_HBP; - - mode->vdisplay = 1920; - mode->vsync_start = mode->vdisplay + JUPITER_DHD_VFP; - mode->vsync_end = mode->vsync_start + JUPITER_DHD_VSYNC; - mode->vtotal = mode->vsync_end + JUPITER_DHD_VBP; - } - else if ( eKnownDisplay == gamescope::GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD ) - { - mode->hdisplay = 800; - mode->hsync_start = mode->hdisplay + JUPITER_HFP; - mode->hsync_end = mode->hsync_start + JUPITER_HSYNC; - mode->htotal = mode->hsync_end + JUPITER_HBP; - - mode->vdisplay = 1280; - mode->vsync_start = mode->vdisplay + JUPITER_VFP; - mode->vsync_end = mode->vsync_start + JUPITER_VSYNC; - mode->vtotal = mode->vsync_end + JUPITER_VBP; - } - mode->clock = ( ( mode->htotal * mode->vtotal * vrefresh ) + 999 ) / 1000; - } + mode->clock = ( ( mode->htotal * mode->vtotal * vrefresh ) + 999 ) / 1000; mode->vrefresh = (1000 * mode->clock) / (mode->htotal * mode->vtotal); snprintf(mode->name, sizeof(mode->name), "%dx%d@%d.00", mode->hdisplay, mode->vdisplay, vrefresh); diff --git a/src/modegen.hpp b/src/modegen.hpp index 010d9cab33..7c2e690890 100644 --- a/src/modegen.hpp +++ b/src/modegen.hpp @@ -10,4 +10,4 @@ void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay, float vrefresh, bool reduced, bool interlaced); void generate_fixed_mode(drmModeModeInfo *mode, const drmModeModeInfo *base, - int vrefresh, gamescope::GamescopeKnownDisplays eKnownDisplay); + int vrefresh ); diff --git a/src/reshade_effect_manager.cpp b/src/reshade_effect_manager.cpp index 0ba5985dba..1d05cc5a37 100644 --- a/src/reshade_effect_manager.cpp +++ b/src/reshade_effect_manager.cpp @@ -36,14 +36,22 @@ static std::mutex g_runtimeUniformsMutex; const char *homedir; -static std::string GetLocalUsrDir() +std::string_view GetHomeDir() { - const char *homedir = nullptr; + static std::string s_sHomeDir = []() -> std::string + { + const char *pszHomeDir = getenv( "HOME" ); + if ( pszHomeDir ) + return pszHomeDir; - if ((homedir = getenv("HOME")) == nullptr) - homedir = getpwuid(getuid())->pw_dir; + return getpwuid( getuid() )->pw_dir; + }(); + return s_sHomeDir; +} - return std::string(homedir) + "/.local"; +static std::string GetLocalUsrDir() +{ + return std::string{ GetHomeDir() } + "/.local"; } static std::string GetUsrDir() diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 02148f423c..11a7cade6c 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -7997,6 +7997,11 @@ steamcompmgr_main(int argc, char **argv) hasRepaint = false; hasRepaintNonBasePlane = false; nIgnoredOverlayRepaints = 0; + + { + gamescope::CScriptScopedLock script; + script.Manager().CallHook( "OnPostPaint" ); + } } if ( bIsVBlankFromTimer ) diff --git a/src/wlserver.cpp b/src/wlserver.cpp index 8cd58dd6a0..78a86ee0e2 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -1154,11 +1154,14 @@ static const struct gamescope_private_interface gamescope_private_impl = { static void gamescope_private_bind( struct wl_client *client, void *data, uint32_t version, uint32_t id ) { struct wl_resource *resource = wl_resource_create( client, &gamescope_private_interface, version, id ); - console_log.m_LoggingListeners[(uintptr_t)resource] = [ resource ]( LogPriority ePriority, std::string_view psvScope, const char *pText ) + console_log.m_LoggingListeners[(uintptr_t)resource] = [ resource ]( LogPriority ePriority, std::string_view psvScope, std::string_view psvText ) { if ( !wlserver_is_lock_held() ) return; - gamescope_private_send_log( resource, pText ); + + // Can't send a length with string on Wayland api currently... :( + std::string sText = std::string{ psvText }; + gamescope_private_send_log( resource, sText.c_str() ); }; wl_resource_set_implementation( resource, &gamescope_private_impl, NULL, [](struct wl_resource *resource) diff --git a/thirdparty/sol/config.hpp b/thirdparty/sol/config.hpp new file mode 100644 index 0000000000..888c336c13 --- /dev/null +++ b/thirdparty/sol/config.hpp @@ -0,0 +1,55 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2020 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// This file was generated with a script. +// Generated 2024-09-03 03:46:49.901324 UTC +// This header was generated with sol v3.3.1 (revision 2b0d2fe8) +// https://github.com/ThePhD/sol2 + +#ifndef SOL_SINGLE_SOL_CONFIG_HPP +#define SOL_SINGLE_SOL_CONFIG_HPP + +// beginning of sol/config.hpp + +/* Base, empty configuration file! + + To override, place a file in your include paths of the form: + +. (your include path here) +| sol (directory, or equivalent) + | config.hpp (your config.hpp file) + + So that when sol2 includes the file + +#include + + it gives you the configuration values you desire. Configuration values can be +seen in the safety.rst of the doc/src, or at +https://sol2.readthedocs.io/en/latest/safety.html ! You can also pass them through +the build system, or the command line options of your compiler. + +*/ + +#define SOL_NO_EXCEPTIONS 1 + +// end of sol/config.hpp + +#endif // SOL_SINGLE_SOL_CONFIG_HPP diff --git a/thirdparty/sol/forward.hpp b/thirdparty/sol/forward.hpp new file mode 100644 index 0000000000..453bcdebf1 --- /dev/null +++ b/thirdparty/sol/forward.hpp @@ -0,0 +1,1340 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2020 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// This file was generated with a script. +// Generated 2024-09-03 03:46:49.899874 UTC +// This header was generated with sol v3.3.1 (revision 2b0d2fe8) +// https://github.com/ThePhD/sol2 + +#ifndef SOL_SINGLE_INCLUDE_SOL_FORWARD_HPP +#define SOL_SINGLE_INCLUDE_SOL_FORWARD_HPP + +// beginning of sol/forward.hpp + +#ifndef SOL_FORWARD_HPP +#define SOL_FORWARD_HPP + +// beginning of sol/version.hpp + +#include + +#define SOL_VERSION_MAJOR 3 +#define SOL_VERSION_MINOR 2 +#define SOL_VERSION_PATCH 3 +#define SOL_VERSION_STRING "3.2.3" +#define SOL_VERSION ((SOL_VERSION_MAJOR * 100000) + (SOL_VERSION_MINOR * 100) + (SOL_VERSION_PATCH)) + +#define SOL_TOKEN_TO_STRING_POST_EXPANSION_I_(_TOKEN) #_TOKEN +#define SOL_TOKEN_TO_STRING_I_(_TOKEN) SOL_TOKEN_TO_STRING_POST_EXPANSION_I_(_TOKEN) + +#define SOL_CONCAT_TOKENS_POST_EXPANSION_I_(_LEFT, _RIGHT) _LEFT##_RIGHT +#define SOL_CONCAT_TOKENS_I_(_LEFT, _RIGHT) SOL_CONCAT_TOKENS_POST_EXPANSION_I_(_LEFT, _RIGHT) + +#define SOL_RAW_IS_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) != 0) +#define SOL_RAW_IS_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3) == 0) +#define SOL_RAW_IS_DEFAULT_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) > 3) +#define SOL_RAW_IS_DEFAULT_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3 OP_SYMBOL 3) < 0) + +#define SOL_IS_ON(OP_SYMBOL) SOL_RAW_IS_ON(OP_SYMBOL ## _I_) +#define SOL_IS_OFF(OP_SYMBOL) SOL_RAW_IS_OFF(OP_SYMBOL ## _I_) +#define SOL_IS_DEFAULT_ON(OP_SYMBOL) SOL_RAW_IS_DEFAULT_ON(OP_SYMBOL ## _I_) +#define SOL_IS_DEFAULT_OFF(OP_SYMBOL) SOL_RAW_IS_DEFAULT_OFF(OP_SYMBOL ## _I_) + +#define SOL_ON | +#define SOL_OFF ^ +#define SOL_DEFAULT_ON + +#define SOL_DEFAULT_OFF - + +#if defined(SOL_BUILD_CXX_MODE) + #if (SOL_BUILD_CXX_MODE != 0) + #define SOL_BUILD_CXX_MODE_I_ SOL_ON + #else + #define SOL_BUILD_CXX_MODE_I_ SOL_OFF + #endif +#elif defined(__cplusplus) + #define SOL_BUILD_CXX_MODE_I_ SOL_DEFAULT_ON +#else + #define SOL_BUILD_CXX_MODE_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_BUILD_C_MODE) + #if (SOL_BUILD_C_MODE != 0) + #define SOL_BUILD_C_MODE_I_ SOL_ON + #else + #define SOL_BUILD_C_MODE_I_ SOL_OFF + #endif +#elif defined(__STDC__) + #define SOL_BUILD_C_MODE_I_ SOL_DEFAULT_ON +#else + #define SOL_BUILD_C_MODE_I_ SOL_DEFAULT_OFF +#endif + +#if SOL_IS_ON(SOL_BUILD_C_MODE) + #include + #include + #include +#else + #include + #include + #include +#endif + +#if defined(SOL_HAS_BUILTIN) + #define SOL_HAS_BUILTIN_I_(...) SOL_HAS_BUILTIN(__VA_ARGS__) +#elif defined(__has_builtin) + #define SOL_HAS_BUILTIN_I_(...) __has_builtin(__VA_ARGS__) +#else + #define SOL_HAS_BUILTIN_I_(...) 0 +#endif + +#if defined(SOL_COMPILER_VCXX) + #if (SOL_COMPILER_VCXX != 0) + #define SOL_COMPILER_VCXX_I_ SOL_ON + #else + #define SOL_COMPILER_VCXX_I_ SOL_OFF + #endif +#elif defined(_MSC_VER) + #define SOL_COMPILER_VCXX_I_ SOL_DEFAULT_ON +#else + #define SOL_COMPILER_VCXX_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_COMPILER_GCC) + #if (SOL_COMPILER_GCC != 0) + #define SOL_COMPILER_GCC_I_ SOL_ON + #else + #define SOL_COMPILER_GCC_I_ SOL_OFF + #endif +#elif defined(__GNUC__) + #define SOL_COMPILER_GCC_I_ SOL_DEFAULT_ON +#else + #define SOL_COMPILER_GCC_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_COMPILER_CLANG) + #if (SOL_COMPILER_CLANG != 0) + #define SOL_COMPILER_CLANG_I_ SOL_ON + #else + #define SOL_COMPILER_CLANG_I_ SOL_OFF + #endif +#elif defined(__clang__) + #define SOL_COMPILER_CLANG_I_ SOL_DEFAULT_ON +#else + #define SOL_COMPILER_CLANG_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_COMPILER_EDG) + #if (SOL_COMPILER_EDG != 0) + #define SOL_COMPILER_EDG_I_ SOL_ON + #else + #define SOL_COMPILER_EDG_I_ SOL_OFF + #endif +#else + #define SOL_COMPILER_EDG_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_COMPILER_MINGW) + #if (SOL_COMPILER_MINGW != 0) + #define SOL_COMPILER_MINGW_I_ SOL_ON + #else + #define SOL_COMPILER_MINGW_I_ SOL_OFF + #endif +#elif defined(__MINGW32__) + #define SOL_COMPILER_MINGW_I_ SOL_DEFAULT_ON +#else + #define SOL_COMPILER_MINGW_I_ SOL_DEFAULT_OFF +#endif + +#if SIZE_MAX <= 0xFFFFULL + #define SOL_PLATFORM_X16_I_ SOL_ON + #define SOL_PLATFORM_X86_I_ SOL_OFF + #define SOL_PLATFORM_X64_I_ SOL_OFF +#elif SIZE_MAX <= 0xFFFFFFFFULL + #define SOL_PLATFORM_X16_I_ SOL_OFF + #define SOL_PLATFORM_X86_I_ SOL_ON + #define SOL_PLATFORM_X64_I_ SOL_OFF +#else + #define SOL_PLATFORM_X16_I_ SOL_OFF + #define SOL_PLATFORM_X86_I_ SOL_OFF + #define SOL_PLATFORM_X64_I_ SOL_ON +#endif + +#define SOL_PLATFORM_ARM32_I_ SOL_OFF +#define SOL_PLATFORM_ARM64_I_ SOL_OFF + +#if defined(SOL_PLATFORM_WINDOWS) + #if (SOL_PLATFORM_WINDOWS != 0) + #define SOL_PLATFORM_WINDOWS_I_ SOL_ON + #else + #define SOL_PLATFORM_WINDOWS_I_ SOL_OFF + #endif +#elif defined(_WIN32) + #define SOL_PLATFORM_WINDOWS_I_ SOL_DEFAULT_ON +#else + #define SOL_PLATFORM_WINDOWS_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_PLATFORM_CYGWIN) + #if (SOL_PLATFORM_CYGWIN != 0) + #define SOL_PLATFORM_CYGWIN_I_ SOL_ON + #else + #define SOL_PLATFORM_CYGWIN_I_ SOL_ON + #endif +#elif defined(__CYGWIN__) + #define SOL_PLATFORM_CYGWIN_I_ SOL_DEFAULT_ON +#else + #define SOL_PLATFORM_CYGWIN_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_PLATFORM_APPLE) + #if (SOL_PLATFORM_APPLE != 0) + #define SOL_PLATFORM_APPLE_I_ SOL_ON + #else + #define SOL_PLATFORM_APPLE_I_ SOL_OFF + #endif +#elif defined(__APPLE__) + #define SOL_PLATFORM_APPLE_I_ SOL_DEFAULT_ON +#else + #define SOL_PLATFORM_APPLE_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_PLATFORM_UNIX) + #if (SOL_PLATFORM_UNIX != 0) + #define SOL_PLATFORM_UNIXLIKE_I_ SOL_ON + #else + #define SOL_PLATFORM_UNIXLIKE_I_ SOL_OFF + #endif +#elif defined(__unix__) + #define SOL_PLATFORM_UNIXLIKE_I_ SOL_DEFAULT_ON +#else + #define SOL_PLATFORM_UNIXLIKE_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_PLATFORM_LINUX) + #if (SOL_PLATFORM_LINUX != 0) + #define SOL_PLATFORM_LINUXLIKE_I_ SOL_ON + #else + #define SOL_PLATFORM_LINUXLIKE_I_ SOL_OFF + #endif +#elif defined(__LINUX__) + #define SOL_PLATFORM_LINUXLIKE_I_ SOL_DEFAULT_ON +#else + #define SOL_PLATFORM_LINUXLIKE_I_ SOL_DEFAULT_OFF +#endif + +#define SOL_PLATFORM_APPLE_IPHONE_I_ SOL_OFF +#define SOL_PLATFORM_BSDLIKE_I_ SOL_OFF + +#if defined(SOL_IN_DEBUG_DETECTED) + #if (SOL_IN_DEBUG_DETECTED != 0) + #define SOL_DEBUG_BUILD_I_ SOL_ON + #else + #define SOL_DEBUG_BUILD_I_ SOL_OFF + #endif +#elif !defined(NDEBUG) + #if SOL_IS_ON(SOL_COMPILER_VCXX) && defined(_DEBUG) + #define SOL_DEBUG_BUILD_I_ SOL_ON + #elif (SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)) && !defined(__OPTIMIZE__) + #define SOL_DEBUG_BUILD_I_ SOL_ON + #else + #define SOL_DEBUG_BUILD_I_ SOL_OFF + #endif +#else + #define SOL_DEBUG_BUILD_I_ SOL_DEFAULT_OFF +#endif // We are in a debug mode of some sort + +#if defined(SOL_NO_EXCEPTIONS) + #if (SOL_NO_EXCEPTIONS != 0) + #define SOL_EXCEPTIONS_I_ SOL_OFF + #else + #define SOL_EXCEPTIONS_I_ SOL_ON + #endif +#elif SOL_IS_ON(SOL_COMPILER_VCXX) + #if !defined(_CPPUNWIND) + #define SOL_EXCEPTIONS_I_ SOL_OFF + #else + #define SOL_EXCEPTIONS_I_ SOL_ON + #endif +#elif SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC) + #if !defined(__EXCEPTIONS) + #define SOL_EXCEPTIONS_I_ SOL_OFF + #else + #define SOL_EXCEPTIONS_I_ SOL_ON + #endif +#else + #define SOL_EXCEPTIONS_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_NO_RTTI) + #if (SOL_NO_RTTI != 0) + #define SOL_RTTI_I_ SOL_OFF + #else + #define SOL_RTTI_I_ SOL_ON + #endif +#elif SOL_IS_ON(SOL_COMPILER_VCXX) + #if !defined(_CPPRTTI) + #define SOL_RTTI_I_ SOL_OFF + #else + #define SOL_RTTI_I_ SOL_ON + #endif +#elif SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC) + #if !defined(__GXX_RTTI) + #define SOL_RTTI_I_ SOL_OFF + #else + #define SOL_RTTI_I_ SOL_ON + #endif +#else + #define SOL_RTTI_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_NO_THREAD_LOCAL) + #if (SOL_NO_THREAD_LOCAL != 0) + #define SOL_USE_THREAD_LOCAL_I_ SOL_OFF + #else + #define SOL_USE_THREAD_LOCAL_I_ SOL_ON + #endif +#else + #define SOL_USE_THREAD_LOCAL_I_ SOL_DEFAULT_ON +#endif // thread_local keyword is bjorked on some platforms + +#if defined(SOL_ALL_SAFETIES_ON) + #if (SOL_ALL_SAFETIES_ON != 0) + #define SOL_ALL_SAFETIES_ON_I_ SOL_ON + #else + #define SOL_ALL_SAFETIES_ON_I_ SOL_OFF + #endif +#else + #define SOL_ALL_SAFETIES_ON_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_SAFE_GETTER) + #if (SOL_SAFE_GETTER != 0) + #define SOL_SAFE_GETTER_I_ SOL_ON + #else + #define SOL_SAFE_GETTER_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_GETTER_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_SAFE_USERTYPE) + #if (SOL_SAFE_USERTYPE != 0) + #define SOL_SAFE_USERTYPE_I_ SOL_ON + #else + #define SOL_SAFE_USERTYPE_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_USERTYPE_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_SAFE_REFERENCES) + #if (SOL_SAFE_REFERENCES != 0) + #define SOL_SAFE_REFERENCES_I_ SOL_ON + #else + #define SOL_SAFE_REFERENCES_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_REFERENCES_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_SAFE_FUNCTIONS) + #if (SOL_SAFE_FUNCTIONS != 0) + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON + #else + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF + #endif +#elif defined (SOL_SAFE_FUNCTION_OBJECTS) + #if (SOL_SAFE_FUNCTION_OBJECTS != 0) + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON + #else + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_SAFE_FUNCTION_CALLS) + #if (SOL_SAFE_FUNCTION_CALLS != 0) + #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON + #else + #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_SAFE_PROXIES) + #if (SOL_SAFE_PROXIES != 0) + #define SOL_SAFE_PROXIES_I_ SOL_ON + #else + #define SOL_SAFE_PROXIES_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_PROXIES_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_SAFE_NUMERICS) + #if (SOL_SAFE_NUMERICS != 0) + #define SOL_SAFE_NUMERICS_I_ SOL_ON + #else + #define SOL_SAFE_NUMERICS_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_NUMERICS_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_ALL_INTEGER_VALUES_FIT) + #if (SOL_ALL_INTEGER_VALUES_FIT != 0) + #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_ON + #else + #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_OFF + #endif +#elif !SOL_IS_DEFAULT_OFF(SOL_SAFE_NUMERICS) && SOL_IS_OFF(SOL_SAFE_NUMERICS) + // if numerics is intentionally turned off, flip this on + #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_DEFAULT_ON +#else + // default to off + #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_SAFE_STACK_CHECK) + #if (SOL_SAFE_STACK_CHECK != 0) + #define SOL_SAFE_STACK_CHECK_I_ SOL_ON + #else + #define SOL_SAFE_STACK_CHECK_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_STACK_CHECK_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_NO_CHECK_NUMBER_PRECISION) + #if (SOL_NO_CHECK_NUMBER_PRECISION != 0) + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF + #else + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON + #endif +#elif defined(SOL_NO_CHECKING_NUMBER_PRECISION) + #if (SOL_NO_CHECKING_NUMBER_PRECISION != 0) + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF + #else + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON + #elif SOL_IS_ON(SOL_SAFE_NUMERICS) + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_ON + #else + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_STRINGS_ARE_NUMBERS) + #if (SOL_STRINGS_ARE_NUMBERS != 0) + #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_ON + #else + #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_OFF + #endif +#else + #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_ENABLE_INTEROP) + #if (SOL_ENABLE_INTEROP != 0) + #define SOL_USE_INTEROP_I_ SOL_ON + #else + #define SOL_USE_INTEROP_I_ SOL_OFF + #endif +#elif defined(SOL_USE_INTEROP) + #if (SOL_USE_INTEROP != 0) + #define SOL_USE_INTEROP_I_ SOL_ON + #else + #define SOL_USE_INTEROP_I_ SOL_OFF + #endif +#else + #define SOL_USE_INTEROP_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_NO_NIL) + #if (SOL_NO_NIL != 0) + #define SOL_NIL_I_ SOL_OFF + #else + #define SOL_NIL_I_ SOL_ON + #endif +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || defined(__OBJC__) || defined(nil) + #define SOL_NIL_I_ SOL_DEFAULT_OFF +#else + #define SOL_NIL_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_USERTYPE_TYPE_BINDING_INFO) + #if (SOL_USERTYPE_TYPE_BINDING_INFO != 0) + #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_ON + #else + #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_OFF + #endif +#else + #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_DEFAULT_ON +#endif // We should generate a my_type.__type table with lots of class information for usertypes + +#if defined(SOL_AUTOMAGICAL_TYPES_BY_DEFAULT) + #if (SOL_AUTOMAGICAL_TYPES_BY_DEFAULT != 0) + #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON + #else + #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF + #endif +#elif defined(SOL_DEFAULT_AUTOMAGICAL_USERTYPES) + #if (SOL_DEFAULT_AUTOMAGICAL_USERTYPES != 0) + #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON + #else + #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF + #endif +#else + #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_DEFAULT_ON +#endif // make is_automagical on/off by default + +#if defined(SOL_STD_VARIANT) + #if (SOL_STD_VARIANT != 0) + #define SOL_STD_VARIANT_I_ SOL_ON + #else + #define SOL_STD_VARIANT_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_COMPILER_CLANG) && SOL_IS_ON(SOL_PLATFORM_APPLE) + #if defined(__has_include) + #if __has_include() + #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON + #else + #define SOL_STD_VARIANT_I_ SOL_DEFAULT_OFF + #endif + #else + #define SOL_STD_VARIANT_I_ SOL_DEFAULT_OFF + #endif + #else + #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON + #endif +#endif // make is_automagical on/off by default + +#if defined(SOL_NOEXCEPT_FUNCTION_TYPE) + #if (SOL_NOEXCEPT_FUNCTION_TYPE != 0) + #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON + #else + #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF + #endif +#else + #if defined(__cpp_noexcept_function_type) + #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON + #elif SOL_IS_ON(SOL_COMPILER_VCXX) && (defined(_MSVC_LANG) && (_MSVC_LANG < 201403L)) + // There is a bug in the VC++ compiler?? + // on /std:c++latest under x86 conditions (VS 15.5.2), + // compiler errors are tossed for noexcept markings being on function types + // that are identical in every other way to their non-noexcept marked types function types... + // VS 2019: There is absolutely a bug. + #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF + #else + #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_DEFAULT_ON + #endif +#endif // noexcept is part of a function's type + +#if defined(SOL_STACK_STRING_OPTIMIZATION_SIZE) && (SOL_STACK_STRING_OPTIMIZATION_SIZE > 0) + #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ SOL_STACK_STRING_OPTIMIZATION_SIZE +#else + #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ 1024 +#endif + +#if defined(SOL_ID_SIZE) && (SOL_ID_SIZE > 0) + #define SOL_ID_SIZE_I_ SOL_ID_SIZE +#else + #define SOL_ID_SIZE_I_ 512 +#endif + +#if defined(LUA_IDSIZE) && (LUA_IDSIZE > 0) + #define SOL_FILE_ID_SIZE_I_ LUA_IDSIZE +#elif defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0 + #define SOL_FILE_ID_SIZE_I_ SOL_FILE_ID_SIZE +#else + #define SOL_FILE_ID_SIZE_I_ 2048 +#endif + +#if defined(SOL_PRINT_ERRORS) + #if (SOL_PRINT_ERRORS != 0) + #define SOL_PRINT_ERRORS_I_ SOL_ON + #else + #define SOL_PRINT_ERRORS_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_PRINT_ERRORS_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_PRINT_ERRORS_I_ SOL_DEFAULT_ON + #else + #define SOL_PRINT_ERRORS_I_ SOL_OFF + #endif +#endif + +#if defined(SOL_DEFAULT_PASS_ON_ERROR) + #if (SOL_DEFAULT_PASS_ON_ERROR != 0) + #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_ON + #else + #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_OFF + #endif +#else + #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_USING_CXX_LUA) + #if (SOL_USING_CXX_LUA != 0) + #define SOL_USING_CXX_LUA_I_ SOL_ON + #else + #define SOL_USING_CXX_LUA_I_ SOL_OFF + #endif +#elif defined(SOL_USE_CXX_LUA) + // alternative spelling + #if (SOL_USE_CXX_LUA != 0) + #define SOL_USING_CXX_LUA_I_ SOL_ON + #else + #define SOL_USING_CXX_LUA_I_ SOL_OFF + #endif +#else + #define SOL_USING_CXX_LUA_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_USING_CXX_LUAJIT) + #if (SOL_USING_CXX_LUAJIT != 0) + #define SOL_USING_CXX_LUAJIT_I_ SOL_ON + #else + #define SOL_USING_CXX_LUAJIT_I_ SOL_OFF + #endif +#elif defined(SOL_USE_CXX_LUAJIT) + #if (SOL_USE_CXX_LUAJIT != 0) + #define SOL_USING_CXX_LUAJIT_I_ SOL_ON + #else + #define SOL_USING_CXX_LUAJIT_I_ SOL_OFF + #endif +#else + #define SOL_USING_CXX_LUAJIT_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_NO_LUA_HPP) + #if (SOL_NO_LUA_HPP != 0) + #define SOL_USE_LUA_HPP_I_ SOL_OFF + #else + #define SOL_USE_LUA_HPP_I_ SOL_ON + #endif +#elif SOL_IS_ON(SOL_USING_CXX_LUA) + #define SOL_USE_LUA_HPP_I_ SOL_OFF +#elif defined(__has_include) + #if __has_include() + #define SOL_USE_LUA_HPP_I_ SOL_ON + #else + #define SOL_USE_LUA_HPP_I_ SOL_OFF + #endif +#else + #define SOL_USE_LUA_HPP_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_CONTAINERS_START) + #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START +#elif defined(SOL_CONTAINERS_START_INDEX) + #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START_INDEX +#elif defined(SOL_CONTAINER_START_INDEX) + #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINER_START_INDEX +#else + #define SOL_CONTAINER_START_INDEX_I_ 1 +#endif + +#if defined (SOL_NO_MEMORY_ALIGNMENT) + #if (SOL_NO_MEMORY_ALIGNMENT != 0) + #define SOL_ALIGN_MEMORY_I_ SOL_OFF + #else + #define SOL_ALIGN_MEMORY_I_ SOL_ON + #endif +#else + #define SOL_ALIGN_MEMORY_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_USE_BOOST) + #if (SOL_USE_BOOST != 0) + #define SOL_USE_BOOST_I_ SOL_ON + #else + #define SOL_USE_BOOST_I_ SOL_OFF + #endif +#else + #define SOL_USE_BOOST_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_USE_UNSAFE_BASE_LOOKUP) + #if (SOL_USE_UNSAFE_BASE_LOOKUP != 0) + #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_ON + #else + #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_OFF + #endif +#else + #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_INSIDE_UNREAL) + #if (SOL_INSIDE_UNREAL != 0) + #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_ON + #else + #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_OFF + #endif +#else + #if defined(UE_BUILD_DEBUG) || defined(UE_BUILD_DEVELOPMENT) || defined(UE_BUILD_TEST) || defined(UE_BUILD_SHIPPING) || defined(UE_SERVER) + #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_ON + #else + #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_NO_COMPAT) + #if (SOL_NO_COMPAT != 0) + #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_OFF + #else + #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_ON + #endif +#else + #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE) + #if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0) + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON + #else + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF + #endif +#else + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_CONTAINER_CHECK_IS_EXHAUSTIVE) + #if (SOL_CONTAINER_CHECK_IS_EXHAUSTIVE != 0) + #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_ON + #else + #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_OFF + #endif +#else + #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_FUNCTION_CALL_VALUE_SEMANTICS) + #if (SOL_FUNCTION_CALL_VALUE_SEMANTICS != 0) + #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_ON + #else + #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_OFF + #endif +#else + #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_MINGW_CCTYPE_IS_POISONED) + #if (SOL_MINGW_CCTYPE_IS_POISONED != 0) + #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON + #else + #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_OFF + #endif +#elif SOL_IS_ON(SOL_COMPILER_MINGW) && defined(__GNUC__) && (__GNUC__ < 6) + // MinGW is off its rocker in some places... + #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_ON +#else + #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_CHAR8_T) + #if (SOL_CHAR8_T != 0) + #define SOL_CHAR8_T_I_ SOL_ON + #else + #define SOL_CHAR8_T_I_ SOL_OFF + #endif +#else + #if defined(__cpp_char8_t) + #define SOL_CHAR8_T_I_ SOL_DEFAULT_ON + #else + #define SOL_CHAR8_T_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if SOL_IS_ON(SOL_USE_BOOST) + #include + + #if BOOST_VERSION >= 107500 // Since Boost 1.75.0 boost::none is constexpr + #define SOL_BOOST_NONE_CONSTEXPR_I_ constexpr + #else + #define SOL_BOOST_NONE_CONSTEXPR_I_ const + #endif // BOOST_VERSION +#else + // assume boost isn't using a garbage version + #define SOL_BOOST_NONE_CONSTEXPR_I_ constexpr +#endif + +#if defined(SOL2_CI) + #if (SOL2_CI != 0) + #define SOL2_CI_I_ SOL_ON + #else + #define SOL2_CI_I_ SOL_OFF + #endif +#else + #define SOL2_CI_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_ASSERT) + #define SOL_USER_ASSERT_I_ SOL_ON +#else + #define SOL_USER_ASSERT_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_ASSERT_MSG) + #define SOL_USER_ASSERT_MSG_I_ SOL_ON +#else + #define SOL_USER_ASSERT_MSG_I_ SOL_DEFAULT_OFF +#endif + +// beginning of sol/prologue.hpp + +#if defined(SOL_PROLOGUE_I_) + #error "[sol2] Library Prologue was already included in translation unit and not properly ended with an epilogue." +#endif + +#define SOL_PROLOGUE_I_ 1 + +#if SOL_IS_ON(SOL_BUILD_CXX_MODE) + #define _FWD(...) static_cast( __VA_ARGS__ ) + + #if SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG) + #define _MOVE(...) static_cast<__typeof( __VA_ARGS__ )&&>( __VA_ARGS__ ) + #else + #include + + #define _MOVE(...) static_cast<::std::remove_reference_t<( __VA_ARGS__ )>&&>( __VA_OPT__(,) ) + #endif +#endif + +// end of sol/prologue.hpp + +// beginning of sol/epilogue.hpp + +#if !defined(SOL_PROLOGUE_I_) + #error "[sol2] Library Prologue is missing from this translation unit." +#else + #undef SOL_PROLOGUE_I_ +#endif + +#if SOL_IS_ON(SOL_BUILD_CXX_MODE) + #undef _FWD + #undef _MOVE +#endif + +// end of sol/epilogue.hpp + +// beginning of sol/detail/build_version.hpp + +#if defined(SOL_DLL) + #if (SOL_DLL != 0) + #define SOL_DLL_I_ SOL_ON + #else + #define SOL_DLL_I_ SOL_OFF + #endif +#elif SOL_IS_ON(SOL_COMPILER_VCXX) && (defined(DLL_) || defined(_DLL)) + #define SOL_DLL_I_ SOL_DEFAULT_ON +#else + #define SOL_DLL_I_ SOL_DEFAULT_OFF +#endif // DLL definition + +#if defined(SOL_HEADER_ONLY) + #if (SOL_HEADER_ONLY != 0) + #define SOL_HEADER_ONLY_I_ SOL_ON + #else + #define SOL_HEADER_ONLY_I_ SOL_OFF + #endif +#else + #define SOL_HEADER_ONLY_I_ SOL_DEFAULT_OFF +#endif // Header only library + +#if defined(SOL_BUILD) + #if (SOL_BUILD != 0) + #define SOL_BUILD_I_ SOL_ON + #else + #define SOL_BUILD_I_ SOL_OFF + #endif +#elif SOL_IS_ON(SOL_HEADER_ONLY) + #define SOL_BUILD_I_ SOL_DEFAULT_OFF +#else + #define SOL_BUILD_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_UNITY_BUILD) + #if (SOL_UNITY_BUILD != 0) + #define SOL_UNITY_BUILD_I_ SOL_ON + #else + #define SOL_UNITY_BUILD_I_ SOL_OFF + #endif +#else + #define SOL_UNITY_BUILD_I_ SOL_DEFAULT_OFF +#endif // Header only library + +#if defined(SOL_C_FUNCTION_LINKAGE) + #define SOL_C_FUNCTION_LINKAGE_I_ SOL_C_FUNCTION_LINKAGE +#else + #if SOL_IS_ON(SOL_BUILD_CXX_MODE) + // C++ + #define SOL_C_FUNCTION_LINKAGE_I_ extern "C" + #else + // normal + #define SOL_C_FUNCTION_LINKAGE_I_ + #endif // C++ or not +#endif // Linkage specification for C functions + +#if defined(SOL_API_LINKAGE) + #define SOL_API_LINKAGE_I_ SOL_API_LINKAGE +#else + #if SOL_IS_ON(SOL_DLL) + #if SOL_IS_ON(SOL_COMPILER_VCXX) || SOL_IS_ON(SOL_PLATFORM_WINDOWS) || SOL_IS_ON(SOL_PLATFORM_CYGWIN) + // MSVC Compiler; or, Windows, or Cygwin platforms + #if SOL_IS_ON(SOL_BUILD) + // Building the library + #if SOL_IS_ON(SOL_COMPILER_GCC) + // Using GCC + #define SOL_API_LINKAGE_I_ __attribute__((dllexport)) + #else + // Using Clang, MSVC, etc... + #define SOL_API_LINKAGE_I_ __declspec(dllexport) + #endif + #else + #if SOL_IS_ON(SOL_COMPILER_GCC) + #define SOL_API_LINKAGE_I_ __attribute__((dllimport)) + #else + #define SOL_API_LINKAGE_I_ __declspec(dllimport) + #endif + #endif + #else + // extern if building normally on non-MSVC + #define SOL_API_LINKAGE_I_ extern + #endif + #elif SOL_IS_ON(SOL_UNITY_BUILD) + // Built-in library, like how stb typical works + #if SOL_IS_ON(SOL_HEADER_ONLY) + // Header only, so functions are defined "inline" + #define SOL_API_LINKAGE_I_ inline + #else + // Not header only, so seperately compiled files + #define SOL_API_LINKAGE_I_ extern + #endif + #else + // Normal static library + #if SOL_IS_ON(SOL_BUILD_CXX_MODE) + #define SOL_API_LINKAGE_I_ + #else + #define SOL_API_LINKAGE_I_ extern + #endif + #endif // DLL or not +#endif // Build definitions + +#if defined(SOL_PUBLIC_FUNC_DECL) + #define SOL_PUBLIC_FUNC_DECL_I_ SOL_PUBLIC_FUNC_DECL +#else + #define SOL_PUBLIC_FUNC_DECL_I_ SOL_API_LINKAGE_I_ +#endif + +#if defined(SOL_INTERNAL_FUNC_DECL_) + #define SOL_INTERNAL_FUNC_DECL_I_ SOL_INTERNAL_FUNC_DECL_ +#else + #define SOL_INTERNAL_FUNC_DECL_I_ SOL_API_LINKAGE_I_ +#endif + +#if defined(SOL_PUBLIC_FUNC_DEF) + #define SOL_PUBLIC_FUNC_DEF_I_ SOL_PUBLIC_FUNC_DEF +#else + #define SOL_PUBLIC_FUNC_DEF_I_ SOL_API_LINKAGE_I_ +#endif + +#if defined(SOL_INTERNAL_FUNC_DEF) + #define SOL_INTERNAL_FUNC_DEF_I_ SOL_INTERNAL_FUNC_DEF +#else + #define SOL_INTERNAL_FUNC_DEF_I_ SOL_API_LINKAGE_I_ +#endif + +#if defined(SOL_FUNC_DECL) + #define SOL_FUNC_DECL_I_ SOL_FUNC_DECL +#elif SOL_IS_ON(SOL_HEADER_ONLY) + #define SOL_FUNC_DECL_I_ +#elif SOL_IS_ON(SOL_DLL) + #if SOL_IS_ON(SOL_COMPILER_VCXX) + #if SOL_IS_ON(SOL_BUILD) + #define SOL_FUNC_DECL_I_ extern __declspec(dllexport) + #else + #define SOL_FUNC_DECL_I_ extern __declspec(dllimport) + #endif + #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG) + #define SOL_FUNC_DECL_I_ extern __attribute__((visibility("default"))) + #else + #define SOL_FUNC_DECL_I_ extern + #endif +#endif + +#if defined(SOL_FUNC_DEFN) + #define SOL_FUNC_DEFN_I_ SOL_FUNC_DEFN +#elif SOL_IS_ON(SOL_HEADER_ONLY) + #define SOL_FUNC_DEFN_I_ inline +#elif SOL_IS_ON(SOL_DLL) + #if SOL_IS_ON(SOL_COMPILER_VCXX) + #if SOL_IS_ON(SOL_BUILD) + #define SOL_FUNC_DEFN_I_ __declspec(dllexport) + #else + #define SOL_FUNC_DEFN_I_ __declspec(dllimport) + #endif + #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG) + #define SOL_FUNC_DEFN_I_ __attribute__((visibility("default"))) + #else + #define SOL_FUNC_DEFN_I_ + #endif +#endif + +#if defined(SOL_HIDDEN_FUNC_DECL) + #define SOL_HIDDEN_FUNC_DECL_I_ SOL_HIDDEN_FUNC_DECL +#elif SOL_IS_ON(SOL_HEADER_ONLY) + #define SOL_HIDDEN_FUNC_DECL_I_ +#elif SOL_IS_ON(SOL_DLL) + #if SOL_IS_ON(SOL_COMPILER_VCXX) + #if SOL_IS_ON(SOL_BUILD) + #define SOL_HIDDEN_FUNC_DECL_I_ extern __declspec(dllexport) + #else + #define SOL_HIDDEN_FUNC_DECL_I_ extern __declspec(dllimport) + #endif + #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG) + #define SOL_HIDDEN_FUNC_DECL_I_ extern __attribute__((visibility("default"))) + #else + #define SOL_HIDDEN_FUNC_DECL_I_ extern + #endif +#endif + +#if defined(SOL_HIDDEN_FUNC_DEFN) + #define SOL_HIDDEN_FUNC_DEFN_I_ SOL_HIDDEN_FUNC_DEFN +#elif SOL_IS_ON(SOL_HEADER_ONLY) + #define SOL_HIDDEN_FUNC_DEFN_I_ inline +#elif SOL_IS_ON(SOL_DLL) + #if SOL_IS_ON(SOL_COMPILER_VCXX) + #if SOL_IS_ON(SOL_BUILD) + #define SOL_HIDDEN_FUNC_DEFN_I_ + #else + #define SOL_HIDDEN_FUNC_DEFN_I_ + #endif + #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG) + #define SOL_HIDDEN_FUNC_DEFN_I_ __attribute__((visibility("hidden"))) + #else + #define SOL_HIDDEN_FUNC_DEFN_I_ + #endif +#endif + +// end of sol/detail/build_version.hpp + +// end of sol/version.hpp + +#include +#include +#include + +#if SOL_IS_ON(SOL_USING_CXX_LUA) || SOL_IS_ON(SOL_USING_CXX_LUAJIT) +struct lua_State; +#else +extern "C" { +struct lua_State; +} +#endif // C++ Mangling for Lua vs. Not + +namespace sol { + + enum class type; + + class stateless_reference; + template + class basic_reference; + using reference = basic_reference; + using main_reference = basic_reference; + class stateless_stack_reference; + class stack_reference; + + template + class basic_bytecode; + + struct lua_value; + + struct proxy_base_tag; + template + struct proxy_base; + template + struct table_proxy; + + template + class basic_table_core; + template + using table_core = basic_table_core; + template + using main_table_core = basic_table_core; + template + using stack_table_core = basic_table_core; + template + using basic_table = basic_table_core; + using table = table_core; + using global_table = table_core; + using main_table = main_table_core; + using main_global_table = main_table_core; + using stack_table = stack_table_core; + using stack_global_table = stack_table_core; + + template + struct basic_lua_table; + using lua_table = basic_lua_table; + using stack_lua_table = basic_lua_table; + + template + class basic_usertype; + template + using usertype = basic_usertype; + template + using stack_usertype = basic_usertype; + + template + class basic_metatable; + using metatable = basic_metatable; + using stack_metatable = basic_metatable; + + template + struct basic_environment; + using environment = basic_environment; + using main_environment = basic_environment; + using stack_environment = basic_environment; + + template + class basic_function; + template + class basic_protected_function; + using unsafe_function = basic_function; + using safe_function = basic_protected_function; + using main_unsafe_function = basic_function; + using main_safe_function = basic_protected_function; + using stack_unsafe_function = basic_function; + using stack_safe_function = basic_protected_function; + using stack_aligned_unsafe_function = basic_function; + using stack_aligned_safe_function = basic_protected_function; + using protected_function = safe_function; + using main_protected_function = main_safe_function; + using stack_protected_function = stack_safe_function; + using stack_aligned_protected_function = stack_aligned_safe_function; +#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS) + using function = protected_function; + using main_function = main_protected_function; + using stack_function = stack_protected_function; + using stack_aligned_function = stack_aligned_safe_function; +#else + using function = unsafe_function; + using main_function = main_unsafe_function; + using stack_function = stack_unsafe_function; + using stack_aligned_function = stack_aligned_unsafe_function; +#endif + using stack_aligned_stack_handler_function = basic_protected_function; + + struct unsafe_function_result; + struct protected_function_result; + using safe_function_result = protected_function_result; +#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS) + using function_result = safe_function_result; +#else + using function_result = unsafe_function_result; +#endif + + template + class basic_object_base; + template + class basic_object; + template + class basic_userdata; + template + class basic_lightuserdata; + template + class basic_coroutine; + template + class basic_packaged_coroutine; + template + class basic_thread; + + using object = basic_object; + using userdata = basic_userdata; + using lightuserdata = basic_lightuserdata; + using thread = basic_thread; + using coroutine = basic_coroutine; + using packaged_coroutine = basic_packaged_coroutine; + using main_object = basic_object; + using main_userdata = basic_userdata; + using main_lightuserdata = basic_lightuserdata; + using main_coroutine = basic_coroutine; + using stack_object = basic_object; + using stack_userdata = basic_userdata; + using stack_lightuserdata = basic_lightuserdata; + using stack_thread = basic_thread; + using stack_coroutine = basic_coroutine; + + struct stack_proxy_base; + struct stack_proxy; + struct variadic_args; + struct variadic_results; + struct stack_count; + struct this_state; + struct this_main_state; + struct this_environment; + + class state_view; + class state; + + template + struct as_table_t; + template + struct as_container_t; + template + struct nested; + template + struct light; + template + struct user; + template + struct as_args_t; + template + struct protect_t; + template + struct policy_wrapper; + + template + struct usertype_traits; + template + struct unique_usertype_traits; + + template + struct types { + typedef std::make_index_sequence indices; + static constexpr std::size_t size() { + return sizeof...(Args); + } + }; + + template + struct derive : std::false_type { + typedef types<> type; + }; + + template + struct base : std::false_type { + typedef types<> type; + }; + + template + struct weak_derive { + static bool value; + }; + + template + bool weak_derive::value = false; + + namespace stack { + struct record; + } + +#if SOL_IS_OFF(SOL_USE_BOOST) + template + class optional; + + template + class optional; +#endif + + using check_handler_type = int(lua_State*, int, type, type, const char*); + +} // namespace sol + +#define SOL_BASE_CLASSES(T, ...) \ + namespace sol { \ + template <> \ + struct base : std::true_type { \ + typedef ::sol::types<__VA_ARGS__> type; \ + }; \ + } \ + static_assert(true, "") +#define SOL_DERIVED_CLASSES(T, ...) \ + namespace sol { \ + template <> \ + struct derive : std::true_type { \ + typedef ::sol::types<__VA_ARGS__> type; \ + }; \ + } \ + static_assert(true, "") + +#endif // SOL_FORWARD_HPP +// end of sol/forward.hpp + +#endif // SOL_SINGLE_INCLUDE_SOL_FORWARD_HPP diff --git a/thirdparty/sol/sol.hpp b/thirdparty/sol/sol.hpp new file mode 100644 index 0000000000..bbf894233d --- /dev/null +++ b/thirdparty/sol/sol.hpp @@ -0,0 +1,29208 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2020 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// This file was generated with a script. +// Generated 2024-09-03 03:46:49.867770 UTC +// This header was generated with sol v3.3.1 (revision 2b0d2fe8) +// https://github.com/ThePhD/sol2 + +#ifndef SOL_SINGLE_INCLUDE_SOL_HPP +#define SOL_SINGLE_INCLUDE_SOL_HPP + +// beginning of sol/sol.hpp + +#ifndef SOL_HPP +#define SOL_HPP + +// beginning of sol/version.hpp + +#include + +#define SOL_VERSION_MAJOR 3 +#define SOL_VERSION_MINOR 2 +#define SOL_VERSION_PATCH 3 +#define SOL_VERSION_STRING "3.2.3" +#define SOL_VERSION ((SOL_VERSION_MAJOR * 100000) + (SOL_VERSION_MINOR * 100) + (SOL_VERSION_PATCH)) + +#define SOL_TOKEN_TO_STRING_POST_EXPANSION_I_(_TOKEN) #_TOKEN +#define SOL_TOKEN_TO_STRING_I_(_TOKEN) SOL_TOKEN_TO_STRING_POST_EXPANSION_I_(_TOKEN) + +#define SOL_CONCAT_TOKENS_POST_EXPANSION_I_(_LEFT, _RIGHT) _LEFT##_RIGHT +#define SOL_CONCAT_TOKENS_I_(_LEFT, _RIGHT) SOL_CONCAT_TOKENS_POST_EXPANSION_I_(_LEFT, _RIGHT) + +#define SOL_RAW_IS_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) != 0) +#define SOL_RAW_IS_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3) == 0) +#define SOL_RAW_IS_DEFAULT_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) > 3) +#define SOL_RAW_IS_DEFAULT_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3 OP_SYMBOL 3) < 0) + +#define SOL_IS_ON(OP_SYMBOL) SOL_RAW_IS_ON(OP_SYMBOL ## _I_) +#define SOL_IS_OFF(OP_SYMBOL) SOL_RAW_IS_OFF(OP_SYMBOL ## _I_) +#define SOL_IS_DEFAULT_ON(OP_SYMBOL) SOL_RAW_IS_DEFAULT_ON(OP_SYMBOL ## _I_) +#define SOL_IS_DEFAULT_OFF(OP_SYMBOL) SOL_RAW_IS_DEFAULT_OFF(OP_SYMBOL ## _I_) + +#define SOL_ON | +#define SOL_OFF ^ +#define SOL_DEFAULT_ON + +#define SOL_DEFAULT_OFF - + +#if defined(SOL_BUILD_CXX_MODE) + #if (SOL_BUILD_CXX_MODE != 0) + #define SOL_BUILD_CXX_MODE_I_ SOL_ON + #else + #define SOL_BUILD_CXX_MODE_I_ SOL_OFF + #endif +#elif defined(__cplusplus) + #define SOL_BUILD_CXX_MODE_I_ SOL_DEFAULT_ON +#else + #define SOL_BUILD_CXX_MODE_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_BUILD_C_MODE) + #if (SOL_BUILD_C_MODE != 0) + #define SOL_BUILD_C_MODE_I_ SOL_ON + #else + #define SOL_BUILD_C_MODE_I_ SOL_OFF + #endif +#elif defined(__STDC__) + #define SOL_BUILD_C_MODE_I_ SOL_DEFAULT_ON +#else + #define SOL_BUILD_C_MODE_I_ SOL_DEFAULT_OFF +#endif + +#if SOL_IS_ON(SOL_BUILD_C_MODE) + #include + #include + #include +#else + #include + #include + #include +#endif + +#if defined(SOL_HAS_BUILTIN) + #define SOL_HAS_BUILTIN_I_(...) SOL_HAS_BUILTIN(__VA_ARGS__) +#elif defined(__has_builtin) + #define SOL_HAS_BUILTIN_I_(...) __has_builtin(__VA_ARGS__) +#else + #define SOL_HAS_BUILTIN_I_(...) 0 +#endif + +#if defined(SOL_COMPILER_VCXX) + #if (SOL_COMPILER_VCXX != 0) + #define SOL_COMPILER_VCXX_I_ SOL_ON + #else + #define SOL_COMPILER_VCXX_I_ SOL_OFF + #endif +#elif defined(_MSC_VER) + #define SOL_COMPILER_VCXX_I_ SOL_DEFAULT_ON +#else + #define SOL_COMPILER_VCXX_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_COMPILER_GCC) + #if (SOL_COMPILER_GCC != 0) + #define SOL_COMPILER_GCC_I_ SOL_ON + #else + #define SOL_COMPILER_GCC_I_ SOL_OFF + #endif +#elif defined(__GNUC__) + #define SOL_COMPILER_GCC_I_ SOL_DEFAULT_ON +#else + #define SOL_COMPILER_GCC_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_COMPILER_CLANG) + #if (SOL_COMPILER_CLANG != 0) + #define SOL_COMPILER_CLANG_I_ SOL_ON + #else + #define SOL_COMPILER_CLANG_I_ SOL_OFF + #endif +#elif defined(__clang__) + #define SOL_COMPILER_CLANG_I_ SOL_DEFAULT_ON +#else + #define SOL_COMPILER_CLANG_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_COMPILER_EDG) + #if (SOL_COMPILER_EDG != 0) + #define SOL_COMPILER_EDG_I_ SOL_ON + #else + #define SOL_COMPILER_EDG_I_ SOL_OFF + #endif +#else + #define SOL_COMPILER_EDG_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_COMPILER_MINGW) + #if (SOL_COMPILER_MINGW != 0) + #define SOL_COMPILER_MINGW_I_ SOL_ON + #else + #define SOL_COMPILER_MINGW_I_ SOL_OFF + #endif +#elif defined(__MINGW32__) + #define SOL_COMPILER_MINGW_I_ SOL_DEFAULT_ON +#else + #define SOL_COMPILER_MINGW_I_ SOL_DEFAULT_OFF +#endif + +#if SIZE_MAX <= 0xFFFFULL + #define SOL_PLATFORM_X16_I_ SOL_ON + #define SOL_PLATFORM_X86_I_ SOL_OFF + #define SOL_PLATFORM_X64_I_ SOL_OFF +#elif SIZE_MAX <= 0xFFFFFFFFULL + #define SOL_PLATFORM_X16_I_ SOL_OFF + #define SOL_PLATFORM_X86_I_ SOL_ON + #define SOL_PLATFORM_X64_I_ SOL_OFF +#else + #define SOL_PLATFORM_X16_I_ SOL_OFF + #define SOL_PLATFORM_X86_I_ SOL_OFF + #define SOL_PLATFORM_X64_I_ SOL_ON +#endif + +#define SOL_PLATFORM_ARM32_I_ SOL_OFF +#define SOL_PLATFORM_ARM64_I_ SOL_OFF + +#if defined(SOL_PLATFORM_WINDOWS) + #if (SOL_PLATFORM_WINDOWS != 0) + #define SOL_PLATFORM_WINDOWS_I_ SOL_ON + #else + #define SOL_PLATFORM_WINDOWS_I_ SOL_OFF + #endif +#elif defined(_WIN32) + #define SOL_PLATFORM_WINDOWS_I_ SOL_DEFAULT_ON +#else + #define SOL_PLATFORM_WINDOWS_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_PLATFORM_CYGWIN) + #if (SOL_PLATFORM_CYGWIN != 0) + #define SOL_PLATFORM_CYGWIN_I_ SOL_ON + #else + #define SOL_PLATFORM_CYGWIN_I_ SOL_ON + #endif +#elif defined(__CYGWIN__) + #define SOL_PLATFORM_CYGWIN_I_ SOL_DEFAULT_ON +#else + #define SOL_PLATFORM_CYGWIN_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_PLATFORM_APPLE) + #if (SOL_PLATFORM_APPLE != 0) + #define SOL_PLATFORM_APPLE_I_ SOL_ON + #else + #define SOL_PLATFORM_APPLE_I_ SOL_OFF + #endif +#elif defined(__APPLE__) + #define SOL_PLATFORM_APPLE_I_ SOL_DEFAULT_ON +#else + #define SOL_PLATFORM_APPLE_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_PLATFORM_UNIX) + #if (SOL_PLATFORM_UNIX != 0) + #define SOL_PLATFORM_UNIXLIKE_I_ SOL_ON + #else + #define SOL_PLATFORM_UNIXLIKE_I_ SOL_OFF + #endif +#elif defined(__unix__) + #define SOL_PLATFORM_UNIXLIKE_I_ SOL_DEFAULT_ON +#else + #define SOL_PLATFORM_UNIXLIKE_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_PLATFORM_LINUX) + #if (SOL_PLATFORM_LINUX != 0) + #define SOL_PLATFORM_LINUXLIKE_I_ SOL_ON + #else + #define SOL_PLATFORM_LINUXLIKE_I_ SOL_OFF + #endif +#elif defined(__LINUX__) + #define SOL_PLATFORM_LINUXLIKE_I_ SOL_DEFAULT_ON +#else + #define SOL_PLATFORM_LINUXLIKE_I_ SOL_DEFAULT_OFF +#endif + +#define SOL_PLATFORM_APPLE_IPHONE_I_ SOL_OFF +#define SOL_PLATFORM_BSDLIKE_I_ SOL_OFF + +#if defined(SOL_IN_DEBUG_DETECTED) + #if (SOL_IN_DEBUG_DETECTED != 0) + #define SOL_DEBUG_BUILD_I_ SOL_ON + #else + #define SOL_DEBUG_BUILD_I_ SOL_OFF + #endif +#elif !defined(NDEBUG) + #if SOL_IS_ON(SOL_COMPILER_VCXX) && defined(_DEBUG) + #define SOL_DEBUG_BUILD_I_ SOL_ON + #elif (SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)) && !defined(__OPTIMIZE__) + #define SOL_DEBUG_BUILD_I_ SOL_ON + #else + #define SOL_DEBUG_BUILD_I_ SOL_OFF + #endif +#else + #define SOL_DEBUG_BUILD_I_ SOL_DEFAULT_OFF +#endif // We are in a debug mode of some sort + +#if defined(SOL_NO_EXCEPTIONS) + #if (SOL_NO_EXCEPTIONS != 0) + #define SOL_EXCEPTIONS_I_ SOL_OFF + #else + #define SOL_EXCEPTIONS_I_ SOL_ON + #endif +#elif SOL_IS_ON(SOL_COMPILER_VCXX) + #if !defined(_CPPUNWIND) + #define SOL_EXCEPTIONS_I_ SOL_OFF + #else + #define SOL_EXCEPTIONS_I_ SOL_ON + #endif +#elif SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC) + #if !defined(__EXCEPTIONS) + #define SOL_EXCEPTIONS_I_ SOL_OFF + #else + #define SOL_EXCEPTIONS_I_ SOL_ON + #endif +#else + #define SOL_EXCEPTIONS_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_NO_RTTI) + #if (SOL_NO_RTTI != 0) + #define SOL_RTTI_I_ SOL_OFF + #else + #define SOL_RTTI_I_ SOL_ON + #endif +#elif SOL_IS_ON(SOL_COMPILER_VCXX) + #if !defined(_CPPRTTI) + #define SOL_RTTI_I_ SOL_OFF + #else + #define SOL_RTTI_I_ SOL_ON + #endif +#elif SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC) + #if !defined(__GXX_RTTI) + #define SOL_RTTI_I_ SOL_OFF + #else + #define SOL_RTTI_I_ SOL_ON + #endif +#else + #define SOL_RTTI_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_NO_THREAD_LOCAL) + #if (SOL_NO_THREAD_LOCAL != 0) + #define SOL_USE_THREAD_LOCAL_I_ SOL_OFF + #else + #define SOL_USE_THREAD_LOCAL_I_ SOL_ON + #endif +#else + #define SOL_USE_THREAD_LOCAL_I_ SOL_DEFAULT_ON +#endif // thread_local keyword is bjorked on some platforms + +#if defined(SOL_ALL_SAFETIES_ON) + #if (SOL_ALL_SAFETIES_ON != 0) + #define SOL_ALL_SAFETIES_ON_I_ SOL_ON + #else + #define SOL_ALL_SAFETIES_ON_I_ SOL_OFF + #endif +#else + #define SOL_ALL_SAFETIES_ON_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_SAFE_GETTER) + #if (SOL_SAFE_GETTER != 0) + #define SOL_SAFE_GETTER_I_ SOL_ON + #else + #define SOL_SAFE_GETTER_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_GETTER_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_SAFE_USERTYPE) + #if (SOL_SAFE_USERTYPE != 0) + #define SOL_SAFE_USERTYPE_I_ SOL_ON + #else + #define SOL_SAFE_USERTYPE_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_USERTYPE_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_SAFE_REFERENCES) + #if (SOL_SAFE_REFERENCES != 0) + #define SOL_SAFE_REFERENCES_I_ SOL_ON + #else + #define SOL_SAFE_REFERENCES_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_REFERENCES_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_SAFE_FUNCTIONS) + #if (SOL_SAFE_FUNCTIONS != 0) + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON + #else + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF + #endif +#elif defined (SOL_SAFE_FUNCTION_OBJECTS) + #if (SOL_SAFE_FUNCTION_OBJECTS != 0) + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON + #else + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_SAFE_FUNCTION_CALLS) + #if (SOL_SAFE_FUNCTION_CALLS != 0) + #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON + #else + #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_SAFE_PROXIES) + #if (SOL_SAFE_PROXIES != 0) + #define SOL_SAFE_PROXIES_I_ SOL_ON + #else + #define SOL_SAFE_PROXIES_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_PROXIES_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_SAFE_NUMERICS) + #if (SOL_SAFE_NUMERICS != 0) + #define SOL_SAFE_NUMERICS_I_ SOL_ON + #else + #define SOL_SAFE_NUMERICS_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_NUMERICS_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_ALL_INTEGER_VALUES_FIT) + #if (SOL_ALL_INTEGER_VALUES_FIT != 0) + #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_ON + #else + #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_OFF + #endif +#elif !SOL_IS_DEFAULT_OFF(SOL_SAFE_NUMERICS) && SOL_IS_OFF(SOL_SAFE_NUMERICS) + // if numerics is intentionally turned off, flip this on + #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_DEFAULT_ON +#else + // default to off + #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_SAFE_STACK_CHECK) + #if (SOL_SAFE_STACK_CHECK != 0) + #define SOL_SAFE_STACK_CHECK_I_ SOL_ON + #else + #define SOL_SAFE_STACK_CHECK_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_SAFE_STACK_CHECK_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_ON + #else + #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_NO_CHECK_NUMBER_PRECISION) + #if (SOL_NO_CHECK_NUMBER_PRECISION != 0) + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF + #else + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON + #endif +#elif defined(SOL_NO_CHECKING_NUMBER_PRECISION) + #if (SOL_NO_CHECKING_NUMBER_PRECISION != 0) + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF + #else + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON + #elif SOL_IS_ON(SOL_SAFE_NUMERICS) + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_ON + #else + #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_STRINGS_ARE_NUMBERS) + #if (SOL_STRINGS_ARE_NUMBERS != 0) + #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_ON + #else + #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_OFF + #endif +#else + #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_ENABLE_INTEROP) + #if (SOL_ENABLE_INTEROP != 0) + #define SOL_USE_INTEROP_I_ SOL_ON + #else + #define SOL_USE_INTEROP_I_ SOL_OFF + #endif +#elif defined(SOL_USE_INTEROP) + #if (SOL_USE_INTEROP != 0) + #define SOL_USE_INTEROP_I_ SOL_ON + #else + #define SOL_USE_INTEROP_I_ SOL_OFF + #endif +#else + #define SOL_USE_INTEROP_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_NO_NIL) + #if (SOL_NO_NIL != 0) + #define SOL_NIL_I_ SOL_OFF + #else + #define SOL_NIL_I_ SOL_ON + #endif +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || defined(__OBJC__) || defined(nil) + #define SOL_NIL_I_ SOL_DEFAULT_OFF +#else + #define SOL_NIL_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_USERTYPE_TYPE_BINDING_INFO) + #if (SOL_USERTYPE_TYPE_BINDING_INFO != 0) + #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_ON + #else + #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_OFF + #endif +#else + #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_DEFAULT_ON +#endif // We should generate a my_type.__type table with lots of class information for usertypes + +#if defined(SOL_AUTOMAGICAL_TYPES_BY_DEFAULT) + #if (SOL_AUTOMAGICAL_TYPES_BY_DEFAULT != 0) + #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON + #else + #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF + #endif +#elif defined(SOL_DEFAULT_AUTOMAGICAL_USERTYPES) + #if (SOL_DEFAULT_AUTOMAGICAL_USERTYPES != 0) + #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON + #else + #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF + #endif +#else + #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_DEFAULT_ON +#endif // make is_automagical on/off by default + +#if defined(SOL_STD_VARIANT) + #if (SOL_STD_VARIANT != 0) + #define SOL_STD_VARIANT_I_ SOL_ON + #else + #define SOL_STD_VARIANT_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_COMPILER_CLANG) && SOL_IS_ON(SOL_PLATFORM_APPLE) + #if defined(__has_include) + #if __has_include() + #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON + #else + #define SOL_STD_VARIANT_I_ SOL_DEFAULT_OFF + #endif + #else + #define SOL_STD_VARIANT_I_ SOL_DEFAULT_OFF + #endif + #else + #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON + #endif +#endif // make is_automagical on/off by default + +#if defined(SOL_NOEXCEPT_FUNCTION_TYPE) + #if (SOL_NOEXCEPT_FUNCTION_TYPE != 0) + #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON + #else + #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF + #endif +#else + #if defined(__cpp_noexcept_function_type) + #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON + #elif SOL_IS_ON(SOL_COMPILER_VCXX) && (defined(_MSVC_LANG) && (_MSVC_LANG < 201403L)) + // There is a bug in the VC++ compiler?? + // on /std:c++latest under x86 conditions (VS 15.5.2), + // compiler errors are tossed for noexcept markings being on function types + // that are identical in every other way to their non-noexcept marked types function types... + // VS 2019: There is absolutely a bug. + #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF + #else + #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_DEFAULT_ON + #endif +#endif // noexcept is part of a function's type + +#if defined(SOL_STACK_STRING_OPTIMIZATION_SIZE) && (SOL_STACK_STRING_OPTIMIZATION_SIZE > 0) + #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ SOL_STACK_STRING_OPTIMIZATION_SIZE +#else + #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ 1024 +#endif + +#if defined(SOL_ID_SIZE) && (SOL_ID_SIZE > 0) + #define SOL_ID_SIZE_I_ SOL_ID_SIZE +#else + #define SOL_ID_SIZE_I_ 512 +#endif + +#if defined(LUA_IDSIZE) && (LUA_IDSIZE > 0) + #define SOL_FILE_ID_SIZE_I_ LUA_IDSIZE +#elif defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0 + #define SOL_FILE_ID_SIZE_I_ SOL_FILE_ID_SIZE +#else + #define SOL_FILE_ID_SIZE_I_ 2048 +#endif + +#if defined(SOL_PRINT_ERRORS) + #if (SOL_PRINT_ERRORS != 0) + #define SOL_PRINT_ERRORS_I_ SOL_ON + #else + #define SOL_PRINT_ERRORS_I_ SOL_OFF + #endif +#else + #if SOL_IS_ON(SOL_ALL_SAFETIES_ON) + #define SOL_PRINT_ERRORS_I_ SOL_ON + #elif SOL_IS_ON(SOL_DEBUG_BUILD) + #define SOL_PRINT_ERRORS_I_ SOL_DEFAULT_ON + #else + #define SOL_PRINT_ERRORS_I_ SOL_OFF + #endif +#endif + +#if defined(SOL_DEFAULT_PASS_ON_ERROR) + #if (SOL_DEFAULT_PASS_ON_ERROR != 0) + #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_ON + #else + #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_OFF + #endif +#else + #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_USING_CXX_LUA) + #if (SOL_USING_CXX_LUA != 0) + #define SOL_USING_CXX_LUA_I_ SOL_ON + #else + #define SOL_USING_CXX_LUA_I_ SOL_OFF + #endif +#elif defined(SOL_USE_CXX_LUA) + // alternative spelling + #if (SOL_USE_CXX_LUA != 0) + #define SOL_USING_CXX_LUA_I_ SOL_ON + #else + #define SOL_USING_CXX_LUA_I_ SOL_OFF + #endif +#else + #define SOL_USING_CXX_LUA_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_USING_CXX_LUAJIT) + #if (SOL_USING_CXX_LUAJIT != 0) + #define SOL_USING_CXX_LUAJIT_I_ SOL_ON + #else + #define SOL_USING_CXX_LUAJIT_I_ SOL_OFF + #endif +#elif defined(SOL_USE_CXX_LUAJIT) + #if (SOL_USE_CXX_LUAJIT != 0) + #define SOL_USING_CXX_LUAJIT_I_ SOL_ON + #else + #define SOL_USING_CXX_LUAJIT_I_ SOL_OFF + #endif +#else + #define SOL_USING_CXX_LUAJIT_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_NO_LUA_HPP) + #if (SOL_NO_LUA_HPP != 0) + #define SOL_USE_LUA_HPP_I_ SOL_OFF + #else + #define SOL_USE_LUA_HPP_I_ SOL_ON + #endif +#elif SOL_IS_ON(SOL_USING_CXX_LUA) + #define SOL_USE_LUA_HPP_I_ SOL_OFF +#elif defined(__has_include) + #if __has_include() + #define SOL_USE_LUA_HPP_I_ SOL_ON + #else + #define SOL_USE_LUA_HPP_I_ SOL_OFF + #endif +#else + #define SOL_USE_LUA_HPP_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_CONTAINERS_START) + #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START +#elif defined(SOL_CONTAINERS_START_INDEX) + #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START_INDEX +#elif defined(SOL_CONTAINER_START_INDEX) + #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINER_START_INDEX +#else + #define SOL_CONTAINER_START_INDEX_I_ 1 +#endif + +#if defined (SOL_NO_MEMORY_ALIGNMENT) + #if (SOL_NO_MEMORY_ALIGNMENT != 0) + #define SOL_ALIGN_MEMORY_I_ SOL_OFF + #else + #define SOL_ALIGN_MEMORY_I_ SOL_ON + #endif +#else + #define SOL_ALIGN_MEMORY_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_USE_BOOST) + #if (SOL_USE_BOOST != 0) + #define SOL_USE_BOOST_I_ SOL_ON + #else + #define SOL_USE_BOOST_I_ SOL_OFF + #endif +#else + #define SOL_USE_BOOST_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_USE_UNSAFE_BASE_LOOKUP) + #if (SOL_USE_UNSAFE_BASE_LOOKUP != 0) + #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_ON + #else + #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_OFF + #endif +#else + #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_INSIDE_UNREAL) + #if (SOL_INSIDE_UNREAL != 0) + #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_ON + #else + #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_OFF + #endif +#else + #if defined(UE_BUILD_DEBUG) || defined(UE_BUILD_DEVELOPMENT) || defined(UE_BUILD_TEST) || defined(UE_BUILD_SHIPPING) || defined(UE_SERVER) + #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_ON + #else + #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if defined(SOL_NO_COMPAT) + #if (SOL_NO_COMPAT != 0) + #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_OFF + #else + #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_ON + #endif +#else + #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE) + #if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0) + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON + #else + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF + #endif +#else + #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_CONTAINER_CHECK_IS_EXHAUSTIVE) + #if (SOL_CONTAINER_CHECK_IS_EXHAUSTIVE != 0) + #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_ON + #else + #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_OFF + #endif +#else + #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_FUNCTION_CALL_VALUE_SEMANTICS) + #if (SOL_FUNCTION_CALL_VALUE_SEMANTICS != 0) + #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_ON + #else + #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_OFF + #endif +#else + #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_MINGW_CCTYPE_IS_POISONED) + #if (SOL_MINGW_CCTYPE_IS_POISONED != 0) + #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON + #else + #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_OFF + #endif +#elif SOL_IS_ON(SOL_COMPILER_MINGW) && defined(__GNUC__) && (__GNUC__ < 6) + // MinGW is off its rocker in some places... + #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_ON +#else + #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_CHAR8_T) + #if (SOL_CHAR8_T != 0) + #define SOL_CHAR8_T_I_ SOL_ON + #else + #define SOL_CHAR8_T_I_ SOL_OFF + #endif +#else + #if defined(__cpp_char8_t) + #define SOL_CHAR8_T_I_ SOL_DEFAULT_ON + #else + #define SOL_CHAR8_T_I_ SOL_DEFAULT_OFF + #endif +#endif + +#if SOL_IS_ON(SOL_USE_BOOST) + #include + + #if BOOST_VERSION >= 107500 // Since Boost 1.75.0 boost::none is constexpr + #define SOL_BOOST_NONE_CONSTEXPR_I_ constexpr + #else + #define SOL_BOOST_NONE_CONSTEXPR_I_ const + #endif // BOOST_VERSION +#else + // assume boost isn't using a garbage version + #define SOL_BOOST_NONE_CONSTEXPR_I_ constexpr +#endif + +#if defined(SOL2_CI) + #if (SOL2_CI != 0) + #define SOL2_CI_I_ SOL_ON + #else + #define SOL2_CI_I_ SOL_OFF + #endif +#else + #define SOL2_CI_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_ASSERT) + #define SOL_USER_ASSERT_I_ SOL_ON +#else + #define SOL_USER_ASSERT_I_ SOL_DEFAULT_OFF +#endif + +#if defined(SOL_ASSERT_MSG) + #define SOL_USER_ASSERT_MSG_I_ SOL_ON +#else + #define SOL_USER_ASSERT_MSG_I_ SOL_DEFAULT_OFF +#endif + +// beginning of sol/prologue.hpp + +#if defined(SOL_PROLOGUE_I_) + #error "[sol2] Library Prologue was already included in translation unit and not properly ended with an epilogue." +#endif + +#define SOL_PROLOGUE_I_ 1 + +#if SOL_IS_ON(SOL_BUILD_CXX_MODE) + #define _FWD(...) static_cast( __VA_ARGS__ ) + + #if SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG) + #define _MOVE(...) static_cast<__typeof( __VA_ARGS__ )&&>( __VA_ARGS__ ) + #else + #include + + #define _MOVE(...) static_cast<::std::remove_reference_t<( __VA_ARGS__ )>&&>( __VA_OPT__(,) ) + #endif +#endif + +// end of sol/prologue.hpp + +// beginning of sol/epilogue.hpp + +#if !defined(SOL_PROLOGUE_I_) + #error "[sol2] Library Prologue is missing from this translation unit." +#else + #undef SOL_PROLOGUE_I_ +#endif + +#if SOL_IS_ON(SOL_BUILD_CXX_MODE) + #undef _FWD + #undef _MOVE +#endif + +// end of sol/epilogue.hpp + +// beginning of sol/detail/build_version.hpp + +#if defined(SOL_DLL) + #if (SOL_DLL != 0) + #define SOL_DLL_I_ SOL_ON + #else + #define SOL_DLL_I_ SOL_OFF + #endif +#elif SOL_IS_ON(SOL_COMPILER_VCXX) && (defined(DLL_) || defined(_DLL)) + #define SOL_DLL_I_ SOL_DEFAULT_ON +#else + #define SOL_DLL_I_ SOL_DEFAULT_OFF +#endif // DLL definition + +#if defined(SOL_HEADER_ONLY) + #if (SOL_HEADER_ONLY != 0) + #define SOL_HEADER_ONLY_I_ SOL_ON + #else + #define SOL_HEADER_ONLY_I_ SOL_OFF + #endif +#else + #define SOL_HEADER_ONLY_I_ SOL_DEFAULT_OFF +#endif // Header only library + +#if defined(SOL_BUILD) + #if (SOL_BUILD != 0) + #define SOL_BUILD_I_ SOL_ON + #else + #define SOL_BUILD_I_ SOL_OFF + #endif +#elif SOL_IS_ON(SOL_HEADER_ONLY) + #define SOL_BUILD_I_ SOL_DEFAULT_OFF +#else + #define SOL_BUILD_I_ SOL_DEFAULT_ON +#endif + +#if defined(SOL_UNITY_BUILD) + #if (SOL_UNITY_BUILD != 0) + #define SOL_UNITY_BUILD_I_ SOL_ON + #else + #define SOL_UNITY_BUILD_I_ SOL_OFF + #endif +#else + #define SOL_UNITY_BUILD_I_ SOL_DEFAULT_OFF +#endif // Header only library + +#if defined(SOL_C_FUNCTION_LINKAGE) + #define SOL_C_FUNCTION_LINKAGE_I_ SOL_C_FUNCTION_LINKAGE +#else + #if SOL_IS_ON(SOL_BUILD_CXX_MODE) + // C++ + #define SOL_C_FUNCTION_LINKAGE_I_ extern "C" + #else + // normal + #define SOL_C_FUNCTION_LINKAGE_I_ + #endif // C++ or not +#endif // Linkage specification for C functions + +#if defined(SOL_API_LINKAGE) + #define SOL_API_LINKAGE_I_ SOL_API_LINKAGE +#else + #if SOL_IS_ON(SOL_DLL) + #if SOL_IS_ON(SOL_COMPILER_VCXX) || SOL_IS_ON(SOL_PLATFORM_WINDOWS) || SOL_IS_ON(SOL_PLATFORM_CYGWIN) + // MSVC Compiler; or, Windows, or Cygwin platforms + #if SOL_IS_ON(SOL_BUILD) + // Building the library + #if SOL_IS_ON(SOL_COMPILER_GCC) + // Using GCC + #define SOL_API_LINKAGE_I_ __attribute__((dllexport)) + #else + // Using Clang, MSVC, etc... + #define SOL_API_LINKAGE_I_ __declspec(dllexport) + #endif + #else + #if SOL_IS_ON(SOL_COMPILER_GCC) + #define SOL_API_LINKAGE_I_ __attribute__((dllimport)) + #else + #define SOL_API_LINKAGE_I_ __declspec(dllimport) + #endif + #endif + #else + // extern if building normally on non-MSVC + #define SOL_API_LINKAGE_I_ extern + #endif + #elif SOL_IS_ON(SOL_UNITY_BUILD) + // Built-in library, like how stb typical works + #if SOL_IS_ON(SOL_HEADER_ONLY) + // Header only, so functions are defined "inline" + #define SOL_API_LINKAGE_I_ inline + #else + // Not header only, so seperately compiled files + #define SOL_API_LINKAGE_I_ extern + #endif + #else + // Normal static library + #if SOL_IS_ON(SOL_BUILD_CXX_MODE) + #define SOL_API_LINKAGE_I_ + #else + #define SOL_API_LINKAGE_I_ extern + #endif + #endif // DLL or not +#endif // Build definitions + +#if defined(SOL_PUBLIC_FUNC_DECL) + #define SOL_PUBLIC_FUNC_DECL_I_ SOL_PUBLIC_FUNC_DECL +#else + #define SOL_PUBLIC_FUNC_DECL_I_ SOL_API_LINKAGE_I_ +#endif + +#if defined(SOL_INTERNAL_FUNC_DECL_) + #define SOL_INTERNAL_FUNC_DECL_I_ SOL_INTERNAL_FUNC_DECL_ +#else + #define SOL_INTERNAL_FUNC_DECL_I_ SOL_API_LINKAGE_I_ +#endif + +#if defined(SOL_PUBLIC_FUNC_DEF) + #define SOL_PUBLIC_FUNC_DEF_I_ SOL_PUBLIC_FUNC_DEF +#else + #define SOL_PUBLIC_FUNC_DEF_I_ SOL_API_LINKAGE_I_ +#endif + +#if defined(SOL_INTERNAL_FUNC_DEF) + #define SOL_INTERNAL_FUNC_DEF_I_ SOL_INTERNAL_FUNC_DEF +#else + #define SOL_INTERNAL_FUNC_DEF_I_ SOL_API_LINKAGE_I_ +#endif + +#if defined(SOL_FUNC_DECL) + #define SOL_FUNC_DECL_I_ SOL_FUNC_DECL +#elif SOL_IS_ON(SOL_HEADER_ONLY) + #define SOL_FUNC_DECL_I_ +#elif SOL_IS_ON(SOL_DLL) + #if SOL_IS_ON(SOL_COMPILER_VCXX) + #if SOL_IS_ON(SOL_BUILD) + #define SOL_FUNC_DECL_I_ extern __declspec(dllexport) + #else + #define SOL_FUNC_DECL_I_ extern __declspec(dllimport) + #endif + #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG) + #define SOL_FUNC_DECL_I_ extern __attribute__((visibility("default"))) + #else + #define SOL_FUNC_DECL_I_ extern + #endif +#endif + +#if defined(SOL_FUNC_DEFN) + #define SOL_FUNC_DEFN_I_ SOL_FUNC_DEFN +#elif SOL_IS_ON(SOL_HEADER_ONLY) + #define SOL_FUNC_DEFN_I_ inline +#elif SOL_IS_ON(SOL_DLL) + #if SOL_IS_ON(SOL_COMPILER_VCXX) + #if SOL_IS_ON(SOL_BUILD) + #define SOL_FUNC_DEFN_I_ __declspec(dllexport) + #else + #define SOL_FUNC_DEFN_I_ __declspec(dllimport) + #endif + #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG) + #define SOL_FUNC_DEFN_I_ __attribute__((visibility("default"))) + #else + #define SOL_FUNC_DEFN_I_ + #endif +#endif + +#if defined(SOL_HIDDEN_FUNC_DECL) + #define SOL_HIDDEN_FUNC_DECL_I_ SOL_HIDDEN_FUNC_DECL +#elif SOL_IS_ON(SOL_HEADER_ONLY) + #define SOL_HIDDEN_FUNC_DECL_I_ +#elif SOL_IS_ON(SOL_DLL) + #if SOL_IS_ON(SOL_COMPILER_VCXX) + #if SOL_IS_ON(SOL_BUILD) + #define SOL_HIDDEN_FUNC_DECL_I_ extern __declspec(dllexport) + #else + #define SOL_HIDDEN_FUNC_DECL_I_ extern __declspec(dllimport) + #endif + #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG) + #define SOL_HIDDEN_FUNC_DECL_I_ extern __attribute__((visibility("default"))) + #else + #define SOL_HIDDEN_FUNC_DECL_I_ extern + #endif +#endif + +#if defined(SOL_HIDDEN_FUNC_DEFN) + #define SOL_HIDDEN_FUNC_DEFN_I_ SOL_HIDDEN_FUNC_DEFN +#elif SOL_IS_ON(SOL_HEADER_ONLY) + #define SOL_HIDDEN_FUNC_DEFN_I_ inline +#elif SOL_IS_ON(SOL_DLL) + #if SOL_IS_ON(SOL_COMPILER_VCXX) + #if SOL_IS_ON(SOL_BUILD) + #define SOL_HIDDEN_FUNC_DEFN_I_ + #else + #define SOL_HIDDEN_FUNC_DEFN_I_ + #endif + #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG) + #define SOL_HIDDEN_FUNC_DEFN_I_ __attribute__((visibility("hidden"))) + #else + #define SOL_HIDDEN_FUNC_DEFN_I_ + #endif +#endif + +// end of sol/detail/build_version.hpp + +// end of sol/version.hpp + +#if SOL_IS_ON(SOL_INSIDE_UNREAL_ENGINE) +#ifdef check +#pragma push_macro("check") +#undef check +#endif +#endif // Unreal Engine 4 Bullshit + +#if SOL_IS_ON(SOL_COMPILER_GCC) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wconversion" +#if __GNUC__ > 6 +#pragma GCC diagnostic ignored "-Wnoexcept-type" +#endif +#elif SOL_IS_ON(SOL_COMPILER_CLANG) +#elif SOL_IS_ON(SOL_COMPILER_VCXX) +#pragma warning(push) +#pragma warning(disable : 4505) // unreferenced local function has been removed GEE THANKS +#endif // clang++ vs. g++ vs. VC++ + +// beginning of sol/forward.hpp + +#ifndef SOL_FORWARD_HPP +#define SOL_FORWARD_HPP + +#include +#include +#include + +#if SOL_IS_ON(SOL_USING_CXX_LUA) || SOL_IS_ON(SOL_USING_CXX_LUAJIT) +struct lua_State; +#else +extern "C" { +struct lua_State; +} +#endif // C++ Mangling for Lua vs. Not + +namespace sol { + + enum class type; + + class stateless_reference; + template + class basic_reference; + using reference = basic_reference; + using main_reference = basic_reference; + class stateless_stack_reference; + class stack_reference; + + template + class basic_bytecode; + + struct lua_value; + + struct proxy_base_tag; + template + struct proxy_base; + template + struct table_proxy; + + template + class basic_table_core; + template + using table_core = basic_table_core; + template + using main_table_core = basic_table_core; + template + using stack_table_core = basic_table_core; + template + using basic_table = basic_table_core; + using table = table_core; + using global_table = table_core; + using main_table = main_table_core; + using main_global_table = main_table_core; + using stack_table = stack_table_core; + using stack_global_table = stack_table_core; + + template + struct basic_lua_table; + using lua_table = basic_lua_table; + using stack_lua_table = basic_lua_table; + + template + class basic_usertype; + template + using usertype = basic_usertype; + template + using stack_usertype = basic_usertype; + + template + class basic_metatable; + using metatable = basic_metatable; + using stack_metatable = basic_metatable; + + template + struct basic_environment; + using environment = basic_environment; + using main_environment = basic_environment; + using stack_environment = basic_environment; + + template + class basic_function; + template + class basic_protected_function; + using unsafe_function = basic_function; + using safe_function = basic_protected_function; + using main_unsafe_function = basic_function; + using main_safe_function = basic_protected_function; + using stack_unsafe_function = basic_function; + using stack_safe_function = basic_protected_function; + using stack_aligned_unsafe_function = basic_function; + using stack_aligned_safe_function = basic_protected_function; + using protected_function = safe_function; + using main_protected_function = main_safe_function; + using stack_protected_function = stack_safe_function; + using stack_aligned_protected_function = stack_aligned_safe_function; +#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS) + using function = protected_function; + using main_function = main_protected_function; + using stack_function = stack_protected_function; + using stack_aligned_function = stack_aligned_safe_function; +#else + using function = unsafe_function; + using main_function = main_unsafe_function; + using stack_function = stack_unsafe_function; + using stack_aligned_function = stack_aligned_unsafe_function; +#endif + using stack_aligned_stack_handler_function = basic_protected_function; + + struct unsafe_function_result; + struct protected_function_result; + using safe_function_result = protected_function_result; +#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS) + using function_result = safe_function_result; +#else + using function_result = unsafe_function_result; +#endif + + template + class basic_object_base; + template + class basic_object; + template + class basic_userdata; + template + class basic_lightuserdata; + template + class basic_coroutine; + template + class basic_packaged_coroutine; + template + class basic_thread; + + using object = basic_object; + using userdata = basic_userdata; + using lightuserdata = basic_lightuserdata; + using thread = basic_thread; + using coroutine = basic_coroutine; + using packaged_coroutine = basic_packaged_coroutine; + using main_object = basic_object; + using main_userdata = basic_userdata; + using main_lightuserdata = basic_lightuserdata; + using main_coroutine = basic_coroutine; + using stack_object = basic_object; + using stack_userdata = basic_userdata; + using stack_lightuserdata = basic_lightuserdata; + using stack_thread = basic_thread; + using stack_coroutine = basic_coroutine; + + struct stack_proxy_base; + struct stack_proxy; + struct variadic_args; + struct variadic_results; + struct stack_count; + struct this_state; + struct this_main_state; + struct this_environment; + + class state_view; + class state; + + template + struct as_table_t; + template + struct as_container_t; + template + struct nested; + template + struct light; + template + struct user; + template + struct as_args_t; + template + struct protect_t; + template + struct policy_wrapper; + + template + struct usertype_traits; + template + struct unique_usertype_traits; + + template + struct types { + typedef std::make_index_sequence indices; + static constexpr std::size_t size() { + return sizeof...(Args); + } + }; + + template + struct derive : std::false_type { + typedef types<> type; + }; + + template + struct base : std::false_type { + typedef types<> type; + }; + + template + struct weak_derive { + static bool value; + }; + + template + bool weak_derive::value = false; + + namespace stack { + struct record; + } + +#if SOL_IS_OFF(SOL_USE_BOOST) + template + class optional; + + template + class optional; +#endif + + using check_handler_type = int(lua_State*, int, type, type, const char*); + +} // namespace sol + +#define SOL_BASE_CLASSES(T, ...) \ + namespace sol { \ + template <> \ + struct base : std::true_type { \ + typedef ::sol::types<__VA_ARGS__> type; \ + }; \ + } \ + static_assert(true, "") +#define SOL_DERIVED_CLASSES(T, ...) \ + namespace sol { \ + template <> \ + struct derive : std::true_type { \ + typedef ::sol::types<__VA_ARGS__> type; \ + }; \ + } \ + static_assert(true, "") + +#endif // SOL_FORWARD_HPP +// end of sol/forward.hpp + +// beginning of sol/forward_detail.hpp + +#ifndef SOL_FORWARD_DETAIL_HPP +#define SOL_FORWARD_DETAIL_HPP + +// beginning of sol/traits.hpp + +// beginning of sol/tuple.hpp + +// beginning of sol/base_traits.hpp + +#include + +namespace sol { + namespace detail { + struct unchecked_t { }; + const unchecked_t unchecked = unchecked_t {}; + } // namespace detail + + namespace meta { + using sfinae_yes_t = std::true_type; + using sfinae_no_t = std::false_type; + + template + using void_t = void; + + template + using unqualified = std::remove_cv>; + + template + using unqualified_t = typename unqualified::type; + + namespace meta_detail { + template + struct unqualified_non_alias : unqualified { }; + + template