Code to encode key events
This commit is contained in:
parent
c0b6078438
commit
c8a9336160
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1,6 +1,5 @@
|
|||||||
kitty/wcwidth-std.h linguist-generated=true
|
kitty/wcwidth-std.h linguist-generated=true
|
||||||
kitty/emoji.h linguist-generated=true
|
kitty/emoji.h linguist-generated=true
|
||||||
kitty/keys.h linguist-generated=true
|
|
||||||
kitty/charsets.c linguist-generated=true
|
kitty/charsets.c linguist-generated=true
|
||||||
kitty/key_encoding.py linguist-generated=true
|
kitty/key_encoding.py linguist-generated=true
|
||||||
kitty/unicode-data.c linguist-generated=true
|
kitty/unicode-data.c linguist-generated=true
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import subprocess
|
|||||||
files_to_exclude = '''\
|
files_to_exclude = '''\
|
||||||
kitty/wcwidth-std.h
|
kitty/wcwidth-std.h
|
||||||
kitty/glfw.c
|
kitty/glfw.c
|
||||||
kitty/keys.h
|
|
||||||
kitty/charsets.c
|
kitty/charsets.c
|
||||||
kitty/unicode-data.c
|
kitty/unicode-data.c
|
||||||
kitty/key_encoding.py
|
kitty/key_encoding.py
|
||||||
|
|||||||
@ -1,128 +0,0 @@
|
|||||||
Key encoding for extended keyboard protocol
|
|
||||||
===============================================
|
|
||||||
|
|
||||||
See :ref:`extended-key-protocol` for more information and `this table in JSON
|
|
||||||
format <https://github.com/kovidgoyal/kitty/blob/master/key_encoding.json>`_.
|
|
||||||
|
|
||||||
===================== ======================
|
|
||||||
Name Encoded representation
|
|
||||||
0 ``G``
|
|
||||||
1 ``H``
|
|
||||||
2 ``I``
|
|
||||||
3 ``J``
|
|
||||||
4 ``K``
|
|
||||||
5 ``L``
|
|
||||||
6 ``M``
|
|
||||||
7 ``N``
|
|
||||||
8 ``O``
|
|
||||||
9 ``P``
|
|
||||||
A ``S``
|
|
||||||
APOSTROPHE ``B``
|
|
||||||
B ``T``
|
|
||||||
BACKSLASH ``t``
|
|
||||||
BACKSPACE ``1``
|
|
||||||
C ``U``
|
|
||||||
CAPS LOCK ``:``
|
|
||||||
COMMA ``C``
|
|
||||||
D ``V``
|
|
||||||
DELETE ``3``
|
|
||||||
DOWN ``6``
|
|
||||||
E ``W``
|
|
||||||
END ``-``
|
|
||||||
ENTER ``z``
|
|
||||||
EQUAL ``R``
|
|
||||||
ESCAPE ``y``
|
|
||||||
F ``X``
|
|
||||||
F1 ``/``
|
|
||||||
F10 ``]``
|
|
||||||
F11 ``{``
|
|
||||||
F12 ``}``
|
|
||||||
F13 ``@``
|
|
||||||
F14 ``%``
|
|
||||||
F15 ``$``
|
|
||||||
F16 ``#``
|
|
||||||
F17 ``BA``
|
|
||||||
F18 ``BB``
|
|
||||||
F19 ``BC``
|
|
||||||
F2 ``*``
|
|
||||||
F20 ``BD``
|
|
||||||
F21 ``BE``
|
|
||||||
F22 ``BF``
|
|
||||||
F23 ``BG``
|
|
||||||
F24 ``BH``
|
|
||||||
F25 ``BI``
|
|
||||||
F3 ``?``
|
|
||||||
F4 ``&``
|
|
||||||
F5 ``<``
|
|
||||||
F6 ``>``
|
|
||||||
F7 ``(``
|
|
||||||
F8 ``)``
|
|
||||||
F9 ``[``
|
|
||||||
G ``Y``
|
|
||||||
GRAVE ACCENT ``v``
|
|
||||||
H ``Z``
|
|
||||||
HOME ``.``
|
|
||||||
I ``a``
|
|
||||||
INSERT ``2``
|
|
||||||
J ``b``
|
|
||||||
K ``c``
|
|
||||||
KP 0 ``BJ``
|
|
||||||
KP 1 ``BK``
|
|
||||||
KP 2 ``BL``
|
|
||||||
KP 3 ``BM``
|
|
||||||
KP 4 ``BN``
|
|
||||||
KP 5 ``BO``
|
|
||||||
KP 6 ``BP``
|
|
||||||
KP 7 ``BQ``
|
|
||||||
KP 8 ``BR``
|
|
||||||
KP 9 ``BS``
|
|
||||||
KP ADD ``BX``
|
|
||||||
KP DECIMAL ``BT``
|
|
||||||
KP DIVIDE ``BU``
|
|
||||||
KP ENTER ``BY``
|
|
||||||
KP EQUAL ``BZ``
|
|
||||||
KP MULTIPLY ``BV``
|
|
||||||
KP SUBTRACT ``BW``
|
|
||||||
L ``d``
|
|
||||||
LEFT ``5``
|
|
||||||
LEFT ALT ``Bc``
|
|
||||||
LEFT BRACKET ``s``
|
|
||||||
LEFT CONTROL ``Bb``
|
|
||||||
LEFT SHIFT ``Ba``
|
|
||||||
LEFT SUPER ``Bd``
|
|
||||||
M ``e``
|
|
||||||
MINUS ``D``
|
|
||||||
N ``f``
|
|
||||||
NUM LOCK ``=``
|
|
||||||
O ``g``
|
|
||||||
P ``h``
|
|
||||||
PAGE DOWN ``9``
|
|
||||||
PAGE UP ``8``
|
|
||||||
PAUSE ``!``
|
|
||||||
PERIOD ``E``
|
|
||||||
PRINT SCREEN ``^``
|
|
||||||
Q ``i``
|
|
||||||
R ``j``
|
|
||||||
RIGHT ``4``
|
|
||||||
RIGHT ALT ``Bg``
|
|
||||||
RIGHT BRACKET ``u``
|
|
||||||
RIGHT CONTROL ``Bf``
|
|
||||||
RIGHT SHIFT ``Be``
|
|
||||||
RIGHT SUPER ``Bh``
|
|
||||||
S ``k``
|
|
||||||
SCROLL LOCK ``+``
|
|
||||||
SEMICOLON ``Q``
|
|
||||||
SLASH ``F``
|
|
||||||
SPACE ``A``
|
|
||||||
T ``l``
|
|
||||||
TAB ``0``
|
|
||||||
U ``m``
|
|
||||||
UP ``7``
|
|
||||||
V ``n``
|
|
||||||
W ``o``
|
|
||||||
WORLD 1 ``w``
|
|
||||||
WORLD 2 ``x``
|
|
||||||
X ``p``
|
|
||||||
Y ``q``
|
|
||||||
Z ``r``
|
|
||||||
===================== ======================
|
|
||||||
2
glfw/glfw3.h
vendored
2
glfw/glfw3.h
vendored
@ -1379,7 +1379,7 @@ typedef enum {
|
|||||||
typedef struct GLFWkeyevent
|
typedef struct GLFWkeyevent
|
||||||
{
|
{
|
||||||
// The [keyboard key](@ref keys) that was pressed or released.
|
// The [keyboard key](@ref keys) that was pressed or released.
|
||||||
uint32_t key;
|
uint32_t key, shifted_key, alternate_key;
|
||||||
|
|
||||||
// The platform-specific identifier of the key.
|
// The platform-specific identifier of the key.
|
||||||
int native_key;
|
int native_key;
|
||||||
|
|||||||
2
kitty/glfw-wrapper.h
generated
2
kitty/glfw-wrapper.h
generated
@ -1117,7 +1117,7 @@ typedef enum {
|
|||||||
typedef struct GLFWkeyevent
|
typedef struct GLFWkeyevent
|
||||||
{
|
{
|
||||||
// The [keyboard key](@ref keys) that was pressed or released.
|
// The [keyboard key](@ref keys) that was pressed or released.
|
||||||
uint32_t key;
|
uint32_t key, shifted_key, alternate_key;
|
||||||
|
|
||||||
// The platform-specific identifier of the key.
|
// The platform-specific identifier of the key.
|
||||||
int native_key;
|
int native_key;
|
||||||
|
|||||||
224
kitty/key_encoding.c
Normal file
224
kitty/key_encoding.c
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* key_encoding.c
|
||||||
|
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "keys.h"
|
||||||
|
#include "charsets.h"
|
||||||
|
|
||||||
|
typedef enum { SHIFT=1, ALT=2, CTRL=4, SUPER=8 } 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;
|
||||||
|
unsigned value;
|
||||||
|
char encoded[4];
|
||||||
|
} mods;
|
||||||
|
KeyAction action;
|
||||||
|
bool cursor_key_mode, disambiguate, report_all_event_types, report_alternate_key;
|
||||||
|
const char *text;
|
||||||
|
bool has_text;
|
||||||
|
} KeyEvent;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t key, shifted_key, alternate_key;
|
||||||
|
bool add_alternates, has_mods, add_actions;
|
||||||
|
char encoded_mods[4];
|
||||||
|
KeyAction action;
|
||||||
|
} EncodingData;
|
||||||
|
|
||||||
|
|
||||||
|
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.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;
|
||||||
|
snprintf(ev->mods.encoded, sizeof(ev->mods.encoded), "%u", ev->mods.value + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
encode_csi_string(const char csi_trailer, const char *payload, char *output) {
|
||||||
|
return snprintf(output, KEY_BUFFER_SIZE, "\x1b[%s%c", payload, csi_trailer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
init_encoding_data(EncodingData *ans, const KeyEvent *ev) {
|
||||||
|
ans->add_actions = ev->report_all_event_types && ev->action != PRESS;
|
||||||
|
ans->has_mods = ev->mods.encoded[0] && ev->mods.encoded[0] != '1';
|
||||||
|
ans->add_alternates = ev->report_alternate_key && (ev->shifted_key > 0 || ev->alternate_key > 0);
|
||||||
|
if (ans->add_alternates) { ans->shifted_key = ev->shifted_key; ans->alternate_key = ev->alternate_key; }
|
||||||
|
ans->key = ev->key;
|
||||||
|
memcpy(ans->encoded_mods, ev->mods.encoded, sizeof(ans->encoded_mods));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
serialize(const EncodingData *data, char *output, const char csi_trailer) {
|
||||||
|
int pos = 0;
|
||||||
|
#define P(fmt, ...) pos += snprintf(output + pos, KEY_BUFFER_SIZE - 2 - pos, fmt, __VA_ARGS__)
|
||||||
|
P("\x1b[%u", data->key);
|
||||||
|
if (data->add_alternates && (data->shifted_key || data->alternate_key)) {
|
||||||
|
P("%s", ":");
|
||||||
|
if (data->shifted_key) P("%u", data->shifted_key);
|
||||||
|
if (data->alternate_key) P(":%u", data->alternate_key);
|
||||||
|
}
|
||||||
|
if (data->has_mods || data->add_actions) {
|
||||||
|
P(";%s", data->encoded_mods);
|
||||||
|
if (data->add_actions) P(":%u", data->action + 1);
|
||||||
|
}
|
||||||
|
#undef P
|
||||||
|
output[pos++] = csi_trailer;
|
||||||
|
output[pos] = 0;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
encode_function_key(const KeyEvent *ev, char *output) {
|
||||||
|
#define SIMPLE(val) return snprintf(output, KEY_BUFFER_SIZE, "%s", val);
|
||||||
|
char csi_trailer = 'u';
|
||||||
|
uint32_t key_number = ev->key;
|
||||||
|
switch(key_number) {
|
||||||
|
#define S(x) case GLFW_FKEY_KP_##x: key_number = GLFW_FKEY_##x; break;
|
||||||
|
S(ENTER) S(HOME) S(END) S(INSERT) S(DELETE) S(PAGE_UP) S(PAGE_DOWN)
|
||||||
|
#undef S
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev->cursor_key_mode && !ev->disambiguate && !ev->report_all_event_types) {
|
||||||
|
switch(key_number) {
|
||||||
|
case GLFW_FKEY_UP: SIMPLE("\x1bOA");
|
||||||
|
case GLFW_FKEY_DOWN: SIMPLE("\x1bOB");
|
||||||
|
case GLFW_FKEY_LEFT: SIMPLE("\x1bOD");
|
||||||
|
case GLFW_FKEY_RIGHT: SIMPLE("\x1bOC");
|
||||||
|
case GLFW_FKEY_HOME: SIMPLE("\x1bOH");
|
||||||
|
case GLFW_FKEY_END: SIMPLE("\x1bOF");
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ev->mods.value) {
|
||||||
|
switch(key_number) {
|
||||||
|
case GLFW_FKEY_ENTER: SIMPLE("\r");
|
||||||
|
case GLFW_FKEY_ESCAPE: {
|
||||||
|
if (ev->disambiguate) { return encode_csi_string('u', "27u", output); }
|
||||||
|
SIMPLE("\x1b");
|
||||||
|
}
|
||||||
|
case GLFW_FKEY_BACKSPACE: SIMPLE("\x7f");
|
||||||
|
case GLFW_FKEY_TAB: SIMPLE("\t");
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (key_number == GLFW_FKEY_TAB) {
|
||||||
|
if (ev->mods.value == SHIFT) return encode_csi_string('Z', "", output);
|
||||||
|
if (ev->mods.value == (CTRL | SHIFT)) return encode_csi_string('Z', "1;5", output);
|
||||||
|
if (ev->mods.value == ALT) SIMPLE("\x1b\t");
|
||||||
|
}
|
||||||
|
if (ev->mods.value == ALT) {
|
||||||
|
switch(key_number) {
|
||||||
|
case GLFW_FKEY_TAB: SIMPLE("\x1b\t");
|
||||||
|
case GLFW_FKEY_ENTER: SIMPLE("\x1b\r");
|
||||||
|
case GLFW_FKEY_BACKSPACE: SIMPLE("\x1b\x7f");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef SIMPLE
|
||||||
|
|
||||||
|
#define S(number, trailer) key_number = number; csi_trailer = trailer; break
|
||||||
|
switch(key_number) {
|
||||||
|
case GLFW_FKEY_UP: S(1, 'A');
|
||||||
|
case GLFW_FKEY_DOWN: S(1, 'B');
|
||||||
|
case GLFW_FKEY_LEFT: S(1, 'C');
|
||||||
|
case GLFW_FKEY_RIGHT: S(1, 'D');
|
||||||
|
case GLFW_FKEY_HOME: S(1, 'H');
|
||||||
|
case GLFW_FKEY_END: S(1, 'F');
|
||||||
|
case GLFW_FKEY_F1: S(1, 'P');
|
||||||
|
case GLFW_FKEY_F2: S(1, 'Q');
|
||||||
|
case GLFW_FKEY_F3: S(1, 'R');
|
||||||
|
case GLFW_FKEY_F4: S(1, 'S');
|
||||||
|
case GLFW_FKEY_INSERT: S(2, '~');
|
||||||
|
case GLFW_FKEY_DELETE: S(3, '~');
|
||||||
|
case GLFW_FKEY_PAGE_UP: S(5, '~');
|
||||||
|
case GLFW_FKEY_PAGE_DOWN: S(6, '~');
|
||||||
|
case GLFW_FKEY_F5: S(15, '~');
|
||||||
|
case GLFW_FKEY_F6: S(17, '~');
|
||||||
|
case GLFW_FKEY_F7: S(18, '~');
|
||||||
|
case GLFW_FKEY_F8: S(19, '~');
|
||||||
|
case GLFW_FKEY_F9: S(20, '~');
|
||||||
|
case GLFW_FKEY_F10: S(21, '~');
|
||||||
|
case GLFW_FKEY_F11: S(23, '~');
|
||||||
|
case GLFW_FKEY_F12: S(24, '~');
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
#undef S
|
||||||
|
EncodingData ed = {0};
|
||||||
|
init_encoding_data(&ed, ev);
|
||||||
|
ed.add_alternates = false;
|
||||||
|
return serialize(&ed, output, csi_trailer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
encode_printable_ascii_key_legacy(const KeyEvent *ev, char *output) {
|
||||||
|
char shifted_key = 0;
|
||||||
|
|
||||||
|
if ('a' <= ev->key && ev->key <= 'z') shifted_key = ev->key + ('A' - 'a');
|
||||||
|
switch(ev->key) {
|
||||||
|
#define S(which, val) case which: shifted_key = val; break;
|
||||||
|
S('0', ')') S('9', '(') S('8', '*') S('7', '&') S('6', '^') S('5', '%') S('4', '$') S('3', '#') S('2', '@') S('1', '!')
|
||||||
|
S('`', '~') S('-', '_') S('=', '+') S('[', '{') S(']', '}') S('\\', '|') S(';', ':') S('\'', '"') S(',', '<') S('.', '>') S('/', '?')
|
||||||
|
#undef S
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ev->mods.value) return snprintf(output, KEY_BUFFER_SIZE, "%c", (char)ev->key);
|
||||||
|
if (!ev->disambiguate) {
|
||||||
|
if ((ev->mods.value == ALT || ev->mods.value == (SHIFT | ALT)))
|
||||||
|
return snprintf(output, KEY_BUFFER_SIZE, "\x1b%c", (shifted_key && ev->mods.shift) ? shifted_key : (char)ev->key);
|
||||||
|
}
|
||||||
|
if (ev->mods.value == CTRL && (ev->key != 'i' && ev->key != 'm' && ev->key != '[' && ev->key != '@'))
|
||||||
|
return snprintf(output, KEY_BUFFER_SIZE, "%c", ev->key & 0x7f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
encode_key(const KeyEvent *ev, char *output) {
|
||||||
|
if (!ev->report_all_event_types && ev->action == RELEASE) return 0;
|
||||||
|
if (GLFW_FKEY_FIRST <= ev->key && ev->key <= GLFW_FKEY_LAST) return encode_function_key(ev, output);
|
||||||
|
EncodingData ed = {0};
|
||||||
|
init_encoding_data(&ed, ev);
|
||||||
|
bool simple_encoding_ok = !ed.add_actions && !ed.add_alternates;
|
||||||
|
|
||||||
|
if (32 <= ev->key && ev->key <= 126 && simple_encoding_ok) {
|
||||||
|
int ret = encode_printable_ascii_key_legacy(ev, output);
|
||||||
|
if (ret > 0) return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simple_encoding_ok && !ed.has_mods) return encode_utf8(ev->key, output);
|
||||||
|
return serialize(&ed, output, 'u');
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
is_ascii_control_char(char c) {
|
||||||
|
return (0 <= c && c <= 31) || c == 127;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
encode_glfw_key_event(const GLFWkeyevent *e, const bool cursor_key_mode, const unsigned key_encoding_flags, char *output) {
|
||||||
|
KeyEvent ev = {
|
||||||
|
.key = e->key, .shifted_key = e->shifted_key, .alternate_key = e->alternate_key,
|
||||||
|
.text = e->text,
|
||||||
|
.cursor_key_mode = cursor_key_mode,
|
||||||
|
.disambiguate = key_encoding_flags & 1,
|
||||||
|
.report_all_event_types = key_encoding_flags & 2,
|
||||||
|
.report_alternate_key = key_encoding_flags & 4
|
||||||
|
};
|
||||||
|
ev.has_text = e->text && !is_ascii_control_char(e->text[0]);
|
||||||
|
switch (e->action) {
|
||||||
|
case GLFW_PRESS: ev.action = PRESS; break;
|
||||||
|
case GLFW_REPEAT: ev.action = REPEAT; break;
|
||||||
|
case GLFW_RELEASE: ev.action = RELEASE; break;
|
||||||
|
}
|
||||||
|
if (ev.has_text && (ev.action == PRESS || ev.action == REPEAT)) return SEND_TEXT_TO_CHILD;
|
||||||
|
convert_glfw_mods(e->mods, &ev);
|
||||||
|
return encode_key(&ev, output);
|
||||||
|
}
|
||||||
138
kitty/keys.c
138
kitty/keys.c
@ -11,44 +11,6 @@
|
|||||||
#include "glfw-wrapper.h"
|
#include "glfw-wrapper.h"
|
||||||
#include "control-codes.h"
|
#include "control-codes.h"
|
||||||
|
|
||||||
static bool needs_special_handling[128 * 16] = {0};
|
|
||||||
|
|
||||||
const char*
|
|
||||||
key_to_bytes(int glfw_key, bool smkx, bool extended, int mods, int action) {
|
|
||||||
if ((action & 3) == 3) return NULL;
|
|
||||||
if ((unsigned)glfw_key >= sizeof(key_map)/sizeof(key_map[0]) || glfw_key < 0) return NULL;
|
|
||||||
uint16_t key = key_map[glfw_key];
|
|
||||||
if (key == UINT8_MAX) return NULL;
|
|
||||||
KeyboardMode mode = extended ? EXTENDED : (smkx ? APPLICATION : NORMAL);
|
|
||||||
return key_lookup(key, mode, mods, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SPECIAL_INDEX(key) ((key & 0x7f) | ( (mods & 0xF) << 7))
|
|
||||||
#define IS_ALT_MODS(mods) (mods == GLFW_MOD_ALT || mods == (GLFW_MOD_ALT | GLFW_MOD_SHIFT))
|
|
||||||
|
|
||||||
typedef struct { int mods, native_key; } NativeKey;
|
|
||||||
static NativeKey *native_special_keys = NULL;
|
|
||||||
static size_t native_special_keys_capacity = 0, native_special_keys_count = 0;
|
|
||||||
|
|
||||||
void
|
|
||||||
set_special_key_combo(int glfw_key, int mods, bool is_native) {
|
|
||||||
if (is_native) {
|
|
||||||
if (native_special_keys_count >= native_special_keys_capacity) {
|
|
||||||
native_special_keys_capacity = MAX(128u, 2 * native_special_keys_capacity);
|
|
||||||
native_special_keys = realloc(native_special_keys, sizeof(native_special_keys[0]) * native_special_keys_capacity);
|
|
||||||
if (native_special_keys == NULL) fatal("Out of memory");
|
|
||||||
}
|
|
||||||
native_special_keys[native_special_keys_count].mods = mods;
|
|
||||||
native_special_keys[native_special_keys_count++].native_key = glfw_key;
|
|
||||||
} else {
|
|
||||||
uint16_t key = key_map[glfw_key];
|
|
||||||
if (key != UINT8_MAX) {
|
|
||||||
key = SPECIAL_INDEX(key);
|
|
||||||
needs_special_handling[key] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline Window*
|
static inline Window*
|
||||||
active_window(void) {
|
active_window(void) {
|
||||||
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
|
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
|
||||||
@ -75,42 +37,6 @@ is_modifier_key(int key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
|
||||||
send_key_to_child(Window *w, int key, int mods, int action) {
|
|
||||||
Screen *screen = w->render_data.screen;
|
|
||||||
const char *data = key_to_bytes(key, screen->modes.mDECCKM, screen->modes.mEXTENDED_KEYBOARD, mods, action);
|
|
||||||
if (data) {
|
|
||||||
if (screen->modes.mEXTENDED_KEYBOARD) {
|
|
||||||
if (*data == 1) schedule_write_to_child(w->id, 1, (data + 1), 1);
|
|
||||||
else write_escape_code_to_child(screen, APC, data + 1);
|
|
||||||
} else {
|
|
||||||
if (*data > 2 && data[1] == 0x1b && data[2] == '[') { // CSI code
|
|
||||||
write_escape_code_to_child(screen, CSI, data + 3);
|
|
||||||
} else schedule_write_to_child(w->id, 1, (data + 1), *data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
is_ascii_control_char(char c) {
|
|
||||||
return c == 0 || (1 <= c && c <= 31) || c == 127;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
check_if_special(int key, int mods, int native_key) {
|
|
||||||
uint16_t qkey = (0 <= key && key < (ssize_t)arraysz(key_map)) ? key_map[key] : UINT8_MAX;
|
|
||||||
bool special = false;
|
|
||||||
if (qkey != UINT8_MAX) {
|
|
||||||
qkey = SPECIAL_INDEX(qkey);
|
|
||||||
special = needs_special_handling[qkey];
|
|
||||||
}
|
|
||||||
for (size_t i = 0; !special && i < native_special_keys_count; i++) {
|
|
||||||
if (native_key == native_special_keys[i].native_key && mods == native_special_keys[i].mods)
|
|
||||||
special = true;
|
|
||||||
}
|
|
||||||
return special;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
update_ime_position(OSWindow *os_window, Window* w, Screen *screen) {
|
update_ime_position(OSWindow *os_window, Window* w, Screen *screen) {
|
||||||
unsigned int cell_width = os_window->fonts_data->cell_width, cell_height = os_window->fonts_data->cell_height;
|
unsigned int cell_width = os_window->fonts_data->cell_width, cell_height = os_window->fonts_data->cell_height;
|
||||||
@ -120,12 +46,10 @@ update_ime_position(OSWindow *os_window, Window* w, Screen *screen) {
|
|||||||
glfwUpdateIMEState(global_state.callback_os_window->handle, 2, left, top, cell_width, cell_height);
|
glfwUpdateIMEState(global_state.callback_os_window->handle, 2, left, top, cell_width, cell_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define debug(...) if (OPT(debug_keyboard)) printf(__VA_ARGS__);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
on_key_input(GLFWkeyevent *ev) {
|
on_key_input(GLFWkeyevent *ev) {
|
||||||
Window *w = active_window();
|
Window *w = active_window();
|
||||||
int action = ev->action, native_key = ev->native_key, key = ev->key, mods = ev->mods;
|
const int action = ev->action, native_key = ev->native_key, key = ev->key, mods = ev->mods;
|
||||||
const char *text = ev->text ? ev->text : "";
|
const char *text = ev->text ? ev->text : "";
|
||||||
|
|
||||||
debug("on_key_input: glfw key: %d native_code: 0x%x action: %s mods: 0x%x text: '%s' state: %d ",
|
debug("on_key_input: glfw key: %d native_code: 0x%x action: %s mods: 0x%x text: '%s' state: %d ",
|
||||||
@ -167,10 +91,8 @@ on_key_input(GLFWkeyevent *ev) {
|
|||||||
) call_boss(process_sequence, "iiii", key, native_key, action, mods);
|
) call_boss(process_sequence, "iiii", key, native_key, action, mods);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool has_text = text[0] && !is_ascii_control_char(text[0]);
|
|
||||||
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
||||||
if (check_if_special(key, mods, native_key)) {
|
PyObject *ret = PyObject_CallMethod(global_state.boss, "dispatch_possible_special_key", "iiii", key, native_key, action, mods);
|
||||||
PyObject *ret = PyObject_CallMethod(global_state.boss, "dispatch_special_key", "iiii", key, native_key, action, mods);
|
|
||||||
if (ret == NULL) { PyErr_Print(); }
|
if (ret == NULL) { PyErr_Print(); }
|
||||||
else {
|
else {
|
||||||
bool consumed = ret == Py_True;
|
bool consumed = ret == Py_True;
|
||||||
@ -181,7 +103,6 @@ on_key_input(GLFWkeyevent *ev) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (action == GLFW_REPEAT && !screen->modes.mDECARM) {
|
if (action == GLFW_REPEAT && !screen->modes.mDECARM) {
|
||||||
debug("discarding repeat key event as DECARM is off\n");
|
debug("discarding repeat key event as DECARM is off\n");
|
||||||
return;
|
return;
|
||||||
@ -189,15 +110,14 @@ on_key_input(GLFWkeyevent *ev) {
|
|||||||
if (screen->scrolled_by && action == GLFW_PRESS && !is_modifier_key(key)) {
|
if (screen->scrolled_by && action == GLFW_PRESS && !is_modifier_key(key)) {
|
||||||
screen_history_scroll(screen, SCROLL_FULL, false); // scroll back to bottom
|
screen_history_scroll(screen, SCROLL_FULL, false); // scroll back to bottom
|
||||||
}
|
}
|
||||||
bool ok_to_send = action == GLFW_PRESS || action == GLFW_REPEAT || screen->modes.mEXTENDED_KEYBOARD;
|
char encoded_key[KEY_BUFFER_SIZE] = {0};
|
||||||
if (ok_to_send) {
|
int size = encode_glfw_key_event(ev, screen->modes.mDECCKM, screen->key_encoding_flags, encoded_key);
|
||||||
if (has_text) {
|
if (size == SEND_TEXT_TO_CHILD) {
|
||||||
schedule_write_to_child(w->id, 1, text, strlen(text));
|
schedule_write_to_child(w->id, 1, text, strlen(text));
|
||||||
debug("sent text to child\n");
|
debug("sent text to child\n");
|
||||||
} else {
|
} else if (size > 0) {
|
||||||
send_key_to_child(w, key, mods, action);
|
schedule_write_to_child(w->id, 1, encoded_key, size);
|
||||||
debug("sent key to child\n");
|
debug("sent key to child\n");
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
debug("ignoring as keyboard mode does not allow %s events\n", action == GLFW_RELEASE ? "release" : "repeat");
|
debug("ignoring as keyboard mode does not allow %s events\n", action == GLFW_RELEASE ? "release" : "repeat");
|
||||||
}
|
}
|
||||||
@ -207,9 +127,16 @@ void
|
|||||||
fake_scroll(Window *w, int amount, bool upwards) {
|
fake_scroll(Window *w, int amount, bool upwards) {
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
int key = upwards ? GLFW_KEY_UP : GLFW_KEY_DOWN;
|
int key = upwards ? GLFW_KEY_UP : GLFW_KEY_DOWN;
|
||||||
|
GLFWkeyevent ev = {.key = key };
|
||||||
|
char encoded_key[KEY_BUFFER_SIZE] = {0};
|
||||||
|
Screen *screen = w->render_data.screen;
|
||||||
while (amount-- > 0) {
|
while (amount-- > 0) {
|
||||||
send_key_to_child(w, key, 0, GLFW_PRESS);
|
ev.action = GLFW_PRESS;
|
||||||
send_key_to_child(w, key, 0, GLFW_RELEASE);
|
int size = encode_glfw_key_event(&ev, screen->modes.mDECCKM, screen->key_encoding_flags, encoded_key);
|
||||||
|
if (size > 0) schedule_write_to_child(w->id, 1, encoded_key, size);
|
||||||
|
ev.action = GLFW_RELEASE;
|
||||||
|
size = encode_glfw_key_event(&ev, screen->modes.mDECCKM, screen->key_encoding_flags, encoded_key);
|
||||||
|
if (size > 0) schedule_write_to_child(w->id, 1, encoded_key, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,14 +144,6 @@ fake_scroll(Window *w, int amount, bool upwards) {
|
|||||||
#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;
|
#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;
|
||||||
#define M(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}
|
#define M(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}
|
||||||
|
|
||||||
PYWRAP1(key_to_bytes) {
|
|
||||||
int glfw_key, smkx, extended, mods, action;
|
|
||||||
PA("ippii", &glfw_key, &smkx, &extended, &mods, &action);
|
|
||||||
const char *ans = key_to_bytes(glfw_key, smkx & 1, extended & 1, mods, action);
|
|
||||||
if (ans == NULL) return Py_BuildValue("y#", "", 0);
|
|
||||||
return Py_BuildValue("y#", ans + 1, *ans);
|
|
||||||
}
|
|
||||||
|
|
||||||
PYWRAP1(key_for_native_key_name) {
|
PYWRAP1(key_for_native_key_name) {
|
||||||
const char *name;
|
const char *name;
|
||||||
int case_sensitive = 0;
|
int case_sensitive = 0;
|
||||||
@ -238,23 +157,28 @@ PYWRAP1(key_for_native_key_name) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
pyencode_key_for_tty(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
||||||
|
char *kwds[] = {"key", "shifted_key", "alternate_key", "mods", "action", "text", "cursor_key_mode", "key_encoding_flags"};
|
||||||
|
unsigned int key = 0, shifted_key = 0, alternate_key = 0, mods = 0, action = 0, key_encoding_flags = 0;
|
||||||
|
const char *text = NULL;
|
||||||
|
int cursor_key_mode = 0;
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kw, "IIIIIspI", kwds, &key, &shifted_key, &alternate_key, &mods, &action, &text, &cursor_key_mode, &key_encoding_flags)) return NULL;
|
||||||
|
GLFWkeyevent ev = { .key = key, .shifted_key = shifted_key, .alternate_key = alternate_key, .text = text, .action = action, .mods = mods };
|
||||||
|
char output[KEY_BUFFER_SIZE+1] = {0};
|
||||||
|
int num = encode_glfw_key_event(&ev, cursor_key_mode, key_encoding_flags, output);
|
||||||
|
if (num == SEND_TEXT_TO_CHILD) return PyUnicode_FromString(text);
|
||||||
|
return PyUnicode_FromString(output);
|
||||||
|
}
|
||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
M(key_to_bytes, METH_VARARGS),
|
|
||||||
M(key_for_native_key_name, METH_VARARGS),
|
M(key_for_native_key_name, METH_VARARGS),
|
||||||
|
M(encode_key_for_tty, METH_VARARGS | METH_KEYWORDS),
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
|
||||||
finalize(void) {
|
|
||||||
free(native_special_keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
init_keys(PyObject *module) {
|
init_keys(PyObject *module) {
|
||||||
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||||
if (Py_AtExit(finalize) != 0) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Failed to register the keys at exit handler");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
23420
kitty/keys.h
23420
kitty/keys.h
File diff suppressed because it is too large
Load Diff
@ -80,6 +80,3 @@
|
|||||||
#define BRACKETED_PASTE (2004 << 5)
|
#define BRACKETED_PASTE (2004 << 5)
|
||||||
#define BRACKETED_PASTE_START "200~"
|
#define BRACKETED_PASTE_START "200~"
|
||||||
#define BRACKETED_PASTE_END "201~"
|
#define BRACKETED_PASTE_END "201~"
|
||||||
|
|
||||||
// Extended keyboard protocol
|
|
||||||
#define EXTENDED_KEYBOARD (2017 << 5)
|
|
||||||
|
|||||||
@ -150,6 +150,7 @@ void
|
|||||||
screen_reset(Screen *self) {
|
screen_reset(Screen *self) {
|
||||||
if (self->linebuf == self->alt_linebuf) screen_toggle_screen_buffer(self, true, true);
|
if (self->linebuf == self->alt_linebuf) screen_toggle_screen_buffer(self, true, true);
|
||||||
if (self->overlay_line.is_active) deactivate_overlay_line(self);
|
if (self->overlay_line.is_active) deactivate_overlay_line(self);
|
||||||
|
self->key_encoding_flags = 0;
|
||||||
self->last_graphic_char = 0;
|
self->last_graphic_char = 0;
|
||||||
self->main_savepoint.is_valid = false;
|
self->main_savepoint.is_valid = false;
|
||||||
self->alt_savepoint.is_valid = false;
|
self->alt_savepoint.is_valid = false;
|
||||||
@ -750,7 +751,6 @@ set_mode_from_const(Screen *self, unsigned int mode, bool val) {
|
|||||||
SIMPLE_MODE(IRM)
|
SIMPLE_MODE(IRM)
|
||||||
SIMPLE_MODE(DECARM)
|
SIMPLE_MODE(DECARM)
|
||||||
SIMPLE_MODE(BRACKETED_PASTE)
|
SIMPLE_MODE(BRACKETED_PASTE)
|
||||||
SIMPLE_MODE(EXTENDED_KEYBOARD)
|
|
||||||
SIMPLE_MODE(FOCUS_TRACKING)
|
SIMPLE_MODE(FOCUS_TRACKING)
|
||||||
MOUSE_MODE(MOUSE_BUTTON_TRACKING, mouse_tracking_mode, BUTTON_MODE)
|
MOUSE_MODE(MOUSE_BUTTON_TRACKING, mouse_tracking_mode, BUTTON_MODE)
|
||||||
MOUSE_MODE(MOUSE_MOTION_TRACKING, mouse_tracking_mode, MOTION_MODE)
|
MOUSE_MODE(MOUSE_MOTION_TRACKING, mouse_tracking_mode, MOTION_MODE)
|
||||||
@ -1143,7 +1143,7 @@ screen_restore_modes(Screen *self) {
|
|||||||
if (m == NULL) m = &empty_modes;
|
if (m == NULL) m = &empty_modes;
|
||||||
#define S(name) set_mode_from_const(self, name, m->m##name)
|
#define S(name) set_mode_from_const(self, name, m->m##name)
|
||||||
S(DECTCEM); S(DECSCNM); S(DECSCNM); S(DECOM); S(DECAWM); S(DECARM); S(DECCKM);
|
S(DECTCEM); S(DECSCNM); S(DECSCNM); S(DECOM); S(DECAWM); S(DECARM); S(DECCKM);
|
||||||
S(BRACKETED_PASTE); S(FOCUS_TRACKING); S(EXTENDED_KEYBOARD);
|
S(BRACKETED_PASTE); S(FOCUS_TRACKING);
|
||||||
self->modes.mouse_tracking_mode = m->mouse_tracking_mode;
|
self->modes.mouse_tracking_mode = m->mouse_tracking_mode;
|
||||||
self->modes.mouse_tracking_protocol = m->mouse_tracking_protocol;
|
self->modes.mouse_tracking_protocol = m->mouse_tracking_protocol;
|
||||||
#undef S
|
#undef S
|
||||||
@ -1490,7 +1490,6 @@ report_mode_status(Screen *self, unsigned int which, bool private) {
|
|||||||
KNOWN_MODE(DECARM);
|
KNOWN_MODE(DECARM);
|
||||||
KNOWN_MODE(DECCKM);
|
KNOWN_MODE(DECCKM);
|
||||||
KNOWN_MODE(BRACKETED_PASTE);
|
KNOWN_MODE(BRACKETED_PASTE);
|
||||||
KNOWN_MODE(EXTENDED_KEYBOARD);
|
|
||||||
KNOWN_MODE(FOCUS_TRACKING);
|
KNOWN_MODE(FOCUS_TRACKING);
|
||||||
#undef KNOWN_MODE
|
#undef KNOWN_MODE
|
||||||
case ALTERNATE_SCREEN:
|
case ALTERNATE_SCREEN:
|
||||||
@ -2201,7 +2200,6 @@ WRAP0(scroll_until_cursor)
|
|||||||
static int name##_set(Screen *self, PyObject *val, void UNUSED *closure) { if (val == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete attribute"); return -1; } set_mode_from_const(self, uname, PyObject_IsTrue(val) ? true : false); return 0; }
|
static int name##_set(Screen *self, PyObject *val, void UNUSED *closure) { if (val == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete attribute"); return -1; } set_mode_from_const(self, uname, PyObject_IsTrue(val) ? true : false); return 0; }
|
||||||
|
|
||||||
MODE_GETSET(in_bracketed_paste_mode, BRACKETED_PASTE)
|
MODE_GETSET(in_bracketed_paste_mode, BRACKETED_PASTE)
|
||||||
MODE_GETSET(extended_keyboard, EXTENDED_KEYBOARD)
|
|
||||||
MODE_GETSET(focus_tracking_enabled, FOCUS_TRACKING)
|
MODE_GETSET(focus_tracking_enabled, FOCUS_TRACKING)
|
||||||
MODE_GETSET(auto_repeat_enabled, DECARM)
|
MODE_GETSET(auto_repeat_enabled, DECARM)
|
||||||
MODE_GETSET(cursor_visible, DECTCEM)
|
MODE_GETSET(cursor_visible, DECTCEM)
|
||||||
@ -2859,7 +2857,6 @@ static PyMethodDef methods[] = {
|
|||||||
|
|
||||||
static PyGetSetDef getsetters[] = {
|
static PyGetSetDef getsetters[] = {
|
||||||
GETSET(in_bracketed_paste_mode)
|
GETSET(in_bracketed_paste_mode)
|
||||||
GETSET(extended_keyboard)
|
|
||||||
GETSET(auto_repeat_enabled)
|
GETSET(auto_repeat_enabled)
|
||||||
GETSET(focus_tracking_enabled)
|
GETSET(focus_tracking_enabled)
|
||||||
GETSET(cursor_visible)
|
GETSET(cursor_visible)
|
||||||
|
|||||||
@ -14,7 +14,7 @@ typedef enum ScrollTypes { SCROLL_LINE = -999999, SCROLL_PAGE, SCROLL_FULL } Scr
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool mLNM, mIRM, mDECTCEM, mDECSCNM, mDECOM, mDECAWM, mDECCOLM, mDECARM, mDECCKM,
|
bool mLNM, mIRM, mDECTCEM, mDECSCNM, mDECOM, mDECAWM, mDECCOLM, mDECARM, mDECCKM,
|
||||||
mBRACKETED_PASTE, mFOCUS_TRACKING, mEXTENDED_KEYBOARD, mDECSACE;
|
mBRACKETED_PASTE, mFOCUS_TRACKING, mDECSACE;
|
||||||
MouseTrackingMode mouse_tracking_mode;
|
MouseTrackingMode mouse_tracking_mode;
|
||||||
MouseTrackingProtocol mouse_tracking_protocol;
|
MouseTrackingProtocol mouse_tracking_protocol;
|
||||||
bool eight_bit_controls; // S8C1T
|
bool eight_bit_controls; // S8C1T
|
||||||
@ -129,6 +129,7 @@ typedef struct {
|
|||||||
HYPERLINK_POOL_HANDLE hyperlink_pool;
|
HYPERLINK_POOL_HANDLE hyperlink_pool;
|
||||||
ANSIBuf as_ansi_buf;
|
ANSIBuf as_ansi_buf;
|
||||||
char_type last_graphic_char;
|
char_type last_graphic_char;
|
||||||
|
unsigned key_encoding_flags;
|
||||||
} Screen;
|
} Screen;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user