Consume releases for compositor shortcuts
d8086fdebfc429d42c04f54b07c66a34ed4ee446
1 parent
c8cc6a7b
swm.c
+29 -3
| 255 | 255 | bool is_virtual; |
|
| 256 | 256 | int nsyms; |
|
| 257 | 257 | const xkb_keysym_t *keysyms; /* Valid only when nsyms is nonzero. */ |
|
| 258 | 258 | uint32_t mods; /* Valid only when nsyms is nonzero. */ |
|
| 259 | 259 | struct wl_event_source *key_repeat_source; |
|
| 260 | + | uint32_t handled_keys[(KEY_CNT + 31) / 32]; |
|
| 260 | 261 | struct wl_listener modifiers; |
|
| 261 | 262 | struct wl_listener key; |
|
| 262 | 263 | struct wl_listener destroy; |
|
| 263 | 264 | } keyboard_group_t; |
|
| 264 | 265 |
| 3753 | 3754 | /* Handle window-manager shortcuts before applications receive the key. */ |
|
| 3754 | 3755 | const key_t *k; |
|
| 3755 | 3756 | int inhibited = shortcuts_inhibited(); |
|
| 3756 | 3757 | ||
| 3757 | 3758 | for (k = keys; k < END(keys); k++) { |
|
| 3759 | + | ||
| 3758 | 3760 | if (CLEANMASK(mods) == CLEANMASK(k->mod) && |
|
| 3759 | 3761 | xkb_keysym_to_lower(sym) == xkb_keysym_to_lower(k->keysym) && k->func) { |
|
| 3760 | 3762 | int repeatable = k->func == focus_stack || k->func == swap_client || |
|
| 3761 | 3763 | k->func == stack_config || k->func == cycle_workspace; |
|
| 3762 | 3764 |
| 3772 | 3774 | } |
|
| 3773 | 3775 | } |
|
| 3774 | 3776 | return 0; |
|
| 3775 | 3777 | } |
|
| 3776 | 3778 | ||
| 3779 | + | /* Track compositor-consumed presses so their unmatched releases are not sent |
|
| 3780 | + | * to the focused application. */ |
|
| 3781 | + | static bool key_event_should_forward( |
|
| 3782 | + | keyboard_group_t *group, uint32_t keycode, uint32_t state, bool handled |
|
| 3783 | + | ) { |
|
| 3784 | + | uint32_t *word; |
|
| 3785 | + | uint32_t mask; |
|
| 3786 | + | ||
| 3787 | + | if (keycode >= KEY_CNT) |
|
| 3788 | + | return !handled; |
|
| 3789 | + | word = &group->handled_keys[keycode / 32]; |
|
| 3790 | + | mask = 1u << (keycode % 32); |
|
| 3791 | + | ||
| 3792 | + | if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { |
|
| 3793 | + | if (handled) |
|
| 3794 | + | *word |= mask; |
|
| 3795 | + | else |
|
| 3796 | + | *word &= ~mask; |
|
| 3797 | + | return !handled; |
|
| 3798 | + | } |
|
| 3799 | + | if (state == WL_KEYBOARD_KEY_STATE_RELEASED && (*word & mask)) { |
|
| 3800 | + | *word &= ~mask; |
|
| 3801 | + | return false; |
|
| 3802 | + | } |
|
| 3803 | + | return true; |
|
| 3804 | + | } |
|
| 3777 | 3805 | /* Handle shortcuts and forward unhandled key events to the focused application. */ |
|
| 3778 | 3806 | void key_press(struct wl_listener *listener, void *data) { |
|
| 3779 | 3807 | int i; |
|
| 3780 | 3808 | ||
| 3781 | 3809 | /* This event is raised when a key is pressed or released. */ |
| 3798 | 3826 | for (i = 0; i < nsyms; i++) |
|
| 3799 | 3827 | handled |= key_binding(mods, syms[i], 0); |
|
| 3800 | 3828 | } |
|
| 3801 | 3829 | if ((handled & 2) && group->wlr_group->keyboard.repeat_info.delay > 0) { |
|
| 3802 | 3830 | group->mods = mods; |
|
| 3803 | - | group->keysyms = syms; |
|
| 3804 | - | group->nsyms = nsyms; |
|
| 3805 | 3831 | wl_event_source_timer_update( |
|
| 3806 | 3832 | group->key_repeat_source, group->wlr_group->keyboard.repeat_info.delay |
|
| 3807 | 3833 | ); |
|
| 3808 | 3834 | } else { |
|
| 3809 | 3835 | group->nsyms = 0; |
|
| 3810 | 3836 | wl_event_source_timer_update(group->key_repeat_source, 0); |
|
| 3811 | 3837 | } |
|
| 3812 | - | if (handled) |
|
| 3838 | + | if (!key_event_should_forward(group, event->keycode, event->state, handled)) |
|
| 3813 | 3839 | return; |
|
| 3814 | 3840 | ||
| 3815 | 3841 | wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); |
|
| 3816 | 3842 | /* Forward unhandled keys to the focused application. */ |
|
| 3817 | 3843 | wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state); |
test/unit.c
+10 -0
| 942 | 942 | create_pointer_constraint(nullptr, &constraint); |
|
| 943 | 943 | ||
| 944 | 944 | for (i = 0; i < LENGTH(pointer_constraint_used); i++) |
|
| 945 | 945 | ||
| 946 | 946 | if (pointer_constraint_used[i]) { |
|
| 947 | + | ||
| 947 | 948 | wrapper = &pointer_constraint_items[i]; |
|
| 948 | 949 | break; |
|
| 949 | 950 | } |
|
| 950 | 951 | assert(wrapper && wrapper->constraint == &constraint); |
|
| 951 | 952 | destroy_pointer_constraint(&wrapper->destroy, nullptr); |
| 964 | 965 | assert(client.bw == 0); |
|
| 965 | 966 | maximize_notify(&client.maximize, nullptr); |
|
| 966 | 967 | create_decoration(nullptr, &decoration); |
|
| 967 | 968 | urgent(nullptr, &(struct wlr_xdg_activation_v1_request_activate_event){}); |
|
| 968 | 969 | } |
|
| 970 | + | /* Verify a consumed shortcut press also consumes its release. */ |
|
| 971 | + | static void test_shortcut_release(void) { |
|
| 972 | + | keyboard_group_t group = {}; |
|
| 973 | + | ||
| 974 | + | assert(!key_event_should_forward(&group, KEY_P, WL_KEYBOARD_KEY_STATE_PRESSED, true)); |
|
| 975 | + | assert(!key_event_should_forward(&group, KEY_P, WL_KEYBOARD_KEY_STATE_RELEASED, false)); |
|
| 976 | + | assert(key_event_should_forward(&group, KEY_A, WL_KEYBOARD_KEY_STATE_RELEASED, false)); |
|
| 977 | + | } |
|
| 969 | 978 | ||
| 970 | 979 | /* Verify modifier handling, repeatable bindings, and key-repeat dispatch. */ |
|
| 971 | 980 | static void test_keybindings_and_repeat(void) { |
|
| 972 | 981 | struct wlr_seat fake_seat = {}; |
|
| 973 | 982 | struct wlr_keyboard_group keyboard_group = {}; |
| 1027 | 1036 | { "small-helpers", test_small_helpers }, |
|
| 1028 | 1037 | { "pointer-grab-button", test_pointer_grab_button }, |
|
| 1029 | 1038 | { "client-accessors", test_client_accessors }, |
|
| 1030 | 1039 | { "autostart", test_autostart_execution }, |
|
| 1031 | 1040 | { "pointer-gestures", test_pointer_gestures }, |
|
| 1041 | + | { "shortcut-release", test_shortcut_release }, |
|
| 1032 | 1042 | { "input-method", test_input_method_lifecycle }, |
|
| 1033 | 1043 | { "input-early-paths", test_input_early_paths }, |
|
| 1034 | 1044 | { "command-line", test_command_line_errors }, |
|
| 1035 | 1045 | { "pointer-and-decoration", test_pointer_and_decoration_lifecycle }, |
|
| 1036 | 1046 | { "keybindings-and-repeat", test_keybindings_and_repeat }, |