86 lines
2.7 KiB
Python
86 lines
2.7 KiB
Python
"""Local PC/Steam/Proton control backend (xdotool/scrot wrappers)."""
|
|
import os
|
|
import subprocess
|
|
import time
|
|
|
|
import cv2
|
|
|
|
from ba_auto import config
|
|
|
|
PROBE_SHOT_PATH = os.path.join(config.SCRATCHPAD_DIR, "probe.png")
|
|
|
|
|
|
def run_command(args, **kwargs):
|
|
kwargs.setdefault("env", config.ENV)
|
|
kwargs.setdefault("check", True)
|
|
return subprocess.run(args, **kwargs)
|
|
|
|
|
|
def focus_game():
|
|
result = run_command(
|
|
["xdotool", "search", "--name", config.WINDOW_NAME],
|
|
check=False, capture_output=True, text=True,
|
|
)
|
|
window_ids = result.stdout.split()
|
|
if not window_ids:
|
|
raise RuntimeError("Blue Archive window not found. Is the game running?")
|
|
run_command(["xdotool", "windowactivate", window_ids[0]])
|
|
# windowactivate alone isn't enough -- confirmed live (twice now, see
|
|
# Handoff.md's original finding and plan.md's Bounty phase for a second
|
|
# occurrence) that a stray anti-cheat XIGNCODE window can render
|
|
# visibly on top of the game and intercept clicks at fixed positions
|
|
# (notably the shared BACK_BUTTON coordinate) even while xdotool still
|
|
# reports BlueArchive as the "active" window -- windowactivate changes
|
|
# input focus, not stacking order, so it doesn't fix this by itself.
|
|
# windowraise does. Cheap and harmless when no overlay is present.
|
|
run_command(["xdotool", "windowraise", window_ids[0]])
|
|
wait(0.5)
|
|
|
|
|
|
def click(x, y):
|
|
# A combined "mousemove X Y click 1" invocation is unreliable here --
|
|
# empirically (see plan.md's click-flakiness writeups) the game's input
|
|
# handler sometimes misses clicks fired before it's processed the move.
|
|
# Splitting into separate commands with a short pause between fixes it.
|
|
run_command(["xdotool", "mousemove", str(x), str(y)])
|
|
wait(0.2)
|
|
run_command(["xdotool", "click", "1"])
|
|
wait(0.5)
|
|
|
|
|
|
def move_mouse(x, y):
|
|
run_command(["xdotool", "mousemove", str(x), str(y)])
|
|
|
|
|
|
def scroll(x, y, direction, clicks=1):
|
|
# xdotool button 4/5 = scroll wheel up/down. A drag (mousedown/move/mouseup)
|
|
# does not register as a list-scroll gesture in this Proton client; the
|
|
# wheel does.
|
|
button = "4" if direction == "up" else "5"
|
|
run_command(["xdotool", "mousemove", str(x), str(y)])
|
|
wait(0.2)
|
|
for _ in range(clicks):
|
|
run_command(["xdotool", "click", button])
|
|
wait(0.15)
|
|
wait(0.3)
|
|
|
|
|
|
def keypress(key):
|
|
run_command(["xdotool", "key", key])
|
|
wait(0.5)
|
|
|
|
|
|
def screenshot(path):
|
|
run_command(["scrot", "-a", "0,0,1920,1200", "-o", path])
|
|
|
|
|
|
def color_at(x, y):
|
|
screenshot(PROBE_SHOT_PATH)
|
|
image = cv2.imread(PROBE_SHOT_PATH)
|
|
b, g, r = image[y, x]
|
|
return int(r), int(g), int(b)
|
|
|
|
|
|
def wait(seconds):
|
|
time.sleep(seconds)
|