1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-06-08 19:32:15 +03:00

[1805] Implement win32con in Hotkey

This commit is contained in:
David Sangrey 2024-06-11 10:42:57 -04:00
parent b1ba45ab90
commit f8d354a4dd
No known key found for this signature in database
GPG Key ID: 3AEADBB0186884BC
3 changed files with 41 additions and 77 deletions

View File

@ -253,23 +253,17 @@ if __name__ == '__main__': # noqa: C901
logger.trace_if('frontier-auth.windows', 'Begin...') logger.trace_if('frontier-auth.windows', 'Begin...')
if sys.platform == 'win32': if sys.platform == 'win32':
# If *this* instance hasn't locked, then another already has and we # If *this* instance hasn't locked, then another already has and we
# now need to do the edmc:// checks for auth callback # now need to do the edmc:// checks for auth callback
if locked != JournalLockResult.LOCKED: if locked != JournalLockResult.LOCKED:
from ctypes import windll, c_int, create_unicode_buffer, WINFUNCTYPE from ctypes import windll, create_unicode_buffer, WINFUNCTYPE
from ctypes.wintypes import BOOL, HWND, INT, LPARAM, LPCWSTR, LPWSTR from ctypes.wintypes import BOOL, HWND, LPARAM
import win32gui
import win32api
EnumWindows = windll.user32.EnumWindows # noqa: N806
GetClassName = windll.user32.GetClassNameW # noqa: N806
GetClassName.argtypes = [HWND, LPWSTR, c_int]
GetWindowText = windll.user32.GetWindowTextW # noqa: N806
GetWindowText.argtypes = [HWND, LPWSTR, c_int]
GetWindowTextLength = windll.user32.GetWindowTextLengthW # noqa: N806
GetProcessHandleFromHwnd = windll.oleacc.GetProcessHandleFromHwnd # noqa: N806 GetProcessHandleFromHwnd = windll.oleacc.GetProcessHandleFromHwnd # noqa: N806
SW_RESTORE = 9 # noqa: N806 SW_RESTORE = 9 # noqa: N806
SetForegroundWindow = windll.user32.SetForegroundWindow # noqa: N806
ShowWindow = windll.user32.ShowWindow # noqa: N806 ShowWindow = windll.user32.ShowWindow # noqa: N806
ShowWindowAsync = windll.user32.ShowWindowAsync # noqa: N806 ShowWindowAsync = windll.user32.ShowWindowAsync # noqa: N806
@ -278,14 +272,11 @@ if __name__ == '__main__': # noqa: C901
COINIT_DISABLE_OLE1DDE = 0x4 # noqa: N806 COINIT_DISABLE_OLE1DDE = 0x4 # noqa: N806
CoInitializeEx = windll.ole32.CoInitializeEx # noqa: N806 CoInitializeEx = windll.ole32.CoInitializeEx # noqa: N806
ShellExecute = windll.shell32.ShellExecuteW # noqa: N806
ShellExecute.argtypes = [HWND, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, INT]
def window_title(h: int) -> str | None: def window_title(h: int) -> str | None:
if h: if h:
text_length = GetWindowTextLength(h) + 1 text_length = win32gui.GetWindowTextLength(h) + 1
buf = create_unicode_buffer(text_length) buf = create_unicode_buffer(text_length)
if GetWindowText(h, buf, text_length): if win32gui.GetWindowText(h):
return buf.value return buf.value
return None return None
@ -309,7 +300,7 @@ if __name__ == '__main__': # noqa: C901
# class name limited to 256 - https://msdn.microsoft.com/en-us/library/windows/desktop/ms633576 # class name limited to 256 - https://msdn.microsoft.com/en-us/library/windows/desktop/ms633576
cls = create_unicode_buffer(257) cls = create_unicode_buffer(257)
# This conditional is exploded to make debugging slightly easier # This conditional is exploded to make debugging slightly easier
if GetClassName(window_handle, cls, 257): if win32gui.GetClassName(window_handle, cls, 257):
if cls.value == 'TkTopLevel': if cls.value == 'TkTopLevel':
if window_title(window_handle) == applongname: if window_title(window_handle) == applongname:
if GetProcessHandleFromHwnd(window_handle): if GetProcessHandleFromHwnd(window_handle):
@ -318,11 +309,11 @@ if __name__ == '__main__': # noqa: C901
CoInitializeEx(0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE) CoInitializeEx(0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
# Wait for it to be responsive to avoid ShellExecute recursing # Wait for it to be responsive to avoid ShellExecute recursing
ShowWindow(window_handle, SW_RESTORE) ShowWindow(window_handle, SW_RESTORE)
ShellExecute(0, None, sys.argv[1], None, None, SW_RESTORE) win32api.ShellExecute(0, None, sys.argv[1], None, None, SW_RESTORE)
else: else:
ShowWindowAsync(window_handle, SW_RESTORE) ShowWindowAsync(window_handle, SW_RESTORE)
SetForegroundWindow(window_handle) win32gui.SetForegroundWindow(window_handle)
return False # Indicate window found, so stop iterating return False # Indicate window found, so stop iterating
@ -334,7 +325,7 @@ if __name__ == '__main__': # noqa: C901
# enumwindwsproc() on each. When an invocation returns False it # enumwindwsproc() on each. When an invocation returns False it
# stops iterating. # stops iterating.
# Ref: <https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumwindows> # Ref: <https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumwindows>
EnumWindows(enumwindowsproc, 0) win32gui.EnumWindows(enumwindowsproc, 0)
def already_running_popup(): def already_running_popup():
"""Create the "already running" popup.""" """Create the "already running" popup."""
@ -701,13 +692,14 @@ class AppWindow:
if match: if match:
if sys.platform == 'win32': if sys.platform == 'win32':
# Check that the titlebar will be at least partly on screen # Check that the titlebar will be at least partly on screen
import ctypes import win32api
from ctypes.wintypes import POINT import win32con
x = int(match.group(1)) + 16
y = int(match.group(2)) + 16
point = (x, y)
# https://msdn.microsoft.com/en-us/library/dd145064 # https://msdn.microsoft.com/en-us/library/dd145064
MONITOR_DEFAULTTONULL = 0 # noqa: N806 if win32api.MonitorFromPoint(point, win32con.MONITOR_DEFAULTTONULL):
if ctypes.windll.user32.MonitorFromPoint(POINT(int(match.group(1)) + 16, int(match.group(2)) + 16),
MONITOR_DEFAULTTONULL):
self.w.geometry(config.get_str('geometry')) self.w.geometry(config.get_str('geometry'))
else: else:
self.w.geometry(config.get_str('geometry')) self.w.geometry(config.get_str('geometry'))

View File

@ -12,6 +12,7 @@ from ctypes.wintypes import DWORD, LONG, MSG, ULONG, WORD, HWND, BOOL, UINT
import pywintypes import pywintypes
import win32api import win32api
import win32gui import win32gui
import win32con
from config import config from config import config
from EDMCLogging import get_main_logger from EDMCLogging import get_main_logger
from hotkey import AbstractHotkeyMgr from hotkey import AbstractHotkeyMgr
@ -24,40 +25,10 @@ UnregisterHotKey = ctypes.windll.user32.UnregisterHotKey # TODO: Coming Soon
UnregisterHotKey.argtypes = [HWND, ctypes.c_int] UnregisterHotKey.argtypes = [HWND, ctypes.c_int]
UnregisterHotKey.restype = BOOL UnregisterHotKey.restype = BOOL
MOD_ALT = 0x0001
MOD_CONTROL = 0x0002
MOD_SHIFT = 0x0004
MOD_WIN = 0x0008
MOD_NOREPEAT = 0x4000 MOD_NOREPEAT = 0x4000
WM_SND_GOOD = win32con.WM_APP + 1
WM_QUIT = 0x0012 WM_SND_BAD = win32con.WM_APP + 2
WM_HOTKEY = 0x0312
WM_APP = 0x8000
WM_SND_GOOD = WM_APP + 1
WM_SND_BAD = WM_APP + 2
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_OEM_MINUS = 0xbd
VK_NUMLOCK = 0x90
VK_SCROLL = 0x91
VK_PROCESSKEY = 0xe5
VK_OEM_CLEAR = 0xfe
# VirtualKey mapping values # VirtualKey mapping values
# <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapvirtualkeyexa> # <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapvirtualkeyexa>
@ -200,7 +171,7 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
logger.debug('Thread is/was running') logger.debug('Thread is/was running')
self.thread = None # type: ignore self.thread = None # type: ignore
logger.debug('Telling thread WM_QUIT') logger.debug('Telling thread WM_QUIT')
win32gui.PostThreadMessage(thread.ident, WM_QUIT, 0, 0) win32gui.PostThreadMessage(thread.ident, win32con.WM_QUIT, 0, 0)
logger.debug('Joining thread') logger.debug('Joining thread')
thread.join() # Wait for it to unregister hotkey and quit thread.join() # Wait for it to unregister hotkey and quit
@ -226,7 +197,7 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
logger.debug('Entering GetMessage() loop...') logger.debug('Entering GetMessage() loop...')
while win32gui.GetMessage(ctypes.byref(msg), None, 0, 0) != 0: while win32gui.GetMessage(ctypes.byref(msg), None, 0, 0) != 0:
logger.debug('Got message') logger.debug('Got message')
if msg.message == WM_HOTKEY: if msg.message == win32con.WM_HOTKEY:
logger.debug('WM_HOTKEY') logger.debug('WM_HOTKEY')
if ( if (
@ -284,31 +255,32 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
:param event: tk event ? :param event: tk event ?
:return: False to retain previous, None to not use, else (keycode, modifiers) :return: False to retain previous, None to not use, else (keycode, modifiers)
""" """
modifiers = ((win32api.GetKeyState(VK_MENU) & 0x8000) and MOD_ALT) \ modifiers = ((win32api.GetKeyState(win32con.VK_MENU) & 0x8000) and win32con.MOD_ALT) \
| ((win32api.GetKeyState(VK_CONTROL) & 0x8000) and MOD_CONTROL) \ | ((win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000) and win32con.MOD_CONTROL) \
| ((win32api.GetKeyState(VK_SHIFT) & 0x8000) and MOD_SHIFT) \ | ((win32api.GetKeyState(win32con.VK_SHIFT) & 0x8000) and win32con.MOD_SHIFT) \
| ((win32api.GetKeyState(VK_LWIN) & 0x8000) and MOD_WIN) \ | ((win32api.GetKeyState(win32con.VK_LWIN) & 0x8000) and win32con.MOD_WIN) \
| ((win32api.GetKeyState(VK_RWIN) & 0x8000) and MOD_WIN) | ((win32api.GetKeyState(win32con.VK_RWIN) & 0x8000) and win32con.MOD_WIN)
keycode = event.keycode keycode = event.keycode
if keycode in (VK_SHIFT, VK_CONTROL, VK_MENU, VK_LWIN, VK_RWIN): if keycode in (win32con.VK_SHIFT, win32con.VK_CONTROL, win32con.VK_MENU, win32con.VK_LWIN, win32con.VK_RWIN):
return 0, modifiers return 0, modifiers
if not modifiers: if not modifiers:
if keycode == VK_ESCAPE: # Esc = retain previous if keycode == win32con.VK_ESCAPE: # Esc = retain previous
return False return False
if keycode in (VK_BACK, VK_DELETE, VK_CLEAR, VK_OEM_CLEAR): # BkSp, Del, Clear = clear hotkey if keycode in (win32con.VK_BACK, win32con.VK_DELETE,
win32con.VK_CLEAR, win32con.VK_OEM_CLEAR): # BkSp, Del, Clear = clear hotkey
return None return None
if ( if (
keycode in (VK_RETURN, VK_SPACE, VK_OEM_MINUS) or ord('A') <= keycode <= ord('Z') keycode in (win32con.VK_RETURN, win32con.VK_SPACE, VK_OEM_MINUS) or ord('A') <= keycode <= ord('Z')
): # don't allow keys needed for typing in System Map ): # don't allow keys needed for typing in System Map
winsound.MessageBeep() winsound.MessageBeep()
return None return None
if (keycode in (VK_NUMLOCK, VK_SCROLL, VK_PROCESSKEY) if (keycode in (win32con.VK_NUMLOCK, win32con.VK_SCROLL, win32con.VK_PROCESSKEY)
or VK_CAPITAL <= keycode <= VK_MODECHANGE): # ignore unmodified mode switch keys or win32con.VK_CAPITAL <= keycode <= win32con.VK_MODECHANGE): # ignore unmodified mode switch keys
return 0, modifiers return 0, modifiers
# See if the keycode is usable and available # See if the keycode is usable and available
@ -329,26 +301,26 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
:return: string form :return: string form
""" """
text = '' text = ''
if modifiers & MOD_WIN: if modifiers & win32con.MOD_WIN:
text += '❖+' text += '❖+'
if modifiers & MOD_CONTROL: if modifiers & win32con.MOD_CONTROL:
text += 'Ctrl+' text += 'Ctrl+'
if modifiers & MOD_ALT: if modifiers & win32con.MOD_ALT:
text += 'Alt+' text += 'Alt+'
if modifiers & MOD_SHIFT: if modifiers & win32con.MOD_SHIFT:
text += '⇧+' text += '⇧+'
if VK_NUMPAD0 <= keycode <= VK_DIVIDE: if win32con.VK_NUMPAD0 <= keycode <= win32con.VK_DIVIDE:
text += '' text += ''
if not keycode: if not keycode:
pass pass
elif VK_F1 <= keycode <= VK_F24: elif win32con.VK_F1 <= keycode <= win32con.VK_F24:
text += f'F{keycode + 1 - VK_F1}' text += f'F{keycode + 1 - win32con.VK_F1}'
elif keycode in WindowsHotkeyMgr.DISPLAY: # specials elif keycode in WindowsHotkeyMgr.DISPLAY: # specials
text += WindowsHotkeyMgr.DISPLAY[keycode] text += WindowsHotkeyMgr.DISPLAY[keycode]

View File

@ -2127,7 +2127,7 @@ class EDLogs(FileSystemEventHandler):
if h: if h:
length = win32gui.GetWindowTextLength(h) + 1 length = win32gui.GetWindowTextLength(h) + 1
buf = ctypes.create_unicode_buffer(length) buf = ctypes.create_unicode_buffer(length)
if win32gui.GetWindowText(h, buf, length): if win32gui.GetWindowText(h):
return buf.value return buf.value
return None return None