From 528400503c7501b2c45b2a73fbafef02e7b1bd39 Mon Sep 17 00:00:00 2001 From: Vladimir Makaev Date: Fri, 18 Feb 2022 17:27:31 +0000 Subject: [PATCH] Enhancement: Ability to customize window properties to look for when win32 api searching for d2r window (#563) --- config/params.ini | 6 +++++- src/config.py | 5 +++++ src/screen.py | 10 +++++++--- src/utils/misc.py | 43 +++++++++++++++++++++++++++++++++++++------ 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/config/params.ini b/config/params.ini index 67f9803..9215f8f 100644 --- a/config/params.ini +++ b/config/params.ini @@ -250,4 +250,8 @@ settings_backup_key=f8 auto_settings_key=f9 graphic_debugger_key=f10 resume_key=f11 -exit_key=f12 \ No newline at end of file +exit_key=f12 +hwnd_window_title= +hwnd_window_process=D2R\.exe +;If you want to control Hyper-V window from host use 0,51 here +window_client_area_offset=0,0 diff --git a/src/config.py b/src/config.py index fb7aa14..4da132d 100644 --- a/src/config.py +++ b/src/config.py @@ -8,6 +8,8 @@ from logger import Logger config_lock = threading.Lock() +def _default_iff(value, iff, default = None): + return default if value == iff else value @dataclass class ItemProps: @@ -312,6 +314,9 @@ class Config: "restore_settings_from_backup_key": Config._select_val("advanced_options", "restore_settings_from_backup_key"), "settings_backup_key": Config._select_val("advanced_options", "settings_backup_key"), "graphic_debugger_key": Config._select_val("advanced_options", "graphic_debugger_key"), + "hwnd_window_title": _default_iff(Config._select_val("advanced_options", "hwnd_window_title"), ''), + "hwnd_window_process": _default_iff(Config._select_val("advanced_options", "hwnd_window_process"), ''), + "window_client_area_offset": tuple(map(int, Config._select_val("advanced_options", "window_client_area_offset").split(","))) } Config.items = {} diff --git a/src/screen.py b/src/screen.py index ed1cea7..c90325f 100644 --- a/src/screen.py +++ b/src/screen.py @@ -5,7 +5,7 @@ import time from logger import Logger from typing import Tuple from config import Config -from utils.misc import find_d2r_window +from utils.misc import WindowSpec, find_d2r_window import os @@ -22,8 +22,12 @@ class Screen: # Find d2r screen offsets and monitor idx self.found_offsets = False position = None - Logger.debug("Using WinAPI to search for window under D2R.exe process") - position = find_d2r_window() + find_window = WindowSpec( + title_regex=Config.advanced_options["hwnd_window_title"], + process_name_regex=Config.advanced_options["hwnd_window_process"], + ) + Logger.debug(f"Using WinAPI to search for window: {find_window}") + position = find_d2r_window(find_window, offset=Config.advanced_options["window_client_area_offset"]) if position is not None: self._set_window_position(*position) else: diff --git a/src/utils/misc.py b/src/utils/misc.py index f462e64..b19a263 100644 --- a/src/utils/misc.py +++ b/src/utils/misc.py @@ -1,12 +1,17 @@ +from dataclasses import dataclass +from decimal import InvalidOperation +from re import RegexFlag import time import random import ctypes import numpy as np from copy import deepcopy +from pyparsing import Regex + from logger import Logger import cv2 -from typing import List, Tuple +from typing import List, Tuple, Union import os from math import cos, sin, dist import subprocess @@ -20,15 +25,34 @@ import psutil def close_down_d2(): subprocess.call(["taskkill","/F","/IM","D2R.exe"], stderr=subprocess.DEVNULL) -def find_d2r_window() -> tuple[int, int]: +@dataclass +class WindowSpec: + title_regex: 'Union[str, None]' = None + process_name_regex: 'Union[str, None]' = None + + def match(self, hwnd) -> bool: + result = True + if self.title_regex is not None: + result = result and Regex(self.title_regex).matches(GetWindowText(hwnd)) + if self.process_name_regex is not None: + _, process_id = GetWindowThreadProcessId(hwnd) + result = result and Regex(self.process_name_regex).matches(psutil.Process(process_id).name()) + if self.title_regex is None and self.process_name_regex is None: + result = False + return result + + +def find_d2r_window(spec: WindowSpec, offset = (0, 0)) -> tuple[int, int]: + offset_x, offset_y = offset + if os.name == 'nt': window_list = [] - EnumWindows(lambda w, l: l.append((w, *GetWindowThreadProcessId(w))), window_list) - for (hwnd, _, process_id) in window_list: - if psutil.Process(process_id).name() == "D2R.exe": + EnumWindows(lambda w, l: l.append(w), window_list) + for hwnd in window_list: + if spec.match(hwnd): left, top, right, bottom = GetClientRect(hwnd) (left, top), (right, bottom) = ClientToScreen(hwnd, (left, top)), ClientToScreen(hwnd, (right, bottom)) - return (left, top) + return (left + offset_x, top + offset_y) return None def set_d2r_always_on_top(): @@ -187,3 +211,10 @@ def rotate_vec(vec: np.ndarray, deg: float) -> np.ndarray: def unit_vector(vec: np.ndarray) -> np.ndarray: return vec / dist(vec, (0, 0)) + + +if __name__ == "__main__": + spec1 = WindowSpec(title_regex="D2R1 on .+") + spec2 = WindowSpec(process_name_regex="D2R.exe") + print(find_d2r_window(spec1)) + print(find_d2r_window(spec2)) \ No newline at end of file