From c69d205ab8eea1cde79a9186aa30c66e9aa49088 Mon Sep 17 00:00:00 2001 From: lizzie Date: Fri, 29 Aug 2025 23:48:25 +0000 Subject: [PATCH 01/20] [gamemode] Make available on other platforms Signed-off-by: lizzie --- CMakeLists.txt | 2 +- externals/gamemode/gamemode_client.h | 28 +++++++++++++++- src/common/CMakeLists.txt | 8 ++--- src/common/gamemode.cpp | 50 ++++++++++++++++++++++++++++ src/common/gamemode.h | 17 ++++++++++ src/common/linux/gamemode.cpp | 40 ---------------------- src/common/linux/gamemode.h | 24 ------------- src/yuzu/main.cpp | 18 +++------- src/yuzu_cmd/yuzu.cpp | 16 ++------- 9 files changed, 105 insertions(+), 98 deletions(-) create mode 100644 src/common/gamemode.cpp create mode 100644 src/common/gamemode.h delete mode 100644 src/common/linux/gamemode.cpp delete mode 100644 src/common/linux/gamemode.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ef3c0bef6e..9e95967b0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -546,7 +546,7 @@ else() find_package(Catch2 3.0.1 REQUIRED) endif() - if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR ANDROID) + if (PLATFORM_LINUX OR ANDROID) find_package(gamemode 1.7 MODULE) endif() diff --git a/externals/gamemode/gamemode_client.h b/externals/gamemode/gamemode_client.h index b9f64fe460..bfbf61c0c2 100644 --- a/externals/gamemode/gamemode_client.h +++ b/externals/gamemode/gamemode_client.h @@ -1,6 +1,9 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* -Copyright (c) 2017-2019, Feral Interactive +Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors All rights reserved. Redistribution and use in source and binary forms, with or without @@ -103,6 +106,7 @@ typedef int (*api_call_pid_return_int)(pid_t); static api_call_return_int REAL_internal_gamemode_request_start = NULL; static api_call_return_int REAL_internal_gamemode_request_end = NULL; static api_call_return_int REAL_internal_gamemode_query_status = NULL; +static api_call_return_int REAL_internal_gamemode_request_restart = NULL; static api_call_return_cstring REAL_internal_gamemode_error_string = NULL; static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL; static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL; @@ -166,6 +170,10 @@ __attribute__((always_inline)) static inline int internal_load_libgamemode(void) (void **)&REAL_internal_gamemode_query_status, sizeof(REAL_internal_gamemode_query_status), false }, + { "real_gamemode_request_restart", + (void **)&REAL_internal_gamemode_request_restart, + sizeof(REAL_internal_gamemode_request_restart), + false }, { "real_gamemode_error_string", (void **)&REAL_internal_gamemode_error_string, sizeof(REAL_internal_gamemode_error_string), @@ -319,6 +327,24 @@ __attribute__((always_inline)) static inline int gamemode_query_status(void) return REAL_internal_gamemode_query_status(); } +/* Redirect to the real libgamemode */ +__attribute__((always_inline)) static inline int gamemode_request_restart(void) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } + + if (REAL_internal_gamemode_request_restart == NULL) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "gamemode_request_restart missing (older host?)"); + return -1; + } + + return REAL_internal_gamemode_request_restart(); +} + /* Redirect to the real libgamemode */ __attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid) { diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 96ea429e5a..ab983f2def 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -68,6 +68,8 @@ add_library( fs/fs_util.h fs/path_util.cpp fs/path_util.h + gamemode.cpp + gamemode.h hash.h heap_tracker.cpp heap_tracker.h @@ -184,11 +186,7 @@ if(ANDROID) android/applets/software_keyboard.h) endif() -if(LINUX AND NOT APPLE) - target_sources(common PRIVATE linux/gamemode.cpp linux/gamemode.h) - - target_link_libraries(common PRIVATE gamemode::headers) -endif() +target_link_libraries(common PRIVATE gamemode::headers) if(ARCHITECTURE_x86_64) target_sources( diff --git a/src/common/gamemode.cpp b/src/common/gamemode.cpp new file mode 100644 index 0000000000..a3f0ba37ab --- /dev/null +++ b/src/common/gamemode.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// While technically available on al *NIX platforms, Linux is only available +// as the primary target of libgamemode.so - so warnings are suppressed +#ifdef __unix__ +#include +#endif +#include "common/gamemode.h" +#include "common/logging/log.h" +#include "common/settings.h" + +namespace Common::FeralGamemode { + +void Start() noexcept { + if (Settings::values.enable_gamemode) { +#ifdef __unix__ + if (gamemode_request_start() < 0) { +#ifdef __linux__ + LOG_WARNING(Frontend, "{}", gamemode_error_string()); +#else + LOG_INFO(Frontend, "{}", gamemode_error_string()); +#endif + } else { + LOG_INFO(Frontend, "Done"); + } +#endif + } +} + +void Stop() noexcept { + if (Settings::values.enable_gamemode) { +#ifdef __unix__ + if (gamemode_request_end() < 0) { +#ifdef __linux__ + LOG_WARNING(Frontend, "{}", gamemode_error_string()); +#else + LOG_INFO(Frontend, "{}", gamemode_error_string()); +#endif + } else { + LOG_INFO(Frontend, "Done"); + } +#endif + } +} + +} // namespace Common::Linux diff --git a/src/common/gamemode.h b/src/common/gamemode.h new file mode 100644 index 0000000000..05b1936bb5 --- /dev/null +++ b/src/common/gamemode.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Common::FeralGamemode { + +/// @brief Start the gamemode client +void Start() noexcept; + +/// @brief Stop the gmemode client +void Stop() noexcept; + +} // namespace Common::FeralGamemode diff --git a/src/common/linux/gamemode.cpp b/src/common/linux/gamemode.cpp deleted file mode 100644 index 8d3e2934a6..0000000000 --- a/src/common/linux/gamemode.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "common/linux/gamemode.h" -#include "common/logging/log.h" -#include "common/settings.h" - -namespace Common::Linux { - -void StartGamemode() { - if (Settings::values.enable_gamemode) { - if (gamemode_request_start() < 0) { - LOG_WARNING(Frontend, "Failed to start gamemode: {}", gamemode_error_string()); - } else { - LOG_INFO(Frontend, "Started gamemode"); - } - } -} - -void StopGamemode() { - if (Settings::values.enable_gamemode) { - if (gamemode_request_end() < 0) { - LOG_WARNING(Frontend, "Failed to stop gamemode: {}", gamemode_error_string()); - } else { - LOG_INFO(Frontend, "Stopped gamemode"); - } - } -} - -void SetGamemodeState(bool state) { - if (state) { - StartGamemode(); - } else { - StopGamemode(); - } -} - -} // namespace Common::Linux diff --git a/src/common/linux/gamemode.h b/src/common/linux/gamemode.h deleted file mode 100644 index b80646ae27..0000000000 --- a/src/common/linux/gamemode.h +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -namespace Common::Linux { - -/** - * Start the (Feral Interactive) Linux gamemode if it is installed and it is activated - */ -void StartGamemode(); - -/** - * Stop the (Feral Interactive) Linux gamemode if it is installed and it is activated - */ -void StopGamemode(); - -/** - * Start or stop the (Feral Interactive) Linux gamemode if it is installed and it is activated - * @param state The new state the gamemode should have - */ -void SetGamemodeState(bool state); - -} // namespace Common::Linux diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d2c12c9d40..7fdf8ed9d9 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -22,9 +22,7 @@ #include #include #endif -#ifdef __linux__ -#include "common/linux/gamemode.h" -#endif +#include "common/gamemode.h" #include @@ -2254,9 +2252,7 @@ void GMainWindow::OnEmulationStopped() { discord_rpc->Update(); -#ifdef __linux__ - Common::Linux::StopGamemode(); -#endif + Common::FeralGamemode::Stop(); // The emulation is stopped, so closing the window or not does not matter anymore disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); @@ -3095,10 +3091,7 @@ void GMainWindow::OnStartGame() { play_time_manager->Start(); discord_rpc->Update(); - -#ifdef __linux__ - Common::Linux::StartGamemode(); -#endif + Common::FeralGamemode::StartGamemode(); } void GMainWindow::OnRestartGame() { @@ -3119,10 +3112,7 @@ void GMainWindow::OnPauseGame() { play_time_manager->Stop(); UpdateMenuState(); AllowOSSleep(); - -#ifdef __linux__ - Common::Linux::StopGamemode(); -#endif + Common::FeralGamemode::Stop(); } void GMainWindow::OnPauseContinueGame() { diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 4a99f34861..f9f83858e4 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -62,10 +62,7 @@ __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; } #endif - -#ifdef __linux__ -#include "common/linux/gamemode.h" -#endif +#include "common/gamemode.h" static void PrintHelp(const char* argv0) { std::cout << "Usage: " << argv0 @@ -432,10 +429,7 @@ int main(int argc, char** argv) { // Just exit right away. exit(0); }); - -#ifdef __linux__ - Common::Linux::StartGamemode(); -#endif + Common::FeralGamemode::StartGamemode(); void(system.Run()); if (system.DebuggerEnabled()) { @@ -447,11 +441,7 @@ int main(int argc, char** argv) { system.DetachDebugger(); void(system.Pause()); system.ShutdownMainProcess(); - -#ifdef __linux__ - Common::Linux::StopGamemode(); -#endif - + Common::FeralGamemode::Stop(); detached_tasks.WaitForAllTasks(); return 0; } From c27b940103b37b5d789dfa6aef87a0890de1b431 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 30 Aug 2025 00:04:36 +0000 Subject: [PATCH 02/20] [gamemode] make option available on all nixes Signed-off-by: lizzie --- src/yuzu/main.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 7fdf8ed9d9..d33b5008e3 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -415,10 +415,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) #ifdef __unix__ SetupSigInterrupts(); #endif - -#ifdef __linux__ SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); -#endif UISettings::RestoreWindowState(config); @@ -3407,9 +3404,7 @@ void GMainWindow::OnConfigure() { const auto old_theme = UISettings::values.theme; const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); const auto old_language_index = Settings::values.language_index.GetValue(); -#ifdef __linux__ const bool old_gamemode = Settings::values.enable_gamemode.GetValue(); -#endif Settings::SetConfiguringGlobal(true); ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), @@ -3469,11 +3464,9 @@ void GMainWindow::OnConfigure() { if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); } -#ifdef __linux__ if (Settings::values.enable_gamemode.GetValue() != old_gamemode) { SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); } -#endif if (!multiplayer_state->IsHostingPublicRoom()) { multiplayer_state->UpdateCredentials(); @@ -4755,13 +4748,15 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { discord_rpc->Update(); } -#ifdef __linux__ void GMainWindow::SetGamemodeEnabled(bool state) { if (emulation_running) { - Common::Linux::SetGamemodeState(state); + if (state) { + Common::FeralGamemode::Start(); + } else { + Common::FeralGamemode::Stop(); + } } } -#endif void GMainWindow::changeEvent(QEvent* event) { #ifdef __unix__ @@ -4920,7 +4915,9 @@ int main(int argc, char* argv[]) { // the user folder in the Qt Frontend, we need to cd into that working directory const auto bin_path = Common::FS::GetBundleDirectory() / ".."; chdir(Common::FS::PathToUTF8String(bin_path).c_str()); -#elif defined(__unix__) && !defined(__ANDROID__) +#endif + +#ifdef __unix__ // Set the DISPLAY variable in order to open web browsers // TODO (lat9nq): Find a better solution for AppImages to start external applications if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) { From 2e7ffb53d2938dd0380b74a051edc396c8d7ab7d Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 30 Aug 2025 08:43:59 +0000 Subject: [PATCH 03/20] [gamemode] extra win/lin/macos fixes Signed-off-by: lizzie --- externals/CMakeLists.txt | 2 +- src/yuzu/main.cpp | 2 +- src/yuzu_cmd/yuzu.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 754ba61a0b..4c11104de2 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -140,7 +140,7 @@ if (ANDROID AND ARCHITECTURE_arm64) AddJsonPackage(libadrenotools) endif() -if (UNIX AND NOT APPLE AND NOT TARGET gamemode::headers) +if (NOT TARGET gamemode::headers) add_library(gamemode INTERFACE) target_include_directories(gamemode INTERFACE gamemode) add_library(gamemode::headers ALIAS gamemode) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d33b5008e3..4ab578067d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3088,7 +3088,7 @@ void GMainWindow::OnStartGame() { play_time_manager->Start(); discord_rpc->Update(); - Common::FeralGamemode::StartGamemode(); + Common::FeralGamemode::Start(); } void GMainWindow::OnRestartGame() { diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index f9f83858e4..8e46f36aec 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -429,7 +429,7 @@ int main(int argc, char** argv) { // Just exit right away. exit(0); }); - Common::FeralGamemode::StartGamemode(); + Common::FeralGamemode::Start(); void(system.Run()); if (system.DebuggerEnabled()) { From a7c22c19587033fb7a34e0d1ab8658217520cc23 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 30 Aug 2025 13:40:57 +0000 Subject: [PATCH 04/20] [gamemode] windows fix Signed-off-by: lizzie --- src/yuzu/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.h b/src/yuzu/main.h index e3922759b0..c6980968c8 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -315,8 +315,8 @@ private: void SetupSigInterrupts(); static void HandleSigInterrupt(int); void OnSigInterruptNotifierActivated(); - void SetGamemodeEnabled(bool state); #endif + void SetGamemodeEnabled(bool state); Service::AM::FrontendAppletParameters ApplicationAppletParameters(); Service::AM::FrontendAppletParameters LibraryAppletParameters(u64 program_id, From c7db55f80dd8c0d96648624fcae550a5c66ec7df Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 13 Sep 2025 20:48:40 +0000 Subject: [PATCH 05/20] [gamemode] default disable on msvc, move to UI category Signed-off-by: lizzie --- src/common/settings.h | 10 ++++++++-- src/common/settings_common.h | 1 - src/qt_common/shared_translation.cpp | 5 ++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/common/settings.h b/src/common/settings.h index 8605445837..7b676cb497 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -641,8 +641,14 @@ struct Values { true, true}; - // Linux - SwitchableSetting enable_gamemode{linkage, true, "enable_gamemode", Category::Linux}; + // Linux/MinGW may support (requires libdl support) + SwitchableSetting enable_gamemode{linkage, +#ifndef _MSC_VER + true, +#else + false, +#endif + "enable_gamemode", Category::UiGeneral}; // Controls InputSetting> players; diff --git a/src/common/settings_common.h b/src/common/settings_common.h index af16ec692b..7902cbf945 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h @@ -44,7 +44,6 @@ enum class Category : u32 { Multiplayer, Services, Paths, - Linux, LibraryApplet, MaxEnum, }; diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index 98a55e1fcf..230321e722 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -445,7 +445,10 @@ std::unique_ptr InitializeTranslations(QObject* parent) tr("Whether or not to check for updates upon startup.")); // Linux - INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QString()); + INSERT(UISettings, + enable_gamemode, + tr("Enable Gamemode"), + QString()); // Ui Debugging From 929de88588e0ee5c818b3ddaec84f08b198170ab Mon Sep 17 00:00:00 2001 From: crueter Date: Fri, 26 Sep 2025 19:30:17 -0400 Subject: [PATCH 06/20] [cmake] vendor gamemode Signed-off-by: crueter --- CMakeLists.txt | 6 +- externals/CMakeLists.txt | 6 +- externals/cpmfile.json | 7 + externals/gamemode/gamemode_client.h | 402 --------------------------- 4 files changed, 12 insertions(+), 409 deletions(-) delete mode 100644 externals/gamemode/gamemode_client.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e95967b0a..9238a40924 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -320,7 +320,6 @@ endif() if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX)) set(HAS_NCE 1) add_compile_definitions(HAS_NCE=1) - find_package(oaknut 2.0.1) endif() if (YUZU_ROOM) @@ -546,10 +545,6 @@ else() find_package(Catch2 3.0.1 REQUIRED) endif() - if (PLATFORM_LINUX OR ANDROID) - find_package(gamemode 1.7 MODULE) - endif() - if (ENABLE_OPENSSL) find_package(OpenSSL 1.1.1 REQUIRED) endif() @@ -673,6 +668,7 @@ add_subdirectory(externals) # pass targets from externals find_package(libusb) find_package(VulkanMemoryAllocator) +find_package(gamemode) if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) find_package(xbyak) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 4c11104de2..2d0d5a7401 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -140,9 +140,11 @@ if (ANDROID AND ARCHITECTURE_arm64) AddJsonPackage(libadrenotools) endif() -if (NOT TARGET gamemode::headers) +AddJsonPackage(gamemode) + +if (gamemode_ADDED) add_library(gamemode INTERFACE) - target_include_directories(gamemode INTERFACE gamemode) + target_include_directories(gamemode INTERFACE ${gamemode_SOURCE_DIR}) add_library(gamemode::headers ALIAS gamemode) endif() diff --git a/externals/cpmfile.json b/externals/cpmfile.json index dcafc8f97d..6758957550 100644 --- a/externals/cpmfile.json +++ b/externals/cpmfile.json @@ -70,5 +70,12 @@ "sha": "73f3cbb237", "hash": "c08c03063938339d61392b687562909c1a92615b6ef39ec8df19ea472aa6b6478e70d7d5e33d4a27b5d23f7806daf57fe1bacb8124c8a945c918c7663a9e8532", "find_args": "CONFIG" + }, + "gamemode": { + "repo": "FeralInteractive/gamemode", + "sha": "ce6fe122f3", + "hash": "ff62f3c8638528e7afb7336f7c8f9940453d2871446c36f639e3114cc8b5c7f9a817ed209c45e0e061793fc3ee9ba35a4b05140d39f5175c2aacfa0703fddfc4", + "version": "1.7", + "find_args": "MODULE" } } diff --git a/externals/gamemode/gamemode_client.h b/externals/gamemode/gamemode_client.h deleted file mode 100644 index bfbf61c0c2..0000000000 --- a/externals/gamemode/gamemode_client.h +++ /dev/null @@ -1,402 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -/* - -Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Feral Interactive nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - */ -#ifndef CLIENT_GAMEMODE_H -#define CLIENT_GAMEMODE_H -/* - * GameMode supports the following client functions - * Requests are refcounted in the daemon - * - * int gamemode_request_start() - Request gamemode starts - * 0 if the request was sent successfully - * -1 if the request failed - * - * int gamemode_request_end() - Request gamemode ends - * 0 if the request was sent successfully - * -1 if the request failed - * - * GAMEMODE_AUTO can be defined to make the above two functions apply during static init and - * destruction, as appropriate. In this configuration, errors will be printed to stderr - * - * int gamemode_query_status() - Query the current status of gamemode - * 0 if gamemode is inactive - * 1 if gamemode is active - * 2 if gamemode is active and this client is registered - * -1 if the query failed - * - * int gamemode_request_start_for(pid_t pid) - Request gamemode starts for another process - * 0 if the request was sent successfully - * -1 if the request failed - * -2 if the request was rejected - * - * int gamemode_request_end_for(pid_t pid) - Request gamemode ends for another process - * 0 if the request was sent successfully - * -1 if the request failed - * -2 if the request was rejected - * - * int gamemode_query_status_for(pid_t pid) - Query status of gamemode for another process - * 0 if gamemode is inactive - * 1 if gamemode is active - * 2 if gamemode is active and this client is registered - * -1 if the query failed - * - * const char* gamemode_error_string() - Get an error string - * returns a string describing any of the above errors - * - * Note: All the above requests can be blocking - dbus requests can and will block while the daemon - * handles the request. It is not recommended to make these calls in performance critical code - */ - -#include -#include - -#include -#include - -#include - -#include - -static char internal_gamemode_client_error_string[512] = { 0 }; - -/** - * Load libgamemode dynamically to dislodge us from most dependencies. - * This allows clients to link and/or use this regardless of runtime. - * See SDL2 for an example of the reasoning behind this in terms of - * dynamic versioning as well. - */ -static volatile int internal_libgamemode_loaded = 1; - -/* Typedefs for the functions to load */ -typedef int (*api_call_return_int)(void); -typedef const char *(*api_call_return_cstring)(void); -typedef int (*api_call_pid_return_int)(pid_t); - -/* Storage for functors */ -static api_call_return_int REAL_internal_gamemode_request_start = NULL; -static api_call_return_int REAL_internal_gamemode_request_end = NULL; -static api_call_return_int REAL_internal_gamemode_query_status = NULL; -static api_call_return_int REAL_internal_gamemode_request_restart = NULL; -static api_call_return_cstring REAL_internal_gamemode_error_string = NULL; -static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL; -static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL; -static api_call_pid_return_int REAL_internal_gamemode_query_status_for = NULL; - -/** - * Internal helper to perform the symbol binding safely. - * - * Returns 0 on success and -1 on failure - */ -__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol( - void *handle, const char *name, void **out_func, size_t func_size, bool required) -{ - void *symbol_lookup = NULL; - char *dl_error = NULL; - - /* Safely look up the symbol */ - symbol_lookup = dlsym(handle, name); - dl_error = dlerror(); - if (required && (dl_error || !symbol_lookup)) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "dlsym failed - %s", - dl_error); - return -1; - } - - /* Have the symbol correctly, copy it to make it usable */ - memcpy(out_func, &symbol_lookup, func_size); - return 0; -} - -/** - * Loads libgamemode and needed functions - * - * Returns 0 on success and -1 on failure - */ -__attribute__((always_inline)) static inline int internal_load_libgamemode(void) -{ - /* We start at 1, 0 is a success and -1 is a fail */ - if (internal_libgamemode_loaded != 1) { - return internal_libgamemode_loaded; - } - - /* Anonymous struct type to define our bindings */ - struct binding { - const char *name; - void **functor; - size_t func_size; - bool required; - } bindings[] = { - { "real_gamemode_request_start", - (void **)&REAL_internal_gamemode_request_start, - sizeof(REAL_internal_gamemode_request_start), - true }, - { "real_gamemode_request_end", - (void **)&REAL_internal_gamemode_request_end, - sizeof(REAL_internal_gamemode_request_end), - true }, - { "real_gamemode_query_status", - (void **)&REAL_internal_gamemode_query_status, - sizeof(REAL_internal_gamemode_query_status), - false }, - { "real_gamemode_request_restart", - (void **)&REAL_internal_gamemode_request_restart, - sizeof(REAL_internal_gamemode_request_restart), - false }, - { "real_gamemode_error_string", - (void **)&REAL_internal_gamemode_error_string, - sizeof(REAL_internal_gamemode_error_string), - true }, - { "real_gamemode_request_start_for", - (void **)&REAL_internal_gamemode_request_start_for, - sizeof(REAL_internal_gamemode_request_start_for), - false }, - { "real_gamemode_request_end_for", - (void **)&REAL_internal_gamemode_request_end_for, - sizeof(REAL_internal_gamemode_request_end_for), - false }, - { "real_gamemode_query_status_for", - (void **)&REAL_internal_gamemode_query_status_for, - sizeof(REAL_internal_gamemode_query_status_for), - false }, - }; - - void *libgamemode = NULL; - - /* Try and load libgamemode */ - libgamemode = dlopen("libgamemode.so.0", RTLD_NOW); - if (!libgamemode) { - /* Attempt to load unversioned library for compatibility with older - * versions (as of writing, there are no ABI changes between the two - - * this may need to change if ever ABI-breaking changes are made) */ - libgamemode = dlopen("libgamemode.so", RTLD_NOW); - if (!libgamemode) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "dlopen failed - %s", - dlerror()); - internal_libgamemode_loaded = -1; - return -1; - } - } - - /* Attempt to bind all symbols */ - for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) { - struct binding *binder = &bindings[i]; - - if (internal_bind_libgamemode_symbol(libgamemode, - binder->name, - binder->functor, - binder->func_size, - binder->required)) { - internal_libgamemode_loaded = -1; - return -1; - }; - } - - /* Success */ - internal_libgamemode_loaded = 0; - return 0; -} - -/** - * Redirect to the real libgamemode - */ -__attribute__((always_inline)) static inline const char *gamemode_error_string(void) -{ - /* If we fail to load the system gamemode, or we have an error string already, return our error - * string instead of diverting to the system version */ - if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') { - return internal_gamemode_client_error_string; - } - - /* Assert for static analyser that the function is not NULL */ - assert(REAL_internal_gamemode_error_string != NULL); - - return REAL_internal_gamemode_error_string(); -} - -/** - * Redirect to the real libgamemode - * Allow automatically requesting game mode - * Also prints errors as they happen. - */ -#ifdef GAMEMODE_AUTO -__attribute__((constructor)) -#else -__attribute__((always_inline)) static inline -#endif -int gamemode_request_start(void) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { -#ifdef GAMEMODE_AUTO - fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); -#endif - return -1; - } - - /* Assert for static analyser that the function is not NULL */ - assert(REAL_internal_gamemode_request_start != NULL); - - if (REAL_internal_gamemode_request_start() < 0) { -#ifdef GAMEMODE_AUTO - fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); -#endif - return -1; - } - - return 0; -} - -/* Redirect to the real libgamemode */ -#ifdef GAMEMODE_AUTO -__attribute__((destructor)) -#else -__attribute__((always_inline)) static inline -#endif -int gamemode_request_end(void) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { -#ifdef GAMEMODE_AUTO - fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); -#endif - return -1; - } - - /* Assert for static analyser that the function is not NULL */ - assert(REAL_internal_gamemode_request_end != NULL); - - if (REAL_internal_gamemode_request_end() < 0) { -#ifdef GAMEMODE_AUTO - fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); -#endif - return -1; - } - - return 0; -} - -/* Redirect to the real libgamemode */ -__attribute__((always_inline)) static inline int gamemode_query_status(void) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } - - if (REAL_internal_gamemode_query_status == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_query_status missing (older host?)"); - return -1; - } - - return REAL_internal_gamemode_query_status(); -} - -/* Redirect to the real libgamemode */ -__attribute__((always_inline)) static inline int gamemode_request_restart(void) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } - - if (REAL_internal_gamemode_request_restart == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_request_restart missing (older host?)"); - return -1; - } - - return REAL_internal_gamemode_request_restart(); -} - -/* Redirect to the real libgamemode */ -__attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } - - if (REAL_internal_gamemode_request_start_for == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_request_start_for missing (older host?)"); - return -1; - } - - return REAL_internal_gamemode_request_start_for(pid); -} - -/* Redirect to the real libgamemode */ -__attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } - - if (REAL_internal_gamemode_request_end_for == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_request_end_for missing (older host?)"); - return -1; - } - - return REAL_internal_gamemode_request_end_for(pid); -} - -/* Redirect to the real libgamemode */ -__attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } - - if (REAL_internal_gamemode_query_status_for == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_query_status_for missing (older host?)"); - return -1; - } - - return REAL_internal_gamemode_query_status_for(pid); -} - -#endif // CLIENT_GAMEMODE_H From 33a1355caa5534de3edaca44eaf878e2184e8332 Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 07:46:59 +0000 Subject: [PATCH 07/20] fix licsense Signed-off-by: lizzie --- src/common/settings_common.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 7902cbf945..0107895c8c 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later From bf302d7917f2bd4b2638ac61a751fedd5727c7a6 Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 18:40:29 +0200 Subject: [PATCH 08/20] [common] No need to specify min/max for settings; fix crash when OOB value is given for some settings (#2609) This fixes issues when migrating settings that refer to invalid filters/scales. For example if we had 5 filters, but we set filter=6, the program would crash. This also makes so specifying min/max manually isn't needed (but can still be set for cases like NCE). Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2609 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/common/settings.h | 41 ++----- src/common/settings_enums.h | 120 ++++++++------------- src/common/settings_setting.h | 67 ++++++------ src/yuzu/configuration/configure_audio.cpp | 6 +- 4 files changed, 84 insertions(+), 150 deletions(-) diff --git a/src/common/settings.h b/src/common/settings.h index 8605445837..891bde608c 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -178,7 +178,7 @@ struct Values { SwitchableSetting audio_input_device_id{ linkage, "auto", "input_device", Category::Audio, Specialization::RuntimeList}; SwitchableSetting sound_index{ - linkage, AudioMode::Stereo, AudioMode::Mono, AudioMode::Surround, + linkage, AudioMode::Stereo, "sound_index", Category::SystemAudio, Specialization::Default, true, true}; SwitchableSetting volume{linkage, @@ -199,8 +199,6 @@ struct Values { SwitchableSetting use_multi_core{linkage, true, "use_multi_core", Category::Core}; SwitchableSetting memory_layout_mode{linkage, MemoryLayout::Memory_4Gb, - MemoryLayout::Memory_4Gb, - MemoryLayout::Memory_12Gb, "memory_layout_mode", Category::Core, Specialization::Default, @@ -240,9 +238,8 @@ struct Values { #endif "cpu_backend", Category::Cpu}; - SwitchableSetting cpu_accuracy{linkage, CpuAccuracy::Auto, - CpuAccuracy::Auto, CpuAccuracy::Paranoid, - "cpu_accuracy", Category::Cpu}; + SwitchableSetting cpu_accuracy{linkage, CpuAccuracy::Auto, + "cpu_accuracy", Category::Cpu}; SwitchableSetting use_fast_cpu_time{linkage, false, @@ -324,10 +321,10 @@ struct Values { // Renderer SwitchableSetting renderer_backend{ - linkage, RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, + linkage, RendererBackend::Vulkan, "backend", Category::Renderer}; SwitchableSetting shader_backend{ - linkage, ShaderBackend::SpirV, ShaderBackend::Glsl, ShaderBackend::SpirV, + linkage, ShaderBackend::SpirV, "shader_backend", Category::Renderer, Specialization::RuntimeList}; SwitchableSetting vulkan_device{linkage, 0, "vulkan_device", Category::Renderer, Specialization::RuntimeList}; @@ -342,8 +339,6 @@ struct Values { Category::Renderer}; SwitchableSetting optimize_spirv_output{linkage, SpirvOptimizeMode::Never, - SpirvOptimizeMode::Never, - SpirvOptimizeMode::Always, "optimize_spirv_output", Category::Renderer}; SwitchableSetting use_asynchronous_gpu_emulation{ @@ -354,12 +349,10 @@ struct Values { #else AstcDecodeMode::Gpu, #endif - AstcDecodeMode::Cpu, - AstcDecodeMode::CpuAsynchronous, "accelerate_astc", Category::Renderer}; SwitchableSetting vsync_mode{ - linkage, VSyncMode::Fifo, VSyncMode::Immediate, VSyncMode::FifoRelaxed, + linkage, VSyncMode::Fifo, "use_vsync", Category::Renderer, Specialization::RuntimeList, true, true}; SwitchableSetting nvdec_emulation{linkage, NvdecEmulation::Gpu, @@ -372,8 +365,6 @@ struct Values { #else FullscreenMode::Exclusive, #endif - FullscreenMode::Borderless, - FullscreenMode::Exclusive, "fullscreen_mode", Category::Renderer, Specialization::Default, @@ -381,8 +372,6 @@ struct Values { true}; SwitchableSetting aspect_ratio{linkage, AspectRatio::R16_9, - AspectRatio::R16_9, - AspectRatio::Stretch, "aspect_ratio", Category::Renderer, Specialization::Default, @@ -430,8 +419,6 @@ struct Values { #else GpuAccuracy::High, #endif - GpuAccuracy::Normal, - GpuAccuracy::Extreme, "gpu_accuracy", Category::RendererAdvanced, Specialization::Default, @@ -442,8 +429,6 @@ struct Values { SwitchableSetting dma_accuracy{linkage, DmaAccuracy::Default, - DmaAccuracy::Default, - DmaAccuracy::Safe, "dma_accuracy", Category::RendererAdvanced, Specialization::Default, @@ -456,20 +441,14 @@ struct Values { #else AnisotropyMode::Automatic, #endif - AnisotropyMode::Automatic, - AnisotropyMode::X16, "max_anisotropy", Category::RendererAdvanced}; SwitchableSetting astc_recompression{linkage, AstcRecompression::Uncompressed, - AstcRecompression::Uncompressed, - AstcRecompression::Bc3, "astc_recompression", Category::RendererAdvanced}; SwitchableSetting vram_usage_mode{linkage, VramUsageMode::Conservative, - VramUsageMode::Conservative, - VramUsageMode::Aggressive, "vram_usage_mode", Category::RendererAdvanced}; SwitchableSetting skip_cpu_inner_invalidation{linkage, @@ -595,14 +574,10 @@ struct Values { // System SwitchableSetting language_index{linkage, Language::EnglishAmerican, - Language::Japanese, - Language::Serbian, "language_index", Category::System}; - SwitchableSetting region_index{linkage, Region::Usa, Region::Japan, - Region::Taiwan, "region_index", Category::System}; - SwitchableSetting time_zone_index{linkage, TimeZone::Auto, - TimeZone::Auto, TimeZone::Zulu, + SwitchableSetting region_index{linkage, Region::Usa, "region_index", Category::System}; + SwitchableSetting time_zone_index{linkage, TimeZone::Auto, "time_zone_index", Category::System}; // Measured in seconds since epoch SwitchableSetting custom_rtc_enabled{ diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index ebfa4ceb9e..0e5a08d845 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -10,6 +10,7 @@ #pragma once #include +#include #include #include #include "common/common_types.h" @@ -18,8 +19,10 @@ namespace Settings { template struct EnumMetadata { - static std::vector> Canonicalizations(); + static std::vector> Canonicalizations(); static u32 Index(); + static constexpr T GetFirst(); + static constexpr T GetLast(); }; #define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__)) @@ -69,138 +72,101 @@ struct EnumMetadata { #define PAIR_1(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_2(N, __VA_ARGS__)) #define PAIR(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_1(N, __VA_ARGS__)) -#define ENUM(NAME, ...) \ - enum class NAME : u32 { __VA_ARGS__ }; \ - template <> \ - inline std::vector> EnumMetadata::Canonicalizations() { \ - return {PAIR(NAME, __VA_ARGS__)}; \ - } \ - template <> \ - inline u32 EnumMetadata::Index() { \ - return __COUNTER__; \ +#define PP_HEAD(A, ...) A + +#define ENUM(NAME, ...) \ + enum class NAME : u32 { __VA_ARGS__ }; \ + template<> inline std::vector> EnumMetadata::Canonicalizations() { \ + return {PAIR(NAME, __VA_ARGS__)}; \ + } \ + template<> inline u32 EnumMetadata::Index() { \ + return __COUNTER__; \ + } \ + template<> inline constexpr NAME EnumMetadata::GetFirst() { \ + return NAME::PP_HEAD(__VA_ARGS__); \ + } \ + template<> inline constexpr NAME EnumMetadata::GetLast() { \ + return (std::vector>{PAIR(NAME, __VA_ARGS__)}).back().second; \ } // AudioEngine must be specified discretely due to having existing but slightly different // canonicalizations // TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id -enum class AudioEngine : u32 { - Auto, - Cubeb, - Sdl2, - Null, - Oboe, -}; - -template <> -inline std::vector> -EnumMetadata::Canonicalizations() { +enum class AudioEngine : u32 { Auto, Cubeb, Sdl2, Null, Oboe, }; +template<> +inline std::vector> EnumMetadata::Canonicalizations() { return { {"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2}, {"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe}, }; } - -template <> +/// @brief This is just a sufficiently large number that is more than the number of other enums declared here +template<> inline u32 EnumMetadata::Index() { - // This is just a sufficiently large number that is more than the number of other enums declared - // here return 100; } +template<> +inline constexpr AudioEngine EnumMetadata::GetFirst() { + return AudioEngine::Auto; +} +template<> +inline constexpr AudioEngine EnumMetadata::GetLast() { + return AudioEngine::Oboe; +} ENUM(AudioMode, Mono, Stereo, Surround); +static_assert(EnumMetadata::GetFirst() == AudioMode::Mono); +static_assert(EnumMetadata::GetLast() == AudioMode::Surround); ENUM(Language, Japanese, EnglishAmerican, French, German, Italian, Spanish, Chinese, Korean, Dutch, Portuguese, Russian, Taiwanese, EnglishBritish, FrenchCanadian, SpanishLatin, ChineseSimplified, ChineseTraditional, PortugueseBrazilian, Serbian); - ENUM(Region, Japan, Usa, Europe, Australia, China, Korea, Taiwan); - ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt, Gb, GbEire, Gmt, - GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica, - Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt, - Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu); - + GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica, + Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt, + Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu); ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16); - ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous); - ENUM(AstcRecompression, Uncompressed, Bc1, Bc3); - ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed); - ENUM(VramUsageMode, Conservative, Aggressive); - ENUM(RendererBackend, OpenGL, Vulkan, Null); - ENUM(ShaderBackend, Glsl, Glasm, SpirV); - ENUM(GpuAccuracy, Normal, High, Extreme); - ENUM(DmaAccuracy, Default, Unsafe, Safe); - ENUM(CpuBackend, Dynarmic, Nce); - ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); - ENUM(CpuClock, Boost, Fast) - ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb, Memory_10Gb, Memory_12Gb); - ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never); - ENUM(FullscreenMode, Borderless, Exclusive); - ENUM(NvdecEmulation, Off, Cpu, Gpu); - -ENUM(ResolutionSetup, - Res1_4X, - Res1_2X, - Res3_4X, - Res1X, - Res3_2X, - Res2X, - Res3X, - Res4X, - Res5X, - Res6X, - Res7X, - Res8X); - +ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X); ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); - ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); - ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); - ENUM(ConsoleMode, Handheld, Docked); - ENUM(AppletMode, HLE, LLE); - ENUM(SpirvOptimizeMode, Never, OnLoad, Always); - ENUM(GpuOverclock, Low, Medium, High) - ENUM(TemperatureUnits, Celsius, Fahrenheit) template -inline std::string CanonicalizeEnum(Type id) { +inline std::string_view CanonicalizeEnum(Type id) { const auto group = EnumMetadata::Canonicalizations(); - for (auto& [name, value] : group) { - if (value == id) { + for (auto& [name, value] : group) + if (value == id) return name; - } - } return "unknown"; } template inline Type ToEnum(const std::string& canonicalization) { const auto group = EnumMetadata::Canonicalizations(); - for (auto& [name, value] : group) { - if (name == canonicalization) { + for (auto& [name, value] : group) + if (name == canonicalization) return value; - } - } return {}; } } // namespace Settings diff --git a/src/common/settings_setting.h b/src/common/settings_setting.h index 0aba2e11c9..a7e6bb6168 100644 --- a/src/common/settings_setting.h +++ b/src/common/settings_setting.h @@ -72,10 +72,17 @@ public: u32 specialization_ = Specialization::Default, bool save_ = true, bool runtime_modifiable_ = false, BasicSetting* other_setting_ = nullptr) requires(ranged) - : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_, - other_setting_), + : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_, other_setting_), value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val} {} + explicit Setting(Linkage& linkage, const Type& default_val, + const std::string& name, Category category_, + u32 specialization_ = Specialization::Default, bool save_ = true, + bool runtime_modifiable_ = false, BasicSetting* other_setting_ = nullptr) + requires(ranged && std::is_enum_v) + : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_, other_setting_), + value{default_val}, default_value{default_val}, maximum{EnumMetadata::GetLast()}, minimum{EnumMetadata::GetFirst()} {} + /** * Returns a reference to the setting's value. * @@ -119,9 +126,6 @@ protected: return value_.has_value() ? std::to_string(*value_) : "none"; } else if constexpr (std::is_same_v) { return value_ ? "true" : "false"; - } else if constexpr (std::is_same_v) { - // Compatibility with old AudioEngine setting being a string - return CanonicalizeEnum(value_); } else if constexpr (std::is_floating_point_v) { return fmt::format("{:f}", value_); } else if constexpr (std::is_enum_v) { @@ -207,7 +211,7 @@ public: [[nodiscard]] std::string Canonicalize() const override final { if constexpr (std::is_enum_v) { - return CanonicalizeEnum(this->GetValue()); + return std::string{CanonicalizeEnum(this->GetValue())}; } else { return ToString(this->GetValue()); } @@ -288,41 +292,32 @@ public: * @param other_setting_ A second Setting to associate to this one in metadata */ template - explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name, - Category category_, u32 specialization_ = Specialization::Default, - bool save_ = true, bool runtime_modifiable_ = false, - typename std::enable_if::type other_setting_ = nullptr) - : Setting{ - linkage, default_val, name, category_, specialization_, - save_, runtime_modifiable_, other_setting_} { + explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name, Category category_, u32 specialization_ = Specialization::Default, bool save_ = true, bool runtime_modifiable_ = false, T* other_setting_ = nullptr) requires(!ranged) + : Setting{ linkage, default_val, name, category_, specialization_, save_, runtime_modifiable_, other_setting_} { linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); }); } virtual ~SwitchableSetting() = default; - /** - * Sets a default value, minimum value, maximum value, and label. - * - * @param linkage Setting registry - * @param default_val Initial value of the setting, and default value of the setting - * @param min_val Sets the minimum allowed value of the setting - * @param max_val Sets the maximum allowed value of the setting - * @param name Label for the setting - * @param category_ Category of the setting AKA INI group - * @param specialization_ Suggestion for how frontend implementations represent this in a config - * @param save_ Suggests that this should or should not be saved to a frontend config file - * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded - * @param other_setting_ A second Setting to associate to this one in metadata - */ + /// @brief Sets a default value, minimum value, maximum value, and label. + /// @param linkage Setting registry + /// @param default_val Initial value of the setting, and default value of the setting + /// @param min_val Sets the minimum allowed value of the setting + /// @param max_val Sets the maximum allowed value of the setting + /// @param name Label for the setting + /// @param category_ Category of the setting AKA INI group + /// @param specialization_ Suggestion for how frontend implementations represent this in a config + /// @param save_ Suggests that this should or should not be saved to a frontend config file + /// @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded + /// @param other_setting_ A second Setting to associate to this one in metadata template - explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val, - const Type& max_val, const std::string& name, Category category_, - u32 specialization_ = Specialization::Default, bool save_ = true, - bool runtime_modifiable_ = false, - typename std::enable_if::type other_setting_ = nullptr) - : Setting{linkage, default_val, min_val, - max_val, name, category_, - specialization_, save_, runtime_modifiable_, - other_setting_} { + explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val, const Type& max_val, const std::string& name, Category category_, u32 specialization_ = Specialization::Default, bool save_ = true, bool runtime_modifiable_ = false, T* other_setting_ = nullptr) requires(ranged) + : Setting{linkage, default_val, min_val, max_val, name, category_, specialization_, save_, runtime_modifiable_, other_setting_} { + linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); }); + } + + template + explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name, Category category_, u32 specialization_ = Specialization::Default, bool save_ = true, bool runtime_modifiable_ = false, T* other_setting_ = nullptr) requires(ranged) + : Setting{linkage, default_val, EnumMetadata::GetFirst(), EnumMetadata::GetLast(), name, category_, specialization_, save_, runtime_modifiable_, other_setting_} { linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); }); } diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index a7ebae91f8..af81ef552e 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -270,10 +270,8 @@ void ConfigureAudio::UpdateAudioDevices(int sink_index) { void ConfigureAudio::InitializeAudioSinkComboBox() { sink_combo_box->clear(); sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); - - for (const auto& id : AudioCore::Sink::GetSinkIDs()) { - sink_combo_box->addItem(QString::fromStdString(Settings::CanonicalizeEnum(id))); - } + for (const auto& id : AudioCore::Sink::GetSinkIDs()) + sink_combo_box->addItem(QString::fromStdString(std::string{Settings::CanonicalizeEnum(id)})); } void ConfigureAudio::RetranslateUI() { From ecb811ad04e75b221e0eee31f6bbd582620bd7c7 Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 18:41:28 +0200 Subject: [PATCH 09/20] [qt] move addons row to rightmost side (#2610) This is because the rightmost row is "extended" to the rest of the table, and add-ons have long names, play time doesn't need that much space. Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2610 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/yuzu/game_list.h | 4 ++-- src/yuzu/game_list_worker.cpp | 36 ++++++++++++----------------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 94e7b2dc42..cd71fb2139 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -58,11 +58,11 @@ class GameList : public QWidget { public: enum { COLUMN_NAME, - COLUMN_COMPATIBILITY, - COLUMN_ADD_ONS, COLUMN_FILE_TYPE, COLUMN_SIZE, COLUMN_PLAY_TIME, + COLUMN_ADD_ONS, + COLUMN_COMPATIBILITY, COLUMN_COUNT, // Number of columns }; diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 538c7ab822..2914c275a8 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -204,36 +204,24 @@ QList MakeGameListEntry(const std::string& path, const PlayTime::PlayTimeManager& play_time_manager, const FileSys::PatchManager& patch) { - const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); + auto const it = FindMatchingCompatibilityEntry(compatibility_list, program_id); + // The game list uses 99 as compatibility number for untested games + QString compatibility = it != compatibility_list.end() ? it->second.first : QStringLiteral("99"); - // The game list uses this as compatibility number for untested games - QString compatibility{QStringLiteral("99")}; - if (it != compatibility_list.end()) { - compatibility = it->second.first; - } + auto const file_type = loader.GetFileType(); + auto const file_type_string = QString::fromStdString(Loader::GetFileTypeString(file_type)); - const auto file_type = loader.GetFileType(); - const auto file_type_string = QString::fromStdString(Loader::GetFileTypeString(file_type)); - - QList list{ - new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name), - file_type_string, program_id), - new GameListItemCompat(compatibility), + QString patch_versions = GetGameListCachedObject(fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] { + return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable()); + }); + return QList{ + new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name), file_type_string, program_id), new GameListItem(file_type_string), new GameListItemSize(size), new GameListItemPlayTime(play_time_manager.GetPlayTime(program_id)), + new GameListItem(patch_versions), + new GameListItemCompat(compatibility), }; - - QString patch_versions; - - patch_versions = GetGameListCachedObject( - fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] { - return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable()); - }); - - list.insert(2, new GameListItem(patch_versions)); - - return list; } } // Anonymous namespace From 50ceb9a43a6dfe417a7760042e4710ce59553d94 Mon Sep 17 00:00:00 2001 From: Caio Oliveira Date: Mon, 29 Sep 2025 18:42:04 +0200 Subject: [PATCH 10/20] [.ci] install-msvc: fix installation on MSVC (#2611) * changed from Build Tools to Community (congrats Microsoft very cool) * add spining to show it didnt stopped installing Signed-off-by: Caio Oliveira Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2611 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: Caio Oliveira Co-committed-by: Caio Oliveira --- .ci/windows/install-msvc.ps1 | 40 ++++++++++++++++++++++++++---------- docs/Deps.md | 6 +++--- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/.ci/windows/install-msvc.ps1 b/.ci/windows/install-msvc.ps1 index b88f727ed8..788b2848ad 100755 --- a/.ci/windows/install-msvc.ps1 +++ b/.ci/windows/install-msvc.ps1 @@ -10,7 +10,7 @@ if (-not ([bool](net session 2>$null))) { } $VSVer = "17" -$ExeFile = "vs_BuildTools.exe" +$ExeFile = "vs_community.exe" $Uri = "https://aka.ms/vs/$VSVer/release/$ExeFile" $Destination = "./$ExeFile" @@ -19,21 +19,39 @@ $WebClient = New-Object System.Net.WebClient $WebClient.DownloadFile($Uri, $Destination) Write-Host "Finished downloading $ExeFile" -$VSROOT = "C:/VSBuildTools/$VSVer" $Arguments = @( - "--installPath `"$VSROOT`"", # set custom installation path - "--quiet", # suppress UI - "--wait", # wait for installation to complete - "--norestart", # prevent automatic restart - "--add Microsoft.VisualStudio.Workload.VCTools", # add C++ build tools workload - "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64", # add core x86/x64 C++ tools - "--add Microsoft.VisualStudio.Component.Windows10SDK.19041" # add specific Windows SDK + "--quiet", # Suppress installer UI + "--wait", # Wait for installation to complete + "--norestart", # Prevent automatic restart + "--force", # Force installation even if components are already installed + "--add Microsoft.VisualStudio.Workload.NativeDesktop", # Desktop development with C++ + "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64", # Core C++ compiler/tools for x86/x64 + "--add Microsoft.VisualStudio.Component.Windows11SDK.26100",# Windows 11 SDK (26100) + "--add Microsoft.VisualStudio.Component.Windows10SDK.19041",# Windows 10 SDK (19041) + "--add Microsoft.VisualStudio.Component.VC.Llvm.Clang", # LLVM Clang compiler + "--add Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset", # LLVM Clang integration toolset + "--add Microsoft.VisualStudio.Component.Windows11SDK.22621",# Windows 11 SDK (22621) + "--add Microsoft.VisualStudio.Component.VC.CMake.Project", # CMake project support + "--add Microsoft.VisualStudio.ComponentGroup.VC.Tools.142.x86.x64", # VC++ 14.2 toolset + "--add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang" # LLVM Clang for native desktop ) Write-Host "Installing Visual Studio Build Tools" -$InstallProcess = Start-Process -FilePath $Destination -NoNewWindow -PassThru -Wait -ArgumentList $Arguments -$ExitCode = $InstallProcess.ExitCode +$InstallProcess = Start-Process -FilePath $Destination -NoNewWindow -PassThru -ArgumentList $Arguments +# Spinner while installing +$Spinner = "|/-\" +$i = 0 +while (-not $InstallProcess.HasExited) { + Write-Host -NoNewline ("`rInstalling... " + $Spinner[$i % $Spinner.Length]) + Start-Sleep -Milliseconds 250 + $i++ +} + +# Clear spinner line +Write-Host "`rSetup completed! " + +$ExitCode = $InstallProcess.ExitCode if ($ExitCode -ne 0) { Write-Host "Error installing Visual Studio Build Tools (Error: $ExitCode)" Exit $ExitCode diff --git a/docs/Deps.md b/docs/Deps.md index cfc6f0365b..0e7b7cff62 100644 --- a/docs/Deps.md +++ b/docs/Deps.md @@ -4,8 +4,8 @@ To build Eden, you MUST have a C++ compiler. * On Linux, this is usually [GCC](https://gcc.gnu.org/) 11+ or [Clang](https://clang.llvm.org/) v14+ - GCC 12 also requires Clang 14+ * On Windows, this is either: - - **[MSVC](https://visualstudio.microsoft.com/downloads/)**, - * *A convenience script to install the **minimal** version (Visual Build Tools) is provided in `.ci/windows/install-msvc.ps1`* + - **[MSVC](https://visualstudio.microsoft.com/downloads/)** (you should select *Community* option), + * *A convenience script to install the Visual Community Studio 2022 with necessary tools is provided in `.ci/windows/install-msvc.ps1`* - clang-cl - can be downloaded from the MSVC installer, - or **[MSYS2](https://www.msys2.org)** * On macOS, this is Apple Clang @@ -211,4 +211,4 @@ Then install the libraries: `sudo pkg install qt6 boost glslang libzip library/l ## All Done -You may now return to the **[root build guide](Build.md)**. \ No newline at end of file +You may now return to the **[root build guide](Build.md)**. From 9f423a24b82e1b3223d8d4204455e637f86271fa Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 18:42:28 +0200 Subject: [PATCH 11/20] [linux] fix aarch64 builds (again) + fix with slightly outdated qt (#2612) Fixes issues building on aarch64 linux with a slightly outdated system qt; also fixes linker selection process Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2612 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- CMakeLists.txt | 5 ++--- src/dynarmic/tests/CMakeLists.txt | 4 +++- src/yuzu/configuration/shared_widget.h | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef3c0bef6e..f5d7126f92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -895,13 +895,13 @@ if (MSVC AND CXX_CLANG) endif() if (YUZU_USE_FASTER_LD) + # fallback if everything fails (bfd) + set(LINKER bfd) # clang should always use lld find_program(LLD lld) - if (LLD) set(LINKER lld) endif() - # GNU appears to work better with mold # TODO: mold has been slow lately, see if better options exist (search for gold?) if (CXX_GCC) @@ -910,7 +910,6 @@ if (YUZU_USE_FASTER_LD) set(LINKER mold) endif() endif() - message(NOTICE "Selecting ${LINKER} as linker") add_link_options("-fuse-ld=${LINKER}") endif() diff --git a/src/dynarmic/tests/CMakeLists.txt b/src/dynarmic/tests/CMakeLists.txt index 4ace6c2afd..df90168a52 100644 --- a/src/dynarmic/tests/CMakeLists.txt +++ b/src/dynarmic/tests/CMakeLists.txt @@ -135,6 +135,8 @@ target_include_directories(dynarmic_tests PRIVATE . ../src) target_compile_options(dynarmic_tests PRIVATE ${DYNARMIC_CXX_FLAGS}) target_compile_definitions(dynarmic_tests PRIVATE FMT_USE_USER_DEFINED_LITERALS=1) -target_compile_options(dynarmic_tests PRIVATE -mavx2) +if ("x86_64" IN_LIST ARCHITECTURE) + target_compile_options(dynarmic_tests PRIVATE -mavx2) +endif() add_test(dynarmic_tests dynarmic_tests --durations yes) diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h index 9e718098a3..dd5d5b7257 100644 --- a/src/yuzu/configuration/shared_widget.h +++ b/src/yuzu/configuration/shared_widget.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "qt_common/shared_translation.h" From 33f93ad003a47419d545a6bae018d3bb1c5ee3fb Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 18:42:51 +0200 Subject: [PATCH 12/20] [macos, qt] workaround upstream rendering bug (#2616) See https://bugreports.qt.io/browse/QTBUG-138942 Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2616 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/yuzu/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yuzu/Info.plist b/src/yuzu/Info.plist index 96096c84d1..0c43c834d4 100644 --- a/src/yuzu/Info.plist +++ b/src/yuzu/Info.plist @@ -49,5 +49,7 @@ SPDX-License-Identifier: GPL-2.0-or-later NSApplication NSHighResolutionCapable True + UIDesignRequiresCompatibility + From 324ace3cd635bac4230a04c4a769bed236ed1e8d Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 18:43:13 +0200 Subject: [PATCH 13/20] [macos] associate .XCI/NSP file extensions (#2617) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2617 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/yuzu/Info.plist | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/yuzu/Info.plist b/src/yuzu/Info.plist index 0c43c834d4..773c4ee302 100644 --- a/src/yuzu/Info.plist +++ b/src/yuzu/Info.plist @@ -45,6 +45,26 @@ SPDX-License-Identifier: GPL-2.0-or-later NSHumanReadableCopyright + + LSApplicationCategoryType + public.app-category.games + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + nsp + xci + nro + + CFBundleTypeName + Switch File + CFBundleTypeRole + Viewer + LSHandlerRank + Default + + NSPrincipalClass NSApplication NSHighResolutionCapable From 3de8f96373f2ad66d74c29d161c8d9c7460a76ec Mon Sep 17 00:00:00 2001 From: lizzie Date: Fri, 29 Aug 2025 23:48:25 +0000 Subject: [PATCH 14/20] [gamemode] Make available on other platforms Signed-off-by: lizzie --- CMakeLists.txt | 2 +- externals/gamemode/gamemode_client.h | 28 +++++++++++++++- src/common/CMakeLists.txt | 8 ++--- src/common/gamemode.cpp | 50 ++++++++++++++++++++++++++++ src/common/gamemode.h | 17 ++++++++++ src/common/linux/gamemode.cpp | 40 ---------------------- src/common/linux/gamemode.h | 24 ------------- src/yuzu/main.cpp | 18 +++------- src/yuzu_cmd/yuzu.cpp | 16 ++------- 9 files changed, 105 insertions(+), 98 deletions(-) create mode 100644 src/common/gamemode.cpp create mode 100644 src/common/gamemode.h delete mode 100644 src/common/linux/gamemode.cpp delete mode 100644 src/common/linux/gamemode.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f5d7126f92..5e041e3202 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -546,7 +546,7 @@ else() find_package(Catch2 3.0.1 REQUIRED) endif() - if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR ANDROID) + if (PLATFORM_LINUX OR ANDROID) find_package(gamemode 1.7 MODULE) endif() diff --git a/externals/gamemode/gamemode_client.h b/externals/gamemode/gamemode_client.h index b9f64fe460..bfbf61c0c2 100644 --- a/externals/gamemode/gamemode_client.h +++ b/externals/gamemode/gamemode_client.h @@ -1,6 +1,9 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* -Copyright (c) 2017-2019, Feral Interactive +Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors All rights reserved. Redistribution and use in source and binary forms, with or without @@ -103,6 +106,7 @@ typedef int (*api_call_pid_return_int)(pid_t); static api_call_return_int REAL_internal_gamemode_request_start = NULL; static api_call_return_int REAL_internal_gamemode_request_end = NULL; static api_call_return_int REAL_internal_gamemode_query_status = NULL; +static api_call_return_int REAL_internal_gamemode_request_restart = NULL; static api_call_return_cstring REAL_internal_gamemode_error_string = NULL; static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL; static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL; @@ -166,6 +170,10 @@ __attribute__((always_inline)) static inline int internal_load_libgamemode(void) (void **)&REAL_internal_gamemode_query_status, sizeof(REAL_internal_gamemode_query_status), false }, + { "real_gamemode_request_restart", + (void **)&REAL_internal_gamemode_request_restart, + sizeof(REAL_internal_gamemode_request_restart), + false }, { "real_gamemode_error_string", (void **)&REAL_internal_gamemode_error_string, sizeof(REAL_internal_gamemode_error_string), @@ -319,6 +327,24 @@ __attribute__((always_inline)) static inline int gamemode_query_status(void) return REAL_internal_gamemode_query_status(); } +/* Redirect to the real libgamemode */ +__attribute__((always_inline)) static inline int gamemode_request_restart(void) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } + + if (REAL_internal_gamemode_request_restart == NULL) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "gamemode_request_restart missing (older host?)"); + return -1; + } + + return REAL_internal_gamemode_request_restart(); +} + /* Redirect to the real libgamemode */ __attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid) { diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 96ea429e5a..ab983f2def 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -68,6 +68,8 @@ add_library( fs/fs_util.h fs/path_util.cpp fs/path_util.h + gamemode.cpp + gamemode.h hash.h heap_tracker.cpp heap_tracker.h @@ -184,11 +186,7 @@ if(ANDROID) android/applets/software_keyboard.h) endif() -if(LINUX AND NOT APPLE) - target_sources(common PRIVATE linux/gamemode.cpp linux/gamemode.h) - - target_link_libraries(common PRIVATE gamemode::headers) -endif() +target_link_libraries(common PRIVATE gamemode::headers) if(ARCHITECTURE_x86_64) target_sources( diff --git a/src/common/gamemode.cpp b/src/common/gamemode.cpp new file mode 100644 index 0000000000..a3f0ba37ab --- /dev/null +++ b/src/common/gamemode.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// While technically available on al *NIX platforms, Linux is only available +// as the primary target of libgamemode.so - so warnings are suppressed +#ifdef __unix__ +#include +#endif +#include "common/gamemode.h" +#include "common/logging/log.h" +#include "common/settings.h" + +namespace Common::FeralGamemode { + +void Start() noexcept { + if (Settings::values.enable_gamemode) { +#ifdef __unix__ + if (gamemode_request_start() < 0) { +#ifdef __linux__ + LOG_WARNING(Frontend, "{}", gamemode_error_string()); +#else + LOG_INFO(Frontend, "{}", gamemode_error_string()); +#endif + } else { + LOG_INFO(Frontend, "Done"); + } +#endif + } +} + +void Stop() noexcept { + if (Settings::values.enable_gamemode) { +#ifdef __unix__ + if (gamemode_request_end() < 0) { +#ifdef __linux__ + LOG_WARNING(Frontend, "{}", gamemode_error_string()); +#else + LOG_INFO(Frontend, "{}", gamemode_error_string()); +#endif + } else { + LOG_INFO(Frontend, "Done"); + } +#endif + } +} + +} // namespace Common::Linux diff --git a/src/common/gamemode.h b/src/common/gamemode.h new file mode 100644 index 0000000000..05b1936bb5 --- /dev/null +++ b/src/common/gamemode.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Common::FeralGamemode { + +/// @brief Start the gamemode client +void Start() noexcept; + +/// @brief Stop the gmemode client +void Stop() noexcept; + +} // namespace Common::FeralGamemode diff --git a/src/common/linux/gamemode.cpp b/src/common/linux/gamemode.cpp deleted file mode 100644 index 8d3e2934a6..0000000000 --- a/src/common/linux/gamemode.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "common/linux/gamemode.h" -#include "common/logging/log.h" -#include "common/settings.h" - -namespace Common::Linux { - -void StartGamemode() { - if (Settings::values.enable_gamemode) { - if (gamemode_request_start() < 0) { - LOG_WARNING(Frontend, "Failed to start gamemode: {}", gamemode_error_string()); - } else { - LOG_INFO(Frontend, "Started gamemode"); - } - } -} - -void StopGamemode() { - if (Settings::values.enable_gamemode) { - if (gamemode_request_end() < 0) { - LOG_WARNING(Frontend, "Failed to stop gamemode: {}", gamemode_error_string()); - } else { - LOG_INFO(Frontend, "Stopped gamemode"); - } - } -} - -void SetGamemodeState(bool state) { - if (state) { - StartGamemode(); - } else { - StopGamemode(); - } -} - -} // namespace Common::Linux diff --git a/src/common/linux/gamemode.h b/src/common/linux/gamemode.h deleted file mode 100644 index b80646ae27..0000000000 --- a/src/common/linux/gamemode.h +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -namespace Common::Linux { - -/** - * Start the (Feral Interactive) Linux gamemode if it is installed and it is activated - */ -void StartGamemode(); - -/** - * Stop the (Feral Interactive) Linux gamemode if it is installed and it is activated - */ -void StopGamemode(); - -/** - * Start or stop the (Feral Interactive) Linux gamemode if it is installed and it is activated - * @param state The new state the gamemode should have - */ -void SetGamemodeState(bool state); - -} // namespace Common::Linux diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d2c12c9d40..7fdf8ed9d9 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -22,9 +22,7 @@ #include #include #endif -#ifdef __linux__ -#include "common/linux/gamemode.h" -#endif +#include "common/gamemode.h" #include @@ -2254,9 +2252,7 @@ void GMainWindow::OnEmulationStopped() { discord_rpc->Update(); -#ifdef __linux__ - Common::Linux::StopGamemode(); -#endif + Common::FeralGamemode::Stop(); // The emulation is stopped, so closing the window or not does not matter anymore disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); @@ -3095,10 +3091,7 @@ void GMainWindow::OnStartGame() { play_time_manager->Start(); discord_rpc->Update(); - -#ifdef __linux__ - Common::Linux::StartGamemode(); -#endif + Common::FeralGamemode::StartGamemode(); } void GMainWindow::OnRestartGame() { @@ -3119,10 +3112,7 @@ void GMainWindow::OnPauseGame() { play_time_manager->Stop(); UpdateMenuState(); AllowOSSleep(); - -#ifdef __linux__ - Common::Linux::StopGamemode(); -#endif + Common::FeralGamemode::Stop(); } void GMainWindow::OnPauseContinueGame() { diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 4a99f34861..f9f83858e4 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -62,10 +62,7 @@ __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; } #endif - -#ifdef __linux__ -#include "common/linux/gamemode.h" -#endif +#include "common/gamemode.h" static void PrintHelp(const char* argv0) { std::cout << "Usage: " << argv0 @@ -432,10 +429,7 @@ int main(int argc, char** argv) { // Just exit right away. exit(0); }); - -#ifdef __linux__ - Common::Linux::StartGamemode(); -#endif + Common::FeralGamemode::StartGamemode(); void(system.Run()); if (system.DebuggerEnabled()) { @@ -447,11 +441,7 @@ int main(int argc, char** argv) { system.DetachDebugger(); void(system.Pause()); system.ShutdownMainProcess(); - -#ifdef __linux__ - Common::Linux::StopGamemode(); -#endif - + Common::FeralGamemode::Stop(); detached_tasks.WaitForAllTasks(); return 0; } From 49cf701fe928a17cb56d936df3720675f260a4e0 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 30 Aug 2025 00:04:36 +0000 Subject: [PATCH 15/20] [gamemode] make option available on all nixes Signed-off-by: lizzie --- src/yuzu/main.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 7fdf8ed9d9..d33b5008e3 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -415,10 +415,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) #ifdef __unix__ SetupSigInterrupts(); #endif - -#ifdef __linux__ SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); -#endif UISettings::RestoreWindowState(config); @@ -3407,9 +3404,7 @@ void GMainWindow::OnConfigure() { const auto old_theme = UISettings::values.theme; const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); const auto old_language_index = Settings::values.language_index.GetValue(); -#ifdef __linux__ const bool old_gamemode = Settings::values.enable_gamemode.GetValue(); -#endif Settings::SetConfiguringGlobal(true); ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), @@ -3469,11 +3464,9 @@ void GMainWindow::OnConfigure() { if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); } -#ifdef __linux__ if (Settings::values.enable_gamemode.GetValue() != old_gamemode) { SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); } -#endif if (!multiplayer_state->IsHostingPublicRoom()) { multiplayer_state->UpdateCredentials(); @@ -4755,13 +4748,15 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { discord_rpc->Update(); } -#ifdef __linux__ void GMainWindow::SetGamemodeEnabled(bool state) { if (emulation_running) { - Common::Linux::SetGamemodeState(state); + if (state) { + Common::FeralGamemode::Start(); + } else { + Common::FeralGamemode::Stop(); + } } } -#endif void GMainWindow::changeEvent(QEvent* event) { #ifdef __unix__ @@ -4920,7 +4915,9 @@ int main(int argc, char* argv[]) { // the user folder in the Qt Frontend, we need to cd into that working directory const auto bin_path = Common::FS::GetBundleDirectory() / ".."; chdir(Common::FS::PathToUTF8String(bin_path).c_str()); -#elif defined(__unix__) && !defined(__ANDROID__) +#endif + +#ifdef __unix__ // Set the DISPLAY variable in order to open web browsers // TODO (lat9nq): Find a better solution for AppImages to start external applications if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) { From c49501ec7eb3acc739337beed51bd2e7f09ac7fa Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 30 Aug 2025 08:43:59 +0000 Subject: [PATCH 16/20] [gamemode] extra win/lin/macos fixes Signed-off-by: lizzie --- externals/CMakeLists.txt | 2 +- src/yuzu/main.cpp | 2 +- src/yuzu_cmd/yuzu.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 754ba61a0b..4c11104de2 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -140,7 +140,7 @@ if (ANDROID AND ARCHITECTURE_arm64) AddJsonPackage(libadrenotools) endif() -if (UNIX AND NOT APPLE AND NOT TARGET gamemode::headers) +if (NOT TARGET gamemode::headers) add_library(gamemode INTERFACE) target_include_directories(gamemode INTERFACE gamemode) add_library(gamemode::headers ALIAS gamemode) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d33b5008e3..4ab578067d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3088,7 +3088,7 @@ void GMainWindow::OnStartGame() { play_time_manager->Start(); discord_rpc->Update(); - Common::FeralGamemode::StartGamemode(); + Common::FeralGamemode::Start(); } void GMainWindow::OnRestartGame() { diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index f9f83858e4..8e46f36aec 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -429,7 +429,7 @@ int main(int argc, char** argv) { // Just exit right away. exit(0); }); - Common::FeralGamemode::StartGamemode(); + Common::FeralGamemode::Start(); void(system.Run()); if (system.DebuggerEnabled()) { From 125a7dacb7673017c14577ca23d9ce0c52c881c1 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 30 Aug 2025 13:40:57 +0000 Subject: [PATCH 17/20] [gamemode] windows fix Signed-off-by: lizzie --- src/yuzu/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.h b/src/yuzu/main.h index e3922759b0..c6980968c8 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -315,8 +315,8 @@ private: void SetupSigInterrupts(); static void HandleSigInterrupt(int); void OnSigInterruptNotifierActivated(); - void SetGamemodeEnabled(bool state); #endif + void SetGamemodeEnabled(bool state); Service::AM::FrontendAppletParameters ApplicationAppletParameters(); Service::AM::FrontendAppletParameters LibraryAppletParameters(u64 program_id, From 178d20b780d6c55ab4d3f14cc8717cfa52b9a0c7 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 13 Sep 2025 20:48:40 +0000 Subject: [PATCH 18/20] [gamemode] default disable on msvc, move to UI category Signed-off-by: lizzie --- src/common/settings.h | 10 ++++++++-- src/common/settings_common.h | 1 - src/qt_common/shared_translation.cpp | 5 ++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/common/settings.h b/src/common/settings.h index 891bde608c..3b301ccda4 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -616,8 +616,14 @@ struct Values { true, true}; - // Linux - SwitchableSetting enable_gamemode{linkage, true, "enable_gamemode", Category::Linux}; + // Linux/MinGW may support (requires libdl support) + SwitchableSetting enable_gamemode{linkage, +#ifndef _MSC_VER + true, +#else + false, +#endif + "enable_gamemode", Category::UiGeneral}; // Controls InputSetting> players; diff --git a/src/common/settings_common.h b/src/common/settings_common.h index af16ec692b..7902cbf945 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h @@ -44,7 +44,6 @@ enum class Category : u32 { Multiplayer, Services, Paths, - Linux, LibraryApplet, MaxEnum, }; diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index 98a55e1fcf..230321e722 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -445,7 +445,10 @@ std::unique_ptr InitializeTranslations(QObject* parent) tr("Whether or not to check for updates upon startup.")); // Linux - INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QString()); + INSERT(UISettings, + enable_gamemode, + tr("Enable Gamemode"), + QString()); // Ui Debugging From 67d537f7b4687b23b4148222c239d65c1aeb4863 Mon Sep 17 00:00:00 2001 From: crueter Date: Fri, 26 Sep 2025 19:30:17 -0400 Subject: [PATCH 19/20] [cmake] vendor gamemode Signed-off-by: crueter --- CMakeLists.txt | 6 +- externals/CMakeLists.txt | 6 +- externals/cpmfile.json | 7 + externals/gamemode/gamemode_client.h | 402 --------------------------- 4 files changed, 12 insertions(+), 409 deletions(-) delete mode 100644 externals/gamemode/gamemode_client.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e041e3202..024badc275 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -320,7 +320,6 @@ endif() if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX)) set(HAS_NCE 1) add_compile_definitions(HAS_NCE=1) - find_package(oaknut 2.0.1) endif() if (YUZU_ROOM) @@ -546,10 +545,6 @@ else() find_package(Catch2 3.0.1 REQUIRED) endif() - if (PLATFORM_LINUX OR ANDROID) - find_package(gamemode 1.7 MODULE) - endif() - if (ENABLE_OPENSSL) find_package(OpenSSL 1.1.1 REQUIRED) endif() @@ -673,6 +668,7 @@ add_subdirectory(externals) # pass targets from externals find_package(libusb) find_package(VulkanMemoryAllocator) +find_package(gamemode) if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) find_package(xbyak) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 4c11104de2..2d0d5a7401 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -140,9 +140,11 @@ if (ANDROID AND ARCHITECTURE_arm64) AddJsonPackage(libadrenotools) endif() -if (NOT TARGET gamemode::headers) +AddJsonPackage(gamemode) + +if (gamemode_ADDED) add_library(gamemode INTERFACE) - target_include_directories(gamemode INTERFACE gamemode) + target_include_directories(gamemode INTERFACE ${gamemode_SOURCE_DIR}) add_library(gamemode::headers ALIAS gamemode) endif() diff --git a/externals/cpmfile.json b/externals/cpmfile.json index dcafc8f97d..6758957550 100644 --- a/externals/cpmfile.json +++ b/externals/cpmfile.json @@ -70,5 +70,12 @@ "sha": "73f3cbb237", "hash": "c08c03063938339d61392b687562909c1a92615b6ef39ec8df19ea472aa6b6478e70d7d5e33d4a27b5d23f7806daf57fe1bacb8124c8a945c918c7663a9e8532", "find_args": "CONFIG" + }, + "gamemode": { + "repo": "FeralInteractive/gamemode", + "sha": "ce6fe122f3", + "hash": "ff62f3c8638528e7afb7336f7c8f9940453d2871446c36f639e3114cc8b5c7f9a817ed209c45e0e061793fc3ee9ba35a4b05140d39f5175c2aacfa0703fddfc4", + "version": "1.7", + "find_args": "MODULE" } } diff --git a/externals/gamemode/gamemode_client.h b/externals/gamemode/gamemode_client.h deleted file mode 100644 index bfbf61c0c2..0000000000 --- a/externals/gamemode/gamemode_client.h +++ /dev/null @@ -1,402 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -/* - -Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Feral Interactive nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - */ -#ifndef CLIENT_GAMEMODE_H -#define CLIENT_GAMEMODE_H -/* - * GameMode supports the following client functions - * Requests are refcounted in the daemon - * - * int gamemode_request_start() - Request gamemode starts - * 0 if the request was sent successfully - * -1 if the request failed - * - * int gamemode_request_end() - Request gamemode ends - * 0 if the request was sent successfully - * -1 if the request failed - * - * GAMEMODE_AUTO can be defined to make the above two functions apply during static init and - * destruction, as appropriate. In this configuration, errors will be printed to stderr - * - * int gamemode_query_status() - Query the current status of gamemode - * 0 if gamemode is inactive - * 1 if gamemode is active - * 2 if gamemode is active and this client is registered - * -1 if the query failed - * - * int gamemode_request_start_for(pid_t pid) - Request gamemode starts for another process - * 0 if the request was sent successfully - * -1 if the request failed - * -2 if the request was rejected - * - * int gamemode_request_end_for(pid_t pid) - Request gamemode ends for another process - * 0 if the request was sent successfully - * -1 if the request failed - * -2 if the request was rejected - * - * int gamemode_query_status_for(pid_t pid) - Query status of gamemode for another process - * 0 if gamemode is inactive - * 1 if gamemode is active - * 2 if gamemode is active and this client is registered - * -1 if the query failed - * - * const char* gamemode_error_string() - Get an error string - * returns a string describing any of the above errors - * - * Note: All the above requests can be blocking - dbus requests can and will block while the daemon - * handles the request. It is not recommended to make these calls in performance critical code - */ - -#include -#include - -#include -#include - -#include - -#include - -static char internal_gamemode_client_error_string[512] = { 0 }; - -/** - * Load libgamemode dynamically to dislodge us from most dependencies. - * This allows clients to link and/or use this regardless of runtime. - * See SDL2 for an example of the reasoning behind this in terms of - * dynamic versioning as well. - */ -static volatile int internal_libgamemode_loaded = 1; - -/* Typedefs for the functions to load */ -typedef int (*api_call_return_int)(void); -typedef const char *(*api_call_return_cstring)(void); -typedef int (*api_call_pid_return_int)(pid_t); - -/* Storage for functors */ -static api_call_return_int REAL_internal_gamemode_request_start = NULL; -static api_call_return_int REAL_internal_gamemode_request_end = NULL; -static api_call_return_int REAL_internal_gamemode_query_status = NULL; -static api_call_return_int REAL_internal_gamemode_request_restart = NULL; -static api_call_return_cstring REAL_internal_gamemode_error_string = NULL; -static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL; -static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL; -static api_call_pid_return_int REAL_internal_gamemode_query_status_for = NULL; - -/** - * Internal helper to perform the symbol binding safely. - * - * Returns 0 on success and -1 on failure - */ -__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol( - void *handle, const char *name, void **out_func, size_t func_size, bool required) -{ - void *symbol_lookup = NULL; - char *dl_error = NULL; - - /* Safely look up the symbol */ - symbol_lookup = dlsym(handle, name); - dl_error = dlerror(); - if (required && (dl_error || !symbol_lookup)) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "dlsym failed - %s", - dl_error); - return -1; - } - - /* Have the symbol correctly, copy it to make it usable */ - memcpy(out_func, &symbol_lookup, func_size); - return 0; -} - -/** - * Loads libgamemode and needed functions - * - * Returns 0 on success and -1 on failure - */ -__attribute__((always_inline)) static inline int internal_load_libgamemode(void) -{ - /* We start at 1, 0 is a success and -1 is a fail */ - if (internal_libgamemode_loaded != 1) { - return internal_libgamemode_loaded; - } - - /* Anonymous struct type to define our bindings */ - struct binding { - const char *name; - void **functor; - size_t func_size; - bool required; - } bindings[] = { - { "real_gamemode_request_start", - (void **)&REAL_internal_gamemode_request_start, - sizeof(REAL_internal_gamemode_request_start), - true }, - { "real_gamemode_request_end", - (void **)&REAL_internal_gamemode_request_end, - sizeof(REAL_internal_gamemode_request_end), - true }, - { "real_gamemode_query_status", - (void **)&REAL_internal_gamemode_query_status, - sizeof(REAL_internal_gamemode_query_status), - false }, - { "real_gamemode_request_restart", - (void **)&REAL_internal_gamemode_request_restart, - sizeof(REAL_internal_gamemode_request_restart), - false }, - { "real_gamemode_error_string", - (void **)&REAL_internal_gamemode_error_string, - sizeof(REAL_internal_gamemode_error_string), - true }, - { "real_gamemode_request_start_for", - (void **)&REAL_internal_gamemode_request_start_for, - sizeof(REAL_internal_gamemode_request_start_for), - false }, - { "real_gamemode_request_end_for", - (void **)&REAL_internal_gamemode_request_end_for, - sizeof(REAL_internal_gamemode_request_end_for), - false }, - { "real_gamemode_query_status_for", - (void **)&REAL_internal_gamemode_query_status_for, - sizeof(REAL_internal_gamemode_query_status_for), - false }, - }; - - void *libgamemode = NULL; - - /* Try and load libgamemode */ - libgamemode = dlopen("libgamemode.so.0", RTLD_NOW); - if (!libgamemode) { - /* Attempt to load unversioned library for compatibility with older - * versions (as of writing, there are no ABI changes between the two - - * this may need to change if ever ABI-breaking changes are made) */ - libgamemode = dlopen("libgamemode.so", RTLD_NOW); - if (!libgamemode) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "dlopen failed - %s", - dlerror()); - internal_libgamemode_loaded = -1; - return -1; - } - } - - /* Attempt to bind all symbols */ - for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) { - struct binding *binder = &bindings[i]; - - if (internal_bind_libgamemode_symbol(libgamemode, - binder->name, - binder->functor, - binder->func_size, - binder->required)) { - internal_libgamemode_loaded = -1; - return -1; - }; - } - - /* Success */ - internal_libgamemode_loaded = 0; - return 0; -} - -/** - * Redirect to the real libgamemode - */ -__attribute__((always_inline)) static inline const char *gamemode_error_string(void) -{ - /* If we fail to load the system gamemode, or we have an error string already, return our error - * string instead of diverting to the system version */ - if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') { - return internal_gamemode_client_error_string; - } - - /* Assert for static analyser that the function is not NULL */ - assert(REAL_internal_gamemode_error_string != NULL); - - return REAL_internal_gamemode_error_string(); -} - -/** - * Redirect to the real libgamemode - * Allow automatically requesting game mode - * Also prints errors as they happen. - */ -#ifdef GAMEMODE_AUTO -__attribute__((constructor)) -#else -__attribute__((always_inline)) static inline -#endif -int gamemode_request_start(void) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { -#ifdef GAMEMODE_AUTO - fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); -#endif - return -1; - } - - /* Assert for static analyser that the function is not NULL */ - assert(REAL_internal_gamemode_request_start != NULL); - - if (REAL_internal_gamemode_request_start() < 0) { -#ifdef GAMEMODE_AUTO - fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); -#endif - return -1; - } - - return 0; -} - -/* Redirect to the real libgamemode */ -#ifdef GAMEMODE_AUTO -__attribute__((destructor)) -#else -__attribute__((always_inline)) static inline -#endif -int gamemode_request_end(void) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { -#ifdef GAMEMODE_AUTO - fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); -#endif - return -1; - } - - /* Assert for static analyser that the function is not NULL */ - assert(REAL_internal_gamemode_request_end != NULL); - - if (REAL_internal_gamemode_request_end() < 0) { -#ifdef GAMEMODE_AUTO - fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); -#endif - return -1; - } - - return 0; -} - -/* Redirect to the real libgamemode */ -__attribute__((always_inline)) static inline int gamemode_query_status(void) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } - - if (REAL_internal_gamemode_query_status == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_query_status missing (older host?)"); - return -1; - } - - return REAL_internal_gamemode_query_status(); -} - -/* Redirect to the real libgamemode */ -__attribute__((always_inline)) static inline int gamemode_request_restart(void) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } - - if (REAL_internal_gamemode_request_restart == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_request_restart missing (older host?)"); - return -1; - } - - return REAL_internal_gamemode_request_restart(); -} - -/* Redirect to the real libgamemode */ -__attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } - - if (REAL_internal_gamemode_request_start_for == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_request_start_for missing (older host?)"); - return -1; - } - - return REAL_internal_gamemode_request_start_for(pid); -} - -/* Redirect to the real libgamemode */ -__attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } - - if (REAL_internal_gamemode_request_end_for == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_request_end_for missing (older host?)"); - return -1; - } - - return REAL_internal_gamemode_request_end_for(pid); -} - -/* Redirect to the real libgamemode */ -__attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid) -{ - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } - - if (REAL_internal_gamemode_query_status_for == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_query_status_for missing (older host?)"); - return -1; - } - - return REAL_internal_gamemode_query_status_for(pid); -} - -#endif // CLIENT_GAMEMODE_H From eac3addacc699c6ede63e86a2c590d8a0e039af2 Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 29 Sep 2025 07:46:59 +0000 Subject: [PATCH 20/20] fix licsense Signed-off-by: lizzie --- src/common/settings_common.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 7902cbf945..0107895c8c 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later