mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-04-12 23:37:14 +03:00
[#1805] pywin32 Handoff
This commit is contained in:
parent
b10548da57
commit
17a7af959a
@ -7,41 +7,23 @@ See LICENSE file.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import ctypes
|
|
||||||
import functools
|
import functools
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
import uuid
|
import uuid
|
||||||
import winreg
|
import winreg
|
||||||
from ctypes.wintypes import DWORD, HANDLE
|
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
from config import AbstractConfig, applongname, appname, logger
|
from config import AbstractConfig, applongname, appname, logger
|
||||||
|
from win32comext.shell import shell
|
||||||
|
|
||||||
assert sys.platform == 'win32'
|
assert sys.platform == 'win32'
|
||||||
|
|
||||||
REG_RESERVED_ALWAYS_ZERO = 0
|
REG_RESERVED_ALWAYS_ZERO = 0
|
||||||
|
|
||||||
# This is the only way to do this from python without external deps (which do this anyway).
|
|
||||||
FOLDERID_Documents = uuid.UUID('{FDD39AD0-238F-46AF-ADB4-6C85480369C7}')
|
|
||||||
FOLDERID_LocalAppData = uuid.UUID('{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}')
|
|
||||||
FOLDERID_Profile = uuid.UUID('{5E6C858F-0E22-4760-9AFE-EA3317B67173}')
|
|
||||||
FOLDERID_SavedGames = uuid.UUID('{4C5C32FF-BB9D-43b0-B5B4-2D72E54EAAA4}')
|
|
||||||
|
|
||||||
SHGetKnownFolderPath = ctypes.windll.shell32.SHGetKnownFolderPath
|
|
||||||
SHGetKnownFolderPath.argtypes = [ctypes.c_char_p, DWORD, HANDLE, ctypes.POINTER(ctypes.c_wchar_p)]
|
|
||||||
|
|
||||||
CoTaskMemFree = ctypes.windll.ole32.CoTaskMemFree
|
|
||||||
CoTaskMemFree.argtypes = [ctypes.c_void_p]
|
|
||||||
|
|
||||||
|
|
||||||
def known_folder_path(guid: uuid.UUID) -> str | None:
|
def known_folder_path(guid: uuid.UUID) -> str | None:
|
||||||
"""Look up a Windows GUID to actual folder path name."""
|
"""Look up a Windows GUID to actual folder path name."""
|
||||||
buf = ctypes.c_wchar_p()
|
return shell.SHGetKnownFolderPath(guid, 0, 0)
|
||||||
if SHGetKnownFolderPath(ctypes.create_string_buffer(guid.bytes_le), 0, 0, ctypes.byref(buf)):
|
|
||||||
return None
|
|
||||||
retval = buf.value # copy data
|
|
||||||
CoTaskMemFree(buf) # and free original
|
|
||||||
return retval
|
|
||||||
|
|
||||||
|
|
||||||
class WinConfig(AbstractConfig):
|
class WinConfig(AbstractConfig):
|
||||||
@ -49,7 +31,8 @@ class WinConfig(AbstractConfig):
|
|||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.app_dir_path = pathlib.Path(known_folder_path(FOLDERID_LocalAppData)) / appname # type: ignore
|
if local_appdata := known_folder_path(shell.FOLDERID_LocalAppData):
|
||||||
|
self.app_dir_path = pathlib.Path(local_appdata) / appname
|
||||||
self.app_dir_path.mkdir(exist_ok=True)
|
self.app_dir_path.mkdir(exist_ok=True)
|
||||||
|
|
||||||
self.plugin_dir_path = self.app_dir_path / 'plugins'
|
self.plugin_dir_path = self.app_dir_path / 'plugins'
|
||||||
@ -65,7 +48,7 @@ class WinConfig(AbstractConfig):
|
|||||||
self.home_path = pathlib.Path.home()
|
self.home_path = pathlib.Path.home()
|
||||||
|
|
||||||
journal_dir_path = pathlib.Path(
|
journal_dir_path = pathlib.Path(
|
||||||
known_folder_path(FOLDERID_SavedGames)) / 'Frontier Developments' / 'Elite Dangerous' # type: ignore
|
known_folder_path(shell.FOLDERID_SavedGames)) / 'Frontier Developments' / 'Elite Dangerous' # type: ignore
|
||||||
self.default_journal_dir_path = journal_dir_path if journal_dir_path.is_dir() else None # type: ignore
|
self.default_journal_dir_path = journal_dir_path if journal_dir_path.is_dir() else None # type: ignore
|
||||||
|
|
||||||
REGISTRY_SUBKEY = r'Software\Marginal\EDMarketConnector' # noqa: N806
|
REGISTRY_SUBKEY = r'Software\Marginal\EDMarketConnector' # noqa: N806
|
||||||
@ -84,7 +67,7 @@ class WinConfig(AbstractConfig):
|
|||||||
|
|
||||||
self.identifier = applongname
|
self.identifier = applongname
|
||||||
if (outdir_str := self.get_str('outdir')) is None or not pathlib.Path(outdir_str).is_dir():
|
if (outdir_str := self.get_str('outdir')) is None or not pathlib.Path(outdir_str).is_dir():
|
||||||
docs = known_folder_path(FOLDERID_Documents)
|
docs = known_folder_path(shell.FOLDERID_Documents)
|
||||||
self.set("outdir", docs if docs is not None else self.home)
|
self.set("outdir", docs if docs is not None else self.home)
|
||||||
|
|
||||||
def __get_regentry(self, key: str) -> None | list | str | int:
|
def __get_regentry(self, key: str) -> None | list | str | int:
|
||||||
|
@ -8,7 +8,10 @@ import sys
|
|||||||
import threading
|
import threading
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
import winsound
|
import winsound
|
||||||
from ctypes.wintypes import DWORD, HWND, LONG, LPWSTR, MSG, ULONG, WORD
|
from ctypes.wintypes import DWORD, LONG, MSG, ULONG, WORD
|
||||||
|
import pywintypes
|
||||||
|
import win32api
|
||||||
|
import win32gui
|
||||||
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
|
||||||
@ -17,26 +20,20 @@ assert sys.platform == 'win32'
|
|||||||
|
|
||||||
logger = get_main_logger()
|
logger = get_main_logger()
|
||||||
|
|
||||||
RegisterHotKey = ctypes.windll.user32.RegisterHotKey
|
UnregisterHotKey = ctypes.windll.user32.UnregisterHotKey # TODO: Coming Soon
|
||||||
UnregisterHotKey = ctypes.windll.user32.UnregisterHotKey
|
|
||||||
MOD_ALT = 0x0001
|
MOD_ALT = 0x0001
|
||||||
MOD_CONTROL = 0x0002
|
MOD_CONTROL = 0x0002
|
||||||
MOD_SHIFT = 0x0004
|
MOD_SHIFT = 0x0004
|
||||||
MOD_WIN = 0x0008
|
MOD_WIN = 0x0008
|
||||||
MOD_NOREPEAT = 0x4000
|
MOD_NOREPEAT = 0x4000
|
||||||
|
|
||||||
GetMessage = ctypes.windll.user32.GetMessageW
|
|
||||||
TranslateMessage = ctypes.windll.user32.TranslateMessage
|
|
||||||
DispatchMessage = ctypes.windll.user32.DispatchMessageW
|
|
||||||
PostThreadMessage = ctypes.windll.user32.PostThreadMessageW
|
|
||||||
WM_QUIT = 0x0012
|
WM_QUIT = 0x0012
|
||||||
WM_HOTKEY = 0x0312
|
WM_HOTKEY = 0x0312
|
||||||
WM_APP = 0x8000
|
WM_APP = 0x8000
|
||||||
WM_SND_GOOD = WM_APP + 1
|
WM_SND_GOOD = WM_APP + 1
|
||||||
WM_SND_BAD = WM_APP + 2
|
WM_SND_BAD = WM_APP + 2
|
||||||
|
|
||||||
GetKeyState = ctypes.windll.user32.GetKeyState
|
|
||||||
MapVirtualKey = ctypes.windll.user32.MapVirtualKeyW
|
|
||||||
VK_BACK = 0x08
|
VK_BACK = 0x08
|
||||||
VK_CLEAR = 0x0c
|
VK_CLEAR = 0x0c
|
||||||
VK_RETURN = 0x0d
|
VK_RETURN = 0x0d
|
||||||
@ -60,10 +57,13 @@ VK_SCROLL = 0x91
|
|||||||
VK_PROCESSKEY = 0xe5
|
VK_PROCESSKEY = 0xe5
|
||||||
VK_OEM_CLEAR = 0xfe
|
VK_OEM_CLEAR = 0xfe
|
||||||
|
|
||||||
GetForegroundWindow = ctypes.windll.user32.GetForegroundWindow
|
# VirtualKey mapping values
|
||||||
GetWindowText = ctypes.windll.user32.GetWindowTextW
|
# <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapvirtualkeyexa>
|
||||||
GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int]
|
MAPVK_VK_TO_VSC = 0
|
||||||
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
|
MAPVK_VSC_TO_VK = 1
|
||||||
|
MAPVK_VK_TO_CHAR = 2
|
||||||
|
MAPVK_VSC_TO_VK_EX = 3
|
||||||
|
MAPVK_VK_TO_VSC_EX = 4
|
||||||
|
|
||||||
|
|
||||||
def window_title(h) -> str:
|
def window_title(h) -> str:
|
||||||
@ -74,9 +74,9 @@ def window_title(h) -> str:
|
|||||||
:return: Window title.
|
:return: Window title.
|
||||||
"""
|
"""
|
||||||
if h:
|
if h:
|
||||||
title_length = GetWindowTextLength(h) + 1
|
title_length = win32gui.GetWindowTextLength(h) + 1
|
||||||
buf = ctypes.create_unicode_buffer(title_length)
|
buf = ctypes.create_unicode_buffer(title_length)
|
||||||
if GetWindowText(h, buf, title_length):
|
if win32gui.GetWindowText(h, buf, title_length):
|
||||||
return buf.value
|
return buf.value
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
@ -197,7 +197,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')
|
||||||
PostThreadMessage(thread.ident, WM_QUIT, 0, 0)
|
win32gui.PostThreadMessage(thread.ident, 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
|
||||||
|
|
||||||
@ -210,8 +210,10 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
|
|||||||
"""Handle hotkeys."""
|
"""Handle hotkeys."""
|
||||||
logger.debug('Begin...')
|
logger.debug('Begin...')
|
||||||
# Hotkey must be registered by the thread that handles it
|
# Hotkey must be registered by the thread that handles it
|
||||||
if not RegisterHotKey(None, 1, modifiers | MOD_NOREPEAT, keycode):
|
try:
|
||||||
logger.debug("We're not the right thread?")
|
win32gui.RegisterHotKey(None, 1, modifiers | MOD_NOREPEAT, keycode)
|
||||||
|
except pywintypes.error:
|
||||||
|
logger.exception("We're not the right thread?")
|
||||||
self.thread = None # type: ignore
|
self.thread = None # type: ignore
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -219,14 +221,14 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
|
|||||||
|
|
||||||
msg = MSG()
|
msg = MSG()
|
||||||
logger.debug('Entering GetMessage() loop...')
|
logger.debug('Entering GetMessage() loop...')
|
||||||
while 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 == WM_HOTKEY:
|
||||||
logger.debug('WM_HOTKEY')
|
logger.debug('WM_HOTKEY')
|
||||||
|
|
||||||
if (
|
if (
|
||||||
config.get_int('hotkey_always')
|
config.get_int('hotkey_always')
|
||||||
or window_title(GetForegroundWindow()).startswith('Elite - Dangerous')
|
or window_title(win32gui.GetForegroundWindow()).startswith('Elite - Dangerous')
|
||||||
):
|
):
|
||||||
if not config.shutting_down:
|
if not config.shutting_down:
|
||||||
logger.debug('Sending event <<Invoke>>')
|
logger.debug('Sending event <<Invoke>>')
|
||||||
@ -236,8 +238,10 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
|
|||||||
logger.debug('Passing key on')
|
logger.debug('Passing key on')
|
||||||
UnregisterHotKey(None, 1)
|
UnregisterHotKey(None, 1)
|
||||||
SendInput(1, fake, ctypes.sizeof(INPUT))
|
SendInput(1, fake, ctypes.sizeof(INPUT))
|
||||||
if not RegisterHotKey(None, 1, modifiers | MOD_NOREPEAT, keycode):
|
try:
|
||||||
logger.debug("We aren't registered for this ?")
|
win32gui.RegisterHotKey(None, 1, modifiers | MOD_NOREPEAT, keycode)
|
||||||
|
except pywintypes.error:
|
||||||
|
logger.exception("We aren't registered for this ?")
|
||||||
break
|
break
|
||||||
|
|
||||||
elif msg.message == WM_SND_GOOD:
|
elif msg.message == WM_SND_GOOD:
|
||||||
@ -250,8 +254,8 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
logger.debug('Something else')
|
logger.debug('Something else')
|
||||||
TranslateMessage(ctypes.byref(msg))
|
win32gui.TranslateMessage(ctypes.byref(msg))
|
||||||
DispatchMessage(ctypes.byref(msg))
|
win32gui.DispatchMessage(ctypes.byref(msg))
|
||||||
|
|
||||||
logger.debug('Exited GetMessage() loop.')
|
logger.debug('Exited GetMessage() loop.')
|
||||||
UnregisterHotKey(None, 1)
|
UnregisterHotKey(None, 1)
|
||||||
@ -266,7 +270,7 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
|
|||||||
"""Stop acquiring hotkey state."""
|
"""Stop acquiring hotkey state."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def fromevent(self, event) -> bool | tuple | None: # noqa: CCR001
|
def fromevent(self, event) -> bool | tuple | None:
|
||||||
"""
|
"""
|
||||||
Return configuration (keycode, modifiers) or None=clear or False=retain previous.
|
Return configuration (keycode, modifiers) or None=clear or False=retain previous.
|
||||||
|
|
||||||
@ -277,11 +281,11 @@ 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 = ((GetKeyState(VK_MENU) & 0x8000) and MOD_ALT) \
|
modifiers = ((win32api.GetKeyState(VK_MENU) & 0x8000) and MOD_ALT) \
|
||||||
| ((GetKeyState(VK_CONTROL) & 0x8000) and MOD_CONTROL) \
|
| ((win32api.GetKeyState(VK_CONTROL) & 0x8000) and MOD_CONTROL) \
|
||||||
| ((GetKeyState(VK_SHIFT) & 0x8000) and MOD_SHIFT) \
|
| ((win32api.GetKeyState(VK_SHIFT) & 0x8000) and MOD_SHIFT) \
|
||||||
| ((GetKeyState(VK_LWIN) & 0x8000) and MOD_WIN) \
|
| ((win32api.GetKeyState(VK_LWIN) & 0x8000) and MOD_WIN) \
|
||||||
| ((GetKeyState(VK_RWIN) & 0x8000) and MOD_WIN)
|
| ((win32api.GetKeyState(VK_RWIN) & 0x8000) and MOD_WIN)
|
||||||
keycode = event.keycode
|
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):
|
||||||
@ -295,7 +299,7 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if (
|
if (
|
||||||
keycode in (VK_RETURN, VK_SPACE, VK_OEM_MINUS) or ord('A') <= keycode <= ord('Z')
|
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
|
): # don't allow keys needed for typing in System Map
|
||||||
winsound.MessageBeep()
|
winsound.MessageBeep()
|
||||||
return None
|
return None
|
||||||
@ -305,12 +309,13 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
|
|||||||
return 0, modifiers
|
return 0, modifiers
|
||||||
|
|
||||||
# See if the keycode is usable and available
|
# See if the keycode is usable and available
|
||||||
if RegisterHotKey(None, 2, modifiers | MOD_NOREPEAT, keycode):
|
try:
|
||||||
|
win32gui.RegisterHotKey(None, 2, modifiers | MOD_NOREPEAT, keycode)
|
||||||
UnregisterHotKey(None, 2)
|
UnregisterHotKey(None, 2)
|
||||||
return keycode, modifiers
|
return keycode, modifiers
|
||||||
|
except pywintypes.error:
|
||||||
winsound.MessageBeep()
|
winsound.MessageBeep()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def display(self, keycode, modifiers) -> str:
|
def display(self, keycode, modifiers) -> str:
|
||||||
"""
|
"""
|
||||||
@ -346,7 +351,7 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
|
|||||||
text += WindowsHotkeyMgr.DISPLAY[keycode]
|
text += WindowsHotkeyMgr.DISPLAY[keycode]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
c = MapVirtualKey(keycode, 2) # printable ?
|
c = win32api.MapVirtualKey(keycode, MAPVK_VK_TO_CHAR)
|
||||||
if not c: # oops not printable
|
if not c: # oops not printable
|
||||||
text += '⁈'
|
text += '⁈'
|
||||||
|
|
||||||
@ -361,9 +366,9 @@ class WindowsHotkeyMgr(AbstractHotkeyMgr):
|
|||||||
def play_good(self) -> None:
|
def play_good(self) -> None:
|
||||||
"""Play the 'good' sound."""
|
"""Play the 'good' sound."""
|
||||||
if self.thread:
|
if self.thread:
|
||||||
PostThreadMessage(self.thread.ident, WM_SND_GOOD, 0, 0)
|
win32gui.PostThreadMessage(self.thread.ident, WM_SND_GOOD, 0, 0)
|
||||||
|
|
||||||
def play_bad(self) -> None:
|
def play_bad(self) -> None:
|
||||||
"""Play the 'bad' sound."""
|
"""Play the 'bad' sound."""
|
||||||
if self.thread:
|
if self.thread:
|
||||||
PostThreadMessage(self.thread.ident, WM_SND_BAD, 0, 0)
|
win32gui.PostThreadMessage(self.thread.ident, WM_SND_BAD, 0, 0)
|
||||||
|
18
monitor.py
18
monitor.py
@ -36,23 +36,15 @@ MAX_FCMATERIALS_DISCREPANCY = 5 # Timestamp difference in seconds
|
|||||||
|
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
import ctypes
|
import ctypes
|
||||||
from ctypes.wintypes import BOOL, HWND, LPARAM, LPWSTR
|
from ctypes.wintypes import BOOL, HWND, LPARAM
|
||||||
|
import win32gui
|
||||||
|
|
||||||
from watchdog.events import FileSystemEventHandler, FileSystemEvent
|
from watchdog.events import FileSystemEventHandler, FileSystemEvent
|
||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
from watchdog.observers.api import BaseObserver
|
from watchdog.observers.api import BaseObserver
|
||||||
|
|
||||||
EnumWindows = ctypes.windll.user32.EnumWindows
|
|
||||||
EnumWindowsProc = ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM)
|
EnumWindowsProc = ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM)
|
||||||
|
|
||||||
CloseHandle = ctypes.windll.kernel32.CloseHandle
|
CloseHandle = ctypes.windll.kernel32.CloseHandle
|
||||||
|
|
||||||
GetWindowText = ctypes.windll.user32.GetWindowTextW
|
|
||||||
GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int]
|
|
||||||
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
|
|
||||||
GetWindowTextLength.argtypes = [ctypes.wintypes.HWND]
|
|
||||||
GetWindowTextLength.restype = ctypes.c_int
|
|
||||||
|
|
||||||
GetProcessHandleFromHwnd = ctypes.windll.oleacc.GetProcessHandleFromHwnd
|
GetProcessHandleFromHwnd = ctypes.windll.oleacc.GetProcessHandleFromHwnd
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -2131,9 +2123,9 @@ class EDLogs(FileSystemEventHandler):
|
|||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
def WindowTitle(h): # noqa: N802
|
def WindowTitle(h): # noqa: N802
|
||||||
if h:
|
if h:
|
||||||
length = GetWindowTextLength(h) + 1
|
length = win32gui.GetWindowTextLength(h) + 1
|
||||||
buf = ctypes.create_unicode_buffer(length)
|
buf = ctypes.create_unicode_buffer(length)
|
||||||
if GetWindowText(h, buf, length):
|
if win32gui.GetWindowText(h, buf, length):
|
||||||
return buf.value
|
return buf.value
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -2147,7 +2139,7 @@ class EDLogs(FileSystemEventHandler):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return not EnumWindows(EnumWindowsProc(callback), 0)
|
return not win32gui.EnumWindows(EnumWindowsProc(callback), 0)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
10
prefs.py
10
prefs.py
@ -188,7 +188,8 @@ class AutoInc(contextlib.AbstractContextManager):
|
|||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
import ctypes
|
import ctypes
|
||||||
import winreg
|
import winreg
|
||||||
from ctypes.wintypes import HINSTANCE, HWND, LPCWSTR, LPWSTR, MAX_PATH, POINT, RECT, SIZE, UINT
|
from ctypes.wintypes import HINSTANCE, LPCWSTR, LPWSTR, MAX_PATH, POINT, RECT, SIZE, UINT
|
||||||
|
import win32gui
|
||||||
is_wine = False
|
is_wine = False
|
||||||
try:
|
try:
|
||||||
WINE_REGISTRY_KEY = r'HKEY_LOCAL_MACHINE\Software\Wine'
|
WINE_REGISTRY_KEY = r'HKEY_LOCAL_MACHINE\Software\Wine'
|
||||||
@ -219,11 +220,6 @@ if sys.platform == 'win32':
|
|||||||
ctypes.POINTER(RECT)
|
ctypes.POINTER(RECT)
|
||||||
]
|
]
|
||||||
|
|
||||||
GetParent = ctypes.windll.user32.GetParent
|
|
||||||
GetParent.argtypes = [HWND]
|
|
||||||
GetWindowRect = ctypes.windll.user32.GetWindowRect
|
|
||||||
GetWindowRect.argtypes = [HWND, ctypes.POINTER(RECT)]
|
|
||||||
|
|
||||||
SHGetLocalizedName = ctypes.windll.shell32.SHGetLocalizedName
|
SHGetLocalizedName = ctypes.windll.shell32.SHGetLocalizedName
|
||||||
SHGetLocalizedName.argtypes = [LPCWSTR, LPWSTR, UINT, ctypes.POINTER(ctypes.c_int)]
|
SHGetLocalizedName.argtypes = [LPCWSTR, LPWSTR, UINT, ctypes.POINTER(ctypes.c_int)]
|
||||||
|
|
||||||
@ -314,7 +310,7 @@ class PreferencesDialog(tk.Toplevel):
|
|||||||
# Ensure fully on-screen
|
# Ensure fully on-screen
|
||||||
if sys.platform == 'win32' and CalculatePopupWindowPosition:
|
if sys.platform == 'win32' and CalculatePopupWindowPosition:
|
||||||
position = RECT()
|
position = RECT()
|
||||||
GetWindowRect(GetParent(self.winfo_id()), position)
|
win32gui.GetWindowRect(win32gui.GetParent(self.winfo_id()), position)
|
||||||
if CalculatePopupWindowPosition(
|
if CalculatePopupWindowPosition(
|
||||||
POINT(parent.winfo_rootx(), parent.winfo_rooty()),
|
POINT(parent.winfo_rootx(), parent.winfo_rooty()),
|
||||||
SIZE(position.right - position.left, position.bottom - position.top), # type: ignore
|
SIZE(position.right - position.left, position.bottom - position.top), # type: ignore
|
||||||
|
@ -42,8 +42,6 @@ pytest==8.2.0
|
|||||||
pytest-cov==5.0.0 # Pytest code coverage support
|
pytest-cov==5.0.0 # Pytest code coverage support
|
||||||
coverage[toml]==7.5.0 # pytest-cov dep. This is here to ensure that it includes TOML support for pyproject.toml configs
|
coverage[toml]==7.5.0 # pytest-cov dep. This is here to ensure that it includes TOML support for pyproject.toml configs
|
||||||
coverage-conditional-plugin==0.9.0
|
coverage-conditional-plugin==0.9.0
|
||||||
# For manipulating folder permissions and the like.
|
|
||||||
pywin32==306; sys_platform == 'win32'
|
|
||||||
|
|
||||||
|
|
||||||
# All of the normal requirements
|
# All of the normal requirements
|
||||||
|
@ -3,3 +3,5 @@ pillow==10.3.0
|
|||||||
watchdog==4.0.0
|
watchdog==4.0.0
|
||||||
simplesystray==0.1.0; sys_platform == 'win32'
|
simplesystray==0.1.0; sys_platform == 'win32'
|
||||||
semantic-version==2.10.0
|
semantic-version==2.10.0
|
||||||
|
# For manipulating folder permissions and the like.
|
||||||
|
pywin32==306; sys_platform == 'win32'
|
9
stats.py
9
stats.py
@ -25,17 +25,14 @@ logger = EDMCLogging.get_main_logger()
|
|||||||
|
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
import ctypes
|
import ctypes
|
||||||
from ctypes.wintypes import HWND, POINT, RECT, SIZE, UINT
|
from ctypes.wintypes import POINT, RECT, SIZE, UINT
|
||||||
|
import win32gui
|
||||||
|
|
||||||
try:
|
try:
|
||||||
CalculatePopupWindowPosition = ctypes.windll.user32.CalculatePopupWindowPosition
|
CalculatePopupWindowPosition = ctypes.windll.user32.CalculatePopupWindowPosition
|
||||||
CalculatePopupWindowPosition.argtypes = [
|
CalculatePopupWindowPosition.argtypes = [
|
||||||
ctypes.POINTER(POINT), ctypes.POINTER(SIZE), UINT, ctypes.POINTER(RECT), ctypes.POINTER(RECT)
|
ctypes.POINTER(POINT), ctypes.POINTER(SIZE), UINT, ctypes.POINTER(RECT), ctypes.POINTER(RECT)
|
||||||
]
|
]
|
||||||
GetParent = ctypes.windll.user32.GetParent
|
|
||||||
GetParent.argtypes = [HWND]
|
|
||||||
GetWindowRect = ctypes.windll.user32.GetWindowRect
|
|
||||||
GetWindowRect.argtypes = [HWND, ctypes.POINTER(RECT)]
|
|
||||||
|
|
||||||
except Exception: # Not supported under Wine 4.0
|
except Exception: # Not supported under Wine 4.0
|
||||||
CalculatePopupWindowPosition = None # type: ignore
|
CalculatePopupWindowPosition = None # type: ignore
|
||||||
@ -423,7 +420,7 @@ class StatsResults(tk.Toplevel):
|
|||||||
# Ensure fully on-screen
|
# Ensure fully on-screen
|
||||||
if sys.platform == 'win32' and CalculatePopupWindowPosition:
|
if sys.platform == 'win32' and CalculatePopupWindowPosition:
|
||||||
position = RECT()
|
position = RECT()
|
||||||
GetWindowRect(GetParent(self.winfo_id()), position)
|
win32gui.GetWindowRect(win32gui.GetParent(self.winfo_id()), position)
|
||||||
if CalculatePopupWindowPosition(
|
if CalculatePopupWindowPosition(
|
||||||
POINT(parent.winfo_rootx(), parent.winfo_rooty()),
|
POINT(parent.winfo_rootx(), parent.winfo_rooty()),
|
||||||
# - is evidently supported on the C side
|
# - is evidently supported on the C side
|
||||||
|
11
theme.py
11
theme.py
@ -34,6 +34,7 @@ if sys.platform == "linux":
|
|||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
import ctypes
|
import ctypes
|
||||||
from ctypes.wintypes import DWORD, LPCVOID, LPCWSTR
|
from ctypes.wintypes import DWORD, LPCVOID, LPCWSTR
|
||||||
|
import win32gui
|
||||||
AddFontResourceEx = ctypes.windll.gdi32.AddFontResourceExW
|
AddFontResourceEx = ctypes.windll.gdi32.AddFontResourceExW
|
||||||
AddFontResourceEx.restypes = [LPCWSTR, DWORD, LPCVOID] # type: ignore
|
AddFontResourceEx.restypes = [LPCWSTR, DWORD, LPCVOID] # type: ignore
|
||||||
FR_PRIVATE = 0x10
|
FR_PRIVATE = 0x10
|
||||||
@ -427,8 +428,6 @@ class _Theme:
|
|||||||
GWL_EXSTYLE = -20 # noqa: N806 # ctypes
|
GWL_EXSTYLE = -20 # noqa: N806 # ctypes
|
||||||
WS_EX_APPWINDOW = 0x00040000 # noqa: N806 # ctypes
|
WS_EX_APPWINDOW = 0x00040000 # noqa: N806 # ctypes
|
||||||
WS_EX_LAYERED = 0x00080000 # noqa: N806 # ctypes
|
WS_EX_LAYERED = 0x00080000 # noqa: N806 # ctypes
|
||||||
GetWindowLongW = ctypes.windll.user32.GetWindowLongW # noqa: N806 # ctypes
|
|
||||||
SetWindowLongW = ctypes.windll.user32.SetWindowLongW # noqa: N806 # ctypes
|
|
||||||
|
|
||||||
# FIXME: Lose the "treat this like a boolean" bullshit
|
# FIXME: Lose the "treat this like a boolean" bullshit
|
||||||
if theme == self.THEME_DEFAULT:
|
if theme == self.THEME_DEFAULT:
|
||||||
@ -445,14 +444,14 @@ class _Theme:
|
|||||||
|
|
||||||
root.withdraw()
|
root.withdraw()
|
||||||
root.update_idletasks() # Size and windows styles get recalculated here
|
root.update_idletasks() # Size and windows styles get recalculated here
|
||||||
hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
|
hwnd = win32gui.GetParent(root.winfo_id())
|
||||||
SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) & ~WS_MAXIMIZEBOX) # disable maximize
|
win32gui.SetWindowLong(hwnd, GWL_STYLE, win32gui.GetWindowLong(hwnd, GWL_STYLE) & ~WS_MAXIMIZEBOX) # disable maximize
|
||||||
|
|
||||||
if theme == self.THEME_TRANSPARENT:
|
if theme == self.THEME_TRANSPARENT:
|
||||||
SetWindowLongW(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_LAYERED) # Add to taskbar
|
win32gui.SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_LAYERED) # Add to taskbar
|
||||||
|
|
||||||
else:
|
else:
|
||||||
SetWindowLongW(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW) # Add to taskbar
|
win32gui.SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW) # Add to taskbar
|
||||||
|
|
||||||
root.deiconify()
|
root.deiconify()
|
||||||
root.wait_visibility() # need main window to be displayed before returning
|
root.wait_visibility() # need main window to be displayed before returning
|
||||||
|
Loading…
x
Reference in New Issue
Block a user