use std::collections::HashSet;
use std::sync::{
atomic::{AtomicBool, Ordering},
Mutex, OnceLock,
};
use auditaur_core::drive_bridge::{
DriveBridgeRequest, DriveBridgeResponse, WINDOW_ACTION, WINDOW_MODE_ENV,
};
use serde_json::{json, Value};
use tauri::{AppHandle, Manager, Runtime, WebviewWindow, Window};
#[derive(Default)]
struct ModeState {
background: bool,
prior: Option<isize>,
handled: HashSet<String>,
events: Vec<Value>,
before_front: Option<isize>,
}
static STATE: Mutex<Option<ModeState>> = Mutex::new(None);
static STARTING: AtomicBool = AtomicBool::new(true);
type Handler = Box<dyn Fn(&str, Option<&str>) -> Result<Value, String> + Send + Sync>;
static HANDLER: OnceLock<Handler> = OnceLock::new();
fn with_state<T>(f: impl FnOnce(&mut ModeState) -> T) -> T {
let mut guard = STATE.lock().unwrap_or_else(|poison| poison.into_inner());
f(guard.get_or_insert_with(ModeState::default))
}
pub(crate) fn supported() -> bool {
cfg!(windows)
}
pub(crate) fn init<R: Runtime>(app: &AppHandle<R>) {
let background = std::env::var(WINDOW_MODE_ENV).is_ok_and(|mode| mode == "background");
let prior = if background {
platform::foreground().filter(|&hwnd| platform::foreign(hwnd))
} else {
None
};
with_state(|state| {
state.background = background;
state.prior = prior;
});
if HANDLER.get().is_none() {
let app = app.clone();
let _ = HANDLER.set(Box::new(move |command, label| {
handle_command(&app, command, label)
}));
}
}
pub(crate) fn started() {
STARTING.store(false, Ordering::SeqCst);
}
pub(crate) fn window_activated<R: Runtime>(window: &Window<R>, trigger: &str) {
if !STARTING.load(Ordering::SeqCst) {
return;
}
let Some(hwnd) = platform::window_hwnd(window) else {
return;
};
if platform::foreground() != Some(hwnd) {
return;
}
let label = window.label().to_owned();
let Some(prior) = with_state(|state| claim(state, &label)) else {
return;
};
let mut outcome = platform::hand_back(hwnd, prior);
outcome["window"] = json!(label);
outcome["trigger"] = json!(trigger);
if outcome["restored"] == true {
::tracing::debug!(window = %label, "Auditaur kept the drilled window in the background");
} else {
::tracing::warn!(window = %label, outcome = %outcome, "Auditaur could not hand the foreground back from the drilled window");
}
with_state(|state| state.events.push(outcome));
}
fn claim(state: &mut ModeState, label: &str) -> Option<Option<isize>> {
(state.background && state.handled.insert(label.to_owned())).then_some(state.prior)
}
pub(crate) fn handle(request: &DriveBridgeRequest) -> Option<DriveBridgeResponse> {
if request.action != WINDOW_ACTION {
return None;
}
let command = request.value.as_deref().unwrap_or("status");
let result = match HANDLER.get() {
Some(handler) => handler(command, request.window_label.as_deref()),
None => Err("the Auditaur plugin is not set up".to_owned()),
};
let (ok, payload, error) = match result {
Ok(payload) => (true, payload, None),
Err(error) => (false, Value::Null, Some(error)),
};
Some(DriveBridgeResponse {
schema_version: request.schema_version,
protocol_version: request.protocol_version,
request_id: request.request_id.clone(),
action: request.action.clone(),
selector: request.selector.clone(),
visible_only: request.visible_only,
ok,
payload,
error,
completed_at_unix_nanos: crate::now_unix_nanos(),
})
}
fn handle_command<R: Runtime>(
app: &AppHandle<R>,
command: &str,
label: Option<&str>,
) -> Result<Value, String> {
match command {
"status" => Ok(with_state(|state| {
json!({
"mode": if state.background { "background" } else { "foreground" },
"supported": supported(),
"events": state.events,
})
})),
"front" => {
let window = resolve(app, label)?;
let before = platform::foreground().filter(|&hwnd| platform::foreign(hwnd));
with_state(|state| state.before_front = before);
let _ = window.unminimize();
let _ = window.show();
window
.set_focus()
.map_err(|error| format!("could not bring the window to the front: {error}"))?;
Ok(json!({ "window": window.label() }))
}
"back" => {
let window = resolve(app, label)?;
let Some(hwnd) = platform::webview_hwnd(&window) else {
return Ok(json!({ "skipped": "not supported on this platform" }));
};
if platform::foreground() != Some(hwnd) {
return Ok(json!({ "skipped": "the app no longer has the foreground" }));
}
let before = with_state(|state| state.before_front.take());
Ok(platform::hand_back(hwnd, before))
}
other => Err(format!(
"unknown `{WINDOW_ACTION}` command `{other}`; expected status, front, or back"
)),
}
}
fn resolve<R: Runtime>(
app: &AppHandle<R>,
label: Option<&str>,
) -> Result<WebviewWindow<R>, String> {
if let Some(label) = label {
return app
.get_webview_window(label)
.ok_or_else(|| format!("no Tauri WebView window matched `{label}`"));
}
let mut windows: Vec<_> = app.webview_windows().into_iter().collect();
windows.sort_by(|(a, _), (b, _)| (a != "main").cmp(&(b != "main")).then(a.cmp(b)));
windows
.into_iter()
.next()
.map(|(_, window)| window)
.ok_or_else(|| "the app has no WebView windows".to_owned())
}
#[cfg(windows)]
mod platform {
use serde_json::{json, Value};
use tauri::{Runtime, WebviewWindow, Window};
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::WindowsAndMessaging::{
GetForegroundWindow, GetWindowThreadProcessId, IsWindow, SetForegroundWindow, SetWindowPos,
HWND_BOTTOM, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE,
};
fn handle(raw: isize) -> HWND {
HWND(raw as *mut _)
}
pub fn foreground() -> Option<isize> {
let hwnd = unsafe { GetForegroundWindow() };
(!hwnd.is_invalid()).then_some(hwnd.0 as isize)
}
pub fn foreign(raw: isize) -> bool {
let hwnd = handle(raw);
if !unsafe { IsWindow(Some(hwnd)) }.as_bool() {
return false;
}
let mut pid = 0;
unsafe { GetWindowThreadProcessId(hwnd, Some(&mut pid)) };
pid != 0 && pid != std::process::id()
}
pub fn window_hwnd<R: Runtime>(window: &Window<R>) -> Option<isize> {
window.hwnd().ok().map(|hwnd| hwnd.0 as isize)
}
pub fn webview_hwnd<R: Runtime>(window: &WebviewWindow<R>) -> Option<isize> {
window.hwnd().ok().map(|hwnd| hwnd.0 as isize)
}
pub fn hand_back(app: isize, prior: Option<isize>) -> Value {
let prior = prior.filter(|&hwnd| foreign(hwnd));
let restored = prior.is_some_and(|hwnd| {
unsafe { SetForegroundWindow(handle(hwnd)) }.as_bool() && foreground() == Some(hwnd)
});
let after = match prior {
Some(hwnd) if restored => handle(hwnd),
_ => HWND_BOTTOM,
};
let placed = unsafe {
SetWindowPos(
handle(app),
Some(after),
0,
0,
0,
0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE,
)
}
.is_ok();
let error = match (prior, restored) {
(_, true) => None,
(None, _) => Some("the window that had the foreground before is gone"),
(Some(_), false) => Some("Windows refused to give the foreground back"),
};
json!({
"restored": restored,
"behind": if restored { "previous foreground window" } else { "all windows" },
"placed": placed,
"error": error,
})
}
}
#[cfg(not(windows))]
mod platform {
use serde_json::{json, Value};
use tauri::{Runtime, WebviewWindow, Window};
pub fn foreground() -> Option<isize> {
None
}
pub fn foreign(_raw: isize) -> bool {
false
}
pub fn window_hwnd<R: Runtime>(_window: &Window<R>) -> Option<isize> {
None
}
pub fn webview_hwnd<R: Runtime>(_window: &WebviewWindow<R>) -> Option<isize> {
None
}
pub fn hand_back(_app: isize, _prior: Option<isize>) -> Value {
json!({ "restored": false, "error": "not supported on this platform" })
}
}
#[cfg(test)]
mod tests {
use super::{claim, handle, ModeState};
use auditaur_core::drive_bridge::{DriveBridgeRequest, WINDOW_ACTION};
#[test]
fn each_window_is_handed_back_once_and_only_in_background_mode() {
let mut state = ModeState {
background: true,
prior: Some(42),
..ModeState::default()
};
assert_eq!(claim(&mut state, "main"), Some(Some(42)));
assert_eq!(claim(&mut state, "main"), None);
assert_eq!(claim(&mut state, "other"), Some(Some(42)));
let mut foreground = ModeState::default();
assert_eq!(claim(&mut foreground, "main"), None);
}
fn request(action: &str, value: Option<&str>) -> DriveBridgeRequest {
DriveBridgeRequest {
schema_version: 1,
protocol_version: 1,
request_id: "r1".into(),
action: action.into(),
selector: None,
value: value.map(str::to_owned),
values: Vec::new(),
visible_only: false,
window_label: None,
test_id: None,
step_id: None,
created_at_unix_nanos: 0,
}
}
#[test]
fn only_window_requests_are_answered_natively() {
assert!(handle(&request("click", None)).is_none());
let response = handle(&request(WINDOW_ACTION, Some("status"))).unwrap();
assert_eq!(response.request_id, "r1");
assert_eq!(response.action, WINDOW_ACTION);
}
}