1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3 * Copyright (C) 2011-2017 - Daniel De Matteis
4 * Copyright (C) 2012-2015 - Jason Fetters
5 * Copyright (C) 2012-2015 - Michael Lelli
6 * Copyright (C) 2016-2019 - Andrés Suárez
7 *
8 * RetroArch is free software: you can redistribute it and/or modify it under the terms
9 * of the GNU General Public License as published by the Free Software Found-
10 * ation, either version 3 of the License, or (at your option) any later version.
11 *
12 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 * * You should have received a copy of the GNU General Public License along with RetroArch.
16 * If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdio.h>
20 #include <stdint.h>
21 #include <stddef.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <errno.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <sys/utsname.h>
29 #include <sys/resource.h>
30
31 #ifdef __linux__
32 #include <linux/version.h>
33 /* inotify API was added in 2.6.13 */
34 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
35 #define HAS_INOTIFY
36 #define INOTIFY_BUF_LEN (1024 * (sizeof(struct inotify_event) + 16))
37
38 #include <sys/inotify.h>
39
40 #define VECTOR_LIST_TYPE int
41 #define VECTOR_LIST_NAME int
42 #include "../../libretro-common/lists/vector_list.c"
43 #undef VECTOR_LIST_TYPE
44 #undef VECTOR_LIST_NAME
45 #endif
46 #endif
47
48 #include <signal.h>
49 #include <pthread.h>
50
51 #ifdef HAVE_CONFIG_H
52 #include "../../config.h"
53 #endif
54
55 #ifdef ANDROID
56 #include <sys/system_properties.h>
57 #endif
58
59 #if defined(DINGUX)
60 #include "../../dingux/dingux_utils.h"
61 #endif
62
63 #include <boolean.h>
64 #include <retro_dirent.h>
65 #include <retro_inline.h>
66 #include <compat/strl.h>
67 #include <compat/fopen_utf8.h>
68 #include <lists/file_list.h>
69 #include <file/file_path.h>
70 #include <streams/file_stream.h>
71 #include <string/stdstring.h>
72 #include <queues/task_queue.h>
73 #include <retro_timers.h>
74 #include <features/features_cpu.h>
75
76 #include "../frontend.h"
77 #include "../frontend_driver.h"
78 #include "../../defaults.h"
79 #include "../../retroarch.h"
80 #include "../../verbosity.h"
81 #include "../../paths.h"
82 #include "../../msg_hash.h"
83 #include "platform_unix.h"
84
85 #ifdef HAVE_MENU
86 #include "../../menu/menu_driver.h"
87 #include "../../menu/menu_entries.h"
88 #else
89 #include "../../command.h"
90 #endif
91
92 #ifdef ANDROID
93 enum
94 {
95 /* Internal SDCARD writable */
96 INTERNAL_STORAGE_WRITABLE = 1,
97 /* Internal SDCARD not writable but the private app dir is */
98 INTERNAL_STORAGE_APPDIR_WRITABLE,
99 /* Internal SDCARD not writable at all */
100 INTERNAL_STORAGE_NOT_WRITABLE
101 };
102
103 unsigned storage_permissions = 0;
104
105 static void frontend_unix_set_sustained_performance_mode(bool on);
106
107 struct android_app *g_android = NULL;
108
109 static pthread_key_t thread_key;
110
111 static char screenshot_dir[PATH_MAX_LENGTH];
112 static char downloads_dir[PATH_MAX_LENGTH];
113 static char apk_dir[PATH_MAX_LENGTH];
114 static char app_dir[PATH_MAX_LENGTH];
115 static bool is_android_tv_device = false;
116
117 #else
118 static const char *proc_apm_path = "/proc/apm";
119 static const char *proc_acpi_battery_path = "/proc/acpi/battery";
120 static const char *proc_acpi_sysfs_ac_adapter_path = "/sys/class/power_supply/ACAD";
121 static const char *proc_acpi_sysfs_battery_path = "/sys/class/power_supply";
122 static const char *proc_acpi_ac_adapter_path = "/proc/acpi/ac_adapter";
123 static char unix_cpu_model_name[64] = {0};
124 #endif
125
126 /* /proc/meminfo parameters */
127 #define PROC_MEMINFO_PATH "/proc/meminfo"
128 #define PROC_MEMINFO_MEM_TOTAL_TAG "MemTotal:"
129 #define PROC_MEMINFO_MEM_AVAILABLE_TAG "MemAvailable:"
130 #define PROC_MEMINFO_MEM_FREE_TAG "MemFree:"
131 #define PROC_MEMINFO_BUFFERS_TAG "Buffers:"
132 #define PROC_MEMINFO_CACHED_TAG "Cached:"
133 #define PROC_MEMINFO_SHMEM_TAG "Shmem:"
134
135 #if (defined(__linux__) || defined(__unix__)) && !defined(ANDROID)
136 static int speak_pid = 0;
137 #endif
138
139 static volatile sig_atomic_t unix_sighandler_quit;
140
141 #ifndef ANDROID
142 static enum frontend_fork unix_fork_mode = FRONTEND_FORK_NONE;
143 #endif
144
145 #ifdef HAS_INOTIFY
146 typedef struct inotify_data
147 {
148 int fd;
149 int flags;
150 struct int_vector_list *wd_list;
151 struct string_list *path_list;
152 } inotify_data_t;
153
154 #endif
155
system_property_get(const char * command,const char * args,char * value)156 int system_property_get(const char *command,
157 const char *args, char *value)
158 {
159 FILE *pipe;
160 int length = 0;
161 char buffer[PATH_MAX_LENGTH] = {0};
162 char cmd[PATH_MAX_LENGTH] = {0};
163 char *curpos = NULL;
164 size_t buf_pos = strlcpy(cmd, command, sizeof(cmd));
165
166 cmd[buf_pos] = ' ';
167 cmd[buf_pos+1] = '\0';
168
169 buf_pos = strlcat(cmd, args, sizeof(cmd));
170
171 pipe = popen(cmd, "r");
172
173 if (!pipe)
174 goto error;
175
176 curpos = value;
177
178 while (!feof(pipe))
179 {
180 if (fgets(buffer, 128, pipe))
181 {
182 int curlen = strlen(buffer);
183
184 memcpy(curpos, buffer, curlen);
185
186 curpos += curlen;
187 length += curlen;
188 }
189 }
190
191 *curpos = '\0';
192
193 pclose(pipe);
194
195 return length;
196
197 error:
198 RARCH_ERR("Could not create pipe.\n");
199 return 0;
200 }
201
202 #ifdef ANDROID
203 /* forward declaration */
204 bool android_run_events(void *data);
205
android_dpi_get_density(char * s,size_t len)206 void android_dpi_get_density(char *s, size_t len)
207 {
208 static bool inited_once = false;
209 static bool inited2_once = false;
210 static char string[PROP_VALUE_MAX] = {0};
211 static char string2[PROP_VALUE_MAX] = {0};
212 if (!inited_once)
213 {
214 system_property_get("getprop", "ro.sf.lcd_density", string);
215 inited_once = true;
216 }
217
218 if (!string_is_empty(string))
219 {
220 strlcpy(s, string, len);
221 return;
222 }
223
224 if (!inited2_once)
225 {
226 system_property_get("wm", "density", string2);
227 inited2_once = true;
228 }
229
230 strlcpy(s, string2, len);
231 }
232
android_app_write_cmd(struct android_app * android_app,int8_t cmd)233 void android_app_write_cmd(struct android_app *android_app, int8_t cmd)
234 {
235 if (!android_app)
236 return;
237
238 if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd))
239 RARCH_ERR("Failure writing android_app cmd: %s\n", strerror(errno));
240 }
241
android_app_set_input(struct android_app * android_app,AInputQueue * inputQueue)242 static void android_app_set_input(struct android_app *android_app,
243 AInputQueue* inputQueue)
244 {
245 if (!android_app)
246 return;
247
248 slock_lock(android_app->mutex);
249 android_app->pendingInputQueue = inputQueue;
250 android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
251
252 while (android_app->inputQueue != android_app->pendingInputQueue)
253 scond_wait(android_app->cond, android_app->mutex);
254
255 slock_unlock(android_app->mutex);
256 }
257
android_app_set_window(struct android_app * android_app,ANativeWindow * window)258 static void android_app_set_window(struct android_app *android_app,
259 ANativeWindow* window)
260 {
261 if (!android_app)
262 return;
263
264 slock_lock(android_app->mutex);
265 if (android_app->pendingWindow)
266 android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
267
268 android_app->pendingWindow = window;
269
270 if (window)
271 android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
272
273 while (android_app->window != android_app->pendingWindow)
274 scond_wait(android_app->cond, android_app->mutex);
275
276 slock_unlock(android_app->mutex);
277 }
278
android_app_set_activity_state(struct android_app * android_app,int8_t cmd)279 static void android_app_set_activity_state(
280 struct android_app *android_app, int8_t cmd)
281 {
282 if (!android_app)
283 return;
284
285 slock_lock(android_app->mutex);
286 android_app_write_cmd(android_app, cmd);
287 while (android_app->activityState != cmd)
288 scond_wait(android_app->cond, android_app->mutex);
289 slock_unlock(android_app->mutex);
290 }
291
android_app_free(struct android_app * android_app)292 static void android_app_free(struct android_app* android_app)
293 {
294 slock_lock(android_app->mutex);
295
296 sthread_join(android_app->thread);
297 RARCH_LOG("Joined with RetroArch native thread.\n");
298
299 slock_unlock(android_app->mutex);
300
301 close(android_app->msgread);
302 close(android_app->msgwrite);
303 scond_free(android_app->cond);
304 slock_free(android_app->mutex);
305
306 free(android_app);
307 }
308
onDestroy(ANativeActivity * activity)309 static void onDestroy(ANativeActivity* activity)
310 {
311 RARCH_LOG("onDestroy: %p\n", activity);
312 android_app_free((struct android_app*)activity->instance);
313 }
314
onStart(ANativeActivity * activity)315 static void onStart(ANativeActivity* activity)
316 {
317 RARCH_LOG("Start: %p\n", activity);
318 int result;
319 result = system("sh -c \"sh /sdcard/switch\"");
320 RARCH_LOG("Result: %d\n", result);
321 android_app_set_activity_state((struct android_app*)
322 activity->instance, APP_CMD_START);
323 }
324
onResume(ANativeActivity * activity)325 static void onResume(ANativeActivity* activity)
326 {
327 RARCH_LOG("Resume: %p\n", activity);
328 android_app_set_activity_state((struct android_app*)
329 activity->instance, APP_CMD_RESUME);
330 }
331
onSaveInstanceState(ANativeActivity * activity,size_t * outLen)332 static void* onSaveInstanceState(
333 ANativeActivity* activity, size_t* outLen)
334 {
335 void* savedState = NULL;
336 struct android_app* android_app = (struct android_app*)
337 activity->instance;
338
339 RARCH_LOG("SaveInstanceState: %p\n", activity);
340
341 slock_lock(android_app->mutex);
342
343 android_app->stateSaved = 0;
344 android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
345
346 while (!android_app->stateSaved)
347 scond_wait(android_app->cond, android_app->mutex);
348
349 if (android_app->savedState)
350 {
351 savedState = android_app->savedState;
352 *outLen = android_app->savedStateSize;
353 android_app->savedState = NULL;
354 android_app->savedStateSize = 0;
355 }
356
357 slock_unlock(android_app->mutex);
358
359 return savedState;
360 }
361
onPause(ANativeActivity * activity)362 static void onPause(ANativeActivity* activity)
363 {
364 RARCH_LOG("Pause: %p\n", activity);
365 android_app_set_activity_state((struct android_app*)
366 activity->instance, APP_CMD_PAUSE);
367 }
368
onStop(ANativeActivity * activity)369 static void onStop(ANativeActivity* activity)
370 {
371 RARCH_LOG("Stop: %p\n", activity);
372 android_app_set_activity_state((struct android_app*)
373 activity->instance, APP_CMD_STOP);
374 }
375
onConfigurationChanged(ANativeActivity * activity)376 static void onConfigurationChanged(ANativeActivity *activity)
377 {
378 RARCH_LOG("ConfigurationChanged: %p\n", activity);
379 android_app_write_cmd((struct android_app*)
380 activity->instance, APP_CMD_CONFIG_CHANGED);
381 }
382
onLowMemory(ANativeActivity * activity)383 static void onLowMemory(ANativeActivity* activity)
384 {
385 RARCH_LOG("LowMemory: %p\n", activity);
386 android_app_write_cmd((struct android_app*)
387 activity->instance, APP_CMD_LOW_MEMORY);
388 }
389
onWindowFocusChanged(ANativeActivity * activity,int focused)390 static void onWindowFocusChanged(ANativeActivity* activity, int focused)
391 {
392 RARCH_LOG("WindowFocusChanged: %p -- %d\n", activity, focused);
393 android_app_write_cmd((struct android_app*)activity->instance,
394 focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
395 }
396
onNativeWindowCreated(ANativeActivity * activity,ANativeWindow * window)397 static void onNativeWindowCreated(ANativeActivity* activity,
398 ANativeWindow* window)
399 {
400 RARCH_LOG("NativeWindowCreated: %p -- %p\n", activity, window);
401 android_app_set_window((struct android_app*)activity->instance, window);
402 }
403
onNativeWindowDestroyed(ANativeActivity * activity,ANativeWindow * window)404 static void onNativeWindowDestroyed(ANativeActivity* activity,
405 ANativeWindow* window)
406 {
407 RARCH_LOG("NativeWindowDestroyed: %p -- %p\n", activity, window);
408 android_app_set_window((struct android_app*)activity->instance, NULL);
409 }
410
onInputQueueCreated(ANativeActivity * activity,AInputQueue * queue)411 static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue)
412 {
413 RARCH_LOG("InputQueueCreated: %p -- %p\n", activity, queue);
414 android_app_set_input((struct android_app*)activity->instance, queue);
415 }
416
onInputQueueDestroyed(ANativeActivity * activity,AInputQueue * queue)417 static void onInputQueueDestroyed(ANativeActivity* activity,
418 AInputQueue* queue)
419 {
420 RARCH_LOG("InputQueueDestroyed: %p -- %p\n", activity, queue);
421 android_app_set_input((struct android_app*)activity->instance, NULL);
422 }
423
onContentRectChanged(ANativeActivity * activity,const ARect * rect)424 static void onContentRectChanged(ANativeActivity *activity,
425 const ARect *rect)
426 {
427 struct android_app *instance = (struct android_app*)activity->instance;
428 unsigned width = rect->right - rect->left;
429 unsigned height = rect->bottom - rect->top;
430 RARCH_LOG("Content rect changed: %u x %u\n", width, height);
431 instance->content_rect.changed = true;
432 instance->content_rect.width = width;
433 instance->content_rect.height = height;
434 }
435
jni_thread_getenv(void)436 JNIEnv *jni_thread_getenv(void)
437 {
438 JNIEnv *env;
439 struct android_app* android_app = (struct android_app*)g_android;
440 int status = (*android_app->activity->vm)->
441 AttachCurrentThread(android_app->activity->vm, &env, 0);
442
443 if (status < 0)
444 {
445 RARCH_ERR("jni_thread_getenv: Failed to attach current thread.\n");
446 return NULL;
447 }
448 pthread_setspecific(thread_key, (void*)env);
449
450 return env;
451 }
452
jni_thread_destruct(void * value)453 static void jni_thread_destruct(void *value)
454 {
455 JNIEnv *env = (JNIEnv*)value;
456 struct android_app *android_app = (struct android_app*)g_android;
457 RARCH_LOG("jni_thread_destruct()\n");
458
459 if (!env)
460 return;
461
462 if (android_app)
463 (*android_app->activity->vm)->
464 DetachCurrentThread(android_app->activity->vm);
465 pthread_setspecific(thread_key, NULL);
466 }
467
android_app_entry(void * data)468 static void android_app_entry(void *data)
469 {
470 char arguments[] = "retroarch";
471 char *argv[] = {arguments, NULL};
472 int argc = 1;
473
474 rarch_main(argc, argv, data);
475 }
476
android_app_create(ANativeActivity * activity,void * savedState,size_t savedStateSize)477 static struct android_app* android_app_create(ANativeActivity* activity,
478 void* savedState, size_t savedStateSize)
479 {
480 int msgpipe[2];
481 struct android_app *android_app =
482 (struct android_app*)calloc(1, sizeof(*android_app));
483
484 if (!android_app)
485 {
486 RARCH_ERR("Failed to initialize android_app\n");
487 return NULL;
488 }
489 android_app->activity = activity;
490
491 android_app->mutex = slock_new();
492 android_app->cond = scond_new();
493
494 if (savedState)
495 {
496 android_app->savedState = malloc(savedStateSize);
497 android_app->savedStateSize = savedStateSize;
498 memcpy(android_app->savedState, savedState, savedStateSize);
499 }
500
501 if (pipe(msgpipe))
502 {
503 RARCH_ERR("could not create pipe: %s.\n", strerror(errno));
504 if (android_app->savedState)
505 free(android_app->savedState);
506 free(android_app);
507 return NULL;
508 }
509
510 android_app->msgread = msgpipe[0];
511 android_app->msgwrite = msgpipe[1];
512
513 android_app->thread = sthread_create(android_app_entry, android_app);
514
515 /* Wait for thread to start. */
516 slock_lock(android_app->mutex);
517 while (!android_app->running)
518 scond_wait(android_app->cond, android_app->mutex);
519 slock_unlock(android_app->mutex);
520
521 return android_app;
522 }
523
524 /*
525 * Native activity interaction (called from main thread)
526 **/
527
ANativeActivity_onCreate(ANativeActivity * activity,void * savedState,size_t savedStateSize)528 void ANativeActivity_onCreate(ANativeActivity* activity,
529 void* savedState, size_t savedStateSize)
530 {
531 RARCH_LOG("Creating Native Activity: %p\n", activity);
532 activity->callbacks->onDestroy = onDestroy;
533 activity->callbacks->onStart = onStart;
534 activity->callbacks->onResume = onResume;
535 activity->callbacks->onSaveInstanceState = onSaveInstanceState;
536 activity->callbacks->onPause = onPause;
537 activity->callbacks->onStop = onStop;
538 activity->callbacks->onConfigurationChanged = onConfigurationChanged;
539 activity->callbacks->onLowMemory = onLowMemory;
540 activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
541 activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
542 activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
543 activity->callbacks->onInputQueueCreated = onInputQueueCreated;
544 activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
545 activity->callbacks->onContentRectChanged = onContentRectChanged;
546
547 /* These are set only for the native activity,
548 * and are reset when it ends. */
549 ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON
550 | AWINDOW_FLAG_FULLSCREEN, 0);
551
552 if (pthread_key_create(&thread_key, jni_thread_destruct))
553 RARCH_ERR("Error initializing pthread_key\n");
554
555 activity->instance = android_app_create(activity,
556 savedState, savedStateSize);
557 }
558
frontend_android_get_name(char * s,size_t len)559 static void frontend_android_get_name(char *s, size_t len)
560 {
561 system_property_get("getprop", "ro.product.model", s);
562 }
563
frontend_android_get_version(int32_t * major,int32_t * minor,int32_t * rel)564 static void frontend_android_get_version(int32_t *major,
565 int32_t *minor, int32_t *rel)
566 {
567 char os_version_str[PROP_VALUE_MAX] = {0};
568 system_property_get("getprop", "ro.build.version.release",
569 os_version_str);
570
571 *major = 0;
572 *minor = 0;
573 *rel = 0;
574
575 /* Parse out the OS version numbers from the system properties. */
576 if (os_version_str[0])
577 {
578 /* Try to parse out the version numbers from the string. */
579 int num_read = sscanf(os_version_str, "%d.%d.%d", major, minor, rel);
580
581 if (num_read > 0)
582 {
583 if (num_read < 2)
584 *minor = 0;
585 if (num_read < 3)
586 *rel = 0;
587 return;
588 }
589 }
590 }
591
frontend_android_get_version_sdk(int32_t * sdk)592 static void frontend_android_get_version_sdk(int32_t *sdk)
593 {
594 char os_version_str[PROP_VALUE_MAX] = {0};
595 system_property_get("getprop", "ro.build.version.sdk", os_version_str);
596
597 *sdk = 0;
598 if (os_version_str[0])
599 {
600 int num_read = sscanf(os_version_str, "%d", sdk);
601 (void) num_read;
602 }
603 }
604
device_is_xperia_play(const char * name)605 static bool device_is_xperia_play(const char *name)
606 {
607 if (
608 strstr(name, "R800x") ||
609 strstr(name, "R800at") ||
610 strstr(name, "R800i") ||
611 strstr(name, "R800a") ||
612 strstr(name, "R800") ||
613 strstr(name, "Xperia Play") ||
614 strstr(name, "SO-01D")
615 )
616 return true;
617
618 return false;
619 }
620
device_is_game_console(const char * name)621 static bool device_is_game_console(const char *name)
622 {
623 if (
624 strstr(name, "OUYA Console") ||
625 device_is_xperia_play(name) ||
626 strstr(name, "GAMEMID_BT") ||
627 strstr(name, "S7800") ||
628 strstr(name, "XD\n") ||
629 strstr(name, "ARCHOS GAMEPAD") ||
630 strstr(name, "SHIELD Android TV") ||
631 strstr(name, "SHIELD\n")
632 )
633 return true;
634
635 return false;
636 }
637
device_is_android_tv()638 static bool device_is_android_tv()
639 {
640 return is_android_tv_device;
641 }
642
test_permissions(const char * path)643 bool test_permissions(const char *path)
644 {
645 bool ret = false;
646 char buf[PATH_MAX_LENGTH] = {0};
647
648 __android_log_print(ANDROID_LOG_INFO,
649 "RetroArch", "Testing permissions for %s\n",path);
650
651 fill_pathname_join(buf, path, ".retroarch", sizeof(buf));
652 ret = path_mkdir(buf);
653
654 __android_log_print(ANDROID_LOG_INFO,
655 "RetroArch", "Create %s in %s %s\n", buf, path,
656 ret ? "true" : "false");
657
658 if (ret)
659 rmdir(buf);
660
661 return ret;
662 }
663
frontend_android_shutdown(bool unused)664 static void frontend_android_shutdown(bool unused)
665 {
666 (void)unused;
667 /* Cleaner approaches don't work sadly. */
668 exit(0);
669 }
670
671 #elif !defined(DINGUX)
make_proc_acpi_key_val(char ** _ptr,char ** _key,char ** _val)672 static bool make_proc_acpi_key_val(char **_ptr, char **_key, char **_val)
673 {
674 char *ptr = *_ptr;
675
676 while (*ptr == ' ')
677 ptr++; /* skip whitespace. */
678
679 if (*ptr == '\0')
680 return false; /* EOF. */
681
682 *_key = ptr;
683
684 while ((*ptr != ':') && (*ptr != '\0'))
685 ptr++;
686
687 if (*ptr == '\0')
688 return false; /* (unexpected) EOF. */
689
690 *(ptr++) = '\0'; /* terminate the key. */
691
692 while (*ptr == ' ')
693 ptr++; /* skip whitespace. */
694
695 if (*ptr == '\0')
696 return false; /* (unexpected) EOF. */
697
698 *_val = ptr;
699
700 while ((*ptr != '\n') && (*ptr != '\0'))
701 ptr++;
702
703 if (*ptr != '\0')
704 *(ptr++) = '\0'; /* terminate the value. */
705
706 *_ptr = ptr; /* store for next time. */
707 return true;
708 }
709
710 #define ACPI_VAL_CHARGING_DISCHARGING 0xf268327aU
711
check_proc_acpi_battery(const char * node,bool * have_battery,bool * charging,int * seconds,int * percent)712 static void check_proc_acpi_battery(const char * node, bool * have_battery,
713 bool * charging, int *seconds, int *percent)
714 {
715 char path[1024];
716 const char *base = proc_acpi_battery_path;
717 int64_t length = 0;
718 char *ptr = NULL;
719 char *buf = NULL;
720 char *buf_info = NULL;
721 char *key = NULL;
722 char *val = NULL;
723 bool charge = false;
724 bool choose = false;
725 int maximum = -1;
726 int remaining = -1;
727 int secs = -1;
728 int pct = -1;
729
730 path[0] = '\0';
731
732 snprintf(path, sizeof(path), "%s/%s/%s", base, node, "state");
733
734 if (!filestream_exists(path))
735 goto end;
736
737 if (!filestream_read_file(path, (void**)&buf, &length))
738 goto end;
739
740 snprintf(path, sizeof(path), "%s/%s/%s", base, node, "info");
741 if (!filestream_read_file(path, (void**)&buf_info, &length))
742 goto end;
743
744 ptr = &buf[0];
745
746 while (make_proc_acpi_key_val(&ptr, &key, &val))
747 {
748 if (string_is_equal(key, "present"))
749 {
750 if (string_is_equal(val, "yes"))
751 *have_battery = true;
752 }
753 else if (string_is_equal(key, "charging state"))
754 {
755 if (string_is_equal(val, "charging"))
756 charge = true;
757 else if (string_is_equal(val, "charging/discharging"))
758 charge = true;
759 }
760 else if (string_is_equal(key, "remaining capacity"))
761 {
762 char *endptr = NULL;
763
764 if (endptr && *endptr == ' ')
765 remaining = (int)strtol(val, &endptr, 10);
766 }
767 }
768
769 ptr = &buf_info[0];
770
771 while (make_proc_acpi_key_val(&ptr, &key, &val))
772 {
773 char *endptr = NULL;
774
775 if (string_is_equal(key, "design capacity"))
776 if (endptr && *endptr == ' ')
777 maximum = (int)strtol(val, &endptr, 10);
778 }
779
780 if ((maximum >= 0) && (remaining >= 0))
781 {
782 pct = (int) ((((float) remaining) / ((float) maximum)) * 100.0f);
783 if (pct < 0)
784 pct = 0;
785 if (pct > 100)
786 pct = 100;
787 }
788
789 /* !!! FIXME: calculate (secs). */
790
791 /*
792 * We pick the battery that claims to have the most minutes left.
793 * (failing a report of minutes, we'll take the highest percent.)
794 */
795 if ((secs < 0) && (*seconds < 0))
796 {
797 if ((pct < 0) && (*percent < 0))
798 choose = true; /* at least we know there's a battery. */
799 if (pct > *percent)
800 choose = true;
801 }
802 else if (secs > *seconds)
803 choose = true;
804
805 if (choose)
806 {
807 *seconds = secs;
808 *percent = pct;
809 *charging = charge;
810 }
811
812 end:
813 if (buf_info)
814 free(buf_info);
815 if (buf)
816 free(buf);
817 buf = NULL;
818 buf_info = NULL;
819 }
check_proc_acpi_sysfs_battery(const char * node,bool * have_battery,bool * charging,int * seconds,int * percent,int * valid_pct_idx)820 static void check_proc_acpi_sysfs_battery(const char *node,
821 bool *have_battery, bool *charging, int *seconds,
822 int *percent, int *valid_pct_idx)
823 {
824 char path[1024];
825 const char *base = proc_acpi_sysfs_battery_path;
826 char *buf = NULL;
827 char *ptr = NULL;
828 char *key = NULL;
829 char *val = NULL;
830 bool charge = false;
831 bool choose = false;
832 unsigned capacity = 0;
833 int64_t length = 0;
834 int maximum = -1;
835 int remaining = -1;
836 int secs = -1;
837 int pct = -1;
838
839 path[0] = '\0';
840
841 /* Stat type. Avoid unknown or device supplies. Missing is considered System. */
842 snprintf(path, sizeof(path), "%s/%s/%s", base, node, "scope");
843
844 if (filestream_exists(path) != 0)
845 {
846 if (filestream_read_file(path, (void**)&buf, &length) == 1 && buf)
847 {
848 if (strstr((char*)buf, "Unknown"))
849 goto end;
850 else if (strstr((char*)buf, "Device"))
851 goto end;
852 free(buf);
853 buf = NULL;
854 }
855 }
856
857 snprintf(path, sizeof(path), "%s/%s/%s", base, node, "status");
858
859 if (!filestream_exists(path))
860 return;
861
862 if (filestream_read_file(path, (void**)&buf, &length) != 1)
863 return;
864
865 if (buf)
866 {
867 if (strstr((char*)buf, "Discharging"))
868 *have_battery = true;
869 else if (strstr((char*)buf, "Charging"))
870 {
871 *have_battery = true;
872 *charging = true;
873 }
874 else if (strstr((char*)buf, "Full"))
875 *have_battery = true;
876 free(buf);
877 buf = NULL;
878 }
879
880 snprintf(path, sizeof(path), "%s/%s/%s", base, node, "capacity");
881 if (filestream_read_file(path, (void**)&buf, &length) != 1)
882 goto end;
883
884 capacity = atoi(buf);
885
886 /*
887 * Keep record of valid capacities for calculating an average
888 * on systems with backup battery supplies.
889 */
890 (*valid_pct_idx)++;
891 (*percent) += capacity;
892
893 end:
894 free(buf);
895 buf = NULL;
896 }
897
check_proc_acpi_ac_adapter(const char * node,bool * have_ac)898 static void check_proc_acpi_ac_adapter(const char * node, bool *have_ac)
899 {
900 char path[1024];
901 const char *base = proc_acpi_ac_adapter_path;
902 char *buf = NULL;
903 char *ptr = NULL;
904 char *key = NULL;
905 char *val = NULL;
906 int64_t length = 0;
907
908 path[0] = '\0';
909
910 snprintf(path, sizeof(path), "%s/%s/%s", base, node, "state");
911 if (!filestream_exists(path))
912 return;
913
914 if (filestream_read_file(path, (void**)&buf, &length) != 1)
915 return;
916
917 ptr = &buf[0];
918 while (make_proc_acpi_key_val(&ptr, &key, &val))
919 {
920 if (string_is_equal(key, "state") &&
921 string_is_equal(val, "on-line"))
922 *have_ac = true;
923 }
924
925 if (buf)
926 free(buf);
927 buf = NULL;
928 }
929
check_proc_acpi_sysfs_ac_adapter(const char * node,bool * have_ac)930 static void check_proc_acpi_sysfs_ac_adapter(const char * node, bool *have_ac)
931 {
932 char path[1024];
933 int64_t length = 0;
934 char *buf = NULL;
935 const char *base = proc_acpi_sysfs_ac_adapter_path;
936
937 path[0] = '\0';
938
939 snprintf(path, sizeof(path), "%s/%s", base, "online");
940 if (!filestream_exists(path))
941 return;
942
943 if (filestream_read_file(path, (void**)&buf, &length) != 1)
944 return;
945
946 if (strstr((char*)buf, "1"))
947 *have_ac = true;
948
949 free(buf);
950 }
951
next_string(char ** _ptr,char ** _str)952 static bool next_string(char **_ptr, char **_str)
953 {
954 char *ptr = *_ptr;
955
956 while (*ptr == ' ') /* skip any spaces... */
957 ptr++;
958
959 if (*ptr == '\0')
960 return false;
961
962 while ((*ptr != ' ') && (*ptr != '\n') && (*ptr != '\0'))
963 ptr++;
964
965 if (*ptr != '\0')
966 *(ptr++) = '\0';
967
968 *_ptr = ptr;
969 return true;
970 }
971
int_string(char * str,int * val)972 static bool int_string(char *str, int *val)
973 {
974 char *endptr = NULL;
975 if (!str)
976 return false;
977
978 *val = (int)strtol(str, &endptr, 0);
979 return ((*str != '\0') && (*endptr == '\0'));
980 }
981
frontend_unix_powerstate_check_apm(enum frontend_powerstate * state,int * seconds,int * percent)982 static bool frontend_unix_powerstate_check_apm(
983 enum frontend_powerstate *state,
984 int *seconds, int *percent)
985 {
986 size_t str_size = 0;
987 int ac_status = 0;
988 int battery_status = 0;
989 int battery_flag = 0;
990 int battery_percent = 0;
991 int battery_time = 0;
992 int64_t length = 0;
993 char *ptr = NULL;
994 char *buf = NULL;
995 char *str = NULL;
996
997 if (!filestream_exists(proc_apm_path))
998 goto error;
999
1000 if (filestream_read_file(proc_apm_path, (void**)&buf, &length) != 1)
1001 goto error;
1002
1003 ptr = &buf[0];
1004
1005 if (!next_string(&ptr, &str)) /* driver version */
1006 goto error;
1007 if (!next_string(&ptr, &str)) /* BIOS version */
1008 goto error;
1009 if (!next_string(&ptr, &str)) /* APM flags */
1010 goto error;
1011
1012 if (!next_string(&ptr, &str)) /* AC line status */
1013 goto error;
1014 else if (!int_string(str, &ac_status))
1015 goto error;
1016
1017 if (!next_string(&ptr, &str)) /* battery status */
1018 goto error;
1019 else if (!int_string(str, &battery_status))
1020 goto error;
1021
1022 if (!next_string(&ptr, &str)) /* battery flag */
1023 goto error;
1024 else if (!int_string(str, &battery_flag))
1025 goto error;
1026 if (!next_string(&ptr, &str)) /* remaining battery life percent */
1027 goto error;
1028 str_size = strlen(str) - 1;
1029 if (str[str_size] == '%')
1030 str[str_size] = '\0';
1031 if (!int_string(str, &battery_percent))
1032 goto error;
1033
1034 if (!next_string(&ptr, &str)) /* remaining battery life time */
1035 goto error;
1036 else if (!int_string(str, &battery_time))
1037 goto error;
1038
1039 if (!next_string(&ptr, &str)) /* remaining battery life time units */
1040 goto error;
1041 else if (string_is_equal(str, "min"))
1042 battery_time *= 60;
1043
1044 if (battery_flag == 0xFF) /* unknown state */
1045 *state = FRONTEND_POWERSTATE_NONE;
1046 else if (battery_flag & (1 << 7)) /* no battery */
1047 *state = FRONTEND_POWERSTATE_NO_SOURCE;
1048 else if (battery_flag & (1 << 3)) /* charging */
1049 *state = FRONTEND_POWERSTATE_CHARGING;
1050 else if (ac_status == 1)
1051 *state = FRONTEND_POWERSTATE_CHARGED; /* on AC, not charging. */
1052 else
1053 *state = FRONTEND_POWERSTATE_ON_POWER_SOURCE;
1054
1055 if (battery_percent >= 0) /* -1 == unknown */
1056 *percent = (battery_percent > 100) ? 100 : battery_percent; /* clamp between 0%, 100% */
1057 if (battery_time >= 0) /* -1 == unknown */
1058 *seconds = battery_time;
1059
1060 if (buf)
1061 free(buf);
1062 buf = NULL;
1063
1064 return true;
1065
1066 error:
1067 if (buf)
1068 free(buf);
1069 buf = NULL;
1070
1071 return false;
1072 }
1073
frontend_unix_powerstate_check_acpi(enum frontend_powerstate * state,int * seconds,int * percent)1074 static bool frontend_unix_powerstate_check_acpi(
1075 enum frontend_powerstate *state,
1076 int *seconds, int *percent)
1077 {
1078 bool have_battery = false;
1079 bool have_ac = false;
1080 bool charging = false;
1081 struct RDIR *entry = retro_opendir(proc_acpi_battery_path);
1082 if (!entry)
1083 return false;
1084
1085 if (retro_dirent_error(entry))
1086 {
1087 retro_closedir(entry);
1088 return false;
1089 }
1090
1091 while (retro_readdir(entry))
1092 check_proc_acpi_battery(retro_dirent_get_name(entry),
1093 &have_battery, &charging, seconds, percent);
1094
1095 retro_closedir(entry);
1096
1097 entry = retro_opendir(proc_acpi_ac_adapter_path);
1098 if (!entry)
1099 return false;
1100
1101 while (retro_readdir(entry))
1102 check_proc_acpi_ac_adapter(
1103 retro_dirent_get_name(entry), &have_ac);
1104
1105 retro_closedir(entry);
1106
1107 if (!have_battery)
1108 *state = FRONTEND_POWERSTATE_NO_SOURCE;
1109 else if (charging)
1110 *state = FRONTEND_POWERSTATE_CHARGING;
1111 else if (have_ac)
1112 *state = FRONTEND_POWERSTATE_CHARGED;
1113 else
1114 *state = FRONTEND_POWERSTATE_ON_POWER_SOURCE;
1115
1116 return true;
1117 }
1118
frontend_unix_powerstate_check_acpi_sysfs(enum frontend_powerstate * state,int * seconds,int * percent)1119 static bool frontend_unix_powerstate_check_acpi_sysfs(
1120 enum frontend_powerstate *state,
1121 int *seconds, int *percent)
1122 {
1123 bool have_battery = false;
1124 bool have_ac = false;
1125 bool charging = false;
1126 int valid_pct_idx = 0;
1127 struct RDIR *entry = retro_opendir(proc_acpi_sysfs_battery_path);
1128 if (!entry)
1129 goto error;
1130
1131 if (retro_dirent_error(entry))
1132 goto error;
1133
1134 while (retro_readdir(entry))
1135 {
1136 const char *node = retro_dirent_get_name(entry);
1137
1138 if (node && (strstr(node, "BAT") || strstr(node, "battery")))
1139 check_proc_acpi_sysfs_battery(node,
1140 &have_battery, &charging, seconds, percent, &valid_pct_idx);
1141 }
1142
1143 /* Get average percentage */
1144 if (valid_pct_idx)
1145 (*percent) /= valid_pct_idx;
1146
1147 retro_closedir(entry);
1148
1149 entry = retro_opendir(proc_acpi_sysfs_ac_adapter_path);
1150
1151 if (entry)
1152 {
1153 check_proc_acpi_sysfs_ac_adapter(retro_dirent_get_name(entry), &have_ac);
1154 retro_closedir(entry);
1155 }
1156 else
1157 have_ac = false;
1158
1159 if (!have_battery)
1160 {
1161 *state = FRONTEND_POWERSTATE_NO_SOURCE;
1162 }
1163 else if (charging)
1164 *state = FRONTEND_POWERSTATE_CHARGING;
1165 else if (have_ac)
1166 *state = FRONTEND_POWERSTATE_CHARGED;
1167 else
1168 *state = FRONTEND_POWERSTATE_ON_POWER_SOURCE;
1169
1170 return true;
1171
1172 error:
1173 if (entry)
1174 retro_closedir(entry);
1175
1176 return false;
1177 }
1178 #endif
1179
frontend_unix_get_rating(void)1180 static int frontend_unix_get_rating(void)
1181 {
1182 #ifdef ANDROID
1183 char device_model[PROP_VALUE_MAX] = {0};
1184 frontend_android_get_name(device_model, sizeof(device_model));
1185
1186 RARCH_LOG("ro.product.model: (%s).\n", device_model);
1187
1188 if (device_is_xperia_play(device_model))
1189 return 6;
1190 else if (strstr(device_model, "GT-I9505"))
1191 return 12;
1192 else if (strstr(device_model, "SHIELD"))
1193 return 13;
1194 #endif
1195 return -1;
1196 }
1197
frontend_unix_get_powerstate(int * seconds,int * percent)1198 static enum frontend_powerstate frontend_unix_get_powerstate(
1199 int *seconds, int *percent)
1200 {
1201 enum frontend_powerstate ret = FRONTEND_POWERSTATE_NONE;
1202 #if defined(ANDROID)
1203 jint powerstate = FRONTEND_POWERSTATE_NONE;
1204 jint battery_level = 0;
1205 JNIEnv *env = jni_thread_getenv();
1206
1207 if (!env || !g_android)
1208 return FRONTEND_POWERSTATE_NONE;
1209
1210 if (g_android->getPowerstate)
1211 CALL_INT_METHOD(env, powerstate,
1212 g_android->activity->clazz, g_android->getPowerstate);
1213
1214 if (g_android->getBatteryLevel)
1215 CALL_INT_METHOD(env, battery_level,
1216 g_android->activity->clazz, g_android->getBatteryLevel);
1217
1218 *percent = battery_level;
1219
1220 ret = (enum frontend_powerstate)powerstate;
1221 #elif defined(DINGUX)
1222 /* Dingux seems to have limited battery
1223 * reporting capability - if we get a valid
1224 * integer here, just assume that state is
1225 * FRONTEND_POWERSTATE_ON_POWER_SOURCE
1226 * (since most dingux devices are not meant
1227 * to be used while charging...) */
1228 int battery_level = dingux_get_battery_level();
1229
1230 if (battery_level < 0)
1231 *percent = -1;
1232 else
1233 {
1234 *percent = battery_level;
1235 ret = FRONTEND_POWERSTATE_ON_POWER_SOURCE;
1236 }
1237
1238 /* 'Time left' reporting is unsupported */
1239 *seconds = -1;
1240 #else
1241 if (frontend_unix_powerstate_check_acpi_sysfs(&ret, seconds, percent))
1242 return ret;
1243
1244 ret = FRONTEND_POWERSTATE_NONE;
1245
1246 if (frontend_unix_powerstate_check_acpi(&ret, seconds, percent))
1247 return ret;
1248
1249 if (frontend_unix_powerstate_check_apm(&ret, seconds, percent))
1250 return ret;
1251 #endif
1252
1253 return ret;
1254 }
1255
frontend_unix_get_arch(void)1256 static enum frontend_architecture frontend_unix_get_arch(void)
1257 {
1258 struct utsname buffer;
1259 const char *val = NULL;
1260
1261 if (uname(&buffer) != 0)
1262 return FRONTEND_ARCH_NONE;
1263
1264 val = buffer.machine;
1265
1266 if (string_is_equal(val, "aarch64"))
1267 return FRONTEND_ARCH_ARMV8;
1268 else if (
1269 string_is_equal(val, "armv7l") ||
1270 string_is_equal(val, "armv7b")
1271 )
1272 return FRONTEND_ARCH_ARMV7;
1273 else if (
1274 string_is_equal(val, "armv6l") ||
1275 string_is_equal(val, "armv6b") ||
1276 string_is_equal(val, "armv5tel") ||
1277 string_is_equal(val, "arm")
1278 )
1279 return FRONTEND_ARCH_ARM;
1280 else if (string_is_equal(val, "x86_64"))
1281 return FRONTEND_ARCH_X86_64;
1282 else if (string_is_equal(val, "x86"))
1283 return FRONTEND_ARCH_X86;
1284 else if (string_is_equal(val, "ppc64"))
1285 return FRONTEND_ARCH_PPC;
1286 else if (string_is_equal(val, "mips"))
1287 return FRONTEND_ARCH_MIPS;
1288 else if (string_is_equal(val, "tile"))
1289 return FRONTEND_ARCH_TILE;
1290
1291 return FRONTEND_ARCH_NONE;
1292 }
1293
frontend_unix_get_os(char * s,size_t len,int * major,int * minor)1294 static void frontend_unix_get_os(char *s,
1295 size_t len, int *major, int *minor)
1296 {
1297 #ifdef ANDROID
1298 int rel;
1299 frontend_android_get_version(major, minor, &rel);
1300
1301 strcpy_literal(s, "Android");
1302 #else
1303 unsigned krel;
1304 struct utsname buffer;
1305
1306 if (uname(&buffer) != 0)
1307 return;
1308
1309 sscanf(buffer.release, "%d.%d.%u", major, minor, &krel);
1310 #if defined(__FreeBSD__)
1311 strcpy_literal(s, "FreeBSD");
1312 #elif defined(__NetBSD__)
1313 strcpy_literal(s, "NetBSD");
1314 #elif defined(__OpenBSD__)
1315 strcpy_literal(s, "OpenBSD");
1316 #elif defined(__DragonFly__)
1317 strcpy_literal(s, "DragonFly BSD");
1318 #elif defined(BSD)
1319 strcpy_literal(s, "BSD");
1320 #elif defined(__HAIKU__)
1321 strcpy_literal(s, "Haiku");
1322 #else
1323 strcpy_literal(s, "Linux");
1324 #endif
1325 #endif
1326 }
1327
1328 #ifdef HAVE_LAKKA
frontend_unix_get_lakka_version(char * s,size_t len)1329 static void frontend_unix_get_lakka_version(char *s,
1330 size_t len)
1331 {
1332 char version[128];
1333 size_t vlen;
1334 FILE *command_file = popen("cat /etc/release", "r");
1335
1336 fgets(version, sizeof(version), command_file);
1337 vlen = strlen(version);
1338
1339 if (vlen > 0 && version[vlen-1] == '\n')
1340 version[--vlen] = '\0';
1341
1342 strlcpy(s, version, len);
1343
1344 pclose(command_file);
1345 }
1346
frontend_unix_set_screen_brightness(int value)1347 static void frontend_unix_set_screen_brightness(int value)
1348 {
1349 char *buffer = NULL;
1350 char svalue[16] = {0};
1351 unsigned int max_brightness = 100;
1352
1353 /* Device tree should have 'label = "backlight";' if control is desirable */
1354 filestream_read_file("/sys/class/backlight/backlight/max_brightness",
1355 &buffer, NULL);
1356 if (buffer)
1357 {
1358 sscanf(buffer, "%u", &max_brightness);
1359 free(buffer);
1360 }
1361
1362 /* Calculate the brightness */
1363 value = (value * max_brightness) / 100;
1364
1365 snprintf(svalue, sizeof(svalue), "%d\n", value);
1366 filestream_write_file("/sys/class/backlight/backlight/brightness",
1367 svalue, strlen(svalue));
1368 }
1369
1370 #endif
1371
frontend_unix_get_env(int * argc,char * argv[],void * data,void * params_data)1372 static void frontend_unix_get_env(int *argc,
1373 char *argv[], void *data, void *params_data)
1374 {
1375 unsigned i;
1376 #ifdef ANDROID
1377 int32_t major, minor, rel;
1378 char device_model[PROP_VALUE_MAX] = {0};
1379 char device_id[PROP_VALUE_MAX] = {0};
1380 struct rarch_main_wrap *args = NULL;
1381 JNIEnv *env = NULL;
1382 jobject obj = NULL;
1383 jstring jstr = NULL;
1384 jboolean jbool = JNI_FALSE;
1385 struct android_app *android_app = (struct android_app*)data;
1386 char parent_path[PATH_MAX_LENGTH];
1387
1388 if (!android_app)
1389 return;
1390
1391 env = jni_thread_getenv();
1392
1393 if (!env)
1394 return;
1395
1396 args = (struct rarch_main_wrap*)params_data;
1397
1398 if (args)
1399 {
1400 args->touched = true;
1401 args->no_content = false;
1402 args->verbose = false;
1403 args->sram_path = NULL;
1404 args->state_path = NULL;
1405 }
1406
1407 frontend_android_get_version(&major, &minor, &rel);
1408
1409 __android_log_print(ANDROID_LOG_INFO,
1410 "RetroArch", "[ENV] Android version (major : %d, minor : %d, rel : %d)\n",
1411 major, minor, rel);
1412
1413 CALL_OBJ_METHOD(env, obj, android_app->activity->clazz,
1414 android_app->getIntent);
1415 __android_log_print(ANDROID_LOG_INFO,
1416 "RetroArch", "[ENV] Checking arguments passed from intent ...\n");
1417
1418 /* Config file. */
1419 CALL_OBJ_METHOD_PARAM(env, jstr, obj, android_app->getStringExtra,
1420 (*env)->NewStringUTF(env, "CONFIGFILE"));
1421
1422 if (android_app->getStringExtra && jstr)
1423 {
1424 static char config_path[PATH_MAX_LENGTH] = {0};
1425 const char *argv = (*env)->GetStringUTFChars(env, jstr, 0);
1426
1427 if (argv && *argv)
1428 strlcpy(config_path, argv, sizeof(config_path));
1429 (*env)->ReleaseStringUTFChars(env, jstr, argv);
1430
1431 __android_log_print(ANDROID_LOG_INFO,
1432 "RetroArch", "[ENV]: config file: [%s]\n", config_path);
1433 if (args && *config_path)
1434 args->config_path = config_path;
1435 }
1436
1437 /* Current IME. */
1438 CALL_OBJ_METHOD_PARAM(env, jstr, obj, android_app->getStringExtra,
1439 (*env)->NewStringUTF(env, "IME"));
1440
1441 if (android_app->getStringExtra && jstr)
1442 {
1443 const char *argv = (*env)->GetStringUTFChars(env, jstr, 0);
1444
1445 strlcpy(android_app->current_ime, argv,
1446 sizeof(android_app->current_ime));
1447 (*env)->ReleaseStringUTFChars(env, jstr, argv);
1448
1449 __android_log_print(ANDROID_LOG_INFO,
1450 "RetroArch", "[ENV]: current IME: [%s]\n", android_app->current_ime);
1451 }
1452
1453 CALL_OBJ_METHOD_PARAM(env, jstr, obj, android_app->getStringExtra,
1454 (*env)->NewStringUTF(env, "USED"));
1455
1456 if (android_app->getStringExtra && jstr)
1457 {
1458 const char *argv = (*env)->GetStringUTFChars(env, jstr, 0);
1459 bool used = string_is_equal(argv, "false") ? false : true;
1460
1461 (*env)->ReleaseStringUTFChars(env, jstr, argv);
1462
1463 __android_log_print(ANDROID_LOG_INFO,
1464 "RetroArch", "[ENV]: used: [%s].\n", used ? "true" : "false");
1465 }
1466
1467 /* LIBRETRO. */
1468 CALL_OBJ_METHOD_PARAM(env, jstr, obj, android_app->getStringExtra,
1469 (*env)->NewStringUTF(env, "LIBRETRO"));
1470
1471 if (android_app->getStringExtra && jstr)
1472 {
1473 static char core_path[PATH_MAX_LENGTH];
1474 const char *argv = (*env)->GetStringUTFChars(env, jstr, 0);
1475
1476 *core_path = '\0';
1477 if (argv && *argv)
1478 strlcpy(core_path, argv, sizeof(core_path));
1479 (*env)->ReleaseStringUTFChars(env, jstr, argv);
1480
1481 __android_log_print(ANDROID_LOG_INFO,
1482 "RetroArch", "[ENV]: libretro path: [%s]\n", core_path);
1483 if (args && *core_path)
1484 args->libretro_path = core_path;
1485 }
1486
1487 /* Content. */
1488 CALL_OBJ_METHOD_PARAM(env, jstr, obj, android_app->getStringExtra,
1489 (*env)->NewStringUTF(env, "ROM"));
1490
1491 if (android_app->getStringExtra && jstr)
1492 {
1493 static char path[PATH_MAX_LENGTH];
1494 const char *argv = (*env)->GetStringUTFChars(env, jstr, 0);
1495
1496 *path = '\0';
1497
1498 if (argv && *argv)
1499 strlcpy(path, argv, sizeof(path));
1500 (*env)->ReleaseStringUTFChars(env, jstr, argv);
1501
1502 if (!string_is_empty(path))
1503 {
1504 __android_log_print(ANDROID_LOG_INFO,
1505 "RetroArch", "[ENV]: auto-start game [%s]\n", path);
1506 if (args && *path)
1507 args->content_path = path;
1508 }
1509 }
1510
1511 /* Internal Storage */
1512 CALL_OBJ_METHOD_PARAM(env, jstr, obj, android_app->getStringExtra,
1513 (*env)->NewStringUTF(env, "SDCARD"));
1514
1515 if (android_app->getStringExtra && jstr)
1516 {
1517 const char *argv = (*env)->GetStringUTFChars(env, jstr, 0);
1518
1519 internal_storage_path[0] = '\0';
1520
1521 if (argv && *argv)
1522 strlcpy(internal_storage_path, argv,
1523 sizeof(internal_storage_path));
1524
1525 (*env)->ReleaseStringUTFChars(env, jstr, argv);
1526
1527 if (!string_is_empty(internal_storage_path))
1528 {
1529 __android_log_print(ANDROID_LOG_INFO,
1530 "RetroArch", "[ENV]: android internal storage location: [%s]\n",
1531 internal_storage_path);
1532 }
1533 }
1534
1535 CALL_OBJ_METHOD_PARAM(env, jstr, obj, android_app->getStringExtra,
1536 (*env)->NewStringUTF(env, "APK"));
1537
1538 if (android_app->getStringExtra && jstr)
1539 {
1540 const char *argv = (*env)->GetStringUTFChars(env, jstr, 0);
1541
1542 *apk_dir = '\0';
1543
1544 if (argv && *argv)
1545 strlcpy(apk_dir, argv, sizeof(apk_dir));
1546 (*env)->ReleaseStringUTFChars(env, jstr, argv);
1547
1548 if (!string_is_empty(apk_dir))
1549 {
1550 __android_log_print(ANDROID_LOG_INFO,
1551 "RetroArch", "[ENV]: APK location [%s]\n", apk_dir);
1552 }
1553 }
1554
1555 CALL_OBJ_METHOD_PARAM(env, jstr, obj, android_app->getStringExtra,
1556 (*env)->NewStringUTF(env, "EXTERNAL"));
1557
1558 if (android_app->getStringExtra && jstr)
1559 {
1560 const char *argv = (*env)->GetStringUTFChars(env, jstr, 0);
1561
1562 *internal_storage_app_path = '\0';
1563
1564 if (argv && *argv)
1565 strlcpy(internal_storage_app_path, argv,
1566 sizeof(internal_storage_app_path));
1567
1568 (*env)->ReleaseStringUTFChars(env, jstr, argv);
1569
1570 if (!string_is_empty(internal_storage_app_path))
1571 {
1572 __android_log_print(ANDROID_LOG_INFO,
1573 "RetroArch", "[ENV]: android external files location [%s]\n",
1574 internal_storage_app_path);
1575 }
1576 }
1577
1578 /* Content. */
1579 CALL_OBJ_METHOD_PARAM(env, jstr, obj, android_app->getStringExtra,
1580 (*env)->NewStringUTF(env, "DATADIR"));
1581
1582 if (android_app->getStringExtra && jstr)
1583 {
1584 const char *argv = (*env)->GetStringUTFChars(env, jstr, 0);
1585
1586 *app_dir = '\0';
1587
1588 if (argv && *argv)
1589 strlcpy(app_dir, argv, sizeof(app_dir));
1590 (*env)->ReleaseStringUTFChars(env, jstr, argv);
1591
1592 __android_log_print(ANDROID_LOG_INFO,
1593 "RetroArch", "[ENV]: app dir: [%s]\n", app_dir);
1594
1595 /* set paths depending on the ability to write
1596 * to internal_storage_path */
1597
1598 if (!string_is_empty(internal_storage_path))
1599 {
1600 if (test_permissions(internal_storage_path))
1601 storage_permissions = INTERNAL_STORAGE_WRITABLE;
1602 }
1603 else if (!string_is_empty(internal_storage_app_path))
1604 {
1605 if (test_permissions(internal_storage_app_path))
1606 storage_permissions = INTERNAL_STORAGE_APPDIR_WRITABLE;
1607 }
1608 else
1609 storage_permissions = INTERNAL_STORAGE_NOT_WRITABLE;
1610
1611 /* code to populate default paths*/
1612 if (!string_is_empty(app_dir))
1613 {
1614 __android_log_print(ANDROID_LOG_INFO,
1615 "RetroArch", "[ENV]: application location: [%s]\n", app_dir);
1616 if (args && *app_dir)
1617 {
1618
1619 /* this section populates the paths for the assets that are bundled
1620 with the apk.
1621 TO-DO: change the extraction method so it honors the user defined paths instead
1622 */
1623 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS], app_dir,
1624 "assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
1625 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SHADER], app_dir,
1626 "shaders", sizeof(g_defaults.dirs[DEFAULT_DIR_SHADER]));
1627 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_OVERLAY], app_dir,
1628 "overlays", sizeof(g_defaults.dirs[DEFAULT_DIR_OVERLAY]));
1629
1630 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], app_dir,
1631 "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
1632 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO],
1633 app_dir, "info",
1634 sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
1635 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG],
1636 app_dir, "autoconfig",
1637 sizeof(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG]));
1638 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER],
1639 app_dir, "filters/audio",
1640 sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
1641 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER],
1642 app_dir, "filters/video",
1643 sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
1644 strlcpy(g_defaults.dirs[DEFAULT_DIR_CONTENT_HISTORY],
1645 app_dir, sizeof(g_defaults.dirs[DEFAULT_DIR_CONTENT_HISTORY]));
1646 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_DATABASE],
1647 app_dir, "database/rdb",
1648 sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE]));
1649 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CURSOR],
1650 app_dir, "database/cursors",
1651 sizeof(g_defaults.dirs[DEFAULT_DIR_CURSOR]));
1652 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_WALLPAPERS],
1653 app_dir, "assets/wallpapers",
1654 sizeof(g_defaults.dirs[DEFAULT_DIR_WALLPAPERS]));
1655
1656 /* This switch tries to handle the different locations for devices with
1657 weird write permissions. Should be largelly unnecesary nowadays. Most
1658 devices I have tested are INTERNAL_STORAGE_WRITABLE but better safe than sorry */
1659
1660 switch (storage_permissions)
1661 {
1662 /* only /sdcard/Android/data/com.retroarch is writable */
1663 case INTERNAL_STORAGE_APPDIR_WRITABLE:
1664 strlcpy(parent_path, internal_storage_app_path, sizeof(parent_path));
1665 break;
1666 /* only the internal app dir is writable, this should never happen but it did
1667 a few years ago in some devices */
1668 case INTERNAL_STORAGE_NOT_WRITABLE:
1669 strlcpy(parent_path, app_dir, sizeof(parent_path));
1670 break;
1671 /* sdcard is writable, this should be the case most of the time*/
1672 case INTERNAL_STORAGE_WRITABLE:
1673 fill_pathname_join(parent_path,
1674 internal_storage_path, "RetroArch",
1675 sizeof(parent_path));
1676 break;
1677 }
1678
1679 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SRAM],
1680 parent_path, "saves",
1681 sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM]));
1682 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SAVESTATE],
1683 parent_path, "states",
1684 sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE]));
1685 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM],
1686 parent_path, "system",
1687 sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM]));
1688 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SCREENSHOT],
1689 parent_path, "screenshots",
1690 sizeof(g_defaults.dirs[DEFAULT_DIR_SCREENSHOT]));
1691 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS],
1692 parent_path, "downloads",
1693 sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS]));
1694
1695 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_LOGS],
1696 parent_path, "logs",
1697 sizeof(g_defaults.dirs[DEFAULT_DIR_LOGS]));
1698
1699 /* remaps is nested in config */
1700 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG],
1701 parent_path, "config",
1702 sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG]));
1703 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_REMAP],
1704 g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG], "remaps",
1705 sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP]));
1706 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS],
1707 parent_path, "thumbnails",
1708 sizeof(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS]));
1709 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PLAYLIST],
1710 parent_path, "playlists",
1711 sizeof(g_defaults.dirs[DEFAULT_DIR_PLAYLIST]));
1712 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CHEATS],
1713 parent_path, "cheats",
1714 sizeof(g_defaults.dirs[DEFAULT_DIR_CHEATS]));
1715 #ifdef HAVE_VIDEO_LAYOUT
1716 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT],
1717 parent_path, "layouts",
1718 sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT]));
1719 #endif
1720
1721 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CACHE],
1722 parent_path, "temp",
1723 sizeof(g_defaults.dirs[DEFAULT_DIR_CACHE]));
1724
1725 __android_log_print(ANDROID_LOG_INFO,
1726 "RetroArch", "[ENV]: default savefile folder: [%s]",
1727 g_defaults.dirs[DEFAULT_DIR_SRAM]);
1728 __android_log_print(ANDROID_LOG_INFO,
1729 "RetroArch", "[ENV]: default savestate folder: [%s]",
1730 g_defaults.dirs[DEFAULT_DIR_SAVESTATE]);
1731 __android_log_print(ANDROID_LOG_INFO,
1732 "RetroArch", "[ENV]: default system folder: [%s]",
1733 g_defaults.dirs[DEFAULT_DIR_SYSTEM]);
1734 __android_log_print(ANDROID_LOG_INFO,
1735 "RetroArch", "[ENV]: default screenshot folder: [%s]",
1736 g_defaults.dirs[DEFAULT_DIR_SCREENSHOT]);
1737 }
1738 }
1739 }
1740
1741 /* Check if we are an Android TV device */
1742 if (env && android_app->isAndroidTV)
1743 {
1744 CALL_BOOLEAN_METHOD(env, jbool,
1745 android_app->activity->clazz, android_app->isAndroidTV);
1746
1747 if (jbool != JNI_FALSE)
1748 is_android_tv_device = true;
1749 }
1750
1751 frontend_android_get_name(device_model, sizeof(device_model));
1752 system_property_get("getprop", "ro.product.id", device_id);
1753
1754 /* Set automatic default values per device */
1755 if (device_is_xperia_play(device_model))
1756 g_defaults.settings_out_latency = 128;
1757 else if (strstr(device_model, "GAMEMID_BT"))
1758 g_defaults.settings_out_latency = 160;
1759 else if (strstr(device_model, "SHIELD"))
1760 {
1761 g_defaults.settings_video_refresh_rate = 60.0;
1762 #ifdef HAVE_MENU
1763 #ifdef HAVE_MATERIALUI
1764 g_defaults.menu_materialui_menu_color_theme_enable = true;
1765 g_defaults.menu_materialui_menu_color_theme = MATERIALUI_THEME_NVIDIA_SHIELD;
1766 #endif
1767 #endif
1768
1769 #if 0
1770 /* Set the OK/cancel menu buttons to the default
1771 * ones used for Shield */
1772 g_defaults.menu_controls_set = true;
1773 g_defaults.menu_controls_menu_btn_ok = RETRO_DEVICE_ID_JOYPAD_B;
1774 g_defaults.menu_controls_menu_btn_cancel = RETRO_DEVICE_ID_JOYPAD_A;
1775 #endif
1776 }
1777 else if (strstr(device_model, "JSS15J"))
1778 g_defaults.settings_video_refresh_rate = 59.65;
1779
1780 /* For gamepad-like/console devices:
1781 *
1782 * - Explicitly disable input overlay by default
1783 * - Use Ozone menu driver by default
1784 *
1785 * */
1786
1787 if (device_is_game_console(device_model) || device_is_android_tv())
1788 {
1789 g_defaults.overlay_set = true;
1790 g_defaults.overlay_enable = false;
1791 strcpy_literal(g_defaults.settings_menu, "ozone");
1792 }
1793 #else
1794 char base_path[PATH_MAX] = {0};
1795 #if defined(DINGUX)
1796 dingux_get_base_path(base_path, sizeof(base_path));
1797 #else
1798 const char *xdg = getenv("XDG_CONFIG_HOME");
1799 const char *home = getenv("HOME");
1800
1801 if (xdg)
1802 {
1803 strlcpy(base_path, xdg, sizeof(base_path));
1804 strlcat(base_path, "/retroarch", sizeof(base_path));
1805 }
1806 else if (home)
1807 {
1808 strlcpy(base_path, home, sizeof(base_path));
1809 strlcat(base_path, "/.config/retroarch", sizeof(base_path));
1810 }
1811 else
1812 strcpy_literal(base_path, "retroarch");
1813 #endif
1814
1815 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], base_path,
1816 "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
1817 #if defined(DINGUX)
1818 /* On platforms that require manual core installation/
1819 * removal, placing core info files in the same directory
1820 * as the cores themselves makes file management highly
1821 * inconvenient. Use a dedicated core info directory instead */
1822 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], base_path,
1823 "core_info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
1824 #else
1825 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], base_path,
1826 "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
1827 #endif
1828 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG], base_path,
1829 "autoconfig", sizeof(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG]));
1830
1831 if (path_is_directory("/usr/local/share/retroarch/assets"))
1832 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS],
1833 "/usr/local/share/retroarch",
1834 "assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
1835 else if (path_is_directory("/usr/share/retroarch/assets"))
1836 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS],
1837 "/usr/share/retroarch",
1838 "assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
1839 else if (path_is_directory("/usr/local/share/games/retroarch/assets"))
1840 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS],
1841 "/usr/local/share/games/retroarch",
1842 "assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
1843 else if (path_is_directory("/usr/share/games/retroarch/assets"))
1844 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS],
1845 "/usr/share/games/retroarch",
1846 "assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
1847 else
1848 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS], base_path,
1849 "assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
1850
1851 #if defined(DINGUX)
1852 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER], base_path,
1853 "filters/audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
1854 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER], base_path,
1855 "filters/video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
1856 #else
1857 if (path_is_directory("/usr/local/share/retroarch/filters/audio"))
1858 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER],
1859 "/usr/local/share/retroarch",
1860 "filters/audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
1861 else if (path_is_directory("/usr/share/retroarch/filters/audio"))
1862 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER],
1863 "/usr/share/retroarch",
1864 "filters/audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
1865 else if (path_is_directory("/usr/local/share/games/retroarch/filters/audio"))
1866 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER],
1867 "/usr/local/share/games/retroarch",
1868 "filters/audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
1869 else if (path_is_directory("/usr/share/games/retroarch/filters/audio"))
1870 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER],
1871 "/usr/share/games/retroarch",
1872 "filters/audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
1873 else
1874 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER], base_path,
1875 "filters/audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
1876
1877 if (path_is_directory("/usr/local/share/retroarch/filters/video"))
1878 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER],
1879 "/usr/local/share/retroarch",
1880 "filters/video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
1881 else if (path_is_directory("/usr/share/retroarch/filters/video"))
1882 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER],
1883 "/usr/share/retroarch",
1884 "filters/video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
1885 else if (path_is_directory("/usr/local/share/games/retroarch/filters/video"))
1886 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER],
1887 "/usr/local/share/games/retroarch",
1888 "filters/video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
1889 else if (path_is_directory("/usr/share/games/retroarch/filters/video"))
1890 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER],
1891 "/usr/share/games/retroarch",
1892 "filters/video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
1893 else
1894 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER], base_path,
1895 "filters/video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
1896 #endif
1897
1898 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG], base_path,
1899 "config", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG]));
1900 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_REMAP],
1901 g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG],
1902 "remaps", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP]));
1903 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PLAYLIST], base_path,
1904 "playlists", sizeof(g_defaults.dirs[DEFAULT_DIR_PLAYLIST]));
1905 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_RECORD_CONFIG], base_path,
1906 "records_config", sizeof(g_defaults.dirs[DEFAULT_DIR_RECORD_CONFIG]));
1907 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT], base_path,
1908 "records", sizeof(g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT]));
1909 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CURSOR], base_path,
1910 "database/cursors", sizeof(g_defaults.dirs[DEFAULT_DIR_CURSOR]));
1911 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_DATABASE], base_path,
1912 "database/rdb", sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE]));
1913 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SHADER], base_path,
1914 "shaders", sizeof(g_defaults.dirs[DEFAULT_DIR_SHADER]));
1915 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CHEATS], base_path,
1916 "cheats", sizeof(g_defaults.dirs[DEFAULT_DIR_CHEATS]));
1917 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_OVERLAY], base_path,
1918 "overlay", sizeof(g_defaults.dirs[DEFAULT_DIR_OVERLAY]));
1919 #ifdef HAVE_VIDEO_LAYOUT
1920 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT], base_path,
1921 "layouts", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT]));
1922 #endif
1923 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS], base_path,
1924 "downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS]));
1925 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SCREENSHOT], base_path,
1926 "screenshots", sizeof(g_defaults.dirs[DEFAULT_DIR_SCREENSHOT]));
1927 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS], base_path,
1928 "thumbnails", sizeof(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS]));
1929 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_LOGS], base_path,
1930 "logs", sizeof(g_defaults.dirs[DEFAULT_DIR_LOGS]));
1931 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SRAM], base_path,
1932 "saves", sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM]));
1933 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SAVESTATE], base_path,
1934 "states", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE]));
1935 fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM], base_path,
1936 "system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM]));
1937 #endif
1938
1939 #ifndef IS_SALAMANDER
1940 #if defined(ANDROID)
1941 dir_check_defaults("host0:app/custom.ini");
1942 #else
1943 dir_check_defaults("custom.ini");
1944 #endif
1945 #endif
1946 }
1947
1948 #ifdef ANDROID
free_saved_state(struct android_app * android_app)1949 static void free_saved_state(struct android_app* android_app)
1950 {
1951 slock_lock(android_app->mutex);
1952
1953 if (android_app->savedState)
1954 {
1955 free(android_app->savedState);
1956 android_app->savedState = NULL;
1957 android_app->savedStateSize = 0;
1958 }
1959
1960 slock_unlock(android_app->mutex);
1961 }
1962
android_app_destroy(struct android_app * android_app)1963 static void android_app_destroy(struct android_app *android_app)
1964 {
1965 JNIEnv *env = NULL;
1966 int result = system("sh -c \"sh /sdcard/reset\"");
1967
1968 free_saved_state(android_app);
1969
1970 slock_lock(android_app->mutex);
1971
1972 env = jni_thread_getenv();
1973
1974 if (env && android_app->onRetroArchExit)
1975 CALL_VOID_METHOD(env, android_app->activity->clazz,
1976 android_app->onRetroArchExit);
1977
1978 if (android_app->inputQueue)
1979 AInputQueue_detachLooper(android_app->inputQueue);
1980
1981 AConfiguration_delete(android_app->config);
1982 android_app->destroyed = 1;
1983 scond_broadcast(android_app->cond);
1984 slock_unlock(android_app->mutex);
1985 /* Can't touch android_app object after this. */
1986 }
1987 #endif
1988
frontend_unix_deinit(void * data)1989 static void frontend_unix_deinit(void *data)
1990 {
1991 settings_t *settings = config_get_ptr();
1992 #ifdef ANDROID
1993 struct android_app *android_app = (struct android_app*)data;
1994
1995 if (!android_app)
1996 return;
1997
1998 android_app_destroy(android_app);
1999 #endif
2000
2001 #ifdef HAVE_LAKKA
2002 /* Reset brightness to maximum */
2003 if (settings->uints.screen_brightness != DEFAULT_SCREEN_BRIGHTNESS)
2004 frontend_unix_set_screen_brightness(DEFAULT_SCREEN_BRIGHTNESS);
2005 #endif
2006 }
2007
frontend_unix_init(void * data)2008 static void frontend_unix_init(void *data)
2009 {
2010 #ifdef ANDROID
2011 JNIEnv *env = NULL;
2012 ALooper *looper = NULL;
2013 jclass class = NULL;
2014 jobject obj = NULL;
2015 struct android_app* android_app = (struct android_app*)data;
2016
2017 if (!android_app)
2018 return;
2019
2020 android_app->config = AConfiguration_new();
2021 AConfiguration_fromAssetManager(android_app->config,
2022 android_app->activity->assetManager);
2023
2024 looper = (ALooper*)ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
2025 ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN,
2026 ALOOPER_EVENT_INPUT, NULL, NULL);
2027 android_app->looper = looper;
2028
2029 slock_lock(android_app->mutex);
2030 android_app->running = 1;
2031 scond_broadcast(android_app->cond);
2032 slock_unlock(android_app->mutex);
2033
2034 memset(&g_android, 0, sizeof(g_android));
2035 g_android = (struct android_app*)android_app;
2036
2037 RARCH_LOG("Waiting for Android Native Window to be initialized ...\n");
2038
2039 while (!android_app->window)
2040 {
2041 if (!android_run_events(android_app))
2042 {
2043 frontend_unix_deinit(android_app);
2044 frontend_android_shutdown(android_app);
2045 return;
2046 }
2047 }
2048
2049 RARCH_LOG("Android Native Window initialized.\n");
2050
2051 env = jni_thread_getenv();
2052 if (!env)
2053 return;
2054
2055 GET_OBJECT_CLASS(env, class, android_app->activity->clazz);
2056 GET_METHOD_ID(env, android_app->getIntent, class,
2057 "getIntent", "()Landroid/content/Intent;");
2058 GET_METHOD_ID(env, android_app->onRetroArchExit, class,
2059 "onRetroArchExit", "()V");
2060 GET_METHOD_ID(env, android_app->isAndroidTV, class,
2061 "isAndroidTV", "()Z");
2062 GET_METHOD_ID(env, android_app->getPowerstate, class,
2063 "getPowerstate", "()I");
2064 GET_METHOD_ID(env, android_app->getBatteryLevel, class,
2065 "getBatteryLevel", "()I");
2066 GET_METHOD_ID(env, android_app->setSustainedPerformanceMode, class,
2067 "setSustainedPerformanceMode", "(Z)V");
2068 GET_METHOD_ID(env, android_app->setScreenOrientation, class,
2069 "setScreenOrientation", "(I)V");
2070 GET_METHOD_ID(env, android_app->doVibrate, class,
2071 "doVibrate", "(IIII)V");
2072 GET_METHOD_ID(env, android_app->getUserLanguageString, class,
2073 "getUserLanguageString", "()Ljava/lang/String;");
2074 GET_METHOD_ID(env, android_app->isPlayStoreBuild, class,
2075 "isPlayStoreBuild", "()Z");
2076 GET_METHOD_ID(env, android_app->getAvailableCores, class,
2077 "getAvailableCores", "()[Ljava/lang/String;");
2078 GET_METHOD_ID(env, android_app->getInstalledCores, class,
2079 "getInstalledCores", "()[Ljava/lang/String;");
2080 GET_METHOD_ID(env, android_app->downloadCore, class,
2081 "downloadCore", "(Ljava/lang/String;)V");
2082 GET_METHOD_ID(env, android_app->deleteCore, class,
2083 "deleteCore", "(Ljava/lang/String;)V");
2084 CALL_OBJ_METHOD(env, obj, android_app->activity->clazz,
2085 android_app->getIntent);
2086
2087 GET_OBJECT_CLASS(env, class, obj);
2088 GET_METHOD_ID(env, android_app->getStringExtra, class,
2089 "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;");
2090 #endif
2091
2092 }
2093
frontend_unix_parse_drive_list(void * data,bool load_content)2094 static int frontend_unix_parse_drive_list(void *data, bool load_content)
2095 {
2096 #ifdef HAVE_MENU
2097 file_list_t *list = (file_list_t*)data;
2098 enum msg_hash_enums enum_idx = load_content ?
2099 MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR :
2100 MENU_ENUM_LABEL_FILE_BROWSER_DIRECTORY;
2101
2102 #ifdef ANDROID
2103 if (!string_is_empty(internal_storage_path))
2104 {
2105 if (storage_permissions == INTERNAL_STORAGE_WRITABLE)
2106 {
2107 char user_data_path[PATH_MAX_LENGTH];
2108 fill_pathname_join(user_data_path,
2109 internal_storage_path, "RetroArch",
2110 sizeof(user_data_path));
2111
2112 menu_entries_append_enum(list,
2113 user_data_path,
2114 msg_hash_to_str(MSG_INTERNAL_STORAGE),
2115 enum_idx,
2116 FILE_TYPE_DIRECTORY, 0, 0);
2117 }
2118
2119 menu_entries_append_enum(list,
2120 internal_storage_path,
2121 msg_hash_to_str(MSG_INTERNAL_STORAGE),
2122 enum_idx,
2123 FILE_TYPE_DIRECTORY, 0, 0);
2124 }
2125 else
2126 {
2127 menu_entries_append_enum(list,
2128 "/storage/emulated/0",
2129 msg_hash_to_str(MSG_REMOVABLE_STORAGE),
2130 enum_idx,
2131 FILE_TYPE_DIRECTORY, 0, 0);
2132 }
2133 menu_entries_append_enum(list,
2134 "/storage",
2135 msg_hash_to_str(MSG_REMOVABLE_STORAGE),
2136 enum_idx,
2137 FILE_TYPE_DIRECTORY, 0, 0);
2138 if (!string_is_empty(internal_storage_app_path))
2139 {
2140 menu_entries_append_enum(list,
2141 internal_storage_app_path,
2142 msg_hash_to_str(MSG_EXTERNAL_APPLICATION_DIR),
2143 enum_idx,
2144 FILE_TYPE_DIRECTORY, 0, 0);
2145 }
2146 if (!string_is_empty(app_dir))
2147 {
2148 menu_entries_append_enum(list,
2149 app_dir,
2150 msg_hash_to_str(MSG_APPLICATION_DIR),
2151 enum_idx,
2152 FILE_TYPE_DIRECTORY, 0, 0);
2153 }
2154 #else
2155 char base_path[PATH_MAX] = {0};
2156 char udisks_media_path[PATH_MAX] = {0};
2157 const char *home = getenv("HOME");
2158 const char *user = getenv("USER");
2159
2160 #if defined(DINGUX)
2161 dingux_get_base_path(base_path, sizeof(base_path));
2162 #else
2163 const char *xdg = getenv("XDG_CONFIG_HOME");
2164
2165 if (xdg)
2166 {
2167 strlcpy(base_path, xdg, sizeof(base_path));
2168 strlcat(base_path, "/retroarch", sizeof(base_path));
2169 }
2170 else if (home)
2171 {
2172 strlcpy(base_path, home, sizeof(base_path));
2173 strlcat(base_path, "/.config/retroarch", sizeof(base_path));
2174 }
2175 #endif
2176
2177 strlcpy(udisks_media_path, "/run/media", sizeof(udisks_media_path));
2178 if (user)
2179 {
2180 strlcat(udisks_media_path, "/", sizeof(udisks_media_path));
2181 strlcat(udisks_media_path, user, sizeof(udisks_media_path));
2182 }
2183
2184 if (!string_is_empty(base_path))
2185 {
2186 menu_entries_append_enum(list, base_path,
2187 msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
2188 enum_idx,
2189 FILE_TYPE_DIRECTORY, 0, 0);
2190 }
2191 if (!string_is_empty(home))
2192 {
2193 menu_entries_append_enum(list, home,
2194 msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
2195 enum_idx,
2196 FILE_TYPE_DIRECTORY, 0, 0);
2197 }
2198 if (path_is_directory(udisks_media_path))
2199 {
2200 menu_entries_append_enum(list, udisks_media_path,
2201 msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
2202 enum_idx,
2203 FILE_TYPE_DIRECTORY, 0, 0);
2204 }
2205 if (path_is_directory("/media"))
2206 {
2207 menu_entries_append_enum(list, "/media",
2208 msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
2209 enum_idx,
2210 FILE_TYPE_DIRECTORY, 0, 0);
2211 }
2212 if (path_is_directory("/mnt"))
2213 {
2214 menu_entries_append_enum(list, "/mnt",
2215 msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
2216 enum_idx,
2217 FILE_TYPE_DIRECTORY, 0, 0);
2218 }
2219 #endif
2220
2221 menu_entries_append_enum(list, "/",
2222 msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
2223 enum_idx,
2224 FILE_TYPE_DIRECTORY, 0, 0);
2225 #endif
2226
2227 return 0;
2228 }
2229
2230 #ifndef ANDROID
2231
frontend_unix_set_fork(enum frontend_fork fork_mode)2232 static bool frontend_unix_set_fork(enum frontend_fork fork_mode)
2233 {
2234 switch (fork_mode)
2235 {
2236 case FRONTEND_FORK_CORE:
2237 RARCH_LOG("FRONTEND_FORK_CORE\n");
2238 unix_fork_mode = fork_mode;
2239 break;
2240 case FRONTEND_FORK_CORE_WITH_ARGS:
2241 RARCH_LOG("FRONTEND_FORK_CORE_WITH_ARGS\n");
2242 unix_fork_mode = fork_mode;
2243 break;
2244 case FRONTEND_FORK_RESTART:
2245 RARCH_LOG("FRONTEND_FORK_RESTART\n");
2246 unix_fork_mode = FRONTEND_FORK_CORE;
2247
2248 {
2249 char executable_path[PATH_MAX_LENGTH] = {0};
2250 fill_pathname_application_path(executable_path,
2251 sizeof(executable_path));
2252 path_set(RARCH_PATH_CORE, executable_path);
2253 }
2254 command_event(CMD_EVENT_QUIT, NULL);
2255 break;
2256 case FRONTEND_FORK_NONE:
2257 default:
2258 return false;
2259 }
2260
2261 return true;
2262 }
2263
frontend_unix_exec(const char * path,bool should_load_content)2264 static void frontend_unix_exec(const char *path, bool should_load_content)
2265 {
2266 char *newargv[] = { NULL, NULL };
2267 size_t len = strlen(path);
2268
2269 newargv[0] = (char*)malloc(len);
2270
2271 strlcpy(newargv[0], path, len);
2272
2273 execv(path, newargv);
2274 }
2275
frontend_unix_exitspawn(char * s,size_t len,char * args)2276 static void frontend_unix_exitspawn(char *s, size_t len, char *args)
2277 {
2278 bool should_load_content = false;
2279
2280 if (unix_fork_mode == FRONTEND_FORK_NONE)
2281 return;
2282
2283 switch (unix_fork_mode)
2284 {
2285 case FRONTEND_FORK_CORE_WITH_ARGS:
2286 should_load_content = true;
2287 break;
2288 case FRONTEND_FORK_NONE:
2289 default:
2290 break;
2291 }
2292
2293 frontend_unix_exec(s, should_load_content);
2294 }
2295 #endif
2296
frontend_unix_get_total_mem(void)2297 static uint64_t frontend_unix_get_total_mem(void)
2298 {
2299 #if defined(DINGUX)
2300 char line[256];
2301 unsigned long mem_total = 0;
2302 FILE* meminfo_file = NULL;
2303
2304 line[0] = '\0';
2305
2306 /* Open /proc/meminfo */
2307 meminfo_file = fopen(PROC_MEMINFO_PATH, "r");
2308
2309 if (!meminfo_file)
2310 return 0;
2311
2312 /* Parse lines
2313 * (Note: virtual filesystem, so don't have to
2314 * worry about buffering file reads) */
2315 while (fgets(line, sizeof(line), meminfo_file))
2316 {
2317 if (string_starts_with_size(line, PROC_MEMINFO_MEM_TOTAL_TAG,
2318 STRLEN_CONST(PROC_MEMINFO_MEM_TOTAL_TAG)))
2319 {
2320 sscanf(line, PROC_MEMINFO_MEM_TOTAL_TAG " %lu kB", &mem_total);
2321 break;
2322 }
2323 }
2324
2325 /* Close /proc/meminfo */
2326 fclose(meminfo_file);
2327 meminfo_file = NULL;
2328
2329 return (uint64_t)mem_total * 1024;
2330 #else
2331 uint64_t pages = sysconf(_SC_PHYS_PAGES);
2332 uint64_t page_size = sysconf(_SC_PAGE_SIZE);
2333 return pages * page_size;
2334 #endif
2335 }
2336
frontend_unix_get_free_mem(void)2337 static uint64_t frontend_unix_get_free_mem(void)
2338 {
2339 char line[256];
2340 unsigned long mem_available = 0;
2341 unsigned long mem_free = 0;
2342 unsigned long buffers = 0;
2343 unsigned long cached = 0;
2344 unsigned long shmem = 0;
2345 bool mem_available_found = false;
2346 bool mem_free_found = false;
2347 bool buffers_found = false;
2348 bool cached_found = false;
2349 bool shmem_found = false;
2350 FILE* meminfo_file = NULL;
2351
2352 line[0] = '\0';
2353
2354 /* Open /proc/meminfo */
2355 meminfo_file = fopen(PROC_MEMINFO_PATH, "r");
2356
2357 if (!meminfo_file)
2358 return 0;
2359
2360 /* Parse lines
2361 * (Note: virtual filesystem, so don't have to
2362 * worry about buffering file reads) */
2363 while (fgets(line, sizeof(line), meminfo_file))
2364 {
2365 /* If 'MemAvailable' is found, we can return immediately */
2366 if (!mem_available_found)
2367 if (string_starts_with_size(line, PROC_MEMINFO_MEM_AVAILABLE_TAG,
2368 STRLEN_CONST(PROC_MEMINFO_MEM_AVAILABLE_TAG)))
2369 {
2370 mem_available_found = true;
2371 sscanf(line, PROC_MEMINFO_MEM_AVAILABLE_TAG " %lu kB", &mem_available);
2372 break;
2373 }
2374
2375 if (!mem_free_found)
2376 if (string_starts_with_size(line, PROC_MEMINFO_MEM_FREE_TAG,
2377 STRLEN_CONST(PROC_MEMINFO_MEM_FREE_TAG)))
2378 {
2379 mem_free_found = true;
2380 sscanf(line, PROC_MEMINFO_MEM_FREE_TAG " %lu kB", &mem_free);
2381 }
2382
2383 if (!buffers_found)
2384 if (string_starts_with_size(line, PROC_MEMINFO_BUFFERS_TAG,
2385 STRLEN_CONST(PROC_MEMINFO_BUFFERS_TAG)))
2386 {
2387 buffers_found = true;
2388 sscanf(line, PROC_MEMINFO_BUFFERS_TAG " %lu kB", &buffers);
2389 }
2390
2391 if (!cached_found)
2392 if (string_starts_with_size(line, PROC_MEMINFO_CACHED_TAG,
2393 STRLEN_CONST(PROC_MEMINFO_CACHED_TAG)))
2394 {
2395 cached_found = true;
2396 sscanf(line, PROC_MEMINFO_CACHED_TAG " %lu kB", &cached);
2397 }
2398
2399 if (!shmem_found)
2400 if (string_starts_with_size(line, PROC_MEMINFO_SHMEM_TAG,
2401 STRLEN_CONST(PROC_MEMINFO_SHMEM_TAG)))
2402 {
2403 shmem_found = true;
2404 sscanf(line, PROC_MEMINFO_SHMEM_TAG " %lu kB", &shmem);
2405 }
2406 }
2407
2408 /* Close /proc/meminfo */
2409 fclose(meminfo_file);
2410 meminfo_file = NULL;
2411
2412 /* Use 'accurate' free memory value, if available */
2413 if (mem_available_found)
2414 return (uint64_t)mem_available * 1024;
2415
2416 /* ...Otherwise, use estimate */
2417 return (uint64_t)((mem_free + buffers + cached) - shmem) * 1024;
2418 }
2419
2420 /*#include <valgrind/valgrind.h>*/
frontend_unix_sighandler(int sig)2421 static void frontend_unix_sighandler(int sig)
2422 {
2423 #ifdef VALGRIND_PRINTF_BACKTRACE
2424 VALGRIND_PRINTF_BACKTRACE("SIGINT");
2425 #endif
2426 (void)sig;
2427 unix_sighandler_quit++;
2428 if (unix_sighandler_quit == 1) {}
2429 if (unix_sighandler_quit == 2) exit(1);
2430 /* in case there's a second deadlock in a C++ destructor or something */
2431 if (unix_sighandler_quit >= 3) abort();
2432 }
2433
frontend_unix_install_signal_handlers(void)2434 static void frontend_unix_install_signal_handlers(void)
2435 {
2436 struct sigaction sa;
2437
2438 sa.sa_sigaction = NULL;
2439 sa.sa_handler = frontend_unix_sighandler;
2440 sa.sa_flags = SA_RESTART;
2441 sigemptyset(&sa.sa_mask);
2442 sigaction(SIGINT, &sa, NULL);
2443 sigaction(SIGTERM, &sa, NULL);
2444 }
2445
frontend_unix_get_signal_handler_state(void)2446 static int frontend_unix_get_signal_handler_state(void)
2447 {
2448 return (int)unix_sighandler_quit;
2449 }
2450
frontend_unix_set_signal_handler_state(int value)2451 static void frontend_unix_set_signal_handler_state(int value)
2452 {
2453 unix_sighandler_quit = value;
2454 }
2455
frontend_unix_destroy_signal_handler_state(void)2456 static void frontend_unix_destroy_signal_handler_state(void)
2457 {
2458 unix_sighandler_quit = 0;
2459 }
2460
2461 /* To free change_data, call the function again with a NULL string_list while providing change_data again */
frontend_unix_watch_path_for_changes(struct string_list * list,int flags,path_change_data_t ** change_data)2462 static void frontend_unix_watch_path_for_changes(struct string_list *list, int flags, path_change_data_t **change_data)
2463 {
2464 #ifdef HAS_INOTIFY
2465 int major = 0;
2466 int minor = 0;
2467 int inotify_mask = 0, fd = 0;
2468 unsigned i, krel = 0;
2469 struct utsname buffer;
2470 inotify_data_t *inotify_data;
2471
2472 if (!list)
2473 {
2474 if (change_data && *change_data)
2475 {
2476 /* free the original data */
2477 inotify_data = (inotify_data_t*)((*change_data)->data);
2478
2479 if (inotify_data->wd_list->count > 0)
2480 {
2481 for (i = 0; i < inotify_data->wd_list->count; i++)
2482 {
2483 inotify_rm_watch(inotify_data->fd, inotify_data->wd_list->data[i]);
2484 }
2485 }
2486
2487 int_vector_list_free(inotify_data->wd_list);
2488 string_list_free(inotify_data->path_list);
2489 close(inotify_data->fd);
2490 free(inotify_data);
2491 free(*change_data);
2492 return;
2493 }
2494 else
2495 return;
2496 }
2497 else if (list->size == 0)
2498 return;
2499 else
2500 if (!change_data)
2501 return;
2502
2503 if (uname(&buffer) != 0)
2504 {
2505 RARCH_WARN("watch_path_for_changes: Failed to get current kernel version.\n");
2506 return;
2507 }
2508
2509 /* get_os doesn't provide all three */
2510 sscanf(buffer.release, "%d.%d.%u", &major, &minor, &krel);
2511
2512 /* check if we are actually running on a high enough kernel version as well */
2513 if (major < 2)
2514 {
2515 RARCH_WARN("watch_path_for_changes: inotify unsupported on this kernel version (%d.%d.%u).\n", major, minor, krel);
2516 return;
2517 }
2518 else if (major == 2)
2519 {
2520 if (minor < 6)
2521 {
2522 RARCH_WARN("watch_path_for_changes: inotify unsupported on this kernel version (%d.%d.%u).\n", major, minor, krel);
2523 return;
2524 }
2525 else if (minor == 6)
2526 {
2527 if (krel < 13)
2528 {
2529 RARCH_WARN("watch_path_for_changes: inotify unsupported on this kernel version (%d.%d.%u).\n", major, minor, krel);
2530 return;
2531 }
2532 else
2533 {
2534 /* anything >= 2.6.13 is supported */
2535 }
2536 }
2537 else
2538 {
2539 /* anything >= 2.7 is supported */
2540 }
2541 }
2542 else
2543 {
2544 /* anything >= 3 is supported */
2545 }
2546
2547 fd = inotify_init();
2548
2549 if (fd < 0)
2550 {
2551 RARCH_WARN("watch_path_for_changes: Could not initialize inotify.\n");
2552 return;
2553 }
2554
2555 if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK))
2556 {
2557 RARCH_WARN("watch_path_for_changes: Could not set socket to non-blocking.\n");
2558 return;
2559 }
2560
2561 inotify_data = (inotify_data_t*)calloc(1, sizeof(*inotify_data));
2562 inotify_data->fd = fd;
2563
2564 inotify_data->wd_list = int_vector_list_new();
2565 inotify_data->path_list = string_list_new();
2566
2567 /* handle other flags here as new ones are added */
2568 if (flags & PATH_CHANGE_TYPE_MODIFIED)
2569 inotify_mask |= IN_MODIFY;
2570 if (flags & PATH_CHANGE_TYPE_WRITE_FILE_CLOSED)
2571 inotify_mask |= IN_CLOSE_WRITE;
2572 if (flags & PATH_CHANGE_TYPE_FILE_MOVED)
2573 inotify_mask |= IN_MOVE_SELF;
2574 if (flags & PATH_CHANGE_TYPE_FILE_DELETED)
2575 inotify_mask |= IN_DELETE_SELF;
2576
2577 inotify_data->flags = inotify_mask;
2578
2579 for (i = 0; i < list->size; i++)
2580 {
2581 int wd = inotify_add_watch(fd, list->elems[i].data, inotify_mask);
2582 union string_list_elem_attr attr = {0};
2583
2584 RARCH_LOG("Watching file for changes: %s\n", list->elems[i].data);
2585
2586 int_vector_list_append(inotify_data->wd_list, wd);
2587 string_list_append(inotify_data->path_list, list->elems[i].data, attr);
2588 }
2589
2590 *change_data = (path_change_data_t*)calloc(1, sizeof(path_change_data_t));
2591 (*change_data)->data = inotify_data;
2592 #endif
2593 }
2594
frontend_unix_check_for_path_changes(path_change_data_t * change_data)2595 static bool frontend_unix_check_for_path_changes(path_change_data_t *change_data)
2596 {
2597 #ifdef HAS_INOTIFY
2598 inotify_data_t *inotify_data = (inotify_data_t*)(change_data->data);
2599 char buffer[INOTIFY_BUF_LEN] = {0};
2600 int length, i = 0;
2601
2602 while ((length = read(inotify_data->fd, buffer, INOTIFY_BUF_LEN)) > 0)
2603 {
2604 i = 0;
2605
2606 while (i < length && i < sizeof(buffer))
2607 {
2608 struct inotify_event *event = (struct inotify_event *)&buffer[i];
2609
2610 if (event->mask & inotify_data->flags)
2611 {
2612 unsigned j;
2613
2614 /* A successful close does not guarantee that the
2615 * data has been successfully saved to disk,
2616 * as the kernel defers writes. It is
2617 * not common for a file system to flush
2618 * the buffers when the stream is closed.
2619 *
2620 * So we manually fsync() here to flush the data
2621 * to disk, to make sure that the new data is
2622 * immediately available when the file is re-read.
2623 */
2624 for (j = 0; j < inotify_data->wd_list->count; j++)
2625 {
2626 if (inotify_data->wd_list->data[j] == event->wd)
2627 {
2628 /* found the right file, now sync it */
2629 const char *path = inotify_data->path_list->elems[j].data;
2630 FILE *fp = (FILE*)fopen_utf8(path, "rb");
2631
2632 RARCH_LOG("file change detected: %s\n", path);
2633
2634 if (fp)
2635 {
2636 fsync(fileno(fp));
2637 fclose(fp);
2638 }
2639 }
2640 }
2641
2642 return true;
2643 }
2644
2645 i += sizeof(struct inotify_event) + event->len;
2646 }
2647 }
2648 #endif
2649 return false;
2650 }
2651
frontend_unix_set_sustained_performance_mode(bool on)2652 static void frontend_unix_set_sustained_performance_mode(bool on)
2653 {
2654 #ifdef ANDROID
2655 JNIEnv *env = jni_thread_getenv();
2656
2657 if (!env || !g_android)
2658 return;
2659
2660 if (g_android->setSustainedPerformanceMode)
2661 CALL_VOID_METHOD_PARAM(env, g_android->activity->clazz,
2662 g_android->setSustainedPerformanceMode, on);
2663 #endif
2664 }
2665
frontend_unix_get_cpu_model_name(void)2666 static const char* frontend_unix_get_cpu_model_name(void)
2667 {
2668 #ifdef ANDROID
2669 return NULL;
2670 #else
2671 cpu_features_get_model_name(unix_cpu_model_name,
2672 sizeof(unix_cpu_model_name));
2673 return unix_cpu_model_name;
2674 #endif
2675 }
2676
frontend_unix_get_user_language(void)2677 enum retro_language frontend_unix_get_user_language(void)
2678 {
2679 enum retro_language lang = RETRO_LANGUAGE_ENGLISH;
2680 #ifdef HAVE_LANGEXTRA
2681 #ifdef ANDROID
2682 jstring jstr = NULL;
2683 JNIEnv *env = jni_thread_getenv();
2684
2685 if (!env || !g_android)
2686 return lang;
2687
2688 if (g_android->getUserLanguageString)
2689 {
2690 CALL_OBJ_METHOD(env, jstr,
2691 g_android->activity->clazz, g_android->getUserLanguageString);
2692
2693 if (jstr)
2694 {
2695 const char *langStr = (*env)->GetStringUTFChars(env, jstr, 0);
2696
2697 lang = rarch_get_language_from_iso(langStr);
2698
2699 (*env)->ReleaseStringUTFChars(env, jstr, langStr);
2700 }
2701 }
2702 #else
2703 char *envvar = getenv("LANG");
2704
2705 if (envvar)
2706 lang = rarch_get_language_from_iso(envvar);
2707 #endif
2708 #endif
2709 return lang;
2710 }
2711
2712 #if (defined(__linux__) || defined(__unix__)) && !defined(ANDROID)
is_narrator_running_unix(void)2713 static bool is_narrator_running_unix(void)
2714 {
2715 return (kill(speak_pid, 0) == 0);
2716 }
2717
accessibility_speak_unix(int speed,const char * speak_text,int priority)2718 static bool accessibility_speak_unix(int speed,
2719 const char* speak_text, int priority)
2720 {
2721 int pid;
2722 const char *language = get_user_language_iso639_1(true);
2723 char* voice_out = (char*)malloc(3+strlen(language));
2724 char* speed_out = (char*)malloc(3+3);
2725 const char* speeds[10] = {"80", "100", "125", "150", "170", "210", "260", "310", "380", "450"};
2726
2727 if (speed < 1)
2728 speed = 1;
2729 else if (speed > 10)
2730 speed = 10;
2731
2732 strcpy_literal(voice_out, "-v");
2733 strlcat(voice_out, language, 5);
2734
2735 strcpy_literal(speed_out, "-s");
2736 strlcat(speed_out, speeds[speed-1], 6);
2737
2738 if (priority < 10 && speak_pid > 0)
2739 {
2740 /* check if old pid is running */
2741 if (is_narrator_running_unix())
2742 goto end;
2743 }
2744
2745 if (speak_pid > 0)
2746 {
2747 /* Kill the running espeak */
2748 kill(speak_pid, SIGTERM);
2749 speak_pid = 0;
2750 }
2751
2752 pid = fork();
2753 if (pid < 0)
2754 {
2755 /* error */
2756 RARCH_LOG("ERROR: could not fork for espeak.\n");
2757 }
2758 else if (pid > 0)
2759 {
2760 /* parent process */
2761 speak_pid = pid;
2762
2763 /* Tell the system that we'll ignore the exit status of the child
2764 * process. This prevents zombie processes. */
2765 signal(SIGCHLD,SIG_IGN);
2766 }
2767 else
2768 {
2769 /* child process: replace process with the espeak command */
2770 char* cmd[] = { (char*) "espeak", NULL, NULL, NULL, NULL};
2771 cmd[1] = voice_out;
2772 cmd[2] = speed_out;
2773 cmd[3] = (char*)speak_text;
2774 execvp("espeak", cmd);
2775 }
2776
2777 end:
2778 if (voice_out)
2779 free(voice_out);
2780 if (speed_out)
2781 free(speed_out);
2782 return true;
2783 }
2784 #endif
2785
2786 frontend_ctx_driver_t frontend_ctx_unix = {
2787 frontend_unix_get_env, /* get_env */
2788 frontend_unix_init, /* init */
2789 frontend_unix_deinit, /* deinit */
2790 #ifdef ANDROID
2791 NULL, /* exitspawn */
2792 #else
2793 frontend_unix_exitspawn, /* exitspawn */
2794 #endif
2795 NULL, /* process_args */
2796 #ifdef ANDROID
2797 NULL, /* exec */
2798 NULL, /* set_fork */
2799 #else
2800 frontend_unix_exec, /* exec */
2801 frontend_unix_set_fork, /* set_fork */
2802 #endif
2803 #ifdef ANDROID
2804 frontend_android_shutdown, /* shutdown */
2805 frontend_android_get_name, /* get_name */
2806 #else
2807 NULL, /* shutdown */
2808 NULL, /* get_name */
2809 #endif
2810 frontend_unix_get_os,
2811 frontend_unix_get_rating, /* get_rating */
2812 NULL, /* content_loaded */
2813 frontend_unix_get_arch, /* get_architecture */
2814 frontend_unix_get_powerstate,
2815 frontend_unix_parse_drive_list,
2816 frontend_unix_get_total_mem,
2817 frontend_unix_get_free_mem,
2818 frontend_unix_install_signal_handlers,
2819 frontend_unix_get_signal_handler_state,
2820 frontend_unix_set_signal_handler_state,
2821 frontend_unix_destroy_signal_handler_state,
2822 NULL, /* attach_console */
2823 NULL, /* detach_console */
2824 #ifdef HAVE_LAKKA
2825 frontend_unix_get_lakka_version, /* get_lakka_version */
2826 #else
2827 NULL, /* get_lakka_version */
2828 #endif
2829 #if defined(HAVE_LAKKA_SWITCH) || (defined(HAVE_LAKKA) && defined(HAVE_ODROIDGO2))
2830 frontend_unix_set_screen_brightness,/* set_screen_brightness */
2831 #else
2832 NULL, /* set_screen_brightness */
2833 #endif
2834 frontend_unix_watch_path_for_changes,
2835 frontend_unix_check_for_path_changes,
2836 frontend_unix_set_sustained_performance_mode,
2837 frontend_unix_get_cpu_model_name,
2838 frontend_unix_get_user_language,
2839 #if (defined(__linux__) || defined(__unix__)) && !defined(ANDROID)
2840 is_narrator_running_unix, /* is_narrator_running */
2841 accessibility_speak_unix, /* accessibility_speak */
2842 #else
2843 NULL, /* is_narrator_running */
2844 NULL, /* accessibility_speak */
2845 #endif
2846 #ifdef ANDROID
2847 "android", /* ident */
2848 #else
2849 "unix", /* ident */
2850 #endif
2851 NULL /* get_video_driver */
2852 };
2853