Implement keyboard input

This commit is contained in:
Kovid Goyal 2016-10-30 14:56:53 +05:30
parent 48e7226a2a
commit e5293fba16
3 changed files with 62 additions and 69 deletions

View File

@ -376,6 +376,7 @@ cursorenterfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int)
scrollfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_double, c_double) scrollfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_double, c_double)
keyfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int, c_int, c_int) keyfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int, c_int, c_int)
charfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_uint) charfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_uint)
charmodsfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_uint, c_int)
monitorfun = CFUNCTYPE(None, POINTER(GLFWmonitor), c_int) monitorfun = CFUNCTYPE(None, POINTER(GLFWmonitor), c_int)
# --- Init -------------------------------------------------------------------- # --- Init --------------------------------------------------------------------
@ -505,6 +506,7 @@ def glfwCreateWindow(width=640, height=480, title="GLFW Window",
'framebuffersizefun': None, 'framebuffersizefun': None,
'keyfun': None, 'keyfun': None,
'charfun': None, 'charfun': None,
'charmodsfun': None,
'mousebuttonfun': None, 'mousebuttonfun': None,
'cursorposfun': None, 'cursorposfun': None,
'cursorenterfun': None, 'cursorenterfun': None,
@ -645,6 +647,7 @@ exec(__callback__('WindowIconify'))
exec(__callback__('FramebufferSize')) exec(__callback__('FramebufferSize'))
exec(__callback__('Key')) exec(__callback__('Key'))
exec(__callback__('Char')) exec(__callback__('Char'))
exec(__callback__('CharMods'))
exec(__callback__('MouseButton')) exec(__callback__('MouseButton'))
exec(__callback__('CursorPos')) exec(__callback__('CursorPos'))
exec(__callback__('Scroll')) exec(__callback__('Scroll'))

View File

@ -12,6 +12,7 @@ import glfw
from pyte.streams import Stream, DebugStream from pyte.streams import Stream, DebugStream
from .char_grid import CharGrid from .char_grid import CharGrid
from .keys import interpret_text_event, interpret_key_event
from .screen import Screen from .screen import Screen
from .tracker import ChangeTracker from .tracker import ChangeTracker
from .utils import resize_pty, create_pty from .utils import resize_pty, create_pty
@ -42,11 +43,24 @@ class Boss(Thread):
self.stream = sclass(self.screen) self.stream = sclass(self.screen)
self.write_buf = memoryview(b'') self.write_buf = memoryview(b'')
resize_pty(80, 24) resize_pty(80, 24)
glfw.glfwSetCharModsCallback(window, self.on_text_input)
glfw.glfwSetKeyCallback(window, self.on_key)
def initialize(self): def initialize(self):
self.char_grid.initialize() self.char_grid.initialize()
glfw.glfwPostEmptyEvent() 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): def on_window_resize(self, window, w, h):
self.queue_action(self.apply_resize_screen, w, h) self.queue_action(self.apply_resize_screen, w, h)

View File

@ -2,82 +2,58 @@
# vim:fileencoding=utf-8 # vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import glfw
from PyQt5.QtCore import Qt, QObject, QEvent
CTRL_MASK = 0b10011111
key_map = { key_map = {
Qt.Key_Up: 'OA', glfw.GLFW_KEY_UP: b'OA',
Qt.Key_Down: 'OB', glfw.GLFW_KEY_DOWN: b'OB',
Qt.Key_Left: 'OD', glfw.GLFW_KEY_LEFT: b'OD',
Qt.Key_Right: 'OC', glfw.GLFW_KEY_RIGHT: b'OC',
Qt.Key_Home: 'OH', glfw.GLFW_KEY_HOME: b'OH',
Qt.Key_End: 'OF', glfw.GLFW_KEY_END: b'OF',
Qt.Key_Insert: '[2~', glfw.GLFW_KEY_ESCAPE: b'',
Qt.Key_Delete: '[3~', glfw.GLFW_KEY_INSERT: b'[2~',
Qt.Key_PageUp: '[5~', glfw.GLFW_KEY_DELETE: b'[3~',
Qt.Key_PageDown: '[6~', glfw.GLFW_KEY_PAGE_UP: b'[5~',
Qt.Key_F1: 'OP', glfw.GLFW_KEY_PAGE_DOWN: b'[6~',
Qt.Key_F2: 'OQ', glfw.GLFW_KEY_F1: b'OP',
Qt.Key_F3: 'OR', glfw.GLFW_KEY_F2: b'OQ',
Qt.Key_F4: 'OS', glfw.GLFW_KEY_F3: b'OR',
Qt.Key_F5: '[15~', glfw.GLFW_KEY_F4: b'OS',
Qt.Key_F6: '[17~', glfw.GLFW_KEY_F5: b'[15~',
Qt.Key_F7: '[18~', glfw.GLFW_KEY_F6: b'[17~',
Qt.Key_F8: '[19~', glfw.GLFW_KEY_F7: b'[18~',
Qt.Key_F9: '[20~', glfw.GLFW_KEY_F8: b'[19~',
Qt.Key_F10: '[21~', glfw.GLFW_KEY_F9: b'[20~',
Qt.Key_F11: '[23~', glfw.GLFW_KEY_F10: b'[21~',
Qt.Key_F12: '[24~', 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() data = bytearray()
if mods & Qt.AltModifier: if mods == glfw.GLFW_MOD_CONTROL and key in control_codes:
data.append(27) # Map Ctrl-key to ascii control code
x = key_map.get(ev.key()) data.append(control_codes[key])
if x is not None: elif mods == glfw.GLFW_MOD_ALT and key in alt_codes:
data.extend(b'\033' + x.encode('ascii')) # Map Alt+key to Esc-key
data.extend(alt_codes[key])
else: else:
t = ev.text() x = key_map.get(key)
if t: if x is not None:
t = t.encode('utf-8') data.extend(x)
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)
return bytes(data) return bytes(data)
class KeyFilter(QObject): def interpret_text_event(codepoint, mods):
if mods > glfw.GLFW_MOD_SHIFT:
def __init__(self, parent=None): return b'' # Handled by interpret_key_event above
QObject.__init__(self, parent) data = chr(codepoint).encode('utf-8')
self.disabled = False return data
@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