diff --git a/glfw.py b/glfw.py index a300458d1..47e4eba2e 100644 --- a/glfw.py +++ b/glfw.py @@ -376,6 +376,7 @@ cursorenterfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int) scrollfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_double, c_double) keyfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int, c_int, c_int) charfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_uint) +charmodsfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_uint, c_int) monitorfun = CFUNCTYPE(None, POINTER(GLFWmonitor), c_int) # --- Init -------------------------------------------------------------------- @@ -505,6 +506,7 @@ def glfwCreateWindow(width=640, height=480, title="GLFW Window", 'framebuffersizefun': None, 'keyfun': None, 'charfun': None, + 'charmodsfun': None, 'mousebuttonfun': None, 'cursorposfun': None, 'cursorenterfun': None, @@ -645,6 +647,7 @@ exec(__callback__('WindowIconify')) exec(__callback__('FramebufferSize')) exec(__callback__('Key')) exec(__callback__('Char')) +exec(__callback__('CharMods')) exec(__callback__('MouseButton')) exec(__callback__('CursorPos')) exec(__callback__('Scroll')) diff --git a/kitty/boss.py b/kitty/boss.py index bee508818..26839a1f4 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -12,6 +12,7 @@ import glfw from pyte.streams import Stream, DebugStream from .char_grid import CharGrid +from .keys import interpret_text_event, interpret_key_event from .screen import Screen from .tracker import ChangeTracker from .utils import resize_pty, create_pty @@ -42,11 +43,24 @@ class Boss(Thread): self.stream = sclass(self.screen) self.write_buf = memoryview(b'') resize_pty(80, 24) + glfw.glfwSetCharModsCallback(window, self.on_text_input) + glfw.glfwSetKeyCallback(window, self.on_key) def initialize(self): self.char_grid.initialize() glfw.glfwPostEmptyEvent() + def on_key(self, window, key, scancode, action, mods): + if action == glfw.GLFW_PRESS or action == glfw.GLFW_REPEAT: + data = interpret_key_event(key, scancode, mods) + if data: + self.write_to_child(data) + + def on_text_input(self, window, codepoint, mods): + data = interpret_text_event(codepoint, mods) + if data: + self.write_to_child(data) + def on_window_resize(self, window, w, h): self.queue_action(self.apply_resize_screen, w, h) diff --git a/kitty/keys.py b/kitty/keys.py index ad2c59562..e60fe1e79 100644 --- a/kitty/keys.py +++ b/kitty/keys.py @@ -2,82 +2,58 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2016, Kovid Goyal - -from PyQt5.QtCore import Qt, QObject, QEvent - -CTRL_MASK = 0b10011111 +import glfw key_map = { - Qt.Key_Up: 'OA', - Qt.Key_Down: 'OB', - Qt.Key_Left: 'OD', - Qt.Key_Right: 'OC', - Qt.Key_Home: 'OH', - Qt.Key_End: 'OF', - Qt.Key_Insert: '[2~', - Qt.Key_Delete: '[3~', - Qt.Key_PageUp: '[5~', - Qt.Key_PageDown: '[6~', - Qt.Key_F1: 'OP', - Qt.Key_F2: 'OQ', - Qt.Key_F3: 'OR', - Qt.Key_F4: 'OS', - Qt.Key_F5: '[15~', - Qt.Key_F6: '[17~', - Qt.Key_F7: '[18~', - Qt.Key_F8: '[19~', - Qt.Key_F9: '[20~', - Qt.Key_F10: '[21~', - Qt.Key_F11: '[23~', - Qt.Key_F12: '[24~', + glfw.GLFW_KEY_UP: b'OA', + glfw.GLFW_KEY_DOWN: b'OB', + glfw.GLFW_KEY_LEFT: b'OD', + glfw.GLFW_KEY_RIGHT: b'OC', + glfw.GLFW_KEY_HOME: b'OH', + glfw.GLFW_KEY_END: b'OF', + glfw.GLFW_KEY_ESCAPE: b'', + glfw.GLFW_KEY_INSERT: b'[2~', + glfw.GLFW_KEY_DELETE: b'[3~', + glfw.GLFW_KEY_PAGE_UP: b'[5~', + glfw.GLFW_KEY_PAGE_DOWN: b'[6~', + glfw.GLFW_KEY_F1: b'OP', + glfw.GLFW_KEY_F2: b'OQ', + glfw.GLFW_KEY_F3: b'OR', + glfw.GLFW_KEY_F4: b'OS', + glfw.GLFW_KEY_F5: b'[15~', + glfw.GLFW_KEY_F6: b'[17~', + glfw.GLFW_KEY_F7: b'[18~', + glfw.GLFW_KEY_F8: b'[19~', + glfw.GLFW_KEY_F9: b'[20~', + glfw.GLFW_KEY_F10: b'[21~', + glfw.GLFW_KEY_F11: b'[23~', + glfw.GLFW_KEY_F12: b'[24~', } +key_map = {k: b'\x1b' + v for k, v in key_map.items()} +key_map[glfw.GLFW_KEY_ENTER] = b'\n\r' +key_map[glfw.GLFW_KEY_BACKSPACE] = b'\x08' + +control_codes = {k: 1 + i for i, k in enumerate(range(glfw.GLFW_KEY_A, glfw.GLFW_KEY_RIGHT_BRACKET))} +alt_codes = {k: (0x1b, k) for i, k in enumerate(range(glfw.GLFW_KEY_A, glfw.GLFW_KEY_RIGHT_BRACKET))} -def key_event_to_data(ev, mods): +def interpret_key_event(key, scancode, mods): data = bytearray() - if mods & Qt.AltModifier: - data.append(27) - x = key_map.get(ev.key()) - if x is not None: - data.extend(b'\033' + x.encode('ascii')) + if mods == glfw.GLFW_MOD_CONTROL and key in control_codes: + # Map Ctrl-key to ascii control code + data.append(control_codes[key]) + elif mods == glfw.GLFW_MOD_ALT and key in alt_codes: + # Map Alt+key to Esc-key + data.extend(alt_codes[key]) else: - t = ev.text() - if t: - t = t.encode('utf-8') - if mods & Qt.ControlModifier and len(t) == 1 and 0 < t[0] & CTRL_MASK < 33: - data.append(t[0] & CTRL_MASK) - else: - data.extend(t) + x = key_map.get(key) + if x is not None: + data.extend(x) return bytes(data) -class KeyFilter(QObject): - - def __init__(self, parent=None): - QObject.__init__(self, parent) - self.disabled = False - - @property - def disable_filtering(self): - return self - - def __enter__(self): - self.disabled = True - - def __exit__(self, *args): - self.disabled = False - - def eventFilter(self, watched, event): - if self.disabled: - return False - etype = event.type() - if etype == QEvent.KeyPress: - # We use a global event filter to prevent Qt from re-painting the - # entire terminal widget on a Tab key press - app = self.parent() - window, fw = app.activeWindow(), app.focusWidget() - if hasattr(window, 'boss') and fw is window.boss.term: - window.boss.term.keyPressEvent(event) - if event.isAccepted(): - return True - return False +def interpret_text_event(codepoint, mods): + if mods > glfw.GLFW_MOD_SHIFT: + return b'' # Handled by interpret_key_event above + data = chr(codepoint).encode('utf-8') + return data