From 54f85fabaa063bfb523f12bbfbc083c74dd83827 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Thu, 7 Apr 2016 15:32:22 +0200 Subject: [PATCH] keybindings: Rework the matching Now we check all bindings in one place, allowing for future naughty matching. Signed-off-by: Quentin Glidic --- include/keyb.h | 6 +- include/textbox.h | 11 +- include/view-internal.h | 3 +- source/keyb.c | 15 ++- source/textbox.c | 51 ++++------ source/view.c | 216 +++++++++++++++++++++++----------------- 6 files changed, 167 insertions(+), 135 deletions(-) diff --git a/include/keyb.h b/include/keyb.h index 6dd201ad..e7128498 100644 --- a/include/keyb.h +++ b/include/keyb.h @@ -95,10 +95,10 @@ void setup_abe ( void ); void cleanup_abe ( void ); /** - * Check if this key has been triggered. - * @returns TRUE if key combo matches, FALSE otherwise. + * Find if a binding has been triggered. + * @returns NUM_ABE if no key combo matches, a valid action otherwise. */ -int abe_test_action ( KeyBindingAction action, unsigned int mask, xkb_keysym_t key ); +KeyBindingAction abe_find_action ( unsigned int mask, xkb_keysym_t key ); /*@}*/ #endif // ROFI_KEYB_H diff --git a/include/textbox.h b/include/textbox.h index 6c4eb4fe..afadd9e4 100644 --- a/include/textbox.h +++ b/include/textbox.h @@ -8,6 +8,7 @@ #include #include "widget.h" #include "x11-helper.h" +#include "keyb.h" /** * @defgroup Textbox Textbox @@ -101,15 +102,7 @@ void textbox_text ( textbox *tb, const char *text ); */ void textbox_draw ( textbox *tb, cairo_t *draw ); -/** - * @param tb Handle to the textbox - * @param ev XEvent key inputs to textbox - * - * Let the textbox handle the input event. - * - * @returns if the key was handled (1), unhandled(0) or handled and return was pressed (-1) - */ -int textbox_keybinding ( textbox *tb, unsigned int modstate, xkb_keysym_t key ); +int textbox_keybinding ( textbox *tb, KeyBindingAction action ); gboolean textbox_append ( textbox *tb, char *pad, int pad_len ); /** diff --git a/include/view-internal.h b/include/view-internal.h index 3d89170d..80ae7805 100644 --- a/include/view-internal.h +++ b/include/view-internal.h @@ -3,6 +3,7 @@ #include "widget.h" #include "textbox.h" #include "scrollbar.h" +#include "keyb.h" #include "x11-helper.h" /** @@ -47,7 +48,7 @@ struct RofiViewState // Last offset in paginating. unsigned int last_offset; - xkb_keysym_t prev_key; + KeyBindingAction prev_action; xcb_timestamp_t last_button_press; int quit; diff --git a/source/keyb.c b/source/keyb.c index dd0a0bc3..1a1e5690 100644 --- a/source/keyb.c +++ b/source/keyb.c @@ -135,7 +135,7 @@ void cleanup_abe ( void ) } } -int abe_test_action ( KeyBindingAction action, unsigned int mask, xkb_keysym_t key ) +static gboolean abe_test_action ( KeyBindingAction action, unsigned int mask, xkb_keysym_t key ) { ActionBindingEntry *akb = &( abe[action] ); @@ -148,3 +148,16 @@ int abe_test_action ( KeyBindingAction action, unsigned int mask, xkb_keysym_t k return FALSE; } + +KeyBindingAction abe_find_action ( unsigned int mask, xkb_keysym_t key ) +{ + KeyBindingAction action; + + for ( action = 0 ; action < NUM_ABE ; ++action ) { + if ( abe_test_action ( action, mask, key ) ) { + break; + } + } + + return action; +} diff --git a/source/textbox.c b/source/textbox.c index c7684453..c3d254fb 100644 --- a/source/textbox.c +++ b/source/textbox.c @@ -525,78 +525,67 @@ static void textbox_cursor_del_word ( textbox *tb ) // 0 = unhandled // 1 = handled // -1 = handled and return pressed (finished) -int textbox_keybinding ( textbox *tb, unsigned int modstate, xkb_keysym_t key ) +int textbox_keybinding ( textbox *tb, KeyBindingAction action ) { if ( !( tb->flags & TB_EDITABLE ) ) { g_return_val_if_reached(0); } - int old_blink = tb->blink; - tb->blink = 2; + switch ( action ) + { // Left or Ctrl-b - if ( abe_test_action ( MOVE_CHAR_BACK, modstate, key ) ) { + case MOVE_CHAR_BACK: textbox_cursor_dec ( tb ); return 2; - } // Right or Ctrl-F - if ( abe_test_action ( MOVE_CHAR_FORWARD, modstate, key ) ) { + case MOVE_CHAR_FORWARD: textbox_cursor_inc ( tb ); return 2; - } - // Ctrl-U: Kill from the beginning to the end of the line. - if ( abe_test_action ( CLEAR_LINE, modstate, key ) ) { + case CLEAR_LINE: textbox_text ( tb, "" ); return 1; - } // Ctrl-A - if ( abe_test_action ( MOVE_FRONT, modstate, key ) ) { + case MOVE_FRONT: textbox_cursor ( tb, 0 ); return 2; - } // Ctrl-E - if ( abe_test_action ( MOVE_END, modstate, key ) ) { + case MOVE_END: textbox_cursor_end ( tb ); return 2; - } // Ctrl-Alt-h - if ( abe_test_action ( REMOVE_WORD_BACK, modstate, key ) ) { + case REMOVE_WORD_BACK: textbox_cursor_bkspc_word ( tb ); return 1; - } // Ctrl-Alt-d - if ( abe_test_action ( REMOVE_WORD_FORWARD, modstate, key ) ) { + case REMOVE_WORD_FORWARD: textbox_cursor_del_word ( tb ); return 1; - } // Delete or Ctrl-D - if ( abe_test_action ( REMOVE_CHAR_FORWARD, modstate, key ) ) { + // Delete or Ctrl-D + case REMOVE_CHAR_FORWARD: textbox_cursor_del ( tb ); return 1; - } // Alt-B - if ( abe_test_action ( MOVE_WORD_BACK, modstate, key ) ) { + case MOVE_WORD_BACK: textbox_cursor_dec_word ( tb ); return 2; - } // Alt-F - if ( abe_test_action ( MOVE_WORD_FORWARD, modstate, key ) ) { + case MOVE_WORD_FORWARD: textbox_cursor_inc_word ( tb ); return 2; - } // BackSpace, Ctrl-h - if ( abe_test_action ( REMOVE_CHAR_BACK, modstate, key ) ) { + case REMOVE_CHAR_BACK: textbox_cursor_bkspc ( tb ); return 1; - } - if ( abe_test_action ( ACCEPT_CUSTOM, modstate, key ) ) { + case ACCEPT_CUSTOM: return -2; - } - if ( abe_test_action ( ACCEPT_ENTRY, modstate, key ) ) { + case ACCEPT_ENTRY: return -1; + default: + g_return_val_if_reached(0); } - tb->blink = old_blink; - return 0; + g_return_val_if_reached(0); } gboolean textbox_append ( textbox *tb, char *pad, int pad_len ) diff --git a/source/view.c b/source/view.c index 90b68a39..d60ba13e 100644 --- a/source/view.c +++ b/source/view.c @@ -1087,30 +1087,30 @@ static void rofi_view_paste ( RofiViewState *state, xcb_selection_notify_event_t * * Keyboard navigation through the elements. */ -static int rofi_view_keyboard_navigation ( RofiViewState *state, xkb_keysym_t key, unsigned int modstate ) +static void rofi_view_keyboard_navigation ( RofiViewState *state, KeyBindingAction action ) { + switch ( action ) + { // pressing one of the global key bindings closes the switcher. This allows fast closing of the // menu if an item is not selected - if ( abe_test_action ( CANCEL, modstate, key ) ) { + case CANCEL: state->retv = MENU_CANCEL; state->quit = TRUE; - return 1; - } + break; // Up, Ctrl-p or Shift-Tab - if ( abe_test_action ( ROW_UP, modstate, key ) ) { + case ROW_UP: rofi_view_nav_up ( state ); - return 1; - } - if ( abe_test_action ( ROW_TAB, modstate, key ) ) { + break; + case ROW_TAB: if ( state->filtered_lines == 1 ) { state->retv = MENU_OK; ( state->selected_line ) = state->line_map[state->selected]; state->quit = 1; - return 1; + break; } // Double tab! - if ( state->filtered_lines == 0 && key == state->prev_key ) { + if ( state->filtered_lines == 0 && action == state->prev_action ) { state->retv = MENU_NEXT; ( state->selected_line ) = 0; state->quit = TRUE; @@ -1118,39 +1118,31 @@ static int rofi_view_keyboard_navigation ( RofiViewState *state, xkb_keysym_t ke else { rofi_view_nav_down ( state ); } - state->prev_key = key; - return 1; - } + state->prev_action = action; + break; // Down, Ctrl-n - if ( abe_test_action ( ROW_DOWN, modstate, key ) ) { + case ROW_DOWN: rofi_view_nav_down ( state ); - return 1; - } - if ( abe_test_action ( ROW_LEFT, modstate, key ) ) { + break; + case ROW_LEFT: rofi_view_nav_left ( state ); - return 1; - } - if ( abe_test_action ( ROW_RIGHT, modstate, key ) ) { + break; + case ROW_RIGHT: rofi_view_nav_right ( state ); - return 1; - } - if ( abe_test_action ( PAGE_PREV, modstate, key ) ) { + break; + case PAGE_PREV: rofi_view_nav_page_prev ( state ); - return 1; - } - if ( abe_test_action ( PAGE_NEXT, modstate, key ) ) { + break; + case PAGE_NEXT: rofi_view_nav_page_next ( state ); - return 1; - } - if ( abe_test_action ( ROW_FIRST, modstate, key ) ) { + break; + case ROW_FIRST: rofi_view_nav_first ( state ); - return 1; - } - if ( abe_test_action ( ROW_LAST, modstate, key ) ) { + break; + case ROW_LAST: rofi_view_nav_last ( state ); - return 1; - } - if ( abe_test_action ( ROW_SELECT, modstate, key ) ) { + break; + case ROW_SELECT: // If a valid item is selected, return that.. if ( state->selected < state->filtered_lines ) { char *str = mode_get_completion ( state->sw, state->line_map[state->selected] ); @@ -1160,9 +1152,10 @@ static int rofi_view_keyboard_navigation ( RofiViewState *state, xkb_keysym_t ke state->update = TRUE; state->refilter = TRUE; } - return 1; + break; + default: + g_return_if_reached(); } - return 0; } static void rofi_view_mouse_navigation ( RofiViewState *state, xcb_button_press_event_t *xbe ) @@ -1356,106 +1349,149 @@ static void rofi_view_handle_keypress ( RofiViewState *state, xkb_stuff *xkb, xc unsigned int modstate = x11_canonalize_mask ( xkpe->state ); if ( key != XKB_KEY_NoSymbol ) { + KeyBindingAction action; + + action = abe_find_action ( modstate, key ); + switch ( action ) + { // Handling of paste - if ( abe_test_action ( PASTE_PRIMARY, modstate, key ) ) { + case PASTE_PRIMARY: xcb_convert_selection ( xcb->connection, CacheState.main_window, XCB_ATOM_PRIMARY, xcb->ewmh.UTF8_STRING, xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME ); xcb_flush ( xcb->connection ); return; - } - if ( abe_test_action ( PASTE_SECONDARY, modstate, key ) ) { + case PASTE_SECONDARY: xcb_convert_selection ( xcb->connection, CacheState.main_window, XCB_ATOM_SECONDARY, xcb->ewmh.UTF8_STRING, xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME ); xcb_flush ( xcb->connection ); return; - } - if ( abe_test_action ( SCREENSHOT, modstate, key ) ) { + case SCREENSHOT: menu_capture_screenshot ( ); return; - } - if ( abe_test_action ( TOGGLE_SORT, modstate, key ) ) { + case TOGGLE_SORT: config.levenshtein_sort = !config.levenshtein_sort; state->refilter = TRUE; state->update = TRUE; textbox_text ( state->case_indicator, get_matching_state () ); return; - } - if ( abe_test_action ( MODE_PREVIOUS, modstate, key ) ) { + case MODE_PREVIOUS: state->retv = MENU_PREVIOUS; ( state->selected_line ) = 0; state->quit = TRUE; return; - } // Menu navigation. - if ( abe_test_action ( MODE_NEXT, modstate, key ) ) { + case MODE_NEXT: state->retv = MENU_NEXT; ( state->selected_line ) = 0; state->quit = TRUE; return; - } // Toggle case sensitivity. - if ( abe_test_action ( TOGGLE_CASE_SENSITIVITY, modstate, key ) ) { + case TOGGLE_CASE_SENSITIVITY: config.case_sensitive = !config.case_sensitive; ( state->selected_line ) = 0; state->refilter = TRUE; state->update = TRUE; textbox_text ( state->case_indicator, get_matching_state () ); return; - } // Special delete entry command. - if ( abe_test_action ( DELETE_ENTRY, modstate, key ) ) { + case DELETE_ENTRY: if ( state->selected < state->filtered_lines ) { ( state->selected_line ) = state->line_map[state->selected]; state->retv = MENU_ENTRY_DELETE; state->quit = TRUE; return; } - } - for ( unsigned int a = CUSTOM_1; a <= CUSTOM_19; a++ ) { - if ( abe_test_action ( a, modstate, key ) ) { - state->selected_line = UINT32_MAX; - if ( state->selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[state->selected]; - } - state->retv = MENU_QUICK_SWITCH | ( ( a - CUSTOM_1 ) & MENU_LOWER_MASK ); - state->quit = TRUE; - return; - } - } - if ( rofi_view_keyboard_navigation ( state, key, modstate ) ) { - return; - } - - int rc = textbox_keybinding ( state->text, modstate, key ); - // Row is accepted. - if ( rc < 0 ) { - // If a valid item is selected, return that.. + break; + case CUSTOM_1: + case CUSTOM_2: + case CUSTOM_3: + case CUSTOM_4: + case CUSTOM_5: + case CUSTOM_6: + case CUSTOM_7: + case CUSTOM_8: + case CUSTOM_9: + case CUSTOM_10: + case CUSTOM_11: + case CUSTOM_12: + case CUSTOM_13: + case CUSTOM_14: + case CUSTOM_15: + case CUSTOM_16: + case CUSTOM_17: + case CUSTOM_18: + case CUSTOM_19: state->selected_line = UINT32_MAX; if ( state->selected < state->filtered_lines ) { ( state->selected_line ) = state->line_map[state->selected]; - state->retv = MENU_OK; } - else { - // Nothing entered and nothing selected. - state->retv = MENU_CUSTOM_INPUT; - } - if ( rc == -2 ) { - state->retv |= MENU_CUSTOM_ACTION; - } - + state->retv = MENU_QUICK_SWITCH | ( ( action - CUSTOM_1 ) & MENU_LOWER_MASK ); state->quit = TRUE; return; - } - // Key press is handled by entry box. - else if ( rc == 1 ) { - state->refilter = TRUE; - state->update = TRUE; + // If you add a binding here, make sure to add it to rofi_view_keyboard_navigation too + case CANCEL: + case ROW_UP: + case ROW_TAB: + case ROW_DOWN: + case ROW_LEFT: + case ROW_RIGHT: + case PAGE_PREV: + case PAGE_NEXT: + case ROW_FIRST: + case ROW_LAST: + case ROW_SELECT: + rofi_view_keyboard_navigation ( state, action ); return; + // If you add a binding here, make sure to add it to textbox_keybinding too + case MOVE_CHAR_BACK: + case MOVE_CHAR_FORWARD: + case CLEAR_LINE: + case MOVE_FRONT: + case MOVE_END: + case REMOVE_WORD_BACK: + case REMOVE_WORD_FORWARD: + case REMOVE_CHAR_FORWARD: + case MOVE_WORD_BACK: + case MOVE_WORD_FORWARD: + case REMOVE_CHAR_BACK: + case ACCEPT_CUSTOM: + case ACCEPT_ENTRY: + { + int rc = textbox_keybinding ( state->text, action ); + // Row is accepted. + if ( rc < 0 ) { + // If a valid item is selected, return that.. + state->selected_line = UINT32_MAX; + if ( state->selected < state->filtered_lines ) { + ( state->selected_line ) = state->line_map[state->selected]; + state->retv = MENU_OK; + } + else { + // Nothing entered and nothing selected. + state->retv = MENU_CUSTOM_INPUT; + } + if ( rc == -2 ) { + state->retv |= MENU_CUSTOM_ACTION; + } + + state->quit = TRUE; + return; + } + // Key press is handled by entry box. + else if ( rc == 1 ) { + state->refilter = TRUE; + state->update = TRUE; + return; + } + else if ( rc == 2 ) { + // redraw. + state->update = TRUE; + return; + } + break; } - else if ( rc == 2 ) { - // redraw. - state->update = TRUE; - return; + case NUM_ABE: + break; } }