mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-24 16:39:43 +00:00
775c272029
* task_screenshot: fix hang with menu widgets * word_wrap: add max_lines parameter * vulkan: better instance creation sanity check
2397 lines
72 KiB
C
2397 lines
72 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2014-2017 - Jean-André Santoni
|
|
* Copyright (C) 2015-2018 - Andre Leiradella
|
|
* Copyright (C) 2018-2019 - natinusala
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "menu_widgets.h"
|
|
|
|
#include "../../verbosity.h"
|
|
#include "../../retroarch.h"
|
|
#include "../../configuration.h"
|
|
#include "../../msg_hash.h"
|
|
|
|
#include "../../tasks/task_content.h"
|
|
#include "../../ui/ui_companion_driver.h"
|
|
|
|
#include "../menu_driver.h"
|
|
#include "../menu_animation.h"
|
|
|
|
#include "../../gfx/font_driver.h"
|
|
|
|
#include <lists/file_list.h>
|
|
#include <queues/fifo_queue.h>
|
|
#include <file/file_path.h>
|
|
#include <streams/file_stream.h>
|
|
#include <formats/image.h>
|
|
#include <string/stdstring.h>
|
|
|
|
#ifndef PI
|
|
#define PI 3.14159265359f
|
|
#endif
|
|
|
|
#ifndef max
|
|
#define max(x, y) x >= y ? x : y
|
|
#endif
|
|
|
|
/* 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,
|
|
};
|
|
|
|
/* Generic message */
|
|
#define GENERIC_MESSAGE_SIZE 256
|
|
static char generic_message[GENERIC_MESSAGE_SIZE] = {'\0'};
|
|
|
|
/* Achievement notification */
|
|
static char *cheevo_title = NULL;
|
|
static menu_texture_item cheevo_badge = 0;
|
|
static float cheevo_unfold = 0.0f;
|
|
|
|
static menu_timer_t cheevo_timer;
|
|
|
|
static float cheevo_y = 0.0f;
|
|
static unsigned cheevo_width = 0;
|
|
static unsigned cheevo_height = 0;
|
|
|
|
/* 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_ACHIEVEMENT,
|
|
|
|
MENU_WIDGETS_ICON_LAST
|
|
};
|
|
|
|
static const 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",
|
|
|
|
"menu_achievements.png"
|
|
};
|
|
|
|
static menu_texture_item menu_widgets_icons_textures[MENU_WIDGETS_ICON_LAST] = {0};
|
|
|
|
/* 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;
|
|
|
|
/* Generic message */
|
|
static unsigned generic_message_height;
|
|
static menu_timer_t generic_message_timer;
|
|
static float generic_message_alpha = 0.0f;
|
|
|
|
/* Metrics */
|
|
static unsigned simple_widget_padding;
|
|
static unsigned simple_widget_height;
|
|
static unsigned glyph_width;
|
|
static unsigned line_height;
|
|
|
|
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 && 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)
|
|
{
|
|
const char *title = msg;
|
|
|
|
msg_widget = (menu_widget_msg_t*)calloc(1, sizeof(*msg_widget));
|
|
|
|
if (task)
|
|
title = task->title;
|
|
|
|
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, 2);
|
|
|
|
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)
|
|
{
|
|
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;
|
|
|
|
if (!task->alternative_look)
|
|
{
|
|
menu_animation_ctx_entry_t entry;
|
|
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
msg_widget_msg_transition_animation_done(msg_widget);
|
|
}
|
|
|
|
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 = (menu_widget_msg_t*)
|
|
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)
|
|
{
|
|
size_t 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 = (menu_widget_msg_t*)
|
|
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 = (menu_widget_msg_t*)
|
|
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)
|
|
{
|
|
menu_timer_ctx_entry_t timer;
|
|
if (msg_widget->expiration_timer_started)
|
|
return;
|
|
|
|
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_timer_ctx_entry_t timer;
|
|
menu_widget_msg_t *msg = (menu_widget_msg_t*) userdata;
|
|
|
|
msg->hourglass_rotation = 0.0f;
|
|
|
|
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)
|
|
{
|
|
size_t 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 = (menu_widget_msg_t*)
|
|
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_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 = 0.0f;
|
|
|
|
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)
|
|
{
|
|
size_t 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;
|
|
|
|
/* Generic message */
|
|
if (generic_message_alpha > 0.0f)
|
|
{
|
|
unsigned text_color = COLOR_TEXT_ALPHA(0xffffffff, (unsigned)(generic_message_alpha*255.0f));
|
|
menu_display_set_alpha(menu_widgets_backdrop_orig, generic_message_alpha);
|
|
|
|
menu_display_draw_quad(video_info,
|
|
0, video_info->height-generic_message_height,
|
|
video_info->width, generic_message_height,
|
|
video_info->width, video_info->height,
|
|
menu_widgets_backdrop_orig);
|
|
|
|
menu_display_draw_text(font_regular, generic_message,
|
|
video_info->width/2,
|
|
video_info->height - generic_message_height/2 + line_height/4,
|
|
video_info->width, video_info->height,
|
|
text_color, TEXT_ALIGN_CENTER,
|
|
1, false, 0, false);
|
|
}
|
|
|
|
/* 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
|
|
);
|
|
}
|
|
|
|
/* Achievement notification */
|
|
if (cheevo_title)
|
|
{
|
|
unsigned unfold_offet = ((1.0f-cheevo_unfold) * cheevo_width/2);
|
|
|
|
menu_display_set_alpha(menu_widgets_backdrop_orig, DEFAULT_BACKDROP);
|
|
menu_display_set_alpha(menu_widgets_pure_white, 1.0f);
|
|
|
|
/* Default icon */
|
|
if (!cheevo_badge)
|
|
{
|
|
/* Backdrop */
|
|
menu_display_draw_quad(video_info,
|
|
0, (int)cheevo_y,
|
|
cheevo_height, cheevo_height,
|
|
video_info->width, video_info->height,
|
|
menu_widgets_backdrop_orig);
|
|
|
|
/* Icon */
|
|
if (menu_widgets_icons_textures[MENU_WIDGETS_ICON_ACHIEVEMENT])
|
|
{
|
|
menu_display_blend_begin(video_info);
|
|
menu_widgets_draw_icon(video_info,
|
|
cheevo_height, cheevo_height,
|
|
menu_widgets_icons_textures[MENU_WIDGETS_ICON_ACHIEVEMENT], 0, cheevo_y,
|
|
video_info->width, video_info->height, 0, 1, menu_widgets_pure_white);
|
|
menu_display_blend_end(video_info);
|
|
}
|
|
}
|
|
/* Badge */
|
|
else
|
|
{
|
|
menu_widgets_draw_icon(video_info,
|
|
cheevo_height, cheevo_height,
|
|
cheevo_badge, 0, cheevo_y,
|
|
video_info->width, video_info->height, 0, 1, menu_widgets_pure_white);
|
|
}
|
|
|
|
if (cheevo_unfold != 1.0f)
|
|
{
|
|
menu_display_scissor_begin(video_info,
|
|
cheevo_height, 0,
|
|
(unsigned)((float)(cheevo_width) * cheevo_unfold), cheevo_height);
|
|
}
|
|
|
|
/* Backdrop */
|
|
menu_display_draw_quad(video_info,
|
|
cheevo_height, (int)cheevo_y,
|
|
cheevo_width, cheevo_height,
|
|
video_info->width, video_info->height,
|
|
menu_widgets_backdrop_orig);
|
|
|
|
/* Title */
|
|
menu_display_draw_text(font_regular,
|
|
msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED),
|
|
cheevo_height + simple_widget_padding - unfold_offet, settings->floats.video_font_size * 1.9f + cheevo_y,
|
|
video_info->width, video_info->height,
|
|
text_color_faint,
|
|
TEXT_ALIGN_LEFT,
|
|
1, false, 0, true
|
|
);
|
|
|
|
/* Title */
|
|
|
|
/* TODO: is a ticker necessary ? */
|
|
|
|
menu_display_draw_text(font_regular,
|
|
cheevo_title,
|
|
cheevo_height + simple_widget_padding - unfold_offet, settings->floats.video_font_size * 2.9f + cheevo_y,
|
|
video_info->width, video_info->height,
|
|
text_color_info,
|
|
TEXT_ALIGN_LEFT,
|
|
1, false, 0, true
|
|
);
|
|
|
|
if (cheevo_unfold != 1.0f)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* 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 = (menu_widget_msg_t*)
|
|
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)
|
|
{
|
|
const 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;
|
|
|
|
if (!menu_widgets_inited)
|
|
return;
|
|
|
|
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);
|
|
line_height = font_driver_get_line_height(font_regular, 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;
|
|
|
|
generic_message_height = settings->floats.video_font_size * 2;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void menu_widgets_achievement_free(void *userdata)
|
|
{
|
|
if (cheevo_title)
|
|
{
|
|
free(cheevo_title);
|
|
cheevo_title = NULL;
|
|
}
|
|
|
|
if (cheevo_badge)
|
|
{
|
|
video_driver_texture_unload(&cheevo_badge);
|
|
cheevo_badge = 0;
|
|
}
|
|
}
|
|
|
|
void menu_widgets_free(void)
|
|
{
|
|
size_t i;
|
|
|
|
if (!menu_widgets_inited)
|
|
return;
|
|
|
|
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 = (menu_widget_msg_t*)
|
|
file_list_get_userdata_at_offset(current_msgs, i);
|
|
|
|
menu_widgets_msg_queue_free(msg, false);
|
|
}
|
|
file_list_free(current_msgs);
|
|
}
|
|
|
|
/* Achievement notification */
|
|
menu_widgets_achievement_free(NULL);
|
|
|
|
/* Screenshot texture */
|
|
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;
|
|
|
|
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 = NULL;
|
|
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);
|
|
}
|
|
|
|
static void menu_widgets_play_screenshot_flash(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);
|
|
}
|
|
|
|
void menu_widgets_screenshot_taken(const char *shotname, const char *filename)
|
|
{
|
|
if (!menu_widgets_inited)
|
|
return;
|
|
|
|
menu_widgets_play_screenshot_flash();
|
|
strlcpy(screenshot_filename, filename, sizeof(screenshot_filename));
|
|
strlcpy(screenshot_shotname, shotname, sizeof(screenshot_shotname));
|
|
}
|
|
|
|
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, (enum message_queue_icon)MESSAGE_QUEUE_CATEGORY_INFO, (enum message_queue_category)MESSAGE_QUEUE_ICON_DEFAULT, prio, flush);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void menu_widgets_end_load_content_animation(void *userdata)
|
|
{
|
|
#if 0
|
|
task_load_content_resume(); /* TODO: Restore that */
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void menu_widgets_achievement_dismiss(void *userdata)
|
|
{
|
|
menu_animation_ctx_entry_t entry;
|
|
|
|
/* Slide up animation */
|
|
entry.cb = menu_widgets_achievement_free;
|
|
entry.duration = MSG_QUEUE_ANIMATION_DURATION;
|
|
entry.easing_enum = EASING_OUT_QUAD;
|
|
entry.subject = &cheevo_y;
|
|
entry.tag = generic_tag;
|
|
entry.target_value = (float)(-(int)(cheevo_height));
|
|
entry.userdata = NULL;
|
|
|
|
menu_animation_push(&entry);
|
|
}
|
|
|
|
static void menu_widgets_achievement_fold(void *userdata)
|
|
{
|
|
menu_animation_ctx_entry_t entry;
|
|
|
|
/* Fold */
|
|
entry.cb = menu_widgets_achievement_dismiss;
|
|
entry.duration = MSG_QUEUE_ANIMATION_DURATION;
|
|
entry.easing_enum = EASING_OUT_QUAD;
|
|
entry.subject = &cheevo_unfold;
|
|
entry.tag = generic_tag;
|
|
entry.target_value = 0.0f;
|
|
entry.userdata = NULL;
|
|
|
|
menu_animation_push(&entry);
|
|
}
|
|
|
|
static void menu_widgets_achievement_unfold(void *userdata)
|
|
{
|
|
menu_animation_ctx_entry_t entry;
|
|
menu_timer_ctx_entry_t timer;
|
|
|
|
/* Unfold */
|
|
entry.cb = NULL;
|
|
entry.duration = MSG_QUEUE_ANIMATION_DURATION;
|
|
entry.easing_enum = EASING_OUT_QUAD;
|
|
entry.subject = &cheevo_unfold;
|
|
entry.tag = generic_tag;
|
|
entry.target_value = 1.0f;
|
|
entry.userdata = NULL;
|
|
|
|
menu_animation_push(&entry);
|
|
|
|
/* Wait before dismissing */
|
|
timer.cb = menu_widgets_achievement_fold;
|
|
timer.duration = MSG_QUEUE_ANIMATION_DURATION + CHEEVO_NOTIFICATION_DURATION;
|
|
timer.userdata = NULL;
|
|
|
|
menu_timer_start(&cheevo_timer, &timer);
|
|
}
|
|
|
|
static void menu_widgets_start_achievement_notification()
|
|
{
|
|
settings_t *settings = config_get_ptr();
|
|
menu_animation_ctx_entry_t entry;
|
|
cheevo_height = settings->floats.video_font_size * 4;
|
|
cheevo_width = max(
|
|
font_driver_get_message_width(font_regular, msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), 0, 1),
|
|
font_driver_get_message_width(font_regular, cheevo_title, 0, 1)
|
|
);
|
|
cheevo_width += simple_widget_padding * 2;
|
|
cheevo_y = (float)(-(int)cheevo_height);
|
|
cheevo_unfold = 0.0f;
|
|
|
|
/* Slide down animation */
|
|
entry.cb = menu_widgets_achievement_unfold;
|
|
entry.duration = MSG_QUEUE_ANIMATION_DURATION;
|
|
entry.easing_enum = EASING_OUT_QUAD;
|
|
entry.subject = &cheevo_y;
|
|
entry.tag = generic_tag;
|
|
entry.target_value = 0.0f;
|
|
entry.userdata = NULL;
|
|
|
|
menu_animation_push(&entry);
|
|
}
|
|
|
|
static void menu_widgets_get_badge_texture(menu_texture_item *tex, const char *badge)
|
|
{
|
|
char badge_file[16];
|
|
char fullpath[PATH_MAX_LENGTH];
|
|
|
|
if (!badge)
|
|
{
|
|
*tex = 0;
|
|
return;
|
|
}
|
|
|
|
snprintf(badge_file, sizeof(badge_file), "%s.png", badge);
|
|
|
|
fill_pathname_application_special(fullpath,
|
|
PATH_MAX_LENGTH * sizeof(char),
|
|
APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
|
|
|
|
menu_display_reset_textures_list(badge_file, fullpath,
|
|
tex, TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL);
|
|
}
|
|
|
|
bool menu_widgets_push_achievement(const char *title, const char *badge)
|
|
{
|
|
if (!menu_widgets_inited)
|
|
return false;
|
|
|
|
menu_widgets_achievement_free(NULL);
|
|
|
|
/* TODO: Make a queue of notifications to display */
|
|
|
|
cheevo_title = strdup(title);
|
|
menu_widgets_get_badge_texture(&cheevo_badge, badge);
|
|
|
|
menu_widgets_start_achievement_notification();
|
|
|
|
return true;
|
|
}
|
|
|
|
static void menu_widgets_generic_message_fadeout(void *userdata)
|
|
{
|
|
menu_animation_ctx_tag tag = (uintptr_t) &generic_message_timer;
|
|
menu_animation_ctx_entry_t entry;
|
|
|
|
/* Start fade out animation */
|
|
entry.cb = NULL;
|
|
entry.duration = MSG_QUEUE_ANIMATION_DURATION;
|
|
entry.easing_enum = EASING_OUT_QUAD;
|
|
entry.subject = &generic_message_alpha;
|
|
entry.tag = tag;
|
|
entry.target_value = 0.0f;
|
|
entry.userdata = NULL;
|
|
|
|
menu_animation_push(&entry);
|
|
}
|
|
|
|
bool menu_widgets_set_message(char *msg)
|
|
{
|
|
menu_animation_ctx_tag tag = (uintptr_t) &generic_message_timer;
|
|
menu_timer_ctx_entry_t timer;
|
|
|
|
if (!menu_widgets_inited)
|
|
return false;
|
|
|
|
snprintf(generic_message, GENERIC_MESSAGE_SIZE, "%s", msg);
|
|
|
|
generic_message_alpha = DEFAULT_BACKDROP;
|
|
|
|
/* Kill and restart the timer / animation */
|
|
menu_timer_kill(&generic_message_timer);
|
|
menu_animation_kill_by_tag(&tag);
|
|
|
|
timer.cb = menu_widgets_generic_message_fadeout;
|
|
timer.duration = GENERIC_MESSAGE_DURATION;
|
|
timer.userdata = NULL;
|
|
|
|
menu_timer_start(&generic_message_timer, &timer);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool menu_widgets_ready(void)
|
|
{
|
|
return menu_widgets_inited;
|
|
}
|