mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 13:20:03 +00:00 
			
		
		
		
	Port yuzu-emu/yuzu#11946: "Enable (Feral Interactive) Gamemode on Linux" (#7245)
This commit is contained in:
		
							parent
							
								
									2e369c03b8
								
							
						
					
					
						commit
						c7e9f8449e
					
				
					 14 changed files with 540 additions and 3 deletions
				
			
		
							
								
								
									
										4
									
								
								externals/CMakeLists.txt
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								externals/CMakeLists.txt
									
										
									
									
										vendored
									
									
								
							|  | @ -319,6 +319,10 @@ if(ANDROID) | ||||||
|     target_link_libraries(httplib INTERFACE ifaddrs) |     target_link_libraries(httplib INTERFACE ifaddrs) | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
|  | if (UNIX AND NOT APPLE) | ||||||
|  |     add_subdirectory(gamemode) | ||||||
|  | endif() | ||||||
|  | 
 | ||||||
| # cpp-jwt | # cpp-jwt | ||||||
| if (ENABLE_WEB_SERVICE) | if (ENABLE_WEB_SERVICE) | ||||||
|     if (USE_SYSTEM_CPP_JWT) |     if (USE_SYSTEM_CPP_JWT) | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								externals/gamemode/CMakeLists.txt
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								externals/gamemode/CMakeLists.txt
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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) | ||||||
							
								
								
									
										379
									
								
								externals/gamemode/include/gamemode_client.h
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										379
									
								
								externals/gamemode/include/gamemode_client.h
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
|  | @ -38,6 +38,10 @@ | ||||||
| #include "network/network.h" | #include "network/network.h" | ||||||
| #include "video_core/renderer_base.h" | #include "video_core/renderer_base.h" | ||||||
| 
 | 
 | ||||||
|  | #ifdef __unix__ | ||||||
|  | #include "common/linux/gamemode.h" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #undef _UNICODE | #undef _UNICODE | ||||||
| #include <getopt.h> | #include <getopt.h> | ||||||
| #ifndef _MSC_VER | #ifndef _MSC_VER | ||||||
|  | @ -442,6 +446,10 @@ int main(int argc, char** argv) { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | #ifdef __unix__ | ||||||
|  |     Common::Linux::StartGamemode(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|     std::thread main_render_thread([&emu_window] { emu_window->Present(); }); |     std::thread main_render_thread([&emu_window] { emu_window->Present(); }); | ||||||
|     std::thread secondary_render_thread([&secondary_window] { |     std::thread secondary_render_thread([&secondary_window] { | ||||||
|         if (secondary_window) { |         if (secondary_window) { | ||||||
|  | @ -493,6 +501,10 @@ int main(int argc, char** argv) { | ||||||
| 
 | 
 | ||||||
|     system.Shutdown(); |     system.Shutdown(); | ||||||
| 
 | 
 | ||||||
|  | #ifdef __unix__ | ||||||
|  |     Common::Linux::StopGamemode(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|     detached_tasks.WaitForAllTasks(); |     detached_tasks.WaitForAllTasks(); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -308,7 +308,7 @@ if (NOT WIN32) | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
| if (UNIX AND NOT APPLE) | if (UNIX AND NOT APPLE) | ||||||
|     target_link_libraries(citra-qt PRIVATE Qt6::DBus) |     target_link_libraries(citra-qt PRIVATE Qt6::DBus gamemode) | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
| target_compile_definitions(citra-qt PRIVATE | target_compile_definitions(citra-qt PRIVATE | ||||||
|  |  | ||||||
|  | @ -528,6 +528,7 @@ void Config::ReadMiscellaneousValues() { | ||||||
|     qt_config->beginGroup(QStringLiteral("Miscellaneous")); |     qt_config->beginGroup(QStringLiteral("Miscellaneous")); | ||||||
| 
 | 
 | ||||||
|     ReadBasicSetting(Settings::values.log_filter); |     ReadBasicSetting(Settings::values.log_filter); | ||||||
|  |     ReadBasicSetting(Settings::values.enable_gamemode); | ||||||
| 
 | 
 | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
| } | } | ||||||
|  | @ -1044,6 +1045,7 @@ void Config::SaveMiscellaneousValues() { | ||||||
|     qt_config->beginGroup(QStringLiteral("Miscellaneous")); |     qt_config->beginGroup(QStringLiteral("Miscellaneous")); | ||||||
| 
 | 
 | ||||||
|     WriteBasicSetting(Settings::values.log_filter); |     WriteBasicSetting(Settings::values.log_filter); | ||||||
|  |     WriteBasicSetting(Settings::values.enable_gamemode); | ||||||
| 
 | 
 | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -36,6 +36,9 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) | ||||||
|     ui->emulation_speed_combo->setVisible(!Settings::IsConfiguringGlobal()); |     ui->emulation_speed_combo->setVisible(!Settings::IsConfiguringGlobal()); | ||||||
|     ui->screenshot_combo->setVisible(!Settings::IsConfiguringGlobal()); |     ui->screenshot_combo->setVisible(!Settings::IsConfiguringGlobal()); | ||||||
|     ui->updateBox->setVisible(UISettings::values.updater_found); |     ui->updateBox->setVisible(UISettings::values.updater_found); | ||||||
|  | #ifndef __unix__ | ||||||
|  |     ui->toggle_gamemode->setVisible(false); | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|     SetupPerGameUI(); |     SetupPerGameUI(); | ||||||
|     SetConfiguration(); |     SetConfiguration(); | ||||||
|  | @ -76,6 +79,9 @@ void ConfigureGeneral::SetConfiguration() { | ||||||
|         ui->toggle_update_check->setChecked( |         ui->toggle_update_check->setChecked( | ||||||
|             UISettings::values.check_for_update_on_start.GetValue()); |             UISettings::values.check_for_update_on_start.GetValue()); | ||||||
|         ui->toggle_auto_update->setChecked(UISettings::values.update_on_close.GetValue()); |         ui->toggle_auto_update->setChecked(UISettings::values.update_on_close.GetValue()); | ||||||
|  | #ifdef __unix__ | ||||||
|  |         ui->toggle_gamemode->setChecked(Settings::values.enable_gamemode.GetValue()); | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (Settings::values.frame_limit.GetValue() == 0) { |     if (Settings::values.frame_limit.GetValue() == 0) { | ||||||
|  | @ -172,6 +178,9 @@ void ConfigureGeneral::ApplyConfiguration() { | ||||||
| 
 | 
 | ||||||
|         UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked(); |         UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked(); | ||||||
|         UISettings::values.update_on_close = ui->toggle_auto_update->isChecked(); |         UISettings::values.update_on_close = ui->toggle_auto_update->isChecked(); | ||||||
|  | #ifdef __unix__ | ||||||
|  |         Settings::values.enable_gamemode = ui->toggle_gamemode->isChecked(); | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -201,6 +210,7 @@ void ConfigureGeneral::SetupPerGameUI() { | ||||||
|     ui->general_group->setVisible(false); |     ui->general_group->setVisible(false); | ||||||
|     ui->updateBox->setVisible(false); |     ui->updateBox->setVisible(false); | ||||||
|     ui->button_reset_defaults->setVisible(false); |     ui->button_reset_defaults->setVisible(false); | ||||||
|  |     ui->toggle_gamemode->setVisible(false); | ||||||
| 
 | 
 | ||||||
|     ConfigurationShared::SetColoredComboBox( |     ConfigurationShared::SetColoredComboBox( | ||||||
|         ui->region_combobox, ui->widget_region, |         ui->region_combobox, ui->widget_region, | ||||||
|  |  | ||||||
|  | @ -43,6 +43,13 @@ | ||||||
|           </property> |           </property> | ||||||
|          </widget> |          </widget> | ||||||
|         </item> |         </item> | ||||||
|  |         <item> | ||||||
|  |          <widget class="QCheckBox" name="toggle_gamemode"> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string>Enable Gamemode</string> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|        </layout> |        </layout> | ||||||
|       </widget> |       </widget> | ||||||
|      </item> |      </item> | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ | ||||||
| #include <QVariant> | #include <QVariant> | ||||||
| #include <QtDBus/QDBusInterface> | #include <QtDBus/QDBusInterface> | ||||||
| #include <QtDBus/QtDBus> | #include <QtDBus/QtDBus> | ||||||
|  | #include "common/linux/gamemode.h" | ||||||
| #endif | #endif | ||||||
| #include "citra_qt/aboutdialog.h" | #include "citra_qt/aboutdialog.h" | ||||||
| #include "citra_qt/applets/mii_selector.h" | #include "citra_qt/applets/mii_selector.h" | ||||||
|  | @ -182,6 +183,10 @@ GMainWindow::GMainWindow(Core::System& system_) | ||||||
| 
 | 
 | ||||||
|     Debugger::ToggleConsole(); |     Debugger::ToggleConsole(); | ||||||
| 
 | 
 | ||||||
|  | #ifdef __unix__ | ||||||
|  |     SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|     // register types to use in slots and signals
 |     // register types to use in slots and signals
 | ||||||
|     qRegisterMetaType<std::size_t>("std::size_t"); |     qRegisterMetaType<std::size_t>("std::size_t"); | ||||||
|     qRegisterMetaType<Service::AM::InstallStatus>("Service::AM::InstallStatus"); |     qRegisterMetaType<Service::AM::InstallStatus>("Service::AM::InstallStatus"); | ||||||
|  | @ -1323,6 +1328,10 @@ void GMainWindow::ShutdownGame() { | ||||||
| 
 | 
 | ||||||
|     discord_rpc->Update(); |     discord_rpc->Update(); | ||||||
| 
 | 
 | ||||||
|  | #ifdef __unix__ | ||||||
|  |     Common::Linux::StopGamemode(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|     // The emulation is stopped, so closing the window or not does not matter anymore
 |     // The emulation is stopped, so closing the window or not does not matter anymore
 | ||||||
|     disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |     disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | ||||||
|     disconnect(secondary_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |     disconnect(secondary_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | ||||||
|  | @ -1834,6 +1843,10 @@ void GMainWindow::OnStartGame() { | ||||||
| 
 | 
 | ||||||
|     discord_rpc->Update(); |     discord_rpc->Update(); | ||||||
| 
 | 
 | ||||||
|  | #ifdef __unix__ | ||||||
|  |     Common::Linux::StartGamemode(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|     UpdateSaveStates(); |     UpdateSaveStates(); | ||||||
|     UpdateAPIIndicator(); |     UpdateAPIIndicator(); | ||||||
| } | } | ||||||
|  | @ -1852,6 +1865,10 @@ void GMainWindow::OnPauseGame() { | ||||||
| 
 | 
 | ||||||
|     UpdateMenuState(); |     UpdateMenuState(); | ||||||
|     AllowOSSleep(); |     AllowOSSleep(); | ||||||
|  | 
 | ||||||
|  | #ifdef __unix__ | ||||||
|  |     Common::Linux::StopGamemode(); | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GMainWindow::OnPauseContinueGame() { | void GMainWindow::OnPauseContinueGame() { | ||||||
|  | @ -2076,15 +2093,25 @@ void GMainWindow::OnConfigure() { | ||||||
|     const auto old_input_profiles = Settings::values.input_profiles; |     const auto old_input_profiles = Settings::values.input_profiles; | ||||||
|     const auto old_touch_from_button_maps = Settings::values.touch_from_button_maps; |     const auto old_touch_from_button_maps = Settings::values.touch_from_button_maps; | ||||||
|     const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); |     const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); | ||||||
|  | #ifdef __unix__ | ||||||
|  |     const bool old_gamemode = Settings::values.enable_gamemode.GetValue(); | ||||||
|  | #endif | ||||||
|     auto result = configureDialog.exec(); |     auto result = configureDialog.exec(); | ||||||
|     game_list->SetDirectoryWatcherEnabled(true); |     game_list->SetDirectoryWatcherEnabled(true); | ||||||
|     if (result == QDialog::Accepted) { |     if (result == QDialog::Accepted) { | ||||||
|         configureDialog.ApplyConfiguration(); |         configureDialog.ApplyConfiguration(); | ||||||
|         InitializeHotkeys(); |         InitializeHotkeys(); | ||||||
|         if (UISettings::values.theme != old_theme) |         if (UISettings::values.theme != old_theme) { | ||||||
|             UpdateUITheme(); |             UpdateUITheme(); | ||||||
|         if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) |         } | ||||||
|  |         if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { | ||||||
|             SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); |             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()) |         if (!multiplayer_state->IsHostingPublicRoom()) | ||||||
|             multiplayer_state->UpdateCredentials(); |             multiplayer_state->UpdateCredentials(); | ||||||
|         emit UpdateThemedIcons(); |         emit UpdateThemedIcons(); | ||||||
|  | @ -2931,6 +2958,14 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { | ||||||
|     discord_rpc->Update(); |     discord_rpc->Update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #ifdef __unix__ | ||||||
|  | void GMainWindow::SetGamemodeEnabled(bool state) { | ||||||
|  |     if (emulation_running) { | ||||||
|  |         Common::Linux::SetGamemodeState(state); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef main | #ifdef main | ||||||
| #undef main | #undef main | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -276,6 +276,9 @@ private: | ||||||
|     void ShowMouseCursor(); |     void ShowMouseCursor(); | ||||||
|     void OpenPerGameConfiguration(u64 title_id, const QString& file_name); |     void OpenPerGameConfiguration(u64 title_id, const QString& file_name); | ||||||
|     void UpdateAPIIndicator(bool update = false); |     void UpdateAPIIndicator(bool update = false); | ||||||
|  | #ifdef __unix__ | ||||||
|  |     void SetGamemodeEnabled(bool state); | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<Ui::MainWindow> ui; |     std::unique_ptr<Ui::MainWindow> ui; | ||||||
|     Core::System& system; |     Core::System& system; | ||||||
|  |  | ||||||
|  | @ -154,6 +154,15 @@ add_library(citra_common STATIC | ||||||
|     zstd_compression.h |     zstd_compression.h | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | if (UNIX AND NOT APPLE) | ||||||
|  |   target_sources(citra_common PRIVATE | ||||||
|  |     linux/gamemode.cpp | ||||||
|  |     linux/gamemode.h | ||||||
|  |   ) | ||||||
|  | 
 | ||||||
|  |   target_link_libraries(citra_common PRIVATE gamemode) | ||||||
|  | endif() | ||||||
|  | 
 | ||||||
| if (APPLE) | if (APPLE) | ||||||
|     target_sources(citra_common PUBLIC |     target_sources(citra_common PUBLIC | ||||||
|         apple_authorization.h |         apple_authorization.h | ||||||
|  |  | ||||||
							
								
								
									
										39
									
								
								src/common/linux/gamemode.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/common/linux/gamemode.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
							
								
								
									
										24
									
								
								src/common/linux/gamemode.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/common/linux/gamemode.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
|  | @ -421,6 +421,8 @@ struct Values { | ||||||
|     std::vector<InputProfile> input_profiles; ///< The list of input profiles
 |     std::vector<InputProfile> input_profiles; ///< The list of input profiles
 | ||||||
|     std::vector<TouchFromButtonMap> touch_from_button_maps; |     std::vector<TouchFromButtonMap> touch_from_button_maps; | ||||||
| 
 | 
 | ||||||
|  |     SwitchableSetting<bool> enable_gamemode{true, "enable_gamemode"}; | ||||||
|  | 
 | ||||||
|     // Core
 |     // Core
 | ||||||
|     Setting<bool> use_cpu_jit{true, "use_cpu_jit"}; |     Setting<bool> use_cpu_jit{true, "use_cpu_jit"}; | ||||||
|     SwitchableSetting<s32, true> cpu_clock_percentage{100, 5, 400, "cpu_clock_percentage"}; |     SwitchableSetting<s32, true> cpu_clock_percentage{100, 5, 400, "cpu_clock_percentage"}; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue