Extended core message interface - add API support for 'progress' messages

This commit is contained in:
jdgleaver 2020-05-26 11:42:46 +01:00
parent 23916f88c7
commit a1210d63cc
2 changed files with 123 additions and 73 deletions

View File

@ -2548,7 +2548,8 @@ enum retro_message_type
{
RETRO_MESSAGE_TYPE_NOTIFICATION = 0,
RETRO_MESSAGE_TYPE_NOTIFICATION_ALT,
RETRO_MESSAGE_TYPE_STATUS
RETRO_MESSAGE_TYPE_STATUS,
RETRO_MESSAGE_TYPE_PROGRESS
};
struct retro_message_ext
@ -2604,12 +2605,34 @@ struct retro_message_ext
* 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
* > RETRO_MESSAGE_TYPE_PROGRESS: Indicates that message reports
* the progress of an internal core task. For example, in cases
* where a core itself handles the loading of content from a file,
* this may correspond to the percentage of the file that has been
* read. Alternatively, an audio/video playback core may use a
* message of type RETRO_MESSAGE_TYPE_PROGRESS to display the current
* playback position as a percentage of the runtime. 'Progress' type
* messages should therefore be displayed as a literal progress bar,
* where:
* - 'retro_message_ext.msg' is the progress bar title/label
* - 'retro_message_ext.progress' determines the length of
* the progress bar
* 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;
/* Task progress when targeting the OSD and message is
* of type RETRO_MESSAGE_TYPE_PROGRESS
* > -1: Unmetered/indeterminate
* > 0-100: Current progress percentage
* NOTE: Since message type is a hint, a frontend may ignore
* progress values. Where relevant, a core should therefore
* include progress percentage within the message string,
* such that the message intent remains clear when displayed
* as a standard frontend-generated notification */
int8_t progress;
};
/* Describes how the libretro implementation maps a libretro input bind

View File

@ -10174,7 +10174,38 @@ static bool rarch_clear_all_thread_waits(unsigned clear_threads, void *data)
return true;
}
static void runloop_core_msg_queue_push(const struct retro_message_ext *msg)
{
struct retro_system_av_info *av_info = &video_driver_av_info;
enum message_queue_category category;
double fps;
unsigned duration_frames;
/* Assign category */
switch (msg->level)
{
case RETRO_LOG_WARN:
category = MESSAGE_QUEUE_CATEGORY_WARNING;
break;
case RETRO_LOG_ERROR:
category = MESSAGE_QUEUE_CATEGORY_ERROR;
break;
case RETRO_LOG_INFO:
case RETRO_LOG_DEBUG:
default:
category = MESSAGE_QUEUE_CATEGORY_INFO;
break;
}
/* Get duration in frames */
fps = av_info ? av_info->timing.fps : 60.0;
duration_frames = (unsigned)((fps * (float)msg->duration / 1000.0f) + 0.5f);
runloop_msg_queue_push(msg->msg,
msg->priority, duration_frames,
true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
category);
}
/**
* rarch_environment_cb:
@ -10340,8 +10371,7 @@ 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;
struct retro_system_av_info *av_info = &video_driver_av_info;
const struct retro_message_ext *msg = (const struct retro_message_ext*)data;
/* Log message, if required */
if (msg->target != RETRO_MESSAGE_TARGET_OSD)
@ -10373,84 +10403,81 @@ 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)
switch (msg->type)
{
/* 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();
/* Handle 'status' messages */
case RETRO_MESSAGE_TYPE_STATUS:
/* 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))
/* 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))
{
strlcpy(runloop_core_status_msg.str, msg->msg,
sizeof(runloop_core_status_msg.str));
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;
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;
}
}
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 */
runloop_msg_queue_unlock();
break;
#if defined(HAVE_GFX_WIDGETS)
else if ((msg->type == RETRO_MESSAGE_TYPE_NOTIFICATION_ALT) &&
gfx_widgets_active())
gfx_widget_set_libretro_message(msg->msg, msg->duration);
/* Handle 'alternate' non-queued notifications */
case RETRO_MESSAGE_TYPE_NOTIFICATION_ALT:
if (gfx_widgets_active())
gfx_widget_set_libretro_message(msg->msg, msg->duration);
else
runloop_core_msg_queue_push(msg);
break;
/* Handle 'progress' messages
* TODO/FIXME: At present, we also display messages
* of type RETRO_MESSAGE_TYPE_PROGRESS via
* gfx_widget_set_libretro_message(). We need to
* implement a separate 'progress bar' widget to
* handle these correctly */
case RETRO_MESSAGE_TYPE_PROGRESS:
if (gfx_widgets_active())
gfx_widget_set_libretro_message(msg->msg, msg->duration);
else
runloop_core_msg_queue_push(msg);
break;
#endif
/* Handle standard (queued) notifications */
else
{
enum message_queue_category category;
unsigned duration_frames = 0;
/* Assign category */
switch (msg->level)
{
case RETRO_LOG_WARN:
category = MESSAGE_QUEUE_CATEGORY_WARNING;
break;
case RETRO_LOG_ERROR:
category = MESSAGE_QUEUE_CATEGORY_ERROR;
break;
case RETRO_LOG_INFO:
case RETRO_LOG_DEBUG:
default:
category = MESSAGE_QUEUE_CATEGORY_INFO;
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, duration_frames,
true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
category);
/* Handle standard (queued) notifications */
case RETRO_MESSAGE_TYPE_NOTIFICATION:
default:
runloop_core_msg_queue_push(msg);
break;
}
}