feat(clients): Wake-on-LAN in apple/linux/windows/android/decky
Each client learns a host's MAC from the mDNS `mac` TXT while it's awake, persists it on the saved-host record, and — when reconnecting to an offline host — sends a magic packet before connecting, plus an explicit "Wake host" action. Apple wraps the C-ABI; linux/windows call the core fn directly (linux also gains a --wake CLI mode); android via a new nativeWakeOnLan JNI export (the mDNS browse record gains a 7th mac field); decky shells out to the linux client's --wake before launching the stream. iOS/tvOS need the managed com.apple.developer.networking.multicast entitlement (pending Apple approval), so the wake path + UI are gated off via PunktfunkConnection.wakeOnLANAvailable and the entitlement is commented out — keeping iOS/tvOS releasable. MAC-learning stays active on every platform so it lights up the moment it's ungated. macOS works today. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -408,6 +408,7 @@ struct ContentView: View {
|
||||
_ host: StoredHost, launchID: String? = nil,
|
||||
allowTofu: Bool, requestAccess: Bool = false
|
||||
) {
|
||||
prepareWake(for: host)
|
||||
model.connect(
|
||||
to: host,
|
||||
width: UInt32(clamping: width), height: UInt32(clamping: height),
|
||||
@@ -426,6 +427,25 @@ struct ContentView: View {
|
||||
requestAccess: requestAccess)
|
||||
}
|
||||
|
||||
/// Learn-while-awake, wake-while-asleep — run just before every connect:
|
||||
/// • host currently advertising (awake) → refresh its stored Wake-on-LAN MAC(s) from the live
|
||||
/// advert, so a later wake has an up-to-date target;
|
||||
/// • host NOT advertising (likely asleep/off) and we have MAC(s) → fire a magic packet first.
|
||||
/// The connect that follows already retries/times out long enough for a woken host to come
|
||||
/// up; if it's genuinely off/unreachable the connect fails as before. Best-effort and
|
||||
/// non-blocking (the send runs off the main thread).
|
||||
private func prepareWake(for host: StoredHost) {
|
||||
if let live = discovery.hosts.first(where: { host.matches($0) }) {
|
||||
store.updateMacs(host.id, macs: live.macAddresses) // learn — on every platform
|
||||
} else if PunktfunkConnection.wakeOnLANAvailable, !host.wakeMacs.isEmpty {
|
||||
let macs = host.wakeMacs
|
||||
let ip = host.address
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
PunktfunkConnection.wakeOnLAN(macs: macs, lastKnownIP: ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The no-PIN delegated-approval flow: open an identified connect the host parks until the
|
||||
/// operator approves it in the console, showing the cancelable "Waiting for approval" prompt
|
||||
/// meanwhile. On success the SAME connection is admitted (no reconnect) and the host is pinned
|
||||
@@ -455,7 +475,9 @@ struct ContentView: View {
|
||||
/// inside `connect`.)
|
||||
private func connectDiscovered(_ d: DiscoveredHost) {
|
||||
guard !model.isBusy else { return }
|
||||
let host = StoredHost(name: d.name, address: d.host, port: d.port)
|
||||
let host = StoredHost(
|
||||
name: d.name, address: d.host, port: d.port,
|
||||
macAddresses: d.macAddresses.isEmpty ? nil : d.macAddresses)
|
||||
store.add(host)
|
||||
if d.allowsTofu {
|
||||
connect(host, allowTofu: true)
|
||||
|
||||
Reference in New Issue
Block a user