Last active
April 11, 2024 03:57
-
-
Save aont/69428c4aa092916d127769e3872efbd5 to your computer and use it in GitHub Desktop.
change active window
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import os | |
import sys | |
import ctypes | |
import ctypes.wintypes | |
import time | |
import ctypes | |
import ctypes.wintypes | |
import win32api | |
import win32gui | |
import win32process | |
import pywintypes | |
import win32con | |
import winerror | |
# import clr | |
# clr.AddReference("Microsoft.VisualBasic") | |
# import Microsoft.VisualBasic | |
# import System | |
user32 = ctypes.WinDLL("User32") | |
SystemParametersInfo = user32.SystemParametersInfoA | |
SystemParametersInfo.restype = ctypes.wintypes.BOOL | |
SystemParametersInfo.argtypes = (ctypes.wintypes.UINT, ctypes.wintypes.UINT, ctypes.c_void_p, ctypes.wintypes.UINT) | |
SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000 | |
SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001 | |
SPIF_UPDATEINIFILE = 0x01 | |
SPIF_SENDCHANGE = 0x02 | |
ShowWindowAsync = user32.ShowWindowAsync | |
ShowWindowAsync.restype = ctypes.wintypes.BOOL | |
ShowWindowAsync.argtypes = (ctypes.wintypes.HWND, ctypes.c_int) | |
# GetShellWindow = user32.GetShellWindow | |
# GetShellWindow.restype = ctypes.wintypes.HWND | |
# GetShellWindow.argtypes = () | |
# GetLastActivePopup = user32.GetLastActivePopup | |
# GetLastActivePopup.restype = ctypes.wintypes.HWND | |
# GetLastActivePopup.argtypes = (ctypes.wintypes.HWND, ) | |
HRESULT = ctypes.wintypes.LONG | |
DwmGetWindowAttribute = ctypes.windll.dwmapi.DwmGetWindowAttribute | |
DwmGetWindowAttribute.restype = HRESULT | |
DwmGetWindowAttribute.argtypes = (ctypes.wintypes.HWND, ctypes.wintypes.DWORD, ctypes.c_void_p, ctypes.wintypes.DWORD) | |
def IsAltTabWindow(hwnd): | |
if not win32gui.IsWindowVisible(hwnd): return False | |
exstyle = win32api.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) | |
if exstyle & win32con.WS_EX_TOOLWINDOW: return False | |
DWMWA_CLOAKED = 14 | |
CloakedVal = ctypes.wintypes.UINT() | |
DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, ctypes.byref(CloakedVal), ctypes.sizeof(CloakedVal)) | |
return CloakedVal.value==0 | |
# def find_window_callback(hwnd: int, hwnd_set: set): | |
# hwnd_set.add(hwnd) | |
class ChangeActiveWindow: | |
def __init__(self): | |
self.hwnd_set_to_check = set() | |
self.hwnd_set_checked = set() | |
def __iter__(self): | |
return self | |
def find_new_window_callback(self, hwnd, _): | |
if hwnd not in self.hwnd_set_checked: | |
self.hwnd_set_to_check.add(hwnd) | |
def __next__(self): | |
if len(self.hwnd_set_to_check)==0: | |
self.hwnd_set_checked.clear() | |
win32gui.EnumWindows(self.find_new_window_callback, self.hwnd_set_to_check) | |
if len(self.hwnd_set_to_check)>0: | |
hwnd = self.hwnd_set_to_check.pop() | |
thread_id, process_id = win32process.GetWindowThreadProcessId(hwnd) | |
process_handle = None | |
try: | |
process_handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, 0, process_id) | |
except pywintypes.error as e: | |
if e.winerror not in (winerror.ERROR_ACCESS_DENIED, ): | |
sys.stderr.write("[warn] pid %s: %s\n" % (process_id, e.strerror)) | |
return False | |
# return True | |
process_name = None | |
if process_handle: | |
module_handle_tuple = win32process.EnumProcessModules(process_handle) | |
# module_handle_tuple = win32process.EnumProcessModulesEx(process_handle, win32process.LIST_MODULES_ALL) | |
if len(module_handle_tuple)>0: | |
module_handle = module_handle_tuple[0] | |
process_path = win32process.GetModuleFileNameEx(process_handle, module_handle) | |
process_name = os.path.basename(process_path) | |
if not process_name: | |
return False | |
if not IsAltTabWindow(hwnd): return False | |
# if not win32gui.IsWindow(hwnd): continue | |
# if not win32gui.IsWindowVisible(hwnd): continue | |
# if win32gui.IsIconic(hwnd): continue | |
window_title = win32gui.GetWindowText(hwnd) | |
# if window_title=='': continue | |
# exstyle = win32api.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) | |
# flags, showCmd, _, _, _ = win32gui.GetWindowPlacement(hwnd) | |
flags, showCmd, minpos, maxpos, normalpos = win32gui.GetWindowPlacement(hwnd) | |
exstyle = win32api.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) | |
# sys.stderr.write(f"add: {process_id=}\n") | |
# window_title = win32gui.GetWindowText(hwnd) | |
# sys.stderr.write(f"{process_name=} {hwnd=} {window_title=} {exstyle=:x} {flags=} {showCmd=} {normalpos=}\n") | |
sys.stderr.write(f"debug: {process_name=} {hwnd=} {window_title=}\n") | |
if True: | |
# try: Microsoft.VisualBasic.Interaction.AppActivate(process_id) | |
# except System.ArgumentException: pass | |
# ShowWindowAsync(hwnd, win32con.SW_RESTORE) | |
current_tid = win32api.GetCurrentThreadId() | |
hwnd_foreground = win32gui.GetForegroundWindow() | |
foreground_tid, foreground_pid = win32process.GetWindowThreadProcessId(hwnd_foreground) | |
if current_tid != foreground_tid: | |
timeout = ctypes.wintypes.UINT(200000) | |
# SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, ctypes.byref(timeout), 0) | |
# SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, 0) | |
try: win32process.AttachThreadInput(current_tid, foreground_tid, True) | |
except pywintypes.error: pass | |
try: win32gui.SetForegroundWindow(hwnd) | |
except pywintypes.error: pass | |
try: win32gui.SetWindowPos(hwnd, win32con.HWND_TOP, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE | win32con.SWP_SHOWWINDOW | win32con.SWP_ASYNCWINDOWPOS) | |
except pywintypes.error: pass | |
win32gui.BringWindowToTop(hwnd) | |
ShowWindowAsync(hwnd, win32con.SW_SHOW) | |
try: win32gui.SetFocus(hwnd) | |
except pywintypes.error: pass | |
if current_tid != foreground_tid: | |
# SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, timeout.value, 0) | |
try: win32process.AttachThreadInput(current_tid, foreground_tid, False) | |
except pywintypes.error: pass | |
return True | |
def main(): | |
inactive_threshold = 60 | |
inactive_count = 0 | |
last_input_tick_prev = win32api.GetLastInputInfo() | |
caw = ChangeActiveWindow() | |
# do_mouse_move = False | |
while True: | |
inactive_count_prev = inactive_count | |
last_input_tick = win32api.GetLastInputInfo() | |
cur_tick = win32api.GetTickCount() | |
inactive_dur = (cur_tick - last_input_tick) / 1000 | |
if last_input_tick <= last_input_tick_prev: | |
inactive_count += 1 | |
else: | |
last_input_tick_prev = last_input_tick | |
inactive_count = 0 | |
# sys.stderr.write(f"{cur_tick=}\n") | |
# sys.stderr.write(f"{last_input_tick_prev=}\n") | |
# sys.stderr.write(f"{last_input_tick=}\n") | |
# sys.stderr.write(f"{inactive_count=}\n") | |
# if inactive_count_prev > inactive_threshold and inactive_count < inactive_threshold: | |
# do_mouse_move = False | |
# if inactive_count_prev < inactive_threshold and inactive_threshold <= inactive_count: | |
# sys.stderr.write(f"info: start mouse move\n") | |
# do_mouse_move = True | |
if inactive_threshold <= inactive_count: | |
do_mouse_move = True | |
else: | |
do_mouse_move = False | |
# sys.stderr.write(f"{do_mouse_move=}\n") | |
if do_mouse_move: | |
while True: | |
result = next(caw) | |
if result: | |
last_input_tick_prev = win32api.GetLastInputInfo() | |
time.sleep(1) | |
break | |
else: | |
time.sleep(1) | |
if __name__ == '__main__': | |
sys.exit(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment