import logging import math from enum import Enum from threading import Thread import d3dshot import pywintypes import win32api import win32gui from ctypes import windll from fishy.helper.helper import print_exc class Status(Enum): CRASHED = -1 STOPPED = 0 RUNNING = 1 class WindowServer: """ Records the game window, and allows to create instance to process it """ Screen = None windowOffset = None hwnd = None status = Status.STOPPED d3: d3dshot.D3DShot = d3dshot.create(capture_output="numpy") monitor_top_left = None def init(): """ Executed once before the main loop, Finds the game window, and calculates the offset to remove the title bar """ # noinspection PyUnresolvedReferences try: WindowServer.hwnd = win32gui.FindWindow(None, "Elder Scrolls Online") monitor_id = windll.user32.MonitorFromWindow(WindowServer.hwnd, 2) WindowServer.monitor_top_left = win32api.GetMonitorInfo(monitor_id)["Monitor"][:2] rect = win32gui.GetWindowRect(WindowServer.hwnd) client_rect = win32gui.GetClientRect(WindowServer.hwnd) WindowServer.windowOffset = math.floor(((rect[2] - rect[0]) - client_rect[2]) / 2) WindowServer.status = Status.RUNNING WindowServer.d3.display = next((m for m in WindowServer.d3.displays if m.hmonitor == monitor_id), None) except pywintypes.error: logging.error("Game window not found") WindowServer.status = Status.CRASHED def loop(): """ Executed in the start of the main loop finds the game window location and captures it """ temp_screen = WindowServer.d3.screenshot() rect = win32gui.GetWindowRect(WindowServer.hwnd) client_rect = win32gui.GetClientRect(WindowServer.hwnd) fullscreen = WindowServer.d3.display.resolution[1] == (rect[3] - rect[1]) title_offset = ((rect[3] - rect[1]) - client_rect[3]) - WindowServer.windowOffset if not fullscreen else 0 crop = ( rect[0] + WindowServer.windowOffset - WindowServer.monitor_top_left[0], rect[1] + title_offset - WindowServer.monitor_top_left[1], rect[2] - WindowServer.windowOffset - WindowServer.monitor_top_left[0], rect[3] - WindowServer.windowOffset - WindowServer.monitor_top_left[1] ) WindowServer.Screen = temp_screen[crop[1]:crop[3], crop[0]:crop[2]] if not fullscreen else temp_screen if WindowServer.Screen.size == 0: logging.error("Don't minimize or drag game window outside the screen") WindowServer.status = Status.CRASHED # noinspection PyBroadException def run(): # todo use config logging.debug("window server started") while WindowServer.status == Status.RUNNING: try: loop() except Exception: print_exc() WindowServer.status = Status.CRASHED if WindowServer.status == Status.CRASHED: logging.debug("window server crashed") elif WindowServer.status == Status.STOPPED: logging.debug("window server stopped") def start(): if WindowServer.status == Status.RUNNING: return init() if WindowServer.status == Status.RUNNING: Thread(target=run).start() def screen_ready(): return WindowServer.Screen is not None or WindowServer.status == Status.CRASHED def stop(): WindowServer.status = Status.STOPPED