static int action_right_scroll(unsigned type, const char *label, bool wraparound) { size_t selection; size_t scroll_accel = 0; unsigned scroll_speed = 0, fast_scroll_speed = 0; if (!menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection)) return false; if (!menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SCROLL_ACCEL, &scroll_accel)) return false; scroll_speed = (MAX(scroll_accel, 2) - 2) / 4 + 1; fast_scroll_speed = 4 + 4 * scroll_speed; if (selection + fast_scroll_speed < (menu_entries_get_size())) { size_t idx = selection + fast_scroll_speed; bool scroll = true; menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &idx); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET, &scroll); } else { if ((menu_entries_get_size() > 0)) menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_LAST, NULL); } return 0; }
static int zarch_zui_render_lay_root_recent(zui_t *zui, struct zui_tabbed *tabbed) { if (zarch_zui_tab(zui, tabbed, "Recent", 0)) { static int gamepad_index = 0; unsigned size = menu_entries_get_size(); unsigned i, j = 0; if (zarch_zui_gamepad_input(zui, &gamepad_index, &zui->recent_dlist_first, 0)) zui->recent_dlist_first = gamepad_index; for (i = zui->recent_dlist_first; i < size; ++i) { menu_entry_t entry; menu_entry_get(&entry, 0, i, NULL, true); if (zarch_zui_list_item(zui, tabbed, 0, tabbed->tabline_size + j * ZUI_ITEM_SIZE_PX, entry.path, i, entry.value, gamepad_index == (signed)i)) { if (menu_entry_action(&entry, i, MENU_ACTION_OK)) return 1; } j++; } } return 0; }
/** * Before a refresh, we could have deleted a * file on disk, causing selection_ptr to * suddendly be out of range. * * Ensure it doesn't overflow. **/ void menu_entries_refresh(file_list_t *list) { size_t list_size, selection; if (!list) return; if (!menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection)) return; menu_entries_build_scroll_indices(list); list_size = menu_entries_get_size(); if ((selection >= list_size) && list_size) { size_t idx = list_size - 1; bool scroll = true; menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &idx); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET, &scroll); } else if (!list_size) { bool pending_push = true; menu_navigation_ctl(MENU_NAVIGATION_CTL_CLEAR, &pending_push); } }
static int mui_pointer_tap(void *userdata, unsigned x, unsigned y, unsigned ptr, menu_file_list_cbs_t *cbs, menu_entry_t *entry, unsigned action) { size_t selection, idx; unsigned header_height, width, height, i; bool scroll = false; mui_handle_t *mui = (mui_handle_t*)userdata; file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); if (!mui) return 0; video_driver_get_size(&width, &height); menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection); menu_display_ctl(MENU_DISPLAY_CTL_HEADER_HEIGHT, &header_height); if (y < header_height) { menu_entries_pop_stack(&selection, 0); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &selection); } else if (y > height - mui->tabs_height) { 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))) { mui->categories.selection_ptr = i; mui_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 <= (menu_entries_get_size() - 1)) { if (ptr == selection && cbs && cbs->action_select) return menu_entry_action(entry, selection, MENU_ACTION_SELECT); idx = ptr; menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &idx); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET, &scroll); } return 0; }
static int action_bind_down_generic(unsigned type, const char *label) { size_t scroll_accel = 0; unsigned scroll_speed = 0; if (!menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SCROLL_ACCEL, &scroll_accel)) return -1; scroll_speed = (max(scroll_accel, 2) - 2) / 4 + 1; if (menu_entries_get_size() <= 0) return 0; menu_navigation_ctl(MENU_NAVIGATION_CTL_INCREMENT, &scroll_speed); return 0; }
void nk_menu_wnd_test(nk_menu_handle_t *zr) { struct nk_panel layout; struct nk_context *ctx = &zr->ctx; const int id = ZRMENU_WND_TEST; settings_t *settings = config_get_ptr(); if (nk_begin(ctx, &layout, "Test", nk_rect(140, 90, 500, 600), NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_MOVABLE| NK_WINDOW_SCALABLE|NK_WINDOW_BORDER)) { unsigned size; struct nk_panel combo; menu_entry_t entry; static const char *themes[] = {"Dark", "Light"}; enum nk_menu_theme old = zr->theme; nk_layout_row_dynamic(ctx, 30, 2); nk_layout_row_dynamic(ctx, 30, 4); //nk_checkbox_bool(ctx, "Show FPS", &(settings->fps_show)); //nk_checkbox_bool(ctx, "Show FPS", &(settings->fps_show)); //nk_checkbox_bool(ctx, "Show FPS", &(settings->fps_show)); //nk_checkbox_bool(ctx, "Show FPS", &(settings->fps_show)); nk_layout_row_dynamic(ctx, 30, 2); nk_label(ctx, "Volume:", NK_TEXT_LEFT); nk_slider_float(ctx, -80, &settings->audio.volume, 12, 0.5); nk_layout_row_dynamic(ctx, 30, 1); nk_property_int(ctx, "Max Users:", 1, (int*)&(settings->input.max_users), MAX_USERS, 1, 1); nk_label(ctx, "History:", NK_TEXT_LEFT); size = menu_entries_get_size(); } /* save position and size to restore after context reset */ nk_menu_set_state(zr, id, nk_window_get_position(ctx), nk_window_get_size(ctx)); nk_end(ctx); }
static int zarch_zui_render_lay_root_recent(zui_t *zui, struct zui_tabbed *tabbed) { if (zarch_zui_tab(zui, tabbed, "Recent", 0)) { static int gamepad_index = 0; unsigned size = menu_entries_get_size(); unsigned i, j = 0; if (zarch_zui_gamepad_input(zui, &gamepad_index, &zui->recent_dlist_first, 0)) zui->recent_dlist_first = gamepad_index; for (i = zui->recent_dlist_first; i < size; ++i) { char rich_label[PATH_MAX_LENGTH]; char entry_value[PATH_MAX_LENGTH]; menu_entry_t entry = {{0}}; rich_label[0] = entry_value[0] = '\0'; menu_entry_get(&entry, 0, i, NULL, true); menu_entry_get_rich_label(i, rich_label, sizeof(rich_label)); menu_entry_get_value(i, NULL, entry_value,sizeof(entry_value)); if (zarch_zui_list_item(zui, tabbed, 0, tabbed->tabline_size + j * ZUI_ITEM_SIZE_PX, rich_label, i, entry_value, gamepad_index == (signed)i)) { if (menu_entry_action(&entry, i, MENU_ACTION_OK)) return 1; } j++; } } return 0; }
static int zarch_zui_render_lay_root_recent(zui_t *zui, zui_tabbed_t *tabbed) { if (zarch_zui_tab(zui, tabbed, "Recent", 0)) { unsigned size = menu_entries_get_size(); unsigned i, j = 0; zui->recent_dlist_first += zui->mouse.wheel; if (zui->recent_dlist_first < 0) zui->recent_dlist_first = 0; else if (zui->recent_dlist_first > size - 5) zui->recent_dlist_first = size - 5; zui->recent_dlist_first = min(max(zui->recent_dlist_first, 0), size - 5); for (i = zui->recent_dlist_first; i < size; ++i) { menu_entry_t entry; menu_entry_get(&entry, 0, i, NULL, true); if (zarch_zui_list_item(zui, tabbed, 0, tabbed->tabline_size + j * 54, entry.path, i, entry.value)) { zui->pending_action_ok.enable = true; zui->pending_action_ok.idx = i; return 1; } j++; } } return 0; }
/** * Before a refresh, we could have deleted a * file on disk, causing selection_ptr to * suddendly be out of range. * * Ensure it doesn't overflow. **/ static bool menu_entries_refresh(void *data) { size_t list_size; file_list_t *list = (file_list_t*)data; size_t selection = menu_navigation_get_selection(); menu_entries_build_scroll_indices(list); list_size = menu_entries_get_size(); if ((selection >= list_size) && list_size) { size_t idx = list_size - 1; menu_navigation_set_selection(idx); menu_driver_navigation_set(true); } else if (!list_size) { bool pending_push = true; menu_driver_ctl(MENU_NAVIGATION_CTL_CLEAR, &pending_push); } return true; }
static int menu_input_mouse_post_iterate(uint64_t *input_mouse, menu_file_list_cbs_t *cbs, unsigned action) { settings_t *settings = config_get_ptr(); static bool mouse_oldleft = false; static bool mouse_oldright = false; if ( !settings->menu.mouse.enable #ifdef HAVE_OVERLAY || (settings->input.overlay_enable && input_overlay_is_alive(NULL)) #endif ) { /* HACK: Need to lie to avoid false hits if mouse is held * when entering the RetroArch window. */ /* This happens if, for example, someone double clicks the * window border to maximize it. * * The proper fix is, of course, triggering on WM_LBUTTONDOWN * rather than this state change. */ mouse_oldleft = true; mouse_oldright = true; return 0; } if (menu_input_mouse_state(MENU_MOUSE_LEFT_BUTTON)) { if (!mouse_oldleft) { size_t selection; menu_input_t *menu_input = menu_input_get_ptr(); menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection); BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_BUTTON_L); mouse_oldleft = true; if ((menu_input->mouse.ptr == selection) && cbs && cbs->action_select) { BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_BUTTON_L_TOGGLE); } else if (menu_input->mouse.ptr <= (menu_entries_get_size() - 1)) { BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_BUTTON_L_SET_NAVIGATION); } } } else mouse_oldleft = false; if (menu_input_mouse_state(MENU_MOUSE_RIGHT_BUTTON)) { if (!mouse_oldright) { mouse_oldright = true; BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_BUTTON_R); } } else mouse_oldright = false; if (menu_input_mouse_state(MENU_MOUSE_WHEEL_DOWN)) { BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_WHEEL_DOWN); } if (menu_input_mouse_state(MENU_MOUSE_WHEEL_UP)) { BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_WHEEL_UP); } if (menu_input_mouse_state(MENU_MOUSE_HORIZ_WHEEL_DOWN)) { BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_HORIZ_WHEEL_DOWN); } if (menu_input_mouse_state(MENU_MOUSE_HORIZ_WHEEL_UP)) { BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_HORIZ_WHEEL_UP); } return 0; }
/** * menu_iterate: * @input : input sample for this frame * @old_input : input sample of the previous frame * @trigger_input : difference' input sample - difference * between 'input' and 'old_input' * * Runs RetroArch menu for one frame. * * Returns: 0 on success, -1 if we need to quit out of the loop. **/ int generic_menu_iterate(enum menu_action action) { size_t selection; menu_entry_t entry; enum action_iterate_type iterate_type; const char *label = NULL; int ret = 0; uint32_t label_hash = 0; uint32_t hash = 0; menu_handle_t *menu = menu_driver_get_ptr(); file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(); file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(); menu_entries_get_last_stack(NULL, &label, NULL, NULL); if (!menu) return 0; if (!menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection)) return 0; menu->menu_state.msg[0] = '\0'; hash = menu_hash_calculate(label); iterate_type = action_iterate_type(hash); if (action != MENU_ACTION_NOOP || menu_entries_needs_refresh() || menu_display_ctl(MENU_DISPLAY_CTL_UPDATE_PENDING, NULL)) { BIT64_SET(menu->state, MENU_STATE_RENDER_FRAMEBUFFER); } switch (iterate_type) { case ITERATE_TYPE_HELP: ret = action_iterate_help(menu->menu_state.msg, sizeof(menu->menu_state.msg), label); BIT64_SET(menu->state, MENU_STATE_RENDER_MESSAGEBOX); BIT64_SET(menu->state, MENU_STATE_POST_ITERATE); if (ret == 1 || action == MENU_ACTION_OK) BIT64_SET(menu->state, MENU_STATE_POP_STACK); break; case ITERATE_TYPE_BIND: if (menu_input_key_bind_iterate(menu->menu_state.msg, sizeof(menu->menu_state.msg))) { menu_entries_pop_stack(&selection); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &selection); } else BIT64_SET(menu->state, MENU_STATE_RENDER_MESSAGEBOX); break; case ITERATE_TYPE_INFO: { menu_file_list_cbs_t *cbs = menu_entries_get_actiondata_at_offset(selection_buf, selection); rarch_setting_t *setting = cbs->setting; if (setting) { char needle[PATH_MAX_LENGTH]; strlcpy(needle, menu_setting_get_name(setting), sizeof(needle)); label_hash = menu_hash_calculate(needle); } ret = menu_hash_get_help(label_hash, menu->menu_state.msg, sizeof(menu->menu_state.msg)); } BIT64_SET(menu->state, MENU_STATE_RENDER_MESSAGEBOX); BIT64_SET(menu->state, MENU_STATE_POST_ITERATE); if (action == MENU_ACTION_OK) BIT64_SET(menu->state, MENU_STATE_POP_STACK); break; case ITERATE_TYPE_DEFAULT: /* FIXME: Crappy hack, needed for mouse controls to not be completely broken * in case we press back. * * We need to fix this entire mess, mouse controls should not rely on a * hack like this in order to work. */ selection = max(min(selection, (menu_entries_get_size() - 1)), 0); menu_entry_get(&entry, selection, NULL, false); ret = menu_entry_action(&entry, selection, (enum menu_action)action); if (ret) goto end; BIT64_SET(menu->state, MENU_STATE_POST_ITERATE); /* Have to defer it so we let settings refresh. */ if (menu->push_help_screen) { menu_displaylist_info_t info = {0}; info.list = menu_stack; strlcpy(info.label, menu_hash_to_str(MENU_LABEL_HELP), sizeof(info.label)); menu_displaylist_push_list(&info, DISPLAYLIST_HELP); } break; } BIT64_SET(menu->state, MENU_STATE_BLIT); if (BIT64_GET(menu->state, MENU_STATE_POP_STACK)) { size_t new_selection_ptr = selection; menu_entries_pop_stack(&new_selection_ptr); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &selection); } if (BIT64_GET(menu->state, MENU_STATE_POST_ITERATE)) menu_input_post_iterate(&ret, action); end: if (ret) return -1; return 0; }
/* Returns the last index (+1) of the menu entry list. */ size_t menu_entries_get_end(void) { return menu_entries_get_size(); }
bool menu_navigation_ctl(enum menu_navigation_ctl_state state, void *data) { /* Quick jumping indices with L/R. * Rebuilt when parsing directory. */ static struct scroll_indices { size_t list[2 * (26 + 2) + 1]; unsigned size; } scroll_index; static unsigned scroll_acceleration = 0; static size_t selection_ptr = 0; switch (state) { case MENU_NAVIGATION_CTL_DEINIT: scroll_acceleration = 0; selection_ptr = 0; memset(&scroll_index, 0, sizeof(struct scroll_indices)); break; case MENU_NAVIGATION_CTL_CLEAR: { size_t idx = 0; bool scroll = true; menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &idx); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET, &scroll); menu_driver_ctl(RARCH_MENU_CTL_NAVIGATION_CLEAR, data); } break; case MENU_NAVIGATION_CTL_INCREMENT: { settings_t *settings = config_get_ptr(); unsigned *scroll_speed = (unsigned*)data; size_t menu_list_size = menu_entries_get_size(); if (!scroll_speed) return false; if (selection_ptr >= menu_list_size - 1 && !settings->menu.navigation.wraparound.enable) return false; if ((selection_ptr + (*scroll_speed)) < menu_list_size) { size_t idx = selection_ptr + (*scroll_speed); bool scroll = true; menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &idx); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET, &scroll); menu_navigation_ctl(MENU_NAVIGATION_CTL_INCREMENT, NULL); } else { if (settings->menu.navigation.wraparound.enable) { bool pending_push = false; menu_navigation_ctl(MENU_NAVIGATION_CTL_CLEAR, &pending_push); } else { if (menu_list_size > 0) { menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_LAST, NULL); menu_navigation_ctl(MENU_NAVIGATION_CTL_INCREMENT, NULL); } } } menu_driver_ctl(RARCH_MENU_CTL_NAVIGATION_INCREMENT, NULL); } break; case MENU_NAVIGATION_CTL_DECREMENT: { size_t idx = 0; bool scroll = true; settings_t *settings = config_get_ptr(); unsigned *scroll_speed = (unsigned*)data; size_t menu_list_size = menu_entries_get_size(); if (!scroll_speed) return false; if (selection_ptr == 0 && !settings->menu.navigation.wraparound.enable) return false; if (selection_ptr >= *scroll_speed) idx = selection_ptr - *scroll_speed; else { idx = menu_list_size - 1; if (!settings->menu.navigation.wraparound.enable) idx = 0; } menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &idx); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET, &scroll); menu_navigation_ctl(MENU_NAVIGATION_CTL_DECREMENT, NULL); menu_driver_ctl(RARCH_MENU_CTL_NAVIGATION_DECREMENT, NULL); } break; case MENU_NAVIGATION_CTL_SET: menu_driver_ctl(RARCH_MENU_CTL_NAVIGATION_SET, data); break; case MENU_NAVIGATION_CTL_SET_LAST: { size_t menu_list_size = menu_entries_get_size(); size_t new_selection = menu_list_size - 1; menu_navigation_ctl( MENU_NAVIGATION_CTL_SET_SELECTION, &new_selection); menu_driver_ctl(RARCH_MENU_CTL_NAVIGATION_SET_LAST, NULL); } break; case MENU_NAVIGATION_CTL_ASCEND_ALPHABET: { size_t i = 0, ptr; size_t *ptr_out = (size_t*)&selection_ptr; size_t menu_list_size = menu_entries_get_size(); if (!scroll_index.size || !ptr_out) return false; ptr = *ptr_out; if (ptr == scroll_index.list[scroll_index.size - 1]) { *ptr_out = menu_list_size - 1; menu_driver_ctl(RARCH_MENU_CTL_NAVIGATION_ASCEND_ALPHABET, ptr_out); return true; } while (i < scroll_index.size - 1 && scroll_index.list[i + 1] <= ptr) i++; *ptr_out = scroll_index.list[i + 1]; if (*ptr_out >= menu_list_size) *ptr_out = menu_list_size - 1; menu_driver_ctl(RARCH_MENU_CTL_NAVIGATION_ASCEND_ALPHABET, ptr_out); } break; case MENU_NAVIGATION_CTL_DESCEND_ALPHABET: { size_t i = 0, ptr; size_t *ptr_out = (size_t*)&selection_ptr; if (!scroll_index.size || !ptr_out) return false; ptr = *ptr_out; if (ptr == 0) return false; i = scroll_index.size - 1; while (i && scroll_index.list[i - 1] >= ptr) i--; *ptr_out = scroll_index.list[i - 1]; menu_driver_ctl( RARCH_MENU_CTL_NAVIGATION_DESCEND_ALPHABET, ptr_out); } break; case MENU_NAVIGATION_CTL_GET_SELECTION: { size_t *sel = (size_t*)data; if (!sel) return false; *sel = selection_ptr; } break; case MENU_NAVIGATION_CTL_SET_SELECTION: { size_t *sel = (size_t*)data; if (!sel) return false; selection_ptr = *sel; } break; case MENU_NAVIGATION_CTL_CLEAR_SCROLL_INDICES: scroll_index.size = 0; break; case MENU_NAVIGATION_CTL_ADD_SCROLL_INDEX: { size_t *sel = (size_t*)data; if (!sel) return false; scroll_index.list[scroll_index.size++] = *sel; } break; case MENU_NAVIGATION_CTL_GET_SCROLL_ACCEL: { size_t *sel = (size_t*)data; if (!sel) return false; *sel = scroll_acceleration; } break; case MENU_NAVIGATION_CTL_SET_SCROLL_ACCEL: { size_t *sel = (size_t*)data; if (!sel) return false; scroll_acceleration = *sel; } break; default: case MENU_NAVIGATION_CTL_NONE: break; } return true; }
static int menu_input_mouse_post_iterate(uint64_t *input_mouse, menu_file_list_cbs_t *cbs, unsigned action) { size_t selection; unsigned header_height; settings_t *settings = config_get_ptr(); menu_input_t *menu_input = menu_input_get_ptr(); *input_mouse = MOUSE_ACTION_NONE; menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection); if (!settings->menu.mouse.enable #ifdef HAVE_OVERLAY || (settings->input.overlay_enable && input_overlay_is_alive()) #endif ) { menu_input->mouse.wheeldown = false; menu_input->mouse.wheelup = false; menu_input->mouse.oldleft = false; menu_input->mouse.oldright = false; return 0; } if (menu_input_mouse_state(MENU_MOUSE_LEFT_BUTTON)) { if (!menu_input->mouse.oldleft) { menu_display_ctl(MENU_DISPLAY_CTL_HEADER_HEIGHT, &header_height); BIT64_SET(*input_mouse, MOUSE_ACTION_BUTTON_L); menu_input->mouse.oldleft = true; if ((unsigned)menu_input->mouse.y < header_height) { menu_entries_pop_stack(&selection, 0); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &selection); return 0; } if ((menu_input->mouse.ptr == selection) && cbs && cbs->action_select) { BIT64_SET(*input_mouse, MOUSE_ACTION_BUTTON_L_TOGGLE); } else if (menu_input->mouse.ptr <= (menu_entries_get_size() - 1)) { BIT64_SET(*input_mouse, MOUSE_ACTION_BUTTON_L_SET_NAVIGATION); } } } else menu_input->mouse.oldleft = false; if (menu_input_mouse_state(MENU_MOUSE_RIGHT_BUTTON)) { if (!menu_input->mouse.oldright) { menu_input->mouse.oldright = true; BIT64_SET(*input_mouse, MOUSE_ACTION_BUTTON_R); } } else menu_input->mouse.oldright = false; if (menu_input->mouse.wheeldown) { BIT64_SET(*input_mouse, MOUSE_ACTION_WHEEL_DOWN); } if (menu_input->mouse.wheelup) { BIT64_SET(*input_mouse, MOUSE_ACTION_WHEEL_UP); } return 0; }
static bool zarch_zui_gamepad_input(zui_t *zui, int *gamepad_index, int *list_first, unsigned skip) { unsigned size = menu_entries_get_size(); unsigned cutoff_point = size - 5; switch (zui->action) { case MENU_ACTION_LEFT: if (*gamepad_index == 0) break; *gamepad_index = *gamepad_index - 5; if (*gamepad_index < 0) *gamepad_index = 0; return true; case MENU_ACTION_RIGHT: if (*gamepad_index == (signed)(size-1)) break; *gamepad_index = *gamepad_index + 5; if (*gamepad_index > (signed)(size-1)) *gamepad_index = (size -1); return true; case MENU_ACTION_UP: *gamepad_index = *gamepad_index - 1; if (*gamepad_index < 0) /* and wraparound enabled */ *gamepad_index = size -1; else if (*gamepad_index >= (signed)cutoff_point) /* if greater than cutoff point, don't scroll */ return false; return true; case MENU_ACTION_DOWN: *gamepad_index = *gamepad_index + 1; if (*gamepad_index > (signed)(size - 1)) /* and wraparound enabled */ *gamepad_index = 0; else if (*gamepad_index >= (signed)cutoff_point) /* if greater than cutoff point, don't scroll */ return false; return true; default: { *list_first += zui->mouse.wheel; if (*list_first < 0) *list_first = 0; if (*list_first > (int)cutoff_point) *list_first = cutoff_point; *list_first = MIN(MAX(*list_first, 0), cutoff_point - skip); } return false; } return false; }
static int menu_input_mouse_post_iterate(uint64_t *input_mouse, menu_file_list_cbs_t *cbs, unsigned action) { settings_t *settings = config_get_ptr(); static bool mouse_oldleft = false; static bool mouse_oldright = false; *input_mouse = MENU_MOUSE_ACTION_NONE; if ( !settings->menu.mouse.enable #ifdef HAVE_OVERLAY || (settings->input.overlay_enable && input_overlay_is_alive(NULL)) #endif ) { mouse_oldleft = false; mouse_oldright = false; return 0; } if (menu_input_mouse_state(MENU_MOUSE_LEFT_BUTTON)) { if (!mouse_oldleft) { size_t selection; unsigned header_height; menu_input_t *menu_input = menu_input_get_ptr(); menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection); header_height = menu_display_get_header_height(); BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_BUTTON_L); mouse_oldleft = true; /* Back button */ if ((unsigned)menu_input_mouse_state(MENU_MOUSE_X_AXIS) < header_height) { menu_entries_pop_stack(&selection, 0, 1); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &selection); return 0; } if ((menu_input->mouse.ptr == selection) && cbs && cbs->action_select) { BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_BUTTON_L_TOGGLE); } else if (menu_input->mouse.ptr <= (menu_entries_get_size() - 1)) { BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_BUTTON_L_SET_NAVIGATION); } } } else mouse_oldleft = false; if (menu_input_mouse_state(MENU_MOUSE_RIGHT_BUTTON)) { if (!mouse_oldright) { mouse_oldright = true; BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_BUTTON_R); } } else mouse_oldright = false; if (menu_input_mouse_state(MENU_MOUSE_WHEEL_DOWN)) { BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_WHEEL_DOWN); } if (menu_input_mouse_state(MENU_MOUSE_WHEEL_UP)) { BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_WHEEL_UP); } if (menu_input_mouse_state(MENU_MOUSE_HORIZ_WHEEL_DOWN)) { BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_HORIZ_WHEEL_DOWN); } if (menu_input_mouse_state(MENU_MOUSE_HORIZ_WHEEL_UP)) { BIT64_SET(*input_mouse, MENU_MOUSE_ACTION_HORIZ_WHEEL_UP); } return 0; }
/** * menu_iterate: * @input : input sample for this frame * @old_input : input sample of the previous frame * @trigger_input : difference' input sample - difference * between 'input' and 'old_input' * * Runs RetroArch menu for one frame. * * Returns: 0 on success, -1 if we need to quit out of the loop. **/ int generic_menu_iterate(void *data, void *userdata, enum menu_action action) { menu_entry_t entry; enum action_iterate_type iterate_type; size_t selection = 0; unsigned file_type = 0; int ret = 0; uint32_t hash = 0; enum msg_hash_enums enum_idx = MSG_UNKNOWN; const char *label = NULL; menu_handle_t *menu = (menu_handle_t*)data; menu_entries_get_last_stack(NULL, &label, &file_type, &enum_idx, NULL); if (!menu) return 0; if (!menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection)) return 0; menu->menu_state.msg[0] = '\0'; hash = msg_hash_calculate(label); iterate_type = action_iterate_type(hash); menu_driver_set_binding_state(iterate_type == ITERATE_TYPE_BIND); if ( action != MENU_ACTION_NOOP || menu_entries_ctl(MENU_ENTRIES_CTL_NEEDS_REFRESH, NULL) || menu_display_get_update_pending()) { BIT64_SET(menu->state, MENU_STATE_RENDER_FRAMEBUFFER); } switch (iterate_type) { case ITERATE_TYPE_HELP: ret = menu_dialog_iterate( menu->menu_state.msg, sizeof(menu->menu_state.msg), label); BIT64_SET(menu->state, MENU_STATE_RENDER_MESSAGEBOX); BIT64_SET(menu->state, MENU_STATE_POST_ITERATE); if (ret == 1 || action == MENU_ACTION_OK) { BIT64_SET(menu->state, MENU_STATE_POP_STACK); menu_dialog_set_active(false); } if (action == MENU_ACTION_CANCEL) { BIT64_SET(menu->state, MENU_STATE_POP_STACK); menu_dialog_set_active(false); } break; case ITERATE_TYPE_BIND: { menu_input_ctx_bind_t bind; bind.s = menu->menu_state.msg; bind.len = sizeof(menu->menu_state.msg); if (menu_input_key_bind_iterate(&bind)) { menu_entries_pop_stack(&selection, 0, 0); menu_navigation_ctl( MENU_NAVIGATION_CTL_SET_SELECTION, &selection); } else BIT64_SET(menu->state, MENU_STATE_RENDER_MESSAGEBOX); } break; case ITERATE_TYPE_INFO: { file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0); menu_file_list_cbs_t *cbs = menu_entries_get_actiondata_at_offset(selection_buf, selection); if (cbs->enum_idx != MSG_UNKNOWN) { ret = menu_hash_get_help_enum(cbs->enum_idx, menu->menu_state.msg, sizeof(menu->menu_state.msg)); } else { unsigned type = 0; enum msg_hash_enums enum_idx = MSG_UNKNOWN; menu_entries_get_at_offset(selection_buf, selection, NULL, NULL, &type, NULL, NULL); switch (type) { case FILE_TYPE_FONT: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_FONT; break; case FILE_TYPE_RDB: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_RDB; break; case FILE_TYPE_OVERLAY: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_OVERLAY; break; case FILE_TYPE_CHEAT: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_CHEAT; break; case FILE_TYPE_SHADER_PRESET: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_SHADER_PRESET; break; case FILE_TYPE_SHADER: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_SHADER; break; case FILE_TYPE_REMAP: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_REMAP; break; case FILE_TYPE_RECORD_CONFIG: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_RECORD_CONFIG; break; case FILE_TYPE_CURSOR: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_CURSOR; break; case FILE_TYPE_CONFIG: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_CONFIG; break; case FILE_TYPE_CARCHIVE: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_COMPRESSED_ARCHIVE; break; case FILE_TYPE_DIRECTORY: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_DIRECTORY; break; case FILE_TYPE_VIDEOFILTER: /* TODO/FIXME */ case FILE_TYPE_AUDIOFILTER: /* TODO/FIXME */ case FILE_TYPE_SHADER_SLANG: /* TODO/FIXME */ case FILE_TYPE_SHADER_GLSL: /* TODO/FIXME */ case FILE_TYPE_SHADER_HLSL: /* TODO/FIXME */ case FILE_TYPE_SHADER_CG: /* TODO/FIXME */ case FILE_TYPE_SHADER_PRESET_GLSLP: /* TODO/FIXME */ case FILE_TYPE_SHADER_PRESET_HLSLP: /* TODO/FIXME */ case FILE_TYPE_SHADER_PRESET_CGP: /* TODO/FIXME */ case FILE_TYPE_SHADER_PRESET_SLANGP: /* TODO/FIXME */ case FILE_TYPE_PLAIN: enum_idx = MENU_ENUM_LABEL_FILE_BROWSER_PLAIN_FILE; break; default: break; } if (enum_idx != MSG_UNKNOWN) ret = menu_hash_get_help_enum(enum_idx, menu->menu_state.msg, sizeof(menu->menu_state.msg)); } } BIT64_SET(menu->state, MENU_STATE_RENDER_MESSAGEBOX); BIT64_SET(menu->state, MENU_STATE_POST_ITERATE); if (action == MENU_ACTION_OK || action == MENU_ACTION_CANCEL) { BIT64_SET(menu->state, MENU_STATE_POP_STACK); } menu_dialog_set_active(false); break; case ITERATE_TYPE_DEFAULT: /* FIXME: Crappy hack, needed for mouse controls * to not be completely broken in case we press back. * * We need to fix this entire mess, mouse controls * should not rely on a hack like this in order to work. */ selection = MAX(MIN(selection, (menu_entries_get_size() - 1)), 0); menu_entry_get(&entry, 0, selection, NULL, false); ret = menu_entry_action(&entry, selection, (enum menu_action)action); if (ret) goto end; BIT64_SET(menu->state, MENU_STATE_POST_ITERATE); /* Have to defer it so we let settings refresh. */ menu_dialog_push(); break; } BIT64_SET(menu->state, MENU_STATE_BLIT); if (BIT64_GET(menu->state, MENU_STATE_POP_STACK)) { size_t new_selection_ptr = selection; menu_entries_pop_stack(&new_selection_ptr, 0, 0); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &selection); } if (BIT64_GET(menu->state, MENU_STATE_POST_ITERATE)) menu_input_post_iterate(&ret, action); end: if (ret) return -1; return 0; }
static void xui_render(void *data, bool is_idle) { size_t end, i, selection, fb_pitch; unsigned fb_width, fb_height; char title[PATH_MAX_LENGTH] = {0}; const char *dir = NULL; const char *label = NULL; unsigned menu_type = 0; uint64_t frame_count = xui_frame_count; bool msg_force = menu_display_get_msg_force(); menu_display_get_fb_size(&fb_width, &fb_height, &fb_pitch); if ( menu_entries_ctl(MENU_ENTRIES_CTL_NEEDS_REFRESH, NULL) && menu_driver_is_alive() && !msg_force ) return; menu_display_unset_framebuffer_dirty_flag(); menu_animation_ctl(MENU_ANIMATION_CTL_CLEAR_ACTIVE, NULL); xui_render_background(); if (XuiHandleIsValid(m_menutitle)) { menu_animation_ctx_ticker_t ticker; menu_entries_get_title(title, sizeof(title)); mbstowcs(strw_buffer, title, sizeof(strw_buffer) / sizeof(wchar_t)); XuiTextElementSetText(m_menutitle, strw_buffer); ticker.s = title; ticker.len = RXUI_TERM_WIDTH(fb_width) - 3; ticker.idx = (unsigned int)frame_count / 15; ticker.str = title; ticker.selected = true; menu_animation_ticker(&ticker); } if (XuiHandleIsValid(m_menutitle)) { if ( menu_entries_get_core_title(title, sizeof(title)) == 0) { mbstowcs(strw_buffer, title, sizeof(strw_buffer) / sizeof(wchar_t)); XuiTextElementSetText(m_menutitlebottom, strw_buffer); } } end = menu_entries_get_size(); for (i = 0; i < end; i++) { menu_entry_t entry; char *entry_path = NULL; char entry_value[PATH_MAX_LENGTH] = {0}; wchar_t msg_right[PATH_MAX_LENGTH] = {0}; wchar_t msg_left[PATH_MAX_LENGTH] = {0}; menu_entry_init(&entry); menu_entry_get(&entry, 0, i, NULL, true); menu_entry_get_value(&entry, entry_value, sizeof(entry_value)); entry_path = menu_entry_get_path(&entry); mbstowcs(msg_left, entry_path, sizeof(msg_left) / sizeof(wchar_t)); mbstowcs(msg_right, entry_value, sizeof(msg_right) / sizeof(wchar_t)); xui_set_list_text(i, msg_left, msg_right); menu_entry_free(&entry); if (!string_is_empty(entry_path)) free(entry_path); } selection = menu_navigation_get_selection(); XuiListSetCurSelVisible(m_menulist, selection); if (menu_input_dialog_get_display_kb()) { char msg[1024] = {0}; const char *str = menu_input_dialog_get_buffer(); const char *label = menu_input_dialog_get_label_buffer(); snprintf(msg, sizeof(msg), "%s\n%s", label, str); xui_render_messagebox(NULL, msg); } }
bool menu_navigation_ctl(enum menu_navigation_ctl_state state, void *data) { const menu_ctx_driver_t *driver = menu_ctx_driver_get_ptr(); settings_t *settings = config_get_ptr(); menu_navigation_t *nav = menu_navigation_get_ptr(); size_t menu_list_size = menu_entries_get_size(); size_t selection = nav->selection_ptr; (void)settings; switch (state) { case MENU_NAVIGATION_CTL_CLEAR: { size_t idx = 0; bool scroll = true; bool *pending_push = (bool*)data; if (!pending_push) return false; menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &idx); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET, &scroll); if (driver->navigation_clear) driver->navigation_clear(*pending_push); } return true; case MENU_NAVIGATION_CTL_INCREMENT: { unsigned *scroll_speed = (unsigned*)data; if (!scroll_speed) return false; if (selection >= menu_list_size - 1 && !settings->menu.navigation.wraparound.enable) return false; if ((selection + (*scroll_speed)) < menu_list_size) { size_t idx = selection + (*scroll_speed); bool scroll = true; menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &idx); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET, &scroll); menu_navigation_ctl(MENU_NAVIGATION_CTL_INCREMENT, NULL); } else { if (settings->menu.navigation.wraparound.enable) { bool pending_push = false; menu_navigation_ctl(MENU_NAVIGATION_CTL_CLEAR, &pending_push); } else { if (menu_list_size > 0) { menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_LAST, NULL); menu_navigation_ctl(MENU_NAVIGATION_CTL_INCREMENT, NULL); } } } if (driver->navigation_increment) driver->navigation_increment(); } return true; case MENU_NAVIGATION_CTL_DECREMENT: { size_t idx = 0; bool scroll = true; unsigned *scroll_speed = (unsigned*)data; if (!scroll_speed) return false; if (selection == 0 && !settings->menu.navigation.wraparound.enable) return false; if (selection >= *scroll_speed) idx = selection - *scroll_speed; else { idx = menu_list_size - 1; if (!settings->menu.navigation.wraparound.enable) idx = 0; } menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &idx); menu_navigation_ctl(MENU_NAVIGATION_CTL_SET, &scroll); menu_navigation_ctl(MENU_NAVIGATION_CTL_DECREMENT, NULL); if (driver->navigation_decrement) driver->navigation_decrement(); } return true; case MENU_NAVIGATION_CTL_SET: { bool *scroll = (bool*)data; if (!scroll) return false; if (driver->navigation_set) driver->navigation_set(*scroll); } return true; case MENU_NAVIGATION_CTL_SET_LAST: { size_t new_selection = menu_list_size - 1; menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SELECTION, &new_selection); if (driver->navigation_set_last) driver->navigation_set_last(); } return true; case MENU_NAVIGATION_CTL_ASCEND_ALPHABET: { size_t i = 0, ptr; size_t *ptr_out = nav ? (size_t*)&nav->selection_ptr : NULL; if (!nav || !nav->scroll.indices.size || !ptr_out) return false; ptr = *ptr_out; if (ptr == nav->scroll.indices.list[nav->scroll.indices.size - 1]) return false; while (i < nav->scroll.indices.size - 1 && nav->scroll.indices.list[i + 1] <= ptr) i++; *ptr_out = nav->scroll.indices.list[i + 1]; if (driver->navigation_ascend_alphabet) driver->navigation_ascend_alphabet(ptr_out); } return true; case MENU_NAVIGATION_CTL_DESCEND_ALPHABET: { size_t i = 0, ptr; size_t *ptr_out = nav ? (size_t*)&nav->selection_ptr : NULL; if (!nav || !nav->scroll.indices.size || !ptr_out) return false; ptr = *ptr_out; if (ptr == 0) return false; i = nav->scroll.indices.size - 1; while (i && nav->scroll.indices.list[i - 1] >= ptr) i--; *ptr_out = nav->scroll.indices.list[i - 1]; if (driver->navigation_descend_alphabet) driver->navigation_descend_alphabet(ptr_out); } return true; case MENU_NAVIGATION_CTL_GET_SELECTION: { size_t *sel = (size_t*)data; if (!nav || !sel) return false; *sel = selection; } return true; case MENU_NAVIGATION_CTL_SET_SELECTION: { size_t *sel = (size_t*)data; if (!nav || !sel) return false; nav->selection_ptr = *sel; } return true; case MENU_NAVIGATION_CTL_CLEAR_SCROLL_INDICES: { if (!nav) return false; nav->scroll.indices.size = 0; } return true; case MENU_NAVIGATION_CTL_ADD_SCROLL_INDEX: { size_t *sel = (size_t*)data; if (!nav || !sel) return false; nav->scroll.indices.list[nav->scroll.indices.size++] = *sel; } return true; case MENU_NAVIGATION_CTL_GET_SCROLL_ACCEL: { size_t *sel = (size_t*)data; if (!nav || !sel) return false; *sel = nav->scroll.acceleration; } return true; case MENU_NAVIGATION_CTL_SET_SCROLL_ACCEL: { size_t *sel = (size_t*)data; if (!nav || !sel) return false; nav->scroll.acceleration = *sel; } return true; } return false; }