diff --git a/doc/rofi-manpage.markdown b/doc/rofi-manpage.markdown index 78865739..e795659e 100644 --- a/doc/rofi-manpage.markdown +++ b/doc/rofi-manpage.markdown @@ -704,6 +704,8 @@ The first two fields specify the alpha level. This determines the amount of tran To get a full list of keybindings, see `rofi -dump-xresources | grep kb-`. Keybindings can be modified using the configuration systems. +A keybinding starting with `!` will act when all keys have been released. + ## Available Modi ### Window diff --git a/doc/rofi.1 b/doc/rofi.1 index 40d30c62..443b6205 100644 --- a/doc/rofi.1 +++ b/doc/rofi.1 @@ -1160,6 +1160,9 @@ The first two fields specify the alpha level\. This determines the amount of tra .P To get a full list of keybindings, see \fBrofi \-dump\-xresources | grep kb\-\fR\. Keybindings can be modified using the configuration systems\. . +.P +A keybinding starting with \fB!\fR will act when all keys have been released\. +. .SH "Available Modi" . .SS "Window" diff --git a/include/keyb.h b/include/keyb.h index e7128498..5bdea14b 100644 --- a/include/keyb.h +++ b/include/keyb.h @@ -100,5 +100,7 @@ void cleanup_abe ( void ); */ KeyBindingAction abe_find_action ( unsigned int mask, xkb_keysym_t key ); +void abe_trigger_release ( void ); + /*@}*/ #endif // ROFI_KEYB_H diff --git a/include/x11-helper.h b/include/x11-helper.h index c6704ff4..ef761bd9 100644 --- a/include/x11-helper.h +++ b/include/x11-helper.h @@ -81,6 +81,8 @@ int take_keyboard ( xcb_window_t w ); */ unsigned int x11_canonalize_mask ( unsigned int mask ); +unsigned int x11_get_current_mask ( xkb_stuff *xkb ); + /** * @param combo String representing the key combo * @param mod [out] The modifier specified (or AnyModifier if not specified) @@ -88,7 +90,7 @@ unsigned int x11_canonalize_mask ( unsigned int mask ); * * Parse key from user input string. */ -gboolean x11_parse_key ( char *combo, unsigned int *mod, xkb_keysym_t *key ); +gboolean x11_parse_key ( char *combo, unsigned int *mod, xkb_keysym_t *key, gboolean *release ); /** * @param display The connection to the X server. diff --git a/source/keyb.c b/source/keyb.c index 1a1e5690..c9fe83dc 100644 --- a/source/keyb.c +++ b/source/keyb.c @@ -8,6 +8,7 @@ typedef struct { unsigned int modmask; xkb_keysym_t keysym; + gboolean release; } KeyBinding; typedef struct @@ -114,7 +115,8 @@ gboolean parse_keys_abe ( void ) for ( char *entry = strtok_r ( keystr, sep, &sp ); entry != NULL; entry = strtok_r ( NULL, sep, &sp ) ) { abe[iter].kb = g_realloc ( abe[iter].kb, ( abe[iter].num_bindings + 1 ) * sizeof ( KeyBinding ) ); KeyBinding *kb = &( abe[iter].kb[abe[iter].num_bindings] ); - if ( !x11_parse_key ( entry, &( kb->modmask ), &( kb->keysym ) ) ) { + memset(kb, 0, sizeof(KeyBinding)); + if ( !x11_parse_key ( entry, &( kb->modmask ), &( kb->keysym ), &( kb->release ) ) ) { g_free ( keystr ); return FALSE; } @@ -135,6 +137,8 @@ void cleanup_abe ( void ) } } +static gboolean _abe_trigger_on_release[NUM_ABE] = { 0 }; + static gboolean abe_test_action ( KeyBindingAction action, unsigned int mask, xkb_keysym_t key ) { ActionBindingEntry *akb = &( abe[action] ); @@ -142,13 +146,19 @@ static gboolean abe_test_action ( KeyBindingAction action, unsigned int mask, xk for ( int iter = 0; iter < akb->num_bindings; iter++ ) { const KeyBinding * const kb = &( akb->kb[iter] ); if ( ( kb->keysym == key ) && ( kb->modmask == mask ) ) { - return TRUE; + if ( kb->release ) { + _abe_trigger_on_release[action] = TRUE; + } + else { + return TRUE; + } } } return FALSE; } + KeyBindingAction abe_find_action ( unsigned int mask, xkb_keysym_t key ) { KeyBindingAction action; @@ -161,3 +171,19 @@ KeyBindingAction abe_find_action ( unsigned int mask, xkb_keysym_t key ) return action; } + +void abe_trigger_release ( void ) +{ + RofiViewState *state; + KeyBindingAction action; + + state = rofi_view_get_active ( ); + for ( action = 0 ; action < NUM_ABE ; ++action ) { + if ( _abe_trigger_on_release[action] ) { + rofi_view_trigger_action ( state, action ); + _abe_trigger_on_release[action] = FALSE; + } + } + + rofi_view_update ( state ); +} diff --git a/source/rofi.c b/source/rofi.c index b1684d21..b293c570 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -439,6 +439,7 @@ static gboolean main_loop_x11_event_handler ( xcb_generic_event_t *ev, G_GNUC_UN case XCB_XKB_STATE_NOTIFY: { xcb_xkb_state_notify_event_t *ksne = (xcb_xkb_state_notify_event_t *) ev; + guint modmask; xkb_state_update_mask ( xkb.state, ksne->baseMods, ksne->latchedMods, @@ -446,6 +447,10 @@ static gboolean main_loop_x11_event_handler ( xcb_generic_event_t *ev, G_GNUC_UN ksne->baseGroup, ksne->latchedGroup, ksne->lockedGroup ); + modmask = x11_get_current_mask ( &xkb ); + if ( modmask == 0 ) { + abe_trigger_release ( ); + } break; } } diff --git a/source/view.c b/source/view.c index a338df4d..885b8051 100644 --- a/source/view.c +++ b/source/view.c @@ -550,8 +550,8 @@ void __create_window ( MenuFlags menu_flags ) 0, 0, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | - XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | - XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_1_MOTION, + XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_KEYMAP_STATE | + XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_1_MOTION, map }; @@ -1542,9 +1542,33 @@ static void rofi_view_mainloop_iter ( RofiViewState *state, xcb_generic_event_t case XCB_SELECTION_NOTIFY: rofi_view_paste ( state, (xcb_selection_notify_event_t *) ev ); break; + case XCB_KEYMAP_NOTIFY: + { + xcb_keymap_notify_event_t *kne = (xcb_keymap_notify_event_t *) ev; + guint modstate = x11_get_current_mask ( xkb ); + for ( gint32 by = 0 ; by < 32 ; ++by ) { + for ( gint8 bi = 0 ; bi < 7 ; ++bi ) { + if ( kne->keys[by] & (1 << bi) ) { + // X11 keycodes starts at 8 + xkb_keysym_t key = xkb_state_key_get_one_sym ( xkb->state, ( 8 * by + bi ) + 8 ); + abe_find_action ( modstate, key ); + } + } + } + break; + } case XCB_KEY_PRESS: rofi_view_handle_keypress ( state, xkb, (xcb_key_press_event_t *) ev ); break; + case XCB_KEY_RELEASE: + { + xcb_key_release_event_t *xkre = (xcb_key_release_event_t *) ev; + unsigned int modstate = x11_canonalize_mask ( xkre->state ); + if ( modstate == 0 ) { + abe_trigger_release ( ); + } + break; + } } // Update if requested. if ( state->refilter ) { diff --git a/source/x11-helper.c b/source/x11-helper.c index b6c0b232..8adb6c54 100644 --- a/source/x11-helper.c +++ b/source/x11-helper.c @@ -437,12 +437,28 @@ unsigned int x11_canonalize_mask ( unsigned int mask ) return mask; } +unsigned int x11_get_current_mask ( xkb_stuff *xkb ) +{ + unsigned int mask = 0; + for ( gsize i = 0 ; i < xkb_keymap_num_mods(xkb->keymap) ; ++i ) { + if ( xkb_state_mod_index_is_active ( xkb->state, i, XKB_STATE_MODS_EFFECTIVE ) ) { + mask |= ( 1 << i ); + } + } + return x11_canonalize_mask ( mask ); +} + // convert a Mod+key arg to mod mask and keysym -gboolean x11_parse_key ( char *combo, unsigned int *mod, xkb_keysym_t *key ) +gboolean x11_parse_key ( char *combo, unsigned int *mod, xkb_keysym_t *key, gboolean *release ) { GString *str = g_string_new ( "" ); unsigned int modmask = 0; + if ( g_str_has_prefix(combo, "!") ) { + ++combo; + *release = TRUE; + } + if ( strcasestr ( combo, "shift" ) ) { modmask |= x11_mod_masks[X11MOD_SHIFT]; if ( x11_mod_masks[X11MOD_SHIFT] == 0 ) {