/* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2017 - Daniel De Matteis * Copyright (C) 2019-2020 - James Leaver * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with RetroArch. * If not, see . */ #include #include #include #if defined(RS90) #include #endif #include #include "dingux_utils.h" #define DINGUX_ALLOW_DOWNSCALING_FILE "/sys/devices/platform/jz-lcd.0/allow_downscaling" #define DINGUX_KEEP_ASPECT_RATIO_FILE "/sys/devices/platform/jz-lcd.0/keep_aspect_ratio" #define DINGUX_INTEGER_SCALING_FILE "/sys/devices/platform/jz-lcd.0/integer_scaling" #define DINGUX_SHARPNESS_UPSCALING_FILE "/sys/devices/platform/jz-lcd.0/sharpness_upscaling" #define DINGUX_SHARPNESS_DOWNSCALING_FILE "/sys/devices/platform/jz-lcd.0/sharpness_downscaling" #define DINGUX_BATTERY_CAPACITY_FILE "/sys/class/power_supply/battery/capacity" /* Base path defines */ #define DINGUX_HOME_ENVAR "HOME" #define DINGUX_BASE_DIR "retroarch" #define DINGUX_BASE_DIR_HIDDEN ".retroarch" #define DINGUX_RS90_MEDIA_PATH "/media" #define DINGUX_RS90_DEFAULT_SD_PATH "/media/mmcblk0p1" #define DINGUX_RS90_DATA_PATH "/media/data" /* OpenDingux Beta defines */ #define DINGUX_BATTERY_VOLTAGE_MIN "/sys/class/power_supply/jz-battery/voltage_min_design" #define DINGUX_BATTERY_VOLTAGE_MAX "/sys/class/power_supply/jz-battery/voltage_max_design" #define DINGUX_BATTERY_VOLTAGE_NOW "/sys/class/power_supply/jz-battery/voltage_now" #define DINGUX_SCALING_MODE_ENVAR "SDL_VIDEO_KMSDRM_SCALING_MODE" #define DINGUX_SCALING_SHARPNESS_ENVAR "SDL_VIDEO_KMSDRM_SCALING_SHARPNESS" #define DINGUX_VIDEO_REFRESHRATE_ENVAR "SDL_VIDEO_REFRESHRATE" /* Enables/disables downscaling when using * the IPU hardware scaler */ bool dingux_ipu_set_downscaling_enable(bool enable) { #if defined(DINGUX_BETA) return true; #else const char *path = DINGUX_ALLOW_DOWNSCALING_FILE; const char *enable_str = enable ? "1" : "0"; /* Check whether file exists */ if (!path_is_valid(path)) return false; /* Write enable state to file */ return filestream_write_file( path, enable_str, 1); #endif } /* Sets the video scaling mode when using the * IPU hardware scaler * - keep_aspect: When 'true', aspect ratio correction * (1:1 PAR) is applied. When 'false', image is * stretched to full screen dimensions * - integer_scale: When 'true', enables integer * scaling. This implicitly sets keep_aspect to * 'true' (since integer scaling is by definition * 1:1 PAR) * Note: OpenDingux stock firmware allows keep_aspect * and integer_scale to be set independently, hence * separate boolean values. OpenDingux beta properly * groups the settings into a single scaling type * parameter. When supporting both firmwares, it would * be cleaner to refactor this function to accept one * enum rather than 2 booleans - but this would break * users' existing configs, so we maintain the old * format... */ bool dingux_ipu_set_scaling_mode(bool keep_aspect, bool integer_scale) { #if defined(DINGUX_BETA) const char *scaling_str = "0"; /* integer_scale takes priority */ if (integer_scale) scaling_str = "2"; else if (keep_aspect) scaling_str = "1"; return (setenv(DINGUX_SCALING_MODE_ENVAR, scaling_str, 1) == 0); #else const char *keep_aspect_path = DINGUX_KEEP_ASPECT_RATIO_FILE; const char *keep_aspect_str = keep_aspect ? "1" : "0"; bool keep_aspect_success = false; const char *integer_scale_path = DINGUX_INTEGER_SCALING_FILE; const char *integer_scale_str = integer_scale ? "1" : "0"; bool integer_scale_success = false; /* Set keep_aspect */ if (path_is_valid(keep_aspect_path)) keep_aspect_success = filestream_write_file( keep_aspect_path, keep_aspect_str, 1); /* Set integer_scale */ if (path_is_valid(integer_scale_path)) integer_scale_success = filestream_write_file( integer_scale_path, integer_scale_str, 1); return (keep_aspect_success && integer_scale_success); #endif } /* Sets the image filtering method when * using the IPU hardware scaler */ bool dingux_ipu_set_filter_type(enum dingux_ipu_filter_type filter_type) { /* Sharpness settings range is [0,32] * - 0: nearest-neighbour * - 1: bilinear * - 2...32: bicubic (translating to a sharpness * factor of -0.25..-4.0 internally) * Default bicubic sharpness factor is * (-0.125 * 8) = -1.0 */ #if !defined(DINGUX_BETA) const char *upscaling_path = DINGUX_SHARPNESS_UPSCALING_FILE; const char *downscaling_path = DINGUX_SHARPNESS_DOWNSCALING_FILE; bool upscaling_success = false; bool downscaling_success = false; #endif const char *sharpness_str = "8"; /* Check filter type */ switch (filter_type) { case DINGUX_IPU_FILTER_BILINEAR: sharpness_str = "1"; break; case DINGUX_IPU_FILTER_NEAREST: sharpness_str = "0"; break; default: /* sharpness_str is already set to 8 * by default */ break; } #if defined(DINGUX_BETA) return (setenv(DINGUX_SCALING_SHARPNESS_ENVAR, sharpness_str, 1) == 0); #else /* Set upscaling sharpness */ if (path_is_valid(upscaling_path)) upscaling_success = filestream_write_file( upscaling_path, sharpness_str, 1); /* Set downscaling sharpness */ if (path_is_valid(downscaling_path)) downscaling_success = filestream_write_file( downscaling_path, sharpness_str, 1); return (upscaling_success && downscaling_success); #endif } #if defined(DINGUX_BETA) /* Sets the refresh rate of the integral LCD panel. * If specified value is invalid, will set refresh * rate to 60 Hz. * Returns a floating point representation of the * resultant hardware refresh rate. In the event * that a refresh rate cannot be set (i.e. hardware * error), returns 0.0 */ float dingux_set_video_refresh_rate(enum dingux_refresh_rate refresh_rate) { float refresh_rate_float = 60.0f; const char *refresh_rate_str = "60"; /* Check filter type */ switch (refresh_rate) { case DINGUX_REFRESH_RATE_50HZ: refresh_rate_float = 50.0f; refresh_rate_str = "50"; break; default: /* Refresh rate is already set to 60 Hz * by default */ break; } if (setenv(DINGUX_VIDEO_REFRESHRATE_ENVAR, refresh_rate_str, 1) != 0) refresh_rate_float = 0.0f; return refresh_rate_float; } /* Gets the currently set refresh rate of the * integral LCD panel. */ bool dingux_get_video_refresh_rate(enum dingux_refresh_rate *refresh_rate) { const char *refresh_rate_str = getenv(DINGUX_VIDEO_REFRESHRATE_ENVAR); /* If environment variable is unset, refresh * rate defaults to 60 Hz */ if (!refresh_rate_str) { *refresh_rate = DINGUX_REFRESH_RATE_60HZ; return true; } if (string_is_equal(refresh_rate_str, "60")) *refresh_rate = DINGUX_REFRESH_RATE_60HZ; else if (string_is_equal(refresh_rate_str, "50")) *refresh_rate = DINGUX_REFRESH_RATE_50HZ; else return false; return true; } #endif /* Resets the IPU hardware scaler to the * default configuration */ bool dingux_ipu_reset(void) { #if defined(DINGUX_BETA) unsetenv(DINGUX_SCALING_MODE_ENVAR); unsetenv(DINGUX_SCALING_SHARPNESS_ENVAR); unsetenv(DINGUX_VIDEO_REFRESHRATE_ENVAR); return true; #else return dingux_ipu_set_scaling_mode(true, false) && dingux_ipu_set_filter_type(DINGUX_IPU_FILTER_BICUBIC); #endif } static int dingux_read_battery_sys_file(const char *path) { int64_t file_len = 0; char *file_buf = NULL; int sys_file_value = 0; /* Check whether file exists */ if (!path_is_valid(path)) goto error; /* Read file */ if (!filestream_read_file(path, (void**)&file_buf, &file_len) || (file_len == 0) || !file_buf) goto error; /* Convert to integer */ sys_file_value = atoi(file_buf); free(file_buf); file_buf = NULL; return sys_file_value; error: if (file_buf) { free(file_buf); file_buf = NULL; } return -1; } /* Fetches internal battery level */ int dingux_get_battery_level(void) { #if defined(DINGUX_BETA) /* Taken from https://github.com/OpenDingux/gmenu2x/blob/master/src/battery.cpp * No 'capacity' file in sysfs - Do a dumb approximation of the capacity * using the current voltage reported and the min/max voltages of the * battery */ int voltage_min = 0; int voltage_max = 0; int voltage_now = 0; voltage_min = dingux_read_battery_sys_file(DINGUX_BATTERY_VOLTAGE_MIN); if (voltage_min < 0) return -1; voltage_max = dingux_read_battery_sys_file(DINGUX_BATTERY_VOLTAGE_MAX); if (voltage_max < 0) return -1; voltage_now = dingux_read_battery_sys_file(DINGUX_BATTERY_VOLTAGE_NOW); if (voltage_now < 0) return -1; if ((voltage_max <= voltage_min) || (voltage_now < voltage_min)) return -1; return (int)(((voltage_now - voltage_min) * 100) / (voltage_max - voltage_min)); #else return dingux_read_battery_sys_file(DINGUX_BATTERY_CAPACITY_FILE); #endif } /* Fetches the path of the base 'retroarch' * directory */ void dingux_get_base_path(char *path, size_t len) { const char *home = NULL; #if defined(RS90) struct string_list *dir_list = NULL; #endif if (!path || (len < 1)) return; #if defined(RS90) /* The RS-90 home directory is located on the * device's internal storage. This has limited * space (a total of only 256MB), such that it * is impractical to store cores and user files * here. We therefore attempt to use a base * path on the external microsd card */ /* Get list of directories in /media */ dir_list = dir_list_new(DINGUX_RS90_MEDIA_PATH, NULL, true, true, false, false); if (dir_list) { size_t i; bool path_found = false; for (i = 0; i < dir_list->size; i++) { const char *dir_path = dir_list->elems[i].data; int dir_type = dir_list->elems[i].attr.i; /* Skip files and invalid entries */ if ((dir_type != RARCH_DIRECTORY) || string_is_empty(dir_path) || string_is_equal(dir_path, DINGUX_RS90_DATA_PATH)) continue; /* Build 'retroarch' subdirectory path */ snprintf(path, len, "%s%c%s", dir_path, PATH_DEFAULT_SLASH_C(), DINGUX_BASE_DIR); /* We can use this subdirectory path if: * - Directory corresponds to an unlabelled * microsd card * - Subdirectory already exists */ if (string_is_equal(dir_path, DINGUX_RS90_DEFAULT_SD_PATH) || path_is_directory(path)) { path_found = true; break; } } dir_list_free(dir_list); if (path_found) return; } #endif /* Get home directory */ home = getenv(DINGUX_HOME_ENVAR); /* If a home directory is found (which should * always be the case), base path is "$HOME/.retroarch" * > If home path is unset, use existing UNIX frontend * driver default of "retroarch" (this will ultimately * fail, but there is nothing else we can do...) */ if (home) snprintf(path, len, "%s%c%s", home, PATH_DEFAULT_SLASH_C(), DINGUX_BASE_DIR_HIDDEN); else strlcpy(path, DINGUX_BASE_DIR, len); }