diff options
Diffstat (limited to 'Python/venv1/Lib/site-packages/keyboard/_nixkeyboard.py')
| -rw-r--r-- | Python/venv1/Lib/site-packages/keyboard/_nixkeyboard.py | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/Python/venv1/Lib/site-packages/keyboard/_nixkeyboard.py b/Python/venv1/Lib/site-packages/keyboard/_nixkeyboard.py new file mode 100644 index 0000000..d3950a1 --- /dev/null +++ b/Python/venv1/Lib/site-packages/keyboard/_nixkeyboard.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +import struct +import traceback +from time import time as now +from collections import namedtuple +from ._keyboard_event import KeyboardEvent, KEY_DOWN, KEY_UP +from ._canonical_names import all_modifiers, normalize_name +from ._nixcommon import EV_KEY, aggregate_devices, ensure_root + +# TODO: start by reading current keyboard state, as to not missing any already pressed keys. +# See: http://stackoverflow.com/questions/3649874/how-to-get-keyboard-state-in-linux + +def cleanup_key(name): + """ Formats a dumpkeys format to our standard. """ + name = name.lstrip('+') + is_keypad = name.startswith('KP_') + for mod in ('Meta_', 'Control_', 'dead_', 'KP_'): + if name.startswith(mod): + name = name[len(mod):] + + # Dumpkeys is weird like that. + if name == 'Remove': + name = 'Delete' + elif name == 'Delete': + name = 'Backspace' + + if name.endswith('_r'): + name = 'right ' + name[:-2] + if name.endswith('_l'): + name = 'left ' + name[:-2] + + + return normalize_name(name), is_keypad + +def cleanup_modifier(modifier): + modifier = normalize_name(modifier) + if modifier in all_modifiers: + return modifier + if modifier[:-1] in all_modifiers: + return modifier[:-1] + raise ValueError('Unknown modifier {}'.format(modifier)) + +""" +Use `dumpkeys --keys-only` to list all scan codes and their names. We +then parse the output and built a table. For each scan code and modifiers we +have a list of names and vice-versa. +""" +from subprocess import check_output +from collections import defaultdict +import re + +to_name = defaultdict(list) +from_name = defaultdict(list) +keypad_scan_codes = set() + +def register_key(key_and_modifiers, name): + if name not in to_name[key_and_modifiers]: + to_name[key_and_modifiers].append(name) + if key_and_modifiers not in from_name[name]: + from_name[name].append(key_and_modifiers) + +def build_tables(): + if to_name and from_name: return + ensure_root() + + modifiers_bits = { + 'shift': 1, + 'alt gr': 2, + 'ctrl': 4, + 'alt': 8, + } + keycode_template = r'^keycode\s+(\d+)\s+=(.*?)$' + dump = check_output(['dumpkeys', '--keys-only'], universal_newlines=True) + for str_scan_code, str_names in re.findall(keycode_template, dump, re.MULTILINE): + scan_code = int(str_scan_code) + for i, str_name in enumerate(str_names.strip().split()): + modifiers = tuple(sorted(modifier for modifier, bit in modifiers_bits.items() if i & bit)) + name, is_keypad = cleanup_key(str_name) + register_key((scan_code, modifiers), name) + if is_keypad: + keypad_scan_codes.add(scan_code) + register_key((scan_code, modifiers), 'keypad ' + name) + + # dumpkeys consistently misreports the Windows key, sometimes + # skipping it completely or reporting as 'alt. 125 = left win, + # 126 = right win. + if (125, ()) not in to_name or to_name[(125, ())] == 'alt': + register_key((125, ()), 'windows') + if (126, ()) not in to_name or to_name[(126, ())] == 'alt': + register_key((126, ()), 'windows') + + # The menu key is usually skipped altogether, so we also add it manually. + if (127, ()) not in to_name: + register_key((127, ()), 'menu') + + synonyms_template = r'^(\S+)\s+for (.+)$' + dump = check_output(['dumpkeys', '--long-info'], universal_newlines=True) + for synonym_str, original_str in re.findall(synonyms_template, dump, re.MULTILINE): + synonym, _ = cleanup_key(synonym_str) + original, _ = cleanup_key(original_str) + if synonym != original: + from_name[original].extend(from_name[synonym]) + from_name[synonym].extend(from_name[original]) + +device = None +def build_device(): + global device + if device: return + ensure_root() + device = aggregate_devices('kbd') + +def init(): + build_device() + build_tables() + +pressed_modifiers = set() + +def listen(callback): + build_device() + build_tables() + + while True: + time, type, code, value, device_id = device.read_event() + if type != EV_KEY: + continue + + scan_code = code + event_type = KEY_DOWN if value else KEY_UP # 0 = UP, 1 = DOWN, 2 = HOLD + + pressed_modifiers_tuple = tuple(sorted(pressed_modifiers)) + names = to_name[(scan_code, pressed_modifiers_tuple)] or to_name[(scan_code, ())] or ['unknown'] + name = names[0] + + if name in all_modifiers: + if event_type == KEY_DOWN: + pressed_modifiers.add(name) + else: + pressed_modifiers.discard(name) + + is_keypad = scan_code in keypad_scan_codes + callback(KeyboardEvent(event_type=event_type, scan_code=scan_code, name=name, time=time, device=device_id, is_keypad=is_keypad, modifiers=pressed_modifiers_tuple)) + +def write_event(scan_code, is_down): + build_device() + device.write_event(EV_KEY, scan_code, int(is_down)) + +def map_name(name): + build_tables() + for entry in from_name[name]: + yield entry + + parts = name.split(' ', 1) + if len(parts) > 1 and parts[0] in ('left', 'right'): + for entry in from_name[parts[1]]: + yield entry + +def press(scan_code): + write_event(scan_code, True) + +def release(scan_code): + write_event(scan_code, False) + +def type_unicode(character): + codepoint = ord(character) + hexadecimal = hex(codepoint)[len('0x'):] + + for key in ['ctrl', 'shift', 'u']: + scan_code, _ = next(map_name(key)) + press(scan_code) + + for key in hexadecimal: + scan_code, _ = next(map_name(key)) + press(scan_code) + release(scan_code) + + for key in ['ctrl', 'shift', 'u']: + scan_code, _ = next(map_name(key)) + release(scan_code) + +if __name__ == '__main__': + def p(e): + print(e) + listen(p) |
