From 71cd400e566ee9872913da652d5dda89f927497f Mon Sep 17 00:00:00 2001 From: Jamiras Date: Thu, 24 Sep 2020 09:25:56 -0600 Subject: [PATCH] add display widget for active leaderboards --- Makefile.common | 3 +- cheevos/cheevos.c | 123 +++++++++-- cheevos/cheevos.h | 2 + gfx/gfx_widgets.c | 1 + gfx/gfx_widgets.h | 2 + gfx/widgets/gfx_widget_leaderboard_display.c | 202 +++++++++++++++++++ griffin/griffin.c | 1 + menu/menu_setting.c | 7 +- 8 files changed, 320 insertions(+), 21 deletions(-) create mode 100644 gfx/widgets/gfx_widget_leaderboard_display.c diff --git a/Makefile.common b/Makefile.common index 3bd4b4b246..dc7597db67 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1041,7 +1041,8 @@ ifeq ($(HAVE_GFX_WIDGETS), 1) gfx/widgets/gfx_widget_progress_message.o \ gfx/widgets/gfx_widget_load_content_animation.o ifeq ($(HAVE_CHEEVOS), 1) - OBJ += gfx/widgets/gfx_widget_achievement_popup.o + OBJ += gfx/widgets/gfx_widget_achievement_popup.o \ + gfx/widgets/gfx_widget_leaderboard_display.o endif endif diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index 871cc41d87..5295a124f6 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -974,6 +974,12 @@ static void rcheevos_lboard_submit(rcheevos_locals_t *locals, formatted_value, lboard->title); runloop_msg_queue_push(buffer, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); +#if defined(HAVE_GFX_WIDGETS) + /* Hide the tracker */ + if (gfx_widgets_ready()) + gfx_widgets_set_leaderboard_display(lboard->id, NULL); +#endif + /* Start the submit task. */ { rcheevos_async_io_request* request = (rcheevos_async_io_request*)calloc(1, sizeof(rcheevos_async_io_request)); @@ -996,12 +1002,17 @@ static void rcheevos_lboard_canceled(rcheevos_ralboard_t * lboard) CHEEVOS_LOG(RCHEEVOS_TAG "Leaderboard %u canceled: %s\n", lboard->id, lboard->title); +#if defined(HAVE_GFX_WIDGETS) + if (gfx_widgets_ready()) + gfx_widgets_set_leaderboard_display(lboard->id, NULL); +#endif + snprintf(buffer, sizeof(buffer), "Leaderboard attempt failed: %s", lboard->title); runloop_msg_queue_push(buffer, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); } -static void rcheevos_lboard_started(rcheevos_ralboard_t * lboard) +static void rcheevos_lboard_started(rcheevos_ralboard_t * lboard, int value) { char buffer[256]; if (!lboard) @@ -1009,6 +1020,14 @@ static void rcheevos_lboard_started(rcheevos_ralboard_t * lboard) CHEEVOS_LOG(RCHEEVOS_TAG "Leaderboard %u started: %s\n", lboard->id, lboard->title); +#if defined(HAVE_GFX_WIDGETS) + if (gfx_widgets_ready()) + { + rc_format_value(buffer, sizeof(buffer), value, lboard->format); + gfx_widgets_set_leaderboard_display(lboard->id, buffer); + } +#endif + if (lboard->description && *lboard->description) snprintf(buffer, sizeof(buffer), "Leaderboard attempt started: %s - %s", lboard->title, lboard->description); else @@ -1018,6 +1037,21 @@ static void rcheevos_lboard_started(rcheevos_ralboard_t * lboard) MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); } +#if defined(HAVE_GFX_WIDGETS) +static void rcheevos_lboard_updated(rcheevos_ralboard_t* lboard, int value) +{ + if (!lboard) + return; + + if (gfx_widgets_ready()) + { + char buffer[32]; + rc_format_value(buffer, sizeof(buffer), value, lboard->format); + gfx_widgets_set_leaderboard_display(lboard->id, buffer); + } +} +#endif + const char* rcheevos_get_richpresence(void) { return rc_runtime_get_richpresence(&rcheevos_locals.runtime); @@ -1025,6 +1059,18 @@ const char* rcheevos_get_richpresence(void) void rcheevos_reset_game(void) { +#if defined(HAVE_GFX_WIDGETS) + /* Hide any visible trackers */ + if (gfx_widgets_ready()) + { + rcheevos_ralboard_t* lboard = rcheevos_locals.patchdata.lboards; + unsigned i; + + for (i = 0; i < rcheevos_locals.patchdata.lboard_count; ++i, ++lboard) + gfx_widgets_set_leaderboard_display(lboard->id, NULL); + } +#endif + rc_runtime_reset(&rcheevos_locals.runtime); /* some cores reallocate memory on reset, make sure we update our pointers */ @@ -1271,12 +1317,57 @@ static void rcheevos_toggle_hardcore_achievements(rcheevos_locals_t *locals, } } -static void rcheevos_toggle_hardcore_active(rcheevos_locals_t *locals) +static void rcheevos_activate_leaderboards(rcheevos_locals_t* locals) +{ + rcheevos_ralboard_t* lboard = locals->patchdata.lboards; + unsigned i; + + for (i = 0; i < locals->patchdata.lboard_count; ++i, ++lboard) + { + if (lboard->mem) + rc_runtime_activate_lboard(&locals->runtime, lboard->id, lboard->mem, NULL, 0); + } +} + +static void rcheevos_deactivate_leaderboards(rcheevos_locals_t* locals) +{ + rcheevos_ralboard_t* lboard = locals->patchdata.lboards; + unsigned i; + + for (i = 0; i < locals->patchdata.lboard_count; ++i, ++lboard) + { + if (lboard->mem) + { + rc_runtime_deactivate_lboard(&locals->runtime, lboard->id); + +#if defined(HAVE_GFX_WIDGETS) + /* Hide any visible trackers */ + gfx_widgets_set_leaderboard_display(lboard->id, NULL); +#endif + } + } +} + +void rcheevos_leaderboards_enabled_changed(void) +{ + if (rcheevos_locals.loaded) + { + const settings_t* settings = config_get_ptr(); + const bool enabled = settings && settings->bools.cheevos_enable && + settings->bools.cheevos_leaderboards_enable && + settings->bools.cheevos_hardcore_mode_enable; + + if (enabled) + rcheevos_activate_leaderboards(&rcheevos_locals); + else + rcheevos_deactivate_leaderboards(&rcheevos_locals); + } +} + +static void rcheevos_toggle_hardcore_active(rcheevos_locals_t* locals) { settings_t* settings = config_get_ptr(); bool rewind_enable = settings->bools.rewind_enable; - rcheevos_ralboard_t* lboard; - unsigned i; if (!locals->hardcore_active) { @@ -1292,14 +1383,7 @@ static void rcheevos_toggle_hardcore_active(rcheevos_locals_t *locals) /* reactivate leaderboards */ if (settings->bools.cheevos_leaderboards_enable) - { - lboard = locals->patchdata.lboards; - for (i = 0; i < locals->patchdata.lboard_count; ++i, ++lboard) - { - if (lboard->mem) - rc_runtime_activate_lboard(&locals->runtime, lboard->id, lboard->mem, NULL, 0); - } - } + rcheevos_activate_leaderboards(locals); /* reset the game */ command_event(CMD_EVENT_RESET, NULL); @@ -1319,12 +1403,7 @@ static void rcheevos_toggle_hardcore_active(rcheevos_locals_t *locals) CHEEVOS_LOG(RCHEEVOS_TAG "Hardcore paused\n"); /* deactivate leaderboards */ - lboard = locals->patchdata.lboards; - for (i = 0; i < locals->patchdata.lboard_count; ++i, ++lboard) - { - if (lboard->mem) - rc_runtime_deactivate_lboard(&locals->runtime, lboard->id); - } + rcheevos_deactivate_leaderboards(locals); } /* re-init rewind */ @@ -1370,9 +1449,15 @@ static void rcheevos_runtime_event_handler(const rc_runtime_event_t* runtime_eve break; case RC_RUNTIME_EVENT_LBOARD_STARTED: - rcheevos_lboard_started(rcheevos_find_lboard(runtime_event->id)); + rcheevos_lboard_started(rcheevos_find_lboard(runtime_event->id), runtime_event->value); break; +#if defined(HAVE_GFX_WIDGETS) + case RC_RUNTIME_EVENT_LBOARD_UPDATED: + rcheevos_lboard_updated(rcheevos_find_lboard(runtime_event->id), runtime_event->value); + break; +#endif + case RC_RUNTIME_EVENT_LBOARD_CANCELED: rcheevos_lboard_canceled(rcheevos_find_lboard(runtime_event->id)); break; diff --git a/cheevos/cheevos.h b/cheevos/cheevos.h index cb5983ce5e..6496e4e8ee 100644 --- a/cheevos/cheevos.h +++ b/cheevos/cheevos.h @@ -57,6 +57,8 @@ bool rcheevos_unload(void); void rcheevos_hardcore_enabled_changed(void); void rcheevos_toggle_hardcore_paused(void); +void rcheevos_leaderboards_enabled_changed(void); + void rcheevos_test(void); void rcheevos_set_support_cheevos(bool state); diff --git a/gfx/gfx_widgets.c b/gfx/gfx_widgets.c index 7b2a6b677a..1a9284bae1 100644 --- a/gfx/gfx_widgets.c +++ b/gfx/gfx_widgets.c @@ -181,6 +181,7 @@ const static gfx_widget_t* const widgets[] = { &gfx_widget_volume, #ifdef HAVE_CHEEVOS &gfx_widget_achievement_popup, + &gfx_widget_leaderboard_display, #endif &gfx_widget_generic_message, &gfx_widget_libretro_message, diff --git a/gfx/gfx_widgets.h b/gfx/gfx_widgets.h index 25706902b8..f80ae93e23 100644 --- a/gfx/gfx_widgets.h +++ b/gfx/gfx_widgets.h @@ -365,6 +365,7 @@ void gfx_widgets_ai_service_overlay_unload(dispgfx_widget_t *p_dispwidget); #ifdef HAVE_CHEEVOS void gfx_widgets_push_achievement(const char *title, const char *badge); +void gfx_widgets_set_leaderboard_display(unsigned id, const char* value); #endif /* Warning: not thread safe! */ @@ -401,6 +402,7 @@ extern const gfx_widget_t gfx_widget_load_content_animation; #ifdef HAVE_CHEEVOS extern const gfx_widget_t gfx_widget_achievement_popup; +extern const gfx_widget_t gfx_widget_leaderboard_display; #endif #endif diff --git a/gfx/widgets/gfx_widget_leaderboard_display.c b/gfx/widgets/gfx_widget_leaderboard_display.c new file mode 100644 index 0000000000..5535a239ca --- /dev/null +++ b/gfx/widgets/gfx_widget_leaderboard_display.c @@ -0,0 +1,202 @@ +/* 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" + +#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_LBOARD_ARRAY_SIZE 4 + +#define CHEEVO_LBOARD_DISPLAY_SPACING 6 +#define CHEEVO_LBOARD_DISPLAY_PADDING 3 + +struct leaderboard_display_info +{ + unsigned id; + unsigned width; + char display[24]; /* should never exceed 12 bytes, but aligns the structure at 32 bytes */ +}; + +struct gfx_widget_leaderboard_display_state +{ +#ifdef HAVE_THREADS + slock_t* array_lock; +#endif + struct leaderboard_display_info info[CHEEVO_LBOARD_ARRAY_SIZE]; + int count; +}; + +typedef struct gfx_widget_leaderboard_display_state gfx_widget_leaderboard_display_state_t; + +static gfx_widget_leaderboard_display_state_t p_w_leaderboard_display_st; + +static gfx_widget_leaderboard_display_state_t* gfx_widget_leaderboard_display_get_ptr(void) +{ + return &p_w_leaderboard_display_st; +} + +static bool gfx_widget_leaderboard_display_init(bool video_is_threaded, bool fullscreen) +{ + gfx_widget_leaderboard_display_state_t* state = gfx_widget_leaderboard_display_get_ptr(); + memset(state, 0, sizeof(*state)); + + return true; +} + +static void gfx_widget_leaderboard_display_free_all(gfx_widget_leaderboard_display_state_t* state) +{ + state->count = 0; +} + +static void gfx_widget_leaderboard_display_free(void) +{ + gfx_widget_leaderboard_display_state_t* state = gfx_widget_leaderboard_display_get_ptr(); + + gfx_widget_leaderboard_display_free_all(state); + +#ifdef HAVE_THREADS + slock_free(state->array_lock); + state->array_lock = NULL; +#endif +} + +static void gfx_widget_leaderboard_display_context_destroy(void) +{ + gfx_widget_leaderboard_display_state_t* state = gfx_widget_leaderboard_display_get_ptr(); + + gfx_widget_leaderboard_display_free_all(state); +} + +static void gfx_widget_leaderboard_display_frame(void* data, void* userdata) +{ + gfx_widget_leaderboard_display_state_t* state = gfx_widget_leaderboard_display_get_ptr(); + + /* if there's nothing to display, just bail */ + if (state->count == 0) + return; + + SLOCK_LOCK(state->array_lock); + { + static float 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, + }; + dispgfx_widget_t* p_dispwidget = (dispgfx_widget_t*)userdata; + 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; + const unsigned widget_height = p_dispwidget->gfx_widget_fonts.regular.line_height + CHEEVO_LBOARD_DISPLAY_PADDING * 2; + unsigned y = video_height; + unsigned x; + int i; + + gfx_display_set_alpha(gfx_widgets_get_backdrop_orig(), DEFAULT_BACKDROP); + gfx_display_set_alpha(pure_white, 1.0f); + + for (i = 0; i < state->count; ++i) + { + const unsigned widget_width = state->info[i].width; + x = video_width - widget_width - CHEEVO_LBOARD_DISPLAY_SPACING; + y -= (widget_height + CHEEVO_LBOARD_DISPLAY_SPACING); + + /* Backdrop */ + gfx_display_draw_quad(video_info->userdata, + video_width, video_height, + (int)x, (int)y, widget_width, widget_height, + video_width, video_height, + gfx_widgets_get_backdrop_orig()); + + /* Text */ + gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.regular, + state->info[i].display, + (float)(x + CHEEVO_LBOARD_DISPLAY_PADDING), + (float)(y + p_dispwidget->gfx_widget_fonts.regular.line_height), + video_width, video_height, + TEXT_COLOR_INFO, + TEXT_ALIGN_LEFT, + true); + } + } + + SLOCK_UNLOCK(state->array_lock); +} + +void gfx_widgets_set_leaderboard_display(unsigned id, const char* value) +{ + gfx_widget_leaderboard_display_state_t* state = gfx_widget_leaderboard_display_get_ptr(); + int i; + + SLOCK_LOCK(state->array_lock); + + for (i = 0; i < state->count; ++i) + { + if (state->info[i].id == id) + break; + } + + if (i < CHEEVO_LBOARD_ARRAY_SIZE) + { + if (value == NULL) + { + /* hide display */ + if (i < state->count) + { + --state->count; + if (i < state->count) + { + memcpy(&state->info[i], &state->info[i + 1], + (state->count - i) * sizeof(state->info[i])); + } + } + } + else + { + /* show or update display */ + const dispgfx_widget_t* p_dispwidget = (const dispgfx_widget_t*)dispwidget_get_ptr(); + + if (i == state->count) + state->info[state->count++].id = id; + + strncpy(state->info[i].display, value, sizeof(state->info[i].display)); + state->info[i].width = font_driver_get_message_width( + p_dispwidget->gfx_widget_fonts.regular.font, + state->info[i].display, 0, 1); + state->info[i].width += CHEEVO_LBOARD_DISPLAY_PADDING * 2; + } + } + + SLOCK_UNLOCK(state->array_lock); +} + +const gfx_widget_t gfx_widget_leaderboard_display = { + &gfx_widget_leaderboard_display_init, + &gfx_widget_leaderboard_display_free, + NULL, /* context_reset*/ + &gfx_widget_leaderboard_display_context_destroy, + NULL, /* layout */ + NULL, /* iterate */ + &gfx_widget_leaderboard_display_frame +}; diff --git a/griffin/griffin.c b/griffin/griffin.c index ddca147bfc..f96f5f9e17 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1347,6 +1347,7 @@ MENU #include "../gfx/widgets/gfx_widget_progress_message.c" #ifdef HAVE_CHEEVOS #include "../gfx/widgets/gfx_widget_achievement_popup.c" +#include "../gfx/widgets/gfx_widget_leaderboard_display.c" #endif #include "../gfx/widgets/gfx_widget_load_content_animation.c" #endif diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 281372b632..a18d65fb50 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -7431,6 +7431,11 @@ static void achievement_hardcore_mode_write_handler(rarch_setting_t *setting) { rcheevos_hardcore_enabled_changed(); } + +static void achievement_leaderboards_enabled_write_handler(rarch_setting_t* setting) +{ + rcheevos_leaderboards_enabled_changed(); +} #endif static void update_streaming_url_write_handler(rarch_setting_t *setting) @@ -16781,7 +16786,7 @@ static bool setting_append_list( &group_info, &subgroup_info, parent_group, - general_write_handler, + achievement_leaderboards_enabled_write_handler, general_read_handler, SD_FLAG_NONE );