mirror of
https://github.com/libretro/RetroArch
synced 2025-01-30 21:32:45 +00:00
616 lines
20 KiB
C
616 lines
20 KiB
C
/* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <features/features_cpu.h>
|
|
|
|
#include "../gfx_display.h"
|
|
#include "../gfx_widgets.h"
|
|
|
|
#include "../../cheevos/cheevos.h"
|
|
|
|
#define CHEEVO_LBOARD_ARRAY_SIZE 4
|
|
#define CHEEVO_CHALLENGE_ARRAY_SIZE 8
|
|
|
|
#define CHEEVO_LBOARD_DISPLAY_PADDING 3
|
|
|
|
#define CHEEVO_PROGRESS_TRACKER_DURATION 2000
|
|
|
|
struct leaderboard_display_info
|
|
{
|
|
unsigned id;
|
|
unsigned width;
|
|
char display[24]; /* should never exceed 12 bytes, but aligns the structure at 32 bytes */
|
|
};
|
|
|
|
struct challenge_display_info
|
|
{
|
|
unsigned id;
|
|
uintptr_t image;
|
|
};
|
|
|
|
struct progress_tracker_info
|
|
{
|
|
uintptr_t image;
|
|
unsigned width;
|
|
char display[32];
|
|
retro_time_t show_until;
|
|
};
|
|
|
|
#define CHEEVO_LBOARD_FIRST_FIXED_CHAR 0x2D /* -./0123456789: */
|
|
#define CHEEVO_LBOARD_LAST_FIXED_CHAR 0x3A
|
|
|
|
/* TODO: rename; this file handles all achievement tracker information, not just leaderboards */
|
|
struct gfx_widget_leaderboard_display_state
|
|
{
|
|
#ifdef HAVE_THREADS
|
|
slock_t* array_lock;
|
|
#endif
|
|
const dispgfx_widget_t *dispwidget_ptr;
|
|
struct leaderboard_display_info tracker_info[CHEEVO_LBOARD_ARRAY_SIZE];
|
|
struct challenge_display_info challenge_info[CHEEVO_CHALLENGE_ARRAY_SIZE];
|
|
struct progress_tracker_info progress_tracker;
|
|
unsigned tracker_count;
|
|
unsigned challenge_count;
|
|
uint16_t char_width[CHEEVO_LBOARD_LAST_FIXED_CHAR - CHEEVO_LBOARD_FIRST_FIXED_CHAR + 1];
|
|
uint16_t fixed_char_width;
|
|
uint16_t loading;
|
|
bool disconnected;
|
|
};
|
|
|
|
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 bool gfx_widget_leaderboard_display_init(
|
|
gfx_display_t *p_disp,
|
|
gfx_animation_t *p_anim,
|
|
bool video_is_threaded, bool fullscreen)
|
|
{
|
|
gfx_widget_leaderboard_display_state_t *state =
|
|
&p_w_leaderboard_display_st;
|
|
memset(state, 0, sizeof(*state));
|
|
state->dispwidget_ptr = (const dispgfx_widget_t*)
|
|
dispwidget_get_ptr();
|
|
|
|
return true;
|
|
}
|
|
|
|
static void gfx_widget_leaderboard_display_free(void)
|
|
{
|
|
gfx_widget_leaderboard_display_state_t *state = &p_w_leaderboard_display_st;
|
|
|
|
state->tracker_count = 0;
|
|
state->challenge_count = 0;
|
|
#ifdef HAVE_THREADS
|
|
slock_free(state->array_lock);
|
|
state->array_lock = NULL;
|
|
#endif
|
|
state->dispwidget_ptr = NULL;
|
|
}
|
|
|
|
static void gfx_widget_leaderboard_display_context_destroy(void)
|
|
{
|
|
gfx_widget_leaderboard_display_state_t *state = &p_w_leaderboard_display_st;
|
|
state->tracker_count = 0;
|
|
state->challenge_count = 0;
|
|
}
|
|
|
|
static void gfx_widget_leaderboard_display_frame(void* data, void* userdata)
|
|
{
|
|
gfx_widget_leaderboard_display_state_t *state = &p_w_leaderboard_display_st;
|
|
|
|
/* if there's nothing to display, just bail */
|
|
if (state->tracker_count == 0 &&
|
|
state->challenge_count == 0 &&
|
|
state->progress_tracker.show_until == 0 &&
|
|
!state->loading &&
|
|
!state->disconnected)
|
|
return;
|
|
|
|
#ifdef HAVE_THREADS
|
|
slock_lock(state->array_lock);
|
|
#endif
|
|
|
|
{
|
|
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,
|
|
};
|
|
unsigned i, x;
|
|
dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)userdata;
|
|
const video_frame_info_t *video_info = (const video_frame_info_t*)data;
|
|
gfx_display_t *p_disp = (gfx_display_t*)video_info->disp_userdata;
|
|
const unsigned video_width = video_info->width;
|
|
const unsigned video_height = video_info->height;
|
|
const unsigned spacing = MIN(video_width, video_height) / 64;
|
|
const unsigned widget_height = p_dispwidget->gfx_widget_fonts.regular.line_height + (CHEEVO_LBOARD_DISPLAY_PADDING - 1) * 2;
|
|
unsigned y = video_height;
|
|
char buffer[2] = "0";
|
|
const char* ptr;
|
|
float char_x, char_y;
|
|
|
|
gfx_display_set_alpha(p_dispwidget->backdrop_orig, DEFAULT_BACKDROP);
|
|
gfx_display_set_alpha(pure_white, 1.0f);
|
|
|
|
for (i = 0; i < state->tracker_count; ++i)
|
|
{
|
|
const unsigned widget_width = state->tracker_info[i].width;
|
|
x = video_width - widget_width - spacing;
|
|
y -= (widget_height + spacing);
|
|
|
|
/* Backdrop */
|
|
gfx_display_draw_quad(
|
|
p_disp,
|
|
video_info->userdata,
|
|
video_width, video_height,
|
|
(int)x, (int)y, widget_width, widget_height,
|
|
video_width, video_height,
|
|
p_dispwidget->backdrop_orig,
|
|
NULL);
|
|
|
|
/* Text */
|
|
char_x = (float)(x + CHEEVO_LBOARD_DISPLAY_PADDING);
|
|
char_y = (float)(y + widget_height - (CHEEVO_LBOARD_DISPLAY_PADDING - 1)
|
|
- p_dispwidget->gfx_widget_fonts.regular.line_descender);
|
|
|
|
ptr = state->tracker_info[i].display;
|
|
while (*ptr) {
|
|
float next_char_x = char_x + state->fixed_char_width;
|
|
const char c = *ptr++;
|
|
if (c >= CHEEVO_LBOARD_FIRST_FIXED_CHAR && c <= CHEEVO_LBOARD_LAST_FIXED_CHAR)
|
|
{
|
|
unsigned char_width = state->char_width[c - CHEEVO_LBOARD_FIRST_FIXED_CHAR];
|
|
if (c >= '0' && c <= '9')
|
|
{
|
|
float padding = (float)(state->fixed_char_width - char_width) / 2.0;
|
|
char_x += padding;
|
|
}
|
|
else
|
|
{
|
|
next_char_x = char_x + char_width;
|
|
}
|
|
}
|
|
|
|
buffer[0] = c;
|
|
gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.regular,
|
|
buffer, char_x, char_y,
|
|
video_width, video_height,
|
|
TEXT_COLOR_INFO, TEXT_ALIGN_LEFT, true);
|
|
|
|
char_x = next_char_x;
|
|
}
|
|
}
|
|
|
|
if (state->challenge_count)
|
|
{
|
|
const unsigned widget_size = spacing * 4;
|
|
|
|
x = video_width;
|
|
y -= (widget_size + spacing);
|
|
|
|
for (i = 0; i < state->challenge_count; ++i)
|
|
{
|
|
x -= (widget_size + spacing);
|
|
|
|
if (!state->challenge_info[i].image)
|
|
{
|
|
/* default icon */
|
|
if (p_dispwidget->gfx_widgets_icons_textures[
|
|
MENU_WIDGETS_ICON_ACHIEVEMENT])
|
|
{
|
|
gfx_display_ctx_driver_t* dispctx = p_disp->dispctx;
|
|
if (dispctx && dispctx->blend_begin)
|
|
dispctx->blend_begin(video_info->userdata);
|
|
|
|
gfx_widgets_draw_icon(
|
|
video_info->userdata,
|
|
p_disp,
|
|
video_width,
|
|
video_height,
|
|
widget_size,
|
|
widget_size,
|
|
p_dispwidget->gfx_widgets_icons_textures[
|
|
MENU_WIDGETS_ICON_ACHIEVEMENT],
|
|
x,
|
|
y,
|
|
0.0f, /* rad */
|
|
1.0f, /* cos(rad) = cos(0) = 1.0f */
|
|
0.0f, /* sine(rad) = sine(0) = 0.0f */
|
|
pure_white);
|
|
|
|
if (dispctx && dispctx->blend_end)
|
|
dispctx->blend_end(video_info->userdata);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* achievement badge */
|
|
gfx_widgets_draw_icon(
|
|
video_info->userdata,
|
|
p_disp,
|
|
video_width,
|
|
video_height,
|
|
widget_size,
|
|
widget_size,
|
|
state->challenge_info[i].image,
|
|
x,
|
|
y,
|
|
0.0f, /* rad */
|
|
1.0f, /* cos(rad) = cos(0) = 1.0f */
|
|
0.0f, /* sine(rad) = sine(0) = 0.0f */
|
|
pure_white);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (state->progress_tracker.show_until)
|
|
{
|
|
retro_time_t now = cpu_features_get_time_usec();
|
|
if (now >= state->progress_tracker.show_until)
|
|
{
|
|
gfx_widget_set_achievement_progress(NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
const unsigned image_size = spacing * 4;
|
|
const unsigned tracker_height = image_size + spacing;
|
|
const unsigned tracker_width = state->progress_tracker.width + image_size + spacing * 2;
|
|
x = video_width - tracker_width - spacing;
|
|
y -= (tracker_height + spacing);
|
|
|
|
/* Backdrop */
|
|
gfx_display_draw_quad(
|
|
p_disp,
|
|
video_info->userdata,
|
|
video_width, video_height,
|
|
(int)x, (int)y, tracker_width, tracker_height,
|
|
video_width, video_height,
|
|
p_dispwidget->backdrop_orig,
|
|
NULL);
|
|
|
|
x += spacing / 2;
|
|
y += spacing / 2;
|
|
|
|
if (!state->progress_tracker.image)
|
|
{
|
|
/* default icon */
|
|
if (p_dispwidget->gfx_widgets_icons_textures[
|
|
MENU_WIDGETS_ICON_ACHIEVEMENT])
|
|
{
|
|
gfx_display_ctx_driver_t* dispctx = p_disp->dispctx;
|
|
if (dispctx && dispctx->blend_begin)
|
|
dispctx->blend_begin(video_info->userdata);
|
|
|
|
gfx_widgets_draw_icon(
|
|
video_info->userdata,
|
|
p_disp,
|
|
video_width,
|
|
video_height,
|
|
image_size,
|
|
image_size,
|
|
p_dispwidget->gfx_widgets_icons_textures[
|
|
MENU_WIDGETS_ICON_ACHIEVEMENT],
|
|
x,
|
|
y,
|
|
0.0f, /* rad */
|
|
1.0f, /* cos(rad) = cos(0) = 1.0f */
|
|
0.0f, /* sine(rad) = sine(0) = 0.0f */
|
|
pure_white);
|
|
|
|
if (dispctx && dispctx->blend_end)
|
|
dispctx->blend_end(video_info->userdata);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* achievement badge */
|
|
gfx_widgets_draw_icon(
|
|
video_info->userdata,
|
|
p_disp,
|
|
video_width,
|
|
video_height,
|
|
image_size,
|
|
image_size,
|
|
state->progress_tracker.image,
|
|
x,
|
|
y,
|
|
0.0f, /* rad */
|
|
1.0f, /* cos(rad) = cos(0) = 1.0f */
|
|
0.0f, /* sine(rad) = sine(0) = 0.0f */
|
|
pure_white);
|
|
}
|
|
|
|
x += image_size + spacing;
|
|
y = (float)y + image_size / 2 + p_dispwidget->gfx_widget_fonts.regular.line_height / 2 - p_dispwidget->gfx_widget_fonts.regular.line_descender;
|
|
gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.regular,
|
|
state->progress_tracker.display, x, y,
|
|
video_width, video_height,
|
|
TEXT_COLOR_INFO, TEXT_ALIGN_LEFT, true);
|
|
}
|
|
}
|
|
|
|
if (state->disconnected || state->loading)
|
|
{
|
|
char loading_buffer[8] = "RA ...";
|
|
const char* disconnected_text = state->disconnected ? "! RA !" : loading_buffer;
|
|
const unsigned disconnect_widget_width = font_driver_get_message_width(
|
|
state->dispwidget_ptr->gfx_widget_fonts.msg_queue.font,
|
|
disconnected_text, 0, 1) + CHEEVO_LBOARD_DISPLAY_PADDING * 2;
|
|
const unsigned disconnect_widget_height =
|
|
p_dispwidget->gfx_widget_fonts.msg_queue.line_height + (CHEEVO_LBOARD_DISPLAY_PADDING - 1) * 2;
|
|
x = video_width - disconnect_widget_width - spacing;
|
|
y -= disconnect_widget_height + spacing;
|
|
|
|
if (state->loading) {
|
|
const uint16_t loading_shift = 5;
|
|
loading_buffer[((state->loading - 1) >> loading_shift) + 3] = '\0';
|
|
state->loading &= (1 << (loading_shift + 2)) - 1;
|
|
++state->loading;
|
|
}
|
|
|
|
/* Backdrop */
|
|
gfx_display_draw_quad(
|
|
p_disp,
|
|
video_info->userdata,
|
|
video_width, video_height,
|
|
(int)x, (int)y, disconnect_widget_width, disconnect_widget_height,
|
|
video_width, video_height,
|
|
p_dispwidget->backdrop_orig,
|
|
NULL);
|
|
|
|
/* Text */
|
|
char_x = (float)(x + CHEEVO_LBOARD_DISPLAY_PADDING);
|
|
char_y = (float)(y + disconnect_widget_height - (CHEEVO_LBOARD_DISPLAY_PADDING - 1)
|
|
- p_dispwidget->gfx_widget_fonts.msg_queue.line_descender);
|
|
|
|
gfx_widgets_draw_text(&p_dispwidget->gfx_widget_fonts.msg_queue,
|
|
disconnected_text, char_x, char_y,
|
|
video_width, video_height,
|
|
TEXT_COLOR_INFO, TEXT_ALIGN_LEFT, true);
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_THREADS
|
|
slock_unlock(state->array_lock);
|
|
#endif
|
|
}
|
|
|
|
void gfx_widgets_clear_leaderboard_displays(void)
|
|
{
|
|
gfx_widget_leaderboard_display_state_t* state = &p_w_leaderboard_display_st;
|
|
|
|
#ifdef HAVE_THREADS
|
|
slock_lock(state->array_lock);
|
|
#endif
|
|
|
|
state->tracker_count = 0;
|
|
|
|
#ifdef HAVE_THREADS
|
|
slock_unlock(state->array_lock);
|
|
#endif
|
|
}
|
|
|
|
void gfx_widgets_set_leaderboard_display(unsigned id, const char* value)
|
|
{
|
|
unsigned i;
|
|
gfx_widget_leaderboard_display_state_t *state = &p_w_leaderboard_display_st;
|
|
|
|
#ifdef HAVE_THREADS
|
|
slock_lock(state->array_lock);
|
|
#endif
|
|
|
|
for (i = 0; i < state->tracker_count; ++i)
|
|
{
|
|
if (state->tracker_info[i].id == id)
|
|
break;
|
|
}
|
|
|
|
if (i < CHEEVO_LBOARD_ARRAY_SIZE)
|
|
{
|
|
if (value == NULL)
|
|
{
|
|
/* hide display */
|
|
if (i < state->tracker_count)
|
|
{
|
|
--state->tracker_count;
|
|
if (i < state->tracker_count)
|
|
{
|
|
memcpy(&state->tracker_info[i], &state->tracker_info[i + 1],
|
|
(state->tracker_count - i) * sizeof(state->tracker_info[i]));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* calculate fixed width spacing */
|
|
if (state->fixed_char_width == 0)
|
|
{
|
|
char buffer[2] = "0";
|
|
int j = 0;
|
|
for (j = 0; j < (int)ARRAY_SIZE(state->char_width); ++j)
|
|
{
|
|
buffer[0] = (char)(j + CHEEVO_LBOARD_FIRST_FIXED_CHAR);
|
|
state->char_width[j] = (uint16_t)font_driver_get_message_width(
|
|
state->dispwidget_ptr->gfx_widget_fonts.regular.font,
|
|
buffer, 0, 1);
|
|
if (state->char_width[j] > state->fixed_char_width)
|
|
state->fixed_char_width = state->char_width[j];
|
|
}
|
|
}
|
|
|
|
/* show or update display */
|
|
if (i == state->tracker_count)
|
|
state->tracker_info[state->tracker_count++].id = id;
|
|
|
|
strncpy(state->tracker_info[i].display, value, sizeof(state->tracker_info[i].display));
|
|
|
|
{
|
|
unsigned width = CHEEVO_LBOARD_DISPLAY_PADDING * 2;
|
|
const char* ptr = state->tracker_info[i].display;
|
|
while (*ptr)
|
|
{
|
|
const char c = *ptr++;
|
|
if (c >= '0' && c <= '9')
|
|
width += state->fixed_char_width;
|
|
else if (c >= CHEEVO_LBOARD_FIRST_FIXED_CHAR && c <= CHEEVO_LBOARD_LAST_FIXED_CHAR)
|
|
width += state->char_width[c - CHEEVO_LBOARD_FIRST_FIXED_CHAR];
|
|
else
|
|
width += state->fixed_char_width;
|
|
}
|
|
|
|
state->tracker_info[i].width = width;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_THREADS
|
|
slock_unlock(state->array_lock);
|
|
#endif
|
|
}
|
|
|
|
void gfx_widgets_clear_challenge_displays(void)
|
|
{
|
|
gfx_widget_leaderboard_display_state_t* state = &p_w_leaderboard_display_st;
|
|
|
|
#ifdef HAVE_THREADS
|
|
slock_lock(state->array_lock);
|
|
#endif
|
|
|
|
state->challenge_count = 0;
|
|
|
|
#ifdef HAVE_THREADS
|
|
slock_unlock(state->array_lock);
|
|
#endif
|
|
}
|
|
|
|
void gfx_widgets_set_challenge_display(unsigned id, const char* badge)
|
|
{
|
|
unsigned i;
|
|
gfx_widget_leaderboard_display_state_t* state = &p_w_leaderboard_display_st;
|
|
|
|
/* 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 = badge ? rcheevos_get_badge_texture(badge, false, true) : 0;
|
|
uintptr_t old_badge_id = 0;
|
|
|
|
#ifdef HAVE_THREADS
|
|
slock_lock(state->array_lock);
|
|
#endif
|
|
|
|
for (i = 0; i < state->challenge_count; ++i)
|
|
{
|
|
if (state->challenge_info[i].id == id)
|
|
break;
|
|
}
|
|
|
|
if (i < CHEEVO_CHALLENGE_ARRAY_SIZE)
|
|
{
|
|
if (badge == NULL)
|
|
{
|
|
/* hide indicator */
|
|
if (i < state->challenge_count)
|
|
{
|
|
old_badge_id = state->challenge_info[i].image;
|
|
|
|
--state->challenge_count;
|
|
if (i < state->challenge_count)
|
|
{
|
|
memcpy(&state->challenge_info[i], &state->challenge_info[i + 1],
|
|
(state->challenge_count - i) * sizeof(state->challenge_info[i]));
|
|
}
|
|
|
|
state->challenge_info[state->challenge_count].image = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* show indicator */
|
|
if (i == state->challenge_count)
|
|
{
|
|
/* new indicator, assign id */
|
|
state->challenge_info[state->challenge_count++].id = id;
|
|
}
|
|
else if (state->challenge_info[i].image)
|
|
{
|
|
/* existing indicator, free old image */
|
|
old_badge_id = state->challenge_info[i].image;
|
|
}
|
|
|
|
state->challenge_info[i].image = badge_id;
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_THREADS
|
|
slock_unlock(state->array_lock);
|
|
#endif
|
|
|
|
if (old_badge_id)
|
|
video_driver_texture_unload(&old_badge_id);
|
|
}
|
|
|
|
void gfx_widget_set_achievement_progress(const char* badge, const char* progress)
|
|
{
|
|
gfx_widget_leaderboard_display_state_t* state = &p_w_leaderboard_display_st;
|
|
uintptr_t old_badge_id = state->progress_tracker.image;
|
|
|
|
if (badge == NULL)
|
|
{
|
|
/* hide indicator */
|
|
state->progress_tracker.image = 0;
|
|
state->progress_tracker.show_until = 0;
|
|
}
|
|
else
|
|
{
|
|
/* show indicator */
|
|
state->progress_tracker.show_until = cpu_features_get_time_usec() + CHEEVO_PROGRESS_TRACKER_DURATION * 1000;
|
|
state->progress_tracker.image = rcheevos_get_badge_texture(badge, true, true);
|
|
|
|
snprintf(state->progress_tracker.display, sizeof(state->progress_tracker.display), "%s", progress);
|
|
state->progress_tracker.width = (uint16_t)font_driver_get_message_width(
|
|
state->dispwidget_ptr->gfx_widget_fonts.regular.font,
|
|
progress, 0, 1);
|
|
}
|
|
|
|
if (old_badge_id)
|
|
video_driver_texture_unload(&old_badge_id);
|
|
}
|
|
|
|
void gfx_widget_set_cheevos_disconnect(bool value)
|
|
{
|
|
gfx_widget_leaderboard_display_state_t* state = &p_w_leaderboard_display_st;
|
|
state->disconnected = value;
|
|
}
|
|
|
|
void gfx_widget_set_cheevos_set_loading(bool value)
|
|
{
|
|
gfx_widget_leaderboard_display_state_t* state = &p_w_leaderboard_display_st;
|
|
state->loading = value ? 1 : 0;
|
|
}
|
|
|
|
|
|
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
|
|
};
|