diff options
Diffstat (limited to 'libraries/gamemode/include')
| -rw-r--r-- | libraries/gamemode/include/gamemode_client.h | 365 | 
1 files changed, 365 insertions, 0 deletions
| diff --git a/libraries/gamemode/include/gamemode_client.h b/libraries/gamemode/include/gamemode_client.h new file mode 100644 index 00000000..b6f7afd4 --- /dev/null +++ b/libraries/gamemode/include/gamemode_client.h @@ -0,0 +1,365 @@ +/* + +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 <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; +	} + +	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; +	} + +	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; +	} + +	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 | 
