@ -9,88 +9,93 @@
# include "sway/commands.h"
# include "log.h"
static bool keysym_is_modifier ( xkb_keysym_t keysym ) {
switch ( keysym ) {
case XKB_KEY_Shift_L : case XKB_KEY_Shift_R :
case XKB_KEY_Control_L : case XKB_KEY_Control_R :
case XKB_KEY_Caps_Lock :
case XKB_KEY_Shift_Lock :
case XKB_KEY_Meta_L : case XKB_KEY_Meta_R :
case XKB_KEY_Alt_L : case XKB_KEY_Alt_R :
case XKB_KEY_Super_L : case XKB_KEY_Super_R :
case XKB_KEY_Hyper_L : case XKB_KEY_Hyper_R :
return true ;
default :
return false ;
}
}
static size_t pressed_keysyms_length ( xkb_keysym_t * pressed_keysyms ) {
size_t n = 0 ;
for ( size_t i = 0 ; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP ; + + i ) {
if ( pressed_keysyms [ i ] ! = XKB_KEY_NoSymbol ) {
+ + n ;
/**
* Update the shortcut model state in response to new input
*/
static void update_shortcut_model ( struct sway_shortcut_state * state ,
struct wlr_event_keyboard_key * event ,
uint32_t new_key ,
bool last_key_was_a_modifier ) {
if ( event - > state = = WLR_KEY_PRESSED ) {
if ( last_key_was_a_modifier & & state - > last_key_index > = 0 ) {
// Last pressed key before this one was a modifier. We nullify
// the key id but not the keycode (as that is used for erasure
// on release)
state - > pressed_keys [ state - > last_key_index ] = 0 ;
state - > last_key_index = - 1 ;
}
}
return n ;
}
static ssize_t pressed_keysyms_index ( xkb_keysym_t * pressed_keysyms ,
xkb_keysym_t keysym ) {
for ( size_t i = 0 ; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP ; + + i ) {
if ( pressed_keysyms [ i ] = = keysym ) {
return i ;
// Add current key to set; there may be duplicates
for ( size_t i = 0 ; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP ; i + + ) {
if ( ! state - > pressed_keys [ i ] ) {
state - > pressed_keys [ i ] = new_key ;
state - > pressed_keycodes [ i ] = event - > keycode ;
state - > last_key_index = i ;
break ;
}
}
}
return - 1 ;
}
static void pressed_keysyms_add ( xkb_keysym_t * pressed_keysyms ,
xkb_keysym_t keysym ) {
ssize_t i = pressed_keysyms_index ( pressed_keysyms , keysym ) ;
if ( i < 0 ) {
i = pressed_keysyms_index ( pressed_keysyms , XKB_KEY_NoSymbol ) ;
if ( i > = 0 ) {
pressed_keysyms [ i ] = keysym ;
} else {
for ( size_t i = 0 ; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP ; i + + ) {
// The same keycode may match multiple keysyms.
if ( state - > pressed_keycodes [ i ] = = event - > keycode ) {
state - > pressed_keys [ i ] = 0 ;
state - > pressed_keycodes [ i ] = 0 ;
}
}
}
}
static void pressed_keysyms_remove ( xkb_keysym_t * pressed_keysyms ,
xkb_keysym_t keysym ) {
ssize_t i = pressed_keysyms_index ( pressed_keysyms , keysym ) ;
if ( i > = 0 ) {
pressed_keysyms [ i ] = XKB_KEY_NoSymbol ;
/**
*
* Returns a binding which matches the shortcut model state ( ignoring the
* ` release ` flag ) .
*/
static struct sway_binding * check_shortcut_model (
struct sway_shortcut_state * state , list_t * bindings ,
uint32_t modifiers , bool locked ) {
int npressed_keys = 0 ;
for ( size_t i = 0 ; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP ; i + + ) {
if ( state - > pressed_keys [ i ] ) {
+ + npressed_keys ;
}
}
}
for ( int i = 0 ; i < bindings - > length ; + + i ) {
struct sway_binding * binding = bindings - > items [ i ] ;
static void pressed_keysyms_update ( xkb_keysym_t * pressed_keysyms ,
const xkb_keysym_t * keysyms , size_t keysyms_len ,
enum wlr_key_state state ) {
for ( size_t i = 0 ; i < keysyms_len ; + + i ) {
if ( keysym_is_modifier ( keysyms [ i ] ) ) {
if ( modifiers ^ binding - > modifiers | |
npressed_keys ! = binding - > keys - > length | |
locked > binding - > locked ) {
continue ;
}
if ( state = = WLR_KEY_PRESSED ) {
pressed_keysyms_add ( pressed_keysyms , keysyms [ i ] ) ;
} else { // WLR_KEY_RELEASED
pressed_keysyms_remove ( pressed_keysyms , keysyms [ i ] ) ;
bool match = true ;
for ( int j = 0 ; j < binding - > keys - > length ; + + j ) {
uint32_t key = * ( uint32_t * ) binding - > keys - > items [ j ] ;
bool key_found = false ;
for ( int k = 0 ; k < SWAY_KEYBOARD_PRESSED_KEYS_CAP ; k + + ) {
if ( state - > pressed_keys [ k ] = = key ) {
key_found = true ;
break ;
}
}
if ( ! key_found ) {
match = false ;
break ;
}
}
}
}
static bool binding_matches_key_state ( struct sway_binding * binding ,
enum wlr_key_state key_state ) {
if ( key_state = = WLR_KEY_PRESSED & & ! binding - > release ) {
return true ;
}
if ( key_state = = WLR_KEY_RELEASED & & binding - > release ) {
return true ;
if ( match ) {
return binding ;
}
}
return false ;
return NULL ;
}
/**
* Execute the command associated to a binding
*/
static void keyboard_execute_command ( struct sway_keyboard * keyboard ,
struct sway_binding * binding ) {
wlr_log ( L_DEBUG , " running command for binding: %s " ,
@ -113,7 +118,7 @@ static void keyboard_execute_command(struct sway_keyboard *keyboard,
* should be propagated to clients .
*/
static bool keyboard_execute_compositor_binding ( struct sway_keyboard * keyboard ,
xkb_keysym_t * pressed_keysyms , uint32_t modifiers , size_t keysyms_len ) {
const xkb_keysym_t * pressed_keysyms , uint32_t modifiers , size_t keysyms_len ) {
for ( size_t i = 0 ; i < keysyms_len ; + + i ) {
xkb_keysym_t keysym = pressed_keysyms [ i ] ;
if ( keysym > = XKB_KEY_XF86Switch_VT_1 & &
@ -133,157 +138,6 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
return false ;
}
/**
* Execute keyboard bindings bound with ` bindysm ` for the given keyboard state .
*
* Returns true if the keysym was handled by a binding and false if the event
* should be propagated to clients .
*/
static bool keyboard_execute_bindsym ( struct sway_keyboard * keyboard ,
xkb_keysym_t * pressed_keysyms , uint32_t modifiers ,
enum wlr_key_state key_state , bool locked ) {
// configured bindings
int n = pressed_keysyms_length ( pressed_keysyms ) ;
list_t * keysym_bindings = config - > current_mode - > keysym_bindings ;
for ( int i = 0 ; i < keysym_bindings - > length ; + + i ) {
struct sway_binding * binding = keysym_bindings - > items [ i ] ;
if ( ! binding_matches_key_state ( binding , key_state ) | |
modifiers ^ binding - > modifiers | |
n ! = binding - > keys - > length | | locked > binding - > locked ) {
continue ;
}
bool match = true ;
for ( int j = 0 ; j < binding - > keys - > length ; + + j ) {
match =
pressed_keysyms_index ( pressed_keysyms ,
* ( int * ) binding - > keys - > items [ j ] ) > = 0 ;
if ( ! match ) {
break ;
}
}
if ( match ) {
keyboard_execute_command ( keyboard , binding ) ;
return true ;
}
}
return false ;
}
static bool binding_matches_keycodes ( struct wlr_keyboard * keyboard ,
struct sway_binding * binding , struct wlr_event_keyboard_key * event , bool locked ) {
assert ( binding - > bindcode ) ;
uint32_t keycode = event - > keycode + 8 ;
if ( ! binding_matches_key_state ( binding , event - > state ) ) {
return false ;
}
if ( locked > binding - > locked ) {
return false ;
}
uint32_t modifiers = wlr_keyboard_get_modifiers ( keyboard ) ;
if ( modifiers ^ binding - > modifiers ) {
return false ;
}
// on release, the released key must be in the binding
if ( event - > state = = WLR_KEY_RELEASED ) {
bool found = false ;
for ( int i = 0 ; i < binding - > keys - > length ; + + i ) {
uint32_t binding_keycode = * ( uint32_t * ) binding - > keys - > items [ i ] + 8 ;
if ( binding_keycode = = keycode ) {
found = true ;
break ;
}
}
if ( ! found ) {
return false ;
}
}
// every keycode in the binding must be present in the pressed keys on the
// keyboard
for ( int i = 0 ; i < binding - > keys - > length ; + + i ) {
uint32_t binding_keycode = * ( uint32_t * ) binding - > keys - > items [ i ] + 8 ;
if ( event - > state = = WLR_KEY_RELEASED & & keycode = = binding_keycode ) {
continue ;
}
bool found = false ;
for ( size_t j = 0 ; j < keyboard - > num_keycodes ; + + j ) {
xkb_keycode_t keycode = keyboard - > keycodes [ j ] + 8 ;
if ( keycode = = binding_keycode ) {
found = true ;
break ;
}
}
if ( ! found ) {
return false ;
}
}
// every keycode pressed on the keyboard must be present within the binding
// keys (unless it is a modifier)
for ( size_t i = 0 ; i < keyboard - > num_keycodes ; + + i ) {
xkb_keycode_t keycode = keyboard - > keycodes [ i ] + 8 ;
bool found = false ;
for ( int j = 0 ; j < binding - > keys - > length ; + + j ) {
uint32_t binding_keycode = * ( uint32_t * ) binding - > keys - > items [ j ] + 8 ;
if ( binding_keycode = = keycode ) {
found = true ;
break ;
}
}
if ( ! found ) {
if ( ! binding - > modifiers ) {
return false ;
}
// check if it is a modifier, which we know matched from the check
// above
const xkb_keysym_t * keysyms ;
int num_keysyms =
xkb_state_key_get_syms ( keyboard - > xkb_state ,
keycode , & keysyms ) ;
if ( num_keysyms ! = 1 | | ! keysym_is_modifier ( keysyms [ 0 ] ) ) {
return false ;
}
}
}
return true ;
}
/**
* Execute keyboard bindings bound with ` bindcode ` for the given keyboard state .
*
* Returns true if the keysym was handled by a binding and false if the event
* should be propagated to clients .
*/
static bool keyboard_execute_bindcode ( struct sway_keyboard * keyboard ,
struct wlr_event_keyboard_key * event , bool locked ) {
struct wlr_keyboard * wlr_keyboard =
keyboard - > seat_device - > input_device - > wlr_device - > keyboard ;
list_t * keycode_bindings = config - > current_mode - > keycode_bindings ;
for ( int i = 0 ; i < keycode_bindings - > length ; + + i ) {
struct sway_binding * binding = keycode_bindings - > items [ i ] ;
if ( binding_matches_keycodes ( wlr_keyboard , binding , event , locked ) ) {
keyboard_execute_command ( keyboard , binding ) ;
return true ;
}
}
return false ;
}
/**
* Get keysyms and modifiers from the keyboard as xkb sees them .
*
@ -339,61 +193,100 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
struct wlr_event_keyboard_key * event = data ;
bool input_inhibited = keyboard - > seat_device - > sway_seat - > exclusive_client ! = NULL ;
// Identify new keycode, raw keysym(s), and translated keysym(s)
xkb_keycode_t keycode = event - > keycode + 8 ;
bool handled = false ;
// handle keycodes
handled = keyboard_execute_bindcode ( keyboard , event , input_inhibited ) ;
// handle translated keysyms
if ( ! handled & & event - > state = = WLR_KEY_RELEASED ) {
handled = keyboard_execute_bindsym ( keyboard ,
keyboard - > pressed_keysyms_translated ,
keyboard - > modifiers_translated ,
event - > state , input_inhibited ) ;
}
const xkb_keysym_t * translated_keysyms ;
uint32_t translated_modifiers ;
size_t translated_keysyms_len =
keyboard_keysyms_translated ( keyboard , keycode , & translated_keysyms ,
& keyboard - > modifiers_translated ) ;
pressed_keysyms_update ( keyboard - > pressed_keysyms_translated ,
translated_keysyms , translated_keysyms_len , event - > state ) ;
if ( ! handled & & event - > state = = WLR_KEY_PRESSED ) {
handled = keyboard_execute_bindsym ( keyboard ,
keyboard - > pressed_keysyms_translated ,
keyboard - > modifiers_translated ,
event - > state , input_inhibited ) ;
}
& translated_modifiers ) ;
// Handle raw keysyms
if ( ! handled & & event - > state = = WLR_KEY_RELEASED ) {
handled = keyboard_execute_bindsym ( keyboard ,
keyboard - > pressed_keysyms_raw , keyboard - > modifiers_raw ,
event - > state , input_inhibited ) ;
}
const xkb_keysym_t * raw_keysyms ;
uint32_t raw_modifiers ;
size_t raw_keysyms_len =
keyboard_keysyms_raw ( keyboard , keycode , & raw_keysyms , & keyboard - > modifiers_raw ) ;
pressed_keysyms_update ( keyboard - > pressed_keysyms_raw , raw_keysyms ,
raw_keysyms_len , event - > state ) ;
if ( ! handled & & event - > state = = WLR_KEY_PRESSED ) {
handled = keyboard_execute_bindsym ( keyboard ,
keyboard - > pressed_keysyms_raw , keyboard - > modifiers_raw ,
event - > state , input_inhibited ) ;
keyboard_keysyms_raw ( keyboard , keycode , & raw_keysyms , & raw_modifiers ) ;
struct wlr_input_device * device =
keyboard - > seat_device - > input_device - > wlr_device ;
uint32_t code_modifiers = wlr_keyboard_get_modifiers ( device - > keyboard ) ;
bool last_key_was_a_modifier = code_modifiers ! = keyboard - > last_modifiers ;
keyboard - > last_modifiers = code_modifiers ;
// Update shortcut models
update_shortcut_model ( & keyboard - > state_keycodes , event ,
( uint32_t ) keycode , last_key_was_a_modifier ) ;
for ( size_t i = 0 ; i < translated_keysyms_len ; i + + ) {
update_shortcut_model ( & keyboard - > state_keysyms_translated ,
event , ( uint32_t ) translated_keysyms [ i ] ,
last_key_was_a_modifier ) ;
}
for ( size_t i = 0 ; i < raw_keysyms_len ; i + + ) {
update_shortcut_model ( & keyboard - > state_keysyms_raw ,
event , ( uint32_t ) raw_keysyms [ i ] ,
last_key_was_a_modifier ) ;
}
// identify which binding should be executed.
struct sway_binding * binding =
check_shortcut_model ( & keyboard - > state_keycodes ,
config - > current_mode - > keycode_bindings ,
code_modifiers , input_inhibited ) ;
for ( size_t i = 0 ; i < translated_keysyms_len ; i + + ) {
struct sway_binding * translated_binding =
check_shortcut_model ( & keyboard - > state_keysyms_translated ,
config - > current_mode - > keysym_bindings ,
translated_modifiers , input_inhibited ) ;
if ( translated_binding & & ! binding ) {
binding = translated_binding ;
} else if ( binding & & translated_binding & & binding ! = translated_binding ) {
wlr_log ( L_DEBUG , " encountered duplicate bindings %d and %d " ,
binding - > order , translated_binding - > order ) ;
}
}
for ( size_t i = 0 ; i < raw_keysyms_len ; i + + ) {
struct sway_binding * raw_binding =
check_shortcut_model ( & keyboard - > state_keysyms_raw ,
config - > current_mode - > keysym_bindings ,
raw_modifiers , input_inhibited ) ;
if ( raw_binding & & ! binding ) {
binding = raw_binding ;
} else if ( binding & & raw_binding & & binding ! = raw_binding ) {
wlr_log ( L_DEBUG , " encountered duplicate bindings %d and %d " ,
binding - > order , raw_binding - > order ) ;
}
}
bool handled = false ;
// Execute the identified binding if need be.
if ( keyboard - > held_binding & & binding ! = keyboard - > held_binding & &
event - > state = = WLR_KEY_RELEASED ) {
keyboard_execute_command ( keyboard , keyboard - > held_binding ) ;
handled = true ;
}
if ( binding ! = keyboard - > held_binding ) {
keyboard - > held_binding = NULL ;
}
if ( binding & & event - > state = = WLR_KEY_PRESSED ) {
if ( binding - > release ) {
keyboard - > held_binding = binding ;
} else {
keyboard_execute_command ( keyboard , binding ) ;
handled = true ;
}
}
// Compositor bindings
if ( ! handled & & event - > state = = WLR_KEY_PRESSED ) {
handled =
keyboard_execute_compositor_binding ( keyboard ,
keyboard - > pressed_keysyms_translated ,
keyboard - > modifiers_translated ,
handled = keyboard_execute_compositor_binding (
keyboard , translated_keysyms , translated_modifiers ,
translated_keysyms_len ) ;
}
if ( ! handled & & event - > state = = WLR_KEY_PRESSED ) {
handled =
keyboard_execute_compositor_binding ( keyboard ,
keyboard - > pressed_keysyms_raw , keyboard - > modifiers_raw ,
handled = keyboard_execute_compositor_binding (
keyboard , raw_keysyms , raw_modifiers ,
raw_keysyms_len ) ;
}
@ -429,6 +322,10 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
wl_list_init ( & keyboard - > keyboard_key . link ) ;
wl_list_init ( & keyboard - > keyboard_modifiers . link ) ;
keyboard - > state_keycodes . last_key_index = - 1 ;
keyboard - > state_keysyms_raw . last_key_index = - 1 ;
keyboard - > state_keysyms_translated . last_key_index = - 1 ;
return keyboard ;
}