From 9ea6f15c1df0a55c2fd2cbc566e21d09bdc0ab3d Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 31 Aug 2021 18:43:58 +0200 Subject: [PATCH] (Ozone) Forgot to add ozone.c file --- menu/drivers/ozone.c | 9988 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 9988 insertions(+) create mode 100644 menu/drivers/ozone.c diff --git a/menu/drivers/ozone.c b/menu/drivers/ozone.c new file mode 100644 index 0000000000..070c7e3e84 --- /dev/null +++ b/menu/drivers/ozone.c @@ -0,0 +1,9988 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2014-2017 - Jean-André Santoni + * Copyright (C) 2016-2019 - Brad Parker + * Copyright (C) 2018 - Alfredo Monclús + * Copyright (C) 2018-2020 - natinusala + * Copyright (C) 2019 - Patrick Scheurenbrand + * + * 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 + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "../../config.def.h" + +#if 0 +#include "../../discord/discord.h" +#endif + +#include "../menu_driver.h" +#include "../menu_screensaver.h" + +#ifdef HAVE_CHEEVOS +#include "../../cheevos/cheevos_menu.h" +#endif + +#include "../../gfx/gfx_animation.h" +#include "../../gfx/gfx_display.h" +#include "../../gfx/gfx_thumbnail_path.h" +#include "../../gfx/gfx_thumbnail.h" +#include "../../runtime_file.h" + +#include "../../input/input_osk.h" + +#include "../../configuration.h" +#include "../../content.h" +#include "../../core_info.h" +#include "../../verbosity.h" + +#define ANIMATION_PUSH_ENTRY_DURATION 166 +#define ANIMATION_CURSOR_DURATION 133 +#define ANIMATION_CURSOR_PULSE 500 + +#define FONT_SIZE_FOOTER 18 +#define FONT_SIZE_TITLE 36 +#define FONT_SIZE_TIME 22 +#define FONT_SIZE_ENTRIES_LABEL 24 +#define FONT_SIZE_ENTRIES_SUBLABEL 18 +#define FONT_SIZE_SIDEBAR 24 + +#define HEADER_HEIGHT 87 +#define FOOTER_HEIGHT 78 + +#define ENTRY_PADDING_HORIZONTAL_HALF 60 +#define ENTRY_PADDING_HORIZONTAL_FULL 150 +#define ENTRY_PADDING_VERTICAL 20 +#define ENTRY_HEIGHT 50 +#define ENTRY_SPACING 8 +#define ENTRY_ICON_SIZE 46 +#define ENTRY_ICON_PADDING 15 + +/* > 'SIDEBAR_WIDTH' must be kept in sync with + * menu driver metrics */ +#define SIDEBAR_WIDTH 408 +#define SIDEBAR_X_PADDING 40 +#define SIDEBAR_Y_PADDING 20 +#define SIDEBAR_ENTRY_HEIGHT 50 +#define SIDEBAR_ENTRY_Y_PADDING 10 +#define SIDEBAR_ENTRY_ICON_SIZE 46 +#define SIDEBAR_ENTRY_ICON_PADDING 15 +#define SIDEBAR_GRADIENT_HEIGHT 28 + +#define FULLSCREEN_THUMBNAIL_PADDING 48 + +#define CURSOR_SIZE 64 +/* Cursor becomes active when it moves more + * than CURSOR_ACTIVE_DELTA pixels (adjusted + * by current scale factor) */ +#define CURSOR_ACTIVE_DELTA 3 + +#define INTERVAL_OSK_CURSOR (0.5f * 1000000) + +#if defined(__APPLE__) +/* UTF-8 support is currently broken on Apple devices... */ +#define OZONE_TICKER_SPACER " | " +#else +/* + * UCN equivalent: "\u2003\u2022\u2003" */ +#define OZONE_TICKER_SPACER "\xE2\x80\x83\xE2\x80\xA2\xE2\x80\x83" +#endif + +#define OZONE_WIGGLE_DURATION 15 + +/* Returns true if specified entry is currently + * displayed on screen */ +/* Check whether selected item is already on screen */ +#define OZONE_ENTRY_ONSCREEN(ozone, idx) (((idx) >= (ozone)->first_onscreen_entry) && ((idx) <= (ozone)->last_onscreen_entry)) + +enum ozone_onscreen_entry_position_type +{ + OZONE_ONSCREEN_ENTRY_FIRST = 0, + OZONE_ONSCREEN_ENTRY_LAST, + OZONE_ONSCREEN_ENTRY_CENTRE +}; + +enum +{ + OZONE_SYSTEM_TAB_MAIN = 0, + OZONE_SYSTEM_TAB_SETTINGS, + OZONE_SYSTEM_TAB_HISTORY, + OZONE_SYSTEM_TAB_FAVORITES, + OZONE_SYSTEM_TAB_MUSIC, +#if defined(HAVE_FFMPEG) || defined(HAVE_MPV) + OZONE_SYSTEM_TAB_VIDEO, +#endif +#ifdef HAVE_IMAGEVIEWER + OZONE_SYSTEM_TAB_IMAGES, +#endif +#ifdef HAVE_NETWORKING + OZONE_SYSTEM_TAB_NETPLAY, +#endif + OZONE_SYSTEM_TAB_ADD, +#if defined(HAVE_LIBRETRODB) + OZONE_SYSTEM_TAB_EXPLORE, +#endif + + /* End of this enum - use the last one to determine num of possible tabs */ + OZONE_SYSTEM_TAB_LAST +}; + +enum OZONE_TEXTURE +{ + OZONE_TEXTURE_RETROARCH = 0, + OZONE_TEXTURE_CURSOR_BORDER, +#if 0 + OZONE_TEXTURE_DISCORD_OWN_AVATAR, +#endif + OZONE_TEXTURE_LAST +}; + +enum OZONE_THEME_TEXTURES +{ + OZONE_THEME_TEXTURE_SWITCH = 0, + OZONE_THEME_TEXTURE_CHECK, + + OZONE_THEME_TEXTURE_CURSOR_NO_BORDER, + OZONE_THEME_TEXTURE_CURSOR_STATIC, + + OZONE_THEME_TEXTURE_LAST +}; + +enum OZONE_TAB_TEXTURES +{ + OZONE_TAB_TEXTURE_MAIN_MENU = 0, + OZONE_TAB_TEXTURE_SETTINGS, + OZONE_TAB_TEXTURE_HISTORY, + OZONE_TAB_TEXTURE_FAVORITES, + OZONE_TAB_TEXTURE_MUSIC, + OZONE_TAB_TEXTURE_VIDEO, + OZONE_TAB_TEXTURE_IMAGE, + OZONE_TAB_TEXTURE_NETWORK, + OZONE_TAB_TEXTURE_SCAN_CONTENT, + + OZONE_TAB_TEXTURE_LAST +}; + +enum +{ + OZONE_ENTRIES_ICONS_TEXTURE_MAIN_MENU = 0, + OZONE_ENTRIES_ICONS_TEXTURE_SETTINGS, + OZONE_ENTRIES_ICONS_TEXTURE_HISTORY, + OZONE_ENTRIES_ICONS_TEXTURE_FAVORITES, + OZONE_ENTRIES_ICONS_TEXTURE_MUSICS, +#if defined(HAVE_FFMPEG) || defined(HAVE_MPV) + OZONE_ENTRIES_ICONS_TEXTURE_MOVIES, +#endif +#ifdef HAVE_NETWORKING + OZONE_ENTRIES_ICONS_TEXTURE_NETPLAY, + OZONE_ENTRIES_ICONS_TEXTURE_ROOM, + OZONE_ENTRIES_ICONS_TEXTURE_ROOM_LAN, + OZONE_ENTRIES_ICONS_TEXTURE_ROOM_RELAY, +#endif +#ifdef HAVE_IMAGEVIEWER + OZONE_ENTRIES_ICONS_TEXTURE_IMAGES, +#endif + OZONE_ENTRIES_ICONS_TEXTURE_SETTING, + OZONE_ENTRIES_ICONS_TEXTURE_SUBSETTING, + OZONE_ENTRIES_ICONS_TEXTURE_ARROW, + OZONE_ENTRIES_ICONS_TEXTURE_RUN, + OZONE_ENTRIES_ICONS_TEXTURE_CLOSE, + OZONE_ENTRIES_ICONS_TEXTURE_RESUME, + OZONE_ENTRIES_ICONS_TEXTURE_SAVESTATE, + OZONE_ENTRIES_ICONS_TEXTURE_LOADSTATE, + OZONE_ENTRIES_ICONS_TEXTURE_UNDO, + OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO, + OZONE_ENTRIES_ICONS_TEXTURE_BLUETOOTH, + OZONE_ENTRIES_ICONS_TEXTURE_WIFI, + OZONE_ENTRIES_ICONS_TEXTURE_CORE_OPTIONS, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_REMAPPING_OPTIONS, + OZONE_ENTRIES_ICONS_TEXTURE_CHEAT_OPTIONS, + OZONE_ENTRIES_ICONS_TEXTURE_DISK_OPTIONS, + OZONE_ENTRIES_ICONS_TEXTURE_SHADER_OPTIONS, + OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENT_LIST, + OZONE_ENTRIES_ICONS_TEXTURE_SCREENSHOT, + OZONE_ENTRIES_ICONS_TEXTURE_RELOAD, + OZONE_ENTRIES_ICONS_TEXTURE_RENAME, + OZONE_ENTRIES_ICONS_TEXTURE_FILE, + OZONE_ENTRIES_ICONS_TEXTURE_FOLDER, + OZONE_ENTRIES_ICONS_TEXTURE_ZIP, + OZONE_ENTRIES_ICONS_TEXTURE_FAVORITE, + OZONE_ENTRIES_ICONS_TEXTURE_ADD_FAVORITE, + OZONE_ENTRIES_ICONS_TEXTURE_MUSIC, + OZONE_ENTRIES_ICONS_TEXTURE_IMAGE, + OZONE_ENTRIES_ICONS_TEXTURE_MOVIE, + OZONE_ENTRIES_ICONS_TEXTURE_CORE, + OZONE_ENTRIES_ICONS_TEXTURE_RDB, + OZONE_ENTRIES_ICONS_TEXTURE_CURSOR, + OZONE_ENTRIES_ICONS_TEXTURE_SWITCH_ON, + OZONE_ENTRIES_ICONS_TEXTURE_SWITCH_OFF, + OZONE_ENTRIES_ICONS_TEXTURE_CLOCK, + OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_FULL, + OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_CHARGING, + OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_80, + OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_60, + OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_40, + OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_20, + OZONE_ENTRIES_ICONS_TEXTURE_POINTER, + OZONE_ENTRIES_ICONS_TEXTURE_ADD, + OZONE_ENTRIES_ICONS_TEXTURE_DISC, + OZONE_ENTRIES_ICONS_TEXTURE_KEY, + OZONE_ENTRIES_ICONS_TEXTURE_KEY_HOVER, + OZONE_ENTRIES_ICONS_TEXTURE_DIALOG_SLICE, + OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENTS, + OZONE_ENTRIES_ICONS_TEXTURE_AUDIO, + OZONE_ENTRIES_ICONS_TEXTURE_EXIT, + OZONE_ENTRIES_ICONS_TEXTURE_FRAMESKIP, + OZONE_ENTRIES_ICONS_TEXTURE_INFO, + OZONE_ENTRIES_ICONS_TEXTURE_HELP, + OZONE_ENTRIES_ICONS_TEXTURE_NETWORK, + OZONE_ENTRIES_ICONS_TEXTURE_POWER, + OZONE_ENTRIES_ICONS_TEXTURE_SAVING, + OZONE_ENTRIES_ICONS_TEXTURE_UPDATER, + OZONE_ENTRIES_ICONS_TEXTURE_VIDEO, + OZONE_ENTRIES_ICONS_TEXTURE_RECORD, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SETTINGS, + OZONE_ENTRIES_ICONS_TEXTURE_MIXER, + OZONE_ENTRIES_ICONS_TEXTURE_LOG, + OZONE_ENTRIES_ICONS_TEXTURE_OSD, + OZONE_ENTRIES_ICONS_TEXTURE_UI, + OZONE_ENTRIES_ICONS_TEXTURE_USER, + OZONE_ENTRIES_ICONS_TEXTURE_PRIVACY, + OZONE_ENTRIES_ICONS_TEXTURE_LATENCY, + OZONE_ENTRIES_ICONS_TEXTURE_DRIVERS, + OZONE_ENTRIES_ICONS_TEXTURE_PLAYLIST, + OZONE_ENTRIES_ICONS_TEXTURE_QUICKMENU, + OZONE_ENTRIES_ICONS_TEXTURE_REWIND, + OZONE_ENTRIES_ICONS_TEXTURE_OVERLAY, + OZONE_ENTRIES_ICONS_TEXTURE_OVERRIDE, + OZONE_ENTRIES_ICONS_TEXTURE_NOTIFICATIONS, + OZONE_ENTRIES_ICONS_TEXTURE_STREAM, + OZONE_ENTRIES_ICONS_TEXTURE_SHUTDOWN, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_U, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_D, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_L, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_R, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_U, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_D, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_L, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_R, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_P, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SELECT, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_START, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_U, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_D, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_L, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_R, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LB, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_RB, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LT, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_RT, + OZONE_ENTRIES_ICONS_TEXTURE_CHECKMARK, + OZONE_ENTRIES_ICONS_TEXTURE_MENU_ADD, + OZONE_ENTRIES_ICONS_TEXTURE_BRIGHTNESS, + OZONE_ENTRIES_ICONS_TEXTURE_PAUSE, + OZONE_ENTRIES_ICONS_TEXTURE_MENU_APPLY_TOGGLE, + OZONE_ENTRIES_ICONS_TEXTURE_MENU_APPLY_COG, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_ADC, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BIND_ALL, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_MOUSE, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LGUN, + OZONE_ENTRIES_ICONS_TEXTURE_INPUT_TURBO, + OZONE_ENTRIES_ICONS_TEXTURE_LAST +}; + +/* This structure holds all objects + metadata + * corresponding to a particular font */ +typedef struct +{ + font_data_t *font; + video_font_raster_block_t raster_block; /* ptr alignment */ + int glyph_width; + int wideglyph_width; + int line_height; + int line_ascender; + int line_centre_offset; +} ozone_font_data_t; + +/* Container for a footer text label */ +typedef struct +{ + const char *str; + int width; +} ozone_footer_label_t; + +typedef struct ozone_theme +{ + /* Background color */ + float background[16]; + float *background_libretro_running; + + /* Float colors for quads and icons */ + float header_footer_separator[16]; + float text[16]; + float selection[16]; + float selection_border[16]; + float entries_border[16]; + float entries_icon[16]; + float text_selected[16]; + float message_background[16]; + + /* RGBA colors for text */ + uint32_t text_rgba; + uint32_t text_selected_rgba; + uint32_t text_sublabel_rgba; + + /* Screensaver 'tint' (RGB24) */ + uint32_t screensaver_tint; + + /* Sidebar color */ + float *sidebar_background; + float *sidebar_top_gradient; + float *sidebar_bottom_gradient; + + /* + Fancy cursor colors + */ + float *cursor_border_0; + float *cursor_border_1; + + uintptr_t textures[OZONE_THEME_TEXTURE_LAST]; + + const char *name; +} ozone_theme_t; + + +struct ozone_handle +{ + menu_input_pointer_t pointer; /* retro_time_t alignment */ + + ozone_theme_t *theme; + gfx_thumbnail_path_data_t *thumbnail_path_data; + char *pending_message; + file_list_t selection_buf_old; /* ptr alignment */ + file_list_t horizontal_list; /* console tabs */ /* ptr alignment */ + menu_screensaver_t *screensaver; + + struct + { + ozone_font_data_t footer; + ozone_font_data_t title; + ozone_font_data_t time; + ozone_font_data_t entries_label; + ozone_font_data_t entries_sublabel; + ozone_font_data_t sidebar; + } fonts; + + void (*word_wrap)(char *dst, size_t dst_size, const char *src, + int line_width, int wideglyph_width, unsigned max_lines); + + struct + { + ozone_footer_label_t ok; + ozone_footer_label_t back; + ozone_footer_label_t search; + ozone_footer_label_t fullscreen_thumbs; + ozone_footer_label_t metadata_toggle; + } footer_labels; + + struct + { + gfx_thumbnail_t right; /* uintptr_t alignment */ + gfx_thumbnail_t left; /* uintptr_t alignment */ + } thumbnails; + uintptr_t textures[OZONE_THEME_TEXTURE_LAST]; + uintptr_t icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LAST]; + uintptr_t tab_textures[OZONE_TAB_TEXTURE_LAST]; + + size_t categories_selection_ptr; /* active tab id */ + size_t categories_active_idx_old; + + size_t selection; /* currently selected entry */ + size_t selection_old; /* previously selected entry (for fancy animation) */ + size_t selection_old_list; + size_t fullscreen_thumbnail_selection; + size_t num_search_terms_old; + size_t pointer_categories_selection; + size_t first_onscreen_entry; + size_t last_onscreen_entry; + size_t first_onscreen_category; + size_t last_onscreen_category; + + int depth; + + struct + { + int header_height; + int footer_height; + + int entry_padding_horizontal_half; + int entry_padding_horizontal_full; + int entry_padding_vertical; + int entry_height; + int entry_spacing; + int entry_icon_size; + int entry_icon_padding; + + int sidebar_width_normal; + int sidebar_width_collapsed; + + int sidebar_padding_horizontal; + int sidebar_padding_vertical; + int sidebar_entry_padding_vertical; + int sidebar_entry_height; + int sidebar_entry_icon_size; + int sidebar_entry_icon_padding; + int sidebar_gradient_height; + + int cursor_size; + + int thumbnail_bar_width; + int fullscreen_thumbnail_padding; + + int spacer_1px; + int spacer_2px; + int spacer_3px; + int spacer_5px; + } dimensions; + + unsigned footer_labels_language; + unsigned last_width; + unsigned last_height; + unsigned entries_height; + unsigned theme_dynamic_cursor_state; /* 0 -> 1 -> 0 -> 1 [...] */ + unsigned selection_core_name_lines; + unsigned selection_lastplayed_lines; + unsigned old_list_offset_y; + + float dimensions_sidebar_width; /* animated field */ + float sidebar_offset; + float last_scale_factor; + float pure_white[16]; + + struct + { + float cursor_alpha; + float scroll_y; + float scroll_y_sidebar; + + float list_alpha; + + float messagebox_alpha; + + float sidebar_text_alpha; + float thumbnail_bar_position; + + float fullscreen_thumbnail_alpha; + float left_thumbnail_alpha; + } animations; + + struct + { + float selection_border[16]; + float selection[16]; + float entries_border[16]; + float entries_icon[16]; + float entries_checkmark[16]; + float cursor_alpha[16]; + + float cursor_border[16]; + float message_background[16]; + } theme_dynamic; + + float scroll_old; + + int16_t pointer_active_delta; + int16_t cursor_x_old; + int16_t cursor_y_old; + + uint8_t system_tab_end; + uint8_t tabs[OZONE_SYSTEM_TAB_LAST]; + + char title[PATH_MAX_LENGTH]; + + char assets_path[PATH_MAX_LENGTH]; + char png_path[PATH_MAX_LENGTH]; + char icons_path[PATH_MAX_LENGTH]; + char tab_path[PATH_MAX_LENGTH]; + char fullscreen_thumbnail_label[255]; + + char selection_core_name[255]; + char selection_playtime[255]; + char selection_lastplayed[255]; + char selection_entry_enumeration[255]; + + bool cursor_in_sidebar; + bool cursor_in_sidebar_old; + + bool fade_direction; /* false = left to right, true = right to left */ + + bool draw_sidebar; + bool empty_playlist; + + bool osk_cursor; /* true = display it, false = don't */ + bool messagebox_state; + bool messagebox_state_old; + bool should_draw_messagebox; + + bool need_compute; + bool draw_old_list; + bool has_all_assets; + + bool is_playlist; + bool is_playlist_old; + + bool pointer_in_sidebar; + bool last_pointer_in_sidebar; + bool show_cursor; + bool show_screensaver; + bool cursor_mode; + bool sidebar_collapsed; + bool show_thumbnail_bar; + bool pending_hide_thumbnail_bar; + bool fullscreen_thumbnails_available; + bool show_fullscreen_thumbnails; + bool selection_core_is_viewer; + + bool force_metadata_display; + + bool is_db_manager_list; + bool is_file_list; + bool is_quick_menu; + bool first_frame; + + struct + { + retro_time_t start_time; + float amplitude; + enum menu_action direction; + bool wiggling; + } cursor_wiggle_state; +}; + +/* If you change this struct, also + change ozone_alloc_node and + ozone_copy_node */ +typedef struct ozone_node +{ + char *fullpath; /* Entry fullpath */ + char *console_name; /* Console tab name */ + uintptr_t icon; /* Console tab icon */ + uintptr_t content_icon; /* console content icon */ + unsigned height; /* Entry height */ + unsigned position_y; /* Entry position Y */ + unsigned sublabel_lines; /* Entry sublabel lines */ + bool wrap; /* Wrap entry? */ +} ozone_node_t; + +typedef struct ozone_handle ozone_handle_t; + +static const char *OZONE_TEXTURES_FILES[OZONE_TEXTURE_LAST] = { + "retroarch", + "cursor_border" +}; + +static const char *OZONE_TAB_TEXTURES_FILES[OZONE_TAB_TEXTURE_LAST] = { + "retroarch", + "settings", + "history", + "favorites", + "music", + "video", + "image", + "netplay", + "add" +}; + +static const enum msg_hash_enums ozone_system_tabs_value[OZONE_SYSTEM_TAB_LAST] = { + MENU_ENUM_LABEL_VALUE_MAIN_MENU, + MENU_ENUM_LABEL_VALUE_SETTINGS_TAB, + MENU_ENUM_LABEL_VALUE_HISTORY_TAB, + MENU_ENUM_LABEL_VALUE_FAVORITES_TAB, + MENU_ENUM_LABEL_VALUE_MUSIC_TAB, +#if defined(HAVE_FFMPEG) || defined(HAVE_MPV) + MENU_ENUM_LABEL_VALUE_VIDEO_TAB, +#endif +#ifdef HAVE_IMAGEVIEWER + MENU_ENUM_LABEL_VALUE_IMAGES_TAB, +#endif +#ifdef HAVE_NETWORKING + MENU_ENUM_LABEL_VALUE_NETPLAY_TAB, +#endif +#ifdef HAVE_LIBRETRODB + MENU_ENUM_LABEL_VALUE_ADD_TAB, + MENU_ENUM_LABEL_VALUE_EXPLORE_TAB +#else + MENU_ENUM_LABEL_VALUE_ADD_TAB +#endif +}; + +static const enum menu_settings_type ozone_system_tabs_type[OZONE_SYSTEM_TAB_LAST] = { + MENU_SETTINGS, + MENU_SETTINGS_TAB, + MENU_HISTORY_TAB, + MENU_FAVORITES_TAB, + MENU_MUSIC_TAB, +#if defined(HAVE_FFMPEG) || defined(HAVE_MPV) + MENU_VIDEO_TAB, +#endif +#ifdef HAVE_IMAGEVIEWER + MENU_IMAGES_TAB, +#endif +#ifdef HAVE_NETWORKING + MENU_NETPLAY_TAB, +#endif +#ifdef HAVE_LIBRETRODB + MENU_ADD_TAB, + MENU_EXPLORE_TAB +#else + MENU_ADD_TAB +#endif +}; + +static const enum msg_hash_enums ozone_system_tabs_idx[OZONE_SYSTEM_TAB_LAST] = { + MENU_ENUM_LABEL_MAIN_MENU, + MENU_ENUM_LABEL_SETTINGS_TAB, + MENU_ENUM_LABEL_HISTORY_TAB, + MENU_ENUM_LABEL_FAVORITES_TAB, + MENU_ENUM_LABEL_MUSIC_TAB, +#if defined(HAVE_FFMPEG) || defined(HAVE_MPV) + MENU_ENUM_LABEL_VIDEO_TAB, +#endif +#ifdef HAVE_IMAGEVIEWER + MENU_ENUM_LABEL_IMAGES_TAB, +#endif +#ifdef HAVE_NETWORKING + MENU_ENUM_LABEL_NETPLAY_TAB, +#endif +#ifdef HAVE_LIBRETRODB + MENU_ENUM_LABEL_ADD_TAB, + MENU_ENUM_LABEL_EXPLORE_TAB +#else + MENU_ENUM_LABEL_ADD_TAB +#endif +}; + +static const unsigned ozone_system_tabs_icons[OZONE_SYSTEM_TAB_LAST] = { + OZONE_TAB_TEXTURE_MAIN_MENU, + OZONE_TAB_TEXTURE_SETTINGS, + OZONE_TAB_TEXTURE_HISTORY, + OZONE_TAB_TEXTURE_FAVORITES, + OZONE_TAB_TEXTURE_MUSIC, +#if defined(HAVE_FFMPEG) || defined(HAVE_MPV) + OZONE_TAB_TEXTURE_VIDEO, +#endif +#ifdef HAVE_IMAGEVIEWER + OZONE_TAB_TEXTURE_IMAGE, +#endif +#ifdef HAVE_NETWORKING + OZONE_TAB_TEXTURE_NETWORK, +#endif + OZONE_TAB_TEXTURE_SCAN_CONTENT +}; + +static const char *OZONE_THEME_TEXTURES_FILES[OZONE_THEME_TEXTURE_LAST] = { + "switch", + "check", + + "cursor_noborder", + "cursor_static" +}; + +static float ozone_sidebar_gradient_top_light[16] = { + 0.94, 0.94, 0.94, 1.00, + 0.94, 0.94, 0.94, 1.00, + 0.922, 0.922, 0.922, 1.00, + 0.922, 0.922, 0.922, 1.00, +}; + +static float ozone_sidebar_gradient_bottom_light[16] = { + 0.922, 0.922, 0.922, 1.00, + 0.922, 0.922, 0.922, 1.00, + 0.94, 0.94, 0.94, 1.00, + 0.94, 0.94, 0.94, 1.00, +}; + +static float ozone_sidebar_gradient_top_dark[16] = { + 0.2, 0.2, 0.2, 1.00, + 0.2, 0.2, 0.2, 1.00, + 0.18, 0.18, 0.18, 1.00, + 0.18, 0.18, 0.18, 1.00, +}; + +static float ozone_sidebar_gradient_bottom_dark[16] = { + 0.18, 0.18, 0.18, 1.00, + 0.18, 0.18, 0.18, 1.00, + 0.2, 0.2, 0.2, 1.00, + 0.2, 0.2, 0.2, 1.00, +}; + +static float ozone_sidebar_gradient_top_nord[16] = { + 0.2078431f, 0.2352941f, 0.2901961f, 1.0f, + 0.2078431f, 0.2352941f, 0.2901961f, 1.0f, + 0.1921569f, 0.2196078f, 0.2705882f, 0.9f, + 0.1921569f, 0.2196078f, 0.2705882f, 0.9f, +}; + +static float ozone_sidebar_gradient_bottom_nord[16] = { + 0.1921569f, 0.2196078f, 0.2705882f, 0.9f, + 0.1921569f, 0.2196078f, 0.2705882f, 0.9f, + 0.2078431f, 0.2352941f, 0.2901961f, 1.0f, + 0.2078431f, 0.2352941f, 0.2901961f, 1.0f, +}; + +static float ozone_sidebar_gradient_top_gruvbox_dark[16] = { + 0.1960784f, 0.1882353f, 0.1843137f, 1.0f, + 0.1960784f, 0.1882353f, 0.1843137f, 1.0f, + 0.1686275f, 0.1686275f, 0.1686275f, 0.9f, + 0.1686275f, 0.1686275f, 0.1686275f, 0.9f, +}; + +static float ozone_sidebar_gradient_bottom_gruvbox_dark[16] = { + 0.1686275f, 0.1686275f, 0.1686275f, 0.9f, + 0.1686275f, 0.1686275f, 0.1686275f, 0.9f, + 0.1960784f, 0.1882353f, 0.1843137f, 1.0f, + 0.1960784f, 0.1882353f, 0.1843137f, 1.0f, +}; + +static float ozone_sidebar_background_light[16] = { + 0.94, 0.94, 0.94, 1.00, + 0.94, 0.94, 0.94, 1.00, + 0.94, 0.94, 0.94, 1.00, + 0.94, 0.94, 0.94, 1.00, +}; + +static float ozone_sidebar_background_dark[16] = { + 0.2, 0.2, 0.2, 1.00, + 0.2, 0.2, 0.2, 1.00, + 0.2, 0.2, 0.2, 1.00, + 0.2, 0.2, 0.2, 1.00, +}; + +static float ozone_sidebar_background_nord[16] = { + 0.2078431f, 0.2352941f, 0.2901961f, 1.0f, + 0.2078431f, 0.2352941f, 0.2901961f, 1.0f, + 0.2078431f, 0.2352941f, 0.2901961f, 1.0f, + 0.2078431f, 0.2352941f, 0.2901961f, 1.0f, +}; + +static float ozone_sidebar_background_gruvbox_dark[16] = { + 0.1960784f, 0.1882353f, 0.1843137f, 1.0f, + 0.1960784f, 0.1882353f, 0.1843137f, 1.0f, + 0.1960784f, 0.1882353f, 0.1843137f, 1.0f, + 0.1960784f, 0.1882353f, 0.1843137f, 1.0f, +}; + +static float ozone_background_libretro_running_light[16] = { + 0.690, 0.690, 0.690, 0.75, + 0.690, 0.690, 0.690, 0.75, + 0.922, 0.922, 0.922, 1.0, + 0.922, 0.922, 0.922, 1.0 +}; + +static float ozone_background_libretro_running_dark[16] = { + 0.176, 0.176, 0.176, 0.75, + 0.176, 0.176, 0.176, 0.75, + 0.178, 0.178, 0.178, 1.0, + 0.178, 0.178, 0.178, 1.0, +}; + +static float ozone_background_libretro_running_nord[16] = { + 0.1803922f, 0.2039216f, 0.2509804f, 0.75f, + 0.1803922f, 0.2039216f, 0.2509804f, 0.75f, + 0.1803922f, 0.2039216f, 0.2509804f, 1.0f, + 0.1803922f, 0.2039216f, 0.2509804f, 1.0f, +}; + +static float ozone_background_libretro_running_gruvbox_dark[16] = { + 0.1568627f, 0.1568627f, 0.1568627f, 0.75f, + 0.1568627f, 0.1568627f, 0.1568627f, 0.75f, + 0.1568627f, 0.1568627f, 0.1568627f, 1.0f, + 0.1568627f, 0.1568627f, 0.1568627f, 1.0f, +}; + +static float ozone_background_libretro_running_boysenberry[16] = { + 0.27058823529, 0.09803921568, 0.14117647058, 0.75f, + 0.27058823529, 0.09803921568, 0.14117647058, 0.75f, + 0.27058823529, 0.09803921568, 0.14117647058, 0.75f, + 0.27058823529, 0.09803921568, 0.14117647058, 0.75f, +}; + +static float ozone_sidebar_background_boysenberry[16] = { + 0.27058823529, 0.09803921568, 0.14117647058, 1.00, + 0.27058823529, 0.09803921568, 0.14117647058, 1.00, + 0.27058823529, 0.09803921568, 0.14117647058, 1.00, + 0.27058823529, 0.09803921568, 0.14117647058, 1.00, +}; + +static float ozone_sidebar_gradient_top_boysenberry[16] = { + 0.27058823529, 0.09803921568, 0.14117647058, 1.00, + 0.27058823529, 0.09803921568, 0.14117647058, 1.00, + 0.19215686274, 0.0, 0.04705882352, 1.00, + 0.19215686274, 0.0, 0.04705882352, 1.00, +}; + +static float ozone_sidebar_gradient_bottom_boysenberry[16] = { + 0.19215686274, 0.0, 0.04705882352, 1.00, + 0.19215686274, 0.0, 0.04705882352, 1.00, + 0.27058823529, 0.09803921568, 0.14117647058, 1.00, + 0.27058823529, 0.09803921568, 0.14117647058, 1.00, +}; + +static float ozone_background_libretro_running_hacking_the_kernel[16] = { + 0.0, 0.0666666f, 0.0, 0.75f, + 0.0, 0.0666666f, 0.0, 0.75f, + 0.0, 0.0666666f, 0.0, 1.0f, + 0.0, 0.0666666f, 0.0, 1.0f, +}; + +static float ozone_sidebar_background_hacking_the_kernel[16] = { + 0.0, 0.1333333f, 0.0, 1.0f, + 0.0, 0.1333333f, 0.0, 1.0f, + 0.0, 0.1333333f, 0.0, 1.0f, + 0.0, 0.1333333f, 0.0, 1.0f, +}; + +static float ozone_sidebar_gradient_top_hacking_the_kernel[16] = { + 0.0, 0.13333333, 0.0, 1.0f, + 0.0, 0.13333333, 0.0, 1.0f, + 0.0, 0.13333333, 0.0, 1.0f, + 0.0, 0.13333333, 0.0, 1.0f, +}; + +static float ozone_sidebar_gradient_bottom_hacking_the_kernel[16] = { + 0.0, 0.0666666f, 0.0, 1.0f, + 0.0, 0.0666666f, 0.0, 1.0f, + 0.0, 0.13333333, 0.0, 1.0f, + 0.0, 0.13333333, 0.0, 1.0f, +}; + +static float ozone_background_libretro_running_twilight_zone[16] = { + 0.0078431, 0.0, 0.0156862, 0.75f, + 0.0078431, 0.0, 0.0156862, 0.75f, + 0.0078431, 0.0, 0.0156862, 1.0f, + 0.0078431, 0.0, 0.0156862, 1.0f, +}; + +static float ozone_sidebar_background_twilight_zone[16] = { + 0.0078431, 0.0, 0.0156862, 1.0f, + 0.0078431, 0.0, 0.0156862, 1.0f, + 0.0078431, 0.0, 0.0156862, 1.0f, + 0.0078431, 0.0, 0.0156862, 1.0f, +}; + +static float ozone_sidebar_gradient_top_twilight_zone[16] = { + 0.0078431, 0.0, 0.0156862, 1.0f, + 0.0078431, 0.0, 0.0156862, 1.0f, + 0.0078431, 0.0, 0.0156862, 1.0f, + 0.0078431, 0.0, 0.0156862, 1.0f, +}; + +static float ozone_sidebar_gradient_bottom_twilight_zone[16] = { + 0.0078431, 0.0, 0.0156862, 1.0f, + 0.0078431, 0.0, 0.0156862, 1.0f, + 0.0078431, 0.0, 0.0156862, 1.0f, + 0.0078431, 0.0, 0.0156862, 1.0f, +}; + +static float ozone_background_libretro_running_dracula[16] = { + 0.1568627, 0.1647058, 0.2117647, 0.75f, + 0.1568627, 0.1647058, 0.2117647, 0.75f, + 0.1568627, 0.1647058, 0.2117647, 1.0f, + 0.1568627, 0.1647058, 0.2117647, 1.0f, +}; + +static float ozone_sidebar_background_dracula[16] = { + 0.2666666, 0.2784314, 0.3529412, 1.0f, + 0.2666666, 0.2784314, 0.3529412, 1.0f, + 0.2666666, 0.2784314, 0.3529412, 1.0f, + 0.2666666, 0.2784314, 0.3529412, 1.0f, +}; + +static float ozone_sidebar_gradient_top_dracula[16] = { + 0.2666666, 0.2784314, 0.3529412, 1.0f, + 0.2666666, 0.2784314, 0.3529412, 1.0f, + 0.2666666, 0.2784314, 0.3529412, 1.0f, + 0.2666666, 0.2784314, 0.3529412, 1.0f, +}; + +static float ozone_sidebar_gradient_bottom_dracula[16] = { + 0.2666666, 0.2784314, 0.3529412, 1.0f, + 0.2666666, 0.2784314, 0.3529412, 1.0f, + 0.2666666, 0.2784314, 0.3529412, 1.0f, + 0.2666666, 0.2784314, 0.3529412, 1.0f, +}; + + +static float ozone_border_0_light[16] = COLOR_HEX_TO_FLOAT(0x50EFD9, 1.00); +static float ozone_border_1_light[16] = COLOR_HEX_TO_FLOAT(0x0DB6D5, 1.00); + +static float ozone_border_0_dark[16] = COLOR_HEX_TO_FLOAT(0x198AC6, 1.00); +static float ozone_border_1_dark[16] = COLOR_HEX_TO_FLOAT(0x89F1F2, 1.00); + +static float ozone_border_0_nord[16] = COLOR_HEX_TO_FLOAT(0x5E81AC, 1.0f); +static float ozone_border_1_nord[16] = COLOR_HEX_TO_FLOAT(0x88C0D0, 1.0f); + +static float ozone_border_0_gruvbox_dark[16] = COLOR_HEX_TO_FLOAT(0xAF3A03, 1.0f); +static float ozone_border_1_gruvbox_dark[16] = COLOR_HEX_TO_FLOAT(0xFE8019, 1.0f); + +static float ozone_border_0_boysenberry[16] = COLOR_HEX_TO_FLOAT(0x50EFD9, 1.00); +static float ozone_border_1_boysenberry[16] = COLOR_HEX_TO_FLOAT(0x0DB6D5, 1.00); + +static float ozone_border_0_hacking_the_kernel[16] = COLOR_HEX_TO_FLOAT(0x008C00, 1.0f); +static float ozone_border_1_hacking_the_kernel[16] = COLOR_HEX_TO_FLOAT(0x00E000, 1.0f); + +static float ozone_border_0_twilight_zone[16] = COLOR_HEX_TO_FLOAT(0xC3A0E0, 1.0f); +static float ozone_border_1_twilight_zone[16] = COLOR_HEX_TO_FLOAT(0x9B61CC, 1.0f); + +static float ozone_border_0_dracula[16] = COLOR_HEX_TO_FLOAT(0xC3A0E0, 1.0f); +static float ozone_border_1_dracula[16] = COLOR_HEX_TO_FLOAT(0x9B61CC, 1.0f); + +ozone_theme_t ozone_theme_light = { + COLOR_HEX_TO_FLOAT(0xEBEBEB, 1.00), /* background */ + ozone_background_libretro_running_light, /* background_libretro_running */ + + COLOR_HEX_TO_FLOAT(0x2B2B2B, 1.00), /* header_footer_separator */ + COLOR_HEX_TO_FLOAT(0x333333, 1.00), /* text */ + COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.00), /* selection */ + COLOR_HEX_TO_FLOAT(0x10BEC5, 1.00), /* selection_border */ + COLOR_HEX_TO_FLOAT(0xCDCDCD, 1.00), /* entries_border */ + COLOR_HEX_TO_FLOAT(0x333333, 1.00), /* entries_icon */ + COLOR_HEX_TO_FLOAT(0x374CFF, 1.00), /* text_selected */ + COLOR_HEX_TO_FLOAT(0xF0F0F0, 1.00), /* message_background */ + + 0x333333FF, /* text_rgba */ + 0x374CFFFF, /* text_selected_rgba */ + 0x878787FF, /* text_sublabel_rgba */ + + /* Screensaver 'tint' (RGB24) */ + 0xEBEBEB, /* screensaver_tint */ + + ozone_sidebar_background_light, /* sidebar_background */ + ozone_sidebar_gradient_top_light, /* sidebar_top_gradient */ + ozone_sidebar_gradient_bottom_light, /* sidebar_bottom_gradient */ + + ozone_border_0_light, /* cursor_border_0 */ + ozone_border_1_light, /* cursor_border_1 */ + + {0}, /* textures */ + + "light" /* name */ +}; + +ozone_theme_t ozone_theme_dark = { + COLOR_HEX_TO_FLOAT(0x2D2D2D, 1.00), /* background */ + ozone_background_libretro_running_dark, /* background_libretro_running */ + + COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.00), /* header_footer_separator */ + COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.00), /* text */ + COLOR_HEX_TO_FLOAT(0x212227, 1.00), /* selection */ + COLOR_HEX_TO_FLOAT(0x2DA3CB, 1.00), /* selection_border */ + COLOR_HEX_TO_FLOAT(0x51514F, 1.00), /* entries_border */ + COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.00), /* entries_icon */ + COLOR_HEX_TO_FLOAT(0x00D9AE, 1.00), /* text_selected */ + COLOR_HEX_TO_FLOAT(0x464646, 1.00), /* message_background */ + + 0xFFFFFFFF, /* text_rgba */ + 0x00FFC5FF, /* text_selected_rgba */ + 0x9F9FA1FF, /* text_sublabel_rgba */ + + /* Screensaver 'tint' (RGB24) */ + 0xFFFFFF, /* screensaver_tint */ + + ozone_sidebar_background_dark, /* sidebar_background */ + ozone_sidebar_gradient_top_dark, /* sidebar_top_gradient */ + ozone_sidebar_gradient_bottom_dark, /* sidebar_bottom_gradient */ + + ozone_border_0_dark, /* cursor_border_0 */ + ozone_border_1_dark, /* cursor_border_1 */ + + {0}, /* textures */ + + "dark" /* name */ +}; + +ozone_theme_t ozone_theme_nord = { + /* Background color */ + COLOR_HEX_TO_FLOAT(0x2E3440, 1.0f), /* background */ + ozone_background_libretro_running_nord, /* background_libretro_running */ + + /* Float colors for quads and icons */ + COLOR_HEX_TO_FLOAT(0xD8DEE9, 1.0f), /* header_footer_separator */ + COLOR_HEX_TO_FLOAT(0xECEFF4, 1.0f), /* text */ + COLOR_HEX_TO_FLOAT(0x232730, 1.0f), /* selection */ + COLOR_HEX_TO_FLOAT(0x73A1BE, 1.0f), /* selection_border */ + COLOR_HEX_TO_FLOAT(0x4C566A, 1.0f), /* entries_border */ + COLOR_HEX_TO_FLOAT(0xE5E9F0, 1.0f), /* entries_icon */ + COLOR_HEX_TO_FLOAT(0xA9C791, 1.0f), /* text_selected */ + COLOR_HEX_TO_FLOAT(0x434C5E, 1.0f), /* message_background */ + + /* RGBA colors for text */ + 0xECEFF4FF, /* text_rgba */ + 0xA9C791FF, /* text_selected_rgba */ + 0x8FBCBBFF, /* text_sublabel_rgba */ + + /* Screensaver 'tint' (RGB24) */ + 0xECEFF4, /* screensaver_tint */ + + /* Sidebar color */ + ozone_sidebar_background_nord, /* sidebar_background */ + ozone_sidebar_gradient_top_nord, /* sidebar_top_gradient */ + ozone_sidebar_gradient_bottom_nord, /* sidebar_bottom_gradient */ + + /* Fancy cursor colors */ + ozone_border_0_nord, /* cursor_border_0 */ + ozone_border_1_nord, /* cursor_border_1 */ + + {0}, /* textures */ + + "nord" /* name */ +}; + +ozone_theme_t ozone_theme_gruvbox_dark = { + /* Background color */ + COLOR_HEX_TO_FLOAT(0x282828, 1.0f), /* background */ + ozone_background_libretro_running_gruvbox_dark, /* background_libretro_running */ + + /* Float colors for quads and icons */ + COLOR_HEX_TO_FLOAT(0xD5C4A1, 1.0f), /* header_footer_separator */ + COLOR_HEX_TO_FLOAT(0xEBDBB2, 1.0f), /* text */ + COLOR_HEX_TO_FLOAT(0x1D2021, 1.0f), /* selection */ + COLOR_HEX_TO_FLOAT(0xD75D0E, 1.0f), /* selection_border */ + COLOR_HEX_TO_FLOAT(0x665C54, 1.0f), /* entries_border */ + COLOR_HEX_TO_FLOAT(0xEBDBB2, 1.0f), /* entries_icon */ + COLOR_HEX_TO_FLOAT(0x8EC07C, 1.0f), /* text_selected */ + COLOR_HEX_TO_FLOAT(0x32302F, 1.0f), /* message_background */ + + /* RGBA colors for text */ + 0xEBDBB2FF, /* text_rgba */ + 0x8EC07CFF, /* text_selected_rgba */ + 0xD79921FF, /* text_sublabel_rgba */ + + /* Screensaver 'tint' (RGB24) */ + 0xEBDBB2, /* screensaver_tint */ + + /* Sidebar color */ + ozone_sidebar_background_gruvbox_dark, /* sidebar_background */ + ozone_sidebar_gradient_top_gruvbox_dark, /* sidebar_top_gradient */ + ozone_sidebar_gradient_bottom_gruvbox_dark, /* sidebar_bottom_gradient */ + + /* Fancy cursor colors */ + ozone_border_0_gruvbox_dark, /* cursor_border_0 */ + ozone_border_1_gruvbox_dark, /* cursor_border_1 */ + + {0}, /* textures */ + + "gruvbox_dark" /* name */ +}; + +ozone_theme_t ozone_theme_boysenberry = { + /* Background color */ + COLOR_HEX_TO_FLOAT(0x31000C, 1.0f), /* background */ + ozone_background_libretro_running_boysenberry, /* background_libretro_running */ + + /* Float colors for quads and icons */ + COLOR_HEX_TO_FLOAT(0x85535F, 1.0f), /* header_footer_separator */ + COLOR_HEX_TO_FLOAT(0xEBDBB2, 1.0f), /* text */ + COLOR_HEX_TO_FLOAT(0x4E2A35, 1.0f), /* selection */ + COLOR_HEX_TO_FLOAT(0xD599FF, 1.0f), /* selection_border */ + COLOR_HEX_TO_FLOAT(0x73434C, 1.0f), /* entries_border */ + COLOR_HEX_TO_FLOAT(0xFEBCFF, 1.0f), /* entries_icon */ + COLOR_HEX_TO_FLOAT(0xD599FF, 1.0f), /* text_selected */ + COLOR_HEX_TO_FLOAT(0x32302F, 1.0f), /* message_background */ + + /* RGBA colors for text */ + 0xFEBCFFFF, /* text_rgba */ + 0xFEBCFFFF, /* text_selected_rgba */ + 0xD599FFFF, /* text_sublabel_rgba */ + + /* Screensaver 'tint' (RGB24) */ + 0xFEBCFF, /* screensaver_tint */ + + /* Sidebar color */ + ozone_sidebar_background_boysenberry, /* sidebar_background */ + ozone_sidebar_gradient_top_boysenberry, /* sidebar_top_gradient */ + ozone_sidebar_gradient_bottom_boysenberry, /* sidebar_bottom_gradient */ + + /* Fancy cursor colors */ + ozone_border_0_boysenberry, /* cursor_border_0 */ + ozone_border_1_boysenberry, /* cursor_border_1 */ + + {0}, /* textures */ + + "boysenberry" /* name */ +}; + +ozone_theme_t ozone_theme_hacking_the_kernel = { + /* Background color */ + COLOR_HEX_TO_FLOAT(0x001100, 1.0f), /* background */ + ozone_background_libretro_running_hacking_the_kernel, /* background_libretro_running */ + + /* Float colors for quads and icons */ + COLOR_HEX_TO_FLOAT(0x17C936, 1.0f), /* header_footer_separator */ + COLOR_HEX_TO_FLOAT(0x00FF29, 1.0f), /* text */ + COLOR_HEX_TO_FLOAT(0x003400, 1.0f), /* selection */ + COLOR_HEX_TO_FLOAT(0x1BDA3C, 1.0f), /* selection_border */ + COLOR_HEX_TO_FLOAT(0x008C00, 0.1f), /* entries_border */ + COLOR_HEX_TO_FLOAT(0x00FF00, 1.0f), /* entries_icon */ + COLOR_HEX_TO_FLOAT(0x8EC07C, 1.0f), /* text_selected */ + COLOR_HEX_TO_FLOAT(0x0D0E0F, 1.0f), /* message_background */ + + /* RGBA colors for text */ + 0x00E528FF, /* text_rgba */ + 0x83FF83FF, /* text_selected_rgba */ + 0x53E63DFF, /* text_sublabel_rgba */ + + /* Screensaver 'tint' (RGB24) */ + 0x00E528, /* screensaver_tint */ + + /* Sidebar color */ + ozone_sidebar_background_hacking_the_kernel, /* sidebar_background */ + ozone_sidebar_gradient_top_hacking_the_kernel, /* sidebar_top_gradient */ + ozone_sidebar_gradient_bottom_hacking_the_kernel, /* sidebar_bottom_gradient */ + + /* Fancy cursor colors */ + ozone_border_0_hacking_the_kernel, /* cursor_border_0 */ + ozone_border_1_hacking_the_kernel, /* cursor_border_1 */ + + {0}, /* textures */ + + "hacking_the_kernel" /* name */ +}; + +ozone_theme_t ozone_theme_twilight_zone = { + /* Background color */ + COLOR_HEX_TO_FLOAT(0x020004, 1.0f), /* background */ + ozone_background_libretro_running_twilight_zone, /* background_libretro_running */ + + /* Float colors for quads and icons */ + COLOR_HEX_TO_FLOAT(0x5B5069, 1.0f), /* header_footer_separator */ + COLOR_HEX_TO_FLOAT(0xF7F0FA, 1.0f), /* text */ + COLOR_HEX_TO_FLOAT(0x232038, 1.0f), /* selection */ + COLOR_HEX_TO_FLOAT(0x9B61CC, 1.0f), /* selection_border */ + COLOR_HEX_TO_FLOAT(0xC27AFF, 1.0f), /* entries_border */ + COLOR_HEX_TO_FLOAT(0xFFFFFF, 1.0f), /* entries_icon */ + COLOR_HEX_TO_FLOAT(0xB78CC8, 1.0f), /* text_selected */ + COLOR_HEX_TO_FLOAT(0xB78CC8, 1.0f), /* message_background */ + + /* RGBA colors for text */ + 0xFDFCFEFF, /* text_rgba */ + 0xB78CC8FF, /* text_selected_rgba */ + 0x9A6C99FF, /* text_sublabel_rgba */ + + /* Screensaver 'tint' (RGB24) */ + 0xFDFCFE, /* screensaver_tint */ + + /* Sidebar color */ + ozone_sidebar_background_twilight_zone, /* sidebar_background */ + ozone_sidebar_gradient_top_twilight_zone, /* sidebar_top_gradient */ + ozone_sidebar_gradient_bottom_twilight_zone, /* sidebar_bottom_gradient */ + + /* Fancy cursor colors */ + ozone_border_0_twilight_zone, /* cursor_border_0 */ + ozone_border_1_twilight_zone, /* cursor_border_1 */ + + {0}, /* textures */ + + "twilight_zone" /* name */ +}; + +ozone_theme_t ozone_theme_dracula = { + /* Background color */ + COLOR_HEX_TO_FLOAT(0x282A36, 1.0f), /* background */ + ozone_background_libretro_running_dracula, /* background_libretro_running */ + + /* Float colors for quads and icons */ + COLOR_HEX_TO_FLOAT(0x44475A, 1.0f), /* header_footer_separator */ + COLOR_HEX_TO_FLOAT(0xF8F8F2, 1.0f), /* text */ + COLOR_HEX_TO_FLOAT(0x44475A, 1.0f), /* selection */ + COLOR_HEX_TO_FLOAT(0xBD93F9, 1.0f), /* selection_border */ + COLOR_HEX_TO_FLOAT(0x44475A, 1.0f), /* entries_border */ + COLOR_HEX_TO_FLOAT(0xF8F8F2, 1.0f), /* entries_icon */ + COLOR_HEX_TO_FLOAT(0xF8F8F2, 1.0f), /* text_selected */ + COLOR_HEX_TO_FLOAT(0x6272A4, 1.0f), /* message_background */ + + /* RGBA colors for text */ + 0xF8F8F2FF, /* text_rgba */ + 0xFF79C6FF, /* text_selected_rgba */ + 0xBD93F9FF, /* text_sublabel_rgba */ + + /* Screensaver 'tint' (RGB24) */ + 0xF8F8F2, /* screensaver_tint */ + + /* Sidebar color */ + ozone_sidebar_background_dracula, /* sidebar_background */ + ozone_sidebar_gradient_top_dracula, /* sidebar_top_gradient */ + ozone_sidebar_gradient_bottom_dracula, /* sidebar_bottom_gradient */ + + /* Fancy cursor colors */ + ozone_border_0_dracula, /* cursor_border_0 */ + ozone_border_1_dracula, /* cursor_border_1 */ + + {0}, /* textures */ + + "dracula" /* name */ +}; + + +ozone_theme_t *ozone_themes[] = { + &ozone_theme_light, + &ozone_theme_dark, + &ozone_theme_nord, + &ozone_theme_gruvbox_dark, + &ozone_theme_boysenberry, + &ozone_theme_hacking_the_kernel, + &ozone_theme_twilight_zone, + &ozone_theme_dracula + +}; + +static const unsigned ozone_themes_count = sizeof(ozone_themes) / sizeof(ozone_themes[0]); +/* TODO/FIXME - global variables referenced outside */ +static unsigned ozone_last_color_theme = 0; +static bool +ozone_last_use_preferred_system_color_theme = false; +static ozone_theme_t *ozone_default_theme = &ozone_theme_dark; /* also used as a tag for cursor animation */ +/* Enable runtime configuration of framebuffer + * opacity */ +static float ozone_last_framebuffer_opacity = -1.0f; + +/* Forward declarations */ +static void ozone_cursor_animation_cb(void *userdata); + +static INLINE unsigned ozone_count_lines(const char *str) +{ + unsigned c = 0; + unsigned lines = 1; + + for (c = 0; str[c]; c++) + lines += (str[c] == '\n'); + return lines; +} + +static void ozone_animate_cursor(ozone_handle_t *ozone, + float *dst, float *target) +{ + int i; + gfx_animation_ctx_entry_t entry; + + entry.easing_enum = EASING_OUT_QUAD; + entry.tag = (uintptr_t)&ozone_default_theme; + entry.duration = ANIMATION_CURSOR_PULSE; + entry.userdata = ozone; + + for (i = 0; i < 16; i++) + { + if (i == 3 || i == 7 || i == 11 || i == 15) + continue; + + if (i == 14) + entry.cb = ozone_cursor_animation_cb; + else + entry.cb = NULL; + + entry.subject = &dst[i]; + entry.target_value = target[i]; + + gfx_animation_push(&entry); + } +} + +static void ozone_cursor_animation_cb(void *userdata) +{ + float *target = NULL; + ozone_handle_t *ozone = (ozone_handle_t*) userdata; + + switch (ozone->theme_dynamic_cursor_state) + { + case 0: + target = ozone->theme->cursor_border_1; + break; + case 1: + target = ozone->theme->cursor_border_0; + break; + } + + ozone->theme_dynamic_cursor_state = + (ozone->theme_dynamic_cursor_state + 1) % 2; + + ozone_animate_cursor(ozone, ozone->theme_dynamic.cursor_border, target); +} + +static void ozone_restart_cursor_animation(ozone_handle_t *ozone) +{ + uintptr_t tag = (uintptr_t)&ozone_default_theme; + + if (!ozone->has_all_assets) + return; + + ozone->theme_dynamic_cursor_state = 1; + memcpy(ozone->theme_dynamic.cursor_border, + ozone->theme->cursor_border_0, + sizeof(ozone->theme_dynamic.cursor_border)); + gfx_animation_kill_by_tag(&tag); + + ozone_animate_cursor(ozone, + ozone->theme_dynamic.cursor_border, + ozone->theme->cursor_border_1); +} + +static void ozone_set_color_theme(ozone_handle_t *ozone, unsigned color_theme) +{ + ozone_theme_t *theme = ozone_default_theme; + + if (!ozone) + return; + + switch (color_theme) + { + case OZONE_COLOR_THEME_BASIC_WHITE: + theme = &ozone_theme_light; + break; + case OZONE_COLOR_THEME_BASIC_BLACK: + theme = &ozone_theme_dark; + break; + case OZONE_COLOR_THEME_NORD: + theme = &ozone_theme_nord; + break; + case OZONE_COLOR_THEME_GRUVBOX_DARK: + theme = &ozone_theme_gruvbox_dark; + break; + case OZONE_COLOR_THEME_BOYSENBERRY: + theme = &ozone_theme_boysenberry; + break; + case OZONE_COLOR_THEME_HACKING_THE_KERNEL: + theme = &ozone_theme_hacking_the_kernel; + break; + case OZONE_COLOR_THEME_TWILIGHT_ZONE: + theme = &ozone_theme_twilight_zone; + break; + case OZONE_COLOR_THEME_DRACULA: + theme = &ozone_theme_dracula; + break; + default: + break; + } + + ozone->theme = theme; + + memcpy(ozone->theme_dynamic.selection_border, ozone->theme->selection_border, sizeof(ozone->theme_dynamic.selection_border)); + memcpy(ozone->theme_dynamic.selection, ozone->theme->selection, sizeof(ozone->theme_dynamic.selection)); + memcpy(ozone->theme_dynamic.entries_border, ozone->theme->entries_border, sizeof(ozone->theme_dynamic.entries_border)); + memcpy(ozone->theme_dynamic.entries_icon, ozone->theme->entries_icon, sizeof(ozone->theme_dynamic.entries_icon)); + memcpy(ozone->theme_dynamic.entries_checkmark, ozone->pure_white, sizeof(ozone->theme_dynamic.entries_checkmark)); + memcpy(ozone->theme_dynamic.cursor_alpha, ozone->pure_white, sizeof(ozone->theme_dynamic.cursor_alpha)); + memcpy(ozone->theme_dynamic.message_background, ozone->theme->message_background, sizeof(ozone->theme_dynamic.message_background)); + + ozone_restart_cursor_animation(ozone); + + ozone_last_color_theme = color_theme; +} + +static unsigned ozone_get_system_theme(void) +{ +#ifdef HAVE_LIBNX + unsigned ret = 0; + if (R_SUCCEEDED(setsysInitialize())) + { + ColorSetId theme; + setsysGetColorSetId(&theme); + ret = (theme == ColorSetId_Dark) ? 1 : 0; + setsysExit(); + } + + return ret; +#else + return DEFAULT_OZONE_COLOR_THEME; +#endif +} + +static void ozone_set_background_running_opacity( + ozone_handle_t *ozone, float framebuffer_opacity) +{ + static float background_running_alpha_top = 1.0f; + static float background_running_alpha_bottom = 0.75f; + float *background = NULL; + + if (!ozone || !ozone->theme->background_libretro_running) + return; + + background = + ozone->theme->background_libretro_running; + + /* When content is running, background is a + * gradient that from top to bottom transitions + * from maximum to minimum opacity + * > RetroArch default 'framebuffer_opacity' + * is 0.900. At this setting: + * - Background top has an alpha of 1.0 + * - Background bottom has an alpha of 0.75 */ + background_running_alpha_top = framebuffer_opacity / 0.9f; + background_running_alpha_top = (background_running_alpha_top > 1.0f) ? + 1.0f : (background_running_alpha_top < 0.0f) ? + 0.0f : background_running_alpha_top; + + background_running_alpha_bottom = (2.5f * framebuffer_opacity) - 1.5f; + background_running_alpha_bottom = (background_running_alpha_bottom > 1.0f) ? + 1.0f : (background_running_alpha_bottom < 0.0f) ? + 0.0f : background_running_alpha_bottom; + + background[11] = background_running_alpha_top; + background[15] = background_running_alpha_top; + background[3] = background_running_alpha_bottom; + background[7] = background_running_alpha_bottom; + + ozone_last_framebuffer_opacity = framebuffer_opacity; +} + +static uintptr_t ozone_entries_icon_get_texture(ozone_handle_t *ozone, + enum msg_hash_enums enum_idx, unsigned type, bool active) +{ + switch (enum_idx) + { + case MENU_ENUM_LABEL_LOAD_DISC: + case MENU_ENUM_LABEL_DUMP_DISC: + case MENU_ENUM_LABEL_DISC_INFORMATION: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DISC]; + case MENU_ENUM_LABEL_CORE_OPTIONS: + case MENU_ENUM_LABEL_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE_OPTIONS]; + case MENU_ENUM_LABEL_ADD_TO_FAVORITES: + case MENU_ENUM_LABEL_ADD_TO_FAVORITES_PLAYLIST: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ADD_FAVORITE]; + case MENU_ENUM_LABEL_RESET_CORE_ASSOCIATION: + case MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_UNDO]; + case MENU_ENUM_LABEL_CORE_INPUT_REMAPPING_OPTIONS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_REMAPPING_OPTIONS]; + case MENU_ENUM_LABEL_CORE_CHEAT_OPTIONS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CHEAT_OPTIONS]; + case MENU_ENUM_LABEL_DISK_OPTIONS: + case MENU_ENUM_LABEL_DISK_TRAY_EJECT: + case MENU_ENUM_LABEL_DISK_TRAY_INSERT: + case MENU_ENUM_LABEL_DISK_IMAGE_APPEND: + case MENU_ENUM_LABEL_DISK_INDEX: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DISK_OPTIONS]; + case MENU_ENUM_LABEL_SHADER_OPTIONS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SHADER_OPTIONS]; + case MENU_ENUM_LABEL_ACHIEVEMENT_LIST: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENT_LIST]; + case MENU_ENUM_LABEL_ACHIEVEMENT_LIST_HARDCORE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENT_LIST]; + case MENU_ENUM_LABEL_STATE_SLOT: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING]; + case MENU_ENUM_LABEL_SAVE_STATE: + case MENU_ENUM_LABEL_CORE_CREATE_BACKUP: + case MENU_ENUM_LABEL_GAME_SPECIFIC_CORE_OPTIONS_CREATE: + case MENU_ENUM_LABEL_FOLDER_SPECIFIC_CORE_OPTIONS_CREATE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SAVESTATE]; + case MENU_ENUM_LABEL_LOAD_STATE: + case MENU_ENUM_LABEL_CORE_RESTORE_BACKUP_LIST: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LOADSTATE]; + case MENU_ENUM_LABEL_PARENT_DIRECTORY: + case MENU_ENUM_LABEL_UNDO_LOAD_STATE: + case MENU_ENUM_LABEL_UNDO_SAVE_STATE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_UNDO]; + case MENU_ENUM_LABEL_TAKE_SCREENSHOT: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SCREENSHOT]; + case MENU_ENUM_LABEL_DELETE_ENTRY: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOSE]; + case MENU_ENUM_LABEL_RESTART_CONTENT: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD]; + case MENU_ENUM_LABEL_RENAME_ENTRY: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RENAME]; + case MENU_ENUM_LABEL_RESUME_CONTENT: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RESUME]; + case MENU_ENUM_LABEL_FAVORITES: + case MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FOLDER]; + case MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RDB]; + + /* Menu collection submenus*/ + case MENU_ENUM_LABEL_PLAYLISTS_TAB: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ZIP]; + case MENU_ENUM_LABEL_GOTO_FAVORITES: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FAVORITE]; + case MENU_ENUM_LABEL_GOTO_IMAGES: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_IMAGE]; + case MENU_ENUM_LABEL_GOTO_VIDEO: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MOVIE]; + case MENU_ENUM_LABEL_GOTO_MUSIC: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MUSIC]; + case MENU_ENUM_LABEL_GOTO_EXPLORE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RDB]; + + /* Menu icons */ + case MENU_ENUM_LABEL_CONTENT_SETTINGS: + case MENU_ENUM_LABEL_UPDATE_ASSETS: + case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_GAME: + case MENU_ENUM_LABEL_REMAP_FILE_SAVE_GAME: + case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_GLOBAL: + case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_GAME: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_QUICKMENU]; + case MENU_ENUM_LABEL_START_CORE: + case MENU_ENUM_LABEL_CHEAT_START_OR_CONT: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RUN]; + case MENU_ENUM_LABEL_CORE_LIST: + case MENU_ENUM_LABEL_SIDELOAD_CORE_LIST: + case MENU_ENUM_LABEL_CORE_SETTINGS: + case MENU_ENUM_LABEL_CORE_UPDATER_LIST: + case MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES: + case MENU_ENUM_LABEL_SWITCH_INSTALLED_CORES_PFD: + case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_CORE: + case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CORE: + case MENU_ENUM_LABEL_REMAP_FILE_SAVE_CORE: + case MENU_ENUM_LABEL_SET_CORE_ASSOCIATION: + case MENU_ENUM_LABEL_CORE_INFORMATION: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE]; + case MENU_ENUM_LABEL_LOAD_CONTENT_LIST: + case MENU_ENUM_LABEL_SUBSYSTEM_SETTINGS: + case MENU_ENUM_LABEL_SCAN_FILE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FILE]; + case MENU_ENUM_LABEL_ONLINE_UPDATER: + case MENU_ENUM_LABEL_UPDATER_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_UPDATER]; + case MENU_ENUM_LABEL_UPDATE_LAKKA: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MAIN_MENU]; + case MENU_ENUM_LABEL_UPDATE_CHEATS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CHEAT_OPTIONS]; + case MENU_ENUM_LABEL_THUMBNAILS_UPDATER_LIST: + case MENU_ENUM_LABEL_PL_THUMBNAILS_UPDATER_LIST: + case MENU_ENUM_LABEL_DOWNLOAD_PL_ENTRY_THUMBNAILS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_IMAGE]; + case MENU_ENUM_LABEL_UPDATE_OVERLAYS: + case MENU_ENUM_LABEL_ONSCREEN_OVERLAY_SETTINGS: +#ifdef HAVE_VIDEO_LAYOUT + case MENU_ENUM_LABEL_ONSCREEN_VIDEO_LAYOUT_SETTINGS: +#endif + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_OVERLAY]; + case MENU_ENUM_LABEL_UPDATE_CG_SHADERS: + case MENU_ENUM_LABEL_UPDATE_GLSL_SHADERS: + case MENU_ENUM_LABEL_UPDATE_SLANG_SHADERS: + case MENU_ENUM_LABEL_AUTO_SHADERS_ENABLE: + case MENU_ENUM_LABEL_VIDEO_SHADER_PARAMETERS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SHADER_OPTIONS]; + case MENU_ENUM_LABEL_INFORMATION: + case MENU_ENUM_LABEL_INFORMATION_LIST: + case MENU_ENUM_LABEL_SYSTEM_INFORMATION: + case MENU_ENUM_LABEL_UPDATE_CORE_INFO_FILES: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INFO]; + case MENU_ENUM_LABEL_EXPLORE_TAB: + case MENU_ENUM_LABEL_UPDATE_DATABASES: + case MENU_ENUM_LABEL_DATABASE_MANAGER_LIST: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RDB]; + case MENU_ENUM_LABEL_CURSOR_MANAGER_LIST: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CURSOR]; + case MENU_ENUM_LABEL_HELP_LIST: + case MENU_ENUM_LABEL_HELP_CONTROLS: + case MENU_ENUM_LABEL_HELP_LOADING_CONTENT: + case MENU_ENUM_LABEL_HELP_SCANNING_CONTENT: + case MENU_ENUM_LABEL_HELP_WHAT_IS_A_CORE: + case MENU_ENUM_LABEL_HELP_CHANGE_VIRTUAL_GAMEPAD: + case MENU_ENUM_LABEL_HELP_AUDIO_VIDEO_TROUBLESHOOTING: + case MENU_ENUM_LABEL_HELP_SEND_DEBUG_INFO: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_HELP]; + case MENU_ENUM_LABEL_QUIT_RETROARCH: + case MENU_ENUM_LABEL_BLOCK_SRAM_OVERWRITE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_EXIT]; + /* Settings icons*/ + case MENU_ENUM_LABEL_DRIVER_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DRIVERS]; + case MENU_ENUM_LABEL_VIDEO_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_VIDEO]; + case MENU_ENUM_LABEL_AUDIO_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_AUDIO]; + case MENU_ENUM_LABEL_AUDIO_MIXER_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MIXER]; + case MENU_ENUM_LABEL_SCREEN_RESOLUTION: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SUBSETTING]; + case MENU_ENUM_LABEL_INPUT_SETTINGS: + case MENU_ENUM_LABEL_UPDATE_AUTOCONFIG_PROFILES: + case MENU_ENUM_LABEL_INPUT_USER_1_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_2_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_3_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_4_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_5_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_6_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_7_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_8_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_9_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_10_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_11_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_12_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_13_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_14_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_15_BINDS: + case MENU_ENUM_LABEL_INPUT_USER_16_BINDS: + case MENU_ENUM_LABEL_START_NET_RETROPAD: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SETTINGS]; + case MENU_ENUM_LABEL_INPUT_TURBO_FIRE_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_TURBO]; + case MENU_ENUM_LABEL_LATENCY_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LATENCY]; + case MENU_ENUM_LABEL_SAVING_SETTINGS: + case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG: + case MENU_ENUM_LABEL_SAVE_NEW_CONFIG: + case MENU_ENUM_LABEL_CONFIG_SAVE_ON_EXIT: + case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE: + case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_AS: + case MENU_ENUM_LABEL_CHEAT_FILE_SAVE_AS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SAVING]; + case MENU_ENUM_LABEL_LOGGING_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LOG]; + case MENU_ENUM_LABEL_FRAME_THROTTLE_SETTINGS: + case MENU_ENUM_LABEL_FASTFORWARD_RATIO: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FRAMESKIP]; + case MENU_ENUM_LABEL_QUICK_MENU_START_RECORDING: + case MENU_ENUM_LABEL_RECORDING_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RECORD]; + case MENU_ENUM_LABEL_QUICK_MENU_START_STREAMING: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_STREAM]; + case MENU_ENUM_LABEL_QUICK_MENU_STOP_STREAMING: + case MENU_ENUM_LABEL_QUICK_MENU_STOP_RECORDING: + case MENU_ENUM_LABEL_CHEAT_DELETE_ALL: + case MENU_ENUM_LABEL_REMAP_FILE_REMOVE_CORE: + case MENU_ENUM_LABEL_REMAP_FILE_REMOVE_GAME: + case MENU_ENUM_LABEL_REMAP_FILE_REMOVE_CONTENT_DIR: + case MENU_ENUM_LABEL_CORE_DELETE: + case MENU_ENUM_LABEL_DELETE_PLAYLIST: + case MENU_ENUM_LABEL_CORE_DELETE_BACKUP_LIST: + case MENU_ENUM_LABEL_VIDEO_FILTER_REMOVE: + case MENU_ENUM_LABEL_AUDIO_DSP_PLUGIN_REMOVE: + case MENU_ENUM_LABEL_GAME_SPECIFIC_CORE_OPTIONS_REMOVE: + case MENU_ENUM_LABEL_FOLDER_SPECIFIC_CORE_OPTIONS_REMOVE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOSE]; + case MENU_ENUM_LABEL_CORE_OPTIONS_RESET: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_UNDO]; + case MENU_ENUM_LABEL_CORE_OPTIONS_FLUSH: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FILE]; + case MENU_ENUM_LABEL_CORE_LOCK: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE]; + case MENU_ENUM_LABEL_ONSCREEN_DISPLAY_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_OSD]; + case MENU_ENUM_LABEL_SHOW_WIMP: + case MENU_ENUM_LABEL_USER_INTERFACE_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_UI]; +#ifdef HAVE_LAKKA_SWITCH + case MENU_ENUM_LABEL_SWITCH_GPU_PROFILE: +#endif +#if defined(HAVE_LAKKA_SWITCH) || defined(HAVE_LIBNX) + case MENU_ENUM_LABEL_SWITCH_CPU_PROFILE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_POWER]; +#endif + case MENU_ENUM_LABEL_POWER_MANAGEMENT_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_POWER]; + case MENU_ENUM_LABEL_RETRO_ACHIEVEMENTS_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENTS]; + case MENU_ENUM_LABEL_NETWORK_INFORMATION: + case MENU_ENUM_LABEL_NETWORK_SETTINGS: + case MENU_ENUM_LABEL_WIFI_SETTINGS: + case MENU_ENUM_LABEL_NETWORK_INFO_ENTRY: + case MENU_ENUM_LABEL_NETWORK_HOSTING_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_NETWORK]; + case MENU_ENUM_LABEL_BLUETOOTH_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_BLUETOOTH]; + case MENU_ENUM_LABEL_PLAYLIST_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_PLAYLIST]; + case MENU_ENUM_LABEL_USER_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_USER]; + case MENU_ENUM_LABEL_DIRECTORY_SETTINGS: + case MENU_ENUM_LABEL_ADD_CONTENT_LIST: + case MENU_ENUM_LABEL_SCAN_DIRECTORY: + case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST: + case MENU_ENUM_LABEL_REMAP_FILE_SAVE_CONTENT_DIR: + case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CONTENT_DIR: + case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_PARENT: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FOLDER]; + case MENU_ENUM_LABEL_PRIVACY_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_PRIVACY]; + + case MENU_ENUM_LABEL_REWIND_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_REWIND]; + case MENU_ENUM_LABEL_QUICK_MENU_OVERRIDE_OPTIONS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_OVERRIDE]; + case MENU_ENUM_LABEL_ONSCREEN_NOTIFICATIONS_SETTINGS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_NOTIFICATIONS]; +#ifdef HAVE_NETWORKING + case MENU_ENUM_LABEL_NETPLAY_ENABLE_HOST: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RUN]; + case MENU_ENUM_LABEL_NETPLAY_DISCONNECT: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOSE]; + case MENU_ENUM_LABEL_NETPLAY_ENABLE_CLIENT: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ROOM]; + case MENU_ENUM_LABEL_NETPLAY_REFRESH_ROOMS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD]; +#endif + case MENU_ENUM_LABEL_REBOOT: + case MENU_ENUM_LABEL_RESET_TO_DEFAULT_CONFIG: + case MENU_ENUM_LABEL_CHEAT_RELOAD_CHEATS: + case MENU_ENUM_LABEL_RESTART_RETROARCH: + case MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE: + case MENU_ENUM_LABEL_AUTOSAVE_INTERVAL: + case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_SETTINGS: + case MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD]; + case MENU_ENUM_LABEL_SHUTDOWN: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SHUTDOWN]; + case MENU_ENUM_LABEL_CONFIGURATIONS: + case MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS: + case MENU_ENUM_LABEL_REMAP_FILE_LOAD: + case MENU_ENUM_LABEL_AUTO_OVERRIDES_ENABLE: + case MENU_ENUM_LABEL_AUTO_REMAPS_ENABLE: + case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET: + case MENU_ENUM_LABEL_CHEAT_FILE_LOAD: + case MENU_ENUM_LABEL_CHEAT_FILE_LOAD_APPEND: + case MENU_ENUM_LABEL_SAVESTATE_AUTO_LOAD: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LOADSTATE]; + case MENU_ENUM_LABEL_CHEAT_APPLY_CHANGES: + case MENU_ENUM_LABEL_SHADER_APPLY_CHANGES: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CHECKMARK]; + case MENU_ENUM_LABEL_CHEAT_ADD_NEW_AFTER: + case MENU_ENUM_LABEL_CHEAT_ADD_NEW_BEFORE: + case MENU_ENUM_LABEL_CHEAT_ADD_NEW_TOP: + case MENU_ENUM_LABEL_CHEAT_ADD_NEW_BOTTOM: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MENU_ADD]; + case MENU_ENUM_LABEL_CHEAT_APPLY_AFTER_TOGGLE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MENU_APPLY_TOGGLE]; + case MENU_ENUM_LABEL_CHEAT_APPLY_AFTER_LOAD: + case MENU_ENUM_LABEL_SAVESTATE_AUTO_INDEX: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MENU_APPLY_COG]; + case MENU_ENUM_LABEL_SAVESTATE_AUTO_SAVE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SAVESTATE]; + case MENU_ENUM_LABEL_SLOWMOTION_RATIO: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RESUME]; + case MENU_ENUM_LABEL_START_VIDEO_PROCESSOR: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MOVIE]; +#ifdef HAVE_LIBRETRODB + case MENU_ENUM_LABEL_EXPLORE_ITEM: + { + uintptr_t icon = menu_explore_get_entry_icon(type); + if (icon) return icon; + break; + } +#endif + default: + break; + } + + switch(type) + { + case MENU_SET_CDROM_INFO: + case MENU_SET_CDROM_LIST: + case MENU_SET_LOAD_CDROM_LIST: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DISC]; + case FILE_TYPE_DIRECTORY: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FOLDER]; + case FILE_TYPE_PLAIN: + case FILE_TYPE_IN_CARCHIVE: + case FILE_TYPE_RPL_ENTRY: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FILE]; + case FILE_TYPE_SHADER: + case FILE_TYPE_SHADER_PRESET: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SHADER_OPTIONS]; + case FILE_TYPE_CARCHIVE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ZIP]; + case FILE_TYPE_MUSIC: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MUSIC]; + case FILE_TYPE_IMAGE: + case FILE_TYPE_IMAGEVIEWER: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_IMAGE]; + case FILE_TYPE_MOVIE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MOVIE]; + case FILE_TYPE_CORE: + case FILE_TYPE_DIRECT_LOAD: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE]; + case FILE_TYPE_RDB: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RDB]; + case FILE_TYPE_CURSOR: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CURSOR]; + case FILE_TYPE_PLAYLIST_ENTRY: + case MENU_SETTING_ACTION_RUN: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RUN]; + case MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RESUME]; + case MENU_SETTING_ACTION_CLOSE: + case MENU_SETTING_ACTION_CLOSE_HORIZONTAL: + case MENU_SETTING_ACTION_DELETE_ENTRY: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOSE]; + case MENU_SETTING_ACTION_SAVESTATE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SAVESTATE]; + case MENU_SETTING_ACTION_LOADSTATE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_LOADSTATE]; + case FILE_TYPE_RDB_ENTRY: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO]; + case MENU_SETTING_ACTION_CORE_OPTIONS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE_OPTIONS]; + case MENU_SETTING_ACTION_CORE_OPTION_OVERRIDE_LIST: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING]; + case MENU_SETTING_ACTION_CORE_INPUT_REMAPPING_OPTIONS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_REMAPPING_OPTIONS]; + case MENU_SETTING_ACTION_CORE_CHEAT_OPTIONS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CHEAT_OPTIONS]; + case MENU_SETTING_ACTION_CORE_DISK_OPTIONS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DISK_OPTIONS]; + case MENU_SETTING_ACTION_CORE_SHADER_OPTIONS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SHADER_OPTIONS]; + case MENU_SETTING_ACTION_SCREENSHOT: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SCREENSHOT]; + case MENU_SETTING_ACTION_RESET: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD]; + case MENU_SETTING_ACTION_PAUSE_ACHIEVEMENTS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_PAUSE]; + case MENU_SETTING_GROUP: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING]; + case MENU_SET_SCREEN_BRIGHTNESS: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_BRIGHTNESS]; + case MENU_INFO_MESSAGE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO]; + case MENU_BLUETOOTH: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_BLUETOOTH]; + case MENU_WIFI: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_WIFI]; +#ifdef HAVE_NETWORKING + case MENU_ROOM: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ROOM]; + case MENU_ROOM_LAN: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ROOM_LAN]; + case MENU_ROOM_RELAY: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ROOM_RELAY]; +#endif + case MENU_SETTING_ACTION: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING]; + case MENU_SETTINGS_INPUT_LIBRETRO_DEVICE: + case MENU_SETTINGS_INPUT_INPUT_REMAP_PORT: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING]; + case MENU_SETTINGS_INPUT_ANALOG_DPAD_MODE: + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_ADC]; + } + +#ifdef HAVE_CHEEVOS + if ( + (type >= MENU_SETTINGS_CHEEVOS_START) && + (type < MENU_SETTINGS_NETPLAY_ROOMS_START) + ) + { + char buffer[64]; + int index = type - MENU_SETTINGS_CHEEVOS_START; + uintptr_t badge_texture = rcheevos_menu_get_badge_texture(index); + if (badge_texture) + return badge_texture; + + /* no state means its a header - show the info icon */ + if (!rcheevos_menu_get_state(index, buffer, sizeof(buffer))) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INFO]; + + /* placeholder badge image was not found, show generic menu icon */ + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENTS]; + } +#endif + + if ( + (type >= MENU_SETTINGS_INPUT_BEGIN) && + (type <= MENU_SETTINGS_INPUT_DESC_KBD_END) + ) + { + /* This part is only utilized by Input User # Binds */ + unsigned input_id; + if (type < MENU_SETTINGS_INPUT_DESC_BEGIN) + { + input_id = MENU_SETTINGS_INPUT_BEGIN; + if (type == input_id) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SETTING]; + if (type == input_id + 1) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_ADC]; +#ifdef HAVE_LIBNX + /* account for the additional split joycon option in Input User # Binds */ + input_id++; +#endif + if (type == input_id + 2) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SETTINGS]; + if (type == input_id + 3) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_MOUSE]; + if (type == input_id + 4) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BIND_ALL]; + if (type == input_id + 5) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD]; + if (type == input_id + 6) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SAVING]; + if ((type > (input_id + 30)) && (type < (input_id + 42))) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LGUN]; + if (type == input_id + 42) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_TURBO]; + /* align to use the same code of Quickmenu controls*/ + input_id = input_id + 7; + } + else + { + /* Quickmenu controls repeats the same icons for all users*/ + if (type < MENU_SETTINGS_INPUT_DESC_KBD_BEGIN) + input_id = MENU_SETTINGS_INPUT_DESC_BEGIN; + else + input_id = MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; + while (type > (input_id + 23)) + input_id = (input_id + 24); + + /* Human readable bind order */ + if (type < (input_id + RARCH_ANALOG_BIND_LIST_END)) + { + unsigned index = 0; + int input_num = type - input_id; + for (index = 0; index < sizeof(input_config_bind_order); index++) + { + if (input_config_bind_order[index] == input_num) + { + type = input_id + index; + break; + } + } + } + } + + /* This is utilized for both Input Binds and Quickmenu controls*/ + if (type == input_id) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_U]; + if (type == (input_id + 1)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_D]; + if (type == (input_id + 2)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_L]; + if (type == (input_id + 3)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_R]; + if (type == (input_id + 4)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_R]; + if (type == (input_id + 5)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_D]; + if (type == (input_id + 6)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_U]; + if (type == (input_id + 7)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_L]; + if (type == (input_id + 8)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SELECT]; + if (type == (input_id + 9)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_START]; + if (type == (input_id + 10)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LB]; + if (type == (input_id + 11)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_RB]; + if (type == (input_id + 12)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LT]; + if (type == (input_id + 13)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_RT]; + if (type == (input_id + 14)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_P]; + if (type == (input_id + 15)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_P]; + if (type == (input_id + 16)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_U]; + if (type == (input_id + 17)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_D]; + if (type == (input_id + 18)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_L]; + if (type == (input_id + 19)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_R]; + if (type == (input_id + 20)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_U]; + if (type == (input_id + 21)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_D]; + if (type == (input_id + 22)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_L]; + if (type == (input_id + 23)) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_R]; + } + + if ( + (type >= MENU_SETTINGS_REMAPPING_PORT_BEGIN) && + (type <= MENU_SETTINGS_REMAPPING_PORT_END) + ) + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SETTINGS]; + + return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SUBSETTING]; +} + +static const char *ozone_entries_icon_texture_path(unsigned id) +{ +switch (id) + { + case OZONE_ENTRIES_ICONS_TEXTURE_MAIN_MENU: +#if defined(HAVE_LAKKA) + return "lakka.png"; +#else + return "retroarch.png"; +#endif + case OZONE_ENTRIES_ICONS_TEXTURE_SETTINGS: + return "settings.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_HISTORY: + return "history.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_FAVORITES: + return "favorites.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_ADD_FAVORITE: + return "add-favorite.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_MUSICS: + return "musics.png"; +#if defined(HAVE_FFMPEG) || defined(HAVE_MPV) + case OZONE_ENTRIES_ICONS_TEXTURE_MOVIES: + return "movies.png"; +#endif +#ifdef HAVE_IMAGEVIEWER + case OZONE_ENTRIES_ICONS_TEXTURE_IMAGES: + return "images.png"; +#endif + case OZONE_ENTRIES_ICONS_TEXTURE_SETTING: + return "setting.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_SUBSETTING: + return "subsetting.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_ARROW: + return "arrow.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_RUN: + return "run.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_CLOSE: + return "close.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_RESUME: + return "resume.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_CLOCK: + return "clock.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_FULL: + return "battery-full.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_CHARGING: + return "battery-charging.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_80: + return "battery-80.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_60: + return "battery-60.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_40: + return "battery-40.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_20: + return "battery-20.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_POINTER: + return "pointer.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_SAVESTATE: + return "savestate.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_LOADSTATE: + return "loadstate.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_UNDO: + return "undo.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO: + return "core-infos.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_BLUETOOTH: + return "bluetooth.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_WIFI: + return "wifi.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_CORE_OPTIONS: + return "core-options.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_REMAPPING_OPTIONS: + return "core-input-remapping-options.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_CHEAT_OPTIONS: + return "core-cheat-options.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_DISK_OPTIONS: + return "core-disk-options.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_SHADER_OPTIONS: + return "core-shader-options.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENT_LIST: + return "achievement-list.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_SCREENSHOT: + return "screenshot.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_RELOAD: + return "reload.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_RENAME: + return "rename.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_FILE: + return "file.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_FOLDER: + return "folder.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_ZIP: + return "zip.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_MUSIC: + return "music.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_FAVORITE: + return "favorites-content.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_IMAGE: + return "image.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_MOVIE: + return "movie.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_CORE: + return "core.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_RDB: + return "database.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_CURSOR: + return "cursor.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_SWITCH_ON: + return "on.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_SWITCH_OFF: + return "off.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_DISC: + return "disc.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_ADD: + return "add.png"; +#ifdef HAVE_NETWORKING + case OZONE_ENTRIES_ICONS_TEXTURE_NETPLAY: + return "netplay.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_ROOM: + return "menu_room.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_ROOM_LAN: + return "menu_room_lan.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_ROOM_RELAY: + return "menu_room_relay.png"; +#endif + case OZONE_ENTRIES_ICONS_TEXTURE_KEY: + return "key.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_KEY_HOVER: + return "key-hover.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_DIALOG_SLICE: + return "dialog-slice.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENTS: + return "menu_achievements.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_AUDIO: + return "menu_audio.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_DRIVERS: + return "menu_drivers.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_EXIT: + return "menu_exit.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_FRAMESKIP: + return "menu_frameskip.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_HELP: + return "menu_help.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INFO: + return "menu_info.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SETTINGS: + return "Libretro - Pad.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_LATENCY: + return "menu_latency.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_NETWORK: + return "menu_network.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_POWER: + return "menu_power.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_RECORD: + return "menu_record.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_SAVING: + return "menu_saving.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_UPDATER: + return "menu_updater.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_VIDEO: + return "menu_video.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_MIXER: + return "menu_mixer.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_LOG: + return "menu_log.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_OSD: + return "menu_osd.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_UI: + return "menu_ui.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_USER: + return "menu_user.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_PRIVACY: + return "menu_privacy.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_PLAYLIST: + return "menu_playlist.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_QUICKMENU: + return "menu_quickmenu.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_REWIND: + return "menu_rewind.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_OVERLAY: + return "menu_overlay.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_OVERRIDE: + return "menu_override.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_NOTIFICATIONS: + return "menu_notifications.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_STREAM: + return "menu_stream.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_SHUTDOWN: + return "menu_shutdown.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_U: + return "input_DPAD-U.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_D: + return "input_DPAD-D.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_L: + return "input_DPAD-L.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_DPAD_R: + return "input_DPAD-R.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_U: + return "input_STCK-U.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_D: + return "input_STCK-D.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_L: + return "input_STCK-L.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_R: + return "input_STCK-R.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_STCK_P: + return "input_STCK-P.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_U: + return "input_BTN-U.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_D: + return "input_BTN-D.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_L: + return "input_BTN-L.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_R: + return "input_BTN-R.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LB: + return "input_LB.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_RB: + return "input_RB.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LT: + return "input_LT.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_RT: + return "input_RT.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SELECT: + return "input_SELECT.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_START: + return "input_START.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_CHECKMARK: + return "menu_check.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_MENU_ADD: + return "menu_add.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_BRIGHTNESS: + return "menu_brightness.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_PAUSE: + return "menu_pause.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_MENU_APPLY_TOGGLE: + return "menu_apply_toggle.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_MENU_APPLY_COG: + return "menu_apply_cog.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_ADC: + return "input_ADC.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BIND_ALL: + return "input_BIND_ALL.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_MOUSE: + return "input_MOUSE.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LGUN: + return "input_LGUN.png"; + case OZONE_ENTRIES_ICONS_TEXTURE_INPUT_TURBO: + return "input_TURBO.png"; + } + return NULL; +} + +static void ozone_unload_theme_textures(ozone_handle_t *ozone) +{ + unsigned i, j; + + for (j = 0; j < ozone_themes_count; j++) + { + ozone_theme_t *theme = ozone_themes[j]; + for (i = 0; i < OZONE_THEME_TEXTURE_LAST; i++) + video_driver_texture_unload(&theme->textures[i]); + } +} + +static bool ozone_reset_theme_textures(ozone_handle_t *ozone) +{ + unsigned i, j; + char theme_path[255]; + bool result = true; + + for (j = 0; j < ozone_themes_count; j++) + { + ozone_theme_t *theme = ozone_themes[j]; + + fill_pathname_join( + theme_path, + ozone->png_path, + theme->name, + sizeof(theme_path) + ); + + for (i = 0; i < OZONE_THEME_TEXTURE_LAST; i++) + { + char filename[PATH_MAX_LENGTH]; + strlcpy(filename, OZONE_THEME_TEXTURES_FILES[i], + sizeof(filename)); + strlcat(filename, FILE_PATH_PNG_EXTENSION, sizeof(filename)); + + if (!gfx_display_reset_textures_list(filename, theme_path, &theme->textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL)) + result = false; + } + } + + return result; +} + +static void ozone_sidebar_collapse_end(void *userdata) +{ + ozone_handle_t *ozone = (ozone_handle_t*)userdata; + + ozone->sidebar_collapsed = true; +} + +static unsigned ozone_get_sidebar_height(ozone_handle_t *ozone) +{ + int entries = (int)(ozone->system_tab_end + 1 + (ozone->horizontal_list.size )); + return entries * ozone->dimensions.sidebar_entry_height + (entries - 1) * ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.sidebar_padding_vertical + + (ozone->horizontal_list.size > 0 ? ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px : 0); +} + +static unsigned ozone_get_selected_sidebar_y_position(ozone_handle_t *ozone) +{ + return ozone->categories_selection_ptr * ozone->dimensions.sidebar_entry_height + + (ozone->categories_selection_ptr - 1) * ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.sidebar_padding_vertical + + (ozone->categories_selection_ptr > ozone->system_tab_end ? ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px : 0); +} + +static float ozone_sidebar_get_scroll_y( + ozone_handle_t *ozone, unsigned video_height) +{ + float scroll_y = + ozone->animations.scroll_y_sidebar; + float selected_position_y = + ozone_get_selected_sidebar_y_position(ozone); + float current_selection_middle_onscreen = + ozone->dimensions.header_height + + ozone->dimensions.spacer_1px + + ozone->animations.scroll_y_sidebar + + selected_position_y + + ozone->dimensions.sidebar_entry_height / 2.0f; + float bottom_boundary = + (float)video_height + - (ozone->dimensions.header_height + ozone->dimensions.spacer_1px) + - ozone->dimensions.footer_height; + float entries_middle = (float)video_height / 2.0f; + float entries_height = ozone_get_sidebar_height(ozone); + + if (current_selection_middle_onscreen != entries_middle) + scroll_y = ozone->animations.scroll_y_sidebar - (current_selection_middle_onscreen - entries_middle); + + if (scroll_y + entries_height < bottom_boundary) + scroll_y = bottom_boundary - entries_height - ozone->dimensions.sidebar_padding_vertical; + + if (scroll_y > 0.0f) + return 0.0f; + return scroll_y; +} + +static void ozone_font_flush( + unsigned video_width, unsigned video_height, + ozone_font_data_t *font_data) +{ + /* Flushing is slow - only do it if font + * has actually been used */ + if (!font_data || + (font_data->raster_block.carr.coords.vertices == 0)) + return; + + font_driver_flush(video_width, video_height, font_data->font); + font_data->raster_block.carr.coords.vertices = 0; +} + +static void ozone_draw_icon( + gfx_display_t *p_disp, + void *userdata, + unsigned video_width, + unsigned video_height, + unsigned icon_width, + unsigned icon_height, + uintptr_t texture, + float x, float y, + unsigned width, unsigned height, + float rotation, float scale_factor, + float *color) +{ + gfx_display_ctx_rotate_draw_t rotate_draw; + gfx_display_ctx_draw_t draw; + struct video_coords coords; + math_matrix_4x4 mymat; + gfx_display_ctx_driver_t + *dispctx = p_disp->dispctx; + + rotate_draw.matrix = &mymat; + rotate_draw.rotation = rotation; + rotate_draw.scale_x = scale_factor; + rotate_draw.scale_y = scale_factor; + rotate_draw.scale_z = 1; + rotate_draw.scale_enable = true; + + gfx_display_rotate_z(p_disp, &rotate_draw, userdata); + + coords.vertices = 4; + coords.vertex = NULL; + coords.tex_coord = NULL; + coords.lut_tex_coord = NULL; + coords.color = (const float*)color; + + draw.x = x; + draw.y = height - y - icon_height; + draw.width = icon_width; + draw.height = icon_height; + draw.scale_factor = scale_factor; + draw.rotation = rotation; + draw.coords = &coords; + draw.matrix_data = &mymat; + draw.texture = texture; + draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; + draw.pipeline_id = 0; + + if (draw.height > 0 && draw.width > 0) + dispctx->draw(&draw, userdata, video_width, video_height); +} + +static int ozone_wiggle(ozone_handle_t* ozone, float t) +{ + float a = ozone->cursor_wiggle_state.amplitude; + /* Damped sine wave */ + float w = 0.8f; /* period */ + float c = 0.35f; /* damp factor */ + return roundf(a * exp(-(c * t)) * sin(w * t)); +} + +/** + * Changes x and y to the current offset of the cursor wiggle animation + */ +static void ozone_apply_cursor_wiggle_offset(ozone_handle_t* ozone, int* x, size_t* y) +{ + retro_time_t cur_time; + retro_time_t t; + + /* Don't do anything if we are not wiggling */ + if (!ozone || !ozone->cursor_wiggle_state.wiggling) + return; + + cur_time = menu_driver_get_current_time() / 1000; + t = (cur_time - ozone->cursor_wiggle_state.start_time) / 10; + + /* Has the animation ended? */ + if (t >= OZONE_WIGGLE_DURATION) + { + ozone->cursor_wiggle_state.wiggling = false; + return; + } + + /* Change cursor position depending on wiggle direction */ + switch (ozone->cursor_wiggle_state.direction) + { + case MENU_ACTION_RIGHT: + *x += ozone_wiggle(ozone, t); + break; + case MENU_ACTION_LEFT: + *x -= ozone_wiggle(ozone, t); + break; + case MENU_ACTION_DOWN: + *y += ozone_wiggle(ozone, t); + break; + case MENU_ACTION_UP: + *y -= ozone_wiggle(ozone, t); + break; + default: + break; + } +} + +static void ozone_draw_cursor_slice( + ozone_handle_t *ozone, + gfx_display_t *p_disp, + void *userdata, + unsigned video_width, + unsigned video_height, + int x_offset, + unsigned width, unsigned height, + size_t y, float alpha) +{ + float scale_factor = ozone->last_scale_factor; + int slice_x = x_offset - 12 * scale_factor; + int slice_y = (int)y + 8 * scale_factor; + unsigned slice_new_w = width + (24 + 1) * scale_factor; + unsigned slice_new_h = height + 20 * scale_factor; + gfx_display_ctx_driver_t + *dispctx = p_disp->dispctx; + static float + last_alpha = 0.0f; + + if (alpha != last_alpha) + { + gfx_display_set_alpha(ozone->theme_dynamic.cursor_alpha, alpha); + gfx_display_set_alpha(ozone->theme_dynamic.cursor_border, alpha); + last_alpha = alpha; + } + + if (dispctx && dispctx->blend_begin) + dispctx->blend_begin(userdata); + + /* Cursor without border */ + gfx_display_draw_texture_slice( + p_disp, + userdata, + video_width, + video_height, + slice_x, + slice_y, + 80, 80, + slice_new_w, + slice_new_h, + video_width, video_height, + ozone->theme_dynamic.cursor_alpha, + 20, scale_factor, + ozone->theme->textures[OZONE_THEME_TEXTURE_CURSOR_NO_BORDER] + ); + + /* Tainted border */ + gfx_display_draw_texture_slice( + p_disp, + userdata, + video_width, + video_height, + slice_x, + slice_y, + 80, 80, + slice_new_w, + slice_new_h, + video_width, video_height, + ozone->theme_dynamic.cursor_border, + 20, scale_factor, + ozone->textures[OZONE_TEXTURE_CURSOR_BORDER] + ); + + if (dispctx && dispctx->blend_end) + dispctx->blend_end(userdata); +} + +static void ozone_draw_cursor_fallback( + ozone_handle_t *ozone, + gfx_display_t *p_disp, + void *userdata, + unsigned video_width, + unsigned video_height, + int x_offset, + unsigned width, unsigned height, + size_t y, float alpha) +{ + static float last_alpha = 0.0f; + + if (alpha != last_alpha) + { + gfx_display_set_alpha(ozone->theme_dynamic.selection_border, alpha); + gfx_display_set_alpha(ozone->theme_dynamic.selection, alpha); + last_alpha = alpha; + } + + /* Fill */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + x_offset, + (int)y, + width, + height - ozone->dimensions.spacer_3px, + video_width, + video_height, + ozone->theme_dynamic.selection, + NULL); + + /* Borders (can't do one single quad because of alpha) */ + + /* Top */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + x_offset - ozone->dimensions.spacer_3px, + (int)(y - ozone->dimensions.spacer_3px), + width + ozone->dimensions.spacer_3px * 2, + ozone->dimensions.spacer_3px, + video_width, + video_height, + ozone->theme_dynamic.selection_border, + NULL); + + /* Bottom */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + x_offset - ozone->dimensions.spacer_3px, + (int)(y + height - ozone->dimensions.spacer_3px), + width + ozone->dimensions.spacer_3px * 2, + ozone->dimensions.spacer_3px, + video_width, + video_height, + ozone->theme_dynamic.selection_border, + NULL); + + /* Left */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + (int)(x_offset - ozone->dimensions.spacer_3px), + (int)y, + ozone->dimensions.spacer_3px, + height - ozone->dimensions.spacer_3px, + video_width, + video_height, + ozone->theme_dynamic.selection_border, + NULL); + + /* Right */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + x_offset + width, + (int)y, + ozone->dimensions.spacer_3px, + height - ozone->dimensions.spacer_3px, + video_width, + video_height, + ozone->theme_dynamic.selection_border, + NULL); +} + + +static void ozone_draw_cursor( + ozone_handle_t *ozone, + gfx_display_t *p_disp, + void *userdata, + unsigned video_width, + unsigned video_height, + int x_offset, + unsigned width, unsigned height, + size_t y, float alpha) +{ + int new_x = x_offset; + size_t new_y = y; + + /* Apply wiggle animation if needed */ + if (ozone->cursor_wiggle_state.wiggling) + ozone_apply_cursor_wiggle_offset(ozone, &new_x, &new_y); + + /* Draw the cursor */ + if (ozone->has_all_assets) + ozone_draw_cursor_slice(ozone, + p_disp, + userdata, + video_width, video_height, + new_x, width, height, new_y, alpha); + else + ozone_draw_cursor_fallback(ozone, + p_disp, + userdata, + video_width, + video_height, + new_x, width, height, new_y, alpha); +} + + +static void ozone_draw_sidebar( + ozone_handle_t *ozone, + gfx_display_t *p_disp, + gfx_animation_t *p_anim, + settings_t *settings, + void *userdata, + unsigned video_width, + unsigned video_height, + bool libretro_running, + float menu_framebuffer_opacity + ) +{ + size_t y; + int entry_width; + char console_title[255]; + unsigned i, sidebar_height; + gfx_animation_ctx_ticker_t ticker; + gfx_animation_ctx_ticker_smooth_t ticker_smooth; + static const char* const + ticker_spacer = OZONE_TICKER_SPACER; + unsigned ticker_x_offset = 0; + uint32_t text_alpha = ozone->animations.sidebar_text_alpha + * 255.0f; + bool use_smooth_ticker = settings->bools.menu_ticker_smooth; + float scale_factor = ozone->last_scale_factor; + enum gfx_animation_ticker_type + menu_ticker_type = (enum gfx_animation_ticker_type) + settings->uints.menu_ticker_type; + unsigned selection_y = 0; + unsigned selection_old_y = 0; + unsigned horizontal_list_size = 0; + gfx_display_ctx_driver_t *dispctx = p_disp->dispctx; + + /* Initial ticker configuration */ + if (use_smooth_ticker) + { + ticker_smooth.idx = p_anim->ticker_pixel_idx; + ticker_smooth.font = ozone->fonts.sidebar.font; + ticker_smooth.font_scale = 1.0f; + ticker_smooth.type_enum = menu_ticker_type; + ticker_smooth.spacer = ticker_spacer; + ticker_smooth.x_offset = &ticker_x_offset; + ticker_smooth.dst_str_width = NULL; + } + else + { + ticker.idx = p_anim->ticker_idx; + ticker.type_enum = menu_ticker_type; + ticker.spacer = ticker_spacer; + } + + horizontal_list_size = (unsigned)ozone->horizontal_list.size; + + gfx_display_scissor_begin( + p_disp, + userdata, + video_width, video_height, + 0, + ozone->dimensions.header_height + ozone->dimensions.spacer_1px, + (unsigned) ozone->dimensions_sidebar_width, + video_height - ozone->dimensions.header_height - ozone->dimensions.footer_height - ozone->dimensions.spacer_1px); + + /* Background */ + sidebar_height = video_height - ozone->dimensions.header_height - ozone->dimensions.sidebar_gradient_height * 2 - ozone->dimensions.footer_height; + + if (!libretro_running || (menu_framebuffer_opacity >= 1.0f)) + { + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + ozone->sidebar_offset, + ozone->dimensions.header_height + ozone->dimensions.spacer_1px, + (unsigned)ozone->dimensions_sidebar_width, + ozone->dimensions.sidebar_gradient_height, + video_width, + video_height, + ozone->theme->sidebar_top_gradient, + NULL); + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + ozone->sidebar_offset, + ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.sidebar_gradient_height, + (unsigned)ozone->dimensions_sidebar_width, + sidebar_height, + video_width, + video_height, + ozone->theme->sidebar_background, + NULL); + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + ozone->sidebar_offset, + video_height + - ozone->dimensions.footer_height + - ozone->dimensions.sidebar_gradient_height + - ozone->dimensions.spacer_1px, + (unsigned)ozone->dimensions_sidebar_width, + ozone->dimensions.sidebar_gradient_height + + ozone->dimensions.spacer_1px, + video_width, + video_height, + ozone->theme->sidebar_bottom_gradient, + NULL); + } + + /* Tabs */ + /* y offset computation */ + y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.sidebar_padding_vertical; + for (i = 0; i < ozone->system_tab_end + horizontal_list_size + 1; i++) + { + if (i == ozone->categories_selection_ptr) + { + selection_y = (unsigned)y; + if (ozone->categories_selection_ptr > ozone->system_tab_end) + selection_y += ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px; + } + + if (i == ozone->categories_active_idx_old) + { + selection_old_y = (unsigned)y; + if (ozone->categories_active_idx_old > ozone->system_tab_end) + selection_old_y += ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px; + } + + y += ozone->dimensions.sidebar_entry_height + ozone->dimensions.sidebar_entry_padding_vertical; + } + + entry_width = (unsigned) ozone->dimensions_sidebar_width - ozone->dimensions.sidebar_padding_horizontal * 2; + + /* Cursor */ + if (ozone->cursor_in_sidebar) + ozone_draw_cursor( + ozone, + p_disp, + userdata, + video_width, + video_height, + ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal + ozone->dimensions.spacer_3px, + entry_width - ozone->dimensions.spacer_5px, + ozone->dimensions.sidebar_entry_height + ozone->dimensions.spacer_1px, + selection_y + ozone->animations.scroll_y_sidebar, + ozone->animations.cursor_alpha); + + if (ozone->cursor_in_sidebar_old) + ozone_draw_cursor( + ozone, + p_disp, + userdata, + video_width, + video_height, + ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal + ozone->dimensions.spacer_3px, + entry_width - ozone->dimensions.spacer_5px, + ozone->dimensions.sidebar_entry_height + ozone->dimensions.spacer_1px, + selection_old_y + ozone->animations.scroll_y_sidebar, + 1-ozone->animations.cursor_alpha); + + /* Menu tabs */ + y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.sidebar_padding_vertical; + if (dispctx && dispctx->blend_begin) + dispctx->blend_begin(userdata); + + for (i = 0; i < (unsigned)(ozone->system_tab_end+1); i++) + { + enum msg_hash_enums value_idx; + const char *title = NULL; + bool selected = (ozone->categories_selection_ptr == i); + unsigned icon = ozone_system_tabs_icons[ozone->tabs[i]]; + + uint32_t text_color = COLOR_TEXT_ALPHA((selected ? ozone->theme->text_selected_rgba : ozone->theme->text_rgba), text_alpha); + float *col = (selected ? ozone->theme->text_selected : ozone->theme->entries_icon); + + if (!col) + col = ozone->pure_white; + + /* Icon */ + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + ozone->dimensions.sidebar_entry_icon_size, + ozone->dimensions.sidebar_entry_icon_size, + ozone->tab_textures[icon], + ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal + ozone->dimensions.sidebar_entry_icon_padding, + y + ozone->dimensions.sidebar_entry_height / 2 - ozone->dimensions.sidebar_entry_icon_size / 2 + ozone->animations.scroll_y_sidebar, + video_width, + video_height, + 0, + 1, + col); + + value_idx = ozone_system_tabs_value[ozone->tabs[i]]; + title = msg_hash_to_str(value_idx); + + /* Text */ + if (!ozone->sidebar_collapsed) + gfx_display_draw_text( + ozone->fonts.sidebar.font, + title, + ozone->sidebar_offset + + ozone->dimensions.sidebar_padding_horizontal + + ozone->dimensions.sidebar_entry_icon_padding * 2 + + ozone->dimensions.sidebar_entry_icon_size, + y + ozone->dimensions.sidebar_entry_height / 2.0f + + ozone->fonts.sidebar.line_centre_offset + + ozone->animations.scroll_y_sidebar, + video_width, + video_height, + text_color, + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + true); + + y += ozone->dimensions.sidebar_entry_height + ozone->dimensions.sidebar_entry_padding_vertical; + } + + if (dispctx && dispctx->blend_end) + dispctx->blend_end(userdata); + + /* Console tabs */ + if (horizontal_list_size > 0) + { + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal, + y + ozone->animations.scroll_y_sidebar, + entry_width, + ozone->dimensions.spacer_1px, + video_width, + video_height, + ozone->theme->entries_border, + NULL); + + y += ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px; + + if (dispctx && dispctx->blend_begin) + dispctx->blend_begin(userdata); + + for (i = 0; i < horizontal_list_size; i++) + { + bool selected = (ozone->categories_selection_ptr == ozone->system_tab_end + 1 + i); + + uint32_t text_color = COLOR_TEXT_ALPHA((selected ? ozone->theme->text_selected_rgba : ozone->theme->text_rgba), text_alpha); + + ozone_node_t *node = (ozone_node_t*)ozone->horizontal_list.list[i].userdata; + float *col = (selected ? ozone->theme->text_selected : ozone->theme->entries_icon); + + if (!node) + goto console_iterate; + + if (!col) + col = ozone->pure_white; + + /* Icon */ + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + ozone->dimensions.sidebar_entry_icon_size, + ozone->dimensions.sidebar_entry_icon_size, + node->icon, + ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal + ozone->dimensions.sidebar_entry_icon_padding, + y + ozone->dimensions.sidebar_entry_height / 2 - ozone->dimensions.sidebar_entry_icon_size / 2 + ozone->animations.scroll_y_sidebar, + video_width, + video_height, + 0, + 1, + col); + + /* Text */ + if (ozone->sidebar_collapsed) + goto console_iterate; + + if (use_smooth_ticker) + { + ticker_smooth.selected = selected; + /* TODO/FIXME - undefined behavior reported by ASAN - + *-12.549 is outside the range of representable values + of type 'unsigned int' + * */ + ticker_smooth.field_width = (entry_width - ozone->dimensions.sidebar_entry_icon_size - 40 * scale_factor); + ticker_smooth.src_str = node->console_name; + ticker_smooth.dst_str = console_title; + ticker_smooth.dst_str_len = sizeof(console_title); + + gfx_animation_ticker_smooth(&ticker_smooth); + } + else + { + ticker.len = (entry_width - ozone->dimensions.sidebar_entry_icon_size - 40 * scale_factor) / ozone->fonts.sidebar.glyph_width; + ticker.s = console_title; + ticker.selected = selected; + ticker.str = node->console_name; + + gfx_animation_ticker(&ticker); + } + + gfx_display_draw_text( + ozone->fonts.sidebar.font, + console_title, + ticker_x_offset + ozone->sidebar_offset + + ozone->dimensions.sidebar_padding_horizontal + + ozone->dimensions.sidebar_entry_icon_padding * 2 + + ozone->dimensions.sidebar_entry_icon_size, + y + ozone->dimensions.sidebar_entry_height / 2 + + ozone->fonts.sidebar.line_centre_offset + + ozone->animations.scroll_y_sidebar, + video_width, + video_height, + text_color, + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + true); + +console_iterate: + y += ozone->dimensions.sidebar_entry_height + ozone->dimensions.sidebar_entry_padding_vertical; + } + + if (dispctx && dispctx->blend_end) + dispctx->blend_end(userdata); + } + + ozone_font_flush(video_width, video_height, &ozone->fonts.sidebar); + + if (dispctx && dispctx->scissor_end) + dispctx->scissor_end(userdata, + video_width, video_height); +} + +static void ozone_thumbnail_bar_hide_end(void *userdata) +{ + ozone_handle_t *ozone = (ozone_handle_t*) userdata; + ozone->show_thumbnail_bar = false; + ozone->pending_hide_thumbnail_bar = false; +} + +static void ozone_entries_update_thumbnail_bar( + ozone_handle_t *ozone, bool is_playlist, bool allow_animation) +{ + struct gfx_animation_ctx_entry entry; + uintptr_t tag = (uintptr_t)&ozone->show_thumbnail_bar; + + entry.duration = ANIMATION_CURSOR_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.tag = tag; + entry.subject = &ozone->animations.thumbnail_bar_position; + + gfx_animation_kill_by_tag(&tag); + + /* Show it + * > We only want to trigger a 'show' animation + * if 'show_thumbnail_bar' is currently false. + * However: 'show_thumbnail_bar' is only set + * to false by the 'ozone_thumbnail_bar_hide_end' + * callback. If the above 'gfx_animation_kill_by_tag()' + * kills an existing 'hide' animation, then the + * callback will not fire - so the sidebar will be + * off screen, but a subsequent attempt to show it + * here will fail, since 'show_thumbnail_bar' will + * be a false positive. We therefore require an + * additional 'pending_hide_thumbnail_bar' parameter + * to track mid-animation state changes... */ + if (is_playlist && + !ozone->cursor_in_sidebar && + (!ozone->show_thumbnail_bar || ozone->pending_hide_thumbnail_bar) && + (ozone->depth == 1)) + { + if (allow_animation) + { + ozone->show_thumbnail_bar = true; + + entry.cb = NULL; + entry.userdata = NULL; + entry.target_value = ozone->dimensions.thumbnail_bar_width; + + gfx_animation_push(&entry); + } + else + { + ozone->animations.thumbnail_bar_position = ozone->dimensions.thumbnail_bar_width; + ozone->show_thumbnail_bar = true; + } + + ozone->pending_hide_thumbnail_bar = false; + } + /* Hide it */ + else + { + if (allow_animation) + { + entry.cb = ozone_thumbnail_bar_hide_end; + entry.userdata = ozone; + entry.target_value = 0.0f; + + ozone->pending_hide_thumbnail_bar = true; + gfx_animation_push(&entry); + } + else + { + ozone->animations.thumbnail_bar_position = 0.0f; + ozone_thumbnail_bar_hide_end(ozone); + } + } +} + +static bool ozone_is_playlist(ozone_handle_t *ozone, bool depth) +{ + bool is_playlist; + + if (ozone->categories_selection_ptr > ozone->system_tab_end) + is_playlist = true; + else + { + switch (ozone->tabs[ozone->categories_selection_ptr]) + { + case OZONE_SYSTEM_TAB_MAIN: + case OZONE_SYSTEM_TAB_SETTINGS: + case OZONE_SYSTEM_TAB_ADD: +#ifdef HAVE_NETWORKING + case OZONE_SYSTEM_TAB_NETPLAY: +#endif +#ifdef HAVE_LIBRETRODB + case OZONE_SYSTEM_TAB_EXPLORE: +#endif + is_playlist = false; + break; + case OZONE_SYSTEM_TAB_HISTORY: + case OZONE_SYSTEM_TAB_FAVORITES: + case OZONE_SYSTEM_TAB_MUSIC: +#if defined(HAVE_FFMPEG) || defined(HAVE_MPV) + case OZONE_SYSTEM_TAB_VIDEO: +#endif +#ifdef HAVE_IMAGEVIEWER + case OZONE_SYSTEM_TAB_IMAGES: +#endif + default: + is_playlist = true; + break; + } + } + + if (depth) + return is_playlist && ozone->depth == 1; + + return is_playlist; +} + + +static void ozone_sidebar_update_collapse( + ozone_handle_t *ozone, + settings_t *settings, + bool allow_animation) +{ + /* Collapse sidebar if needed */ + struct gfx_animation_ctx_entry entry; + bool is_playlist = ozone_is_playlist(ozone, false); + uintptr_t tag = (uintptr_t)&ozone->sidebar_collapsed; + bool collapse_sidebar = settings->bools.ozone_collapse_sidebar; + + entry.easing_enum = EASING_OUT_QUAD; + entry.tag = tag; + entry.userdata = ozone; + entry.duration = ANIMATION_CURSOR_DURATION; + + gfx_animation_kill_by_tag(&tag); + + /* Collapse it */ + if (collapse_sidebar || (is_playlist && !ozone->cursor_in_sidebar)) + { + if (allow_animation) + { + entry.cb = ozone_sidebar_collapse_end; + + /* Text alpha */ + entry.subject = &ozone->animations.sidebar_text_alpha; + entry.target_value = 0.0f; + + gfx_animation_push(&entry); + + /* Collapse */ + entry.subject = &ozone->dimensions_sidebar_width; + entry.target_value = ozone->dimensions.sidebar_width_collapsed; + + gfx_animation_push(&entry); + } + else + { + ozone->animations.sidebar_text_alpha = 0.0f; + ozone->dimensions_sidebar_width = + ozone->dimensions.sidebar_width_collapsed; + ozone->sidebar_collapsed = true; + } + } + /* Show it */ + else if (ozone->cursor_in_sidebar || (!is_playlist && !collapse_sidebar)) + { + if (allow_animation) + { + ozone->sidebar_collapsed = false; + + entry.cb = NULL; + + /* Text alpha */ + entry.subject = &ozone->animations.sidebar_text_alpha; + entry.target_value = 1.0f; + + gfx_animation_push(&entry); + + /* Collapse */ + entry.subject = &ozone->dimensions_sidebar_width; + entry.target_value = ozone->dimensions.sidebar_width_normal; + + gfx_animation_push(&entry); + } + else + { + ozone->animations.sidebar_text_alpha = 1.0f; + ozone->dimensions_sidebar_width = ozone->dimensions.sidebar_width_normal; + ozone->sidebar_collapsed = false; + } + } + + ozone_entries_update_thumbnail_bar(ozone, is_playlist, allow_animation); +} + + +static void ozone_go_to_sidebar(ozone_handle_t *ozone, + settings_t *settings, + uintptr_t tag) +{ + struct gfx_animation_ctx_entry entry; + + ozone->selection_old = ozone->selection; + ozone->cursor_in_sidebar_old = ozone->cursor_in_sidebar; + ozone->cursor_in_sidebar = true; + + /* Cursor animation */ + ozone->animations.cursor_alpha = 0.0f; + + entry.cb = NULL; + entry.duration = ANIMATION_CURSOR_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &ozone->animations.cursor_alpha; + entry.tag = tag; + entry.target_value = 1.0f; + entry.userdata = NULL; + + gfx_animation_push(&entry); + + ozone_sidebar_update_collapse(ozone, settings, true); +} + +static void ozone_update_content_metadata(ozone_handle_t *ozone) +{ + const char *core_name = NULL; + size_t selection = menu_navigation_get_selection(); + playlist_t *playlist = playlist_get_cached(); + settings_t *settings = config_get_ptr(); + bool scroll_content_metadata = settings->bools.ozone_scroll_content_metadata; + bool show_entry_idx = settings->bools.playlist_show_entry_idx; + bool content_runtime_log = settings->bools.content_runtime_log; + bool content_runtime_log_aggr = settings->bools.content_runtime_log_aggregate; + const char *directory_runtime_log = settings->paths.directory_runtime_log; + const char *directory_playlist = settings->paths.directory_playlist; + unsigned runtime_type = settings->uints.playlist_sublabel_runtime_type; + enum playlist_sublabel_last_played_style_type + runtime_last_played_style = + (enum playlist_sublabel_last_played_style_type) + settings->uints.playlist_sublabel_last_played_style; + enum playlist_sublabel_last_played_date_separator_type + runtime_date_separator = + (enum playlist_sublabel_last_played_date_separator_type) + settings->uints.menu_timedate_date_separator; + + /* Must check whether core corresponds to 'viewer' + * content even when not using a playlist, otherwise + * file browser image updates are mishandled */ + if (gfx_thumbnail_get_core_name(ozone->thumbnail_path_data, &core_name)) + ozone->selection_core_is_viewer = string_is_equal(core_name, "imageviewer") + || string_is_equal(core_name, "musicplayer") + || string_is_equal(core_name, "movieplayer"); + else + ozone->selection_core_is_viewer = false; + + if (ozone->is_playlist && playlist) + { + const char *core_label = NULL; + const struct playlist_entry *entry = NULL; + size_t list_size = menu_entries_get_size(); + file_list_t *list = menu_entries_get_selection_buf_ptr(0); + bool playlist_valid = false; + size_t playlist_index = selection; + + /* Get playlist index corresponding + * to the selected entry */ + if (list && + (selection < list_size) && + (list->list[selection].type == FILE_TYPE_RPL_ENTRY)) + { + playlist_valid = true; + playlist_index = list->list[selection].entry_idx; + } + + /* Fill entry enumeration */ + if (show_entry_idx) + snprintf(ozone->selection_entry_enumeration, sizeof(ozone->selection_entry_enumeration), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT_INFO_ENTRY_IDX), + (unsigned long)(playlist_index + 1), (unsigned long)list_size); + else + ozone->selection_entry_enumeration[0] = '\0'; + + /* Fill core name */ + if (!core_name || string_is_equal(core_name, "DETECT")) + core_label = msg_hash_to_str(MSG_AUTODETECT); + else + core_label = core_name; + + snprintf(ozone->selection_core_name, sizeof(ozone->selection_core_name), + "%s %s", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_SUBLABEL_CORE), core_label); + + /* Word wrap core name string, if required */ + if (!scroll_content_metadata) + { + char tmpstr[sizeof(ozone->selection_core_name)]; + unsigned metadata_len = + (ozone->dimensions.thumbnail_bar_width - ((ozone->dimensions.sidebar_entry_icon_padding * 2) * 2)) / + ozone->fonts.footer.glyph_width; + + strlcpy(tmpstr, ozone->selection_core_name, sizeof(tmpstr)); + (ozone->word_wrap)(ozone->selection_core_name, sizeof(ozone->selection_core_name), + tmpstr, metadata_len, ozone->fonts.footer.wideglyph_width, 0); + ozone->selection_core_name_lines = ozone_count_lines(ozone->selection_core_name); + } + else + ozone->selection_core_name_lines = 1; + + /* Fill play time if applicable */ + if (playlist_valid && + (content_runtime_log || content_runtime_log_aggr)) + playlist_get_index(playlist, playlist_index, &entry); + + if (entry) + { + if (entry->runtime_status == PLAYLIST_RUNTIME_UNKNOWN) + runtime_update_playlist( + playlist, playlist_index, + directory_runtime_log, + directory_playlist, + (runtime_type == PLAYLIST_RUNTIME_PER_CORE), + runtime_last_played_style, + runtime_date_separator); + + if (!string_is_empty(entry->runtime_str)) + strlcpy(ozone->selection_playtime, entry->runtime_str, sizeof(ozone->selection_playtime)); + if (!string_is_empty(entry->last_played_str)) + strlcpy(ozone->selection_lastplayed, entry->last_played_str, sizeof(ozone->selection_lastplayed)); + } + else + { + snprintf(ozone->selection_playtime, sizeof(ozone->selection_playtime), "%s %s", + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_SUBLABEL_RUNTIME), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISABLED)); + + snprintf(ozone->selection_lastplayed, sizeof(ozone->selection_lastplayed), "%s %s", + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_SUBLABEL_LAST_PLAYED), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISABLED)); + } + + /* Word wrap last played string, if required */ + if (!scroll_content_metadata) + { + /* Note: Have to use a fixed length of '30' here, to + * avoid awkward wrapping for certain last played time + * formats. Last played strings are well defined, however + * (unlike core names), so this should never overflow the + * side bar */ + char tmpstr[sizeof(ozone->selection_lastplayed)]; + + strlcpy(tmpstr, ozone->selection_lastplayed, sizeof(tmpstr)); + (ozone->word_wrap)(ozone->selection_lastplayed, sizeof(ozone->selection_lastplayed), tmpstr, 30, 100, 0); + ozone->selection_lastplayed_lines = ozone_count_lines(ozone->selection_lastplayed); + } + else + ozone->selection_lastplayed_lines = 1; + } +} + + +static void ozone_leave_sidebar(ozone_handle_t *ozone, + settings_t *settings, + uintptr_t tag) +{ + struct gfx_animation_ctx_entry entry; + + if (ozone->empty_playlist) + return; + + ozone_update_content_metadata(ozone); + + ozone->categories_active_idx_old = ozone->categories_selection_ptr; + ozone->cursor_in_sidebar_old = ozone->cursor_in_sidebar; + ozone->cursor_in_sidebar = false; + + /* Cursor animation */ + ozone->animations.cursor_alpha = 0.0f; + + entry.cb = NULL; + entry.duration = ANIMATION_CURSOR_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &ozone->animations.cursor_alpha; + entry.tag = tag; + entry.target_value = 1.0f; + entry.userdata = NULL; + + gfx_animation_push(&entry); + + ozone_sidebar_update_collapse(ozone, settings, true); +} + +static void ozone_free_node(ozone_node_t *node) +{ + if (!node) + return; + + if (node->console_name) + free(node->console_name); + + node->console_name = NULL; + + if (node->fullpath) + free(node->fullpath); + + node->fullpath = NULL; + + free(node); +} + + +static void ozone_free_list_nodes(file_list_t *list, bool actiondata) +{ + unsigned i, size = list ? (unsigned)list->size : 0; + + for (i = 0; i < size; ++i) + { + ozone_free_node((ozone_node_t*)file_list_get_userdata_at_offset(list, i)); + + list->list[i].userdata = NULL; + + if (actiondata) + file_list_free_actiondata(list, i); + } +} + +static ozone_node_t *ozone_copy_node(const ozone_node_t *old_node) +{ + ozone_node_t *new_node = (ozone_node_t*)malloc(sizeof(*new_node)); + + *new_node = *old_node; + new_node->fullpath = old_node->fullpath + ? strdup(old_node->fullpath) + : NULL; + + return new_node; +} + +static void ozone_list_deep_copy( + const file_list_t *src, file_list_t *dst, + size_t first, size_t last) +{ + size_t i, j = 0; + uintptr_t tag = (uintptr_t)dst; + + gfx_animation_kill_by_tag(&tag); + + ozone_free_list_nodes(dst, true); + + file_list_clear(dst); + file_list_reserve(dst, (last + 1) - first); + + for (i = first; i <= last; ++i) + { + struct item_file *d = &dst->list[j]; + struct item_file *s = &src->list[i]; + void *src_udata = s->userdata; + void *src_adata = s->actiondata; + + *d = *s; + d->alt = string_is_empty(d->alt) ? NULL : strdup(d->alt); + d->path = string_is_empty(d->path) ? NULL : strdup(d->path); + d->label = string_is_empty(d->label) ? NULL : strdup(d->label); + + if (src_udata) + dst->list[j].userdata = (void*)ozone_copy_node((const ozone_node_t*)src_udata); + + if (src_adata) + { + void *data = malloc(sizeof(menu_file_list_cbs_t)); + memcpy(data, src_adata, sizeof(menu_file_list_cbs_t)); + dst->list[j].actiondata = data; + } + + ++j; + } + + dst->size = j; +} + + +static void ozone_list_cache(void *data, + enum menu_list_type type, unsigned action) +{ + size_t y, entries_end; + unsigned i; + unsigned video_info_height; + float bottom_boundary; + ozone_node_t *first_node; + float scale_factor; + unsigned first = 0; + unsigned last = 0; + file_list_t *selection_buf = NULL; + ozone_handle_t *ozone = (ozone_handle_t*)data; + + if (!ozone) + return; + + scale_factor = ozone->last_scale_factor; + ozone->need_compute = true; + ozone->selection_old_list = ozone->selection; + ozone->scroll_old = ozone->animations.scroll_y; + ozone->is_playlist_old = ozone->is_playlist; + + /* Deep copy visible elements */ + video_driver_get_size(NULL, &video_info_height); + y = ozone->dimensions.header_height + ozone->dimensions.entry_padding_vertical; + entries_end = menu_entries_get_size(); + selection_buf = menu_entries_get_selection_buf_ptr(0); + bottom_boundary = video_info_height - ozone->dimensions.header_height - ozone->dimensions.footer_height; + + for (i = 0; i < entries_end; i++) + { + ozone_node_t *node = (ozone_node_t*)selection_buf->list[i].userdata; + + if (!node) + continue; + + if (y + ozone->animations.scroll_y + node->height + 20 * scale_factor < ozone->dimensions.header_height + ozone->dimensions.entry_padding_vertical) + { + first++; + goto text_iterate; + } + else if (y + ozone->animations.scroll_y - node->height - 20 * scale_factor > bottom_boundary) + goto text_iterate; + + last++; +text_iterate: + y += node->height; + } + + last -= 1; + last += first; + + first_node = (ozone_node_t*)selection_buf->list[first].userdata; + ozone->old_list_offset_y = first_node->position_y; + + ozone_list_deep_copy(selection_buf, + &ozone->selection_buf_old, first, last); +} + +static void ozone_change_tab(ozone_handle_t *ozone, + enum msg_hash_enums tab, + enum menu_settings_type type) +{ + file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + size_t stack_size = menu_stack->size; + + if (menu_stack->list[stack_size - 1].label) + free(menu_stack->list[stack_size - 1].label); + menu_stack->list[stack_size - 1].label = NULL; + + menu_stack->list[stack_size - 1].label = + strdup(msg_hash_to_str(tab)); + menu_stack->list[stack_size - 1].type = + type; + + ozone_list_cache(ozone, MENU_LIST_HORIZONTAL, + MENU_ACTION_LEFT); + + menu_driver_deferred_push_content_list(selection_buf); +} + +static void ozone_sidebar_goto(ozone_handle_t *ozone, unsigned new_selection) +{ + unsigned video_info_height; + struct gfx_animation_ctx_entry entry; + uintptr_t tag = (uintptr_t)ozone; + + video_driver_get_size(NULL, &video_info_height); + + if (ozone->categories_selection_ptr != new_selection) + { + ozone->categories_active_idx_old = ozone->categories_selection_ptr; + ozone->categories_selection_ptr = new_selection; + + ozone->cursor_in_sidebar_old = ozone->cursor_in_sidebar; + + gfx_animation_kill_by_tag(&tag); + } + + /* ozone->animations.scroll_y_sidebar will be modified + * > Set scroll acceleration to zero to minimise + * potential conflicts */ + menu_input_set_pointer_y_accel(0.0f); + + /* Cursor animation */ + ozone->animations.cursor_alpha = 0.0f; + + entry.cb = NULL; + entry.duration = ANIMATION_CURSOR_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &ozone->animations.cursor_alpha; + entry.tag = tag; + entry.target_value = 1.0f; + entry.userdata = NULL; + + gfx_animation_push(&entry); + + /* Scroll animation */ + entry.cb = NULL; + entry.duration = ANIMATION_CURSOR_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &ozone->animations.scroll_y_sidebar; + entry.tag = tag; + entry.target_value = ozone_sidebar_get_scroll_y(ozone, video_info_height); + entry.userdata = NULL; + + gfx_animation_push(&entry); + + if (new_selection > ozone->system_tab_end) + ozone_change_tab(ozone, MENU_ENUM_LABEL_HORIZONTAL_MENU, MENU_SETTING_HORIZONTAL_MENU); + else + ozone_change_tab(ozone, ozone_system_tabs_idx[ozone->tabs[new_selection]], ozone_system_tabs_type[ozone->tabs[new_selection]]); +} + +static void ozone_refresh_sidebars( + ozone_handle_t *ozone, + settings_t *settings, + unsigned video_height) +{ + uintptr_t collapsed_tag = (uintptr_t)&ozone->sidebar_collapsed; + uintptr_t offset_tag = (uintptr_t)&ozone->sidebar_offset; + uintptr_t thumbnail_tag = (uintptr_t)&ozone->show_thumbnail_bar; + uintptr_t scroll_tag = (uintptr_t)ozone; + bool is_playlist = ozone_is_playlist(ozone, false); + bool collapse_sidebar = settings->bools.ozone_collapse_sidebar; + + /* Kill any existing animations */ + gfx_animation_kill_by_tag(&collapsed_tag); + gfx_animation_kill_by_tag(&offset_tag); + gfx_animation_kill_by_tag(&thumbnail_tag); + if (ozone->depth == 1) + gfx_animation_kill_by_tag(&scroll_tag); + + /* Set sidebar width */ + if (collapse_sidebar || (is_playlist && !ozone->cursor_in_sidebar)) + { + ozone->animations.sidebar_text_alpha = 0.0f; + ozone->dimensions_sidebar_width = ozone->dimensions.sidebar_width_collapsed; + ozone->sidebar_collapsed = true; + } + else if (ozone->cursor_in_sidebar || (!is_playlist && !collapse_sidebar)) + { + ozone->animations.sidebar_text_alpha = 1.0f; + ozone->dimensions_sidebar_width = ozone->dimensions.sidebar_width_normal; + ozone->sidebar_collapsed = false; + } + + /* Set sidebar offset */ + if (ozone->depth == 1) + { + ozone->sidebar_offset = 0.0f; + ozone->draw_sidebar = true; + } + else if (ozone->depth > 1) + { + ozone->sidebar_offset = -ozone->dimensions_sidebar_width; + ozone->draw_sidebar = false; + } + + /* Set thumbnail bar position */ + if (is_playlist && !ozone->cursor_in_sidebar && ozone->depth == 1) + { + ozone->animations.thumbnail_bar_position = ozone->dimensions.thumbnail_bar_width; + ozone->show_thumbnail_bar = true; + } + else + { + ozone->animations.thumbnail_bar_position = 0.0f; + ozone->show_thumbnail_bar = false; + } + ozone->pending_hide_thumbnail_bar = false; + + /* If sidebar is on-screen, update scroll position */ + if (ozone->depth == 1) + { + ozone->animations.cursor_alpha = 1.0f; + ozone->animations.scroll_y_sidebar = ozone_sidebar_get_scroll_y(ozone, video_height); + } +} + +static size_t ozone_list_get_size(void *data, enum menu_list_type type) +{ + ozone_handle_t *ozone = (ozone_handle_t*) data; + + if (!ozone) + return 0; + + switch (type) + { + case MENU_LIST_PLAIN: + return menu_entries_get_stack_size(0); + case MENU_LIST_HORIZONTAL: + return ozone->horizontal_list.size; + case MENU_LIST_TABS: + return ozone->system_tab_end; + } + + return 0; +} + + +static void ozone_init_horizontal_list(ozone_handle_t *ozone, + settings_t *settings) +{ + menu_displaylist_info_t info; + size_t list_size; + size_t i; + const char *dir_playlist = settings->paths.directory_playlist; + bool menu_content_show_playlists = settings->bools.menu_content_show_playlists; + bool ozone_truncate_playlist_name = settings->bools.ozone_truncate_playlist_name; + bool ozone_sort_after_truncate = settings->bools.ozone_sort_after_truncate_playlist_name; + + menu_displaylist_info_init(&info); + + info.list = &ozone->horizontal_list; + info.path = strdup(dir_playlist); + info.label = strdup( + msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB)); + info.exts = strdup("lpl"); + info.type_default = FILE_TYPE_PLAIN; + info.enum_idx = MENU_ENUM_LABEL_PLAYLISTS_TAB; + + if (menu_content_show_playlists && !string_is_empty(info.path)) + { + if (menu_displaylist_ctl(DISPLAYLIST_DATABASE_PLAYLISTS_HORIZONTAL, + &info, settings)) + menu_displaylist_process(&info); + } + + menu_displaylist_info_free(&info); + + /* Loop through list and set console names */ + list_size = ozone_list_get_size(ozone, MENU_LIST_HORIZONTAL); + + for (i = 0; i < list_size; i++) + { + char playlist_file_noext[255]; + char *console_name = NULL; + const char *playlist_file = ozone->horizontal_list.list[i].path; + + playlist_file_noext[0] = '\0'; + + if (!playlist_file) + { + file_list_set_alt_at_offset(&ozone->horizontal_list, i, NULL); + continue; + } + + /* Remove extension */ + fill_pathname_base_noext(playlist_file_noext, + playlist_file, sizeof(playlist_file_noext)); + + console_name = playlist_file_noext; + + /* Truncate playlist names, if required + * > Format: "Vendor - Console" + Remove everything before the hyphen + and the subsequent space */ + if (ozone_truncate_playlist_name) + { + bool hyphen_found = false; + + for (;;) + { + /* Check for "- " */ + if (*console_name == '\0') + break; + else if (*console_name == '-' && *(console_name + 1) == ' ') + { + hyphen_found = true; + break; + } + + console_name++; + } + + if (hyphen_found) + console_name += 2; + else + console_name = playlist_file_noext; + } + + /* Assign console name to list */ + file_list_set_alt_at_offset(&ozone->horizontal_list, i, console_name); + } + + /* If playlist names were truncated and option is + * enabled, re-sort list by console name */ + if (ozone_truncate_playlist_name && + ozone_sort_after_truncate && + (list_size > 0)) + file_list_sort_on_alt(&ozone->horizontal_list); +} + +static void ozone_context_destroy_horizontal_list(ozone_handle_t *ozone) +{ + unsigned i; + size_t list_size = ozone_list_get_size(ozone, MENU_LIST_HORIZONTAL); + + for (i = 0; i < list_size; i++) + { + const char *path = NULL; + ozone_node_t *node = (ozone_node_t*)ozone->horizontal_list.list[i].userdata; + + if (!node) + continue; + + if (!(path = ozone->horizontal_list.list[i].path)) + continue; + if (string_ends_with_size(path, ".lpl", + strlen(path), STRLEN_CONST(".lpl"))) + { + video_driver_texture_unload(&node->icon); + video_driver_texture_unload(&node->content_icon); + } + } +} + +static ozone_node_t *ozone_alloc_node(void) +{ + ozone_node_t *node = (ozone_node_t*)malloc(sizeof(*node)); + + node->height = 0; + node->position_y = 0; + node->console_name = NULL; + node->icon = 0; + node->content_icon = 0; + node->fullpath = NULL; + node->sublabel_lines = 0; + node->wrap = false; + + return node; +} + + +static void ozone_context_reset_horizontal_list(ozone_handle_t *ozone) +{ + unsigned i; + size_t list_size = ozone_list_get_size(ozone, MENU_LIST_HORIZONTAL); + + for (i = 0; i < list_size; i++) + { + const char *path = NULL; + const char *console_name = NULL; + ozone_node_t *node = (ozone_node_t*)ozone->horizontal_list.list[i].userdata; + + if (!node) + { + node = ozone_alloc_node(); + if (!node) + continue; + } + + if (!(path = ozone->horizontal_list.list[i].path)) + continue; + if (string_ends_with_size(path, ".lpl", + strlen(path), STRLEN_CONST(".lpl"))) + { + struct texture_image ti; + char sysname[PATH_MAX_LENGTH]; + char texturepath[PATH_MAX_LENGTH]; + char content_texturepath[PATH_MAX_LENGTH]; + char icons_path[PATH_MAX_LENGTH]; + + strlcpy(icons_path, ozone->icons_path, sizeof(icons_path)); + + sysname[0] = texturepath[0] = content_texturepath[0] = '\0'; + + fill_pathname_base_noext(sysname, path, sizeof(sysname)); + + fill_pathname_join_concat(texturepath, icons_path, sysname, + ".png", sizeof(texturepath)); + + /* If the playlist icon doesn't exist return default */ + + if (!path_is_valid(texturepath)) + fill_pathname_join_concat(texturepath, icons_path, "default", + ".png", sizeof(texturepath)); + + ti.width = 0; + ti.height = 0; + ti.pixels = NULL; + ti.supports_rgba = video_driver_supports_rgba(); + + if (image_texture_load(&ti, texturepath)) + { + if (ti.pixels) + { + video_driver_texture_unload(&node->icon); + video_driver_texture_load(&ti, + TEXTURE_FILTER_MIPMAP_LINEAR, &node->icon); + } + + image_texture_free(&ti); + } + + fill_pathname_join_delim(sysname, sysname, + "content.png", '-', sizeof(sysname)); + strlcat(content_texturepath, icons_path, sizeof(content_texturepath)); + strlcat(content_texturepath, PATH_DEFAULT_SLASH(), sizeof(content_texturepath)); + strlcat(content_texturepath, sysname, sizeof(content_texturepath)); + + /* If the content icon doesn't exist return default-content */ + if (!path_is_valid(content_texturepath)) + { + strlcat(icons_path, + PATH_DEFAULT_SLASH() "default", sizeof(icons_path)); + fill_pathname_join_delim(content_texturepath, icons_path, + "content.png", '-', sizeof(content_texturepath)); + } + + if (image_texture_load(&ti, content_texturepath)) + { + if (ti.pixels) + { + video_driver_texture_unload(&node->content_icon); + video_driver_texture_load(&ti, + TEXTURE_FILTER_MIPMAP_LINEAR, &node->content_icon); + } + + image_texture_free(&ti); + } + + /* Console name */ + console_name = ozone->horizontal_list.list[i].alt + ? ozone->horizontal_list.list[i].alt + : ozone->horizontal_list.list[i].path; + + if (node->console_name) + free(node->console_name); + + /* Note: console_name will *always* be valid here, + * but provide a fallback to prevent NULL pointer + * dereferencing in case of unknown errors... */ + if (console_name) + node->console_name = strdup(console_name); + else + node->console_name = strdup(path); + } + } +} + +static void ozone_refresh_horizontal_list(ozone_handle_t *ozone, + settings_t *settings) +{ + ozone_context_destroy_horizontal_list(ozone); + ozone_free_list_nodes(&ozone->horizontal_list, false); + file_list_deinitialize(&ozone->horizontal_list); + + menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); + + file_list_initialize(&ozone->horizontal_list); + ozone_init_horizontal_list(ozone, settings); + + ozone_context_reset_horizontal_list(ozone); +} + + +static int ozone_get_entries_padding(ozone_handle_t* ozone, bool old_list) +{ + if (ozone->depth == 1) + { + if (!old_list) + return ozone->dimensions.entry_padding_horizontal_half; + } + else if (ozone->depth == 2) + { + if (old_list && !ozone->fade_direction) /* false = left to right */ + return ozone->dimensions.entry_padding_horizontal_half; + } + return ozone->dimensions.entry_padding_horizontal_full; +} + +static void ozone_draw_entry_value( + ozone_handle_t *ozone, + gfx_display_t *p_disp, + void *userdata, + unsigned video_width, + unsigned video_height, + char *value, + unsigned x, unsigned y, + uint32_t alpha_uint32, + menu_entry_t *entry) +{ + bool switch_is_on = true; + bool do_draw_text = false; + float scale_factor = ozone->last_scale_factor; + gfx_display_ctx_driver_t *dispctx = p_disp->dispctx; + + /* check icon */ + if (entry->checked) + { + float *col = ozone->theme_dynamic.entries_checkmark; + if (dispctx && dispctx->blend_begin) + dispctx->blend_begin(userdata); + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + 30 * scale_factor, + 30 * scale_factor, + ozone->theme->textures[OZONE_THEME_TEXTURE_CHECK], + x - 20 * scale_factor, + y - 22 * scale_factor, + video_width, + video_height, + 0, + 1, + col); + if (dispctx && dispctx->blend_end) + dispctx->blend_end(userdata); + return; + } + else if (string_is_empty(value)) + return; + + + /* text value */ + if (string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)) || + (string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)))) + switch_is_on = false; + else if (string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) || + (string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)))) { } + else + { + if (!string_is_empty(entry->value)) + { + if (string_is_equal(entry->value, "...")) + return; + if (string_starts_with_size(entry->value, "(", STRLEN_CONST("(")) && + string_ends_with (entry->value, ")") + ) + { + if ( + string_is_equal(entry->value, "(PRESET)") || + string_is_equal(entry->value, "(SHADER)") || + string_is_equal(entry->value, "(COMP)") || + string_is_equal(entry->value, "(CORE)") || + string_is_equal(entry->value, "(MOVIE)") || + string_is_equal(entry->value, "(MUSIC)") || + string_is_equal(entry->value, "(DIR)") || + string_is_equal(entry->value, "(RDB)") || + string_is_equal(entry->value, "(CURSOR)")|| + string_is_equal(entry->value, "(CFILE)") || + string_is_equal(entry->value, "(FILE)") || + string_is_equal(entry->value, "(IMAGE)") + ) + return; + } + } + + do_draw_text = true; + } + + if (do_draw_text) + { + gfx_display_draw_text( + ozone->fonts.entries_label.font, + value, + x, + y, + video_width, + video_height, + COLOR_TEXT_ALPHA(ozone->theme->text_selected_rgba, alpha_uint32), + TEXT_ALIGN_RIGHT, + 1.0f, + false, + 1.0f, + false); + } + else + gfx_display_draw_text( + ozone->fonts.entries_label.font, + (switch_is_on ? msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON) : msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)), + x, + y, + video_width, + video_height, + COLOR_TEXT_ALPHA((switch_is_on ? ozone->theme->text_selected_rgba : ozone->theme->text_sublabel_rgba), alpha_uint32), + TEXT_ALIGN_RIGHT, + 1.0f, + false, + 1.0f, + false); +} + +static void ozone_draw_no_thumbnail_available( + ozone_handle_t *ozone, + gfx_display_t *p_disp, + void *userdata, + unsigned video_width, + unsigned video_height, + unsigned x_position, + unsigned sidebar_width, + unsigned y_offset) +{ + unsigned icon = OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO; + unsigned icon_size = (unsigned)((float) + ozone->dimensions.sidebar_entry_icon_size * 1.5f); + gfx_display_ctx_driver_t *dispctx = p_disp->dispctx; + float *col = ozone->theme->entries_icon; + + if (dispctx) + { + if (dispctx->blend_begin) + dispctx->blend_begin(userdata); + if (dispctx->draw) + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + icon_size, + icon_size, + ozone->icons_textures[icon], + x_position + sidebar_width/2 - icon_size/2, + video_height/2 - icon_size/2 - y_offset, + video_width, + video_height, + 0, 1, col); + if (dispctx->blend_end) + dispctx->blend_end(userdata); + } + + gfx_display_draw_text( + ozone->fonts.footer.font, + msg_hash_to_str(MSG_NO_THUMBNAIL_AVAILABLE), + x_position + sidebar_width / 2, + video_height / 2 + icon_size / 2 + + ozone->fonts.footer.line_ascender - y_offset, + video_width, + video_height, + ozone->theme->text_rgba, + TEXT_ALIGN_CENTER, + 1.0f, + false, + 1.0f, + true); +} + +static void ozone_content_metadata_line( + unsigned video_width, + unsigned video_height, + ozone_handle_t *ozone, + unsigned *y, + unsigned column_x, + const char *text, + uint32_t color, + unsigned lines_count) +{ + gfx_display_draw_text( + ozone->fonts.footer.font, + text, + column_x, + *y + ozone->fonts.footer.line_ascender, + video_width, + video_height, + color, + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + true); + + if (lines_count > 0) + *y += (unsigned)(ozone->fonts.footer.line_height * (lines_count - 1)) + (unsigned)((float)ozone->fonts.footer.line_height * 1.5f); +} + + +/* Compute new scroll position + * If the center of the currently selected entry is not in the middle + * And if we can scroll so that it's in the middle + * Then scroll + */ +static void ozone_update_scroll(ozone_handle_t *ozone, + bool allow_animation, ozone_node_t *node) +{ + unsigned video_info_height; + gfx_animation_ctx_entry_t entry; + float new_scroll = 0, entries_middle; + float bottom_boundary, current_selection_middle_onscreen; + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + uintptr_t tag = (uintptr_t) selection_buf; + + video_driver_get_size(NULL, &video_info_height); + + if (!node) + return; + + current_selection_middle_onscreen = + ozone->dimensions.header_height + + ozone->dimensions.entry_padding_vertical + + ozone->animations.scroll_y + + node->position_y + + node->height / 2; + + bottom_boundary = video_info_height - ozone->dimensions.header_height - ozone->dimensions.spacer_1px - ozone->dimensions.footer_height; + entries_middle = video_info_height/2; + + new_scroll = ozone->animations.scroll_y - (current_selection_middle_onscreen - entries_middle); + + if (new_scroll + ozone->entries_height < bottom_boundary) + new_scroll = bottom_boundary - ozone->entries_height - ozone->dimensions.entry_padding_vertical * 2; + + if (new_scroll > 0) + new_scroll = 0; + + /* Kill any existing scroll animation */ + gfx_animation_kill_by_tag(&tag); + + /* ozone->animations.scroll_y will be modified + * > Set scroll acceleration to zero to minimise + * potential conflicts */ + menu_input_set_pointer_y_accel(0.0f); + + if (allow_animation) + { + /* Cursor animation */ + ozone->animations.cursor_alpha = 0.0f; + + entry.cb = NULL; + entry.duration = ANIMATION_CURSOR_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &ozone->animations.cursor_alpha; + entry.tag = tag; + entry.target_value = 1.0f; + entry.userdata = NULL; + + gfx_animation_push(&entry); + + /* Scroll animation */ + entry.cb = NULL; + entry.duration = ANIMATION_CURSOR_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &ozone->animations.scroll_y; + entry.tag = tag; + entry.target_value = new_scroll; + entry.userdata = NULL; + + gfx_animation_push(&entry); + } + else + { + ozone->selection_old = ozone->selection; + ozone->animations.scroll_y = new_scroll; + } +} + +static void ozone_compute_entries_position( + ozone_handle_t *ozone, + settings_t *settings, + size_t entries_end) +{ + /* Compute entries height and adjust scrolling if needed */ + unsigned video_info_height; + unsigned video_info_width; + size_t i; + file_list_t *selection_buf = NULL; + int entry_padding = ozone_get_entries_padding(ozone, false); + float scale_factor = ozone->last_scale_factor; + bool menu_show_sublabels = settings->bools.menu_show_sublabels; + + menu_entries_ctl(MENU_ENTRIES_CTL_START_GET, &i); + + selection_buf = menu_entries_get_selection_buf_ptr(0); + + video_driver_get_size(&video_info_width, &video_info_height); + + ozone->entries_height = 0; + + for (i = 0; i < entries_end; i++) + { + /* Entry */ + menu_entry_t entry; + ozone_node_t *node = NULL; + + MENU_ENTRY_INIT(entry); + entry.path_enabled = false; + entry.label_enabled = false; + entry.rich_label_enabled = false; + entry.value_enabled = false; + menu_entry_get(&entry, 0, (unsigned)i, NULL, true); + + /* Empty playlist detection: + only one item which icon is + OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO */ + if (ozone->is_playlist && entries_end == 1) + { + uintptr_t tex = ozone_entries_icon_get_texture(ozone, entry.enum_idx, entry.type, false); + ozone->empty_playlist = tex == ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO]; + } + else + ozone->empty_playlist = false; + + /* Cache node */ + node = (ozone_node_t*)selection_buf->list[i].userdata; + + if (!node) + continue; + + node->height = ozone->dimensions.entry_height; + node->wrap = false; + node->sublabel_lines = 0; + + if (menu_show_sublabels) + { + if (!string_is_empty(entry.sublabel)) + { + int sublabel_max_width; + char wrapped_sublabel_str[MENU_SUBLABEL_MAX_LENGTH]; + wrapped_sublabel_str[0] = '\0'; + + node->height += ozone->dimensions.entry_spacing + 40 * scale_factor; + + sublabel_max_width = video_info_width - + entry_padding * 2 - ozone->dimensions.entry_icon_padding * 2; + + if (ozone->depth == 1) + { + sublabel_max_width -= (unsigned) ozone->dimensions_sidebar_width; + + if (ozone->show_thumbnail_bar) + sublabel_max_width -= ozone->dimensions.thumbnail_bar_width; + } + + (ozone->word_wrap)(wrapped_sublabel_str, sizeof(wrapped_sublabel_str), entry.sublabel, + sublabel_max_width / + ozone->fonts.entries_sublabel.glyph_width, + ozone->fonts.entries_sublabel.wideglyph_width, 0); + + node->sublabel_lines = ozone_count_lines(wrapped_sublabel_str); + + if (node->sublabel_lines > 1) + { + node->height += (node->sublabel_lines - 1) * ozone->fonts.entries_sublabel.line_height; + node->wrap = true; + } + } + } + + node->position_y = ozone->entries_height; + + ozone->entries_height += node->height; + } + + /* Update scrolling */ + ozone->selection = menu_navigation_get_selection(); + ozone_update_scroll(ozone, false, (ozone_node_t*)selection_buf->list[ozone->selection].userdata); +} + +static void ozone_draw_entries( + ozone_handle_t *ozone, + gfx_display_t *p_disp, + gfx_animation_t *p_anim, + settings_t *settings, + void *userdata, + unsigned video_width, + unsigned video_height, + unsigned selection, + unsigned selection_old, + file_list_t *selection_buf, + float alpha, + float scroll_y, + bool is_playlist) +{ + uint32_t alpha_uint32; + size_t i; + float bottom_boundary; + unsigned video_info_height, video_info_width; + bool menu_show_sublabels = settings->bools.menu_show_sublabels; + bool use_smooth_ticker = settings->bools.menu_ticker_smooth; + enum gfx_animation_ticker_type + menu_ticker_type = (enum gfx_animation_ticker_type) + settings->uints.menu_ticker_type; + bool old_list = selection_buf == &ozone->selection_buf_old; + int x_offset = 0; + size_t selection_y = 0; /* 0 means no selection (we assume that no entry has y = 0) */ + size_t old_selection_y = 0; + int entry_padding = ozone_get_entries_padding(ozone, old_list); + float scale_factor = ozone->last_scale_factor; + gfx_display_ctx_driver_t *dispctx = p_disp->dispctx; + size_t entries_end = selection_buf ? selection_buf->size : 0; + size_t y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.entry_padding_vertical; + float sidebar_offset = ozone->sidebar_offset; + unsigned entry_width = video_width - (unsigned) ozone->dimensions_sidebar_width - ozone->sidebar_offset - entry_padding * 2 - ozone->animations.thumbnail_bar_position; + unsigned button_height = ozone->dimensions.entry_height; /* height of the button (entry minus sublabel) */ + float invert = (ozone->fade_direction) ? -1 : 1; + float alpha_anim = old_list ? alpha : 1.0f - alpha; + + video_driver_get_size(&video_info_width, &video_info_height); + + bottom_boundary = video_info_height - ozone->dimensions.header_height - ozone->dimensions.footer_height; + + if (old_list) + { + alpha = 1.0f - alpha; + if (alpha != 1.0f) + x_offset += invert * -(alpha_anim * 120 * scale_factor); /* left */ + } + else + { + if (alpha != 1.0f) + x_offset += invert * (alpha_anim * 120 * scale_factor); /* right */ + } + + x_offset += (int)sidebar_offset; + alpha_uint32 = (uint32_t)(alpha * 255.0f); + + /* Borders layer */ + for (i = 0; i < entries_end; i++) + { + bool entry_selected = selection == i; + bool entry_old_selected = selection_old == i; + + int border_start_x, border_start_y; + + ozone_node_t *node = NULL; + + if (entry_selected && selection_y == 0) + selection_y = y; + + if (entry_old_selected && old_selection_y == 0) + old_selection_y = y; + + node = (ozone_node_t*)selection_buf->list[i].userdata; + + if (!node || ozone->empty_playlist) + goto border_iterate; + + if (y + scroll_y + node->height + 20 * scale_factor < ozone->dimensions.header_height + ozone->dimensions.entry_padding_vertical) + goto border_iterate; + else if (y + scroll_y - node->height - 20 * scale_factor > bottom_boundary) + goto border_iterate; + + border_start_x = (unsigned) ozone->dimensions_sidebar_width + + x_offset + entry_padding; + border_start_y = y + scroll_y; + + gfx_display_set_alpha(ozone->theme_dynamic.entries_border, alpha); + gfx_display_set_alpha(ozone->theme_dynamic.entries_checkmark, alpha); + + /* Borders */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + border_start_x, + border_start_y, + entry_width, + ozone->dimensions.spacer_1px, + video_width, + video_height, + ozone->theme_dynamic.entries_border, + NULL); + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + border_start_x, + border_start_y + button_height, + entry_width, + ozone->dimensions.spacer_1px, + video_width, + video_height, + ozone->theme_dynamic.entries_border, + NULL); + +border_iterate: + if (node) + y += node->height; + } + + /* Cursor(s) layer - current */ + if (!ozone->cursor_in_sidebar) + ozone_draw_cursor( + ozone, + p_disp, + userdata, + video_width, + video_height, + ozone->dimensions_sidebar_width + + x_offset + entry_padding + ozone->dimensions.spacer_3px, + entry_width - ozone->dimensions.spacer_5px, + button_height + ozone->dimensions.spacer_1px, + selection_y + scroll_y, + ozone->animations.cursor_alpha * alpha); + + /* Old*/ + if (!ozone->cursor_in_sidebar_old) + ozone_draw_cursor( + ozone, + p_disp, + userdata, + video_width, + video_height, + (unsigned)ozone->dimensions_sidebar_width + + x_offset + entry_padding + ozone->dimensions.spacer_3px, + /* TODO/FIXME - undefined behavior reported by ASAN - + *-35.2358 is outside the range of representable values + of type 'unsigned int' + * */ + entry_width - ozone->dimensions.spacer_5px, + button_height + ozone->dimensions.spacer_1px, + old_selection_y + scroll_y, + (1-ozone->animations.cursor_alpha) * alpha); + + /* Icons + text */ + y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.entry_padding_vertical; + + if (old_list) + y += ozone->old_list_offset_y; + + for (i = 0; i < entries_end; i++) + { + char rich_label[255]; + char entry_value_ticker[255]; + char wrapped_sublabel_str[MENU_SUBLABEL_MAX_LENGTH]; + uintptr_t tex; + menu_entry_t entry; + gfx_animation_ctx_ticker_t ticker; + gfx_animation_ctx_ticker_smooth_t ticker_smooth; + unsigned ticker_x_offset = 0; + unsigned ticker_str_width = 0; + int value_x_offset = 0; + static const char* const + ticker_spacer = OZONE_TICKER_SPACER; + const char *sublabel_str = NULL; + ozone_node_t *node = NULL; + const char *entry_rich_label = NULL; + const char *entry_value = NULL; + bool entry_selected = false; + int text_offset = -ozone->dimensions.entry_icon_padding - ozone->dimensions.entry_icon_size; + float *icon_color = NULL; + + /* Initial ticker configuration */ + if (use_smooth_ticker) + { + ticker_smooth.idx = p_anim->ticker_pixel_idx; + ticker_smooth.font = ozone->fonts.entries_label.font; + ticker_smooth.font_scale = 1.0f; + ticker_smooth.type_enum = menu_ticker_type; + ticker_smooth.spacer = ticker_spacer; + ticker_smooth.x_offset = &ticker_x_offset; + ticker_smooth.dst_str_width = &ticker_str_width; + } + else + { + ticker.idx = p_anim->ticker_idx; + ticker.type_enum = menu_ticker_type; + ticker.spacer = ticker_spacer; + } + + node = (ozone_node_t*)selection_buf->list[i].userdata; + + if (!node) + continue; + + if (y + scroll_y + node->height + 20 * scale_factor + < ozone->dimensions.header_height + + ozone->dimensions.entry_padding_vertical) + { + y += node->height; + continue; + } + else if (y + scroll_y - node->height - 20 * scale_factor + > bottom_boundary) + { + y += node->height; + continue; + } + + entry_selected = selection == i; + + MENU_ENTRY_INIT(entry); + entry.path_enabled = false; + entry.label_enabled = false; + menu_entry_get(&entry, 0, (unsigned)i, selection_buf, true); + + if (entry.enum_idx == MENU_ENUM_LABEL_CHEEVOS_PASSWORD) + entry_value = entry.password_value; + else + entry_value = entry.value; + + /* Prepare text */ + if (!string_is_empty(entry.rich_label)) + entry_rich_label = entry.rich_label; + else + entry_rich_label = entry.path; + + if (use_smooth_ticker) + { + ticker_smooth.selected = entry_selected && !ozone->cursor_in_sidebar; + ticker_smooth.field_width = entry_width - entry_padding - (10 * scale_factor) - ozone->dimensions.entry_icon_padding; + ticker_smooth.src_str = entry_rich_label; + ticker_smooth.dst_str = rich_label; + ticker_smooth.dst_str_len = sizeof(rich_label); + + gfx_animation_ticker_smooth(&ticker_smooth); + } + else + { + ticker.s = rich_label; + ticker.str = entry_rich_label; + ticker.selected = entry_selected && !ozone->cursor_in_sidebar; + ticker.len = (entry_width - entry_padding - (10 * scale_factor) - ozone->dimensions.entry_icon_padding) / ozone->fonts.entries_label.glyph_width; + + gfx_animation_ticker(&ticker); + } + + if (ozone->empty_playlist) + { + /* Note: This entry can never be selected, so ticker_x_offset + * is irrelevant here (i.e. this text will never scroll) */ + unsigned text_width = font_driver_get_message_width(ozone->fonts.entries_label.font, rich_label, (unsigned)strlen(rich_label), 1); + x_offset = (video_info_width - (unsigned) + ozone->dimensions_sidebar_width - entry_padding * 2) + / 2 - text_width / 2 - 60 * scale_factor; + y = video_info_height / 2 - 60 * scale_factor; + } + + sublabel_str = entry.sublabel; + + if (menu_show_sublabels) + { + if (node->wrap && !string_is_empty(sublabel_str)) + { + int sublabel_max_width = video_info_width - + entry_padding * 2 - ozone->dimensions.entry_icon_padding * 2; + + if (ozone->depth == 1) + { + sublabel_max_width -= (unsigned) + ozone->dimensions_sidebar_width; + + if (ozone->show_thumbnail_bar) + sublabel_max_width -= ozone->dimensions.thumbnail_bar_width; + } + + wrapped_sublabel_str[0] = '\0'; + (ozone->word_wrap)(wrapped_sublabel_str, sizeof(wrapped_sublabel_str), + sublabel_str, sublabel_max_width / ozone->fonts.entries_sublabel.glyph_width, + ozone->fonts.entries_sublabel.wideglyph_width, 0); + sublabel_str = wrapped_sublabel_str; + } + } + + /* Icon */ + tex = ozone_entries_icon_get_texture(ozone, entry.enum_idx, entry.type, entry_selected); + if (tex != ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SUBSETTING]) + { + uintptr_t texture = tex; + + /* Console specific icons */ + if ( entry.type == FILE_TYPE_RPL_ENTRY + && ozone->categories_selection_ptr > ozone->system_tab_end) + { + ozone_node_t *sidebar_node = (ozone_node_t*) file_list_get_userdata_at_offset(&ozone->horizontal_list, ozone->categories_selection_ptr - ozone->system_tab_end-1); + + if (!sidebar_node || !sidebar_node->content_icon) + texture = tex; + else + texture = sidebar_node->content_icon; + } + + /* Cheevos badges should not be recolored */ + if (!( + (entry.type >= MENU_SETTINGS_CHEEVOS_START) && + (entry.type < MENU_SETTINGS_NETPLAY_ROOMS_START) + )) + icon_color = ozone->theme_dynamic.entries_icon; + else + icon_color = ozone->pure_white; + + gfx_display_set_alpha(icon_color, alpha); + + if (dispctx) + { + if (dispctx->blend_begin) + dispctx->blend_begin(userdata); + if (dispctx->draw) + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + ozone->dimensions.entry_icon_size, + ozone->dimensions.entry_icon_size, + texture, + ozone->dimensions_sidebar_width + + x_offset + entry_padding + + ozone->dimensions.entry_icon_padding, + y + scroll_y + ozone->dimensions.entry_height + / 2 - ozone->dimensions.entry_icon_size / 2, + video_width, + video_height, + 0, + 1, + icon_color); + if (dispctx->blend_end) + dispctx->blend_end(userdata); + } + + if (icon_color == ozone->pure_white) + gfx_display_set_alpha(icon_color, 1.0f); + + text_offset = 0; + } + + /* Draw text */ + gfx_display_draw_text( + ozone->fonts.entries_label.font, + rich_label, + ticker_x_offset + text_offset + (unsigned) + ozone->dimensions_sidebar_width + x_offset + + entry_padding + ozone->dimensions.entry_icon_size + + ozone->dimensions.entry_icon_padding * 2, + y + ozone->dimensions.entry_height / 2.0f + + ozone->fonts.entries_label.line_centre_offset + + scroll_y, + video_width, + video_height, + COLOR_TEXT_ALPHA(ozone->theme->text_rgba, alpha_uint32), + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + false); + + if (menu_show_sublabels) + { + if (!string_is_empty(sublabel_str)) + gfx_display_draw_text( + ozone->fonts.entries_sublabel.font, + sublabel_str, + (unsigned) ozone->dimensions_sidebar_width + + x_offset + entry_padding + + ozone->dimensions.entry_icon_padding, + y + ozone->dimensions.entry_height - ozone->dimensions.spacer_1px + (node->height - ozone->dimensions.entry_height - (node->sublabel_lines * ozone->fonts.entries_sublabel.line_height))/2.0f + ozone->fonts.entries_sublabel.line_ascender + scroll_y, + video_width, + video_height, + COLOR_TEXT_ALPHA(ozone->theme->text_sublabel_rgba,alpha_uint32), + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + false); + } + + /* Value */ + if (use_smooth_ticker) + { + ticker_smooth.selected = entry_selected && !ozone->cursor_in_sidebar; + ticker_smooth.field_width = (entry_width - ozone->dimensions.entry_icon_size - ozone->dimensions.entry_icon_padding * 2 - + ((unsigned)utf8len(entry_rich_label) * ozone->fonts.entries_label.glyph_width)); + ticker_smooth.src_str = entry_value; + ticker_smooth.dst_str = entry_value_ticker; + ticker_smooth.dst_str_len = sizeof(entry_value_ticker); + + /* Value text is right aligned, so have to offset x + * by the 'padding' width at the end of the ticker string... */ + if (gfx_animation_ticker_smooth(&ticker_smooth)) + value_x_offset = (ticker_x_offset + ticker_str_width) - ticker_smooth.field_width; + } + else + { + ticker.s = entry_value_ticker; + ticker.str = entry_value; + ticker.selected = entry_selected && !ozone->cursor_in_sidebar; + ticker.len = (entry_width - ozone->dimensions.entry_icon_size - ozone->dimensions.entry_icon_padding * 2 - + ((unsigned)utf8len(entry_rich_label) * ozone->fonts.entries_label.glyph_width)) / ozone->fonts.entries_label.glyph_width; + + gfx_animation_ticker(&ticker); + } + + ozone_draw_entry_value(ozone, + p_disp, + userdata, + video_width, + video_height, + entry_value_ticker, + value_x_offset + (unsigned) ozone->dimensions_sidebar_width + + entry_padding + x_offset + + entry_width - ozone->dimensions.entry_icon_padding, + y + ozone->dimensions.entry_height / 2 + ozone->fonts.entries_label.line_centre_offset + scroll_y, + alpha_uint32, + &entry); + + y += node->height; + } + + /* Text layer */ + ozone_font_flush(video_width, video_height, &ozone->fonts.entries_label); + + if (menu_show_sublabels) + ozone_font_flush(video_width, video_height, &ozone->fonts.entries_sublabel); +} + +static void ozone_draw_thumbnail_bar( + ozone_handle_t *ozone, + gfx_display_t *p_disp, + gfx_animation_t *p_anim, + settings_t *settings, + void *userdata, + unsigned video_width, + unsigned video_height, + bool libretro_running, + float menu_framebuffer_opacity) +{ + enum gfx_thumbnail_alignment right_thumbnail_alignment; + enum gfx_thumbnail_alignment left_thumbnail_alignment; + unsigned sidebar_width = ozone->dimensions.thumbnail_bar_width; + unsigned thumbnail_width = sidebar_width - (ozone->dimensions.sidebar_entry_icon_padding * 2); + int right_thumbnail_y_position = 0; + int left_thumbnail_y_position = 0; + int bottom_row_y_position = 0; + bool show_right_thumbnail = false; + bool show_left_thumbnail = false; + unsigned sidebar_height = video_height - ozone->dimensions.header_height - ozone->dimensions.sidebar_gradient_height * 2 - ozone->dimensions.footer_height; + unsigned x_position = video_width - (unsigned) ozone->animations.thumbnail_bar_position; + int thumbnail_x_position = x_position + ozone->dimensions.sidebar_entry_icon_padding; + unsigned thumbnail_height = (video_height - ozone->dimensions.header_height - ozone->dimensions.spacer_2px - ozone->dimensions.footer_height - (ozone->dimensions.sidebar_entry_icon_padding * 3)) / 2; + float scale_factor = ozone->last_scale_factor; + gfx_display_ctx_driver_t *dispctx = p_disp->dispctx; + + /* Background */ + if (!libretro_running || (menu_framebuffer_opacity >= 1.0f)) + { + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + x_position, + ozone->dimensions.header_height + ozone->dimensions.spacer_1px, + (unsigned)ozone->animations.thumbnail_bar_position, + ozone->dimensions.sidebar_gradient_height, + video_width, + video_height, + ozone->theme->sidebar_top_gradient, + NULL); + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + x_position, + ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.sidebar_gradient_height, + (unsigned)ozone->animations.thumbnail_bar_position, + sidebar_height, + video_width, + video_height, + ozone->theme->sidebar_background, + NULL); + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + x_position, + video_height - ozone->dimensions.footer_height - ozone->dimensions.sidebar_gradient_height - ozone->dimensions.spacer_1px, + (unsigned) ozone->animations.thumbnail_bar_position, + ozone->dimensions.sidebar_gradient_height + ozone->dimensions.spacer_1px, + video_width, + video_height, + ozone->theme->sidebar_bottom_gradient, + NULL); + } + + /* Thumbnails */ + show_right_thumbnail = + (ozone->thumbnails.right.status != GFX_THUMBNAIL_STATUS_MISSING) && + gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT); + show_left_thumbnail = + (ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_MISSING) && + gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT) && + !ozone->selection_core_is_viewer; + + /* If this entry is associated with the image viewer + * and no right thumbnail is available, show a centred + * message and return immediately */ + if (ozone->selection_core_is_viewer && !show_right_thumbnail) + { + ozone_draw_no_thumbnail_available( + ozone, + p_disp, + userdata, + video_width, + video_height, + x_position, sidebar_width, 0); + return; + } + + /* Top row + * > Displays one item, with the following order + * of preference: + * 1) Right thumbnail, if available + * 2) Left thumbnail, if available + * 3) 'No thumbnail available' message */ + + /* > If this entry is associated with the image viewer + * core, there can be only one thumbnail and no + * content metadata -> centre image vertically */ + if (ozone->selection_core_is_viewer) + { + right_thumbnail_y_position = + ozone->dimensions.header_height + + ((thumbnail_height / 2) + + (int)(1.5f * (float)ozone->dimensions.sidebar_entry_icon_padding)); + + right_thumbnail_alignment = GFX_THUMBNAIL_ALIGN_CENTRE; + } + else + { + right_thumbnail_y_position = + ozone->dimensions.header_height + ozone->dimensions.spacer_1px + + ozone->dimensions.sidebar_entry_icon_padding; + + right_thumbnail_alignment = GFX_THUMBNAIL_ALIGN_BOTTOM; + } + + /* > If we have a right thumbnail, show it */ + if (show_right_thumbnail) + gfx_thumbnail_draw( + userdata, + video_width, + video_height, + &ozone->thumbnails.right, + (float)thumbnail_x_position, + (float)right_thumbnail_y_position, + thumbnail_width, + thumbnail_height, + right_thumbnail_alignment, + 1.0f, 1.0f, NULL); + /* > If we have neither a right thumbnail nor + * a left thumbnail to show in its place, + * display 'no thumbnail available' message */ + else if (!show_left_thumbnail) + ozone_draw_no_thumbnail_available( + ozone, + p_disp, + userdata, + video_width, + video_height, + x_position, + sidebar_width, + thumbnail_height / 2); + + /* Bottom row + * > Displays one item, with the following order + * of preference: + * 1) Left thumbnail, if available + * *and* + * right thumbnail has been placed in the top row + * *and* + * content metadata override is not enabled + * 2) Content metadata */ + + /* > Get baseline 'start' position of bottom row */ + bottom_row_y_position = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + + thumbnail_height + + (ozone->dimensions.sidebar_entry_icon_padding * 2); + + /* > If we have a left thumbnail, show it */ + if (show_left_thumbnail) + { + float left_thumbnail_alpha; + + /* Normally a right thumbnail will be shown + * in the top row - if so, left thumbnail + * goes at the bottom */ + if (show_right_thumbnail) + { + left_thumbnail_y_position = bottom_row_y_position; + left_thumbnail_alignment = GFX_THUMBNAIL_ALIGN_TOP; + /* In this case, thumbnail opacity is dependent + * upon the content metadata override + * > i.e. Need to handle fade in/out animations + * and set opacity to zero when override + * is fully active */ + left_thumbnail_alpha = ozone->animations.left_thumbnail_alpha; + } + /* If right thumbnail is missing, shift left + * thumbnail up to the top row */ + else + { + left_thumbnail_y_position = right_thumbnail_y_position; + left_thumbnail_alignment = right_thumbnail_alignment; + /* In this case, there is no dependence on content + * metadata - thumbnail is always shown at full + * opacity */ + left_thumbnail_alpha = 1.0f; + } + + /* Note: This is a NOOP when alpha is zero + * (i.e. no performance impact when content + * metadata override is fully active) */ + gfx_thumbnail_draw( + userdata, + video_width, + video_height, + &ozone->thumbnails.left, + (float)thumbnail_x_position, + (float)left_thumbnail_y_position, + thumbnail_width, + thumbnail_height, + left_thumbnail_alignment, + left_thumbnail_alpha, + 1.0f, NULL); + } + + /* > Display content metadata in the bottom + * row if: + * - This is *not* image viewer content + * *and* + * - There is no left thumbnail + * *or* + * left thumbnail has been shifted to + * the top row + * *or* + * content metadata override is enabled + * (i.e. fade in, fade out, or fully + * active) */ + if (!ozone->selection_core_is_viewer && + (!show_left_thumbnail || !show_right_thumbnail || + (ozone->animations.left_thumbnail_alpha < 1.0f))) + { + char ticker_buf[255]; + gfx_animation_ctx_ticker_t ticker; + gfx_animation_ctx_ticker_smooth_t ticker_smooth; + static const char* const ticker_spacer = OZONE_TICKER_SPACER; + unsigned ticker_x_offset = 0; + bool scroll_content_metadata = settings->bools.ozone_scroll_content_metadata; + bool use_smooth_ticker = settings->bools.menu_ticker_smooth; + enum gfx_animation_ticker_type + menu_ticker_type = (enum gfx_animation_ticker_type) + settings->uints.menu_ticker_type; + bool show_entry_idx = settings->bools.playlist_show_entry_idx; + unsigned y = (unsigned)bottom_row_y_position; + unsigned separator_padding = ozone->dimensions.sidebar_entry_icon_padding*2; + unsigned column_x = x_position + separator_padding; + bool metadata_override_enabled = show_left_thumbnail && + show_right_thumbnail && + (ozone->animations.left_thumbnail_alpha < 1.0f); + float metadata_alpha = metadata_override_enabled ? + (1.0f - ozone->animations.left_thumbnail_alpha) : 1.0f; + uint32_t text_color = COLOR_TEXT_ALPHA( + ozone->theme->text_rgba, (uint32_t)(metadata_alpha * 255.0f)); + + if (scroll_content_metadata) + { + /* Initial ticker configuration */ + if (use_smooth_ticker) + { + ticker_smooth.idx = p_anim->ticker_pixel_idx; + ticker_smooth.font_scale = 1.0f; + ticker_smooth.type_enum = menu_ticker_type; + ticker_smooth.spacer = ticker_spacer; + ticker_smooth.x_offset = &ticker_x_offset; + ticker_smooth.dst_str_width = NULL; + + ticker_smooth.font = ozone->fonts.footer.font; + ticker_smooth.selected = true; + ticker_smooth.field_width = sidebar_width - (separator_padding * 2); + ticker_smooth.dst_str = ticker_buf; + ticker_smooth.dst_str_len = sizeof(ticker_buf); + } + else + { + ticker.idx = p_anim->ticker_idx; + ticker.type_enum = menu_ticker_type; + ticker.spacer = ticker_spacer; + + ticker.selected = true; + ticker.len = (sidebar_width - (separator_padding * 2)) / ozone->fonts.footer.glyph_width; + ticker.s = ticker_buf; + } + } + + /* Content metadata */ + + /* Separator */ + gfx_display_set_alpha(ozone->theme_dynamic.entries_border, metadata_alpha); + + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + x_position + separator_padding, + y, + sidebar_width - separator_padding*2, + ozone->dimensions.spacer_1px, + video_width, + video_height, + ozone->theme_dynamic.entries_border, + NULL); + + y += 18 * scale_factor; + + if (scroll_content_metadata) + { + /* Entry enumeration */ + if (show_entry_idx) + { + ticker_buf[0] = '\0'; + + if (use_smooth_ticker) + { + ticker_smooth.src_str = ozone->selection_entry_enumeration; + gfx_animation_ticker_smooth(&ticker_smooth); + } + else + { + ticker.str = ozone->selection_entry_enumeration; + gfx_animation_ticker(&ticker); + } + + ozone_content_metadata_line( + video_width, + video_height, + ozone, + &y, + ticker_x_offset + column_x, + ticker_buf, + text_color, + 1); + } + + /* Core association */ + ticker_buf[0] = '\0'; + + if (use_smooth_ticker) + { + ticker_smooth.src_str = ozone->selection_core_name; + gfx_animation_ticker_smooth(&ticker_smooth); + } + else + { + ticker.str = ozone->selection_core_name; + gfx_animation_ticker(&ticker); + } + + ozone_content_metadata_line( + video_width, + video_height, + ozone, + &y, + ticker_x_offset + column_x, + ticker_buf, + text_color, + 1); + + /* Playtime + * Note: It is essentially impossible for this string + * to exceed the width of the sidebar, but since we + * are ticker-texting everything else, we include this + * by default */ + ticker_buf[0] = '\0'; + + if (use_smooth_ticker) + { + ticker_smooth.src_str = ozone->selection_playtime; + gfx_animation_ticker_smooth(&ticker_smooth); + } + else + { + ticker.str = ozone->selection_playtime; + gfx_animation_ticker(&ticker); + } + + ozone_content_metadata_line( + video_width, + video_height, + ozone, + &y, + ticker_x_offset + column_x, + ticker_buf, + text_color, + 1); + + /* Last played */ + ticker_buf[0] = '\0'; + + if (use_smooth_ticker) + { + ticker_smooth.src_str = ozone->selection_lastplayed; + gfx_animation_ticker_smooth(&ticker_smooth); + } + else + { + ticker.str = ozone->selection_lastplayed; + gfx_animation_ticker(&ticker); + } + + ozone_content_metadata_line( + video_width, + video_height, + ozone, + &y, + ticker_x_offset + column_x, + ticker_buf, + text_color, + 1); + } + else + { + /* Entry enumeration */ + if (show_entry_idx) + ozone_content_metadata_line( + video_width, + video_height, + ozone, + &y, + column_x, + ozone->selection_entry_enumeration, + text_color, + 1); + + /* Core association */ + ozone_content_metadata_line( + video_width, + video_height, + ozone, + &y, + column_x, + ozone->selection_core_name, + text_color, + ozone->selection_core_name_lines); + + /* Playtime */ + ozone_content_metadata_line( + video_width, + video_height, + ozone, + &y, + column_x, + ozone->selection_playtime, + text_color, + 1); + + /* Last played */ + ozone_content_metadata_line( + video_width, + video_height, + ozone, + &y, + column_x, + ozone->selection_lastplayed, + text_color, + ozone->selection_lastplayed_lines); + } + + /* If metadata override is active, display an + * icon to notify that a left thumbnail image + * is available */ + if (metadata_override_enabled) + { + float *col = ozone->theme_dynamic.entries_icon; + /* Icon should be small and unobtrusive + * > Make it 80% of the normal entry icon size */ + unsigned icon_size = (unsigned)((float)ozone->dimensions.sidebar_entry_icon_size * 0.8f); + + /* > Set its opacity to a maximum of 80% */ + gfx_display_set_alpha(ozone->theme_dynamic.entries_icon, metadata_alpha * 0.8f); + + if (dispctx) + { + /* Draw icon in the bottom right corner of + * the thumbnail bar */ + if (dispctx->blend_begin) + dispctx->blend_begin(userdata); + if (dispctx->draw) + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + icon_size, + icon_size, + ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_IMAGE], + x_position + sidebar_width - separator_padding - icon_size, + video_height - ozone->dimensions.footer_height - ozone->dimensions.sidebar_entry_icon_padding - icon_size, + video_width, + video_height, + 0, 1, col); + if (dispctx->blend_end) + dispctx->blend_end(userdata); + } + } + } +} + +static void ozone_draw_backdrop( + void *userdata, + void *disp_data, + unsigned video_width, + unsigned video_height, + float alpha) +{ + static float ozone_backdrop[16] = { + 0.00, 0.00, 0.00, 0.75, + 0.00, 0.00, 0.00, 0.75, + 0.00, 0.00, 0.00, 0.75, + 0.00, 0.00, 0.00, 0.75, + }; + static float last_alpha = 0.0f; + + /* TODO: Replace this backdrop by a blur shader + * on the whole screen if available */ + if (alpha != last_alpha) + { + gfx_display_set_alpha(ozone_backdrop, alpha); + last_alpha = alpha; + } + + gfx_display_draw_quad( + (gfx_display_t*)disp_data, + userdata, + video_width, + video_height, + 0, + 0, + video_width, + video_height, + video_width, + video_height, + ozone_backdrop, + NULL); +} + +static void ozone_draw_osk(ozone_handle_t *ozone, + void *userdata, + void *disp_userdata, + unsigned video_width, + unsigned video_height, + const char *label, const char *str) +{ + unsigned i; + char message[2048]; + gfx_display_t *p_disp = (gfx_display_t*)disp_userdata; + const char *text = str; + unsigned text_color = 0xffffffff; + static float ozone_osk_backdrop[16] = { + 0.00, 0.00, 0.00, 0.15, + 0.00, 0.00, 0.00, 0.15, + 0.00, 0.00, 0.00, 0.15, + 0.00, 0.00, 0.00, 0.15, + }; + static retro_time_t last_time = 0; + struct string_list list = {0}; + float scale_factor = ozone->last_scale_factor; + unsigned margin = 75 * scale_factor; + unsigned padding = 10 * scale_factor; + unsigned bottom_end = video_height / 2; + unsigned y_offset = 0; + bool draw_placeholder = string_is_empty(str); + retro_time_t current_time = menu_driver_get_current_time(); + + if (current_time - last_time >= INTERVAL_OSK_CURSOR) + { + ozone->osk_cursor = !ozone->osk_cursor; + last_time = current_time; + } + + /* Border */ + /* Top */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + margin, + margin, + video_width - margin*2, + ozone->dimensions.spacer_1px, + video_width, + video_height, + ozone->theme->entries_border, + NULL); + + /* Bottom */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + margin, + bottom_end - margin, + video_width - margin*2, + ozone->dimensions.spacer_1px, + video_width, + video_height, + ozone->theme->entries_border, + NULL); + + /* Left */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + margin, + margin, + ozone->dimensions.spacer_1px, + bottom_end - margin*2, + video_width, + video_height, + ozone->theme->entries_border, + NULL); + + /* Right */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + video_width - margin, + margin, + ozone->dimensions.spacer_1px, + bottom_end - margin*2, + video_width, + video_height, + ozone->theme->entries_border, + NULL); + + /* Backdrop */ + /* TODO: Remove the backdrop if blur shader is available */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + margin + ozone->dimensions.spacer_1px, + margin + ozone->dimensions.spacer_1px, + video_width - margin*2 - ozone->dimensions.spacer_2px, + bottom_end - margin*2 - ozone->dimensions.spacer_2px, + video_width, + video_height, + ozone_osk_backdrop, + NULL); + + /* Placeholder & text*/ + if (draw_placeholder) + { + text = label; + text_color = ozone_theme_light.text_sublabel_rgba; + } + + (ozone->word_wrap)(message, sizeof(message), text, + (video_width - margin*2 - padding*2) / ozone->fonts.entries_label.glyph_width, + ozone->fonts.entries_label.wideglyph_width, 0); + + string_list_initialize(&list); + string_split_noalloc(&list, message, "\n"); + + for (i = 0; i < list.size; i++) + { + const char *msg = list.elems[i].data; + + gfx_display_draw_text( + ozone->fonts.entries_label.font, + msg, + margin + padding * 2, /* x */ + margin + padding + + ozone->fonts.entries_label.line_height + + y_offset, /* y */ + video_width, video_height, + text_color, + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + false); + + /* Cursor */ + if (i == list.size - 1) + { + if (ozone->osk_cursor) + { + unsigned cursor_x = draw_placeholder + ? 0 + : font_driver_get_message_width( + ozone->fonts.entries_label.font, msg, + (unsigned)strlen(msg), 1); + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + margin + + padding * 2 + + cursor_x, + margin + + padding + + y_offset + + ozone->fonts.entries_label.line_height + - ozone->fonts.entries_label.line_ascender + + ozone->dimensions.spacer_3px, + ozone->dimensions.spacer_1px, + ozone->fonts.entries_label.line_ascender, + video_width, + video_height, + ozone->pure_white, + NULL); + } + } + else + y_offset += 25 * scale_factor; + } + + /* Keyboard */ + gfx_display_draw_keyboard( + p_disp, + userdata, + video_width, + video_height, + ozone->theme->textures[OZONE_THEME_TEXTURE_CURSOR_STATIC], + ozone->fonts.entries_label.font, + input_event_get_osk_grid(), + input_event_get_osk_ptr(), + ozone->theme->text_rgba); + + string_list_deinitialize(&list); +} + +static void ozone_draw_messagebox( + ozone_handle_t *ozone, + gfx_display_t *p_disp, + void *userdata, + unsigned video_width, + unsigned video_height, + const char *message) +{ + unsigned i, y_position; + char wrapped_message[MENU_SUBLABEL_MAX_LENGTH]; + int x, y, longest_width = 0; + int usable_width = 0; + struct string_list list = {0}; + float scale_factor = 0.0f; + unsigned width = video_width; + unsigned height = video_height; + gfx_display_ctx_driver_t + *dispctx = p_disp->dispctx; + + wrapped_message[0] = '\0'; + + /* Sanity check */ + if (string_is_empty(message) || + !ozone->fonts.footer.font) + return; + + scale_factor = ozone->last_scale_factor; + usable_width = (int)width - (48 * 8 * scale_factor); + + if (usable_width < 1) + return; + + /* Split message into lines */ + (ozone->word_wrap)( + wrapped_message, sizeof(wrapped_message), message, + usable_width / (int)ozone->fonts.footer.glyph_width, + ozone->fonts.footer.wideglyph_width, 0); + + string_list_initialize(&list); + if ( + !string_split_noalloc(&list, wrapped_message, "\n") + || list.elems == 0) + { + string_list_deinitialize(&list); + return; + } + + y_position = height / 2; + if (menu_input_dialog_get_display_kb()) + y_position = height / 4; + + x = width / 2; + y = y_position - (list.size + * ozone->fonts.footer.line_height) / 2; + + /* find the longest line width */ + for (i = 0; i < list.size; i++) + { + const char *msg = list.elems[i].data; + + if (!string_is_empty(msg)) + { + int width = font_driver_get_message_width( + ozone->fonts.footer.font, msg, (unsigned)strlen(msg), 1); + + if (width > longest_width) + longest_width = width; + } + } + + gfx_display_set_alpha(ozone->theme_dynamic.message_background, ozone->animations.messagebox_alpha); + + if (dispctx && dispctx->blend_begin) + dispctx->blend_begin(userdata); + + /* Avoid drawing a black box if there's no assets */ + if (ozone->has_all_assets) + { + /* Note: The fact that we use a texture slice here + * makes things very messy + * > The actual size and offset of a texture slice + * is quite 'loose', and depends upon source image + * size, draw size and scale factor... */ + unsigned slice_new_w = longest_width + 48 * 2 * scale_factor; + unsigned slice_new_h = ozone->fonts.footer.line_height * (list.size + 2); + int slice_x = x - longest_width/2 - 48 * scale_factor; + int slice_y = y - ozone->fonts.footer.line_height + + ((slice_new_h >= 256) + ? (16.0f * scale_factor) + : (16.0f * ((float)slice_new_h / 256.0f))); + + gfx_display_draw_texture_slice( + p_disp, + userdata, + video_width, + video_height, + slice_x, + slice_y, + 256, 256, + slice_new_w, + slice_new_h, + width, height, + ozone->theme_dynamic.message_background, + 16, scale_factor, + ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DIALOG_SLICE] + ); + } + + for (i = 0; i < list.size; i++) + { + const char *msg = list.elems[i].data; + + if (msg) + gfx_display_draw_text( + ozone->fonts.footer.font, + msg, + x - longest_width/2.0, + y + (i * ozone->fonts.footer.line_height) + + ozone->fonts.footer.line_ascender, + width, + height, + COLOR_TEXT_ALPHA(ozone->theme->text_rgba, (uint32_t)(ozone->animations.messagebox_alpha*255.0f)), + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + false); + } + + string_list_deinitialize(&list); +} + +static void ozone_hide_fullscreen_thumbnails(ozone_handle_t *ozone, bool animate) +{ + uintptr_t alpha_tag = (uintptr_t) + &ozone->animations.fullscreen_thumbnail_alpha; + gfx_thumbnail_state_t *p_gfx_thumb = gfx_thumb_get_ptr(); + + /* Kill any existing fade in/out animations */ + gfx_animation_kill_by_tag(&alpha_tag); + + /* Check whether animations are enabled */ + if (animate && (ozone->animations.fullscreen_thumbnail_alpha > 0.0f)) + { + gfx_animation_ctx_entry_t animation_entry; + + /* Configure fade out animation */ + animation_entry.easing_enum = EASING_OUT_QUAD; + animation_entry.tag = alpha_tag; + animation_entry.duration = p_gfx_thumb->fade_duration; + animation_entry.target_value = 0.0f; + animation_entry.subject = &ozone->animations.fullscreen_thumbnail_alpha; + animation_entry.cb = NULL; + animation_entry.userdata = NULL; + + /* Push animation */ + gfx_animation_push(&animation_entry); + } + /* No animation - just set thumbnail alpha to zero */ + else + ozone->animations.fullscreen_thumbnail_alpha = 0.0f; + + /* Disable fullscreen thumbnails */ + ozone->show_fullscreen_thumbnails = false; +} + +static void ozone_show_fullscreen_thumbnails(ozone_handle_t *ozone) +{ + menu_entry_t selected_entry; + gfx_animation_ctx_entry_t animation_entry; + const char *thumbnail_label = NULL; + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + uintptr_t alpha_tag = (uintptr_t)&ozone->animations.fullscreen_thumbnail_alpha; + uintptr_t scroll_tag = (uintptr_t)selection_buf; + gfx_thumbnail_state_t *p_gfx_thumb = gfx_thumb_get_ptr(); + + /* Before showing fullscreen thumbnails, must + * ensure that any existing fullscreen thumbnail + * view is disabled... */ + ozone_hide_fullscreen_thumbnails(ozone, false); + + /* Sanity check: Return immediately if this is + * a menu without thumbnail support, or cursor + * is currently in the sidebar */ + if (!ozone->fullscreen_thumbnails_available || + ozone->cursor_in_sidebar) + return; + + /* We can only enable fullscreen thumbnails if + * current selection has at least one valid thumbnail + * and all thumbnails for current selection are already + * loaded/available */ + if (ozone->selection_core_is_viewer) + { + /* imageviewer content requires special treatment, + * since only the right thumbnail is ever loaded */ + if (!gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT)) + return; + + if (ozone->thumbnails.right.status != GFX_THUMBNAIL_STATUS_AVAILABLE) + return; + } + else + { + bool left_thumbnail_enabled = gfx_thumbnail_is_enabled( + ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT); + + if (!left_thumbnail_enabled && + !gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT)) + return; + + if ((ozone->thumbnails.right.status == GFX_THUMBNAIL_STATUS_AVAILABLE) && + (left_thumbnail_enabled && + ((ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_MISSING) && + (ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_AVAILABLE)))) + return; + + if ((ozone->thumbnails.right.status == GFX_THUMBNAIL_STATUS_MISSING) && + (!left_thumbnail_enabled || + (ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_AVAILABLE))) + return; + } + + /* Menu list must be stationary while fullscreen + * thumbnails are shown + * > Kill any existing scroll animations and + * reset scroll acceleration */ + gfx_animation_kill_by_tag(&scroll_tag); + menu_input_set_pointer_y_accel(0.0f); + + /* Cache selected entry label + * (used as title when fullscreen thumbnails + * are shown) */ + ozone->fullscreen_thumbnail_label[0] = '\0'; + + /* > Get menu entry */ + MENU_ENTRY_INIT(selected_entry); + selected_entry.path_enabled = false; + selected_entry.value_enabled = false; + selected_entry.sublabel_enabled = false; + menu_entry_get(&selected_entry, 0, (size_t)ozone->selection, NULL, true); + + /* > Get entry label */ + if (!string_is_empty(selected_entry.rich_label)) + thumbnail_label = selected_entry.rich_label; + else + thumbnail_label = selected_entry.path; + + /* > Sanity check */ + if (!string_is_empty(thumbnail_label)) + strlcpy( + ozone->fullscreen_thumbnail_label, + thumbnail_label, + sizeof(ozone->fullscreen_thumbnail_label)); + + /* Configure fade in animation */ + animation_entry.easing_enum = EASING_OUT_QUAD; + animation_entry.tag = alpha_tag; + animation_entry.duration = p_gfx_thumb->fade_duration; + animation_entry.target_value = 1.0f; + animation_entry.subject = &ozone->animations.fullscreen_thumbnail_alpha; + animation_entry.cb = NULL; + animation_entry.userdata = NULL; + + /* Push animation */ + gfx_animation_push(&animation_entry); + + /* Enable fullscreen thumbnails */ + ozone->fullscreen_thumbnail_selection = (size_t)ozone->selection; + ozone->show_fullscreen_thumbnails = true; +} + + + +static void ozone_draw_fullscreen_thumbnails( + ozone_handle_t *ozone, + void *userdata, + void *disp_userdata, + unsigned video_width, + unsigned video_height) +{ + /* Check whether fullscreen thumbnails are visible */ + if (ozone->animations.fullscreen_thumbnail_alpha > 0.0f) + { + /* Note: right thumbnail is drawn at the top + * in the sidebar, so it becomes the *left* + * thumbnail when viewed fullscreen */ + gfx_thumbnail_t *right_thumbnail = &ozone->thumbnails.left; + gfx_thumbnail_t *left_thumbnail = &ozone->thumbnails.right; + unsigned width = video_width; + unsigned height = video_height; + int view_width = (int)width; + gfx_display_t *p_disp = (gfx_display_t*)disp_userdata; + + int view_height = (int)height - ozone->dimensions.header_height - ozone->dimensions.footer_height - ozone->dimensions.spacer_1px; + int thumbnail_margin = ozone->dimensions.fullscreen_thumbnail_padding; + bool show_right_thumbnail = false; + bool show_left_thumbnail = false; + unsigned num_thumbnails = 0; + float right_thumbnail_draw_width = 0.0f; + float right_thumbnail_draw_height = 0.0f; + float left_thumbnail_draw_width = 0.0f; + float left_thumbnail_draw_height = 0.0f; + float background_alpha = 0.85f; + static float background_color[16] = { + 0.0f, 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + }; + int frame_width = (int)((float)thumbnail_margin / 3.0f); + float frame_color[16]; + float separator_color[16]; + int thumbnail_box_width; + int thumbnail_box_height; + int right_thumbnail_x; + int left_thumbnail_x; + int thumbnail_y; + + /* Sanity check: Return immediately if this is + * a menu without thumbnails and we are not currently + * 'fading out' the fullscreen thumbnail view */ + if (!ozone->fullscreen_thumbnails_available && + ozone->show_fullscreen_thumbnails) + goto error; + + /* Safety check: ensure that current + * selection matches the entry selected when + * fullscreen thumbnails were enabled + * > Note that we exclude this check if we are + * currently viewing the quick menu and the + * thumbnail view is fading out. This enables + * a smooth transition if the user presses + * RetroPad A or keyboard 'return' to enter the + * quick menu while fullscreen thumbnails are + * being displayed */ + if (((size_t)ozone->selection != ozone->fullscreen_thumbnail_selection) && + (!ozone->is_quick_menu || ozone->show_fullscreen_thumbnails)) + goto error; + + /* Sanity check: Return immediately if the view + * width/height is < 1 */ + if ((view_width < 1) || (view_height < 1)) + goto error; + + /* Get number of 'active' thumbnails */ + show_right_thumbnail = (right_thumbnail->status == GFX_THUMBNAIL_STATUS_AVAILABLE); + show_left_thumbnail = (left_thumbnail->status == GFX_THUMBNAIL_STATUS_AVAILABLE); + + if (show_right_thumbnail) + num_thumbnails++; + + if (show_left_thumbnail) + num_thumbnails++; + + /* Do nothing if both thumbnails are missing + * > Note: Baring inexplicable internal errors, this + * can never happen... */ + if (num_thumbnails < 1) + goto error; + + /* Get base thumbnail dimensions + draw positions */ + + /* > Thumbnail bounding box height + y position + * are fixed */ + thumbnail_box_height = view_height - (thumbnail_margin * 2); + thumbnail_y = ozone->dimensions.header_height + thumbnail_margin + ozone->dimensions.spacer_1px; + + /* Thumbnail bounding box width and x position + * depend upon number of active thumbnails */ + if (num_thumbnails == 2) + { + thumbnail_box_width = (view_width - (thumbnail_margin * 3) - frame_width) >> 1; + left_thumbnail_x = thumbnail_margin; + right_thumbnail_x = left_thumbnail_x + thumbnail_box_width + frame_width + thumbnail_margin; + } + else + { + thumbnail_box_width = view_width - (thumbnail_margin * 2); + left_thumbnail_x = thumbnail_margin; + right_thumbnail_x = left_thumbnail_x; + } + + /* Sanity check */ + if ((thumbnail_box_width < 1) || + (thumbnail_box_height < 1)) + goto error; + + /* Get thumbnail draw dimensions + * > Note: The following code is a bit awkward, since + * we have to do things in a very specific order + * - i.e. we cannot determine proper thumbnail + * layout until we have thumbnail draw dimensions. + * and we cannot get draw dimensions until we have + * the bounding box dimensions... */ + if (show_right_thumbnail) + { + gfx_thumbnail_get_draw_dimensions( + right_thumbnail, + thumbnail_box_width, thumbnail_box_height, 1.0f, + &right_thumbnail_draw_width, &right_thumbnail_draw_height); + + /* Sanity check */ + if ((right_thumbnail_draw_width <= 0.0f) || + (right_thumbnail_draw_height <= 0.0f)) + goto error; + } + + if (show_left_thumbnail) + { + gfx_thumbnail_get_draw_dimensions( + left_thumbnail, + thumbnail_box_width, thumbnail_box_height, 1.0f, + &left_thumbnail_draw_width, &left_thumbnail_draw_height); + + /* Sanity check */ + if ((left_thumbnail_draw_width <= 0.0f) || + (left_thumbnail_draw_height <= 0.0f)) + goto error; + } + + /* Adjust thumbnail draw positions to achieve + * uniform appearance (accounting for actual + * draw dimensions...) */ + if (num_thumbnails == 2) + { + int left_padding = (thumbnail_box_width - (int)left_thumbnail_draw_width) >> 1; + int right_padding = (thumbnail_box_width - (int)right_thumbnail_draw_width) >> 1; + + /* Move thumbnails as close together as possible, + * and horizontally centre the resultant 'block' + * of images */ + left_thumbnail_x += right_padding; + right_thumbnail_x -= left_padding; + } + + /* Set colour values */ + + /* > Background */ + gfx_display_set_alpha( + background_color, + background_alpha * ozone->animations.fullscreen_thumbnail_alpha); + + /* > Separators */ + memcpy(separator_color, ozone->theme->header_footer_separator, sizeof(separator_color)); + gfx_display_set_alpha( + separator_color, ozone->animations.fullscreen_thumbnail_alpha); + + /* > Thumbnail frame */ + memcpy(frame_color, ozone->theme->sidebar_background, sizeof(frame_color)); + gfx_display_set_alpha( + frame_color, ozone->animations.fullscreen_thumbnail_alpha); + + /* Darken background */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + 0, + ozone->dimensions.header_height + ozone->dimensions.spacer_1px, + width, + (unsigned)view_height, + width, + height, + background_color, + NULL); + + /* Draw full-width separators */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + 0, + ozone->dimensions.header_height, + width, + ozone->dimensions.spacer_1px, + width, + height, + separator_color, + NULL); + + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + 0, + height - ozone->dimensions.footer_height, + width, + ozone->dimensions.spacer_1px, + width, + height, + separator_color, + NULL); + + /* Draw thumbnails */ + + /* > Right */ + if (show_right_thumbnail) + { + /* Background */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + right_thumbnail_x - frame_width + + ((thumbnail_box_width - (int)right_thumbnail_draw_width) >> 1), + thumbnail_y - frame_width + + ((thumbnail_box_height - (int)right_thumbnail_draw_height) >> 1), + (unsigned)right_thumbnail_draw_width + (frame_width << 1), + (unsigned)right_thumbnail_draw_height + (frame_width << 1), + width, + height, + frame_color, + NULL); + + /* Thumbnail */ + gfx_thumbnail_draw( + userdata, + video_width, + video_height, + right_thumbnail, + right_thumbnail_x, + thumbnail_y, + (unsigned)thumbnail_box_width, + (unsigned)thumbnail_box_height, + GFX_THUMBNAIL_ALIGN_CENTRE, + ozone->animations.fullscreen_thumbnail_alpha, + 1.0f, + NULL); + } + + /* > Left */ + if (show_left_thumbnail) + { + /* Background */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + left_thumbnail_x - frame_width + + ((thumbnail_box_width - (int)left_thumbnail_draw_width) >> 1), + thumbnail_y - frame_width + + ((thumbnail_box_height - (int)left_thumbnail_draw_height) >> 1), + (unsigned)left_thumbnail_draw_width + (frame_width << 1), + (unsigned)left_thumbnail_draw_height + (frame_width << 1), + width, + height, + frame_color, + NULL); + + /* Thumbnail */ + gfx_thumbnail_draw( + userdata, + video_width, + video_height, + left_thumbnail, + left_thumbnail_x, + thumbnail_y, + (unsigned)thumbnail_box_width, + (unsigned)thumbnail_box_height, + GFX_THUMBNAIL_ALIGN_CENTRE, + ozone->animations.fullscreen_thumbnail_alpha, + 1.0f, + NULL); + } + } + + return; + +error: + /* If fullscreen thumbnails are enabled at + * this point, must disable them immediately... */ + if (ozone->show_fullscreen_thumbnails) + ozone_hide_fullscreen_thumbnails(ozone, false); +} + +static void ozone_set_thumbnail_content(void *data, const char *s) +{ + ozone_handle_t *ozone = (ozone_handle_t*)data; + + if (!ozone) + return; + + if (ozone->is_playlist) + { + /* Playlist content */ + if (string_is_empty(s)) + { + size_t selection = menu_navigation_get_selection(); + size_t list_size = menu_entries_get_size(); + file_list_t *list = menu_entries_get_selection_buf_ptr(0); + + /* Get playlist index corresponding + * to the selected entry */ + if (list && + (selection < list_size) && + (list->list[selection].type == FILE_TYPE_RPL_ENTRY)) + { + size_t playlist_index = list->list[selection].entry_idx; + gfx_thumbnail_set_content_playlist(ozone->thumbnail_path_data, + playlist_get_cached(), playlist_index); + } + else + gfx_thumbnail_set_content_playlist(ozone->thumbnail_path_data, + NULL, selection); + } + } + else if (ozone->is_db_manager_list) + { + /* Database list content */ + if (string_is_empty(s)) + { + menu_entry_t entry; + size_t selection = menu_navigation_get_selection(); + + MENU_ENTRY_INIT(entry); + entry.label_enabled = false; + entry.rich_label_enabled = false; + entry.value_enabled = false; + entry.sublabel_enabled = false; + menu_entry_get(&entry, 0, selection, NULL, true); + + if (!string_is_empty(entry.path)) + gfx_thumbnail_set_content(ozone->thumbnail_path_data, entry.path); + } + } + else if (string_is_equal(s, "imageviewer")) + { + /* Filebrowser image updates */ + menu_entry_t entry; + size_t selection = menu_navigation_get_selection(); + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + ozone_node_t *node = (ozone_node_t*)selection_buf->list[selection].userdata; + + if (node) + { + MENU_ENTRY_INIT(entry); + entry.label_enabled = false; + entry.rich_label_enabled = false; + entry.value_enabled = false; + entry.sublabel_enabled = false; + menu_entry_get(&entry, 0, selection, NULL, true); + if (!string_is_empty(entry.path) && !string_is_empty(node->fullpath)) + gfx_thumbnail_set_content_image(ozone->thumbnail_path_data, node->fullpath, entry.path); + } + } + else if (!string_is_empty(s)) + { + /* Annoying leftovers... + * This is required to ensure that thumbnails are + * updated correctly when navigating deeply through + * the sublevels of database manager lists. + * Showing thumbnails on database entries is a + * pointless nuisance and a waste of CPU cycles, IMHO... */ + gfx_thumbnail_set_content(ozone->thumbnail_path_data, s); + } + + ozone_update_content_metadata(ozone); +} + +/* Returns true if specified category is currently + * displayed on screen */ +static bool INLINE ozone_category_onscreen( + ozone_handle_t *ozone, size_t idx) +{ + return (idx >= ozone->first_onscreen_category) && + (idx <= ozone->last_onscreen_category); +} + +/* If current category is on screen, returns its + * index. If current category is off screen, returns + * index of centremost on screen category. */ +static size_t ozone_get_onscreen_category_selection( + ozone_handle_t *ozone) +{ + /* Check whether selected category is already on screen */ + if (ozone_category_onscreen(ozone, ozone->categories_selection_ptr)) + return ozone->categories_selection_ptr; + + /* Return index of centremost category */ + return (ozone->first_onscreen_category >> 1) + + (ozone->last_onscreen_category >> 1); +} + +/* If currently selected entry is off screen, + * moves selection to specified on screen target + * > Does nothing if currently selected item is + * already on screen */ +static void ozone_auto_select_onscreen_entry( + ozone_handle_t *ozone, + enum ozone_onscreen_entry_position_type target_entry) +{ + size_t selection = 0; + + /* Update selection index */ + switch (target_entry) + { + case OZONE_ONSCREEN_ENTRY_FIRST: + selection = ozone->first_onscreen_entry; + break; + case OZONE_ONSCREEN_ENTRY_LAST: + selection = ozone->last_onscreen_entry; + break; + case OZONE_ONSCREEN_ENTRY_CENTRE: + default: + selection = (ozone->first_onscreen_entry >> 1) + + (ozone->last_onscreen_entry >> 1); + break; + } + + /* Apply new selection */ + menu_navigation_set_selection(selection); +} + +static bool INLINE ozone_metadata_override_available(ozone_handle_t *ozone) +{ + /* Ugly construct... + * Content metadata display override may be + * toggled if the following are true: + * - We are viewing playlist thumbnails + * - This is *not* an image viewer playlist + * - Both right and left thumbnails are + * enabled/available + * Short circuiting means that in most cases + * only 'ozone->is_playlist' will be evaluated, + * so this isn't too much of a performance hog... */ + return ozone->is_playlist + && ozone->show_thumbnail_bar + && !ozone->selection_core_is_viewer + && (ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_MISSING) + && gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, + GFX_THUMBNAIL_LEFT) + && (ozone->thumbnails.right.status != GFX_THUMBNAIL_STATUS_MISSING) + && gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, + GFX_THUMBNAIL_RIGHT); +} + +static bool ozone_is_current_entry_settings(size_t current_selection) +{ + menu_entry_t last_entry; + const char *entry_value; + + unsigned entry_type = 0; + enum msg_file_type entry_file_type = FILE_TYPE_NONE; + + MENU_ENTRY_INIT(last_entry); + last_entry.path_enabled = false; + last_entry.label_enabled = false; + last_entry.rich_label_enabled = false; + last_entry.sublabel_enabled = false; + + menu_entry_get(&last_entry, 0, current_selection, NULL, true); + + if (last_entry.enum_idx == MENU_ENUM_LABEL_CHEEVOS_PASSWORD) + entry_value = last_entry.password_value; + else + entry_value = last_entry.value; + + entry_file_type = msg_hash_to_file_type(msg_hash_calculate(entry_value)); + entry_type = last_entry.type; + + /* Logic below taken from materialui_pointer_up_swipe_horz_default */ + if (!string_is_empty(entry_value)) + { + /* Toggle switch off */ + if (string_is_equal(entry_value, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)) || + string_is_equal(entry_value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) + { + return true; + } + /* Toggle switch on */ + else if (string_is_equal(entry_value, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) || + string_is_equal(entry_value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON))) + { + return true; + } + /* Normal value text */ + else + { + switch (entry_file_type) + { + case FILE_TYPE_IN_CARCHIVE: + case FILE_TYPE_MORE: + case FILE_TYPE_CORE: + case FILE_TYPE_DIRECT_LOAD: + case FILE_TYPE_RDB: + case FILE_TYPE_CURSOR: + case FILE_TYPE_PLAIN: + case FILE_TYPE_DIRECTORY: + case FILE_TYPE_MUSIC: + case FILE_TYPE_IMAGE: + case FILE_TYPE_MOVIE: + break; + case FILE_TYPE_COMPRESSED: + /* Note that we have to perform a backup check here, + * since the 'manual content scan - file extensions' + * setting may have a value of 'zip' or '7z' etc, which + * means it would otherwise get incorreclty identified as + * an archive file... */ + if (entry_type != FILE_TYPE_CARCHIVE) + return true; + break; + default: + return true; + break; + } + } + } + + return false; +} + +static void ozone_toggle_metadata_override(ozone_handle_t *ozone) +{ + gfx_animation_ctx_entry_t animation_entry; + uintptr_t alpha_tag = (uintptr_t) + &ozone->animations.left_thumbnail_alpha; + gfx_thumbnail_state_t *p_gfx_thumb = gfx_thumb_get_ptr(); + + /* Kill any existing fade in/out animations */ + gfx_animation_kill_by_tag(&alpha_tag); + + /* Set common animation parameters */ + animation_entry.easing_enum = EASING_OUT_QUAD; + animation_entry.tag = alpha_tag; + animation_entry.duration = p_gfx_thumb->fade_duration; + animation_entry.subject = &ozone->animations.left_thumbnail_alpha; + animation_entry.cb = NULL; + animation_entry.userdata = NULL; + + /* Check whether metadata override is + * currently enabled */ + if (ozone->force_metadata_display) + { + /* Thumbnail will fade in */ + animation_entry.target_value = 1.0f; + ozone->force_metadata_display = false; + } + else + { + /* Thumbnail will fade out */ + animation_entry.target_value = 0.0f; + ozone->force_metadata_display = true; + } + + /* Push animation */ + gfx_animation_push(&animation_entry); +} + +/** + * Starts the cursor wiggle animation in the given direction + * Use ozone_get_cursor_wiggle_offset to read the animation + * once it has started + */ +static void ozone_start_cursor_wiggle( + ozone_handle_t* ozone, enum menu_action direction) +{ + /* Don't start another wiggle animation on top of another */ + if (!ozone || ozone->cursor_wiggle_state.wiggling) + return; + + /* Don't allow wiggling in invalid directions */ + if (!( + direction == MENU_ACTION_UP || + direction == MENU_ACTION_DOWN || + direction == MENU_ACTION_LEFT || + direction == MENU_ACTION_RIGHT + )) + return; + + /* Start wiggling */ + ozone->cursor_wiggle_state.start_time = menu_driver_get_current_time() / 1000; + ozone->cursor_wiggle_state.direction = direction; + ozone->cursor_wiggle_state.amplitude = rand() % 15 + 10; + ozone->cursor_wiggle_state.wiggling = true; +} + + +static enum menu_action ozone_parse_menu_entry_action( + ozone_handle_t *ozone, settings_t *settings, + enum menu_action action) +{ + uintptr_t tag; + int new_selection; + enum menu_action new_action = action; + file_list_t *selection_buf = NULL; + unsigned horizontal_list_size = 0; + bool menu_navigation_wraparound_enable; + bool is_current_entry_settings; + size_t selection; + size_t selection_total; + + /* If fullscreen thumbnail view is active, any + * valid menu action will disable it... */ + if (ozone->show_fullscreen_thumbnails) + { + if (action != MENU_ACTION_NOOP) + { + ozone_hide_fullscreen_thumbnails(ozone, true); + + /* ...and any action other than Select/OK + * is ignored + * > We allow pass-through of Select/OK since + * users may want to run content directly + * after viewing fullscreen thumbnails, + * and having to press RetroPad A or the Return + * key twice is navigationally confusing + * > Note that we can only do this for non-pointer + * input + * > Note that we don't do this when viewing a + * file list, since there is no quick menu + * in this case - i.e. content loads directly, + * and a sudden transition from fullscreen + * thumbnail to content is jarring... + * > We also don't do this when viewing a database + * manager list, because the menu transition + * detection becomes too cumbersome... */ + if (ozone->is_file_list || + ozone->is_db_manager_list || + ((action != MENU_ACTION_SELECT) && + (action != MENU_ACTION_OK))) + return MENU_ACTION_NOOP; + } + } + + horizontal_list_size = (unsigned)ozone->horizontal_list.size; + + ozone->messagebox_state = menu_input_dialog_get_display_kb(); + selection_buf = menu_entries_get_selection_buf_ptr(0); + tag = (uintptr_t)selection_buf; + selection = menu_navigation_get_selection(); + selection_total = menu_entries_get_size(); + + menu_navigation_wraparound_enable = settings->bools.menu_navigation_wraparound_enable; + + /* Don't wiggle left or right if the current entry is a setting. This is + partially wrong because some settings don't use left and right to change their value, such as + free input fields (passwords...). This is good enough. */ + is_current_entry_settings = ozone_is_current_entry_settings(selection); + + /* Scan user inputs */ + switch (action) + { + case MENU_ACTION_START: + ozone->cursor_mode = false; + /* If this is a menu with thumbnails and cursor + * is not in the sidebar, attempt to show + * fullscreen thumbnail view */ + if (ozone->fullscreen_thumbnails_available && + !ozone->cursor_in_sidebar) + { + ozone_show_fullscreen_thumbnails(ozone); + new_action = MENU_ACTION_NOOP; + } + break; + case MENU_ACTION_DOWN: + if (ozone->cursor_in_sidebar) + { + /* If cursor is active, ensure we target + * an on screen category */ + size_t selection = (ozone->cursor_mode) + ? ozone_get_onscreen_category_selection(ozone) + : ozone->categories_selection_ptr; + + new_selection = (int)(selection + 1); + + if (new_selection >= (int)(ozone->system_tab_end + + horizontal_list_size + 1)) + new_selection = 0; + + ozone_sidebar_goto(ozone, new_selection); + + new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE; + ozone->cursor_mode = false; + break; + } + else if (!menu_navigation_wraparound_enable && selection == selection_total - 1) + ozone_start_cursor_wiggle(ozone, MENU_ACTION_DOWN); + + /* If pointer is active and current selection + * is off screen, auto select *centre* item */ + if (ozone->cursor_mode) + if (!OZONE_ENTRY_ONSCREEN(ozone, selection)) + ozone_auto_select_onscreen_entry(ozone, + OZONE_ONSCREEN_ENTRY_CENTRE); + ozone->cursor_mode = false; + break; + case MENU_ACTION_UP: + if (ozone->cursor_in_sidebar) + { + /* If cursor is active, ensure we target + * an on screen category */ + size_t selection = (ozone->cursor_mode) + ? ozone_get_onscreen_category_selection(ozone) + : ozone->categories_selection_ptr; + new_selection = (int)selection - 1; + + if (new_selection < 0) + new_selection = horizontal_list_size + ozone->system_tab_end; + + ozone_sidebar_goto(ozone, new_selection); + + new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE; + ozone->cursor_mode = false; + break; + } + else if (!menu_navigation_wraparound_enable && selection == 0) + ozone_start_cursor_wiggle(ozone, MENU_ACTION_UP); + + /* If pointer is active and current selection + * is off screen, auto select *centre* item */ + if (ozone->cursor_mode) + if (!OZONE_ENTRY_ONSCREEN(ozone, selection)) + ozone_auto_select_onscreen_entry(ozone, + OZONE_ONSCREEN_ENTRY_CENTRE); + ozone->cursor_mode = false; + break; + case MENU_ACTION_LEFT: + ozone->cursor_mode = false; + + if (ozone->cursor_in_sidebar) + { + new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE; + ozone_start_cursor_wiggle(ozone, MENU_ACTION_LEFT); + + break; + } + else if (ozone->depth > 1) + { + /* Pressing left goes up but faster, so + wiggle up to say that there is nothing more upwards + even though the user pressed the left button */ + if (!menu_navigation_wraparound_enable && selection == 0 && !is_current_entry_settings) + ozone_start_cursor_wiggle(ozone, MENU_ACTION_DOWN); + + break; + } + + ozone_go_to_sidebar(ozone, settings, tag); + + new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE; + break; + case MENU_ACTION_RIGHT: + ozone->cursor_mode = false; + if (!ozone->cursor_in_sidebar) + { + if (ozone->depth == 1) + { + new_action = MENU_ACTION_NOOP; + ozone_start_cursor_wiggle(ozone, MENU_ACTION_RIGHT); + } + /* Pressing right goes down but faster, so + wiggle down to say that there is nothing more downwards + even though the user pressed the right button */ + else if (!menu_navigation_wraparound_enable && selection == selection_total - 1 && !is_current_entry_settings) + ozone_start_cursor_wiggle(ozone, MENU_ACTION_DOWN); + + break; + } + + ozone_leave_sidebar(ozone, settings, tag); + + new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_LABEL; + break; + case MENU_ACTION_OK: + ozone->cursor_mode = false; + if (ozone->cursor_in_sidebar) + { + ozone_leave_sidebar(ozone, settings, tag); + new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_LABEL; + break; + } + break; + case MENU_ACTION_CANCEL: + ozone->cursor_mode = false; + + /* If this is a playlist, handle 'backing out' + * of a search, if required */ + if (ozone->is_playlist) + if (menu_entries_search_get_terms()) + break; + + if (ozone->cursor_in_sidebar) + { + /* Go back to main menu tab */ + if (ozone->categories_selection_ptr != 0) + ozone_sidebar_goto(ozone, 0); + + new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE; + break; + } + + if (menu_entries_get_stack_size(0) == 1) + { + ozone_go_to_sidebar(ozone, settings, tag); + new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE; + } + break; + + case MENU_ACTION_SCROLL_UP: + /* Descend alphabet (Z towards A) */ + + /* Ignore if cursor is in sidebar */ + if (ozone->cursor_in_sidebar) + { + new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE; + break; + } + + /* If pointer is active and current selection + * is off screen, auto select *last* item */ + if (ozone->cursor_mode) + if (!OZONE_ENTRY_ONSCREEN(ozone, selection)) + ozone_auto_select_onscreen_entry(ozone, + OZONE_ONSCREEN_ENTRY_LAST); + ozone->cursor_mode = false; + break; + case MENU_ACTION_SCROLL_DOWN: + /* Ascend alphabet (A towards Z) */ + + /* > Ignore if cursor is in sidebar */ + if (ozone->cursor_in_sidebar) + { + new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE; + break; + } + + /* If pointer is active and current selection + * is off screen, auto select *first* item */ + if (ozone->cursor_mode) + if (!OZONE_ENTRY_ONSCREEN(ozone, selection)) + ozone_auto_select_onscreen_entry(ozone, + OZONE_ONSCREEN_ENTRY_FIRST); + ozone->cursor_mode = false; + break; + + case MENU_ACTION_INFO: + /* If we currently viewing a playlist with + * dual thumbnails, toggle the content metadata + * override */ + if (ozone_metadata_override_available(ozone)) + { + ozone_toggle_metadata_override(ozone); + new_action = MENU_ACTION_NOOP; + } + /* ...and since the user is likely to trigger + * 'INFO' actions on invalid playlist entries, + * suppress this action entirely when viewing + * playlists under all other conditions + * > Playlists have no 'INFO' entries - the + * user is just greeted with a useless + * 'no information available' message + * > It is incredibly annoying to inadvertently + * trigger this message when you just want to + * toggle metadata... */ + else if (ozone->is_playlist && ozone->show_thumbnail_bar) + new_action = MENU_ACTION_NOOP; + + ozone->cursor_mode = false; + break; + + default: + /* In all other cases, pass through input + * menu action without intervention */ + break; + } + + return new_action; +} + + +/* Menu entry action callback */ +static int ozone_menu_entry_action( + void *userdata, menu_entry_t *entry, + size_t i, enum menu_action action) +{ + menu_entry_t new_entry; + ozone_handle_t *ozone = (ozone_handle_t*)userdata; + menu_entry_t *entry_ptr = entry; + settings_t *settings = config_get_ptr(); + size_t selection = i; + /* Process input action */ + enum menu_action new_action = ozone_parse_menu_entry_action(ozone, settings, + action); + /* Check whether current selection has changed + * (due to automatic on screen entry selection...) */ + size_t new_selection = menu_navigation_get_selection(); + + if (new_selection != selection) + { + /* Selection has changed - must update + * entry pointer */ + MENU_ENTRY_INIT(new_entry); + new_entry.path_enabled = false; + new_entry.label_enabled = false; + new_entry.rich_label_enabled = false; + new_entry.value_enabled = false; + new_entry.sublabel_enabled = false; + menu_entry_get(&new_entry, 0, new_selection, NULL, true); + entry_ptr = &new_entry; + } + + /* Call standard generic_menu_entry_action() function */ + return generic_menu_entry_action(userdata, entry_ptr, + new_selection, new_action); +} + +static void ozone_menu_animation_update_time( + float *ticker_pixel_increment, + unsigned video_width, unsigned video_height) +{ + gfx_display_t *p_disp = disp_get_ptr(); + settings_t *settings = config_get_ptr(); + /* Ozone uses DPI scaling + * > Smooth ticker scaling multiplier is + * gfx_display_get_dpi_scale() multiplied by + * a small correction factor to achieve a + * default scroll speed equal to that of the + * non-smooth ticker */ + *(ticker_pixel_increment) *= gfx_display_get_dpi_scale(p_disp, + settings, video_width, video_height) * 0.5f; +} + +static void *ozone_init(void **userdata, bool video_is_threaded) +{ + unsigned i; + bool fallback_color_theme = false; + unsigned width, height, color_theme = 0; + ozone_handle_t *ozone = NULL; + settings_t *settings = config_get_ptr(); + gfx_animation_t *p_anim = anim_get_ptr(); + gfx_display_t *p_disp = disp_get_ptr(); + menu_handle_t *menu = (menu_handle_t*)calloc(1, sizeof(*menu)); + const char *directory_assets = settings->paths.directory_assets; + + if (!menu) + return NULL; + + video_driver_get_size(&width, &height); + + ozone = (ozone_handle_t*)calloc(1, sizeof(ozone_handle_t)); + + if (!ozone) + goto error; + + *userdata = ozone; + + for (i = 0; i < 15; i++) + ozone->pure_white[i] = 1.00f; + + ozone->last_width = width; + ozone->last_height = height; + ozone->last_scale_factor = gfx_display_get_dpi_scale(p_disp, + settings, width, height); + + file_list_initialize(&ozone->selection_buf_old); + + ozone->draw_sidebar = true; + ozone->sidebar_offset = 0; + ozone->pending_message = NULL; + ozone->is_playlist = false; + ozone->categories_selection_ptr = 0; + ozone->pending_message = NULL; + ozone->show_cursor = false; + ozone->show_screensaver = false; + + ozone->first_frame = true; + ozone->cursor_mode = false; + + ozone->sidebar_collapsed = false; + ozone->animations.sidebar_text_alpha = 1.0f; + + ozone->animations.thumbnail_bar_position = 0.0f; + ozone->show_thumbnail_bar = false; + ozone->pending_hide_thumbnail_bar = false; + ozone->dimensions_sidebar_width = 0.0f; + + ozone->num_search_terms_old = 0; + + ozone->cursor_wiggle_state.wiggling = false; + + ozone->thumbnail_path_data = gfx_thumbnail_path_init(); + if (!ozone->thumbnail_path_data) + goto error; + + ozone->screensaver = menu_screensaver_init(); + if (!ozone->screensaver) + goto error; + + ozone->fullscreen_thumbnails_available = false; + ozone->show_fullscreen_thumbnails = false; + ozone->animations.fullscreen_thumbnail_alpha = 0.0f; + ozone->fullscreen_thumbnail_selection = 0; + ozone->fullscreen_thumbnail_label[0] = '\0'; + + ozone->animations.left_thumbnail_alpha = 1.0f; + ozone->force_metadata_display = false; + + gfx_thumbnail_set_stream_delay(-1.0f); + gfx_thumbnail_set_fade_duration(-1.0f); + gfx_thumbnail_set_fade_missing(false); + + ozone_sidebar_update_collapse(ozone, settings, false); + + ozone->system_tab_end = 0; + ozone->tabs[ozone->system_tab_end] = OZONE_SYSTEM_TAB_MAIN; + if ( settings->bools.menu_content_show_settings + && !settings->bools.kiosk_mode_enable) + ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_SETTINGS; + if (settings->bools.menu_content_show_favorites) + ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_FAVORITES; + if (settings->bools.menu_content_show_history) + ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_HISTORY; +#ifdef HAVE_IMAGEVIEWER + if (settings->bools.menu_content_show_images) + ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_IMAGES; +#endif + if (settings->bools.menu_content_show_music) + ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_MUSIC; +#if defined(HAVE_FFMPEG) || defined(HAVE_MPV) + if (settings->bools.menu_content_show_video) + ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_VIDEO; +#endif +#ifdef HAVE_NETWORKING + if (settings->bools.menu_content_show_netplay) + ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_NETPLAY; +#endif + + if ( settings->bools.menu_content_show_add + && !settings->bools.kiosk_mode_enable) + ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_ADD; + +#if defined(HAVE_LIBRETRODB) + if (settings->bools.menu_content_show_explore) + ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_EXPLORE; +#endif + + menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL); + + gfx_display_set_width(width); + gfx_display_set_height(height); + + gfx_display_init_white_texture(gfx_display_white_texture); + + file_list_initialize(&ozone->horizontal_list); + + ozone_init_horizontal_list(ozone, settings); + + /* Theme */ + if (settings->bools.menu_use_preferred_system_color_theme) + { +#ifdef HAVE_LIBNX + if (R_SUCCEEDED(setsysInitialize())) + { + ColorSetId theme; + setsysGetColorSetId(&theme); + color_theme = (theme == ColorSetId_Dark) ? 1 : 0; + ozone_set_color_theme(ozone, color_theme); + configuration_set_uint(settings, + settings->uints.menu_ozone_color_theme, color_theme); + configuration_set_bool(settings, + settings->bools.menu_preferred_system_color_theme_set, true); + setsysExit(); + } + else +#endif + fallback_color_theme = true; + } + else + fallback_color_theme = true; + + if (fallback_color_theme) + { + color_theme = settings->uints.menu_ozone_color_theme; + ozone_set_color_theme(ozone, color_theme); + } + + ozone->need_compute = false; + ozone->animations.scroll_y = 0.0f; + ozone->animations.scroll_y_sidebar = 0.0f; + + ozone->first_onscreen_entry = 0; + ozone->last_onscreen_entry = 0; + ozone->first_onscreen_category = 0; + ozone->last_onscreen_category = 0; + + /* Assets path */ + fill_pathname_join( + ozone->assets_path, + directory_assets, + "ozone", + sizeof(ozone->assets_path) + ); + + /* PNG path */ + fill_pathname_join( + ozone->png_path, + ozone->assets_path, + "png", + sizeof(ozone->png_path) + ); + + /* Sidebar path */ + fill_pathname_join( + ozone->tab_path, + ozone->png_path, + "sidebar", + sizeof(ozone->tab_path) + ); + + /* Icons path */ + fill_pathname_application_special(ozone->icons_path, + sizeof(ozone->icons_path), + APPLICATION_SPECIAL_DIRECTORY_ASSETS_OZONE_ICONS); + + ozone_last_use_preferred_system_color_theme = settings->bools.menu_use_preferred_system_color_theme; + p_anim->updatetime_cb = ozone_menu_animation_update_time; + + /* set word_wrap function pointer */ + ozone->word_wrap = msg_hash_get_wideglyph_str() ? word_wrap_wideglyph : word_wrap; + + return menu; + +error: + if (ozone) + { + ozone_free_list_nodes(&ozone->horizontal_list, false); + ozone_free_list_nodes(&ozone->selection_buf_old, false); + file_list_deinitialize(&ozone->horizontal_list); + file_list_deinitialize(&ozone->selection_buf_old); + } + + if (menu) + free(menu); + + return NULL; +} + +static void ozone_free(void *data) +{ + ozone_handle_t *ozone = (ozone_handle_t*) data; + + if (ozone) + { + video_coord_array_free(&ozone->fonts.footer.raster_block.carr); + video_coord_array_free(&ozone->fonts.title.raster_block.carr); + video_coord_array_free(&ozone->fonts.time.raster_block.carr); + video_coord_array_free(&ozone->fonts.entries_label.raster_block.carr); + video_coord_array_free(&ozone->fonts.entries_sublabel.raster_block.carr); + video_coord_array_free(&ozone->fonts.sidebar.raster_block.carr); + + ozone_free_list_nodes(&ozone->selection_buf_old, false); + ozone_free_list_nodes(&ozone->horizontal_list, false); + file_list_deinitialize(&ozone->selection_buf_old); + file_list_deinitialize(&ozone->horizontal_list); + + if (!string_is_empty(ozone->pending_message)) + free(ozone->pending_message); + + if (ozone->thumbnail_path_data) + free(ozone->thumbnail_path_data); + + menu_screensaver_free(ozone->screensaver); + } + + if (gfx_display_white_texture) + video_driver_texture_unload(&gfx_display_white_texture); + + font_driver_bind_block(NULL, NULL); +} + +static void ozone_update_thumbnail_image(void *data) +{ + ozone_handle_t *ozone = (ozone_handle_t*)data; + size_t selection = menu_navigation_get_selection(); + settings_t *settings = config_get_ptr(); + playlist_t *playlist = playlist_get_cached(); + unsigned gfx_thumbnail_upscale_threshold = settings->uints.gfx_thumbnail_upscale_threshold; + bool network_on_demand_thumbnails = settings->bools.network_on_demand_thumbnails; + + if (!ozone) + return; + + gfx_thumbnail_cancel_pending_requests(); + + gfx_thumbnail_request( + ozone->thumbnail_path_data, + GFX_THUMBNAIL_RIGHT, + playlist, + selection, + &ozone->thumbnails.right, + gfx_thumbnail_upscale_threshold, + network_on_demand_thumbnails + ); + + /* Image (and video/music) content requires special + * treatment... */ + if (ozone->selection_core_is_viewer) + { + /* Left thumbnail is simply reset */ + gfx_thumbnail_reset(&ozone->thumbnails.left); + } + else + { + /* Left thumbnail */ + gfx_thumbnail_request( + ozone->thumbnail_path_data, + GFX_THUMBNAIL_LEFT, + playlist, + selection, + &ozone->thumbnails.left, + gfx_thumbnail_upscale_threshold, + network_on_demand_thumbnails); + } +} + +static void ozone_refresh_thumbnail_image(void *data, unsigned i) +{ + ozone_handle_t *ozone = (ozone_handle_t*)data; + + if (!ozone) + return; + + /* Only refresh thumbnails if thumbnails are enabled + * and we are currently viewing a playlist */ + if ((gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) || + gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT)) && + (ozone->is_playlist && ozone->depth == 1)) + ozone_update_thumbnail_image(ozone); +} + +static bool ozone_init_font( + ozone_font_data_t *font_data, + bool is_threaded, char *font_path, float font_size) +{ + int glyph_width = 0; + gfx_display_t *p_disp = disp_get_ptr(); + const char *wideglyph_str = msg_hash_get_wideglyph_str(); + + /* Free existing */ + if (font_data->font) + { + gfx_display_font_free(font_data->font); + font_data->font = NULL; + } + + /* Cache approximate dimensions */ + font_data->line_height = (int)(font_size + 0.5f); + font_data->glyph_width = (int)((font_size * (3.0f / 4.0f)) + 0.5f); + + /* Create font */ + font_data->font = gfx_display_font_file(p_disp, + font_path, font_size, is_threaded); + + if (!font_data->font) + return false; + + /* Get font metadata */ + glyph_width = font_driver_get_message_width(font_data->font, "a", 1, 1.0f); + if (glyph_width > 0) + font_data->glyph_width = glyph_width; + + font_data->wideglyph_width = 100; + + if (wideglyph_str) + { + int wideglyph_width = + font_driver_get_message_width(font_data->font, wideglyph_str, strlen(wideglyph_str), 1.0f); + + if (wideglyph_width > 0 && glyph_width > 0) + font_data->wideglyph_width = wideglyph_width * 100 / glyph_width; + } + + font_data->line_height = font_driver_get_line_height(font_data->font, 1.0f); + font_data->line_ascender = font_driver_get_line_ascender(font_data->font, 1.0f); + font_data->line_centre_offset = font_driver_get_line_centre_offset(font_data->font, 1.0f); + + return true; +} + +static void ozone_cache_footer_label(ozone_handle_t *ozone, + ozone_footer_label_t *label, enum msg_hash_enums enum_idx) +{ + const char *str = msg_hash_to_str(enum_idx); + /* Determine pixel width */ + unsigned length = (unsigned)strlen(str); + + /* Assign string */ + label->str = str; + label->width = font_driver_get_message_width( + ozone->fonts.footer.font, + label->str, length, 1.0f); + /* If font_driver_get_message_width() fails, + * use predetermined glyph_width as a fallback */ + if (label->width < 0) + label->width = length * ozone->fonts.footer.glyph_width; +} + +/* Assigns footer label strings (based on current + * menu language) and calculates pixel widths */ +static void ozone_cache_footer_labels(ozone_handle_t *ozone) +{ + ozone_cache_footer_label( + ozone, &ozone->footer_labels.ok, + MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_OK); + + ozone_cache_footer_label( + ozone, &ozone->footer_labels.back, + MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_BACK); + + ozone_cache_footer_label( + ozone, &ozone->footer_labels.search, + MENU_ENUM_LABEL_VALUE_SEARCH); + + ozone_cache_footer_label( + ozone, &ozone->footer_labels.fullscreen_thumbs, + MSG_TOGGLE_FULLSCREEN_THUMBNAILS); + + ozone_cache_footer_label( + ozone, &ozone->footer_labels.metadata_toggle, + MSG_TOGGLE_CONTENT_METADATA); + + /* Record current language setting */ + ozone->footer_labels_language = *msg_hash_get_uint(MSG_HASH_USER_LANGUAGE); +} + +/* Determines the size of all menu elements */ +static void ozone_set_layout( + ozone_handle_t *ozone, + settings_t *settings, + bool is_threaded) +{ + char s1[PATH_MAX_LENGTH]; + char font_path[PATH_MAX_LENGTH]; + float scale_factor = 0.0f; + bool font_inited = false; + + font_path[0] = s1[0]= '\0'; + + if (!ozone) + return; + + scale_factor = ozone->last_scale_factor; + + /* Calculate dimensions */ + ozone->dimensions.header_height = HEADER_HEIGHT * scale_factor; + ozone->dimensions.footer_height = FOOTER_HEIGHT * scale_factor; + + ozone->dimensions.entry_padding_horizontal_half = ENTRY_PADDING_HORIZONTAL_HALF * scale_factor; + ozone->dimensions.entry_padding_horizontal_full = ENTRY_PADDING_HORIZONTAL_FULL * scale_factor; + ozone->dimensions.entry_padding_vertical = ENTRY_PADDING_VERTICAL * scale_factor; + ozone->dimensions.entry_height = ENTRY_HEIGHT * scale_factor; + ozone->dimensions.entry_spacing = ENTRY_SPACING * scale_factor; + ozone->dimensions.entry_icon_size = ENTRY_ICON_SIZE * scale_factor; + ozone->dimensions.entry_icon_padding = ENTRY_ICON_PADDING * scale_factor; + + ozone->dimensions.sidebar_entry_height = SIDEBAR_ENTRY_HEIGHT * scale_factor; + ozone->dimensions.sidebar_padding_horizontal = SIDEBAR_X_PADDING * scale_factor; + ozone->dimensions.sidebar_padding_vertical = SIDEBAR_Y_PADDING * scale_factor; + ozone->dimensions.sidebar_entry_padding_vertical = SIDEBAR_ENTRY_Y_PADDING * scale_factor; + ozone->dimensions.sidebar_entry_icon_size = SIDEBAR_ENTRY_ICON_SIZE * scale_factor; + ozone->dimensions.sidebar_entry_icon_padding = SIDEBAR_ENTRY_ICON_PADDING * scale_factor; + ozone->dimensions.sidebar_gradient_height = SIDEBAR_GRADIENT_HEIGHT * scale_factor; + + ozone->dimensions.sidebar_width_normal = SIDEBAR_WIDTH * scale_factor; + ozone->dimensions.sidebar_width_collapsed = + ozone->dimensions.sidebar_entry_icon_size + + ozone->dimensions.sidebar_entry_icon_padding * 2 + + ozone->dimensions.sidebar_padding_horizontal * 2; + + if (ozone->dimensions_sidebar_width == 0) + ozone->dimensions_sidebar_width = (float)ozone->dimensions.sidebar_width_normal; + + ozone->dimensions.thumbnail_bar_width = + ozone->dimensions.sidebar_width_normal - + ozone->dimensions.sidebar_entry_icon_size - + ozone->dimensions.sidebar_entry_icon_padding; + + ozone->dimensions.cursor_size = CURSOR_SIZE * scale_factor; + + ozone->dimensions.fullscreen_thumbnail_padding = FULLSCREEN_THUMBNAIL_PADDING * scale_factor; + + ozone->dimensions.spacer_1px = (scale_factor > 1.0f) ? (unsigned)(scale_factor + 0.5f) : 1; + ozone->dimensions.spacer_2px = ozone->dimensions.spacer_1px * 2; + ozone->dimensions.spacer_3px = (unsigned)((scale_factor * 3.0f) + 0.5f); + ozone->dimensions.spacer_5px = (unsigned)((scale_factor * 5.0f) + 0.5f); + + /* Determine movement delta size for activating + * pointer input (note: not a dimension as such, + * so not included in the 'dimensions' struct) */ + ozone->pointer_active_delta = CURSOR_ACTIVE_DELTA * scale_factor; + + /* Initialise fonts */ + switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE)) + { + case RETRO_LANGUAGE_ARABIC: + case RETRO_LANGUAGE_PERSIAN: + fill_pathname_application_special(s1, sizeof(s1), + APPLICATION_SPECIAL_DIRECTORY_ASSETS_PKG); + fill_pathname_join(font_path, s1, "fallback-font.ttf", sizeof(font_path)); + break; + case RETRO_LANGUAGE_CHINESE_SIMPLIFIED: + case RETRO_LANGUAGE_CHINESE_TRADITIONAL: + fill_pathname_application_special(s1, sizeof(s1), + APPLICATION_SPECIAL_DIRECTORY_ASSETS_PKG); + fill_pathname_join(font_path, s1, "chinese-fallback-font.ttf", sizeof(font_path)); + break; + case RETRO_LANGUAGE_KOREAN: + fill_pathname_application_special(s1, sizeof(s1), + APPLICATION_SPECIAL_DIRECTORY_ASSETS_PKG); + fill_pathname_join(font_path, s1, "korean-fallback-font.ttf", sizeof(font_path)); + break; + default: + fill_pathname_join(font_path, ozone->assets_path, "bold.ttf", sizeof(font_path)); + } + + font_inited = ozone_init_font(&ozone->fonts.title, + is_threaded, font_path, FONT_SIZE_TITLE * scale_factor); + ozone->has_all_assets = ozone->has_all_assets && font_inited; + + switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE)) + { + case RETRO_LANGUAGE_ARABIC: + case RETRO_LANGUAGE_PERSIAN: + fill_pathname_application_special(s1, sizeof(s1), + APPLICATION_SPECIAL_DIRECTORY_ASSETS_PKG); + fill_pathname_join(font_path, s1, "fallback-font.ttf", sizeof(font_path)); + break; + case RETRO_LANGUAGE_CHINESE_SIMPLIFIED: + case RETRO_LANGUAGE_CHINESE_TRADITIONAL: + fill_pathname_application_special(s1, sizeof(s1), + APPLICATION_SPECIAL_DIRECTORY_ASSETS_PKG); + fill_pathname_join(font_path, s1, "chinese-fallback-font.ttf", sizeof(font_path)); + break; + case RETRO_LANGUAGE_KOREAN: + fill_pathname_application_special(s1, sizeof(s1), + APPLICATION_SPECIAL_DIRECTORY_ASSETS_PKG); + fill_pathname_join(font_path, s1, "korean-fallback-font.ttf", sizeof(font_path)); + break; + default: + fill_pathname_join(font_path, ozone->assets_path, "regular.ttf", sizeof(font_path)); + } + + font_inited = ozone_init_font(&ozone->fonts.footer, + is_threaded, font_path, FONT_SIZE_FOOTER * scale_factor); + ozone->has_all_assets = ozone->has_all_assets && font_inited; + + font_inited = ozone_init_font(&ozone->fonts.time, + is_threaded, font_path, FONT_SIZE_TIME * scale_factor); + ozone->has_all_assets = ozone->has_all_assets && font_inited; + + font_inited = ozone_init_font(&ozone->fonts.entries_label, + is_threaded, font_path, FONT_SIZE_ENTRIES_LABEL * scale_factor); + ozone->has_all_assets = ozone->has_all_assets && font_inited; + + font_inited = ozone_init_font(&ozone->fonts.entries_sublabel, + is_threaded, font_path, FONT_SIZE_ENTRIES_SUBLABEL * scale_factor); + ozone->has_all_assets = ozone->has_all_assets && font_inited; + + font_inited = ozone_init_font(&ozone->fonts.sidebar, + is_threaded, font_path, FONT_SIZE_SIDEBAR * scale_factor); + ozone->has_all_assets = ozone->has_all_assets && font_inited; + + /* Cache footer text labels + * > Fonts have been (re)initialised, so need + * to recalculate label widths */ + ozone_cache_footer_labels(ozone); + + /* Multiple sidebar parameters are set via animations + * > ozone_refresh_sidebars() cancels any existing + * animations and 'force updates' the affected + * variables with newly scaled values */ + ozone_refresh_sidebars(ozone, settings, ozone->last_height); + + /* Entry dimensions must be recalculated after + * updating menu layout */ + ozone->need_compute = true; +} + +static void ozone_context_reset(void *data, bool is_threaded) +{ + unsigned i; + ozone_handle_t *ozone = (ozone_handle_t*) data; + settings_t *settings = config_get_ptr(); + + if (ozone) + { + ozone->has_all_assets = true; + + ozone_set_layout(ozone, settings, is_threaded); + + /* Textures init */ + for (i = 0; i < OZONE_TEXTURE_LAST; i++) + { + char filename[PATH_MAX_LENGTH]; + filename[0] = '\0'; +#if 0 + if (i == OZONE_TEXTURE_DISCORD_OWN_AVATAR && discord_avatar_is_ready()) + strlcpy(filename, discord_get_own_avatar(), sizeof(filename)); + else +#endif + strlcpy(filename, OZONE_TEXTURES_FILES[i], sizeof(filename)); + + strlcat(filename, FILE_PATH_PNG_EXTENSION, sizeof(filename)); + +#if 0 + if (i == OZONE_TEXTURE_DISCORD_OWN_AVATAR && discord_avatar_is_ready()) + { + char buf[PATH_MAX_LENGTH]; + fill_pathname_application_special(buf, + sizeof(buf), + APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_DISCORD_AVATARS); + if (!gfx_display_reset_textures_list(filename, buf, &ozone->textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL)) + RARCH_WARN("[OZONE] Asset missing: %s%s%s\n", ozone->png_path, + PATH_DEFAULT_SLASH(), filename); + } + else + { +#endif + if (!gfx_display_reset_textures_list(filename, ozone->png_path, &ozone->textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL)) + ozone->has_all_assets = false; +#if 0 + } +#endif + } + + /* Sidebar textures */ + for (i = 0; i < OZONE_TAB_TEXTURE_LAST; i++) + { + char filename[PATH_MAX_LENGTH]; + + filename[0] = '\0'; + strlcpy(filename, + OZONE_TAB_TEXTURES_FILES[i], sizeof(filename)); + strlcat(filename, FILE_PATH_PNG_EXTENSION, sizeof(filename)); + + if (!gfx_display_reset_textures_list(filename, ozone->tab_path, &ozone->tab_textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL)) + { + ozone->has_all_assets = false; + RARCH_WARN("[OZONE] Asset missing: %s%s%s\n", ozone->tab_path, + PATH_DEFAULT_SLASH(), filename); + } + } + + /* Theme textures */ + if (!ozone_reset_theme_textures(ozone)) + ozone->has_all_assets = false; + + /* Icons textures init */ + for (i = 0; i < OZONE_ENTRIES_ICONS_TEXTURE_LAST; i++) + { + if (!gfx_display_reset_textures_list(ozone_entries_icon_texture_path(i), ozone->icons_path, &ozone->icons_textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL)) + { + ozone->has_all_assets = false; + RARCH_WARN("[OZONE] Asset missing: %s%s%s\n", ozone->icons_path, + PATH_DEFAULT_SLASH(), ozone_entries_icon_texture_path(i)); + } + } + + if (gfx_display_white_texture) + video_driver_texture_unload(&gfx_display_white_texture); + gfx_display_init_white_texture(gfx_display_white_texture); + + /* Horizontal list */ + ozone_context_reset_horizontal_list(ozone); + + /* State reset */ + ozone->fade_direction = false; + ozone->cursor_in_sidebar = false; + ozone->cursor_in_sidebar_old = false; + ozone->draw_old_list = false; + ozone->messagebox_state = false; + ozone->messagebox_state_old = false; + + ozone->cursor_wiggle_state.wiggling = false; + + /* Animations */ + ozone->animations.cursor_alpha = 1.0f; + ozone->animations.scroll_y = 0.0f; + ozone->animations.list_alpha = 1.0f; + + /* Missing assets message */ + if (!ozone->has_all_assets) + runloop_msg_queue_push(msg_hash_to_str(MSG_MISSING_ASSETS), 1, 256, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + + /* Thumbnails */ + ozone_update_thumbnail_image(ozone); + + /* TODO: update savestate thumbnail image */ + + ozone_restart_cursor_animation(ozone); + + /* Screensaver */ + menu_screensaver_context_destroy(ozone->screensaver); + } + video_driver_monitor_reset(); +} + +static void ozone_collapse_end(void *userdata) +{ + ozone_handle_t *ozone = (ozone_handle_t*) userdata; + ozone->draw_sidebar = false; +} + +static void ozone_unload_thumbnail_textures(void *data) +{ + ozone_handle_t *ozone = (ozone_handle_t*)data; + if (!ozone) + return; + + gfx_thumbnail_cancel_pending_requests(); + gfx_thumbnail_reset(&ozone->thumbnails.right); + gfx_thumbnail_reset(&ozone->thumbnails.left); +} + +static void INLINE ozone_font_free(ozone_font_data_t *font_data) +{ + if (font_data->font) + gfx_display_font_free(font_data->font); + + font_data->font = NULL; +} + +static void ozone_context_destroy(void *data) +{ + unsigned i; + uintptr_t tag; + ozone_handle_t *ozone = (ozone_handle_t*) data; + + if (!ozone) + return; + + /* Theme */ + ozone_unload_theme_textures(ozone); + + /* Icons */ + for (i = 0; i < OZONE_ENTRIES_ICONS_TEXTURE_LAST; i++) + video_driver_texture_unload(&ozone->icons_textures[i]); + + /* Textures */ + for (i = 0; i < OZONE_TEXTURE_LAST; i++) + video_driver_texture_unload(&ozone->textures[i]); + + /* Icons */ + for (i = 0; i < OZONE_TAB_TEXTURE_LAST; i++) + video_driver_texture_unload(&ozone->tab_textures[i]); + + /* Thumbnails */ + ozone_unload_thumbnail_textures(ozone); + + video_driver_texture_unload(&gfx_display_white_texture); + + /* Fonts */ + ozone_font_free(&ozone->fonts.footer); + ozone_font_free(&ozone->fonts.title); + ozone_font_free(&ozone->fonts.time); + ozone_font_free(&ozone->fonts.entries_label); + ozone_font_free(&ozone->fonts.entries_sublabel); + ozone_font_free(&ozone->fonts.sidebar); + + tag = (uintptr_t) &ozone_default_theme; + gfx_animation_kill_by_tag(&tag); + + /* Horizontal list */ + ozone_context_destroy_horizontal_list(ozone); + + /* Screensaver */ + menu_screensaver_context_destroy(ozone->screensaver); +} + +static void *ozone_list_get_entry(void *data, + enum menu_list_type type, unsigned i) +{ + size_t list_size = 0; + ozone_handle_t* ozone = (ozone_handle_t*) data; + + switch (type) + { + case MENU_LIST_PLAIN: + { + file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + list_size = menu_entries_get_stack_size(0); + if (i < list_size) + return (void*)&menu_stack->list[i]; + } + break; + case MENU_LIST_HORIZONTAL: + list_size = ozone->horizontal_list.size; + if (i < list_size) + return (void*)&ozone->horizontal_list.list[i]; + break; + default: + break; + } + + return NULL; +} + +static int ozone_list_push(void *data, void *userdata, + menu_displaylist_info_t *info, unsigned type) +{ + int ret = -1; + core_info_list_t *list = NULL; + + switch (type) + { + case DISPLAYLIST_LOAD_CONTENT_LIST: + { + settings_t *settings = config_get_ptr(); + bool menu_content_show_playlists = settings->bools.menu_content_show_playlists; + bool kiosk_mode_enable = settings->bools.kiosk_mode_enable; + + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); + + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES), + msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES), + MENU_ENUM_LABEL_FAVORITES, + MENU_SETTING_ACTION_FAVORITES_DIR, 0, 0); + + core_info_get_list(&list); + if (list && list->info_count > 0) + { + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DOWNLOADED_FILE_DETECT_CORE_LIST), + msg_hash_to_str(MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST), + MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST, + MENU_SETTING_ACTION, 0, 0); + } + + if (menu_content_show_playlists) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLISTS_TAB), + msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB), + MENU_ENUM_LABEL_PLAYLISTS_TAB, + MENU_SETTING_ACTION, 0, 0); + + if (frontend_driver_parse_drive_list(info->list, true) != 0) + menu_entries_append_enum(info->list, "/", + msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR), + MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR, + MENU_SETTING_ACTION, 0, 0); + + if (!kiosk_mode_enable) + { + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MENU_FILE_BROWSER_SETTINGS), + msg_hash_to_str(MENU_ENUM_LABEL_MENU_FILE_BROWSER_SETTINGS), + MENU_ENUM_LABEL_MENU_FILE_BROWSER_SETTINGS, + MENU_SETTING_ACTION, 0, 0); + } + + info->need_push = true; + info->need_refresh = true; + ret = 0; + } + break; + case DISPLAYLIST_MAIN_MENU: + { + settings_t *settings = config_get_ptr(); + rarch_system_info_t *system = runloop_get_system_info(); + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); + + if (rarch_ctl(RARCH_CTL_CORE_IS_RUNNING, NULL)) + { + if (!rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_CONTENT_SETTINGS, + PARSE_ACTION, + false); + } + } + else + { + if (system->load_no_content) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_START_CORE, + PARSE_ACTION, + false); + } + +#ifndef HAVE_DYNAMIC + if (frontend_driver_has_fork()) +#endif + { + if (settings->bools.menu_show_load_core) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_CORE_LIST, + PARSE_ACTION, + false); + } + } + } + + if (settings->bools.menu_show_load_content) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_LOAD_CONTENT_LIST, + PARSE_ACTION, + false); + + if (menu_displaylist_has_subsystems()) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_SUBSYSTEM_SETTINGS, + PARSE_ACTION, + false); + } + } + + if (settings->bools.menu_show_load_disc) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_LOAD_DISC, + PARSE_ACTION, + false); + } + + if (settings->bools.menu_show_dump_disc) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_DUMP_DISC, + PARSE_ACTION, + false); + } + + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_ADD_CONTENT_LIST, + PARSE_ACTION, + false); +#ifdef HAVE_QT + if (settings->bools.desktop_menu_enable) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_SHOW_WIMP, + PARSE_ACTION, + false); + } +#endif +#if defined(HAVE_NETWORKING) +#if defined(HAVE_ONLINE_UPDATER) + if (settings->bools.menu_show_online_updater && !settings->bools.kiosk_mode_enable) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_ONLINE_UPDATER, + PARSE_ACTION, + false); + } +#endif +#endif + if (!settings->bools.menu_content_show_settings && !string_is_empty(settings->paths.menu_content_show_settings_password)) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_XMB_MAIN_MENU_ENABLE_SETTINGS, + PARSE_ACTION, + false); + } + + if (settings->bools.kiosk_mode_enable && !string_is_empty(settings->paths.kiosk_mode_password)) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_MENU_DISABLE_KIOSK_MODE, + PARSE_ACTION, + false); + } + + if (settings->bools.menu_show_information) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_INFORMATION_LIST, + PARSE_ACTION, + false); + } + +#if defined(HAVE_LAKKA_SWITCH) || defined(HAVE_LIBNX) + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_SWITCH_CPU_PROFILE, + PARSE_ACTION, + false); +#endif + +#ifdef HAVE_LAKKA_SWITCH + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_SWITCH_GPU_PROFILE, + PARSE_ACTION, + false); +#endif + + if (settings->bools.menu_show_configurations && !settings->bools.kiosk_mode_enable) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_CONFIGURATIONS_LIST, + PARSE_ACTION, + false); + } + + if (settings->bools.menu_show_help) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_HELP_LIST, + PARSE_ACTION, + false); + } + +#if !defined(IOS) + if (settings->bools.menu_show_restart_retroarch) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_RESTART_RETROARCH, + PARSE_ACTION, + false); + } + + if (settings->bools.menu_show_quit_retroarch) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_QUIT_RETROARCH, + PARSE_ACTION, + false); + } +#endif + + if (settings->bools.menu_show_reboot) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_REBOOT, + PARSE_ACTION, + false); + } + + if (settings->bools.menu_show_shutdown) + { + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM( + info->list, + MENU_ENUM_LABEL_SHUTDOWN, + PARSE_ACTION, + false); + } + + info->need_push = true; + ret = 0; + } + break; + } + return ret; +} + +static size_t ozone_list_get_selection(void *data) +{ + ozone_handle_t *ozone = (ozone_handle_t*)data; + + if (!ozone) + return 0; + + return ozone->categories_selection_ptr; +} + +static void ozone_list_clear(file_list_t *list) +{ + uintptr_t tag = (uintptr_t)list; + gfx_animation_kill_by_tag(&tag); + + ozone_free_list_nodes(list, false); +} + +static void ozone_list_free(file_list_t *list, size_t a, size_t b) +{ + ozone_list_clear(list); +} + +static void ozone_render(void *data, + unsigned width, unsigned height, + bool is_idle) +{ + size_t i; + /* c.f. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 + * On some platforms (e.g. 32-bit x86 without SSE), + * gcc can produce inconsistent floating point results + * depending upon optimisation level. This can break + * floating point variable comparisons. A workaround is + * to declare the affected variable as 'volatile', which + * disables optimisations and removes excess precision + * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323#c87) */ + volatile float scale_factor; + unsigned entries_end = (unsigned)menu_entries_get_size(); + bool pointer_enabled = false; + unsigned language = *msg_hash_get_uint(MSG_HASH_USER_LANGUAGE); + ozone_handle_t *ozone = (ozone_handle_t*)data; + gfx_display_t *p_disp = disp_get_ptr(); + gfx_animation_t *p_anim = anim_get_ptr(); + settings_t *settings = config_get_ptr(); + if (!ozone) + return; + + /* Check whether screen dimensions or menu scale + * factor have changed */ + scale_factor = gfx_display_get_dpi_scale(p_disp, settings, width, height); + + if ((scale_factor != ozone->last_scale_factor) || + (width != ozone->last_width) || + (height != ozone->last_height)) + { + ozone->last_scale_factor = scale_factor; + ozone->last_width = width; + ozone->last_height = height; + + /* Note: We don't need a full context reset here + * > Just rescale layout, and reset frame time counter */ + ozone_set_layout(ozone, settings, video_driver_is_threaded()); + video_driver_monitor_reset(); + } + + if (ozone->need_compute) + { + ozone_compute_entries_position(ozone, settings, entries_end); + ozone->need_compute = false; + } + + /* Check whether menu language has changed + * > If so, need to re-cache footer text labels */ + if (ozone->footer_labels_language != language) + { + ozone->footer_labels_language = language; + ozone_cache_footer_labels(ozone); + } + + ozone->selection = menu_navigation_get_selection(); + + /* Need to update this each frame, otherwise touchscreen + * input breaks when changing orientation */ + p_disp->framebuf_width = width; + p_disp->framebuf_height = height; + + /* Read pointer state */ + menu_input_get_pointer_state(&ozone->pointer); + + /* If menu screensaver is active, update + * screensaver and return */ + if (ozone->show_screensaver) + { + menu_screensaver_iterate( + ozone->screensaver, + p_disp, p_anim, + (enum menu_screensaver_effect)settings->uints.menu_screensaver_animation, + settings->floats.menu_screensaver_animation_speed, + ozone->theme->screensaver_tint, + width, height, + settings->paths.directory_assets); + GFX_ANIMATION_CLEAR_ACTIVE(p_anim); + return; + } + + /* Check whether pointer is enabled */ + if (ozone->pointer.type != MENU_POINTER_DISABLED) + { + /* When using a mouse, entry under pointer is + * automatically selected + * > Must therefore filter out small movements, + * otherwise scrolling with the mouse wheel + * becomes impossible... */ + if (ozone->pointer.type == MENU_POINTER_MOUSE) + { + int16_t cursor_x_delta = ozone->pointer.x - ozone->cursor_x_old; + int16_t cursor_y_delta = ozone->pointer.y - ozone->cursor_y_old; + + if ((cursor_x_delta > ozone->pointer_active_delta) || + (cursor_x_delta < -ozone->pointer_active_delta) || + (cursor_y_delta > ozone->pointer_active_delta) || + (cursor_y_delta < -ozone->pointer_active_delta)) + ozone->cursor_mode = true; + } + /* On touchscreens, just check for any movement */ + else + { + if ((ozone->pointer.x != ozone->cursor_x_old) || + (ozone->pointer.y != ozone->cursor_y_old)) + ozone->cursor_mode = true; + } + } + + ozone->cursor_x_old = ozone->pointer.x; + ozone->cursor_y_old = ozone->pointer.y; + + /* Pointer is disabled when: + * - Showing fullscreen thumbnails + * - On-screen keyboard is active + * - A message box is being displayed */ + pointer_enabled = ozone->cursor_mode && + !ozone->show_fullscreen_thumbnails && + !menu_input_dialog_get_display_kb() && + !ozone->should_draw_messagebox; + + /* Process pointer input, if required */ + if (pointer_enabled) + { + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + uintptr_t animation_tag = (uintptr_t)selection_buf; + + int entry_padding = (ozone->depth == 1) ? + ozone->dimensions.entry_padding_horizontal_half : + ozone->dimensions.entry_padding_horizontal_full; + float entry_x = ozone->dimensions_sidebar_width + + ozone->sidebar_offset + entry_padding; + float entry_width = width - ozone->dimensions_sidebar_width - + ozone->sidebar_offset - entry_padding * 2 - + ozone->animations.thumbnail_bar_position; + bool first_entry_found = false; + bool last_entry_found = false; + + unsigned horizontal_list_size = (unsigned)ozone->horizontal_list.size; + float category_height = ozone->dimensions.sidebar_entry_height + + ozone->dimensions.sidebar_entry_padding_vertical; + bool first_category_found = false; + bool last_category_found = false; + + /* Check whether pointer is operating on entries + * or sidebar + * > Note 1: Since touchscreens effectively 'lose their + * place' when a touch is released, we can only perform + * this this check if the pointer is currently + * pressed - i.e. we must preserve the values set the + * last time the screen was touched. + * With mouse input we have a permanent cursor, so this + * is not an issue + * > Note 2: Windows seems to report negative pointer + * coordinates when the cursor goes off the left hand + * side of the screen/window, so checking whether + * pointer.x is less than the effective sidebar width + * generates a false positive when ozone->depth > 1. + * We therefore must also check whether the sidebar + * is currently being drawn */ + ozone->last_pointer_in_sidebar = ozone->pointer_in_sidebar; + if ((ozone->pointer.type == MENU_POINTER_MOUSE) || + ozone->pointer.pressed) + ozone->pointer_in_sidebar = ozone->draw_sidebar && + (ozone->pointer.x < ozone->dimensions_sidebar_width + + ozone->sidebar_offset); + + /* If pointer has switched from entries to sidebar + * or vice versa, must reset pointer acceleration */ + if (ozone->pointer_in_sidebar != ozone->last_pointer_in_sidebar) + { + menu_input_set_pointer_y_accel(0.0f); + ozone->pointer.y_accel = 0.0f; + } + + /* If pointer is a mouse, then automatically follow + * mouse focus from entries to sidebar (and vice versa) */ + if (ozone->pointer.type == MENU_POINTER_MOUSE) + { + if (ozone->pointer_in_sidebar && + !ozone->last_pointer_in_sidebar && + !ozone->cursor_in_sidebar) + ozone_go_to_sidebar(ozone, settings, animation_tag); + else if (!ozone->pointer_in_sidebar && + ozone->last_pointer_in_sidebar && + ozone->cursor_in_sidebar) + ozone_leave_sidebar(ozone, settings, animation_tag); + } + + /* Update scrolling - must be done first, otherwise + * cannot determine entry/category positions + * > Entries */ + if (!ozone->pointer_in_sidebar) + { + float entry_bottom_boundary = height - ozone->dimensions.header_height - + ozone->dimensions.spacer_1px - ozone->dimensions.footer_height - + ozone->dimensions.entry_padding_vertical * 2; + + ozone->animations.scroll_y += ozone->pointer.y_accel; + + if (ozone->animations.scroll_y + ozone->entries_height < entry_bottom_boundary) + ozone->animations.scroll_y = entry_bottom_boundary - ozone->entries_height; + + if (ozone->animations.scroll_y > 0.0f) + ozone->animations.scroll_y = 0.0f; + } + /* > Sidebar + * Only process sidebar input here if the + * cursor is currently *in* the sidebar */ + else if (ozone->cursor_in_sidebar) + { + float sidebar_bottom_boundary = height - + (ozone->dimensions.header_height + ozone->dimensions.spacer_1px) - + ozone->dimensions.footer_height - + ozone->dimensions.sidebar_padding_vertical; + float sidebar_height = ozone_get_sidebar_height(ozone); + + ozone->animations.scroll_y_sidebar += ozone->pointer.y_accel; + + if (ozone->animations.scroll_y_sidebar + sidebar_height < sidebar_bottom_boundary) + ozone->animations.scroll_y_sidebar = sidebar_bottom_boundary - sidebar_height; + + if (ozone->animations.scroll_y_sidebar > 0.0f) + ozone->animations.scroll_y_sidebar = 0.0f; + } + + /* Regardless of pointer location, have to process + * all entries/categories in order to determine + * the indices of the first and last entries/categories + * displayed on screen + * > Needed so we can determine proper cursor positions + * when mixing pointer + gamepad/keyboard input */ + + /* >> Loop over all entries */ + ozone->first_onscreen_entry = 0; + ozone->last_onscreen_entry = (entries_end > 0) ? entries_end - 1 : 0; + + for (i = 0; i < entries_end; i++) + { + float entry_y; + ozone_node_t *node = (ozone_node_t*)selection_buf->list[i].userdata; + + /* Sanity check */ + if (!node) + break; + + /* Get current entry y position */ + entry_y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + + ozone->dimensions.entry_padding_vertical + ozone->animations.scroll_y + + node->position_y; + + /* Check whether this is the first on screen entry */ + if (!first_entry_found) + { + if ((entry_y + node->height) > ozone->dimensions.header_height) + { + ozone->first_onscreen_entry = i; + first_entry_found = true; + } + } + /* Check whether this is the last on screen entry */ + else if (!last_entry_found) + { + if (entry_y > (height - ozone->dimensions.footer_height)) + { + /* Current entry is off screen - get index + * of previous entry */ + if (i > 0) + { + ozone->last_onscreen_entry = i - 1; + last_entry_found = true; + } + } + } + + /* Track pointer input, if required */ + if (!ozone->pointer_in_sidebar && + first_entry_found && + !last_entry_found) + { + /* Check whether pointer is within the bounds + * of the current entry */ + if ((ozone->pointer.x > entry_x) && + (ozone->pointer.x < entry_x + entry_width) && + (ozone->pointer.y > entry_y) && + (ozone->pointer.y < entry_y + node->height)) + { + /* Pointer selection is always updated */ + menu_input_set_pointer_selection((unsigned)i); + + /* If pointer is a mouse, then automatically + * select entry under cursor */ + if (ozone->pointer.type == MENU_POINTER_MOUSE) + { + /* Note the fudge factor - cannot auto select + * items while drag-scrolling the entry list, + * so have to wait until pointer acceleration + * drops below a 'sensible' level... */ + if (!ozone->cursor_in_sidebar && + (i != ozone->selection) && + (ozone->pointer.y_accel < ozone->last_scale_factor) && + (ozone->pointer.y_accel > -ozone->last_scale_factor)) + { + menu_navigation_set_selection(i); + + /* If this is a playlist, must update thumbnails */ + if (ozone->is_playlist && (ozone->depth == 1)) + { + ozone_set_thumbnail_content(ozone, ""); + ozone_update_thumbnail_image(ozone); + } + } + } + + /* If pointer is pressed and stationary, and + * if pointer has been held for at least + * MENU_INPUT_PRESS_TIME_SHORT ms, automatically + * select current entry */ + if (ozone->pointer.pressed && + !ozone->pointer.dragged && + (ozone->pointer.press_duration >= MENU_INPUT_PRESS_TIME_SHORT) && + (i != ozone->selection)) + { + menu_navigation_set_selection(i); + + /* If we are currently in the sidebar, leave it */ + if (ozone->cursor_in_sidebar) + ozone_leave_sidebar(ozone, settings, animation_tag); + /* If this is a playlist, must update thumbnails */ + else if (ozone->is_playlist && (ozone->depth == 1)) + { + ozone_set_thumbnail_content(ozone, ""); + ozone_update_thumbnail_image(ozone); + } + } + } + } + + if (last_entry_found) + break; + } + + /* >> Loop over all categories */ + ozone->first_onscreen_category = 0; + ozone->last_onscreen_category = ozone->system_tab_end + horizontal_list_size; + + for (i = 0; i < ozone->system_tab_end + horizontal_list_size + 1; i++) + { + /* Get current category y position */ + float category_y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + + ozone->dimensions.sidebar_padding_vertical + (category_height * i) + + ((i > ozone->system_tab_end) ? + (ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px) : 0) + + ozone->animations.scroll_y_sidebar; + + /* Check whether this is the first on screen category */ + if (!first_category_found) + { + if ((category_y + category_height) > ozone->dimensions.header_height) + { + ozone->first_onscreen_category = i; + first_category_found = true; + } + } + /* Check whether this is the last on screen category */ + else if (!last_category_found) + { + if (category_y > (height - ozone->dimensions.footer_height)) + { + /* Current category is off screen - get index + * of previous category */ + if (i > 0) + { + ozone->last_onscreen_category = i - 1; + last_category_found = true; + } + } + } + + /* Track pointer input, if required */ + if (ozone->pointer_in_sidebar && + ozone->cursor_in_sidebar && + first_category_found && + !last_category_found) + { + /* If pointer is within the bounds of the + * current category, cache category index + * (for use in next 'pointer up' event) */ + if ((ozone->pointer.y > category_y) && + (ozone->pointer.y < category_y + category_height)) + ozone->pointer_categories_selection = i; + } + + if (last_category_found) + break; + } + } + + menu_entries_ctl(MENU_ENTRIES_CTL_START_GET, &i); + + if (i >= entries_end) + { + i = 0; + menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &i); + } + + GFX_ANIMATION_CLEAR_ACTIVE(p_anim); +} + +static void ozone_draw_header( + ozone_handle_t *ozone, + gfx_display_t *p_disp, + gfx_animation_t *p_anim, + settings_t *settings, + void *userdata, + unsigned video_width, + unsigned video_height, + bool battery_level_enable, + bool timedate_enable) +{ + char title[255]; + gfx_animation_ctx_ticker_t ticker; + gfx_animation_ctx_ticker_smooth_t ticker_smooth; + static const char* const ticker_spacer = OZONE_TICKER_SPACER; + unsigned ticker_x_offset = 0; + unsigned timedate_offset = 0; + bool use_smooth_ticker = settings->bools.menu_ticker_smooth; + float scale_factor = ozone->last_scale_factor; + unsigned logo_icon_size = 60 * scale_factor; + unsigned status_icon_size = 92 * scale_factor; + unsigned seperator_margin = 30 * scale_factor; + enum gfx_animation_ticker_type + menu_ticker_type = (enum gfx_animation_ticker_type)settings->uints.menu_ticker_type; + gfx_display_ctx_driver_t *dispctx = p_disp->dispctx; + float *col = ozone->theme->entries_icon; + + /* Initial ticker configuration */ + if (use_smooth_ticker) + { + ticker_smooth.idx = p_anim->ticker_pixel_idx; + ticker_smooth.font_scale = 1.0f; + ticker_smooth.type_enum = menu_ticker_type; + ticker_smooth.spacer = ticker_spacer; + ticker_smooth.x_offset = &ticker_x_offset; + ticker_smooth.dst_str_width = NULL; + } + else + { + ticker.idx = p_anim->ticker_idx; + ticker.type_enum = menu_ticker_type; + ticker.spacer = ticker_spacer; + } + + /* Separator */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + seperator_margin, + ozone->dimensions.header_height, + video_width - seperator_margin * 2, + ozone->dimensions.spacer_1px, + video_width, + video_height, + ozone->theme->header_footer_separator, + NULL); + + /* Title */ + if (use_smooth_ticker) + { + ticker_smooth.font = ozone->fonts.title.font; + ticker_smooth.selected = true; + ticker_smooth.field_width = (video_width - (128 + 47 + 180) * scale_factor); + ticker_smooth.src_str = ozone->show_fullscreen_thumbnails ? ozone->fullscreen_thumbnail_label : ozone->title; + ticker_smooth.dst_str = title; + ticker_smooth.dst_str_len = sizeof(title); + + gfx_animation_ticker_smooth(&ticker_smooth); + } + else + { + ticker.s = title; + ticker.len = (video_width - (128 + 47 + 180) * scale_factor) / ozone->fonts.title.glyph_width; + ticker.str = ozone->show_fullscreen_thumbnails ? ozone->fullscreen_thumbnail_label : ozone->title; + ticker.selected = true; + + gfx_animation_ticker(&ticker); + } + + gfx_display_draw_text( + ozone->fonts.title.font, + title, + ticker_x_offset + 128 * scale_factor, + ozone->dimensions.header_height / 2 + + ozone->fonts.title.line_centre_offset, + video_width, + video_height, + ozone->theme->text_rgba, + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + false); + + /* Icon */ + if (dispctx) + { + if (dispctx->blend_begin) + dispctx->blend_begin(userdata); + if (dispctx->draw) + { +#if 0 + if (discord_avatar_is_ready()) + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + logo_icon_size, + logo_icon_size, + ozone->textures[OZONE_TEXTURE_DISCORD_OWN_AVATAR], + 47 * scale_factor, + 14 * scale_factor, /* Where does this come from...? */ + video_width, + video_height, + 0, 1, col); + else +#endif + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + logo_icon_size, + logo_icon_size, + ozone->textures[OZONE_TEXTURE_RETROARCH], + 47 * scale_factor, + (ozone->dimensions.header_height - logo_icon_size) / 2, + video_width, + video_height, + 0, 1, col); + } + if (dispctx->blend_end) + dispctx->blend_end(userdata); + } + + /* Battery */ + if (battery_level_enable) + { + gfx_display_ctx_powerstate_t powerstate; + char msg[12]; + + msg[0] = '\0'; + + powerstate.s = msg; + powerstate.len = sizeof(msg); + + menu_display_powerstate(&powerstate); + + if (powerstate.battery_enabled) + { + timedate_offset = 95 * scale_factor; + + gfx_display_draw_text( + ozone->fonts.time.font, + msg, + video_width - 85 * scale_factor, + ozone->dimensions.header_height / 2 + + ozone->fonts.time.line_centre_offset, + video_width, + video_height, + ozone->theme->text_rgba, + TEXT_ALIGN_RIGHT, + 1.0f, + false, + 1.0f, + false); + + if (dispctx) + { + if (dispctx->blend_begin) + dispctx->blend_begin(userdata); + if (dispctx->draw) + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + status_icon_size, + status_icon_size, + ozone->icons_textures[powerstate.charging? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_CHARGING : (powerstate.percent > 80)? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_FULL : (powerstate.percent > 60)? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_80 : (powerstate.percent > 40)? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_60 : (powerstate.percent > 20)? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_40 : OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_20], + video_width - (60 + 56) * scale_factor, + 0, + video_width, + video_height, + 0, 1, col); + if (dispctx->blend_end) + dispctx->blend_end(userdata); + } + } + } + + /* Timedate */ + if (timedate_enable) + { + gfx_display_ctx_datetime_t datetime; + char timedate[255]; + + timedate[0] = '\0'; + + datetime.s = timedate; + datetime.time_mode = settings->uints.menu_timedate_style; + datetime.date_separator = settings->uints.menu_timedate_date_separator; + datetime.len = sizeof(timedate); + + menu_display_timedate(&datetime); + + gfx_display_draw_text( + ozone->fonts.time.font, + timedate, + video_width - (85 * scale_factor) - timedate_offset, + ozone->dimensions.header_height / 2 + + ozone->fonts.time.line_centre_offset, + video_width, + video_height, + ozone->theme->text_rgba, + TEXT_ALIGN_RIGHT, + 1.0f, + false, + 1.0f, + false); + + if (dispctx) + { + if (dispctx->blend_begin) + dispctx->blend_begin(userdata); + if (dispctx->draw) + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + status_icon_size, + status_icon_size, + ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOCK], + video_width - (60 + 56) * scale_factor - timedate_offset, + 0, + video_width, + video_height, + 0, 1, col); + if (dispctx->blend_end) + dispctx->blend_end(userdata); + } + } +} + +static void ozone_draw_footer( + ozone_handle_t *ozone, + gfx_display_t *p_disp, + gfx_animation_t *p_anim, + void *userdata, + unsigned video_width, + unsigned video_height, + bool input_menu_swap_ok_cancel_buttons, + settings_t *settings) +{ + bool menu_core_enable = settings->bools.menu_core_enable; + float scale_factor = ozone->last_scale_factor; + unsigned seperator_margin = 30 * scale_factor; + float footer_margin = 59 * scale_factor; + float footer_text_y = (float)video_height - + (ozone->dimensions.footer_height / 2.0f) + + ozone->fonts.footer.line_centre_offset; + float icon_size = 35 * scale_factor; + float icon_padding = 12 * scale_factor; + float icon_y = (float)video_height - + (ozone->dimensions.footer_height / 2.0f) - + (icon_size / 2.0f); + /* Button enable states + * > Note: Only show 'metadata_toggle' if + * 'fullscreen_thumbs' is shown. This condition + * should be guaranteed anyway, but enforce it + * here to prevent 'gaps' in the button list in + * the event of unknown errors */ + bool fullscreen_thumbnails_available = + ozone->fullscreen_thumbnails_available && + !ozone->cursor_in_sidebar && + ozone->show_thumbnail_bar && + ((ozone->thumbnails.right.status != GFX_THUMBNAIL_STATUS_MISSING) || + (ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_MISSING)) && + (gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) || + gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT)); + bool metadata_override_available = + fullscreen_thumbnails_available && + ozone_metadata_override_available(ozone); + /* Determine x origin positions of each + * button + * > From right to left, these are ordered: + * - ok + * - back + * - search + * - toggle fullscreen thumbs (playlists only) + * - toggle metadata (playlists only) */ + float ok_x = (float)video_width - footer_margin - + ozone->footer_labels.ok.width - icon_size - icon_padding; + float back_x = ok_x - + ozone->footer_labels.back.width - icon_size - (2.0f * icon_padding); + float search_x = back_x - + ozone->footer_labels.search.width - icon_size - (2.0f * icon_padding); + float fullscreen_thumbs_x = search_x - + ozone->footer_labels.fullscreen_thumbs.width - icon_size - (2.0f * icon_padding); + float metadata_toggle_x = fullscreen_thumbs_x - + ozone->footer_labels.metadata_toggle.width - icon_size - (2.0f * icon_padding); + gfx_display_ctx_driver_t *dispctx = p_disp->dispctx; + float *col = ozone->theme_dynamic.entries_icon; + + /* Separator */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + seperator_margin, + video_height - ozone->dimensions.footer_height, + video_width - seperator_margin * 2, + ozone->dimensions.spacer_1px, + video_width, + video_height, + ozone->theme->header_footer_separator, + NULL); + + /* Buttons */ + + /* Draw icons */ + if (dispctx) + { + if (dispctx->blend_begin) + dispctx->blend_begin(userdata); + gfx_display_set_alpha(ozone->theme_dynamic.entries_icon, 1.0f); + + if (dispctx->draw) + { + /* > ok */ + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + icon_size, + icon_size, + input_menu_swap_ok_cancel_buttons ? + ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_D] : + ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_R], + ok_x, + icon_y, + video_width, + video_height, + 0, 1, col); + + /* > back */ + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + icon_size, + icon_size, + input_menu_swap_ok_cancel_buttons ? + ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_R] : + ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_D], + back_x, + icon_y, + video_width,video_height, + 0, 1, col); + + /* > search */ + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + icon_size, + icon_size, + ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_U], + search_x, + icon_y, + video_width,video_height, + 0, 1, col); + + /* > fullscreen_thumbs */ + if (fullscreen_thumbnails_available) + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + icon_size, + icon_size, + ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_START], + fullscreen_thumbs_x, + icon_y, + video_width,video_height, + 0, 1, col); + + /* > metadata_toggle */ + if (metadata_override_available) + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + icon_size, + icon_size, + ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SELECT], + metadata_toggle_x, + icon_y, + video_width,video_height, + 0, 1, col); + } + + if (dispctx->blend_end) + dispctx->blend_end(userdata); + } + + /* Draw labels */ + + /* > ok */ + gfx_display_draw_text( + ozone->fonts.footer.font, + ozone->footer_labels.ok.str, + ok_x + icon_size + icon_padding, + footer_text_y, + video_width, + video_height, + ozone->theme->text_rgba, + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + false); + + /* > back */ + gfx_display_draw_text( + ozone->fonts.footer.font, + ozone->footer_labels.back.str, + back_x + icon_size + icon_padding, + footer_text_y, + video_width, + video_height, + ozone->theme->text_rgba, + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + false); + + /* > search */ + gfx_display_draw_text( + ozone->fonts.footer.font, + ozone->footer_labels.search.str, + search_x + icon_size + icon_padding, + footer_text_y, + video_width, + video_height, + ozone->theme->text_rgba, + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + false); + + /* > fullscreen_thumbs */ + if (fullscreen_thumbnails_available) + gfx_display_draw_text( + ozone->fonts.footer.font, + ozone->footer_labels.fullscreen_thumbs.str, + fullscreen_thumbs_x + icon_size + icon_padding, + footer_text_y, + video_width, + video_height, + ozone->theme->text_rgba, + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + false); + + /* > metadata_toggle */ + if (metadata_override_available) + gfx_display_draw_text( + ozone->fonts.footer.font, + ozone->footer_labels.metadata_toggle.str, + metadata_toggle_x + icon_size + icon_padding, + footer_text_y, + video_width, + video_height, + ozone->theme->text_rgba, + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + false); + + /* Core title or Switch icon */ + if (menu_core_enable) + { + gfx_animation_ctx_ticker_t ticker; + gfx_animation_ctx_ticker_smooth_t ticker_smooth; + char core_title[255]; + char core_title_buf[255]; + int usable_width; + bool use_smooth_ticker = + settings->bools.menu_ticker_smooth; + enum gfx_animation_ticker_type menu_ticker_type = + (enum gfx_animation_ticker_type)settings->uints.menu_ticker_type; + static const char* const ticker_spacer = OZONE_TICKER_SPACER; + unsigned ticker_x_offset = 0; + + core_title[0] = '\0'; + core_title_buf[0] = '\0'; + + /* Determine available width for core + * title string */ + usable_width = metadata_override_available ? + metadata_toggle_x : + fullscreen_thumbnails_available ? + fullscreen_thumbs_x : + search_x; + usable_width -= footer_margin + (icon_padding * 3); + + if (usable_width > 0) + { + /* Get core title */ + menu_entries_get_core_title(core_title, sizeof(core_title)); + + /* Configure and run ticker */ + if (use_smooth_ticker) + { + ticker_smooth.idx = p_anim->ticker_pixel_idx; + ticker_smooth.font_scale = 1.0f; + ticker_smooth.type_enum = menu_ticker_type; + ticker_smooth.spacer = ticker_spacer; + ticker_smooth.x_offset = &ticker_x_offset; + ticker_smooth.dst_str_width = NULL; + + ticker_smooth.font = ozone->fonts.footer.font; + ticker_smooth.selected = true; + ticker_smooth.field_width = usable_width; + ticker_smooth.src_str = core_title; + ticker_smooth.dst_str = core_title_buf; + ticker_smooth.dst_str_len = sizeof(core_title_buf); + + gfx_animation_ticker_smooth(&ticker_smooth); + } + else + { + ticker.idx = p_anim->ticker_idx; + ticker.type_enum = menu_ticker_type; + ticker.spacer = ticker_spacer; + + ticker.s = core_title_buf; + ticker.len = usable_width / ozone->fonts.footer.glyph_width; + ticker.str = core_title; + ticker.selected = true; + + gfx_animation_ticker(&ticker); + } + + /* Draw text */ + gfx_display_draw_text( + ozone->fonts.footer.font, + core_title_buf, + ticker_x_offset + footer_margin, + footer_text_y, + video_width, + video_height, + ozone->theme->text_rgba, + TEXT_ALIGN_LEFT, + 1.0f, + false, + 1.0f, + false); + } + } +#ifdef HAVE_LIBNX + else + { + if (dispctx) + { + if (dispctx->blend_begin) + dispctx->blend_begin(userdata); + if (dispctx->draw) + ozone_draw_icon( + p_disp, + userdata, + video_width, + video_height, + 69 * scale_factor, + 30 * scale_factor, + ozone->theme->textures[OZONE_THEME_TEXTURE_SWITCH], + footer_margin, + video_height - ozone->dimensions.footer_height / 2 - 15 * scale_factor, + video_width, + video_height, + 0, + 1, + ozone->pure_white); + if (dispctx->blend_end) + dispctx->blend_end(userdata); + } + } +#endif +} + +static void ozone_set_thumbnail_system(void *data, char*s, size_t len) +{ + ozone_handle_t *ozone = (ozone_handle_t*)data; + if (!ozone) + return; + + gfx_thumbnail_set_system( + ozone->thumbnail_path_data, s, playlist_get_cached()); +} + +static void ozone_get_thumbnail_system(void *data, char*s, size_t len) +{ + ozone_handle_t *ozone = (ozone_handle_t*)data; + const char *system = NULL; + if (!ozone) + return; + + if (gfx_thumbnail_get_system(ozone->thumbnail_path_data, &system)) + strlcpy(s, system, len); +} + +static void ozone_selection_changed(ozone_handle_t *ozone, bool allow_animation) +{ + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + size_t new_selection = menu_navigation_get_selection(); + ozone_node_t *node = (ozone_node_t*)selection_buf->list[new_selection].userdata; + + if (!node) + return; + + if (ozone->selection != new_selection) + { + menu_entry_t entry; + unsigned entry_type; + uintptr_t tag = (uintptr_t)selection_buf; + size_t selection = menu_navigation_get_selection(); + + MENU_ENTRY_INIT(entry); + entry.path_enabled = false; + entry.label_enabled = false; + entry.rich_label_enabled = false; + entry.value_enabled = false; + entry.sublabel_enabled = false; + menu_entry_get(&entry, 0, selection, NULL, true); + + entry_type = entry.type; + + ozone->selection_old = ozone->selection; + ozone->selection = new_selection; + + ozone->cursor_in_sidebar_old = ozone->cursor_in_sidebar; + + gfx_animation_kill_by_tag(&tag); + ozone_update_scroll(ozone, allow_animation, node); + + /* Update thumbnail */ + if (gfx_thumbnail_is_enabled( + ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) || + gfx_thumbnail_is_enabled( + ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT)) + { + bool update_thumbnails = false; + + /* Playlist updates */ + if (ozone->is_playlist && ozone->depth == 1) + { + ozone_set_thumbnail_content(ozone, ""); + update_thumbnails = true; + } + /* Database list updates + * (pointless nuisance...) */ + else if (ozone->depth == 4 && ozone->is_db_manager_list) + { + ozone_set_thumbnail_content(ozone, ""); + update_thumbnails = true; + } + /* Filebrowser image updates */ + else if (ozone->is_file_list) + { + if ((entry_type == FILE_TYPE_IMAGEVIEWER) || + (entry_type == FILE_TYPE_IMAGE)) + { + ozone_set_thumbnail_content(ozone, "imageviewer"); + update_thumbnails = true; + } + else + { + /* If this is a file list and current + * entry is not an image, have to 'reset' + * content + right/left thumbnails + * (otherwise last loaded thumbnail will + * persist, and be shown on the wrong entry) */ + gfx_thumbnail_set_content(ozone->thumbnail_path_data, NULL); + ozone_unload_thumbnail_textures(ozone); + } + } + + if (update_thumbnails) + ozone_update_thumbnail_image(ozone); + } + + /* TODO: update savestate thumbnail and path */ + } +} + +static void ozone_navigation_clear(void *data, bool pending_push) +{ + ozone_handle_t *ozone = (ozone_handle_t*)data; + if (!pending_push) + ozone_selection_changed(ozone, true); +} + +static void ozone_navigation_pointer_changed(void *data) +{ + ozone_handle_t *ozone = (ozone_handle_t*)data; + ozone_selection_changed(ozone, true); +} + +static void ozone_navigation_set(void *data, bool scroll) +{ + ozone_handle_t *ozone = (ozone_handle_t*)data; + ozone_selection_changed(ozone, true); +} + +static void ozone_navigation_alphabet(void *data, size_t *unused) +{ + ozone_handle_t *ozone = (ozone_handle_t*)data; + ozone_selection_changed(ozone, true); +} + +static void ozone_messagebox_fadeout_cb(void *userdata) +{ + ozone_handle_t *ozone = (ozone_handle_t*) userdata; + + free(ozone->pending_message); + ozone->pending_message = NULL; + + ozone->should_draw_messagebox = false; +} + +static void INLINE ozone_font_bind(ozone_font_data_t *font_data) +{ + font_driver_bind_block(font_data->font, &font_data->raster_block); + font_data->raster_block.carr.coords.vertices = 0; +} + +static void INLINE ozone_font_unbind(ozone_font_data_t *font_data) +{ + font_driver_bind_block(font_data->font, NULL); +} + +static void ozone_frame(void *data, video_frame_info_t *video_info) +{ + gfx_animation_ctx_entry_t entry; + ozone_handle_t* ozone = (ozone_handle_t*) data; + settings_t *settings = config_get_ptr(); + unsigned color_theme = settings->uints.menu_ozone_color_theme; + bool use_preferred_system_color_theme = settings->bools.menu_use_preferred_system_color_theme; + uintptr_t messagebox_tag = (uintptr_t)ozone->pending_message; + bool draw_osk = menu_input_dialog_get_display_kb(); + static bool draw_osk_old = false; + float *background_color = NULL; + void *userdata = video_info->userdata; + unsigned video_width = video_info->width; + unsigned video_height = video_info->height; + float menu_framebuffer_opacity = video_info->menu_framebuffer_opacity; + bool libretro_running = video_info->libretro_running; + bool video_fullscreen = video_info->fullscreen; + bool mouse_grabbed = video_info->input_driver_grab_mouse_state; + bool menu_mouse_enable = video_info->menu_mouse_enable; + bool input_menu_swap_ok_cancel_buttons = video_info->input_menu_swap_ok_cancel_buttons; + bool battery_level_enable = video_info->battery_level_enable; + bool timedate_enable = video_info->timedate_enable; + gfx_display_t *p_disp = (gfx_display_t*)video_info->disp_userdata; + gfx_animation_t *p_anim = anim_get_ptr(); + gfx_display_ctx_driver_t *dispctx = p_disp->dispctx; + +#if 0 + static bool reset = false; + + if (discord_avatar_is_ready() && !reset) + { + ozone_context_reset(data, false); + reset = true; + } +#endif + + + if (!ozone) + return; + + if (ozone->first_frame) + { + menu_input_get_pointer_state(&ozone->pointer); + + ozone->cursor_x_old = ozone->pointer.x; + ozone->cursor_y_old = ozone->pointer.y; + ozone->first_frame = false; + } + + /* OSK Fade detection */ + if (draw_osk != draw_osk_old) + { + draw_osk_old = draw_osk; + if (!draw_osk) + { + ozone->should_draw_messagebox = false; + ozone->messagebox_state = false; + ozone->messagebox_state_old = false; + ozone->animations.messagebox_alpha = 0.0f; + } + } + + /* Change theme on the fly */ + if ((color_theme != ozone_last_color_theme) || + (ozone_last_use_preferred_system_color_theme != use_preferred_system_color_theme)) + { + if (use_preferred_system_color_theme) + { + color_theme = ozone_get_system_theme(); + configuration_set_uint(settings, + settings->uints.menu_ozone_color_theme, color_theme); + } + + ozone_set_color_theme(ozone, color_theme); + ozone_set_background_running_opacity(ozone, menu_framebuffer_opacity); + + ozone_last_use_preferred_system_color_theme = use_preferred_system_color_theme; + } + + /* If menu screensaver is active, draw + * screensaver and return */ + if (ozone->show_screensaver) + { + menu_screensaver_frame(ozone->screensaver, + video_info, p_disp); + return; + } + + video_driver_set_viewport(video_width, video_height, true, false); + + /* Clear text */ + ozone_font_bind(&ozone->fonts.footer); + ozone_font_bind(&ozone->fonts.title); + ozone_font_bind(&ozone->fonts.time); + ozone_font_bind(&ozone->fonts.entries_label); + ozone_font_bind(&ozone->fonts.entries_sublabel); + ozone_font_bind(&ozone->fonts.sidebar); + + /* Background */ + if (libretro_running && + (menu_framebuffer_opacity < 1.0f)) + { + if (menu_framebuffer_opacity != ozone_last_framebuffer_opacity) + ozone_set_background_running_opacity(ozone, menu_framebuffer_opacity); + + background_color = ozone->theme->background_libretro_running; + } + else + background_color = ozone->theme->background; + + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + 0, 0, video_width, video_height, + video_width, video_height, + background_color, + NULL); + + /* Header, footer */ + ozone_draw_header( + ozone, + p_disp, + p_anim, + settings, + userdata, + video_width, + video_height, + battery_level_enable, + timedate_enable); + ozone_draw_footer(ozone, + p_disp, p_anim, + userdata, + video_width, + video_height, + input_menu_swap_ok_cancel_buttons, + settings); + + /* Sidebar */ + if (ozone->draw_sidebar) + ozone_draw_sidebar( + ozone, + p_disp, + p_anim, + settings, + userdata, + video_width, + video_height, + libretro_running, + menu_framebuffer_opacity); + + /* Menu entries */ + gfx_display_scissor_begin(p_disp, + userdata, + video_width, + video_height, + ozone->sidebar_offset + (unsigned) ozone->dimensions_sidebar_width, + ozone->dimensions.header_height + ozone->dimensions.spacer_1px, + video_width - (unsigned) ozone->dimensions_sidebar_width + + (-ozone->sidebar_offset), + video_height - ozone->dimensions.header_height - ozone->dimensions.footer_height - ozone->dimensions.spacer_1px); + + /* Current list */ + ozone_draw_entries( + ozone, + p_disp, + p_anim, + settings, + userdata, + video_width, + video_height, + (unsigned)ozone->selection, + (unsigned)ozone->selection_old, + menu_entries_get_selection_buf_ptr(0), + ozone->animations.list_alpha, + ozone->animations.scroll_y, + ozone->is_playlist + ); + + /* Old list */ + if (ozone->draw_old_list) + ozone_draw_entries( + ozone, + p_disp, + p_anim, + settings, + userdata, + video_width, + video_height, + (unsigned)ozone->selection_old_list, + (unsigned)ozone->selection_old_list, + &ozone->selection_buf_old, + ozone->animations.list_alpha, + ozone->scroll_old, + ozone->is_playlist_old + ); + + /* Thumbnail bar */ + if (ozone->show_thumbnail_bar) + ozone_draw_thumbnail_bar(ozone, + p_disp, + p_anim, + settings, + userdata, + video_width, + video_height, + libretro_running, + menu_framebuffer_opacity); + + if (dispctx && dispctx->scissor_end) + dispctx->scissor_end(userdata, + video_width, video_height); + + /* Flush first layer of text */ + ozone_font_flush(video_width, video_height, &ozone->fonts.footer); + ozone_font_flush(video_width, video_height, &ozone->fonts.title); + ozone_font_flush(video_width, video_height, &ozone->fonts.time); + ozone_font_flush(video_width, video_height, &ozone->fonts.entries_label); + ozone_font_flush(video_width, video_height, &ozone->fonts.entries_sublabel); + ozone_font_flush(video_width, video_height, &ozone->fonts.sidebar); + + /* Draw fullscreen thumbnails, if required */ + ozone_draw_fullscreen_thumbnails(ozone, + userdata, + video_info->disp_userdata, + video_width, + video_height); + + /* Message box & OSK - second layer of text */ + if (ozone->should_draw_messagebox || draw_osk) + { + /* Fade in animation */ + if (ozone->messagebox_state_old != ozone->messagebox_state && ozone->messagebox_state) + { + ozone->messagebox_state_old = ozone->messagebox_state; + + gfx_animation_kill_by_tag(&messagebox_tag); + ozone->animations.messagebox_alpha = 0.0f; + + entry.cb = NULL; + entry.duration = ANIMATION_PUSH_ENTRY_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &ozone->animations.messagebox_alpha; + entry.tag = messagebox_tag; + entry.target_value = 1.0f; + entry.userdata = NULL; + + gfx_animation_push(&entry); + } + /* Fade out animation */ + else if (ozone->messagebox_state_old != ozone->messagebox_state && !ozone->messagebox_state) + { + ozone->messagebox_state_old = ozone->messagebox_state; + ozone->messagebox_state = false; + + gfx_animation_kill_by_tag(&messagebox_tag); + ozone->animations.messagebox_alpha = 1.0f; + + entry.cb = ozone_messagebox_fadeout_cb; + entry.duration = ANIMATION_PUSH_ENTRY_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &ozone->animations.messagebox_alpha; + entry.tag = messagebox_tag; + entry.target_value = 0.0f; + entry.userdata = ozone; + + gfx_animation_push(&entry); + } + + ozone_draw_backdrop( + userdata, + video_info->disp_userdata, + video_width, + video_height, + float_min(ozone->animations.messagebox_alpha, 0.75f)); + + if (draw_osk) + { + const char *label = menu_input_dialog_get_label_buffer(); + const char *str = menu_input_dialog_get_buffer(); + + ozone_draw_osk(ozone, + userdata, + video_info->disp_userdata, + video_width, + video_height, + label, str); + } + else + ozone_draw_messagebox( + ozone, + p_disp, + userdata, + video_width, + video_height, + ozone->pending_message); + + /* Flush second layer of text */ + ozone_font_flush(video_width, video_height, &ozone->fonts.footer); + ozone_font_flush(video_width, video_height, &ozone->fonts.entries_label); + } + + /* Cursor */ + if (ozone->show_cursor && (ozone->pointer.type != MENU_POINTER_DISABLED)) + { + bool cursor_visible = (video_fullscreen || mouse_grabbed) && + menu_mouse_enable; + + gfx_display_set_alpha(ozone->pure_white, 1.0f); + if (cursor_visible) + gfx_display_draw_cursor( + p_disp, + userdata, + video_width, + video_height, + cursor_visible, + ozone->pure_white, + ozone->dimensions.cursor_size, + ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_POINTER], + ozone->pointer.x, + ozone->pointer.y, + video_width, + video_height + ); + } + + /* Unbind fonts */ + ozone_font_unbind(&ozone->fonts.footer); + ozone_font_unbind(&ozone->fonts.title); + ozone_font_unbind(&ozone->fonts.time); + ozone_font_unbind(&ozone->fonts.entries_label); + ozone_font_unbind(&ozone->fonts.entries_sublabel); + ozone_font_unbind(&ozone->fonts.sidebar); + + video_driver_set_viewport(video_width, video_height, false, true); +} + +static void ozone_set_header(ozone_handle_t *ozone) +{ + if (ozone->categories_selection_ptr <= ozone->system_tab_end) + menu_entries_get_title(ozone->title, sizeof(ozone->title)); + else + { + ozone_node_t *node = (ozone_node_t*)file_list_get_userdata_at_offset(&ozone->horizontal_list, ozone->categories_selection_ptr - ozone->system_tab_end-1); + + if (node && node->console_name) + { + strlcpy(ozone->title, node->console_name, sizeof(ozone->title)); + + /* Add current search terms */ + menu_entries_search_append_terms_string( + ozone->title, sizeof(ozone->title)); + } + } +} + +static void ozone_animation_end(void *userdata) +{ + ozone_handle_t *ozone = (ozone_handle_t*) userdata; + ozone->draw_old_list = false; + ozone->animations.cursor_alpha = 1.0f; +} + +static void ozone_list_open(ozone_handle_t *ozone, settings_t *settings) +{ + struct gfx_animation_ctx_entry entry; + uintptr_t sidebar_tag = (uintptr_t)&ozone->sidebar_offset; + + ozone->draw_old_list = true; + + /* Left/right animation */ + ozone->animations.list_alpha = 0.0f; + + entry.cb = ozone_animation_end; + entry.duration = ANIMATION_PUSH_ENTRY_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &ozone->animations.list_alpha; + entry.tag = (uintptr_t)NULL; + entry.target_value = 1.0f; + entry.userdata = ozone; + + gfx_animation_push(&entry); + + /* Sidebar animation */ + ozone_sidebar_update_collapse(ozone, settings, true); + + /* Kill any existing sidebar slide-in/out animations + * before pushing a new one + * > This is required since the 'ozone_collapse_end' + * callback from an unfinished slide-out animation + * may subsequently override the 'draw_sidebar' + * value set at the beginning of the next slide-in + * animation... */ + gfx_animation_kill_by_tag(&sidebar_tag); + + if (ozone->depth == 1) + { + ozone->draw_sidebar = true; + + entry.cb = NULL; + entry.duration = ANIMATION_PUSH_ENTRY_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &ozone->sidebar_offset; + entry.tag = sidebar_tag; + entry.target_value = 0.0f; + entry.userdata = NULL; + + gfx_animation_push(&entry); + } + else if (ozone->depth > 1) + { + struct gfx_animation_ctx_entry entry; + + entry.cb = ozone_collapse_end; + entry.duration = ANIMATION_PUSH_ENTRY_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &ozone->sidebar_offset; + entry.tag = sidebar_tag; + entry.target_value = -ozone->dimensions_sidebar_width; + entry.userdata = (void*)ozone; + + gfx_animation_push(&entry); + } +} + +static void ozone_populate_entries(void *data, + const char *path, const char *label, unsigned k) +{ + settings_t *settings = NULL; + ozone_handle_t *ozone = (ozone_handle_t*) data; + + int new_depth; + bool animate; + + if (!ozone) + return; + + settings = config_get_ptr(); + + ozone_set_header(ozone); + + if (menu_driver_ctl(RARCH_MENU_CTL_IS_PREVENT_POPULATE, NULL)) + { + menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL); + ozone_selection_changed(ozone, false); + + /* Handle playlist searches + * (Ozone is a fickle beast...) */ + if (ozone->is_playlist) + { + menu_serch_terms_t *menu_search_terms = + menu_entries_search_get_terms(); + size_t num_search_terms = + menu_search_terms ? menu_search_terms->size : 0; + + if (ozone->num_search_terms_old != num_search_terms) + { + /* Refresh thumbnails */ + ozone_unload_thumbnail_textures(ozone); + + if (gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) || + gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT)) + { + ozone_set_thumbnail_content(ozone, ""); + ozone_update_thumbnail_image(ozone); + } + + /* If we are currently inside an empty + * playlist, return to the sidebar */ + if (!ozone->cursor_in_sidebar) + { + file_list_t *list = menu_entries_get_selection_buf_ptr(0); + uintptr_t animation_tag = (uintptr_t)&ozone->animations.cursor_alpha; + bool goto_sidebar = false; + + if (!list || (list->size < 1)) + goto_sidebar = true; + + if (!goto_sidebar && + (list->list[0].type != FILE_TYPE_RPL_ENTRY)) + goto_sidebar = true; + + if (goto_sidebar) + { + gfx_animation_kill_by_tag(&animation_tag); + ozone->empty_playlist = true; + ozone_go_to_sidebar(ozone, settings, animation_tag); + } + } + + ozone->num_search_terms_old = num_search_terms; + } + } + + return; + } + + ozone->need_compute = true; + + ozone->first_onscreen_entry = 0; + ozone->last_onscreen_entry = 0; + + new_depth = (int)ozone_list_get_size(ozone, MENU_LIST_PLAIN); + + animate = new_depth != ozone->depth; + ozone->fade_direction = new_depth <= ozone->depth; + ozone->depth = new_depth; + ozone->is_playlist = ozone_is_playlist(ozone, true); + ozone->is_db_manager_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DATABASE_MANAGER_LIST)); + ozone->is_file_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES)); + ozone->is_quick_menu = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_RPL_ENTRY_ACTIONS)) || + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_SETTINGS)); + + if (animate) + if (ozone->categories_selection_ptr == ozone->categories_active_idx_old) + ozone_list_open(ozone, settings); + + /* Thumbnails + * > Note: Leave current thumbnails loaded when + * opening the quick menu - allows proper fade + * out of the fullscreen thumbnail viewer */ + if (!ozone->is_quick_menu) + { + ozone_unload_thumbnail_textures(ozone); + + if (gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) || + gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT)) + { + /* Only auto-load thumbnails if we are viewing + * a playlist or a database manager list + * > Note that we can ignore file browser lists, + * since the first selected item on such a list + * can never have a thumbnail */ + if (ozone->is_playlist || + (ozone->depth == 4 && ozone->is_db_manager_list)) + { + ozone_set_thumbnail_content(ozone, ""); + ozone_update_thumbnail_image(ozone); + } + } + } + + /* Fullscreen thumbnails are only enabled on + * playlists, database manager lists and file + * lists */ + ozone->fullscreen_thumbnails_available = + (ozone->is_playlist && ozone->depth == 1) || + (ozone->is_db_manager_list && ozone->depth == 4) || + ozone->is_file_list; +} + +/* TODO: Fancy toggle animation */ + +static void ozone_toggle(void *userdata, bool menu_on) +{ + settings_t *settings = NULL; + bool tmp = false; + ozone_handle_t *ozone = (ozone_handle_t*) userdata; + + if (!ozone) + return; + + settings = config_get_ptr(); + tmp = !menu_entries_ctl( + MENU_ENTRIES_CTL_NEEDS_REFRESH, NULL); + + if (tmp) + menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); + else + menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL); + + if (ozone->depth == 1) + { + ozone->draw_sidebar = true; + ozone->sidebar_offset = 0.0f; + } + + ozone_sidebar_update_collapse(ozone, settings, false); +} + +static bool ozone_menu_init_list(void *data) +{ + menu_displaylist_info_t info; + + settings_t *settings = config_get_ptr(); + file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + + menu_displaylist_info_init(&info); + + info.label = strdup( + msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU)); + info.exts = strdup("lpl"); + info.type_default = FILE_TYPE_PLAIN; + info.enum_idx = MENU_ENUM_LABEL_MAIN_MENU; + + menu_entries_append_enum(menu_stack, info.path, + info.label, + MENU_ENUM_LABEL_MAIN_MENU, + info.type, info.flags, 0); + + info.list = selection_buf; + + if (!menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info, settings)) + goto error; + + info.need_push = true; + + if (!menu_displaylist_process(&info)) + goto error; + + menu_displaylist_info_free(&info); + return true; + +error: + menu_displaylist_info_free(&info); + return false; +} + +static void ozone_list_insert(void *userdata, + file_list_t *list, + const char *path, + const char *fullpath, + const char *label, + size_t list_size, + unsigned type) +{ + ozone_handle_t *ozone = (ozone_handle_t*) userdata; + ozone_node_t *node = NULL; + int i = (int)list_size; + + if (!ozone || !list) + return; + + ozone->need_compute = true; + + node = (ozone_node_t*)list->list[i].userdata; + + if (!node) + node = ozone_alloc_node(); + + if (!node) + { + RARCH_ERR("ozone node could not be allocated.\n"); + return; + } + + if (!string_is_empty(fullpath)) + { + if (node->fullpath) + free(node->fullpath); + + node->fullpath = strdup(fullpath); + } + + list->list[i].userdata = node; +} + +static int ozone_environ_cb(enum menu_environ_cb type, void *data, void *userdata) +{ + ozone_handle_t *ozone = (ozone_handle_t*) userdata; + + if (!ozone) + return -1; + + switch (type) + { + case MENU_ENVIRON_ENABLE_MOUSE_CURSOR: + ozone->show_cursor = true; + break; + case MENU_ENVIRON_DISABLE_MOUSE_CURSOR: + ozone->show_cursor = false; + break; + case MENU_ENVIRON_RESET_HORIZONTAL_LIST: + if (!ozone) + return -1; + { + settings_t *settings = config_get_ptr(); + ozone_refresh_horizontal_list(ozone, settings); + } + break; + case MENU_ENVIRON_ENABLE_SCREENSAVER: + ozone->show_screensaver = true; + break; + case MENU_ENVIRON_DISABLE_SCREENSAVER: + ozone->show_screensaver = false; + break; + default: + return -1; + } + + return 0; +} + +static void ozone_messagebox(void *data, const char *message) +{ + ozone_handle_t *ozone = (ozone_handle_t*) data; + + if (!ozone || string_is_empty(message)) + return; + + if (ozone->pending_message) + { + free(ozone->pending_message); + ozone->pending_message = NULL; + } + + ozone->pending_message = strdup(message); + ozone->messagebox_state = true; + ozone->should_draw_messagebox = true; +} + +static int ozone_deferred_push_content_actions(menu_displaylist_info_t *info) +{ + settings_t *settings = config_get_ptr(); + if (!menu_displaylist_ctl( + DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info, settings)) + return -1; + menu_displaylist_process(info); + menu_displaylist_info_free(info); + return 0; +} + +static int ozone_list_bind_init_compare_label(menu_file_list_cbs_t *cbs) +{ + if (cbs && cbs->enum_idx != MSG_UNKNOWN) + { + switch (cbs->enum_idx) + { + case MENU_ENUM_LABEL_CONTENT_ACTIONS: + cbs->action_deferred_push = ozone_deferred_push_content_actions; + break; + default: + return -1; + } + } + + return 0; +} + +static int ozone_list_bind_init(menu_file_list_cbs_t *cbs, + const char *path, const char *label, unsigned type, size_t idx) +{ + if (ozone_list_bind_init_compare_label(cbs) == 0) + return 0; + + return -1; +} + +static int ozone_pointer_up(void *userdata, + unsigned x, unsigned y, unsigned ptr, + enum menu_input_pointer_gesture gesture, + menu_file_list_cbs_t *cbs, + menu_entry_t *entry, unsigned action) +{ + unsigned width, height; + ozone_handle_t *ozone = (ozone_handle_t*)userdata; + file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); + uintptr_t sidebar_tag = (uintptr_t)selection_buf; + size_t selection = menu_navigation_get_selection(); + size_t entries_end = menu_entries_get_size(); + settings_t *settings = config_get_ptr(); + + if (!ozone) + return -1; + + /* If fullscreen thumbnail view is enabled, + * all input will disable it and otherwise + * be ignored */ + if (ozone->show_fullscreen_thumbnails) + { + /* Must reset scroll acceleration, in case + * user performed a swipe (don't want menu + * list to 'drift' after hiding fullscreen + * thumbnails...) */ + menu_input_set_pointer_y_accel(0.0f); + + ozone_hide_fullscreen_thumbnails(ozone, true); + return 0; + } + + video_driver_get_size(&width, &height); + + switch (gesture) + { + case MENU_INPUT_GESTURE_TAP: + case MENU_INPUT_GESTURE_SHORT_PRESS: + /* Tap/press header or footer: Menu back/cancel */ + if ((y < ozone->dimensions.header_height) || + (y > height - ozone->dimensions.footer_height)) + return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_CANCEL); + /* Tap/press entries: Activate and/or select item */ + else if ((ptr < entries_end) && + (x > ozone->dimensions_sidebar_width + + ozone->sidebar_offset) && + (x < width - ozone->animations.thumbnail_bar_position)) + { + if (gesture == MENU_INPUT_GESTURE_TAP) + { + /* A 'tap' always produces a menu action */ + + /* If current 'pointer' item is not active, + * activate it immediately */ + if (ptr != selection) + menu_navigation_set_selection(ptr); + + /* If we are not currently in the sidebar, + * perform a MENU_ACTION_SELECT on currently + * active item + * > NOTE 1: Cannot perform a 'leave sidebar' operation + * and a MENU_ACTION_SELECT at the same time... + * > NOTE 2: We still use 'selection' (i.e. old selection + * value) here. This ensures that ozone_menu_entry_action() + * registers any change due to the above automatic + * 'pointer item' activation, and thus operates + * on the correct target entry */ + if (!ozone->cursor_in_sidebar) + return ozone_menu_entry_action(ozone, entry, + selection, MENU_ACTION_SELECT); + + /* If we currently in the sidebar, leave it */ + ozone_leave_sidebar(ozone, settings, sidebar_tag); + } + else + { + /* A 'short' press is used only to activate (highlight) + * an item - it does not invoke a MENU_ACTION_SELECT + * action */ + menu_input_set_pointer_y_accel(0.0f); + + if (ptr != selection) + menu_navigation_set_selection(ptr); + + /* If we are currently in the sidebar, leave it */ + if (ozone->cursor_in_sidebar) + ozone_leave_sidebar(ozone, settings, sidebar_tag); + /* If this is a playlist and the selection + * has changed, must update thumbnails */ + else if (ozone->is_playlist && + (ozone->depth == 1) && + (ptr != selection)) + { + ozone_set_thumbnail_content(ozone, ""); + ozone_update_thumbnail_image(ozone); + } + } + } + /* Tap/press thumbnail bar: toggle content metadata + * override */ + else if (x > width - ozone->animations.thumbnail_bar_position) + { + /* Want to capture all input here, but only act + * upon it if the content metadata toggle is + * available (i.e. viewing a playlist with dual + * thumbnails) */ + if (ozone_metadata_override_available(ozone)) + return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_INFO); + } + /* Tap/press sidebar: return to sidebar or select + * category */ + else if (ozone->pointer_in_sidebar) + { + /* If cursor is not in sidebar, return to sidebar */ + if (!ozone->cursor_in_sidebar) + ozone_go_to_sidebar(ozone, settings, sidebar_tag); + /* Otherwise, select current category */ + else if ( + ozone->pointer_categories_selection + != ozone->categories_selection_ptr) + { + unsigned horizontal_list_size = (unsigned)ozone->horizontal_list.size; + + /* Ensure that current category is valid */ + if (ozone->pointer_categories_selection <= ozone->system_tab_end + horizontal_list_size) + ozone_sidebar_goto(ozone, (unsigned)ozone->pointer_categories_selection); + } + } + break; + case MENU_INPUT_GESTURE_LONG_PRESS: + /* 'Reset to default' action */ + if ((y > ozone->dimensions.header_height) && + (y < height - ozone->dimensions.footer_height) && + (ptr < entries_end) && + (ptr == selection) && + (x > ozone->dimensions_sidebar_width + ozone->sidebar_offset) && + (x < width - ozone->animations.thumbnail_bar_position)) + return ozone_menu_entry_action(ozone, + entry, selection, MENU_ACTION_START); + break; + case MENU_INPUT_GESTURE_SWIPE_LEFT: + /* If this is a playlist, descend alphabet + * > Note: Can only do this if we are not using + * a mouse, since it conflicts with auto selection + * of entry under cursor */ + if ((ozone->pointer.type != MENU_POINTER_MOUSE) && + ozone->is_playlist && + (ozone->depth == 1)) + return ozone_menu_entry_action(ozone, entry, (size_t)ptr, MENU_ACTION_SCROLL_UP); + break; + case MENU_INPUT_GESTURE_SWIPE_RIGHT: + /* If this is a playlist, ascend alphabet + * > Note: Can only do this if we are not using + * a mouse, since it conflicts with auto selection + * of entry under cursor */ + if ((ozone->pointer.type != MENU_POINTER_MOUSE) && + ozone->is_playlist && + (ozone->depth == 1)) + return ozone_menu_entry_action(ozone, entry, (size_t)ptr, MENU_ACTION_SCROLL_DOWN); + break; + default: + /* Ignore input */ + break; + } + + return 0; +} + +menu_ctx_driver_t menu_ctx_ozone = { + NULL, /* set_texture */ + ozone_messagebox, + ozone_render, + ozone_frame, + ozone_init, + ozone_free, + ozone_context_reset, + ozone_context_destroy, + ozone_populate_entries, + ozone_toggle, + ozone_navigation_clear, + NULL, + NULL, + ozone_navigation_set, + ozone_navigation_pointer_changed, + ozone_navigation_alphabet, + ozone_navigation_alphabet, + ozone_menu_init_list, + ozone_list_insert, + NULL, /* list_prepend */ + ozone_list_free, + ozone_list_clear, + ozone_list_cache, + ozone_list_push, + ozone_list_get_selection, + ozone_list_get_size, + ozone_list_get_entry, + NULL, /* list_set_selection */ + ozone_list_bind_init, + NULL, + "ozone", + ozone_environ_cb, + NULL, + ozone_update_thumbnail_image, + ozone_refresh_thumbnail_image, + ozone_set_thumbnail_system, + ozone_get_thumbnail_system, + ozone_set_thumbnail_content, + gfx_display_osk_ptr_at_pos, + NULL, /* update_savestate_thumbnail_path */ + NULL, /* update_savestate_thumbnail_image */ + NULL, /* pointer_down */ + ozone_pointer_up, + ozone_menu_entry_action +};