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,9 +141,10 @@ 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)
@ -151,6 +152,8 @@ based systems with special XKB rules. Modifiers are encoded as a bit field with:
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