From a6cffc0e295fab3dfe7ea32cb219655ea2dd448a Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Tue, 13 Apr 2021 13:58:42 +0100 Subject: [PATCH] (XMB/GLUI/Ozone) Add optional menu screensaver animations --- Makefile.common | 7 + config.def.h | 12 + configuration.c | 6 + configuration.h | 2 + griffin/griffin.c | 3 + intl/msg_hash_lbl.h | 8 + intl/msg_hash_us.h | 28 ++ menu/cbs/menu_cbs_sublabel.c | 12 + menu/drivers/materialui.c | 106 +++- menu/drivers/ozone/ozone.c | 47 +- menu/drivers/ozone/ozone.h | 3 +- menu/drivers/ozone/ozone_theme.c | 21 + menu/drivers/ozone/ozone_theme.h | 3 + menu/drivers/xmb.c | 58 ++- menu/menu_displaylist.c | 28 +- menu/menu_screensaver.c | 814 +++++++++++++++++++++++++++++++ menu/menu_screensaver.h | 87 ++++ menu/menu_setting.c | 78 +++ msg_hash.h | 7 + 19 files changed, 1264 insertions(+), 66 deletions(-) create mode 100644 menu/menu_screensaver.c create mode 100644 menu/menu_screensaver.h diff --git a/Makefile.common b/Makefile.common index bd550e27f7..22e36a5c94 100644 --- a/Makefile.common +++ b/Makefile.common @@ -988,12 +988,14 @@ ifeq ($(HAVE_MENU), 1) OBJ += menu/drivers/materialui.o DEFINES += -DHAVE_MATERIALUI HAVE_ASSETS = 1 + HAVE_MENU_SCREENSAVER = 1 endif ifeq ($(HAVE_XMB), 1) OBJ += menu/drivers/xmb.o DEFINES += -DHAVE_XMB HAVE_ASSETS = 1 + HAVE_MENU_SCREENSAVER = 1 endif ifeq ($(HAVE_OZONE), 1) @@ -1005,12 +1007,17 @@ ifeq ($(HAVE_MENU), 1) menu/drivers/ozone/ozone_sidebar.o DEFINES += -DHAVE_OZONE HAVE_ASSETS = 1 + HAVE_MENU_SCREENSAVER = 1 endif ifeq ($(HAVE_STRIPES), 1) OBJ += menu/drivers/stripes.o DEFINES += -DHAVE_STRIPES endif + + ifeq ($(HAVE_MENU_SCREENSAVER), 1) + OBJ += menu/menu_screensaver.o + endif endif ifeq ($(HAVE_BLUETOOTH), 1) diff --git a/config.def.h b/config.def.h index 2341cfce9a..b7518420a2 100644 --- a/config.def.h +++ b/config.def.h @@ -43,6 +43,11 @@ #include "dingux/dingux_utils.h" #endif +/* Required for menu screensaver animation */ +#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE) +#include "menu/menu_screensaver.h" +#endif + #if defined(HW_RVL) #define MAX_GAMMA_SETTING 30 #elif defined(GEKKO) @@ -588,6 +593,13 @@ static const bool menu_savestate_resume = false; * the screensaver */ #define DEFAULT_MENU_SCREENSAVER_TIMEOUT 0 +#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE) +/* When menu screensaver is enabled, specifies + * animation effect and animation speed */ +#define DEFAULT_MENU_SCREENSAVER_ANIMATION MENU_SCREENSAVER_BLANK +#define DEFAULT_MENU_SCREENSAVER_ANIMATION_SPEED 1.0f +#endif + static const bool content_show_settings = true; static const bool content_show_favorites = true; #ifdef HAVE_IMAGEVIEWER diff --git a/configuration.c b/configuration.c index 6137646cfc..ce58499661 100644 --- a/configuration.c +++ b/configuration.c @@ -1839,6 +1839,9 @@ static struct config_float_setting *populate_settings_float( SETTING_FLOAT("menu_header_opacity", &settings->floats.menu_header_opacity, true, menu_header_opacity, false); SETTING_FLOAT("menu_ticker_speed", &settings->floats.menu_ticker_speed, true, menu_ticker_speed, false); SETTING_FLOAT("rgui_particle_effect_speed", &settings->floats.menu_rgui_particle_effect_speed, true, DEFAULT_RGUI_PARTICLE_EFFECT_SPEED, false); +#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE) + SETTING_FLOAT("menu_screensaver_animation_speed", &settings->floats.menu_screensaver_animation_speed, true, DEFAULT_MENU_SCREENSAVER_ANIMATION_SPEED, false); +#endif #endif SETTING_FLOAT("video_message_pos_x", &settings->floats.video_msg_pos_x, true, message_pos_offset_x, false); SETTING_FLOAT("video_message_pos_y", &settings->floats.video_msg_pos_y, true, message_pos_offset_y, false); @@ -1932,6 +1935,9 @@ static struct config_uint_setting *populate_settings_uint( SETTING_UINT("menu_scroll_delay", &settings->uints.menu_scroll_delay, true, DEFAULT_MENU_SCROLL_DELAY, false); SETTING_UINT("content_show_add_entry", &settings->uints.menu_content_show_add_entry, true, DEFAULT_MENU_CONTENT_SHOW_ADD_ENTRY, false); SETTING_UINT("menu_screensaver_timeout", &settings->uints.menu_screensaver_timeout, true, DEFAULT_MENU_SCREENSAVER_TIMEOUT, false); +#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE) + SETTING_UINT("menu_screensaver_animation", &settings->uints.menu_screensaver_animation, true, DEFAULT_MENU_SCREENSAVER_ANIMATION, false); +#endif #ifdef HAVE_RGUI SETTING_UINT("rgui_menu_color_theme", &settings->uints.menu_rgui_color_theme, true, DEFAULT_RGUI_COLOR_THEME, false); SETTING_UINT("rgui_thumbnail_downscaler", &settings->uints.menu_rgui_thumbnail_downscaler, true, rgui_thumbnail_downscaler, false); diff --git a/configuration.h b/configuration.h index ed81e015c6..4c7dcd6bfe 100644 --- a/configuration.h +++ b/configuration.h @@ -252,6 +252,7 @@ typedef struct settings unsigned menu_scroll_delay; unsigned menu_content_show_add_entry; unsigned menu_screensaver_timeout; + unsigned menu_screensaver_animation; unsigned playlist_entry_remove_enable; unsigned playlist_show_inline_core_name; @@ -309,6 +310,7 @@ typedef struct settings float menu_header_opacity; float menu_ticker_speed; float menu_rgui_particle_effect_speed; + float menu_screensaver_animation_speed; float audio_max_timing_skew; float audio_volume; /* dB scale. */ diff --git a/griffin/griffin.c b/griffin/griffin.c index 7b9b54f1e1..4af17585a1 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1370,6 +1370,9 @@ MENU #ifdef HAVE_MENU #include "../menu/menu_setting.c" +#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE) +#include "../menu/menu_screensaver.c" +#endif #include "../menu/cbs/menu_cbs_ok.c" #include "../menu/cbs/menu_cbs_cancel.c" diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index f091b58f0f..a2e27a1440 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -2170,6 +2170,14 @@ MSG_HASH( MENU_ENUM_LABEL_MENU_SCREENSAVER_TIMEOUT, "menu_screensaver_timeout" ) +MSG_HASH( + MENU_ENUM_LABEL_MENU_SCREENSAVER_ANIMATION, + "menu_screensaver_animation" + ) +MSG_HASH( + MENU_ENUM_LABEL_MENU_SCREENSAVER_ANIMATION_SPEED, + "menu_screensaver_animation_speed" + ) MSG_HASH( MENU_ENUM_LABEL_PERFCNT_ENABLE, "perfcnt_enable" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 2d045a559c..fca7024d53 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3965,6 +3965,34 @@ MSG_HASH( MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_TIMEOUT, "While menu is active, a screensaver will be displayed after the specified period of inactivity." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION, + "Menu Screensaver Animation" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_ANIMATION, + "Enable an animation effect while the menu screensaver is active. Has a modest performance impact." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_SNOW, + "Snow" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_STARFIELD, + "Star Field" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_VORTEX, + "Vortex" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_SPEED, + "Menu Screensaver Animation Speed" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_ANIMATION_SPEED, + "Adjust speed of menu screensaver animation effect." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_MOUSE_ENABLE, "Mouse Support" diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index e6cfa468ac..a07a17a551 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -525,6 +525,10 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_savestate_resume, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_insert_disk_resume, MENU_ENUM_SUBLABEL_MENU_INSERT_DISK_RESUME) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_quit_on_close_content, MENU_ENUM_SUBLABEL_QUIT_ON_CLOSE_CONTENT) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_screensaver_timeout, MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_TIMEOUT) +#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_screensaver_animation, MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_ANIMATION) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_screensaver_animation_speed, MENU_ENUM_SUBLABEL_MENU_SCREENSAVER_ANIMATION_SPEED) +#endif DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_driver, MENU_ENUM_SUBLABEL_VIDEO_DRIVER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_driver, MENU_ENUM_SUBLABEL_AUDIO_DRIVER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_driver, MENU_ENUM_SUBLABEL_INPUT_DRIVER) @@ -2931,6 +2935,14 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_MENU_SCREENSAVER_TIMEOUT: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_screensaver_timeout); break; +#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE) + case MENU_ENUM_LABEL_MENU_SCREENSAVER_ANIMATION: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_screensaver_animation); + break; + case MENU_ENUM_LABEL_MENU_SCREENSAVER_ANIMATION_SPEED: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_screensaver_animation_speed); + break; +#endif case MENU_ENUM_LABEL_MENU_INPUT_SWAP_OK_CANCEL: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_swap_ok_cancel); break; diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index dcb29306ca..b95bce261d 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -39,6 +39,7 @@ #include "../../frontend/frontend_driver.h" #include "../menu_driver.h" +#include "../menu_screensaver.h" #include "../../gfx/gfx_animation.h" #include "../../gfx/gfx_thumbnail_path.h" @@ -105,6 +106,8 @@ typedef struct uint32_t nav_bar_icon_active; uint32_t nav_bar_icon_passive; uint32_t nav_bar_icon_disabled; + /* Screensaver */ + uint32_t screensaver_tint; /* Misc. colours */ uint32_t header_shadow; uint32_t landscape_border_shadow; @@ -150,6 +153,8 @@ static const materialui_theme_t materialui_theme_blue = { 0x0069c0, /* nav_bar_icon_active */ 0x9ea7aa, /* nav_bar_icon_passive */ 0xffffff, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xF5F5F6, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -195,6 +200,8 @@ static const materialui_theme_t materialui_theme_blue_grey = { 0x34515e, /* nav_bar_icon_active */ 0xaeaeae, /* nav_bar_icon_passive */ 0xffffff, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xF5F5F6, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -240,6 +247,8 @@ static const materialui_theme_t materialui_theme_dark_blue = { 0x6ec6ff, /* nav_bar_icon_active */ 0xA5B4BB, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xDEDEDE, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x3B3B3B, /* landscape_border_shadow */ @@ -285,6 +294,8 @@ static const materialui_theme_t materialui_theme_green = { 0x087f23, /* nav_bar_icon_active */ 0xaeaeae, /* nav_bar_icon_passive */ 0xffffff, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xF5F5F6, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -330,6 +341,8 @@ static const materialui_theme_t materialui_theme_red = { 0xba000d, /* nav_bar_icon_active */ 0xaeaeae, /* nav_bar_icon_passive */ 0xffffff, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xF5F5F6, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -375,6 +388,8 @@ static const materialui_theme_t materialui_theme_yellow = { 0xc6a700, /* nav_bar_icon_active */ 0xaeaeae, /* nav_bar_icon_passive */ 0xFFFFFF, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xF5F5F6, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -420,6 +435,8 @@ static const materialui_theme_t materialui_theme_nvidia_shield = { 0x7ab547, /* nav_bar_icon_active */ 0x558b2f, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xDEDEDE, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x3B3B3B, /* landscape_border_shadow */ @@ -465,6 +482,8 @@ static const materialui_theme_t materialui_theme_materialui = { 0x018786, /* nav_bar_icon_active */ 0xaeaeae, /* nav_bar_icon_passive */ 0xffffff, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xF5F5F6, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -510,6 +529,8 @@ static const materialui_theme_t materialui_theme_materialui_dark = { 0x03DAC6, /* nav_bar_icon_active */ 0x00a895, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xDEDEDE, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x3B3B3B, /* landscape_border_shadow */ @@ -555,6 +576,8 @@ static const materialui_theme_t materialui_theme_ozone_dark = { 0x00FFC5, /* nav_bar_icon_active */ 0xDADADA, /* nav_bar_icon_passive */ 0x242424, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xDADADA, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -600,6 +623,8 @@ static const materialui_theme_t materialui_theme_nord = { 0xD8DEE9, /* nav_bar_icon_active */ 0x81A1C1, /* nav_bar_icon_passive */ 0x242A33, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xD8DEE9, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -645,6 +670,8 @@ static const materialui_theme_t materialui_theme_gruvbox_dark = { 0xBF9137, /* nav_bar_icon_active */ 0xA89984, /* nav_bar_icon_passive */ 0x3C3836, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xEBDBB2, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -690,6 +717,8 @@ static const materialui_theme_t materialui_theme_solarized_dark = { 0x2AA198, /* nav_bar_icon_active */ 0x839496, /* nav_bar_icon_passive */ 0x00222B, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0x839496, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -735,6 +764,8 @@ static const materialui_theme_t materialui_theme_cutie_blue = { 0x3399FF, /* nav_bar_icon_active */ 0xDADADA, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xFFFFFF, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -780,6 +811,8 @@ static const materialui_theme_t materialui_theme_cutie_cyan = { 0x39859A, /* nav_bar_icon_active */ 0xDADADA, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xFFFFFF, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -825,6 +858,8 @@ static const materialui_theme_t materialui_theme_cutie_green = { 0x23A367, /* nav_bar_icon_active */ 0xDADADA, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xFFFFFF, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -870,6 +905,8 @@ static const materialui_theme_t materialui_theme_cutie_orange = { 0xCE6E1F, /* nav_bar_icon_active */ 0xDADADA, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xFFFFFF, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -915,6 +952,8 @@ static const materialui_theme_t materialui_theme_cutie_pink = { 0xD16FD8, /* nav_bar_icon_active */ 0xDADADA, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xFFFFFF, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -960,6 +999,8 @@ static const materialui_theme_t materialui_theme_cutie_purple = { 0x814FFF, /* nav_bar_icon_active */ 0xDADADA, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xFFFFFF, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -1005,6 +1046,8 @@ static const materialui_theme_t materialui_theme_cutie_red = { 0xCB1619, /* nav_bar_icon_active */ 0xDADADA, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xFFFFFF, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -1050,6 +1093,8 @@ static const materialui_theme_t materialui_theme_virtual_boy = { 0xF00000, /* nav_bar_icon_active */ 0xA10000, /* nav_bar_icon_passive */ 0x300000, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0xE60000, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x000000, /* landscape_border_shadow */ @@ -1095,6 +1140,8 @@ static const materialui_theme_t materialui_theme_hacking_the_kernel = { 0x00E02D, /* nav_bar_icon_active */ 0x008C00, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ + /* Screensaver */ + 0x00E000, /* screensaver_tint */ /* Misc. colours */ 0x000000, /* header_shadow */ 0x08ED8D, /* landscape_border_shadow */ @@ -1121,6 +1168,8 @@ typedef struct uint32_t list_hint_text; uint32_t list_hint_text_highlighted; uint32_t status_bar_text; + /* Screensaver */ + uint32_t screensaver_tint; /* Background colours */ float sys_bar_background[16]; float title_bar_background[16]; @@ -1155,7 +1204,6 @@ typedef struct float divider[16]; float entry_divider[16]; float screen_fade[16]; - float screensaver_bg[16]; float missing_thumbnail_icon[16]; float landscape_border_shadow_opacity; float status_bar_shadow_opacity; @@ -1557,8 +1605,10 @@ typedef struct materialui_handle materialui_font_data_t list; /* ptr alignment */ materialui_font_data_t hint; /* ptr alignment */ } font_data; + /* Thumbnail helpers */ gfx_thumbnail_path_data_t *thumbnail_path_data; + struct { materialui_playlist_icons_t playlist; /* ptr alignment */ @@ -1566,6 +1616,8 @@ typedef struct materialui_handle uintptr_t list[MUI_TEXTURE_LAST]; } textures; + menu_screensaver_t *screensaver; + /* Status bar */ materialui_status_bar_t status_bar; /* size_t alignment */ size_t last_stack_size; @@ -1850,10 +1902,6 @@ static void materialui_prepare_colors( current_theme->screen_fade, mui->colors.screen_fade, mui->colors.screen_fade_opacity); - /* Screensaver background is always pure black, - * 100% opacity */ - hex32_to_rgba_normalized(0x000000, mui->colors.screensaver_bg, 1.0f); - /* Shadow colours require special handling * (since they are gradients) */ mui->colors.header_shadow[11] = current_theme->header_shadow_opacity; @@ -1871,6 +1919,9 @@ static void materialui_prepare_colors( mui->colors.selection_marker_shadow_bottom[3] = current_theme->selection_marker_shadow_opacity; mui->colors.selection_marker_shadow_bottom[7] = current_theme->selection_marker_shadow_opacity; mui->colors.selection_marker_shadow_opacity = current_theme->selection_marker_shadow_opacity; + + /* Screensaver 'tint' */ + mui->colors.screensaver_tint = current_theme->screensaver_tint; } static const char *materialui_texture_path(unsigned id) @@ -3545,10 +3596,20 @@ static void materialui_render(void *data, /* Read pointer state */ menu_input_get_pointer_state(&mui->pointer); - /* If menu screensaver is active, no further - * action is required */ + /* If menu screensaver is active, update + * screensaver and return */ if (mui->show_screensaver) + { + menu_screensaver_iterate( + mui->screensaver, + p_disp, anim_get_ptr(), + (enum menu_screensaver_effect)settings->uints.menu_screensaver_animation, + settings->floats.menu_screensaver_animation_speed, + mui->colors.screensaver_tint, + width, height, + settings->paths.directory_assets); return; + } /* Need to adjust/range-check scroll position first, * otherwise cannot determine correct entry index for @@ -6701,25 +6762,17 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) if (!mui) return; - video_driver_set_viewport(video_width, video_height, true, false); - - /* If menu screensaver is active, blank the - * screen and skip drawing menu elements */ + /* If menu screensaver is active, draw + * screensaver and return */ if (mui->show_screensaver) { - gfx_display_draw_quad( - p_disp, - userdata, - video_width, - video_height, - 0, 0, - video_width, video_height, - video_width, video_height, - mui->colors.screensaver_bg); - video_driver_set_viewport(video_width, video_height, false, true); + menu_screensaver_frame(mui->screensaver, + video_info, p_disp); return; } + video_driver_set_viewport(video_width, video_height, true, false); + /* Clear text */ materialui_font_bind(&mui->font_data.title); materialui_font_bind(&mui->font_data.list); @@ -7808,6 +7861,11 @@ static void *materialui_init(void **userdata, bool video_is_threaded) settings->uints.menu_materialui_color_theme; materialui_prepare_colors(mui, (enum materialui_color_theme)mui->color_theme); + /* Initialise screensaver */ + mui->screensaver = menu_screensaver_init(); + if (!mui->screensaver) + goto error; + /* Initial ticker configuration */ mui->use_smooth_ticker = settings->bools.menu_ticker_smooth; mui->ticker_smooth.font_scale = 1.0f; @@ -7895,6 +7953,8 @@ static void materialui_free(void *data) materialui_free_playlist_icon_list(mui); p_anim->updatetime_cb = NULL; + + menu_screensaver_free(mui->screensaver); } static void materialui_context_bg_destroy(materialui_handle_t *mui) @@ -7960,6 +8020,9 @@ static void materialui_context_destroy(void *data) /* Free background/wallpaper textures */ materialui_context_bg_destroy(mui); + + /* Destroy screensaver context */ + menu_screensaver_context_destroy(mui->screensaver); } /* Note: This is only used for loading wallpaper @@ -8423,6 +8486,7 @@ static void materialui_context_reset(void *data, bool is_threaded) gfx_display_init_white_texture(gfx_display_white_texture); materialui_context_reset_textures(mui); materialui_context_reset_playlist_icons(mui); + menu_screensaver_context_destroy(mui->screensaver); if (path_is_valid(path_menu_wallpaper)) task_push_image_load(path_menu_wallpaper, diff --git a/menu/drivers/ozone/ozone.c b/menu/drivers/ozone/ozone.c index a9ac201d3d..06d414f7f0 100644 --- a/menu/drivers/ozone/ozone.c +++ b/menu/drivers/ozone/ozone.c @@ -705,10 +705,7 @@ static void *ozone_init(void **userdata, bool video_is_threaded) *userdata = ozone; for (i = 0; i < 15; i++) - { ozone->pure_white[i] = 1.00f; - ozone->pure_black[i] = 0.00f; - } ozone->last_width = width; ozone->last_height = height; @@ -744,6 +741,10 @@ static void *ozone_init(void **userdata, bool video_is_threaded) if (!ozone->thumbnail_path_data) goto error; + ozone->screensaver = menu_screensaver_init(); + if (!ozone->screensaver) + goto error; + ozone->fullscreen_thumbnails_available = false; ozone->show_fullscreen_thumbnails = false; ozone->animations.fullscreen_thumbnail_alpha = 0.0f; @@ -913,6 +914,8 @@ static void ozone_free(void *data) if (ozone->thumbnail_path_data) free(ozone->thumbnail_path_data); + + menu_screensaver_free(ozone->screensaver); } if (gfx_display_white_texture) @@ -1328,6 +1331,9 @@ static void ozone_context_reset(void *data, bool is_threaded) /* TODO: update savestate thumbnail image */ ozone_restart_cursor_animation(ozone); + + /* Screensaver */ + menu_screensaver_context_destroy(ozone->screensaver); } video_driver_monitor_reset(); } @@ -1399,6 +1405,9 @@ static void ozone_context_destroy(void *data) /* Horizontal list */ ozone_context_destroy_horizontal_list(ozone); + + /* Screensaver */ + menu_screensaver_context_destroy(ozone->screensaver); } static void *ozone_list_get_entry(void *data, @@ -1782,10 +1791,18 @@ static void ozone_render(void *data, /* Read pointer state */ menu_input_get_pointer_state(&ozone->pointer); - /* If menu screensaver is active, no further - * action is required */ + /* If menu screensaver is active, update + * screensaver and return */ if (ozone->show_screensaver) { + menu_screensaver_iterate( + ozone->screensaver, + p_disp, p_anim, + (enum menu_screensaver_effect)settings->uints.menu_screensaver_animation, + settings->floats.menu_screensaver_animation_speed, + ozone->theme->screensaver_tint, + width, height, + settings->paths.directory_assets); GFX_ANIMATION_CLEAR_ACTIVE(p_anim); return; } @@ -2950,25 +2967,17 @@ static void ozone_frame(void *data, video_frame_info_t *video_info) last_use_preferred_system_color_theme = use_preferred_system_color_theme; } - video_driver_set_viewport(video_width, video_height, true, false); - - /* If menu screensaver is active, blank the - * screen and skip drawing menu elements */ + /* If menu screensaver is active, draw + * screensaver and return */ if (ozone->show_screensaver) { - gfx_display_set_alpha(ozone->pure_black, 1.0f); - gfx_display_draw_quad( - p_disp, - userdata, - video_width, - video_height, - 0, 0, video_width, video_height, - video_width, video_height, - ozone->pure_black); - video_driver_set_viewport(video_width, video_height, false, true); + menu_screensaver_frame(ozone->screensaver, + video_info, p_disp); return; } + video_driver_set_viewport(video_width, video_height, true, false); + /* Clear text */ ozone_font_bind(&ozone->fonts.footer); ozone_font_bind(&ozone->fonts.title); diff --git a/menu/drivers/ozone/ozone.h b/menu/drivers/ozone/ozone.h index a9e27d1aea..d07d152f28 100644 --- a/menu/drivers/ozone/ozone.h +++ b/menu/drivers/ozone/ozone.h @@ -29,6 +29,7 @@ typedef struct ozone_handle ozone_handle_t; #include "../../gfx/gfx_display.h" #include "../../gfx/gfx_thumbnail_path.h" #include "../../gfx/gfx_thumbnail.h" +#include "../../menu_screensaver.h" #include "../../configuration.h" @@ -119,6 +120,7 @@ struct ozone_handle char *pending_message; file_list_t selection_buf_old; /* ptr alignment */ file_list_t horizontal_list; /* console tabs */ /* ptr alignment */ + menu_screensaver_t *screensaver; struct { @@ -212,7 +214,6 @@ struct ozone_handle float sidebar_offset; float last_scale_factor; float pure_white[16]; - float pure_black[16]; struct { diff --git a/menu/drivers/ozone/ozone_theme.c b/menu/drivers/ozone/ozone_theme.c index 3389c540c7..b1550f19ec 100644 --- a/menu/drivers/ozone/ozone_theme.c +++ b/menu/drivers/ozone/ozone_theme.c @@ -257,6 +257,9 @@ ozone_theme_t ozone_theme_light = { 0x374CFFFF, 0x878787FF, + /* Screensaver 'tint' (RGB24) */ + 0xEBEBEB, /* screensaver_tint */ + ozone_sidebar_background_light, ozone_sidebar_gradient_top_light, ozone_sidebar_gradient_bottom_light, @@ -286,6 +289,9 @@ ozone_theme_t ozone_theme_dark = { 0x00FFC5FF, 0x9F9FA1FF, + /* Screensaver 'tint' (RGB24) */ + 0xFFFFFF, /* screensaver_tint */ + ozone_sidebar_background_dark, ozone_sidebar_gradient_top_dark, ozone_sidebar_gradient_bottom_dark, @@ -318,6 +324,9 @@ ozone_theme_t ozone_theme_nord = { 0xA9C791FF, /* text_selected_rgba */ 0x8FBCBBFF, /* text_sublabel_rgba */ + /* Screensaver 'tint' (RGB24) */ + 0xECEFF4, /* screensaver_tint */ + /* Sidebar color */ ozone_sidebar_background_nord, /* sidebar_background */ ozone_sidebar_gradient_top_nord, /* sidebar_top_gradient */ @@ -352,6 +361,9 @@ ozone_theme_t ozone_theme_gruvbox_dark = { 0x8EC07CFF, /* text_selected_rgba */ 0xD79921FF, /* text_sublabel_rgba */ + /* Screensaver 'tint' (RGB24) */ + 0xEBDBB2, /* screensaver_tint */ + /* Sidebar color */ ozone_sidebar_background_gruvbox_dark, /* sidebar_background */ ozone_sidebar_gradient_top_gruvbox_dark, /* sidebar_top_gradient */ @@ -386,6 +398,9 @@ ozone_theme_t ozone_theme_boysenberry = { 0xFEBCFFFF, /* text_selected_rgba */ 0xD599FFFF, /* text_sublabel_rgba */ + /* Screensaver 'tint' (RGB24) */ + 0xFEBCFF, /* screensaver_tint */ + /* Sidebar color */ ozone_sidebar_background_boysenberry, /* sidebar_background */ ozone_sidebar_gradient_top_boysenberry, /* sidebar_top_gradient */ @@ -420,6 +435,9 @@ ozone_theme_t ozone_theme_hacking_the_kernel = { 0x83FF83FF, /* text_selected_rgba */ 0x53E63DFF, /* text_sublabel_rgba */ + /* Screensaver 'tint' (RGB24) */ + 0x00E528, /* screensaver_tint */ + /* Sidebar color */ ozone_sidebar_background_hacking_the_kernel, /* sidebar_background */ ozone_sidebar_gradient_top_hacking_the_kernel, /* sidebar_top_gradient */ @@ -454,6 +472,9 @@ ozone_theme_t ozone_theme_twilight_zone = { 0xB78CC8FF, /* text_selected_rgba */ 0x9A6C99FF, /* text_sublabel_rgba */ + /* Screensaver 'tint' (RGB24) */ + 0xFDFCFE, /* screensaver_tint */ + /* Sidebar color */ ozone_sidebar_background_twilight_zone, /* sidebar_background */ ozone_sidebar_gradient_top_twilight_zone, /* sidebar_top_gradient */ diff --git a/menu/drivers/ozone/ozone_theme.h b/menu/drivers/ozone/ozone_theme.h index 7e1bb76768..f5108a09d6 100644 --- a/menu/drivers/ozone/ozone_theme.h +++ b/menu/drivers/ozone/ozone_theme.h @@ -41,6 +41,9 @@ typedef struct ozone_theme uint32_t text_selected_rgba; uint32_t text_sublabel_rgba; + /* Screensaver 'tint' (RGB24) */ + uint32_t screensaver_tint; + /* Sidebar color */ float *sidebar_background; float *sidebar_top_gradient; diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 71a7c6cbba..9a60aec701 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -41,6 +41,7 @@ #include "../menu_driver.h" #include "../menu_entries.h" +#include "../menu_screensaver.h" #include "../../gfx/gfx_animation.h" #include "../../gfx/gfx_thumbnail_path.h" @@ -87,6 +88,12 @@ * smooth motion */ #define XMB_TAB_SWITCH_REPEAT_DELAY 99000 +/* XMB does not have a clean colour theme + * implementation. Until this is available, + * the menu screensaver tint will be set to + * a fixed colour: HTML WhiteSmoke */ +#define XMB_SCREENSAVER_TINT 0xF5F5F5 + #if 0 #define XMB_DEBUG #endif @@ -295,6 +302,8 @@ typedef struct xmb_handle video_font_raster_block_t raster_block; video_font_raster_block_t raster_block2; + menu_screensaver_t *screensaver; + gfx_thumbnail_path_data_t *thumbnail_path_data; struct { gfx_thumbnail_t right; @@ -3902,10 +3911,18 @@ static void xmb_render(void *data, /* Read pointer state */ menu_input_get_pointer_state(&xmb->pointer); - /* If menu screensaver is active, no further - * action is required */ + /* If menu screensaver is active, update + * screensaver and return */ if (xmb->show_screensaver) { + menu_screensaver_iterate( + xmb->screensaver, + p_disp, p_anim, + (enum menu_screensaver_effect)settings->uints.menu_screensaver_animation, + settings->floats.menu_screensaver_animation_speed, + XMB_SCREENSAVER_TINT, + width, height, + settings->paths.directory_assets); GFX_ANIMATION_CLEAR_ACTIVE(p_anim); return; } @@ -4713,31 +4730,17 @@ static void xmb_frame(void *data, video_frame_info_t *video_info) title_msg[0] = '\0'; title_truncated[0] = '\0'; - video_driver_set_viewport(video_width, video_height, true, false); - - /* If menu screensaver is active, blank the - * screen and skip drawing menu elements */ + /* If menu screensaver is active, draw + * screensaver and return */ if (xmb->show_screensaver) { - float black[16] = { - 0, 0, 0, 1, - 0, 0, 0, 1, - 0, 0, 0, 1, - 0, 0, 0, 1, - }; - gfx_display_draw_quad( - p_disp, - userdata, - video_width, - video_height, - 0, 0, - video_width, video_height, - video_width, video_height, - black); - video_driver_set_viewport(video_width, video_height, false, true); + menu_screensaver_frame(xmb->screensaver, + video_info, p_disp); return; } + video_driver_set_viewport(video_width, video_height, true, false); + pseudo_font_length = xmb->icon_spacing_horizontal * 4 - xmb->icon_size / 4.0f; left_thumbnail_margin_width = xmb->icon_size * 3.4f; right_thumbnail_margin_width = @@ -5739,6 +5742,11 @@ static void *xmb_init(void **userdata, bool video_is_threaded) xmb_init_ribbon(xmb); + /* Initialise screensaver */ + xmb->screensaver = menu_screensaver_init(); + if (!xmb->screensaver) + goto error; + /* Thumbnail initialisation */ xmb->thumbnail_path_data = gfx_thumbnail_path_init(); if (!xmb->thumbnail_path_data) @@ -5795,6 +5803,8 @@ static void xmb_free(void *data) if (xmb->thumbnail_path_data) free(xmb->thumbnail_path_data); + + menu_screensaver_free(xmb->screensaver); } if (gfx_display_white_texture) @@ -6313,6 +6323,8 @@ static void xmb_context_reset_internal(xmb_handle_t *xmb, xmb_context_reset_horizontal_list(xmb); + menu_screensaver_context_destroy(xmb->screensaver); + /* Only reload thumbnails if: * > Thumbnails are enabled * > This is a playlist, a database list, a file list @@ -6660,6 +6672,8 @@ static void xmb_context_destroy(void *data) xmb->font = NULL; xmb->font2 = NULL; + + menu_screensaver_context_destroy(xmb->screensaver); } static void xmb_toggle(void *userdata, bool menu_on) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index bb10a5e1bc..c48e9b0c65 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -79,6 +79,9 @@ #include "menu_shader.h" #endif #include "menu_dialog.h" +#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE) +#include "menu_screensaver.h" +#endif #include "../configuration.h" #include "../file_path_special.h" @@ -7298,8 +7301,14 @@ unsigned menu_displaylist_build_list( break; case DISPLAYLIST_USER_INTERFACE_SETTINGS_LIST: { - bool kiosk_mode_enable = settings->bools.kiosk_mode_enable; - bool desktop_menu_enable = settings->bools.desktop_menu_enable; + bool kiosk_mode_enable = settings->bools.kiosk_mode_enable; + bool desktop_menu_enable = settings->bools.desktop_menu_enable; + bool menu_screensaver_supported = menu_driver_screensaver_supported(); +#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE) + enum menu_screensaver_effect menu_screensaver_animation = + (enum menu_screensaver_effect)settings->uints.menu_screensaver_animation; +#endif + menu_displaylist_build_info_selective_t build_list[] = { {MENU_ENUM_LABEL_MENU_VIEWS_SETTINGS, PARSE_ACTION, true}, {MENU_ENUM_LABEL_MENU_SETTINGS, PARSE_ACTION, true}, @@ -7314,6 +7323,8 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_MENU_INSERT_DISK_RESUME, PARSE_ONLY_BOOL, true}, {MENU_ENUM_LABEL_QUIT_ON_CLOSE_CONTENT, PARSE_ONLY_UINT, true}, {MENU_ENUM_LABEL_MENU_SCREENSAVER_TIMEOUT, PARSE_ONLY_UINT, false}, + {MENU_ENUM_LABEL_MENU_SCREENSAVER_ANIMATION, PARSE_ONLY_UINT, false}, + {MENU_ENUM_LABEL_MENU_SCREENSAVER_ANIMATION_SPEED, PARSE_ONLY_FLOAT, false}, {MENU_ENUM_LABEL_MOUSE_ENABLE, PARSE_ONLY_BOOL, true}, {MENU_ENUM_LABEL_POINTER_ENABLE, PARSE_ONLY_BOOL, true}, {MENU_ENUM_LABEL_THREADED_DATA_RUNLOOP_ENABLE, PARSE_ONLY_BOOL, true}, @@ -7336,9 +7347,20 @@ unsigned menu_displaylist_build_list( build_list[i].checked = true; break; case MENU_ENUM_LABEL_MENU_SCREENSAVER_TIMEOUT: - if (menu_driver_screensaver_supported()) + if (menu_screensaver_supported) build_list[i].checked = true; break; +#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE) + case MENU_ENUM_LABEL_MENU_SCREENSAVER_ANIMATION: + if (menu_screensaver_supported) + build_list[i].checked = true; + break; + case MENU_ENUM_LABEL_MENU_SCREENSAVER_ANIMATION_SPEED: + if (menu_screensaver_supported && + (menu_screensaver_animation != MENU_SCREENSAVER_BLANK)) + build_list[i].checked = true; + break; +#endif case MENU_ENUM_LABEL_UI_COMPANION_TOGGLE: if (desktop_menu_enable) build_list[i].checked = true; diff --git a/menu/menu_screensaver.c b/menu/menu_screensaver.c new file mode 100644 index 0000000000..947c8a8fac --- /dev/null +++ b/menu/menu_screensaver.c @@ -0,0 +1,814 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2021 - Daniel De Matteis + * Copyright (C) 2019-2021 - James Leaver + * + * 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 +#include + +#include +#include +#include + +#include "../verbosity.h" + +#if defined(HAVE_CONFIG_H) +#include "../config.h" +#endif + +#include "menu_screensaver.h" + +#ifndef PI +#define PI 3.14159265359f +#endif + +/* Font path defines */ +#define MENU_SS_PKG_DIR "pkg" +#define MENU_SS_FONT_FILE "osd-font.ttf" + +/* Determines whether current platform has + * Unicode character support */ +#if defined(HAVE_FREETYPE) || (defined(__APPLE__) && defined(HAVE_CORETEXT)) || (defined(HAVE_STB_FONT) && (defined(VITA) || defined(WIIU) || defined(ANDROID) || (defined(_WIN32) && !defined(_XBOX) && !defined(_MSC_VER) && _MSC_VER >= 1400) || (defined(_WIN32) && !defined(_XBOX) && defined(_MSC_VER)) || defined(HAVE_LIBNX) || defined(__linux__) || defined (HAVE_EMSCRIPTEN) || defined(__APPLE__) || defined(HAVE_ODROIDGO2) || defined(__PS3__))) +#define MENU_SS_UNICODE_ENABLED true +#else +#define MENU_SS_UNICODE_ENABLED false +#endif + +/* 256 on-screen particles provides a good + * balance between effect density and draw + * performance */ +#define MENU_SS_NUM_PARTICLES 256 + +/* Particle effect animations update at a base rate + * of 60Hz (-> 16.666 ms update period) */ +#define MENU_SS_EFFECT_PERIOD ((1.0f / 60.0f) * 1000.0f) + +/* To produce a sharp image, font glyphs must + * be oversized and scaled down. Requested font + * size is: + * (smallest screen dimension) * MENU_SS_FONT_SIZE_FACTOR */ +#define MENU_SS_FONT_SIZE_FACTOR 0.1f + +/* On a 240p display, base particle size should + * be 4 pixels. To achieve this, we apply a global + * scaling factor of: + * ((smallest screen dimension) * MENU_SS_PARTICLE_SIZE_FACTOR) / (font size) */ +#define MENU_SS_PARTICLE_SIZE_FACTOR (4.0f / 240.0f) + +/* Produces a colour value in RGBA32 format + * from the specified 'tint' adjusted by the + * specified 'luminosity' + * > lum must not exceed 1.0f */ +#define MENU_SS_PARTICLE_COLOR(tint_r, tint_g, tint_b, lum) (((uint32_t)(tint_r * lum) << 24) | ((uint32_t)(tint_g * lum) << 16) | ((uint32_t)(tint_b * lum) << 8) | 0xFF) + +/* Definition of screensaver 'particle': + * - symbol: string representation of a font glyph + * - x: centre x-coordinate of draw position + * - y: centre y-coordinate of draw position + * - size: glyph scale factor when drawn + * (1.0 == default size) + * - a,b,c,d: general purpose effect-specific variables + * (e.g. velocity, radius, theta, ...) + * - color: particle colour in RGBA32 format */ +typedef struct +{ + uint32_t color; + float x; + float y; + float size; + float a; + float b; + float c; + float d; + const char *symbol; +} menu_ss_particle_t; + +/* Holds all objects + metadata corresponding + * to a font */ +typedef struct +{ + font_data_t *font; + video_font_raster_block_t raster_block; /* ptr alignment */ + float y_centre_offset; +} menu_ss_font_data_t; + +/* Holds values used to colourise a particle */ +typedef struct +{ + uint32_t color; + uint32_t r; + uint32_t g; + uint32_t b; +} menu_ss_particle_tint_t; + +struct menu_ss_handle +{ + float bg_color[16]; + menu_ss_font_data_t font_data; + unsigned last_width; + unsigned last_height; + float font_size; + float particle_scale; + menu_ss_particle_tint_t particle_tint; + menu_ss_particle_t *particles; + enum menu_screensaver_effect effect; + bool font_enabled; + bool unicode_enabled; +}; + +/* UTF-8 Symbols */ +static const char * const menu_ss_fallback_symbol = "*"; + +#define MENU_SS_NUM_SNOW_SYMBOLS 3 +static const char * const menu_ss_snow_symbols[] = { + "\xE2\x9D\x84", /* Snowflake, U+2744 */ + "\xE2\x9D\x85", /* Tight Trifoliate Snowflake, U+2745 */ + "\xE2\x9D\x86" /* Heavy Chevron Snowflake, U+2746 */ +}; + +#define MENU_SS_NUM_STARFIELD_SYMBOLS 8 +static const char * const menu_ss_starfield_symbols[] = { + "\xE2\x98\x85", /* Black Star, U+2605 */ + "\xE2\x9C\xA6", /* Black Four Pointed Star, U+2726 */ + "\xE2\x9C\xB4", /* Eight Pointed Black Star, U+2734 */ + "\xE2\x9C\xB6", /* Six Pointed Black Star, U+2736 */ + "\xE2\x9C\xB7", /* Eight Pointed Rectilinear Black Star, U+2737 */ + "\xE2\x9C\xB8", /* Heavy Eight Pointed Rectilinear Black Star, U+2738 */ + "\xE2\x9C\xB9", /* Twelve Pointed Black Star, U+2739 */ + "\xE2\x97\x8F" /* Black Circle, U+25CF */ +}; + +#define MENU_SS_NUM_VORTEX_SYMBOLS 6 +static const char * const menu_ss_vortex_symbols[] = { + "\xE2\x9D\x8B", /* Heavy Eight Teardrop-Spoked Propeller Asterisk, U+274B */ + "\xE2\x9C\xBD", /* Heavy Teardrop-Spoked Asterisk, U+273D */ + "\xE2\x9C\xBB", /* Teardrop-Spoked Asterisk, U+273B */ + "\xE2\x97\x89", /* Fisheye, U+25C9 */ + "\xE2\x97\x8F", /* Black Circle, U+25CF */ + "\xE2\x97\x86" /* Black Diamond, U+25C6 */ +}; + +/******************/ +/* Initialisation */ +/******************/ + +/* Creates and initialises a new screensaver object. + * Returned object must be freed using + * menu_screensaver_free(). + * Returns NULL in the event of an error. */ +menu_screensaver_t *menu_screensaver_init(void) +{ + menu_screensaver_t *screensaver = NULL; + /* Screensaver background must be pure black, + * 100% opacity */ + float bg_color[16] = COLOR_HEX_TO_FLOAT(0x000000, 1.0f); + + /* Create menu_screensaver_t object */ + screensaver = (menu_screensaver_t*)malloc(sizeof(*screensaver)); + + if (!screensaver) + return NULL; + + /* Initial effect is always 'blank' */ + screensaver->effect = MENU_SCREENSAVER_BLANK; + + /* > Fonts must be enabled for the screensaver to + * function. 'font_enabled' flag exists purely + * to prevent re-initialisation spam in the event + * that font creation fails + * > Initialise 'Unicode enabled' state based on + * compiler flags */ + screensaver->font_enabled = true; + screensaver->unicode_enabled = MENU_SS_UNICODE_ENABLED; + + /* Set background colour */ + memcpy(screensaver->bg_color, bg_color, sizeof(screensaver->bg_color)); + + /* Font is loaded on-demand + * (Don't waste memory unless we are actually + * drawing an effect) */ + memset(&screensaver->font_data, 0, sizeof(screensaver->font_data)); + + /* Particle array is created on-demand + * (Don't waste memory unless we are actually + * drawing an effect) */ + screensaver->particles = NULL; + screensaver->particle_tint.color = 0; + screensaver->particle_tint.r = 0; + screensaver->particle_tint.g = 0; + screensaver->particle_tint.b = 0; + + /* Initial dimensions are zeroed out - will be set + * on first call of menu_screensaver_iterate() */ + screensaver->last_width = 0; + screensaver->last_height = 0; + screensaver->font_size = 0.0f; + screensaver->particle_scale = 0.0f; + + return screensaver; +} + +/* Frees specified screensaver object */ +void menu_screensaver_free(menu_screensaver_t *screensaver) +{ + if (!screensaver) + return; + + /* Free font */ + if (screensaver->font_data.font) + { + gfx_display_font_free(screensaver->font_data.font); + video_coord_array_free(&screensaver->font_data.raster_block.carr); + screensaver->font_data.font = NULL; + + font_driver_bind_block(NULL, NULL); + } + + /* Free particle array */ + if (screensaver->particles) + free(screensaver->particles); + screensaver->particles = NULL; + + free(screensaver); +} + +/*********************/ +/* Context functions */ +/*********************/ + +/* Called when the graphics context is destroyed + * or reset (a dedicated 'reset' function is + * unnecessary) */ +void menu_screensaver_context_destroy(menu_screensaver_t *screensaver) +{ + if (!screensaver) + return; + + /* Free any existing font + * (will be recreated, if required, on the next + * call of menu_screensaver_iterate()) */ + if (screensaver->font_data.font) + { + gfx_display_font_free(screensaver->font_data.font); + video_coord_array_free(&screensaver->font_data.raster_block.carr); + screensaver->font_data.font = NULL; + } +} + +/**********************/ +/* Run loop functions */ +/**********************/ + +/* qsort() helpers */ +static int menu_ss_starfield_qsort_func(const menu_ss_particle_t *a, + const menu_ss_particle_t *b) +{ + return a->c > b->c ? -1 : 1; +} + +static int menu_ss_vortex_qsort_func(const menu_ss_particle_t *a, + const menu_ss_particle_t *b) +{ + return a->a < b->a ? -1 : 1; +} + +/* Calculates base font size and particle + * scale based on current screen dimensions */ +static INLINE void menu_screensaver_set_dimensions( + menu_screensaver_t *screensaver, + unsigned width, unsigned height) +{ + float screen_size = (float)((width < height) ? width : height); + screensaver->font_size = (screen_size * MENU_SS_FONT_SIZE_FACTOR) + 0.5f; + screensaver->particle_scale = (screen_size * MENU_SS_PARTICLE_SIZE_FACTOR) / screensaver->font_size; + screensaver->last_width = width; + screensaver->last_height = height; +} + +static bool menu_screensaver_init_effect(menu_screensaver_t *screensaver) +{ + unsigned width; + unsigned height; + size_t i; + + /* Create particle array, if required */ + if (!screensaver->particles) + { + screensaver->particles = (menu_ss_particle_t*) + calloc(MENU_SS_NUM_PARTICLES, sizeof(*screensaver->particles)); + + if (!screensaver->particles) + return false; + } + + width = screensaver->last_width; + height = screensaver->last_height; + + /* Initialise array */ + switch (screensaver->effect) + { + case MENU_SCREENSAVER_SNOW: + { + for (i = 0; i < MENU_SS_NUM_PARTICLES; i++) + { + menu_ss_particle_t *particle = &screensaver->particles[i]; + float size_factor; + + particle->x = (float)(rand() % width); + particle->y = (float)(rand() % height); + particle->a = (float)(rand() % 64 - 16) * 0.1f; + particle->b = (float)(rand() % 64 - 48) * 0.1f; + + /* Get particle size */ + size_factor = (float)i / (float)MENU_SS_NUM_PARTICLES; + size_factor = size_factor * size_factor; + particle->size = 1.0f + (size_factor * 2.0f); + + /* If Unicode is supported, select a random + * snowflake symbol */ + particle->symbol = menu_ss_fallback_symbol; + if (screensaver->unicode_enabled) + particle->symbol = menu_ss_snow_symbols[(unsigned)(rand() % MENU_SS_NUM_SNOW_SYMBOLS)]; + } + } + break; + case MENU_SCREENSAVER_STARFIELD: + { + float max_depth = (float)(width > height ? width : height); + float initial_speed_factor = 0.02f * max_depth / 240.0f; + + for (i = 0; i < MENU_SS_NUM_PARTICLES; i++) + { + menu_ss_particle_t *particle = &screensaver->particles[i]; + + /* x pos ('physical' space) */ + particle->a = (float)(rand() % width); + /* y pos ('physical' space) */ + particle->b = (float)(rand() % height); + /* depth */ + particle->c = max_depth; + /* speed */ + particle->d = 1.0f + ((float)(rand() % 20) * initial_speed_factor); + + /* If Unicode is supported, select a random + * star symbol */ + particle->symbol = menu_ss_fallback_symbol; + if (screensaver->unicode_enabled) + particle->symbol = menu_ss_starfield_symbols[(unsigned)(rand() % MENU_SS_NUM_STARFIELD_SYMBOLS)]; + } + } + break; + case MENU_SCREENSAVER_VORTEX: + { + float min_screen_dimension = (float)(width < height ? width : height); + float max_radius = (float)sqrt((double)((width * width) + (height * height))) / 2.0f; + float radial_speed_factor = 0.001f * min_screen_dimension / 240.0f; + + for (i = 0; i < MENU_SS_NUM_PARTICLES; i++) + { + menu_ss_particle_t *particle = &screensaver->particles[i]; + + /* radius */ + particle->a = 1.0f + (((float)rand() / (float)RAND_MAX) * max_radius); + /* theta */ + particle->b = ((float)rand() / (float)RAND_MAX) * 2.0f * PI; + /* radial speed */ + particle->c = (float)((rand() % 100) + 1) * radial_speed_factor; + /* rotational speed */ + particle->d = (((float)((rand() % 50) + 1) * 0.005f) + 0.1f) * (PI / 360.0f); + + /* If Unicode is supported, select a random + * star symbol */ + particle->symbol = menu_ss_fallback_symbol; + if (screensaver->unicode_enabled) + particle->symbol = menu_ss_vortex_symbols[(unsigned)(rand() % MENU_SS_NUM_VORTEX_SYMBOLS)]; + } + } + break; + default: + /* Error condition - do nothing */ + return false; + } + + return true; +} + +/* Checks for and applies any pending screensaver + * state changes */ +static bool menu_screensaver_update_state( + menu_screensaver_t *screensaver, gfx_display_t *p_disp, + enum menu_screensaver_effect effect, uint32_t particle_tint, + unsigned width, unsigned height, const char *dir_assets) +{ + bool init_effect = false; + + /* Check if dimensions have changed */ + if ((screensaver->last_width != width) || + (screensaver->last_height != height)) + { + menu_screensaver_set_dimensions(screensaver, width, height); + + /* Free any existing font */ + if (screensaver->font_data.font) + { + gfx_display_font_free(screensaver->font_data.font); + video_coord_array_free(&screensaver->font_data.raster_block.carr); + screensaver->font_data.font = NULL; + } + + init_effect = true; + } + + /* Check if effect has changed */ + if (screensaver->effect != effect) + { + screensaver->effect = effect; + init_effect = true; + } + + /* Check if particle tint has changed */ + if (screensaver->particle_tint.color != particle_tint) + { + screensaver->particle_tint.color = particle_tint; + screensaver->particle_tint.r = (particle_tint >> 16) & 0xFF; + screensaver->particle_tint.g = (particle_tint >> 8) & 0xFF; + screensaver->particle_tint.b = (particle_tint ) & 0xFF; + } + + /* Create font, if required */ + if ((screensaver->effect != MENU_SCREENSAVER_BLANK) && + !screensaver->font_data.font && + screensaver->font_enabled) + { +#if defined(HAVE_FREETYPE) || (defined(__APPLE__) && defined(HAVE_CORETEXT)) || defined(HAVE_STB_FONT) + char font_file[PATH_MAX_LENGTH]; + char pkg_path[PATH_MAX_LENGTH]; + + font_file[0] = '\0'; + pkg_path[0] = '\0'; + + /* Get font file path */ + if (!string_is_empty(dir_assets)) + fill_pathname_join(pkg_path, dir_assets, MENU_SS_PKG_DIR, sizeof(pkg_path)); + else + strlcpy(pkg_path, MENU_SS_PKG_DIR, sizeof(pkg_path)); + + fill_pathname_join(font_file, pkg_path, MENU_SS_FONT_FILE, + sizeof(font_file)); + + /* Warn if font file is missing */ + if (!path_is_valid(font_file)) + { + RARCH_WARN("[Menu Screensaver] Font asset missing: %s\n", font_file); + screensaver->unicode_enabled = false; + } +#else + /* On platforms without TTF support, there is + * no need to generate a font path (a bitmap + * font will be created automatically) */ + char font_file[PATH_MAX_LENGTH]; + font_file[0] = '\0'; +#endif + + /* Create font */ + screensaver->font_data.font = gfx_display_font_file(p_disp, + font_file, screensaver->font_size, + video_driver_is_threaded()); + + /* If font was created successfully, fetch metadata */ + if (screensaver->font_data.font) + screensaver->font_data.y_centre_offset = + (float)font_driver_get_line_centre_offset( + screensaver->font_data.font, 1.0f); + /* In case of error, warn and disable + * further attempts to create fonts */ + else + { + RARCH_WARN("[Menu Screensaver] Failed to initialise font - animation disabled\n"); + screensaver->font_enabled = false; + return false; + } + } + + /* Initialise animation effect, if required */ + if (init_effect) + return menu_screensaver_init_effect(screensaver); + + return true; +} + +/* Processes screensaver animation logic + * Called every frame on the main thread */ +void menu_screensaver_iterate( + menu_screensaver_t *screensaver, + gfx_display_t *p_disp, gfx_animation_t *p_anim, + enum menu_screensaver_effect effect, float effect_speed, + uint32_t particle_tint, unsigned width, unsigned height, + const char *dir_assets) +{ + float base_particle_size; + uint32_t tint_r; + uint32_t tint_g; + uint32_t tint_b; + float global_speed_factor; + size_t i; + + if (!screensaver) + return; + + /* Apply pending state changes */ + if (!menu_screensaver_update_state( + screensaver, p_disp, + effect, particle_tint, + width, height, dir_assets) || + (screensaver->effect == MENU_SCREENSAVER_BLANK) || + !screensaver->particles) + return; + + base_particle_size = screensaver->particle_scale * screensaver->font_size; + tint_r = screensaver->particle_tint.r; + tint_g = screensaver->particle_tint.g; + tint_b = screensaver->particle_tint.b; + + /* Set global animation speed */ + global_speed_factor = p_anim->delta_time / MENU_SS_EFFECT_PERIOD; + if (effect_speed > 0.0001f) + global_speed_factor *= effect_speed; + + /* Update particle array */ + switch (screensaver->effect) + { + case MENU_SCREENSAVER_SNOW: + for (i = 0; i < MENU_SS_NUM_PARTICLES; i++) + { + menu_ss_particle_t *particle = &screensaver->particles[i]; + float particle_size_px = particle->size * base_particle_size; + bool update_symbol = false; + float luminosity; + + /* Update particle 'speed' */ + particle->a = particle->a + (float)(rand() % 16 - 9) * 0.01f; + particle->b = particle->b + (float)(rand() % 16 - 7) * 0.01f; + + particle->a = (particle->a < -0.4f) ? -0.4f : particle->a; + particle->a = (particle->a > 0.1f) ? 0.1f : particle->a; + + particle->b = (particle->b < -0.1f) ? -0.1f : particle->b; + particle->b = (particle->b > 0.4f) ? 0.4f : particle->b; + + /* Update particle location */ + particle->x = particle->x + (global_speed_factor * particle->size * particle->a); + particle->y = particle->y + (global_speed_factor * particle->size * particle->b); + + /* Get particle colour */ + luminosity = 0.5f + (particle->size / 6.0f); + particle->color = MENU_SS_PARTICLE_COLOR(tint_r, tint_g, tint_b, luminosity); + + /* Reset particle if it has fallen off screen */ + if (particle->x < -particle_size_px) + { + particle->x = (float)width + particle_size_px; + update_symbol = true; + } + + if (particle->y > (float)height + particle_size_px) + { + particle->y = -particle_size_px; + update_symbol = true; + } + + if (update_symbol && screensaver->unicode_enabled) + particle->symbol = menu_ss_snow_symbols[(unsigned)(rand() % MENU_SS_NUM_SNOW_SYMBOLS)]; + } + break; + case MENU_SCREENSAVER_STARFIELD: + { + float max_depth = (float)(width > height ? width : height); + float initial_speed_factor = 0.02f * max_depth / 240.0f; + float focal_length = max_depth * 2.0f; + float x_centre = (float)(width >> 1); + float y_centre = (float)(height >> 1); + float particle_size_px; + float luminosity; + + /* Based on an example found here: + * https://codepen.io/nodws/pen/pejBNb */ + for (i = 0; i < MENU_SS_NUM_PARTICLES; i++) + { + menu_ss_particle_t *particle = &screensaver->particles[i]; + + /* Get particle size */ + particle->size = focal_length / (2.0f * particle->c); + particle_size_px = particle->size * base_particle_size; + + /* Update depth */ + particle->c -= particle->d * global_speed_factor; + + /* Reset particle if it has: + * - Dropped off the edge of the screen + * - Reached the screen depth */ + if ((particle->x < -particle_size_px) || + (particle->x > (float)width + particle_size_px) || + (particle->y < -particle_size_px) || + (particle->y > (float)height + particle_size_px) || + (particle->c <= 0.0f)) + { + /* x pos ('physical' space) */ + particle->a = (float)(rand() % width); + /* y pos ('physical' space) */ + particle->b = (float)(rand() % height); + /* depth */ + particle->c = max_depth; + /* speed */ + particle->d = 1.0f + ((float)(rand() % 20) * initial_speed_factor); + + /* Reset size */ + particle->size = 1.0f; + + /* If Unicode is supported, select a random + * star symbol */ + if (screensaver->unicode_enabled) + particle->symbol = menu_ss_starfield_symbols[(unsigned)(rand() % MENU_SS_NUM_STARFIELD_SYMBOLS)]; + } + + /* Get particle location */ + particle->x = (particle->a - x_centre) * (focal_length / particle->c); + particle->x += x_centre; + + particle->y = (particle->b - y_centre) * (focal_length / particle->c); + particle->y += y_centre; + + /* Get particle colour */ + luminosity = 0.25f + (0.75f - (particle->c / max_depth) * 0.75f); + particle->color = MENU_SS_PARTICLE_COLOR(tint_r, tint_g, tint_b, luminosity); + } + + /* Particles must be drawn in order of depth, + * from furthest away to nearest */ + qsort(screensaver->particles, + MENU_SS_NUM_PARTICLES, sizeof(menu_ss_particle_t), + (int (*)(const void *, const void *))menu_ss_starfield_qsort_func); + } + break; + case MENU_SCREENSAVER_VORTEX: + { + float min_screen_dimension = (float)(width < height ? width : height); + float max_radius = (float)sqrt((double)((width * width) + (height * height))) / 2.0f; + float radial_speed_factor = 0.001f * min_screen_dimension / 240.0f; + float x_centre = (float)(width >> 1); + float y_centre = (float)(height >> 1); + float r_speed; + float theta_speed; + float size_factor; + float luminosity; + + for (i = 0; i < MENU_SS_NUM_PARTICLES; i++) + { + menu_ss_particle_t *particle = &screensaver->particles[i]; + + /* Update particle speed */ + r_speed = particle->c * global_speed_factor; + theta_speed = particle->d * global_speed_factor; + if ((particle->a > 0.0f) && (particle->a < min_screen_dimension)) + { + float base_scale_factor = (min_screen_dimension - particle->a) / min_screen_dimension; + r_speed *= 1.0f + (base_scale_factor * 8.0f); + theta_speed *= 1.0f + (base_scale_factor * base_scale_factor * 6.0f); + } + particle->a -= r_speed; + particle->b += theta_speed; + + /* Reset particle if it has reached the centre of the screen */ + if (particle->a < 0.0f) + { + /* radius + * Note: In theory, this should be: + * > particle->a = max_radius; + * ...but it turns out that spawning new particles at random + * locations produces a more visually appealing result... */ + particle->a = 1.0f + (((float)rand() / (float)RAND_MAX) * max_radius); + /* theta */ + particle->b = ((float)rand() / (float)RAND_MAX) * 2.0f * PI; + /* radial speed */ + particle->c = (float)((rand() % 100) + 1) * radial_speed_factor; + /* rotational speed */ + particle->d = (((float)((rand() % 50) + 1) * 0.005f) + 0.1f) * (PI / 360.0f); + + /* If Unicode is supported, select a random + * star symbol */ + if (screensaver->unicode_enabled) + particle->symbol = menu_ss_vortex_symbols[(unsigned)(rand() % MENU_SS_NUM_VORTEX_SYMBOLS)]; + } + + /* Get particle location */ + particle->x = (particle->a * cos(particle->b)) + x_centre; + particle->y = (particle->a * sin(particle->b)) + y_centre; + + /* Get particle size */ + size_factor = 1.0f - ((max_radius - particle->a) / max_radius); + particle->size = sqrt(size_factor) * 2.5f; + + /* Get particle colour */ + luminosity = 0.2f + (0.8f - size_factor * 0.8f); + particle->color = MENU_SS_PARTICLE_COLOR(tint_r, tint_g, tint_b, luminosity); + } + + /* Particles must be drawn in order of radius; + * particles closest to the centre are further away + * from the screen, and must be drawn first */ + qsort(screensaver->particles, + MENU_SS_NUM_PARTICLES, sizeof(menu_ss_particle_t), + (int (*)(const void *, const void *))menu_ss_vortex_qsort_func); + } + break; + default: + /* Error condition - do nothing */ + break; + } +} + +/* Draws screensaver + * Called every frame (on the video thread, + * if threaded video is on) */ +void menu_screensaver_frame(menu_screensaver_t *screensaver, + video_frame_info_t *video_info, gfx_display_t *p_disp) +{ + void *userdata = NULL; + unsigned video_width; + unsigned video_height; + + if (!screensaver) + return; + + video_width = video_info->width; + video_height = video_info->height; + userdata = video_info->userdata; + + /* Set viewport */ + video_driver_set_viewport(video_width, video_height, true, false); + + /* Draw background */ + gfx_display_draw_quad( + p_disp, + userdata, + video_width, + video_height, + 0, 0, + screensaver->last_width, screensaver->last_height, + screensaver->last_width, screensaver->last_height, + screensaver->bg_color); + + /* Draw particle effect, if required */ + if ((screensaver->effect != MENU_SCREENSAVER_BLANK) && + screensaver->font_data.font && + screensaver->particles) + { + font_data_t *font = screensaver->font_data.font; + float y_centre_offset = screensaver->font_data.y_centre_offset; + float particle_scale = screensaver->particle_scale; + size_t i; + + /* Bind font */ + font_driver_bind_block(font, &screensaver->font_data.raster_block); + screensaver->font_data.raster_block.carr.coords.vertices = 0; + + /* Render text */ + for (i = 0; i < MENU_SS_NUM_PARTICLES; i++) + { + menu_ss_particle_t *particle = &screensaver->particles[i]; + + gfx_display_draw_text( + font, + particle->symbol, + particle->x, + particle->y + y_centre_offset, + video_width, video_height, + particle->color, + TEXT_ALIGN_CENTER, + particle->size * particle_scale, + false, 0.0f, true); + } + + /* Flush text and unbind font */ + if (screensaver->font_data.raster_block.carr.coords.vertices != 0) + { + font_driver_flush(video_width, video_height, font); + screensaver->font_data.raster_block.carr.coords.vertices = 0; + } + font_driver_bind_block(font, NULL); + } + + /* Unset viewport */ + video_driver_set_viewport(video_width, video_height, false, true); +} diff --git a/menu/menu_screensaver.h b/menu/menu_screensaver.h new file mode 100644 index 0000000000..5ded79429b --- /dev/null +++ b/menu/menu_screensaver.h @@ -0,0 +1,87 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2021 - Daniel De Matteis + * Copyright (C) 2019-2021 - James Leaver + * + * 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 . + */ + +#ifndef _MENU_SCREENSAVER_H +#define _MENU_SCREENSAVER_H + +#include +#include + +#include "../retroarch.h" +#include "../gfx/gfx_display.h" +#include "../gfx/gfx_animation.h" + +RETRO_BEGIN_DECLS + +/* Prevent direct access to menu_screensaver_t members */ +typedef struct menu_ss_handle menu_screensaver_t; + +/* Specifies all available screensaver effects */ +enum menu_screensaver_effect +{ + MENU_SCREENSAVER_BLANK = 0, + MENU_SCREENSAVER_SNOW, + MENU_SCREENSAVER_STARFIELD, + MENU_SCREENSAVER_VORTEX, + MENU_SCREENSAVER_LAST +}; + +/******************/ +/* Initialisation */ +/******************/ + +/* Creates a new, 'blank' screensaver object. Auxiliary + * internal structures will be initialised on the first + * call of menu_screensaver_iterate(). + * Returned object must be freed using menu_screensaver_free(). + * Returns NULL in the event of an error. */ +menu_screensaver_t *menu_screensaver_init(void); + +/* Frees specified screensaver object */ +void menu_screensaver_free(menu_screensaver_t *screensaver); + +/*********************/ +/* Context functions */ +/*********************/ + +/* Called when the graphics context is destroyed + * or reset (a dedicated 'reset' function is + * unnecessary) */ +void menu_screensaver_context_destroy(menu_screensaver_t *screensaver); + +/**********************/ +/* Run loop functions */ +/**********************/ + +/* Processes screensaver animation logic + * Called every frame on the main thread + * (Note: particle_tint is in RGB24 format) */ +void menu_screensaver_iterate( + menu_screensaver_t *screensaver, + gfx_display_t *p_disp, gfx_animation_t *p_anim, + enum menu_screensaver_effect effect, float effect_speed, + uint32_t particle_tint, unsigned width, unsigned height, + const char *dir_assets); + +/* Draws screensaver + * Called every frame (on the video thread, + * if threaded video is on) */ +void menu_screensaver_frame(menu_screensaver_t *screensaver, + video_frame_info_t *video_info, gfx_display_t *p_disp); + +RETRO_END_DECLS + +#endif diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 7fb5a724b2..43fa36f00b 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -6508,6 +6508,44 @@ static void setting_get_string_representation_uint_menu_screensaver_timeout( msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SECONDS)); } +#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE) +static void setting_get_string_representation_uint_menu_screensaver_animation( + rarch_setting_t *setting, + char *s, size_t len) +{ + if (!setting) + return; + + switch (*setting->value.target.unsigned_integer) + { + case MENU_SCREENSAVER_BLANK: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_OFF), + len); + break; + case MENU_SCREENSAVER_SNOW: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_SNOW), + len); + break; + case MENU_SCREENSAVER_STARFIELD: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_STARFIELD), + len); + break; + case MENU_SCREENSAVER_VORTEX: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_VORTEX), + len); + break; + } +} +#endif + enum setting_type menu_setting_get_browser_selection_type(rarch_setting_t *setting) { if (!setting) @@ -14266,6 +14304,46 @@ static bool setting_append_list( &setting_get_string_representation_uint_menu_screensaver_timeout; menu_settings_list_current_add_range(list, list_info, 0, 1800, 10, true, true); +#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE) + if (string_is_equal(settings->arrays.menu_driver, "glui") || + string_is_equal(settings->arrays.menu_driver, "xmb") || + string_is_equal(settings->arrays.menu_driver, "ozone")) + { + CONFIG_UINT( + list, list_info, + &settings->uints.menu_screensaver_animation, + MENU_ENUM_LABEL_MENU_SCREENSAVER_ANIMATION, + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION, + DEFAULT_MENU_SCREENSAVER_ANIMATION, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + (*list)[list_info->index - 1].action_left = &setting_uint_action_left_with_refresh; + (*list)[list_info->index - 1].action_right = &setting_uint_action_right_with_refresh; + (*list)[list_info->index - 1].get_string_representation = + &setting_get_string_representation_uint_menu_screensaver_animation; + menu_settings_list_current_add_range(list, list_info, 0, MENU_SCREENSAVER_LAST-1, 1, true, true); + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_COMBOBOX; + + CONFIG_FLOAT( + list, list_info, + &settings->floats.menu_screensaver_animation_speed, + MENU_ENUM_LABEL_MENU_SCREENSAVER_ANIMATION_SPEED, + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_SPEED, + DEFAULT_MENU_SCREENSAVER_ANIMATION_SPEED, + "%.1fx", + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + menu_settings_list_current_add_range(list, list_info, 0.1, 10.0, 0.1, true, true); + } +#endif CONFIG_BOOL( list, list_info, &settings->bools.menu_mouse_enable, diff --git a/msg_hash.h b/msg_hash.h index 761fca338a..972a8087df 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1102,6 +1102,13 @@ enum msg_hash_enums MENU_LABEL(DYNAMIC_WALLPAPER), MENU_LABEL(PAUSE_NONACTIVE), MENU_LABEL(MENU_SCREENSAVER_TIMEOUT), + MENU_LABEL(MENU_SCREENSAVER_ANIMATION), + MENU_LABEL(MENU_SCREENSAVER_ANIMATION_SPEED), + + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_SNOW, + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_STARFIELD, + MENU_ENUM_LABEL_VALUE_MENU_SCREENSAVER_ANIMATION_VORTEX, + MENU_LABEL(MOUSE_ENABLE), MENU_LABEL(POINTER_ENABLE), MENU_LABEL(MENU_RGUI_BORDER_FILLER_ENABLE),