diff --git a/Makefile.common b/Makefile.common index 5f0ee6ab53..5e102ce1dc 100644 --- a/Makefile.common +++ b/Makefile.common @@ -849,6 +849,9 @@ ifeq ($(HAVE_GFX_WIDGETS), 1) gfx/widgets/gfx_widget_volume.o \ gfx/widgets/gfx_widget_generic_message.o \ gfx/widgets/gfx_widget_libretro_message.o + ifeq ($(HAVE_CHEEVOS), 1) + OBJ += gfx/widgets/gfx_widget_achievement_popup.o + endif endif ifeq ($(HAVE_OVERLAY), 1) diff --git a/gfx/gfx_widgets.c b/gfx/gfx_widgets.c index 875590401e..7cf538f539 100644 --- a/gfx/gfx_widgets.c +++ b/gfx/gfx_widgets.c @@ -32,10 +32,6 @@ #include "../tasks/task_content.h" -#ifdef HAVE_CHEEVOS -#include "../cheevos/badges.h" -#endif - #ifdef HAVE_THREADS #define SLOCK_LOCK(x) slock_lock(x) #define SLOCK_UNLOCK(x) slock_unlock(x) @@ -112,10 +108,6 @@ static const char }; /* Forward declarations */ -#ifdef HAVE_CHEEVOS -static void gfx_widgets_start_achievement_notification( - dispgfx_widget_t *p_dispwidget); -#endif #ifdef HAVE_MENU bool menu_driver_get_load_content_animation_data( uintptr_t *icon, char **playlist_name); @@ -206,6 +198,9 @@ size_t gfx_widgets_get_msg_queue_size(void *data) const static gfx_widget_t* const widgets[] = { &gfx_widget_screenshot, &gfx_widget_volume, +#ifdef HAVE_CHEEVOS + &gfx_widget_achievement_popup, +#endif &gfx_widget_generic_message, &gfx_widget_libretro_message }; @@ -1402,9 +1397,6 @@ static void INLINE gfx_widgets_font_unbind(gfx_widget_font_data_t *font_data) void gfx_widgets_frame(void *data) { size_t i; -#ifdef HAVE_CHEEVOS - int scissor_me_timbers = 0; -#endif dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)dispwidget_get_ptr(); video_frame_info_t *video_info = (video_frame_info_t*)data; bool framecount_show = video_info->framecount_show; @@ -1506,143 +1498,6 @@ void gfx_widgets_frame(void *data) } #endif -#ifdef HAVE_CHEEVOS - /* Achievement notification */ - if (p_dispwidget->cheevo_popup_queue_read_index >= 0 && - p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].title) - { - SLOCK_LOCK(p_dispwidget->cheevo_popup_queue_lock); - - if (p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].title) - { - unsigned unfold_offet = ((1.0f - p_dispwidget->cheevo_unfold) - * p_dispwidget->cheevo_width / 2); - - gfx_display_set_alpha(gfx_widgets_backdrop_orig, DEFAULT_BACKDROP); - gfx_display_set_alpha(gfx_widgets_pure_white, 1.0f); - - /* Default icon */ - if (!p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].badge) - { - /* Backdrop */ - gfx_display_draw_quad(userdata, - video_width, video_height, - 0, (int)p_dispwidget->cheevo_y, - p_dispwidget->cheevo_height, - p_dispwidget->cheevo_height, - video_width, video_height, - gfx_widgets_backdrop_orig); - - /* Icon */ - if (p_dispwidget->gfx_widgets_icons_textures[ - MENU_WIDGETS_ICON_ACHIEVEMENT]) - { - gfx_display_blend_begin(userdata); - gfx_widgets_draw_icon( - userdata, - video_width, - video_height, - p_dispwidget->cheevo_height, - p_dispwidget->cheevo_height, - p_dispwidget->gfx_widgets_icons_textures[ - MENU_WIDGETS_ICON_ACHIEVEMENT], - 0, - p_dispwidget->cheevo_y, - video_width, video_height, 0, 1, gfx_widgets_pure_white); - gfx_display_blend_end(userdata); - } - } - /* Badge */ - else - { - gfx_widgets_draw_icon( - userdata, - video_width, - video_height, - p_dispwidget->cheevo_height, - p_dispwidget->cheevo_height, - p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].badge, - 0, - p_dispwidget->cheevo_y, - video_width, - video_height, - 0, - 1, - gfx_widgets_pure_white); - } - - /* I _think_ cheevo_unfold changes in another thread */ - scissor_me_timbers = (fabs(p_dispwidget->cheevo_unfold - 1.0f) > 0.01); - if (scissor_me_timbers) - gfx_display_scissor_begin(userdata, - video_width, - video_height, - p_dispwidget->cheevo_height, - 0, - (unsigned)((float)(p_dispwidget->cheevo_width) - * p_dispwidget->cheevo_unfold), - p_dispwidget->cheevo_height); - - /* Backdrop */ - gfx_display_draw_quad( - userdata, - video_width, - video_height, - p_dispwidget->cheevo_height, - (int)p_dispwidget->cheevo_y, - p_dispwidget->cheevo_width, - p_dispwidget->cheevo_height, - video_width, - video_height, - gfx_widgets_backdrop_orig); - - /* Title */ - gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.regular, - msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), - p_dispwidget->cheevo_height - + p_dispwidget->simple_widget_padding - unfold_offet, - p_dispwidget->cheevo_y - + p_dispwidget->gfx_widget_fonts.regular.line_height - + p_dispwidget->gfx_widget_fonts.regular.line_ascender, - video_width, video_height, - TEXT_COLOR_FAINT, - TEXT_ALIGN_LEFT, - true); - - /* Cheevo name */ - - /* TODO: is a ticker necessary ? */ - gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.regular, - p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].title, - p_dispwidget->cheevo_height - + p_dispwidget->simple_widget_padding - unfold_offet, - p_dispwidget->cheevo_y - + p_dispwidget->cheevo_height - - p_dispwidget->gfx_widget_fonts.regular.line_height - - p_dispwidget->gfx_widget_fonts.regular.line_descender, - video_width, video_height, - TEXT_COLOR_INFO, - TEXT_ALIGN_LEFT, - true); - - if (scissor_me_timbers) - { - gfx_widgets_flush_text(video_width, video_height, - &p_dispwidget->gfx_widget_fonts.regular); - gfx_display_scissor_end(userdata, - video_width, video_height); - } - } - - SLOCK_UNLOCK(p_dispwidget->cheevo_popup_queue_lock); - } -#endif - /* FPS Counter */ if ( fps_show || framecount_show @@ -1811,9 +1666,6 @@ bool gfx_widgets_init(uintptr_t widgets_active_ptr, { dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*) dispwidget_get_ptr(); -#ifdef HAVE_CHEEVOS - p_dispwidget->cheevo_popup_queue_read_index = -1; -#endif p_dispwidget->divider_width_1px = 1; p_dispwidget->gfx_widgets_generic_tag = (uintptr_t)widgets_active_ptr; @@ -2189,50 +2041,6 @@ static void gfx_widgets_context_destroy(dispgfx_widget_t *p_dispwidget) gfx_widgets_font_free(&p_dispwidget->gfx_widget_fonts.msg_queue); } -#ifdef HAVE_CHEEVOS -static void gfx_widgets_achievement_free_current(dispgfx_widget_t *p_dispwidget) -{ - if (p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].title) - { - free(p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].title); - p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].title = NULL; - } - - if (p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].badge) - { - video_driver_texture_unload(&p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].badge); - p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].badge = 0; - } - - p_dispwidget->cheevo_popup_queue_read_index = ( - p_dispwidget->cheevo_popup_queue_read_index + 1) % CHEEVO_QUEUE_SIZE; -} - -static void gfx_widgets_achievement_next(void* userdata) -{ - dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)dispwidget_get_ptr(); - SLOCK_LOCK(p_dispwidget->cheevo_popup_queue_lock); - - if (p_dispwidget->cheevo_popup_queue_read_index >= 0) - { - gfx_widgets_achievement_free_current(p_dispwidget); - - /* start the next popup (if present) */ - if (p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].title) - gfx_widgets_start_achievement_notification(p_dispwidget); - } - - SLOCK_UNLOCK(p_dispwidget->cheevo_popup_queue_lock); -} -#endif - static void gfx_widgets_free(dispgfx_widget_t *p_dispwidget) { size_t i; @@ -2310,24 +2118,6 @@ static void gfx_widgets_free(dispgfx_widget_t *p_dispwidget) p_dispwidget->msg_queue_tasks_count = 0; -#ifdef HAVE_CHEEVOS - /* Achievement notification */ - if (p_dispwidget->cheevo_popup_queue_read_index >= 0) - { - SLOCK_LOCK(p_dispwidget->cheevo_popup_queue_lock); - - while (p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].title) - gfx_widgets_achievement_free_current(p_dispwidget); - - SLOCK_UNLOCK(p_dispwidget->cheevo_popup_queue_lock); - } -#ifdef HAVE_THREADS - slock_free(p_dispwidget->cheevo_popup_queue_lock); - p_dispwidget->cheevo_popup_queue_lock = NULL; -#endif -#endif - /* Font */ video_coord_array_free( &p_dispwidget->gfx_widget_fonts.regular.raster_block.carr); @@ -2511,145 +2301,3 @@ void gfx_widgets_start_load_content_animation( p_dispwidget->load_content_animation_running = true; #endif } - -#ifdef HAVE_CHEEVOS -static void gfx_widgets_achievement_dismiss(void *userdata) -{ - gfx_animation_ctx_entry_t entry; - dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)dispwidget_get_ptr(); - - /* Slide up animation */ - entry.cb = gfx_widgets_achievement_next; - entry.duration = MSG_QUEUE_ANIMATION_DURATION; - entry.easing_enum = EASING_OUT_QUAD; - entry.subject = &p_dispwidget->cheevo_y; - entry.tag = p_dispwidget->gfx_widgets_generic_tag; - entry.target_value = (float)(-(int)(p_dispwidget->cheevo_height)); - entry.userdata = NULL; - - gfx_animation_push(&entry); -} - -static void gfx_widgets_achievement_fold(void *userdata) -{ - gfx_animation_ctx_entry_t entry; - dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)dispwidget_get_ptr(); - - /* Fold */ - entry.cb = gfx_widgets_achievement_dismiss; - entry.duration = MSG_QUEUE_ANIMATION_DURATION; - entry.easing_enum = EASING_OUT_QUAD; - entry.subject = &p_dispwidget->cheevo_unfold; - entry.tag = p_dispwidget->gfx_widgets_generic_tag; - entry.target_value = 0.0f; - entry.userdata = NULL; - - gfx_animation_push(&entry); -} - -static void gfx_widgets_achievement_unfold(void *userdata) -{ - gfx_timer_ctx_entry_t timer; - gfx_animation_ctx_entry_t entry; - dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)dispwidget_get_ptr(); - - /* Unfold */ - entry.cb = NULL; - entry.duration = MSG_QUEUE_ANIMATION_DURATION; - entry.easing_enum = EASING_OUT_QUAD; - entry.subject = &p_dispwidget->cheevo_unfold; - entry.tag = p_dispwidget->gfx_widgets_generic_tag; - entry.target_value = 1.0f; - entry.userdata = NULL; - - gfx_animation_push(&entry); - - /* Wait before dismissing */ - timer.cb = gfx_widgets_achievement_fold; - timer.duration = MSG_QUEUE_ANIMATION_DURATION + CHEEVO_NOTIFICATION_DURATION; - timer.userdata = NULL; - - gfx_timer_start(&p_dispwidget->cheevo_timer, &timer); -} - -static void gfx_widgets_start_achievement_notification( - dispgfx_widget_t *p_dispwidget) -{ - gfx_animation_ctx_entry_t entry; - p_dispwidget->cheevo_height = - p_dispwidget->gfx_widget_fonts.regular.line_height * 4; - p_dispwidget->cheevo_width = MAX( - font_driver_get_message_width( - p_dispwidget->gfx_widget_fonts.regular.font, - msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), 0, 1), - font_driver_get_message_width( - p_dispwidget->gfx_widget_fonts.regular.font, - p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_read_index].title, 0, 1) - ); - p_dispwidget->cheevo_width += p_dispwidget->simple_widget_padding * 2; - p_dispwidget->cheevo_y = (float)(-(int)p_dispwidget->cheevo_height); - p_dispwidget->cheevo_unfold = 0.0f; - - /* Slide down animation */ - entry.cb = gfx_widgets_achievement_unfold; - entry.duration = MSG_QUEUE_ANIMATION_DURATION; - entry.easing_enum = EASING_OUT_QUAD; - entry.subject = &p_dispwidget->cheevo_y; - entry.tag = p_dispwidget->gfx_widgets_generic_tag; - entry.target_value = 0.0f; - entry.userdata = NULL; - - gfx_animation_push(&entry); -} - -void gfx_widgets_push_achievement(const char *title, const char *badge) -{ - dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)dispwidget_get_ptr(); - int start_notification = 1; - - /* important - this must be done outside the lock because it has the potential to need to - * lock the video thread, which may be waiting for the popup queue lock to render popups */ - uintptr_t badge_id = cheevos_get_badge_texture(badge, 0); - - if (p_dispwidget->cheevo_popup_queue_read_index < 0) - { - /* queue uninitialized */ - memset(&p_dispwidget->cheevo_popup_queue, 0, - sizeof(p_dispwidget->cheevo_popup_queue)); - p_dispwidget->cheevo_popup_queue_read_index = 0; - -#ifdef HAVE_THREADS - p_dispwidget->cheevo_popup_queue_lock = slock_new(); -#endif - } - - SLOCK_LOCK(p_dispwidget->cheevo_popup_queue_lock); - - if (p_dispwidget->cheevo_popup_queue_write_index - == p_dispwidget->cheevo_popup_queue_read_index) - { - if (p_dispwidget->cheevo_popup_queue[ - p_dispwidget->cheevo_popup_queue_write_index].title) - { - /* queue full */ - SLOCK_UNLOCK(p_dispwidget->cheevo_popup_queue_lock); - return; - } - - /* queue empty */ - } - else - start_notification = 0; /* notification already being displayed */ - - p_dispwidget->cheevo_popup_queue[p_dispwidget->cheevo_popup_queue_write_index].badge = badge_id; - p_dispwidget->cheevo_popup_queue[p_dispwidget->cheevo_popup_queue_write_index].title = strdup(title); - - p_dispwidget->cheevo_popup_queue_write_index = (p_dispwidget->cheevo_popup_queue_write_index + 1) % CHEEVO_QUEUE_SIZE; - - if (start_notification) - gfx_widgets_start_achievement_notification(p_dispwidget); - - SLOCK_UNLOCK(p_dispwidget->cheevo_popup_queue_lock); -} -#endif diff --git a/gfx/gfx_widgets.h b/gfx/gfx_widgets.h index cc4f91aef2..da1aa35746 100644 --- a/gfx/gfx_widgets.h +++ b/gfx/gfx_widgets.h @@ -37,7 +37,6 @@ #define MSG_QUEUE_ONSCREEN_MAX 4 #define MSG_QUEUE_ANIMATION_DURATION 330 -#define CHEEVO_NOTIFICATION_DURATION 4000 #define TASK_FINISHED_DURATION 3000 #define HOURGLASS_INTERVAL 5000 #define HOURGLASS_DURATION 1000 @@ -52,10 +51,6 @@ #endif #define TEXT_COLOR_FAINT 0x878787FF -#ifdef HAVE_CHEEVOS -#define CHEEVO_QUEUE_SIZE 8 -#endif - enum gfx_widgets_icon { MENU_WIDGETS_ICON_PAUSED = 0, @@ -155,19 +150,9 @@ typedef struct dispgfx_widget bool load_content_animation_running; #endif -#ifdef HAVE_CHEEVOS - int cheevo_popup_queue_read_index; -#endif #ifdef HAVE_TRANSLATE int ai_service_overlay_state; #endif -#ifdef HAVE_CHEEVOS - int cheevo_popup_queue_write_index; -#endif -#ifdef HAVE_CHEEVOS - float cheevo_unfold; - float cheevo_y; -#endif #ifdef HAVE_MENU float load_content_animation_icon_color[16]; float load_content_animation_icon_size; @@ -189,10 +174,7 @@ typedef struct dispgfx_widget unsigned msg_queue_kill; /* Count of messages bound to a task in current_msgs */ unsigned msg_queue_tasks_count; -#ifdef HAVE_CHEEVOS - unsigned cheevo_width; - unsigned cheevo_height; -#endif + unsigned simple_widget_padding; unsigned simple_widget_height; @@ -241,14 +223,6 @@ typedef struct dispgfx_widget #endif uintptr_t gfx_widgets_generic_tag; gfx_widget_fonts_t gfx_widget_fonts; -#ifdef HAVE_CHEEVOS -/* Achievement notification */ - cheevo_popup cheevo_popup_queue[CHEEVO_QUEUE_SIZE]; - gfx_timer_t cheevo_timer; -#ifdef HAVE_THREADS - slock_t* cheevo_popup_queue_lock; -#endif -#endif fifo_buffer_t *msg_queue; menu_widget_msg_t* current_msgs[MSG_QUEUE_ONSCREEN_MAX]; size_t current_msgs_size; @@ -296,6 +270,8 @@ struct gfx_widget /* called every frame * (on the video thread if threaded video is on) + * -- data is a video_frame_info_t + * -- userdata is a dispgfx_widget_t * -> draw the widget here */ void (*frame)(void* data, void *userdata); }; @@ -401,7 +377,9 @@ void gfx_widgets_start_load_content_animation( void gfx_widgets_cleanup_load_content_animation(void); +#ifdef HAVE_CHEEVOS void gfx_widgets_push_achievement(const char *title, const char *badge); +#endif /* Warning: not thread safe! */ void gfx_widget_set_message(char *message); @@ -427,5 +405,8 @@ extern const gfx_widget_t gfx_widget_volume; extern const gfx_widget_t gfx_widget_generic_message; extern const gfx_widget_t gfx_widget_libretro_message; +#ifdef HAVE_CHEEVOS +extern const gfx_widget_t gfx_widget_achievement_popup; +#endif #endif diff --git a/gfx/widgets/gfx_widget_achievement_popup.c b/gfx/widgets/gfx_widget_achievement_popup.c new file mode 100644 index 0000000000..968d74c64b --- /dev/null +++ b/gfx/widgets/gfx_widget_achievement_popup.c @@ -0,0 +1,414 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2014-2017 - Jean-André Santoni + * Copyright (C) 2015-2018 - Andre Leiradella + * Copyright (C) 2018-2020 - 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 . + */ + +#include "../gfx_display.h" +#include "../gfx_widgets.h" + +#include "../cheevos/badges.h" + +#ifdef HAVE_THREADS +#define SLOCK_LOCK(x) slock_lock(x) +#define SLOCK_UNLOCK(x) slock_unlock(x) +#else +#define SLOCK_LOCK(x) +#define SLOCK_UNLOCK(x) +#endif + +#define CHEEVO_NOTIFICATION_DURATION 4000 + +#define CHEEVO_QUEUE_SIZE 8 + +struct gfx_widget_achievement_popup_state +{ + gfx_timer_t timer; + float unfold; + float y; + + unsigned width; + unsigned height; + + cheevo_popup queue[CHEEVO_QUEUE_SIZE]; + int queue_read_index; + int queue_write_index; + +#ifdef HAVE_THREADS + slock_t* queue_lock; +#endif +}; + +typedef struct gfx_widget_achievement_popup_state gfx_widget_achievement_popup_state_t; + +static gfx_widget_achievement_popup_state_t p_w_achievement_popup_st; + +static gfx_widget_achievement_popup_state_t* gfx_widget_achievement_popup_get_ptr(void) +{ + return &p_w_achievement_popup_st; +} + +/* Forward declarations */ +static void gfx_widget_achievement_popup_start( + gfx_widget_achievement_popup_state_t* state); +static void gfx_widget_achievement_popup_free_current( + gfx_widget_achievement_popup_state_t* state); + +static bool gfx_widget_achievement_popup_init(bool video_is_threaded, bool fullscreen) +{ + gfx_widget_achievement_popup_state_t* state = gfx_widget_achievement_popup_get_ptr(); + memset(state, 0, sizeof(*state)); + + state->queue_read_index = -1; + + return true; +} + +static void gfx_widget_achievement_popup_free(void) +{ + gfx_widget_achievement_popup_state_t* state = gfx_widget_achievement_popup_get_ptr(); + + if (state->queue_read_index >= 0) + { + SLOCK_LOCK(state->queue_lock); + + while (state->queue[state->queue_read_index].title) + gfx_widget_achievement_popup_free_current(state); + + SLOCK_UNLOCK(state->queue_lock); + } + +#ifdef HAVE_THREADS + slock_free(state->queue_lock); + state->queue_lock = NULL; +#endif +} + +static void gfx_widget_achievement_popup_frame(void* data, void* userdata) +{ + gfx_widget_achievement_popup_state_t* state = gfx_widget_achievement_popup_get_ptr(); + + /* if there's nothing in the queue, just bail */ + if (state->queue_read_index < 0 || !state->queue[state->queue_read_index].title) + return; + + SLOCK_LOCK(state->queue_lock); + + if (state->queue[state->queue_read_index].title) + { + const video_frame_info_t* video_info = (const video_frame_info_t*)data; + const unsigned video_width = video_info->width; + const unsigned video_height = video_info->height; + + dispgfx_widget_t* p_dispwidget = (dispgfx_widget_t*)userdata; + const unsigned unfold_offet = ((1.0f - state->unfold) * state->width / 2); + + int scissor_me_timbers = 0; + + gfx_display_set_alpha(gfx_widgets_get_backdrop_orig(), DEFAULT_BACKDROP); + gfx_display_set_alpha(gfx_widgets_get_pure_white(), 1.0f); + + /* Default icon */ + if (!state->queue[state->queue_read_index].badge) + { + /* Backdrop */ + gfx_display_draw_quad(video_info->userdata, + video_width, video_height, + 0, (int)state->y, + state->height, + state->height, + video_width, video_height, + gfx_widgets_get_backdrop_orig()); + + /* Icon */ + if (p_dispwidget->gfx_widgets_icons_textures[MENU_WIDGETS_ICON_ACHIEVEMENT]) + { + gfx_display_blend_begin(userdata); + gfx_widgets_draw_icon( + video_info->userdata, + video_width, + video_height, + state->height, + state->height, + p_dispwidget->gfx_widgets_icons_textures[MENU_WIDGETS_ICON_ACHIEVEMENT], + 0, + state->y, + video_width, video_height, 0, 1, gfx_widgets_get_pure_white()); + gfx_display_blend_end(userdata); + } + } + /* Badge */ + else + { + gfx_widgets_draw_icon( + video_info->userdata, + video_width, + video_height, + state->height, + state->height, + state->queue[state->queue_read_index].badge, + 0, + state->y, + video_width, + video_height, + 0, + 1, + gfx_widgets_get_pure_white()); + } + + /* I _think_ state->unfold changes in another thread */ + scissor_me_timbers = (fabs(state->unfold - 1.0f) > 0.01); + if (scissor_me_timbers) + gfx_display_scissor_begin(video_info->userdata, + video_width, + video_height, + state->height, + 0, + (unsigned)((float)(state->width) * state->unfold), + state->height); + + /* Backdrop */ + gfx_display_draw_quad( + video_info->userdata, + video_width, + video_height, + state->height, + (int)state->y, + state->width, + state->height, + video_width, + video_height, + gfx_widgets_get_backdrop_orig()); + + /* Title */ + gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.regular, + msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), + state->height + p_dispwidget->simple_widget_padding - unfold_offet, + state->y + p_dispwidget->gfx_widget_fonts.regular.line_height + + p_dispwidget->gfx_widget_fonts.regular.line_ascender, + video_width, video_height, + TEXT_COLOR_FAINT, + TEXT_ALIGN_LEFT, + true); + + /* Cheevo name */ + + /* TODO: is a ticker necessary ? */ + gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.regular, + state->queue[state->queue_read_index].title, + state->height + p_dispwidget->simple_widget_padding - unfold_offet, + state->y + state->height + - p_dispwidget->gfx_widget_fonts.regular.line_height + - p_dispwidget->gfx_widget_fonts.regular.line_descender, + video_width, video_height, + TEXT_COLOR_INFO, + TEXT_ALIGN_LEFT, + true); + + if (scissor_me_timbers) + { + gfx_widgets_flush_text(video_width, video_height, + &p_dispwidget->gfx_widget_fonts.regular); + gfx_display_scissor_end(userdata, + video_width, video_height); + } + } + + SLOCK_UNLOCK(state->queue_lock); +} + +static void gfx_widget_achievement_popup_free_current(gfx_widget_achievement_popup_state_t* state) +{ + if (state->queue[state->queue_read_index].title) + { + free(state->queue[state->queue_read_index].title); + state->queue[state->queue_read_index].title = NULL; + } + + if (state->queue[state->queue_read_index].badge) + { + video_driver_texture_unload(&state->queue[state->queue_read_index].badge); + state->queue[state->queue_read_index].badge = 0; + } + + state->queue_read_index = (state->queue_read_index + 1) % ARRAY_SIZE(state->queue); +} + +static void gfx_widget_achievement_popup_next(void* userdata) +{ + gfx_widget_achievement_popup_state_t* state = gfx_widget_achievement_popup_get_ptr(); + + SLOCK_LOCK(state->queue_lock); + + if (state->queue_read_index >= 0) + { + gfx_widget_achievement_popup_free_current(state); + + /* start the next popup (if present) */ + if (state->queue[state->queue_read_index].title) + gfx_widget_achievement_popup_start(state); + } + + SLOCK_UNLOCK(state->queue_lock); +} + +static void gfx_widget_achievement_popup_dismiss(void *userdata) +{ + gfx_animation_ctx_entry_t entry; + const dispgfx_widget_t *p_dispwidget = (const dispgfx_widget_t*)dispwidget_get_ptr(); + gfx_widget_achievement_popup_state_t* state = gfx_widget_achievement_popup_get_ptr(); + + /* Slide up animation */ + entry.cb = gfx_widget_achievement_popup_next; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &state->y; + entry.tag = p_dispwidget->gfx_widgets_generic_tag; + entry.target_value = (float)(-(int)(state->height)); + entry.userdata = NULL; + + gfx_animation_push(&entry); +} + +static void gfx_widget_achievement_popup_fold(void *userdata) +{ + gfx_animation_ctx_entry_t entry; + const dispgfx_widget_t *p_dispwidget = (const dispgfx_widget_t*)dispwidget_get_ptr(); + gfx_widget_achievement_popup_state_t* state = gfx_widget_achievement_popup_get_ptr(); + + /* Fold */ + entry.cb = gfx_widget_achievement_popup_dismiss; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &state->unfold; + entry.tag = p_dispwidget->gfx_widgets_generic_tag; + entry.target_value = 0.0f; + entry.userdata = NULL; + + gfx_animation_push(&entry); +} + +static void gfx_widget_achievement_popup_unfold(void *userdata) +{ + gfx_timer_ctx_entry_t timer; + gfx_animation_ctx_entry_t entry; + const dispgfx_widget_t *p_dispwidget = (const dispgfx_widget_t*)dispwidget_get_ptr(); + gfx_widget_achievement_popup_state_t* state = gfx_widget_achievement_popup_get_ptr(); + + /* Unfold */ + entry.cb = NULL; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &state->unfold; + entry.tag = p_dispwidget->gfx_widgets_generic_tag; + entry.target_value = 1.0f; + entry.userdata = NULL; + + gfx_animation_push(&entry); + + /* Wait before dismissing */ + timer.cb = gfx_widget_achievement_popup_fold; + timer.duration = MSG_QUEUE_ANIMATION_DURATION + CHEEVO_NOTIFICATION_DURATION; + timer.userdata = NULL; + + gfx_timer_start(&state->timer, &timer); +} + +static void gfx_widget_achievement_popup_start( + gfx_widget_achievement_popup_state_t* state) +{ + const dispgfx_widget_t* p_dispwidget = (const dispgfx_widget_t*)dispwidget_get_ptr(); + gfx_animation_ctx_entry_t entry; + + state->height = p_dispwidget->gfx_widget_fonts.regular.line_height * 4; + state->width = MAX( + font_driver_get_message_width( + p_dispwidget->gfx_widget_fonts.regular.font, + msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), 0, 1), + font_driver_get_message_width( + p_dispwidget->gfx_widget_fonts.regular.font, + state->queue[state->queue_read_index].title, 0, 1) + ); + state->width += p_dispwidget->simple_widget_padding * 2; + state->y = (float)(-(int)state->height); + state->unfold = 0.0f; + + /* Slide down animation */ + entry.cb = gfx_widget_achievement_popup_unfold; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &state->y; + entry.tag = p_dispwidget->gfx_widgets_generic_tag; + entry.target_value = 0.0f; + entry.userdata = NULL; + + gfx_animation_push(&entry); +} + +void gfx_widgets_push_achievement(const char *title, const char *badge) +{ + gfx_widget_achievement_popup_state_t* state = gfx_widget_achievement_popup_get_ptr(); + int start_notification = 1; + + /* important - this must be done outside the lock because it has the potential to need to + * lock the video thread, which may be waiting for the popup queue lock to render popups */ + uintptr_t badge_id = cheevos_get_badge_texture(badge, 0); + + if (state->queue_read_index < 0) + { + /* queue uninitialized */ + memset(&state->queue, 0, sizeof(state->queue)); + state->queue_read_index = 0; + +#ifdef HAVE_THREADS + state->queue_lock = slock_new(); +#endif + } + + SLOCK_LOCK(state->queue_lock); + + if (state->queue_write_index == state->queue_read_index) + { + if (state->queue[state->queue_write_index].title) + { + /* queue full */ + SLOCK_UNLOCK(state->queue_lock); + return; + } + + /* queue empty */ + } + else + start_notification = 0; /* notification already being displayed */ + + state->queue[state->queue_write_index].badge = badge_id; + state->queue[state->queue_write_index].title = strdup(title); + + state->queue_write_index = (state->queue_write_index + 1) % ARRAY_SIZE(state->queue); + + if (start_notification) + gfx_widget_achievement_popup_start(state); + + SLOCK_UNLOCK(state->queue_lock); +} + +const gfx_widget_t gfx_widget_achievement_popup = { + &gfx_widget_achievement_popup_init, + &gfx_widget_achievement_popup_free, + NULL, /* context_reset*/ + NULL, /* context_destroy */ + NULL, /* layout */ + NULL, /* iterate */ + &gfx_widget_achievement_popup_frame +}; diff --git a/griffin/griffin.c b/griffin/griffin.c index 7451f54ed9..8f28970817 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1276,6 +1276,9 @@ MENU #include "../gfx/widgets/gfx_widget_volume.c" #include "../gfx/widgets/gfx_widget_generic_message.c" #include "../gfx/widgets/gfx_widget_libretro_message.c" +#ifdef HAVE_CHEEVOS +#include "../gfx/widgets/gfx_widget_achievement_popup.c" +#endif #endif #ifdef HAVE_MENU