1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-21 11:27:38 +03:00

hotkey: win32 flake8 linting

This commit is contained in:
Athanasius 2021-04-07 16:54:13 +01:00
parent 7dbf150846
commit 42cd2c501e

298
hotkey.py

@ -32,10 +32,10 @@ if platform == 'darwin': # noqa: C901
# https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSEvent_Class/#//apple_ref/doc/constant_group/Function_Key_Unicodes
DISPLAY = {
0x03: u'', 0x09: u'', 0xd: u'', 0x19: u'', 0x1b: u'esc', 0x20: u'', 0x7f: u'',
0xf700: u'', 0xf701: u'', 0xf702: u'', 0xf703: u'',
0xf700: u'', 0xf701: u'', 0xf702: u'', 0xf703: u'',
0xf727: u'Ins',
0xf728: u'', 0xf729: u'', 0xf72a: u'Fn', 0xf72b: u'',
0xf72c: u'', 0xf72d: u'', 0xf72e: u'PrtScr', 0xf72f: u'ScrollLock',
0xf728: u'', 0xf729: u'', 0xf72a: u'Fn', 0xf72b: u'',
0xf72c: u'', 0xf72d: u'', 0xf72e: u'PrtScr', 0xf72f: u'ScrollLock',
0xf730: u'Pause', 0xf731: u'SysReq', 0xf732: u'Break', 0xf733: u'Reset',
0xf739: u'',
}
@ -122,6 +122,7 @@ if platform == 'darwin': # noqa: C901
# replace empty characters with charactersIgnoringModifiers to avoid crash
elif the_event.type() in (NSKeyDown, NSKeyUp) and not the_event.characters():
the_event = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( # noqa: E501
# noqa: E501
the_event.type(),
the_event.locationInWindow(),
the_event.modifierFlags(),
@ -203,7 +204,7 @@ if platform == 'darwin': # noqa: C901
(keycode, modifiers) = (event.keycode & 0xffff, event.keycode & 0xffff0000) # Set by _acquire_poll()
if (keycode
and not (modifiers & (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask))):
if keycode == 0x1b: # Esc = retain previous
if keycode == 0x1b: # Esc = retain previous
self.acquire_state = HotkeyMgr.ACQUIRE_INACTIVE
return False
@ -254,7 +255,7 @@ if platform == 'darwin': # noqa: C901
text += HotkeyMgr.DISPLAY[keycode]
elif keycode < 0x20: # control keys
text += chr(keycode+0x40)
text += chr(keycode + 0x40)
elif keycode < 0xf700: # key char
text += chr(keycode).upper()
@ -281,78 +282,117 @@ elif platform == 'win32':
import winsound
from ctypes.wintypes import DWORD, HWND, LONG, LPWSTR, MSG, ULONG, WORD
RegisterHotKey = ctypes.windll.user32.RegisterHotKey
UnregisterHotKey = ctypes.windll.user32.UnregisterHotKey
MOD_ALT = 0x0001
MOD_CONTROL = 0x0002
MOD_SHIFT = 0x0004
MOD_WIN = 0x0008
RegisterHotKey = ctypes.windll.user32.RegisterHotKey
UnregisterHotKey = ctypes.windll.user32.UnregisterHotKey
MOD_ALT = 0x0001
MOD_CONTROL = 0x0002
MOD_SHIFT = 0x0004
MOD_WIN = 0x0008
MOD_NOREPEAT = 0x4000
GetMessage = ctypes.windll.user32.GetMessageW
TranslateMessage = ctypes.windll.user32.TranslateMessage
DispatchMessage = ctypes.windll.user32.DispatchMessageW
GetMessage = ctypes.windll.user32.GetMessageW
TranslateMessage = ctypes.windll.user32.TranslateMessage
DispatchMessage = ctypes.windll.user32.DispatchMessageW
PostThreadMessage = ctypes.windll.user32.PostThreadMessageW
WM_QUIT = 0x0012
WM_HOTKEY = 0x0312
WM_APP = 0x8000
WM_SND_GOOD = WM_APP + 1
WM_SND_BAD = WM_APP + 2
WM_QUIT = 0x0012
WM_HOTKEY = 0x0312
WM_APP = 0x8000
WM_SND_GOOD = WM_APP + 1
WM_SND_BAD = WM_APP + 2
GetKeyState = ctypes.windll.user32.GetKeyState
MapVirtualKey = ctypes.windll.user32.MapVirtualKeyW
VK_BACK = 0x08
VK_CLEAR = 0x0c
VK_RETURN = 0x0d
VK_SHIFT = 0x10
VK_CONTROL = 0x11
VK_MENU = 0x12
VK_CAPITAL = 0x14
VK_MODECHANGE= 0x1f
VK_ESCAPE = 0x1b
VK_SPACE = 0x20
VK_DELETE = 0x2e
VK_LWIN = 0x5b
VK_RWIN = 0x5c
VK_NUMPAD0 = 0x60
VK_DIVIDE = 0x6f
VK_F1 = 0x70
VK_F24 = 0x87
GetKeyState = ctypes.windll.user32.GetKeyState
MapVirtualKey = ctypes.windll.user32.MapVirtualKeyW
VK_BACK = 0x08
VK_CLEAR = 0x0c
VK_RETURN = 0x0d
VK_SHIFT = 0x10
VK_CONTROL = 0x11
VK_MENU = 0x12
VK_CAPITAL = 0x14
VK_MODECHANGE = 0x1f
VK_ESCAPE = 0x1b
VK_SPACE = 0x20
VK_DELETE = 0x2e
VK_LWIN = 0x5b
VK_RWIN = 0x5c
VK_NUMPAD0 = 0x60
VK_DIVIDE = 0x6f
VK_F1 = 0x70
VK_F24 = 0x87
VK_OEM_MINUS = 0xbd
VK_NUMLOCK = 0x90
VK_SCROLL = 0x91
VK_PROCESSKEY= 0xe5
VK_NUMLOCK = 0x90
VK_SCROLL = 0x91
VK_PROCESSKEY = 0xe5
VK_OEM_CLEAR = 0xfe
GetForegroundWindow = ctypes.windll.user32.GetForegroundWindow
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int]
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
def WindowTitle(h):
def window_title(h) -> str:
"""
Determine the title for a window.
:param h: Window handle.
:return: Window title.
"""
if h:
l = GetWindowTextLength(h) + 1
buf = ctypes.create_unicode_buffer(l)
if GetWindowText(h, buf, l):
title_length = GetWindowTextLength(h) + 1
buf = ctypes.create_unicode_buffer(title_length)
if GetWindowText(h, buf, title_length):
return buf.value
return ''
class MOUSEINPUT(ctypes.Structure):
_fields_ = [('dx', LONG), ('dy', LONG), ('mouseData', DWORD), ('dwFlags', DWORD), ('time', DWORD), ('dwExtraInfo', ctypes.POINTER(ULONG))]
"""Mouse Input structure."""
_fields_ = [
('dx', LONG),
('dy', LONG),
('mouseData', DWORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', ctypes.POINTER(ULONG))
]
class KEYBDINPUT(ctypes.Structure):
_fields_ = [('wVk', WORD), ('wScan', WORD), ('dwFlags', DWORD), ('time', DWORD), ('dwExtraInfo', ctypes.POINTER(ULONG))]
"""Keyboard Input structure."""
_fields_ = [
('wVk', WORD),
('wScan', WORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', ctypes.POINTER(ULONG))
]
class HARDWAREINPUT(ctypes.Structure):
_fields_ = [('uMsg', DWORD), ('wParamL', WORD), ('wParamH', WORD)]
"""Hardware Input structure."""
class INPUT_union(ctypes.Union):
_fields_ = [('mi', MOUSEINPUT), ('ki', KEYBDINPUT), ('hi', HARDWAREINPUT)]
_fields_ = [
('uMsg', DWORD),
('wParamL', WORD),
('wParamH', WORD)
]
class INPUTUNION(ctypes.Union):
"""Input union."""
_fields_ = [
('mi', MOUSEINPUT),
('ki', KEYBDINPUT),
('hi', HARDWAREINPUT)
]
class INPUT(ctypes.Structure):
_fields_ = [('type', DWORD), ('union', INPUT_union)]
"""Input structure."""
_fields_ = [
('type', DWORD),
('union', INPUTUNION)
]
SendInput = ctypes.windll.user32.SendInput
SendInput.argtypes = [ctypes.c_uint, ctypes.POINTER(INPUT), ctypes.c_int]
@ -361,19 +401,20 @@ elif platform == 'win32':
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2
class HotkeyMgr:
"""Hot key management."""
# https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
# Limit ourselves to symbols in Windows 7 Segoe UI
DISPLAY = { 0x03: 'Break', 0x08: 'Bksp', 0x09: u'', 0x0c: 'Clear', 0x0d: u'', 0x13: 'Pause',
0x14: u'', 0x1b: 'Esc',
0x20: u'', 0x21: 'PgUp', 0x22: 'PgDn', 0x23: 'End', 0x24: 'Home',
0x25: u'', 0x26: u'', 0x27: u'', 0x28: u'',
0x2c: 'PrtScn', 0x2d: 'Ins', 0x2e: 'Del', 0x2f: 'Help',
0x5d: u'', 0x5f: u'',
0x90: u'', 0x91: 'ScrLk',
0xa6: u'', 0xa7: u'', 0xa9: u'', 0xab: u'', 0xac: u'', 0xb4: u'',
DISPLAY = {
0x03: 'Break', 0x08: 'Bksp', 0x09: u'', 0x0c: 'Clear', 0x0d: u'', 0x13: 'Pause',
0x14: u'', 0x1b: 'Esc',
0x20: u'', 0x21: 'PgUp', 0x22: 'PgDn', 0x23: 'End', 0x24: 'Home',
0x25: u'', 0x26: u'', 0x27: u'', 0x28: u'',
0x2c: 'PrtScn', 0x2d: 'Ins', 0x2e: 'Del', 0x2f: 'Help',
0x5d: u'', 0x5f: u'',
0x90: u'', 0x91: 'ScrLk',
0xa6: u'', 0xa7: u'', 0xa9: u'', 0xab: u'', 0xac: u'', 0xb4: u'',
}
def __init__(self):
@ -385,7 +426,7 @@ elif platform == 'win32':
self.snd_bad = sb.read()
atexit.register(self.unregister)
def register(self, root, keycode, modifiers):
def register(self, root: tk.Tk, keycode, modifiers) -> None:
"""Register the hotkey handler."""
self.root = root
@ -395,13 +436,17 @@ elif platform == 'win32':
if keycode or modifiers:
logger.debug('Creating thread worker...')
self.thread = threading.Thread(target=self.worker, name=f'Hotkey "{keycode}:{modifiers}"', args=(keycode, modifiers))
self.thread = threading.Thread(
target=self.worker,
name=f'Hotkey "{keycode}:{modifiers}"',
args=(keycode, modifiers)
)
self.thread.daemon = True
logger.debug('Starting thread worker...')
self.thread.start()
logger.debug('Done.')
def unregister(self):
def unregister(self) -> None:
"""Unregister the hotkey handling."""
thread = self.thread
@ -418,7 +463,7 @@ elif platform == 'win32':
logger.debug('Done.')
def worker(self, keycode, modifiers):
def worker(self, keycode, modifiers) -> None: # noqa: CCR001
"""Handle hotkeys."""
logger.debug('Begin...')
# Hotkey must be registered by the thread that handles it
@ -427,7 +472,7 @@ elif platform == 'win32':
self.thread = None
return
fake = INPUT(INPUT_KEYBOARD, INPUT_union(ki=KEYBDINPUT(keycode, keycode, 0, 0, None)))
fake = INPUT(INPUT_KEYBOARD, INPUTUNION(ki=KEYBDINPUT(keycode, keycode, 0, 0, None)))
msg = MSG()
logger.debug('Entering GetMessage() loop...')
@ -438,7 +483,7 @@ elif platform == 'win32':
if (
config.get_int('hotkey_always')
or WindowTitle(GetForegroundWindow()).startswith('Elite - Dangerous')
or window_title(GetForegroundWindow()).startswith('Elite - Dangerous')
):
if not config.shutting_down:
logger.debug('Sending event <<Invoke>>')
@ -458,7 +503,7 @@ elif platform == 'win32':
elif msg.message == WM_SND_BAD:
logger.debug('WM_SND_BAD')
winsound.PlaySound(self.snd_bad, winsound.SND_MEMORY) # synchronous
winsound.PlaySound(self.snd_bad, winsound.SND_MEMORY) # synchronous
else:
logger.debug('Something else')
@ -470,86 +515,135 @@ elif platform == 'win32':
self.thread = None
logger.debug('Done.')
def acquire_start(self):
def acquire_start(self) -> None:
"""Start acquiring hotkey state via polling."""
pass
def acquire_stop(self):
def acquire_stop(self) -> None:
"""Stop acquiring hotkey state."""
pass
def fromevent(self, event):
# event.state is a pain - it shows the state of the modifiers *before* a modifier key was pressed.
# event.state *does* differentiate between left and right Ctrl and Alt and between Return and Enter
# by putting KF_EXTENDED in bit 18, but RegisterHotKey doesn't differentiate.
modifiers = ((GetKeyState(VK_MENU) & 0x8000) and MOD_ALT) | ((GetKeyState(VK_CONTROL) & 0x8000) and MOD_CONTROL) | ((GetKeyState(VK_SHIFT) & 0x8000) and MOD_SHIFT) | ((GetKeyState(VK_LWIN) & 0x8000) and MOD_WIN) | ((GetKeyState(VK_RWIN) & 0x8000) and MOD_WIN)
def fromevent(self, event) -> Optional[Union[bool, Tuple]]: # noqa: CCR001
"""
Return configuration (keycode, modifiers) or None=clear or False=retain previous.
event.state is a pain - it shows the state of the modifiers *before* a modifier key was pressed.
event.state *does* differentiate between left and right Ctrl and Alt and between Return and Enter
by putting KF_EXTENDED in bit 18, but RegisterHotKey doesn't differentiate.
:param event: tk event ?
:return: False to retain previous, None to not use, else (keycode, modifiers)
"""
modifiers = ((GetKeyState(VK_MENU) & 0x8000) and MOD_ALT) \
| ((GetKeyState(VK_CONTROL) & 0x8000) and MOD_CONTROL) \
| ((GetKeyState(VK_SHIFT) & 0x8000) and MOD_SHIFT) \
| ((GetKeyState(VK_LWIN) & 0x8000) and MOD_WIN) \
| ((GetKeyState(VK_RWIN) & 0x8000) and MOD_WIN)
keycode = event.keycode
if keycode in [ VK_SHIFT, VK_CONTROL, VK_MENU, VK_LWIN, VK_RWIN ]:
if keycode in [VK_SHIFT, VK_CONTROL, VK_MENU, VK_LWIN, VK_RWIN]:
return (0, modifiers)
if not modifiers:
if keycode == VK_ESCAPE: # Esc = retain previous
if keycode == VK_ESCAPE: # Esc = retain previous
return False
elif keycode in [ VK_BACK, VK_DELETE, VK_CLEAR, VK_OEM_CLEAR ]: # BkSp, Del, Clear = clear hotkey
elif keycode in [VK_BACK, VK_DELETE, VK_CLEAR, VK_OEM_CLEAR]: # BkSp, Del, Clear = clear hotkey
return None
elif keycode in [ VK_RETURN, VK_SPACE, VK_OEM_MINUS] or ord('A') <= keycode <= ord('Z'): # don't allow keys needed for typing in System Map
elif keycode in [VK_RETURN, VK_SPACE, VK_OEM_MINUS] or ord('A') <= keycode <= ord(
'Z'): # don't allow keys needed for typing in System Map
winsound.MessageBeep()
return None
elif keycode in [ VK_NUMLOCK, VK_SCROLL, VK_PROCESSKEY ] or VK_CAPITAL <= keycode <= VK_MODECHANGE: # ignore unmodified mode switch keys
elif (keycode in [VK_NUMLOCK, VK_SCROLL, VK_PROCESSKEY]
or VK_CAPITAL <= keycode <= VK_MODECHANGE): # ignore unmodified mode switch keys
return (0, modifiers)
# See if the keycode is usable and available
if RegisterHotKey(None, 2, modifiers|MOD_NOREPEAT, keycode):
if RegisterHotKey(None, 2, modifiers | MOD_NOREPEAT, keycode):
UnregisterHotKey(None, 2)
return (keycode, modifiers)
else:
winsound.MessageBeep()
return None
def display(self, keycode, modifiers):
def display(self, keycode, modifiers) -> str:
"""
Return displayable form of given hotkey + modifiers.
:param keycode:
:param modifiers:
:return: string form
"""
text = ''
if modifiers & MOD_WIN: text += u'❖+'
if modifiers & MOD_CONTROL: text += u'Ctrl+'
if modifiers & MOD_ALT: text += u'Alt+'
if modifiers & MOD_SHIFT: text += u'⇧+'
if VK_NUMPAD0 <= keycode <= VK_DIVIDE: text += u''
if modifiers & MOD_WIN:
text += u'❖+'
if modifiers & MOD_CONTROL:
text += u'Ctrl+'
if modifiers & MOD_ALT:
text += u'Alt+'
if modifiers & MOD_SHIFT:
text += u'⇧+'
if VK_NUMPAD0 <= keycode <= VK_DIVIDE:
text += u''
if not keycode:
pass
elif VK_F1 <= keycode <= VK_F24:
text += 'F%d' % (keycode + 1 - VK_F1)
elif keycode in HotkeyMgr.DISPLAY: # specials
text += f'F{keycode + 1 - VK_F1}'
elif keycode in HotkeyMgr.DISPLAY: # specials
text += HotkeyMgr.DISPLAY[keycode]
else:
c = MapVirtualKey(keycode, 2) # printable ?
if not c: # oops not printable
c = MapVirtualKey(keycode, 2) # printable ?
if not c: # oops not printable
text += u''
elif c < 0x20: # control keys
text += chr(c+0x40)
elif c < 0x20: # control keys
text += chr(c + 0x40)
else:
text += chr(c).upper()
return text
def play_good(self):
def play_good(self) -> None:
"""Play the 'good' sound."""
if self.thread:
PostThreadMessage(self.thread.ident, WM_SND_GOOD, 0, 0)
def play_bad(self):
def play_bad(self) -> None:
"""Play the 'bad' sound."""
if self.thread:
PostThreadMessage(self.thread.ident, WM_SND_BAD, 0, 0)
else: # Linux
else: # Linux
class HotkeyMgr:
"""Hot key management."""
def register(self, root, keycode, modifiers):
def register(self, root, keycode, modifiers) -> None:
"""Register the hotkey handler."""
pass
def unregister(self):
def unregister(self) -> None:
"""Unregister the hotkey handling."""
pass
def play_good(self):
def play_good(self) -> None:
"""Play the 'good' sound."""
pass
def play_bad(self):
def play_bad(self) -> None:
"""Play the 'bad' sound."""
pass
# singleton