diff options
| -rw-r--r-- | externals/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | externals/gamemode/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | externals/gamemode/include/gamemode_client.h | 379 | ||||
| -rw-r--r-- | src/common/CMakeLists.txt | 9 | ||||
| -rw-r--r-- | src/common/linux/gamemode.cpp | 39 | ||||
| -rw-r--r-- | src/common/linux/gamemode.h | 24 | ||||
| -rw-r--r-- | src/common/settings.cpp | 2 | ||||
| -rw-r--r-- | src/common/settings.h | 3 | ||||
| -rw-r--r-- | src/common/settings_common.h | 1 | ||||
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_general.cpp | 43 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_general.ui | 27 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_system.ui | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/shared_translation.cpp | 3 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 30 | ||||
| -rw-r--r-- | src/yuzu/main.h | 1 | ||||
| -rw-r--r-- | src/yuzu_cmd/yuzu.cpp | 12 | 
17 files changed, 583 insertions, 9 deletions
| diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index fc922c31b..070151bec 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -189,6 +189,10 @@ if (ANDROID)     endif()  endif() +if (UNIX) +    add_subdirectory(gamemode) +endif() +  # Breakpad  # https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt  if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client) diff --git a/externals/gamemode/CMakeLists.txt b/externals/gamemode/CMakeLists.txt new file mode 100644 index 000000000..87095642e --- /dev/null +++ b/externals/gamemode/CMakeLists.txt @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +project(gamemode LANGUAGES CXX C) + +add_library(gamemode include/gamemode_client.h) + +target_link_libraries(gamemode PRIVATE common) + +target_include_directories(gamemode PUBLIC include) +set_target_properties(gamemode PROPERTIES LINKER_LANGUAGE C) diff --git a/externals/gamemode/include/gamemode_client.h b/externals/gamemode/include/gamemode_client.h new file mode 100644 index 000000000..184812334 --- /dev/null +++ b/externals/gamemode/include/gamemode_client.h @@ -0,0 +1,379 @@ +// SPDX-FileCopyrightText: Copyright 2017-2019 Feral Interactive +// SPDX-License-Identifier: BSD-3-Clause + +/* + +Copyright (c) 2017-2019, Feral Interactive +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 <stdbool.h> +#include <stdio.h> + +#include <dlfcn.h> +#include <string.h> + +#include <assert.h> + +#include <sys/types.h> + +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_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_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_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 diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index e216eb3de..57cbb9d07 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -174,6 +174,15 @@ if(ANDROID)      )  endif() +if (UNIX) +  target_sources(common PRIVATE +    linux/gamemode.cpp +    linux/gamemode.h +  ) + +  target_link_libraries(common PRIVATE gamemode) +endif() +  if(ARCHITECTURE_x86_64)      target_sources(common          PRIVATE diff --git a/src/common/linux/gamemode.cpp b/src/common/linux/gamemode.cpp new file mode 100644 index 000000000..8876d8dc4 --- /dev/null +++ b/src/common/linux/gamemode.cpp @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <gamemode_client.h> + +#include "common/linux/gamemode.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 new file mode 100644 index 000000000..b80646ae2 --- /dev/null +++ b/src/common/linux/gamemode.h @@ -0,0 +1,24 @@ +// 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/common/settings.cpp b/src/common/settings.cpp index a10131eb2..3e829253f 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -219,6 +219,8 @@ const char* TranslateCategory(Category category) {          return "Services";      case Category::Paths:          return "Paths"; +    case Category::Linux: +        return "Linux";      case Category::MaxEnum:          break;      } diff --git a/src/common/settings.h b/src/common/settings.h index b929fd957..6425cd98f 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -429,6 +429,9 @@ struct Values {                                                     true,                                                     true}; +    // Linux +    SwitchableSetting<bool> enable_gamemode{linkage, true, "enable_gamemode", Category::Linux}; +      // Controls      InputSetting<std::array<PlayerInput, 10>> players; diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 7943223eb..344c04439 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h @@ -41,6 +41,7 @@ enum class Category : u32 {      Multiplayer,      Services,      Paths, +    Linux,      MaxEnum,  }; diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 90278052a..f3ad2214b 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -386,7 +386,7 @@ if (NOT WIN32)      target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})  endif()  if (UNIX AND NOT APPLE) -    target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus) +    target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus gamemode)  endif()  target_compile_definitions(yuzu PRIVATE diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index c727fadd1..701b895e7 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -36,12 +36,29 @@ ConfigureGeneral::~ConfigureGeneral() = default;  void ConfigureGeneral::SetConfiguration() {}  void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { -    QLayout& layout = *ui->general_widget->layout(); +    QLayout& general_layout = *ui->general_widget->layout(); +    QLayout& linux_layout = *ui->linux_widget->layout(); -    std::map<u32, QWidget*> hold{}; +    std::map<u32, QWidget*> general_hold{}; +    std::map<u32, QWidget*> linux_hold{}; -    for (const auto setting : -         UISettings::values.linkage.by_category[Settings::Category::UiGeneral]) { +    std::vector<Settings::BasicSetting*> settings; + +    auto push = [&settings](auto& list) { +        for (auto setting : list) { +            settings.push_back(setting); +        } +    }; + +    push(UISettings::values.linkage.by_category[Settings::Category::UiGeneral]); +    push(Settings::values.linkage.by_category[Settings::Category::Linux]); + +    // Only show Linux group on Unix +#ifndef __unix__ +    ui->LinuxGroupBox->setVisible(false); +#endif + +    for (const auto setting : settings) {          auto* widget = builder.BuildWidget(setting, apply_funcs);          if (widget == nullptr) { @@ -52,11 +69,23 @@ void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) {              continue;          } -        hold.emplace(setting->Id(), widget); +        switch (setting->GetCategory()) { +        case Settings::Category::UiGeneral: +            general_hold.emplace(setting->Id(), widget); +            break; +        case Settings::Category::Linux: +            linux_hold.emplace(setting->Id(), widget); +            break; +        default: +            widget->deleteLater(); +        }      } -    for (const auto& [id, widget] : hold) { -        layout.addWidget(widget); +    for (const auto& [id, widget] : general_hold) { +        general_layout.addWidget(widget); +    } +    for (const auto& [id, widget] : linux_hold) { +        linux_layout.addWidget(widget);      }  } diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index a10e7d3a5..ef20891a3 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -47,6 +47,33 @@        </widget>       </item>       <item> +      <widget class="QGroupBox" name="LinuxGroupBox"> +       <property name="title"> +        <string>Linux</string> +       </property> +       <layout class="QVBoxLayout" name="LinuxVerticalLayout_1"> +        <item> +         <widget class="QWidget" name="linux_widget" native="true"> +          <layout class="QVBoxLayout" name="LinuxVerticalLayout_2"> +           <property name="leftMargin"> +            <number>0</number> +           </property> +           <property name="topMargin"> +            <number>0</number> +           </property> +           <property name="rightMargin"> +            <number>0</number> +           </property> +           <property name="bottomMargin"> +            <number>0</number> +           </property> +          </layout> +         </widget> +        </item> +       </layout> +      </widget> +     </item> +     <item>        <spacer name="verticalSpacer">         <property name="orientation">          <enum>Qt::Vertical</enum> diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index 2a735836e..04b771129 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -57,7 +57,7 @@        </widget>       </item>       <item> -      <widget class="QGroupBox" name="groupBox"> +      <widget class="QGroupBox" name="coreGroup">         <property name="title">          <string>Core</string>         </property> diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index a7b5def32..ee0ca4aa7 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -176,6 +176,9 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {      INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),             QStringLiteral()); +    // Linux +    INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QStringLiteral()); +      // Ui Debugging      // Ui Multiplayer diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 31aabb78a..10c788290 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -17,6 +17,7 @@  #ifdef __unix__  #include <csignal>  #include <sys/socket.h> +#include "common/linux/gamemode.h"  #endif  #include <boost/container/flat_set.hpp> @@ -319,6 +320,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk        provider{std::make_unique<FileSys::ManualContentProvider>()} {  #ifdef __unix__      SetupSigInterrupts(); +    SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());  #endif      system->Initialize(); @@ -2120,6 +2122,10 @@ void GMainWindow::OnEmulationStopped() {      discord_rpc->Update(); +#ifdef __unix__ +    Common::Linux::StopGamemode(); +#endif +      // The emulation is stopped, so closing the window or not does not matter anymore      disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); @@ -3502,6 +3508,10 @@ void GMainWindow::OnStartGame() {      play_time_manager->Start();      discord_rpc->Update(); + +#ifdef __unix__ +    Common::Linux::StartGamemode(); +#endif  }  void GMainWindow::OnRestartGame() { @@ -3522,6 +3532,10 @@ void GMainWindow::OnPauseGame() {      play_time_manager->Stop();      UpdateMenuState();      AllowOSSleep(); + +#ifdef __unix__ +    Common::Linux::StopGamemode(); +#endif  }  void GMainWindow::OnPauseContinueGame() { @@ -3803,6 +3817,9 @@ 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 __unix__ +    const bool old_gamemode = Settings::values.enable_gamemode.GetValue(); +#endif      Settings::SetConfiguringGlobal(true);      ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), @@ -3864,6 +3881,11 @@ void GMainWindow::OnConfigure() {      if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) {          SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());      } +#ifdef __unix__ +    if (Settings::values.enable_gamemode.GetValue() != old_gamemode) { +        SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); +    } +#endif      if (!multiplayer_state->IsHostingPublicRoom()) {          multiplayer_state->UpdateCredentials(); @@ -5172,6 +5194,14 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {      discord_rpc->Update();  } +#ifdef __unix__ +void GMainWindow::SetGamemodeEnabled(bool state) { +    if (emulation_running) { +        Common::Linux::SetGamemodeState(state); +    } +} +#endif +  void GMainWindow::changeEvent(QEvent* event) {  #ifdef __unix__      // PaletteChange event appears to only reach so far into the GUI, explicitly asking to diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 733d6291e..530e445f9 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -340,6 +340,7 @@ private:      void SetupSigInterrupts();      static void HandleSigInterrupt(int);      void OnSigInterruptNotifierActivated(); +    void SetGamemodeEnabled(bool state);  #endif  private slots: diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 0416d5951..a81635fa4 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -63,6 +63,10 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;  }  #endif +#ifdef __unix__ +#include "common/linux/gamemode.h" +#endif +  static void PrintHelp(const char* argv0) {      std::cout << "Usage: " << argv0                << " [options] <filename>\n" @@ -425,6 +429,10 @@ int main(int argc, char** argv) {          exit(0);      }); +#ifdef __unix__ +    Common::Linux::StartGamemode(); +#endif +      void(system.Run());      if (system.DebuggerEnabled()) {          system.InitializeDebugger(); @@ -436,6 +444,10 @@ int main(int argc, char** argv) {      void(system.Pause());      system.ShutdownMainProcess(); +#ifdef __unix__ +    Common::Linux::StopGamemode(); +#endif +      detached_tasks.WaitForAllTasks();      return 0;  } | 
