feat: sensor-triggered window switcher

This commit is contained in:
Claude
2026-04-07 23:17:08 +00:00
commit df7a1f0a47
2 changed files with 114 additions and 0 deletions

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
pyserial
pywin32

112
window_switcher.py Normal file
View File

@@ -0,0 +1,112 @@
import serial
import serial.tools.list_ports
import win32gui
import win32con
import win32com.client
import time
import sys
# --- Config ---
COM_PORT = "COM3" # change to your Arduino's COM port
BAUD_RATE = 9600
TRIGGER_WORD = "DETECTED"
COOLDOWN_SEC = 1.5 # minimum seconds between switches
IGNORE_TITLES = {"", "Program Manager", "Windows Input Experience"}
def list_ports():
ports = serial.tools.list_ports.comports()
if not ports:
print("No serial ports found.")
else:
print("Available ports:")
for p in ports:
print(f" {p.device}{p.description}")
def get_windows():
"""Return list of (hwnd, title) for all visible, non-minimized top-level windows."""
results = []
def callback(hwnd, _):
if not win32gui.IsWindowVisible(hwnd):
return
title = win32gui.GetWindowText(hwnd)
if title and title not in IGNORE_TITLES:
results.append((hwnd, title))
win32gui.EnumWindows(callback, None)
return results
def force_focus(hwnd):
"""Bring window to foreground, bypassing Windows focus-steal protection."""
shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys('%') # simulate Alt — tricks Windows into allowing focus change
if win32gui.IsIconic(hwnd):
win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
win32gui.SetForegroundWindow(hwnd)
class WindowSwitcher:
def __init__(self):
self.index = 0
self.last_switch = 0
def switch(self):
now = time.time()
if now - self.last_switch < COOLDOWN_SEC:
return
windows = get_windows()
if not windows:
print("No windows found.")
return
self.index = (self.index + 1) % len(windows)
hwnd, title = windows[self.index]
try:
force_focus(hwnd)
print(f"[{time.strftime('%H:%M:%S')}] → {title}")
self.last_switch = now
except Exception as e:
print(f"Could not switch to '{title}': {e}")
def main():
if "--list-ports" in sys.argv:
list_ports()
return
if "--test" in sys.argv:
# Test window switching without Arduino
print("Test mode — switching window in 2 seconds...")
time.sleep(2)
WindowSwitcher().switch()
return
print(f"Connecting to {COM_PORT} at {BAUD_RATE} baud...")
print("Press Ctrl+C to stop.\n")
switcher = WindowSwitcher()
try:
ser = serial.Serial(COM_PORT, BAUD_RATE, timeout=1)
print(f"Connected. Waiting for '{TRIGGER_WORD}' signal...\n")
while True:
line = ser.readline().decode("utf-8", errors="ignore").strip()
if line == TRIGGER_WORD:
switcher.switch()
except serial.SerialException as e:
print(f"Serial error: {e}")
print("\nAvailable ports:")
list_ports()
except KeyboardInterrupt:
print("\nStopped.")
if __name__ == "__main__":
main()