Extended core message interface - add concept of message 'type'

This commit is contained in:
jdgleaver 2020-05-24 15:40:18 +01:00
parent fc8b76708a
commit 23916f88c7
7 changed files with 224 additions and 28 deletions

View File

@ -339,7 +339,6 @@ unsigned gfx_widgets_get_height(void)
return simple_widget_height;
}
unsigned gfx_widgets_get_generic_message_height(void)
{
return generic_message_height;
@ -355,6 +354,11 @@ unsigned gfx_widgets_get_last_video_height(void)
return last_video_height;
}
size_t gfx_widgets_get_msg_queue_size(void)
{
return current_msgs ? current_msgs->size : 0;
}
/* Widgets list */
const static gfx_widget_t* const widgets[] = {
&gfx_widget_screenshot,
@ -1500,6 +1504,7 @@ void gfx_widgets_frame(void *data)
video_frame_info_t *video_info;
bool framecount_show;
bool memory_show;
bool core_status_msg_show;
void *userdata;
unsigned video_width;
unsigned video_height;
@ -1521,6 +1526,7 @@ void gfx_widgets_frame(void *data)
video_info = (video_frame_info_t*)data;
framecount_show = video_info->framecount_show;
memory_show = video_info->memory_show;
core_status_msg_show = video_info->core_status_msg_show;
userdata = video_info->userdata;
video_width = video_info->width;
video_height = video_info->height;
@ -1705,26 +1711,11 @@ void gfx_widgets_frame(void *data)
}
#endif
/* Draw all messages */
for (i = 0; i < current_msgs->size; i++)
{
menu_widget_msg_t *msg = (menu_widget_msg_t*)current_msgs->list[i].userdata;
if (!msg)
continue;
if (msg->task_ptr)
gfx_widgets_draw_task_msg(msg, userdata,
video_width, video_height);
else
gfx_widgets_draw_regular_msg(msg, userdata,
video_width, video_height);
}
/* FPS Counter */
if ( fps_show
|| framecount_show
|| memory_show
|| core_status_msg_show
)
{
const char *text = *gfx_widgets_fps_text == '\0' ? "N/A" : gfx_widgets_fps_text;
@ -1798,6 +1789,22 @@ void gfx_widgets_frame(void *data)
widget->frame(data);
}
/* Draw all messages */
for (i = 0; i < current_msgs->size; i++)
{
menu_widget_msg_t *msg = (menu_widget_msg_t*)current_msgs->list[i].userdata;
if (!msg)
continue;
if (msg->task_ptr)
gfx_widgets_draw_task_msg(msg, userdata,
video_width, video_height);
else
gfx_widgets_draw_regular_msg(msg, userdata,
video_width, video_height);
}
#ifdef HAVE_MENU
/* Load content animation */
if (load_content_animation_running)

View File

@ -113,6 +113,8 @@ float* gfx_widgets_get_backdrop_orig(void);
unsigned gfx_widgets_get_last_video_width(void);
unsigned gfx_widgets_get_last_video_height(void);
unsigned gfx_widgets_get_generic_message_height(void);
/* Warning: not thread safe! */
size_t gfx_widgets_get_msg_queue_size(void);
float gfx_widgets_get_thumbnail_scale_factor(
const float dst_width, const float dst_height,

View File

@ -95,6 +95,7 @@ static void gfx_widget_generic_message_frame(void* data)
unsigned height = gfx_widgets_get_generic_message_height();
unsigned text_color = COLOR_TEXT_ALPHA(0xffffffff, (unsigned)(state->alpha*255.0f));
gfx_widget_font_data_t* font_regular = gfx_widgets_get_font_regular();
size_t msg_queue_size = gfx_widgets_get_msg_queue_size();
gfx_display_set_alpha(gfx_widgets_get_backdrop_orig(), state->alpha);
@ -111,6 +112,11 @@ static void gfx_widget_generic_message_frame(void* data)
video_width, video_height,
text_color, TEXT_ALIGN_CENTER,
false);
/* If the message queue is active, must flush the
* text here to avoid overlaps */
if (msg_queue_size > 0)
gfx_widgets_flush_text(video_width, video_height, font_regular);
}
}

View File

@ -103,6 +103,7 @@ static void gfx_widget_libretro_message_frame(void *data)
float* backdrop_orign = gfx_widgets_get_backdrop_orig();
unsigned text_color = COLOR_TEXT_ALPHA(0xffffffff, (unsigned)(state->alpha*255.0f));
gfx_widget_font_data_t* font_regular = gfx_widgets_get_font_regular();
size_t msg_queue_size = gfx_widgets_get_msg_queue_size();
gfx_display_set_alpha(backdrop_orign, state->alpha);
@ -119,6 +120,11 @@ static void gfx_widget_libretro_message_frame(void *data)
video_width, video_height,
text_color, TEXT_ALIGN_LEFT,
false);
/* If the message queue is active, must flush the
* text here to avoid overlaps */
if (msg_queue_size > 0)
gfx_widgets_flush_text(video_width, video_height, font_regular);
}
}

View File

@ -2544,13 +2544,72 @@ enum retro_message_target
RETRO_MESSAGE_TARGET_LOG
};
enum retro_message_type
{
RETRO_MESSAGE_TYPE_NOTIFICATION = 0,
RETRO_MESSAGE_TYPE_NOTIFICATION_ALT,
RETRO_MESSAGE_TYPE_STATUS
};
struct retro_message_ext
{
const char *msg; /* Message to be displayed/logged */
unsigned frames; /* Duration in frames of message when targeting OSD */
unsigned priority; /* Message priority when targeting OSD */
enum retro_log_level level; /* Message logging level (info, warn, etc.) */
enum retro_message_target target; /* Message destination: OSD, logging interface or both */
/* Message string to be displayed/logged */
const char *msg;
/* Duration (in ms) of message when targeting the OSD */
unsigned duration;
/* Message priority when targeting the OSD
* > When multiple concurrent messages are sent to
* the frontend and the frontend does not have the
* capacity to display them all, messages with the
* *highest* priority value should be shown
* > There is no upper limit to a message priority
* value (within the bounds of the unsigned data type)
* > In the reference frontend (RetroArch), the same
* priority values are used for frontend-generated
* notifications, which are typically assigned values
* between 0 and 3 depending upon importance */
unsigned priority;
/* Message logging level (info, warn, error, etc.) */
enum retro_log_level level;
/* Message destination: OSD, logging interface or both */
enum retro_message_target target;
/* Message 'type' when targeting the OSD
* > RETRO_MESSAGE_TYPE_NOTIFICATION: Specifies that a
* message should be handled in identical fashion to
* a standard frontend-generated notification
* > RETRO_MESSAGE_TYPE_NOTIFICATION_ALT: Specifies that
* message is a notification that requires user attention
* or action, but that it should be displayed in a manner
* that differs from standard frontend-generated notifications.
* This would typically correspond to messages that should be
* displayed immediately (independently from any internal
* frontend message queue), and/or which should be visually
* distinguishable from frontend-generated notifications.
* For example, a core may wish to inform the user of
* information related to a disk-change event. It is
* expected that the frontend itself may provide a
* notification in this case; if the core sends a
* message of type RETRO_MESSAGE_TYPE_NOTIFICATION, an
* uncomfortable 'double-notification' may occur. A message
* of RETRO_MESSAGE_TYPE_NOTIFICATION_ALT should therefore
* be presented such that visual conflict with regular
* notifications does not occur
* > RETRO_MESSAGE_TYPE_STATUS: Indicates that message
* is not a standard notification. This typically
* corresponds to 'status' indicators, such as a core's
* internal FPS, which are intended to be displayed
* either permanently while a core is running, or in
* a manner that does not suggest user attention or action
* is required. 'Status' type messages should therefore be
* displayed in a different on-screen location and in a manner
* easily distinguishable from both standard frontend-generated
* notifications and messages of type RETRO_MESSAGE_TYPE_NOTIFICATION_ALT
* NOTE: Message type is a *hint*, and may be ignored
* by the frontend. If a frontend lacks support for
* displaying messages via alternate means than standard
* frontend-generated notifications, it will treat *all*
* messages as having the type RETRO_MESSAGE_TYPE_NOTIFICATION */
enum retro_message_type type;
};
/* Describes how the libretro implementation maps a libretro input bind

View File

@ -1446,6 +1446,14 @@ typedef struct runloop_ctx_msg_info
bool flush;
} runloop_ctx_msg_info_t;
typedef struct
{
char str[128];
unsigned priority;
float duration;
bool set;
} runloop_core_status_msg_t;
struct rarch_dir_list
{
struct string_list *list;
@ -1772,6 +1780,14 @@ static retro_keyboard_event_t runloop_frontend_key_event = NULL;
static core_option_manager_t *runloop_core_options = NULL;
static msg_queue_t *runloop_msg_queue = NULL;
static runloop_core_status_msg_t runloop_core_status_msg =
{
"",
0,
0.0f,
false
};
static retro_usec_t runloop_frame_time_last = 0;
#ifdef HAVE_DISCORD
@ -10324,7 +10340,8 @@ static bool rarch_environment_cb(unsigned cmd, void *data)
case RETRO_ENVIRONMENT_SET_MESSAGE_EXT:
{
const struct retro_message_ext *msg = (const struct retro_message_ext*)data;
const struct retro_message_ext *msg = (const struct retro_message_ext*)data;
struct retro_system_av_info *av_info = &video_driver_av_info;
/* Log message, if required */
if (msg->target != RETRO_MESSAGE_TARGET_OSD)
@ -10356,15 +10373,60 @@ static bool rarch_environment_cb(unsigned cmd, void *data)
/* Display message via OSD, if required */
if (msg->target != RETRO_MESSAGE_TARGET_LOG)
{
/* Handle 'status' messages */
if (msg->type == RETRO_MESSAGE_TYPE_STATUS)
{
/* Note: We need to lock a mutex here. Strictly
* speaking, runloop_core_status_msg is not part
* of the message queue, but:
* - It may be implemented as a queue in the future
* - It seems unnecessary to create a new slock_t
* object for this type of message when
* _runloop_msg_queue_lock is already available
* We therefore just call runloop_msg_queue_lock()/
* runloop_msg_queue_unlock() in this case */
runloop_msg_queue_lock();
/* If a message is already set, only overwrite
* it if the new message has the same or higher
* priority */
if (!runloop_core_status_msg.set ||
(runloop_core_status_msg.priority <= msg->priority))
{
if (!string_is_empty(msg->msg))
{
strlcpy(runloop_core_status_msg.str, msg->msg,
sizeof(runloop_core_status_msg.str));
runloop_core_status_msg.duration = (float)msg->duration;
runloop_core_status_msg.set = true;
}
else
{
/* Ensure sane behaviour if core sends an
* empty message */
runloop_core_status_msg.str[0] = '\0';
runloop_core_status_msg.priority = 0;
runloop_core_status_msg.duration = 0.0f;
runloop_core_status_msg.set = false;
}
}
runloop_msg_queue_unlock();
}
/* Handle 'alternate' non-queued notifications */
#if defined(HAVE_GFX_WIDGETS)
if (gfx_widgets_active())
gfx_widget_set_libretro_message(msg->msg,
roundf((float)msg->frames / 60.0f * 1000.0f));
else
else if ((msg->type == RETRO_MESSAGE_TYPE_NOTIFICATION_ALT) &&
gfx_widgets_active())
gfx_widget_set_libretro_message(msg->msg, msg->duration);
#endif
/* Handle standard (queued) notifications */
else
{
enum message_queue_category category;
unsigned duration_frames = 0;
/* Assign category */
switch (msg->level)
{
case RETRO_LOG_WARN:
@ -10380,8 +10442,13 @@ static bool rarch_environment_cb(unsigned cmd, void *data)
break;
}
/* Get duration in frames */
if (av_info)
duration_frames = (unsigned)((av_info->timing.fps *
(float)msg->duration / 1000.0f) + 0.5f);
runloop_msg_queue_push(msg->msg,
msg->priority, msg->frames,
msg->priority, duration_frames,
true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
category);
}
@ -22978,6 +23045,52 @@ static void video_driver_frame(const void *data, unsigned width,
video_driver_window_title_update = true;
}
/* Add core status message to 'fps_text' string
* TODO/FIXME: fps_text is used for several status
* parameters, not just FPS. It should probably be
* renamed to reflect this... */
if (video_info.core_status_msg_show)
{
/* Note: We need to lock a mutex here. Strictly
* speaking, runloop_core_status_msg is not part
* of the message queue, but:
* - It may be implemented as a queue in the future
* - It seems unnecessary to create a new slock_t
* object for this type of message when
* _runloop_msg_queue_lock is already available
* We therefore just call runloop_msg_queue_lock()/
* runloop_msg_queue_unlock() in this case */
runloop_msg_queue_lock();
/* Check whether duration timer has elapsed */
runloop_core_status_msg.duration -= gfx_animation_get_delta_time();
if (runloop_core_status_msg.duration < 0.0f)
{
runloop_core_status_msg.str[0] = '\0';
runloop_core_status_msg.priority = 0;
runloop_core_status_msg.duration = 0.0f;
runloop_core_status_msg.set = false;
}
else
{
/* If 'fps_text' is already set, add status
* message at the end */
if (!string_is_empty(fps_text))
{
strlcat(fps_text,
" || ", sizeof(fps_text));
strlcat(fps_text,
runloop_core_status_msg.str, sizeof(fps_text));
}
else
strlcpy(fps_text, runloop_core_status_msg.str,
sizeof(fps_text));
}
runloop_msg_queue_unlock();
}
/* Slightly messy code,
* but we really need to do processing before blocking on VSync
* for best possible scheduling.
@ -23139,6 +23252,7 @@ static void video_driver_frame(const void *data, unsigned width,
if ( video_info.fps_show
|| video_info.framecount_show
|| video_info.memory_show
|| video_info.core_status_msg_show
)
{
#if defined(HAVE_GFX_WIDGETS)
@ -23289,6 +23403,7 @@ void video_driver_build_info(video_frame_info_t *video_info)
video_info->memory_show = settings->bools.video_memory_show;
video_info->statistics_show = settings->bools.video_statistics_show;
video_info->framecount_show = settings->bools.video_framecount_show;
video_info->core_status_msg_show = runloop_core_status_msg.set;
video_info->aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
video_info->post_filter_record = settings->bools.video_post_filter_record;
video_info->input_menu_swap_ok_cancel_buttons = settings->bools.input_menu_swap_ok_cancel_buttons;

View File

@ -1098,6 +1098,7 @@ typedef struct video_frame_info
bool memory_show;
bool statistics_show;
bool framecount_show;
bool core_status_msg_show;
bool post_filter_record;
bool windowed_fullscreen;
bool fullscreen;