aboutsummaryrefslogtreecommitdiff
path: root/Python/venv1/Lib/site-packages/keyboard/__init__.py
diff options
context:
space:
mode:
authorSyndamia <kamen.d.mladenov@protonmail.com>2020-09-29 22:43:00 +0300
committerSyndamia <kamen.d.mladenov@protonmail.com>2020-09-29 22:43:00 +0300
commit9a24decd41726ef54fa6613d09829f3f65627b22 (patch)
treee65d5dbc8623cf4b257abaa8fb9047f3c97fe5a0 /Python/venv1/Lib/site-packages/keyboard/__init__.py
parent6f4cfe7b5c6c74d83b03e18870aeb6977db6c0de (diff)
downloadSelf-learning-9a24decd41726ef54fa6613d09829f3f65627b22.tar
Self-learning-9a24decd41726ef54fa6613d09829f3f65627b22.tar.gz
Self-learning-9a24decd41726ef54fa6613d09829f3f65627b22.zip
Removed python setup files
Diffstat (limited to 'Python/venv1/Lib/site-packages/keyboard/__init__.py')
-rw-r--r--Python/venv1/Lib/site-packages/keyboard/__init__.py1155
1 files changed, 0 insertions, 1155 deletions
diff --git a/Python/venv1/Lib/site-packages/keyboard/__init__.py b/Python/venv1/Lib/site-packages/keyboard/__init__.py
deleted file mode 100644
index 5d8f305..0000000
--- a/Python/venv1/Lib/site-packages/keyboard/__init__.py
+++ /dev/null
@@ -1,1155 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-keyboard
-========
-
-Take full control of your keyboard with this small Python library. Hook global events, register hotkeys, simulate key presses and much more.
-
-## Features
-
-- **Global event hook** on all keyboards (captures keys regardless of focus).
-- **Listen** and **send** keyboard events.
-- Works with **Windows** and **Linux** (requires sudo), with experimental **OS X** support (thanks @glitchassassin!).
-- **Pure Python**, no C modules to be compiled.
-- **Zero dependencies**. Trivial to install and deploy, just copy the files.
-- **Python 2 and 3**.
-- Complex hotkey support (e.g. `ctrl+shift+m, ctrl+space`) with controllable timeout.
-- Includes **high level API** (e.g. [record](#keyboard.record) and [play](#keyboard.play), [add_abbreviation](#keyboard.add_abbreviation)).
-- Maps keys as they actually are in your layout, with **full internationalization support** (e.g. `Ctrl+ç`).
-- Events automatically captured in separate thread, doesn't block main program.
-- Tested and documented.
-- Doesn't break accented dead keys (I'm looking at you, pyHook).
-- Mouse support available via project [mouse](https://github.com/boppreh/mouse) (`pip install mouse`).
-
-## Usage
-
-Install the [PyPI package](https://pypi.python.org/pypi/keyboard/):
-
- pip install keyboard
-
-or clone the repository (no installation required, source files are sufficient):
-
- git clone https://github.com/boppreh/keyboard
-
-or [download and extract the zip](https://github.com/boppreh/keyboard/archive/master.zip) into your project folder.
-
-Then check the [API docs below](https://github.com/boppreh/keyboard#api) to see what features are available.
-
-
-## Example
-
-
-```py
-import keyboard
-
-keyboard.press_and_release('shift+s, space')
-
-keyboard.write('The quick brown fox jumps over the lazy dog.')
-
-keyboard.add_hotkey('ctrl+shift+a', print, args=('triggered', 'hotkey'))
-
-# Press PAGE UP then PAGE DOWN to type "foobar".
-keyboard.add_hotkey('page up, page down', lambda: keyboard.write('foobar'))
-
-# Blocks until you press esc.
-keyboard.wait('esc')
-
-# Record events until 'esc' is pressed.
-recorded = keyboard.record(until='esc')
-# Then replay back at three times the speed.
-keyboard.play(recorded, speed_factor=3)
-
-# Type @@ then press space to replace with abbreviation.
-keyboard.add_abbreviation('@@', 'my.long.email@example.com')
-
-# Block forever, like `while True`.
-keyboard.wait()
-```
-
-## Known limitations:
-
-- Events generated under Windows don't report device id (`event.device == None`). [#21](https://github.com/boppreh/keyboard/issues/21)
-- Media keys on Linux may appear nameless (scan-code only) or not at all. [#20](https://github.com/boppreh/keyboard/issues/20)
-- Key suppression/blocking only available on Windows. [#22](https://github.com/boppreh/keyboard/issues/22)
-- To avoid depending on X, the Linux parts reads raw device files (`/dev/input/input*`)
-but this requries root.
-- Other applications, such as some games, may register hooks that swallow all
-key events. In this case `keyboard` will be unable to report events.
-- This program makes no attempt to hide itself, so don't use it for keyloggers or online gaming bots. Be responsible.
-"""
-from __future__ import print_function as _print_function
-
-import re as _re
-import itertools as _itertools
-import collections as _collections
-from threading import Thread as _Thread, Lock as _Lock
-import time as _time
-# Python2... Buggy on time changes and leap seconds, but no other good option (https://stackoverflow.com/questions/1205722/how-do-i-get-monotonic-time-durations-in-python).
-_time.monotonic = getattr(_time, 'monotonic', None) or _time.time
-
-try:
- # Python2
- long, basestring
- _is_str = lambda x: isinstance(x, basestring)
- _is_number = lambda x: isinstance(x, (int, long))
- import Queue as _queue
- # threading.Event is a function in Python2 wrappin _Event (?!).
- from threading import _Event as _UninterruptibleEvent
-except NameError:
- # Python3
- _is_str = lambda x: isinstance(x, str)
- _is_number = lambda x: isinstance(x, int)
- import queue as _queue
- from threading import Event as _UninterruptibleEvent
-_is_list = lambda x: isinstance(x, (list, tuple))
-
-# Just a dynamic object to store attributes for the closures.
-class _State(object): pass
-
-# The "Event" class from `threading` ignores signals when waiting and is
-# impossible to interrupt with Ctrl+C. So we rewrite `wait` to wait in small,
-# interruptible intervals.
-class _Event(_UninterruptibleEvent):
- def wait(self):
- while True:
- if _UninterruptibleEvent.wait(self, 0.5):
- break
-
-import platform as _platform
-if _platform.system() == 'Windows':
- from. import _winkeyboard as _os_keyboard
-elif _platform.system() == 'Linux':
- from. import _nixkeyboard as _os_keyboard
-elif _platform.system() == 'Darwin':
- from. import _darwinkeyboard as _os_keyboard
-else:
- raise OSError("Unsupported platform '{}'".format(_platform.system()))
-
-from ._keyboard_event import KEY_DOWN, KEY_UP, KeyboardEvent
-from ._generic import GenericListener as _GenericListener
-from ._canonical_names import all_modifiers, sided_modifiers, normalize_name
-
-_modifier_scan_codes = set()
-def is_modifier(key):
- """
- Returns True if `key` is a scan code or name of a modifier key.
- """
- if _is_str(key):
- return key in all_modifiers
- else:
- if not _modifier_scan_codes:
- scan_codes = (key_to_scan_codes(name, False) for name in all_modifiers)
- _modifier_scan_codes.update(*scan_codes)
- return key in _modifier_scan_codes
-
-_pressed_events_lock = _Lock()
-_pressed_events = {}
-_physically_pressed_keys = _pressed_events
-_logically_pressed_keys = {}
-class _KeyboardListener(_GenericListener):
- transition_table = {
- #Current state of the modifier, per `modifier_states`.
- #|
- #| Type of event that triggered this modifier update.
- #| |
- #| | Type of key that triggered this modiier update.
- #| | |
- #| | | Should we send a fake key press?
- #| | | |
- #| | | => | Accept the event?
- #| | | | |
- #| | | | | Next state.
- #v v v v v v
- ('free', KEY_UP, 'modifier'): (False, True, 'free'),
- ('free', KEY_DOWN, 'modifier'): (False, False, 'pending'),
- ('pending', KEY_UP, 'modifier'): (True, True, 'free'),
- ('pending', KEY_DOWN, 'modifier'): (False, True, 'allowed'),
- ('suppressed', KEY_UP, 'modifier'): (False, False, 'free'),
- ('suppressed', KEY_DOWN, 'modifier'): (False, False, 'suppressed'),
- ('allowed', KEY_UP, 'modifier'): (False, True, 'free'),
- ('allowed', KEY_DOWN, 'modifier'): (False, True, 'allowed'),
-
- ('free', KEY_UP, 'hotkey'): (False, None, 'free'),
- ('free', KEY_DOWN, 'hotkey'): (False, None, 'free'),
- ('pending', KEY_UP, 'hotkey'): (False, None, 'suppressed'),
- ('pending', KEY_DOWN, 'hotkey'): (False, None, 'suppressed'),
- ('suppressed', KEY_UP, 'hotkey'): (False, None, 'suppressed'),
- ('suppressed', KEY_DOWN, 'hotkey'): (False, None, 'suppressed'),
- ('allowed', KEY_UP, 'hotkey'): (False, None, 'allowed'),
- ('allowed', KEY_DOWN, 'hotkey'): (False, None, 'allowed'),
-
- ('free', KEY_UP, 'other'): (False, True, 'free'),
- ('free', KEY_DOWN, 'other'): (False, True, 'free'),
- ('pending', KEY_UP, 'other'): (True, True, 'allowed'),
- ('pending', KEY_DOWN, 'other'): (True, True, 'allowed'),
- # Necessary when hotkeys are removed after beign triggered, such as
- # TestKeyboard.test_add_hotkey_multistep_suppress_modifier.
- ('suppressed', KEY_UP, 'other'): (False, False, 'allowed'),
- ('suppressed', KEY_DOWN, 'other'): (True, True, 'allowed'),
- ('allowed', KEY_UP, 'other'): (False, True, 'allowed'),
- ('allowed', KEY_DOWN, 'other'): (False, True, 'allowed'),
- }
-
- def init(self):
- _os_keyboard.init()
-
- self.active_modifiers = set()
- self.blocking_hooks = []
- self.blocking_keys = _collections.defaultdict(list)
- self.nonblocking_keys = _collections.defaultdict(list)
- self.blocking_hotkeys = _collections.defaultdict(list)
- self.nonblocking_hotkeys = _collections.defaultdict(list)
- self.filtered_modifiers = _collections.Counter()
- self.is_replaying = False
-
- # Supporting hotkey suppression is harder than it looks. See
- # https://github.com/boppreh/keyboard/issues/22
- self.modifier_states = {} # "alt" -> "allowed"
-
- def pre_process_event(self, event):
- for key_hook in self.nonblocking_keys[event.scan_code]:
- key_hook(event)
-
- with _pressed_events_lock:
- hotkey = tuple(sorted(_pressed_events))
- for callback in self.nonblocking_hotkeys[hotkey]:
- callback(event)
-
- return event.scan_code or (event.name and event.name != 'unknown')
-
- def direct_callback(self, event):
- """
- This function is called for every OS keyboard event and decides if the
- event should be blocked or not, and passes a copy of the event to
- other, non-blocking, listeners.
-
- There are two ways to block events: remapped keys, which translate
- events by suppressing and re-emitting; and blocked hotkeys, which
- suppress specific hotkeys.
- """
- # Pass through all fake key events, don't even report to other handlers.
- if self.is_replaying:
- return True
-
- if not all(hook(event) for hook in self.blocking_hooks):
- return False
-
- event_type = event.event_type
- scan_code = event.scan_code
-
- # Update tables of currently pressed keys and modifiers.
- with _pressed_events_lock:
- if event_type == KEY_DOWN:
- if is_modifier(scan_code): self.active_modifiers.add(scan_code)
- _pressed_events[scan_code] = event
- hotkey = tuple(sorted(_pressed_events))
- if event_type == KEY_UP:
- self.active_modifiers.discard(scan_code)
- if scan_code in _pressed_events: del _pressed_events[scan_code]
-
- # Mappings based on individual keys instead of hotkeys.
- for key_hook in self.blocking_keys[scan_code]:
- if not key_hook(event):
- return False
-
- # Default accept.
- accept = True
-
- if self.blocking_hotkeys:
- if self.filtered_modifiers[scan_code]:
- origin = 'modifier'
- modifiers_to_update = set([scan_code])
- else:
- modifiers_to_update = self.active_modifiers
- if is_modifier(scan_code):
- modifiers_to_update = modifiers_to_update | {scan_code}
- callback_results = [callback(event) for callback in self.blocking_hotkeys[hotkey]]
- if callback_results:
- accept = all(callback_results)
- origin = 'hotkey'
- else:
- origin = 'other'
-
- for key in sorted(modifiers_to_update):
- transition_tuple = (self.modifier_states.get(key, 'free'), event_type, origin)
- should_press, new_accept, new_state = self.transition_table[transition_tuple]
- if should_press: press(key)
- if new_accept is not None: accept = new_accept
- self.modifier_states[key] = new_state
-
- if accept:
- if event_type == KEY_DOWN:
- _logically_pressed_keys[scan_code] = event
- elif event_type == KEY_UP and scan_code in _logically_pressed_keys:
- del _logically_pressed_keys[scan_code]
-
- # Queue for handlers that won't block the event.
- self.queue.put(event)
-
- return accept
-
- def listen(self):
- _os_keyboard.listen(self.direct_callback)
-
-_listener = _KeyboardListener()
-
-def key_to_scan_codes(key, error_if_missing=True):
- """
- Returns a list of scan codes associated with this key (name or scan code).
- """
- if _is_number(key):
- return (key,)
- elif _is_list(key):
- return sum((key_to_scan_codes(i) for i in key), ())
- elif not _is_str(key):
- raise ValueError('Unexpected key type ' + str(type(key)) + ', value (' + repr(key) + ')')
-
- normalized = normalize_name(key)
- if normalized in sided_modifiers:
- left_scan_codes = key_to_scan_codes('left ' + normalized, False)
- right_scan_codes = key_to_scan_codes('right ' + normalized, False)
- return left_scan_codes + tuple(c for c in right_scan_codes if c not in left_scan_codes)
-
- try:
- # Put items in ordered dict to remove duplicates.
- t = tuple(_collections.OrderedDict((scan_code, True) for scan_code, modifier in _os_keyboard.map_name(normalized)))
- e = None
- except (KeyError, ValueError) as exception:
- t = ()
- e = exception
-
- if not t and error_if_missing:
- raise ValueError('Key {} is not mapped to any known key.'.format(repr(key)), e)
- else:
- return t
-
-def parse_hotkey(hotkey):
- """
- Parses a user-provided hotkey into nested tuples representing the
- parsed structure, with the bottom values being lists of scan codes.
- Also accepts raw scan codes, which are then wrapped in the required
- number of nestings.
-
- Example:
-
- parse_hotkey("alt+shift+a, alt+b, c")
- # Keys: ^~^ ^~~~^ ^ ^~^ ^ ^
- # Steps: ^~~~~~~~~~^ ^~~~^ ^
-
- # ((alt_codes, shift_codes, a_codes), (alt_codes, b_codes), (c_codes,))
- """
- if _is_number(hotkey) or len(hotkey) == 1:
- scan_codes = key_to_scan_codes(hotkey)
- step = (scan_codes,)
- steps = (step,)
- return steps
- elif _is_list(hotkey):
- if not any(map(_is_list, hotkey)):
- step = tuple(key_to_scan_codes(k) for k in hotkey)
- steps = (step,)
- return steps
- return hotkey
-
- steps = []
- for step in _re.split(r',\s?', hotkey):
- keys = _re.split(r'\s?\+\s?', step)
- steps.append(tuple(key_to_scan_codes(key) for key in keys))
- return tuple(steps)
-
-def send(hotkey, do_press=True, do_release=True):
- """
- Sends OS events that perform the given *hotkey* hotkey.
-
- - `hotkey` can be either a scan code (e.g. 57 for space), single key
- (e.g. 'space') or multi-key, multi-step hotkey (e.g. 'alt+F4, enter').
- - `do_press` if true then press events are sent. Defaults to True.
- - `do_release` if true then release events are sent. Defaults to True.
-
- send(57)
- send('ctrl+alt+del')
- send('alt+F4, enter')
- send('shift+s')
-
- Note: keys are released in the opposite order they were pressed.
- """
- _listener.is_replaying = True
-
- parsed = parse_hotkey(hotkey)
- for step in parsed:
- if do_press:
- for scan_codes in step:
- _os_keyboard.press(scan_codes[0])
-
- if do_release:
- for scan_codes in reversed(step):
- _os_keyboard.release(scan_codes[0])
-
- _listener.is_replaying = False
-
-# Alias.
-press_and_release = send
-
-def press(hotkey):
- """ Presses and holds down a hotkey (see `send`). """
- send(hotkey, True, False)
-
-def release(hotkey):
- """ Releases a hotkey (see `send`). """
- send(hotkey, False, True)
-
-def is_pressed(hotkey):
- """
- Returns True if the key is pressed.
-
- is_pressed(57) #-> True
- is_pressed('space') #-> True
- is_pressed('ctrl+space') #-> True
- """
- _listener.start_if_necessary()
-
- if _is_number(hotkey):
- # Shortcut.
- with _pressed_events_lock:
- return hotkey in _pressed_events
-
- steps = parse_hotkey(hotkey)
- if len(steps) > 1:
- raise ValueError("Impossible to check if multi-step hotkeys are pressed (`a+b` is ok, `a, b` isn't).")
-
- # Convert _pressed_events into a set
- with _pressed_events_lock:
- pressed_scan_codes = set(_pressed_events)
- for scan_codes in steps[0]:
- if not any(scan_code in pressed_scan_codes for scan_code in scan_codes):
- return False
- return True
-
-def call_later(fn, args=(), delay=0.001):
- """
- Calls the provided function in a new thread after waiting some time.
- Useful for giving the system some time to process an event, without blocking
- the current execution flow.
- """
- thread = _Thread(target=lambda: (_time.sleep(delay), fn(*args)))
- thread.start()
-
-_hooks = {}
-def hook(callback, suppress=False, on_remove=lambda: None):
- """
- Installs a global listener on all available keyboards, invoking `callback`
- each time a key is pressed or released.
-
- The event passed to the callback is of type `keyboard.KeyboardEvent`,
- with the following attributes:
-
- - `name`: an Unicode representation of the character (e.g. "&") or
- description (e.g. "space"). The name is always lower-case.
- - `scan_code`: number representing the physical key, e.g. 55.
- - `time`: timestamp of the time the event occurred, with as much precision
- as given by the OS.
-
- Returns the given callback for easier development.
- """
- if suppress:
- _listener.start_if_necessary()
- append, remove = _listener.blocking_hooks.append, _listener.blocking_hooks.remove
- else:
- append, remove = _listener.add_handler, _listener.remove_handler
-
- append(callback)
- def remove_():
- del _hooks[callback]
- del _hooks[remove_]
- remove(callback)
- on_remove()
- _hooks[callback] = _hooks[remove_] = remove_
- return remove_
-
-def on_press(callback, suppress=False):
- """
- Invokes `callback` for every KEY_DOWN event. For details see `hook`.
- """
- return hook(lambda e: e.event_type == KEY_UP or callback(e), suppress=suppress)
-
-def on_release(callback, suppress=False):
- """
- Invokes `callback` for every KEY_UP event. For details see `hook`.
- """
- return hook(lambda e: e.event_type == KEY_DOWN or callback(e), suppress=suppress)
-
-def hook_key(key, callback, suppress=False):
- """
- Hooks key up and key down events for a single key. Returns the event handler
- created. To remove a hooked key use `unhook_key(key)` or
- `unhook_key(handler)`.
-
- Note: this function shares state with hotkeys, so `clear_all_hotkeys`
- affects it aswell.
- """
- _listener.start_if_necessary()
- store = _listener.blocking_keys if suppress else _listener.nonblocking_keys
- scan_codes = key_to_scan_codes(key)
- for scan_code in scan_codes:
- store[scan_code].append(callback)
-
- def remove_():
- del _hooks[callback]
- del _hooks[key]
- del _hooks[remove_]
- for scan_code in scan_codes:
- store[scan_code].remove(callback)
- _hooks[callback] = _hooks[key] = _hooks[remove_] = remove_
- return remove_
-
-def on_press_key(key, callback, suppress=False):
- """
- Invokes `callback` for KEY_DOWN event related to the given key. For details see `hook`.
- """
- return hook_key(key, lambda e: e.event_type == KEY_UP or callback(e), suppress=suppress)
-
-def on_release_key(key, callback, suppress=False):
- """
- Invokes `callback` for KEY_UP event related to the given key. For details see `hook`.
- """
- return hook_key(key, lambda e: e.event_type == KEY_DOWN or callback(e), suppress=suppress)
-
-def unhook(remove):
- """
- Removes a previously added hook, either by callback or by the return value
- of `hook`.
- """
- _hooks[remove]()
-unhook_key = unhook
-
-def unhook_all():
- """
- Removes all keyboard hooks in use, including hotkeys, abbreviations, word
- listeners, `record`ers and `wait`s.
- """
- _listener.start_if_necessary()
- _listener.blocking_keys.clear()
- _listener.nonblocking_keys.clear()
- del _listener.blocking_hooks[:]
- del _listener.handlers[:]
- unhook_all_hotkeys()
-
-def block_key(key):
- """
- Suppresses all key events of the given key, regardless of modifiers.
- """
- return hook_key(key, lambda e: False, suppress=True)
-unblock_key = unhook_key
-
-def remap_key(src, dst):
- """
- Whenever the key `src` is pressed or released, regardless of modifiers,
- press or release the hotkey `dst` instead.
- """
- def handler(event):
- if event.event_type == KEY_DOWN:
- press(dst)
- else:
- release(dst)
- return False
- return hook_key(src, handler, suppress=True)
-unremap_key = unhook_key
-
-def parse_hotkey_combinations(hotkey):
- """
- Parses a user-provided hotkey. Differently from `parse_hotkey`,
- instead of each step being a list of the different scan codes for each key,
- each step is a list of all possible combinations of those scan codes.
- """
- def combine_step(step):
- # A single step may be composed of many keys, and each key can have
- # multiple scan codes. To speed up hotkey matching and avoid introducing
- # event delays, we list all possible combinations of scan codes for these
- # keys. Hotkeys are usually small, and there are not many combinations, so
- # this is not as insane as it sounds.
- return (tuple(sorted(scan_codes)) for scan_codes in _itertools.product(*step))
-
- return tuple(tuple(combine_step(step)) for step in parse_hotkey(hotkey))
-
-def _add_hotkey_step(handler, combinations, suppress):
- """
- Hooks a single-step hotkey (e.g. 'shift+a').
- """
- container = _listener.blocking_hotkeys if suppress else _listener.nonblocking_hotkeys
-
- # Register the scan codes of every possible combination of
- # modfiier + main key. Modifiers have to be registered in
- # filtered_modifiers too, so suppression and replaying can work.
- for scan_codes in combinations:
- for scan_code in scan_codes:
- if is_modifier(scan_code):
- _listener.filtered_modifiers[scan_code] += 1
- container[scan_codes].append(handler)
-
- def remove():
- for scan_codes in combinations:
- for scan_code in scan_codes:
- if is_modifier(scan_code):
- _listener.filtered_modifiers[scan_code] -= 1
- container[scan_codes].remove(handler)
- return remove
-
-_hotkeys = {}
-def add_hotkey(hotkey, callback, args=(), suppress=False, timeout=1, trigger_on_release=False):
- """
- Invokes a callback every time a hotkey is pressed. The hotkey must
- be in the format `ctrl+shift+a, s`. This would trigger when the user holds
- ctrl, shift and "a" at once, releases, and then presses "s". To represent
- literal commas, pluses, and spaces, use their names ('comma', 'plus',
- 'space').
-
- - `args` is an optional list of arguments to passed to the callback during
- each invocation.
- - `suppress` defines if successful triggers should block the keys from being
- sent to other programs.
- - `timeout` is the amount of seconds allowed to pass between key presses.
- - `trigger_on_release` if true, the callback is invoked on key release instead
- of key press.
-
- The event handler function is returned. To remove a hotkey call
- `remove_hotkey(hotkey)` or `remove_hotkey(handler)`.
- before the hotkey state is reset.
-
- Note: hotkeys are activated when the last key is *pressed*, not released.
- Note: the callback is executed in a separate thread, asynchronously. For an
- example of how to use a callback synchronously, see `wait`.
-
- Examples:
-
- # Different but equivalent ways to listen for a spacebar key press.
- add_hotkey(' ', print, args=['space was pressed'])
- add_hotkey('space', print, args=['space was pressed'])
- add_hotkey('Space', print, args=['space was pressed'])
- # Here 57 represents the keyboard code for spacebar; so you will be
- # pressing 'spacebar', not '57' to activate the print function.
- add_hotkey(57, print, args=['space was pressed'])
-
- add_hotkey('ctrl+q', quit)
- add_hotkey('ctrl+alt+enter, space', some_callback)
- """
- if args:
- callback = lambda callback=callback: callback(*args)
-
- _listener.start_if_necessary()
-
- steps = parse_hotkey_combinations(hotkey)
-
- event_type = KEY_UP if trigger_on_release else KEY_DOWN
- if len(steps) == 1:
- # Deciding when to allow a KEY_UP event is far harder than I thought,
- # and any mistake will make that key "sticky". Therefore just let all
- # KEY_UP events go through as long as that's not what we are listening
- # for.
- handler = lambda e: (event_type == KEY_DOWN and e.event_type == KEY_UP and e.scan_code in _logically_pressed_keys) or (event_type == e.event_type and callback())
- remove_step = _add_hotkey_step(handler, steps[0], suppress)
- def remove_():
- remove_step()
- del _hotkeys[hotkey]
- del _hotkeys[remove_]
- del _hotkeys[callback]
- # TODO: allow multiple callbacks for each hotkey without overwriting the
- # remover.
- _hotkeys[hotkey] = _hotkeys[remove_] = _hotkeys[callback] = remove_
- return remove_
-
- state = _State()
- state.remove_catch_misses = None
- state.remove_last_step = None
- state.suppressed_events = []
- state.last_update = float('-inf')
-
- def catch_misses(event, force_fail=False):
- if (
- event.event_type == event_type
- and state.index
- and event.scan_code not in allowed_keys_by_step[state.index]
- ) or (
- timeout
- and _time.monotonic() - state.last_update >= timeout
- ) or force_fail: # Weird formatting to ensure short-circuit.
-
- state.remove_last_step()
-
- for event in state.suppressed_events:
- if event.event_type == KEY_DOWN:
- press(event.scan_code)
- else:
- release(event.scan_code)
- del state.suppressed_events[:]
-
- index = 0
- set_index(0)
- return True
-
- def set_index(new_index):
- state.index = new_index
-
- if new_index == 0:
- # This is done for performance reasons, avoiding a global key hook
- # that is always on.
- state.remove_catch_misses = lambda: None
- elif new_index == 1:
- state.remove_catch_misses()
- # Must be `suppress=True` to ensure `send` has priority.
- state.remove_catch_misses = hook(catch_misses, suppress=True)
-
- if new_index == len(steps) - 1:
- def handler(event):
- if event.event_type == KEY_UP:
- remove()
- set_index(0)
- accept = event.event_type == event_type and callback()
- if accept:
- return catch_misses(event, force_fail=True)
- else:
- state.suppressed_events[:] = [event]
- return False
- remove = _add_hotkey_step(handler, steps[state.index], suppress)
- else:
- # Fix value of next_index.
- def handler(event, new_index=state.index+1):
- if event.event_type == KEY_UP:
- remove()
- set_index(new_index)
- state.suppressed_events.append(event)
- return False
- remove = _add_hotkey_step(handler, steps[state.index], suppress)
- state.remove_last_step = remove
- state.last_update = _time.monotonic()
- return False
- set_index(0)
-
- allowed_keys_by_step = [
- set().union(*step)
- for step in steps
- ]
-
- def remove_():
- state.remove_catch_misses()
- state.remove_last_step()
- del _hotkeys[hotkey]
- del _hotkeys[remove_]
- del _hotkeys[callback]
- # TODO: allow multiple callbacks for each hotkey without overwriting the
- # remover.
- _hotkeys[hotkey] = _hotkeys[remove_] = _hotkeys[callback] = remove_
- return remove_
-register_hotkey = add_hotkey
-
-def remove_hotkey(hotkey_or_callback):
- """
- Removes a previously hooked hotkey. Must be called wtih the value returned
- by `add_hotkey`.
- """
- _hotkeys[hotkey_or_callback]()
-unregister_hotkey = clear_hotkey = remove_hotkey
-
-def unhook_all_hotkeys():
- """
- Removes all keyboard hotkeys in use, including abbreviations, word listeners,
- `record`ers and `wait`s.
- """
- # Because of "alises" some hooks may have more than one entry, all of which
- # are removed together.
- _listener.blocking_hotkeys.clear()
- _listener.nonblocking_hotkeys.clear()
-unregister_all_hotkeys = remove_all_hotkeys = clear_all_hotkeys = unhook_all_hotkeys
-
-def remap_hotkey(src, dst, suppress=True, trigger_on_release=False):
- """
- Whenever the hotkey `src` is pressed, suppress it and send
- `dst` instead.
-
- Example:
-
- remap('alt+w', 'ctrl+up')
- """
- def handler():
- active_modifiers = sorted(modifier for modifier, state in _listener.modifier_states.items() if state == 'allowed')
- for modifier in active_modifiers:
- release(modifier)
- send(dst)
- for modifier in reversed(active_modifiers):
- press(modifier)
- return False
- return add_hotkey(src, handler, suppress=suppress, trigger_on_release=trigger_on_release)
-unremap_hotkey = remove_hotkey
-
-def stash_state():
- """
- Builds a list of all currently pressed scan codes, releases them and returns
- the list. Pairs well with `restore_state` and `restore_modifiers`.
- """
- # TODO: stash caps lock / numlock /scrollock state.
- with _pressed_events_lock:
- state = sorted(_pressed_events)
- for scan_code in state:
- _os_keyboard.release(scan_code)
- return state
-
-def restore_state(scan_codes):
- """
- Given a list of scan_codes ensures these keys, and only these keys, are
- pressed. Pairs well with `stash_state`, alternative to `restore_modifiers`.
- """
- _listener.is_replaying = True
-
- with _pressed_events_lock:
- current = set(_pressed_events)
- target = set(scan_codes)
- for scan_code in current - target:
- _os_keyboard.release(scan_code)
- for scan_code in target - current:
- _os_keyboard.press(scan_code)
-
- _listener.is_replaying = False
-
-def restore_modifiers(scan_codes):
- """
- Like `restore_state`, but only restores modifier keys.
- """
- restore_state((scan_code for scan_code in scan_codes if is_modifier(scan_code)))
-
-def write(text, delay=0, restore_state_after=True, exact=None):
- """
- Sends artificial keyboard events to the OS, simulating the typing of a given
- text. Characters not available on the keyboard are typed as explicit unicode
- characters using OS-specific functionality, such as alt+codepoint.
-
- To ensure text integrity, all currently pressed keys are released before
- the text is typed, and modifiers are restored afterwards.
-
- - `delay` is the number of seconds to wait between keypresses, defaults to
- no delay.
- - `restore_state_after` can be used to restore the state of pressed keys
- after the text is typed, i.e. presses the keys that were released at the
- beginning. Defaults to True.
- - `exact` forces typing all characters as explicit unicode (e.g.
- alt+codepoint or special events). If None, uses platform-specific suggested
- value.
- """
- if exact is None:
- exact = _platform.system() == 'Windows'
-
- state = stash_state()
-
- # Window's typing of unicode characters is quite efficient and should be preferred.
- if exact:
- for letter in text:
- if letter in '\n\b':
- send(letter)
- else:
- _os_keyboard.type_unicode(letter)
- if delay: _time.sleep(delay)
- else:
- for letter in text:
- try:
- entries = _os_keyboard.map_name(normalize_name(letter))
- scan_code, modifiers = next(iter(entries))
- except (KeyError, ValueError):
- _os_keyboard.type_unicode(letter)
- continue
-
- for modifier in modifiers:
- press(modifier)
-
- _os_keyboard.press(scan_code)
- _os_keyboard.release(scan_code)
-
- for modifier in modifiers:
- release(modifier)
-
- if delay:
- _time.sleep(delay)
-
- if restore_state_after:
- restore_modifiers(state)
-
-def wait(hotkey=None, suppress=False, trigger_on_release=False):
- """
- Blocks the program execution until the given hotkey is pressed or,
- if given no parameters, blocks forever.
- """
- if hotkey:
- lock = _Event()
- remove = add_hotkey(hotkey, lambda: lock.set(), suppress=suppress, trigger_on_release=trigger_on_release)
- lock.wait()
- remove_hotkey(remove)
- else:
- while True:
- _time.sleep(1e6)
-
-def get_hotkey_name(names=None):
- """
- Returns a string representation of hotkey from the given key names, or
- the currently pressed keys if not given. This function:
-
- - normalizes names;
- - removes "left" and "right" prefixes;
- - replaces the "+" key name with "plus" to avoid ambiguity;
- - puts modifier keys first, in a standardized order;
- - sort remaining keys;
- - finally, joins everything with "+".
-
- Example:
-
- get_hotkey_name(['+', 'left ctrl', 'shift'])
- # "ctrl+shift+plus"
- """
- if names is None:
- _listener.start_if_necessary()
- with _pressed_events_lock:
- names = [e.name for e in _pressed_events.values()]
- else:
- names = [normalize_name(name) for name in names]
- clean_names = set(e.replace('left ', '').replace('right ', '').replace('+', 'plus') for e in names)
- # https://developer.apple.com/macos/human-interface-guidelines/input-and-output/keyboard/
- # > List modifier keys in the correct order. If you use more than one modifier key in a
- # > hotkey, always list them in this order: Control, Option, Shift, Command.
- modifiers = ['ctrl', 'alt', 'shift', 'windows']
- sorting_key = lambda k: (modifiers.index(k) if k in modifiers else 5, str(k))
- return '+'.join(sorted(clean_names, key=sorting_key))
-
-def read_event(suppress=False):
- """
- Blocks until a keyboard event happens, then returns that event.
- """
- queue = _queue.Queue(maxsize=1)
- hooked = hook(queue.put, suppress=suppress)
- while True:
- event = queue.get()
- unhook(hooked)
- return event
-
-def read_key(suppress=False):
- """
- Blocks until a keyboard event happens, then returns that event's name or,
- if missing, its scan code.
- """
- event = read_event(suppress)
- return event.name or event.scan_code
-
-def read_hotkey(suppress=True):
- """
- Similar to `read_key()`, but blocks until the user presses and releases a
- hotkey (or single key), then returns a string representing the hotkey
- pressed.
-
- Example:
-
- read_hotkey()
- # "ctrl+shift+p"
- """
- queue = _queue.Queue()
- fn = lambda e: queue.put(e) or e.event_type == KEY_DOWN
- hooked = hook(fn, suppress=suppress)
- while True:
- event = queue.get()
- if event.event_type == KEY_UP:
- unhook(hooked)
- with _pressed_events_lock:
- names = [e.name for e in _pressed_events.values()] + [event.name]
- return get_hotkey_name(names)
-
-def get_typed_strings(events, allow_backspace=True):
- """
- Given a sequence of events, tries to deduce what strings were typed.
- Strings are separated when a non-textual key is pressed (such as tab or
- enter). Characters are converted to uppercase according to shift and
- capslock status. If `allow_backspace` is True, backspaces remove the last
- character typed.
-
- This function is a generator, so you can pass an infinite stream of events
- and convert them to strings in real time.
-
- Note this functions is merely an heuristic. Windows for example keeps per-
- process keyboard state such as keyboard layout, and this information is not
- available for our hooks.
-
- get_type_strings(record()) #-> ['This is what', 'I recorded', '']
- """
- backspace_name = 'delete' if _platform.system() == 'Darwin' else 'backspace'
-
- shift_pressed = False
- capslock_pressed = False
- string = ''
- for event in events:
- name = event.name
-
- # Space is the only key that we _parse_hotkey to the spelled out name
- # because of legibility. Now we have to undo that.
- if event.name == 'space':
- name = ' '
-
- if 'shift' in event.name:
- shift_pressed = event.event_type == 'down'
- elif event.name == 'caps lock' and event.event_type == 'down':
- capslock_pressed = not capslock_pressed
- elif allow_backspace and event.name == backspace_name and event.event_type == 'down':
- string = string[:-1]
- elif event.event_type == 'down':
- if len(name) == 1:
- if shift_pressed ^ capslock_pressed:
- name = name.upper()
- string = string + name
- else:
- yield string
- string = ''
- yield string
-
-_recording = None
-def start_recording(recorded_events_queue=None):
- """
- Starts recording all keyboard events into a global variable, or the given
- queue if any. Returns the queue of events and the hooked function.
-
- Use `stop_recording()` or `unhook(hooked_function)` to stop.
- """
- recorded_events_queue = recorded_events_queue or _queue.Queue()
- global _recording
- _recording = (recorded_events_queue, hook(recorded_events_queue.put))
- return _recording
-
-def stop_recording():
- """
- Stops the global recording of events and returns a list of the events
- captured.
- """
- global _recording
- if not _recording:
- raise ValueError('Must call "start_recording" before.')
- recorded_events_queue, hooked = _recording
- unhook(hooked)
- return list(recorded_events_queue.queue)
-
-def record(until='escape', suppress=False, trigger_on_release=False):
- """
- Records all keyboard events from all keyboards until the user presses the
- given hotkey. Then returns the list of events recorded, of type
- `keyboard.KeyboardEvent`. Pairs well with
- `play(events)`.
-
- Note: this is a blocking function.
- Note: for more details on the keyboard hook and events see `hook`.
- """
- start_recording()
- wait(until, suppress=suppress, trigger_on_release=trigger_on_release)
- return stop_recording()
-
-def play(events, speed_factor=1.0):
- """
- Plays a sequence of recorded events, maintaining the relative time
- intervals. If speed_factor is <= 0 then the actions are replayed as fast
- as the OS allows. Pairs well with `record()`.
-
- Note: the current keyboard state is cleared at the beginning and restored at
- the end of the function.
- """
- state = stash_state()
-
- last_time = None
- for event in events:
- if speed_factor > 0 and last_time is not None:
- _time.sleep((event.time - last_time) / speed_factor)
- last_time = event.time
-
- key = event.scan_code or event.name
- press(key) if event.event_type == KEY_DOWN else release(key)
-
- restore_modifiers(state)
-replay = play
-
-_word_listeners = {}
-def add_word_listener(word, callback, triggers=['space'], match_suffix=False, timeout=2):
- """
- Invokes a callback every time a sequence of characters is typed (e.g. 'pet')
- and followed by a trigger key (e.g. space). Modifiers (e.g. alt, ctrl,
- shift) are ignored.
-
- - `word` the typed text to be matched. E.g. 'pet'.
- - `callback` is an argument-less function to be invoked each time the word
- is typed.
- - `triggers` is the list of keys that will cause a match to be checked. If
- the user presses some key that is not a character (len>1) and not in
- triggers, the characters so far will be discarded. By default the trigger
- is only `space`.
- - `match_suffix` defines if endings of words should also be checked instead
- of only whole words. E.g. if true, typing 'carpet'+space will trigger the
- listener for 'pet'. Defaults to false, only whole words are checked.
- - `timeout` is the maximum number of seconds between typed characters before
- the current word is discarded. Defaults to 2 seconds.
-
- Returns the event handler created. To remove a word listener use
- `remove_word_listener(word)` or `remove_word_listener(handler)`.
-
- Note: all actions are performed on key down. Key up events are ignored.
- Note: word mathes are **case sensitive**.
- """
- state = _State()
- state.current = ''
- state.time = -1
-
- def handler(event):
- name = event.name
- if event.event_type == KEY_UP or name in all_modifiers: return
-
- if timeout and event.time - state.time > timeout:
- state.current = ''
- state.time = event.time
-
- matched = state.current == word or (match_suffix and state.current.endswith(word))
- if name in triggers and matched:
- callback()
- state.current = ''
- elif len(name) > 1:
- state.current = ''
- else:
- state.current += name
-
- hooked = hook(handler)
- def remove():
- hooked()
- del _word_listeners[word]
- del _word_listeners[handler]
- del _word_listeners[remove]
- _word_listeners[word] = _word_listeners[handler] = _word_listeners[remove] = remove
- # TODO: allow multiple word listeners and removing them correctly.
- return remove
-
-def remove_word_listener(word_or_handler):
- """
- Removes a previously registered word listener. Accepts either the word used
- during registration (exact string) or the event handler returned by the
- `add_word_listener` or `add_abbreviation` functions.
- """
- _word_listeners[word_or_handler]()
-
-def add_abbreviation(source_text, replacement_text, match_suffix=False, timeout=2):
- """
- Registers a hotkey that replaces one typed text with another. For example
-
- add_abbreviation('tm', u'™')
-
- Replaces every "tm" followed by a space with a ™ symbol (and no space). The
- replacement is done by sending backspace events.
-
- - `match_suffix` defines if endings of words should also be checked instead
- of only whole words. E.g. if true, typing 'carpet'+space will trigger the
- listener for 'pet'. Defaults to false, only whole words are checked.
- - `timeout` is the maximum number of seconds between typed characters before
- the current word is discarded. Defaults to 2 seconds.
-
- For more details see `add_word_listener`.
- """
- replacement = '\b'*(len(source_text)+1) + replacement_text
- callback = lambda: write(replacement)
- return add_word_listener(source_text, callback, match_suffix=match_suffix, timeout=timeout)
-
-# Aliases.
-register_word_listener = add_word_listener
-register_abbreviation = add_abbreviation
-remove_abbreviation = remove_word_listener \ No newline at end of file