Move the key handler to C
This commit is contained in:
parent
a4d71bcf5c
commit
c5e989bc94
@ -6,16 +6,13 @@ from gettext import gettext as _
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
from .config import MINIMUM_FONT_SIZE
|
||||
from .constants import (
|
||||
MODIFIER_KEYS, cell_size, set_boss, viewport_size, wakeup
|
||||
)
|
||||
from .constants import cell_size, set_boss, viewport_size, wakeup
|
||||
from .fast_data_types import (
|
||||
GLFW_KEY_DOWN, GLFW_KEY_UP, GLFW_PRESS, GLFW_REPEAT, ChildMonitor,
|
||||
destroy_global_data, destroy_sprite_map, glfw_post_empty_event,
|
||||
layout_sprite_map
|
||||
GLFW_KEY_DOWN, GLFW_KEY_UP, ChildMonitor, destroy_global_data,
|
||||
destroy_sprite_map, glfw_post_empty_event, layout_sprite_map
|
||||
)
|
||||
from .fonts.render import render_cell_wrapper, set_font_family
|
||||
from .keys import get_key_map, get_sent_data, get_shortcut, interpret_key_event
|
||||
from .keys import get_key_map, get_sent_data, get_shortcut
|
||||
from .session import create_session
|
||||
from .tabs import SpecialWindow, TabManager
|
||||
from .utils import (
|
||||
@ -72,7 +69,6 @@ class Boss:
|
||||
self.opts, self.args = opts, args
|
||||
self.glfw_window = glfw_window
|
||||
glfw_window.framebuffer_size_callback = self.on_window_resize
|
||||
glfw_window.key_callback = self.on_key
|
||||
glfw_window.window_focus_callback = self.on_focus
|
||||
load_shader_programs()
|
||||
self.tab_manager = TabManager(opts, args)
|
||||
@ -171,35 +167,34 @@ class Boss:
|
||||
if t is not None:
|
||||
return t.active_window
|
||||
|
||||
def on_key(self, window, key, scancode, action, mods):
|
||||
func = None
|
||||
if action == GLFW_PRESS or action == GLFW_REPEAT:
|
||||
def dispatch_special_key(self, key, scancode, action, mods):
|
||||
# Handles shortcuts, return True if the key was consumed
|
||||
func = get_shortcut(self.opts.keymap, mods, key, scancode)
|
||||
if func is not None:
|
||||
f = getattr(self, func, None)
|
||||
if f is not None:
|
||||
passthrough = f()
|
||||
if not passthrough:
|
||||
return
|
||||
return True
|
||||
tab = self.active_tab
|
||||
if tab is None:
|
||||
return
|
||||
return False
|
||||
window = self.active_window
|
||||
if window is None:
|
||||
return
|
||||
return False
|
||||
if func is not None:
|
||||
f = getattr(tab, func, getattr(window, func, None))
|
||||
if f is not None:
|
||||
passthrough = f()
|
||||
if not passthrough:
|
||||
return
|
||||
if window.screen.scrolled_by and key not in MODIFIER_KEYS and action == GLFW_PRESS:
|
||||
window.scroll_end()
|
||||
return True
|
||||
data = get_sent_data(
|
||||
self.opts.send_text_map, key, scancode, mods, window, action
|
||||
) or interpret_key_event(key, scancode, mods, window, action)
|
||||
)
|
||||
if data:
|
||||
window.write_to_child(data)
|
||||
return True
|
||||
return False
|
||||
|
||||
def on_focus(self, window, focused):
|
||||
self.window_is_focused = focused
|
||||
|
||||
@ -8,10 +8,7 @@ import ctypes
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
|
||||
from .fast_data_types import (
|
||||
GLFW_KEY_LEFT_SHIFT, GLFW_KEY_RIGHT_SHIFT, GLFW_KEY_LEFT_ALT,
|
||||
GLFW_KEY_RIGHT_ALT, GLFW_KEY_LEFT_CONTROL, GLFW_KEY_RIGHT_CONTROL,
|
||||
GLFW_KEY_LEFT_SUPER, GLFW_KEY_RIGHT_SUPER, set_boss as set_c_boss)
|
||||
from .fast_data_types import set_boss as set_c_boss
|
||||
|
||||
appname = 'kitty'
|
||||
version = (0, 3, 0)
|
||||
@ -78,8 +75,3 @@ if ctypes.sizeof(GLfloat) != 4:
|
||||
raise RuntimeError('float size is not 4')
|
||||
if ctypes.sizeof(GLint) != 4:
|
||||
raise RuntimeError('int size is not 4')
|
||||
|
||||
MODIFIER_KEYS = (
|
||||
GLFW_KEY_LEFT_SHIFT, GLFW_KEY_RIGHT_SHIFT, GLFW_KEY_LEFT_ALT,
|
||||
GLFW_KEY_RIGHT_ALT, GLFW_KEY_LEFT_CONTROL, GLFW_KEY_RIGHT_CONTROL,
|
||||
GLFW_KEY_LEFT_SUPER, GLFW_KEY_RIGHT_SUPER)
|
||||
|
||||
@ -319,3 +319,4 @@ void mouse_event(int, int);
|
||||
void scroll_event(double, double);
|
||||
void set_special_key_combo(int glfw_key, int mods);
|
||||
void on_text_input(unsigned int codepoint, int mods);
|
||||
void on_key_input(int key, int scancode, int action, int mods);
|
||||
|
||||
11
kitty/glfw.c
11
kitty/glfw.c
@ -36,7 +36,7 @@ typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
GLFWwindow *window;
|
||||
PyObject *framebuffer_size_callback, *key_callback, *window_focus_callback;
|
||||
PyObject *framebuffer_size_callback, *window_focus_callback;
|
||||
GLFWcursor *standard_cursor, *click_cursor, *arrow_cursor;
|
||||
} WindowWrapper;
|
||||
|
||||
@ -63,8 +63,10 @@ char_mods_callback(GLFWwindow UNUSED *w, unsigned int codepoint, int mods) {
|
||||
static void
|
||||
key_callback(GLFWwindow UNUSED *w, int key, int scancode, int action, int mods) {
|
||||
global_state.cursor_blink_zero_time = monotonic();
|
||||
if (key >= 0 && key <= GLFW_KEY_LAST) global_state.is_key_pressed[key] = action == GLFW_RELEASE ? false : true;
|
||||
WINDOW_CALLBACK(key_callback, "iiii", key, scancode, action, mods);
|
||||
if (key >= 0 && key <= GLFW_KEY_LAST) {
|
||||
global_state.is_key_pressed[key] = action == GLFW_RELEASE ? false : true;
|
||||
on_key_input(key, scancode, action, mods);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -259,7 +261,7 @@ glfw_init_hint_string(PyObject UNUSED *self, PyObject *args) {
|
||||
static void
|
||||
dealloc(WindowWrapper* self) {
|
||||
the_window = NULL;
|
||||
Py_CLEAR(self->framebuffer_size_callback); Py_CLEAR(self->key_callback); Py_CLEAR(self->window_focus_callback);
|
||||
Py_CLEAR(self->framebuffer_size_callback); Py_CLEAR(self->window_focus_callback);
|
||||
if (self->window != NULL) glfwDestroyWindow(self->window);
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
}
|
||||
@ -446,7 +448,6 @@ static PyMethodDef methods[] = {
|
||||
static PyMemberDef members[] = {
|
||||
#define CBE(name) {#name, T_OBJECT_EX, offsetof(WindowWrapper, name), 0, #name}
|
||||
CBE(framebuffer_size_callback),
|
||||
CBE(key_callback),
|
||||
CBE(window_focus_callback),
|
||||
{NULL}
|
||||
#undef CBE
|
||||
|
||||
123
kitty/keys.c
123
kitty/keys.c
@ -7,6 +7,7 @@
|
||||
|
||||
#include "keys.h"
|
||||
#include "state.h"
|
||||
#include "screen.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
const uint8_t*
|
||||
@ -24,10 +25,15 @@ key_to_bytes(int glfw_key, bool smkx, bool extended, int mods, int action) {
|
||||
return key_bytes[key];
|
||||
}
|
||||
|
||||
#define SPECIAL_INDEX(key) ((key & 0x7f) | ( (mods & 0xF) << 7))
|
||||
|
||||
void
|
||||
set_special_key_combo(int glfw_key, int mods) {
|
||||
int k = (glfw_key & 0x7f) | ( (mods & 0xF) << 7);
|
||||
needs_special_handling[k] = true;
|
||||
uint16_t key = key_map[glfw_key];
|
||||
if (key != UINT8_MAX) {
|
||||
key = SPECIAL_INDEX(key);
|
||||
needs_special_handling[key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline Window*
|
||||
@ -56,6 +62,119 @@ on_text_input(unsigned int codepoint, int mods) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_modifier_key(int key) {
|
||||
switch(key) {
|
||||
case GLFW_KEY_LEFT_SHIFT:
|
||||
case GLFW_KEY_RIGHT_SHIFT:
|
||||
case GLFW_KEY_LEFT_ALT:
|
||||
case GLFW_KEY_RIGHT_ALT:
|
||||
case GLFW_KEY_LEFT_CONTROL:
|
||||
case GLFW_KEY_RIGHT_CONTROL:
|
||||
case GLFW_KEY_LEFT_SUPER:
|
||||
case GLFW_KEY_RIGHT_SUPER:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
get_localized_key(int key, int scancode) {
|
||||
const char *name = glfwGetKeyName(key, scancode);
|
||||
if (name == NULL || name[1] != 0) return key;
|
||||
switch(name[0]) {
|
||||
#define K(ch, name) case ch: return GLFW_KEY_##name
|
||||
// key names {{{
|
||||
K('A', A);
|
||||
K('B', B);
|
||||
K('C', C);
|
||||
K('D', D);
|
||||
K('E', E);
|
||||
K('F', F);
|
||||
K('G', G);
|
||||
K('H', H);
|
||||
K('I', I);
|
||||
K('J', J);
|
||||
K('K', K);
|
||||
K('L', L);
|
||||
K('M', M);
|
||||
K('N', N);
|
||||
K('O', O);
|
||||
K('P', P);
|
||||
K('Q', Q);
|
||||
K('S', S);
|
||||
K('T', T);
|
||||
K('U', U);
|
||||
K('V', V);
|
||||
K('W', W);
|
||||
K('X', X);
|
||||
K('Y', Y);
|
||||
K('Z', Z);
|
||||
K('0', 0);
|
||||
K('1', 1);
|
||||
K('2', 2);
|
||||
K('3', 3);
|
||||
K('5', 5);
|
||||
K('6', 6);
|
||||
K('7', 7);
|
||||
K('8', 8);
|
||||
K('9', 9);
|
||||
K('\'', APOSTROPHE);
|
||||
K(',', COMMA);
|
||||
K('.', PERIOD);
|
||||
K('/', SLASH);
|
||||
K('-', MINUS);
|
||||
K(';', SEMICOLON);
|
||||
K('=', EQUAL);
|
||||
K('[', LEFT_BRACKET);
|
||||
K(']', RIGHT_BRACKET);
|
||||
K('`', GRAVE_ACCENT);
|
||||
K('\\', BACKSLASH);
|
||||
// }}}
|
||||
#undef K
|
||||
default:
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
on_key_input(int key, int scancode, int action, int mods) {
|
||||
Window *w = active_window();
|
||||
if (!w) return;
|
||||
Screen *screen = w->render_data.screen;
|
||||
if (screen->scrolled_by && action == GLFW_PRESS && !is_modifier_key(key)) {
|
||||
screen_history_scroll(screen, SCROLL_FULL, false); // scroll back to bottom
|
||||
}
|
||||
int lkey = get_localized_key(key, scancode);
|
||||
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
||||
uint16_t qkey = key_map[lkey];
|
||||
bool special = false;
|
||||
if (qkey != UINT8_MAX) {
|
||||
qkey = SPECIAL_INDEX(qkey);
|
||||
special = needs_special_handling[qkey];
|
||||
}
|
||||
/* printf("key: %s mods: %d special: %d\n", key_name(lkey), mods, special); */
|
||||
if (special) {
|
||||
PyObject *ret = PyObject_CallMethod(global_state.boss, "dispatch_special_key", "iiii", lkey, scancode, action, mods);
|
||||
if (ret == NULL) { PyErr_Print(); }
|
||||
else {
|
||||
bool consumed = ret == Py_True ? true : false;
|
||||
Py_DECREF(ret);
|
||||
if (consumed) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
action == GLFW_PRESS ||
|
||||
(action == GLFW_REPEAT && screen->modes.mDECARM) ||
|
||||
screen->modes.mEXTENDED_KEYBOARD
|
||||
) {
|
||||
const uint8_t *data = key_to_bytes(lkey, screen->modes.mDECCKM, screen->modes.mEXTENDED_KEYBOARD, mods, action);
|
||||
if (data) schedule_write_to_child(w->id, (char*)(data + 1), *data);
|
||||
}
|
||||
}
|
||||
|
||||
#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)
|
||||
#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;
|
||||
#define M(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}
|
||||
|
||||
7990
kitty/keys.h
7990
kitty/keys.h
File diff suppressed because it is too large
Load Diff
@ -111,32 +111,6 @@ def keyboard_mode_name(screen):
|
||||
return 'application' if screen.cursor_key_mode else 'normal'
|
||||
|
||||
|
||||
valid_localized_key_names = {
|
||||
k: getattr(defines, 'GLFW_KEY_' + k)
|
||||
for k in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||
}
|
||||
|
||||
for name, ch in {
|
||||
'APOSTROPHE': "'",
|
||||
'COMMA': ',',
|
||||
'PERIOD': '.',
|
||||
'SLASH': '/',
|
||||
'MINUS': '-',
|
||||
'SEMICOLON': ';',
|
||||
'EQUAL': '=',
|
||||
'LEFT_BRACKET': '[',
|
||||
'RIGHT_BRACKET': ']',
|
||||
'GRAVE_ACCENT': '`',
|
||||
'BACKSLASH': '\\'
|
||||
}.items():
|
||||
valid_localized_key_names[ch] = getattr(defines, 'GLFW_KEY_' + name)
|
||||
|
||||
|
||||
def get_localized_key(key, scancode):
|
||||
name = defines.glfw_get_key_name(key, scancode)
|
||||
return valid_localized_key_names.get((name or '').upper(), key)
|
||||
|
||||
|
||||
action_map = {
|
||||
defines.GLFW_PRESS: 'p',
|
||||
defines.GLFW_RELEASE: 'r',
|
||||
@ -182,26 +156,23 @@ def key_to_bytes(key, smkx, extended, mods, action):
|
||||
return bytes(data)
|
||||
|
||||
|
||||
def interpret_key_event(key, scancode, mods, window, action, get_localized_key=get_localized_key):
|
||||
def interpret_key_event(key, scancode, mods, window, action):
|
||||
screen = window.screen
|
||||
if (
|
||||
action == defines.GLFW_PRESS or
|
||||
(action == defines.GLFW_REPEAT and screen.auto_repeat_enabled) or
|
||||
screen.extended_keyboard
|
||||
):
|
||||
key = get_localized_key(key, scancode)
|
||||
return defines.key_to_bytes(key, screen.cursor_key_mode, screen.extended_keyboard, mods, action)
|
||||
return b''
|
||||
|
||||
|
||||
def get_shortcut(keymap, mods, key, scancode):
|
||||
key = get_localized_key(key, scancode)
|
||||
return keymap.get((mods & 0b1111, key))
|
||||
|
||||
|
||||
def get_sent_data(send_text_map, key, scancode, mods, window, action):
|
||||
if action in (defines.GLFW_PRESS, defines.GLFW_REPEAT):
|
||||
key = get_localized_key(key, scancode)
|
||||
m = keyboard_mode_name(window.screen)
|
||||
keymap = send_text_map[m]
|
||||
return keymap.get((mods & 0b1111, key))
|
||||
@ -215,6 +186,7 @@ def generate_key_table():
|
||||
w = partial(print, file=f)
|
||||
w('// auto-generated from keys.py, do not edit!')
|
||||
w('#pragma once')
|
||||
w('#include <stddef.h>')
|
||||
w('#include <stdint.h>')
|
||||
w('#include <stdbool.h>')
|
||||
w('#include <limits.h>')
|
||||
@ -222,7 +194,11 @@ def generate_key_table():
|
||||
number_of_keys = defines.GLFW_KEY_LAST + 1
|
||||
w('static const uint8_t key_map[%d] = {' % number_of_keys)
|
||||
key_count = 0
|
||||
keys = {v: k for k, v in vars(defines).items() if k.startswith('GLFW_KEY_') and k not in 'GLFW_KEY_LAST GLFW_KEY_UNKNOWN'}
|
||||
|
||||
def key_name(k):
|
||||
return k[len('GLFW_KEY_'):]
|
||||
|
||||
keys = {v: k for k, v in vars(defines).items() if k.startswith('GLFW_KEY_') and k not in {'GLFW_KEY_LAST', 'GLFW_KEY_UNKNOWN'}}
|
||||
key_rmap = []
|
||||
for i in range(number_of_keys):
|
||||
k = keys.get(i)
|
||||
@ -235,6 +211,12 @@ def generate_key_table():
|
||||
if key_count > 128:
|
||||
raise OverflowError('Too many keys')
|
||||
w('};\n')
|
||||
w('static inline const char* key_name(int key) { switch(key) {')
|
||||
for i in range(number_of_keys):
|
||||
k = keys.get(i)
|
||||
if k is not None:
|
||||
w('case %d: return "%s";' % (i, key_name(k)))
|
||||
w('default: return NULL; }}\n')
|
||||
number_entries = 128 * 256
|
||||
inits = []
|
||||
longest = 0
|
||||
@ -275,6 +257,6 @@ def generate_key_table():
|
||||
else:
|
||||
b, k, mods, smkx, extended = b
|
||||
b = bytearray(b)
|
||||
name = '+'.join([k for k, v in all_mods.items() if v & mods] + [k.rpartition('_')[2]])
|
||||
name = '+'.join([k for k, v in all_mods.items() if v & mods] + [key_name(k)])
|
||||
w('{0x%x, ' % len(b) + ', '.join(map(str, b)) + '}, //', name, 'smkx:', smkx, 'extended:', extended)
|
||||
w('};')
|
||||
|
||||
@ -15,12 +15,6 @@
|
||||
static MouseShape mouse_cursor_shape = BEAM;
|
||||
typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction;
|
||||
|
||||
#define call_boss(name, ...) { \
|
||||
PyObject *cret_ = PyObject_CallMethod(global_state.boss, #name, __VA_ARGS__); \
|
||||
if (cret_ == NULL) { PyErr_Print(); } \
|
||||
else Py_DECREF(cret_); \
|
||||
}
|
||||
|
||||
#define SHIFT_INDICATOR (1 << 2)
|
||||
#define ALT_INDICATOR (1 << 3)
|
||||
#define CONTROL_INDICATOR (1 << 4)
|
||||
|
||||
@ -127,7 +127,7 @@ color_as_int(PyObject *color) {
|
||||
}
|
||||
|
||||
#define dict_iter(d) { \
|
||||
PyObject *key, *value; Py_ssize_t pos; \
|
||||
PyObject *key, *value; Py_ssize_t pos = 0; \
|
||||
while (PyDict_Next(d, &pos, &key, &value))
|
||||
|
||||
static inline void
|
||||
|
||||
@ -78,6 +78,12 @@ typedef struct {
|
||||
extern GlobalState global_state;
|
||||
bool drag_scroll(Window *);
|
||||
|
||||
#define call_boss(name, ...) { \
|
||||
PyObject *cret_ = PyObject_CallMethod(global_state.boss, #name, __VA_ARGS__); \
|
||||
if (cret_ == NULL) { PyErr_Print(); } \
|
||||
else Py_DECREF(cret_); \
|
||||
}
|
||||
|
||||
#define EXTERNAL_FUNC(name, ret, ...) typedef ret (*name##_func)(__VA_ARGS__); extern name##_func name
|
||||
#define EXTERNAL_FUNC0(name, ret) typedef ret (*name##_func)(); extern name##_func name
|
||||
EXTERNAL_FUNC0(draw_borders, void);
|
||||
|
||||
@ -39,7 +39,6 @@ class TestParser(BaseTest):
|
||||
mods,
|
||||
w,
|
||||
defines.GLFW_PRESS,
|
||||
get_localized_key=lambda k, s: k
|
||||
)
|
||||
self.ae(b'\033' + expected.encode('ascii'), actual)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user