Created
December 26, 2020 01:23
-
-
Save antoniusf/e78b2ffc6ae0e4bee7b8c63148655162 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# Copyright (c) 2010 Joshua Harlan Lifton. | |
# See LICENSE.txt for details. | |
# | |
# keyboardcontrol.py - Abstracted keyboard control. | |
# | |
# Uses OS appropriate module. | |
"""Keyboard capture and control. | |
This module provides an interface for basic keyboard event capture and | |
emulation. Set the key_up and key_down functions of the | |
KeyboardCapture class to capture keyboard input. Call the send_string | |
and send_backspaces functions of the KeyboardEmulation class to | |
emulate keyboard input. | |
""" | |
import sys | |
from .wayland import Display, make_event | |
from .wayland import interfaces | |
from dataclasses import dataclass, field | |
@dataclass | |
class SurroundingText: | |
text: str | |
cursor: int | |
anchor: int | |
@dataclass | |
class InputMethodState: | |
active: bool = field(default=False) | |
surrounding_text: SurroundingText = field(default=None) | |
change_cause: interfaces.zwp_text_input_v3._enum_change_cause = field(default=interfaces.zwp_text_input_v3._enum_change_cause.input_method) | |
# TODO: content hint/purpose | |
class InputMethod(interfaces.zwp_input_method_v2): | |
def __init__(self, _id, display): | |
super().__init__(_id, display) | |
self.state = InputMethodState() | |
self.pending_state = InputMethodState() | |
self.serial = 0 | |
def handle_activate(self): | |
self.pending_state.active = True | |
def handle_deactivate(self): | |
self.pending_state.active = False | |
def handle_surrounding_text(self, text, cursor, anchor): | |
self.pending_state.surrounding_text = SurroundingText( | |
text=text, | |
cursor=cursor, | |
anchor=anchor) | |
def handle_text_change_cause(self, cause): | |
self.pending_state.change_cause = cause | |
def handle_done(self): | |
print("State:", self.pending_state) | |
self.state = self.pending_state | |
self.pending_state = InputMethodState( | |
active=self.state.active) | |
self.serial += 1 | |
class KeyboardCapture(object): | |
"""Listen to keyboard events.""" | |
# Supported keys. | |
SUPPORTED_KEYS_LAYOUT = ''' | |
Escape F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 | |
` 1 2 3 4 5 6 7 8 9 0 - = \\ BackSpace Insert Home Page_Up | |
Tab q w e r t y u i o p [ ] Delete End Page_Down | |
a s d f g h j k l ; ' Return | |
z x c v b n m , . / Up | |
space Left Down Right | |
''' | |
SUPPORTED_KEYS = tuple(SUPPORTED_KEYS_LAYOUT.split()) | |
def suppress_keyboard(self, keys): | |
pass | |
def start(self): | |
pass | |
def cancel(self): | |
pass | |
class KeyboardEmulation(object): | |
"""Emulate printable key presses and backspaces.""" | |
def __init__(self): | |
print("\n\n\nhelloo\n\n\n") | |
display = Display() | |
registry = display.get_registry() | |
display.roundtrip() | |
display.set_interface_class("zwp_input_method_v2", InputMethod) | |
seat = registry.bind_interface("wl_seat") | |
im_manager = registry.bind_interface("zwp_input_method_manager_v2") | |
input_method = im_manager.get_input_method(seat) | |
self.display = display | |
self.input_method = input_method | |
self.buffer = "" | |
@input_method | |
def handle_deactivate(): | |
self.buffer = "" | |
def send_backspaces(self, number_of_backspaces): | |
self.display.dispatch_events() | |
if self.input_method.state.active: | |
if number_of_backspaces <= len(self.buffer): | |
self.buffer = self.buffer[:-number_of_backspaces] | |
cursor = len(self.buffer.encode("utf-8")) | |
self.input_method.set_preedit_string(self.buffer, cursor, cursor) | |
else: | |
leftover = number_of_backspaces - len(self.buffer) | |
self.buffer = "" | |
self.input_method.set_preedit_string(self.buffer, 0, 0) | |
self.input_method.delete_surrounding_text(leftover, 0) | |
self.input_method.commit(self.input_method.serial) | |
def send_string(self, string): | |
self.display.dispatch_events() | |
if self.input_method.state.active: | |
self.buffer += string | |
commit = self.buffer[:-20] | |
pre_edit = self.buffer[-20:] | |
self.buffer = pre_edit | |
if commit: | |
self.input_method.commit_string(commit) | |
cursor = len(pre_edit.encode("utf-8")) | |
self.input_method.set_preedit_string(pre_edit, cursor, cursor) | |
self.input_method.commit(self.input_method.serial) | |
#else: | |
# print("adding string to buffer") | |
# self.buffer.extend(string.encode("utf-8")) | |
def send_key_combination(self, combo_string): | |
pass | |
if __name__ == '__main__': | |
import time | |
kc = KeyboardCapture() | |
ke = KeyboardEmulation() | |
pressed = set() | |
status = 'pressed: ' | |
def test(key, action): | |
global status | |
print(key, action) | |
if 'pressed' == action: | |
pressed.add(key) | |
elif key in pressed: | |
pressed.remove(key) | |
new_status = 'pressed: ' + '+'.join(pressed) | |
if status != new_status: | |
ke.send_backspaces(len(status)) | |
ke.send_string(new_status) | |
status = new_status | |
kc.key_down = lambda k: test(k, 'pressed') | |
kc.key_up = lambda k: test(k, 'released') | |
kc.suppress_keyboard(KeyboardCapture.SUPPORTED_KEYS) | |
kc.start() | |
print('Press CTRL-c to quit.') | |
try: | |
while True: | |
time.sleep(1) | |
except KeyboardInterrupt: | |
kc.cancel() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment