diff --git a/configuration.c b/configuration.c index f27376525b..2edb598279 100644 --- a/configuration.c +++ b/configuration.c @@ -1185,6 +1185,47 @@ const char *config_get_midi_driver_options(void) return char_list_new_special(STRING_LIST_MIDI_DRIVERS, NULL); } +#ifdef HAVE_LAKKA +void config_set_timezone(char *timezone) +{ + setenv("TZ", timezone, 1); + tzset(); +} + +const char *config_get_all_timezones(void) +{ + return char_list_new_special(STRING_LIST_TIMEZONES, NULL); +} + +static void load_timezone(char *setting) +{ + char haystack[TIMEZONE_LENGTH+32]; + static char *needle = "TIMEZONE="; + size_t needle_len = strlen(needle); + + RFILE *tzfp = filestream_open(LAKKA_TIMEZONE_PATH, + RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE); + + if (tzfp != NULL) + { + filestream_gets(tzfp, haystack, sizeof(haystack)-1); + filestream_close(tzfp); + + char *start = strstr(haystack, needle); + + if (start != NULL) + snprintf(setting, TIMEZONE_LENGTH, "%s", start + needle_len); + else + snprintf(setting, TIMEZONE_LENGTH, "%s", DEFAULT_TIMEZONE); + } + else + snprintf(setting, TIMEZONE_LENGTH, "%s", DEFAULT_TIMEZONE); + + config_set_timezone(setting); +} +#endif + bool config_overlay_enable_default(void) { if (g_defaults.overlay_set) @@ -2347,6 +2388,7 @@ void config_set_defaults(void *data) configuration_set_bool(settings, settings->bools.bluetooth_enable, filestream_exists(LAKKA_BLUETOOTH_PATH)); configuration_set_bool(settings, settings->bools.localap_enable, false); + load_timezone(settings->arrays.timezone); #endif #ifdef HAVE_MENU diff --git a/configuration.h b/configuration.h index e03455679b..0d728a5be8 100644 --- a/configuration.h +++ b/configuration.h @@ -29,6 +29,10 @@ #include "input/input_defines.h" #include "led/led_defines.h" +#ifdef HAVE_LAKKA +#include "lakka.h" +#endif + #define configuration_set_float(settings, var, newvar) \ { \ settings->modified = true; \ @@ -381,6 +385,9 @@ typedef struct settings char ai_service_url[PATH_MAX_LENGTH]; char crt_switch_timings[255]; +#ifdef HAVE_LAKKA + char timezone[TIMEZONE_LENGTH]; +#endif } arrays; struct @@ -1011,6 +1018,11 @@ void config_save_file_salamander(void); settings_t *config_get_ptr(void); +#ifdef HAVE_LAKKA +const char *config_get_all_timezones(void); +void config_set_timezone(char *timezone); +#endif + RETRO_END_DECLS #endif diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index dca4d8c9ce..c2b03d1b7f 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -195,6 +195,10 @@ MSG_HASH( MENU_ENUM_LABEL_BLUETOOTH_ENABLE, "bluetooth_enable" ) +MSG_HASH( + MENU_ENUM_LABEL_TIMEZONE, + "timezone" + ) #endif MSG_HASH( MENU_ENUM_LABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 514aed48ae..39418993bf 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -2289,6 +2289,15 @@ int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "MIDI driver to use."); break; +#ifdef HAVE_LAKKA + case MENU_ENUM_LABEL_TIMEZONE: + snprintf(s, len, + "Displays a list of available timezones. After\n" + "selecting a time zone, time and date is adjusted\n" + "to the selected time zone. It assumes, that system/\n" + "hardware clock is set to UTC."); + break; +#endif case MENU_ENUM_LABEL_MIDI_INPUT: snprintf(s, len, "Sets the input device (driver specific).\n" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 4c354ac29c..0959680230 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -12218,6 +12218,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_LOCALAP_ENABLE, "Enable or disable Wi-Fi Access Point." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_TIMEZONE, + "Time zone" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_TIMEZONE, + "Select your time zone to adjust the date and time to your location." + ) MSG_HASH( MSG_LOCALAP_SWITCHING_OFF, "Switching off Wi-Fi Access Point." diff --git a/lakka.h b/lakka.h index f098a1080c..3f369ff05c 100644 --- a/lakka.h +++ b/lakka.h @@ -24,6 +24,10 @@ #define LAKKA_UPDATE_DIR "/storage/.update/" #define LAKKA_CONNMAN_DIR "/storage/.cache/connman/" #define LAKKA_LOCALAP_PATH "/storage/.cache/services/localap.conf" +#define LAKKA_TIMEZONE_PATH "/storage/.cache/timezone" + +#define DEFAULT_TIMEZONE "UTC" +#define TIMEZONE_LENGTH 255 #include "switch_performance_profiles.h" diff --git a/list_special.h b/list_special.h index cbdbf0b877..ae245be6e3 100644 --- a/list_special.h +++ b/list_special.h @@ -56,6 +56,9 @@ enum string_list_type STRING_LIST_RECORD_DRIVERS, STRING_LIST_MIDI_DRIVERS, STRING_LIST_SUPPORTED_CORES_PATHS, +#ifdef HAVE_LAKKA + STRING_LIST_TIMEZONES, +#endif STRING_LIST_SUPPORTED_CORES_NAMES }; diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 2b0d01e794..c734f81efe 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -1572,6 +1572,9 @@ static int menu_cbs_init_bind_get_string_representation_compare_label( case MENU_ENUM_LABEL_BLUETOOTH_DRIVER: case MENU_ENUM_LABEL_WIFI_DRIVER: case MENU_ENUM_LABEL_MENU_DRIVER: +#ifdef HAVE_LAKKA + case MENU_ENUM_LABEL_TIMEZONE: +#endif BIND_ACTION_GET_VALUE(cbs, menu_action_setting_disp_set_label); break; case MENU_ENUM_LABEL_CONNECT_BLUETOOTH: diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 4193cfe8d3..6a6da555e9 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -227,6 +227,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_ssh_enable, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_samba_enable, MENU_ENUM_SUBLABEL_SAMBA_ENABLE ) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_bluetooth_enable, MENU_ENUM_SUBLABEL_BLUETOOTH_ENABLE ) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_localap_enable, MENU_ENUM_SUBLABEL_LOCALAP_ENABLE ) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_timezone, MENU_ENUM_SUBLABEL_TIMEZONE) #endif DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_user_language, MENU_ENUM_SUBLABEL_USER_LANGUAGE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_max_swapchain_images, MENU_ENUM_SUBLABEL_VIDEO_MAX_SWAPCHAIN_IMAGES ) @@ -3907,6 +3908,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_LOCALAP_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_localap_enable); break; + case MENU_ENUM_LABEL_TIMEZONE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_timezone); + break; #endif case MENU_ENUM_LABEL_USER_LANGUAGE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_user_language); diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 2ef6228b7f..15bab6631c 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -7704,6 +7704,7 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_SAMBA_ENABLE, PARSE_ONLY_BOOL}, {MENU_ENUM_LABEL_BLUETOOTH_ENABLE, PARSE_ONLY_BOOL}, {MENU_ENUM_LABEL_LOCALAP_ENABLE, PARSE_ONLY_BOOL}, + {MENU_ENUM_LABEL_TIMEZONE, PARSE_ONLY_STRING_OPTIONS}, }; for (i = 0; i < ARRAY_SIZE(build_list); i++) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 18bf8eff50..97164655a3 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -7991,6 +7991,24 @@ static void localap_enable_toggle_change_handler(rarch_setting_t *setting) driver_wifi_tether_start_stop(*setting->value.target.boolean, LAKKA_LOCALAP_PATH); } + +static void timezone_change_handler(rarch_setting_t *setting) +{ + if (!setting) + return; + + config_set_timezone(setting->value.target.string); + + RFILE *tzfp = filestream_open(LAKKA_TIMEZONE_PATH, + RETRO_VFS_FILE_ACCESS_WRITE, + RETRO_VFS_FILE_ACCESS_HINT_NONE); + + if (tzfp != NULL) + { + filestream_printf(tzfp, "TIMEZONE=%s", setting->value.target.string); + filestream_close(tzfp); + } +} #endif static bool setting_append_list_input_player_options( @@ -18539,6 +18557,23 @@ static bool setting_append_list( SD_FLAG_NONE); (*list)[list_info->index - 1].change_handler = localap_enable_toggle_change_handler; + CONFIG_STRING_OPTIONS( + list, list_info, + settings->arrays.timezone, + sizeof(settings->arrays.timezone), + MENU_ENUM_LABEL_TIMEZONE, + MENU_ENUM_LABEL_VALUE_TIMEZONE, + DEFAULT_TIMEZONE, + config_get_all_timezones(), + &group_info, + &subgroup_info, + parent_group, + general_read_handler, + general_write_handler); + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_IS_DRIVER); + (*list)[list_info->index - 1].action_ok = setting_action_ok_mapped_string; + (*list)[list_info->index - 1].change_handler = timezone_change_handler; + END_SUB_GROUP(list, list_info, parent_group); END_GROUP(list, list_info, parent_group); #endif diff --git a/msg_hash.h b/msg_hash.h index 819411ec91..a7a987dd4b 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1826,6 +1826,7 @@ enum msg_hash_enums MENU_LABEL(SAMBA_ENABLE), MENU_LABEL(BLUETOOTH_ENABLE), MENU_LABEL(LOCALAP_ENABLE), + MENU_LABEL(TIMEZONE), #endif MENU_LABEL(NETPLAY_DELAY_FRAMES), MENU_LABEL(NETPLAY_PUBLIC_ANNOUNCE), diff --git a/retroarch.c b/retroarch.c index 0d4db54a19..82babc5b79 100644 --- a/retroarch.c +++ b/retroarch.c @@ -277,6 +277,10 @@ /* Forward declarations */ #include "retroarch_fwd_decls.h" +#ifdef HAVE_LAKKA +#include "lakka.h" +#endif + /* GLOBAL POINTER GETTERS */ #ifdef HAVE_NETWORKING @@ -8722,6 +8726,40 @@ struct string_list *string_list_new_special(enum string_list_type type, string_list_append(s, opt, attr); } break; +#ifdef HAVE_LAKKA + case STRING_LIST_TIMEZONES: + { + const char *opt = DEFAULT_TIMEZONE; + *len += strlen(opt) + 1; + string_list_append(s, opt, attr); + + FILE *zones_file = popen("grep -v ^# /usr/share/zoneinfo/zone.tab | " + "cut -f3 | " + "sort", "r"); + + if (zones_file != NULL) + { + char zone_desc[TIMEZONE_LENGTH]; + while (fgets(zone_desc, TIMEZONE_LENGTH, zones_file)) + { + size_t zone_desc_len = strlen(zone_desc); + + if (zone_desc_len > 0) + if (zone_desc[--zone_desc_len] == '\n') + zone_desc[zone_desc_len] = '\0'; + + if (strlen(zone_desc) > 0) + { + const char *opt = zone_desc; + *len += strlen(opt) + 1; + string_list_append(s, opt, attr); + } + } + pclose(zones_file); + } + } + break; +#endif case STRING_LIST_SUPPORTED_CORES_PATHS: core_info_get_list(&core_info_list);