summaryrefslogtreecommitdiff
path: root/externals/gamemode/gamemode_client.h
diff options
context:
space:
mode:
Diffstat (limited to 'externals/gamemode/gamemode_client.h')
-rw-r--r--externals/gamemode/gamemode_client.h376
1 files changed, 376 insertions, 0 deletions
diff --git a/externals/gamemode/gamemode_client.h b/externals/gamemode/gamemode_client.h
new file mode 100644
index 000000000..b9f64fe46
--- /dev/null
+++ b/externals/gamemode/gamemode_client.h
@@ -0,0 +1,376 @@
1/*
2
3Copyright (c) 2017-2019, Feral Interactive
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are met:
8
9 * Redistributions of source code must retain the above copyright notice,
10 this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 * Neither the name of Feral Interactive nor the names of its contributors
15 may be used to endorse or promote products derived from this software
16 without specific prior written permission.
17
18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28POSSIBILITY OF SUCH DAMAGE.
29
30 */
31#ifndef CLIENT_GAMEMODE_H
32#define CLIENT_GAMEMODE_H
33/*
34 * GameMode supports the following client functions
35 * Requests are refcounted in the daemon
36 *
37 * int gamemode_request_start() - Request gamemode starts
38 * 0 if the request was sent successfully
39 * -1 if the request failed
40 *
41 * int gamemode_request_end() - Request gamemode ends
42 * 0 if the request was sent successfully
43 * -1 if the request failed
44 *
45 * GAMEMODE_AUTO can be defined to make the above two functions apply during static init and
46 * destruction, as appropriate. In this configuration, errors will be printed to stderr
47 *
48 * int gamemode_query_status() - Query the current status of gamemode
49 * 0 if gamemode is inactive
50 * 1 if gamemode is active
51 * 2 if gamemode is active and this client is registered
52 * -1 if the query failed
53 *
54 * int gamemode_request_start_for(pid_t pid) - Request gamemode starts for another process
55 * 0 if the request was sent successfully
56 * -1 if the request failed
57 * -2 if the request was rejected
58 *
59 * int gamemode_request_end_for(pid_t pid) - Request gamemode ends for another process
60 * 0 if the request was sent successfully
61 * -1 if the request failed
62 * -2 if the request was rejected
63 *
64 * int gamemode_query_status_for(pid_t pid) - Query status of gamemode for another process
65 * 0 if gamemode is inactive
66 * 1 if gamemode is active
67 * 2 if gamemode is active and this client is registered
68 * -1 if the query failed
69 *
70 * const char* gamemode_error_string() - Get an error string
71 * returns a string describing any of the above errors
72 *
73 * Note: All the above requests can be blocking - dbus requests can and will block while the daemon
74 * handles the request. It is not recommended to make these calls in performance critical code
75 */
76
77#include <stdbool.h>
78#include <stdio.h>
79
80#include <dlfcn.h>
81#include <string.h>
82
83#include <assert.h>
84
85#include <sys/types.h>
86
87static char internal_gamemode_client_error_string[512] = { 0 };
88
89/**
90 * Load libgamemode dynamically to dislodge us from most dependencies.
91 * This allows clients to link and/or use this regardless of runtime.
92 * See SDL2 for an example of the reasoning behind this in terms of
93 * dynamic versioning as well.
94 */
95static volatile int internal_libgamemode_loaded = 1;
96
97/* Typedefs for the functions to load */
98typedef int (*api_call_return_int)(void);
99typedef const char *(*api_call_return_cstring)(void);
100typedef int (*api_call_pid_return_int)(pid_t);
101
102/* Storage for functors */
103static api_call_return_int REAL_internal_gamemode_request_start = NULL;
104static api_call_return_int REAL_internal_gamemode_request_end = NULL;
105static api_call_return_int REAL_internal_gamemode_query_status = NULL;
106static api_call_return_cstring REAL_internal_gamemode_error_string = NULL;
107static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL;
108static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL;
109static api_call_pid_return_int REAL_internal_gamemode_query_status_for = NULL;
110
111/**
112 * Internal helper to perform the symbol binding safely.
113 *
114 * Returns 0 on success and -1 on failure
115 */
116__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol(
117 void *handle, const char *name, void **out_func, size_t func_size, bool required)
118{
119 void *symbol_lookup = NULL;
120 char *dl_error = NULL;
121
122 /* Safely look up the symbol */
123 symbol_lookup = dlsym(handle, name);
124 dl_error = dlerror();
125 if (required && (dl_error || !symbol_lookup)) {
126 snprintf(internal_gamemode_client_error_string,
127 sizeof(internal_gamemode_client_error_string),
128 "dlsym failed - %s",
129 dl_error);
130 return -1;
131 }
132
133 /* Have the symbol correctly, copy it to make it usable */
134 memcpy(out_func, &symbol_lookup, func_size);
135 return 0;
136}
137
138/**
139 * Loads libgamemode and needed functions
140 *
141 * Returns 0 on success and -1 on failure
142 */
143__attribute__((always_inline)) static inline int internal_load_libgamemode(void)
144{
145 /* We start at 1, 0 is a success and -1 is a fail */
146 if (internal_libgamemode_loaded != 1) {
147 return internal_libgamemode_loaded;
148 }
149
150 /* Anonymous struct type to define our bindings */
151 struct binding {
152 const char *name;
153 void **functor;
154 size_t func_size;
155 bool required;
156 } bindings[] = {
157 { "real_gamemode_request_start",
158 (void **)&REAL_internal_gamemode_request_start,
159 sizeof(REAL_internal_gamemode_request_start),
160 true },
161 { "real_gamemode_request_end",
162 (void **)&REAL_internal_gamemode_request_end,
163 sizeof(REAL_internal_gamemode_request_end),
164 true },
165 { "real_gamemode_query_status",
166 (void **)&REAL_internal_gamemode_query_status,
167 sizeof(REAL_internal_gamemode_query_status),
168 false },
169 { "real_gamemode_error_string",
170 (void **)&REAL_internal_gamemode_error_string,
171 sizeof(REAL_internal_gamemode_error_string),
172 true },
173 { "real_gamemode_request_start_for",
174 (void **)&REAL_internal_gamemode_request_start_for,
175 sizeof(REAL_internal_gamemode_request_start_for),
176 false },
177 { "real_gamemode_request_end_for",
178 (void **)&REAL_internal_gamemode_request_end_for,
179 sizeof(REAL_internal_gamemode_request_end_for),
180 false },
181 { "real_gamemode_query_status_for",
182 (void **)&REAL_internal_gamemode_query_status_for,
183 sizeof(REAL_internal_gamemode_query_status_for),
184 false },
185 };
186
187 void *libgamemode = NULL;
188
189 /* Try and load libgamemode */
190 libgamemode = dlopen("libgamemode.so.0", RTLD_NOW);
191 if (!libgamemode) {
192 /* Attempt to load unversioned library for compatibility with older
193 * versions (as of writing, there are no ABI changes between the two -
194 * this may need to change if ever ABI-breaking changes are made) */
195 libgamemode = dlopen("libgamemode.so", RTLD_NOW);
196 if (!libgamemode) {
197 snprintf(internal_gamemode_client_error_string,
198 sizeof(internal_gamemode_client_error_string),
199 "dlopen failed - %s",
200 dlerror());
201 internal_libgamemode_loaded = -1;
202 return -1;
203 }
204 }
205
206 /* Attempt to bind all symbols */
207 for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) {
208 struct binding *binder = &bindings[i];
209
210 if (internal_bind_libgamemode_symbol(libgamemode,
211 binder->name,
212 binder->functor,
213 binder->func_size,
214 binder->required)) {
215 internal_libgamemode_loaded = -1;
216 return -1;
217 };
218 }
219
220 /* Success */
221 internal_libgamemode_loaded = 0;
222 return 0;
223}
224
225/**
226 * Redirect to the real libgamemode
227 */
228__attribute__((always_inline)) static inline const char *gamemode_error_string(void)
229{
230 /* If we fail to load the system gamemode, or we have an error string already, return our error
231 * string instead of diverting to the system version */
232 if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') {
233 return internal_gamemode_client_error_string;
234 }
235
236 /* Assert for static analyser that the function is not NULL */
237 assert(REAL_internal_gamemode_error_string != NULL);
238
239 return REAL_internal_gamemode_error_string();
240}
241
242/**
243 * Redirect to the real libgamemode
244 * Allow automatically requesting game mode
245 * Also prints errors as they happen.
246 */
247#ifdef GAMEMODE_AUTO
248__attribute__((constructor))
249#else
250__attribute__((always_inline)) static inline
251#endif
252int gamemode_request_start(void)
253{
254 /* Need to load gamemode */
255 if (internal_load_libgamemode() < 0) {
256#ifdef GAMEMODE_AUTO
257 fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
258#endif
259 return -1;
260 }
261
262 /* Assert for static analyser that the function is not NULL */
263 assert(REAL_internal_gamemode_request_start != NULL);
264
265 if (REAL_internal_gamemode_request_start() < 0) {
266#ifdef GAMEMODE_AUTO
267 fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
268#endif
269 return -1;
270 }
271
272 return 0;
273}
274
275/* Redirect to the real libgamemode */
276#ifdef GAMEMODE_AUTO
277__attribute__((destructor))
278#else
279__attribute__((always_inline)) static inline
280#endif
281int gamemode_request_end(void)
282{
283 /* Need to load gamemode */
284 if (internal_load_libgamemode() < 0) {
285#ifdef GAMEMODE_AUTO
286 fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
287#endif
288 return -1;
289 }
290
291 /* Assert for static analyser that the function is not NULL */
292 assert(REAL_internal_gamemode_request_end != NULL);
293
294 if (REAL_internal_gamemode_request_end() < 0) {
295#ifdef GAMEMODE_AUTO
296 fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
297#endif
298 return -1;
299 }
300
301 return 0;
302}
303
304/* Redirect to the real libgamemode */
305__attribute__((always_inline)) static inline int gamemode_query_status(void)
306{
307 /* Need to load gamemode */
308 if (internal_load_libgamemode() < 0) {
309 return -1;
310 }
311
312 if (REAL_internal_gamemode_query_status == NULL) {
313 snprintf(internal_gamemode_client_error_string,
314 sizeof(internal_gamemode_client_error_string),
315 "gamemode_query_status missing (older host?)");
316 return -1;
317 }
318
319 return REAL_internal_gamemode_query_status();
320}
321
322/* Redirect to the real libgamemode */
323__attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid)
324{
325 /* Need to load gamemode */
326 if (internal_load_libgamemode() < 0) {
327 return -1;
328 }
329
330 if (REAL_internal_gamemode_request_start_for == NULL) {
331 snprintf(internal_gamemode_client_error_string,
332 sizeof(internal_gamemode_client_error_string),
333 "gamemode_request_start_for missing (older host?)");
334 return -1;
335 }
336
337 return REAL_internal_gamemode_request_start_for(pid);
338}
339
340/* Redirect to the real libgamemode */
341__attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid)
342{
343 /* Need to load gamemode */
344 if (internal_load_libgamemode() < 0) {
345 return -1;
346 }
347
348 if (REAL_internal_gamemode_request_end_for == NULL) {
349 snprintf(internal_gamemode_client_error_string,
350 sizeof(internal_gamemode_client_error_string),
351 "gamemode_request_end_for missing (older host?)");
352 return -1;
353 }
354
355 return REAL_internal_gamemode_request_end_for(pid);
356}
357
358/* Redirect to the real libgamemode */
359__attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid)
360{
361 /* Need to load gamemode */
362 if (internal_load_libgamemode() < 0) {
363 return -1;
364 }
365
366 if (REAL_internal_gamemode_query_status_for == NULL) {
367 snprintf(internal_gamemode_client_error_string,
368 sizeof(internal_gamemode_client_error_string),
369 "gamemode_query_status_for missing (older host?)");
370 return -1;
371 }
372
373 return REAL_internal_gamemode_query_status_for(pid);
374}
375
376#endif // CLIENT_GAMEMODE_H