fix(acr-wedge): use AppleScript on macOS for keystroke injection
pyautogui's Quartz-based keystroke path often fails on newer macOS because the Python CLI binary doesn't auto-surface in System Settings > Accessibility. User reported the daemon detected taps fine but keystrokes never landed in any window. Switch to AppleScript / System Events on macOS. Permission attaches to whatever terminal/app launched the Python process (Terminal.app, iTerm, etc.) — a familiar named app the user can grant Accessibility to in one click. Combined keystroke + Return in a single osascript call to keep latency ~100ms per tap. Fall back to pyautogui if osascript fails (handles edge cases) and on non-macOS platforms.
This commit is contained in:
@@ -112,23 +112,62 @@ def read_uid(connection):
|
|||||||
return ":".join(f"{b:02X}" for b in data)
|
return ":".join(f"{b:02X}" for b in data)
|
||||||
|
|
||||||
|
|
||||||
|
def _emit_macos(uid):
|
||||||
|
"""Type via AppleScript / System Events.
|
||||||
|
|
||||||
|
Permission attaches to the terminal/app that launched this Python
|
||||||
|
process — typically Terminal.app, iTerm, or another known GUI app
|
||||||
|
that the user can easily find in Accessibility settings. More
|
||||||
|
reliable than pyautogui's Quartz path which often fails to surface
|
||||||
|
Python in the Accessibility list on newer macOS.
|
||||||
|
"""
|
||||||
|
import subprocess
|
||||||
|
script = (
|
||||||
|
'tell application "System Events"\n'
|
||||||
|
f' keystroke "{uid}"\n'
|
||||||
|
' key code 36\n' # Return
|
||||||
|
'end tell'
|
||||||
|
)
|
||||||
|
result = subprocess.run(
|
||||||
|
["osascript", "-e", script],
|
||||||
|
capture_output=True, text=True, check=False, timeout=3,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise RuntimeError(f"osascript failed: {result.stderr.strip()}")
|
||||||
|
|
||||||
|
|
||||||
|
def _emit_other(uid):
|
||||||
|
"""Type via pyautogui (works on Linux, Windows, and as macOS fallback)."""
|
||||||
|
import pyautogui
|
||||||
|
pyautogui.typewrite(uid, interval=0.005)
|
||||||
|
pyautogui.press("enter")
|
||||||
|
|
||||||
|
|
||||||
def emit_uid(uid, type_keys=True, verbose=False):
|
def emit_uid(uid, type_keys=True, verbose=False):
|
||||||
"""Type UID + Enter into the focused window via pyautogui."""
|
"""Type UID + Enter into the focused window."""
|
||||||
if verbose:
|
if verbose:
|
||||||
print(f"[wedge] TAP {uid}")
|
print(f"[wedge] TAP {uid}")
|
||||||
if not type_keys:
|
if not type_keys:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
try:
|
||||||
|
_emit_macos(uid)
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
if verbose:
|
||||||
|
print(f"[wedge] osascript path failed ({e}); falling back to pyautogui")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import pyautogui
|
_emit_other(uid)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print(
|
print(
|
||||||
"[wedge] pyautogui is not installed; cannot type. "
|
"[wedge] pyautogui is not installed; cannot type. "
|
||||||
"Run: pip3 install pyautogui (or use --no-type for detect-only)",
|
"Run: pip3 install pyautogui (or use --no-type for detect-only)",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
return
|
except Exception as e:
|
||||||
pyautogui.typewrite(uid, interval=0.005)
|
print(f"[wedge] type error: {e}", file=sys.stderr)
|
||||||
pyautogui.press("enter")
|
|
||||||
|
|
||||||
|
|
||||||
def run_loop(args):
|
def run_loop(args):
|
||||||
|
|||||||
Reference in New Issue
Block a user