RetroArch/menu/drivers/xmb.c
Cthulhu-throwaway 690c802921
Netplay Stuff (#13375)
* Netplay Stuff

## PROTOCOL FALLBACK
In order to support older clients a protocol fallback system was introduced.
The host will no longer send its header automatically after a TCP connection is established, instead, it awaits for the client to send his before determining which protocol this connection is going to operate on.
Netplay has now two protocols, a low protocol and a high protocol; the low protocol is the minimum protocol it supports, while the high protocol is the highest protocol it can operate on.
To fully support older clients, a hack was necessary: sending the high protocol in the unused client's header salt field, while keeping the protocol field to the low protocol. Without this hack we would only be able to support older clients if a newer client was the host.
Any future system can make use of this system by checking connection->netplay_protocol, which is available for both the client and host.

## NETPLAY CHAT
Starting with protocol 6, netplay chat is available through the new NETPLAY_CMD_PLAYER_CHAT command.
Limitations of the command code, which causes a disconnection on unknown commands, makes this system not possible on protocol 5.
Protocol 5 connections can neither send nor receive chat, but other netplay operations are unaffected.
Clients send chat as a string to the server, and it's the server's sole responsability to relay chat messages.
As of now, sending chat uses RetroArch's input menu, while the display of on-screen chat uses a widget overlay and RetroArch's notifications as a fallback.
If a new overlay and/or input system is desired, no backwards compatibility changes need to be made.
Only clients in playing mode (as opposed to spectating mode) can send and receive chat.

## SETTINGS SHARING
Some settings are better used when both host and clients share the same configuration.
As of protocol 6, the following settings will be shared from host to clients (without altering a client's configuration file): input latency frames and allow pausing.

## NETPLAY TUNNEL/MITM
With the current MITM system being defunct (at least as of 1.9.X), a new system was in order to solve most if not all of the problems with the current system.
This new system uses a tunneling approach, which is similar to most VPN and tunneling services around.

Tunnel commands:
RATS[unique id] (RetroArch Tunnel Session) - 16 bytes -> When this command is sent with a zeroed unique id, the tunnel server interprets this as a netplay host wanting to create a new session, in this case, the same command is returned to the host, but now with its unique session id. When a client needs to connect to a host, this command is sent with the unique session id of the host, causing the tunnel server to send a RATL command to the host.
RATL[unique id] (RetroArch Tunnel Link) - 16 bytes -> The tunnel server sends this command to the host when a client wants to connect to the host. Once the host receives this command, it establishes a new connection to the tunnel server, sending this command together with the client's unique id through this new connection, causing the tunnel server to link this connection to the connection of the client.
RATP (RetroArch Tunnel Ping) - 4 bytes -> The tunnel server sends this command to verify that the host, whom the session belongs to, is still around. The host replies with the same command. A session is closed if the tunnel server can not verify that the host is alive.

Operations:
Host -> Instead of listening and accepting connections, it connects to the tunnel server, requests a new session and then monitor this connection for new linking requests. Once a request is received, it establishes a new connection to the tunnel server for linking with a client. The tunnel server's address and port are obtained by querying the lobby server. The host will publish its session id together with the rest of its info to the lobby server.
Client -> It connects to the tunnel server and then sends the session id of the host it wants to connect to. A host's session id is obtained from the json data sent by the lobby server.

Improvements (from current MITM system):
No longer a risk of TCP port exhaustion; we only use one port now at the tunnel server.
Very little cpu usage. About 95% net I/O bound now.
Future backwards compatible with any and all changes to netplay as it no longer runs any netplay logic at MITM servers.
No longer operates the host in client mode, which was a source of many of the current problems.
Cleaner and more maintainable system and code.

Notable functions:
netplay_mitm_query -> Grabs the tunnel's address and port from the lobby server.
init_tcp_socket -> Handles the creation and operation mode of the TCP socket based on whether it's host, host+MITM or client.
handle_mitm_connection -> Creates and completes linking connections and replies to ping commands (only 1 of each per call to not affect performance).

## MISC
Ping Limiter: If a client's estimated latency to the server is higher than this value, connection will be dropped just before finishing the netplay handshake.
Ping Counter: A ping counter (similar to the FPS one) can be shown in the bottom right corner of the screen, if you are connected to a host.
LAN Discovery: Refactored and moved to its own "Refresh Netplay LAN List" button.

## FIXES
Many minor fixes to the current netplay implementation are also included.

* Remove NETPLAY_TEST_BUILD
2021-12-19 16:58:01 +01:00

7442 lines
242 KiB
C

/* RetroArch - A frontend for libretro.
* Copyright (C) 2011-2017 - Daniel De Matteis
* Copyright (C) 2014-2017 - Jean-André Santoni
* Copyright (C) 2016-2019 - Brad Parker
* Copyright (C) 2018 - Alfredo Monclús
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <file/file_path.h>
#include <compat/posix_string.h>
#include <compat/strl.h>
#include <formats/image.h>
#include <string/stdstring.h>
#include <lists/string_list.h>
#include <gfx/math/matrix_4x4.h>
#include <streams/file_stream.h>
#include <encodings/utf.h>
#include <features/features_cpu.h>
#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif
#include "../../frontend/frontend_driver.h"
#include "../menu_driver.h"
#include "../menu_entries.h"
#include "../menu_screensaver.h"
#include "../../gfx/gfx_animation.h"
#include "../../gfx/gfx_thumbnail_path.h"
#include "../../gfx/gfx_thumbnail.h"
#include "../../core_info.h"
#include "../../core.h"
#include "../../input/input_osk.h"
#include "../../file_path_special.h"
#include "../../verbosity.h"
#include "../../configuration.h"
#include "../../tasks/tasks_internal.h"
#ifdef HAVE_CHEEVOS
#include "../../cheevos/cheevos_menu.h"
#endif
#include "../../content.h"
#define XMB_RIBBON_ROWS 64
#define XMB_RIBBON_COLS 64
#define XMB_RIBBON_VERTICES 2*XMB_RIBBON_COLS*XMB_RIBBON_ROWS-2*XMB_RIBBON_COLS
#ifndef XMB_DELAY
#define XMB_DELAY 166.66667f
#endif
/* Specifies minimum period (in usec) between
* tab switch events when input repeat is
* active (i.e. when navigating between top level
* menu categories by *holding* left/right on
* RetroPad or keyboard)
* > Note: We want to set a value of 100 ms
* here, but doing so leads to bad pacing when
* running at 60 Hz (due to random frame time
* deviations - input repeat cycles always take
* slightly more or less than 100 ms, so tab
* switches occur every n or (n + 1) frames,
* which gives the appearance of stuttering).
* Reducing the delay by 1 ms accommodates
* any timing fluctuations, resulting in
* 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
enum
{
XMB_TEXTURE_MAIN_MENU = 0,
XMB_TEXTURE_SETTINGS,
XMB_TEXTURE_HISTORY,
XMB_TEXTURE_FAVORITES,
XMB_TEXTURE_MUSICS,
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
XMB_TEXTURE_MOVIES,
#endif
#ifdef HAVE_NETWORKING
XMB_TEXTURE_NETPLAY,
XMB_TEXTURE_ROOM,
XMB_TEXTURE_ROOM_LAN,
XMB_TEXTURE_ROOM_RELAY,
#endif
#ifdef HAVE_IMAGEVIEWER
XMB_TEXTURE_IMAGES,
#endif
XMB_TEXTURE_SETTING,
XMB_TEXTURE_SUBSETTING,
XMB_TEXTURE_ARROW,
XMB_TEXTURE_RUN,
XMB_TEXTURE_CLOSE,
XMB_TEXTURE_RESUME,
XMB_TEXTURE_SAVESTATE,
XMB_TEXTURE_LOADSTATE,
XMB_TEXTURE_UNDO,
XMB_TEXTURE_CORE_INFO,
XMB_TEXTURE_BLUETOOTH,
XMB_TEXTURE_WIFI,
XMB_TEXTURE_CORE_OPTIONS,
XMB_TEXTURE_INPUT_REMAPPING_OPTIONS,
XMB_TEXTURE_CHEAT_OPTIONS,
XMB_TEXTURE_DISK_OPTIONS,
XMB_TEXTURE_SHADER_OPTIONS,
XMB_TEXTURE_ACHIEVEMENT_LIST,
XMB_TEXTURE_SCREENSHOT,
XMB_TEXTURE_RELOAD,
XMB_TEXTURE_RENAME,
XMB_TEXTURE_FILE,
XMB_TEXTURE_FOLDER,
XMB_TEXTURE_ZIP,
XMB_TEXTURE_FAVORITE,
XMB_TEXTURE_ADD_FAVORITE,
XMB_TEXTURE_MUSIC,
XMB_TEXTURE_IMAGE,
XMB_TEXTURE_MOVIE,
XMB_TEXTURE_CORE,
XMB_TEXTURE_RDB,
XMB_TEXTURE_CURSOR,
XMB_TEXTURE_SWITCH_ON,
XMB_TEXTURE_SWITCH_OFF,
XMB_TEXTURE_CLOCK,
XMB_TEXTURE_BATTERY_FULL,
XMB_TEXTURE_BATTERY_CHARGING,
XMB_TEXTURE_BATTERY_80,
XMB_TEXTURE_BATTERY_60,
XMB_TEXTURE_BATTERY_40,
XMB_TEXTURE_BATTERY_20,
XMB_TEXTURE_POINTER,
XMB_TEXTURE_ADD,
XMB_TEXTURE_KEY,
XMB_TEXTURE_KEY_HOVER,
XMB_TEXTURE_DIALOG_SLICE,
XMB_TEXTURE_ACHIEVEMENTS,
XMB_TEXTURE_AUDIO,
XMB_TEXTURE_EXIT,
XMB_TEXTURE_FRAMESKIP,
XMB_TEXTURE_INFO,
XMB_TEXTURE_HELP,
XMB_TEXTURE_NETWORK,
XMB_TEXTURE_POWER,
XMB_TEXTURE_SAVING,
XMB_TEXTURE_UPDATER,
XMB_TEXTURE_VIDEO,
XMB_TEXTURE_RECORD,
XMB_TEXTURE_INPUT_SETTINGS,
XMB_TEXTURE_MIXER,
XMB_TEXTURE_LOG,
XMB_TEXTURE_OSD,
XMB_TEXTURE_UI,
XMB_TEXTURE_USER,
XMB_TEXTURE_PRIVACY,
XMB_TEXTURE_LATENCY,
XMB_TEXTURE_DRIVERS,
XMB_TEXTURE_PLAYLIST,
XMB_TEXTURE_QUICKMENU,
XMB_TEXTURE_REWIND,
XMB_TEXTURE_OVERLAY,
XMB_TEXTURE_OVERRIDE,
XMB_TEXTURE_NOTIFICATIONS,
XMB_TEXTURE_STREAM,
XMB_TEXTURE_SHUTDOWN,
XMB_TEXTURE_INPUT_DPAD_U,
XMB_TEXTURE_INPUT_DPAD_D,
XMB_TEXTURE_INPUT_DPAD_L,
XMB_TEXTURE_INPUT_DPAD_R,
XMB_TEXTURE_INPUT_STCK_U,
XMB_TEXTURE_INPUT_STCK_D,
XMB_TEXTURE_INPUT_STCK_L,
XMB_TEXTURE_INPUT_STCK_R,
XMB_TEXTURE_INPUT_STCK_P,
XMB_TEXTURE_INPUT_SELECT,
XMB_TEXTURE_INPUT_START,
XMB_TEXTURE_INPUT_BTN_U,
XMB_TEXTURE_INPUT_BTN_D,
XMB_TEXTURE_INPUT_BTN_L,
XMB_TEXTURE_INPUT_BTN_R,
XMB_TEXTURE_INPUT_LB,
XMB_TEXTURE_INPUT_RB,
XMB_TEXTURE_INPUT_LT,
XMB_TEXTURE_INPUT_RT,
XMB_TEXTURE_INPUT_ADC,
XMB_TEXTURE_INPUT_BIND_ALL,
XMB_TEXTURE_INPUT_MOUSE,
XMB_TEXTURE_INPUT_LGUN,
XMB_TEXTURE_INPUT_TURBO,
XMB_TEXTURE_CHECKMARK,
XMB_TEXTURE_MENU_ADD,
XMB_TEXTURE_BRIGHTNESS,
XMB_TEXTURE_PAUSE,
XMB_TEXTURE_DEFAULT,
XMB_TEXTURE_DEFAULT_CONTENT,
XMB_TEXTURE_MENU_APPLY_TOGGLE,
XMB_TEXTURE_MENU_APPLY_COG,
XMB_TEXTURE_DISC,
XMB_TEXTURE_LAST
};
enum
{
XMB_SYSTEM_TAB_MAIN = 0,
XMB_SYSTEM_TAB_SETTINGS,
XMB_SYSTEM_TAB_HISTORY,
XMB_SYSTEM_TAB_FAVORITES,
XMB_SYSTEM_TAB_MUSIC,
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
XMB_SYSTEM_TAB_VIDEO,
#endif
#ifdef HAVE_IMAGEVIEWER
XMB_SYSTEM_TAB_IMAGES,
#endif
#ifdef HAVE_NETWORKING
XMB_SYSTEM_TAB_NETPLAY,
#endif
XMB_SYSTEM_TAB_ADD,
#if defined(HAVE_LIBRETRODB)
XMB_SYSTEM_TAB_EXPLORE,
#endif
/* End of this enum - use the last one to determine num of possible tabs */
XMB_SYSTEM_TAB_MAX_LENGTH
};
/* NOTE: If you change this you HAVE to update
* xmb_alloc_node() and xmb_copy_node() */
typedef struct
{
char *fullpath;
uintptr_t icon;
uintptr_t content_icon;
float alpha;
float label_alpha;
float zoom;
float x;
float y;
} xmb_node_t;
typedef struct xmb_handle
{
/* Keeps track of the last time tabs were switched
* via a MENU_ACTION_LEFT/MENU_ACTION_RIGHT event */
retro_time_t last_tab_switch_time; /* uint64_t alignment */
char *box_message;
char *bg_file_path;
file_list_t selection_buf_old; /* ptr alignment */
file_list_t horizontal_list; /* ptr alignment */
xmb_node_t main_menu_node;
#ifdef HAVE_IMAGEVIEWER
xmb_node_t images_tab_node;
#endif
xmb_node_t music_tab_node;
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
xmb_node_t video_tab_node;
#endif
xmb_node_t settings_tab_node;
xmb_node_t history_tab_node;
xmb_node_t favorites_tab_node;
xmb_node_t add_tab_node;
#if defined(HAVE_LIBRETRODB)
xmb_node_t explore_tab_node;
#endif
xmb_node_t netplay_tab_node;
menu_input_pointer_t pointer;
font_data_t *font;
font_data_t *font2;
video_font_raster_block_t raster_block;
video_font_raster_block_t raster_block2;
void (*word_wrap)(char *dst, size_t dst_size, const char *src,
int line_width, int wideglyph_width, unsigned max_lines);
menu_screensaver_t *screensaver;
gfx_thumbnail_path_data_t *thumbnail_path_data;
struct {
gfx_thumbnail_t right;
gfx_thumbnail_t left;
gfx_thumbnail_t savestate;
} thumbnails;
struct
{
uintptr_t bg;
uintptr_t list[XMB_TEXTURE_LAST];
} textures;
size_t categories_selection_ptr;
size_t categories_selection_ptr_old;
size_t selection_ptr_old;
size_t fullscreen_thumbnail_selection;
/* size of the current list */
size_t list_size;
int depth;
int old_depth;
int icon_size;
int cursor_size;
int wideglyph_width;
unsigned categories_active_idx;
unsigned categories_active_idx_old;
float fullscreen_thumbnail_alpha;
float x;
float alpha;
float above_subitem_offset;
float above_item_offset;
float active_item_factor;
float under_item_offset;
float shadow_offset;
float font_size;
float font2_size;
float last_scale_factor;
float margins_screen_left;
float margins_screen_top;
float margins_setting_left;
float margins_title_left;
float margins_title_top;
float margins_title_bottom;
float margins_label_left;
float margins_label_top;
float icon_spacing_horizontal;
float icon_spacing_vertical;
float items_active_alpha;
float items_active_zoom;
float items_passive_alpha;
float items_passive_zoom;
float margins_dialog;
float margins_slice;
float textures_arrow_alpha;
float categories_x_pos;
float categories_passive_alpha;
float categories_passive_zoom;
float categories_active_zoom;
float categories_active_alpha;
uint8_t system_tab_end;
uint8_t tabs[XMB_SYSTEM_TAB_MAX_LENGTH];
char title_name[255];
/* Cached texts showing current entry index / current list size */
char entry_index_str[32];
/* These have to be huge, because runloop_st->name.savestate
* has a hard-coded size of 8192...
* (the extra space here is required to silence compiler
* warnings...) */
char savestate_thumbnail_file_path[8204];
char prev_savestate_thumbnail_file_path[8204];
char fullscreen_thumbnail_label[255];
bool fullscreen_thumbnails_available;
bool show_fullscreen_thumbnails;
bool show_mouse;
bool show_screensaver;
bool use_ps3_layout;
bool last_use_ps3_layout;
bool assets_missing;
/* Favorites, History, Images, Music, Videos, user generated */
bool is_playlist;
bool is_db_manager_list;
/* Load Content file browser */
bool is_file_list;
bool is_quick_menu;
/* Whether to show entry index for current list */
bool entry_idx_enabled;
} xmb_handle_t;
static float xmb_scale_mod[8] = {
1, 1, 1, 1, 1, 1, 1, 1
};
static float xmb_coord_shadow[] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
};
static float xmb_coord_black[] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
};
static float xmb_coord_white[] = {
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1
};
static float item_color[] = {
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1
};
float gradient_dark_purple[16] = {
20/255.0, 13/255.0, 20/255.0, 1.0,
20/255.0, 13/255.0, 20/255.0, 1.0,
92/255.0, 44/255.0, 92/255.0, 1.0,
148/255.0, 90/255.0, 148/255.0, 1.0,
};
float gradient_midnight_blue[16] = {
44/255.0, 62/255.0, 80/255.0, 1.0,
44/255.0, 62/255.0, 80/255.0, 1.0,
44/255.0, 62/255.0, 80/255.0, 1.0,
44/255.0, 62/255.0, 80/255.0, 1.0,
};
float gradient_golden[16] = {
174/255.0, 123/255.0, 44/255.0, 1.0,
205/255.0, 174/255.0, 84/255.0, 1.0,
58/255.0, 43/255.0, 24/255.0, 1.0,
58/255.0, 43/255.0, 24/255.0, 1.0,
};
float gradient_legacy_red[16] = {
171/255.0, 70/255.0, 59/255.0, 1.0,
171/255.0, 70/255.0, 59/255.0, 1.0,
190/255.0, 80/255.0, 69/255.0, 1.0,
190/255.0, 80/255.0, 69/255.0, 1.0,
};
float gradient_electric_blue[16] = {
1/255.0, 2/255.0, 67/255.0, 1.0,
1/255.0, 73/255.0, 183/255.0, 1.0,
1/255.0, 93/255.0, 194/255.0, 1.0,
3/255.0, 162/255.0, 254/255.0, 1.0,
};
float gradient_apple_green[16] = {
102/255.0, 134/255.0, 58/255.0, 1.0,
122/255.0, 131/255.0, 52/255.0, 1.0,
82/255.0, 101/255.0, 35/255.0, 1.0,
63/255.0, 95/255.0, 30/255.0, 1.0,
};
float gradient_undersea[16] = {
23/255.0, 18/255.0, 41/255.0, 1.0,
30/255.0, 72/255.0, 114/255.0, 1.0,
52/255.0, 88/255.0, 110/255.0, 1.0,
69/255.0, 125/255.0, 140/255.0, 1.0,
};
float gradient_volcanic_red[16] = {
1.0, 0.0, 0.1, 1.00,
1.0, 0.1, 0.0, 1.00,
0.1, 0.0, 0.1, 1.00,
0.1, 0.0, 0.1, 1.00,
};
float gradient_dark[16] = {
0.1, 0.1, 0.1, 1.00,
0.1, 0.1, 0.1, 1.00,
0.0, 0.0, 0.0, 1.00,
0.0, 0.0, 0.0, 1.00,
};
float gradient_light[16] = {
1.0, 1.0, 1.0, 1.00,
1.0, 1.0, 1.0, 1.00,
1.0, 1.0, 1.0, 1.00,
1.0, 1.0, 1.0, 1.00,
};
float gradient_morning_blue[16] = {
221/255.0, 241/255.0, 254/255.0, 1.00,
135/255.0, 206/255.0, 250/255.0, 1.00,
1.0, 1.0, 1.0, 1.00,
170/255.0, 200/255.0, 252/255.0, 1.00,
};
float gradient_sunbeam[16] = {
20/255.0, 13/255.0, 20/255.0, 1.0,
30/255.0, 72/255.0, 114/255.0, 1.0,
1.0, 1.0, 1.0, 1.00,
0.1, 0.0, 0.1, 1.00,
};
float gradient_lime_green[16] = {
209/255.0, 255/255.0, 82/255.0, 1.0,
146/255.0, 232/255.0, 66/255.0, 1.0,
82/255.0, 101/255.0, 35/255.0, 1.0,
63/255.0, 95/255.0, 30/255.0, 1.0,
};
float gradient_pikachu_yellow[16] = {
63/255.0, 63/255.0, 1/255.0, 1.0,
174/255.0, 174/255.0, 1/255.0, 1.0,
191/255.0, 194/255.0, 1/255.0, 1.0,
254/255.0, 221/255.0, 3/255.0, 1.0,
};
float gradient_gamecube_purple[16] = {
40/255.0, 20/255.0, 91/255.0, 1.0,
160/255.0, 140/255.0, 211/255.0, 1.0,
107/255.0, 92/255.0, 177/255.0, 1.0,
84/255.0, 71/255.0, 132/255.0, 1.0,
};
float gradient_famicom_red[16] = {
255/255.0, 191/255.0, 171/255.0, 1.0,
119/255.0, 49/255.0, 28/255.0, 1.0,
148/255.0, 10/255.0, 36/255.0, 1.0,
206/255.0, 126/255.0, 110/255.0, 1.0,
};
float gradient_flaming_hot[16] = {
231/255.0, 53/255.0, 53/255.0, 1.0,
242/255.0, 138/255.0, 97/255.0, 1.0,
236/255.0, 97/255.0, 76/255.0, 1.0,
255/255.0, 125/255.0, 3/255.0, 1.0,
};
float gradient_ice_cold[16] = {
66/255.0, 183/255.0, 229/255.0, 1.0,
29/255.0, 164/255.0, 255/255.0, 1.0,
176/255.0, 255/255.0, 247/255.0, 1.0,
174/255.0, 240/255.0, 255/255.0, 1.0,
};
float gradient_midgar[16] = {
255/255.0, 0/255.0, 0/255.0, 1.0,
0/255.0, 0/255.0, 255/255.0, 1.0,
0/255.0, 255/255.0, 0/255.0, 1.0,
32/255.0, 32/255.0, 32/255.0, 1.0,
};
static INLINE float xmb_item_y(const xmb_handle_t *xmb, int i, size_t current)
{
float iy = xmb->icon_spacing_vertical;
if (i < (int)current)
if (xmb->depth > 1)
iy *= (i - (int)current + xmb->above_subitem_offset);
else
iy *= (i - (int)current + xmb->above_item_offset);
else
iy *= (i - (int)current + xmb->under_item_offset);
if (i == (int)current)
iy = xmb->icon_spacing_vertical * xmb->active_item_factor;
return iy;
}
static void xmb_calculate_visible_range(const xmb_handle_t *xmb,
unsigned height, size_t list_size, unsigned current,
unsigned *first, unsigned *last)
{
unsigned j;
float base_y = xmb->margins_screen_top;
*first = 0;
*last = (unsigned)(list_size ? list_size - 1 : 0);
if (current)
{
for (j = current; j-- > 0; )
{
float bottom = xmb_item_y(xmb, j, current)
+ base_y + xmb->icon_size;
if (bottom < 0)
break;
*first = j;
}
}
for (j = current+1; j < list_size; j++)
{
float top = xmb_item_y(xmb, j, current) + base_y;
if (top > height)
break;
*last = j;
}
}
const char* xmb_theme_ident(void)
{
settings_t *settings = config_get_ptr();
unsigned menu_xmb_theme = settings->uints.menu_xmb_theme;
switch (menu_xmb_theme)
{
case XMB_ICON_THEME_FLATUI:
return "flatui";
case XMB_ICON_THEME_RETROACTIVE:
return "retroactive";
case XMB_ICON_THEME_RETROSYSTEM:
return "retrosystem";
case XMB_ICON_THEME_PIXEL:
return "pixel";
case XMB_ICON_THEME_NEOACTIVE:
return "neoactive";
case XMB_ICON_THEME_SYSTEMATIC:
return "systematic";
case XMB_ICON_THEME_DOTART:
return "dot-art";
case XMB_ICON_THEME_CUSTOM:
return "custom";
case XMB_ICON_THEME_MONOCHROME_INVERTED:
return "monochrome";
case XMB_ICON_THEME_AUTOMATIC:
return "automatic";
case XMB_ICON_THEME_AUTOMATIC_INVERTED:
return "automatic";
case XMB_ICON_THEME_MONOCHROME:
default:
break;
}
return "monochrome";
}
/* NOTE: This exists because calloc()ing xmb_node_t is expensive
* when you can have big lists like MAME and fba playlists */
static xmb_node_t *xmb_alloc_node(void)
{
xmb_node_t *node = (xmb_node_t*)malloc(sizeof(*node));
if (!node)
return NULL;
node->alpha = node->label_alpha = 0;
node->zoom = node->x = node->y = 0;
node->icon = node->content_icon = 0;
node->fullpath = NULL;
return node;
}
static void xmb_free_node(xmb_node_t *node)
{
if (!node)
return;
if (node->fullpath)
free(node->fullpath);
node->fullpath = NULL;
free(node);
}
/**
* @brief frees all xmb_node_t in a file_list_t
*
* file_list_t asumes userdata holds a simple structure and
* free()'s it. Can't change this at the time because other
* code depends on this behavior.
*
* @param list
* @param actiondata whether to free actiondata too
*/
static void xmb_free_list_nodes(file_list_t *list, bool actiondata)
{
unsigned i, size = list ? (unsigned)list->size : 0;
for (i = 0; i < size; ++i)
{
xmb_free_node((xmb_node_t*)file_list_get_userdata_at_offset(list, i));
list->list[i].userdata = NULL;
if (actiondata)
file_list_free_actiondata(list, i);
}
}
static xmb_node_t *xmb_copy_node(const xmb_node_t *old_node)
{
xmb_node_t *new_node = (xmb_node_t*)malloc(sizeof(*new_node));
if (!new_node)
return NULL;
*new_node = *old_node;
new_node->fullpath = old_node->fullpath ? strdup(old_node->fullpath) : NULL;
return new_node;
}
static float *xmb_gradient_ident(unsigned xmb_color_theme)
{
switch (xmb_color_theme)
{
case XMB_THEME_DARK_PURPLE:
return &gradient_dark_purple[0];
case XMB_THEME_MIDNIGHT_BLUE:
return &gradient_midnight_blue[0];
case XMB_THEME_GOLDEN:
return &gradient_golden[0];
case XMB_THEME_ELECTRIC_BLUE:
return &gradient_electric_blue[0];
case XMB_THEME_APPLE_GREEN:
return &gradient_apple_green[0];
case XMB_THEME_UNDERSEA:
return &gradient_undersea[0];
case XMB_THEME_VOLCANIC_RED:
return &gradient_volcanic_red[0];
case XMB_THEME_DARK:
return &gradient_dark[0];
case XMB_THEME_LIGHT:
return &gradient_light[0];
case XMB_THEME_MORNING_BLUE:
return &gradient_morning_blue[0];
case XMB_THEME_SUNBEAM:
return &gradient_sunbeam[0];
case XMB_THEME_LIME:
return &gradient_lime_green[0];
case XMB_THEME_MIDGAR:
return &gradient_midgar[0];
case XMB_THEME_PIKACHU_YELLOW:
return &gradient_pikachu_yellow[0];
case XMB_THEME_GAMECUBE_PURPLE:
return &gradient_gamecube_purple[0];
case XMB_THEME_FAMICOM_RED:
return &gradient_famicom_red[0];
case XMB_THEME_FLAMING_HOT:
return &gradient_flaming_hot[0];
case XMB_THEME_ICE_COLD:
return &gradient_ice_cold[0];
case XMB_THEME_LEGACY_RED:
default:
break;
}
return &gradient_legacy_red[0];
}
static size_t xmb_list_get_selection(void *data)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
if (!xmb)
return 0;
return xmb->categories_selection_ptr;
}
static size_t xmb_list_get_size(void *data, enum menu_list_type type)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
switch (type)
{
case MENU_LIST_PLAIN:
return menu_entries_get_stack_size(0);
case MENU_LIST_HORIZONTAL:
return xmb->horizontal_list.size;
case MENU_LIST_TABS:
return xmb->system_tab_end;
}
return 0;
}
static void *xmb_list_get_entry(void *data,
enum menu_list_type type, unsigned i)
{
size_t list_size = 0;
xmb_handle_t *xmb = (xmb_handle_t*)data;
switch (type)
{
case MENU_LIST_PLAIN:
{
file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0);
list_size = menu_entries_get_stack_size(0);
if (i < list_size)
return (void*)&menu_stack->list[i];
}
break;
case MENU_LIST_HORIZONTAL:
list_size = xmb->horizontal_list.size;
if (i < list_size)
return (void*)&xmb->horizontal_list.list[i];
break;
default:
break;
}
return NULL;
}
static void xmb_draw_icon(
void *userdata,
gfx_display_t *p_disp,
gfx_display_ctx_driver_t *dispctx,
unsigned video_width,
unsigned video_height,
bool xmb_shadows_enable,
int icon_size,
uintptr_t texture,
float x,
float y,
unsigned width,
unsigned height,
float alpha,
float rotation,
float scale_factor,
float *color,
float shadow_offset,
math_matrix_4x4 *mymat)
{
gfx_display_ctx_draw_t draw;
struct video_coords coords;
if (
(x < (-icon_size / 2.0f)) ||
(x > width) ||
(y < (icon_size / 2.0f)) ||
(y > height + icon_size)
)
return;
coords.vertices = 4;
coords.vertex = NULL;
coords.tex_coord = NULL;
coords.lut_tex_coord = NULL;
draw.width = icon_size;
draw.height = icon_size;
draw.rotation = rotation;
draw.scale_factor = scale_factor;
#if defined(VITA) || defined(WIIU)
draw.width *= scale_factor;
draw.height *= scale_factor;
#endif
draw.coords = &coords;
draw.matrix_data = mymat;
draw.texture = texture;
draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP;
draw.pipeline_id = 0;
if (xmb_shadows_enable)
{
gfx_display_set_alpha(xmb_coord_shadow, color[3] * 0.35f);
coords.color = xmb_coord_shadow;
draw.x = x + shadow_offset;
draw.y = height - y - shadow_offset;
#if defined(VITA) || defined(WIIU)
if (scale_factor < 1)
{
draw.x = draw.x + (icon_size-draw.width)/2;
draw.y = draw.y + (icon_size-draw.width)/2;
}
#endif
if (draw.height > 0 && draw.width > 0)
if (dispctx && dispctx->draw)
dispctx->draw(&draw, userdata, video_width, video_height);
}
coords.color = (const float*)color;
draw.x = x;
draw.y = height - y;
#if defined(VITA) || defined(WIIU)
if (scale_factor < 1)
{
draw.x = draw.x + (icon_size-draw.width)/2;
draw.y = draw.y + (icon_size-draw.width)/2;
}
#endif
if (draw.height > 0 && draw.width > 0)
if (dispctx && dispctx->draw)
dispctx->draw(&draw, userdata, video_width, video_height);
}
static void xmb_draw_text(
bool xmb_shadows_enable,
xmb_handle_t *xmb,
settings_t *settings,
const char *str, float x,
float y, float scale_factor, float alpha,
enum text_alignment text_align,
unsigned width, unsigned height, font_data_t* font)
{
uint32_t color;
uint8_t a8;
if (alpha > xmb->alpha)
alpha = xmb->alpha;
a8 = 255 * alpha;
/* Avoid drawing 100% transparent text */
if (a8 == 0)
return;
color = FONT_COLOR_RGBA(
settings->uints.menu_font_color_red,
settings->uints.menu_font_color_green,
settings->uints.menu_font_color_blue, a8);
gfx_display_draw_text(font, str, x, y,
width, height, color, text_align, scale_factor,
xmb_shadows_enable,
xmb->shadow_offset, false);
}
static void xmb_messagebox(void *data, const char *message)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
if (!xmb || string_is_empty(message))
return;
xmb->box_message = strdup(message);
}
static void xmb_render_messagebox_internal(
void *userdata,
gfx_display_t *p_disp,
unsigned video_width,
unsigned video_height,
xmb_handle_t *xmb, const char *message,
math_matrix_4x4 *mymat)
{
unsigned i, y_position;
char wrapped_message[MENU_SUBLABEL_MAX_LENGTH];
int x, y, longest_width = 0;
float line_height = 0;
int usable_width = 0;
struct string_list list = {0};
gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
bool input_dialog_display_kb = false;
wrapped_message[0] = '\0';
/* Sanity check */
if (string_is_empty(message) ||
!xmb ||
!xmb->font)
return;
usable_width = (int)video_width - (xmb->margins_dialog * 8);
if (usable_width < 1)
return;
/* Split message into lines */
(xmb->word_wrap)(
wrapped_message, sizeof(wrapped_message), message,
usable_width / (xmb->font_size * 0.6f),
xmb->wideglyph_width, 0);
string_list_initialize(&list);
if (!string_split_noalloc(&list, wrapped_message, "\n")
|| list.elems == 0
)
{
string_list_deinitialize(&list);
return;
}
input_dialog_display_kb = menu_input_dialog_get_display_kb();
line_height = xmb->font->size * 1.2;
y_position = video_height / 2;
if (input_dialog_display_kb)
y_position = video_height / 4;
x = video_width / 2;
y = y_position - (list.size-1) * line_height / 2;
/* find the longest line width */
for (i = 0; i < list.size; i++)
{
const char *msg = list.elems[i].data;
if (!string_is_empty(msg))
{
int width = font_driver_get_message_width(
xmb->font, msg, (unsigned)strlen(msg), 1);
longest_width = (width > longest_width) ?
width : longest_width;
}
}
if (dispctx && dispctx->blend_begin)
dispctx->blend_begin(userdata);
gfx_display_draw_texture_slice(
p_disp,
userdata,
video_width,
video_height,
x - longest_width/2 - xmb->margins_dialog,
y + xmb->margins_slice - xmb->margins_dialog,
256, 256,
longest_width + xmb->margins_dialog * 2,
line_height * list.size + xmb->margins_dialog * 2,
video_width, video_height,
NULL,
xmb->margins_slice, xmb->last_scale_factor,
xmb->textures.list[XMB_TEXTURE_DIALOG_SLICE],
mymat);
for (i = 0; i < list.size; i++)
{
const char *msg = list.elems[i].data;
if (msg)
gfx_display_draw_text(xmb->font, msg,
x - longest_width/2.0,
y + (i+0.75) * line_height,
video_width, video_height, 0x444444ff,
TEXT_ALIGN_LEFT, 1.0f, false, 0.0f, false);
}
if (input_dialog_display_kb)
{
input_driver_state_t *input_st = input_state_get_ptr();
gfx_display_draw_keyboard(
p_disp,
userdata,
video_width,
video_height,
xmb->textures.list[XMB_TEXTURE_KEY_HOVER],
xmb->font,
input_st->osk_grid,
input_st->osk_ptr,
0xffffffff);
}
string_list_deinitialize(&list);
}
static char* xmb_path_dynamic_wallpaper(xmb_handle_t *xmb)
{
char path[PATH_MAX_LENGTH];
char *tmp = string_replace_substring(xmb->title_name, "/", " ");
settings_t *settings = config_get_ptr();
const char *dir_dynamic_wallpapers = settings->paths.directory_dynamic_wallpapers;
path[0] = '\0';
if (tmp)
{
fill_pathname_join_noext(
path,
dir_dynamic_wallpapers,
tmp,
sizeof(path));
free(tmp);
}
strlcat(path, FILE_PATH_PNG_EXTENSION, sizeof(path));
if (!path_is_valid(path))
fill_pathname_application_special(path, sizeof(path),
APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_BG);
return strdup(path);
}
static void xmb_update_dynamic_wallpaper(xmb_handle_t *xmb)
{
char *path;
settings_t *settings = config_get_ptr();
if (!settings->bools.menu_dynamic_wallpaper_enable)
return;
path = xmb_path_dynamic_wallpaper(xmb);
if (!string_is_equal(path, xmb->bg_file_path))
{
if (path_is_valid(path))
{
task_push_image_load(path,
video_driver_supports_rgba(), 0,
menu_display_handle_wallpaper_upload, NULL);
if (!string_is_empty(xmb->bg_file_path))
free(xmb->bg_file_path);
xmb->bg_file_path = strdup(path);
}
}
free(path);
path = NULL;
}
static void xmb_update_savestate_thumbnail_path(void *data, unsigned i)
{
settings_t *settings = config_get_ptr();
xmb_handle_t *xmb = (xmb_handle_t*)data;
int state_slot = settings->ints.state_slot;
bool savestate_thumbnail_enable
= settings->bools.savestate_thumbnail_enable;
if (!xmb)
return;
/* Cache previous savestate thumbnail path */
strlcpy(
xmb->prev_savestate_thumbnail_file_path,
xmb->savestate_thumbnail_file_path,
sizeof(xmb->prev_savestate_thumbnail_file_path));
xmb->savestate_thumbnail_file_path[0] = '\0';
/* Savestate thumbnails are only relevant
* when viewing the quick menu */
if (!xmb->is_quick_menu)
return;
if (savestate_thumbnail_enable)
{
menu_entry_t entry;
MENU_ENTRY_INIT(entry);
entry.path_enabled = false;
entry.rich_label_enabled = false;
entry.value_enabled = false;
entry.sublabel_enabled = false;
menu_entry_get(&entry, 0, i, NULL, true);
if (!string_is_empty(entry.label))
{
if (string_is_equal(entry.label, "state_slot") ||
string_is_equal(entry.label, "loadstate") ||
string_is_equal(entry.label, "savestate"))
{
char path[8204];
runloop_state_t *runloop_st = runloop_state_get_ptr();
path[0] = '\0';
if (state_slot > 0)
snprintf(path, sizeof(path), "%s%d",
runloop_st->name.savestate, state_slot);
else if (state_slot < 0)
fill_pathname_join_delim(path,
runloop_st->name.savestate, "auto", '.', sizeof(path));
else
strlcpy(path, runloop_st->name.savestate, sizeof(path));
strlcat(path, FILE_PATH_PNG_EXTENSION, sizeof(path));
if (path_is_valid(path))
strlcpy(
xmb->savestate_thumbnail_file_path, path,
sizeof(xmb->savestate_thumbnail_file_path));
}
}
}
}
static void xmb_update_thumbnail_image(void *data)
{
const char *core_name = NULL;
xmb_handle_t *xmb = (xmb_handle_t*)data;
size_t selection = menu_navigation_get_selection();
playlist_t *playlist = playlist_get_cached();
settings_t *settings = config_get_ptr();
unsigned thumbnail_upscale_threshold = settings->uints.gfx_thumbnail_upscale_threshold;
bool network_on_demand_thumbnails = settings->bools.network_on_demand_thumbnails;
if (!xmb)
return;
gfx_thumbnail_cancel_pending_requests();
/* imageviewer content requires special treatment... */
gfx_thumbnail_get_core_name(xmb->thumbnail_path_data, &core_name);
if (string_is_equal(core_name, "imageviewer"))
{
gfx_thumbnail_reset(&xmb->thumbnails.right);
gfx_thumbnail_reset(&xmb->thumbnails.left);
/* Right thumbnail */
if (gfx_thumbnail_is_enabled(xmb->thumbnail_path_data,
GFX_THUMBNAIL_RIGHT))
gfx_thumbnail_request(
xmb->thumbnail_path_data,
GFX_THUMBNAIL_RIGHT,
playlist,
selection,
&xmb->thumbnails.right,
thumbnail_upscale_threshold,
network_on_demand_thumbnails);
/* Left thumbnail */
else if (gfx_thumbnail_is_enabled(xmb->thumbnail_path_data,
GFX_THUMBNAIL_LEFT))
gfx_thumbnail_request(
xmb->thumbnail_path_data,
GFX_THUMBNAIL_LEFT,
playlist,
selection,
&xmb->thumbnails.left,
thumbnail_upscale_threshold,
network_on_demand_thumbnails);
}
else
{
/* Right thumbnail */
gfx_thumbnail_request(
xmb->thumbnail_path_data,
GFX_THUMBNAIL_RIGHT,
playlist,
selection,
&xmb->thumbnails.right,
thumbnail_upscale_threshold,
network_on_demand_thumbnails);
/* Left thumbnail */
gfx_thumbnail_request(
xmb->thumbnail_path_data,
GFX_THUMBNAIL_LEFT,
playlist,
selection,
&xmb->thumbnails.left,
thumbnail_upscale_threshold,
network_on_demand_thumbnails);
}
}
static unsigned xmb_get_system_tab(xmb_handle_t *xmb, unsigned i)
{
if (i <= xmb->system_tab_end)
return xmb->tabs[i];
return UINT_MAX;
}
static void xmb_refresh_thumbnail_image(void *data, unsigned i)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
if (!xmb)
return;
/* Only refresh thumbnails if thumbnails are enabled */
if ( gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) ||
gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
{
unsigned depth = (unsigned)xmb_list_get_size(xmb, MENU_LIST_PLAIN);
unsigned xmb_system_tab = xmb_get_system_tab(xmb, (unsigned)xmb->categories_selection_ptr);
/* Only refresh thumbnails if we are viewing a playlist or
* the quick menu... */
if (((((xmb_system_tab > XMB_SYSTEM_TAB_SETTINGS && depth == 1) ||
(xmb_system_tab < XMB_SYSTEM_TAB_SETTINGS && depth == 4)) &&
xmb->is_playlist)) ||
xmb->is_quick_menu)
xmb_update_thumbnail_image(xmb);
}
}
static void xmb_set_thumbnail_system(void *data, char*s, size_t len)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
if (!xmb)
return;
gfx_thumbnail_set_system(
xmb->thumbnail_path_data, s, playlist_get_cached());
}
static void xmb_get_thumbnail_system(void *data, char*s, size_t len)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
const char *system = NULL;
if (!xmb)
return;
if (gfx_thumbnail_get_system(xmb->thumbnail_path_data, &system))
strlcpy(s, system, len);
}
static void xmb_unload_thumbnail_textures(void *data)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
if (!xmb)
return;
gfx_thumbnail_cancel_pending_requests();
gfx_thumbnail_reset(&xmb->thumbnails.right);
gfx_thumbnail_reset(&xmb->thumbnails.left);
gfx_thumbnail_reset(&xmb->thumbnails.savestate);
}
static void xmb_set_thumbnail_content(void *data, const char *s)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
if (!xmb)
return;
/* Disable fullscreen thumbnails by default,
* and only enable if thumbnail content is
* actually set
* > This is the easiest method for verifying
* that we are currently viewing a relevant
* menu type */
xmb->fullscreen_thumbnails_available = false;
if (xmb->is_playlist)
{
/* Playlist content */
if (string_is_empty(s))
{
size_t selection = menu_navigation_get_selection();
size_t list_size = menu_entries_get_size();
file_list_t *list = menu_entries_get_selection_buf_ptr(0);
bool playlist_valid = false;
size_t playlist_index = selection;
/* Get playlist index corresponding
* to the selected entry */
if (list &&
(selection < list_size) &&
(list->list[selection].type == FILE_TYPE_RPL_ENTRY))
{
playlist_valid = true;
playlist_index = list->list[selection].entry_idx;
}
gfx_thumbnail_set_content_playlist(xmb->thumbnail_path_data,
playlist_valid ? playlist_get_cached() : NULL, playlist_index);
xmb->fullscreen_thumbnails_available = true;
}
}
else if (xmb->is_db_manager_list)
{
/* Database list content */
if (string_is_empty(s))
{
menu_entry_t entry;
size_t selection = menu_navigation_get_selection();
MENU_ENTRY_INIT(entry);
entry.label_enabled = false;
entry.rich_label_enabled = false;
entry.value_enabled = false;
entry.sublabel_enabled = false;
menu_entry_get(&entry, 0, selection, NULL, true);
if (!string_is_empty(entry.path))
{
gfx_thumbnail_set_content(xmb->thumbnail_path_data, entry.path);
xmb->fullscreen_thumbnails_available = true;
}
}
}
else if (string_is_equal(s, "imageviewer"))
{
/* Filebrowser image updates */
menu_entry_t entry;
size_t selection = menu_navigation_get_selection();
file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
xmb_node_t *node = (xmb_node_t*)selection_buf->list[selection].userdata;
if (node)
{
MENU_ENTRY_INIT(entry);
entry.label_enabled = false;
entry.rich_label_enabled = false;
entry.value_enabled = false;
entry.sublabel_enabled = false;
menu_entry_get(&entry, 0, selection, NULL, true);
if ( !string_is_empty(entry.path) &&
!string_is_empty(node->fullpath))
{
gfx_thumbnail_set_content_image(xmb->thumbnail_path_data,
node->fullpath, entry.path);
xmb->fullscreen_thumbnails_available = true;
}
}
}
else if (!string_is_empty(s))
{
/* Annoying leftovers...
* This is required to ensure that thumbnails are
* updated correctly when navigating deeply through
* the sublevels of database manager lists.
* Showing thumbnails on database entries is a
* pointless nuisance and a waste of CPU cycles, IMHO... */
gfx_thumbnail_set_content(xmb->thumbnail_path_data, s);
xmb->fullscreen_thumbnails_available = true;
}
}
static void xmb_update_savestate_thumbnail_image(void *data)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
settings_t *settings = config_get_ptr();
unsigned thumbnail_upscale_threshold
= settings->uints.gfx_thumbnail_upscale_threshold;
if (!xmb)
return;
/* Savestate thumbnails are only relevant
* when viewing the quick menu */
if (!xmb->is_quick_menu)
return;
/* If path is empty, just reset thumbnail */
if (string_is_empty(xmb->savestate_thumbnail_file_path))
gfx_thumbnail_reset(&xmb->thumbnails.savestate);
else
{
/* Only request thumbnail if:
* > Thumbnail has never been loaded *OR*
* > Thumbnail path has changed */
if ((xmb->thumbnails.savestate.status == GFX_THUMBNAIL_STATUS_UNKNOWN) ||
!string_is_equal(xmb->savestate_thumbnail_file_path, xmb->prev_savestate_thumbnail_file_path))
gfx_thumbnail_request_file(
xmb->savestate_thumbnail_file_path,
&xmb->thumbnails.savestate,
thumbnail_upscale_threshold);
}
}
/* Is called when the pointer position changes within a list/sub-list (vertically) */
static void xmb_selection_pointer_changed(
xmb_handle_t *xmb, bool allow_animations)
{
unsigned i, end, height;
uintptr_t tag;
size_t num = 0;
int threshold = 0;
file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
size_t selection = menu_navigation_get_selection();
if (!xmb)
return;
end = (unsigned)menu_entries_get_size();
threshold = xmb->icon_size * 10;
video_driver_get_size(NULL, &height);
tag = (uintptr_t)selection_buf;
gfx_animation_kill_by_tag(&tag);
menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &num);
for (i = 0; i < end; i++)
{
float iy, real_iy;
float ia = xmb->items_passive_alpha;
float iz = xmb->items_passive_zoom;
xmb_node_t *node = (xmb_node_t*)selection_buf->list[i].userdata;
if (!node)
continue;
iy = xmb_item_y(xmb, i, selection);
real_iy = iy + xmb->margins_screen_top;
if (i == selection)
{
unsigned depth = (unsigned)xmb_list_get_size(xmb, MENU_LIST_PLAIN);
unsigned xmb_system_tab = xmb_get_system_tab(xmb, (unsigned)xmb->categories_selection_ptr);
/* Update entry index text */
if (xmb->entry_idx_enabled)
{
snprintf(xmb->entry_index_str, sizeof(xmb->entry_index_str),
"%lu/%lu", (unsigned long)selection + 1,
(unsigned long)xmb->list_size);
}
ia = xmb->items_active_alpha;
iz = xmb->items_active_zoom;
if (
gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) ||
gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_LEFT)
)
{
bool update_thumbnails = false;
/* Playlist updates */
if (((xmb_system_tab > XMB_SYSTEM_TAB_SETTINGS && depth == 1) ||
(xmb_system_tab < XMB_SYSTEM_TAB_SETTINGS && depth == 4)) &&
xmb->is_playlist)
{
xmb_set_thumbnail_content(xmb, NULL);
update_thumbnails = true;
}
/* Database list updates
* (pointless nuisance...) */
else if (depth == 4 && xmb->is_db_manager_list)
{
xmb_set_thumbnail_content(xmb, NULL);
update_thumbnails = true;
}
/* Filebrowser image updates */
else if (xmb->is_file_list)
{
menu_entry_t entry;
unsigned entry_type;
MENU_ENTRY_INIT(entry);
entry.path_enabled = false;
entry.label_enabled = false;
entry.rich_label_enabled = false;
entry.value_enabled = false;
entry.sublabel_enabled = false;
menu_entry_get(&entry, 0, selection, NULL, true);
entry_type = entry.type;
if ( (entry_type == FILE_TYPE_IMAGEVIEWER) ||
(entry_type == FILE_TYPE_IMAGE))
{
xmb_set_thumbnail_content(xmb, "imageviewer");
update_thumbnails = true;
}
else
{
/* If this is a file list and current
* entry is not an image, have to 'reset'
* content + right/left thumbnails
* (otherwise last loaded thumbnail will
* persist, and be shown on the wrong entry) */
gfx_thumbnail_set_content(xmb->thumbnail_path_data, NULL);
gfx_thumbnail_cancel_pending_requests();
gfx_thumbnail_reset(&xmb->thumbnails.right);
gfx_thumbnail_reset(&xmb->thumbnails.left);
}
}
if (update_thumbnails)
xmb_update_thumbnail_image(xmb);
}
xmb_update_savestate_thumbnail_path(xmb, i);
xmb_update_savestate_thumbnail_image(xmb);
}
if ( (!allow_animations)
|| (real_iy < -threshold
|| real_iy > height+threshold))
{
node->alpha = node->label_alpha = ia;
node->y = iy;
node->zoom = iz;
}
else
{
settings_t *settings = config_get_ptr();
unsigned menu_xmb_animation_move_up_down = settings->uints.menu_xmb_animation_move_up_down;
/* Move up/down animation */
gfx_animation_ctx_entry_t anim_entry;
anim_entry.target_value = ia;
anim_entry.subject = &node->alpha;
anim_entry.tag = tag;
anim_entry.cb = NULL;
switch (menu_xmb_animation_move_up_down)
{
case 0:
anim_entry.duration = XMB_DELAY;
anim_entry.easing_enum = EASING_OUT_QUAD;
break;
case 1:
anim_entry.duration = XMB_DELAY * 4;
anim_entry.easing_enum = EASING_OUT_EXPO;
break;
}
gfx_animation_push(&anim_entry);
anim_entry.subject = &node->label_alpha;
gfx_animation_push(&anim_entry);
anim_entry.target_value = iz;
anim_entry.subject = &node->zoom;
gfx_animation_push(&anim_entry);
anim_entry.target_value = iy;
anim_entry.subject = &node->y;
gfx_animation_push(&anim_entry);
}
}
}
static void xmb_list_open_old(xmb_handle_t *xmb,
file_list_t *list, int dir, size_t current)
{
unsigned i, height = 0;
int threshold = xmb->icon_size * 10;
size_t end = list ? list->size : 0;
video_driver_get_size(NULL, &height);
for (i = 0; i < end; i++)
{
float ia = 0;
float real_y;
xmb_node_t *node = (xmb_node_t*)list->list[i].userdata;
if (!node)
continue;
if (i == current)
ia = xmb->items_active_alpha;
if (dir == -1)
ia = 0;
real_y = node->y + xmb->margins_screen_top;
if (real_y < -threshold || real_y > height+threshold)
{
node->alpha = ia;
node->label_alpha = 0;
node->x = xmb->icon_size * dir * -2;
}
else
{
gfx_animation_ctx_entry_t anim_entry;
anim_entry.duration = XMB_DELAY;
anim_entry.target_value = ia;
anim_entry.subject = &node->alpha;
anim_entry.easing_enum = EASING_OUT_QUAD;
anim_entry.tag = (uintptr_t)list;
anim_entry.cb = NULL;
gfx_animation_push(&anim_entry);
anim_entry.target_value = 0;
anim_entry.subject = &node->label_alpha;
gfx_animation_push(&anim_entry);
anim_entry.target_value = xmb->icon_size * dir * -2;
anim_entry.subject = &node->x;
gfx_animation_push(&anim_entry);
}
}
}
static void xmb_list_open_new(xmb_handle_t *xmb,
file_list_t *list, int dir, size_t current)
{
unsigned i, height;
unsigned xmb_system_tab = 0;
size_t skip = 0;
int threshold = xmb->icon_size * 10;
size_t end = list ? list->size : 0;
video_driver_get_size(NULL, &height);
for (i = 0; i < end; i++)
{
float ia;
float real_y;
xmb_node_t *node = (xmb_node_t*)list->list[i].userdata;
if (!node)
continue;
if (dir == 1)
{
node->alpha = 0;
node->label_alpha = 0;
}
else if (dir == -1)
{
if (i != current)
node->alpha = 0;
node->label_alpha = 0;
}
node->x = xmb->icon_size * dir * 2;
node->y = xmb_item_y(xmb, i, current);
node->zoom = xmb->categories_passive_zoom;
real_y = node->y + xmb->margins_screen_top;
if (i == current)
{
node->zoom = xmb->categories_active_zoom;
ia = xmb->items_active_alpha;
}
else
ia = xmb->items_passive_alpha;
if (real_y < -threshold || real_y > height+threshold)
{
node->alpha = node->label_alpha = ia;
node->x = 0;
}
else
{
gfx_animation_ctx_entry_t anim_entry;
anim_entry.duration = XMB_DELAY;
anim_entry.target_value = ia;
anim_entry.subject = &node->alpha;
anim_entry.easing_enum = EASING_OUT_QUAD;
anim_entry.tag = (uintptr_t)list;
anim_entry.cb = NULL;
gfx_animation_push(&anim_entry);
anim_entry.subject = &node->label_alpha;
gfx_animation_push(&anim_entry);
anim_entry.target_value = 0;
anim_entry.subject = &node->x;
gfx_animation_push(&anim_entry);
}
}
xmb->old_depth = xmb->depth;
menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &skip);
xmb_system_tab = xmb_get_system_tab(xmb,
(unsigned)xmb->categories_selection_ptr);
if (xmb_system_tab <= XMB_SYSTEM_TAB_SETTINGS)
{
if ( gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) ||
gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
{
/* This code is horrible, full of hacks...
* This hack ensures that thumbnails are not cleared
* when selecting an entry from a collection via
* 'load content'... */
if (xmb->depth != 5)
xmb_unload_thumbnail_textures(xmb);
if (xmb->is_playlist || xmb->is_db_manager_list)
{
xmb_set_thumbnail_content(xmb, NULL);
xmb_update_thumbnail_image(xmb);
}
}
}
}
static xmb_node_t *xmb_node_allocate_userdata(
xmb_handle_t *xmb, unsigned i)
{
xmb_node_t *tmp = NULL;
xmb_node_t *node = xmb_alloc_node();
if (!node)
{
RARCH_ERR("XMB node could not be allocated.\n");
return NULL;
}
node->alpha = xmb->categories_passive_alpha;
node->zoom = xmb->categories_passive_zoom;
if ((i + xmb->system_tab_end) == xmb->categories_active_idx)
{
node->alpha = xmb->categories_active_alpha;
node->zoom = xmb->categories_active_zoom;
}
tmp = (xmb_node_t*)file_list_get_userdata_at_offset(
&xmb->horizontal_list, i);
xmb_free_node(tmp);
xmb->horizontal_list.list[i].userdata = node;
return node;
}
static xmb_node_t* xmb_get_userdata_from_horizontal_list(
xmb_handle_t *xmb, unsigned i)
{
return (xmb_node_t*)
file_list_get_userdata_at_offset(&xmb->horizontal_list, i);
}
static void xmb_push_animations(xmb_node_t *node,
uintptr_t tag, float ia, float ix)
{
gfx_animation_ctx_entry_t anim_entry;
anim_entry.duration = XMB_DELAY;
anim_entry.target_value = ia;
anim_entry.subject = &node->alpha;
anim_entry.easing_enum = EASING_OUT_QUAD;
anim_entry.tag = tag;
anim_entry.cb = NULL;
gfx_animation_push(&anim_entry);
anim_entry.subject = &node->label_alpha;
gfx_animation_push(&anim_entry);
anim_entry.target_value = ix;
anim_entry.subject = &node->x;
gfx_animation_push(&anim_entry);
}
static void xmb_list_switch_old(xmb_handle_t *xmb,
file_list_t *list, int dir, size_t current)
{
unsigned i, height;
size_t end = list ? list->size : 0;
float ix = -xmb->icon_spacing_horizontal * dir;
float ia = 0;
unsigned first = 0;
unsigned last = (unsigned)(end > 0 ? end - 1 : 0);
video_driver_get_size(NULL, &height);
xmb_calculate_visible_range(xmb, height, end,
(unsigned)current, &first, &last);
for (i = 0; i < end; i++)
{
xmb_node_t *node = (xmb_node_t*)list->list[i].userdata;
if (!node)
continue;
if (i >= first && i <= last)
xmb_push_animations(node, (uintptr_t)list, ia, ix);
else
{
node->alpha = node->label_alpha = ia;
node->x = ix;
}
}
}
static void xmb_list_switch_new(xmb_handle_t *xmb,
file_list_t *list, int dir, size_t current)
{
unsigned i, first, last, height;
size_t end = 0;
end = list ? list->size : 0;
first = 0;
last = (unsigned)(end > 0 ? end - 1 : 0);
video_driver_get_size(NULL, &height);
xmb_calculate_visible_range(xmb, height,
end, (unsigned)current, &first, &last);
for (i = 0; i < end; i++)
{
xmb_node_t *node = (xmb_node_t*)list->list[i].userdata;
float ia = xmb->items_passive_alpha;
if (!node)
continue;
node->x = xmb->icon_spacing_horizontal * dir;
node->alpha = 0;
node->label_alpha = 0;
if (i == current)
ia = xmb->items_active_alpha;
if (i >= first && i <= last)
xmb_push_animations(node, (uintptr_t)list, ia, 0);
else
{
node->x = 0;
node->alpha = node->label_alpha = ia;
}
}
}
static void xmb_set_title(xmb_handle_t *xmb)
{
if (xmb->categories_selection_ptr <= xmb->system_tab_end)
menu_entries_get_title(xmb->title_name, sizeof(xmb->title_name));
else
{
const char *path = xmb->horizontal_list.list[
xmb->categories_selection_ptr - (xmb->system_tab_end + 1)].path;
if (!path)
return;
fill_pathname_base_noext(
xmb->title_name, path, sizeof(xmb->title_name));
/* Add current search terms */
menu_entries_search_append_terms_string(
xmb->title_name, sizeof(xmb->title_name));
}
}
static xmb_node_t* xmb_get_node(xmb_handle_t *xmb, unsigned i)
{
switch (xmb_get_system_tab(xmb, i))
{
case XMB_SYSTEM_TAB_SETTINGS:
return &xmb->settings_tab_node;
#ifdef HAVE_IMAGEVIEWER
case XMB_SYSTEM_TAB_IMAGES:
return &xmb->images_tab_node;
#endif
case XMB_SYSTEM_TAB_MUSIC:
return &xmb->music_tab_node;
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
case XMB_SYSTEM_TAB_VIDEO:
return &xmb->video_tab_node;
#endif
case XMB_SYSTEM_TAB_HISTORY:
return &xmb->history_tab_node;
case XMB_SYSTEM_TAB_FAVORITES:
return &xmb->favorites_tab_node;
#ifdef HAVE_NETWORKING
case XMB_SYSTEM_TAB_NETPLAY:
return &xmb->netplay_tab_node;
#endif
case XMB_SYSTEM_TAB_ADD:
return &xmb->add_tab_node;
#if defined(HAVE_LIBRETRODB)
case XMB_SYSTEM_TAB_EXPLORE:
return &xmb->explore_tab_node;
#endif
default:
if (i > xmb->system_tab_end)
return xmb_get_userdata_from_horizontal_list(
xmb, i - (xmb->system_tab_end + 1));
}
return &xmb->main_menu_node;
}
static void xmb_list_switch_horizontal_list(xmb_handle_t *xmb)
{
unsigned j;
settings_t *settings = config_get_ptr();
size_t list_size = xmb_list_get_size(xmb, MENU_LIST_HORIZONTAL)
+ xmb->system_tab_end;
unsigned xmb_animation_horizontal_highlight =
settings->uints.menu_xmb_animation_horizontal_highlight;
for (j = 0; j <= list_size; j++)
{
gfx_animation_ctx_entry_t entry;
float ia = xmb->categories_passive_alpha;
float iz = xmb->categories_passive_zoom;
xmb_node_t *node = xmb_get_node(xmb, j);
if (!node)
continue;
if (j == xmb->categories_active_idx)
{
ia = xmb->categories_active_alpha;
iz = xmb->categories_active_zoom;
}
/* Horizontal icon animation */
entry.target_value = ia;
entry.subject = &node->alpha;
/* TODO/FIXME - integer conversion resulted in change of sign */
entry.tag = -1;
entry.cb = NULL;
switch (xmb_animation_horizontal_highlight)
{
case 0:
entry.duration = XMB_DELAY;
entry.easing_enum = EASING_OUT_QUAD;
break;
case 1:
entry.duration = XMB_DELAY + (XMB_DELAY / 2);
entry.easing_enum = EASING_IN_SINE;
break;
case 2:
entry.duration = XMB_DELAY * 2;
entry.easing_enum = EASING_OUT_BOUNCE;
break;
}
gfx_animation_push(&entry);
entry.target_value = iz;
entry.subject = &node->zoom;
gfx_animation_push(&entry);
}
}
static void xmb_list_switch(xmb_handle_t *xmb)
{
gfx_animation_ctx_entry_t anim_entry;
int dir = -1;
file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
size_t selection = menu_navigation_get_selection();
settings_t *settings = config_get_ptr();
bool menu_horizontal_animation = settings->bools.menu_horizontal_animation;
if (xmb->categories_selection_ptr > xmb->categories_selection_ptr_old)
dir = 1;
xmb->categories_active_idx += dir;
xmb_list_switch_horizontal_list(xmb);
anim_entry.duration = XMB_DELAY;
anim_entry.target_value = xmb->icon_spacing_horizontal
* -(float)xmb->categories_selection_ptr;
anim_entry.subject = &xmb->categories_x_pos;
anim_entry.easing_enum = EASING_OUT_QUAD;
/* TODO/FIXME - integer conversion resulted in change of sign */
anim_entry.tag = -1;
anim_entry.cb = NULL;
if (anim_entry.subject)
gfx_animation_push(&anim_entry);
dir = -1;
if (xmb->categories_selection_ptr > xmb->categories_selection_ptr_old)
dir = 1;
xmb_list_switch_old(xmb, &xmb->selection_buf_old,
dir, xmb->selection_ptr_old);
/* Check if we are to have horizontal animations. */
if (menu_horizontal_animation)
xmb_list_switch_new(xmb, selection_buf, dir, selection);
xmb->categories_active_idx_old = (unsigned)xmb->categories_selection_ptr;
if (gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) ||
gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
{
xmb_unload_thumbnail_textures(xmb);
if (xmb->is_playlist)
{
xmb_set_thumbnail_content(xmb, NULL);
xmb_update_thumbnail_image(xmb);
}
}
}
static void xmb_list_open_horizontal_list(xmb_handle_t *xmb)
{
unsigned j;
size_t list_size = xmb_list_get_size(xmb, MENU_LIST_HORIZONTAL)
+ xmb->system_tab_end;
for (j = 0; j <= list_size; j++)
{
gfx_animation_ctx_entry_t anim_entry;
float ia = 0;
xmb_node_t *node = xmb_get_node(xmb, j);
if (!node)
continue;
if (j == xmb->categories_active_idx)
ia = xmb->categories_active_alpha;
else if (xmb->depth <= 1)
ia = xmb->categories_passive_alpha;
anim_entry.duration = XMB_DELAY;
anim_entry.target_value = ia;
anim_entry.subject = &node->alpha;
anim_entry.easing_enum = EASING_OUT_QUAD;
/* TODO/FIXME - integer conversion resulted in change of sign */
anim_entry.tag = -1;
anim_entry.cb = NULL;
if (anim_entry.subject)
gfx_animation_push(&anim_entry);
}
}
static void xmb_context_destroy_horizontal_list(xmb_handle_t *xmb)
{
unsigned i;
size_t list_size = xmb_list_get_size(xmb, MENU_LIST_HORIZONTAL);
for (i = 0; i < list_size; i++)
{
const char *path = NULL;
xmb_node_t *node = xmb_get_userdata_from_horizontal_list(xmb, i);
if (!node)
continue;
if (!(path = xmb->horizontal_list.list[i].path))
continue;
if (string_ends_with_size(path, ".lpl",
strlen(path), STRLEN_CONST(".lpl")))
{
video_driver_texture_unload(&node->icon);
video_driver_texture_unload(&node->content_icon);
}
}
}
static void xmb_init_horizontal_list(xmb_handle_t *xmb)
{
menu_displaylist_info_t info;
settings_t *settings = config_get_ptr();
const char *dir_playlist = settings->paths.directory_playlist;
bool menu_content_show_playlists = settings->bools.menu_content_show_playlists;
menu_displaylist_info_init(&info);
info.list = &xmb->horizontal_list;
info.path = strdup(dir_playlist);
info.label = strdup(
msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
info.exts = strdup("lpl");
info.type_default = FILE_TYPE_PLAIN;
info.enum_idx = MENU_ENUM_LABEL_PLAYLISTS_TAB;
if (menu_content_show_playlists && !string_is_empty(info.path))
{
if (menu_displaylist_ctl(DISPLAYLIST_DATABASE_PLAYLISTS_HORIZONTAL, &info,
settings))
{
size_t i;
for (i = 0; i < xmb->horizontal_list.size; i++)
xmb_node_allocate_userdata(xmb, (unsigned)i);
menu_displaylist_process(&info);
}
}
menu_displaylist_info_free(&info);
}
static void xmb_toggle_horizontal_list(xmb_handle_t *xmb)
{
unsigned i;
size_t list_size = xmb_list_get_size(xmb, MENU_LIST_HORIZONTAL)
+ xmb->system_tab_end;
for (i = 0; i <= list_size; i++)
{
xmb_node_t *node = xmb_get_node(xmb, i);
if (!node)
continue;
node->alpha = 0;
node->zoom = xmb->categories_passive_zoom;
if (i == xmb->categories_active_idx)
{
node->alpha = xmb->categories_active_alpha;
node->zoom = xmb->categories_active_zoom;
}
else if (xmb->depth <= 1)
node->alpha = xmb->categories_passive_alpha;
}
}
static void xmb_context_reset_horizontal_list(
xmb_handle_t *xmb)
{
unsigned i;
int depth; /* keep this integer */
size_t list_size =
xmb_list_get_size(xmb, MENU_LIST_HORIZONTAL);
xmb->categories_x_pos =
xmb->icon_spacing_horizontal *
-(float)xmb->categories_selection_ptr;
depth = (xmb->depth > 1) ? 2 : 1;
xmb->x = xmb->icon_size * -(depth*2-2);
for (i = 0; i < list_size; i++)
{
const char *path = NULL;
xmb_node_t *node =
xmb_get_userdata_from_horizontal_list(xmb, i);
if (!node)
if (!(node = xmb_node_allocate_userdata(xmb, i)))
continue;
if (!(path = xmb->horizontal_list.list[i].path))
continue;
if (string_ends_with_size(path, ".lpl",
strlen(path), STRLEN_CONST(".lpl")))
{
struct texture_image ti;
char sysname[PATH_MAX_LENGTH];
char iconpath[PATH_MAX_LENGTH];
char texturepath[PATH_MAX_LENGTH];
char content_texturepath[PATH_MAX_LENGTH];
iconpath[0] = sysname[0] =
texturepath[0] = content_texturepath[0] = '\0';
fill_pathname_base_noext(sysname, path, sizeof(sysname));
fill_pathname_application_special(iconpath, sizeof(iconpath),
APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_ICONS);
fill_pathname_join_concat(texturepath, iconpath, sysname,
FILE_PATH_PNG_EXTENSION, sizeof(texturepath));
/* If the playlist icon doesn't exist return default */
if (!path_is_valid(texturepath))
fill_pathname_join_concat(texturepath, iconpath, "default",
FILE_PATH_PNG_EXTENSION, sizeof(texturepath));
ti.width = 0;
ti.height = 0;
ti.pixels = NULL;
ti.supports_rgba = video_driver_supports_rgba();
if (image_texture_load(&ti, texturepath))
{
if (ti.pixels)
{
video_driver_texture_unload(&node->icon);
video_driver_texture_load(&ti,
TEXTURE_FILTER_MIPMAP_LINEAR, &node->icon);
}
image_texture_free(&ti);
}
fill_pathname_join_delim(sysname, sysname,
FILE_PATH_CONTENT_BASENAME, '-',
sizeof(sysname));
strlcat(content_texturepath, iconpath, sizeof(content_texturepath));
strlcat(content_texturepath, sysname, sizeof(content_texturepath));
/* If the content icon doesn't exist return default-content */
if (!path_is_valid(content_texturepath))
{
strlcat(iconpath, "default", sizeof(iconpath));
fill_pathname_join_delim(content_texturepath, iconpath,
FILE_PATH_CONTENT_BASENAME, '-', sizeof(content_texturepath));
}
if (image_texture_load(&ti, content_texturepath))
{
if (ti.pixels)
{
video_driver_texture_unload(&node->content_icon);
video_driver_texture_load(&ti,
TEXTURE_FILTER_MIPMAP_LINEAR, &node->content_icon);
}
image_texture_free(&ti);
}
}
}
xmb_toggle_horizontal_list(xmb);
}
static void xmb_refresh_horizontal_list(xmb_handle_t *xmb)
{
xmb_context_destroy_horizontal_list(xmb);
xmb_free_list_nodes(&xmb->horizontal_list, false);
file_list_deinitialize(&xmb->horizontal_list);
menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
xmb_init_horizontal_list(xmb);
xmb_context_reset_horizontal_list(xmb);
}
static int xmb_environ(enum menu_environ_cb type, void *data, void *userdata)
{
xmb_handle_t *xmb = (xmb_handle_t*)userdata;
if (!xmb)
return -1;
switch (type)
{
case MENU_ENVIRON_ENABLE_MOUSE_CURSOR:
xmb->show_mouse = true;
break;
case MENU_ENVIRON_DISABLE_MOUSE_CURSOR:
xmb->show_mouse = false;
break;
case MENU_ENVIRON_RESET_HORIZONTAL_LIST:
xmb_refresh_horizontal_list(xmb);
break;
case MENU_ENVIRON_ENABLE_SCREENSAVER:
xmb->show_screensaver = true;
break;
case MENU_ENVIRON_DISABLE_SCREENSAVER:
xmb->show_screensaver = false;
break;
default:
return -1;
}
return 0;
}
static void xmb_list_open(xmb_handle_t *xmb)
{
gfx_animation_ctx_entry_t entry;
settings_t *settings = config_get_ptr();
unsigned
menu_xmb_animation_opening_main_menu =
settings->uints.menu_xmb_animation_opening_main_menu;
int dir = 0;
file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
size_t selection = menu_navigation_get_selection();
xmb->depth = (int)xmb_list_get_size(xmb, MENU_LIST_PLAIN);
if (xmb->depth > xmb->old_depth)
dir = 1;
else if (xmb->depth < xmb->old_depth)
dir = -1;
else
return; /* If menu hasn't changed, do nothing */
xmb_list_open_horizontal_list(xmb);
xmb_list_open_old(xmb, &xmb->selection_buf_old,
dir, xmb->selection_ptr_old);
xmb_list_open_new(xmb, selection_buf,
dir, selection);
/* Main Menu opening animation */
entry.target_value = xmb->icon_size * -(xmb->depth*2-2);
entry.subject = &xmb->x;
/* TODO/FIXME - integer conversion resulted in change of sign */
entry.tag = -1;
entry.cb = NULL;
switch (menu_xmb_animation_opening_main_menu)
{
case 0:
entry.easing_enum = EASING_OUT_QUAD;
entry.duration = XMB_DELAY;
break;
case 1:
entry.easing_enum = EASING_OUT_CIRC;
entry.duration = XMB_DELAY * 2;
break;
case 2:
entry.easing_enum = EASING_OUT_EXPO;
entry.duration = XMB_DELAY * 3;
break;
case 3:
entry.easing_enum = EASING_OUT_BOUNCE;
entry.duration = XMB_DELAY * 4;
break;
}
switch (xmb->depth)
{
case 1:
case 2:
gfx_animation_push(&entry);
entry.target_value = xmb->depth - 1;
entry.subject = &xmb->textures_arrow_alpha;
gfx_animation_push(&entry);
break;
}
xmb->old_depth = xmb->depth;
}
/* Is called whenever the list/sub-list changes */
static void xmb_populate_entries(void *data,
const char *path,
const char *label, unsigned k)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
settings_t *settings = config_get_ptr();
bool show_entry_idx = settings ? settings->bools.playlist_show_entry_idx : false;
unsigned depth = (unsigned)xmb_list_get_size(xmb, MENU_LIST_PLAIN);
unsigned xmb_system_tab;
if (!xmb)
return;
/* Determine whether this is a playlist */
xmb_system_tab = xmb_get_system_tab(xmb,
(unsigned)xmb->categories_selection_ptr);
xmb->is_playlist = (depth == 1
&& ((xmb_system_tab == XMB_SYSTEM_TAB_FAVORITES)
|| (xmb_system_tab == XMB_SYSTEM_TAB_HISTORY)
#ifdef HAVE_IMAGEVIEWER
|| (xmb_system_tab == XMB_SYSTEM_TAB_IMAGES)
#endif
|| (xmb_system_tab == XMB_SYSTEM_TAB_MUSIC)
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
|| (xmb_system_tab == XMB_SYSTEM_TAB_VIDEO)
#endif
))
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU))
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_PLAYLIST_LIST))
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_FAVORITES_LIST))
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_IMAGES_LIST))
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_MUSIC_LIST))
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_VIDEO_LIST));
xmb->is_playlist = xmb->is_playlist && !string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_RDB_ENTRY_DETAIL));
/* Determine whether this is a database manager list */
xmb->is_db_manager_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DATABASE_MANAGER_LIST));
/* Determine whether this is a 'file list'
* (needed for handling thumbnails when viewing images
* via 'load content')
* > Note: MENU_ENUM_LABEL_FAVORITES is always set
* as the 'label' when navigating directories after
* selecting 'load content' */
xmb->is_file_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES));
/* Determine whether this is the quick menu */
xmb->is_quick_menu = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_RPL_ENTRY_ACTIONS)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_SETTINGS));
xmb_set_title(xmb);
xmb_update_dynamic_wallpaper(xmb);
if (menu_driver_ctl(RARCH_MENU_CTL_IS_PREVENT_POPULATE, NULL))
{
xmb_selection_pointer_changed(xmb, false);
menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL);
return;
}
if (xmb->categories_selection_ptr != xmb->categories_active_idx_old)
xmb_list_switch(xmb);
else
xmb_list_open(xmb);
/* Determine whether to show entry index */
xmb->entry_idx_enabled = show_entry_idx && xmb->is_playlist;
/* Update list size & entry index texts */
if (xmb->entry_idx_enabled)
{
xmb->list_size = menu_entries_get_size();
snprintf(xmb->entry_index_str, sizeof(xmb->entry_index_str),
"%lu/%lu", (unsigned long)menu_navigation_get_selection() + 1,
(unsigned long)xmb->list_size);
}
/* By default, fullscreen thumbnails are only
* enabled on playlists, database manager
* lists and file lists, in cases where ordinary
* thumbnails would normally be shown
* > This is refined on a case-by-case basis
* inside xmb_set_thumbnail_content() */
xmb->fullscreen_thumbnails_available =
(xmb->is_playlist || xmb->is_db_manager_list || xmb->is_file_list) &&
!xmb->is_quick_menu &&
!((xmb_system_tab > XMB_SYSTEM_TAB_SETTINGS) && (xmb->depth > 2));
/* Hack: XMB gets into complete muddle when
* performing 'complex' directory navigation
* via 'load content'. We have to work around
* this by resetting thumbnails whenever a
* file list is populated... */
if (xmb->is_file_list)
{
gfx_thumbnail_set_content(xmb->thumbnail_path_data, NULL);
gfx_thumbnail_cancel_pending_requests();
gfx_thumbnail_reset(&xmb->thumbnails.right);
gfx_thumbnail_reset(&xmb->thumbnails.left);
}
}
static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb,
xmb_node_t *core_node, xmb_node_t *node,
enum msg_hash_enums enum_idx, unsigned type, bool active, bool checked)
{
switch (enum_idx)
{
case MENU_ENUM_LABEL_CORE_OPTIONS:
case MENU_ENUM_LABEL_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE:
return xmb->textures.list[XMB_TEXTURE_CORE_OPTIONS];
case MENU_ENUM_LABEL_CORE_OPTION_OVERRIDE_LIST:
return xmb->textures.list[XMB_TEXTURE_SETTING];
case MENU_ENUM_LABEL_ADD_TO_FAVORITES:
case MENU_ENUM_LABEL_ADD_TO_FAVORITES_PLAYLIST:
return xmb->textures.list[XMB_TEXTURE_ADD_FAVORITE];
case MENU_ENUM_LABEL_PARENT_DIRECTORY:
case MENU_ENUM_LABEL_UNDO_LOAD_STATE:
case MENU_ENUM_LABEL_UNDO_SAVE_STATE:
case MENU_ENUM_LABEL_RESET_CORE_ASSOCIATION:
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES:
return xmb->textures.list[XMB_TEXTURE_UNDO];
case MENU_ENUM_LABEL_CORE_INPUT_REMAPPING_OPTIONS:
return xmb->textures.list[XMB_TEXTURE_INPUT_REMAPPING_OPTIONS];
case MENU_ENUM_LABEL_CORE_CHEAT_OPTIONS:
return xmb->textures.list[XMB_TEXTURE_CHEAT_OPTIONS];
case MENU_ENUM_LABEL_DISK_OPTIONS:
case MENU_ENUM_LABEL_DISK_TRAY_EJECT:
case MENU_ENUM_LABEL_DISK_TRAY_INSERT:
case MENU_ENUM_LABEL_DISK_IMAGE_APPEND:
case MENU_ENUM_LABEL_DISK_INDEX:
return xmb->textures.list[XMB_TEXTURE_DISK_OPTIONS];
case MENU_ENUM_LABEL_SHADER_OPTIONS:
return xmb->textures.list[XMB_TEXTURE_SHADER_OPTIONS];
case MENU_ENUM_LABEL_ACHIEVEMENT_LIST:
case MENU_ENUM_LABEL_ACHIEVEMENT_LIST_HARDCORE:
return xmb->textures.list[XMB_TEXTURE_ACHIEVEMENT_LIST];
case MENU_ENUM_LABEL_SAVE_STATE:
case MENU_ENUM_LABEL_SAVESTATE_AUTO_SAVE:
case MENU_ENUM_LABEL_CORE_CREATE_BACKUP:
case MENU_ENUM_LABEL_GAME_SPECIFIC_CORE_OPTIONS_CREATE:
case MENU_ENUM_LABEL_FOLDER_SPECIFIC_CORE_OPTIONS_CREATE:
return xmb->textures.list[XMB_TEXTURE_SAVESTATE];
case MENU_ENUM_LABEL_LOAD_STATE:
case MENU_ENUM_LABEL_CONFIGURATIONS:
case MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS:
case MENU_ENUM_LABEL_REMAP_FILE_LOAD:
case MENU_ENUM_LABEL_AUTO_OVERRIDES_ENABLE:
case MENU_ENUM_LABEL_AUTO_REMAPS_ENABLE:
case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET:
case MENU_ENUM_LABEL_CHEAT_FILE_LOAD:
case MENU_ENUM_LABEL_CHEAT_FILE_LOAD_APPEND:
case MENU_ENUM_LABEL_SAVESTATE_AUTO_LOAD:
case MENU_ENUM_LABEL_CORE_RESTORE_BACKUP_LIST:
return xmb->textures.list[XMB_TEXTURE_LOADSTATE];
case MENU_ENUM_LABEL_TAKE_SCREENSHOT:
return xmb->textures.list[XMB_TEXTURE_SCREENSHOT];
case MENU_ENUM_LABEL_DELETE_ENTRY:
return xmb->textures.list[XMB_TEXTURE_CLOSE];
case MENU_ENUM_LABEL_RESTART_CONTENT:
case MENU_ENUM_LABEL_REBOOT:
case MENU_ENUM_LABEL_RESET_TO_DEFAULT_CONFIG:
case MENU_ENUM_LABEL_CHEAT_RELOAD_CHEATS:
case MENU_ENUM_LABEL_RESTART_RETROARCH:
case MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE:
case MENU_ENUM_LABEL_AUTOSAVE_INTERVAL:
case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_SETTINGS:
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST:
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST:
return xmb->textures.list[XMB_TEXTURE_RELOAD];
case MENU_ENUM_LABEL_RENAME_ENTRY:
return xmb->textures.list[XMB_TEXTURE_RENAME];
case MENU_ENUM_LABEL_RESUME_CONTENT:
return xmb->textures.list[XMB_TEXTURE_RESUME];
case MENU_ENUM_LABEL_DIRECTORY_SETTINGS:
case MENU_ENUM_LABEL_SCAN_DIRECTORY:
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST:
case MENU_ENUM_LABEL_REMAP_FILE_SAVE_CONTENT_DIR:
case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CONTENT_DIR:
case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_PARENT:
case MENU_ENUM_LABEL_FAVORITES: /* "Start Directory" */
case MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST:
return xmb->textures.list[XMB_TEXTURE_FOLDER];
case MENU_ENUM_LABEL_ADD_CONTENT_LIST:
return xmb->textures.list[XMB_TEXTURE_ADD];
case MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR:
return xmb->textures.list[XMB_TEXTURE_RDB];
/* Menu collection submenus */
case MENU_ENUM_LABEL_PLAYLISTS_TAB:
return xmb->textures.list[XMB_TEXTURE_ZIP];
case MENU_ENUM_LABEL_GOTO_FAVORITES:
return xmb->textures.list[XMB_TEXTURE_FAVORITE];
case MENU_ENUM_LABEL_GOTO_IMAGES:
return xmb->textures.list[XMB_TEXTURE_IMAGE];
case MENU_ENUM_LABEL_GOTO_VIDEO:
return xmb->textures.list[XMB_TEXTURE_MOVIE];
case MENU_ENUM_LABEL_GOTO_MUSIC:
return xmb->textures.list[XMB_TEXTURE_MUSIC];
case MENU_ENUM_LABEL_GOTO_EXPLORE:
return xmb->textures.list[XMB_TEXTURE_MAIN_MENU];
case MENU_ENUM_LABEL_LOAD_DISC:
case MENU_ENUM_LABEL_DUMP_DISC:
#ifdef HAVE_LAKKA
case MENU_ENUM_LABEL_EJECT_DISC:
#endif
case MENU_ENUM_LABEL_DISC_INFORMATION:
return xmb->textures.list[XMB_TEXTURE_DISC];
case MENU_ENUM_LABEL_CONTENT_SETTINGS:
case MENU_ENUM_LABEL_UPDATE_ASSETS:
case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_GAME:
case MENU_ENUM_LABEL_REMAP_FILE_SAVE_GAME:
case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_GLOBAL:
case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_GAME:
return xmb->textures.list[XMB_TEXTURE_QUICKMENU];
case MENU_ENUM_LABEL_START_CORE:
case MENU_ENUM_LABEL_CHEAT_START_OR_CONT:
return xmb->textures.list[XMB_TEXTURE_RUN];
case MENU_ENUM_LABEL_CORE_LIST:
case MENU_ENUM_LABEL_SIDELOAD_CORE_LIST:
case MENU_ENUM_LABEL_CORE_SETTINGS:
case MENU_ENUM_LABEL_CORE_UPDATER_LIST:
case MENU_ENUM_LABEL_UPDATE_INSTALLED_CORES:
case MENU_ENUM_LABEL_SWITCH_INSTALLED_CORES_PFD:
case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_CORE:
case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG_OVERRIDE_CORE:
case MENU_ENUM_LABEL_REMAP_FILE_SAVE_CORE:
case MENU_ENUM_LABEL_SET_CORE_ASSOCIATION:
case MENU_ENUM_LABEL_CORE_INFORMATION:
return xmb->textures.list[XMB_TEXTURE_CORE];
case MENU_ENUM_LABEL_LOAD_CONTENT_LIST:
case MENU_ENUM_LABEL_SUBSYSTEM_SETTINGS:
case MENU_ENUM_LABEL_SCAN_FILE:
return xmb->textures.list[XMB_TEXTURE_FILE];
case MENU_ENUM_LABEL_ONLINE_UPDATER:
case MENU_ENUM_LABEL_UPDATER_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_UPDATER];
case MENU_ENUM_LABEL_UPDATE_LAKKA:
return xmb->textures.list[XMB_TEXTURE_MAIN_MENU];
case MENU_ENUM_LABEL_UPDATE_CHEATS:
return xmb->textures.list[XMB_TEXTURE_CHEAT_OPTIONS];
case MENU_ENUM_LABEL_THUMBNAILS_UPDATER_LIST:
case MENU_ENUM_LABEL_PL_THUMBNAILS_UPDATER_LIST:
case MENU_ENUM_LABEL_DOWNLOAD_PL_ENTRY_THUMBNAILS:
return xmb->textures.list[XMB_TEXTURE_IMAGE];
case MENU_ENUM_LABEL_UPDATE_OVERLAYS:
case MENU_ENUM_LABEL_ONSCREEN_OVERLAY_SETTINGS:
#ifdef HAVE_VIDEO_LAYOUT
case MENU_ENUM_LABEL_ONSCREEN_VIDEO_LAYOUT_SETTINGS:
#endif
return xmb->textures.list[XMB_TEXTURE_OVERLAY];
case MENU_ENUM_LABEL_UPDATE_CG_SHADERS:
case MENU_ENUM_LABEL_UPDATE_GLSL_SHADERS:
case MENU_ENUM_LABEL_UPDATE_SLANG_SHADERS:
case MENU_ENUM_LABEL_AUTO_SHADERS_ENABLE:
case MENU_ENUM_LABEL_VIDEO_SHADER_PARAMETERS:
return xmb->textures.list[XMB_TEXTURE_SHADER_OPTIONS];
case MENU_ENUM_LABEL_INFORMATION:
case MENU_ENUM_LABEL_INFORMATION_LIST:
case MENU_ENUM_LABEL_SYSTEM_INFORMATION:
case MENU_ENUM_LABEL_UPDATE_CORE_INFO_FILES:
return xmb->textures.list[XMB_TEXTURE_INFO];
case MENU_ENUM_LABEL_UPDATE_DATABASES:
case MENU_ENUM_LABEL_DATABASE_MANAGER_LIST:
return xmb->textures.list[XMB_TEXTURE_RDB];
case MENU_ENUM_LABEL_CURSOR_MANAGER_LIST:
return xmb->textures.list[XMB_TEXTURE_CURSOR];
case MENU_ENUM_LABEL_HELP_LIST:
case MENU_ENUM_LABEL_HELP_CONTROLS:
case MENU_ENUM_LABEL_HELP_LOADING_CONTENT:
case MENU_ENUM_LABEL_HELP_SCANNING_CONTENT:
case MENU_ENUM_LABEL_HELP_WHAT_IS_A_CORE:
case MENU_ENUM_LABEL_HELP_CHANGE_VIRTUAL_GAMEPAD:
case MENU_ENUM_LABEL_HELP_AUDIO_VIDEO_TROUBLESHOOTING:
case MENU_ENUM_LABEL_HELP_SEND_DEBUG_INFO:
return xmb->textures.list[XMB_TEXTURE_HELP];
case MENU_ENUM_LABEL_QUIT_RETROARCH:
case MENU_ENUM_LABEL_BLOCK_SRAM_OVERWRITE:
return xmb->textures.list[XMB_TEXTURE_EXIT];
case MENU_ENUM_LABEL_DRIVER_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_DRIVERS];
case MENU_ENUM_LABEL_VIDEO_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_VIDEO];
case MENU_ENUM_LABEL_AUDIO_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_AUDIO];
case MENU_ENUM_LABEL_AUDIO_MIXER_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_MIXER];
case MENU_ENUM_LABEL_SCREEN_RESOLUTION:
return xmb->textures.list[XMB_TEXTURE_SUBSETTING];
case MENU_ENUM_LABEL_INPUT_SETTINGS:
case MENU_ENUM_LABEL_UPDATE_AUTOCONFIG_PROFILES:
case MENU_ENUM_LABEL_INPUT_USER_1_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_2_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_3_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_4_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_5_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_6_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_7_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_8_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_9_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_10_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_11_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_12_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_13_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_14_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_15_BINDS:
case MENU_ENUM_LABEL_INPUT_USER_16_BINDS:
case MENU_ENUM_LABEL_START_NET_RETROPAD:
return xmb->textures.list[XMB_TEXTURE_INPUT_SETTINGS];
case MENU_ENUM_LABEL_INPUT_TURBO_FIRE_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_INPUT_TURBO];
case MENU_ENUM_LABEL_LATENCY_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_LATENCY];
case MENU_ENUM_LABEL_SAVING_SETTINGS:
case MENU_ENUM_LABEL_SAVE_CURRENT_CONFIG:
case MENU_ENUM_LABEL_SAVE_NEW_CONFIG:
case MENU_ENUM_LABEL_CONFIG_SAVE_ON_EXIT:
case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE:
case MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_AS:
case MENU_ENUM_LABEL_CHEAT_FILE_SAVE_AS:
return xmb->textures.list[XMB_TEXTURE_SAVING];
case MENU_ENUM_LABEL_LOGGING_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_LOG];
case MENU_ENUM_LABEL_FASTFORWARD_RATIO:
case MENU_ENUM_LABEL_FRAME_THROTTLE_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_FRAMESKIP];
case MENU_ENUM_LABEL_QUICK_MENU_START_RECORDING:
case MENU_ENUM_LABEL_RECORDING_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_RECORD];
case MENU_ENUM_LABEL_QUICK_MENU_START_STREAMING:
return xmb->textures.list[XMB_TEXTURE_STREAM];
case MENU_ENUM_LABEL_QUICK_MENU_STOP_STREAMING:
case MENU_ENUM_LABEL_QUICK_MENU_STOP_RECORDING:
case MENU_ENUM_LABEL_CHEAT_DELETE_ALL:
case MENU_ENUM_LABEL_REMAP_FILE_REMOVE_CORE:
case MENU_ENUM_LABEL_REMAP_FILE_REMOVE_GAME:
case MENU_ENUM_LABEL_REMAP_FILE_REMOVE_CONTENT_DIR:
case MENU_ENUM_LABEL_CORE_DELETE:
case MENU_ENUM_LABEL_DELETE_PLAYLIST:
case MENU_ENUM_LABEL_CORE_DELETE_BACKUP_LIST:
case MENU_ENUM_LABEL_VIDEO_FILTER_REMOVE:
case MENU_ENUM_LABEL_AUDIO_DSP_PLUGIN_REMOVE:
case MENU_ENUM_LABEL_GAME_SPECIFIC_CORE_OPTIONS_REMOVE:
case MENU_ENUM_LABEL_FOLDER_SPECIFIC_CORE_OPTIONS_REMOVE:
return xmb->textures.list[XMB_TEXTURE_CLOSE];
case MENU_ENUM_LABEL_CORE_OPTIONS_RESET:
return xmb->textures.list[XMB_TEXTURE_UNDO];
case MENU_ENUM_LABEL_CORE_OPTIONS_FLUSH:
return xmb->textures.list[XMB_TEXTURE_FILE];
case MENU_ENUM_LABEL_CORE_LOCK:
return xmb->textures.list[XMB_TEXTURE_CORE];
case MENU_ENUM_LABEL_ONSCREEN_DISPLAY_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_OSD];
case MENU_ENUM_LABEL_SHOW_WIMP:
case MENU_ENUM_LABEL_USER_INTERFACE_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_UI];
#ifdef HAVE_LAKKA_SWITCH
case MENU_ENUM_LABEL_SWITCH_GPU_PROFILE:
#endif
#if defined(HAVE_LAKKA_SWITCH) || defined(HAVE_LIBNX)
case MENU_ENUM_LABEL_SWITCH_CPU_PROFILE:
return xmb->textures.list[XMB_TEXTURE_POWER];
#endif
case MENU_ENUM_LABEL_POWER_MANAGEMENT_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_POWER];
case MENU_ENUM_LABEL_RETRO_ACHIEVEMENTS_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_ACHIEVEMENTS];
case MENU_ENUM_LABEL_PLAYLIST_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_PLAYLIST];
case MENU_ENUM_LABEL_USER_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_USER];
case MENU_ENUM_LABEL_PRIVACY_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_PRIVACY];
case MENU_ENUM_LABEL_REWIND_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_REWIND];
case MENU_ENUM_LABEL_QUICK_MENU_OVERRIDE_OPTIONS:
return xmb->textures.list[XMB_TEXTURE_OVERRIDE];
case MENU_ENUM_LABEL_ONSCREEN_NOTIFICATIONS_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_NOTIFICATIONS];
#ifdef HAVE_NETWORKING
case MENU_ENUM_LABEL_NETPLAY_ENABLE_HOST:
return xmb->textures.list[XMB_TEXTURE_RUN];
case MENU_ENUM_LABEL_NETPLAY_DISCONNECT:
return xmb->textures.list[XMB_TEXTURE_CLOSE];
case MENU_ENUM_LABEL_NETPLAY_ENABLE_CLIENT:
return xmb->textures.list[XMB_TEXTURE_ROOM];
case MENU_ENUM_LABEL_NETPLAY_REFRESH_ROOMS:
return xmb->textures.list[XMB_TEXTURE_RELOAD];
#ifdef HAVE_NETPLAYDISCOVERY
case MENU_ENUM_LABEL_NETPLAY_REFRESH_LAN:
return xmb->textures.list[XMB_TEXTURE_RELOAD];
#endif
case MENU_ENUM_LABEL_NETWORK_INFORMATION:
case MENU_ENUM_LABEL_NETWORK_SETTINGS:
case MENU_ENUM_LABEL_WIFI_SETTINGS:
case MENU_ENUM_LABEL_NETWORK_INFO_ENTRY:
case MENU_ENUM_LABEL_NETWORK_HOSTING_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_NETWORK];
#endif
case MENU_ENUM_LABEL_BLUETOOTH_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_BLUETOOTH];
case MENU_ENUM_LABEL_SHUTDOWN:
return xmb->textures.list[XMB_TEXTURE_SHUTDOWN];
case MENU_ENUM_LABEL_CHEAT_APPLY_CHANGES:
case MENU_ENUM_LABEL_SHADER_APPLY_CHANGES:
return xmb->textures.list[XMB_TEXTURE_CHECKMARK];
case MENU_ENUM_LABEL_CHEAT_ADD_NEW_AFTER:
case MENU_ENUM_LABEL_CHEAT_ADD_NEW_BEFORE:
case MENU_ENUM_LABEL_CHEAT_ADD_NEW_TOP:
case MENU_ENUM_LABEL_CHEAT_ADD_NEW_BOTTOM:
return xmb->textures.list[XMB_TEXTURE_MENU_ADD];
case MENU_ENUM_LABEL_CHEAT_APPLY_AFTER_TOGGLE:
return xmb->textures.list[XMB_TEXTURE_MENU_APPLY_TOGGLE];
case MENU_ENUM_LABEL_CHEAT_APPLY_AFTER_LOAD:
case MENU_ENUM_LABEL_SAVESTATE_AUTO_INDEX:
return xmb->textures.list[XMB_TEXTURE_MENU_APPLY_COG];
case MENU_ENUM_LABEL_SLOWMOTION_RATIO:
return xmb->textures.list[XMB_TEXTURE_RESUME];
case MENU_ENUM_LABEL_START_VIDEO_PROCESSOR:
return xmb->textures.list[XMB_TEXTURE_MOVIE];
#ifdef HAVE_LIBRETRODB
case MENU_ENUM_LABEL_EXPLORE_ITEM:
{
uintptr_t icon = menu_explore_get_entry_icon(type);
if (icon)
return icon;
break;
}
#endif
default:
break;
}
switch(type)
{
case FILE_TYPE_DIRECTORY:
return xmb->textures.list[XMB_TEXTURE_FOLDER];
case FILE_TYPE_PLAIN:
case FILE_TYPE_IN_CARCHIVE:
return xmb->textures.list[XMB_TEXTURE_FILE];
case FILE_TYPE_RPL_ENTRY:
if (core_node)
return core_node->content_icon;
switch (xmb_get_system_tab(xmb,
(unsigned)xmb->categories_selection_ptr))
{
case XMB_SYSTEM_TAB_FAVORITES:
return xmb->textures.list[XMB_TEXTURE_FAVORITE];
case XMB_SYSTEM_TAB_MUSIC:
return xmb->textures.list[XMB_TEXTURE_MUSIC];
#ifdef HAVE_IMAGEVIEWER
case XMB_SYSTEM_TAB_IMAGES:
return xmb->textures.list[XMB_TEXTURE_IMAGE];
#endif
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
case XMB_SYSTEM_TAB_VIDEO:
return xmb->textures.list[XMB_TEXTURE_MOVIE];
#endif
default:
break;
}
return xmb->textures.list[XMB_TEXTURE_FILE];
case MENU_SET_CDROM_INFO:
case MENU_SET_CDROM_LIST:
case MENU_SET_LOAD_CDROM_LIST:
return xmb->textures.list[XMB_TEXTURE_DISC];
case FILE_TYPE_SHADER:
case FILE_TYPE_SHADER_PRESET:
return xmb->textures.list[XMB_TEXTURE_SHADER_OPTIONS];
case FILE_TYPE_CARCHIVE:
return xmb->textures.list[XMB_TEXTURE_ZIP];
case FILE_TYPE_MUSIC:
return xmb->textures.list[XMB_TEXTURE_MUSIC];
case FILE_TYPE_IMAGE:
case FILE_TYPE_IMAGEVIEWER:
return xmb->textures.list[XMB_TEXTURE_IMAGE];
case FILE_TYPE_MOVIE:
return xmb->textures.list[XMB_TEXTURE_MOVIE];
case FILE_TYPE_CORE:
case FILE_TYPE_DIRECT_LOAD:
return xmb->textures.list[XMB_TEXTURE_CORE];
case FILE_TYPE_RDB:
return xmb->textures.list[XMB_TEXTURE_RDB];
case FILE_TYPE_CURSOR:
return xmb->textures.list[XMB_TEXTURE_CURSOR];
case FILE_TYPE_PLAYLIST_ENTRY:
case MENU_SETTING_ACTION_RUN:
return xmb->textures.list[XMB_TEXTURE_RUN];
case MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS:
return xmb->textures.list[XMB_TEXTURE_RESUME];
case MENU_SETTING_ACTION_CLOSE:
case MENU_SETTING_ACTION_CLOSE_HORIZONTAL:
case MENU_SETTING_ACTION_DELETE_ENTRY:
return xmb->textures.list[XMB_TEXTURE_CLOSE];
case MENU_SETTING_ACTION_SAVESTATE:
return xmb->textures.list[XMB_TEXTURE_SAVESTATE];
case MENU_SETTING_ACTION_LOADSTATE:
return xmb->textures.list[XMB_TEXTURE_LOADSTATE];
case FILE_TYPE_RDB_ENTRY:
return xmb->textures.list[XMB_TEXTURE_CORE_INFO];
case MENU_SETTING_ACTION_CORE_OPTIONS:
return xmb->textures.list[XMB_TEXTURE_CORE_OPTIONS];
case MENU_SETTING_ACTION_CORE_INPUT_REMAPPING_OPTIONS:
return xmb->textures.list[XMB_TEXTURE_INPUT_REMAPPING_OPTIONS];
case MENU_SETTING_ACTION_CORE_CHEAT_OPTIONS:
return xmb->textures.list[XMB_TEXTURE_CHEAT_OPTIONS];
case MENU_SETTING_ACTION_CORE_DISK_OPTIONS:
return xmb->textures.list[XMB_TEXTURE_DISK_OPTIONS];
case MENU_SETTING_ACTION_CORE_SHADER_OPTIONS:
return xmb->textures.list[XMB_TEXTURE_SHADER_OPTIONS];
case MENU_SETTING_ACTION_SCREENSHOT:
return xmb->textures.list[XMB_TEXTURE_SCREENSHOT];
case MENU_SETTING_ACTION_RESET:
return xmb->textures.list[XMB_TEXTURE_RELOAD];
case MENU_SETTING_ACTION_PAUSE_ACHIEVEMENTS:
return xmb->textures.list[XMB_TEXTURE_PAUSE];
case MENU_SET_SCREEN_BRIGHTNESS:
return xmb->textures.list[XMB_TEXTURE_BRIGHTNESS];
case MENU_SETTING_GROUP:
return xmb->textures.list[XMB_TEXTURE_SETTING];
case MENU_INFO_MESSAGE:
return xmb->textures.list[XMB_TEXTURE_CORE_INFO];
case MENU_BLUETOOTH:
return xmb->textures.list[XMB_TEXTURE_BLUETOOTH];
case MENU_WIFI:
return xmb->textures.list[XMB_TEXTURE_WIFI];
#ifdef HAVE_NETWORKING
case MENU_ROOM:
return xmb->textures.list[XMB_TEXTURE_ROOM];
case MENU_ROOM_LAN:
return xmb->textures.list[XMB_TEXTURE_ROOM_LAN];
case MENU_ROOM_RELAY:
return xmb->textures.list[XMB_TEXTURE_ROOM_RELAY];
#endif
case MENU_SETTINGS_INPUT_LIBRETRO_DEVICE:
case MENU_SETTINGS_INPUT_INPUT_REMAP_PORT:
return xmb->textures.list[XMB_TEXTURE_SETTING];
case MENU_SETTINGS_INPUT_ANALOG_DPAD_MODE:
return xmb->textures.list[XMB_TEXTURE_INPUT_ADC];
}
#ifdef HAVE_CHEEVOS
if (
(type >= MENU_SETTINGS_CHEEVOS_START) &&
(type < MENU_SETTINGS_NETPLAY_ROOMS_START)
)
{
char buffer[64];
int index = type - MENU_SETTINGS_CHEEVOS_START;
uintptr_t badge_texture = rcheevos_menu_get_badge_texture(index);
if (badge_texture)
return badge_texture;
/* no state means its a header - show the info icon */
if (!rcheevos_menu_get_state(index, buffer, sizeof(buffer)))
return xmb->textures.list[XMB_TEXTURE_INFO];
/* placeholder badge image was not found, show generic menu icon */
return xmb->textures.list[XMB_TEXTURE_ACHIEVEMENTS];
}
#endif
if (
(type >= MENU_SETTINGS_INPUT_BEGIN) &&
(type <= MENU_SETTINGS_INPUT_DESC_KBD_END)
)
{
unsigned input_id;
if (type < MENU_SETTINGS_INPUT_DESC_BEGIN)
/* Input User # Binds only */
{
input_id = MENU_SETTINGS_INPUT_BEGIN;
if ( type == input_id)
return xmb->textures.list[XMB_TEXTURE_SETTING];
if ( type == input_id + 1)
return xmb->textures.list[XMB_TEXTURE_INPUT_ADC];
#ifdef HAVE_LIBNX
/* account for the additional split joycon option in Input # Binds */
input_id++;
#endif
if ( type == input_id + 2)
return xmb->textures.list[XMB_TEXTURE_INPUT_SETTINGS];
if ( type == input_id + 3)
return xmb->textures.list[XMB_TEXTURE_INPUT_MOUSE];
if ( type == input_id + 4)
return xmb->textures.list[XMB_TEXTURE_INPUT_BIND_ALL];
if ( type == input_id + 5)
return xmb->textures.list[XMB_TEXTURE_RELOAD];
if ( type == input_id + 6)
return xmb->textures.list[XMB_TEXTURE_SAVING];
if ((type > (input_id + 30)) && (type < (input_id + 42)))
return xmb->textures.list[XMB_TEXTURE_INPUT_LGUN];
if ( type == input_id + 42)
return xmb->textures.list[XMB_TEXTURE_INPUT_TURBO];
/* align to use the same code of Quickmenu controls */
input_id = input_id + 7;
}
else
{
/* Quickmenu controls repeats the same icons for all users */
if (type < MENU_SETTINGS_INPUT_DESC_KBD_BEGIN)
input_id = MENU_SETTINGS_INPUT_DESC_BEGIN;
else
input_id = MENU_SETTINGS_INPUT_DESC_KBD_BEGIN;
while (type > (input_id + 23))
input_id = (input_id + 24);
/* Human readable bind order */
if (type < (input_id + RARCH_ANALOG_BIND_LIST_END))
{
unsigned index = 0;
int input_num = type - input_id;
for (index = 0; index < ARRAY_SIZE(input_config_bind_order); index++)
{
if (input_config_bind_order[index] == input_num)
{
type = input_id + index;
break;
}
}
}
}
/* This is utilized for both Input # Binds and Quickmenu controls */
if (type == input_id)
return xmb->textures.list[XMB_TEXTURE_INPUT_DPAD_U];
if (type == (input_id + 1))
return xmb->textures.list[XMB_TEXTURE_INPUT_DPAD_D];
if (type == (input_id + 2))
return xmb->textures.list[XMB_TEXTURE_INPUT_DPAD_L];
if (type == (input_id + 3))
return xmb->textures.list[XMB_TEXTURE_INPUT_DPAD_R];
if (type == (input_id + 4))
return xmb->textures.list[XMB_TEXTURE_INPUT_BTN_R];
if (type == (input_id + 5))
return xmb->textures.list[XMB_TEXTURE_INPUT_BTN_D];
if (type == (input_id + 6))
return xmb->textures.list[XMB_TEXTURE_INPUT_BTN_U];
if (type == (input_id + 7))
return xmb->textures.list[XMB_TEXTURE_INPUT_BTN_L];
if (type == (input_id + 8))
return xmb->textures.list[XMB_TEXTURE_INPUT_SELECT];
if (type == (input_id + 9))
return xmb->textures.list[XMB_TEXTURE_INPUT_START];
if (type == (input_id + 10))
return xmb->textures.list[XMB_TEXTURE_INPUT_LB];
if (type == (input_id + 11))
return xmb->textures.list[XMB_TEXTURE_INPUT_RB];
if (type == (input_id + 12))
return xmb->textures.list[XMB_TEXTURE_INPUT_LT];
if (type == (input_id + 13))
return xmb->textures.list[XMB_TEXTURE_INPUT_RT];
if (type == (input_id + 14))
return xmb->textures.list[XMB_TEXTURE_INPUT_STCK_P];
if (type == (input_id + 15))
return xmb->textures.list[XMB_TEXTURE_INPUT_STCK_P];
if (type == (input_id + 16))
return xmb->textures.list[XMB_TEXTURE_INPUT_STCK_U];
if (type == (input_id + 17))
return xmb->textures.list[XMB_TEXTURE_INPUT_STCK_D];
if (type == (input_id + 18))
return xmb->textures.list[XMB_TEXTURE_INPUT_STCK_L];
if (type == (input_id + 19))
return xmb->textures.list[XMB_TEXTURE_INPUT_STCK_R];
if (type == (input_id + 20))
return xmb->textures.list[XMB_TEXTURE_INPUT_STCK_U];
if (type == (input_id + 21))
return xmb->textures.list[XMB_TEXTURE_INPUT_STCK_D];
if (type == (input_id + 22))
return xmb->textures.list[XMB_TEXTURE_INPUT_STCK_L];
if (type == (input_id + 23))
return xmb->textures.list[XMB_TEXTURE_INPUT_STCK_R];
}
if (
(type >= MENU_SETTINGS_REMAPPING_PORT_BEGIN) &&
(type <= MENU_SETTINGS_REMAPPING_PORT_END)
)
return xmb->textures.list[XMB_TEXTURE_INPUT_SETTINGS];
if (checked)
return xmb->textures.list[XMB_TEXTURE_CHECKMARK];
if (type == MENU_SETTING_ACTION)
return xmb->textures.list[XMB_TEXTURE_SETTING];
return xmb->textures.list[XMB_TEXTURE_SUBSETTING];
}
static int xmb_draw_item(
void *userdata,
gfx_display_t *p_disp,
gfx_animation_t *p_anim,
gfx_display_ctx_driver_t *dispctx,
settings_t *settings,
unsigned video_width,
unsigned video_height,
bool xmb_shadows_enable,
math_matrix_4x4 *mymat,
xmb_handle_t *xmb,
xmb_node_t *core_node,
file_list_t *list,
float *color,
size_t i,
size_t current,
unsigned width,
unsigned height
)
{
menu_entry_t entry;
float icon_x, icon_y, label_offset;
gfx_animation_ctx_ticker_t ticker;
gfx_animation_ctx_ticker_smooth_t ticker_smooth;
char tmp[255];
unsigned ticker_x_offset = 0;
const char *ticker_str = NULL;
unsigned entry_type = 0;
const float half_size = xmb->icon_size / 2.0f;
uintptr_t texture_switch = 0;
bool do_draw_text = false;
unsigned ticker_limit = 35 * xmb_scale_mod[0];
unsigned line_ticker_width = 45 * xmb_scale_mod[3];
xmb_node_t * node = (xmb_node_t*)list->list[i].userdata;
bool use_smooth_ticker = settings->bools.menu_ticker_smooth;
enum gfx_animation_ticker_type
menu_ticker_type = (enum gfx_animation_ticker_type)settings->uints.menu_ticker_type;
unsigned xmb_thumbnail_scale_factor =
settings->uints.menu_xmb_thumbnail_scale_factor;
bool menu_xmb_vertical_thumbnails = settings->bools.menu_xmb_vertical_thumbnails;
bool menu_show_sublabels = settings->bools.menu_show_sublabels;
/* Initial ticker configuration */
if (use_smooth_ticker)
{
ticker_smooth.idx = p_anim->ticker_pixel_idx;
ticker_smooth.font = xmb->font;
ticker_smooth.font_scale = 1.0f;
ticker_smooth.type_enum = menu_ticker_type;
ticker_smooth.spacer = NULL;
ticker_smooth.x_offset = &ticker_x_offset;
ticker_smooth.dst_str_width = NULL;
}
else
{
ticker.idx = p_anim->ticker_idx;
ticker.type_enum = menu_ticker_type;
ticker.spacer = NULL;
}
if (!node)
return 0;
tmp[0] = '\0';
icon_y = xmb->margins_screen_top + node->y + half_size;
if (icon_y < half_size)
return 0;
if (icon_y > height + xmb->icon_size)
return -1;
icon_x = node->x + xmb->margins_screen_left +
xmb->icon_spacing_horizontal - half_size;
if (icon_x < -half_size || icon_x > width)
return 0;
MENU_ENTRY_INIT(entry);
entry.label_enabled = false;
entry.sublabel_enabled = (i == current);
menu_entry_get(&entry, 0, i, list, true);
entry_type = entry.type;
if (entry_type == FILE_TYPE_CONTENTLIST_ENTRY)
{
char entry_path[PATH_MAX_LENGTH];
entry_path[0] = '\0';
strlcpy(entry_path, entry.path, sizeof(entry_path));
fill_short_pathname_representation(entry_path, entry_path,
sizeof(entry_path));
if (!string_is_empty(entry_path))
strlcpy(entry.path, entry_path, sizeof(entry.path));
}
if (string_is_equal(entry.value,
msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)) ||
(string_is_equal(entry.value,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))))
{
if (xmb->textures.list[XMB_TEXTURE_SWITCH_OFF])
texture_switch = xmb->textures.list[XMB_TEXTURE_SWITCH_OFF];
else
do_draw_text = true;
}
else if (string_is_equal(entry.value,
msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) ||
(string_is_equal(entry.value,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON))))
{
if (xmb->textures.list[XMB_TEXTURE_SWITCH_ON])
texture_switch = xmb->textures.list[XMB_TEXTURE_SWITCH_ON];
else
do_draw_text = true;
}
else
{
if (!string_is_empty(entry.value))
{
bool found = false;
if (string_is_equal(entry.value, "..."))
found = true;
else if (string_starts_with_size(entry.value, "(", STRLEN_CONST("(")) &&
string_ends_with (entry.value, ")")
)
{
if (
string_is_equal(entry.value, "(PRESET)") ||
string_is_equal(entry.value, "(SHADER)") ||
string_is_equal(entry.value, "(COMP)") ||
string_is_equal(entry.value, "(CORE)") ||
string_is_equal(entry.value, "(MOVIE)") ||
string_is_equal(entry.value, "(MUSIC)") ||
string_is_equal(entry.value, "(DIR)") ||
string_is_equal(entry.value, "(RDB)") ||
string_is_equal(entry.value, "(CURSOR)") ||
string_is_equal(entry.value, "(CFILE)") ||
string_is_equal(entry.value, "(FILE)") ||
string_is_equal(entry.value, "(IMAGE)")
)
found = true;
}
if (!found)
do_draw_text = true;
}
else
do_draw_text = true;
}
if (string_is_empty(entry.value))
{
if ((xmb->thumbnails.savestate.status == GFX_THUMBNAIL_STATUS_AVAILABLE) ||
!xmb->use_ps3_layout ||
(gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_RIGHT)
&& ((xmb->thumbnails.right.status == GFX_THUMBNAIL_STATUS_AVAILABLE)
|| (xmb->thumbnails.right.status == GFX_THUMBNAIL_STATUS_PENDING))) ||
(gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_LEFT)
&& ((xmb->thumbnails.left.status == GFX_THUMBNAIL_STATUS_AVAILABLE)
|| (xmb->thumbnails.left.status == GFX_THUMBNAIL_STATUS_PENDING))
&& menu_xmb_vertical_thumbnails)
)
{
ticker_limit = 40 * xmb_scale_mod[1];
line_ticker_width = 50 * xmb_scale_mod[3];
/* Can increase text length if thumbnail is downscaled */
if (xmb_thumbnail_scale_factor < 100)
{
float ticker_scale_factor =
1.0f - ((float)xmb_thumbnail_scale_factor / 100.0f);
ticker_limit +=
(unsigned)(ticker_scale_factor * 15.0f * xmb_scale_mod[1]);
line_ticker_width +=
(unsigned)(ticker_scale_factor * 10.0f * xmb_scale_mod[3]);
}
}
else
{
ticker_limit = 70 * xmb_scale_mod[2];
line_ticker_width = 60 * xmb_scale_mod[3];
}
}
if (!string_is_empty(entry.rich_label))
ticker_str = entry.rich_label;
else
ticker_str = entry.path;
if (use_smooth_ticker)
{
ticker_smooth.selected = (i == current);
ticker_smooth.field_width = xmb->font_size * 0.5f * ticker_limit;
ticker_smooth.src_str = ticker_str;
ticker_smooth.dst_str = tmp;
ticker_smooth.dst_str_len = sizeof(tmp);
if (ticker_smooth.src_str)
gfx_animation_ticker_smooth(&ticker_smooth);
}
else
{
ticker.s = tmp;
ticker.len = ticker_limit;
ticker.str = ticker_str;
ticker.selected = (i == current);
if (ticker.str)
gfx_animation_ticker(&ticker);
}
label_offset = xmb->margins_label_top;
if (menu_show_sublabels)
{
if (i == current && width > 320 && height > 240
&& !string_is_empty(entry.sublabel))
{
char entry_sublabel[MENU_SUBLABEL_MAX_LENGTH];
char entry_sublabel_top_fade[MENU_SUBLABEL_MAX_LENGTH >> 2];
char entry_sublabel_bottom_fade[MENU_SUBLABEL_MAX_LENGTH >> 2];
gfx_animation_ctx_line_ticker_t line_ticker;
gfx_animation_ctx_line_ticker_smooth_t line_ticker_smooth;
float ticker_y_offset = 0.0f;
float ticker_top_fade_y_offset = 0.0f;
float ticker_bottom_fade_y_offset = 0.0f;
float ticker_top_fade_alpha = 0.0f;
float ticker_bottom_fade_alpha = 0.0f;
float sublabel_x = node->x + xmb->margins_screen_left +
xmb->icon_spacing_horizontal + xmb->margins_label_left;
float sublabel_y = xmb->margins_screen_top +
node->y + (xmb->margins_label_top * 3.5f);
entry_sublabel[0] = '\0';
entry_sublabel_top_fade[0] = '\0';
entry_sublabel_bottom_fade[0] = '\0';
if (use_smooth_ticker)
{
line_ticker_smooth.fade_enabled = true;
line_ticker_smooth.type_enum = menu_ticker_type;
line_ticker_smooth.idx = p_anim->ticker_pixel_line_idx;
line_ticker_smooth.font = xmb->font2;
line_ticker_smooth.font_scale = 1.0f;
line_ticker_smooth.field_width = (unsigned)(xmb->font2_size * 0.6f * line_ticker_width);
/* The calculation here is incredibly obtuse. I think
* this is correct... (c.f. xmb_item_y()) */
line_ticker_smooth.field_height = (unsigned)(
(xmb->icon_spacing_vertical * ((1 + xmb->under_item_offset) - xmb->active_item_factor)) -
(xmb->margins_label_top * 3.5f) -
xmb->under_item_offset); /* This last one is just a little extra padding (seems to help) */
line_ticker_smooth.src_str = entry.sublabel;
line_ticker_smooth.dst_str = entry_sublabel;
line_ticker_smooth.dst_str_len = sizeof(entry_sublabel);
line_ticker_smooth.y_offset = &ticker_y_offset;
line_ticker_smooth.top_fade_str = entry_sublabel_top_fade;
line_ticker_smooth.top_fade_str_len = sizeof(entry_sublabel_top_fade);
line_ticker_smooth.top_fade_y_offset = &ticker_top_fade_y_offset;
line_ticker_smooth.top_fade_alpha = &ticker_top_fade_alpha;
line_ticker_smooth.bottom_fade_str = entry_sublabel_bottom_fade;
line_ticker_smooth.bottom_fade_str_len = sizeof(entry_sublabel_bottom_fade);
line_ticker_smooth.bottom_fade_y_offset = &ticker_bottom_fade_y_offset;
line_ticker_smooth.bottom_fade_alpha = &ticker_bottom_fade_alpha;
gfx_animation_line_ticker_smooth(&line_ticker_smooth);
}
else
{
line_ticker.type_enum = menu_ticker_type;
line_ticker.idx = p_anim->ticker_idx;
line_ticker.line_len = (size_t)(line_ticker_width);
/* Note: max_lines should be calculated at runtime,
* but this is a nuisance. There is room for 4 lines
* to be displayed when using all existing XMB themes,
* so leave this value hard coded for now. */
line_ticker.max_lines = 4;
line_ticker.s = entry_sublabel;
line_ticker.len = sizeof(entry_sublabel);
line_ticker.str = entry.sublabel;
gfx_animation_line_ticker(&line_ticker);
}
label_offset = - xmb->margins_label_top;
/* Draw sublabel */
xmb_draw_text(xmb_shadows_enable, xmb, settings,
entry_sublabel,
sublabel_x, ticker_y_offset + sublabel_y,
1, node->label_alpha, TEXT_ALIGN_LEFT,
width, height, xmb->font2);
/* Draw top/bottom line fade effect, if required */
if (use_smooth_ticker)
{
if (!string_is_empty(entry_sublabel_top_fade) &&
ticker_top_fade_alpha > 0.0f)
xmb_draw_text(xmb_shadows_enable, xmb, settings,
entry_sublabel_top_fade,
sublabel_x, ticker_top_fade_y_offset + sublabel_y,
1, ticker_top_fade_alpha * node->label_alpha, TEXT_ALIGN_LEFT,
width, height, xmb->font2);
if (!string_is_empty(entry_sublabel_bottom_fade) &&
ticker_bottom_fade_alpha > 0.0f)
xmb_draw_text(xmb_shadows_enable, xmb, settings,
entry_sublabel_bottom_fade,
sublabel_x, ticker_bottom_fade_y_offset + sublabel_y,
1, ticker_bottom_fade_alpha * node->label_alpha, TEXT_ALIGN_LEFT,
width, height, xmb->font2);
}
}
}
/* Draw entry index of current selection */
if (i == current && xmb->entry_idx_enabled)
{
/* Calculate position depending on the current
* list and if Thumbnail Vertical Disposition
* is enabled (branchless version) */
float x_position = (video_width - xmb->margins_title_left/4) *
!menu_xmb_vertical_thumbnails +
(node->x + xmb->margins_screen_left +
xmb->icon_spacing_horizontal -
xmb->margins_label_left) *
menu_xmb_vertical_thumbnails;
float y_position = (video_height - xmb->margins_title_bottom/4) *
!menu_xmb_vertical_thumbnails +
(xmb->margins_screen_top + xmb->margins_label_top +
xmb->icon_spacing_vertical * xmb->active_item_factor) *
menu_xmb_vertical_thumbnails;
xmb_draw_text(xmb_shadows_enable, xmb, settings,
xmb->entry_index_str, x_position, y_position,
1, menu_xmb_vertical_thumbnails ? node->label_alpha : 1,
TEXT_ALIGN_RIGHT, width, height, xmb->font);
}
xmb_draw_text(xmb_shadows_enable, xmb, settings, tmp,
(float)ticker_x_offset + node->x + xmb->margins_screen_left +
xmb->icon_spacing_horizontal + xmb->margins_label_left,
xmb->margins_screen_top + node->y + label_offset,
1, node->label_alpha, TEXT_ALIGN_LEFT,
width, height, xmb->font);
tmp[0] = '\0';
if (use_smooth_ticker)
{
ticker_smooth.selected = (i == current);
ticker_smooth.field_width = xmb->font_size * 0.5f * 35 * xmb_scale_mod[7];
ticker_smooth.src_str = entry.value;
ticker_smooth.dst_str = tmp;
ticker_smooth.dst_str_len = sizeof(tmp);
if (!string_is_empty(entry.value))
gfx_animation_ticker_smooth(&ticker_smooth);
}
else
{
ticker.s = tmp;
ticker.len = 35 * xmb_scale_mod[7];
ticker.selected = (i == current);
ticker.str = entry.value;
if (!string_is_empty(entry.value))
gfx_animation_ticker(&ticker);
}
if (do_draw_text)
xmb_draw_text(xmb_shadows_enable, xmb, settings, tmp,
(float)ticker_x_offset + node->x +
+ xmb->margins_screen_left
+ xmb->icon_spacing_horizontal
+ xmb->margins_label_left
+ xmb->margins_setting_left,
xmb->margins_screen_top + node->y + xmb->margins_label_top,
1,
node->label_alpha,
TEXT_ALIGN_LEFT,
width, height, xmb->font);
gfx_display_set_alpha(color, MIN(node->alpha, xmb->alpha));
if (
(!xmb->assets_missing) &&
(color[3] != 0) &&
(
(entry.checked) ||
!((entry_type >= MENU_SETTING_DROPDOWN_ITEM) && (entry_type <= MENU_SETTING_DROPDOWN_SETTING_UINT_ITEM_SPECIAL))
)
)
{
math_matrix_4x4 mymat_tmp;
gfx_display_ctx_rotate_draw_t rotate_draw;
uintptr_t texture = xmb_icon_get_id(xmb, core_node, node,
entry.enum_idx, entry_type, (i == current), entry.checked);
float x = icon_x;
float y = icon_y;
float scale_factor = node->zoom;
rotate_draw.matrix = &mymat_tmp;
rotate_draw.rotation = 0;
rotate_draw.scale_x = scale_factor;
rotate_draw.scale_y = scale_factor;
rotate_draw.scale_z = 1;
rotate_draw.scale_enable = true;
gfx_display_rotate_z(p_disp, &rotate_draw, userdata);
xmb_draw_icon(
userdata,
p_disp,
dispctx,
video_width,
video_height,
xmb_shadows_enable,
xmb->icon_size,
texture,
x,
y,
width,
height,
1.0,
0, /* rotation */
scale_factor,
&color[0],
xmb->shadow_offset,
&mymat_tmp);
}
gfx_display_set_alpha(color, MIN(node->alpha, xmb->alpha));
if (texture_switch != 0 && color[3] != 0 && !xmb->assets_missing)
xmb_draw_icon(
userdata,
p_disp,
dispctx,
video_width,
video_height,
xmb_shadows_enable,
xmb->icon_size,
texture_switch,
node->x + xmb->margins_screen_left
+ xmb->icon_spacing_horizontal
+ xmb->icon_size / 2.0 + xmb->margins_setting_left,
xmb->margins_screen_top + node->y + xmb->icon_size / 2.0,
width, height,
node->alpha,
0,
1,
&color[0],
xmb->shadow_offset,
mymat);
return 0;
}
static void xmb_draw_items(
void *userdata,
gfx_display_t *p_disp,
gfx_animation_t *p_anim,
settings_t *settings,
unsigned video_width,
unsigned video_height,
bool xmb_shadows_enable,
xmb_handle_t *xmb,
file_list_t *list,
size_t current, size_t cat_selection_ptr, float *color,
unsigned width, unsigned height,
math_matrix_4x4 *mymat)
{
size_t i;
unsigned first, last;
xmb_node_t *core_node = NULL;
size_t end = 0;
gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
if (!list || !list->size || !xmb)
return;
if (cat_selection_ptr > xmb->system_tab_end)
core_node = xmb_get_userdata_from_horizontal_list(
xmb, (unsigned)(cat_selection_ptr - (xmb->system_tab_end + 1)));
end = list ? list->size : 0;
menu_entries_ctl(MENU_ENTRIES_CTL_START_GET, &i);
if (list == &xmb->selection_buf_old)
{
xmb_node_t *node = (xmb_node_t*)list->list[current].userdata;
if (node && (uint8_t)(255 * node->alpha) == 0)
return;
i = 0;
}
first = (unsigned)i;
last = (unsigned)(end - 1);
xmb_calculate_visible_range(xmb, height,
end, (unsigned)current, &first, &last);
for (i = first; i <= last; i++)
{
if (xmb_draw_item(
userdata,
p_disp,
p_anim,
dispctx,
settings,
video_width,
video_height,
xmb_shadows_enable,
mymat,
xmb, core_node,
list, color,
i, current,
width, height) == -1)
break;
}
}
static INLINE bool xmb_use_ps3_layout(
settings_t *settings, unsigned width, unsigned height)
{
unsigned menu_xmb_layout = settings->uints.menu_xmb_layout;
switch (menu_xmb_layout)
{
case 1:
/* PS3 */
return true;
case 2:
/* PSP */
return false;
case 0:
default:
/* Automatic
* > Use PSP layout on tiny screens */
return (width > 320) && (height > 240);
}
}
static INLINE float xmb_get_scale_factor(
settings_t *settings, bool use_ps3_layout, unsigned width)
{
float scale_factor;
float menu_scale_factor = settings->floats.menu_scale_factor;
/* PS3 Layout */
if (use_ps3_layout)
scale_factor = ((menu_scale_factor * (float)width) / 1920.0f);
/* PSP Layout */
else
{
#ifdef _3DS
scale_factor = menu_scale_factor / 4.0f;
#else
scale_factor = ((menu_scale_factor * (float)width) / 1920.0f) * 1.5f;
#endif
}
/* Apply safety limit */
if (scale_factor < 0.1f)
return 0.1f;
return scale_factor;
}
static void xmb_context_reset_internal(xmb_handle_t *xmb,
bool is_threaded, bool reinit_textures);
/* Disables the fullscreen thumbnail view, with
* an optional fade out animation */
static void xmb_hide_fullscreen_thumbnails(
xmb_handle_t *xmb, bool animate)
{
uintptr_t alpha_tag = (uintptr_t)&xmb->fullscreen_thumbnail_alpha;
/* Kill any existing fade in/out animations */
gfx_animation_kill_by_tag(&alpha_tag);
/* Check whether animations are enabled */
if (animate && (xmb->fullscreen_thumbnail_alpha > 0.0f))
{
gfx_animation_ctx_entry_t animation_entry;
/* Configure fade out animation */
animation_entry.easing_enum = EASING_OUT_QUAD;
animation_entry.tag = alpha_tag;
animation_entry.duration = gfx_thumb_get_ptr()->fade_duration;
animation_entry.target_value = 0.0f;
animation_entry.subject = &xmb->fullscreen_thumbnail_alpha;
animation_entry.cb = NULL;
animation_entry.userdata = NULL;
/* Push animation */
gfx_animation_push(&animation_entry);
}
/* No animation - just set thumbnail alpha to zero */
else
xmb->fullscreen_thumbnail_alpha = 0.0f;
/* Disable fullscreen thumbnails */
xmb->show_fullscreen_thumbnails = false;
}
/* Enables (and triggers a fade in of) the fullscreen
* thumbnail view */
static void xmb_show_fullscreen_thumbnails(
xmb_handle_t *xmb, size_t selection)
{
menu_entry_t selected_entry;
gfx_animation_ctx_entry_t animation_entry;
const char *core_name = NULL;
const char *thumbnail_label = NULL;
uintptr_t alpha_tag = (uintptr_t)
&xmb->fullscreen_thumbnail_alpha;
/* Before showing fullscreen thumbnails, must
* ensure that any existing fullscreen thumbnail
* view is disabled... */
xmb_hide_fullscreen_thumbnails(xmb, false);
/* Sanity check: Return immediately if this is
* a menu without thumbnail support */
if (!xmb->fullscreen_thumbnails_available)
return;
/* We can only enable fullscreen thumbnails if
* current selection has at least one valid thumbnail
* and all thumbnails for current selection are already
* loaded/available */
gfx_thumbnail_get_core_name(xmb->thumbnail_path_data, &core_name);
if (string_is_equal(core_name, "imageviewer"))
{
/* imageviewer content requires special treatment,
* since only one thumbnail can ever be loaded
* at a time */
if (gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_RIGHT))
{
if (xmb->thumbnails.right.status != GFX_THUMBNAIL_STATUS_AVAILABLE)
return;
}
else if (gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
{
if (xmb->thumbnails.left.status != GFX_THUMBNAIL_STATUS_AVAILABLE)
return;
}
else
return;
}
else
{
bool left_thumbnail_enabled = gfx_thumbnail_is_enabled(
xmb->thumbnail_path_data, GFX_THUMBNAIL_LEFT);
if ((xmb->thumbnails.right.status == GFX_THUMBNAIL_STATUS_AVAILABLE) &&
(left_thumbnail_enabled &&
((xmb->thumbnails.left.status != GFX_THUMBNAIL_STATUS_MISSING) &&
(xmb->thumbnails.left.status != GFX_THUMBNAIL_STATUS_AVAILABLE))))
return;
if ((xmb->thumbnails.right.status == GFX_THUMBNAIL_STATUS_MISSING) &&
(!left_thumbnail_enabled ||
(xmb->thumbnails.left.status != GFX_THUMBNAIL_STATUS_AVAILABLE)))
return;
}
/* Cache selected entry label
* (used as title when fullscreen thumbnails
* are shown) */
xmb->fullscreen_thumbnail_label[0] = '\0';
/* > Get menu entry */
MENU_ENTRY_INIT(selected_entry);
selected_entry.path_enabled = false;
selected_entry.value_enabled = false;
selected_entry.sublabel_enabled = false;
menu_entry_get(&selected_entry, 0, selection, NULL, true);
/* > Get entry label */
if (!string_is_empty(selected_entry.rich_label))
thumbnail_label = selected_entry.rich_label;
else
thumbnail_label = selected_entry.path;
/* > Sanity check */
if (!string_is_empty(thumbnail_label))
strlcpy(
xmb->fullscreen_thumbnail_label,
thumbnail_label,
sizeof(xmb->fullscreen_thumbnail_label));
/* Configure fade in animation */
animation_entry.easing_enum = EASING_OUT_QUAD;
animation_entry.tag = alpha_tag;
animation_entry.duration = gfx_thumb_get_ptr()->fade_duration;
animation_entry.target_value = 1.0f;
animation_entry.subject = &xmb->fullscreen_thumbnail_alpha;
animation_entry.cb = NULL;
animation_entry.userdata = NULL;
/* Push animation */
gfx_animation_push(&animation_entry);
/* Enable fullscreen thumbnails */
xmb->fullscreen_thumbnail_selection = selection;
xmb->show_fullscreen_thumbnails = true;
}
static enum menu_action xmb_parse_menu_entry_action(
xmb_handle_t *xmb, enum menu_action action)
{
enum menu_action new_action = action;
/* If fullscreen thumbnail view is active, any
* valid menu action will disable it... */
if (xmb->show_fullscreen_thumbnails)
{
if (action != MENU_ACTION_NOOP)
{
xmb_hide_fullscreen_thumbnails(xmb, true);
/* ...and any action other than Select/OK
* is ignored
* > We allow pass-through of Select/OK since
* users may want to run content directly
* after viewing fullscreen thumbnails,
* and having to press RetroPad A or the Return
* key twice is navigationally confusing
* > Note that we can only do this for non-pointer
* input
* > Note that we don't do this when viewing a
* file list, since there is no quick menu
* in this case - i.e. content loads directly,
* and a sudden transition from fullscreen
* thumbnail to content is jarring... */
if (xmb->is_file_list ||
((action != MENU_ACTION_SELECT) &&
(action != MENU_ACTION_OK)))
return MENU_ACTION_NOOP;
}
}
/* Scan user inputs */
switch (action)
{
case MENU_ACTION_LEFT:
case MENU_ACTION_RIGHT:
/* Check whether left/right action will
* trigger a tab switch event */
if (xmb->depth == 1)
{
retro_time_t current_time = menu_driver_get_current_time();
size_t scroll_accel = 0;
/* Determine whether input repeat is
* currently active
* > This is always true when scroll
* acceleration is greater than zero */
menu_driver_ctl(MENU_NAVIGATION_CTL_GET_SCROLL_ACCEL,
&scroll_accel);
if (scroll_accel > 0)
{
/* Ignore input action if tab switch period
* is less than defined limit */
if ((current_time - xmb->last_tab_switch_time) <
XMB_TAB_SWITCH_REPEAT_DELAY)
{
new_action = MENU_ACTION_NOOP;
break;
}
}
xmb->last_tab_switch_time = current_time;
}
break;
case MENU_ACTION_START:
/* If this is a menu with thumbnails, attempt
* to show fullscreen thumbnail view */
if (xmb->fullscreen_thumbnails_available)
{
size_t selection = menu_navigation_get_selection();
xmb_show_fullscreen_thumbnails(xmb, selection);
new_action = MENU_ACTION_NOOP;
}
break;
default:
/* In all other cases, pass through input
* menu action without intervention */
break;
}
return new_action;
}
/* Menu entry action callback */
static int xmb_menu_entry_action(
void *userdata, menu_entry_t *entry,
size_t i, enum menu_action action)
{
xmb_handle_t *xmb = (xmb_handle_t*)userdata;
/* Process input action */
enum menu_action new_action = xmb_parse_menu_entry_action(xmb, action);
/* Call standard generic_menu_entry_action() function */
return generic_menu_entry_action(userdata, entry, i, new_action);
}
static void xmb_render(void *data,
unsigned width, unsigned height, bool is_idle)
{
/* 'i' must be of 'size_t', since it is passed
* by reference to menu_entries_ctl() */
size_t i;
/* c.f. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
* On some platforms (e.g. 32-bit x86 without SSE),
* gcc can produce inconsistent floating point results
* depending upon optimisation level. This can break
* floating point variable comparisons. A workaround is
* to declare the affected variable as 'volatile', which
* disables optimisations and removes excess precision
* (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323#c87) */
volatile float scale_factor;
xmb_handle_t *xmb = (xmb_handle_t*)data;
settings_t *settings = config_get_ptr();
size_t end = menu_entries_get_size();
gfx_display_t *p_disp = disp_get_ptr();
gfx_animation_t *p_anim = anim_get_ptr();
if (!xmb)
return;
xmb->use_ps3_layout = xmb_use_ps3_layout(settings, width, height);
scale_factor = xmb_get_scale_factor(settings, xmb->use_ps3_layout, width);
if ((xmb->use_ps3_layout != xmb->last_use_ps3_layout) ||
(scale_factor != xmb->last_scale_factor))
{
xmb->last_use_ps3_layout = xmb->use_ps3_layout;
xmb->last_scale_factor = scale_factor;
xmb_context_reset_internal(xmb, video_driver_is_threaded(),
false);
}
/* This must be set every frame when using a pointer,
* otherwise touchscreen input breaks when changing
* orientation */
p_disp->framebuf_width = width;
p_disp->framebuf_height = height;
/* Read pointer state */
menu_input_get_pointer_state(&xmb->pointer);
/* 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;
}
if (xmb->pointer.type != MENU_POINTER_DISABLED)
{
size_t selection = menu_navigation_get_selection();
int16_t margin_top = (int16_t)xmb->margins_screen_top;
int16_t margin_left = (int16_t)xmb->margins_screen_left;
int16_t margin_right = (int16_t)((float)width - xmb->margins_screen_left);
int16_t pointer_x = xmb->pointer.x;
int16_t pointer_y = xmb->pointer.y;
/* When determining current pointer selection, we
* only track pointer movements between the left
* and right screen margins */
if ((pointer_x > margin_left) && (pointer_x < margin_right))
{
unsigned first = 0;
unsigned last = (unsigned)end;
if (height)
xmb_calculate_visible_range(xmb, height,
end, (unsigned)selection, &first, &last);
for (i = (size_t)first; i <= (size_t)last; i++)
{
float entry_size = (i == (unsigned)selection) ?
xmb->icon_spacing_vertical * xmb->active_item_factor : xmb->icon_spacing_vertical;
float half_entry_size = entry_size * 0.5f;
float y_curr = xmb_item_y(xmb, (int)i, selection) + xmb->margins_screen_top;
int y1 = (int)((y_curr - half_entry_size) + 0.5f);
int y2 = (int)((y_curr + half_entry_size) + 0.5f);
if ((pointer_y > y1) && (pointer_y < y2))
{
menu_input_set_pointer_selection((unsigned)i);
break;
}
}
}
/* Areas beyond the top/right margins are used
* as a sort of virtual dpad:
* - Above top margin: navigate left/right
* - Beyond right margin: navigate up/down */
if ((pointer_y < margin_top) || (pointer_x > margin_right))
{
menu_entry_t entry;
bool get_entry = false;
switch (xmb->pointer.press_direction)
{
case MENU_INPUT_PRESS_DIRECTION_UP:
if (pointer_x > margin_right)
get_entry = true;
break;
case MENU_INPUT_PRESS_DIRECTION_DOWN:
/* Note: Direction is inverted, since 'down' should
* move list downwards */
if (pointer_x > margin_right)
get_entry = true;
break;
case MENU_INPUT_PRESS_DIRECTION_LEFT:
/* Navigate left
* Note: At the top level, navigating left
* means switching to the 'next' horizontal list,
* which is actually a movement to the *right* */
if (pointer_y < margin_top)
get_entry = true;
break;
case MENU_INPUT_PRESS_DIRECTION_RIGHT:
/* Navigate right
* Note: At the top level, navigating right
* means switching to the 'previous' horizontal list,
* which is actually a movement to the *left* */
if (pointer_y < margin_top)
get_entry = true;
break;
case MENU_INPUT_PRESS_DIRECTION_NONE:
default:
break;
}
if (get_entry)
{
MENU_ENTRY_INIT(entry);
entry.path_enabled = false;
entry.label_enabled = false;
entry.rich_label_enabled = false;
entry.value_enabled = false;
entry.sublabel_enabled = false;
menu_entry_get(&entry, 0, selection, NULL, true);
}
switch (xmb->pointer.press_direction)
{
case MENU_INPUT_PRESS_DIRECTION_UP:
/* Note: Direction is inverted, since 'up' should
* move list upwards */
if (pointer_x > margin_right)
xmb_menu_entry_action(xmb, &entry, selection, MENU_ACTION_DOWN);
break;
case MENU_INPUT_PRESS_DIRECTION_DOWN:
/* Note: Direction is inverted, since 'down' should
* move list downwards */
if (pointer_x > margin_right)
xmb_menu_entry_action(xmb, &entry, selection, MENU_ACTION_UP);
break;
case MENU_INPUT_PRESS_DIRECTION_LEFT:
/* Navigate left
* Note: At the top level, navigating left
* means switching to the 'next' horizontal list,
* which is actually a movement to the *right* */
if (pointer_y < margin_top)
xmb_menu_entry_action(xmb,
&entry, selection, (xmb->depth == 1) ? MENU_ACTION_RIGHT : MENU_ACTION_LEFT);
break;
case MENU_INPUT_PRESS_DIRECTION_RIGHT:
/* Navigate right
* Note: At the top level, navigating right
* means switching to the 'previous' horizontal list,
* which is actually a movement to the *left* */
if (pointer_y < margin_top)
xmb_menu_entry_action(xmb,
&entry, selection, (xmb->depth == 1) ? MENU_ACTION_LEFT : MENU_ACTION_RIGHT);
break;
default:
/* Do nothing */
break;
}
}
}
menu_entries_ctl(MENU_ENTRIES_CTL_START_GET, &i);
if (i >= end)
{
i = 0;
menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &i);
}
GFX_ANIMATION_CLEAR_ACTIVE(p_anim);
}
static bool xmb_shader_pipeline_active(unsigned menu_shader_pipeline)
{
if (string_is_not_equal(menu_driver_ident(), "xmb"))
return false;
if (menu_shader_pipeline == XMB_SHADER_PIPELINE_WALLPAPER)
return false;
return true;
}
static void xmb_draw_bg(
void *userdata,
gfx_display_t *p_disp,
unsigned video_width,
unsigned video_height,
unsigned menu_shader_pipeline,
unsigned xmb_color_theme,
float menu_wallpaper_opacity,
bool libretro_running,
float alpha,
uintptr_t texture_id,
float *coord_black,
float *coord_white)
{
gfx_display_ctx_draw_t draw;
gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
draw.x = 0;
draw.y = 0;
draw.texture = texture_id;
draw.width = video_width;
draw.height = video_height;
draw.color = &coord_black[0];
draw.vertex = NULL;
draw.tex_coord = NULL;
draw.vertex_count = 4;
draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP;
draw.pipeline_id = 0;
draw.pipeline_active = xmb_shader_pipeline_active(menu_shader_pipeline);
if (dispctx && dispctx->blend_begin)
dispctx->blend_begin(userdata);
#ifdef HAVE_SHADERPIPELINE
if (menu_shader_pipeline > XMB_SHADER_PIPELINE_WALLPAPER
&&
(xmb_color_theme != XMB_THEME_WALLPAPER))
{
draw.color = xmb_gradient_ident(xmb_color_theme);
if (libretro_running)
gfx_display_set_alpha(draw.color, coord_black[3]);
else
gfx_display_set_alpha(draw.color, coord_white[3]);
/* Draw gradient */
draw.texture = 0;
draw.x = 0;
draw.y = 0;
gfx_display_draw_bg(p_disp, &draw, userdata, false,
menu_wallpaper_opacity);
if (draw.height > 0 && draw.width > 0)
if (dispctx && dispctx->draw)
dispctx->draw(&draw, userdata, video_width, video_height);
draw.pipeline_id = VIDEO_SHADER_MENU_2;
switch (menu_shader_pipeline)
{
case XMB_SHADER_PIPELINE_RIBBON:
draw.pipeline_id = VIDEO_SHADER_MENU;
break;
#if !defined(VITA)
case XMB_SHADER_PIPELINE_SIMPLE_SNOW:
draw.pipeline_id = VIDEO_SHADER_MENU_3;
break;
case XMB_SHADER_PIPELINE_SNOW:
draw.pipeline_id = VIDEO_SHADER_MENU_4;
break;
case XMB_SHADER_PIPELINE_BOKEH:
draw.pipeline_id = VIDEO_SHADER_MENU_5;
break;
case XMB_SHADER_PIPELINE_SNOWFLAKE:
draw.pipeline_id = VIDEO_SHADER_MENU_6;
break;
#endif
default:
break;
}
if (dispctx && dispctx->draw_pipeline)
dispctx->draw_pipeline(&draw, p_disp,
userdata, video_width, video_height);
}
else
#endif
{
uintptr_t texture = draw.texture;
if (xmb_color_theme != XMB_THEME_WALLPAPER)
draw.color = xmb_gradient_ident(xmb_color_theme);
if (libretro_running)
gfx_display_set_alpha(draw.color, coord_black[3]);
else
gfx_display_set_alpha(draw.color, coord_white[3]);
if (xmb_color_theme != XMB_THEME_WALLPAPER)
{
/* Draw gradient */
draw.texture = 0;
draw.x = 0;
draw.y = 0;
gfx_display_draw_bg(p_disp, &draw, userdata, false,
menu_wallpaper_opacity);
if (draw.height > 0 && draw.width > 0)
if (dispctx && dispctx->draw)
dispctx->draw(&draw, userdata, video_width, video_height);
}
{
bool add_opacity = false;
draw.texture = texture;
gfx_display_set_alpha(draw.color, coord_white[3]);
if (draw.texture)
draw.color = &coord_white[0];
if (libretro_running || xmb_color_theme == XMB_THEME_WALLPAPER)
add_opacity = true;
gfx_display_draw_bg(p_disp, &draw, userdata,
add_opacity, menu_wallpaper_opacity);
}
}
if (dispctx)
{
if (dispctx->draw)
if (draw.height > 0 && draw.width > 0)
dispctx->draw(&draw, userdata, video_width, video_height);
if (dispctx->blend_end)
dispctx->blend_end(userdata);
}
}
static void xmb_draw_dark_layer(
xmb_handle_t *xmb,
gfx_display_t *p_disp,
void *userdata,
unsigned width,
unsigned height)
{
gfx_display_ctx_draw_t draw;
float black[16] = {
0, 0, 0, 1,
0, 0, 0, 1,
0, 0, 0, 1,
0, 0, 0, 1,
};
gfx_display_ctx_driver_t
*dispctx = p_disp->dispctx;
draw.x = 0;
draw.y = 0;
draw.width = width;
draw.height = height;
draw.color = &black[0];
draw.vertex = NULL;
draw.matrix_data = NULL;
draw.tex_coord = NULL;
draw.vertex_count = 4;
draw.texture = 0;
draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP;
draw.pipeline_id = 0;
draw.pipeline_active = false;
if (dispctx)
{
if (dispctx->blend_begin)
dispctx->blend_begin(userdata);
gfx_display_draw_bg(p_disp, &draw, userdata,
true, MIN(xmb->alpha, 0.75));
if (draw.height > 0 && draw.width > 0)
if (dispctx && dispctx->draw)
dispctx->draw(&draw, userdata, width, height);
if (dispctx->blend_end)
dispctx->blend_end(userdata);
}
}
static void xmb_draw_fullscreen_thumbnails(
xmb_handle_t *xmb,
gfx_animation_t *p_anim,
gfx_display_t *p_disp,
void *userdata,
unsigned video_width,
unsigned video_height,
bool xmb_shadows_enable,
unsigned xmb_color_theme,
settings_t *settings, size_t selection)
{
/* Check whether fullscreen thumbnails are visible */
if (xmb->fullscreen_thumbnail_alpha > 0.0f)
{
int header_margin;
int thumbnail_box_width;
int thumbnail_box_height;
int right_thumbnail_x;
int left_thumbnail_x;
int thumbnail_y;
gfx_thumbnail_shadow_t thumbnail_shadow;
gfx_thumbnail_t *right_thumbnail = NULL;
gfx_thumbnail_t *left_thumbnail = NULL;
int view_width = (int)video_width;
int view_height = (int)video_height;
int thumbnail_margin = (int)(xmb->icon_size / 2.0f);
bool show_right_thumbnail = false;
bool show_left_thumbnail = false;
unsigned num_thumbnails = 0;
float right_thumbnail_draw_width = 0.0f;
float right_thumbnail_draw_height = 0.0f;
float left_thumbnail_draw_width = 0.0f;
float left_thumbnail_draw_height = 0.0f;
float *menu_color = xmb_gradient_ident(xmb_color_theme);
/* XMB doesn't have a proper theme interface, so
* hard-code this alpha value for now... */
float background_alpha = 0.75f;
float background_color[16] = {
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
uint32_t title_color = 0xFFFFFF00;
float header_alpha = 0.6f;
float header_color[16] = {
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
};
int frame_width = (int)(xmb->icon_size / 6.0f);
float frame_color[16] = {
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
};
bool show_header = !string_is_empty(xmb->fullscreen_thumbnail_label);
int header_height = show_header ? (int)((float)xmb->font_size * 1.2f) + (frame_width * 2) : 0;
bool xmb_vertical_thumbnails = settings->bools.menu_xmb_vertical_thumbnails;
bool menu_ticker_smooth = settings->bools.menu_ticker_smooth;
enum gfx_animation_ticker_type
menu_ticker_type = (enum gfx_animation_ticker_type)settings->uints.menu_ticker_type;
/* Sanity check: Return immediately if this is
* a menu without thumbnails and we are not currently
* 'fading out' the fullscreen thumbnail view */
if (!xmb->fullscreen_thumbnails_available &&
xmb->show_fullscreen_thumbnails)
goto error;
/* Safety check: ensure that current
* selection matches the entry selected when
* fullscreen thumbnails were enabled
* > Note that we exclude this check if we are
* currently viewing the quick menu and the
* thumbnail view is fading out. This enables
* a smooth transition if the user presses
* RetroPad A or keyboard 'return' to enter the
* quick menu while fullscreen thumbnails are
* being displayed */
if ((selection != xmb->fullscreen_thumbnail_selection) &&
(!xmb->is_quick_menu || xmb->show_fullscreen_thumbnails))
goto error;
/* Get thumbnail pointers
* > Order is swapped when using 'vertical disposition' */
if (xmb_vertical_thumbnails)
{
right_thumbnail = &xmb->thumbnails.left;
left_thumbnail = &xmb->thumbnails.right;
}
else
{
right_thumbnail = &xmb->thumbnails.right;
left_thumbnail = &xmb->thumbnails.left;
}
/* Get number of 'active' thumbnails */
show_right_thumbnail = (right_thumbnail->status == GFX_THUMBNAIL_STATUS_AVAILABLE);
show_left_thumbnail = (left_thumbnail->status == GFX_THUMBNAIL_STATUS_AVAILABLE);
if (show_right_thumbnail)
num_thumbnails++;
if (show_left_thumbnail)
num_thumbnails++;
/* Do nothing if both thumbnails are missing
* > Note: Baring inexplicable internal errors, this
* can never happen... */
if (num_thumbnails < 1)
goto error;
/* Get base thumbnail dimensions + draw positions */
/* > Thumbnail bounding box height + y position
* are fixed */
header_margin = (header_height > thumbnail_margin) ?
header_height : thumbnail_margin;
thumbnail_box_height = view_height - header_margin - thumbnail_margin;
thumbnail_y = header_margin;
/* Thumbnail bounding box width and x position
* depend upon number of active thumbnails */
if (num_thumbnails == 2)
{
thumbnail_box_width = (view_width - (thumbnail_margin * 3) - frame_width) >> 1;
left_thumbnail_x = thumbnail_margin;
right_thumbnail_x = left_thumbnail_x + thumbnail_box_width + frame_width + thumbnail_margin;
}
else
{
thumbnail_box_width = view_width - (thumbnail_margin * 2);
left_thumbnail_x = thumbnail_margin;
right_thumbnail_x = left_thumbnail_x;
}
/* Sanity check */
if ((thumbnail_box_width < 1) ||
(thumbnail_box_height < 1))
goto error;
/* Get thumbnail draw dimensions
* > Note: The following code is a bit awkward, since
* we have to do things in a very specific order
* - i.e. we cannot determine proper thumbnail
* layout until we have thumbnail draw dimensions.
* and we cannot get draw dimensions until we have
* the bounding box dimensions... */
if (show_right_thumbnail)
{
gfx_thumbnail_get_draw_dimensions(
right_thumbnail,
thumbnail_box_width, thumbnail_box_height, 1.0f,
&right_thumbnail_draw_width, &right_thumbnail_draw_height);
/* Sanity check */
if ((right_thumbnail_draw_width <= 0.0f) ||
(right_thumbnail_draw_height <= 0.0f))
goto error;
}
if (show_left_thumbnail)
{
gfx_thumbnail_get_draw_dimensions(
left_thumbnail,
thumbnail_box_width, thumbnail_box_height, 1.0f,
&left_thumbnail_draw_width, &left_thumbnail_draw_height);
/* Sanity check */
if ((left_thumbnail_draw_width <= 0.0f) ||
(left_thumbnail_draw_height <= 0.0f))
goto error;
}
/* Adjust thumbnail draw positions to achieve
* uniform appearance (accounting for actual
* draw dimensions...) */
if (num_thumbnails == 2)
{
int left_padding = (thumbnail_box_width - (int)left_thumbnail_draw_width) >> 1;
int right_padding = (thumbnail_box_width - (int)right_thumbnail_draw_width) >> 1;
/* Move thumbnails as close together as possible,
* and horizontally centre the resultant 'block'
* of images */
left_thumbnail_x += right_padding;
right_thumbnail_x -= left_padding;
}
/* Set colour values */
/* > Background */
gfx_display_set_alpha(
background_color, background_alpha * xmb->fullscreen_thumbnail_alpha);
/* > Header background */
header_color[11] = header_alpha * xmb->fullscreen_thumbnail_alpha;
header_color[15] = header_color[11];
/* > Title text */
title_color |= (unsigned)((255.0f * xmb->fullscreen_thumbnail_alpha) + 0.5f);
/* > Thumbnail frame */
if (menu_color)
{
float mean_menu_color[3];
/* The menu gradients are not entirely consistent...
* The best we can do here is take the mean of the
* first and last vertex colours... */
mean_menu_color[0] = (menu_color[0] + menu_color[12]) / 2.0f;
mean_menu_color[1] = (menu_color[1] + menu_color[13]) / 2.0f;
mean_menu_color[2] = (menu_color[2] + menu_color[14]) / 2.0f;
memcpy(frame_color, mean_menu_color, sizeof(mean_menu_color));
memcpy(frame_color + 4, mean_menu_color, sizeof(mean_menu_color));
memcpy(frame_color + 8, mean_menu_color, sizeof(mean_menu_color));
memcpy(frame_color + 12, mean_menu_color, sizeof(mean_menu_color));
}
gfx_display_set_alpha(
frame_color, xmb->fullscreen_thumbnail_alpha);
/* Darken background */
gfx_display_draw_quad(
p_disp,
userdata,
video_width,
video_height,
0,
0,
(unsigned)view_width,
(unsigned)view_height,
(unsigned)view_width,
(unsigned)view_height,
background_color,
NULL);
/* Draw header */
if (show_header)
{
/* Background */
gfx_display_draw_quad(
p_disp,
userdata,
video_width,
video_height,
0,
0,
(unsigned)view_width,
(unsigned)(header_height - frame_width),
(unsigned)view_width,
(unsigned)view_height,
header_color,
NULL);
/* Title text */
if (menu_ticker_smooth)
{
char title_buf[255];
gfx_animation_ctx_ticker_smooth_t ticker_smooth;
int title_x = 0;
unsigned ticker_x_offset = 0;
unsigned ticker_str_width = 0;
title_buf[0] = '\0';
ticker_smooth.idx = p_anim->ticker_pixel_idx;
ticker_smooth.font = xmb->font;
ticker_smooth.font_scale = 1.0f;
ticker_smooth.type_enum = menu_ticker_type;
ticker_smooth.spacer = NULL;
ticker_smooth.x_offset = &ticker_x_offset;
ticker_smooth.dst_str_width = &ticker_str_width;
ticker_smooth.selected = true;
ticker_smooth.field_width = (unsigned)view_width;
ticker_smooth.src_str = xmb->fullscreen_thumbnail_label;
ticker_smooth.dst_str = title_buf;
ticker_smooth.dst_str_len = sizeof(title_buf);
/* If ticker is not active, centre the title text */
if (!gfx_animation_ticker_smooth(&ticker_smooth))
title_x = (view_width - (int)ticker_str_width) >> 1;
title_x += (int)ticker_x_offset;
gfx_display_draw_text(
xmb->font,
title_buf,
title_x,
xmb->font_size,
(unsigned)view_width,
(unsigned)view_height,
title_color,
TEXT_ALIGN_LEFT,
1.0f, false, 0.0f, false);
}
/* Note: The non-smooth ticker is a complete failure
* here, since actual text width is unknown. This
* causes the text to be horizontally offset - we
* cannot fix this.
* All we can do in this case is just draw the text
* as-is, horizontally centred, and if the ends get
* clipped than so be it... */
else
gfx_display_draw_text(
xmb->font,
xmb->fullscreen_thumbnail_label,
view_width >> 1,
xmb->font_size,
(unsigned)view_width,
(unsigned)view_height,
title_color,
TEXT_ALIGN_CENTER,
1.0f, false, 0.0f, false);
}
/* Draw thumbnails */
/* > Configure shadow effect */
if (xmb_shadows_enable)
{
float shadow_offset = xmb->icon_size / 24.0f;
thumbnail_shadow.type = GFX_THUMBNAIL_SHADOW_DROP;
thumbnail_shadow.alpha = 0.35f;
thumbnail_shadow.drop.x_offset = shadow_offset;
thumbnail_shadow.drop.y_offset = shadow_offset;
}
else
thumbnail_shadow.type = GFX_THUMBNAIL_SHADOW_NONE;
/* > Right */
if (show_right_thumbnail)
{
/* Background */
gfx_display_draw_quad(
p_disp,
userdata,
video_width,
video_height,
right_thumbnail_x - frame_width +
((thumbnail_box_width - (int)right_thumbnail_draw_width) >> 1),
thumbnail_y - frame_width +
((thumbnail_box_height - (int)right_thumbnail_draw_height) >> 1),
(unsigned)right_thumbnail_draw_width + (frame_width << 1),
(unsigned)right_thumbnail_draw_height + (frame_width << 1),
(unsigned)view_width,
(unsigned)view_height,
frame_color,
NULL);
/* Thumbnail */
gfx_thumbnail_draw(
userdata,
video_width,
video_height,
right_thumbnail,
right_thumbnail_x,
thumbnail_y,
(unsigned)thumbnail_box_width,
(unsigned)thumbnail_box_height,
GFX_THUMBNAIL_ALIGN_CENTRE,
xmb->fullscreen_thumbnail_alpha,
1.0f,
&thumbnail_shadow);
}
/* > Left */
if (show_left_thumbnail)
{
/* Background */
gfx_display_draw_quad(
p_disp,
userdata,
video_width,
video_height,
left_thumbnail_x - frame_width +
((thumbnail_box_width - (int)left_thumbnail_draw_width) >> 1),
thumbnail_y - frame_width +
((thumbnail_box_height - (int)left_thumbnail_draw_height) >> 1),
(unsigned)left_thumbnail_draw_width + (frame_width << 1),
(unsigned)left_thumbnail_draw_height + (frame_width << 1),
(unsigned)view_width,
(unsigned)view_height,
frame_color,
NULL);
/* Thumbnail */
gfx_thumbnail_draw(
userdata,
video_width,
video_height,
left_thumbnail,
left_thumbnail_x,
thumbnail_y,
(unsigned)thumbnail_box_width,
(unsigned)thumbnail_box_height,
GFX_THUMBNAIL_ALIGN_CENTRE,
xmb->fullscreen_thumbnail_alpha,
1.0f,
&thumbnail_shadow);
}
}
return;
error:
/* If fullscreen thumbnails are enabled at
* this point, must disable them immediately... */
if (xmb->show_fullscreen_thumbnails)
xmb_hide_fullscreen_thumbnails(xmb, false);
}
static void xmb_frame(void *data, video_frame_info_t *video_info)
{
math_matrix_4x4 mymat;
unsigned i;
gfx_display_ctx_rotate_draw_t rotate_draw;
char msg[1024];
char title_msg[255];
char title_truncated[255];
gfx_thumbnail_shadow_t thumbnail_shadow;
size_t selection = 0;
size_t percent_width = 0;
bool render_background = false;
file_list_t *selection_buf = NULL;
const float under_thumb_margin = 0.96f;
float left_thumbnail_margin_width = 0.0f;
float right_thumbnail_margin_width = 0.0f;
float thumbnail_margin_height_under = 0.0f;
float thumbnail_margin_height_full = 0.0f;
float left_thumbnail_margin_x = 0.0f;
float right_thumbnail_margin_x = 0.0f;
float pseudo_font_length = 0.0f;
xmb_handle_t *xmb = (xmb_handle_t*)data;
settings_t *settings = config_get_ptr();
unsigned xmb_system_tab = xmb_get_system_tab(xmb, (unsigned)xmb->categories_selection_ptr);
bool fade_tab_icons = false;
float fade_tab_icons_x_threshold = 0.0f;
bool menu_core_enable = settings->bools.menu_core_enable;
float thumbnail_scale_factor = (float)settings->uints.menu_xmb_thumbnail_scale_factor / 100.0f;
bool menu_xmb_vertical_thumbnails = settings->bools.menu_xmb_vertical_thumbnails;
void *userdata = video_info->userdata;
unsigned video_width = video_info->width;
unsigned video_height = video_info->height;
bool xmb_shadows_enable = video_info->xmb_shadows_enable;
float xmb_alpha_factor = video_info->xmb_alpha_factor;
bool timedate_enable = video_info->timedate_enable;
bool battery_level_enable = video_info->battery_level_enable;
bool video_fullscreen = video_info->fullscreen;
bool mouse_grabbed = video_info->input_driver_grab_mouse_state;
bool menu_mouse_enable = video_info->menu_mouse_enable;
unsigned xmb_color_theme = video_info->xmb_color_theme;
bool libretro_running = video_info->libretro_running;
unsigned menu_shader_pipeline = video_info->menu_shader_pipeline;
float menu_wallpaper_opacity = video_info->menu_wallpaper_opacity;
gfx_display_t *p_disp = (gfx_display_t*)video_info->disp_userdata;
gfx_animation_t *p_anim = anim_get_ptr();
gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
bool input_dialog_display_kb = menu_input_dialog_get_display_kb();
if (!xmb)
return;
msg[0] = '\0';
title_msg[0] = '\0';
title_truncated[0] = '\0';
/* If menu screensaver is active, draw
* screensaver and return */
if (xmb->show_screensaver)
{
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 =
(float)video_width - (xmb->icon_size / 6) -
(xmb->margins_screen_left * xmb_scale_mod[5]) -
xmb->icon_spacing_horizontal - pseudo_font_length;
thumbnail_margin_height_under = ((float)video_height * under_thumb_margin) - xmb->margins_screen_top - xmb->icon_size;
thumbnail_margin_height_full = (float)video_height - xmb->margins_title_top - ((xmb->icon_size / 4.0f) * 2.0f);
left_thumbnail_margin_x = xmb->icon_size / 6.0f;
right_thumbnail_margin_x = (float)video_width - (xmb->icon_size / 6.0f) - right_thumbnail_margin_width;
/* Configure shadow effect */
if (xmb_shadows_enable)
{
/* Drop shadow for thumbnails needs to be larger
* than for text/icons, and also needs to scale
* with screen dimensions */
float shadow_offset = xmb->shadow_offset * 1.5f * xmb->last_scale_factor;
shadow_offset = (shadow_offset > xmb->shadow_offset)
? shadow_offset
: xmb->shadow_offset;
thumbnail_shadow.type = GFX_THUMBNAIL_SHADOW_DROP;
thumbnail_shadow.alpha = 0.35f;
thumbnail_shadow.drop.x_offset = shadow_offset;
thumbnail_shadow.drop.y_offset = shadow_offset;
}
else
thumbnail_shadow.type = GFX_THUMBNAIL_SHADOW_NONE;
font_driver_bind_block(xmb->font, &xmb->raster_block);
font_driver_bind_block(xmb->font2, &xmb->raster_block2);
xmb->raster_block.carr.coords.vertices = 0;
xmb->raster_block2.carr.coords.vertices = 0;
gfx_display_set_alpha(xmb_coord_black, MIN(
(float)xmb_alpha_factor / 100,
xmb->alpha));
gfx_display_set_alpha(xmb_coord_white, xmb->alpha);
xmb_draw_bg(
userdata,
p_disp,
video_width,
video_height,
menu_shader_pipeline,
xmb_color_theme,
menu_wallpaper_opacity,
libretro_running,
xmb->alpha,
xmb->textures.bg,
xmb_coord_black,
xmb_coord_white);
selection = menu_navigation_get_selection();
strlcpy(title_truncated,
xmb->title_name, sizeof(title_truncated));
if (selection > 1)
{
/* skip 25 UTF8 multi-byte chars */
char *end = title_truncated;
for (i = 0; i < 25 && *end; i++)
{
end++;
while ((*end & 0xC0) == 0x80)
end++;
}
*end = '\0';
}
/* Title text */
xmb_draw_text(xmb_shadows_enable, xmb, settings,
title_truncated, xmb->margins_title_left,
xmb->margins_title_top,
1, 1, TEXT_ALIGN_LEFT,
video_width, video_height, xmb->font);
if (menu_core_enable)
{
menu_entries_get_core_title(title_msg, sizeof(title_msg));
xmb_draw_text(xmb_shadows_enable, xmb, settings,
title_msg, xmb->margins_title_left,
video_height - xmb->margins_title_bottom, 1, 1, TEXT_ALIGN_LEFT,
video_width, video_height, xmb->font);
}
rotate_draw.matrix = &mymat;
rotate_draw.rotation = 0;
rotate_draw.scale_x = 1;
rotate_draw.scale_y = 1;
rotate_draw.scale_z = 1;
rotate_draw.scale_enable = true;
gfx_display_rotate_z(p_disp, &rotate_draw, userdata);
/**************************/
/* Draw thumbnails: START */
/**************************/
/* Note: This is incredibly ugly, but there are
* so many combinations here that we would go insane
* trying to rationalise this any further... */
/* Save state thumbnail, right side */
if (xmb->is_quick_menu &&
((xmb->thumbnails.savestate.status == GFX_THUMBNAIL_STATUS_AVAILABLE) ||
(xmb->thumbnails.savestate.status == GFX_THUMBNAIL_STATUS_PENDING)))
{
float thumb_width = right_thumbnail_margin_width;
float thumb_height = thumbnail_margin_height_full;
float scaled_thumb_width = thumb_width * thumbnail_scale_factor;
float scaled_thumb_height = thumb_height * thumbnail_scale_factor;
float thumb_x = right_thumbnail_margin_x + ((thumb_width - scaled_thumb_width) / 2.0f);
float thumb_y = xmb->margins_title_top + (xmb->icon_size / 4.0f) + ((thumb_height - scaled_thumb_height) / 2.0f);
gfx_thumbnail_draw(
userdata,
video_width,
video_height,
&xmb->thumbnails.savestate,
thumb_x,
thumb_y,
scaled_thumb_width > 0.0f ? (unsigned)scaled_thumb_width : 0,
scaled_thumb_height > 0.0f ? (unsigned)scaled_thumb_height : 0,
GFX_THUMBNAIL_ALIGN_CENTRE,
1.0f, 1.0f, &thumbnail_shadow);
}
/* This is used for hiding thumbnails when going into sub-levels in the
* Quick Menu as well as when selecting "Information" for a playlist entry.
* NOTE: This is currently a pretty crude check, simply going by menu depth
* and not specifically identifying which menu we're actually in. */
else if (!((xmb_system_tab > XMB_SYSTEM_TAB_SETTINGS) && (xmb->depth > 2)))
{
bool show_right_thumbnail =
(xmb->thumbnails.right.status == GFX_THUMBNAIL_STATUS_AVAILABLE) ||
(xmb->thumbnails.right.status == GFX_THUMBNAIL_STATUS_PENDING);
bool show_left_thumbnail =
(xmb->thumbnails.left.status == GFX_THUMBNAIL_STATUS_AVAILABLE) ||
(xmb->thumbnails.left.status == GFX_THUMBNAIL_STATUS_PENDING);
/* Check if we are using the proper PS3 layout,
* or the aborted PSP layout */
if (xmb->use_ps3_layout)
{
/* Check if user has selected vertically
* stacked thumbnails */
if (menu_xmb_vertical_thumbnails)
{
/* Right + left thumbnails, right side */
if (show_right_thumbnail && show_left_thumbnail)
{
float thumb_width = right_thumbnail_margin_width;
float thumb_height = (thumbnail_margin_height_full - (xmb->icon_size / 4.0f)) / 2.0f;
float scaled_thumb_width = thumb_width * thumbnail_scale_factor;
float scaled_thumb_height = thumb_height * thumbnail_scale_factor;
float thumb_x = right_thumbnail_margin_x + ((thumb_width - scaled_thumb_width) / 2.0f);
float thumb_y_base = xmb->margins_title_top + (xmb->icon_size / 4.0f);
float thumb_y_offset = (thumb_height - scaled_thumb_height) / 2.0f;
float right_thumb_y = thumb_y_base + thumb_y_offset;
float left_thumb_y = thumb_y_base + thumb_height + (xmb->icon_size / 4) + thumb_y_offset;
gfx_thumbnail_draw(
userdata,
video_width,
video_height,
&xmb->thumbnails.right,
thumb_x,
right_thumb_y,
scaled_thumb_width > 0.0f ? (unsigned)scaled_thumb_width : 0,
scaled_thumb_height > 0.0f ? (unsigned)scaled_thumb_height : 0,
GFX_THUMBNAIL_ALIGN_CENTRE,
1.0f, 1.0f, &thumbnail_shadow);
gfx_thumbnail_draw(
userdata,
video_width,
video_height,
&xmb->thumbnails.left,
thumb_x,
left_thumb_y,
scaled_thumb_width > 0.0f ? (unsigned)scaled_thumb_width : 0,
scaled_thumb_height > 0.0f ? (unsigned)scaled_thumb_height : 0,
GFX_THUMBNAIL_ALIGN_CENTRE,
1.0f, 1.0f, &thumbnail_shadow);
/* Horizontal tab icons overlapping the top
* right image must be faded out */
fade_tab_icons = true;
fade_tab_icons_x_threshold = thumb_x;
}
/* Right *or* left, right side */
else if (show_right_thumbnail || show_left_thumbnail)
{
float thumb_width = right_thumbnail_margin_width;
float thumb_height = thumbnail_margin_height_under;
float scaled_thumb_width = thumb_width * thumbnail_scale_factor;
float scaled_thumb_height = thumb_height * thumbnail_scale_factor;
float thumb_x = right_thumbnail_margin_x + ((thumb_width - scaled_thumb_width) / 2.0f);
float thumb_y = xmb->margins_screen_top + xmb->icon_size;
gfx_thumbnail_draw(
userdata,
video_width,
video_height,
show_right_thumbnail ? &xmb->thumbnails.right : &xmb->thumbnails.left,
thumb_x,
thumb_y,
scaled_thumb_width > 0.0f ? (unsigned)scaled_thumb_width : 0,
scaled_thumb_height > 0.0f ? (unsigned)scaled_thumb_height : 0,
GFX_THUMBNAIL_ALIGN_TOP,
1.0f, 1.0f, &thumbnail_shadow);
}
}
else
{
/* Right thumbnail, right side */
if (show_right_thumbnail)
{
float thumb_width = right_thumbnail_margin_width;
float thumb_height = thumbnail_margin_height_under;
float scaled_thumb_width = thumb_width * thumbnail_scale_factor;
float scaled_thumb_height = thumb_height * thumbnail_scale_factor;
float thumb_x = right_thumbnail_margin_x + ((thumb_width - scaled_thumb_width) / 2.0f);
float thumb_y = xmb->margins_screen_top + xmb->icon_size;
gfx_thumbnail_draw(
userdata,
video_width,
video_height,
&xmb->thumbnails.right,
thumb_x,
thumb_y,
scaled_thumb_width > 0.0f ? (unsigned)scaled_thumb_width : 0,
scaled_thumb_height > 0.0f ? (unsigned)scaled_thumb_height : 0,
GFX_THUMBNAIL_ALIGN_TOP,
1.0f, 1.0f, &thumbnail_shadow);
}
/* Left thumbnail, left side */
if (show_left_thumbnail)
{
float y_offset = ((xmb->depth != 1) ? 1.2f : 0.0f) * xmb->icon_size;
float thumb_width = left_thumbnail_margin_width;
float thumb_height = thumbnail_margin_height_under - xmb->margins_title_bottom - y_offset;
float scaled_thumb_width = thumb_width * thumbnail_scale_factor;
float scaled_thumb_height = thumb_height * thumbnail_scale_factor;
float thumb_x = left_thumbnail_margin_x + ((thumb_width - scaled_thumb_width) / 2.0f);
float thumb_y = xmb->margins_screen_top + xmb->icon_size + y_offset;
gfx_thumbnail_draw(
userdata,
video_width,
video_height,
&xmb->thumbnails.left,
thumb_x,
thumb_y,
scaled_thumb_width > 0.0f ? (unsigned)scaled_thumb_width : 0,
scaled_thumb_height > 0.0f ? (unsigned)scaled_thumb_height : 0,
GFX_THUMBNAIL_ALIGN_TOP,
1.0f, 1.0f, &thumbnail_shadow);
}
}
}
/* This is the PSP layout - thumbnails are only
* drawn on the left hand side
* > If left thumbnail is available, show it
* > If not, show right thumbnail instead
* (if available) */
else if (show_right_thumbnail || show_left_thumbnail)
{
float y_offset = ((xmb->depth != 1) ? 1.2f : -0.25f) * xmb->icon_size;
float thumb_width = xmb->icon_size * 2.4f;
float thumb_height = thumbnail_margin_height_under - xmb->margins_title_bottom - (xmb->icon_size / 6.0f) - y_offset;
float scaled_thumb_width = thumb_width * thumbnail_scale_factor;
float scaled_thumb_height = thumb_height * thumbnail_scale_factor;
float thumb_x = (thumb_width - scaled_thumb_width) / 2.0f;
float thumb_y = xmb->margins_screen_top + xmb->icon_size + y_offset;
/* Very small thumbnails look ridiculous
* > Impose a minimum size limit */
if (thumb_height > xmb->icon_size)
gfx_thumbnail_draw(
userdata,
video_width,
video_height,
show_left_thumbnail ? &xmb->thumbnails.left : &xmb->thumbnails.right,
thumb_x,
thumb_y,
scaled_thumb_width > 0.0f ? (unsigned)scaled_thumb_width : 0,
scaled_thumb_height > 0.0f ? (unsigned)scaled_thumb_height : 0,
GFX_THUMBNAIL_ALIGN_TOP,
1.0f, 1.0f, &thumbnail_shadow);
}
}
/**************************/
/* Draw thumbnails: END */
/**************************/
/* Clock image */
gfx_display_set_alpha(item_color, MIN(xmb->alpha, 1.00f));
if (battery_level_enable)
{
gfx_display_ctx_powerstate_t powerstate;
char msg[12];
msg[0] = '\0';
powerstate.s = msg;
powerstate.len = sizeof(msg);
menu_display_powerstate(&powerstate);
if (powerstate.battery_enabled)
{
size_t x_pos = xmb->icon_size / 6;
size_t x_pos_icon = xmb->margins_title_left;
if (!xmb->assets_missing)
{
if (dispctx && dispctx->blend_begin)
dispctx->blend_begin(userdata);
xmb_draw_icon(
userdata,
p_disp,
dispctx,
video_width,
video_height,
xmb_shadows_enable,
xmb->icon_size,
xmb->textures.list[
powerstate.charging? XMB_TEXTURE_BATTERY_CHARGING :
(powerstate.percent > 80)? XMB_TEXTURE_BATTERY_FULL :
(powerstate.percent > 60)? XMB_TEXTURE_BATTERY_80 :
(powerstate.percent > 40)? XMB_TEXTURE_BATTERY_60 :
(powerstate.percent > 20)? XMB_TEXTURE_BATTERY_40 :
XMB_TEXTURE_BATTERY_20
],
video_width - (xmb->icon_size / 2) - x_pos_icon,
xmb->icon_size,
video_width,
video_height,
1,
0,
1,
&item_color[0],
xmb->shadow_offset,
&mymat);
if (dispctx && dispctx->blend_end)
dispctx->blend_end(userdata);
}
percent_width = (unsigned)
font_driver_get_message_width(
xmb->font, msg, (unsigned)strlen(msg), 1);
xmb_draw_text(xmb_shadows_enable, xmb, settings, msg,
video_width - xmb->margins_title_left - x_pos,
xmb->margins_title_top, 1, 1, TEXT_ALIGN_RIGHT,
video_width, video_height, xmb->font);
}
}
if (timedate_enable)
{
gfx_display_ctx_datetime_t datetime;
char timedate[255];
int x_pos = 0;
if (!xmb->assets_missing)
{
int x_pos = 0;
if (percent_width)
x_pos = percent_width + (xmb->icon_size / 2.5);
if (dispctx && dispctx->blend_begin)
dispctx->blend_begin(userdata);
xmb_draw_icon(
userdata,
p_disp,
dispctx,
video_width,
video_height,
xmb_shadows_enable,
xmb->icon_size,
xmb->textures.list[XMB_TEXTURE_CLOCK],
video_width - xmb->icon_size - x_pos,
xmb->icon_size,
video_width,
video_height,
1,
0,
1,
&item_color[0],
xmb->shadow_offset,
&mymat);
if (dispctx && dispctx->blend_end)
dispctx->blend_end(userdata);
}
timedate[0] = '\0';
datetime.s = timedate;
datetime.len = sizeof(timedate);
datetime.time_mode = settings->uints.menu_timedate_style;
datetime.date_separator = settings->uints.menu_timedate_date_separator;
menu_display_timedate(&datetime);
if (percent_width)
x_pos = percent_width + (xmb->icon_size / 2.5);
xmb_draw_text(xmb_shadows_enable, xmb, settings, timedate,
video_width - xmb->margins_title_left - xmb->icon_size / 4 - x_pos,
xmb->margins_title_top, 1, 1, TEXT_ALIGN_RIGHT,
video_width, video_height, xmb->font);
}
/* Arrow image */
gfx_display_set_alpha(item_color,
MIN(xmb->textures_arrow_alpha, xmb->alpha));
if (!xmb->assets_missing)
{
if (dispctx && dispctx->blend_begin)
dispctx->blend_begin(userdata);
xmb_draw_icon(
userdata,
p_disp,
dispctx,
video_width,
video_height,
xmb_shadows_enable,
xmb->icon_size,
xmb->textures.list[XMB_TEXTURE_ARROW],
xmb->x + xmb->margins_screen_left +
xmb->icon_spacing_horizontal -
xmb->icon_size / 2.0 + xmb->icon_size,
xmb->margins_screen_top +
xmb->icon_size / 2.0 + xmb->icon_spacing_vertical
* xmb->active_item_factor,
video_width,
video_height,
xmb->textures_arrow_alpha,
0,
1,
&item_color[0],
xmb->shadow_offset,
&mymat);
if (dispctx && dispctx->blend_end)
dispctx->blend_end(userdata);
}
if (dispctx && dispctx->blend_begin)
dispctx->blend_begin(userdata);
/* Horizontal tab icons */
if (!xmb->assets_missing)
{
for (i = 0; i <= xmb_list_get_size(xmb, MENU_LIST_HORIZONTAL)
+ xmb->system_tab_end; i++)
{
xmb_node_t *node = xmb_get_node(xmb, i);
if (!node)
continue;
gfx_display_set_alpha(item_color, MIN(node->alpha, xmb->alpha));
if (item_color[3] != 0)
{
gfx_display_ctx_rotate_draw_t rotate_draw;
math_matrix_4x4 mymat_tmp;
uintptr_t texture = node->icon;
float x = xmb->x + xmb->categories_x_pos +
xmb->margins_screen_left +
xmb->icon_spacing_horizontal
* (i + 1) - xmb->icon_size / 2.0;
float y = xmb->margins_screen_top
+ xmb->icon_size / 2.0;
float scale_factor = node->zoom;
/* Check whether we need to fade out icons
* overlapping the top right thumbnail image */
if (fade_tab_icons)
{
float x_threshold = fade_tab_icons_x_threshold - (xmb->icon_size * 1.5f);
if (x > x_threshold)
{
float fade_alpha = item_color[3];
float fade_offset = (x - x_threshold) * 2.0f;
fade_offset = (fade_offset > xmb->icon_size) ? xmb->icon_size : fade_offset;
fade_alpha *= 1.0f - (fade_offset / xmb->icon_size);
if (fade_alpha <= 0.0f)
continue;
gfx_display_set_alpha(item_color, fade_alpha);
}
}
rotate_draw.matrix = &mymat_tmp;
rotate_draw.rotation = 0;
rotate_draw.scale_x = scale_factor;
rotate_draw.scale_y = scale_factor;
rotate_draw.scale_z = 1;
rotate_draw.scale_enable = true;
gfx_display_rotate_z(p_disp, &rotate_draw, userdata);
xmb_draw_icon(
userdata,
p_disp,
dispctx,
video_width,
video_height,
xmb_shadows_enable,
xmb->icon_size,
texture,
x,
y,
video_width,
video_height,
1.0,
0, /* rotation */
scale_factor,
&item_color[0],
xmb->shadow_offset,
&mymat_tmp);
}
}
}
/* Vertical icons */
xmb_draw_items(
userdata,
p_disp,
p_anim,
settings,
video_width,
video_height,
xmb_shadows_enable,
xmb,
&xmb->selection_buf_old,
xmb->selection_ptr_old,
(xmb_list_get_size(xmb, MENU_LIST_PLAIN) > 1)
? xmb->categories_selection_ptr :
xmb->categories_selection_ptr_old,
&item_color[0],
video_width,
video_height,
&mymat);
selection_buf = menu_entries_get_selection_buf_ptr(0);
xmb_draw_items(
userdata,
p_disp,
p_anim,
settings,
video_width,
video_height,
xmb_shadows_enable,
xmb,
selection_buf,
selection,
xmb->categories_selection_ptr,
&item_color[0],
video_width,
video_height,
&mymat);
if (dispctx && dispctx->blend_end)
dispctx->blend_end(userdata);
font_driver_flush(video_width, video_height, xmb->font);
font_driver_flush(video_width, video_height, xmb->font2);
font_driver_bind_block(xmb->font, NULL);
font_driver_bind_block(xmb->font2, NULL);
/* Draw fullscreen thumbnails, if required */
xmb_draw_fullscreen_thumbnails(
xmb,
p_anim,
p_disp,
userdata,
video_width,
video_height,
xmb_shadows_enable,
xmb_color_theme,
settings, selection);
if (input_dialog_display_kb)
{
const char *str = menu_input_dialog_get_buffer();
const char *label = menu_input_dialog_get_label_buffer();
snprintf(msg, sizeof(msg), "%s\n%s", label, str);
render_background = true;
}
if (!string_is_empty(xmb->box_message))
{
strlcpy(msg, xmb->box_message,
sizeof(msg));
free(xmb->box_message);
xmb->box_message = NULL;
render_background = true;
}
if (render_background)
{
xmb_draw_dark_layer(xmb, p_disp,
userdata, video_width, video_height);
xmb_render_messagebox_internal(userdata, p_disp,
video_width, video_height,
xmb, msg, &mymat);
}
/* Cursor image */
if (xmb->show_mouse)
{
bool cursor_visible = (video_fullscreen || mouse_grabbed) &&
menu_mouse_enable;
gfx_display_set_alpha(xmb_coord_white, MIN(xmb->alpha, 1.00f));
if (cursor_visible)
gfx_display_draw_cursor(
p_disp,
userdata,
video_width,
video_height,
cursor_visible,
&xmb_coord_white[0],
xmb->cursor_size,
xmb->textures.list[XMB_TEXTURE_POINTER],
xmb->pointer.x,
xmb->pointer.y,
video_width,
video_height);
}
video_driver_set_viewport(video_width, video_height, false, true);
}
static void xmb_layout_ps3(xmb_handle_t *xmb, int width)
{
unsigned new_font_size;
float scale_factor = xmb->last_scale_factor;
xmb->above_subitem_offset = 1.5;
xmb->above_item_offset = -1.0;
xmb->active_item_factor = 3.0;
xmb->under_item_offset = 5.0;
xmb->categories_active_zoom = 1.0;
xmb->categories_passive_zoom = 0.5;
xmb->items_active_zoom = 1.0;
xmb->items_passive_zoom = 0.5;
xmb->categories_active_alpha = 1.0;
xmb->categories_passive_alpha = 0.85;
xmb->items_active_alpha = 1.0;
xmb->items_passive_alpha = 0.85;
xmb->shadow_offset = 2.0;
new_font_size = 32.0 * scale_factor;
xmb->font2_size = 24.0 * scale_factor;
xmb->cursor_size = 64.0 * scale_factor;
xmb->icon_spacing_horizontal = 200.0 * scale_factor;
xmb->icon_spacing_vertical = 64.0 * scale_factor;
xmb->margins_screen_top = (256+32) * scale_factor;
xmb->margins_screen_left = 336.0 * scale_factor;
xmb->margins_title_left = 60 * scale_factor;
xmb->margins_title_top = 60 * scale_factor + new_font_size / 3;
xmb->margins_title_bottom = 60 * scale_factor - new_font_size / 3;
xmb->margins_label_left = 85.0 * scale_factor;
xmb->margins_label_top = new_font_size / 3.0;
xmb->margins_setting_left = 600.0 * scale_factor * xmb_scale_mod[6];
xmb->margins_dialog = 48 * scale_factor;
xmb->margins_slice = 16 * scale_factor;
xmb->icon_size = 128.0 * scale_factor;
xmb->font_size = new_font_size;
}
static void xmb_layout_psp(xmb_handle_t *xmb, int width)
{
unsigned new_font_size;
float scale_factor = xmb->last_scale_factor;
xmb->above_subitem_offset = 1.5;
xmb->above_item_offset = -1.0;
xmb->active_item_factor = 2.0;
xmb->under_item_offset = 3.0;
xmb->categories_active_zoom = 1.0;
xmb->categories_passive_zoom = 1.0;
xmb->items_active_zoom = 1.0;
xmb->items_passive_zoom = 1.0;
xmb->categories_active_alpha = 1.0;
xmb->categories_passive_alpha = 0.85;
xmb->items_active_alpha = 1.0;
xmb->items_passive_alpha = 0.85;
xmb->shadow_offset = 1.0;
new_font_size = 32.0 * scale_factor;
xmb->font2_size = 24.0 * scale_factor;
xmb->margins_screen_top = (256+32) * scale_factor;
xmb->cursor_size = 64.0;
xmb->icon_spacing_horizontal = 250.0 * scale_factor;
xmb->icon_spacing_vertical = 108.0 * scale_factor;
xmb->margins_screen_left = 136.0 * scale_factor;
xmb->margins_title_left = 60 * scale_factor;
xmb->margins_title_top = 60 * scale_factor + new_font_size / 3;
xmb->margins_title_bottom = 60 * scale_factor - new_font_size / 3;
xmb->margins_label_left = 85.0 * scale_factor;
xmb->margins_label_top = new_font_size / 3.0;
xmb->margins_setting_left = 600.0 * scale_factor;
xmb->margins_dialog = 48 * scale_factor;
xmb->margins_slice = 16 * scale_factor;
xmb->icon_size = 128.0 * scale_factor;
xmb->font_size = new_font_size;
}
static void xmb_layout(xmb_handle_t *xmb)
{
unsigned width, height, i, current, end;
file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
size_t selection = menu_navigation_get_selection();
video_driver_get_size(&width, &height);
if (xmb->use_ps3_layout)
xmb_layout_ps3(xmb, width);
else
xmb_layout_psp(xmb, width);
#ifdef XMB_DEBUG
RARCH_LOG("[XMB] margin screen left: %.2f\n", xmb->margins_screen_left);
RARCH_LOG("[XMB] margin screen top: %.2f\n", xmb->margins_screen_top);
RARCH_LOG("[XMB] margin title left: %.2f\n", xmb->margins_title_left);
RARCH_LOG("[XMB] margin title top: %.2f\n", xmb->margins_title_top);
RARCH_LOG("[XMB] margin title bott: %.2f\n", xmb->margins_title_bottom);
RARCH_LOG("[XMB] margin label left: %.2f\n", xmb->margins_label_left);
RARCH_LOG("[XMB] margin label top: %.2f\n", xmb->margins_label_top);
RARCH_LOG("[XMB] margin sett left: %.2f\n", xmb->margins_setting_left);
RARCH_LOG("[XMB] icon spacing hor: %.2f\n", xmb->icon_spacing_horizontal);
RARCH_LOG("[XMB] icon spacing ver: %.2f\n", xmb->icon_spacing_vertical);
RARCH_LOG("[XMB] icon size: %.2f\n", xmb->icon_size);
#endif
current = (unsigned)selection;
end = (unsigned)menu_entries_get_size();
for (i = 0; i < end; i++)
{
float ia = xmb->items_passive_alpha;
float iz = xmb->items_passive_zoom;
xmb_node_t *node = (xmb_node_t*)selection_buf->list[i].userdata;
if (!node)
continue;
if (i == current)
{
ia = xmb->items_active_alpha;
iz = xmb->items_active_alpha;
}
node->alpha = ia;
node->label_alpha = ia;
node->zoom = iz;
node->y = xmb_item_y(xmb, i, current);
}
if (xmb->depth <= 1)
return;
current = (unsigned)xmb->selection_ptr_old;
end = (unsigned)xmb->selection_buf_old.size;
for (i = 0; i < end; i++)
{
float ia = 0;
float iz = xmb->items_passive_zoom;
xmb_node_t *node = (xmb_node_t*)file_list_get_userdata_at_offset(
&xmb->selection_buf_old, i);
if (!node)
continue;
if (i == current)
{
ia = xmb->items_active_alpha;
iz = xmb->items_active_alpha;
}
node->alpha = ia;
node->label_alpha = 0;
node->zoom = iz;
node->y = xmb_item_y(xmb, i, current);
node->x = xmb->icon_size * 1 * -2;
}
}
static void xmb_ribbon_set_vertex(float *ribbon_verts,
unsigned idx, unsigned row, unsigned col)
{
ribbon_verts[idx++] = ((float)col) / (XMB_RIBBON_COLS-1) * 2.0f - 1.0f;
ribbon_verts[idx++] = ((float)row) / (XMB_RIBBON_ROWS-1) * 2.0f - 1.0f;
}
static void xmb_init_ribbon(xmb_handle_t * xmb)
{
video_coords_t coords;
unsigned r, c, col;
unsigned i = 0;
gfx_display_t *p_disp = disp_get_ptr();
video_coord_array_t *ca = &p_disp->dispca;
unsigned vertices_total = XMB_RIBBON_VERTICES;
float *dummy = (float*)calloc(4 * vertices_total, sizeof(float));
float *ribbon_verts = (float*)calloc(2 * vertices_total, sizeof(float));
/* Set up vertices */
for (r = 0; r < XMB_RIBBON_ROWS - 1; r++)
{
for (c = 0; c < XMB_RIBBON_COLS; c++)
{
col = r % 2 ? XMB_RIBBON_COLS - c - 1 : c;
xmb_ribbon_set_vertex(ribbon_verts, i, r, col);
xmb_ribbon_set_vertex(ribbon_verts, i + 2, r + 1, col);
i += 4;
}
}
coords.color = dummy;
coords.vertex = ribbon_verts;
coords.tex_coord = dummy;
coords.lut_tex_coord = dummy;
coords.vertices = vertices_total;
video_coord_array_append(ca, &coords, coords.vertices);
free(dummy);
free(ribbon_verts);
}
static void xmb_menu_animation_update_time(
float *ticker_pixel_increment,
unsigned video_width, unsigned video_height)
{
menu_handle_t *menu = menu_state_get_ptr()->driver_data;
xmb_handle_t *xmb = NULL;
if (!menu)
return;
xmb = (xmb_handle_t*)menu->userdata;
if (!xmb)
return;
*(ticker_pixel_increment) *= xmb->last_scale_factor;
}
static void *xmb_init(void **userdata, bool video_is_threaded)
{
unsigned width, height;
int i;
xmb_handle_t *xmb = NULL;
settings_t *settings = config_get_ptr();
gfx_animation_t *p_anim = anim_get_ptr();
menu_handle_t *menu = (menu_handle_t*)calloc(1, sizeof(*menu));
float scale_value = settings->floats.menu_scale_factor * 100.0f;
/* scaling multiplier formulas made from these values: */
/* xmb_scale 50 = {2.5, 2.5, 2, 1.7, 2.5, 4, 2.4, 2.5} */
/* xmb_scale 75 = { 2, 1.6, 1.6, 1.4, 1.5, 2.3, 1.9, 1.3} */
if (scale_value < 100)
{
/* text length & word wrap (base 35 apply to file browser, 1st column) */
xmb_scale_mod[0] = -0.03 * scale_value + 4.083;
/* playlist text length when thumbnail is ON (small, base 40) */
xmb_scale_mod[1] = -0.03 * scale_value + 3.95;
/* playlist text length when thumbnail is OFF (large, base 70) */
xmb_scale_mod[2] = -0.02 * scale_value + 3.033;
/* sub-label length & word wrap */
xmb_scale_mod[3] = -0.014 * scale_value + 2.416;
/* thumbnail size & vertical margin from top */
xmb_scale_mod[4] = -0.03 * scale_value + 3.916;
/* thumbnail horizontal left margin (horizontal positioning) */
xmb_scale_mod[5] = -0.06 * scale_value + 6.933;
/* margin before 2nd column start (shaders parameters, cheats...) */
xmb_scale_mod[6] = -0.028 * scale_value + 3.866;
/* text length & word wrap (base 35 apply to 2nd column in cheats, shaders, etc) */
xmb_scale_mod[7] = 134.179 * pow(scale_value, -1.0778);
for (i = 0; i < 8; i++)
if (xmb_scale_mod[i] < 1)
xmb_scale_mod[i] = 1;
}
if (!menu)
return NULL;
video_driver_get_size(&width, &height);
xmb = (xmb_handle_t*)calloc(1, sizeof(xmb_handle_t));
if (!xmb)
{
free(menu);
return NULL;
}
*userdata = xmb;
file_list_initialize(&xmb->selection_buf_old);
xmb->categories_active_idx = 0;
xmb->categories_active_idx_old = 0;
xmb->x = 0;
xmb->categories_x_pos = 0;
xmb->textures_arrow_alpha = 0;
xmb->depth = 1;
xmb->old_depth = 1;
xmb->alpha = 0;
xmb->system_tab_end = 0;
xmb->tabs[xmb->system_tab_end] = XMB_SYSTEM_TAB_MAIN;
if (settings->bools.menu_content_show_settings && !settings->bools.kiosk_mode_enable)
xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_SETTINGS;
if (settings->bools.menu_content_show_favorites)
xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_FAVORITES;
if (settings->bools.menu_content_show_history)
xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_HISTORY;
#ifdef HAVE_IMAGEVIEWER
if (settings->bools.menu_content_show_images)
xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_IMAGES;
#endif
if (settings->bools.menu_content_show_music)
xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_MUSIC;
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
if (settings->bools.menu_content_show_video)
xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_VIDEO;
#endif
#ifdef HAVE_NETWORKING
if (settings->bools.menu_content_show_netplay)
xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_NETPLAY;
#endif
if ( settings->bools.menu_content_show_add
&& !settings->bools.kiosk_mode_enable)
xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_ADD;
#if defined(HAVE_LIBRETRODB)
if (settings->bools.menu_content_show_explore)
xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_EXPLORE;
#endif
menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL);
/* TODO/FIXME - we don't use framebuffer at all
* for XMB, we should refactor this dependency
* away. */
gfx_display_set_width(width);
gfx_display_set_height(height);
gfx_display_init_white_texture();
file_list_initialize(&xmb->horizontal_list);
xmb_init_horizontal_list(xmb);
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)
goto error;
xmb->savestate_thumbnail_file_path[0] = '\0';
xmb->prev_savestate_thumbnail_file_path[0] = '\0';
xmb->fullscreen_thumbnails_available = false;
xmb->show_fullscreen_thumbnails = false;
xmb->fullscreen_thumbnail_alpha = 0.0f;
xmb->fullscreen_thumbnail_selection = 0;
xmb->fullscreen_thumbnail_label[0] = '\0';
gfx_thumbnail_set_stream_delay(-1.0f);
gfx_thumbnail_set_fade_duration(-1.0f);
gfx_thumbnail_set_fade_missing(false);
xmb->use_ps3_layout = xmb_use_ps3_layout(settings, width, height);
xmb->last_use_ps3_layout = xmb->use_ps3_layout;
xmb->last_scale_factor = xmb_get_scale_factor(settings, xmb->use_ps3_layout, width);
p_anim->updatetime_cb = xmb_menu_animation_update_time;
/* set word_wrap function pointer */
xmb->word_wrap = msg_hash_get_wideglyph_str() ? word_wrap_wideglyph : word_wrap;
return menu;
error:
free(menu);
xmb_free_list_nodes(&xmb->horizontal_list, false);
file_list_deinitialize(&xmb->selection_buf_old);
file_list_deinitialize(&xmb->horizontal_list);
return NULL;
}
static void xmb_free(void *data)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
if (xmb)
{
xmb_free_list_nodes(&xmb->selection_buf_old, false);
xmb_free_list_nodes(&xmb->horizontal_list, false);
file_list_deinitialize(&xmb->selection_buf_old);
file_list_deinitialize(&xmb->horizontal_list);
video_coord_array_free(&xmb->raster_block.carr);
video_coord_array_free(&xmb->raster_block2.carr);
if (!string_is_empty(xmb->box_message))
free(xmb->box_message);
if (!string_is_empty(xmb->bg_file_path))
free(xmb->bg_file_path);
if (xmb->thumbnail_path_data)
free(xmb->thumbnail_path_data);
menu_screensaver_free(xmb->screensaver);
}
gfx_display_deinit_white_texture();
font_driver_bind_block(NULL, NULL);
}
static void xmb_context_bg_destroy(xmb_handle_t *xmb)
{
if (!xmb)
return;
video_driver_texture_unload(&xmb->textures.bg);
gfx_display_deinit_white_texture();
}
static bool xmb_load_image(void *userdata, void *data,
enum menu_image_type type)
{
xmb_handle_t *xmb = (xmb_handle_t*)userdata;
if (!xmb)
return false;
switch (type)
{
case MENU_IMAGE_WALLPAPER:
xmb_context_bg_destroy(xmb);
video_driver_texture_unload(&xmb->textures.bg);
gfx_display_deinit_white_texture();
video_driver_texture_load(data,
TEXTURE_FILTER_MIPMAP_LINEAR,
&xmb->textures.bg);
gfx_display_init_white_texture();
break;
case MENU_IMAGE_NONE:
default:
break;
}
return true;
}
static const char *xmb_texture_path(unsigned id)
{
switch (id)
{
case XMB_TEXTURE_MAIN_MENU:
#if defined(HAVE_LAKKA)
return "lakka.png";
#else
return "retroarch.png";
#endif
case XMB_TEXTURE_SETTINGS:
return "settings.png";
case XMB_TEXTURE_HISTORY:
return "history.png";
case XMB_TEXTURE_FAVORITES:
return "favorites.png";
case XMB_TEXTURE_ADD_FAVORITE:
return "add-favorite.png";
case XMB_TEXTURE_MUSICS:
return "musics.png";
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
case XMB_TEXTURE_MOVIES:
return "movies.png";
#endif
#ifdef HAVE_IMAGEVIEWER
case XMB_TEXTURE_IMAGES:
return "images.png";
#endif
case XMB_TEXTURE_SETTING:
return "setting.png";
case XMB_TEXTURE_SUBSETTING:
return "subsetting.png";
case XMB_TEXTURE_ARROW:
return "arrow.png";
case XMB_TEXTURE_RUN:
return "run.png";
case XMB_TEXTURE_CLOSE:
return "close.png";
case XMB_TEXTURE_RESUME:
return "resume.png";
case XMB_TEXTURE_CLOCK:
return "clock.png";
case XMB_TEXTURE_BATTERY_FULL:
return "battery-full.png";
case XMB_TEXTURE_BATTERY_CHARGING:
return "battery-charging.png";
case XMB_TEXTURE_BATTERY_80:
return "battery-80.png";
case XMB_TEXTURE_BATTERY_60:
return "battery-60.png";
case XMB_TEXTURE_BATTERY_40:
return "battery-40.png";
case XMB_TEXTURE_BATTERY_20:
return "battery-20.png";
case XMB_TEXTURE_POINTER:
return "pointer.png";
case XMB_TEXTURE_SAVESTATE:
return "savestate.png";
case XMB_TEXTURE_LOADSTATE:
return "loadstate.png";
case XMB_TEXTURE_UNDO:
return "undo.png";
case XMB_TEXTURE_CORE_INFO:
return "core-infos.png";
case XMB_TEXTURE_BLUETOOTH:
return "bluetooth.png";
case XMB_TEXTURE_WIFI:
return "wifi.png";
case XMB_TEXTURE_CORE_OPTIONS:
return "core-options.png";
case XMB_TEXTURE_INPUT_REMAPPING_OPTIONS:
return "core-input-remapping-options.png";
case XMB_TEXTURE_CHEAT_OPTIONS:
return "core-cheat-options.png";
case XMB_TEXTURE_DISK_OPTIONS:
return "core-disk-options.png";
case XMB_TEXTURE_SHADER_OPTIONS:
return "core-shader-options.png";
case XMB_TEXTURE_ACHIEVEMENT_LIST:
return "achievement-list.png";
case XMB_TEXTURE_SCREENSHOT:
return "screenshot.png";
case XMB_TEXTURE_RELOAD:
return "reload.png";
case XMB_TEXTURE_RENAME:
return "rename.png";
case XMB_TEXTURE_FILE:
return "file.png";
case XMB_TEXTURE_FOLDER:
return "folder.png";
case XMB_TEXTURE_ZIP:
return "zip.png";
case XMB_TEXTURE_MUSIC:
return "music.png";
case XMB_TEXTURE_FAVORITE:
return "favorites-content.png";
case XMB_TEXTURE_IMAGE:
return "image.png";
case XMB_TEXTURE_MOVIE:
return "movie.png";
case XMB_TEXTURE_CORE:
return "core.png";
case XMB_TEXTURE_RDB:
return "database.png";
case XMB_TEXTURE_CURSOR:
return "cursor.png";
case XMB_TEXTURE_SWITCH_ON:
return "on.png";
case XMB_TEXTURE_SWITCH_OFF:
return "off.png";
case XMB_TEXTURE_ADD:
return "add.png";
#ifdef HAVE_NETWORKING
case XMB_TEXTURE_NETPLAY:
return "netplay.png";
case XMB_TEXTURE_ROOM:
return "menu_room.png";
case XMB_TEXTURE_ROOM_LAN:
return "menu_room_lan.png";
case XMB_TEXTURE_ROOM_RELAY:
return "menu_room_relay.png";
#endif
case XMB_TEXTURE_KEY:
return "key.png";
case XMB_TEXTURE_KEY_HOVER:
return "key-hover.png";
case XMB_TEXTURE_DIALOG_SLICE:
return "dialog-slice.png";
case XMB_TEXTURE_ACHIEVEMENTS:
return "menu_achievements.png";
case XMB_TEXTURE_AUDIO:
return "menu_audio.png";
case XMB_TEXTURE_DRIVERS:
return "menu_drivers.png";
case XMB_TEXTURE_EXIT:
return "menu_exit.png";
case XMB_TEXTURE_FRAMESKIP:
return "menu_frameskip.png";
case XMB_TEXTURE_HELP:
return "menu_help.png";
case XMB_TEXTURE_INFO:
return "menu_info.png";
case XMB_TEXTURE_INPUT_SETTINGS:
return "Libretro - Pad.png";
case XMB_TEXTURE_LATENCY:
return "menu_latency.png";
case XMB_TEXTURE_NETWORK:
return "menu_network.png";
case XMB_TEXTURE_POWER:
return "menu_power.png";
case XMB_TEXTURE_RECORD:
return "menu_record.png";
case XMB_TEXTURE_SAVING:
return "menu_saving.png";
case XMB_TEXTURE_UPDATER:
return "menu_updater.png";
case XMB_TEXTURE_VIDEO:
return "menu_video.png";
case XMB_TEXTURE_MIXER:
return "menu_mixer.png";
case XMB_TEXTURE_LOG:
return "menu_log.png";
case XMB_TEXTURE_OSD:
return "menu_osd.png";
case XMB_TEXTURE_UI:
return "menu_ui.png";
case XMB_TEXTURE_USER:
return "menu_user.png";
case XMB_TEXTURE_PRIVACY:
return "menu_privacy.png";
case XMB_TEXTURE_PLAYLIST:
return "menu_playlist.png";
case XMB_TEXTURE_DISC:
return "disc.png";
case XMB_TEXTURE_QUICKMENU:
return "menu_quickmenu.png";
case XMB_TEXTURE_REWIND:
return "menu_rewind.png";
case XMB_TEXTURE_OVERLAY:
return "menu_overlay.png";
case XMB_TEXTURE_OVERRIDE:
return "menu_override.png";
case XMB_TEXTURE_NOTIFICATIONS:
return "menu_notifications.png";
case XMB_TEXTURE_STREAM:
return "menu_stream.png";
case XMB_TEXTURE_SHUTDOWN:
return "menu_shutdown.png";
case XMB_TEXTURE_INPUT_DPAD_U:
return "input_DPAD-U.png";
case XMB_TEXTURE_INPUT_DPAD_D:
return "input_DPAD-D.png";
case XMB_TEXTURE_INPUT_DPAD_L:
return "input_DPAD-L.png";
case XMB_TEXTURE_INPUT_DPAD_R:
return "input_DPAD-R.png";
case XMB_TEXTURE_INPUT_STCK_U:
return "input_STCK-U.png";
case XMB_TEXTURE_INPUT_STCK_D:
return "input_STCK-D.png";
case XMB_TEXTURE_INPUT_STCK_L:
return "input_STCK-L.png";
case XMB_TEXTURE_INPUT_STCK_R:
return "input_STCK-R.png";
case XMB_TEXTURE_INPUT_STCK_P:
return "input_STCK-P.png";
case XMB_TEXTURE_INPUT_BTN_U:
return "input_BTN-U.png";
case XMB_TEXTURE_INPUT_BTN_D:
return "input_BTN-D.png";
case XMB_TEXTURE_INPUT_BTN_L:
return "input_BTN-L.png";
case XMB_TEXTURE_INPUT_BTN_R:
return "input_BTN-R.png";
case XMB_TEXTURE_INPUT_LB:
return "input_LB.png";
case XMB_TEXTURE_INPUT_RB:
return "input_RB.png";
case XMB_TEXTURE_INPUT_LT:
return "input_LT.png";
case XMB_TEXTURE_INPUT_RT:
return "input_RT.png";
case XMB_TEXTURE_INPUT_SELECT:
return "input_SELECT.png";
case XMB_TEXTURE_INPUT_START:
return "input_START.png";
case XMB_TEXTURE_INPUT_ADC:
return "input_ADC.png";
case XMB_TEXTURE_INPUT_BIND_ALL:
return "input_BIND_ALL.png";
case XMB_TEXTURE_INPUT_MOUSE:
return "input_MOUSE.png";
case XMB_TEXTURE_INPUT_LGUN:
return "input_LGUN.png";
case XMB_TEXTURE_INPUT_TURBO:
return "input_TURBO.png";
case XMB_TEXTURE_CHECKMARK:
return "menu_check.png";
case XMB_TEXTURE_MENU_ADD:
return "menu_add.png";
case XMB_TEXTURE_BRIGHTNESS:
return "menu_brightness.png";
case XMB_TEXTURE_PAUSE:
return "menu_pause.png";
case XMB_TEXTURE_DEFAULT:
return "default.png";
case XMB_TEXTURE_DEFAULT_CONTENT:
return "default-content.png";
case XMB_TEXTURE_MENU_APPLY_TOGGLE:
return "menu_apply_toggle.png";
case XMB_TEXTURE_MENU_APPLY_COG:
return "menu_apply_cog.png";
}
return NULL;
}
static void xmb_context_reset_textures(
xmb_handle_t *xmb, const char *iconpath)
{
unsigned i;
settings_t *settings = config_get_ptr();
xmb->assets_missing = false;
gfx_display_deinit_white_texture();
gfx_display_init_white_texture();
for (i = 0; i < XMB_TEXTURE_LAST; i++)
{
if (!gfx_display_reset_textures_list(xmb_texture_path(i), iconpath, &xmb->textures.list[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
{
RARCH_WARN("[XMB] Asset missing: %s%s\n", iconpath, xmb_texture_path(i));
/* New extra battery icons could be missing */
if (i == XMB_TEXTURE_BATTERY_80 || i == XMB_TEXTURE_BATTERY_60 || i == XMB_TEXTURE_BATTERY_40 || i == XMB_TEXTURE_BATTERY_20)
{
if ( /* If there are no extra battery icons revert to the old behaviour */
!gfx_display_reset_textures_list(xmb_texture_path(XMB_TEXTURE_BATTERY_FULL), iconpath, &xmb->textures.list[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL)
&& !(settings->uints.menu_xmb_theme == XMB_ICON_THEME_CUSTOM)
)
goto error;
else continue;
}
/* If the icon is missing return the subsetting (because some themes are incomplete) */
if (!(i == XMB_TEXTURE_DIALOG_SLICE || i == XMB_TEXTURE_KEY_HOVER || i == XMB_TEXTURE_KEY))
{
/* OSD Warning only if subsetting icon is missing */
if (
!gfx_display_reset_textures_list(xmb_texture_path(XMB_TEXTURE_SUBSETTING), iconpath, &xmb->textures.list[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL)
&& !(settings->uints.menu_xmb_theme == XMB_ICON_THEME_CUSTOM)
)
{
runloop_msg_queue_push(msg_hash_to_str(MSG_MISSING_ASSETS), 1, 256, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
/* Do not draw icons if subsetting is missing */
goto error;
}
/* Do not draw icons if this ones are is missing */
switch (i)
{
case XMB_TEXTURE_POINTER:
case XMB_TEXTURE_ARROW:
case XMB_TEXTURE_CLOCK:
case XMB_TEXTURE_BATTERY_CHARGING:
case XMB_TEXTURE_BATTERY_FULL:
case XMB_TEXTURE_DEFAULT:
case XMB_TEXTURE_DEFAULT_CONTENT:
goto error;
}
}
}
}
xmb->main_menu_node.icon = xmb->textures.list[XMB_TEXTURE_MAIN_MENU];
xmb->main_menu_node.alpha = xmb->categories_active_alpha;
xmb->main_menu_node.zoom = xmb->categories_active_zoom;
xmb->settings_tab_node.icon = xmb->textures.list[XMB_TEXTURE_SETTINGS];
xmb->settings_tab_node.alpha = xmb->categories_active_alpha;
xmb->settings_tab_node.zoom = xmb->categories_active_zoom;
xmb->history_tab_node.icon = xmb->textures.list[XMB_TEXTURE_HISTORY];
xmb->history_tab_node.alpha = xmb->categories_active_alpha;
xmb->history_tab_node.zoom = xmb->categories_active_zoom;
xmb->favorites_tab_node.icon = xmb->textures.list[XMB_TEXTURE_FAVORITES];
xmb->favorites_tab_node.alpha = xmb->categories_active_alpha;
xmb->favorites_tab_node.zoom = xmb->categories_active_zoom;
xmb->music_tab_node.icon = xmb->textures.list[XMB_TEXTURE_MUSICS];
xmb->music_tab_node.alpha = xmb->categories_active_alpha;
xmb->music_tab_node.zoom = xmb->categories_active_zoom;
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
xmb->video_tab_node.icon = xmb->textures.list[XMB_TEXTURE_MOVIES];
xmb->video_tab_node.alpha = xmb->categories_active_alpha;
xmb->video_tab_node.zoom = xmb->categories_active_zoom;
#endif
#ifdef HAVE_IMAGEVIEWER
xmb->images_tab_node.icon = xmb->textures.list[XMB_TEXTURE_IMAGES];
xmb->images_tab_node.alpha = xmb->categories_active_alpha;
xmb->images_tab_node.zoom = xmb->categories_active_zoom;
#endif
xmb->add_tab_node.icon = xmb->textures.list[XMB_TEXTURE_ADD];
xmb->add_tab_node.alpha = xmb->categories_active_alpha;
xmb->add_tab_node.zoom = xmb->categories_active_zoom;
#if defined(HAVE_LIBRETRODB)
xmb->explore_tab_node.icon = xmb->textures.list[XMB_TEXTURE_MAIN_MENU];
xmb->explore_tab_node.alpha = xmb->categories_active_alpha;
xmb->explore_tab_node.zoom = xmb->categories_active_zoom;
#endif
#ifdef HAVE_NETWORKING
xmb->netplay_tab_node.icon = xmb->textures.list[XMB_TEXTURE_NETPLAY];
xmb->netplay_tab_node.alpha = xmb->categories_active_alpha;
xmb->netplay_tab_node.zoom = xmb->categories_active_zoom;
#endif
/* Recolor */
if (
(settings->uints.menu_xmb_theme == XMB_ICON_THEME_MONOCHROME_INVERTED) ||
(settings->uints.menu_xmb_theme == XMB_ICON_THEME_AUTOMATIC_INVERTED)
)
memcpy(item_color, xmb_coord_black, sizeof(item_color));
else
{
if (
(settings->uints.menu_xmb_theme == XMB_ICON_THEME_MONOCHROME) ||
(settings->uints.menu_xmb_theme == XMB_ICON_THEME_AUTOMATIC)
)
{
for (i=0;i<16;i++)
{
if ((i==3) || (i==7) || (i==11) || (i==15))
{
item_color[i] = 1;
continue;
}
item_color[i] = 0.95;
}
}
else
memcpy(item_color, xmb_coord_white, sizeof(item_color));
}
return;
error:
xmb->assets_missing = true;
RARCH_WARN("[XMB] Critical asset missing, no icons will be drawn\n");
}
static void xmb_context_reset_background(xmb_handle_t *xmb, const char *iconpath)
{
char path[PATH_MAX_LENGTH];
settings_t *settings = config_get_ptr();
const char *path_menu_wp = settings->paths.path_menu_wallpaper;
path[0] = '\0';
/* Dynamic wallpaper takes precedence as reset background,
* then comes 'menu_wallpaper', and then iconset 'bg.png' */
if (settings->bools.menu_dynamic_wallpaper_enable)
strlcpy(path, xmb_path_dynamic_wallpaper(xmb), sizeof(path));
if (!string_is_empty(path) && path_is_valid(path))
{
task_push_image_load(path,
video_driver_supports_rgba(), 0,
menu_display_handle_wallpaper_upload, NULL);
}
else if (!string_is_empty(path_menu_wp))
{
if (path_is_valid(path_menu_wp))
task_push_image_load(path_menu_wp,
video_driver_supports_rgba(), 0,
menu_display_handle_wallpaper_upload, NULL);
}
else if (!string_is_empty(iconpath))
{
fill_pathname_join(path, iconpath,
FILE_PATH_BACKGROUND_IMAGE, sizeof(path));
if (path_is_valid(path))
task_push_image_load(path,
video_driver_supports_rgba(), 0,
menu_display_handle_wallpaper_upload, NULL);
}
#ifdef ORBIS
/* To avoid weird behaviour on orbis with remote host */
sleep(5);
#endif
}
static void xmb_context_reset_internal(xmb_handle_t *xmb,
bool is_threaded, bool reinit_textures)
{
char iconpath[PATH_MAX_LENGTH];
char bg_file_path[PATH_MAX_LENGTH];
gfx_display_t *p_disp = disp_get_ptr();
const char *wideglyph_str = msg_hash_get_wideglyph_str();
iconpath[0] = bg_file_path[0] = '\0';
fill_pathname_application_special(bg_file_path,
sizeof(bg_file_path), APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_BG);
if (!string_is_empty(bg_file_path))
{
if (!string_is_empty(xmb->bg_file_path))
free(xmb->bg_file_path);
xmb->bg_file_path = strdup(bg_file_path);
}
fill_pathname_application_special(iconpath, sizeof(iconpath),
APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_ICONS);
xmb_layout(xmb);
if (xmb->font)
{
gfx_display_font_free(xmb->font);
xmb->font = NULL;
}
if (xmb->font2)
{
gfx_display_font_free(xmb->font2);
xmb->font2 = NULL;
}
xmb->font = gfx_display_font(p_disp,
APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_FONT,
xmb->font_size,
is_threaded);
xmb->font2 = gfx_display_font(p_disp,
APPLICATION_SPECIAL_DIRECTORY_ASSETS_XMB_FONT,
xmb->font2_size,
is_threaded);
xmb->wideglyph_width = 100;
if (wideglyph_str)
{
int char_width =
font_driver_get_message_width(xmb->font, "a", 1, 1);
int wideglyph_width =
font_driver_get_message_width(xmb->font, wideglyph_str, (unsigned)strlen(wideglyph_str), 1);
if (wideglyph_width > 0 && char_width > 0)
xmb->wideglyph_width = wideglyph_width * 100 / char_width;
}
if (reinit_textures)
{
xmb_context_reset_textures(xmb, iconpath);
xmb_context_reset_background(xmb, iconpath);
}
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
* or the quick menu */
if (gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) ||
gfx_thumbnail_is_enabled(xmb->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
{
unsigned depth = (unsigned)xmb_list_get_size(xmb, MENU_LIST_PLAIN);
unsigned xmb_system_tab = xmb_get_system_tab(xmb, (unsigned)xmb->categories_selection_ptr);
if (((((xmb_system_tab > XMB_SYSTEM_TAB_SETTINGS && depth == 1) ||
(xmb_system_tab < XMB_SYSTEM_TAB_SETTINGS && depth == 4)) &&
xmb->is_playlist)) ||
xmb->is_db_manager_list ||
xmb->is_file_list ||
xmb->is_quick_menu)
xmb_update_thumbnail_image(xmb);
}
xmb_update_savestate_thumbnail_image(xmb);
}
static void xmb_context_reset(void *data, bool is_threaded)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
if (xmb)
xmb_context_reset_internal(xmb, is_threaded, true);
video_driver_monitor_reset();
}
static void xmb_navigation_clear(void *data, bool pending_push)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
if (!pending_push)
xmb_selection_pointer_changed(xmb, true);
}
static void xmb_navigation_pointer_changed(void *data)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
xmb_selection_pointer_changed(xmb, true);
}
static void xmb_navigation_set(void *data, bool scroll)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
xmb_selection_pointer_changed(xmb, true);
}
static void xmb_navigation_alphabet(void *data, size_t *unused)
{
xmb_handle_t *xmb = (xmb_handle_t*)data;
xmb_selection_pointer_changed(xmb, true);
}
static void xmb_list_insert(void *userdata,
file_list_t *list,
const char *path,
const char *fullpath,
const char *unused,
size_t list_size,
unsigned entry_type)
{
int current = 0;
int i = (int)list_size;
xmb_node_t *node = NULL;
xmb_handle_t *xmb = (xmb_handle_t*)userdata;
size_t selection = menu_navigation_get_selection();
if (!xmb || !list)
return;
node = (xmb_node_t*)list->list[i].userdata;
if (!node)
node = xmb_alloc_node();
if (!node)
{
RARCH_ERR("XMB node could not be allocated.\n");
return;
}
current = (int)selection;
if (!string_is_empty(fullpath))
{
if (node->fullpath)
free(node->fullpath);
node->fullpath = strdup(fullpath);
}
node->alpha = xmb->items_passive_alpha;
node->zoom = xmb->items_passive_zoom;
node->label_alpha = node->alpha;
node->y = xmb_item_y(xmb, i, current);
node->x = 0;
if (i == current)
{
node->alpha = xmb->items_active_alpha;
node->label_alpha = xmb->items_active_alpha;
node->zoom = xmb->items_active_alpha;
}
list->list[i].userdata = node;
}
static void xmb_list_clear(file_list_t *list)
{
uintptr_t tag = (uintptr_t)list;
gfx_animation_kill_by_tag(&tag);
xmb_free_list_nodes(list, false);
}
static void xmb_list_free(file_list_t *list, size_t a, size_t b)
{
xmb_list_clear(list);
}
static void xmb_list_deep_copy(const file_list_t *src, file_list_t *dst,
size_t first, size_t last)
{
size_t i, j = 0;
uintptr_t tag = (uintptr_t)dst;
gfx_animation_kill_by_tag(&tag);
xmb_free_list_nodes(dst, true);
file_list_clear(dst);
file_list_reserve(dst, (last + 1) - first);
for (i = first; i <= last; ++i)
{
struct item_file *d = &dst->list[j];
struct item_file *s = &src->list[i];
void *src_udata = s->userdata;
void *src_adata = s->actiondata;
*d = *s;
d->alt = string_is_empty(d->alt) ? NULL : strdup(d->alt);
d->path = string_is_empty(d->path) ? NULL : strdup(d->path);
d->label = string_is_empty(d->label) ? NULL : strdup(d->label);
if (src_udata)
dst->list[j].userdata = (void*)xmb_copy_node((const xmb_node_t*)src_udata);
if (src_adata)
{
void *data = malloc(sizeof(menu_file_list_cbs_t));
memcpy(data, src_adata, sizeof(menu_file_list_cbs_t));
dst->list[j].actiondata = data;
}
++j;
}
dst->size = j;
}
static void xmb_list_cache(void *data, enum menu_list_type type, unsigned action)
{
size_t stack_size, list_size;
xmb_handle_t *xmb = (xmb_handle_t*)data;
file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0);
file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
size_t selection = menu_navigation_get_selection();
settings_t *settings = config_get_ptr();
bool menu_horizontal_animation = settings->bools.menu_horizontal_animation;
if (!xmb)
return;
/* Check whether to enable the horizontal animation. */
if (menu_horizontal_animation)
{
unsigned first = 0, last = 0;
unsigned height = 0;
video_driver_get_size(NULL, &height);
/* FIXME: this shouldn't be happening at all */
if (selection >= selection_buf->size)
selection = selection_buf->size ? selection_buf->size - 1 : 0;
xmb->selection_ptr_old = selection;
xmb_calculate_visible_range(xmb, height, selection_buf->size,
(unsigned)xmb->selection_ptr_old, &first, &last);
xmb_list_deep_copy(selection_buf,
&xmb->selection_buf_old, first, last);
xmb->selection_ptr_old -= first;
last -= first;
first = 0;
}
else
xmb->selection_ptr_old = 0;
list_size = xmb_list_get_size(xmb, MENU_LIST_HORIZONTAL)
+ xmb->system_tab_end;
switch (type)
{
case MENU_LIST_PLAIN:
break;
case MENU_LIST_HORIZONTAL:
xmb->categories_selection_ptr_old = xmb->categories_selection_ptr;
switch (action)
{
case MENU_ACTION_LEFT:
if (xmb->categories_selection_ptr == 0)
{
xmb->categories_selection_ptr = list_size;
xmb->categories_active_idx = (unsigned)(list_size - 1);
}
else
xmb->categories_selection_ptr--;
break;
default:
if (xmb->categories_selection_ptr == list_size)
{
xmb->categories_selection_ptr = 0;
xmb->categories_active_idx = 1;
}
else
xmb->categories_selection_ptr++;
break;
}
stack_size = menu_stack->size;
if (menu_stack->list[stack_size - 1].label)
free(menu_stack->list[stack_size - 1].label);
menu_stack->list[stack_size - 1].label = NULL;
switch (xmb_get_system_tab(xmb, (unsigned)xmb->categories_selection_ptr))
{
case XMB_SYSTEM_TAB_MAIN:
menu_stack->list[stack_size - 1].label =
strdup(msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU));
menu_stack->list[stack_size - 1].type =
MENU_SETTINGS;
break;
case XMB_SYSTEM_TAB_SETTINGS:
menu_stack->list[stack_size - 1].label =
strdup(msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS_TAB));
menu_stack->list[stack_size - 1].type =
MENU_SETTINGS_TAB;
break;
#ifdef HAVE_IMAGEVIEWER
case XMB_SYSTEM_TAB_IMAGES:
menu_stack->list[stack_size - 1].label =
strdup(msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB));
menu_stack->list[stack_size - 1].type =
MENU_IMAGES_TAB;
break;
#endif
case XMB_SYSTEM_TAB_MUSIC:
menu_stack->list[stack_size - 1].label =
strdup(msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB));
menu_stack->list[stack_size - 1].type =
MENU_MUSIC_TAB;
break;
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
case XMB_SYSTEM_TAB_VIDEO:
menu_stack->list[stack_size - 1].label =
strdup(msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_TAB));
menu_stack->list[stack_size - 1].type =
MENU_VIDEO_TAB;
break;
#endif
case XMB_SYSTEM_TAB_HISTORY:
menu_stack->list[stack_size - 1].label =
strdup(msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB));
menu_stack->list[stack_size - 1].type =
MENU_HISTORY_TAB;
break;
case XMB_SYSTEM_TAB_FAVORITES:
menu_stack->list[stack_size - 1].label =
strdup(msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES_TAB));
menu_stack->list[stack_size - 1].type =
MENU_FAVORITES_TAB;
break;
#ifdef HAVE_NETWORKING
case XMB_SYSTEM_TAB_NETPLAY:
menu_stack->list[stack_size - 1].label =
strdup(msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB));
menu_stack->list[stack_size - 1].type =
MENU_NETPLAY_TAB;
break;
#endif
case XMB_SYSTEM_TAB_ADD:
menu_stack->list[stack_size - 1].label =
strdup(msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB));
menu_stack->list[stack_size - 1].type =
MENU_ADD_TAB;
break;
#if defined(HAVE_LIBRETRODB)
case XMB_SYSTEM_TAB_EXPLORE:
menu_stack->list[stack_size - 1].label =
strdup(msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB));
menu_stack->list[stack_size - 1].type =
MENU_EXPLORE_TAB;
break;
#endif
default:
menu_stack->list[stack_size - 1].label =
strdup(msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU));
menu_stack->list[stack_size - 1].type =
MENU_SETTING_HORIZONTAL_MENU;
break;
}
break;
default:
break;
}
}
static void xmb_context_destroy(void *data)
{
unsigned i;
xmb_handle_t *xmb = (xmb_handle_t*)data;
if (!xmb)
return;
for (i = 0; i < XMB_TEXTURE_LAST; i++)
video_driver_texture_unload(&xmb->textures.list[i]);
xmb_unload_thumbnail_textures(xmb);
xmb_context_destroy_horizontal_list(xmb);
xmb_context_bg_destroy(xmb);
gfx_display_font_free(xmb->font);
gfx_display_font_free(xmb->font2);
xmb->font = NULL;
xmb->font2 = NULL;
menu_screensaver_context_destroy(xmb->screensaver);
}
static void xmb_toggle(void *userdata, bool menu_on)
{
gfx_animation_ctx_entry_t entry;
bool tmp = false;
xmb_handle_t *xmb = (xmb_handle_t*)userdata;
if (!xmb)
return;
xmb->depth = (int)xmb_list_get_size(xmb, MENU_LIST_PLAIN);
if (!menu_on)
{
xmb->alpha = 0;
return;
}
/* Have to reset this, otherwise savestate
* thumbnail won't update after selecting
* 'save state' option */
gfx_thumbnail_reset(&xmb->thumbnails.savestate);
entry.duration = XMB_DELAY * 2;
entry.target_value = 1.0f;
entry.subject = &xmb->alpha;
entry.easing_enum = EASING_OUT_QUAD;
/* TODO/FIXME - integer conversion resulted in change of sign */
entry.tag = -1;
entry.cb = NULL;
gfx_animation_push(&entry);
tmp = !menu_entries_ctl(MENU_ENTRIES_CTL_NEEDS_REFRESH, NULL);
if (tmp)
menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
else
menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL);
xmb_toggle_horizontal_list(xmb);
}
static int xmb_deferred_push_content_actions(menu_displaylist_info_t *info)
{
settings_t *settings = config_get_ptr();
if (!menu_displaylist_ctl(
DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info, settings))
return -1;
menu_displaylist_process(info);
menu_displaylist_info_free(info);
return 0;
}
static int xmb_list_bind_init_compare_label(menu_file_list_cbs_t *cbs)
{
if (cbs && cbs->enum_idx != MSG_UNKNOWN)
{
switch (cbs->enum_idx)
{
case MENU_ENUM_LABEL_CONTENT_ACTIONS:
cbs->action_deferred_push = xmb_deferred_push_content_actions;
break;
default:
return -1;
}
}
return 0;
}
static int xmb_list_bind_init(menu_file_list_cbs_t *cbs,
const char *path, const char *label, unsigned type, size_t idx)
{
if (xmb_list_bind_init_compare_label(cbs) == 0)
return 0;
return -1;
}
static int xmb_list_push(void *data, void *userdata,
menu_displaylist_info_t *info, unsigned type)
{
int ret = -1;
core_info_list_t *list = NULL;
settings_t *settings = config_get_ptr();
bool menu_show_load_core = settings->bools.menu_show_load_core;
bool menu_show_load_content = settings->bools.menu_show_load_content;
bool menu_content_show_pl = settings->bools.menu_content_show_playlists;
bool menu_show_configurations = settings->bools.menu_show_configurations;
bool menu_show_load_disc = settings->bools.menu_show_load_disc;
bool menu_show_dump_disc = settings->bools.menu_show_dump_disc;
#ifdef HAVE_LAKKA
bool menu_show_eject_disc = settings->bools.menu_show_eject_disc;
#endif
bool menu_show_shutdown = settings->bools.menu_show_shutdown;
bool menu_show_reboot = settings->bools.menu_show_reboot;
#if !defined(IOS)
bool menu_show_quit_retroarch = settings->bools.menu_show_quit_retroarch;
bool menu_show_restart_ra = settings->bools.menu_show_restart_retroarch;
#endif
bool menu_show_information = settings->bools.menu_show_information;
bool menu_show_help = settings->bools.menu_show_help;
bool kiosk_mode_enable = settings->bools.kiosk_mode_enable;
#ifdef HAVE_QT
bool desktop_menu_enable = settings->bools.desktop_menu_enable;
#endif
#if defined(HAVE_NETWORKING) && defined(HAVE_ONLINE_UPDATER)
bool menu_show_online_updater = settings->bools.menu_show_online_updater;
#endif
bool menu_content_show_settings = settings->bools.menu_content_show_settings;
const char *menu_content_show_settings_password =
settings->paths.menu_content_show_settings_password;
const char *kiosk_mode_password = settings->paths.kiosk_mode_password;
switch (type)
{
case DISPLAYLIST_LOAD_CONTENT_LIST:
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES),
msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES),
MENU_ENUM_LABEL_FAVORITES,
MENU_SETTING_ACTION_FAVORITES_DIR, 0, 0);
core_info_get_list(&list);
if (list->info_count > 0)
menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DOWNLOADED_FILE_DETECT_CORE_LIST),
msg_hash_to_str(MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST),
MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST,
MENU_SETTING_ACTION, 0, 0);
if (menu_content_show_pl)
menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLISTS_TAB),
msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB),
MENU_ENUM_LABEL_PLAYLISTS_TAB,
MENU_SETTING_ACTION, 0, 0);
if (frontend_driver_parse_drive_list(info->list, true) != 0)
menu_entries_append_enum(info->list, "/",
msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR,
MENU_SETTING_ACTION, 0, 0);
if (!kiosk_mode_enable)
menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MENU_FILE_BROWSER_SETTINGS),
msg_hash_to_str(MENU_ENUM_LABEL_MENU_FILE_BROWSER_SETTINGS),
MENU_ENUM_LABEL_MENU_FILE_BROWSER_SETTINGS,
MENU_SETTING_ACTION, 0, 0);
info->need_push = true;
info->need_refresh = true;
ret = 0;
break;
case DISPLAYLIST_MAIN_MENU:
{
rarch_system_info_t *system = &runloop_state_get_ptr()->system;
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
if (retroarch_ctl(RARCH_CTL_CORE_IS_RUNNING, NULL))
{
if (!retroarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL))
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_CONTENT_SETTINGS,
PARSE_ACTION,
false);
}
}
else
{
if (system->load_no_content)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_START_CORE,
PARSE_ACTION,
false);
}
#ifndef HAVE_DYNAMIC
if (frontend_driver_has_fork())
#endif
{
if (menu_show_load_core)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_CORE_LIST,
PARSE_ACTION,
false);
}
}
}
if (menu_show_load_content)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_LOAD_CONTENT_LIST,
PARSE_ACTION,
false);
if (menu_displaylist_has_subsystems())
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_SUBSYSTEM_SETTINGS,
PARSE_ACTION,
false);
}
}
if (menu_show_load_disc)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_LOAD_DISC,
PARSE_ACTION,
false);
}
if (menu_show_dump_disc)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_DUMP_DISC,
PARSE_ACTION,
false);
}
#ifdef HAVE_LAKKA
if (menu_show_eject_disc)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_EJECT_DISC,
PARSE_ACTION,
false);
}
#endif
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_ADD_CONTENT_LIST,
PARSE_ACTION,
false);
#ifdef HAVE_QT
if (desktop_menu_enable)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_SHOW_WIMP,
PARSE_ACTION,
false);
}
#endif
#if defined(HAVE_NETWORKING)
#if defined(HAVE_ONLINE_UPDATER)
if (menu_show_online_updater && !kiosk_mode_enable)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_ONLINE_UPDATER,
PARSE_ACTION,
false);
}
#endif
#endif
if ( !menu_content_show_settings &&
!string_is_empty(menu_content_show_settings_password))
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_XMB_MAIN_MENU_ENABLE_SETTINGS,
PARSE_ACTION,
false);
}
if (kiosk_mode_enable && !string_is_empty(kiosk_mode_password))
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_MENU_DISABLE_KIOSK_MODE,
PARSE_ACTION,
false);
}
if (menu_show_information)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_INFORMATION_LIST,
PARSE_ACTION,
false);
}
#if defined(HAVE_LAKKA_SWITCH) || defined(HAVE_LIBNX)
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_SWITCH_CPU_PROFILE,
PARSE_ACTION,
false);
#endif
#ifdef HAVE_LAKKA_SWITCH
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_SWITCH_GPU_PROFILE,
PARSE_ACTION,
false);
#endif
if (menu_show_configurations && !kiosk_mode_enable)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_CONFIGURATIONS_LIST,
PARSE_ACTION,
false);
}
if (menu_show_help)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_HELP_LIST,
PARSE_ACTION,
false);
}
#if !defined(IOS)
if (menu_show_restart_ra)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_RESTART_RETROARCH,
PARSE_ACTION,
false);
}
if (menu_show_quit_retroarch)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_QUIT_RETROARCH,
PARSE_ACTION,
false);
}
#endif
if (menu_show_reboot)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_REBOOT,
PARSE_ACTION,
false);
}
if (menu_show_shutdown)
{
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
info->list,
MENU_ENUM_LABEL_SHUTDOWN,
PARSE_ACTION,
false);
}
info->need_push = true;
ret = 0;
}
break;
}
return ret;
}
static bool xmb_menu_init_list(void *data)
{
menu_displaylist_info_t info;
settings_t *settings = config_get_ptr();
file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0);
file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
menu_displaylist_info_init(&info);
info.label = strdup(
msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU));
info.exts = strdup("lpl");
info.type_default = FILE_TYPE_PLAIN;
info.enum_idx = MENU_ENUM_LABEL_MAIN_MENU;
menu_entries_append_enum(menu_stack, info.path,
info.label,
MENU_ENUM_LABEL_MAIN_MENU,
info.type, info.flags, 0);
info.list = selection_buf;
if (!menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info, settings))
goto error;
info.need_push = true;
if (!menu_displaylist_process(&info))
goto error;
menu_displaylist_info_free(&info);
return true;
error:
menu_displaylist_info_free(&info);
return false;
}
static int xmb_pointer_up(void *userdata,
unsigned x, unsigned y, unsigned ptr,
enum menu_input_pointer_gesture gesture,
menu_file_list_cbs_t *cbs,
menu_entry_t *entry, unsigned action)
{
unsigned width;
unsigned height;
int16_t margin_top;
int16_t margin_left;
int16_t margin_right;
xmb_handle_t *xmb = (xmb_handle_t*)userdata;
size_t selection = menu_navigation_get_selection();
unsigned end = (unsigned)menu_entries_get_size();
if (!xmb)
return -1;
/* If fullscreen thumbnail view is enabled,
* all input will disable it and otherwise
* be ignored */
if (xmb->show_fullscreen_thumbnails)
{
xmb_hide_fullscreen_thumbnails(xmb, true);
return 0;
}
video_driver_get_size(&width, &height);
margin_top = (int16_t)xmb->margins_screen_top;
margin_left = (int16_t)xmb->margins_screen_left;
margin_right = (int16_t)((float)width - xmb->margins_screen_left);
switch (gesture)
{
case MENU_INPUT_GESTURE_TAP:
case MENU_INPUT_GESTURE_SHORT_PRESS:
/* - A touch in the left margin:
* > ...triggers a 'cancel' action beneath the top margin
* > ...triggers a 'search' action above the top margin
* - A touch in the right margin triggers a 'select' action
* for the current item
* - Between the left/right margins input is handled normally */
if (x < margin_left)
{
if (y >= margin_top)
return xmb_menu_entry_action(xmb,
entry, selection, MENU_ACTION_CANCEL);
return menu_input_dialog_start_search() ? 0 : -1;
}
else if (x > margin_right)
return xmb_menu_entry_action(xmb,
entry, selection, MENU_ACTION_SELECT);
else if (ptr <= (end - 1))
{
/* If pointer item is already 'active', perform 'select' action */
if (ptr == selection)
return xmb_menu_entry_action(xmb,
entry, selection, MENU_ACTION_SELECT);
/* ...otherwise navigate to the current pointer item */
menu_navigation_set_selection(ptr);
xmb_navigation_set(xmb, false);
}
break;
case MENU_INPUT_GESTURE_LONG_PRESS:
/* 'Reset to default' action */
if ((ptr <= end - 1) && (ptr == selection))
return xmb_menu_entry_action(xmb,
entry, selection, MENU_ACTION_START);
break;
case MENU_INPUT_GESTURE_SWIPE_LEFT:
/* Navigate left
* Note: At the top level, navigating left
* means switching to the 'next' horizontal list,
* which is actually a movement to the *right* */
if (y > margin_top)
xmb_menu_entry_action(xmb,
entry, selection,
(xmb->depth == 1) ? MENU_ACTION_RIGHT : MENU_ACTION_LEFT);
break;
case MENU_INPUT_GESTURE_SWIPE_RIGHT:
/* Navigate right
* Note: At the top level, navigating right
* means switching to the 'previous' horizontal list,
* which is actually a movement to the *left* */
if (y > margin_top)
xmb_menu_entry_action(xmb,
entry, selection,
(xmb->depth == 1) ? MENU_ACTION_LEFT : MENU_ACTION_RIGHT);
break;
case MENU_INPUT_GESTURE_SWIPE_UP:
/* Swipe up in left margin: ascend alphabet */
if (x < margin_left)
xmb_menu_entry_action(xmb,
entry, selection, MENU_ACTION_SCROLL_DOWN);
else if (x < margin_right)
{
/* Swipe up between left and right margins:
* move selection pointer down by 1 'page' */
unsigned first = 0;
unsigned last = end;
if (height)
xmb_calculate_visible_range(xmb, height,
end, (unsigned)selection, &first, &last);
if (last < end)
{
menu_navigation_set_selection((size_t)last);
xmb_navigation_set(xmb, true);
}
else
menu_driver_ctl(MENU_NAVIGATION_CTL_SET_LAST, NULL);
}
break;
case MENU_INPUT_GESTURE_SWIPE_DOWN:
/* Swipe down in left margin: descend alphabet */
if (x < margin_left)
xmb_menu_entry_action(xmb,
entry, selection, MENU_ACTION_SCROLL_UP);
else if (x < margin_right)
{
/* Swipe down between left and right margins:
* move selection pointer up by 1 'page' */
unsigned bottom_idx = (unsigned)selection + 1;
size_t new_idx = 0;
unsigned step = 0;
/* Determine index of entry at bottom of screen
* Note: cannot use xmb_calculate_visible_range()
* here because there may not be sufficient entries
* to reach the bottom of the screen - i.e. we just
* want an index offset to subtract from the current
* selection... */
for (;;)
{
float top = xmb_item_y(xmb, bottom_idx, selection)
+ xmb->margins_screen_top;
if (top > height)
{
/* Since this checks the top position, the
* final index is always 1 greater than it
* should be... */
bottom_idx--;
break;
}
bottom_idx++;
}
if (bottom_idx >= selection)
step = bottom_idx - selection;
if (selection > step)
new_idx = selection - step;
if (new_idx > 0)
{
menu_navigation_set_selection(new_idx);
xmb_navigation_set(xmb, true);
}
else
{
bool pending_push = false;
menu_driver_ctl(MENU_NAVIGATION_CTL_CLEAR, &pending_push);
}
}
break;
default:
/* Ignore input */
break;
}
return 0;
}
menu_ctx_driver_t menu_ctx_xmb = {
NULL,
xmb_messagebox,
xmb_render,
xmb_frame,
xmb_init,
xmb_free,
xmb_context_reset,
xmb_context_destroy,
xmb_populate_entries,
xmb_toggle,
xmb_navigation_clear,
NULL, /*xmb_navigation_pointer_changed,*/ /* Note: navigation_set() is called each time navigation_increment/decrement() */
NULL, /*xmb_navigation_pointer_changed,*/ /* is called, so linking these just duplicates work... */
xmb_navigation_set,
xmb_navigation_pointer_changed,
xmb_navigation_alphabet,
xmb_navigation_alphabet,
xmb_menu_init_list,
xmb_list_insert,
NULL,
xmb_list_free,
xmb_list_clear,
xmb_list_cache,
xmb_list_push,
xmb_list_get_selection,
xmb_list_get_size,
xmb_list_get_entry,
NULL,
xmb_list_bind_init,
xmb_load_image,
"xmb",
xmb_environ,
NULL,
xmb_update_thumbnail_image,
xmb_refresh_thumbnail_image,
xmb_set_thumbnail_system,
xmb_get_thumbnail_system,
xmb_set_thumbnail_content,
gfx_display_osk_ptr_at_pos,
xmb_update_savestate_thumbnail_path,
xmb_update_savestate_thumbnail_image,
NULL, /* pointer_down */
xmb_pointer_up,
xmb_menu_entry_action
};