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 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d9755f113d..2e5476b5e7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,8 +18,9 @@ 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 - - uses: actions/checkout@v2 + libavif libheif aom rav1e libdecor libxdamage \ + luajit + - uses: actions/checkout@v4 with: submodules: recursive - name: Build with gcc @@ -32,8 +33,8 @@ jobs: export CC=gcc CXX=g++ meson build-gcc-novr/ -Dinput_emulation=disabled -Denable_openvr_support=false --werror --auto-features=enabled ninja -C build-gcc-novr/ - - name: Build with clang - run: | - export CC=clang CXX=clang++ - meson build-clang/ -Dinput_emulation=disabled --werror --auto-features=enabled - ninja -C build-clang/ +# - name: Build with clang +# run: | +# export CC=clang CXX=clang++ +# meson build-clang/ -Dinput_emulation=disabled --werror --auto-features=enabled +# ninja -C build-clang/ diff --git a/README.md b/README.md index a56075a91b..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 -- ``` @@ -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/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/layer/VkLayer_FROG_gamescope_wsi.cpp b/layer/VkLayer_FROG_gamescope_wsi.cpp index 2baea4f0ca..c817880f18 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" @@ -90,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; @@ -115,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; } @@ -209,7 +318,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 +335,7 @@ namespace GamescopeWSILayer { return hdrOutput && hdrAllowed; } - bool canBypassXWayland() const { + bool canBypassXWayland() { if (isWayland()) return true; @@ -234,6 +347,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 +409,7 @@ namespace GamescopeWSILayer { bool isBypassingXWayland; bool forceFifo; VkPresentModeKHR presentMode; + VkExtent2D extent; uint32_t serverId = 0; bool retired = false; @@ -391,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(); @@ -407,6 +536,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; } @@ -953,6 +1086,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) @@ -1046,6 +1189,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); @@ -1236,8 +1380,27 @@ 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() && !(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) { + 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; +} + diff --git a/meson.build b/meson.build index 9d0b487eb0..05f7d00ad8 100644 --- a/meson.build +++ b/meson.build @@ -37,6 +37,10 @@ add_project_arguments(cppc.get_supported_arguments([ '-Wno-missing-braces', ]), language: 'cpp') +add_project_arguments(cppc.get_supported_arguments([ + '-fno-exceptions', +]), language: 'cpp') + pipewire_dep = dependency('libpipewire-0.3', required: get_option('pipewire')) librt_dep = cppc.find_library('rt', required : get_option('pipewire')) hwdata_dep = dependency('hwdata', required : false) @@ -90,3 +94,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/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/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/asus.rogally.lcd.lua b/scripts/00-gamescope/displays/asus.rogally.lcd.lua new file mode 100644 index 0000000000..11ba7cc30f --- /dev/null +++ b/scripts/00-gamescope/displays/asus.rogally.lcd.lua @@ -0,0 +1,76 @@ +local rogally_lcd_refresh_rates = { + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120 +} + +gamescope.config.known_displays.rogally_lcd = { + pretty_name = "ASUS ROG Ally/Ally X LCD", + hdr = { + -- Setup some fallbacks for undocking with HDR, meant + -- for the internal panel. It does not support HDR. + 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 the EDID colorimetry for now, but someone should check + -- if the EDID colorimetry truly matches what the display is capable of. + dynamic_refresh_rates = rogally_lcd_refresh_rates, + -- Follow the Steam Deck OLED style for modegen by variang the VFP (Vertical Front Porch) + -- + -- Given that this display is VRR and likely has an FB/Partial FB in the DDIC: + -- it should be able to handle this method, and it is more optimal for latency + -- than elongating the clock. + dynamic_modegen = function(base_mode, refresh) + debug("Generating mode "..refresh.."Hz for ROG Ally with fixed pixel clock") + local vfps = { + 1771, 1720, 1655, 1600, 1549, + 1499, 1455, 1405, 1361, 1320, + 1279, 1224, 1200, 1162, 1120, + 1088, 1055, 1022, 991, 958, + 930, 900, 871, 845, 817, + 794, 762, 740, 715, 690, + 668, 647, 626, 605, 585, + 566, 546, 526, 507, 488, + 470, 452, 435, 419, 402, + 387, 371, 355, 341, 326, + 310, 297, 284, 269, 255, + 244, 229, 217, 204, 194, + 181, 172, 158, 147, 136, + 123, 113, 104, 93, 83, + 74, 64, 54 + } + local vfp = vfps[zero_index(refresh - 48)] + if vfp == nil then + warn("Couldn't do refresh "..refresh.." on ROG Ally") + 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, + -- There is only a single panel model in use across both + -- ROG Ally + ROG Ally X. + matches = function(display) + if display.vendor == "TMX" and display.model == "TL070FVXS01-0" and display.product == 0x0002 then + debug("[rogally_lcd] Matched vendor: "..display.vendor.." model: "..display.model.." product:"..display.product) + return 5000 + end + return -1 + end +} +debug("Registered ASUS ROG Ally/Ally X LCD as a known display") +--debug(inspect(gamescope.config.known_displays.rogally_lcd)) 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..11ed82fa35 --- /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: "..display.vendor.." model: "..display.model.." product:"..display.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 f2951a3931..0b121e8416 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 @@ -70,16 +72,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() @@ -297,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() }; } @@ -382,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 @@ -398,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; @@ -541,6 +536,7 @@ extern GamescopePanelOrientation g_DesiredInternalOrientation; extern bool g_bForceDisableColorMgmt; static LogScope drm_log( "drm" ); +static LogScope liftoff_log_scope( "liftoff" ); static std::unordered_map< std::string, std::string > pnps = {}; @@ -1099,6 +1095,27 @@ extern bool env_to_bool(const char *env); uint32_t g_uAlwaysSignalledSyncobj = 0; int g_nAlwaysSignalledSyncFile = -1; +static void +gamescope_liftoff_log_handler(enum liftoff_log_priority liftoff_priority, const char *fmt, va_list args) +{ + enum LogPriority priority = LOG_DEBUG; + + switch ( liftoff_priority ) + { + case LIFTOFF_ERROR: + priority = LOG_ERROR; + break; + case LIFTOFF_DEBUG: + priority = LOG_DEBUG; + break; + case LIFTOFF_SILENT: + priority = LOG_SILENT; + break; + } + + liftoff_log_scope.vlogf(priority, fmt, args); +} + bool init_drm(struct drm_t *drm, int width, int height, int refresh) { load_pnps(); @@ -1272,8 +1289,9 @@ bool init_drm(struct drm_t *drm, int width, int height, int refresh) std::thread flip_handler_thread( flip_handler_thread_run ); flip_handler_thread.detach(); - if ( drm->bUseLiftoff ) - liftoff_log_set_priority(g_bDebugLayers ? LIFTOFF_DEBUG : LIFTOFF_ERROR); + // Set log priority to the max, liftoff_log_scope will filter for us. + liftoff_log_set_priority(LIFTOFF_DEBUG); + liftoff_log_set_handler(gamescope_liftoff_log_handler); hdr_output_metadata sdr_metadata; memset(&sdr_metadata, 0, sizeof(sdr_metadata)); @@ -2121,71 +2139,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 ); - - 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 ); + bool bHasKnownColorimetry = false; + bool bHasKnownHDRInfo = false; - if ( bSteamDeckDisplay ) { - static constexpr uint32_t kPIDGalileoSDC = 0x3003; - static constexpr uint32_t kPIDGalileoBOE = 0x3004; + CScriptScopedLock script; - if ( pProduct->product == kPIDGalileoSDC ) + 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_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 - { - 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. @@ -2201,6 +2261,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 ); @@ -2211,12 +2276,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; @@ -2299,38 +2359,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 ///////////////////////// @@ -2457,23 +2485,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() ); @@ -3031,17 +3053,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; + } } } } @@ -3190,10 +3220,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..c39caa54b7 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 { @@ -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 c2821a1235..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 @@ -122,7 +123,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; @@ -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: @@ -351,10 +353,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 @@ -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 6498f3b491..32264005c5 100644 --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -38,6 +38,8 @@ #include "drm_include.h" +#define WL_FRACTIONAL_SCALE_DENOMINATOR 120 + extern int g_nPreferredOutputWidth; extern int g_nPreferredOutputHeight; extern bool g_bForceHDR10OutputDebug; @@ -53,6 +55,8 @@ using namespace std::literals; static LogScope xdg_log( "xdg_backend" ); +static const char *GAMESCOPE_plane_tag = "gamescope-plane"; + template auto CallWithAllButLast(Func pFunc, Args&&... args) { @@ -63,6 +67,13 @@ auto CallWithAllButLast(Func pFunc, Args&&... args) return Forwarder(std::forward_as_tuple(args...), std::make_index_sequence()); } +static inline uint32_t WaylandScaleToPhysical( uint32_t pValue, uint32_t pFactor ) { + return pValue * pFactor / WL_FRACTIONAL_SCALE_DENOMINATOR; +} +static inline uint32_t WaylandScaleToLogical( uint32_t pValue, uint32_t pFactor ) { + return div_roundup( pValue * WL_FRACTIONAL_SCALE_DENOMINATOR, pFactor ); +} + #define WAYLAND_NULL() [] ( void *pData, Args... args ) { } #define WAYLAND_USERDATA_TO_THIS(type, name) [] ( void *pData, Args... args ) { type *pThing = (type *)pData; pThing->name( std::forward(args)... ); } @@ -504,7 +515,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; @@ -545,6 +556,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; @@ -577,8 +589,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 +682,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; @@ -896,6 +906,7 @@ namespace gamescope { m_pParent = pParent; m_pSurface = wl_compositor_create_surface( m_pBackend->GetCompositor() ); + wl_proxy_set_tag( (wl_proxy *)m_pSurface, &GAMESCOPE_plane_tag ); wl_surface_set_user_data( m_pSurface, this ); wl_surface_add_listener( m_pSurface, &s_SurfaceListener, this ); @@ -1017,14 +1028,15 @@ namespace gamescope wl_fixed_from_double( oState->flSrcHeight ) ); wp_viewport_set_destination( m_pViewport, - oState->nDstWidth * 120 / uScale, - oState->nDstHeight * 120 / uScale); + WaylandScaleToLogical( oState->nDstWidth, uScale ), + WaylandScaleToLogical( oState->nDstHeight, uScale ) ); + if ( m_pSubsurface ) { wl_subsurface_set_position( m_pSubsurface, - oState->nDestX * 120 / uScale, - oState->nDestY * 120 / uScale ); + WaylandScaleToLogical( oState->nDestX, uScale ), + WaylandScaleToLogical( oState->nDestY, uScale ) ); } // The x/y here does nothing? Why? What is it for... // Use the subsurface set_position thing instead. @@ -1042,7 +1054,10 @@ namespace gamescope void CWaylandPlane::CommitLibDecor( libdecor_configuration *pConfiguration ) { - libdecor_state *pState = libdecor_state_new( g_nOutputWidth, g_nOutputHeight ); + int32_t uScale = GetScale(); + libdecor_state *pState = libdecor_state_new( + WaylandScaleToLogical( g_nOutputWidth, uScale ), + WaylandScaleToLogical( g_nOutputHeight, uScale ) ); libdecor_frame_commit( m_pFrame, pState, pConfiguration ); libdecor_state_free( pState ); } @@ -1149,11 +1164,11 @@ namespace gamescope int nWidth, nHeight; if ( !libdecor_configuration_get_content_size( pConfiguration, m_pFrame, &nWidth, &nHeight ) ) { - nWidth = g_nOutputWidth * 120 / uScale; - nHeight = g_nOutputHeight * 120 / uScale; + nWidth = WaylandScaleToLogical( g_nOutputWidth, uScale ); + nHeight = WaylandScaleToLogical( g_nOutputHeight, uScale ); } - g_nOutputWidth = nWidth * uScale / 120; - g_nOutputHeight = nHeight * uScale / 120; + g_nOutputWidth = WaylandScaleToPhysical( nWidth, uScale ); + g_nOutputHeight = WaylandScaleToPhysical( nHeight, uScale ); CommitLibDecor( pConfiguration ); @@ -1542,23 +1557,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; } @@ -1760,13 +1785,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{}; @@ -1971,6 +1993,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() { @@ -2059,13 +2085,6 @@ namespace gamescope } } - bool CWaylandBackend::SupportsFormat( uint32_t uDRMFormat ) const - { - return UsesModifiers() - ? m_FormatModifiers.contains( uDRMFormat ) - : m_ModifierlessFormats.contains( uDRMFormat ); - } - ///////////////////// // Wayland Callbacks ///////////////////// @@ -2159,11 +2178,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 @@ -2560,6 +2589,9 @@ namespace gamescope void CWaylandInputThread::Wayland_Pointer_Enter( wl_pointer *pPointer, uint32_t uSerial, wl_surface *pSurface, wl_fixed_t fSurfaceX, wl_fixed_t fSurfaceY ) { + if ( !( wl_proxy_get_tag( (wl_proxy *)pSurface ) == &GAMESCOPE_plane_tag ) ) + return; + CWaylandPlane *pPlane = (CWaylandPlane *)wl_surface_get_user_data( pSurface ); if ( !pPlane ) return; @@ -2571,6 +2603,9 @@ namespace gamescope } void CWaylandInputThread::Wayland_Pointer_Leave( wl_pointer *pPointer, uint32_t uSerial, wl_surface *pSurface ) { + if ( !( wl_proxy_get_tag( (wl_proxy *)pSurface ) == &GAMESCOPE_plane_tag ) ) + return; + CWaylandPlane *pPlane = (CWaylandPlane *)wl_surface_get_user_data( pSurface ); if ( !pPlane ) return; 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() ); 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/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/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; diff --git a/src/backend.h b/src/backend.h index 7d9fb465dd..dd295f4ac0 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 @@ -128,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; }; @@ -178,7 +181,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; @@ -203,6 +206,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/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/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 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 ca4001249f..9dff5c4d85 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,5 @@ +#include "Script/Script.h" + #include #include @@ -132,7 +134,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 +196,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" @@ -609,9 +611,6 @@ static void UpdateCompatEnvVars() // Don't minimise stuff on focus loss with SDL. setenv( "SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS", "0", 1 ); - // A sane default here. - setenv( "GAMESCOPE_NV12_COLORSPACE", "k_EStreamColorspace_BT601", 0 ); - const char *pszMangoConfigPath = getenv( "MANGOHUD_CONFIGFILE" ); if ( (g_bLaunchMangoapp && steamMode) && ( !pszMangoConfigPath || !*pszMangoConfigPath ) ) { @@ -808,6 +807,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 da811e8362..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 @@ -161,22 +167,34 @@ 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, check: false).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', 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/pipewire.cpp b/src/pipewire.cpp index c9252eb466..2f06f7cf2c 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; } @@ -98,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, @@ -127,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, diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp index 63d34b3ca9..54d7608dd2 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 @@ -77,7 +78,7 @@ static constexpr mat3x4 g_rgb2yuv_srgb_to_bt709_limited = {{ static constexpr mat3x4 g_rgb2yuv_srgb_to_bt709_full = {{ { 0.2126f, 0.7152f, 0.0722f, 0.0f }, { -0.1146f, -0.3854f, 0.5000f, 0.5f }, - { 0.5000f, -0.4542f, -0.0468f, 0.5f }, + { 0.5000f, -0.4542f, -0.0458f, 0.5f }, }}; static const mat3x4& colorspace_to_conversion_from_srgb_matrix(EStreamColorspace colorspace) { @@ -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, @@ -2159,7 +2154,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 = { @@ -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() @@ -3112,12 +3103,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 +3118,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 +3126,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 +3155,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 +3212,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 +3223,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 +3231,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 +3241,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 +3254,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 +3262,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 +3348,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; @@ -3830,7 +3822,7 @@ std::optional vulkan_screenshot( const struct FrameInfo_t *frameInfo, { float scale = (float)pScreenshotTexture->width() / pYUVOutTexture->width(); - CaptureConvertBlitData_t constants( scale, colorspace_to_conversion_from_srgb_matrix( pScreenshotTexture->streamColorspace() ) ); + CaptureConvertBlitData_t constants( scale, colorspace_to_conversion_from_srgb_matrix( pYUVOutTexture->streamColorspace() ) ); constants.halfExtent[0] = pYUVOutTexture->width() / 2.0f; constants.halfExtent[1] = pYUVOutTexture->height() / 2.0f; cmdBuffer->uploadConstants(constants); @@ -4050,7 +4042,7 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco float scale = (float)compositeImage->width() / pPipewireTexture->width(); if ( ycbcr ) { - CaptureConvertBlitData_t constants( scale, colorspace_to_conversion_from_srgb_matrix( compositeImage->streamColorspace() ) ); + CaptureConvertBlitData_t constants( scale, colorspace_to_conversion_from_srgb_matrix( pPipewireTexture->streamColorspace() ) ); constants.halfExtent[0] = pPipewireTexture->width() / 2.0f; constants.halfExtent[1] = pPipewireTexture->height() / 2.0f; cmdBuffer->uploadConstants(constants); diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp index f5fef302b5..b5371703c0 100644 --- a/src/rendervulkan.hpp +++ b/src/rendervulkan.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -508,8 +509,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; @@ -863,7 +864,7 @@ class CVulkanDevice VkSemaphore m_scratchTimelineSemaphore; std::atomic m_submissionSeqNo = { 0 }; std::vector> m_unusedCmdBufs; - std::unordered_map> m_pendingCmdBufs; + std::map> m_pendingCmdBufs; }; struct TextureState diff --git a/src/reshade_effect_manager.cpp b/src/reshade_effect_manager.cpp index fa37a5cc4e..1d05cc5a37 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,27 +10,48 @@ #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; -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() @@ -172,6 +194,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 +507,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 +672,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 +1665,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 +1929,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_clear_reshade_effect(); +} \ 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 a146e908f1..4d273e125c 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 @@ -195,7 +196,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; @@ -1971,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; @@ -2151,8 +2152,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(); @@ -2188,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 ) @@ -2946,6 +2956,25 @@ std::string get_string_prop( xwayland_ctx_t *ctx, Window win, Atom prop ) return value; } +void set_string_prop( xwayland_ctx_t *ctx, Atom prop, const std::string &value ) +{ + 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 ); +} + static bool win_has_game_id( steamcompmgr_win_t *w ) { @@ -4867,6 +4896,17 @@ 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->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 handle_selection_request(xwayland_ctx_t *ctx, XSelectionRequestEvent *ev) { @@ -4891,11 +4931,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; } @@ -4940,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 { @@ -4957,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 { @@ -6313,7 +6354,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, "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) { @@ -6324,13 +6365,45 @@ 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; } - // 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 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 || !bHasDamage || cv_surface_update_force_only_current_surface; + bool for_current_surface = !w->override_surface() || w->current_surface() == reslistentry.surf; - if (!for_current_surface) + + if ( !for_current_surface ) + { + xwm_log.debugf( "Got commit not for current surface." ); + } + + if ( bOnlyCurrentSurface && !for_current_surface ) { wlserver_lock(); wlr_buffer_unlock( buf ); @@ -7370,13 +7443,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); @@ -7821,7 +7894,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 @@ -7924,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/steamcompmgr.hpp b/src/steamcompmgr.hpp index 91b911b63f..9f384c461c 100644 --- a/src/steamcompmgr.hpp +++ b/src/steamcompmgr.hpp @@ -147,6 +147,8 @@ 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/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; diff --git a/src/wlserver.cpp b/src/wlserver.cpp index ccbd512485..78a86ee0e2 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 @@ -121,7 +122,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 ) { @@ -1153,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) @@ -1178,6 +1182,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 +1746,8 @@ bool wlserver_init( void ) { create_ime_manager( &wlserver ); + create_reshade(); + create_gamescope_xwayland(); create_gamescope_swapchain_factory_v2(); 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