(Menu Input) Add mouse/touchscreen gesture support + full gesture support for XMB

This commit is contained in:
jdgleaver 2019-09-30 16:54:19 +01:00
parent 0a123e8215
commit 3057c31798
12 changed files with 816 additions and 200 deletions

View File

@ -554,6 +554,8 @@ bool x11_alive(void *data)
case 4: /* Grabbed */
/* Scroll up */
case 5: /* Scroll down */
case 6: /* Scroll wheel left */
case 7: /* Scroll wheel right */
x_input_poll_wheel(&event.xbutton, true);
break;
}

View File

@ -20,6 +20,8 @@
static bool x11_mouse_wu;
static bool x11_mouse_wd;
static bool x11_mouse_hwu;
static bool x11_mouse_hwd;
int16_t x_mouse_state_wheel(unsigned id)
{
@ -35,6 +37,14 @@ int16_t x_mouse_state_wheel(unsigned id)
ret = x11_mouse_wd;
x11_mouse_wd = 0;
return ret;
case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP:
ret = x11_mouse_hwu;
x11_mouse_hwu = 0;
return ret;
case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN:
ret = x11_mouse_hwd;
x11_mouse_hwd = 0;
return ret;
}
return 0;
@ -50,5 +60,13 @@ void x_input_poll_wheel(XButtonEvent *event, bool latch)
case 5:
x11_mouse_wd = 1;
break;
case 6:
/* Scroll wheel left == HORIZ_WHEELDOWN */
x11_mouse_hwd = 1;
break;
case 7:
/* Scroll wheel right == HORIZ_WHEELUP */
x11_mouse_hwu = 1;
break;
}
}

View File

@ -106,18 +106,9 @@ static bool x_mouse_button_pressed(
case RETRO_DEVICE_ID_MOUSE_WHEELUP:
case RETRO_DEVICE_ID_MOUSE_WHEELDOWN:
return x_mouse_state_wheel( key );
/* case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP:
result = x11->mouse_hwu;
x11->mouse_hwu = false;
return result;
case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP:
case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN:
result = x11->mouse_hwd;
x11->mouse_hwd = false;
return result;
*/
return x_mouse_state_wheel( key );
}
return false;
@ -237,6 +228,8 @@ static int16_t x_mouse_state(x11_input_t *x11, unsigned id)
return x11->mouse_r;
case RETRO_DEVICE_ID_MOUSE_WHEELUP:
case RETRO_DEVICE_ID_MOUSE_WHEELDOWN:
case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP:
case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN:
return x_mouse_state_wheel(id);
case RETRO_DEVICE_ID_MOUSE_MIDDLE:
return x11->mouse_m;

View File

@ -2348,14 +2348,16 @@ static int materialui_pointer_down(void *userdata,
If we clicked on the tabs, we switch to a new list.
If we clicked on a menu entry, we call the entry action callback. */
static int materialui_pointer_up(void *userdata,
unsigned x, unsigned y,
unsigned ptr, menu_file_list_cbs_t *cbs,
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, height;
unsigned header_height, i;
size_t entries_end = menu_entries_get_size();
materialui_handle_t *mui = (materialui_handle_t*)userdata;
size_t selection = menu_navigation_get_selection();
size_t entries_end = menu_entries_get_size();
materialui_handle_t *mui = (materialui_handle_t*)userdata;
if (!mui)
return 0;
@ -2363,51 +2365,69 @@ static int materialui_pointer_up(void *userdata,
header_height = menu_display_get_header_height();
video_driver_get_size(&width, &height);
if (y < header_height)
switch (gesture)
{
size_t selection = menu_navigation_get_selection();
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL);
}
else if (y > height - mui->tabs_height)
{
file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0);
file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
for (i = 0; i <= MUI_SYSTEM_TAB_END; i++)
{
unsigned tab_width = width / (MUI_SYSTEM_TAB_END + 1);
unsigned start = tab_width * i;
if ((x >= start) && (x < (start + tab_width)))
case MENU_INPUT_GESTURE_TAP:
case MENU_INPUT_GESTURE_SHORT_PRESS:
{
mui->categories_selection_ptr = i;
/* Normal pointer input */
if (y < header_height)
{
size_t selection = menu_navigation_get_selection();
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL);
}
else if (y > height - mui->tabs_height)
{
file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0);
file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
materialui_preswitch_tabs(mui, action);
for (i = 0; i <= MUI_SYSTEM_TAB_END; i++)
{
unsigned tab_width = width / (MUI_SYSTEM_TAB_END + 1);
unsigned start = tab_width * i;
if (cbs && cbs->action_content_list_switch)
return cbs->action_content_list_switch(selection_buf, menu_stack,
"", "", 0);
if ((x >= start) && (x < (start + tab_width)))
{
mui->categories_selection_ptr = i;
materialui_preswitch_tabs(mui, action);
if (cbs && cbs->action_content_list_switch)
return cbs->action_content_list_switch(selection_buf, menu_stack,
"", "", 0);
}
}
}
else if (ptr <= (entries_end - 1))
{
size_t ii;
file_list_t *list = menu_entries_get_selection_buf_ptr(0);
for (ii = 0; ii < entries_end; ii++)
{
materialui_node_t *node = (materialui_node_t*)
file_list_get_userdata_at_offset(list, ii);
if (y > (-mui->scroll_y + header_height + node->y)
&& y < (-mui->scroll_y + header_height + node->y + node->line_height)
)
{
if (ptr == ii && cbs && cbs->action_select)
return menu_entry_action(entry, (unsigned)ii, MENU_ACTION_SELECT);
}
}
}
}
}
}
else if (ptr <= (entries_end - 1))
{
size_t ii;
file_list_t *list = menu_entries_get_selection_buf_ptr(0);
for (ii = 0; ii < entries_end; ii++)
{
materialui_node_t *node = (materialui_node_t*)
file_list_get_userdata_at_offset(list, ii);
if (y > (-mui->scroll_y + header_height + node->y)
&& y < (-mui->scroll_y + header_height + node->y + node->line_height)
)
{
if (ptr == ii && cbs && cbs->action_select)
return menu_entry_action(entry, (unsigned)ii, MENU_ACTION_SELECT);
}
}
break;
case MENU_INPUT_GESTURE_LONG_PRESS:
/* 'Reset to default' action */
if ((ptr <= (menu_entries_get_size() - 1)) &&
(ptr == selection))
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_START);
break;
default:
/* Ignore input */
break;
}
return 0;

View File

@ -2294,15 +2294,33 @@ static bool ozone_get_load_content_animation_data(void *userdata, menu_texture_i
static int ozone_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)
{
size_t selection = menu_navigation_get_selection();
if (ptr == selection)
return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT);
menu_navigation_set_selection(ptr);
menu_driver_navigation_set(false);
switch (gesture)
{
case MENU_INPUT_GESTURE_TAP:
case MENU_INPUT_GESTURE_SHORT_PRESS:
/* Normal pointer input */
if (ptr == selection)
return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT);
menu_navigation_set_selection(ptr);
menu_driver_navigation_set(false);
break;
case MENU_INPUT_GESTURE_LONG_PRESS:
/* 'Reset to default' action */
if ((ptr <= (menu_entries_get_size() - 1)) &&
(ptr == selection))
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_START);
break;
default:
/* Ignore input */
break;
}
return 0;
}

View File

@ -4852,8 +4852,9 @@ static int rgui_environ(enum menu_environ_cb type,
}
static int rgui_pointer_up(void *data,
unsigned x, unsigned y,
unsigned ptr, menu_file_list_cbs_t *cbs,
unsigned x, unsigned y, unsigned ptr,
enum menu_input_pointer_gesture gesture,
menu_file_list_cbs_t *cbs,
menu_entry_t *entry, unsigned action)
{
rgui_t *rgui = (rgui_t*)data;
@ -4869,33 +4870,51 @@ static int rgui_pointer_up(void *data,
rgui->entry_has_thumbnail &&
(fs_thumbnail.is_valid || (rgui->thumbnail_queue_size > 0));
if (show_fs_thumbnail)
switch (gesture)
{
/* If we are currently showing a fullscreen thumbnail:
* - Must provide a mechanism for toggling it off
* - A normal mouse press should just select the current
* entry (for which the thumbnail is being shown) */
if (y < header_height)
rgui_update_thumbnail_image(rgui);
else
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT);
}
else
{
if (y < header_height)
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL);
else if (ptr <= (menu_entries_get_size() - 1))
{
/* If currently selected item matches 'pointer' value,
* perform a MENU_ACTION_SELECT on it */
if (ptr == selection)
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT);
case MENU_INPUT_GESTURE_TAP:
case MENU_INPUT_GESTURE_SHORT_PRESS:
{
/* Normal pointer input */
if (show_fs_thumbnail)
{
/* If we are currently showing a fullscreen thumbnail:
* - Must provide a mechanism for toggling it off
* - A normal mouse press should just select the current
* entry (for which the thumbnail is being shown) */
if (y < header_height)
rgui_update_thumbnail_image(rgui);
else
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT);
}
else
{
if (y < header_height)
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL);
else if (ptr <= (menu_entries_get_size() - 1))
{
/* If currently selected item matches 'pointer' value,
* perform a MENU_ACTION_SELECT on it */
if (ptr == selection)
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT);
/* Otherwise, just move the current selection to the
* 'pointer' value */
menu_navigation_set_selection(ptr);
menu_driver_navigation_set(false);
}
/* Otherwise, just move the current selection to the
* 'pointer' value */
menu_navigation_set_selection(ptr);
menu_driver_navigation_set(false);
}
}
}
break;
case MENU_INPUT_GESTURE_LONG_PRESS:
/* 'Reset to default' action */
if ((ptr <= (menu_entries_get_size() - 1)) &&
(ptr == selection))
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_START);
break;
default:
/* Ignore input */
break;
}
return 0;

View File

@ -4388,24 +4388,41 @@ error:
static int stripes_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 header_height = menu_display_get_header_height();
size_t selection = menu_navigation_get_selection();
if (y < header_height)
switch (gesture)
{
size_t selection = menu_navigation_get_selection();
return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL);
}
else if (ptr <= (menu_entries_get_size() - 1))
{
size_t selection = menu_navigation_get_selection();
if (ptr == selection && cbs && cbs->action_select)
return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT);
case MENU_INPUT_GESTURE_TAP:
case MENU_INPUT_GESTURE_SHORT_PRESS:
{
/* Normal pointer input */
unsigned header_height = menu_display_get_header_height();
menu_navigation_set_selection(ptr);
menu_driver_navigation_set(false);
if (y < header_height)
return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL);
else if (ptr <= (menu_entries_get_size() - 1))
{
if (ptr == selection && cbs && cbs->action_select)
return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT);
menu_navigation_set_selection(ptr);
menu_driver_navigation_set(false);
}
}
break;
case MENU_INPUT_GESTURE_LONG_PRESS:
/* 'Reset to default' action */
if ((ptr <= (menu_entries_get_size() - 1)) &&
(ptr == selection))
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_START);
break;
default:
/* Ignore input */
break;
}
return 0;

View File

@ -3345,25 +3345,108 @@ static void xmb_render(void *data,
if (pointer.type != MENU_POINTER_DISABLED)
{
size_t selection = menu_navigation_get_selection();
int16_t pointer_y = pointer.y;
unsigned first = 0, last = end;
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 = pointer.x;
int16_t pointer_y = pointer.y;
pointer_y = (pointer.type == MENU_POINTER_MOUSE) ?
pointer_y + (xmb->cursor_size/2) : pointer_y;
/* This must be set every frame when using a pointer,
* otherwise touchscreen input breaks when changing
* orientation */
menu_display_set_width(width);
menu_display_set_height(height);
if (height)
xmb_calculate_visible_range(xmb, height,
end, (unsigned)selection, &first, &last);
for (i = first; i <= last; i++)
/* 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))
{
float item_y1 = xmb->margins_screen_top
+ xmb_item_y(xmb, (int)i, selection);
float item_y2 = item_y1 + xmb->icon_size;
unsigned first = 0;
unsigned last = end;
if (pointer_y > item_y1 && pointer_y < item_y2)
menu_input_set_pointer_selection(i);
if (height)
xmb_calculate_visible_range(xmb, height,
end, (unsigned)selection, &first, &last);
for (i = first; i <= last; i++)
{
float entry_size = (i == selection) ?
xmb->icon_spacing_vertical * xmb->active_item_factor : xmb->icon_spacing_vertical;
float half_entry_size = entry_size * 0.5f;
float y_curr;
int y1;
int y2;
y_curr = xmb_item_y(xmb, (int)i, selection) + xmb->margins_screen_top;
y1 = (int)((y_curr - half_entry_size) + 0.5f);
y2 = (int)((y_curr + half_entry_size) + 0.5f);
if ((pointer_y > y1) && (pointer_y < y2))
{
menu_input_set_pointer_selection(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;
if (pointer.press_direction != MENU_INPUT_PRESS_DIRECTION_NONE)
{
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 (pointer.press_direction)
{
case MENU_INPUT_PRESS_DIRECTION_UP:
/* Note: Direction is inverted, since 'up' should
* move list upwards */
if (pointer_x > margin_right)
menu_entry_action(&entry, (unsigned)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)
menu_entry_action(&entry, (unsigned)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)
menu_entry_action(
&entry, (unsigned)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)
menu_entry_action(
&entry, (unsigned)selection, (xmb->depth == 1) ? MENU_ACTION_LEFT : MENU_ACTION_RIGHT);
break;
default:
/* Do nothing */
break;
}
}
}
@ -5977,24 +6060,157 @@ error:
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)
{
xmb_handle_t *xmb = (xmb_handle_t*)userdata;
unsigned header_height = menu_display_get_header_height();
size_t selection = menu_navigation_get_selection();
unsigned end = (unsigned)menu_entries_get_size();
unsigned width;
unsigned height;
int16_t margin_top;
int16_t margin_left;
int16_t margin_right;
if (y < header_height)
{
size_t selection = menu_navigation_get_selection();
return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL);
}
else if (ptr <= (menu_entries_get_size() - 1))
{
size_t selection = menu_navigation_get_selection();
if (ptr == selection)
return (unsigned)menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT);
if (!xmb)
return -1;
menu_navigation_set_selection(ptr);
menu_driver_navigation_set(false);
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 menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL);
else
return menu_input_dialog_start_search() ? 0 : -1;
}
else if (x > margin_right)
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT);
else if (ptr <= (end - 1))
{
/* If pointer item is already 'active', perform 'select' action */
if (ptr == selection)
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_SELECT);
/* ...otherwise navigate to the current pointer item */
menu_navigation_set_selection(ptr);
menu_driver_navigation_set(false);
}
break;
case MENU_INPUT_GESTURE_LONG_PRESS:
/* 'Reset to default' action */
if ((ptr <= end - 1) && (ptr == selection))
return menu_entry_action(entry, (unsigned)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)
menu_entry_action(
entry, (unsigned)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)
menu_entry_action(
entry, (unsigned)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)
menu_entry_action(entry, (unsigned)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);
menu_driver_navigation_set(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)
menu_entry_action(entry, (unsigned)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;
unsigned step;
/* 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... */
while (true)
{
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++;
}
step = (bottom_idx >= selection) ? bottom_idx - selection : 0;
new_idx = (selection > step) ? selection - step : 0;
if (new_idx > 0)
{
menu_navigation_set_selection(new_idx);
menu_driver_navigation_set(true);
}
else
{
bool pending_push = false;
menu_driver_ctl(MENU_NAVIGATION_CTL_CLEAR, &pending_push);
}
}
break;
default:
/* Ignore input */
break;
}
return 0;

View File

@ -2712,7 +2712,6 @@ void menu_display_draw_cursor(
settings_t *settings = config_get_ptr();
bool cursor_visible = settings->bools.video_fullscreen ||
!menu_display_has_windowed;
if (!settings->bools.menu_mouse_enable || !cursor_visible)
return;
@ -3568,6 +3567,7 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data)
}
point->retcode = menu_driver_ctx->pointer_up(menu_userdata,
point->x, point->y, point->ptr,
point->gesture,
point->cbs, point->entry, point->action);
}
break;

View File

@ -319,6 +319,7 @@ typedef struct menu_ctx_driver
menu_file_list_cbs_t *cbs,
menu_entry_t *entry, unsigned action);
int (*pointer_up)(void *data, unsigned x, unsigned y, unsigned ptr,
enum menu_input_pointer_gesture gesture,
menu_file_list_cbs_t *cbs,
menu_entry_t *entry, unsigned action);
bool (*get_load_content_animation_data)(void *userdata, menu_texture_item *icon, char **playlist_name);
@ -458,6 +459,7 @@ typedef struct menu_ctx_pointer
unsigned y;
unsigned ptr;
unsigned action;
enum menu_input_pointer_gesture gesture;
int retcode;
menu_file_list_cbs_t *cbs;
menu_entry_t *entry;

View File

@ -32,16 +32,59 @@ RETRO_BEGIN_DECLS
/* Mouse wheel tilt actions repeat at a very high
* frequency - we ignore any input that occurs
* with a period less than MENU_INPUT_HORIZ_WHEEL_DELAY */
#define MENU_INPUT_HORIZ_WHEEL_DELAY 250000 /* 250 ms */
#define MENU_INPUT_HORIZ_WHEEL_DELAY 250000 /* 250 ms */
#define MENU_INPUT_HIDE_CURSOR_DELAY 4000000 /* 4 seconds */
/* Press directions are triggered as a pulse train.
* Pulse period starts at MENU_INPUT_PRESS_DIRECTION_DELAY_MAX,
* and decreases to MENU_INPUT_PRESS_DIRECTION_DELAY_MIN as
* the start/current delta offset increases from
* MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_MIN to
* MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_MAX */
#define MENU_INPUT_PRESS_DIRECTION_DELAY_MIN 100000 /* 100 ms */
#define MENU_INPUT_PRESS_DIRECTION_DELAY_MAX 500000 /* 500 ms */
#define MENU_INPUT_PRESS_TIME_SHORT 250000 /* 250 ms */
#define MENU_INPUT_PRESS_TIME_LONG 1500000 /* 1.5 second */
/* (Anthing less than 'short' is considered a tap) */
#define MENU_INPUT_HIDE_CURSOR_DELAY 4000000 /* 4 seconds */
#define MENU_INPUT_PRESS_TIME_SHORT 200000 /* 200 ms */
#define MENU_INPUT_PRESS_TIME_LONG 1500000 /* 1.5 second */
/* (Anything less than 'short' is considered a tap) */
#define MENU_INPUT_Y_ACCEL_DECAY_FACTOR 0.96f
/* Pointer is considered stationary if dx/dy remain
* below (display DPI) * MENU_INPUT_DPI_THRESHOLD_DRAG */
#define MENU_INPUT_DPI_THRESHOLD_DRAG 0.1f
/* Press direction detection:
* While holding the pointer down, a press in a
* specific direction (up, down, left, right) will
* be detected if:
* - Current delta (i.e. from start to current) in
* press direction is greater than
* (display DPI) * MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_MIN
* - Current delta in perpendicular axis is less than
* (display DPI) * MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_TANGENT
* Press direction repeat rate is proportional to the current
* delta in press direction.
* Note: 'Tangent' is technically not the correct word here,
* but the alternatives look silly, and the actual meaning
* is transparent... */
#define MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_MIN 0.55f
#define MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_MAX 1.5f
#define MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_TANGENT 0.35f
/* Swipe detection:
* A gesture will register as a swipe if:
* - Total delta in swipe direction is greater than
* (display DPI) * MENU_INPUT_DPI_THRESHOLD_SWIPE
* - Maximum frame delta in swipe direction is greater than
* (display DPI) * MENU_INPUT_DPI_THRESHOLD_SWIPE_DELTA
* - Maximum frame delta in all other directions is less than
* (display DPI) * MENU_INPUT_DPI_THRESHOLD_SWIPE_DELTA_TANGENT */
#define MENU_INPUT_DPI_THRESHOLD_SWIPE 0.7f
#define MENU_INPUT_DPI_THRESHOLD_SWIPE_DELTA 0.4f
#define MENU_INPUT_DPI_THRESHOLD_SWIPE_DELTA_TANGENT 0.3f
enum menu_pointer_type
{
MENU_POINTER_DISABLED = 0,
@ -61,6 +104,27 @@ enum menu_input_mouse_hw_id
MENU_MOUSE_HORIZ_WHEEL_DOWN
};
enum menu_input_pointer_press_direction
{
MENU_INPUT_PRESS_DIRECTION_NONE = 0,
MENU_INPUT_PRESS_DIRECTION_UP,
MENU_INPUT_PRESS_DIRECTION_DOWN,
MENU_INPUT_PRESS_DIRECTION_LEFT,
MENU_INPUT_PRESS_DIRECTION_RIGHT
};
enum menu_input_pointer_gesture
{
MENU_INPUT_GESTURE_NONE = 0,
MENU_INPUT_GESTURE_TAP,
MENU_INPUT_GESTURE_SHORT_PRESS,
MENU_INPUT_GESTURE_LONG_PRESS,
MENU_INPUT_GESTURE_SWIPE_UP,
MENU_INPUT_GESTURE_SWIPE_DOWN,
MENU_INPUT_GESTURE_SWIPE_LEFT,
MENU_INPUT_GESTURE_SWIPE_RIGHT
};
/* Defines set of (abstracted) inputs/states
* common to mouse + touchscreen hardware */
typedef struct menu_input_pointer_hw_state
@ -83,6 +147,7 @@ typedef struct menu_input_pointer
bool pressed;
bool dragged;
retro_time_t press_duration;
enum menu_input_pointer_press_direction press_direction;
int16_t x;
int16_t y;
int16_t dx;

View File

@ -12393,7 +12393,15 @@ static void menu_input_get_mouse_hw_state(menu_input_pointer_hw_state_t *hw_stat
static bool last_cancel_pressed = false;
bool overlay_active = false;
bool mouse_enabled = settings->bools.menu_mouse_enable;
bool is_rgui = string_is_equal(settings->arrays.menu_driver, "rgui");
/* Note: RGUI requires special treatment, but we can't just
* check settings->arrays.menu_driver because this may change
* while another menu driver is active (and applying RGUI corrections
* while another menu driver is active will render the mouse unusable).
* We therefore have to check for the existence of a framebuffer
* texture instead (which is only ever set by RGUI) */
menu_handle_t *menu_data = menu_driver_get_ptr();
bool is_rgui =
(menu_data && menu_data->driver_ctx && menu_data->driver_ctx->set_texture);
/* Easiest to set inactive by default, and toggle
* when input is detected */
@ -12843,7 +12851,7 @@ static unsigned menu_event(
}
/* Populate menu_input_state
* Note: dx, dy, ptr, accel entries are set elsewhere */
* Note: dx, dy, ptr, y_accel, etc. entries are set elsewhere */
if (menu_input->select_inhibit)
{
menu_input->pointer.active = false;
@ -12963,6 +12971,50 @@ static void menu_input_set_pointer_visibility(retro_time_t current_time)
}
}
static float menu_input_get_dpi(void)
{
gfx_ctx_metrics_t metrics;
float dpi = 0.0f;
menu_handle_t *menu_data = menu_driver_get_ptr();
bool is_rgui =
(menu_data && menu_data->driver_ctx && menu_data->driver_ctx->set_texture);
/* Regardless of menu driver, need 'actual'
* screen DPI */
metrics.type = DISPLAY_METRIC_DPI;
metrics.value = &dpi;
if (!video_context_driver_get_metrics(&metrics))
dpi = 0.0f; /* Ensure a sane value (unnecessary check, but no harm being safe) */
/* RGUI uses a framebuffer texture, which means we
* operate in menu space, not screen space.
* DPI in a traditional sense is therefore meaningless,
* so generate a substitute value based upon framebuffer
* dimensions */
if ((dpi > 0.0f) && is_rgui)
{
size_t fb_pitch;
unsigned fb_width, fb_height;
struct video_viewport vp;
/* Read display/framebuffer info */
menu_display_get_fb_size(&fb_width, &fb_height, &fb_pitch);
video_driver_get_viewport_info(&vp);
/* Rationale for current 'DPI' determination method:
* - Divide screen height by DPI, to get number of vertical
* '1 inch' squares
* - Divide RGUI framebuffer height by number of vertical
* '1 inch' squares to get number of menu space pixels
* per inch
* This is crude, but should be sufficient... */
dpi = ((float)fb_height / (float)vp.full_height) * dpi;
}
return dpi;
}
static int menu_input_pointer_post_iterate(
retro_time_t current_time,
menu_file_list_cbs_t *cbs,
@ -12973,12 +13025,17 @@ static int menu_input_pointer_post_iterate(
static int16_t start_y = 0;
static int16_t last_x = 0;
static int16_t last_y = 0;
static uint16_t dx_right_max = 0;
static uint16_t dx_left_max = 0;
static uint16_t dy_up_max = 0;
static uint16_t dy_down_max = 0;
static bool last_select_pressed = false;
static bool last_cancel_pressed = false;
static bool last_left_pressed = false;
static bool last_right_pressed = false;
static retro_time_t last_left_action_time = 0;
static retro_time_t last_right_action_time = 0;
static retro_time_t last_press_direction_time = 0;
bool attenuate_y_accel = true;
bool osk_active = menu_input_dialog_get_display_kb_internal();
int ret = 0;
@ -12999,6 +13056,7 @@ static int menu_input_pointer_post_iterate(
point.cbs = NULL;
point.entry = NULL;
point.action = 0;
point.gesture = MENU_INPUT_GESTURE_NONE;
point.retcode = 0;
menu_driver_ctl(RARCH_MENU_CTL_OSK_PTR_AT_POS, &point);
@ -13022,52 +13080,106 @@ static int menu_input_pointer_post_iterate(
menu_ctx_pointer_t point;
/* Initialise variables */
start_time = current_time;
start_x = x;
start_y = y;
last_x = x;
last_y = y;
accel0 = 0.0f;
accel1 = 0.0f;
start_time = current_time;
start_x = x;
start_y = y;
last_x = x;
last_y = y;
dx_right_max = 0;
dx_left_max = 0;
dy_up_max = 0;
dy_down_max = 0;
accel0 = 0.0f;
accel1 = 0.0f;
last_press_direction_time = 0;
/* If we are not currently showing the onscreen keyboard,
* trigger a 'pointer down' event */
if (!osk_active)
{
point.x = x;
point.y = y;
point.ptr = menu_input->ptr;
point.cbs = cbs;
point.entry = entry;
point.action = action;
point.x = x;
point.y = y;
point.ptr = menu_input->ptr;
point.cbs = cbs;
point.entry = entry;
point.action = action;
point.gesture = MENU_INPUT_GESTURE_NONE;
menu_driver_ctl(RARCH_MENU_CTL_POINTER_DOWN, &point);
ret = point.retcode;
}
}
else if (!osk_active)
else
{
/* Pointer is being held down (i.e. for more than one frame)
* Note: We do not track movement while the onscreen
* keyboard is displayed */
gfx_ctx_metrics_t metrics;
float dpi;
float dpi = menu_input_get_dpi();
metrics.type = DISPLAY_METRIC_DPI;
metrics.value = &dpi;
/* > Update deltas + acceleration
/* > Update deltas + acceleration & detect press direction
* Note: We only do this if the pointer has moved above
* a certain threshold - this requires dpi info */
if (video_context_driver_get_metrics(&metrics))
if (dpi > 0.0f)
{
if ((abs(x - start_x) > (dpi / 10)) ||
(abs(y - start_y) > (dpi / 10)))
uint16_t dpi_threshold_drag =
(uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_DRAG) + 0.5f);
int16_t dx_start = x - start_x;
int16_t dy_start = y - start_y;
uint16_t dx_start_abs = dx_start < 0 ? dx_start * -1 : dx_start;
uint16_t dy_start_abs = dy_start < 0 ? dy_start * -1 : dy_start;
if ((dx_start_abs > dpi_threshold_drag) ||
(dy_start_abs > dpi_threshold_drag))
{
uint16_t dpi_threshold_press_direction_min =
(uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_MIN) + 0.5f);
uint16_t dpi_threshold_press_direction_max =
(uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_MAX) + 0.5f);
uint16_t dpi_threshold_press_direction_tangent =
(uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_PRESS_DIRECTION_TANGENT) + 0.5f);
enum menu_input_pointer_press_direction
press_direction = MENU_INPUT_PRESS_DIRECTION_NONE;
float press_direction_amplitude = 0.0f;
retro_time_t press_direction_delay = MENU_INPUT_PRESS_DIRECTION_DELAY_MAX;
int16_t dx = x - last_x;
int16_t dy = y - last_y;
/* Pointer has moved a sufficient distance to
* trigger a 'dragged' state */
menu_input->pointer.dragged = true;
menu_input->pointer.dx = x - last_x;
menu_input->pointer.dy = y - last_y;
/* Assign current deltas */
menu_input->pointer.dx = dx;
menu_input->pointer.dy = dy;
/* Update maximum deltas */
if (dx > 0)
{
if (dx > dx_right_max)
dx_right_max = (uint16_t)dx;
}
else
{
dx *= -1;
if (dx > dx_left_max)
dx_left_max = (uint16_t)dx;
}
if (dy > 0)
{
if (dy > dy_down_max)
dy_down_max = (uint16_t)dy;
}
else
{
dy *= -1;
if (dy > dy_up_max)
dy_up_max = dy;
}
/* Magic numbers... */
menu_input->pointer.y_accel = (accel0 + accel1 + (float)menu_input->pointer.dy) / 3.0f;
@ -13077,21 +13189,78 @@ static int menu_input_pointer_post_iterate(
/* Acceleration decays over time - but if the value
* has been set on this frame, attenuation should
* be skipped */
attenuate_y_accel = false;
attenuate_y_accel = false;
/* Check if ponter is being held in a particular
* direction */
menu_input->pointer.press_direction = MENU_INPUT_PRESS_DIRECTION_NONE;
/* > Press directions are actually triggered as a pulse train,
* since a continuous direction prevents fine control in the
* context of menu actions (i.e. it would be the same
* as always holding down a cursor key all the time - too fast
* to control). We therefore apply a low pass filter, with
* a variable frequency based upon the distance the user has
* dragged the pointer */
/* > Horizontal */
if ((dx_start_abs >= dpi_threshold_press_direction_min) &&
(dy_start_abs < dpi_threshold_press_direction_tangent))
{
press_direction = (dx_start > 0) ?
MENU_INPUT_PRESS_DIRECTION_RIGHT : MENU_INPUT_PRESS_DIRECTION_LEFT;
/* Get effective amplitude of press direction offset */
press_direction_amplitude =
(float)(dx_start_abs - dpi_threshold_press_direction_min) /
(float)(dpi_threshold_press_direction_max - dpi_threshold_press_direction_min);
}
/* > Vertical */
else if ((dy_start_abs >= dpi_threshold_press_direction_min) &&
(dx_start_abs < dpi_threshold_press_direction_tangent))
{
press_direction = (dy_start > 0) ?
MENU_INPUT_PRESS_DIRECTION_DOWN : MENU_INPUT_PRESS_DIRECTION_UP;
/* Get effective amplitude of press direction offset */
press_direction_amplitude =
(float)(dy_start_abs - dpi_threshold_press_direction_min) /
(float)(dpi_threshold_press_direction_max - dpi_threshold_press_direction_min);
}
if (press_direction != MENU_INPUT_PRESS_DIRECTION_NONE)
{
/* > Update low pass filter frequency */
if (press_direction_amplitude > 1.0f)
press_direction_delay = MENU_INPUT_PRESS_DIRECTION_DELAY_MIN;
else
press_direction_delay = MENU_INPUT_PRESS_DIRECTION_DELAY_MIN +
((MENU_INPUT_PRESS_DIRECTION_DELAY_MAX - MENU_INPUT_PRESS_DIRECTION_DELAY_MIN)*
(1.0f - press_direction_amplitude));
/* > Apply low pass filter */
if (current_time - last_press_direction_time > press_direction_delay)
{
menu_input->pointer.press_direction = press_direction;
last_press_direction_time = current_time;
}
}
}
else
{
/* Pointer is stationary */
menu_input->pointer.dx = 0;
menu_input->pointer.dy = 0;
menu_input->pointer.dx = 0;
menu_input->pointer.dy = 0;
menu_input->pointer.press_direction = MENU_INPUT_PRESS_DIRECTION_NONE;
}
}
else
{
/* No dpi info - just fallback to zero... */
menu_input->pointer.dx = 0;
menu_input->pointer.dy = 0;
menu_input->pointer.y_accel = 0.0f;
menu_input->pointer.dx = 0;
menu_input->pointer.dy = 0;
menu_input->pointer.y_accel = 0.0f;
menu_input->pointer.press_direction = MENU_INPUT_PRESS_DIRECTION_NONE;
}
/* > Update remaining variables */
@ -13103,28 +13272,44 @@ static int menu_input_pointer_post_iterate(
else if (last_select_pressed)
{
/* Transition from select pressed to select unpressed */
int16_t x;
int16_t y;
menu_ctx_pointer_t point;
/* If pointer has been 'dragged', then it counts as
* a miss. Only register 'release' event if pointer
* has remained stationary
* TODO/FIXME: Releasing select should *always* trigger a
* pointer up event, but event type should be specified
* - i.e. long press, short press, swipe left/right.
* The menu driver can then choose how to interpret the
* user action */
if (!menu_input->pointer.dragged)
if (menu_input->pointer.dragged)
{
menu_ctx_pointer_t point;
/* Ponter has moved.
* When using a touchscreen, relasing a press
* resets the x/y postition - so cannot use
* current hardware x/y values. Instead, use
* previous position from last time that a
* press was active */
x = last_x;
y = last_y;
}
else
{
/* Pointer is considered stationary,
* so use start position */
x = start_x;
y = start_y;
}
point.x = start_x;
point.y = start_y;
point.ptr = menu_input->ptr;
point.cbs = cbs;
point.entry = entry;
point.action = action;
point.x = x;
point.y = y;
point.ptr = menu_input->ptr;
point.cbs = cbs;
point.entry = entry;
point.action = action;
point.gesture = MENU_INPUT_GESTURE_NONE;
/* On screen keyboard overrides normal menu input */
if (osk_active)
/* On screen keyboard overrides normal menu input */
if (osk_active)
{
/* If pointer has been 'dragged', then it counts as
* a miss. Only register 'release' event if pointer
* has remained stationary */
if (!menu_input->pointer.dragged)
{
menu_driver_ctl(RARCH_MENU_CTL_OSK_PTR_AT_POS, &point);
if (point.retcode > -1)
@ -13132,33 +13317,94 @@ static int menu_input_pointer_post_iterate(
menu_event_set_osk_ptr(point.retcode);
menu_event_osk_append(point.retcode);
}
ret = point.retcode;
}
}
else
{
/* Detect gesture type */
if (!menu_input->pointer.dragged)
{
/* Pointer hasn't moved - check press duration */
if (menu_input->pointer.press_duration < MENU_INPUT_PRESS_TIME_SHORT)
point.gesture = MENU_INPUT_GESTURE_TAP;
else if (menu_input->pointer.press_duration < MENU_INPUT_PRESS_TIME_LONG)
point.gesture = MENU_INPUT_GESTURE_SHORT_PRESS;
else
point.gesture = MENU_INPUT_GESTURE_LONG_PRESS;
}
else
{
/* A 'long press' triggers a start (reset to default) action */
if (menu_input->pointer.press_duration > MENU_INPUT_PRESS_TIME_LONG)
/* Pointer has moved - check if this is a swipe */
float dpi = menu_input_get_dpi();
if (dpi > 0.0f)
{
size_t selection = menu_navigation_get_selection();
ret = menu_entry_action(entry, (unsigned)selection, MENU_ACTION_START);
}
else
{
menu_driver_ctl(RARCH_MENU_CTL_POINTER_UP, &point);
ret = point.retcode;
uint16_t dpi_threshold_swipe =
(uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_SWIPE) + 0.5f);
uint16_t dpi_threshold_swipe_delta =
(uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_SWIPE_DELTA) + 0.5f);
uint16_t dpi_threshold_swipe_delta_tangent =
(uint16_t)((dpi * MENU_INPUT_DPI_THRESHOLD_SWIPE_DELTA_TANGENT) + 0.5f);
/* Swipe right */
if (dx_right_max >= dpi_threshold_swipe_delta)
{
if ((dx_left_max < dpi_threshold_swipe_delta_tangent) &&
(dy_up_max < dpi_threshold_swipe_delta_tangent) &&
(dy_down_max < dpi_threshold_swipe_delta_tangent) &&
(x - start_x > dpi_threshold_swipe))
point.gesture = MENU_INPUT_GESTURE_SWIPE_RIGHT;
}
/* Swipe left */
else if (dx_left_max >= dpi_threshold_swipe_delta)
{
if ((dx_right_max < dpi_threshold_swipe_delta_tangent) &&
(dy_up_max < dpi_threshold_swipe_delta_tangent) &&
(dy_down_max < dpi_threshold_swipe_delta_tangent) &&
(start_x - x > dpi_threshold_swipe))
point.gesture = MENU_INPUT_GESTURE_SWIPE_LEFT;
}
/* Swipe up */
else if (dy_up_max >= dpi_threshold_swipe_delta)
{
if ((dx_right_max < dpi_threshold_swipe_delta_tangent) &&
(dx_left_max < dpi_threshold_swipe_delta_tangent) &&
(dy_down_max < dpi_threshold_swipe_delta_tangent) &&
(start_y - y > dpi_threshold_swipe))
point.gesture = MENU_INPUT_GESTURE_SWIPE_UP;
}
/* Swipe down */
else if (dy_down_max >= dpi_threshold_swipe_delta)
{
if ((dx_right_max < dpi_threshold_swipe_delta_tangent) &&
(dx_left_max < dpi_threshold_swipe_delta_tangent) &&
(dy_up_max < dpi_threshold_swipe_delta_tangent) &&
(y - start_y > dpi_threshold_swipe))
point.gesture = MENU_INPUT_GESTURE_SWIPE_DOWN;
}
}
}
/* Trigger a 'pointer up' event */
menu_driver_ctl(RARCH_MENU_CTL_POINTER_UP, &point);
ret = point.retcode;
}
/* Reset variables */
start_x = 0;
start_y = 0;
last_x = 0;
last_y = 0;
menu_input->pointer.press_duration = 0;
menu_input->pointer.dx = 0;
menu_input->pointer.dy = 0;
menu_input->pointer.dragged = false;
start_x = 0;
start_y = 0;
last_x = 0;
last_y = 0;
dx_right_max = 0;
dx_left_max = 0;
dy_up_max = 0;
dy_down_max = 0;
last_press_direction_time = 0;
menu_input->pointer.press_duration = 0;
menu_input->pointer.press_direction = MENU_INPUT_PRESS_DIRECTION_NONE;
menu_input->pointer.dx = 0;
menu_input->pointer.dy = 0;
menu_input->pointer.dragged = false;
}
}
last_select_pressed = pointer_hw_state->select_pressed;