diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index f3c08f35b5..20b4cd22bd 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -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 diff --git a/retroarch.c b/retroarch.c index 8c8b3f7c7b..679dd14a7d 100644 --- a/retroarch.c +++ b/retroarch.c @@ -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; } }