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