mirror of
https://github.com/libretro/RetroArch
synced 2025-01-31 06:32:48 +00:00
Move more code over
This commit is contained in:
parent
fc4081cd8f
commit
5677799faf
@ -22,6 +22,7 @@
|
||||
|
||||
#include <retro_timers.h>
|
||||
#include "menu_driver.h"
|
||||
#include "menu_cbs.h"
|
||||
|
||||
#ifdef HAVE_LANGEXTRA
|
||||
/* This file has a UTF8 BOM, we assume HAVE_LANGEXTRA
|
||||
@ -1147,6 +1148,489 @@ void menu_entries_settings_deinit(struct menu_state *menu_st)
|
||||
menu_st->entries.list_settings = NULL;
|
||||
}
|
||||
|
||||
static bool menu_driver_displaylist_push_internal(
|
||||
const char *label,
|
||||
menu_displaylist_info_t *info,
|
||||
settings_t *settings)
|
||||
{
|
||||
if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, info, settings))
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES_TAB)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_FAVORITES, info, settings))
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS_TAB)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_SETTINGS_ALL, info, settings))
|
||||
return true;
|
||||
}
|
||||
#ifdef HAVE_CHEATS
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CHEAT_SEARCH_SETTINGS)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_CHEAT_SEARCH_SETTINGS_LIST, info, settings))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB)))
|
||||
{
|
||||
filebrowser_clear_type();
|
||||
info->type = 42;
|
||||
|
||||
if (!string_is_empty(info->exts))
|
||||
free(info->exts);
|
||||
if (!string_is_empty(info->label))
|
||||
free(info->label);
|
||||
|
||||
info->exts = strdup("lpl");
|
||||
info->label = strdup(
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
|
||||
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
menu_displaylist_ctl(DISPLAYLIST_MUSIC_HISTORY, info, settings);
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_TAB)))
|
||||
{
|
||||
filebrowser_clear_type();
|
||||
info->type = 42;
|
||||
|
||||
if (!string_is_empty(info->exts))
|
||||
free(info->exts);
|
||||
if (!string_is_empty(info->label))
|
||||
free(info->label);
|
||||
|
||||
info->exts = strdup("lpl");
|
||||
info->label = strdup(
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
|
||||
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
menu_displaylist_ctl(DISPLAYLIST_VIDEO_HISTORY, info, settings);
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB)))
|
||||
{
|
||||
filebrowser_clear_type();
|
||||
info->type = 42;
|
||||
|
||||
if (!string_is_empty(info->exts))
|
||||
free(info->exts);
|
||||
if (!string_is_empty(info->label))
|
||||
free(info->label);
|
||||
|
||||
info->exts = strdup("lpl");
|
||||
info->label = strdup(
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
|
||||
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
|
||||
#if 0
|
||||
#ifdef HAVE_SCREENSHOTS
|
||||
if (!rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL))
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_TAKE_SCREENSHOT),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_TAKE_SCREENSHOT),
|
||||
MENU_ENUM_LABEL_TAKE_SCREENSHOT,
|
||||
MENU_SETTING_ACTION_SCREENSHOT, 0, 0);
|
||||
else
|
||||
info->need_push_no_playlist_entries = true;
|
||||
#endif
|
||||
#endif
|
||||
menu_displaylist_ctl(DISPLAYLIST_IMAGES_HISTORY, info, settings);
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB)))
|
||||
{
|
||||
const char *dir_playlist = settings->paths.directory_playlist;
|
||||
|
||||
filebrowser_clear_type();
|
||||
info->type = 42;
|
||||
|
||||
if (!string_is_empty(info->exts))
|
||||
free(info->exts);
|
||||
if (!string_is_empty(info->label))
|
||||
free(info->label);
|
||||
|
||||
info->exts = strdup("lpl");
|
||||
info->label = strdup(
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
|
||||
|
||||
if (string_is_empty(dir_playlist))
|
||||
{
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
info->need_refresh = true;
|
||||
info->need_push_no_playlist_entries = true;
|
||||
info->need_push = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!string_is_empty(info->path))
|
||||
free(info->path);
|
||||
|
||||
info->path = strdup(dir_playlist);
|
||||
|
||||
if (menu_displaylist_ctl(
|
||||
DISPLAYLIST_DATABASE_PLAYLISTS, info, settings))
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_SCAN_DIRECTORY_LIST, info, settings))
|
||||
return true;
|
||||
}
|
||||
#if defined(HAVE_LIBRETRODB)
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_EXPLORE, info, settings))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_NETPLAY_ROOM_LIST, info, settings))
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_HORIZONTAL, info, settings))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool menu_driver_displaylist_push(
|
||||
struct menu_state *menu_st,
|
||||
settings_t *settings,
|
||||
file_list_t *entry_list,
|
||||
file_list_t *entry_stack)
|
||||
{
|
||||
menu_displaylist_info_t info;
|
||||
const char *path = NULL;
|
||||
const char *label = NULL;
|
||||
unsigned type = 0;
|
||||
bool ret = false;
|
||||
enum msg_hash_enums enum_idx = MSG_UNKNOWN;
|
||||
file_list_t *list = MENU_LIST_GET(menu_st->entries.list, 0);
|
||||
menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)
|
||||
list->list[list->size - 1].actiondata;
|
||||
|
||||
menu_displaylist_info_init(&info);
|
||||
|
||||
if (list && list->size)
|
||||
file_list_get_at_offset(list, list->size - 1, &path, &label, &type, NULL);
|
||||
|
||||
if (cbs)
|
||||
enum_idx = cbs->enum_idx;
|
||||
|
||||
info.list = entry_list;
|
||||
info.menu_list = entry_stack;
|
||||
info.type = type;
|
||||
info.enum_idx = enum_idx;
|
||||
|
||||
if (!string_is_empty(path))
|
||||
info.path = strdup(path);
|
||||
|
||||
if (!string_is_empty(label))
|
||||
info.label = strdup(label);
|
||||
|
||||
if (!info.list)
|
||||
goto error;
|
||||
|
||||
if (menu_driver_displaylist_push_internal(label, &info, settings))
|
||||
{
|
||||
ret = menu_displaylist_process(&info);
|
||||
goto end;
|
||||
}
|
||||
|
||||
cbs = (menu_file_list_cbs_t*)list->list[list->size - 1].actiondata;
|
||||
|
||||
if (cbs && cbs->action_deferred_push)
|
||||
if (cbs->action_deferred_push(&info) != 0)
|
||||
goto error;
|
||||
|
||||
ret = true;
|
||||
|
||||
end:
|
||||
menu_displaylist_info_free(&info);
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
menu_displaylist_info_free(&info);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void menu_input_key_bind_poll_bind_state_internal(
|
||||
const input_device_driver_t *joypad,
|
||||
struct menu_bind_state *state,
|
||||
unsigned port,
|
||||
bool timed_out)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
/* poll only the relevant port */
|
||||
for (i = 0; i < MENU_MAX_BUTTONS; i++)
|
||||
state->state[port].buttons[i] = joypad->button(port, i);
|
||||
|
||||
for (i = 0; i < MENU_MAX_AXES; i++)
|
||||
{
|
||||
if (AXIS_POS(i) != AXIS_NONE)
|
||||
state->state[port].axes[i] = joypad->axis(port, AXIS_POS(i));
|
||||
|
||||
if (AXIS_NEG(i) != AXIS_NONE)
|
||||
state->state[port].axes[i] += joypad->axis(port, AXIS_NEG(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < MENU_MAX_HATS; i++)
|
||||
{
|
||||
if (joypad->button(port, HAT_MAP(i, HAT_UP_MASK)))
|
||||
state->state[port].hats[i] |= HAT_UP_MASK;
|
||||
if (joypad->button(port, HAT_MAP(i, HAT_DOWN_MASK)))
|
||||
state->state[port].hats[i] |= HAT_DOWN_MASK;
|
||||
if (joypad->button(port, HAT_MAP(i, HAT_LEFT_MASK)))
|
||||
state->state[port].hats[i] |= HAT_LEFT_MASK;
|
||||
if (joypad->button(port, HAT_MAP(i, HAT_RIGHT_MASK)))
|
||||
state->state[port].hats[i] |= HAT_RIGHT_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
/* This sets up all the callback functions for a menu entry.
|
||||
*
|
||||
* OK : When we press the 'OK' button on an entry.
|
||||
* Cancel : When we press the 'Cancel' button on an entry.
|
||||
* Scan : When we press the 'Scan' button on an entry.
|
||||
* Start : When we press the 'Start' button on an entry.
|
||||
* Select : When we press the 'Select' button on an entry.
|
||||
* Info : When we press the 'Info' button on an entry.
|
||||
* Left : when we press 'Left' on the D-pad while this entry is selected.
|
||||
* Right : when we press 'Right' on the D-pad while this entry is selected.
|
||||
* Deferred push : When pressing an entry results in spawning a new list, it waits until the next
|
||||
* frame to push this onto the stack. This function callback will then be invoked.
|
||||
* Get value: Each entry has associated 'text', which we call the value. This function callback
|
||||
* lets us render that text.
|
||||
* Title: Each entry can have a custom 'title'.
|
||||
* Label: Each entry has a label name. This function callback lets us render that label text.
|
||||
* Sublabel: each entry has a sublabel, which consists of one or more lines of additional information.
|
||||
* This function callback lets us render that text.
|
||||
*/
|
||||
void menu_cbs_init(
|
||||
struct menu_state *menu_st,
|
||||
const menu_ctx_driver_t *menu_driver_ctx,
|
||||
file_list_t *list,
|
||||
menu_file_list_cbs_t *cbs,
|
||||
const char *path, const char *label,
|
||||
unsigned type, size_t idx)
|
||||
{
|
||||
const char *menu_label = NULL;
|
||||
file_list_t *menu_list = MENU_LIST_GET(menu_st->entries.list, 0);
|
||||
#ifdef DEBUG_LOG
|
||||
menu_file_list_cbs_t *menu_cbs = (menu_file_list_cbs_t*)
|
||||
menu_list->list[list->size - 1].actiondata;
|
||||
enum msg_hash_enums enum_idx = menu_cbs ? menu_cbs->enum_idx : MSG_UNKNOWN;
|
||||
#endif
|
||||
|
||||
if (menu_list && menu_list->size)
|
||||
file_list_get_at_offset(menu_list, menu_list->size - 1, NULL, &menu_label, NULL, NULL);
|
||||
|
||||
if (!label || !menu_label)
|
||||
return;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
RARCH_LOG("\n");
|
||||
|
||||
if (cbs && cbs->enum_idx != MSG_UNKNOWN)
|
||||
RARCH_LOG("\t\t\tenum_idx %d [%s]\n", cbs->enum_idx, msg_hash_to_str(cbs->enum_idx));
|
||||
#endif
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_ok.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_ok(cbs, path, label, type, idx, menu_label);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_cancel.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_cancel(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_scan.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_scan(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_start.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_start(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_select.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_select(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_info.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_info(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_left.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_left(cbs, path, label, type, idx, menu_label);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_right.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_right(cbs, path, label, type, idx, menu_label);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_deferred_push.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_deferred_push(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_get_string_representation.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_get_string_representation(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_title.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_title(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_label.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_label(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_sublabel.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_sublabel(cbs, path, label, type, idx);
|
||||
|
||||
if (menu_driver_ctx && menu_driver_ctx->bind_init)
|
||||
menu_driver_ctx->bind_init(
|
||||
cbs,
|
||||
path,
|
||||
label,
|
||||
type,
|
||||
idx);
|
||||
}
|
||||
|
||||
/* Pretty much a stub function. TODO/FIXME - Might as well remove this. */
|
||||
int menu_cbs_exit(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
enum action_iterate_type action_iterate_type(const char *label)
|
||||
{
|
||||
if (string_is_equal(label, "info_screen"))
|
||||
return ITERATE_TYPE_INFO;
|
||||
if (string_starts_with_size(label, "help", STRLEN_CONST("help")))
|
||||
if (
|
||||
string_is_equal(label, "help") ||
|
||||
string_is_equal(label, "help_controls") ||
|
||||
string_is_equal(label, "help_what_is_a_core") ||
|
||||
string_is_equal(label, "help_loading_content") ||
|
||||
string_is_equal(label, "help_scanning_content") ||
|
||||
string_is_equal(label, "help_change_virtual_gamepad") ||
|
||||
string_is_equal(label, "help_audio_video_troubleshooting") ||
|
||||
string_is_equal(label, "help_send_debug_info")
|
||||
)
|
||||
return ITERATE_TYPE_HELP;
|
||||
if (string_is_equal(label, "cheevos_description"))
|
||||
return ITERATE_TYPE_HELP;
|
||||
if (string_starts_with_size(label, "custom_bind", STRLEN_CONST("custom_bind")))
|
||||
if (
|
||||
string_is_equal(label, "custom_bind") ||
|
||||
string_is_equal(label, "custom_bind_all") ||
|
||||
string_is_equal(label, "custom_bind_defaults")
|
||||
)
|
||||
return ITERATE_TYPE_BIND;
|
||||
|
||||
return ITERATE_TYPE_DEFAULT;
|
||||
}
|
||||
|
||||
void menu_input_key_bind_poll_bind_state(
|
||||
input_driver_state_t *input_driver_st,
|
||||
const struct retro_keybind **binds,
|
||||
float input_axis_threshold,
|
||||
unsigned joy_idx,
|
||||
struct menu_bind_state *state,
|
||||
bool timed_out,
|
||||
bool keyboard_mapping_blocked)
|
||||
{
|
||||
unsigned b;
|
||||
rarch_joypad_info_t joypad_info;
|
||||
input_driver_t *current_input = input_driver_st->current_driver;
|
||||
void *input_data = input_driver_st->current_data;
|
||||
unsigned port = state->port;
|
||||
const input_device_driver_t *joypad = input_driver_st->primary_joypad;
|
||||
#ifdef HAVE_MFI
|
||||
const input_device_driver_t *sec_joypad = input_driver_st->secondary_joypad;
|
||||
#else
|
||||
const input_device_driver_t *sec_joypad = NULL;
|
||||
#endif
|
||||
|
||||
memset(state->state, 0, sizeof(state->state));
|
||||
|
||||
joypad_info.axis_threshold = input_axis_threshold;
|
||||
joypad_info.joy_idx = joy_idx;
|
||||
joypad_info.auto_binds = input_autoconf_binds[joy_idx];
|
||||
|
||||
if (current_input->input_state)
|
||||
{
|
||||
/* Poll mouse (on the relevant port)
|
||||
*
|
||||
* Check if key was being pressed by
|
||||
* user with mouse number 'port'
|
||||
*
|
||||
* NOTE: We start iterating on 2 (RETRO_DEVICE_ID_MOUSE_LEFT),
|
||||
* because we want to skip the axes
|
||||
*/
|
||||
for (b = 2; b < MENU_MAX_MBUTTONS; b++)
|
||||
{
|
||||
state->state[port].mouse_buttons[b] =
|
||||
current_input->input_state(
|
||||
input_driver_st->current_data,
|
||||
joypad,
|
||||
sec_joypad,
|
||||
&joypad_info,
|
||||
binds,
|
||||
keyboard_mapping_blocked,
|
||||
port,
|
||||
RETRO_DEVICE_MOUSE, 0, b);
|
||||
}
|
||||
}
|
||||
|
||||
joypad_info.joy_idx = 0;
|
||||
joypad_info.auto_binds = NULL;
|
||||
joypad_info.axis_threshold = 0.0f;
|
||||
|
||||
state->skip = timed_out;
|
||||
if (current_input->input_state)
|
||||
state->skip |=
|
||||
current_input->input_state(
|
||||
input_data,
|
||||
joypad,
|
||||
sec_joypad,
|
||||
&joypad_info,
|
||||
NULL,
|
||||
keyboard_mapping_blocked,
|
||||
0,
|
||||
RETRO_DEVICE_KEYBOARD,
|
||||
0,
|
||||
RETROK_RETURN);
|
||||
|
||||
if (joypad)
|
||||
{
|
||||
if (joypad->poll)
|
||||
joypad->poll();
|
||||
menu_input_key_bind_poll_bind_state_internal(
|
||||
joypad, state, port, timed_out);
|
||||
}
|
||||
|
||||
if (sec_joypad)
|
||||
{
|
||||
if (sec_joypad->poll)
|
||||
sec_joypad->poll();
|
||||
menu_input_key_bind_poll_bind_state_internal(
|
||||
sec_joypad, state, port, timed_out);
|
||||
}
|
||||
}
|
||||
|
||||
int menu_dialog_iterate(
|
||||
menu_dialog_t *p_dialog,
|
||||
settings_t *settings,
|
||||
|
@ -769,6 +769,31 @@ float menu_input_get_dpi(
|
||||
|
||||
void menu_input_pointer_close_messagebox(struct menu_state *menu_st);
|
||||
|
||||
void menu_input_key_bind_poll_bind_state(
|
||||
input_driver_state_t *input_driver_st,
|
||||
const struct retro_keybind **binds,
|
||||
float input_axis_threshold,
|
||||
unsigned joy_idx,
|
||||
struct menu_bind_state *state,
|
||||
bool timed_out,
|
||||
bool keyboard_mapping_blocked);
|
||||
|
||||
enum action_iterate_type action_iterate_type(const char *label);
|
||||
|
||||
void menu_cbs_init(
|
||||
struct menu_state *menu_st,
|
||||
const menu_ctx_driver_t *menu_driver_ctx,
|
||||
file_list_t *list,
|
||||
menu_file_list_cbs_t *cbs,
|
||||
const char *path, const char *label,
|
||||
unsigned type, size_t idx);
|
||||
|
||||
bool menu_driver_displaylist_push(
|
||||
struct menu_state *menu_st,
|
||||
settings_t *settings,
|
||||
file_list_t *entry_list,
|
||||
file_list_t *entry_stack);
|
||||
|
||||
int generic_menu_entry_action(void *userdata, menu_entry_t *entry, size_t i, enum menu_action action);
|
||||
|
||||
extern menu_ctx_driver_t menu_ctx_ozone;
|
||||
|
496
retroarch.c
496
retroarch.c
@ -719,127 +719,6 @@ static bool menu_input_key_bind_custom_bind_keyboard_cb(
|
||||
return (binds->begin <= binds->last);
|
||||
}
|
||||
|
||||
static void menu_input_key_bind_poll_bind_state_internal(
|
||||
const input_device_driver_t *joypad,
|
||||
struct menu_bind_state *state,
|
||||
unsigned port,
|
||||
bool timed_out)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
/* poll only the relevant port */
|
||||
for (i = 0; i < MENU_MAX_BUTTONS; i++)
|
||||
state->state[port].buttons[i] = joypad->button(port, i);
|
||||
|
||||
for (i = 0; i < MENU_MAX_AXES; i++)
|
||||
{
|
||||
if (AXIS_POS(i) != AXIS_NONE)
|
||||
state->state[port].axes[i] = joypad->axis(port, AXIS_POS(i));
|
||||
|
||||
if (AXIS_NEG(i) != AXIS_NONE)
|
||||
state->state[port].axes[i] += joypad->axis(port, AXIS_NEG(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < MENU_MAX_HATS; i++)
|
||||
{
|
||||
if (joypad->button(port, HAT_MAP(i, HAT_UP_MASK)))
|
||||
state->state[port].hats[i] |= HAT_UP_MASK;
|
||||
if (joypad->button(port, HAT_MAP(i, HAT_DOWN_MASK)))
|
||||
state->state[port].hats[i] |= HAT_DOWN_MASK;
|
||||
if (joypad->button(port, HAT_MAP(i, HAT_LEFT_MASK)))
|
||||
state->state[port].hats[i] |= HAT_LEFT_MASK;
|
||||
if (joypad->button(port, HAT_MAP(i, HAT_RIGHT_MASK)))
|
||||
state->state[port].hats[i] |= HAT_RIGHT_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
static void menu_input_key_bind_poll_bind_state(
|
||||
struct rarch_state *p_rarch,
|
||||
float input_axis_threshold,
|
||||
unsigned joy_idx,
|
||||
struct menu_bind_state *state,
|
||||
bool timed_out)
|
||||
{
|
||||
unsigned b;
|
||||
rarch_joypad_info_t joypad_info;
|
||||
input_driver_state_t *input_driver_st = &p_rarch->input_driver_state;
|
||||
input_driver_t *current_input = input_driver_st->current_driver;
|
||||
void *input_data = input_driver_st->current_data;
|
||||
unsigned port = state->port;
|
||||
const input_device_driver_t *joypad = input_driver_st->primary_joypad;
|
||||
#ifdef HAVE_MFI
|
||||
const input_device_driver_t *sec_joypad = input_driver_st->secondary_joypad;
|
||||
#else
|
||||
const input_device_driver_t *sec_joypad = NULL;
|
||||
#endif
|
||||
|
||||
memset(state->state, 0, sizeof(state->state));
|
||||
|
||||
joypad_info.axis_threshold = input_axis_threshold;
|
||||
joypad_info.joy_idx = joy_idx;
|
||||
joypad_info.auto_binds = input_autoconf_binds[joy_idx];
|
||||
|
||||
if (current_input->input_state)
|
||||
{
|
||||
/* Poll mouse (on the relevant port)
|
||||
*
|
||||
* Check if key was being pressed by
|
||||
* user with mouse number 'port'
|
||||
*
|
||||
* NOTE: We start iterating on 2 (RETRO_DEVICE_ID_MOUSE_LEFT),
|
||||
* because we want to skip the axes
|
||||
*/
|
||||
for (b = 2; b < MENU_MAX_MBUTTONS; b++)
|
||||
{
|
||||
state->state[port].mouse_buttons[b] =
|
||||
current_input->input_state(
|
||||
input_driver_st->current_data,
|
||||
joypad,
|
||||
sec_joypad,
|
||||
&joypad_info,
|
||||
p_rarch->libretro_input_binds,
|
||||
p_rarch->keyboard_mapping_blocked,
|
||||
port,
|
||||
RETRO_DEVICE_MOUSE, 0, b);
|
||||
}
|
||||
}
|
||||
|
||||
joypad_info.joy_idx = 0;
|
||||
joypad_info.auto_binds = NULL;
|
||||
joypad_info.axis_threshold = 0.0f;
|
||||
|
||||
state->skip = timed_out;
|
||||
if (current_input->input_state)
|
||||
state->skip |=
|
||||
current_input->input_state(
|
||||
input_data,
|
||||
joypad,
|
||||
sec_joypad,
|
||||
&joypad_info,
|
||||
NULL,
|
||||
p_rarch->keyboard_mapping_blocked,
|
||||
0,
|
||||
RETRO_DEVICE_KEYBOARD,
|
||||
0,
|
||||
RETROK_RETURN);
|
||||
|
||||
if (joypad)
|
||||
{
|
||||
if (joypad->poll)
|
||||
joypad->poll();
|
||||
menu_input_key_bind_poll_bind_state_internal(
|
||||
joypad, state, port, timed_out);
|
||||
}
|
||||
|
||||
if (sec_joypad)
|
||||
{
|
||||
if (sec_joypad->poll)
|
||||
sec_joypad->poll();
|
||||
menu_input_key_bind_poll_bind_state_internal(
|
||||
sec_joypad, state, port, timed_out);
|
||||
}
|
||||
}
|
||||
|
||||
bool menu_input_key_bind_set_mode(
|
||||
enum menu_input_binds_ctl_state state, void *data)
|
||||
{
|
||||
@ -881,10 +760,13 @@ bool menu_input_key_bind_set_mode(
|
||||
joypad,
|
||||
sec_joypad,
|
||||
binds);
|
||||
menu_input_key_bind_poll_bind_state(p_rarch,
|
||||
menu_input_key_bind_poll_bind_state(
|
||||
&p_rarch->input_driver_state,
|
||||
p_rarch->libretro_input_binds,
|
||||
settings->floats.input_axis_threshold,
|
||||
settings->uints.input_joypad_index[binds->port],
|
||||
binds, false);
|
||||
binds, false,
|
||||
p_rarch->keyboard_mapping_blocked);
|
||||
|
||||
current_usec = cpu_features_get_time_usec();
|
||||
|
||||
@ -995,10 +877,13 @@ static bool menu_input_key_bind_iterate(
|
||||
|
||||
p_rarch->keyboard_mapping_blocked = false;
|
||||
|
||||
menu_input_key_bind_poll_bind_state(p_rarch,
|
||||
menu_input_key_bind_poll_bind_state(
|
||||
&p_rarch->input_driver_state,
|
||||
p_rarch->libretro_input_binds,
|
||||
settings->floats.input_axis_threshold,
|
||||
settings->uints.input_joypad_index[new_binds.port],
|
||||
&new_binds, timed_out);
|
||||
&new_binds, timed_out,
|
||||
p_rarch->keyboard_mapping_blocked);
|
||||
|
||||
#ifdef ANDROID
|
||||
/* Keep resetting bind during the hold period,
|
||||
@ -1095,150 +980,6 @@ static bool menu_input_key_bind_iterate(
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This sets up all the callback functions for a menu entry.
|
||||
*
|
||||
* OK : When we press the 'OK' button on an entry.
|
||||
* Cancel : When we press the 'Cancel' button on an entry.
|
||||
* Scan : When we press the 'Scan' button on an entry.
|
||||
* Start : When we press the 'Start' button on an entry.
|
||||
* Select : When we press the 'Select' button on an entry.
|
||||
* Info : When we press the 'Info' button on an entry.
|
||||
* Left : when we press 'Left' on the D-pad while this entry is selected.
|
||||
* Right : when we press 'Right' on the D-pad while this entry is selected.
|
||||
* Deferred push : When pressing an entry results in spawning a new list, it waits until the next
|
||||
* frame to push this onto the stack. This function callback will then be invoked.
|
||||
* Get value: Each entry has associated 'text', which we call the value. This function callback
|
||||
* lets us render that text.
|
||||
* Title: Each entry can have a custom 'title'.
|
||||
* Label: Each entry has a label name. This function callback lets us render that label text.
|
||||
* Sublabel: each entry has a sublabel, which consists of one or more lines of additional information.
|
||||
* This function callback lets us render that text.
|
||||
*/
|
||||
static void menu_cbs_init(
|
||||
struct menu_state *menu_st,
|
||||
const menu_ctx_driver_t *menu_driver_ctx,
|
||||
file_list_t *list,
|
||||
menu_file_list_cbs_t *cbs,
|
||||
const char *path, const char *label,
|
||||
unsigned type, size_t idx)
|
||||
{
|
||||
const char *menu_label = NULL;
|
||||
file_list_t *menu_list = MENU_LIST_GET(menu_st->entries.list, 0);
|
||||
#ifdef DEBUG_LOG
|
||||
menu_file_list_cbs_t *menu_cbs = (menu_file_list_cbs_t*)
|
||||
menu_list->list[list->size - 1].actiondata;
|
||||
enum msg_hash_enums enum_idx = menu_cbs ? menu_cbs->enum_idx : MSG_UNKNOWN;
|
||||
#endif
|
||||
|
||||
if (menu_list && menu_list->size)
|
||||
file_list_get_at_offset(menu_list, menu_list->size - 1, NULL, &menu_label, NULL, NULL);
|
||||
|
||||
if (!label || !menu_label)
|
||||
return;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
RARCH_LOG("\n");
|
||||
|
||||
if (cbs && cbs->enum_idx != MSG_UNKNOWN)
|
||||
RARCH_LOG("\t\t\tenum_idx %d [%s]\n", cbs->enum_idx, msg_hash_to_str(cbs->enum_idx));
|
||||
#endif
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_ok.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_ok(cbs, path, label, type, idx, menu_label);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_cancel.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_cancel(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_scan.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_scan(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_start.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_start(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_select.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_select(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_info.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_info(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_left.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_left(cbs, path, label, type, idx, menu_label);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_right.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_right(cbs, path, label, type, idx, menu_label);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_deferred_push.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_deferred_push(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_get_string_representation.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_get_string_representation(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_title.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_title(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_label.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_label(cbs, path, label, type, idx);
|
||||
|
||||
/* It will try to find a corresponding callback function inside
|
||||
* menu_cbs_sublabel.c, then map this callback to the entry. */
|
||||
menu_cbs_init_bind_sublabel(cbs, path, label, type, idx);
|
||||
|
||||
if (menu_driver_ctx && menu_driver_ctx->bind_init)
|
||||
menu_driver_ctx->bind_init(
|
||||
cbs,
|
||||
path,
|
||||
label,
|
||||
type,
|
||||
idx);
|
||||
}
|
||||
|
||||
/* Pretty much a stub function. TODO/FIXME - Might as well remove this. */
|
||||
int menu_cbs_exit(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static enum action_iterate_type action_iterate_type(const char *label)
|
||||
{
|
||||
if (string_is_equal(label, "info_screen"))
|
||||
return ITERATE_TYPE_INFO;
|
||||
if (string_starts_with_size(label, "help", STRLEN_CONST("help")))
|
||||
if (
|
||||
string_is_equal(label, "help") ||
|
||||
string_is_equal(label, "help_controls") ||
|
||||
string_is_equal(label, "help_what_is_a_core") ||
|
||||
string_is_equal(label, "help_loading_content") ||
|
||||
string_is_equal(label, "help_scanning_content") ||
|
||||
string_is_equal(label, "help_change_virtual_gamepad") ||
|
||||
string_is_equal(label, "help_audio_video_troubleshooting") ||
|
||||
string_is_equal(label, "help_send_debug_info")
|
||||
)
|
||||
return ITERATE_TYPE_HELP;
|
||||
if (string_is_equal(label, "cheevos_description"))
|
||||
return ITERATE_TYPE_HELP;
|
||||
if (string_starts_with_size(label, "custom_bind", STRLEN_CONST("custom_bind")))
|
||||
if (
|
||||
string_is_equal(label, "custom_bind") ||
|
||||
string_is_equal(label, "custom_bind_all") ||
|
||||
string_is_equal(label, "custom_bind_defaults")
|
||||
)
|
||||
return ITERATE_TYPE_BIND;
|
||||
|
||||
return ITERATE_TYPE_DEFAULT;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ACCESSIBILITY
|
||||
static void get_current_menu_value(struct menu_state *menu_st,
|
||||
char *s, size_t len)
|
||||
@ -1722,223 +1463,6 @@ static int generic_menu_iterate(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool menu_driver_displaylist_push_internal(
|
||||
const char *label,
|
||||
menu_displaylist_info_t *info,
|
||||
settings_t *settings)
|
||||
{
|
||||
if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HISTORY_TAB)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_HISTORY, info, settings))
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES_TAB)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_FAVORITES, info, settings))
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS_TAB)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_SETTINGS_ALL, info, settings))
|
||||
return true;
|
||||
}
|
||||
#ifdef HAVE_CHEATS
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CHEAT_SEARCH_SETTINGS)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_CHEAT_SEARCH_SETTINGS_LIST, info, settings))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_MUSIC_TAB)))
|
||||
{
|
||||
filebrowser_clear_type();
|
||||
info->type = 42;
|
||||
|
||||
if (!string_is_empty(info->exts))
|
||||
free(info->exts);
|
||||
if (!string_is_empty(info->label))
|
||||
free(info->label);
|
||||
|
||||
info->exts = strdup("lpl");
|
||||
info->label = strdup(
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
|
||||
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
menu_displaylist_ctl(DISPLAYLIST_MUSIC_HISTORY, info, settings);
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_TAB)))
|
||||
{
|
||||
filebrowser_clear_type();
|
||||
info->type = 42;
|
||||
|
||||
if (!string_is_empty(info->exts))
|
||||
free(info->exts);
|
||||
if (!string_is_empty(info->label))
|
||||
free(info->label);
|
||||
|
||||
info->exts = strdup("lpl");
|
||||
info->label = strdup(
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
|
||||
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
menu_displaylist_ctl(DISPLAYLIST_VIDEO_HISTORY, info, settings);
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_IMAGES_TAB)))
|
||||
{
|
||||
filebrowser_clear_type();
|
||||
info->type = 42;
|
||||
|
||||
if (!string_is_empty(info->exts))
|
||||
free(info->exts);
|
||||
if (!string_is_empty(info->label))
|
||||
free(info->label);
|
||||
|
||||
info->exts = strdup("lpl");
|
||||
info->label = strdup(
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
|
||||
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
|
||||
#if 0
|
||||
#ifdef HAVE_SCREENSHOTS
|
||||
if (!rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL))
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_TAKE_SCREENSHOT),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_TAKE_SCREENSHOT),
|
||||
MENU_ENUM_LABEL_TAKE_SCREENSHOT,
|
||||
MENU_SETTING_ACTION_SCREENSHOT, 0, 0);
|
||||
else
|
||||
info->need_push_no_playlist_entries = true;
|
||||
#endif
|
||||
#endif
|
||||
menu_displaylist_ctl(DISPLAYLIST_IMAGES_HISTORY, info, settings);
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB)))
|
||||
{
|
||||
const char *dir_playlist = settings->paths.directory_playlist;
|
||||
|
||||
filebrowser_clear_type();
|
||||
info->type = 42;
|
||||
|
||||
if (!string_is_empty(info->exts))
|
||||
free(info->exts);
|
||||
if (!string_is_empty(info->label))
|
||||
free(info->label);
|
||||
|
||||
info->exts = strdup("lpl");
|
||||
info->label = strdup(
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
|
||||
|
||||
if (string_is_empty(dir_playlist))
|
||||
{
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
info->need_refresh = true;
|
||||
info->need_push_no_playlist_entries = true;
|
||||
info->need_push = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!string_is_empty(info->path))
|
||||
free(info->path);
|
||||
|
||||
info->path = strdup(dir_playlist);
|
||||
|
||||
if (menu_displaylist_ctl(
|
||||
DISPLAYLIST_DATABASE_PLAYLISTS, info, settings))
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_ADD_TAB)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_SCAN_DIRECTORY_LIST, info, settings))
|
||||
return true;
|
||||
}
|
||||
#if defined(HAVE_LIBRETRODB)
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_EXPLORE, info, settings))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_NETPLAY_ROOM_LIST, info, settings))
|
||||
return true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU)))
|
||||
{
|
||||
if (menu_displaylist_ctl(DISPLAYLIST_HORIZONTAL, info, settings))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool menu_driver_displaylist_push(
|
||||
struct menu_state *menu_st,
|
||||
settings_t *settings,
|
||||
file_list_t *entry_list,
|
||||
file_list_t *entry_stack)
|
||||
{
|
||||
menu_displaylist_info_t info;
|
||||
const char *path = NULL;
|
||||
const char *label = NULL;
|
||||
unsigned type = 0;
|
||||
bool ret = false;
|
||||
enum msg_hash_enums enum_idx = MSG_UNKNOWN;
|
||||
file_list_t *list = MENU_LIST_GET(menu_st->entries.list, 0);
|
||||
menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)
|
||||
list->list[list->size - 1].actiondata;
|
||||
|
||||
menu_displaylist_info_init(&info);
|
||||
|
||||
if (list && list->size)
|
||||
file_list_get_at_offset(list, list->size - 1, &path, &label, &type, NULL);
|
||||
|
||||
if (cbs)
|
||||
enum_idx = cbs->enum_idx;
|
||||
|
||||
info.list = entry_list;
|
||||
info.menu_list = entry_stack;
|
||||
info.type = type;
|
||||
info.enum_idx = enum_idx;
|
||||
|
||||
if (!string_is_empty(path))
|
||||
info.path = strdup(path);
|
||||
|
||||
if (!string_is_empty(label))
|
||||
info.label = strdup(label);
|
||||
|
||||
if (!info.list)
|
||||
goto error;
|
||||
|
||||
if (menu_driver_displaylist_push_internal(label, &info, settings))
|
||||
{
|
||||
ret = menu_displaylist_process(&info);
|
||||
goto end;
|
||||
}
|
||||
|
||||
cbs = (menu_file_list_cbs_t*)list->list[list->size - 1].actiondata;
|
||||
|
||||
if (cbs && cbs->action_deferred_push)
|
||||
if (cbs->action_deferred_push(&info) != 0)
|
||||
goto error;
|
||||
|
||||
ret = true;
|
||||
|
||||
end:
|
||||
menu_displaylist_info_free(&info);
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
menu_displaylist_info_free(&info);
|
||||
return false;
|
||||
}
|
||||
|
||||
int generic_menu_entry_action(
|
||||
void *userdata, menu_entry_t *entry, size_t i, enum menu_action action)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user