diff --git a/clients/windows/src/app/connect.rs b/clients/windows/src/app/connect.rs index f450bc05..98a71c77 100644 --- a/clients/windows/src/app/connect.rs +++ b/clients/windows/src/app/connect.rs @@ -36,7 +36,9 @@ pub(crate) fn initiate_waking( set_screen: &AsyncSetState, set_status: &AsyncSetState, ) { - crate::wol::wake(&target.mac, target.addr.parse().ok()); + if ctx.settings.lock().unwrap().auto_wake { + crate::wol::wake(&target.mac, target.addr.parse().ok()); + } initiate_opts(ctx, target, set_screen, set_status, true) } @@ -291,9 +293,13 @@ fn connect_spawn( *shared.target.lock().unwrap() = target.clone(); ss.call(Screen::Pair); } - Some((_, false)) if wake_on_fail => { + Some((_, false)) + if wake_on_fail && ctx2.settings.lock().unwrap().auto_wake => + { // The dial-first attempt to a non-advertising host failed — it - // may genuinely be asleep. NOW wake and wait. + // may genuinely be asleep. NOW wake and wait. Skipped entirely + // when auto-wake is off: the wait is only worth showing if we + // are actually sending magic packets to end it. wake_and_connect(&ctx2, target.clone(), &ss, &st); } Some((msg, false)) => { diff --git a/clients/windows/src/app/settings.rs b/clients/windows/src/app/settings.rs index 11404eca..ccf2697b 100644 --- a/clients/windows/src/app/settings.rs +++ b/clients/windows/src/app/settings.rs @@ -162,7 +162,10 @@ fn described(control: impl Into, caption: &str) -> Element { .font_size(12.0) .foreground(ThemeRef::SecondaryText) .wrap() - .max_width(420.0), + .max_width(420.0) + // Stretch (the TextBlock default) CENTRES a MaxWidth-capped block in the leftover + // width — the caption must be pinned left or it drifts away from its control. + .horizontal_alignment(HorizontalAlignment::Left), )) .spacing(5.0) .into() @@ -182,6 +185,7 @@ fn group(header: Option<&str>, fields: Vec, footer: Option<&str>) -> Ve .font_size(12.0) .foreground(ThemeRef::SecondaryText) .wrap() + .horizontal_alignment(HorizontalAlignment::Left) .margin(edges(2.0, 6.0, 0.0, 0.0)) .into(), ); @@ -267,6 +271,9 @@ pub(crate) fn settings_page( let comp_combo = setting_combo(ctx, "Host compositor", comp_names, comp_i, |s, i| { s.compositor = COMPOSITORS[i].0.to_string(); }); + let auto_wake_toggle = setting_toggle(ctx, "Auto-wake on connect", s.auto_wake, |s, on| { + s.auto_wake = on + }); let fullscreen_toggle = setting_toggle( ctx, "Start streams fullscreen", @@ -577,10 +584,19 @@ pub(crate) fn settings_page( _ => { let mut out = group( Some("Session"), - vec![described( - fullscreen_toggle, - "Go fullscreen when a session starts; F11 or Alt+Enter switches back live.", - )], + vec![ + described( + fullscreen_toggle, + "Go fullscreen when a session starts; F11 or Alt+Enter switches back \ + live.", + ), + described( + auto_wake_toggle, + "Connecting to a saved host that\u{2019}s offline sends Wake-on-LAN and \ + waits for it to boot. Turn off if hosts behind a VPN look offline when \ + they aren\u{2019}t.", + ), + ], None, ); out.extend(group( @@ -634,9 +650,16 @@ pub(crate) fn settings_page( // // The content column (not the NavigationView — the sidebar must stay put) carries the // section-switch entrance: fade + slide-up from the root-driven tween. - let content = page(vec![vstack(groups).spacing(10.0).with_key(section).into()]) - .opacity(progress) - .margin(edges(0.0, (1.0 - progress) * 22.0, 0.0, 0.0)); + // No max-width cap here (unlike the other pages): the NavigationView already spends the + // left third on its pane, so a 640-wide column left the cards as a narrow ribbon. + let content = scroll_view( + vstack(groups) + .spacing(10.0) + .margin(edges(24.0, 20.0, 28.0, 40.0)) + .with_key(section), + ) + .opacity(progress) + .margin(edges(0.0, (1.0 - progress) * 22.0, 0.0, 0.0)); NavigationView::new(items, content) .pane_title("Settings") .header(title) diff --git a/crates/pf-client-core/src/trust.rs b/crates/pf-client-core/src/trust.rs index 2d2c6f11..d6a2a3c8 100644 --- a/crates/pf-client-core/src/trust.rs +++ b/crates/pf-client-core/src/trust.rs @@ -526,6 +526,13 @@ pub struct Settings { /// Experimental: the game-library browser ("Browse library…" on saved cards) — /// mirrors the Apple client's "Show game library" toggle, default off. pub library_enabled: bool, + /// Send Wake-on-LAN before connecting to a saved host and wait for it to boot (the + /// Apple client's "Auto-wake on connect"). Default ON — that was the unconditional + /// behavior before this became a setting. Off is for hosts reached over a VPN, where + /// an offline-looking host is really just unreachable by broadcast and the wake + + /// wait only adds a delay. + #[serde(default = "default_true")] + pub auto_wake: bool, /// Match-window resolution policy (design/midstream-resolution-resize.md D1): the /// stream mode follows the session window — the connect asks for the window's pixel /// size and a mid-session resize renegotiates the host's virtual display + encoder @@ -614,6 +621,7 @@ impl Default for Settings { stats_verbosity: None, fullscreen_on_stream: true, library_enabled: false, + auto_wake: true, match_window: false, last_window_w: 0, last_window_h: 0,