summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar liamwhite2023-11-29 12:33:09 -0500
committerGravatar GitHub2023-11-29 12:33:09 -0500
commit337e37f91dcc7d5b6a0a5da3f3196aa0f8df4143 (patch)
tree70d10b1f7919e6ed6709acab3259c69b038add6c
parentMerge pull request #11902 from ameerj/ssbo-align (diff)
parentcmake: move gamemode target include into its file (diff)
downloadyuzu-337e37f91dcc7d5b6a0a5da3f3196aa0f8df4143.tar.gz
yuzu-337e37f91dcc7d5b6a0a5da3f3196aa0f8df4143.tar.xz
yuzu-337e37f91dcc7d5b6a0a5da3f3196aa0f8df4143.zip
Merge pull request #11946 from flodavid/gamemode
Enable (Feral Interactive) Gamemode on Linux
Diffstat (limited to '')
-rw-r--r--externals/CMakeLists.txt4
-rw-r--r--externals/gamemode/CMakeLists.txt11
-rw-r--r--externals/gamemode/include/gamemode_client.h379
-rw-r--r--src/common/CMakeLists.txt9
-rw-r--r--src/common/linux/gamemode.cpp39
-rw-r--r--src/common/linux/gamemode.h24
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings.h3
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/configuration/configure_general.cpp43
-rw-r--r--src/yuzu/configuration/configure_general.ui27
-rw-r--r--src/yuzu/configuration/configure_system.ui2
-rw-r--r--src/yuzu/configuration/shared_translation.cpp3
-rw-r--r--src/yuzu/main.cpp30
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu_cmd/yuzu.cpp12
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)
189 endif() 189 endif()
190endif() 190endif()
191 191
192if (UNIX)
193 add_subdirectory(gamemode)
194endif()
195
192# Breakpad 196# Breakpad
193# https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt 197# https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt
194if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client) 198if (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 @@
1# SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-3.0-or-later
3
4project(gamemode LANGUAGES CXX C)
5
6add_library(gamemode include/gamemode_client.h)
7
8target_link_libraries(gamemode PRIVATE common)
9
10target_include_directories(gamemode PUBLIC include)
11set_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 @@
1// SPDX-FileCopyrightText: Copyright 2017-2019 Feral Interactive
2// SPDX-License-Identifier: BSD-3-Clause
3
4/*
5
6Copyright (c) 2017-2019, Feral Interactive
7All rights reserved.
8
9Redistribution and use in source and binary forms, with or without
10modification, are permitted provided that the following conditions are met:
11
12 * Redistributions of source code must retain the above copyright notice,
13 this list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright
15 notice, this list of conditions and the following disclaimer in the
16 documentation and/or other materials provided with the distribution.
17 * Neither the name of Feral Interactive nor the names of its contributors
18 may be used to endorse or promote products derived from this software
19 without specific prior written permission.
20
21THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31POSSIBILITY OF SUCH DAMAGE.
32
33 */
34#ifndef CLIENT_GAMEMODE_H
35#define CLIENT_GAMEMODE_H
36/*
37 * GameMode supports the following client functions
38 * Requests are refcounted in the daemon
39 *
40 * int gamemode_request_start() - Request gamemode starts
41 * 0 if the request was sent successfully
42 * -1 if the request failed
43 *
44 * int gamemode_request_end() - Request gamemode ends
45 * 0 if the request was sent successfully
46 * -1 if the request failed
47 *
48 * GAMEMODE_AUTO can be defined to make the above two functions apply during static init and
49 * destruction, as appropriate. In this configuration, errors will be printed to stderr
50 *
51 * int gamemode_query_status() - Query the current status of gamemode
52 * 0 if gamemode is inactive
53 * 1 if gamemode is active
54 * 2 if gamemode is active and this client is registered
55 * -1 if the query failed
56 *
57 * int gamemode_request_start_for(pid_t pid) - Request gamemode starts for another process
58 * 0 if the request was sent successfully
59 * -1 if the request failed
60 * -2 if the request was rejected
61 *
62 * int gamemode_request_end_for(pid_t pid) - Request gamemode ends for another process
63 * 0 if the request was sent successfully
64 * -1 if the request failed
65 * -2 if the request was rejected
66 *
67 * int gamemode_query_status_for(pid_t pid) - Query status of gamemode for another process
68 * 0 if gamemode is inactive
69 * 1 if gamemode is active
70 * 2 if gamemode is active and this client is registered
71 * -1 if the query failed
72 *
73 * const char* gamemode_error_string() - Get an error string
74 * returns a string describing any of the above errors
75 *
76 * Note: All the above requests can be blocking - dbus requests can and will block while the daemon
77 * handles the request. It is not recommended to make these calls in performance critical code
78 */
79
80#include <stdbool.h>
81#include <stdio.h>
82
83#include <dlfcn.h>
84#include <string.h>
85
86#include <assert.h>
87
88#include <sys/types.h>
89
90static char internal_gamemode_client_error_string[512] = { 0 };
91
92/**
93 * Load libgamemode dynamically to dislodge us from most dependencies.
94 * This allows clients to link and/or use this regardless of runtime.
95 * See SDL2 for an example of the reasoning behind this in terms of
96 * dynamic versioning as well.
97 */
98static volatile int internal_libgamemode_loaded = 1;
99
100/* Typedefs for the functions to load */
101typedef int (*api_call_return_int)(void);
102typedef const char *(*api_call_return_cstring)(void);
103typedef int (*api_call_pid_return_int)(pid_t);
104
105/* Storage for functors */
106static api_call_return_int REAL_internal_gamemode_request_start = NULL;
107static api_call_return_int REAL_internal_gamemode_request_end = NULL;
108static api_call_return_int REAL_internal_gamemode_query_status = NULL;
109static api_call_return_cstring REAL_internal_gamemode_error_string = NULL;
110static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL;
111static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL;
112static api_call_pid_return_int REAL_internal_gamemode_query_status_for = NULL;
113
114/**
115 * Internal helper to perform the symbol binding safely.
116 *
117 * Returns 0 on success and -1 on failure
118 */
119__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol(
120 void *handle, const char *name, void **out_func, size_t func_size, bool required)
121{
122 void *symbol_lookup = NULL;
123 char *dl_error = NULL;
124
125 /* Safely look up the symbol */
126 symbol_lookup = dlsym(handle, name);
127 dl_error = dlerror();
128 if (required && (dl_error || !symbol_lookup)) {
129 snprintf(internal_gamemode_client_error_string,
130 sizeof(internal_gamemode_client_error_string),
131 "dlsym failed - %s",
132 dl_error);
133 return -1;
134 }
135
136 /* Have the symbol correctly, copy it to make it usable */
137 memcpy(out_func, &symbol_lookup, func_size);
138 return 0;
139}
140
141/**
142 * Loads libgamemode and needed functions
143 *
144 * Returns 0 on success and -1 on failure
145 */
146__attribute__((always_inline)) static inline int internal_load_libgamemode(void)
147{
148 /* We start at 1, 0 is a success and -1 is a fail */
149 if (internal_libgamemode_loaded != 1) {
150 return internal_libgamemode_loaded;
151 }
152
153 /* Anonymous struct type to define our bindings */
154 struct binding {
155 const char *name;
156 void **functor;
157 size_t func_size;
158 bool required;
159 } bindings[] = {
160 { "real_gamemode_request_start",
161 (void **)&REAL_internal_gamemode_request_start,
162 sizeof(REAL_internal_gamemode_request_start),
163 true },
164 { "real_gamemode_request_end",
165 (void **)&REAL_internal_gamemode_request_end,
166 sizeof(REAL_internal_gamemode_request_end),
167 true },
168 { "real_gamemode_query_status",
169 (void **)&REAL_internal_gamemode_query_status,
170 sizeof(REAL_internal_gamemode_query_status),
171 false },
172 { "real_gamemode_error_string",
173 (void **)&REAL_internal_gamemode_error_string,
174 sizeof(REAL_internal_gamemode_error_string),
175 true },
176 { "real_gamemode_request_start_for",
177 (void **)&REAL_internal_gamemode_request_start_for,
178 sizeof(REAL_internal_gamemode_request_start_for),
179 false },
180 { "real_gamemode_request_end_for",
181 (void **)&REAL_internal_gamemode_request_end_for,
182 sizeof(REAL_internal_gamemode_request_end_for),
183 false },
184 { "real_gamemode_query_status_for",
185 (void **)&REAL_internal_gamemode_query_status_for,
186 sizeof(REAL_internal_gamemode_query_status_for),
187 false },
188 };
189
190 void *libgamemode = NULL;
191
192 /* Try and load libgamemode */
193 libgamemode = dlopen("libgamemode.so.0", RTLD_NOW);
194 if (!libgamemode) {
195 /* Attempt to load unversioned library for compatibility with older
196 * versions (as of writing, there are no ABI changes between the two -
197 * this may need to change if ever ABI-breaking changes are made) */
198 libgamemode = dlopen("libgamemode.so", RTLD_NOW);
199 if (!libgamemode) {
200 snprintf(internal_gamemode_client_error_string,
201 sizeof(internal_gamemode_client_error_string),
202 "dlopen failed - %s",
203 dlerror());
204 internal_libgamemode_loaded = -1;
205 return -1;
206 }
207 }
208
209 /* Attempt to bind all symbols */
210 for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) {
211 struct binding *binder = &bindings[i];
212
213 if (internal_bind_libgamemode_symbol(libgamemode,
214 binder->name,
215 binder->functor,
216 binder->func_size,
217 binder->required)) {
218 internal_libgamemode_loaded = -1;
219 return -1;
220 };
221 }
222
223 /* Success */
224 internal_libgamemode_loaded = 0;
225 return 0;
226}
227
228/**
229 * Redirect to the real libgamemode
230 */
231__attribute__((always_inline)) static inline const char *gamemode_error_string(void)
232{
233 /* If we fail to load the system gamemode, or we have an error string already, return our error
234 * string instead of diverting to the system version */
235 if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') {
236 return internal_gamemode_client_error_string;
237 }
238
239 /* Assert for static analyser that the function is not NULL */
240 assert(REAL_internal_gamemode_error_string != NULL);
241
242 return REAL_internal_gamemode_error_string();
243}
244
245/**
246 * Redirect to the real libgamemode
247 * Allow automatically requesting game mode
248 * Also prints errors as they happen.
249 */
250#ifdef GAMEMODE_AUTO
251__attribute__((constructor))
252#else
253__attribute__((always_inline)) static inline
254#endif
255int gamemode_request_start(void)
256{
257 /* Need to load gamemode */
258 if (internal_load_libgamemode() < 0) {
259#ifdef GAMEMODE_AUTO
260 fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
261#endif
262 return -1;
263 }
264
265 /* Assert for static analyser that the function is not NULL */
266 assert(REAL_internal_gamemode_request_start != NULL);
267
268 if (REAL_internal_gamemode_request_start() < 0) {
269#ifdef GAMEMODE_AUTO
270 fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
271#endif
272 return -1;
273 }
274
275 return 0;
276}
277
278/* Redirect to the real libgamemode */
279#ifdef GAMEMODE_AUTO
280__attribute__((destructor))
281#else
282__attribute__((always_inline)) static inline
283#endif
284int gamemode_request_end(void)
285{
286 /* Need to load gamemode */
287 if (internal_load_libgamemode() < 0) {
288#ifdef GAMEMODE_AUTO
289 fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
290#endif
291 return -1;
292 }
293
294 /* Assert for static analyser that the function is not NULL */
295 assert(REAL_internal_gamemode_request_end != NULL);
296
297 if (REAL_internal_gamemode_request_end() < 0) {
298#ifdef GAMEMODE_AUTO
299 fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
300#endif
301 return -1;
302 }
303
304 return 0;
305}
306
307/* Redirect to the real libgamemode */
308__attribute__((always_inline)) static inline int gamemode_query_status(void)
309{
310 /* Need to load gamemode */
311 if (internal_load_libgamemode() < 0) {
312 return -1;
313 }
314
315 if (REAL_internal_gamemode_query_status == NULL) {
316 snprintf(internal_gamemode_client_error_string,
317 sizeof(internal_gamemode_client_error_string),
318 "gamemode_query_status missing (older host?)");
319 return -1;
320 }
321
322 return REAL_internal_gamemode_query_status();
323}
324
325/* Redirect to the real libgamemode */
326__attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid)
327{
328 /* Need to load gamemode */
329 if (internal_load_libgamemode() < 0) {
330 return -1;
331 }
332
333 if (REAL_internal_gamemode_request_start_for == NULL) {
334 snprintf(internal_gamemode_client_error_string,
335 sizeof(internal_gamemode_client_error_string),
336 "gamemode_request_start_for missing (older host?)");
337 return -1;
338 }
339
340 return REAL_internal_gamemode_request_start_for(pid);
341}
342
343/* Redirect to the real libgamemode */
344__attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid)
345{
346 /* Need to load gamemode */
347 if (internal_load_libgamemode() < 0) {
348 return -1;
349 }
350
351 if (REAL_internal_gamemode_request_end_for == NULL) {
352 snprintf(internal_gamemode_client_error_string,
353 sizeof(internal_gamemode_client_error_string),
354 "gamemode_request_end_for missing (older host?)");
355 return -1;
356 }
357
358 return REAL_internal_gamemode_request_end_for(pid);
359}
360
361/* Redirect to the real libgamemode */
362__attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid)
363{
364 /* Need to load gamemode */
365 if (internal_load_libgamemode() < 0) {
366 return -1;
367 }
368
369 if (REAL_internal_gamemode_query_status_for == NULL) {
370 snprintf(internal_gamemode_client_error_string,
371 sizeof(internal_gamemode_client_error_string),
372 "gamemode_query_status_for missing (older host?)");
373 return -1;
374 }
375
376 return REAL_internal_gamemode_query_status_for(pid);
377}
378
379#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)
174 ) 174 )
175endif() 175endif()
176 176
177if (UNIX)
178 target_sources(common PRIVATE
179 linux/gamemode.cpp
180 linux/gamemode.h
181 )
182
183 target_link_libraries(common PRIVATE gamemode)
184endif()
185
177if(ARCHITECTURE_x86_64) 186if(ARCHITECTURE_x86_64)
178 target_sources(common 187 target_sources(common
179 PRIVATE 188 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <gamemode_client.h>
5
6#include "common/linux/gamemode.h"
7#include "common/settings.h"
8
9namespace Common::Linux {
10
11void StartGamemode() {
12 if (Settings::values.enable_gamemode) {
13 if (gamemode_request_start() < 0) {
14 LOG_WARNING(Frontend, "Failed to start gamemode: {}", gamemode_error_string());
15 } else {
16 LOG_INFO(Frontend, "Started gamemode");
17 }
18 }
19}
20
21void StopGamemode() {
22 if (Settings::values.enable_gamemode) {
23 if (gamemode_request_end() < 0) {
24 LOG_WARNING(Frontend, "Failed to stop gamemode: {}", gamemode_error_string());
25 } else {
26 LOG_INFO(Frontend, "Stopped gamemode");
27 }
28 }
29}
30
31void SetGamemodeState(bool state) {
32 if (state) {
33 StartGamemode();
34 } else {
35 StopGamemode();
36 }
37}
38
39} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace Common::Linux {
7
8/**
9 * Start the (Feral Interactive) Linux gamemode if it is installed and it is activated
10 */
11void StartGamemode();
12
13/**
14 * Stop the (Feral Interactive) Linux gamemode if it is installed and it is activated
15 */
16void StopGamemode();
17
18/**
19 * Start or stop the (Feral Interactive) Linux gamemode if it is installed and it is activated
20 * @param state The new state the gamemode should have
21 */
22void SetGamemodeState(bool state);
23
24} // 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) {
219 return "Services"; 219 return "Services";
220 case Category::Paths: 220 case Category::Paths:
221 return "Paths"; 221 return "Paths";
222 case Category::Linux:
223 return "Linux";
222 case Category::MaxEnum: 224 case Category::MaxEnum:
223 break; 225 break;
224 } 226 }
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 {
429 true, 429 true,
430 true}; 430 true};
431 431
432 // Linux
433 SwitchableSetting<bool> enable_gamemode{linkage, true, "enable_gamemode", Category::Linux};
434
432 // Controls 435 // Controls
433 InputSetting<std::array<PlayerInput, 10>> players; 436 InputSetting<std::array<PlayerInput, 10>> players;
434 437
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 {
41 Multiplayer, 41 Multiplayer,
42 Services, 42 Services,
43 Paths, 43 Paths,
44 Linux,
44 MaxEnum, 45 MaxEnum,
45}; 46};
46 47
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)
386 target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS}) 386 target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
387endif() 387endif()
388if (UNIX AND NOT APPLE) 388if (UNIX AND NOT APPLE)
389 target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus) 389 target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus gamemode)
390endif() 390endif()
391 391
392target_compile_definitions(yuzu PRIVATE 392target_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;
36void ConfigureGeneral::SetConfiguration() {} 36void ConfigureGeneral::SetConfiguration() {}
37 37
38void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { 38void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) {
39 QLayout& layout = *ui->general_widget->layout(); 39 QLayout& general_layout = *ui->general_widget->layout();
40 QLayout& linux_layout = *ui->linux_widget->layout();
40 41
41 std::map<u32, QWidget*> hold{}; 42 std::map<u32, QWidget*> general_hold{};
43 std::map<u32, QWidget*> linux_hold{};
42 44
43 for (const auto setting : 45 std::vector<Settings::BasicSetting*> settings;
44 UISettings::values.linkage.by_category[Settings::Category::UiGeneral]) { 46
47 auto push = [&settings](auto& list) {
48 for (auto setting : list) {
49 settings.push_back(setting);
50 }
51 };
52
53 push(UISettings::values.linkage.by_category[Settings::Category::UiGeneral]);
54 push(Settings::values.linkage.by_category[Settings::Category::Linux]);
55
56 // Only show Linux group on Unix
57#ifndef __unix__
58 ui->LinuxGroupBox->setVisible(false);
59#endif
60
61 for (const auto setting : settings) {
45 auto* widget = builder.BuildWidget(setting, apply_funcs); 62 auto* widget = builder.BuildWidget(setting, apply_funcs);
46 63
47 if (widget == nullptr) { 64 if (widget == nullptr) {
@@ -52,11 +69,23 @@ void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) {
52 continue; 69 continue;
53 } 70 }
54 71
55 hold.emplace(setting->Id(), widget); 72 switch (setting->GetCategory()) {
73 case Settings::Category::UiGeneral:
74 general_hold.emplace(setting->Id(), widget);
75 break;
76 case Settings::Category::Linux:
77 linux_hold.emplace(setting->Id(), widget);
78 break;
79 default:
80 widget->deleteLater();
81 }
56 } 82 }
57 83
58 for (const auto& [id, widget] : hold) { 84 for (const auto& [id, widget] : general_hold) {
59 layout.addWidget(widget); 85 general_layout.addWidget(widget);
86 }
87 for (const auto& [id, widget] : linux_hold) {
88 linux_layout.addWidget(widget);
60 } 89 }
61} 90}
62 91
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 @@
47 </widget> 47 </widget>
48 </item> 48 </item>
49 <item> 49 <item>
50 <widget class="QGroupBox" name="LinuxGroupBox">
51 <property name="title">
52 <string>Linux</string>
53 </property>
54 <layout class="QVBoxLayout" name="LinuxVerticalLayout_1">
55 <item>
56 <widget class="QWidget" name="linux_widget" native="true">
57 <layout class="QVBoxLayout" name="LinuxVerticalLayout_2">
58 <property name="leftMargin">
59 <number>0</number>
60 </property>
61 <property name="topMargin">
62 <number>0</number>
63 </property>
64 <property name="rightMargin">
65 <number>0</number>
66 </property>
67 <property name="bottomMargin">
68 <number>0</number>
69 </property>
70 </layout>
71 </widget>
72 </item>
73 </layout>
74 </widget>
75 </item>
76 <item>
50 <spacer name="verticalSpacer"> 77 <spacer name="verticalSpacer">
51 <property name="orientation"> 78 <property name="orientation">
52 <enum>Qt::Vertical</enum> 79 <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 @@
57 </widget> 57 </widget>
58 </item> 58 </item>
59 <item> 59 <item>
60 <widget class="QGroupBox" name="groupBox"> 60 <widget class="QGroupBox" name="coreGroup">
61 <property name="title"> 61 <property name="title">
62 <string>Core</string> 62 <string>Core</string>
63 </property> 63 </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) {
176 INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), 176 INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
177 QStringLiteral()); 177 QStringLiteral());
178 178
179 // Linux
180 INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QStringLiteral());
181
179 // Ui Debugging 182 // Ui Debugging
180 183
181 // Ui Multiplayer 184 // 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 @@
17#ifdef __unix__ 17#ifdef __unix__
18#include <csignal> 18#include <csignal>
19#include <sys/socket.h> 19#include <sys/socket.h>
20#include "common/linux/gamemode.h"
20#endif 21#endif
21 22
22#include <boost/container/flat_set.hpp> 23#include <boost/container/flat_set.hpp>
@@ -319,6 +320,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
319 provider{std::make_unique<FileSys::ManualContentProvider>()} { 320 provider{std::make_unique<FileSys::ManualContentProvider>()} {
320#ifdef __unix__ 321#ifdef __unix__
321 SetupSigInterrupts(); 322 SetupSigInterrupts();
323 SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());
322#endif 324#endif
323 system->Initialize(); 325 system->Initialize();
324 326
@@ -2120,6 +2122,10 @@ void GMainWindow::OnEmulationStopped() {
2120 2122
2121 discord_rpc->Update(); 2123 discord_rpc->Update();
2122 2124
2125#ifdef __unix__
2126 Common::Linux::StopGamemode();
2127#endif
2128
2123 // The emulation is stopped, so closing the window or not does not matter anymore 2129 // The emulation is stopped, so closing the window or not does not matter anymore
2124 disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 2130 disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
2125 2131
@@ -3502,6 +3508,10 @@ void GMainWindow::OnStartGame() {
3502 play_time_manager->Start(); 3508 play_time_manager->Start();
3503 3509
3504 discord_rpc->Update(); 3510 discord_rpc->Update();
3511
3512#ifdef __unix__
3513 Common::Linux::StartGamemode();
3514#endif
3505} 3515}
3506 3516
3507void GMainWindow::OnRestartGame() { 3517void GMainWindow::OnRestartGame() {
@@ -3522,6 +3532,10 @@ void GMainWindow::OnPauseGame() {
3522 play_time_manager->Stop(); 3532 play_time_manager->Stop();
3523 UpdateMenuState(); 3533 UpdateMenuState();
3524 AllowOSSleep(); 3534 AllowOSSleep();
3535
3536#ifdef __unix__
3537 Common::Linux::StopGamemode();
3538#endif
3525} 3539}
3526 3540
3527void GMainWindow::OnPauseContinueGame() { 3541void GMainWindow::OnPauseContinueGame() {
@@ -3803,6 +3817,9 @@ void GMainWindow::OnConfigure() {
3803 const auto old_theme = UISettings::values.theme; 3817 const auto old_theme = UISettings::values.theme;
3804 const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); 3818 const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
3805 const auto old_language_index = Settings::values.language_index.GetValue(); 3819 const auto old_language_index = Settings::values.language_index.GetValue();
3820#ifdef __unix__
3821 const bool old_gamemode = Settings::values.enable_gamemode.GetValue();
3822#endif
3806 3823
3807 Settings::SetConfiguringGlobal(true); 3824 Settings::SetConfiguringGlobal(true);
3808 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), 3825 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(),
@@ -3864,6 +3881,11 @@ void GMainWindow::OnConfigure() {
3864 if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { 3881 if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) {
3865 SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); 3882 SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
3866 } 3883 }
3884#ifdef __unix__
3885 if (Settings::values.enable_gamemode.GetValue() != old_gamemode) {
3886 SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());
3887 }
3888#endif
3867 3889
3868 if (!multiplayer_state->IsHostingPublicRoom()) { 3890 if (!multiplayer_state->IsHostingPublicRoom()) {
3869 multiplayer_state->UpdateCredentials(); 3891 multiplayer_state->UpdateCredentials();
@@ -5172,6 +5194,14 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
5172 discord_rpc->Update(); 5194 discord_rpc->Update();
5173} 5195}
5174 5196
5197#ifdef __unix__
5198void GMainWindow::SetGamemodeEnabled(bool state) {
5199 if (emulation_running) {
5200 Common::Linux::SetGamemodeState(state);
5201 }
5202}
5203#endif
5204
5175void GMainWindow::changeEvent(QEvent* event) { 5205void GMainWindow::changeEvent(QEvent* event) {
5176#ifdef __unix__ 5206#ifdef __unix__
5177 // PaletteChange event appears to only reach so far into the GUI, explicitly asking to 5207 // 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:
340 void SetupSigInterrupts(); 340 void SetupSigInterrupts();
341 static void HandleSigInterrupt(int); 341 static void HandleSigInterrupt(int);
342 void OnSigInterruptNotifierActivated(); 342 void OnSigInterruptNotifierActivated();
343 void SetGamemodeEnabled(bool state);
343#endif 344#endif
344 345
345private slots: 346private 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;
63} 63}
64#endif 64#endif
65 65
66#ifdef __unix__
67#include "common/linux/gamemode.h"
68#endif
69
66static void PrintHelp(const char* argv0) { 70static void PrintHelp(const char* argv0) {
67 std::cout << "Usage: " << argv0 71 std::cout << "Usage: " << argv0
68 << " [options] <filename>\n" 72 << " [options] <filename>\n"
@@ -425,6 +429,10 @@ int main(int argc, char** argv) {
425 exit(0); 429 exit(0);
426 }); 430 });
427 431
432#ifdef __unix__
433 Common::Linux::StartGamemode();
434#endif
435
428 void(system.Run()); 436 void(system.Run());
429 if (system.DebuggerEnabled()) { 437 if (system.DebuggerEnabled()) {
430 system.InitializeDebugger(); 438 system.InitializeDebugger();
@@ -436,6 +444,10 @@ int main(int argc, char** argv) {
436 void(system.Pause()); 444 void(system.Pause());
437 system.ShutdownMainProcess(); 445 system.ShutdownMainProcess();
438 446
447#ifdef __unix__
448 Common::Linux::StopGamemode();
449#endif
450
439 detached_tasks.WaitForAllTasks(); 451 detached_tasks.WaitForAllTasks();
440 return 0; 452 return 0;
441} 453}