quick fix 2
This commit is contained in:
620
Lib/site-packages/keyboard/_winkeyboard.py
Normal file
620
Lib/site-packages/keyboard/_winkeyboard.py
Normal file
@ -0,0 +1,620 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This is the Windows backend for keyboard events, and is implemented by
|
||||
invoking the Win32 API through the ctypes module. This is error prone
|
||||
and can introduce very unpythonic failure modes, such as segfaults and
|
||||
low level memory leaks. But it is also dependency-free, very performant
|
||||
well documented on Microsoft's website and scattered examples.
|
||||
|
||||
# TODO:
|
||||
- Keypad numbers still print as numbers even when numlock is off.
|
||||
- No way to specify if user wants a keypad key or not in `map_char`.
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
import re
|
||||
import atexit
|
||||
import traceback
|
||||
from threading import Lock
|
||||
from collections import defaultdict
|
||||
|
||||
from ._keyboard_event import KeyboardEvent, KEY_DOWN, KEY_UP
|
||||
from ._canonical_names import normalize_name
|
||||
try:
|
||||
# Force Python2 to convert to unicode and not to str.
|
||||
chr = unichr
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
# This part is just declaring Win32 API structures using ctypes. In C
|
||||
# this would be simply #include "windows.h".
|
||||
|
||||
import ctypes
|
||||
from ctypes import c_short, c_char, c_uint8, c_int32, c_int, c_uint, c_uint32, c_long, Structure, CFUNCTYPE, POINTER
|
||||
from ctypes.wintypes import WORD, DWORD, BOOL, HHOOK, MSG, LPWSTR, WCHAR, WPARAM, LPARAM, LONG, HMODULE, LPCWSTR, HINSTANCE, HWND
|
||||
LPMSG = POINTER(MSG)
|
||||
ULONG_PTR = POINTER(DWORD)
|
||||
|
||||
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
|
||||
GetModuleHandleW = kernel32.GetModuleHandleW
|
||||
GetModuleHandleW.restype = HMODULE
|
||||
GetModuleHandleW.argtypes = [LPCWSTR]
|
||||
|
||||
#https://github.com/boppreh/mouse/issues/1
|
||||
#user32 = ctypes.windll.user32
|
||||
user32 = ctypes.WinDLL('user32', use_last_error = True)
|
||||
|
||||
VK_PACKET = 0xE7
|
||||
|
||||
INPUT_MOUSE = 0
|
||||
INPUT_KEYBOARD = 1
|
||||
INPUT_HARDWARE = 2
|
||||
|
||||
KEYEVENTF_KEYUP = 0x02
|
||||
KEYEVENTF_UNICODE = 0x04
|
||||
|
||||
class KBDLLHOOKSTRUCT(Structure):
|
||||
_fields_ = [("vk_code", DWORD),
|
||||
("scan_code", DWORD),
|
||||
("flags", DWORD),
|
||||
("time", c_int),
|
||||
("dwExtraInfo", ULONG_PTR)]
|
||||
|
||||
# Included for completeness.
|
||||
class MOUSEINPUT(ctypes.Structure):
|
||||
_fields_ = (('dx', LONG),
|
||||
('dy', LONG),
|
||||
('mouseData', DWORD),
|
||||
('dwFlags', DWORD),
|
||||
('time', DWORD),
|
||||
('dwExtraInfo', ULONG_PTR))
|
||||
|
||||
class KEYBDINPUT(ctypes.Structure):
|
||||
_fields_ = (('wVk', WORD),
|
||||
('wScan', WORD),
|
||||
('dwFlags', DWORD),
|
||||
('time', DWORD),
|
||||
('dwExtraInfo', ULONG_PTR))
|
||||
|
||||
class HARDWAREINPUT(ctypes.Structure):
|
||||
_fields_ = (('uMsg', DWORD),
|
||||
('wParamL', WORD),
|
||||
('wParamH', WORD))
|
||||
|
||||
class _INPUTunion(ctypes.Union):
|
||||
_fields_ = (('mi', MOUSEINPUT),
|
||||
('ki', KEYBDINPUT),
|
||||
('hi', HARDWAREINPUT))
|
||||
|
||||
class INPUT(ctypes.Structure):
|
||||
_fields_ = (('type', DWORD),
|
||||
('union', _INPUTunion))
|
||||
|
||||
LowLevelKeyboardProc = CFUNCTYPE(c_int, WPARAM, LPARAM, POINTER(KBDLLHOOKSTRUCT))
|
||||
|
||||
SetWindowsHookEx = user32.SetWindowsHookExW
|
||||
SetWindowsHookEx.argtypes = [c_int, LowLevelKeyboardProc, HINSTANCE , DWORD]
|
||||
SetWindowsHookEx.restype = HHOOK
|
||||
|
||||
CallNextHookEx = user32.CallNextHookEx
|
||||
#CallNextHookEx.argtypes = [c_int , c_int, c_int, POINTER(KBDLLHOOKSTRUCT)]
|
||||
CallNextHookEx.restype = c_int
|
||||
|
||||
UnhookWindowsHookEx = user32.UnhookWindowsHookEx
|
||||
UnhookWindowsHookEx.argtypes = [HHOOK]
|
||||
UnhookWindowsHookEx.restype = BOOL
|
||||
|
||||
GetMessage = user32.GetMessageW
|
||||
GetMessage.argtypes = [LPMSG, HWND, c_uint, c_uint]
|
||||
GetMessage.restype = BOOL
|
||||
|
||||
TranslateMessage = user32.TranslateMessage
|
||||
TranslateMessage.argtypes = [LPMSG]
|
||||
TranslateMessage.restype = BOOL
|
||||
|
||||
DispatchMessage = user32.DispatchMessageA
|
||||
DispatchMessage.argtypes = [LPMSG]
|
||||
|
||||
|
||||
keyboard_state_type = c_uint8 * 256
|
||||
|
||||
GetKeyboardState = user32.GetKeyboardState
|
||||
GetKeyboardState.argtypes = [keyboard_state_type]
|
||||
GetKeyboardState.restype = BOOL
|
||||
|
||||
GetKeyNameText = user32.GetKeyNameTextW
|
||||
GetKeyNameText.argtypes = [c_long, LPWSTR, c_int]
|
||||
GetKeyNameText.restype = c_int
|
||||
|
||||
MapVirtualKey = user32.MapVirtualKeyW
|
||||
MapVirtualKey.argtypes = [c_uint, c_uint]
|
||||
MapVirtualKey.restype = c_uint
|
||||
|
||||
ToUnicode = user32.ToUnicode
|
||||
ToUnicode.argtypes = [c_uint, c_uint, keyboard_state_type, LPWSTR, c_int, c_uint]
|
||||
ToUnicode.restype = c_int
|
||||
|
||||
SendInput = user32.SendInput
|
||||
SendInput.argtypes = [c_uint, POINTER(INPUT), c_int]
|
||||
SendInput.restype = c_uint
|
||||
|
||||
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms646307(v=vs.85).aspx
|
||||
MAPVK_VK_TO_CHAR = 2
|
||||
MAPVK_VK_TO_VSC = 0
|
||||
MAPVK_VSC_TO_VK = 1
|
||||
MAPVK_VK_TO_VSC_EX = 4
|
||||
MAPVK_VSC_TO_VK_EX = 3
|
||||
|
||||
VkKeyScan = user32.VkKeyScanW
|
||||
VkKeyScan.argtypes = [WCHAR]
|
||||
VkKeyScan.restype = c_short
|
||||
|
||||
LLKHF_INJECTED = 0x00000010
|
||||
|
||||
WM_KEYDOWN = 0x0100
|
||||
WM_KEYUP = 0x0101
|
||||
WM_SYSKEYDOWN = 0x104 # Used for ALT key
|
||||
WM_SYSKEYUP = 0x105
|
||||
|
||||
|
||||
# This marks the end of Win32 API declarations. The rest is ours.
|
||||
|
||||
keyboard_event_types = {
|
||||
WM_KEYDOWN: KEY_DOWN,
|
||||
WM_KEYUP: KEY_UP,
|
||||
WM_SYSKEYDOWN: KEY_DOWN,
|
||||
WM_SYSKEYUP: KEY_UP,
|
||||
}
|
||||
|
||||
# List taken from the official documentation, but stripped of the OEM-specific keys.
|
||||
# Keys are virtual key codes, values are pairs (name, is_keypad).
|
||||
official_virtual_keys = {
|
||||
0x03: ('control-break processing', False),
|
||||
0x08: ('backspace', False),
|
||||
0x09: ('tab', False),
|
||||
0x0c: ('clear', False),
|
||||
0x0d: ('enter', False),
|
||||
0x10: ('shift', False),
|
||||
0x11: ('ctrl', False),
|
||||
0x12: ('alt', False),
|
||||
0x13: ('pause', False),
|
||||
0x14: ('caps lock', False),
|
||||
0x15: ('ime kana mode', False),
|
||||
0x15: ('ime hanguel mode', False),
|
||||
0x15: ('ime hangul mode', False),
|
||||
0x17: ('ime junja mode', False),
|
||||
0x18: ('ime final mode', False),
|
||||
0x19: ('ime hanja mode', False),
|
||||
0x19: ('ime kanji mode', False),
|
||||
0x1b: ('esc', False),
|
||||
0x1c: ('ime convert', False),
|
||||
0x1d: ('ime nonconvert', False),
|
||||
0x1e: ('ime accept', False),
|
||||
0x1f: ('ime mode change request', False),
|
||||
0x20: ('spacebar', False),
|
||||
0x21: ('page up', False),
|
||||
0x22: ('page down', False),
|
||||
0x23: ('end', False),
|
||||
0x24: ('home', False),
|
||||
0x25: ('left', False),
|
||||
0x26: ('up', False),
|
||||
0x27: ('right', False),
|
||||
0x28: ('down', False),
|
||||
0x29: ('select', False),
|
||||
0x2a: ('print', False),
|
||||
0x2b: ('execute', False),
|
||||
0x2c: ('print screen', False),
|
||||
0x2d: ('insert', False),
|
||||
0x2e: ('delete', False),
|
||||
0x2f: ('help', False),
|
||||
0x30: ('0', False),
|
||||
0x31: ('1', False),
|
||||
0x32: ('2', False),
|
||||
0x33: ('3', False),
|
||||
0x34: ('4', False),
|
||||
0x35: ('5', False),
|
||||
0x36: ('6', False),
|
||||
0x37: ('7', False),
|
||||
0x38: ('8', False),
|
||||
0x39: ('9', False),
|
||||
0x41: ('a', False),
|
||||
0x42: ('b', False),
|
||||
0x43: ('c', False),
|
||||
0x44: ('d', False),
|
||||
0x45: ('e', False),
|
||||
0x46: ('f', False),
|
||||
0x47: ('g', False),
|
||||
0x48: ('h', False),
|
||||
0x49: ('i', False),
|
||||
0x4a: ('j', False),
|
||||
0x4b: ('k', False),
|
||||
0x4c: ('l', False),
|
||||
0x4d: ('m', False),
|
||||
0x4e: ('n', False),
|
||||
0x4f: ('o', False),
|
||||
0x50: ('p', False),
|
||||
0x51: ('q', False),
|
||||
0x52: ('r', False),
|
||||
0x53: ('s', False),
|
||||
0x54: ('t', False),
|
||||
0x55: ('u', False),
|
||||
0x56: ('v', False),
|
||||
0x57: ('w', False),
|
||||
0x58: ('x', False),
|
||||
0x59: ('y', False),
|
||||
0x5a: ('z', False),
|
||||
0x5b: ('left windows', False),
|
||||
0x5c: ('right windows', False),
|
||||
0x5d: ('applications', False),
|
||||
0x5f: ('sleep', False),
|
||||
0x60: ('0', True),
|
||||
0x61: ('1', True),
|
||||
0x62: ('2', True),
|
||||
0x63: ('3', True),
|
||||
0x64: ('4', True),
|
||||
0x65: ('5', True),
|
||||
0x66: ('6', True),
|
||||
0x67: ('7', True),
|
||||
0x68: ('8', True),
|
||||
0x69: ('9', True),
|
||||
0x6a: ('*', True),
|
||||
0x6b: ('+', True),
|
||||
0x6c: ('separator', True),
|
||||
0x6d: ('-', True),
|
||||
0x6e: ('decimal', True),
|
||||
0x6f: ('/', True),
|
||||
0x70: ('f1', False),
|
||||
0x71: ('f2', False),
|
||||
0x72: ('f3', False),
|
||||
0x73: ('f4', False),
|
||||
0x74: ('f5', False),
|
||||
0x75: ('f6', False),
|
||||
0x76: ('f7', False),
|
||||
0x77: ('f8', False),
|
||||
0x78: ('f9', False),
|
||||
0x79: ('f10', False),
|
||||
0x7a: ('f11', False),
|
||||
0x7b: ('f12', False),
|
||||
0x7c: ('f13', False),
|
||||
0x7d: ('f14', False),
|
||||
0x7e: ('f15', False),
|
||||
0x7f: ('f16', False),
|
||||
0x80: ('f17', False),
|
||||
0x81: ('f18', False),
|
||||
0x82: ('f19', False),
|
||||
0x83: ('f20', False),
|
||||
0x84: ('f21', False),
|
||||
0x85: ('f22', False),
|
||||
0x86: ('f23', False),
|
||||
0x87: ('f24', False),
|
||||
0x90: ('num lock', False),
|
||||
0x91: ('scroll lock', False),
|
||||
0xa0: ('left shift', False),
|
||||
0xa1: ('right shift', False),
|
||||
0xa2: ('left ctrl', False),
|
||||
0xa3: ('right ctrl', False),
|
||||
0xa4: ('left menu', False),
|
||||
0xa5: ('right menu', False),
|
||||
0xa6: ('browser back', False),
|
||||
0xa7: ('browser forward', False),
|
||||
0xa8: ('browser refresh', False),
|
||||
0xa9: ('browser stop', False),
|
||||
0xaa: ('browser search key', False),
|
||||
0xab: ('browser favorites', False),
|
||||
0xac: ('browser start and home', False),
|
||||
0xad: ('volume mute', False),
|
||||
0xae: ('volume down', False),
|
||||
0xaf: ('volume up', False),
|
||||
0xb0: ('next track', False),
|
||||
0xb1: ('previous track', False),
|
||||
0xb2: ('stop media', False),
|
||||
0xb3: ('play/pause media', False),
|
||||
0xb4: ('start mail', False),
|
||||
0xb5: ('select media', False),
|
||||
0xb6: ('start application 1', False),
|
||||
0xb7: ('start application 2', False),
|
||||
0xbb: ('+', False),
|
||||
0xbc: (',', False),
|
||||
0xbd: ('-', False),
|
||||
0xbe: ('.', False),
|
||||
#0xbe:('/', False), # Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?.
|
||||
0xe5: ('ime process', False),
|
||||
0xf6: ('attn', False),
|
||||
0xf7: ('crsel', False),
|
||||
0xf8: ('exsel', False),
|
||||
0xf9: ('erase eof', False),
|
||||
0xfa: ('play', False),
|
||||
0xfb: ('zoom', False),
|
||||
0xfc: ('reserved ', False),
|
||||
0xfd: ('pa1', False),
|
||||
0xfe: ('clear', False),
|
||||
}
|
||||
|
||||
tables_lock = Lock()
|
||||
to_name = defaultdict(list)
|
||||
from_name = defaultdict(list)
|
||||
scan_code_to_vk = {}
|
||||
|
||||
distinct_modifiers = [
|
||||
(),
|
||||
('shift',),
|
||||
('alt gr',),
|
||||
('num lock',),
|
||||
('shift', 'num lock'),
|
||||
('caps lock',),
|
||||
('shift', 'caps lock'),
|
||||
('alt gr', 'num lock'),
|
||||
]
|
||||
|
||||
name_buffer = ctypes.create_unicode_buffer(32)
|
||||
unicode_buffer = ctypes.create_unicode_buffer(32)
|
||||
keyboard_state = keyboard_state_type()
|
||||
def get_event_names(scan_code, vk, is_extended, modifiers):
|
||||
is_keypad = (scan_code, vk, is_extended) in keypad_keys
|
||||
is_official = vk in official_virtual_keys
|
||||
if is_keypad and is_official:
|
||||
yield official_virtual_keys[vk][0]
|
||||
|
||||
keyboard_state[0x10] = 0x80 * ('shift' in modifiers)
|
||||
keyboard_state[0x11] = 0x80 * ('alt gr' in modifiers)
|
||||
keyboard_state[0x12] = 0x80 * ('alt gr' in modifiers)
|
||||
keyboard_state[0x14] = 0x01 * ('caps lock' in modifiers)
|
||||
keyboard_state[0x90] = 0x01 * ('num lock' in modifiers)
|
||||
keyboard_state[0x91] = 0x01 * ('scroll lock' in modifiers)
|
||||
unicode_ret = ToUnicode(vk, scan_code, keyboard_state, unicode_buffer, len(unicode_buffer), 0)
|
||||
if unicode_ret and unicode_buffer.value:
|
||||
yield unicode_buffer.value
|
||||
# unicode_ret == -1 -> is dead key
|
||||
# ToUnicode has the side effect of setting global flags for dead keys.
|
||||
# Therefore we need to call it twice to clear those flags.
|
||||
# If your 6 and 7 keys are named "^6" and "^7", this is the reason.
|
||||
ToUnicode(vk, scan_code, keyboard_state, unicode_buffer, len(unicode_buffer), 0)
|
||||
|
||||
name_ret = GetKeyNameText(scan_code << 16 | is_extended << 24, name_buffer, 1024)
|
||||
if name_ret and name_buffer.value:
|
||||
yield name_buffer.value
|
||||
|
||||
char = user32.MapVirtualKeyW(vk, MAPVK_VK_TO_CHAR) & 0xFF
|
||||
if char != 0:
|
||||
yield chr(char)
|
||||
|
||||
if not is_keypad and is_official:
|
||||
yield official_virtual_keys[vk][0]
|
||||
|
||||
def _setup_name_tables():
|
||||
"""
|
||||
Ensures the scan code/virtual key code/name translation tables are
|
||||
filled.
|
||||
"""
|
||||
with tables_lock:
|
||||
if to_name: return
|
||||
|
||||
# Go through every possible scan code, and map them to virtual key codes.
|
||||
# Then vice-versa.
|
||||
all_scan_codes = [(sc, user32.MapVirtualKeyExW(sc, MAPVK_VSC_TO_VK_EX, 0)) for sc in range(0x100)]
|
||||
all_vks = [(user32.MapVirtualKeyExW(vk, MAPVK_VK_TO_VSC_EX, 0), vk) for vk in range(0x100)]
|
||||
for scan_code, vk in all_scan_codes + all_vks:
|
||||
# `to_name` and `from_name` entries will be a tuple (scan_code, vk, extended, shift_state).
|
||||
if (scan_code, vk, 0, 0, 0) in to_name:
|
||||
continue
|
||||
|
||||
if scan_code not in scan_code_to_vk:
|
||||
scan_code_to_vk[scan_code] = vk
|
||||
|
||||
# Brute force all combinations to find all possible names.
|
||||
for extended in [0, 1]:
|
||||
for modifiers in distinct_modifiers:
|
||||
entry = (scan_code, vk, extended, modifiers)
|
||||
# Get key names from ToUnicode, GetKeyNameText, MapVirtualKeyW and official virtual keys.
|
||||
names = list(get_event_names(*entry))
|
||||
if names:
|
||||
# Also map lowercased key names, but only after the properly cased ones.
|
||||
lowercase_names = [name.lower() for name in names]
|
||||
to_name[entry] = names + lowercase_names
|
||||
# Remember the "id" of the name, as the first techniques
|
||||
# have better results and therefore priority.
|
||||
for i, name in enumerate(map(normalize_name, names + lowercase_names)):
|
||||
from_name[name].append((i, entry))
|
||||
|
||||
# TODO: single quotes on US INTL is returning the dead key (?), and therefore
|
||||
# not typing properly.
|
||||
|
||||
# Alt gr is way outside the usual range of keys (0..127) and on my
|
||||
# computer is named as 'ctrl'. Therefore we add it manually and hope
|
||||
# Windows is consistent in its inconsistency.
|
||||
for extended in [0, 1]:
|
||||
for modifiers in distinct_modifiers:
|
||||
to_name[(541, 162, extended, modifiers)] = ['alt gr']
|
||||
from_name['alt gr'].append((1, (541, 162, extended, modifiers)))
|
||||
|
||||
modifiers_preference = defaultdict(lambda: 10)
|
||||
modifiers_preference.update({(): 0, ('shift',): 1, ('alt gr',): 2, ('ctrl',): 3, ('alt',): 4})
|
||||
def order_key(line):
|
||||
i, entry = line
|
||||
scan_code, vk, extended, modifiers = entry
|
||||
return modifiers_preference[modifiers], i, extended, vk, scan_code
|
||||
for name, entries in list(from_name.items()):
|
||||
from_name[name] = sorted(set(entries), key=order_key)
|
||||
|
||||
# Called by keyboard/__init__.py
|
||||
init = _setup_name_tables
|
||||
|
||||
# List created manually.
|
||||
keypad_keys = [
|
||||
# (scan_code, virtual_key_code, is_extended)
|
||||
(126, 194, 0),
|
||||
(126, 194, 0),
|
||||
(28, 13, 1),
|
||||
(28, 13, 1),
|
||||
(53, 111, 1),
|
||||
(53, 111, 1),
|
||||
(55, 106, 0),
|
||||
(55, 106, 0),
|
||||
(69, 144, 1),
|
||||
(69, 144, 1),
|
||||
(71, 103, 0),
|
||||
(71, 36, 0),
|
||||
(72, 104, 0),
|
||||
(72, 38, 0),
|
||||
(73, 105, 0),
|
||||
(73, 33, 0),
|
||||
(74, 109, 0),
|
||||
(74, 109, 0),
|
||||
(75, 100, 0),
|
||||
(75, 37, 0),
|
||||
(76, 101, 0),
|
||||
(76, 12, 0),
|
||||
(77, 102, 0),
|
||||
(77, 39, 0),
|
||||
(78, 107, 0),
|
||||
(78, 107, 0),
|
||||
(79, 35, 0),
|
||||
(79, 97, 0),
|
||||
(80, 40, 0),
|
||||
(80, 98, 0),
|
||||
(81, 34, 0),
|
||||
(81, 99, 0),
|
||||
(82, 45, 0),
|
||||
(82, 96, 0),
|
||||
(83, 110, 0),
|
||||
(83, 46, 0),
|
||||
]
|
||||
|
||||
shift_is_pressed = False
|
||||
altgr_is_pressed = False
|
||||
ignore_next_right_alt = False
|
||||
shift_vks = set([0x10, 0xa0, 0xa1])
|
||||
def prepare_intercept(callback):
|
||||
"""
|
||||
Registers a Windows low level keyboard hook. The provided callback will
|
||||
be invoked for each high-level keyboard event, and is expected to return
|
||||
True if the key event should be passed to the next program, or False if
|
||||
the event is to be blocked.
|
||||
|
||||
No event is processed until the Windows messages are pumped (see
|
||||
start_intercept).
|
||||
"""
|
||||
_setup_name_tables()
|
||||
|
||||
def process_key(event_type, vk, scan_code, is_extended):
|
||||
global shift_is_pressed, altgr_is_pressed, ignore_next_right_alt
|
||||
#print(event_type, vk, scan_code, is_extended)
|
||||
|
||||
# Pressing alt-gr also generates an extra "right alt" event
|
||||
if vk == 0xA5 and ignore_next_right_alt:
|
||||
ignore_next_right_alt = False
|
||||
return True
|
||||
|
||||
modifiers = (
|
||||
('shift',) * shift_is_pressed +
|
||||
('alt gr',) * altgr_is_pressed +
|
||||
('num lock',) * (user32.GetKeyState(0x90) & 1) +
|
||||
('caps lock',) * (user32.GetKeyState(0x14) & 1) +
|
||||
('scroll lock',) * (user32.GetKeyState(0x91) & 1)
|
||||
)
|
||||
entry = (scan_code, vk, is_extended, modifiers)
|
||||
if entry not in to_name:
|
||||
to_name[entry] = list(get_event_names(*entry))
|
||||
|
||||
names = to_name[entry]
|
||||
name = names[0] if names else None
|
||||
|
||||
# TODO: inaccurate when holding multiple different shifts.
|
||||
if vk in shift_vks:
|
||||
shift_is_pressed = event_type == KEY_DOWN
|
||||
if scan_code == 541 and vk == 162:
|
||||
ignore_next_right_alt = True
|
||||
altgr_is_pressed = event_type == KEY_DOWN
|
||||
|
||||
is_keypad = (scan_code, vk, is_extended) in keypad_keys
|
||||
return callback(KeyboardEvent(event_type=event_type, scan_code=scan_code or -vk, name=name, is_keypad=is_keypad))
|
||||
|
||||
def low_level_keyboard_handler(nCode, wParam, lParam):
|
||||
try:
|
||||
vk = lParam.contents.vk_code
|
||||
# Ignore the second `alt` DOWN observed in some cases.
|
||||
fake_alt = (LLKHF_INJECTED | 0x20)
|
||||
# Ignore events generated by SendInput with Unicode.
|
||||
if vk != VK_PACKET and lParam.contents.flags & fake_alt != fake_alt:
|
||||
event_type = keyboard_event_types[wParam]
|
||||
is_extended = lParam.contents.flags & 1
|
||||
scan_code = lParam.contents.scan_code
|
||||
should_continue = process_key(event_type, vk, scan_code, is_extended)
|
||||
if not should_continue:
|
||||
return -1
|
||||
except Exception as e:
|
||||
print('Error in keyboard hook:')
|
||||
traceback.print_exc()
|
||||
|
||||
return CallNextHookEx(None, nCode, wParam, lParam)
|
||||
|
||||
WH_KEYBOARD_LL = c_int(13)
|
||||
keyboard_callback = LowLevelKeyboardProc(low_level_keyboard_handler)
|
||||
handle = GetModuleHandleW(None)
|
||||
thread_id = DWORD(0)
|
||||
keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_callback, handle, thread_id)
|
||||
|
||||
# Register to remove the hook when the interpreter exits. Unfortunately a
|
||||
# try/finally block doesn't seem to work here.
|
||||
atexit.register(UnhookWindowsHookEx, keyboard_callback)
|
||||
|
||||
def listen(callback):
|
||||
prepare_intercept(callback)
|
||||
msg = LPMSG()
|
||||
while not GetMessage(msg, 0, 0, 0):
|
||||
TranslateMessage(msg)
|
||||
DispatchMessage(msg)
|
||||
|
||||
def map_name(name):
|
||||
_setup_name_tables()
|
||||
|
||||
entries = from_name.get(name)
|
||||
if not entries:
|
||||
raise ValueError('Key name {} is not mapped to any known key.'.format(repr(name)))
|
||||
for i, entry in entries:
|
||||
scan_code, vk, is_extended, modifiers = entry
|
||||
yield scan_code or -vk, modifiers
|
||||
|
||||
def _send_event(code, event_type):
|
||||
if code == 541:
|
||||
# Alt-gr is made of ctrl+alt. Just sending even 541 doesn't do anything.
|
||||
user32.keybd_event(0x11, code, event_type, 0)
|
||||
user32.keybd_event(0x12, code, event_type, 0)
|
||||
elif code > 0:
|
||||
vk = scan_code_to_vk.get(code, 0)
|
||||
user32.keybd_event(vk, code, event_type, 0)
|
||||
else:
|
||||
# Negative scan code is a way to indicate we don't have a scan code,
|
||||
# and the value actually contains the Virtual key code.
|
||||
user32.keybd_event(-code, 0, event_type, 0)
|
||||
|
||||
def press(code):
|
||||
_send_event(code, 0)
|
||||
|
||||
def release(code):
|
||||
_send_event(code, 2)
|
||||
|
||||
def type_unicode(character):
|
||||
# This code and related structures are based on
|
||||
# http://stackoverflow.com/a/11910555/252218
|
||||
surrogates = bytearray(character.encode('utf-16le'))
|
||||
presses = []
|
||||
releases = []
|
||||
for i in range(0, len(surrogates), 2):
|
||||
higher, lower = surrogates[i:i+2]
|
||||
structure = KEYBDINPUT(0, (lower << 8) + higher, KEYEVENTF_UNICODE, 0, None)
|
||||
presses.append(INPUT(INPUT_KEYBOARD, _INPUTunion(ki=structure)))
|
||||
structure = KEYBDINPUT(0, (lower << 8) + higher, KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, 0, None)
|
||||
releases.append(INPUT(INPUT_KEYBOARD, _INPUTunion(ki=structure)))
|
||||
inputs = presses + releases
|
||||
nInputs = len(inputs)
|
||||
LPINPUT = INPUT * nInputs
|
||||
pInputs = LPINPUT(*inputs)
|
||||
cbSize = c_int(ctypes.sizeof(INPUT))
|
||||
SendInput(nInputs, pInputs, cbSize)
|
||||
|
||||
if __name__ == '__main__':
|
||||
_setup_name_tables()
|
||||
import pprint
|
||||
pprint.pprint(to_name)
|
||||
pprint.pprint(from_name)
|
||||
#listen(lambda e: print(e.to_json()) or True)
|
||||
Reference in New Issue
Block a user