player: active foreground wait replaces fixed 600ms sleep (#14)
BringSutToForeground() now polls GetForegroundWindow() == SUT hwnd at 25ms intervals up to 2s, followed by a 100ms tail settle, instead of the brittle fixed 600ms sleep. First-attempt replay of box-v6.yaml is now reliable (previously dropped the opening "BOX" keystrokes on a cold start). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
5
PLAN.md
5
PLAN.md
@@ -10,9 +10,8 @@
|
||||
|
||||
## P1 — 라이브 검증 (사용자 환경 필요)
|
||||
|
||||
4. **이슈 #14 재생 안정화** — 첫 시도 BOX 타이핑 누락 문제. foreground settle 600ms→능동 대기 전환 또는 첫 type 이전 `Keyboard.Type` warm-up. `/contract player-foreground-stabilize`.
|
||||
5. **recorder Gap I-1** — type 스텝 target을 `Automation.FocusedElement`로 직접 채워 null_target_steps=0 달성. `/contract recorder-focused-element-target`.
|
||||
6. **engine-bridge v3** — ReflectionEngineStateProvider 실매핑 (smoke test 이후)
|
||||
4. **recorder Gap I-1** — type 스텝 target을 `Automation.FocusedElement`로 직접 채워 null_target_steps=0 달성. Player fallback 의존도 줄이기.
|
||||
5. **engine-bridge v3** — ReflectionEngineStateProvider 실매핑 (smoke test 이후)
|
||||
|
||||
## Follow-ups (non-blocking)
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ _(없음)_
|
||||
- [ ] player: `wait_for` UIA 이벤트 매핑 강화 (현재 host passthrough).
|
||||
- [ ] player: `UiaPlayerHost` uia_path resolver가 마지막 `@AutomationId`만 사용 — 전체 ancestor chain 지원 필요.
|
||||
- [ ] recorder: IME 조합 키 처리 (contract risks).
|
||||
- [ ] player: foreground settle이 경계선(600ms) — 1차 재생이 가끔 BOX 타이핑 누락. 능동 대기(focus-ready polling)로 전환 고려. (issue #14 후속)
|
||||
- [x] ~~player: foreground settle 안정화~~ — 능동 대기(`GetForegroundWindow` polling 2s + 100ms settle)로 전환, 1차 재생 성공 확인
|
||||
- [ ] recorder: null_target 이벤트 자체를 줄이기 — `Automation.FocusedElement` 직접 조회해 type 스텝 target 채우기 (issue #14 Gap I-1). 현재는 player fallback으로 우회.
|
||||
|
||||
## Blocked
|
||||
|
||||
@@ -212,21 +212,34 @@ public sealed class UiaPlayerHost : IPlayerHost, IDisposable
|
||||
/// was launched from a shell that still has focus when the first Type/Hotkey
|
||||
/// step fires.
|
||||
/// </summary>
|
||||
[System.Runtime.InteropServices.DllImport("user32.dll")]
|
||||
private static extern IntPtr GetForegroundWindow();
|
||||
|
||||
public void BringSutToForeground()
|
||||
{
|
||||
try
|
||||
{
|
||||
var w = _app?.GetMainWindow(_automation, TimeSpan.FromSeconds(5));
|
||||
if (w is null) return;
|
||||
var targetHwnd = w.Properties.NativeWindowHandle.ValueOrDefault;
|
||||
try { w.SetForeground(); } catch { /* best-effort */ }
|
||||
try { w.Focus(); } catch { /* best-effort */ }
|
||||
// Small settle so the OS-level focus switch takes effect before
|
||||
// the first SendInput. 150ms is enough in practice on Win10.
|
||||
// Increased from 150→600ms because FlaUI Keyboard.Type drops the
|
||||
// first couple of characters if SendInput fires before the OS
|
||||
// finishes the focus transition (observed: "BOX" lost on first
|
||||
// step, "10" succeeded later once the app had settled).
|
||||
System.Threading.Thread.Sleep(600);
|
||||
|
||||
// Issue #14 follow-up: active wait instead of fixed 600ms sleep.
|
||||
// Poll until the OS reports the SUT as the foreground window, up
|
||||
// to 2s. Previously a 600ms fixed sleep was threshold-sensitive
|
||||
// and caused the first "BOX" keystroke to get dropped on a cold
|
||||
// first run.
|
||||
var deadline = DateTime.UtcNow.AddSeconds(2);
|
||||
while (DateTime.UtcNow < deadline)
|
||||
{
|
||||
if (targetHwnd != IntPtr.Zero && GetForegroundWindow() == targetHwnd)
|
||||
break;
|
||||
System.Threading.Thread.Sleep(25);
|
||||
}
|
||||
// Tiny additional settle for the OS keyboard-focus IPC to finish
|
||||
// after the foreground transition is observed.
|
||||
System.Threading.Thread.Sleep(100);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user