diff --git a/gfx/common/x11_common.c b/gfx/common/x11_common.c index 1e49f60fcc..332fd03d9a 100644 --- a/gfx/common/x11_common.c +++ b/gfx/common/x11_common.c @@ -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; } diff --git a/input/common/input_x11_common.c b/input/common/input_x11_common.c index dbbe53fc43..b4c7a21b9d 100644 --- a/input/common/input_x11_common.c +++ b/input/common/input_x11_common.c @@ -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; } } diff --git a/input/drivers/x11_input.c b/input/drivers/x11_input.c index eeced20362..7d5970c21b 100644 --- a/input/drivers/x11_input.c +++ b/input/drivers/x11_input.c @@ -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; diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 1c6819c746..af858e439c 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -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; diff --git a/menu/drivers/ozone/ozone.c b/menu/drivers/ozone/ozone.c index e74cfe60e0..268b5c484f 100644 --- a/menu/drivers/ozone/ozone.c +++ b/menu/drivers/ozone/ozone.c @@ -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; } diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index d803b43bc0..6ac1c01e89 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -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; diff --git a/menu/drivers/stripes.c b/menu/drivers/stripes.c index 1aaefbd210..9c807fc717 100644 --- a/menu/drivers/stripes.c +++ b/menu/drivers/stripes.c @@ -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; diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index cdc76f5e80..2a68e873f9 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -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; diff --git a/menu/menu_driver.c b/menu/menu_driver.c index ac221049c1..0cdd61e642 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -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; diff --git a/menu/menu_driver.h b/menu/menu_driver.h index aa0bdb1c7c..639c6f96ae 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -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; diff --git a/menu/menu_input.h b/menu/menu_input.h index 612c607bbc..8fedf2ad2b 100644 --- a/menu/menu_input.h +++ b/menu/menu_input.h @@ -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; diff --git a/retroarch.c b/retroarch.c index 0f9f2d0f6f..e9c36f366c 100644 --- a/retroarch.c +++ b/retroarch.c @@ -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;