diff --git a/driver.c b/driver.c index 9cc25e40aa..660b48a6f8 100644 --- a/driver.c +++ b/driver.c @@ -26,6 +26,10 @@ #ifdef HAVE_MENU #include "menu/menu_driver.h" +#include "menu/menu_driver.h" +#ifdef HAVE_MENU_WIDGETS +#include "menu/widgets/menu_widgets.h" +#endif #endif #include "dynamic.h" @@ -385,6 +389,13 @@ void drivers_init(int flags) core_info_init_current_core(); #ifdef HAVE_MENU +#ifdef HAVE_MENU_WIDGETS + if (video_driver_has_widgets()) + { + menu_widgets_init(video_is_threaded); + menu_widgets_context_reset(video_is_threaded); + } +#endif if (flags & DRIVER_VIDEO_MASK) { @@ -486,6 +497,15 @@ bool driver_ctl(enum driver_ctl_state state, void *data) switch (state) { case RARCH_DRIVER_CTL_DEINIT: +#if defined(HAVE_MENU) && defined(HAVE_MENU_WIDGETS) + /* Tear down menu widgets no matter what + * in case the handle is lost in the threaded + * video driver in the meantime + * (breaking video_driver_has_widgets) */ + menu_widgets_context_destroy(); + menu_widgets_free(); + +#endif video_driver_destroy(); audio_driver_destroy(); input_driver_destroy(); diff --git a/gfx/drivers_context/switch_ctx.c b/gfx/drivers_context/switch_ctx.c index a162363b50..a28483e07e 100644 --- a/gfx/drivers_context/switch_ctx.c +++ b/gfx/drivers_context/switch_ctx.c @@ -82,9 +82,10 @@ static void *switch_ctx_init(video_frame_info_t *video_info, void *video_driver) nx_ctx_ptr = ctx_nx; -#if 0 + /* Comment below to enable error checking */ setenv("MESA_NO_ERROR", "1", 1); +#if 0 /* Uncomment below to enable Mesa logging: */ setenv("EGL_LOG_LEVEL", "debug", 1); setenv("MESA_VERBOSE", "all", 1); diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 994f2e6f58..cd661ee7ab 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -737,7 +737,6 @@ static void materialui_render_label_value( { menu_entry_t entry; menu_animation_ctx_ticker_t ticker; - static const char ticker_spacer[] = " | "; char label_str[255]; char value_str[255]; char *sublabel_str = NULL; @@ -755,7 +754,7 @@ static void materialui_render_label_value( /* Initial ticker configuration */ ticker.type_enum = (enum menu_animation_ticker_type)settings->uints.menu_ticker_type; - ticker.spacer = ticker_spacer; + ticker.spacer = NULL; label_str[0] = value_str[0] = '\0'; @@ -1066,7 +1065,6 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) menu_display_ctx_clearcolor_t clearcolor; menu_animation_ctx_ticker_t ticker; - static const char ticker_spacer[] = " | "; menu_display_ctx_draw_t draw; char msg[255]; char title_buf[255]; @@ -1172,7 +1170,7 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) /* Initial ticker configuration */ ticker.type_enum = (enum menu_animation_ticker_type)settings->uints.menu_ticker_type; - ticker.spacer = ticker_spacer; + ticker.spacer = NULL; usable_width = width - (mui->margin * 2); diff --git a/menu/drivers/ozone/ozone.c b/menu/drivers/ozone/ozone.c index a4d04e06a2..f1fa216a4c 100644 --- a/menu/drivers/ozone/ozone.c +++ b/menu/drivers/ozone/ozone.c @@ -848,7 +848,7 @@ static void ozone_draw_header(ozone_handle_t *ozone, video_frame_info_t *video_i { char title[255]; menu_animation_ctx_ticker_t ticker; - static const char* const ticker_spacer = TICKER_SPACER; + static const char* const ticker_spacer = OZONE_TICKER_SPACER; settings_t *settings = config_get_ptr(); unsigned timedate_offset = 0; diff --git a/menu/drivers/ozone/ozone.h b/menu/drivers/ozone/ozone.h index 3bf88829ce..7681dd9a61 100644 --- a/menu/drivers/ozone/ozone.h +++ b/menu/drivers/ozone/ozone.h @@ -64,11 +64,11 @@ typedef struct ozone_handle ozone_handle_t; #if defined(__APPLE__) /* UTF-8 support is currently broken on Apple devices... */ -#define TICKER_SPACER " | " +#define OZONE_TICKER_SPACER " | " #else /* * UCN equivalent: "\u2003\u2022\u2003" */ -#define TICKER_SPACER "\xE2\x80\x83\xE2\x80\xA2\xE2\x80\x83" +#define OZONE_TICKER_SPACER "\xE2\x80\x83\xE2\x80\xA2\xE2\x80\x83" #endif struct ozone_handle diff --git a/menu/drivers/ozone/ozone_entries.c b/menu/drivers/ozone/ozone_entries.c index 260abe2778..fcec69d0cd 100644 --- a/menu/drivers/ozone/ozone_entries.c +++ b/menu/drivers/ozone/ozone_entries.c @@ -408,7 +408,7 @@ border_iterate: menu_texture_item tex; menu_entry_t entry; menu_animation_ctx_ticker_t ticker; - static const char* const ticker_spacer = TICKER_SPACER; + static const char* const ticker_spacer = OZONE_TICKER_SPACER; char entry_value[255]; char rich_label[255]; char entry_value_ticker[255]; diff --git a/menu/drivers/ozone/ozone_sidebar.c b/menu/drivers/ozone/ozone_sidebar.c index a3522e8779..b7a4610f85 100644 --- a/menu/drivers/ozone/ozone_sidebar.c +++ b/menu/drivers/ozone/ozone_sidebar.c @@ -110,7 +110,7 @@ void ozone_draw_sidebar(ozone_handle_t *ozone, video_frame_info_t *video_info) unsigned i, sidebar_height, selection_y, selection_old_y, horizontal_list_size; char console_title[255]; menu_animation_ctx_ticker_t ticker; - static const char* const ticker_spacer = TICKER_SPACER; + static const char* const ticker_spacer = OZONE_TICKER_SPACER; settings_t *settings = config_get_ptr(); /* Initial ticker configuration */ diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index 28c6009d43..8bdbe5a8b5 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -62,7 +62,7 @@ #define RGUI_TERM_WIDTH(width) (((width - RGUI_TERM_START_X(width) - RGUI_TERM_START_X(width)) / (FONT_WIDTH_STRIDE))) #define RGUI_TERM_HEIGHT(width, height) (((height - RGUI_TERM_START_Y(height) - RGUI_TERM_START_X(width)) / (FONT_HEIGHT_STRIDE)) - 1) -#define TICKER_SPACER " | " +#define RGUI_TICKER_SPACER " | " typedef struct { @@ -1467,7 +1467,7 @@ static void rgui_frame(void *data, video_frame_info_t *video_info) static void rgui_render(void *data, bool is_idle) { menu_animation_ctx_ticker_t ticker; - static const char* const ticker_spacer = TICKER_SPACER; + static const char* const ticker_spacer = RGUI_TICKER_SPACER; unsigned x, y; size_t i, end, fb_pitch, old_start; unsigned fb_width, fb_height; @@ -2263,7 +2263,7 @@ static void rgui_update_menu_sublabel(rgui_t *rgui) if (!string_is_empty(entry.sublabel)) { - static const char* const sublabel_spacer = TICKER_SPACER; + static const char* const sublabel_spacer = RGUI_TICKER_SPACER; struct string_list *list = NULL; size_t line_index; bool prev_line_empty = true; diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index 5cedf338f6..7d6338c9f2 100644 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -2329,7 +2329,6 @@ static int stripes_draw_item( { float icon_x, icon_y, label_offset; menu_animation_ctx_ticker_t ticker; - static const char ticker_spacer[] = " | "; char tmp[255]; char *ticker_str = NULL; unsigned entry_type = 0; @@ -2343,7 +2342,7 @@ static int stripes_draw_item( /* Initial ticker configuration */ ticker.type_enum = settings->uints.menu_ticker_type; - ticker.spacer = ticker_spacer; + ticker.spacer = NULL; if (!node) goto iterate; diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 7d739a7083..e0d10cdf49 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -2803,7 +2803,6 @@ static int xmb_draw_item( float icon_x, icon_y, label_offset; menu_animation_ctx_ticker_t ticker; char tmp[255]; - static const char ticker_spacer[] = " | "; char *ticker_str = NULL; unsigned entry_type = 0; const float half_size = xmb->icon_size / 2.0f; @@ -2816,7 +2815,7 @@ static int xmb_draw_item( /* Initial ticker configuration */ ticker.type_enum = (enum menu_animation_ticker_type)settings->uints.menu_ticker_type; - ticker.spacer = ticker_spacer; + ticker.spacer = NULL; if (!node) goto iterate; diff --git a/menu/drivers/xui.cpp b/menu/drivers/xui.cpp index f5652550c2..4ae2d803be 100644 --- a/menu/drivers/xui.cpp +++ b/menu/drivers/xui.cpp @@ -560,14 +560,13 @@ static void xui_render(void *data, bool is_idle) if (XuiHandleIsValid(m_menutitle)) { menu_animation_ctx_ticker_t ticker; - static const char ticker_spacer[] = " | "; menu_entries_get_title(title, sizeof(title)); mbstowcs(strw_buffer, title, sizeof(strw_buffer) / sizeof(wchar_t)); XuiTextElementSetText(m_menutitle, strw_buffer); /* Initial ticker configuration */ ticker.type_enum = settings->uints.menu_ticker_type; - ticker.spacer = ticker_spacer; + ticker.spacer = NULL; ticker.s = title; ticker.len = RXUI_TERM_WIDTH(fb_width) - 3; diff --git a/menu/menu_animation.c b/menu/menu_animation.c index 5eab87ab84..3a1bbb5598 100644 --- a/menu/menu_animation.c +++ b/menu/menu_animation.c @@ -63,6 +63,8 @@ typedef struct menu_animation menu_animation_t; #define TICKER_SPEED 333 #define TICKER_SLOW_SPEED 1600 +static const char ticker_spacer_default[] = TICKER_SPACER_DEFAULT; + static menu_animation_t anim; static retro_time_t cur_time = 0; static retro_time_t old_time = 0; @@ -658,10 +660,13 @@ bool menu_animation_update(void) return animation_is_active; } -bool menu_animation_ticker(const menu_animation_ctx_ticker_t *ticker) +bool menu_animation_ticker(menu_animation_ctx_ticker_t *ticker) { size_t str_len = utf8len(ticker->str); + if (!ticker->spacer) + ticker->spacer = ticker_spacer_default; + if ((size_t)str_len <= ticker->len) { utf8cpy(ticker->s, diff --git a/menu/menu_animation.h b/menu/menu_animation.h index 9feec900d2..465991c14f 100644 --- a/menu/menu_animation.h +++ b/menu/menu_animation.h @@ -25,6 +25,8 @@ RETRO_BEGIN_DECLS +#define TICKER_SPACER_DEFAULT " | "; + typedef float (*easing_cb) (float, float, float, float); typedef void (*tween_cb) (void*); @@ -149,7 +151,7 @@ void menu_animation_free(void); bool menu_animation_update(void); -bool menu_animation_ticker(const menu_animation_ctx_ticker_t *ticker); +bool menu_animation_ticker(menu_animation_ctx_ticker_t *ticker); float menu_animation_get_delta_time(void); diff --git a/menu/widgets/menu_widgets.c b/menu/widgets/menu_widgets.c index edac4c2647..b37d532d1c 100644 --- a/menu/widgets/menu_widgets.c +++ b/menu/widgets/menu_widgets.c @@ -36,3 +36,2046 @@ #include #include +#define PI 3.14159265359f + +/* TODO: Fix context reset freezing everything in place (probably kills animations when it shouldn't anymore) */ + + +static float msg_queue_background[16] = COLOR_HEX_TO_FLOAT(0x3A3A3A, 1.0f); +static float msg_queue_info[16] = COLOR_HEX_TO_FLOAT(0x12ACF8, 1.0f); + +/* TODO: Colors for warning, error and success */ + +static float msg_queue_task_progress_1[16] = COLOR_HEX_TO_FLOAT(0x55AE99, 1.0f); /* Color of first progress bar in a task message */ +static float msg_queue_task_progress_2[16] = COLOR_HEX_TO_FLOAT(0x388BBD, 1.0f); /* Color of second progress bar in a task message (for multiple tasks with same message) */ + +static float color_task_progress_bar[16] = COLOR_HEX_TO_FLOAT(0x22B14C, 1.0f); + +static unsigned text_color_info = 0xD8EEFFFF; +static unsigned text_color_success = 0x22B14CFF; +static unsigned text_color_error = 0xC23B22FF; +static unsigned text_color_faint = 0x878787FF; + +static float volume_bar_background[16] = COLOR_HEX_TO_FLOAT(0x1A1A1A, 1.0f); +static float volume_bar_normal[16] = COLOR_HEX_TO_FLOAT(0x198AC6, 1.0f); +static float volume_bar_loud[16] = COLOR_HEX_TO_FLOAT(0xF5DD19, 1.0f); +static float volume_bar_loudest[16] = COLOR_HEX_TO_FLOAT(0xC23B22, 1.0f); + +static bool menu_widgets_inited = false; +static uint64_t menu_widgets_frame_count = 0; +static menu_animation_ctx_tag generic_tag = (uintptr_t) &menu_widgets_inited; + +/* Font data */ +static font_data_t *font_regular; +static font_data_t *font_bold; + +static video_font_raster_block_t font_raster_regular; +static video_font_raster_block_t font_raster_bold; + +static float menu_widgets_pure_white[16] = { + 1.00, 1.00, 1.00, 1.00, + 1.00, 1.00, 1.00, 1.00, + 1.00, 1.00, 1.00, 1.00, + 1.00, 1.00, 1.00, 1.00, +}; + +/* Load content animation */ +#define ANIMATION_LOAD_CONTENT_DURATION 333 + +#define LOAD_CONTENT_ANIMATION_INITIAL_ICON_SIZE 320 +#define LOAD_CONTENT_ANIMATION_TARGET_ICON_SIZE 240 + +static bool load_content_animation_running = false; +static char *load_content_animation_content_name = NULL; +static char *load_content_animation_playlist_name = NULL; +static menu_texture_item load_content_animation_icon = 0; + +static float load_content_animation_icon_color[16]; +static float load_content_animation_icon_size; +static float load_content_animation_icon_alpha; +static float load_content_animation_fade_alpha; +static float load_content_animation_final_fade_alpha; + +static menu_timer_t load_content_animation_end_timer; + +static float menu_widgets_backdrop_orig[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 menu_widgets_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, +}; + +/* Messages queue */ + +typedef struct menu_widget_msg +{ + char *msg; + char *msg_new; + float msg_transition_animation; + unsigned msg_len; + unsigned duration; + + unsigned text_height; + + float offset_y; + + float alpha; + + bool dying; /* is it currently doing the fade out animation ? */ + + unsigned width; + bool expired; /* has the timer expired ? if so, should be set to dying */ + + menu_timer_t expiration_timer; + bool expiration_timer_started; + + retro_task_t *task_ptr; + char *task_title_ptr; /* used to detect title change */ + uint8_t task_count; /* how many tasks have used this notification? */ + + int8_t task_progress; + bool task_finished; + bool task_error; + bool task_cancelled; + uint32_t task_ident; + + bool unfolded; /* unfold animation */ + bool unfolding; + float unfold; + + float hourglass_rotation; + menu_timer_t hourglass_timer; +} menu_widget_msg_t; + +static fifo_buffer_t *msg_queue = NULL; +static file_list_t *current_msgs = NULL; +static unsigned msg_queue_kill = 0; + +static unsigned msg_queue_tasks_count = 0; /* count of messages bound to a taskin current_msgs */ + +/* TODO: Don't display icons if assets are missing */ + +static menu_texture_item msg_queue_icon = 0; +static menu_texture_item msg_queue_icon_outline = 0; +static menu_texture_item msg_queue_icon_rect = 0; +static bool msg_queue_has_icons = false; + +/* there can only be one message animation at a time to avoid confusing users */ +static bool widgets_moving = false; + +/* Icons */ +enum menu_widgets_icon +{ + MENU_WIDGETS_ICON_VOLUME_MED = 0, + MENU_WIDGETS_ICON_VOLUME_MAX, + MENU_WIDGETS_ICON_VOLUME_MIN, + MENU_WIDGETS_ICON_VOLUME_MUTE, + + MENU_WIDGETS_ICON_PAUSED, + MENU_WIDGETS_ICON_FAST_FORWARD, + MENU_WIDGETS_ICON_REWIND, + MENU_WIDGETS_ICON_SLOW_MOTION, + + MENU_WIDGETS_ICON_HOURGLASS, + MENU_WIDGETS_ICON_CHECK, + + MENU_WIDGETS_ICON_INFO, + + MENU_WIDGETS_ICON_LAST +}; + +static char *menu_widgets_icons_names[MENU_WIDGETS_ICON_LAST] = { + "menu_volume_med.png", + "menu_volume_max.png", + "menu_volume_min.png", + "menu_volume_mute.png", + + "menu_pause.png", + "menu_frameskip.png", + "menu_rewind.png", + "resume.png", + + "menu_hourglass.png", + "menu_check.png", + + "menu_info.png" +}; + +static menu_texture_item menu_widgets_icons_textures[MENU_WIDGETS_ICON_LAST]; + +/* Volume */ +static float volume_db = 0.0f; +static float volume_percent = 1.0f; +static menu_timer_t volume_timer = 0.0f; + +static float volume_alpha = 0.0f; +static float volume_text_alpha = 0.0f; +static menu_animation_ctx_tag volume_tag = (uintptr_t) &volume_alpha; +static bool volume_mute = false; + +/* FPS */ +static char menu_widgets_fps_text[255]; + +/* Status icons */ +static bool menu_widgets_paused = false; +static bool menu_widgets_fast_forward = false; +static bool menu_widgets_rewinding = false; + +/* Screenshot */ +static float screenshot_alpha = 0.0f; +static menu_texture_item screenshot_texture = 0; +static unsigned screenshot_texture_width = 0; +static unsigned screenshot_texture_height = 0; +static char screenshot_shotname[256] = {0}; +static char screenshot_filename[256] = {0}; +static bool screenshot_loaded = false; + +static unsigned screenshot_height; +static unsigned screenshot_width; +static float screenshot_scale_factor; +static unsigned screenshot_thumbnail_width; +static unsigned screenshot_thumbnail_height; +static float screenshot_y; +static menu_timer_t screenshot_timer; + +static unsigned screenshot_shotname_length; + +/* Metrics */ +static unsigned simple_widget_padding; +static unsigned simple_widget_height; +static unsigned glyph_width; + +static unsigned msg_queue_height; +static unsigned msg_queue_icon_size_x; +static unsigned msg_queue_icon_size_y; +static float msg_queue_text_scale_factor; +static unsigned msg_queue_base_width; +static unsigned msg_queue_spacing; +static unsigned msg_queue_glyph_width; +static unsigned msg_queue_rect_start_x; +static unsigned msg_queue_internal_icon_size; +static unsigned msg_queue_internal_icon_offset; +static unsigned msg_queue_icon_offset_y; +static unsigned msg_queue_scissor_start_x; +static unsigned msg_queue_default_rect_width; +static unsigned msg_queue_task_text_start_x; +static unsigned msg_queue_regular_padding_x; +static unsigned msg_queue_regular_text_start; +static unsigned msg_queue_regular_text_base_y; +static unsigned msg_queue_task_rect_start_x; +static unsigned msg_queue_task_hourglass_x; + +bool menu_widgets_set_paused(bool is_paused) +{ + if (!menu_widgets_inited) + return false; + + menu_widgets_paused = is_paused; + return true; +} + +static void msg_widget_msg_transition_animation_done(void *userdata) +{ + menu_widget_msg_t *msg = (menu_widget_msg_t*) userdata; + + free(msg->msg); + + msg->msg = msg->msg_new; + msg->msg_new = NULL; + msg->msg_transition_animation = 0.0f; +} + +static bool menu_widgets_msg_queue_push_internal(retro_task_t *task, const char *msg, + unsigned duration, + char *title, + enum message_queue_icon icon, enum message_queue_category category, + unsigned prio, bool flush) +{ + menu_widget_msg_t* msg_widget = NULL; + + if (!menu_widgets_inited) + return false; + + #ifdef HAVE_THREADS + runloop_msg_queue_lock(); + #endif + + ui_companion_driver_msg_queue_push(msg, + prio, task ? duration : duration * 60 / 1000, flush); + + if (fifo_write_avail(msg_queue) > 0) + { + /* Get current msg if it exists */ + if (task != NULL && task->frontend_userdata) + { + msg_widget = (menu_widget_msg_t*) task->frontend_userdata; + msg_widget->task_ptr = task; /* msg_widgets can be passed between tasks */ + } + + /* Spawn a new notification */ + if (msg_widget == NULL) + { + const char *title; + + msg_widget = (menu_widget_msg_t*) calloc(1, sizeof(*msg_widget)); + + title = task != NULL ? task->title : msg; + + msg_widget->duration = duration; + msg_widget->offset_y = 0; + msg_widget->alpha = 1.0f; + + msg_widget->dying = false; + msg_widget->expired = false; + + msg_widget->expiration_timer = 0; + msg_widget->task_ptr = task; + msg_widget->expiration_timer_started = false; + + msg_widget->msg_new = NULL; + msg_widget->msg_transition_animation = 0.0f; + + msg_widget->text_height = 0; + + if (msg_queue_has_icons) + { + msg_widget->unfolded = false; + msg_widget->unfolding = false; + msg_widget->unfold = 0.0f; + } + else + { + msg_widget->unfolded = true; + msg_widget->unfolding = false; + msg_widget->unfold = 1.0f; + } + + if (task) + { + msg_widget->msg = strdup(title); + msg_widget->msg_len = strlen(title); + + msg_widget->task_error = task->error; + msg_widget->task_cancelled = task->cancelled; + msg_widget->task_finished = task->finished; + msg_widget->task_progress = task->progress; + msg_widget->task_ident = task->ident; + msg_widget->task_title_ptr = task->title; + msg_widget->task_count = 1; + + msg_widget->unfolded = true; + + msg_widget->width = font_driver_get_message_width(font_regular, title, msg_widget->msg_len, msg_queue_text_scale_factor) + simple_widget_padding/2; + + task->frontend_userdata = msg_widget; + + msg_widget->hourglass_rotation = 0; + } + else + { + /* Compute rect width, wrap if necessary */ + /* Single line text > two lines text > two lines text with expanded width */ + unsigned title_length = strlen(title); + char *msg = strdup(title); + unsigned width = msg_queue_default_rect_width; + unsigned text_width = font_driver_get_message_width(font_regular, title, title_length, msg_queue_text_scale_factor); + settings_t *settings = config_get_ptr(); + + msg_widget->text_height = msg_queue_text_scale_factor * settings->floats.video_font_size; + + /* Text is too wide, split it into two lines */ + if (text_width > width) + { + if (text_width/2 > width) + { + width = text_width/2; + width += 10 * msg_queue_glyph_width; + } + + word_wrap(msg, msg, title_length/2 + 10, false); + + msg_widget->text_height *= 2.5f; + } + else + { + width = text_width; + msg_widget->text_height *= 1.35f; + } + + msg_widget->msg = msg; + msg_widget->msg_len = strlen(msg); + msg_widget->width = width + simple_widget_padding/2; + } + + fifo_write(msg_queue, &msg_widget, sizeof(msg_widget)); + } + /* Update task info */ + else + { + if (msg_widget->expiration_timer_started) + { + menu_timer_kill(&msg_widget->expiration_timer); + msg_widget->expiration_timer_started = false; + } + + if (task->title != msg_widget->task_title_ptr) + { + menu_animation_ctx_entry_t entry; + unsigned len = strlen(task->title); + unsigned new_width = font_driver_get_message_width(font_regular, task->title, len, msg_queue_text_scale_factor); + + if (msg_widget->msg_new) + free(msg_widget->msg_new); + + msg_widget->msg_new = strdup(task->title); + msg_widget->msg_len = len; + msg_widget->task_title_ptr = task->title; + msg_widget->msg_transition_animation = 0; + + entry.easing_enum = EASING_OUT_QUAD; + entry.tag = (uintptr_t) NULL; + entry.duration = MSG_QUEUE_ANIMATION_DURATION*2; + entry.target_value = msg_queue_height/2.0f; + entry.subject = &msg_widget->msg_transition_animation; + entry.cb = msg_widget_msg_transition_animation_done; + entry.userdata = msg_widget; + + menu_animation_push(&entry); + + msg_widget->task_count++; + + if (new_width > msg_widget->width) + msg_widget->width = new_width; + } + + msg_widget->task_error = task->error; + msg_widget->task_cancelled = task->cancelled; + msg_widget->task_finished = task->finished; + msg_widget->task_progress = task->progress; + } + } + + #ifdef HAVE_THREADS + runloop_msg_queue_unlock(); + #endif + + return true; +} + +bool menu_widgets_msg_queue_push(const char *msg, + unsigned duration, + char *title, + enum message_queue_icon icon, enum message_queue_category category, + unsigned prio, bool flush) +{ + return menu_widgets_msg_queue_push_internal(NULL, msg, duration, title, icon, category, prio, flush); +} + +static void menu_widgets_unfold_end(void *userdata) +{ + menu_widget_msg_t *unfold = (menu_widget_msg_t*) userdata; + + unfold->unfolding = false; + widgets_moving = false; +} + +static void menu_widgets_move_end(void *userdata) +{ + if (userdata) + { + menu_widget_msg_t *unfold = (menu_widget_msg_t*) userdata; + + menu_animation_ctx_entry_t entry; + + entry.cb = menu_widgets_unfold_end; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &unfold->unfold; + entry.tag = generic_tag; + entry.target_value = 1.0f; + entry.userdata = unfold; + + menu_animation_push(&entry); + + unfold->unfolded = true; + unfold->unfolding = true; + } + else + widgets_moving = false; +} + +static void menu_widgets_msg_queue_expired(void *userdata) +{ + menu_widget_msg_t *msg = (menu_widget_msg_t *) userdata; + + if (msg && !msg->expired) + msg->expired = true; +} + +static void menu_widgets_msg_queue_move(void) +{ + int i; + float y = 0; + + menu_widget_msg_t *unfold = NULL; /* there should always be one and only one unfolded message */ + + if (current_msgs->size == 0) + return; + + for (i = current_msgs->size-1; i >= 0; i--) + { + menu_widget_msg_t *msg; + + msg = file_list_get_userdata_at_offset(current_msgs, i); + + if (!msg || msg->dying) + continue; + + y += msg_queue_height / (msg->task_ptr ? 2 : 1) + msg_queue_spacing; + + if (!msg->unfolded) + unfold = msg; + + if (msg->offset_y != y) + { + menu_animation_ctx_entry_t entry; + + entry.cb = i == 0 ? menu_widgets_move_end : NULL; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &msg->offset_y; + entry.tag = generic_tag; + entry.target_value = y; + entry.userdata = unfold; + + menu_animation_push(&entry); + + widgets_moving = true; + } + } +} + +static void menu_widgets_msg_queue_free(menu_widget_msg_t *msg, bool touch_list) +{ + int i; + menu_animation_ctx_tag tag = (uintptr_t) msg; + + /* Update tasks count */ + if (msg->task_ptr) + msg_queue_tasks_count--; + + /* Kill all animations */ + menu_timer_kill(&msg->hourglass_timer); + menu_animation_kill_by_tag(&tag); + + /* Free it */ + if (msg->msg) + free(msg->msg); + + /* Remove it from the list */ + if (touch_list) + { + file_list_free_userdata(current_msgs, msg_queue_kill); + + for (i = msg_queue_kill; i < current_msgs->size-1; i++) + { + current_msgs->list[i] = current_msgs->list[i+1]; + } + + current_msgs->size--; + } + + widgets_moving = false; +} + +static void menu_widgets_msg_queue_kill_end(void *userdata) +{ + menu_widget_msg_t *msg = file_list_get_userdata_at_offset(current_msgs, msg_queue_kill); + + if (!msg) + return; + + menu_widgets_msg_queue_free(msg, true); +} + +static void menu_widgets_msg_queue_kill(unsigned idx) +{ + menu_animation_ctx_entry_t entry; + + menu_widget_msg_t *msg = file_list_get_userdata_at_offset(current_msgs, idx); + + if (!msg) + return; + + widgets_moving = true; + msg->dying = true; + + msg_queue_kill = idx; + + /* Drop down */ + entry.cb = NULL; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.tag = generic_tag; + entry.userdata = NULL; + entry.subject = &msg->offset_y; + entry.target_value = msg->offset_y - msg_queue_height/4; + + menu_animation_push(&entry); + + /* Fade out */ + entry.cb = menu_widgets_msg_queue_kill_end; + entry.subject = &msg->alpha; + entry.target_value = 0.0f; + + menu_animation_push(&entry); + + /* Move all messages back to their correct position */ + menu_widgets_msg_queue_move(); +} + +static void menu_widgets_draw_icon( + video_frame_info_t *video_info, + unsigned icon_width, + unsigned icon_height, + uintptr_t texture, + float x, float y, + unsigned width, unsigned height, + float rotation, float scale_factor, + float *color) +{ + menu_display_ctx_rotate_draw_t rotate_draw; + menu_display_ctx_draw_t draw; + struct video_coords coords; + math_matrix_4x4 mymat; + + if (!texture) + return; + + 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; + + menu_display_rotate_z(&rotate_draw, video_info); + + coords.vertices = 4; + coords.vertex = NULL; + coords.tex_coord = NULL; + coords.lut_tex_coord = NULL; + coords.color = 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 = MENU_DISPLAY_PRIM_TRIANGLESTRIP; + draw.pipeline.id = 0; + + menu_display_draw(&draw, video_info); +} + +static float menu_widgets_get_thumbnail_scale_factor(const float dst_width, const float dst_height, + const float image_width, const float image_height) +{ + float dst_ratio = dst_width / dst_height; + float image_ratio = image_width / image_height; + + return (dst_ratio > image_ratio) ? dst_height / image_height : dst_width / image_width; +} + +static void menu_widgets_screenshot_dispose(void *userdata) +{ + screenshot_loaded = false; + video_driver_texture_unload(&screenshot_texture); +} + +static void menu_widgets_screenshot_end(void *userdata) +{ + menu_animation_ctx_entry_t entry; + + entry.cb = menu_widgets_screenshot_dispose; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &screenshot_y; + entry.tag = generic_tag; + entry.target_value = -((float)screenshot_height); + entry.userdata = NULL; + + menu_animation_push(&entry); +} + +static void menu_widgets_start_msg_expiration_timer(menu_widget_msg_t *msg_widget, unsigned duration) +{ + if (msg_widget->expiration_timer_started) + return; + + menu_timer_ctx_entry_t timer; + + timer.cb = menu_widgets_msg_queue_expired; + timer.duration = duration; + timer.userdata = msg_widget; + + menu_timer_start(&msg_widget->expiration_timer, &timer); + + msg_widget->expiration_timer_started = true; +} + +static void menu_widgets_hourglass_tick(void *userdata); + +static void menu_widgets_hourglass_end(void *userdata) +{ + menu_widget_msg_t *msg = (menu_widget_msg_t*) userdata; + + msg->hourglass_rotation = 0.0f; + + menu_timer_ctx_entry_t timer; + timer.cb = menu_widgets_hourglass_tick; + timer.duration = HOURGLASS_INTERVAL; + timer.userdata = msg; + + menu_timer_start(&msg->hourglass_timer, &timer); +} + +static void menu_widgets_hourglass_tick(void *userdata) +{ + menu_widget_msg_t *msg = (menu_widget_msg_t*) userdata; + menu_animation_ctx_tag tag = (uintptr_t) msg; + + menu_animation_ctx_entry_t entry; + + entry.easing_enum = EASING_OUT_QUAD; + entry.tag = tag; + entry.duration = HOURGLASS_DURATION; + entry.target_value = -(2 * PI); + entry.subject = &msg->hourglass_rotation; + entry.cb = menu_widgets_hourglass_end; + entry.userdata = msg; + + menu_animation_push(&entry); +} + +void menu_widgets_iterate(void) +{ + int i; + settings_t *settings = config_get_ptr(); + + if (!menu_widgets_inited) + return; + + /* Messages queue */ +#ifdef HAVE_THREADS + runloop_msg_queue_lock(); +#endif + + /* Consume one message if available */ + if (fifo_read_avail(msg_queue) > 0 && !widgets_moving && current_msgs->size < MSG_QUEUE_ONSCREEN_MAX) + { + menu_widget_msg_t *msg_widget; + + fifo_read(msg_queue, &msg_widget, sizeof(msg_widget)); + + /* Task messages always appear from the bottom of the screen */ + if (msg_queue_tasks_count == 0 || msg_widget->task_ptr) + { + file_list_append(current_msgs, + NULL, + NULL, + 0, + 0, + 0 + ); + + file_list_set_userdata(current_msgs, current_msgs->size-1, msg_widget); + } + /* Regular messages are always above tasks */ + else + { + unsigned idx = current_msgs->size - msg_queue_tasks_count; + file_list_insert(current_msgs, + NULL, + NULL, + 0, + 0, + 0, + idx + ); + + file_list_set_userdata(current_msgs, idx, msg_widget); + } + + /* Start expiration timer if not associated to a task */ + if (msg_widget->task_ptr == NULL) + { + menu_widgets_start_msg_expiration_timer(msg_widget, MSG_QUEUE_ANIMATION_DURATION*2 + msg_widget->duration); + } + /* Else, start hourglass animation timer */ + else + { + msg_queue_tasks_count++; + menu_widgets_hourglass_end(msg_widget); + } + + menu_widgets_msg_queue_move(); + } + +#ifdef HAVE_THREADS + runloop_msg_queue_unlock(); +#endif + + /* Kill first expired message */ + /* Start expiration timer of dead tasks */ + for (i = 0; i < current_msgs->size ; i++) + { + menu_widget_msg_t *msg = file_list_get_userdata_at_offset(current_msgs, i); + + if (!msg) + continue; + + if (msg->task_ptr != NULL && (msg->task_finished || msg->task_cancelled)) + menu_widgets_start_msg_expiration_timer(msg, TASK_FINISHED_DURATION); + + if (msg->expired && !widgets_moving) + { + menu_widgets_msg_queue_kill(i); + break; + } + } + + /* Load screenshot and start its animation */ + if (screenshot_filename[0] != '\0') + { + menu_animation_ctx_entry_t entry; + menu_timer_ctx_entry_t timer; + unsigned width; + + video_driver_texture_unload(&screenshot_texture); + menu_display_reset_textures_list(screenshot_filename, "", &screenshot_texture, TEXTURE_FILTER_MIPMAP_LINEAR, &screenshot_texture_width, &screenshot_texture_height); + + video_driver_get_size(&width, NULL); + + screenshot_height = settings->floats.video_font_size * 4; + screenshot_width = width; + + screenshot_scale_factor = menu_widgets_get_thumbnail_scale_factor( + width, screenshot_height, + screenshot_texture_width, screenshot_texture_height + ); + + screenshot_thumbnail_width = screenshot_texture_width * screenshot_scale_factor; + screenshot_thumbnail_height = screenshot_texture_height * screenshot_scale_factor; + + screenshot_shotname_length = (width - screenshot_thumbnail_width - simple_widget_padding*2) / glyph_width; + + screenshot_y = -((float)screenshot_height); + + entry.cb = NULL; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &screenshot_y; + entry.tag = generic_tag; + entry.target_value = 0.0f; + entry.userdata = NULL; + + menu_animation_push(&entry); + + timer.cb = menu_widgets_screenshot_end; + timer.duration = SCREENSHOT_NOTIFICATION_DURATION; + timer.userdata = NULL; + + menu_timer_start(&screenshot_timer, &timer); + + screenshot_loaded = true; + screenshot_filename[0] = '\0'; + } +} + +static int menu_widgets_draw_indicator(video_frame_info_t *video_info, + menu_texture_item icon, int y, int top_right_x_advance, + enum msg_hash_enums msg) +{ + unsigned width; + settings_t *settings = config_get_ptr(); + + menu_display_set_alpha(menu_widgets_backdrop_orig, DEFAULT_BACKDROP); + + if (icon) + { + unsigned height = simple_widget_height * 2; + width = height; + + menu_display_draw_quad(video_info, + top_right_x_advance - width, y, + width, height, + video_info->width, video_info->height, + menu_widgets_backdrop_orig + ); + + menu_display_set_alpha(menu_widgets_pure_white, 1.0f); + + menu_display_blend_begin(video_info); + menu_widgets_draw_icon(video_info, width, height, + icon, top_right_x_advance - width, y, + video_info->width, video_info->height, + 0, 1, menu_widgets_pure_white + ); + menu_display_blend_end(video_info); + } + else + { + unsigned height = simple_widget_height; + const char *txt = msg_hash_to_str(msg); + width = font_driver_get_message_width(font_regular, txt, strlen(txt), 1) + simple_widget_padding*2; + + menu_display_draw_quad(video_info, + top_right_x_advance - width, y, + width, height, + video_info->width, video_info->height, + menu_widgets_backdrop_orig + ); + + menu_display_draw_text(font_regular, + txt, + top_right_x_advance - width + simple_widget_padding, settings->floats.video_font_size + simple_widget_padding/4, + video_info->width, video_info->height, + 0xFFFFFFFF, TEXT_ALIGN_LEFT, + 1.0f, + false, 0, false + ); + } + + return width; +} + +static void menu_widgets_draw_task_msg(menu_widget_msg_t *msg, video_frame_info_t *video_info) +{ + unsigned text_color; + unsigned bar_width; + + unsigned rect_x; + unsigned rect_y; + unsigned rect_width; + unsigned rect_height; + + float *msg_queue_current_background; + float *msg_queue_current_bar; + + unsigned task_percentage_offset = 0; + char task_percentage[256] = {0}; + settings_t *settings = config_get_ptr(); + + task_percentage_offset = glyph_width * (msg->task_error ? 12 : 5) + simple_widget_padding * 1.25f; /*11 = strlen("Task failed")+1 */ + + if (msg->task_finished) + { + if (msg->task_error) + { + snprintf(task_percentage, sizeof(task_percentage), "Task failed"); + } + else + { + snprintf(task_percentage, sizeof(task_percentage), " "); + } + } + else if (msg->task_progress >= 0 && msg->task_progress <= 100) + { + snprintf(task_percentage, sizeof(task_percentage), "%i%%", msg->task_progress); + } + + rect_width = simple_widget_padding + msg->width + task_percentage_offset; + bar_width = rect_width * msg->task_progress/100.0f; + text_color = COLOR_TEXT_ALPHA(0xFFFFFF00, (unsigned)(msg->alpha*255.0f)); + + /* Rect */ + if (msg->task_finished) + if (msg->task_count == 1) + msg_queue_current_background = msg_queue_task_progress_1; + else + msg_queue_current_background = msg_queue_task_progress_2; + else + if (msg->task_count == 1) + msg_queue_current_background = msg_queue_background; + else + msg_queue_current_background = msg_queue_task_progress_1; + + rect_x = msg_queue_rect_start_x - msg_queue_icon_size_x; + rect_y = video_info->height - msg->offset_y; + rect_height = msg_queue_height/2; + + menu_display_set_alpha(msg_queue_current_background, msg->alpha); + menu_display_draw_quad(video_info, + rect_x, rect_y, + rect_width, rect_height, + video_info->width, video_info->height, + msg_queue_current_background + ); + + /* Progress bar */ + if (!msg->task_finished && msg->task_progress >= 0 && msg->task_progress <= 100) + { + if (msg->task_count == 1) + msg_queue_current_bar = msg_queue_task_progress_1; + else + msg_queue_current_bar = msg_queue_task_progress_2; + + menu_display_set_alpha(msg_queue_current_bar, 1.0f); + menu_display_draw_quad(video_info, + msg_queue_task_rect_start_x, video_info->height - msg->offset_y, + bar_width, rect_height, + video_info->width, video_info->height, + msg_queue_current_bar + ); + } + + /* Icon */ + menu_display_set_alpha(menu_widgets_pure_white, msg->alpha); + menu_display_blend_begin(video_info); + menu_widgets_draw_icon(video_info, + msg_queue_height/2, + msg_queue_height/2, + menu_widgets_icons_textures[msg->task_finished ? MENU_WIDGETS_ICON_CHECK : MENU_WIDGETS_ICON_HOURGLASS], + msg_queue_task_hourglass_x, + video_info->height - msg->offset_y, + video_info->width, + video_info->height, + msg->task_finished ? 0 : msg->hourglass_rotation, + 1, menu_widgets_pure_white); + menu_display_blend_end(video_info); + + /* Text */ + if (msg->msg_new) + { + font_driver_flush(video_info->width, video_info->height, font_regular, video_info); + font_raster_regular.carr.coords.vertices = 0; + + menu_display_scissor_begin(video_info, rect_x, rect_y, rect_width, rect_height); + menu_display_draw_text(font_regular, + msg->msg_new, + msg_queue_task_text_start_x, + video_info->height - msg->offset_y + msg_queue_text_scale_factor * settings->floats.video_font_size + msg_queue_height/4 - settings->floats.video_font_size/2.25f - msg_queue_height/2 + msg->msg_transition_animation, + video_info->width, video_info->height, + text_color, + TEXT_ALIGN_LEFT, + msg_queue_text_scale_factor, + false, + 0, + true + ); + } + + menu_display_draw_text(font_regular, + msg->msg, + msg_queue_task_text_start_x, + video_info->height - msg->offset_y + msg_queue_text_scale_factor * settings->floats.video_font_size + msg_queue_height/4 - settings->floats.video_font_size/2.25f + msg->msg_transition_animation, + video_info->width, video_info->height, + text_color, + TEXT_ALIGN_LEFT, + msg_queue_text_scale_factor, + false, + 0, + true + ); + + if (msg->msg_new) + { + font_driver_flush(video_info->width, video_info->height, font_regular, video_info); + font_raster_regular.carr.coords.vertices = 0; + + menu_display_scissor_end(video_info); + } + + /* Progress text */ + text_color = COLOR_TEXT_ALPHA(0xFFFFFF00, (unsigned)(msg->alpha/2*255.0f)); + menu_display_draw_text(font_regular, + task_percentage, + msg_queue_rect_start_x - msg_queue_icon_size_x + rect_width - msg_queue_glyph_width, + video_info->height - msg->offset_y + msg_queue_text_scale_factor * settings->floats.video_font_size + msg_queue_height/4 - settings->floats.video_font_size/2.25f, + video_info->width, video_info->height, + text_color, + TEXT_ALIGN_RIGHT, + msg_queue_text_scale_factor, + false, + 0, + true + ); +} + +static void menu_widgets_draw_regular_msg(menu_widget_msg_t *msg, video_frame_info_t *video_info) +{ + menu_texture_item icon = 0; + + unsigned bar_width; + unsigned text_color; + + if (!icon) + icon = menu_widgets_icons_textures[MENU_WIDGETS_ICON_INFO]; /* TODO: Real icon logic here */ + + /* Icon */ + menu_display_set_alpha(msg_queue_info, msg->alpha); + menu_display_set_alpha(menu_widgets_pure_white, msg->alpha); + menu_display_set_alpha(msg_queue_background, msg->alpha); + + if (!msg->unfolded || msg->unfolding) + { + font_driver_flush(video_info->width, video_info->height, font_regular, video_info); + font_driver_flush(video_info->width, video_info->height, font_bold, video_info); + + font_raster_regular.carr.coords.vertices = 0; + font_raster_bold.carr.coords.vertices = 0; + + menu_display_scissor_begin(video_info, msg_queue_scissor_start_x, 0, + (msg_queue_scissor_start_x + msg->width - simple_widget_padding*2) * msg->unfold, video_info->height); + } + + + if (msg_queue_has_icons) + { + menu_display_blend_begin(video_info); + /* (int) cast is to be consistent with the rect drawing and prevent alignment + * issues, don't remove it */ + menu_widgets_draw_icon(video_info, + msg_queue_icon_size_x, msg_queue_icon_size_y, + msg_queue_icon_rect, msg_queue_spacing, (int)(video_info->height - msg->offset_y - msg_queue_icon_offset_y), + video_info->width, video_info->height, + 0, 1, msg_queue_background); + + menu_display_blend_end(video_info); + } + + /* Background */ + bar_width = simple_widget_padding + msg->width; + + menu_display_draw_quad(video_info, + msg_queue_rect_start_x, video_info->height - msg->offset_y, + bar_width, msg_queue_height, + video_info->width, video_info->height, + msg_queue_background + ); + + /* Text */ + text_color = COLOR_TEXT_ALPHA(0xFFFFFF00, (unsigned)(msg->alpha*255.0f)); + + menu_display_draw_text(font_regular, + msg->msg, + msg_queue_regular_text_start - ((1.0f-msg->unfold) * msg->width/2), + video_info->height - msg->offset_y + msg_queue_regular_text_base_y - msg->text_height/2, + video_info->width, video_info->height, + text_color, + TEXT_ALIGN_LEFT, + msg_queue_text_scale_factor, false, 0, true + ); + + if (!msg->unfolded || msg->unfolding) + { + font_driver_flush(video_info->width, video_info->height, font_regular, video_info); + font_driver_flush(video_info->width, video_info->height, font_bold, video_info); + + font_raster_regular.carr.coords.vertices = 0; + font_raster_bold.carr.coords.vertices = 0; + + menu_display_scissor_end(video_info); + } + + if (msg_queue_has_icons) + { + menu_display_blend_begin(video_info); + + menu_widgets_draw_icon(video_info, + msg_queue_icon_size_x, msg_queue_icon_size_y, + msg_queue_icon, msg_queue_spacing, video_info->height - msg->offset_y - msg_queue_icon_offset_y, + video_info->width, video_info->height, + 0, 1, msg_queue_info); + + menu_widgets_draw_icon(video_info, + msg_queue_icon_size_x, msg_queue_icon_size_y, + msg_queue_icon_outline, msg_queue_spacing, video_info->height - msg->offset_y - msg_queue_icon_offset_y, + video_info->width, video_info->height, + 0, 1, menu_widgets_pure_white); + + menu_widgets_draw_icon(video_info, + msg_queue_internal_icon_size, msg_queue_internal_icon_size, + icon, msg_queue_spacing + msg_queue_internal_icon_offset, video_info->height - msg->offset_y - msg_queue_icon_offset_y + msg_queue_internal_icon_offset, + video_info->width, video_info->height, + 0, 1, menu_widgets_pure_white); + + menu_display_blend_end(video_info); + } +} + +static void menu_widgets_draw_backdrop(video_frame_info_t *video_info, float alpha) +{ + menu_display_set_alpha(menu_widgets_backdrop, alpha); + menu_display_draw_quad(video_info, 0, 0, video_info->width, video_info->height, video_info->width, video_info->height, menu_widgets_backdrop); +} + +static void menu_widgets_draw_load_content_animation(video_frame_info_t *video_info) +{ + /* TODO: scale this right ? (change metrics) */ + + int icon_size = (int) load_content_animation_icon_size; + uint32_t text_alpha = load_content_animation_fade_alpha * 255.0f; + uint32_t text_color = COLOR_TEXT_ALPHA(0xB8B8B800, text_alpha); + unsigned text_offset = -25 * load_content_animation_fade_alpha; + float *icon_color = load_content_animation_icon_color; + + /* Fade out */ + menu_widgets_draw_backdrop(video_info, load_content_animation_fade_alpha); + + /* Icon */ + menu_display_set_alpha(icon_color, load_content_animation_icon_alpha); + menu_display_blend_begin(video_info); + menu_widgets_draw_icon(video_info, icon_size, + icon_size, load_content_animation_icon, + video_info->width/2 - icon_size/2, + video_info->height/2 - icon_size/2, + video_info->width, + video_info->height, + 0, 1, icon_color + ); + menu_display_blend_end(video_info); + + /* Text */ + menu_display_draw_text(font_bold, + load_content_animation_content_name, + video_info->width/2, + video_info->height/2 + 175 + 25 + text_offset, + video_info->width, + video_info->height, + text_color, + TEXT_ALIGN_CENTER, + 1, + false, + 0, + false + ); + + /* Flush text layer */ + font_driver_flush(video_info->width, video_info->height, font_regular, video_info); + font_driver_flush(video_info->width, video_info->height, font_bold, video_info); + + font_raster_regular.carr.coords.vertices = 0; + font_raster_bold.carr.coords.vertices = 0; + + /* Everything disappears */ + menu_widgets_draw_backdrop(video_info, load_content_animation_final_fade_alpha); +} + + +void menu_widgets_frame(video_frame_info_t *video_info) +{ + int i; + int top_right_x_advance = video_info->width; + + settings_t *settings = config_get_ptr(); + + if (!menu_widgets_inited) + return; + + menu_widgets_frame_count++; + + menu_display_set_viewport(video_info->width, video_info->height); + + /* Font setup */ + font_driver_bind_block(font_regular, &font_raster_regular); + font_driver_bind_block(font_bold, &font_raster_bold); + + font_raster_regular.carr.coords.vertices = 0; + font_raster_bold.carr.coords.vertices = 0; + + /* Screenshot */ + if (screenshot_loaded) + { + char shotname[256]; + menu_animation_ctx_ticker_t ticker; + + menu_display_set_alpha(menu_widgets_backdrop_orig, DEFAULT_BACKDROP); + + menu_display_draw_quad(video_info, + 0, screenshot_y, + screenshot_width, screenshot_height, + video_info->width, video_info->height, + menu_widgets_backdrop_orig + ); + + menu_display_set_alpha(menu_widgets_pure_white, 1.0f); + menu_widgets_draw_icon(video_info, + screenshot_thumbnail_width, screenshot_thumbnail_height, + screenshot_texture, + 0, screenshot_y, + video_info->width, video_info->height, + 0, 1, menu_widgets_pure_white + ); + + menu_display_draw_text(font_regular, + msg_hash_to_str(MSG_SCREENSHOT_SAVED), + screenshot_thumbnail_width + simple_widget_padding, settings->floats.video_font_size * 1.9f + screenshot_y, + video_info->width, video_info->height, + text_color_faint, + TEXT_ALIGN_LEFT, + 1, false, 0, true + ); + + ticker.idx = menu_animation_get_ticker_idx(); + ticker.len = screenshot_shotname_length; + ticker.s = shotname; + ticker.selected = true; + ticker.str = screenshot_shotname; + + menu_animation_ticker(&ticker); + + menu_display_draw_text(font_regular, + shotname, + screenshot_thumbnail_width + simple_widget_padding, settings->floats.video_font_size * 2.9f + screenshot_y, + video_info->width, video_info->height, + text_color_info, + TEXT_ALIGN_LEFT, + 1, false, 0, true + ); + } + + /* Volume */ + if (volume_alpha > 0.0f) + { + char msg[255]; + char percentage_msg[255]; + + menu_texture_item volume_icon = 0; + + unsigned volume_width = video_info->width / 3; + unsigned volume_height = settings->floats.video_font_size * 4; + unsigned icon_size = menu_widgets_icons_textures[MENU_WIDGETS_ICON_VOLUME_MED] ? volume_height : simple_widget_padding; + unsigned text_color = COLOR_TEXT_ALPHA(0xffffffff, (unsigned)(volume_text_alpha*255.0f)); + unsigned text_color_db = COLOR_TEXT_ALPHA(text_color_faint, (unsigned)(volume_text_alpha*255.0f)); + + unsigned bar_x = icon_size; + unsigned bar_height = settings->floats.video_font_size/2; + unsigned bar_width = volume_width - bar_x - simple_widget_padding; + unsigned bar_y = volume_height / 2 + bar_height/2; + + float *bar_background = NULL; + float *bar_foreground = NULL; + float bar_percentage = 0.0f; + + if (volume_mute) + { + volume_icon = menu_widgets_icons_textures[MENU_WIDGETS_ICON_VOLUME_MUTE]; + } + else if (volume_percent <= 1.0f) + { + if (volume_percent <= 0.5f) + volume_icon = menu_widgets_icons_textures[MENU_WIDGETS_ICON_VOLUME_MIN]; + else + volume_icon = menu_widgets_icons_textures[MENU_WIDGETS_ICON_VOLUME_MED]; + + bar_background = volume_bar_background; + bar_foreground = volume_bar_normal; + bar_percentage = volume_percent; + } + else if (volume_percent > 1.0f && volume_percent <= 2.0f) + { + volume_icon = menu_widgets_icons_textures[MENU_WIDGETS_ICON_VOLUME_MAX]; + + bar_background = volume_bar_normal; + bar_foreground = volume_bar_loud; + bar_percentage = volume_percent - 1.0f; + } + else + { + volume_icon = menu_widgets_icons_textures[MENU_WIDGETS_ICON_VOLUME_MAX]; + + bar_background = volume_bar_loud; + bar_foreground = volume_bar_loudest; + bar_percentage = volume_percent - 2.0f; + } + + if (bar_percentage > 1.0f) + bar_percentage = 1.0f; + + /* Backdrop */ + menu_display_set_alpha(menu_widgets_backdrop_orig, volume_alpha); + + menu_display_draw_quad(video_info, + 0, 0, + volume_width, + volume_height, + video_info->width, + video_info->height, + menu_widgets_backdrop_orig + ); + + /* Icon */ + if (volume_icon) + { + menu_display_set_alpha(menu_widgets_pure_white, volume_text_alpha); + + menu_display_blend_begin(video_info); + menu_widgets_draw_icon(video_info, + icon_size, icon_size, + volume_icon, + 0, 0, + video_info->width, video_info->height, + 0, 1, menu_widgets_pure_white + ); + menu_display_blend_end(video_info); + } + + if (volume_mute) + { + if (!menu_widgets_icons_textures[MENU_WIDGETS_ICON_VOLUME_MUTE]) + { + const char *text = msg_hash_to_str(MSG_AUDIO_MUTED); + menu_display_draw_text(font_regular, + text, + volume_width/2, volume_height/2 + settings->floats.video_font_size/3, + video_info->width, video_info->height, + text_color, TEXT_ALIGN_CENTER, + 1, false, 0, false + ); + } + } + else + { + /* Bar */ + menu_display_set_alpha(bar_background, volume_text_alpha); + menu_display_set_alpha(bar_foreground, volume_text_alpha); + + menu_display_draw_quad(video_info, + bar_x + bar_percentage * bar_width, bar_y, + bar_width - bar_percentage * bar_width, bar_height, + video_info->width, video_info->height, + bar_background + ); + + menu_display_draw_quad(video_info, + bar_x, bar_y, + bar_percentage * bar_width, bar_height, + video_info->width, video_info->height, + bar_foreground + ); + + /* Text */ + snprintf(msg, sizeof(msg), (volume_db >= 0 ? "+%.1f dB" : "%.1f dB"), + volume_db); + + snprintf(percentage_msg, sizeof(percentage_msg), "%d%%", + (int)(volume_percent * 100.0f)); + + menu_display_draw_text(font_regular, + msg, + volume_width - simple_widget_padding, settings->floats.video_font_size * 2, + video_info->width, video_info->height, + text_color_db, + TEXT_ALIGN_RIGHT, + 1, false, 0, false + ); + + menu_display_draw_text(font_regular, + percentage_msg, + icon_size, settings->floats.video_font_size * 2, + video_info->width, video_info->height, + text_color, + TEXT_ALIGN_LEFT, + 1, false, 0, false + ); + } + } + + /* Draw all messages */ + for (i = 0; i < current_msgs->size; i++) + { + menu_widget_msg_t *msg = file_list_get_userdata_at_offset(current_msgs, i); + + if (!msg) + continue; + + if (msg->task_ptr) + menu_widgets_draw_task_msg(msg, video_info); + else + menu_widgets_draw_regular_msg(msg, video_info); + } + + /* FPS Counter */ + if (video_info->fps_show || video_info->framecount_show) + { + char *text = *menu_widgets_fps_text == '\0' ? "n/a" : menu_widgets_fps_text; + + int text_width = font_driver_get_message_width(font_regular, text, strlen(text), 1.0f); + int total_width = text_width + simple_widget_padding * 2; + + menu_display_set_alpha(menu_widgets_backdrop_orig, DEFAULT_BACKDROP); + + menu_display_draw_quad(video_info, + top_right_x_advance - total_width, 0, + total_width, simple_widget_height, + video_info->width, video_info->height, + menu_widgets_backdrop_orig + ); + + menu_display_draw_text(font_regular, + text, + top_right_x_advance - simple_widget_padding - text_width, settings->floats.video_font_size + simple_widget_padding/4, + video_info->width, video_info->height, + 0xFFFFFFFF, + TEXT_ALIGN_LEFT, + 1, false,0, true + ); + } + + /* Indicators */ + if (menu_widgets_paused) + top_right_x_advance -= menu_widgets_draw_indicator(video_info, + menu_widgets_icons_textures[MENU_WIDGETS_ICON_PAUSED], (video_info->fps_show ? simple_widget_height : 0), top_right_x_advance, + MSG_PAUSED); + + if (menu_widgets_fast_forward) + top_right_x_advance -= menu_widgets_draw_indicator(video_info, + menu_widgets_icons_textures[MENU_WIDGETS_ICON_FAST_FORWARD], (video_info->fps_show ? simple_widget_height : 0), top_right_x_advance, + MSG_PAUSED); + + if (menu_widgets_rewinding) + top_right_x_advance -= menu_widgets_draw_indicator(video_info, + menu_widgets_icons_textures[MENU_WIDGETS_ICON_REWIND], (video_info->fps_show ? simple_widget_height : 0), top_right_x_advance, + MSG_REWINDING); + + if (video_info->runloop_is_slowmotion) + top_right_x_advance -= menu_widgets_draw_indicator(video_info, + menu_widgets_icons_textures[MENU_WIDGETS_ICON_SLOW_MOTION], (video_info->fps_show ? simple_widget_height : 0), top_right_x_advance, + MSG_SLOW_MOTION); + + /* Screenshot */ + if (screenshot_alpha > 0.0f) + { + menu_display_set_alpha(menu_widgets_pure_white, screenshot_alpha); + menu_display_draw_quad(video_info, + 0, 0, + video_info->width, video_info->height, + video_info->width, video_info->height, + menu_widgets_pure_white + ); + } + + /* Load content animation */ + if (load_content_animation_running) + menu_widgets_draw_load_content_animation(video_info); + else + { + font_driver_flush(video_info->width, video_info->height, font_regular, video_info); + font_driver_flush(video_info->width, video_info->height, font_bold, video_info); + + font_raster_regular.carr.coords.vertices = 0; + font_raster_bold.carr.coords.vertices = 0; + } + + menu_display_unset_viewport(video_info->width, video_info->height); +} + +void menu_widgets_init(bool video_is_threaded) +{ + if (menu_widgets_inited) + return; + + menu_widgets_inited = true; + + if (!menu_display_init_first_driver(video_is_threaded)) + goto err; + + menu_widgets_frame_count = 0; + + menu_widgets_fps_text[0] = '\0'; + + msg_queue = fifo_new(MSG_QUEUE_PENDING_MAX * sizeof(menu_widget_msg_t*)); + + if (!msg_queue) + goto err; + + current_msgs = (file_list_t*)calloc(1, sizeof(file_list_t)); + + if (!current_msgs) + goto err; + + file_list_reserve(current_msgs, MSG_QUEUE_ONSCREEN_MAX); + + return; +err: + menu_widgets_free(); +} + +void menu_widgets_context_reset(bool is_threaded) +{ + char xmb_path[PATH_MAX_LENGTH]; + char menu_widgets_path[PATH_MAX_LENGTH]; + char theme_path[PATH_MAX_LENGTH]; + int i; + + char monochrome_png_path[PATH_MAX_LENGTH]; + + char ozone_path[PATH_MAX_LENGTH]; + char font_path[PATH_MAX_LENGTH]; + + settings_t *settings = config_get_ptr(); + + unsigned video_info_width; + + video_driver_get_size(&video_info_width, NULL); + + /* Textures paths */ + fill_pathname_join( + menu_widgets_path, + settings->paths.directory_assets, + "menu_widgets", + sizeof(menu_widgets_path) + ); + + fill_pathname_join( + xmb_path, + settings->paths.directory_assets, + "xmb", + sizeof(xmb_path) + ); + + /* Monochrome */ + fill_pathname_join( + theme_path, + xmb_path, + "monochrome", + sizeof(theme_path) + ); + + fill_pathname_join( + monochrome_png_path, + theme_path, + "png", + sizeof(monochrome_png_path) + ); + + /* Load textures */ + /* Icons */ + for (i = 0; i < MENU_WIDGETS_ICON_LAST; i++) + { + menu_display_reset_textures_list(menu_widgets_icons_names[i], monochrome_png_path, &menu_widgets_icons_textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL); + } + + /* Message queue */ + menu_display_reset_textures_list("msg_queue_icon.png", menu_widgets_path, &msg_queue_icon, TEXTURE_FILTER_LINEAR, NULL, NULL); + menu_display_reset_textures_list("msg_queue_icon_outline.png", menu_widgets_path, &msg_queue_icon_outline, TEXTURE_FILTER_LINEAR, NULL, NULL); + menu_display_reset_textures_list("msg_queue_icon_rect.png", menu_widgets_path, &msg_queue_icon_rect, TEXTURE_FILTER_NEAREST, NULL, NULL); + + msg_queue_has_icons = msg_queue_icon && msg_queue_icon_outline && msg_queue_icon_rect; + + /* Fonts paths */ + fill_pathname_join( + ozone_path, + settings->paths.directory_assets, + "ozone", + sizeof(ozone_path) + ); + + /* Fonts */ + if (settings->paths.path_font[0] == '\0') + { + fill_pathname_join(font_path, ozone_path, "regular.ttf", sizeof(font_path)); + font_regular = menu_display_font_file(font_path, settings->floats.video_font_size, is_threaded); + + fill_pathname_join(font_path, ozone_path, "bold.ttf", sizeof(font_path)); + font_bold = menu_display_font_file(font_path, settings->floats.video_font_size, is_threaded); + } + else + { + font_regular = menu_display_font_file(settings->paths.path_font, settings->floats.video_font_size, is_threaded); + font_bold = menu_display_font_file(settings->paths.path_font, settings->floats.video_font_size, is_threaded); + } + + /* Metrics */ + simple_widget_padding = settings->floats.video_font_size * 2/3; + simple_widget_height = settings->floats.video_font_size + simple_widget_padding; + glyph_width = font_driver_get_message_width(font_regular, "a", 1, 1); + + msg_queue_height = settings->floats.video_font_size * 2.5f; + + if (msg_queue_has_icons) + { + msg_queue_icon_size_y = msg_queue_height * 1.2347826087f; /* original image is 280x284 */ + msg_queue_icon_size_x = 0.98591549295f * msg_queue_icon_size_y; + } + else + { + msg_queue_icon_size_x = 0; + msg_queue_icon_size_y = 0; + } + + msg_queue_text_scale_factor = 0.69f; + msg_queue_base_width = video_info_width / 4; + msg_queue_spacing = msg_queue_height / 3; + msg_queue_glyph_width = glyph_width * msg_queue_text_scale_factor; + msg_queue_rect_start_x = msg_queue_spacing + msg_queue_icon_size_x; + msg_queue_internal_icon_size = msg_queue_icon_size_y; + msg_queue_internal_icon_offset = (msg_queue_icon_size_y - msg_queue_internal_icon_size)/2; + msg_queue_icon_offset_y = (msg_queue_icon_size_y - msg_queue_height)/2; + msg_queue_scissor_start_x = msg_queue_spacing + msg_queue_icon_size_x - (msg_queue_icon_size_x * 0.28928571428f); + msg_queue_default_rect_width = msg_queue_glyph_width * 40; + + if (msg_queue_has_icons) + msg_queue_regular_padding_x = simple_widget_padding/2; + else + msg_queue_regular_padding_x = simple_widget_padding; + + msg_queue_task_rect_start_x = msg_queue_rect_start_x - msg_queue_icon_size_x; + + msg_queue_task_text_start_x = msg_queue_task_rect_start_x + msg_queue_height/2; + + if (!menu_widgets_icons_textures[MENU_WIDGETS_ICON_HOURGLASS]) + msg_queue_task_text_start_x -= msg_queue_glyph_width*2; + + msg_queue_regular_text_start = msg_queue_rect_start_x + msg_queue_regular_padding_x; + msg_queue_regular_text_base_y = settings->floats.video_font_size * msg_queue_text_scale_factor + msg_queue_height/2; + + msg_queue_task_hourglass_x = msg_queue_rect_start_x - msg_queue_icon_size_x; +} + +void menu_widgets_context_destroy(void) +{ + int i; + if (!menu_widgets_inited) + return; + + /* TODO: Dismiss onscreen notifications that have been freed */ + + /* Textures */ + for (i = 0; i < MENU_WIDGETS_ICON_LAST; i++) + { + video_driver_texture_unload(&menu_widgets_icons_textures[i]); + } + + video_driver_texture_unload(&msg_queue_icon); + video_driver_texture_unload(&msg_queue_icon_outline); + video_driver_texture_unload(&msg_queue_icon_rect); + + /* Fonts */ + menu_display_font_free(font_regular); + menu_display_font_free(font_bold); + + font_regular = NULL; + font_bold = NULL; +} + +void menu_widgets_free(void) +{ + int i; + + menu_widgets_inited = false; + + /* Kill any pending animation */ + menu_animation_kill_by_tag(&volume_tag); + menu_animation_kill_by_tag(&generic_tag); + + /* Purge everything from the fifo */ + if (msg_queue) + { + while (fifo_read_avail(msg_queue) > 0) + { + menu_widget_msg_t *msg_widget; + + fifo_read(msg_queue, &msg_widget, sizeof(msg_widget)); + + menu_widgets_msg_queue_free(msg_widget, false); + free(msg_widget); + } + + fifo_free(msg_queue); + } + + /* Purge everything from the list */ + if (current_msgs) + { + for (i = 0; i < current_msgs->size; i++) + { + menu_widget_msg_t *msg = file_list_get_userdata_at_offset(current_msgs, i); + + menu_widgets_msg_queue_free(msg, false); + } + file_list_free(current_msgs); + } + + video_driver_texture_unload(&screenshot_texture); + + /* Font */ + video_coord_array_free(&font_raster_regular.carr); + video_coord_array_free(&font_raster_bold.carr); + + font_driver_bind_block(NULL, NULL); +} + +static void menu_widgets_volume_timer_end(void *userdata) +{ + menu_animation_ctx_entry_t entry; + + entry.cb = NULL; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &volume_alpha; + entry.tag = volume_tag; + entry.target_value = 0.0f; + entry.userdata = NULL; + + menu_animation_push(&entry); + + entry.subject = &volume_text_alpha; + + menu_animation_push(&entry); +} + +bool menu_widgets_volume_update_and_show(void) +{ + settings_t *settings = config_get_ptr(); + bool mute = *(audio_get_bool_ptr(AUDIO_ACTION_MUTE_ENABLE)); + float new_volume = settings->floats.audio_volume; + menu_timer_ctx_entry_t entry; + + if (!menu_widgets_inited) + return false; + + menu_animation_kill_by_tag(&volume_tag); + + volume_db = new_volume; + volume_percent = pow(10, new_volume/20); + volume_alpha = DEFAULT_BACKDROP; + volume_text_alpha = 1.0f; + volume_mute = mute; + + /* TODO/FIXME - natinusula - + * +menu/widgets/menu_widgets.c: In function 'menu_widgets_volume_update_and_show': +menu/widgets/menu_widgets.c:1859:19: warning: assignment to 'tween_cb' {aka 'void (*)(void *)'} from incompatible pointer type 'void (*)(void)' [-Wincompatible-pointer-types] + entry.cb = menu_widgets_volume_timer_end; + ^ + * + * + */ + entry.cb = menu_widgets_volume_timer_end; + entry.duration = VOLUME_DURATION; + entry.userdata = NULL; + + menu_timer_start(&volume_timer, &entry); + + return true; +} + +bool menu_widgets_set_fps_text(char *new_fps_text) +{ + if (!menu_widgets_inited) + return false; + + strlcpy(menu_widgets_fps_text, + new_fps_text, sizeof(menu_widgets_fps_text)); + + return true; +} + +bool menu_widgets_set_fast_forward(bool is_fast_forward) +{ + if (!menu_widgets_inited) + return false; + + menu_widgets_fast_forward = is_fast_forward; + + return true; +} + +bool menu_widgets_set_rewind(bool is_rewind) +{ + if (!menu_widgets_inited) + return false; + + menu_widgets_rewinding = is_rewind; + + return true; +} + +static void menu_widgets_screenshot_fadeout(void *userdata) +{ + menu_animation_ctx_entry_t entry; + + if (!menu_widgets_inited) + return; + + entry.cb = menu_widgets_screenshot_fadeout; + entry.duration = SCREENSHOT_DURATION_OUT; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &screenshot_alpha; + entry.tag = generic_tag; + entry.target_value = 0.0f; + entry.userdata = NULL; + + menu_animation_push(&entry); +} + +void menu_widgets_screenshot_taken(const char *shotname, const char *filename) +{ + strlcpy(screenshot_filename, filename, sizeof(screenshot_filename)); + strlcpy(screenshot_shotname, shotname, sizeof(screenshot_shotname)); +} + +void menu_widgets_take_screenshot(void) +{ + menu_animation_ctx_entry_t entry; + + if (!menu_widgets_inited) + return; + + entry.cb = menu_widgets_screenshot_fadeout; + entry.duration = SCREENSHOT_DURATION_IN; + entry.easing_enum = EASING_IN_QUAD; + entry.subject = &screenshot_alpha; + entry.tag = generic_tag; + entry.target_value = 1.0f; + entry.userdata = NULL; + + menu_animation_push(&entry); +} + +bool menu_widgets_task_msg_queue_push(retro_task_t *task, + const char *msg, + unsigned prio, unsigned duration, + bool flush) +{ + if (!menu_widgets_inited) + return false; + + if (task->title != NULL && !task->mute) + menu_widgets_msg_queue_push_internal(task, msg, duration, NULL, MESSAGE_QUEUE_CATEGORY_INFO, MESSAGE_QUEUE_ICON_DEFAULT, prio, flush); + + return true; +} + +static void menu_widgets_end_load_content_animation(void *userdata) +{ + //task_load_content_resume(); TODO: Restore that +} + +void menu_widgets_cleanup_load_content_animation(void) +{ + load_content_animation_running = false; + free(load_content_animation_content_name); +} + +void menu_widgets_start_load_content_animation(const char *content_name, bool remove_extension) +{ + /* TODO: finish the animation based on design, correct all timings */ + /* TODO: scale the icon correctly */ + menu_animation_ctx_entry_t entry; + menu_timer_ctx_entry_t timer_entry; + int i; + + float icon_color[16] = COLOR_HEX_TO_FLOAT(0x0473C9, 1.0f); /* TODO: random color */ + unsigned timing = 0; + + /* Prepare data */ + load_content_animation_icon = 0; + + /* Abort animation if we don't have an icon */ + if (!menu_driver_get_load_content_animation_data(&load_content_animation_icon, + &load_content_animation_playlist_name) || !load_content_animation_icon) + { + menu_widgets_end_load_content_animation(NULL); + return; + } + + load_content_animation_content_name = strdup(content_name); + + if (remove_extension) + path_remove_extension(load_content_animation_content_name); + + /* Reset animation state */ + load_content_animation_icon_size = LOAD_CONTENT_ANIMATION_INITIAL_ICON_SIZE; + load_content_animation_icon_alpha = 0.0f; + load_content_animation_fade_alpha = 0.0f; + load_content_animation_final_fade_alpha = 0.0f; + + memcpy(load_content_animation_icon_color, icon_color, sizeof(load_content_animation_icon_color)); + + /* Setup the animation */ + entry.cb = NULL; + entry.easing_enum = EASING_OUT_QUAD; + entry.tag = (uintptr_t) NULL; + entry.userdata = NULL; + + /* Stage one: icon animation */ + /* Position */ + entry.duration = ANIMATION_LOAD_CONTENT_DURATION; + entry.subject = &load_content_animation_icon_size; + entry.target_value = LOAD_CONTENT_ANIMATION_TARGET_ICON_SIZE; + + menu_animation_push(&entry); + + /* Alpha */ + entry.subject = &load_content_animation_icon_alpha; + entry.target_value = 1.0f; + + menu_animation_push(&entry); + timing += entry.duration; + + /* Stage two: backdrop + text */ + entry.duration = ANIMATION_LOAD_CONTENT_DURATION*1.5; + entry.subject = &load_content_animation_fade_alpha; + entry.target_value = 1.0f; + + menu_animation_push_delayed(timing, &entry); + timing += entry.duration; + + /* Stage three: wait then color transition */ + timing += ANIMATION_LOAD_CONTENT_DURATION*1.5; + + entry.duration = ANIMATION_LOAD_CONTENT_DURATION*3; + + for (i = 0; i < 16; i++) + { + if (i == 3 || i == 7 || i == 11 || i == 15) + continue; + + entry.subject = &load_content_animation_icon_color[i]; + entry.target_value = menu_widgets_pure_white[i]; + + menu_animation_push_delayed(timing, &entry); + } + + timing += entry.duration; + + /* Stage four: wait then make everything disappear */ + timing += ANIMATION_LOAD_CONTENT_DURATION*2; + + entry.duration = ANIMATION_LOAD_CONTENT_DURATION*1.5; + entry.subject = &load_content_animation_final_fade_alpha; + entry.target_value = 1.0f; + + menu_animation_push_delayed(timing, &entry); + timing += entry.duration; + + /* Setup end */ + timer_entry.cb = menu_widgets_end_load_content_animation; + timer_entry.duration = timing; + timer_entry.userdata = NULL; + + menu_timer_start(&load_content_animation_end_timer, &timer_entry); + + /* Draw all the things */ + load_content_animation_running = true; +} + +bool menu_widgets_ready(void) +{ + return menu_widgets_inited; +} \ No newline at end of file diff --git a/menu/widgets/menu_widgets.h b/menu/widgets/menu_widgets.h index fdec3063e9..bf370fc9c6 100644 --- a/menu/widgets/menu_widgets.h +++ b/menu/widgets/menu_widgets.h @@ -21,4 +21,59 @@ #include #include -#endif +#define DEFAULT_BACKDROP 0.75f + +#define MSG_QUEUE_PENDING_MAX 32 +#define MSG_QUEUE_ONSCREEN_MAX 4 + +#define MSG_QUEUE_ANIMATION_DURATION 330 +#define VOLUME_DURATION 3000 +#define SCREENSHOT_DURATION_IN 66 +#define SCREENSHOT_DURATION_OUT SCREENSHOT_DURATION_IN*10 +#define SCREENSHOT_NOTIFICATION_DURATION 4000 +#define TASK_FINISHED_DURATION 3000 +#define HOURGLASS_INTERVAL 5000 +#define HOURGLASS_DURATION 1000 + +void menu_widgets_init(bool video_is_threaded); +void menu_widgets_free(void); +bool menu_widgets_ready(void); + +bool menu_widgets_msg_queue_push(const char *msg, + unsigned duration, + char *title, + enum message_queue_icon icon, enum message_queue_category category, + unsigned prio, bool flush); + +bool menu_widgets_volume_update_and_show(void); + +bool menu_widgets_set_fps_text(char *fps_text); + +void menu_widgets_iterate(void); + +bool menu_widgets_set_paused(bool is_paused); +bool menu_widgets_set_fast_forward(bool is_fast_forward); +bool menu_widgets_set_rewind(bool is_rewind); + +bool menu_widgets_task_msg_queue_push(retro_task_t *task, + const char *msg, + unsigned prio, unsigned duration, + bool flush); + +void menu_widgets_take_screenshot(void); + +void menu_widgets_screenshot_taken(const char *shotname, const char *filename); + +void menu_widgets_start_load_content_animation(const char *content_name, bool remove_extension); +void menu_widgets_cleanup_load_content_animation(void); + +void menu_widgets_context_reset(bool is_threaded); + +void menu_widgets_context_destroy(void); + +/* All the functions below should be called in + * the video driver - once they are all added, set + * enable_menu_widgets to true for that driver */ +void menu_widgets_frame(video_frame_info_t *video_info); + +#endif \ No newline at end of file diff --git a/retroarch.c b/retroarch.c index 99115a14cb..9b846922d9 100644 --- a/retroarch.c +++ b/retroarch.c @@ -64,6 +64,9 @@ #include "menu/menu_input.h" #include "menu/widgets/menu_dialog.h" #include "menu/widgets/menu_input_dialog.h" +#ifdef HAVE_MENU_WIDGETS +#include "menu/widgets/menu_widgets.h" +#endif #endif #ifdef HAVE_CHEEVOS @@ -2531,12 +2534,14 @@ global_t *global_get_ptr(void) return &g_extern; } -void runloop_task_msg_queue_push(retro_task_t *task, - const char *msg, +void runloop_task_msg_queue_push(retro_task_t *task, const char *msg, unsigned prio, unsigned duration, bool flush) { - runloop_msg_queue_push(msg, prio, duration, flush, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); +#if defined(HAVE_MENU) && defined(HAVE_MENU_WIDGETS) + if (!video_driver_has_widgets() || !menu_widgets_task_msg_queue_push(task, msg, prio, duration, flush)) +#endif + runloop_msg_queue_push(msg, prio, duration, flush, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); } void runloop_msg_queue_push(const char *msg, @@ -2547,6 +2552,12 @@ void runloop_msg_queue_push(const char *msg, { runloop_ctx_msg_info_t msg_info; +#if defined(HAVE_MENU) && defined(HAVE_MENU_WIDGETS) + /* People have 60FPS in mind when they use runloop_msg_queue_push */ + if (video_driver_has_widgets() && menu_widgets_msg_queue_push(msg, duration / 60 * 1000, title, icon, category, prio, flush)) + return; +#endif + #ifdef HAVE_THREADS runloop_msg_queue_lock(); #endif @@ -2924,6 +2935,10 @@ static enum runloop_state runloop_check_state( #if defined(HAVE_MENU) menu_animation_update(); +#ifdef HAVE_MENU_WIDGETS + menu_widgets_iterate(); +#endif + if (menu_is_alive) { enum menu_action action;