macos: fold relaunch prompt into the warning row instead of a toast

The cut-off toast UX ("Accessibility granted. Relaunch Lan Mouse so
capture and emulat…") was unreadable in a compact window and split
the "grant" and "relaunch" flows into two disconnected surfaces. Fold
everything into the existing warning row with state-dependent content:

- AX missing:
    title    = "input capture is disabled"
    subtitle = "grant Accessibility permission to enable"
    button   = "Grant"   → opens System Settings → Accessibility

- AX granted, daemon still bailed:
    title    = "relaunch required"
    subtitle = "Accessibility granted — restart to activate capture
                and emulation"
    button   = "Relaunch" → spawns a fresh bundle via `open` after
                            a 1s delay, then quits.

- Both active: row hidden.

The emulation_status_row is kept hidden on macOS because capture and
emulation share the same TCC gate — a single row is sufficient and
two identical-looking warnings were noisy. `handle_emulation` still
exists for the non-macOS platforms where the rows are distinct.

Side effects:
- `relaunch_bundle` moved from lib.rs to macos_privacy so imp.rs can
  call it from the row button handler.
- AX watcher callback shrinks to `window.present()` +
  `refresh_capture_emulation_status()`; the toast-based dialog is
  gone along with its helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jon Kinney
2026-04-24 09:15:40 -05:00
committed by Ferdinand Schober
parent 2dc9ebb6cd
commit 5d7d14fbf7
5 changed files with 112 additions and 102 deletions

View File

@@ -10,16 +10,6 @@ use lan_mouse_ipc::{DEFAULT_PORT, FrontendRequestWriter};
use crate::authorization_window::AuthorizationWindow;
#[cfg(target_os = "macos")]
fn open_accessibility_if_missing(ctx: &str) {
if crate::macos_privacy::accessibility_granted() {
log::info!("{ctx}: Accessibility already granted, retry only");
} else {
log::info!("{ctx}: opening Accessibility pane");
crate::macos_privacy::open_accessibility_settings();
}
}
#[derive(CompositeTemplate, Default)]
#[template(resource = "/de/feschber/LanMouse/window.ui")]
pub struct Window {
@@ -152,15 +142,32 @@ impl Window {
#[template_callback]
fn handle_emulation(&self) {
#[cfg(target_os = "macos")]
open_accessibility_if_missing("Reenable emulation");
// On macOS the emulation_status_row is hidden — capture_status_row
// acts as the shared warning (see update_capture_emulation_status).
// This handler still fires for the non-macOS platforms where the
// emulation row is distinct.
self.obj().request_emulation();
}
#[template_callback]
fn handle_capture(&self) {
#[cfg(target_os = "macos")]
open_accessibility_if_missing("Reenable capture");
{
use crate::macos_privacy;
if macos_privacy::accessibility_granted() {
// AX granted but the row is still visible => the daemon
// subprocess bailed before AX was in place and needs a
// fresh process. Quit + relaunch via Launch Services.
log::info!("capture row clicked in relaunch-required state");
macos_privacy::relaunch_bundle();
if let Some(app) = self.obj().application() {
app.quit();
}
return;
}
log::info!("capture row clicked in AX-missing state, opening pane");
macos_privacy::open_accessibility_settings();
}
self.obj().request_capture();
}