Add NumLock and CapsLock reporting to the keyboard protocol

This commit is contained in:
Kovid Goyal 2021-04-13 07:10:00 +05:30
parent c989a7198b
commit 4c644b8556
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
9 changed files with 62 additions and 27 deletions

View File

@ -141,16 +141,19 @@ Modifiers
~~~~~~~~~~~~~~
This protocol supports six modifier keys, :kbd:`shift, alt, ctrl, super, hyper
and meta`. Here :kbd:`super` is either the *Windows/Linux* key or the *Cmd* key on
mac keyboards. :kbd:`hyper` and :kbd:`meta` are typically present only on X11
based systems with special XKB rules. Modifiers are encoded as a bit field with::
and meta` as well as :kbd:`num_lock and caps_lock`. Here :kbd:`super` is either
the *Windows/Linux* key or the *Cmd* key on mac keyboards. :kbd:`hyper` and
:kbd:`meta` are typically present only on X11 based systems with special XKB
rules. Modifiers are encoded as a bit field with::
shift 0b1 (1)
alt 0b10 (2)
ctrl 0b100 (4)
super 0b1000 (8)
hyper 0b10000 (16)
meta 0b100000 (32)
shift 0b1 (1)
alt 0b10 (2)
ctrl 0b100 (4)
super 0b1000 (8)
hyper 0b10000 (16)
meta 0b100000 (32)
caps_lock 0b1000000 (64)
num_lock 0b10000000 (128)
In the escape code, the modifier value is encoded as a decimal number which is
``1 + actual modifiers``. So to represent :kbd:`shift` only, the value would be ``1 +

1
glfw/glfw3.h vendored
View File

@ -510,7 +510,6 @@ typedef enum {
*
* If this bit is set the Num Lock key is enabled and the @ref
* GLFW_LOCK_KEY_MODS input mode is set.
* @note Ravi: Num lock is not supported in this branch
*/
#define GLFW_MOD_NUM_LOCK 0x0080

View File

@ -6,8 +6,8 @@ import sys
from typing import List
from kitty.key_encoding import (
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, SUPER, HYPER, META, KeyEvent,
encode_key_event
ALT, CAPS_LOCK, CTRL, HYPER, META, NUM_LOCK, PRESS, RELEASE, REPEAT, SHIFT,
SUPER, KeyEvent, encode_key_event
)
from ..tui.handler import Handler
@ -34,7 +34,10 @@ class KeysHandler(Handler):
CTRL: 'Ctrl',
SUPER: 'Super',
HYPER: 'Hyper',
META: 'Meta'}.items():
META: 'Meta',
NUM_LOCK: 'NumLock',
CAPS_LOCK: 'CapsLock',
}.items():
if key_event.mods & m:
lmods.append(name)
mods = '+'.join(lmods)

View File

@ -761,11 +761,12 @@ ShortcutMap = Dict[Tuple[SingleKey, ...], KeyAction]
def print_shortcut(key_sequence: Iterable[SingleKey], action: KeyAction) -> None:
from .fast_data_types import (
GLFW_MOD_ALT, GLFW_MOD_CONTROL, GLFW_MOD_SHIFT, GLFW_MOD_SUPER, GLFW_MOD_HYPER, GLFW_MOD_META,
GLFW_MOD_ALT, GLFW_MOD_CAPS_LOCK, GLFW_MOD_CONTROL, GLFW_MOD_HYPER,
GLFW_MOD_META, GLFW_MOD_NUM_LOCK, GLFW_MOD_SHIFT, GLFW_MOD_SUPER,
glfw_get_key_name
)
modmap = {'shift': GLFW_MOD_SHIFT, 'alt': GLFW_MOD_ALT, 'ctrl': GLFW_MOD_CONTROL, ('cmd' if is_macos else 'super'): GLFW_MOD_SUPER,
'hyper': GLFW_MOD_HYPER, 'meta': GLFW_MOD_META}
'hyper': GLFW_MOD_HYPER, 'meta': GLFW_MOD_META, 'num_lock': GLFW_MOD_NUM_LOCK, 'caps_lock': GLFW_MOD_CAPS_LOCK}
keys = []
for key_spec in key_sequence:
names = []

View File

@ -136,6 +136,8 @@ GLFW_MOD_ALT: int
GLFW_MOD_SUPER: int
GLFW_MOD_HYPER: int
GLFW_MOD_META: int
GLFW_MOD_CAPS_LOCK: int
GLFW_MOD_NUM_LOCK: int
GLFW_MOD_KITTY: int
GLFW_MOUSE_BUTTON_1: int
GLFW_MOUSE_BUTTON_2: int

View File

@ -677,6 +677,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
if (is_first_window) glfwSwapInterval(OPT(sync_to_monitor) && !global_state.is_wayland ? 1 : 0);
#endif
glfwSwapBuffers(glfw_window);
glfwSetInputMode(glfw_window, GLFW_LOCK_KEY_MODS, true);
if (!global_state.is_wayland) {
PyObject *pret = PyObject_CallFunction(pre_show_callback, "N", native_window_handle(glfw_window));
if (pret == NULL) return NULL;
@ -1568,6 +1569,8 @@ init_glfw(PyObject *m) {
ADDC(GLFW_MOD_HYPER);
ADDC(GLFW_MOD_META);
ADDC(GLFW_MOD_KITTY);
ADDC(GLFW_MOD_CAPS_LOCK);
ADDC(GLFW_MOD_NUM_LOCK);
// --- Mouse -------------------------------------------------------------------
ADDC(GLFW_MOUSE_BUTTON_1);

View File

@ -8,12 +8,12 @@
#include "keys.h"
#include "charsets.h"
typedef enum { SHIFT=1, ALT=2, CTRL=4, SUPER=8, HYPER=16, META=32} ModifierMasks;
typedef enum { SHIFT=1, ALT=2, CTRL=4, SUPER=8, HYPER=16, META=32, CAPS_LOCK=64, NUM_LOCK=128} ModifierMasks;
typedef enum { PRESS = 0, REPEAT = 1, RELEASE = 2} KeyAction;
typedef struct {
uint32_t key, shifted_key, alternate_key;
struct {
bool shift, alt, ctrl, super, hyper, meta;
bool shift, alt, ctrl, super, hyper, meta, numlock, capslock;
unsigned value;
char encoded[4];
} mods;
@ -49,12 +49,15 @@ is_modifier_key(const uint32_t key) {
static inline void
convert_glfw_mods(int mods, KeyEvent *ev) {
ev->mods.alt = (mods & GLFW_MOD_ALT) > 0, ev->mods.ctrl = (mods & GLFW_MOD_CONTROL) > 0, ev->mods.shift = (mods & GLFW_MOD_SHIFT) > 0, ev->mods.super = (mods & GLFW_MOD_SUPER) > 0, ev->mods.hyper = (mods & GLFW_MOD_HYPER) > 0, ev->mods.meta = (mods & GLFW_MOD_META) > 0;
ev->mods.numlock = (mods & GLFW_MOD_NUM_LOCK) > 0, ev->mods.capslock = (mods & GLFW_MOD_CAPS_LOCK) > 0;
ev->mods.value = ev->mods.shift ? SHIFT : 0;
if (ev->mods.alt) ev->mods.value |= ALT;
if (ev->mods.ctrl) ev->mods.value |= CTRL;
if (ev->mods.super) ev->mods.value |= SUPER;
if (ev->mods.hyper) ev->mods.value |= HYPER;
if (ev->mods.meta) ev->mods.value |= META;
if (ev->mods.capslock) ev->mods.value |= CAPS_LOCK;
if (ev->mods.numlock) ev->mods.value |= NUM_LOCK;
snprintf(ev->mods.encoded, sizeof(ev->mods.encoded), "%u", ev->mods.value + 1);
}

20
kitty/key_encoding.py generated
View File

@ -212,16 +212,19 @@ class KeyEvent(NamedTuple):
super: bool = False
hyper: bool = False
meta: bool = False
caps_lock: bool = False
num_lock: bool = False
def matches(self, spec: Union[str, ParsedShortcut], types: int = EventType.PRESS | EventType.REPEAT) -> bool:
mods = self.mods & ~(NUM_LOCK | CAPS_LOCK)
if not self.type & types:
return False
if isinstance(spec, str):
spec = parse_shortcut(spec)
if (self.mods, self.key) == spec:
if (mods, self.key) == spec:
return True
is_shifted = bool(self.shifted_key and self.shift)
if is_shifted and (self.mods & ~SHIFT, self.shifted_key) == spec:
if is_shifted and (mods & ~SHIFT, self.shifted_key) == spec:
return True
return False
@ -245,6 +248,10 @@ class KeyEvent(NamedTuple):
mods |= defines.GLFW_MOD_HYPER
if self.meta:
mods |= defines.GLFW_MOD_META
if self.caps_lock:
mods |= defines.GLFW_MOD_CAPS_LOCK
if self.num_lock:
mods |= defines.GLFW_MOD_NUM_LOCK
fnm = get_name_to_functional_number_map()
@ -257,7 +264,7 @@ class KeyEvent(NamedTuple):
action=action, text=self.text)
SHIFT, ALT, CTRL, SUPER, HYPER, META = 1, 2, 4, 8, 16, 32
SHIFT, ALT, CTRL, SUPER, HYPER, META, CAPS_LOCK, NUM_LOCK = 1, 2, 4, 8, 16, 32, 64, 128
enter_key = KeyEvent(key='ENTER')
backspace_key = KeyEvent(key='BACKSPACE')
config_mod_map = {
@ -272,6 +279,8 @@ config_mod_map = {
'CONTROL': CTRL,
'HYPER': HYPER,
'META': META,
'NUM_LOCK': NUM_LOCK,
'CAPS_LOCK': CAPS_LOCK,
}
@ -306,6 +315,7 @@ def decode_key_event(csi: str, csi_type: str) -> KeyEvent:
mods=mods, shift=bool(mods & SHIFT), alt=bool(mods & ALT),
ctrl=bool(mods & CTRL), super=bool(mods & SUPER),
hyper=bool(mods & HYPER), meta=bool(mods & META),
caps_lock=bool(mods & CAPS_LOCK), num_lock=bool(mods & NUM_LOCK),
key=key_name(keynum),
shifted_key=key_name(first_section[1] if len(first_section) > 1 else 0),
alternate_key=key_name(first_section[2] if len(first_section) > 2 else 0),
@ -362,6 +372,10 @@ def encode_key_event(key_event: KeyEvent) -> str:
m |= 16
if key_event.meta:
m |= 32
if key_event.caps_lock:
m |= 64
if key_event.num_lock:
m |= 128
if action > 1 or m:
ans += f';{m+1}'
if action > 1:

View File

@ -5,11 +5,18 @@
from typing import Optional, Union
from .config import KeyAction, KeyMap, SequenceMap, SubSequenceMap
from .fast_data_types import KeyEvent
from .fast_data_types import (
GLFW_MOD_ALT, GLFW_MOD_CAPS_LOCK, GLFW_MOD_CONTROL, GLFW_MOD_HYPER,
GLFW_MOD_META, GLFW_MOD_NUM_LOCK, GLFW_MOD_SHIFT, GLFW_MOD_SUPER, KeyEvent
)
from .types import SingleKey
from .typing import ScreenType
mod_mask = GLFW_MOD_ALT | GLFW_MOD_CONTROL | GLFW_MOD_SHIFT | GLFW_MOD_SUPER | GLFW_MOD_META | GLFW_MOD_HYPER
lock_mask = GLFW_MOD_NUM_LOCK | GLFW_MOD_CAPS_LOCK
def keyboard_mode_name(screen: ScreenType) -> str:
flags = screen.current_key_encoding_flags()
if flags:
@ -18,22 +25,22 @@ def keyboard_mode_name(screen: ScreenType) -> str:
def get_shortcut(keymap: Union[KeyMap, SequenceMap], ev: KeyEvent) -> Optional[Union[KeyAction, SubSequenceMap]]:
mods = ev.mods & 0b1111
mods = ev.mods & mod_mask
ans = keymap.get(SingleKey(mods, False, ev.key))
if ans is None and ev.shifted_key and mods & 0b1:
ans = keymap.get(SingleKey(mods & 0b1110, False, ev.shifted_key))
if ans is None and ev.shifted_key and mods & GLFW_MOD_SHIFT:
ans = keymap.get(SingleKey(mods & (~GLFW_MOD_SHIFT), False, ev.shifted_key))
if ans is None:
ans = keymap.get(SingleKey(mods, True, ev.native_key))
return ans
def shortcut_matches(s: SingleKey, ev: KeyEvent) -> bool:
mods = ev.mods & 0b1111
smods = s.mods & 0b1111
mods = ev.mods & mod_mask
smods = s.mods & mod_mask
if s.is_native:
return s.key == ev.native_key and smods == mods
if s.key == ev.key and mods == smods:
return True
if ev.shifted_key and mods & 0b1 and (mods & 0b1110) == smods and ev.shifted_key == s.key:
if ev.shifted_key and mods & GLFW_MOD_SHIFT and (mods & ~GLFW_MOD_SHIFT) == smods and ev.shifted_key == s.key:
return True
return False