Skip to content

Commit

Permalink
Merge pull request #2263 from HullSeals/enhancement/1805/additional-p…
Browse files Browse the repository at this point in the history
…rototyping

[1805] Prototype More Functions
  • Loading branch information
Rixxan authored Jul 22, 2024
2 parents 2adb440 + b74524a commit 5c5682d
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 280 deletions.
52 changes: 19 additions & 33 deletions EDMarketConnector.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,41 +255,26 @@ def handle_edmc_callback_or_foregrounding() -> None: # noqa: CCR001
logger.trace_if('frontier-auth.windows', 'Begin...')

if sys.platform == 'win32':

# If *this* instance hasn't locked, then another already has and we
# now need to do the edmc:// checks for auth callback
if locked != JournalLockResult.LOCKED:
from ctypes import windll, c_int, create_unicode_buffer, WINFUNCTYPE
from ctypes.wintypes import BOOL, HWND, INT, LPARAM, LPCWSTR, LPWSTR

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
from ctypes import windll, create_unicode_buffer, WINFUNCTYPE
from ctypes.wintypes import BOOL, HWND, LPARAM
import win32gui
import win32api
import win32con

SW_RESTORE = 9 # noqa: N806
SetForegroundWindow = windll.user32.SetForegroundWindow # noqa: N806
ShowWindow = windll.user32.ShowWindow # noqa: N806
GetProcessHandleFromHwnd = windll.oleacc.GetProcessHandleFromHwnd # noqa: N806
ShowWindowAsync = windll.user32.ShowWindowAsync # noqa: N806

COINIT_MULTITHREADED = 0 # noqa: N806,F841
COINIT_APARTMENTTHREADED = 0x2 # noqa: N806
COINIT_DISABLE_OLE1DDE = 0x4 # 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:
if h:
text_length = GetWindowTextLength(h) + 1
buf = create_unicode_buffer(text_length)
if GetWindowText(h, buf, text_length):
return buf.value

return win32gui.GetWindowText(h)
return None

@WINFUNCTYPE(BOOL, HWND, LPARAM)
Expand All @@ -311,20 +296,20 @@ def enumwindowsproc(window_handle, l_param): # noqa: CCR001
# class name limited to 256 - https://msdn.microsoft.com/en-us/library/windows/desktop/ms633576
cls = create_unicode_buffer(257)
# 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 window_title(window_handle) == applongname:
if GetProcessHandleFromHwnd(window_handle):
# If GetProcessHandleFromHwnd succeeds then the app is already running as this user
if len(sys.argv) > 1 and sys.argv[1].startswith(protocolhandler_redirect):
CoInitializeEx(0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
# Wait for it to be responsive to avoid ShellExecute recursing
ShowWindow(window_handle, SW_RESTORE)
ShellExecute(0, None, sys.argv[1], None, None, SW_RESTORE)
win32gui.ShowWindow(window_handle, win32con.SW_RESTORE)
win32api.ShellExecute(0, None, sys.argv[1], None, None, win32con.SW_RESTORE)

else:
ShowWindowAsync(window_handle, SW_RESTORE)
SetForegroundWindow(window_handle)
ShowWindowAsync(window_handle, win32con.SW_RESTORE)
win32gui.SetForegroundWindow(window_handle)

return False # Indicate window found, so stop iterating

Expand All @@ -336,7 +321,7 @@ def enumwindowsproc(window_handle, l_param): # noqa: CCR001
# enumwindwsproc() on each. When an invocation returns False it
# stops iterating.
# 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():
"""Create the "already running" popup."""
Expand Down Expand Up @@ -703,13 +688,14 @@ def open_window(systray: 'SysTrayIcon', *args) -> None:
if match:
if sys.platform == 'win32':
# Check that the titlebar will be at least partly on screen
import ctypes
from ctypes.wintypes import POINT
import win32api
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
MONITOR_DEFAULTTONULL = 0 # noqa: N806
if ctypes.windll.user32.MonitorFromPoint(POINT(int(match.group(1)) + 16, int(match.group(2)) + 16),
MONITOR_DEFAULTTONULL):
if win32api.MonitorFromPoint(point, win32con.MONITOR_DEFAULTTONULL):
self.w.geometry(config.get_str('geometry'))
else:
self.w.geometry(config.get_str('geometry'))
Expand Down
30 changes: 7 additions & 23 deletions config/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,31 @@
"""
from __future__ import annotations

import ctypes
import functools
import pathlib
import sys
import uuid
import winreg
from ctypes.wintypes import DWORD, HANDLE
from typing import Literal
from config import AbstractConfig, applongname, appname, logger
from win32comext.shell import shell

assert sys.platform == 'win32'

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:
"""Look up a Windows GUID to actual folder path name."""
buf = ctypes.c_wchar_p()
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
return shell.SHGetKnownFolderPath(guid, 0, 0)


class WinConfig(AbstractConfig):
"""Implementation of AbstractConfig for Windows."""

def __init__(self) -> None:
super().__init__()

REGISTRY_SUBKEY = r'Software\Marginal\EDMarketConnector' # noqa: N806
create_key_defaults = functools.partial(
winreg.CreateKeyEx,
Expand All @@ -63,7 +46,8 @@ def __init__(self) -> None:
logger.exception('Could not create required registry keys')
raise

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.default_plugin_dir_path = self.app_dir_path / 'plugins'
Expand All @@ -83,12 +67,12 @@ def __init__(self) -> None:
self.home_path = pathlib.Path.home()

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.identifier = applongname
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)

def __get_regentry(self, key: str) -> None | list | str | int:
Expand Down
Loading

0 comments on commit 5c5682d

Please sign in to comment.