mirror of
https://github.com/lahfir/agent-desktop.git
synced 2026-08-18 21:17:29 +00:00
fix: make focused semantics honest and confirm window focus by polling
ensure_app_focused set AXFrontmost unconditionally and reported success identically whether or not a raise happened; it now no-ops when the app is already frontmost, so Ok (and the focused:true response field) means "frontmost ensured" exactly as documented. focus_window_impl gains the same confirmation poll after its raise, and the poll interval widens to 5ms (10 reads max in the 50ms window).
This commit is contained in:
parent
d762f8cd3a
commit
44ce45e89b
1 changed files with 9 additions and 1 deletions
|
|
@ -11,6 +11,10 @@ pub fn pid_from_element(el: &crate::tree::AXElement) -> Option<i32> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Ensures the app is frontmost: a no-op when it already is, otherwise a
|
||||
/// best-effort raise confirmed by polling. `Ok` therefore means "frontmost
|
||||
/// ensured", not "a raise happened" — callers surfacing `focused:true` get
|
||||
/// exactly that ensured semantics.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn ensure_app_focused(pid: i32) -> Result<(), AdapterError> {
|
||||
tracing::debug!("system: ensure_app_focused pid={pid}");
|
||||
|
|
@ -18,6 +22,9 @@ pub fn ensure_app_focused(pid: i32) -> Result<(), AdapterError> {
|
|||
use core_foundation::{base::TCFType, boolean::CFBoolean, string::CFString};
|
||||
|
||||
let app_el = crate::tree::element_for_pid(pid);
|
||||
if crate::tree::copy_bool_attr(&app_el, "AXFrontmost") == Some(true) {
|
||||
return Ok(());
|
||||
}
|
||||
let frontmost_attr = CFString::new("AXFrontmost");
|
||||
let err = unsafe {
|
||||
AXUIElementSetAttributeValue(
|
||||
|
|
@ -43,7 +50,7 @@ pub fn ensure_app_focused(pid: i32) -> Result<(), AdapterError> {
|
|||
fn wait_until_frontmost(app_el: &crate::tree::AXElement) {
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
const POLL_INTERVAL: Duration = Duration::from_millis(1);
|
||||
const POLL_INTERVAL: Duration = Duration::from_millis(5);
|
||||
const FRONTMOST_DEADLINE: Duration = Duration::from_millis(50);
|
||||
|
||||
let deadline = Instant::now() + FRONTMOST_DEADLINE;
|
||||
|
|
@ -89,6 +96,7 @@ pub fn focus_window_impl(win: &WindowInfo) -> Result<(), AdapterError> {
|
|||
"Failed to set AXFrontmost (err={err})"
|
||||
)));
|
||||
}
|
||||
wait_until_frontmost(&app_el);
|
||||
|
||||
let main_win = crate::tree::window_element_for(win.pid, &win.title);
|
||||
let raise_action = CFString::new("AXRaise");
|
||||
|
|
|
|||
Loading…
Reference in a new issue