kitty keyboard protocol: Specify the behavior of the modifier bits during modifier key events

Fixes #6913
This commit is contained in:
Kovid Goyal
2023-12-24 12:06:48 +05:30
parent 79db8b43e0
commit 91cdf4af00
3 changed files with 51 additions and 23 deletions

View File

@@ -67,6 +67,8 @@ Detailed list of changes
- panel kitten: Fix rendering with non-zero margin.padding in kitty.conf (:iss:`6923`) - panel kitten: Fix rendering with non-zero margin.padding in kitty.conf (:iss:`6923`)
- kitty keyboard protocol: Specify the behavior of the modifier bits during modifier key events (:iss:`6913`)
0.31.0 [2023-11-08] 0.31.0 [2023-11-08]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -186,12 +186,12 @@ In the escape code, the modifier value is encoded as a decimal number which is
and so on. If the modifier field is not present in the escape code, its default and so on. If the modifier field is not present in the escape code, its default
value is ``1`` which means no modifiers. value is ``1`` which means no modifiers.
When the key event is related to an actual modifier key, the corresponding modifier's bit When the key event is related to an actual modifier key, the corresponding
must be set for the press event and reset for the release event. For example modifier's bit must be set to the modifier state including the effect for the
when pressing the :kbd:`LEFT_CONTROL` key, the ``ctrl`` bit must be set and current event. For example, when pressing the :kbd:`LEFT_CONTROL` key, the
when releasing it, it must be reset. When both left and right control keys are ``ctrl`` bit must be set and when releasing it, it must be reset. When both
pressed and one is released, the release event must again have the ``ctrl`` bit left and right control keys are pressed and one is released, the release event
reset. must have the ``ctrl`` bit set. See :iss:`6913` for discussion of this design.
.. _event_types: .. _event_types:

View File

@@ -390,25 +390,31 @@ refresh_callback(GLFWwindow *w) {
static int mods_at_last_key_or_button_event = 0; static int mods_at_last_key_or_button_event = 0;
#ifndef __APPLE__
typedef struct modifier_key_state {
bool left, right;
} modifier_key_state;
static int static int
key_to_modifier(uint32_t key) { key_to_modifier(uint32_t key, bool *is_left) {
*is_left = false;
switch(key) { switch(key) {
case GLFW_FKEY_LEFT_SHIFT: case GLFW_FKEY_LEFT_SHIFT: *is_left = true; /* fallthrough */
case GLFW_FKEY_RIGHT_SHIFT: case GLFW_FKEY_RIGHT_SHIFT:
return GLFW_MOD_SHIFT; return GLFW_MOD_SHIFT;
case GLFW_FKEY_LEFT_CONTROL: case GLFW_FKEY_LEFT_CONTROL: *is_left = true; /* fallthrough */
case GLFW_FKEY_RIGHT_CONTROL: case GLFW_FKEY_RIGHT_CONTROL:
return GLFW_MOD_CONTROL; return GLFW_MOD_CONTROL;
case GLFW_FKEY_LEFT_ALT: case GLFW_FKEY_LEFT_ALT: *is_left = true; /* fallthrough */
case GLFW_FKEY_RIGHT_ALT: case GLFW_FKEY_RIGHT_ALT:
return GLFW_MOD_ALT; return GLFW_MOD_ALT;
case GLFW_FKEY_LEFT_SUPER: case GLFW_FKEY_LEFT_SUPER: *is_left = true; /* fallthrough */
case GLFW_FKEY_RIGHT_SUPER: case GLFW_FKEY_RIGHT_SUPER:
return GLFW_MOD_SUPER; return GLFW_MOD_SUPER;
case GLFW_FKEY_LEFT_HYPER: case GLFW_FKEY_LEFT_HYPER: *is_left = true; /* fallthrough */
case GLFW_FKEY_RIGHT_HYPER: case GLFW_FKEY_RIGHT_HYPER:
return GLFW_MOD_HYPER; return GLFW_MOD_HYPER;
case GLFW_FKEY_LEFT_META: case GLFW_FKEY_LEFT_META: *is_left = true; /* fallthrough */
case GLFW_FKEY_RIGHT_META: case GLFW_FKEY_RIGHT_META:
return GLFW_MOD_META; return GLFW_MOD_META;
default: default:
@@ -416,20 +422,40 @@ key_to_modifier(uint32_t key) {
} }
} }
static void
update_modifier_state_on_modifier_key_event(GLFWkeyevent *ev, int key_modifier, bool is_left) {
// Update mods state to be what the kitty keyboard protocol requires, as on Linux modifier key events do not update modifier bits
static modifier_key_state all_states[8] = {0};
modifier_key_state *state = all_states + MIN((unsigned)__builtin_ctz(key_modifier), sizeof(all_states)-1);
const int modifier_was_set_before_event = ev->mods & key_modifier;
const bool is_release = ev->action == GLFW_RELEASE;
if (modifier_was_set_before_event) {
// a press with modifier already set means other modifier key is pressed
if (!is_release) { if (is_left) state->right = true; else state->left = true; }
} else {
// if modifier is not set before event, means both keys are released
state->left = false; state->right = false;
}
if (is_release) {
if (is_left) state->left = false; else state->right = false;
if (modifier_was_set_before_event && !state->left && !state->right) ev->mods &= ~key_modifier;
} else {
if (is_left) state->left = true; else state->right = true;
ev->mods |= key_modifier;
}
}
#endif
static void static void
key_callback(GLFWwindow *w, GLFWkeyevent *ev) { key_callback(GLFWwindow *w, GLFWkeyevent *ev) {
if (!set_callback_window(w)) return; if (!set_callback_window(w)) return;
#ifndef __APPLE__
bool is_left;
int key_modifier = key_to_modifier(ev->key, &is_left);
if (key_modifier != -1) update_modifier_state_on_modifier_key_event(ev, key_modifier, is_left);
#endif
mods_at_last_key_or_button_event = ev->mods; mods_at_last_key_or_button_event = ev->mods;
int key_modifier = key_to_modifier(ev->key);
if (key_modifier != -1) {
if (ev->action == GLFW_RELEASE) {
mods_at_last_key_or_button_event &= ~key_modifier;
} else {
mods_at_last_key_or_button_event |= key_modifier;
}
// Normalize mods state to be what the kitty keyboard protocol requires
ev->mods = mods_at_last_key_or_button_event;
}
global_state.callback_os_window->cursor_blink_zero_time = monotonic(); global_state.callback_os_window->cursor_blink_zero_time = monotonic();
if (is_window_ready_for_callbacks()) on_key_input(ev); if (is_window_ready_for_callbacks()) on_key_input(ev);
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;