Skip to content

Instantly share code, notes, and snippets.

@aont
Last active April 11, 2024 03:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save aont/69428c4aa092916d127769e3872efbd5 to your computer and use it in GitHub Desktop.
Save aont/69428c4aa092916d127769e3872efbd5 to your computer and use it in GitHub Desktop.
change active window
#!/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