committed by
flodavid
11 changed files with 481 additions and 1 deletions
-
6externals/CMakeLists.txt
-
7externals/gamemode/CMakeLists.txt
-
379externals/gamemode/include/gamemode_client.h
-
2src/common/settings.h
-
2src/yuzu/CMakeLists.txt
-
3src/yuzu/configuration/configure_general.cpp
-
1src/yuzu/configuration/shared_translation.cpp
-
54src/yuzu/main.cpp
-
3src/yuzu/uisettings.h
-
1src/yuzu_cmd/CMakeLists.txt
-
24src/yuzu_cmd/yuzu.cpp
@ -0,0 +1,7 @@ |
|||
# SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
|||
# SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
project(gamemode) |
|||
|
|||
add_library(gamemode include/gamemode_client.h) |
|||
set_target_properties(gamemode PROPERTIES LINKER_LANGUAGE C) |
|||
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue