Move the key handler to C

This commit is contained in:
Kovid Goyal 2017-09-15 21:46:00 +05:30
parent a4d71bcf5c
commit c5e989bc94
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
11 changed files with 4226 additions and 4013 deletions

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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}

File diff suppressed because it is too large Load Diff

View File

@ -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('};')

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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)