From c7004a65eaa2c95b0b7e0dcff79d1b822e6e220a Mon Sep 17 00:00:00 2001 From: sciamop <17859+sciamop@users.noreply.github.com> Date: Mon, 5 Oct 2026 11:55:40 -0400 Subject: [PATCH 1/5] Fix virtual display reshapes leaving capture with no video Once a process calls CGDisplaySetDisplayMode (or configures a mode for the session), macOS ignores later CGVirtualDisplay applySettings reshapes, so the second client shape never took effect and capture targeted a stale display. - Never select modes explicitly; report a tiny physical size so macOS picks the HiDPI variant of each mode by default. - Create the display once at up to 8K so any shape fits without recreating it (recreation raced with macOS reusing the display ID). - Wait until the display and ScreenCaptureKit both report the new shape before capturing; run a real main run loop. Co-Authored-By: Claude Opus 5.5 --- Host/HostServer.swift | 12 +++++-- Host/VirtualScreen.swift | 74 +++++++++++++++++++++++++--------------- Host/main.swift | 5 ++- 3 files changed, 60 insertions(+), 31 deletions(-) diff --git a/Host/HostServer.swift b/Host/HostServer.swift index db52498..750a658 100644 --- a/Host/HostServer.swift +++ b/Host/HostServer.swift @@ -235,7 +235,8 @@ final class ClientSession { if virtual == nil { hostLog("falling back to the existing display") } } - let display = try await findDisplay(id: virtual?.displayID) + let expected = virtual.map { VirtualScreen.pointSize(for: $0.request) } + let display = try await findDisplay(id: virtual?.displayID, expectedPoints: expected) let mode = CGDisplayCopyDisplayMode(display.displayID) let refresh = mode?.refreshRate ?? 60 let fps = min(fpsLimit, refresh > 0 ? Int(refresh.rounded()) : 60) @@ -324,11 +325,16 @@ final class ClientSession { } /// A freshly created virtual display can take a moment to show up in ScreenCaptureKit. - private func findDisplay(id: CGDirectDisplayID?) async throws -> SCDisplay { + /// ScreenCaptureKit can briefly report a stale display (old size) after a reshape, so for + /// virtual displays wait until it reports the expected point size too. + private func findDisplay(id: CGDirectDisplayID?, expectedPoints: (width: Int, height: Int)? = nil) async throws -> SCDisplay { for attempt in 0..<20 { let content = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: true) if let id { - if let match = content.displays.first(where: { $0.displayID == id }) { return match } + if let match = content.displays.first(where: { $0.displayID == id }), + expectedPoints.map({ abs(match.width - $0.width) <= 1 && abs(match.height - $0.height) <= 1 }) ?? true { + return match + } } else { let main = CGMainDisplayID() let displays = content.displays.sorted { diff --git a/Host/VirtualScreen.swift b/Host/VirtualScreen.swift index 18c28a5..9e49ad4 100644 --- a/Host/VirtualScreen.swift +++ b/Host/VirtualScreen.swift @@ -29,7 +29,19 @@ final class VirtualScreen { self.refreshRate = refreshRate } + /// Largest backing size (pixels per side) a display may use. The display is created at this + /// size up front so any client shape can be applied in place: tearing a virtual display down + /// and recreating it races with macOS reusing the same display ID. + static let maxBackingPixels = 8192 + + /// Mac point size the display will have for `request` (what ScreenCaptureKit reports). + static func pointSize(for request: DisplayRequest) -> (width: Int, height: Int) { + let size = backingPixels(for: request) + return (size.pointWidth, size.pointHeight) + } + /// Returns a virtual display matching `request`, reusing and reshaping the existing one when possible. + /// Returns nil if the display didn't come online in the requested shape. static func obtain(for request: DisplayRequest, refreshRate: Double) -> VirtualScreen? { let screen = DispatchQueue.main.sync { () -> VirtualScreen? in let backing = backingPixels(for: request) @@ -38,39 +50,56 @@ final class VirtualScreen { if current.request == request, current.refreshRate == refreshRate { return current } if current.apply(request, refreshRate: refreshRate) { return current } } - shared = nil // releasing the old display removes it + if let old = shared { + let oldID = old.displayID + shared = nil // releasing the old display removes it + waitUntilGone(oldID) + } shared = create(for: request, refreshRate: refreshRate) return shared } - screen?.waitUntilReady() + // Check readiness on the main thread too: CoreGraphics only refreshes its view of a + // reshaped display there; background-thread queries keep returning the old mode. + guard let screen, DispatchQueue.main.sync(execute: { screen.waitUntilReady() }) else { return nil } return screen } + private static func waitUntilGone(_ id: CGDirectDisplayID) { + for _ in 0..<60 where CGDisplayIsOnline(id) != 0 { + Thread.sleep(forTimeInterval: 0.05) + } + } + /// A new or reshaped display takes a moment to come online with its mode; capture and - /// frame-rate decisions must wait for it. Called off the main thread. - private func waitUntilReady() { + /// frame-rate decisions must wait for it. Main thread only. + private func waitUntilReady() -> Bool { let size = Self.backingPixels(for: request) - for _ in 0..<60 { - if let mode = CGDisplayCopyDisplayMode(displayID), mode.width == size.pointWidth, mode.refreshRate > 0 { break } - selectMode(pointWidth: size.pointWidth, pixelWidth: size.width, refreshRate: refreshRate) + var ready = false + for _ in 0..<100 { + if let mode = CGDisplayCopyDisplayMode(displayID), mode.width == size.pointWidth, + mode.height == size.pointHeight, mode.pixelWidth == size.width, mode.refreshRate > 0 { + ready = true + break + } Thread.sleep(forTimeInterval: 0.05) } let mode = CGDisplayCopyDisplayMode(displayID) - hostLog("virtual display: \(mode?.width ?? 0)x\(mode?.height ?? 0) pt, \(mode?.pixelWidth ?? 0)x\(mode?.pixelHeight ?? 0) px @ \(Int(mode?.refreshRate ?? 0)) Hz (display \(displayID))") + hostLog("virtual display\(ready ? "" : " NOT READY"): \(mode?.width ?? 0)x\(mode?.height ?? 0) pt, \(mode?.pixelWidth ?? 0)x\(mode?.pixelHeight ?? 0) px @ \(Int(mode?.refreshRate ?? 0)) Hz (display \(displayID))") + return ready } private static func create(for request: DisplayRequest, refreshRate: Double) -> VirtualScreen? { - let backing = backingPixels(for: request) - // Square max size so the same display can rotate between landscape and portrait. - let maxPixels = max(backing.width, backing.height) + // Square, generous max size: any orientation or window shape can then be applied in place. + let maxPixels = maxBackingPixels let descriptor = CGVirtualDisplayDescriptor() descriptor.setDispatchQueue(queue) descriptor.name = "iFrame Display" descriptor.maxPixelsWide = UInt32(maxPixels) descriptor.maxPixelsHigh = UInt32(maxPixels) - // iPad Pro panels are 264 ppi; physical size drives macOS's default scaling choices. - let mm = Double(maxPixels) * 25.4 / 264 - descriptor.sizeInMillimeters = CGSize(width: mm, height: mm) + // A small physical size (very high ppi) makes macOS default to the HiDPI variant of each + // mode. We must never select modes ourselves: after any CGDisplaySetDisplayMode or + // CGConfigureDisplayWithDisplayMode, macOS ignores later applySettings reshapes. + descriptor.sizeInMillimeters = CGSize(width: 263, height: 263) descriptor.vendorID = 0x676C // "gl" descriptor.productID = 0x6964 // "id" descriptor.serialNum = 1 @@ -88,7 +117,10 @@ final class VirtualScreen { /// Mac points and backing pixels for a request. HiDPI modes always render at 2x points. private static func backingPixels(for request: DisplayRequest) -> (width: Int, height: Int, pointWidth: Int, pointHeight: Int) { - let scale = min(max(request.uiScale, 1), 2) + var scale = min(max(request.uiScale, 1), 2) + // HiDPI renders at 2x points; keep that within the display's maximum. + let longest = Double(max(request.width, request.height)) + scale = max(scale, longest * 2 / Double(maxBackingPixels)) let pw = Int((Double(request.width) / scale).rounded()) & ~1 let ph = Int((Double(request.height) / scale).rounded()) & ~1 return (pw * 2, ph * 2, pw, ph) @@ -106,18 +138,6 @@ final class VirtualScreen { guard display.apply(settings) else { return false } self.request = request self.refreshRate = refreshRate - selectMode(pointWidth: size.pointWidth, pixelWidth: size.width, refreshRate: refreshRate) return true } - - /// macOS usually picks the right mode itself; make sure it's the HiDPI one at full refresh. - private func selectMode(pointWidth: Int, pixelWidth: Int, refreshRate: Double) { - let options = [kCGDisplayShowDuplicateLowResolutionModes: true] as CFDictionary - if let current = CGDisplayCopyDisplayMode(displayID), - current.width == pointWidth, current.pixelWidth == pixelWidth, current.refreshRate == refreshRate { return } - let modes = CGDisplayCopyAllDisplayModes(displayID, options) as? [CGDisplayMode] ?? [] - if let match = modes.first(where: { $0.width == pointWidth && $0.pixelWidth == pixelWidth && $0.refreshRate == refreshRate }) { - CGDisplaySetDisplayMode(displayID, match, nil) - } - } } diff --git a/Host/main.swift b/Host/main.swift index dcb7846..0c344b2 100644 --- a/Host/main.swift +++ b/Host/main.swift @@ -98,7 +98,10 @@ signalSource.setEventHandler { } signalSource.resume() -dispatchMain() +// A real run loop, not dispatchMain(): CoreGraphics delivers display reconfiguration +// notifications through the main run loop. Without it, this process never sees a virtual +// display change shape after its first mode, and capture targets a stale display. +RunLoop.main.run() func localIPv4Addresses() -> [String] { var result: [String] = [] From fc58195cb5fb2ab88343881ca9f1de48a5858c71 Mon Sep 17 00:00:00 2001 From: sciamop <17859+sciamop@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:31:17 -0400 Subject: [PATCH 2/5] Log a periodic summary of client input (and flag missing Accessibility) Co-Authored-By: Claude Opus 5.5 --- Host/HostServer.swift | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Host/HostServer.swift b/Host/HostServer.swift index 750a658..ce23d69 100644 --- a/Host/HostServer.swift +++ b/Host/HostServer.swift @@ -1,3 +1,4 @@ +import ApplicationServices import Foundation import IOKit.pwr_mgt import Network @@ -102,6 +103,8 @@ final class ClientSession { private var focusTimer: DispatchSourceTimer? private var lastFocus = FocusWatcher.Focus.none private var streamDisplay: CGDirectDisplayID? + private var inputCounts: [String: Int] = [:] + private var lastInputLog = Date() init(connection: NWConnection, server: HostServer, queue: DispatchQueue) { self.channel = MessageChannel(connection: connection, queue: queue) @@ -156,6 +159,7 @@ final class ClientSession { } var r = ByteReader(data) let input = server.injector + countInput(type, data) switch type { case .mouseMove: if let x = r.f32(), let y = r.f32() { input.move(x: x, y: y) } @@ -213,6 +217,29 @@ final class ClientSession { startFocusWatcher() } + /// Periodic summary of input received, so input problems show up in the log. + private func countInput(_ type: MsgType, _ data: Data) { + let label: String + switch type { + case .mouseMove: label = "moves" + case .mouseButton: label = "clicks" + case .scroll: label = "scrolls" + case .key: label = "keys" + case .text: label = "text" + default: return + } + inputCounts[label, default: 0] += 1 + if type == .key, data.count >= 3 { + inputCounts["last key 0x" + String(Int(data[data.startIndex]) << 8 | Int(data[data.startIndex + 1]), radix: 16)] = 0 + } + guard Date().timeIntervalSince(lastInputLog) > 5 else { return } + let summary = inputCounts.sorted { $0.key < $1.key } + .map { $0.value > 0 ? "\($0.key) \($0.value)" : $0.key }.joined(separator: ", ") + hostLog("input from \(name): \(summary)\(AXIsProcessTrusted() ? "" : " — NOT INJECTED: Accessibility permission missing")") + inputCounts = [:] + lastInputLog = Date() + } + private var hello: Hello? private var streamGeneration = 0 From a79c3f687c39659554471783e676b22c95399d5b Mon Sep 17 00:00:00 2001 From: sciamop <17859+sciamop@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:43:37 -0400 Subject: [PATCH 3/5] Host: forward the Mac's cursor shape to clients that draw it locally Clients that set localCursor in their hello get the system cursor shape (PNG at 2x + hotspot, in Mac points) whenever it changes, and capture runs without the cursor, so the pointer can move with zero latency on the client. Co-Authored-By: Claude Opus 5.5 --- Host/CursorWatcher.swift | 64 ++++++++++++++++++++++++++++++++++++++++ Host/HostServer.swift | 21 ++++++++++++- Host/Streamer.swift | 8 +++-- Shared/Protocol.swift | 3 ++ 4 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 Host/CursorWatcher.swift diff --git a/Host/CursorWatcher.swift b/Host/CursorWatcher.swift new file mode 100644 index 0000000..2b4b539 --- /dev/null +++ b/Host/CursorWatcher.swift @@ -0,0 +1,64 @@ +import AppKit + +/// Tracks the Mac's current cursor shape so clients can draw the pointer locally: it then +/// moves with zero latency and never gets lost in the video. (Capture runs without the +/// cursor for those clients.) +final class CursorWatcher { + struct Shape { + var png: Data + var pointWidth: Int + var pointHeight: Int + var hotspotX: Int + var hotspotY: Int + } + + var onChange: ((Shape) -> Void)? + private var timer: DispatchSourceTimer? + private var lastPixels: Data? + private var lastHotspot = CGPoint(x: -1, y: -1) + + func start() { + let timer = DispatchSource.makeTimerSource(queue: .main) + timer.schedule(deadline: .now(), repeating: .milliseconds(33)) + timer.setEventHandler { [weak self] in self?.poll() } + timer.resume() + self.timer = timer + } + + func stop() { + timer?.cancel() + timer = nil + } + + /// Forces the next poll to report the shape again (e.g. after the stream restarts). + func resend() { + DispatchQueue.main.async { self.lastPixels = nil } + } + + private func poll() { + guard let cursor = NSCursor.currentSystem else { return } + let image = cursor.image + let size = image.size + guard size.width > 0, size.height > 0, size.width <= 256, size.height <= 256 else { return } + + // Render at 2x so it stays crisp on high-density client screens. + guard let rep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(size.width * 2), pixelsHigh: Int(size.height * 2), + bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, + colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 0) else { return } + rep.size = size + NSGraphicsContext.saveGraphicsState() + NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: rep) + image.draw(in: NSRect(origin: .zero, size: size)) + NSGraphicsContext.restoreGraphicsState() + guard let raw = rep.bitmapData else { return } + let pixels = Data(bytes: raw, count: rep.bytesPerRow * rep.pixelsHigh) + let hotspot = cursor.hotSpot + guard pixels != lastPixels || hotspot != lastHotspot else { return } + lastPixels = pixels + lastHotspot = hotspot + + guard let png = rep.representation(using: .png, properties: [:]) else { return } + onChange?(Shape(png: png, pointWidth: Int(size.width.rounded()), pointHeight: Int(size.height.rounded()), + hotspotX: Int(hotspot.x.rounded()), hotspotY: Int(hotspot.y.rounded()))) + } +} diff --git a/Host/HostServer.swift b/Host/HostServer.swift index ce23d69..c865e1e 100644 --- a/Host/HostServer.swift +++ b/Host/HostServer.swift @@ -103,6 +103,7 @@ final class ClientSession { private var focusTimer: DispatchSourceTimer? private var lastFocus = FocusWatcher.Focus.none private var streamDisplay: CGDirectDisplayID? + private var cursorWatcher: CursorWatcher? private var inputCounts: [String: Int] = [:] private var lastInputLog = Date() @@ -140,6 +141,8 @@ final class ClientSession { streamer = nil focusTimer?.cancel() focusTimer = nil + cursorWatcher?.stop() + cursorWatcher = nil if sleepAssertion != 0 { IOPMAssertionRelease(sleepAssertion) sleepAssertion = 0 @@ -215,6 +218,20 @@ final class ClientSession { self.hello = hello restartStream(display: hello.display) startFocusWatcher() + if hello.localCursor == true { + let watcher = CursorWatcher() + watcher.onChange = { [weak self] shape in + var w = ByteWriter(capacity: shape.png.count + 8) + w.u16(UInt16(clamping: shape.hotspotX)) + w.u16(UInt16(clamping: shape.hotspotY)) + w.u16(UInt16(clamping: shape.pointWidth)) + w.u16(UInt16(clamping: shape.pointHeight)) + w.bytes(shape.png) + self?.channel.send(.cursor, w.data) + } + watcher.start() + cursorWatcher = watcher + } } /// Periodic summary of input received, so input problems show up in the log. @@ -284,7 +301,8 @@ final class ClientSession { maxBitrate: Int(startMbps * 1.5 * 1_000_000), maxInflight: server.config.maxInflight) - let streamer = try Streamer(display: display, codec: codec, config: config, captureSize: capture) + let streamer = try Streamer(display: display, codec: codec, config: config, captureSize: capture, + showsCursor: hello.localCursor != true) streamer.onFormat = { [weak self] codec, sets in self?.channel.send(.format, Wire.format(codec: codec, parameterSets: sets)) } @@ -310,6 +328,7 @@ final class ClientSession { codec: streamer.codec, fps: fps, hostName: Host.current().localizedName ?? "Mac", isVirtual: virtual != nil)) + self.cursorWatcher?.resend() // the client rebuilds its cursor for the new scale continuation.resume(returning: true) } } diff --git a/Host/Streamer.swift b/Host/Streamer.swift index d0efe92..41d6c77 100644 --- a/Host/Streamer.swift +++ b/Host/Streamer.swift @@ -70,7 +70,11 @@ final class Streamer: NSObject, SCStreamOutput, SCStreamDelegate { private var statsTimer: DispatchSourceTimer? private var stopped = false - init(display: SCDisplay, codec preferred: VideoCodec, config: Config, captureSize: (width: Int, height: Int)? = nil) throws { + private let showsCursor: Bool + + init(display: SCDisplay, codec preferred: VideoCodec, config: Config, captureSize: (width: Int, height: Int)? = nil, + showsCursor: Bool = true) throws { + self.showsCursor = showsCursor self.display = display self.config = config self.fps = config.fps @@ -158,7 +162,7 @@ final class Streamer: NSObject, SCStreamOutput, SCStreamDelegate { cfg.pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange cfg.colorMatrix = CGDisplayStream.yCbCrMatrix_ITU_R_709_2 cfg.colorSpaceName = CGColorSpace.sRGB - cfg.showsCursor = true + cfg.showsCursor = showsCursor cfg.queueDepth = 5 cfg.capturesAudio = false if #available(macOS 14.0, *) { cfg.captureResolution = .best } diff --git a/Shared/Protocol.swift b/Shared/Protocol.swift index b3cf491..740462a 100644 --- a/Shared/Protocol.swift +++ b/Shared/Protocol.swift @@ -22,6 +22,7 @@ enum MsgType: UInt8 { case pong = 0x05 // echo of ping payload case authFailed = 0x06 case textFocus = 0x07 // editable u8, x f32, y f32, w f32, h f32 (normalized over the stream) + case cursor = 0x08 // hotspot x u16, y u16, size w u16, h u16 (Mac points), PNG at 2x // client -> host case hello = 0x10 // JSON Hello case mouseMove = 0x11 // x f32, y f32 (normalized 0...1) @@ -49,6 +50,8 @@ struct Hello: Codable { var supportsHEVC: Bool var maxFPS: Int var display: DisplayRequest? + /// The client draws the pointer itself from `cursor` messages; capture omits it. + var localCursor: Bool? } /// The client's screen in pixels (current orientation). The host creates a virtual display From 83938577c70c694ed1ad53af3f1050f9cf485d61 Mon Sep 17 00:00:00 2001 From: sciamop <17859+sciamop@users.noreply.github.com> Date: Tue, 6 Oct 2026 08:55:42 -0400 Subject: [PATCH 4/5] Draw the Mac's cursor locally in the Linux and Windows clients The host sends cursor shapes and leaves the pointer out of the capture; the Windows client shows it as a CSS cursor and the Linux client as an SDL color cursor, so the pointer moves with no network delay. Adds probe support for inspecting cursor shapes. Co-Authored-By: Claude Opus 5.5 --- {Client => ClientShared}/StreamSession.swift | 0 Host/H264SPS.swift | 221 +++++++++++++++++++ Host/Probe.swift | 17 +- Host/Streamer.swift | 6 +- Host/main.swift | 2 +- Linux/README.md | 3 +- Linux/src/cursor.c | 62 ++++++ Linux/src/iframe.h | 4 + Linux/src/main.c | 119 +++++++++- Windows/README.md | 2 +- Windows/src/protocol.cjs | 2 +- Windows/src/session.cjs | 7 +- Windows/test/protocol.test.cjs | 6 +- Windows/ui/app.mjs | 13 +- Windows/ui/cursor.mjs | 49 ++++ 15 files changed, 493 insertions(+), 20 deletions(-) rename {Client => ClientShared}/StreamSession.swift (100%) create mode 100644 Host/H264SPS.swift create mode 100644 Linux/src/cursor.c create mode 100644 Windows/ui/cursor.mjs diff --git a/Client/StreamSession.swift b/ClientShared/StreamSession.swift similarity index 100% rename from Client/StreamSession.swift rename to ClientShared/StreamSession.swift diff --git a/Host/H264SPS.swift b/Host/H264SPS.swift new file mode 100644 index 0000000..a5b469a --- /dev/null +++ b/Host/H264SPS.swift @@ -0,0 +1,221 @@ +import Foundation + +/// Rewrites an H.264 SPS so decoders know frames are never reordered. +/// +/// VideoToolbox leaves the VUI `bitstream_restriction_flag` at 0, so a spec-following decoder +/// (Windows/Chromium hardware decoders, for one) assumes it may need to buffer a full DPB of +/// frames before output. Holding 4+ frames deadlocks against the host's `maxInflight` ack +/// window. We encode with `AllowFrameReordering` off, so declaring `max_num_reorder_frames = 0` +/// is accurate and lets those decoders output each frame immediately. +enum H264SPS { + /// Returns the SPS NAL unit (header byte included, no start code) with the bitstream + /// restriction set, or nil if it couldn't be parsed. + static func withoutReordering(_ nal: Data) -> Data? { + let bytes = [UInt8](nal) + guard bytes.count > 4, bytes[0] & 0x1F == 7 else { return nil } + var r = BitReader(removeEmulationPrevention(Array(bytes[1...]))) + var w = BitWriter() + + // Copies one field through unchanged, returning its value. + func bits(_ n: Int) -> UInt32? { + guard let v = r.bits(n) else { return nil } + w.bits(v, n) + return v + } + func ue() -> UInt32? { + guard let v = r.ue() else { return nil } + w.ue(v) + return v + } + func se() -> Bool { + guard let v = r.ue() else { return false } + w.ue(v) // se(v) shares ue(v)'s bit pattern, so copy it as-is + return true + } + func hrd() -> Bool { + guard let count = ue(), count < 32, bits(4) != nil, bits(4) != nil else { return false } + for _ in 0...count { + guard ue() != nil, ue() != nil, bits(1) != nil else { return false } + } + return bits(20) != nil // four 5-bit delay/offset lengths + } + + guard let profile = bits(8), bits(8) != nil, bits(8) != nil, ue() != nil else { return nil } + if [100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134, 135].contains(profile) { + guard let chroma = ue() else { return nil } + if chroma == 3 { guard bits(1) != nil else { return nil } } + guard ue() != nil, ue() != nil, bits(1) != nil, let scaling = bits(1) else { return nil } + if scaling == 1 { + for i in 0..<(chroma == 3 ? 12 : 8) { + guard let present = bits(1) else { return nil } + guard present == 1 else { continue } + var last: Int32 = 8, next: Int32 = 8 + for _ in 0..<(i < 6 ? 16 : 64) where next != 0 { + guard let delta = r.se() else { return nil } + w.se(delta) + next = (last + delta + 256) % 256 + if next != 0 { last = next } + } + } + } + } + guard ue() != nil, let pocType = ue() else { return nil } + if pocType == 0 { + guard ue() != nil else { return nil } + } else if pocType == 1 { + guard bits(1) != nil, se(), se(), let cycle = ue(), cycle < 256 else { return nil } + for _ in 0.. [UInt8] { + var out: [UInt8] = [] + out.reserveCapacity(bytes.count) + var zeros = 0 + for byte in bytes { + if zeros >= 2 && byte == 3 { + zeros = 0 + continue + } + zeros = byte == 0 ? zeros + 1 : 0 + out.append(byte) + } + return out + } + + /// Inserts 0x03 wherever 0x00 0x00 would be followed by a byte <= 0x03. + static func addEmulationPrevention(_ bytes: [UInt8]) -> [UInt8] { + var out: [UInt8] = [] + out.reserveCapacity(bytes.count + 4) + var zeros = 0 + for byte in bytes { + if zeros >= 2 && byte <= 3 { + out.append(3) + zeros = 0 + } + zeros = byte == 0 ? zeros + 1 : 0 + out.append(byte) + } + return out + } +} + +private struct BitReader { + let bytes: [UInt8] + var pos = 0 // in bits + + init(_ bytes: [UInt8]) { self.bytes = bytes } + + mutating func bits(_ n: Int) -> UInt32? { + guard pos + n <= bytes.count * 8 else { return nil } + var v: UInt32 = 0 + for _ in 0..> 3] >> (7 - UInt8(pos & 7)) & 1) + pos += 1 + } + return v + } + + mutating func ue() -> UInt32? { + var zeros = 0 + while true { + guard let bit = bits(1) else { return nil } + if bit == 1 { break } + zeros += 1 + guard zeros < 32 else { return nil } + } + guard let rest = bits(zeros) else { return nil } + return (1 << zeros) - 1 + rest + } + + mutating func se() -> Int32? { + guard let k = ue() else { return nil } + return k & 1 == 1 ? Int32((k + 1) / 2) : -Int32(k / 2) + } +} + +private struct BitWriter { + var bytes: [UInt8] = [] + private var count = 0 // bits written + + mutating func bits(_ value: UInt32, _ n: Int) { + for i in stride(from: n - 1, through: 0, by: -1) { + if count & 7 == 0 { bytes.append(0) } + if value >> i & 1 == 1 { bytes[bytes.count - 1] |= 0x80 >> UInt8(count & 7) } + count += 1 + } + } + + mutating func ue(_ value: UInt32) { + let v = UInt64(value) + 1 + let length = 64 - v.leadingZeroBitCount + bits(0, length - 1) + for i in stride(from: length - 1, through: 0, by: -1) { bits(UInt32(v >> i & 1), 1) } + } + + mutating func se(_ value: Int32) { + ue(value > 0 ? UInt32(value) * 2 - 1 : UInt32(-value) * 2) + } + + mutating func trailingBits() { + bits(1, 1) + while count & 7 != 0 { bits(0, 1) } + } +} diff --git a/Host/Probe.swift b/Host/Probe.swift index 905e0b1..75d2390 100644 --- a/Host/Probe.swift +++ b/Host/Probe.swift @@ -11,6 +11,8 @@ func runProbe() -> Never { let pin = option("--pin") ?? "" let seconds = option("--seconds").flatMap(Double.init) ?? 10 let skipDecode = arguments.contains("--no-decode") + let forceH264 = arguments.contains("--h264") // what Windows clients get + let localCursor = arguments.contains("--local-cursor") // e.g. --screen 2388x1668@2 to exercise the virtual display like an 11" iPad Pro would. let screen: DisplayRequest? = option("--screen").flatMap { spec in let parts = spec.split(separator: "@") @@ -48,8 +50,8 @@ func runProbe() -> Never { print("probe: connected to \(host):\(port)") channel.send(.hello, json: Hello( version: IFrame.protocolVersion, pin: pin, name: "iframe-probe", - supportsHEVC: VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC), maxFPS: 120, - display: screen)) + supportsHEVC: !forceH264 && VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC), maxFPS: 120, + display: screen, localCursor: localCursor)) let timer = DispatchSource.makeTimerSource(queue: queue) timer.schedule(deadline: .now() + 0.25, repeating: 0.25) timer.setEventHandler { @@ -80,9 +82,18 @@ func runProbe() -> Never { print("probe: wrong PIN") exit(2) case .format: - if let (codec, sets) = Wire.parseFormat(data), !decoder.setFormat(codec: codec, parameterSets: sets) { + guard let (codec, sets) = Wire.parseFormat(data) else { return } + if codec == .h264, let sps = sets.first(where: { $0.first.map { $0 & 0x1F == 7 } == true }) { + print("probe: SPS \(sps.map { String(format: "%02x", $0) }.joined())") + } + if !decoder.setFormat(codec: codec, parameterSets: sets) { print("probe: could not create decoder") } + case .cursor: + var r = ByteReader(data) + if let hx = r.u16(), let hy = r.u16(), let w = r.u16(), let h = r.u16() { + print("probe: cursor \(w)x\(h) pt, hotspot \(hx),\(hy), PNG \(r.rest().count) bytes") + } case .frame: guard let frame = Wire.parseFrame(data) else { return } if firstFrameAt == nil { firstFrameAt = nowNanos() } diff --git a/Host/Streamer.swift b/Host/Streamer.swift index 41d6c77..dfebe8e 100644 --- a/Host/Streamer.swift +++ b/Host/Streamer.swift @@ -402,7 +402,11 @@ final class Streamer: NSObject, SCStreamOutput, SCStreamDelegate { var pointer: UnsafePointer? var size = 0 guard get(index, &pointer, &size, nil) == noErr, let pointer else { return nil } - return Data(bytes: pointer, count: size) + let set = Data(bytes: pointer, count: size) + if codec == .h264, set.first.map({ $0 & 0x1F == 7 }) == true { + return H264SPS.withoutReordering(set) ?? set + } + return set } } } diff --git a/Host/main.swift b/Host/main.swift index 0c344b2..77b6695 100644 --- a/Host/main.swift +++ b/Host/main.swift @@ -28,7 +28,7 @@ HOST OPTIONS --pin fixed PIN (default: random each launch) PROBE OPTIONS - --pin --port --seconds --screen x[@scale] + --pin --port --seconds --screen x[@scale] --h264 --local-cursor """ var arguments = Array(CommandLine.arguments.dropFirst()) diff --git a/Linux/README.md b/Linux/README.md index b8f9748..c3982ed 100644 --- a/Linux/README.md +++ b/Linux/README.md @@ -87,7 +87,8 @@ The window title shows the host, resolution, decoder, fps, bitrate, round trip a ### Other options `--scroll-speed X`, `--invert-scroll`, `--local-cursor` (show the Linux pointer over the stream -as well; the host's cursor is part of the video), `--view-only` (watch without sending mouse or +even when the host draws its cursor into the video; a Mac sends its cursor shape instead, which +becomes the pointer), `--view-only` (watch without sending mouse or keyboard), `--no-hw`, `--h264`, `--vsync`, `--stats`. Colours follow the stream's own YUV matrix: BT.709 from the Mac, BT.601 from NVENC. diff --git a/Linux/src/cursor.c b/Linux/src/cursor.c new file mode 100644 index 0000000..052de40 --- /dev/null +++ b/Linux/src/cursor.c @@ -0,0 +1,62 @@ +#include +#include + +#include + +#include "iframe.h" + +// The host sends the Mac's cursor as a PNG. FFmpeg (already linked for video) decodes PNG, +// so this needs no image library. + +bool cursor_decode_png(const uint8_t *png, size_t len, uint8_t **rgba, int *width, int *height) { + *rgba = NULL; + const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_PNG); + if (!codec) return false; + AVCodecContext *ctx = avcodec_alloc_context3(codec); + AVPacket *pkt = av_packet_alloc(); + AVFrame *frame = av_frame_alloc(); + bool ok = false; + if (!ctx || !pkt || !frame || avcodec_open2(ctx, codec, NULL) < 0) goto done; + if (av_new_packet(pkt, (int)len) < 0) goto done; + memcpy(pkt->data, png, len); + if (avcodec_send_packet(ctx, pkt) < 0 || avcodec_receive_frame(ctx, frame) < 0) goto done; + + int w = frame->width, h = frame->height; + if (w <= 0 || h <= 0 || w > 512 || h > 512) goto done; + if (frame->format != AV_PIX_FMT_RGBA && frame->format != AV_PIX_FMT_RGB24 && frame->format != AV_PIX_FMT_PAL8) + goto done; + uint8_t *out = malloc((size_t)w * h * 4); + if (!out) goto done; + for (int y = 0; y < h; y++) { + const uint8_t *src = frame->data[0] + (size_t)y * frame->linesize[0]; + uint8_t *dst = out + (size_t)y * w * 4; + if (frame->format == AV_PIX_FMT_RGBA) { + memcpy(dst, src, (size_t)w * 4); + } else if (frame->format == AV_PIX_FMT_PAL8) { + const uint32_t *palette = (const uint32_t *)frame->data[1]; // native-endian ARGB + for (int x = 0; x < w; x++) { + uint32_t c = palette[src[x]]; + dst[4 * x] = c >> 16; + dst[4 * x + 1] = c >> 8; + dst[4 * x + 2] = c; + dst[4 * x + 3] = c >> 24; + } + } else { + for (int x = 0; x < w; x++) { + dst[4 * x] = src[3 * x]; + dst[4 * x + 1] = src[3 * x + 1]; + dst[4 * x + 2] = src[3 * x + 2]; + dst[4 * x + 3] = 255; + } + } + } + *rgba = out; + *width = w; + *height = h; + ok = true; +done: + av_frame_free(&frame); + av_packet_free(&pkt); + avcodec_free_context(&ctx); + return ok; +} diff --git a/Linux/src/iframe.h b/Linux/src/iframe.h index c23a0cf..72c246c 100644 --- a/Linux/src/iframe.h +++ b/Linux/src/iframe.h @@ -22,6 +22,7 @@ enum { MSG_PONG = 0x05, MSG_AUTH_FAILED = 0x06, MSG_TEXT_FOCUS = 0x07, + MSG_CURSOR = 0x08, // hotspot x, y, size w, h (u16, Mac points), then a PNG at 2x // client -> host MSG_HELLO = 0x10, MSG_MOUSE_MOVE = 0x11, @@ -78,6 +79,9 @@ bool json_string(const char *json, size_t len, const char *key, char *out, size_ bool json_bool(const char *json, size_t len, const char *key, bool *out); void json_escape(const char *in, char *out, size_t cap); +// cursor.c — the Mac's cursor shape arrives as a PNG; decodes it to RGBA (caller frees). +bool cursor_decode_png(const uint8_t *png, size_t len, uint8_t **rgba, int *width, int *height); + // discover.c — Bonjour browsing via avahi-browse. typedef struct { char name[128]; diff --git a/Linux/src/main.c b/Linux/src/main.c index 738743b..5d97344 100644 --- a/Linux/src/main.c +++ b/Linux/src/main.c @@ -59,7 +59,7 @@ static struct { .scroll_speed = 1, }; -static Uint32 EV_FRAME, EV_WELCOME, EV_STATUS, EV_STATS, EV_FATAL; +static Uint32 EV_FRAME, EV_WELCOME, EV_STATUS, EV_STATS, EV_FATAL, EV_CURSOR; static atomic_bool quitting; @@ -90,6 +90,15 @@ static char decoder_name[96]; static atomic_int request_w, request_h; // display size to ask for (drawable pixels) static int max_fps = 60; +// The Mac's current cursor shape (under state_lock). Hosts that send it capture without the +// cursor, so we draw it as the system pointer here: it then moves with no network delay. +static struct { + uint8_t *rgba; // PNG decoded, at 2x Mac points; NULL until the host sends one + int px_w, px_h; + int hot_x, hot_y; // Mac points + int pt_w, pt_h; +} mac_cursor; + // MARK: - Sending static bool send_msg(uint8_t type, const void *payload, uint32_t len) { @@ -148,10 +157,11 @@ static void send_hello(void) { gethostname(host, sizeof host - 1); json_escape(opt.pin, pin, sizeof pin); json_escape(host, name, sizeof name); - char json[512]; + char json[640]; + // localCursor: the host sends cursor shapes and leaves the pointer out of the video. int n = snprintf(json, sizeof json, "{\"version\":%d,\"pin\":\"%s\",\"name\":\"%s\",\"os\":\"linux\",\"supportsHEVC\":%s,\"maxFPS\":%d," - "\"display\":{\"width\":%d,\"height\":%d,\"uiScale\":%g}}", + "\"localCursor\":true,\"display\":{\"width\":%d,\"height\":%d,\"uiScale\":%g}}", IFRAME_PROTOCOL_VERSION, pin, name, opt.h264_only ? "false" : "true", max_fps, atomic_load(&request_w), atomic_load(&request_h), opt.scale); send_msg(MSG_HELLO, json, n); @@ -262,6 +272,24 @@ static void handle_welcome(const uint8_t *p, uint32_t len) { push_event(EV_WELCOME); } +static void handle_cursor(const uint8_t *p, uint32_t len) { + if (len <= 8) return; + uint8_t *rgba; + int w, h; + if (!cursor_decode_png(p + 8, len - 8, &rgba, &w, &h)) return; + SDL_LockMutex(state_lock); + free(mac_cursor.rgba); + mac_cursor.rgba = rgba; + mac_cursor.px_w = w; + mac_cursor.px_h = h; + mac_cursor.hot_x = p[0] << 8 | p[1]; + mac_cursor.hot_y = p[2] << 8 | p[3]; + mac_cursor.pt_w = p[4] << 8 | p[5]; + mac_cursor.pt_h = p[6] << 8 | p[7]; + SDL_UnlockMutex(state_lock); + push_event(EV_CURSOR); +} + static void handle_stats(const uint8_t *p, uint32_t len) { const char *j = (const char *)p; HostStats s = {0}; @@ -335,6 +363,7 @@ static int network_thread(void *unused) { case MSG_FORMAT: handle_format(dec, body, len); break; case MSG_FRAME: handle_frame(dec, body, len, &last_keyframe_request); break; case MSG_STATS: handle_stats(body, len); break; + case MSG_CURSOR: handle_cursor(body, len); break; case MSG_PONG: if (len >= 8) { SDL_LockMutex(state_lock); @@ -354,6 +383,8 @@ static int network_thread(void *unused) { decoder_free(dec); SDL_LockMutex(state_lock); have_welcome = false; + free(mac_cursor.rgba); // the next host may draw the cursor into the video itself + mac_cursor.rgba = NULL; SDL_UnlockMutex(state_lock); SDL_LockMutex(frame_lock); av_frame_unref(pending_frame); @@ -585,6 +616,76 @@ static void render(void) { SDL_RenderPresent(renderer); } +// MARK: - Cursor + +static SDL_Cursor *shown_cursor; + +/// Shows the Mac's cursor shape as our own pointer, sized to how big the Mac's screen appears +/// in the window. Without a shape from the host (older host, Linux host, or not connected) the +/// pointer stays hidden, because the cursor is in the video. +static void apply_cursor(void) { + SDL_LockMutex(state_lock); + double point_w = have_welcome && welcome.width > 0 ? welcome.point_width : 0; + int stream_w = welcome.width, stream_h = welcome.height; + SDL_Surface *src = NULL; + int hot_x = 0, hot_y = 0, pt_w = 0, pt_h = 0; + if (mac_cursor.rgba && point_w > 0 && stream_h > 0) { + src = SDL_CreateRGBSurfaceWithFormat(0, mac_cursor.px_w, mac_cursor.px_h, 32, SDL_PIXELFORMAT_RGBA32); + if (src) { + for (int y = 0; y < src->h; y++) + memcpy((uint8_t *)src->pixels + (size_t)y * src->pitch, mac_cursor.rgba + (size_t)y * src->w * 4, + (size_t)src->w * 4); + } + hot_x = mac_cursor.hot_x; + hot_y = mac_cursor.hot_y; + pt_w = mac_cursor.pt_w; + pt_h = mac_cursor.pt_h; + } + SDL_UnlockMutex(state_lock); + + if (!src) { + if (shown_cursor) { + SDL_SetCursor(SDL_GetDefaultCursor()); + SDL_FreeCursor(shown_cursor); + shown_cursor = NULL; + } + SDL_ShowCursor(opt.local_cursor ? SDL_ENABLE : SDL_DISABLE); + return; + } + + // X11 cursors are in device pixels; Wayland scales cursor surfaces like window content. + int w, h; + const char *driver = SDL_GetCurrentVideoDriver(); + if (driver && !strcmp(driver, "wayland")) SDL_GetWindowSize(window, &w, &h); + else SDL_GetRendererOutputSize(renderer, &w, &h); + float fit = fminf((float)w / stream_w, (float)h / stream_h); + float scale = stream_w * fit / (float)point_w; // cursor pixels per Mac point + int cw = (int)lroundf(fminf(pt_w * scale, 256)), ch = (int)lroundf(fminf(pt_h * scale, 256)); + if (cw < 1) cw = 1; + if (ch < 1) ch = 1; + + SDL_Surface *scaled = SDL_CreateRGBSurfaceWithFormat(0, cw, ch, 32, SDL_PIXELFORMAT_RGBA32); + SDL_Cursor *cursor = NULL; + if (scaled) { +#if SDL_VERSION_ATLEAST(2, 0, 16) + if (SDL_SoftStretchLinear(src, NULL, scaled, NULL) != 0) +#endif + { + SDL_SetSurfaceBlendMode(src, SDL_BLENDMODE_NONE); + SDL_BlitScaled(src, NULL, scaled, NULL); + } + int hx = (int)lroundf(hot_x * scale), hy = (int)lroundf(hot_y * scale); + cursor = SDL_CreateColorCursor(scaled, hx < cw ? hx : cw - 1, hy < ch ? hy : ch - 1); + SDL_FreeSurface(scaled); + } + SDL_FreeSurface(src); + if (!cursor) return; + SDL_SetCursor(cursor); + if (shown_cursor) SDL_FreeCursor(shown_cursor); + shown_cursor = cursor; + SDL_ShowCursor(SDL_ENABLE); +} + static void show_pending_frame(void) { SDL_LockMutex(frame_lock); bool got = frame_pending; @@ -680,7 +781,8 @@ static void usage(FILE *f) { " (on a Linux host every key maps 1:1 unless this is given)\n" " --scroll-speed X wheel multiplier (default 1)\n" " --invert-scroll reverse wheel direction\n" - " --local-cursor show the Linux cursor over the stream too\n" + " --local-cursor show the Linux pointer even when the host draws the cursor into the\n" + " video (Macs send their cursor shape and don't need this)\n" " --no-hw software decoding only\n" " --h264 ask for H.264 instead of HEVC\n" " --vsync sync presentation to the monitor (smoother, adds latency)\n" @@ -815,12 +917,13 @@ int main(int argc, char **argv) { return 1; } SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_BT709); - Uint32 base = SDL_RegisterEvents(5); + Uint32 base = SDL_RegisterEvents(6); EV_FRAME = base; EV_WELCOME = base + 1; EV_STATUS = base + 2; EV_STATS = base + 3; EV_FATAL = base + 4; + EV_CURSOR = base + 5; Uint32 flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; if (opt.fullscreen) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; @@ -882,6 +985,9 @@ int main(int argc, char **argv) { av_frame_unref(shown_frame); render(); } + apply_cursor(); + } else if (e.type == EV_CURSOR) { + apply_cursor(); update_title(false); } else if (e.type == EV_STATUS) { update_title(false); @@ -892,7 +998,7 @@ int main(int argc, char **argv) { running = false; } else if (e.type == SDL_WINDOWEVENT) { switch (e.window.event) { - case SDL_WINDOWEVENT_SIZE_CHANGED: resize_at = SDL_GetTicks(); render(); break; + case SDL_WINDOWEVENT_SIZE_CHANGED: resize_at = SDL_GetTicks(); render(); apply_cursor(); break; case SDL_WINDOWEVENT_EXPOSED: render(); break; case SDL_WINDOWEVENT_FOCUS_LOST: release_all(); break; case SDL_WINDOWEVENT_FOCUS_GAINED: if (keyboard_grab) set_keyboard_grab(true); break; @@ -946,6 +1052,7 @@ int main(int argc, char **argv) { av_frame_free(&pending_frame); av_frame_free(&shown_frame); if (texture) SDL_DestroyTexture(texture); + if (shown_cursor) SDL_FreeCursor(shown_cursor); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); diff --git a/Windows/README.md b/Windows/README.md index a7b8ee0..24d1ebc 100644 --- a/Windows/README.md +++ b/Windows/README.md @@ -62,7 +62,7 @@ FFmpeg is only needed to regenerate that fixture. Real Mac capture, permission b - Converts four-byte AVCC NAL lengths to Annex B and injects SPS/PPS on keyframes. Frames are drawn directly to a canvas and acknowledged after decode and drawing, preserving the host's flow control. Malformed frames trigger throttled keyframe recovery; repeated decoder errors close the session. - The renderer is sandboxed with context isolation, no Node integration, a restrictive content security policy, and a narrow validated IPC bridge. Socket framing is bounded at the host protocol's 32 MiB maximum. - The host protocol is **not encrypted**. Use a trusted LAN or Tailscale; do not expose the host directly to the Internet. -- No audio, automatic clipboard synchronization, HEVC, or local remote-cursor overlay. The Mac cursor is part of the video. The Send text button is an explicit text transfer, not clipboard sync. +- No audio, automatic clipboard synchronization, or HEVC. (The Mac's cursor is drawn locally as the Windows pointer, in the Mac's current shape.) The Send text button is an explicit text transfer, not clipboard sync. - This is an Electron client, not a native WinUI application. It includes Chromium and therefore has a larger distribution and memory footprint. MIT, matching the repository license. Bundled Electron and dependency licenses are included in the distribution. diff --git a/Windows/src/protocol.cjs b/Windows/src/protocol.cjs index 2425e16..96cfc4d 100644 --- a/Windows/src/protocol.cjs +++ b/Windows/src/protocol.cjs @@ -1,7 +1,7 @@ 'use strict'; const MAX_MESSAGE = 32 << 20; const T = Object.freeze({ welcome: 1, format: 2, frame: 3, stats: 4, pong: 5, - authFailed: 6, textFocus: 7, hello: 16, mouseMove: 17, mouseButton: 18, + authFailed: 6, textFocus: 7, cursor: 8, hello: 16, mouseMove: 17, mouseButton: 18, scroll: 19, key: 20, text: 21, requestKeyframe: 22, ping: 23, ack: 24, display: 25 }); function packet(type, payload = Buffer.alloc(0)) { diff --git a/Windows/src/session.cjs b/Windows/src/session.cjs index 6986b0f..78fecbd 100644 --- a/Windows/src/session.cjs +++ b/Windows/src/session.cjs @@ -24,7 +24,9 @@ class Session extends EventEmitter { this.emit('message', { type, data }); this.emit('state', { phase: 'streaming', welcome: w }); } else if (!welcomed) { - throw new Error('Unexpected message before welcome'); + // The Mac's cursor shape can arrive first; anything else is a protocol error. + if (type !== T.cursor) throw new Error('Unexpected message before welcome'); + this.emit('message', { type, data }); } else if (type === T.pong) { if (data.length !== 8) throw new Error('Invalid ping response'); const elapsed = process.hrtime.bigint() - data.readBigUInt64BE(); @@ -45,7 +47,8 @@ class Session extends EventEmitter { socket.on('close', () => fail('The Mac closed the connection. Check its screen recording permission and host log.')); socket.on('connect', () => { this.send(T.hello, Buffer.from(JSON.stringify({ version: 1, pin: o.pin, name: os.hostname(), supportsHEVC: false, - maxFPS: o.fps, display: { width: o.width, height: o.height, uiScale: o.scale } }))); + // localCursor: the Mac sends its cursor shape and leaves the pointer out of the video. + localCursor: true, maxFPS: o.fps, display: { width: o.width, height: o.height, uiScale: o.scale } }))); this.timer = setInterval(() => { if (!this.ready) return; if ([...this.pending.values()].some(time => Date.now() - time > 10000)) diff --git a/Windows/test/protocol.test.cjs b/Windows/test/protocol.test.cjs index 2d09bd7..392f312 100644 --- a/Windows/test/protocol.test.cjs +++ b/Windows/test/protocol.test.cjs @@ -65,7 +65,11 @@ test('loopback handshake, decode acknowledgements, ping and input', async t => { }; await wait(() => messages.length > 0); const hello = JSON.parse(messages[0].data); assert.equal(hello.pin,'1234'); assert.equal(hello.supportsHEVC,false); - assert.equal(hello.display.uiScale,2); + assert.equal(hello.display.uiScale,2); assert.equal(hello.localCursor,true); + // The Mac sends its cursor shape before the welcome; that must not end the session. + const cursor = once(session,'message'); + peer.write(packet(T.cursor,Buffer.from([0,4,0,2,0,17,0,23,0x89,0x50,0x4e,0x47]))); + assert.equal((await cursor)[0].type,T.cursor); const streaming = once(session,'state'); peer.write(packet(T.welcome,Buffer.from(JSON.stringify({width:1920,height:1080,codec:0})))); assert.equal((await streaming)[0].phase,'streaming'); diff --git a/Windows/ui/app.mjs b/Windows/ui/app.mjs index ecf0a6c..49097e6 100644 --- a/Windows/ui/app.mjs +++ b/Windows/ui/app.mjs @@ -1,5 +1,6 @@ import { Video } from './video.mjs'; import { attachInput } from './input.mjs'; +import { MacCursor } from './cursor.mjs'; const $ = id => document.getElementById(id), api = window.iframe; let active = false, welcome, rtt = 0, hostStats = {}, frames = 0, decodeTotal = 0; const settings = ['host','port','resolution','fps','scale']; @@ -15,6 +16,8 @@ function saveSettings() { async function fail(message) { await api.disconnect(); status(message); } const video = new Video($('video'), api, fail, ms => { frames++; decodeTotal += ms; $('waiting').hidden = true; }, message => { $('waiting').textContent = message; }); +const macCursor = new MacCursor($('video')); +window.addEventListener('resize', () => macCursor.apply()); const releaseInput = attachInput($('video'), api, { active: () => active, swap: () => $('swap').checked, shortcut: code => { if (code === 'KeyF') toggleFullscreen(); else if (code === 'KeyQ') disconnect(); else $('fullscreen').focus(); } @@ -60,16 +63,20 @@ api.onState(state => { $('host-name').textContent = welcome.hostName; $('stream-info').textContent = `${welcome.width} × ${welcome.height} · H.264 · ${welcome.fps} fps`; $('keyboard-mode').textContent = $('swap').checked ? 'Ctrl → ⌘ Command' : 'Windows → ⌘ Command'; - $('pin').value = ''; $('video').focus(); + $('pin').value = ''; $('video').focus(); macCursor.setWelcome(welcome); } else if (!connecting) { - releaseInput(); active = false; video.close(); welcome = null; frames = 0; decodeTotal = 0; hostStats = {}; rtt = 0; + releaseInput(); active = false; video.close(); macCursor.reset(); welcome = null; frames = 0; decodeTotal = 0; hostStats = {}; rtt = 0; $('text-dialog').close('cancel'); $('connect-page').hidden = false; $('stream-page').hidden = true; status(state.phase === 'failed' ? state.message : ''); $('connect').focus(); } else status('Connecting to your Mac…'); }); api.onMessage(({type, data}) => { try { - if (type === 1) { video.close(); $('waiting').hidden = false; } + if (type === 1) { // also re-sent when the Mac's display is reshaped + video.close(); $('waiting').hidden = false; + welcome = JSON.parse(new TextDecoder().decode(data)); macCursor.setWelcome(welcome); + } + else if (type === 8) macCursor.update(data); else if (type === 2) video.configure(data); else if (type === 3) video.frame(data); else if (type === 4) hostStats = JSON.parse(new TextDecoder().decode(data)); diff --git a/Windows/ui/cursor.mjs b/Windows/ui/cursor.mjs new file mode 100644 index 0000000..d97dd9f --- /dev/null +++ b/Windows/ui/cursor.mjs @@ -0,0 +1,49 @@ +// The Mac's cursor, drawn as our own pointer. Hosts that send cursor shapes leave the cursor out +// of the video, so the pointer moves with Windows' own zero-latency mouse instead of a frame late. +export class MacCursor { + constructor(canvas) { this.canvas = canvas; this.shape = null; this.welcome = null; this.version = 0; } + + /** Payload: hotspot x, y, size w, h (u16 big endian, Mac points), then a PNG at 2x. */ + update(bytes) { + const data = new Uint8Array(bytes); + if (data.length <= 8) return; + const view = new DataView(data.buffer, data.byteOffset, data.byteLength); + const shape = {hotspotX: view.getUint16(0), hotspotY: view.getUint16(2), width: view.getUint16(4), height: view.getUint16(6)}; + const version = ++this.version; + createImageBitmap(new Blob([data.subarray(8)], {type: 'image/png'})).then(bitmap => { + if (version !== this.version) { bitmap.close(); return; } // a newer shape already arrived + this.shape?.bitmap.close(); + this.shape = {...shape, bitmap}; + this.apply(); + }).catch(() => {}); + } + + setWelcome(welcome) { this.welcome = welcome; this.apply(); } + + reset() { + this.version++; this.shape?.bitmap.close(); this.shape = null; this.welcome = null; + this.canvas.style.cursor = ''; // back to the stylesheet: hidden while the cursor is in the video + } + + /** Scales the shape to how big the Mac's screen appears in the window, as on the Mac itself. */ + apply() { + const {shape, welcome, canvas} = this; + if (!shape || !welcome?.pointWidth || !welcome.width || !welcome.height) return; + const rect = canvas.getBoundingClientRect(); + if (!rect.width || !rect.height) return; + const shownWidth = welcome.width * Math.min(rect.width / welcome.width, rect.height / welcome.height); + const scale = shownWidth / welcome.pointWidth; // CSS px per Mac point + // Chromium ignores cursor images over 128 CSS px. + const cssW = Math.max(1, Math.min(shape.width * scale, 128)), cssH = Math.max(1, Math.min(shape.height * scale, 128)); + const render = ratio => { + const c = document.createElement('canvas'); + c.width = Math.max(1, Math.round(cssW * ratio)); c.height = Math.max(1, Math.round(cssH * ratio)); + const g = c.getContext('2d'); g.imageSmoothingQuality = 'high'; g.drawImage(shape.bitmap, 0, 0, c.width, c.height); + return c.toDataURL('image/png'); + }; + const hx = Math.min(Math.round(shape.hotspotX * scale), Math.round(cssW) - 1); + const hy = Math.min(Math.round(shape.hotspotY * scale), Math.round(cssH) - 1); + const ratio = window.devicePixelRatio || 1; + canvas.style.cursor = `image-set(url("${render(ratio)}") ${ratio}x) ${hx} ${hy}, url("${render(1)}") ${hx} ${hy}, default`; + } +} From b9e7d0f8f4cd8396b03896a32db0e031ef25ab75 Mon Sep 17 00:00:00 2001 From: sciamop <17859+sciamop@users.noreply.github.com> Date: Tue, 6 Oct 2026 08:55:42 -0400 Subject: [PATCH 5/5] Add a native Universal (Apple silicon + Intel) Mac client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New iFrameMac target (MacClient/) built by scripts/build-mac-client.sh into dist/. The host's virtual display follows the window size and full screen; mouse, trackpad scrolling and every key combo pass through (except ⌃⌥⌘ app shortcuts); the remote cursor shape becomes the window's real NSCursor. StreamSession, HostBrowser, SavedHost, theme and stats overlay move to ClientShared/ so the iPad and Mac apps share them. The iPad app also gets the local cursor overlay. Co-Authored-By: Claude Opus 5.5 --- .gitignore | 3 + Client/ConnectView.swift | 80 ++-- Client/IFrameApp.swift | 34 +- Client/StreamView.swift | 222 +++++++++-- ClientShared/HostBrowser.swift | 33 ++ ClientShared/SavedHost.swift | 22 ++ ClientShared/StatsView.swift | 27 ++ ClientShared/StreamSession.swift | 52 ++- ClientShared/Theme.swift | 37 ++ .../AccentColor.colorset/Contents.json | 3 + .../AppIcon.appiconset/Contents.json | 68 ++++ .../AppIcon.appiconset/icon_128x128.png | Bin 0 -> 9095 bytes .../AppIcon.appiconset/icon_128x128@2x.png | Bin 0 -> 22729 bytes .../AppIcon.appiconset/icon_16x16.png | Bin 0 -> 772 bytes .../AppIcon.appiconset/icon_16x16@2x.png | Bin 0 -> 1575 bytes .../AppIcon.appiconset/icon_256x256.png | Bin 0 -> 22729 bytes .../AppIcon.appiconset/icon_256x256@2x.png | Bin 0 -> 58477 bytes .../AppIcon.appiconset/icon_32x32.png | Bin 0 -> 1575 bytes .../AppIcon.appiconset/icon_32x32@2x.png | Bin 0 -> 3517 bytes .../AppIcon.appiconset/icon_512x512.png | Bin 0 -> 58477 bytes .../AppIcon.appiconset/icon_512x512@2x.png | Bin 0 -> 71690 bytes MacClient/Assets.xcassets/Contents.json | 1 + .../IFrameMark.imageset/Contents.json | 2 + .../IFrameMark.imageset/iframe-mark.png | Bin 0 -> 8050 bytes MacClient/IFrameMacApp.swift | 213 +++++++++++ MacClient/Info.plist | 34 ++ MacClient/MacConnectView.swift | 325 ++++++++++++++++ MacClient/MacStreamView.swift | 358 ++++++++++++++++++ README.md | 30 +- project.yml | 36 ++ scripts/build-icons.sh | 17 +- scripts/build-mac-client.sh | 49 +++ 32 files changed, 1514 insertions(+), 132 deletions(-) create mode 100644 ClientShared/HostBrowser.swift create mode 100644 ClientShared/SavedHost.swift create mode 100644 ClientShared/StatsView.swift create mode 100644 ClientShared/Theme.swift create mode 100644 MacClient/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 MacClient/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 MacClient/Assets.xcassets/AppIcon.appiconset/icon_128x128.png create mode 100644 MacClient/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png create mode 100644 MacClient/Assets.xcassets/AppIcon.appiconset/icon_16x16.png create mode 100644 MacClient/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png create mode 100644 MacClient/Assets.xcassets/AppIcon.appiconset/icon_256x256.png create mode 100644 MacClient/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png create mode 100644 MacClient/Assets.xcassets/AppIcon.appiconset/icon_32x32.png create mode 100644 MacClient/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png create mode 100644 MacClient/Assets.xcassets/AppIcon.appiconset/icon_512x512.png create mode 100644 MacClient/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png create mode 100644 MacClient/Assets.xcassets/Contents.json create mode 100644 MacClient/Assets.xcassets/IFrameMark.imageset/Contents.json create mode 100644 MacClient/Assets.xcassets/IFrameMark.imageset/iframe-mark.png create mode 100644 MacClient/IFrameMacApp.swift create mode 100644 MacClient/Info.plist create mode 100644 MacClient/MacConnectView.swift create mode 100644 MacClient/MacStreamView.swift create mode 100755 scripts/build-mac-client.sh diff --git a/.gitignore b/.gitignore index b3a0594..e01123f 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ Windows/test-output/ # Linux client Linux/build/ LinuxHost/build/ + +# Mac client +dist/ diff --git a/Client/ConnectView.swift b/Client/ConnectView.swift index a6c44a6..55b74fa 100644 --- a/Client/ConnectView.swift +++ b/Client/ConnectView.swift @@ -4,12 +4,6 @@ import SwiftUI // Visual language shared with Pinry Saver: one centered column (max 480 pt) on the system // background, a big mark up top, labeled rounded fields, and a single pill-shaped action. -extension Color { - static let iframeTeal = Color(red: 0x2E / 255, green: 0xE6 / 255, blue: 0xD6 / 255) // #2EE6D6 - /// Text on teal: the accent is light, so dark text reads better than white. - static let iframeInk = Color(red: 0x04 / 255, green: 0x16 / 255, blue: 0x1A / 255) -} - struct ConnectView: View { @EnvironmentObject private var session: StreamSession @StateObject private var browser = HostBrowser() @@ -17,6 +11,8 @@ struct ConnectView: View { @AppStorage("manualHost") private var manualHost = "" @AppStorage("uiScale") private var uiScale = 2.0 @AppStorage("lastHostName") private var lastHostName = "" + @AppStorage("savedHosts") private var savedHostsJSON = "[]" + @State private var pendingSave: SavedHost? @State private var selectedHostID: String? @State private var showingError = false @State private var errorMessage = "" @@ -27,6 +23,13 @@ struct ConnectView: View { private var trimmedAddress: String { manualHost.trimmingCharacters(in: .whitespaces) } + private var savedHosts: [SavedHost] { + get { (try? JSONDecoder().decode([SavedHost].self, from: Data(savedHostsJSON.utf8))) ?? [] } + nonmutating set { + savedHostsJSON = (try? String(decoding: JSONEncoder().encode(newValue), as: UTF8.self)) ?? "[]" + } + } + private var targetName: String? { if !trimmedAddress.isEmpty { return trimmedAddress } return selectedHost?.name @@ -69,6 +72,11 @@ struct ConnectView: View { selectedHostID = (hosts.first { $0.name == lastHostName } ?? (hosts.count == 1 ? hosts.first : nil))?.id } .onChange(of: session.phase) { _, phase in + // Only bookmark addresses that actually worked, most recent first. + if phase == .streaming, let saved = pendingSave { + savedHosts = [saved] + savedHosts.filter { $0.address != saved.address } + } + if phase != .connecting { pendingSave = nil } if case .failed(let message) = phase { errorMessage = message showingError = true @@ -105,7 +113,7 @@ struct ConnectView: View { VStack(alignment: .leading, spacing: 24) { VStack(alignment: .leading, spacing: 8) { FieldLabel("Mac:") - if browser.hosts.isEmpty { + if browser.hosts.isEmpty && savedHosts.isEmpty { HStack(spacing: 10) { ProgressView() Text("Looking for Macs running iframe-host…") @@ -123,6 +131,19 @@ struct ConnectView: View { manualHost = "" } } + ForEach(savedHosts) { saved in + HostRow(name: saved.address, symbol: "bookmark", + selected: saved.address == trimmedAddress) { + manualHost = saved.address + pin = saved.pin + } + .contextMenu { + Button("Forget \(saved.address)", systemImage: "trash", role: .destructive) { + savedHosts = savedHosts.filter { $0.address != saved.address } + if trimmedAddress == saved.address { manualHost = "" } + } + } + } } } } @@ -220,15 +241,9 @@ struct ConnectView: View { private func connect() { guard canConnect else { return } if !trimmedAddress.isEmpty { - var host = trimmedAddress - var port = IFrame.defaultPort - if let colon = host.lastIndex(of: ":"), !host.contains("::"), - let p = UInt16(host[host.index(after: colon)...]) { - port = p - host = String(host[.. Void var body: some View { Button(action: action) { HStack(spacing: 12) { - Image(systemName: "macmini") + Image(systemName: symbol) .font(.system(size: 17)) .foregroundColor(selected ? .iframeTeal : Color(uiColor: .secondaryLabel)) Text(name) @@ -308,33 +324,3 @@ private extension View { ) } } - -/// The cursor mark streaking up and to the right, with a short motion trail -/// (iFrame's take on Pinry's falling-pin loader). -struct IFrameLoadingView: View { - let size: CGFloat - @State private var progress: CGFloat = -1 - - var body: some View { - ZStack { - ForEach(0..<3, id: \.self) { index in - Image("IFrameMark") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: size * 0.7, height: size * 0.7) - .opacity(1.0 - CGFloat(index) * 0.3) - .offset(x: (progress - CGFloat(index) * 0.18) * size, - y: -(progress - CGFloat(index) * 0.18) * size) - } - } - .frame(width: size, height: size) - .clipped() - .drawingGroup() - .onAppear { - progress = -1 - withAnimation(.easeInOut(duration: 0.6).repeatForever(autoreverses: false)) { - progress = 1 - } - } - } -} diff --git a/Client/IFrameApp.swift b/Client/IFrameApp.swift index a090ec1..f28dad4 100644 --- a/Client/IFrameApp.swift +++ b/Client/IFrameApp.swift @@ -1,5 +1,6 @@ import Network import SwiftUI +import UIKit @main struct IFrameApp: App { @@ -24,32 +25,15 @@ struct IFrameApp: App { } } -final class HostBrowser: ObservableObject { - struct Found: Identifiable, Hashable { - let id: String - let name: String - let endpoint: NWEndpoint +extension StreamSession { + /// The iPad's screen in pixels, in the current orientation. + static func screenPixels() -> CGSize { + let screen = UIScreen.main + return CGSize(width: screen.bounds.width * screen.scale, height: screen.bounds.height * screen.scale) } - @Published private(set) var hosts: [Found] = [] - private var browser: NWBrowser? - - func start() { - guard browser == nil else { return } - let browser = NWBrowser(for: .bonjour(type: IFrame.serviceType, domain: nil), using: .tcp) - browser.browseResultsChangedHandler = { [weak self] results, _ in - let found = results.compactMap { result -> Found? in - guard case let .service(name, _, _, _) = result.endpoint else { return nil } - return Found(id: "\(result.endpoint)", name: name, endpoint: result.endpoint) - } - self?.hosts = found.sorted { $0.name < $1.name } - } - browser.start(queue: .main) - self.browser = browser - } - - func stop() { - browser?.cancel() - browser = nil + func connect(to endpoint: NWEndpoint, pin: String, label: String, uiScale: Double) { + connect(to: endpoint, pin: pin, label: label, deviceName: UIDevice.current.name, + maxFPS: UIScreen.main.maximumFramesPerSecond, pixels: Self.screenPixels(), uiScale: uiScale) } } diff --git a/Client/StreamView.swift b/Client/StreamView.swift index f599e70..8d2391f 100644 --- a/Client/StreamView.swift +++ b/Client/StreamView.swift @@ -9,12 +9,18 @@ import UIKit /// scrolling map 1:1 to the Mac. Touch: tap = click, two-finger tap = right click, /// drag = move pointer, long-press then drag = click-drag, two-finger drag = scroll, /// three-finger tap = toolbar. +/// +/// Fingers work in one of two modes. Direct: the pointer jumps to your finger. Trackpad (like +/// Microsoft Remote Desktop's mouse mode): drags move the pointer relatively with acceleration, +/// a flick lets it glide to a stop, and taps click wherever the pointer is. Pencil is always direct. final class StreamUIView: UIView, UIPointerInteractionDelegate { override class var layerClass: AnyClass { AVSampleBufferDisplayLayer.self } private var displayLayer: AVSampleBufferDisplayLayer { layer as! AVSampleBufferDisplayLayer } let session: StreamSession - var welcome: Welcome? + var welcome: Welcome? { + didSet { if welcome != oldValue { layoutCursor() } } + } var onThreeFingerTap: (() -> Void)? private let keyboardProxy = KeyboardProxy() @@ -27,6 +33,19 @@ final class StreamUIView: UIView, UIPointerInteractionDelegate { private var focusRect: CGRect? // normalized, while a Mac text field is focused private var keyboardTop: CGFloat? // in view coordinates, while the on-screen keyboard is up + var trackpadMode = true + private var cursor = CGPoint(x: 0.5, y: 0.5) { // normalized, where we last put the Mac pointer + didSet { positionCursor() } + } + /// The Mac's pointer, drawn here so it moves the instant you do; capture leaves it out of the video. + private let cursorLayer = CALayer() + private var macCursor: MacCursor? + private var touchIsPencil = false + private var lastDragLocation: CGPoint? + private var lastDragTime: CFTimeInterval = 0 + private var glideVelocity = CGPoint.zero // view points per second + private var glideLink: CADisplayLink? + init(session: StreamSession) { self.session = session super.init(frame: .zero) @@ -34,11 +53,15 @@ final class StreamUIView: UIView, UIPointerInteractionDelegate { isMultipleTouchEnabled = true displayLayer.videoGravity = .resizeAspect addInteraction(UIPointerInteraction(delegate: self)) + cursorLayer.isHidden = true // until the Mac sends a shape (older hosts draw it in the video) + cursorLayer.zPosition = 1 + layer.addSublayer(cursorLayer) keyboardProxy.session = session addSubview(keyboardProxy) setUpGestures() session.attach(renderer: displayLayer.sampleBufferRenderer) session.onTextFocus = { [weak self] editable, rect in self?.textFocusChanged(editable, rect) } + session.onCursor = { [weak self] cursor in self?.cursorChanged(cursor) } NotificationCenter.default.addObserver(self, selector: #selector(keyboardFrameChanged(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), @@ -102,6 +125,7 @@ final class StreamUIView: UIView, UIPointerInteractionDelegate { DispatchQueue.main.async { self.becomeFirstResponder() } } else { releaseKeys() + stopGlide() // the display link retains us } } @@ -112,6 +136,7 @@ final class StreamUIView: UIView, UIPointerInteractionDelegate { /// When the iPad rotates, reshape the Mac's virtual display to match instead of letterboxing. override func layoutSubviews() { super.layoutSubviews() + layoutCursor() guard let welcome, welcome.isVirtual, bounds.width > 0, bounds.height > 0 else { return } let viewIsLandscape = bounds.width > bounds.height let streamIsLandscape = welcome.width > welcome.height @@ -190,7 +215,113 @@ final class StreamUIView: UIView, UIPointerInteractionDelegate { @objc private func handleHover(_ g: UIHoverGestureRecognizer) { guard g.state == .began || g.state == .changed else { return } - session.mouseMove(normalized(g.location(in: self))) + moveCursor(to: normalized(g.location(in: self))) + } + + // MARK: Local cursor + + private func cursorChanged(_ cursor: MacCursor) { + macCursor = cursor + CATransaction.begin() + CATransaction.setDisableActions(true) + cursorLayer.contents = cursor.image + cursorLayer.contentsScale = 2 + cursorLayer.isHidden = false + CATransaction.commit() + layoutCursor() + } + + /// Sizes the pointer like it would be on the Mac: Mac points scaled to the video on screen. + private func layoutCursor() { + guard let macCursor, let welcome, welcome.pointWidth > 0 else { return } + let scale = videoRect.width / CGFloat(welcome.pointWidth) // view points per Mac point + CATransaction.begin() + CATransaction.setDisableActions(true) + cursorLayer.bounds = CGRect(x: 0, y: 0, width: macCursor.size.width * scale, height: macCursor.size.height * scale) + cursorLayer.anchorPoint = CGPoint(x: macCursor.size.width > 0 ? macCursor.hotspot.x / macCursor.size.width : 0, + y: macCursor.size.height > 0 ? macCursor.hotspot.y / macCursor.size.height : 0) + CATransaction.commit() + positionCursor() + } + + private func positionCursor() { + guard macCursor != nil else { return } + let rect = videoRect + CATransaction.begin() + CATransaction.setDisableActions(true) + cursorLayer.position = CGPoint(x: rect.minX + cursor.x * rect.width, y: rect.minY + cursor.y * rect.height) + CATransaction.commit() + } + + /// Fingers in trackpad mode act on the pointer, not on the spot they touch. + private var relative: Bool { trackpadMode && !touchIsPencil } + + private func moveCursor(to p: CGPoint) { + cursor = p + session.mouseMove(p) + } + + /// Moves the pointer by a distance in view points. + private func moveCursor(by d: CGPoint) { + let rect = videoRect + guard rect.width > 0, rect.height > 0 else { return } + moveCursor(to: CGPoint(x: min(max(cursor.x + d.x / rect.width, 0), 1), + y: min(max(cursor.y + d.y / rect.height, 0), 1))) + } + + /// Pointer acceleration: slow drags stay precise, fast ones cover the screen. + private func gain(forSpeed speed: CGFloat) -> CGFloat { + let t = min(max((speed - 80) / 1400, 0), 1) + return 1 + 2.2 * t * t * (3 - 2 * t) + } + + private func clickTarget(_ g: UIGestureRecognizer) -> CGPoint { + relative ? cursor : normalized(g.location(in: self)) + } + + // MARK: Flick glide + + private func startGlide(_ velocity: CGPoint) { + let speed = hypot(velocity.x, velocity.y) + guard speed > 350 else { return } + let g = gain(forSpeed: speed) + glideVelocity = CGPoint(x: velocity.x * g, y: velocity.y * g) + if glideLink == nil { + let link = CADisplayLink(target: self, selector: #selector(glideStep(_:))) + link.preferredFrameRateRange = CAFrameRateRange(minimum: 60, maximum: 120, preferred: 120) + link.add(to: .main, forMode: .common) + glideLink = link + } + } + + @objc private func glideStep(_ link: CADisplayLink) { + let dt = CGFloat(min(link.targetTimestamp - link.timestamp, 1.0 / 30)) + moveCursor(by: CGPoint(x: glideVelocity.x * dt, y: glideVelocity.y * dt)) + let decay = exp(-dt * 4.5) // ~0.22 s time constant: a quick, smooth ease-out + glideVelocity = CGPoint(x: glideVelocity.x * decay, y: glideVelocity.y * decay) + // Stop when it's crawling or pinned against an edge. + let pinnedX = (cursor.x <= 0 && glideVelocity.x < 0) || (cursor.x >= 1 && glideVelocity.x > 0) + let pinnedY = (cursor.y <= 0 && glideVelocity.y < 0) || (cursor.y >= 1 && glideVelocity.y > 0) + if pinnedX { glideVelocity.x = 0 } + if pinnedY { glideVelocity.y = 0 } + if hypot(glideVelocity.x, glideVelocity.y) < 25 { stopGlide() } + } + + private func stopGlide() { + glideLink?.invalidate() + glideLink = nil + glideVelocity = .zero + } + + /// Relative drag step with acceleration, shared by plain drags and click-drags. + private func relativeDrag(to location: CGPoint, began: Bool) { + let now = CACurrentMediaTime() + defer { lastDragLocation = location; lastDragTime = now } + guard !began, let last = lastDragLocation else { return } + let d = CGPoint(x: location.x - last.x, y: location.y - last.y) + let dt = max(now - lastDragTime, 1.0 / 240) + let g = gain(forSpeed: hypot(d.x, d.y) / CGFloat(dt)) + moveCursor(by: CGPoint(x: d.x * g, y: d.y * g)) } @objc private func handleScroll(_ g: UIPanGestureRecognizer) { @@ -201,11 +332,15 @@ final class StreamUIView: UIView, UIPointerInteractionDelegate { } @objc private func handleTap(_ g: UITapGestureRecognizer) { - session.click(0, at: normalized(g.location(in: self))) + let p = clickTarget(g) + cursor = p + session.click(0, at: p) } @objc private func handleTwoFingerTap(_ g: UITapGestureRecognizer) { - session.click(1, at: normalized(g.location(in: self))) + let p = clickTarget(g) + cursor = p + session.click(1, at: p) } @objc private func handleThreeFingerTap(_ g: UITapGestureRecognizer) { @@ -213,39 +348,65 @@ final class StreamUIView: UIView, UIPointerInteractionDelegate { } @objc private func handleLongPress(_ g: UILongPressGestureRecognizer) { - let p = normalized(g.location(in: self)) + let location = g.location(in: self) switch g.state { case .began: - session.mouseMove(p) - session.mouseButton(0, down: true, at: p) + if relative { + relativeDrag(to: location, began: true) + } else { + moveCursor(to: normalized(location)) + } + session.mouseButton(0, down: true, at: cursor) case .changed: - session.mouseMove(p) + if relative { + relativeDrag(to: location, began: false) + } else { + moveCursor(to: normalized(location)) + } case .ended, .cancelled, .failed: - session.mouseButton(0, down: false, at: p) + session.mouseButton(0, down: false, at: cursor) default: break } } @objc private func handleDrag(_ g: UIPanGestureRecognizer) { - guard g.state == .began || g.state == .changed else { return } - session.mouseMove(normalized(g.location(in: self))) + let location = g.location(in: self) + switch g.state { + case .began, .changed: + if relative { + relativeDrag(to: location, began: g.state == .began) + } else { + moveCursor(to: normalized(location)) + } + case .ended: + if relative { startGlide(g.velocity(in: self)) } + default: + break + } } // MARK: Trackpad / mouse buttons (needs UIApplicationSupportsIndirectInputEvents) override func touchesBegan(_ touches: Set, with event: UIEvent?) { + if let touch = touches.first(where: { $0.type != .indirectPointer }) { + touchIsPencil = touch.type == .pencil + stopGlide() // touching down catches a gliding pointer, like a trackpad + } for touch in touches where touch.type == .indirectPointer { + stopGlide() let button: UInt8 = event?.buttonMask.contains(.secondary) == true ? 1 : 0 pointerButton = button - session.mouseButton(button, down: true, at: normalized(touch.location(in: self))) + let p = normalized(touch.location(in: self)) + cursor = p + session.mouseButton(button, down: true, at: p) } super.touchesBegan(touches, with: event) } override func touchesMoved(_ touches: Set, with event: UIEvent?) { for touch in touches where touch.type == .indirectPointer { - session.mouseMove(normalized(touch.location(in: self))) + moveCursor(to: normalized(touch.location(in: self))) } super.touchesMoved(touches, with: event) } @@ -267,7 +428,7 @@ final class StreamUIView: UIView, UIPointerInteractionDelegate { } } - /// Hide the iPad pointer; the Mac's own cursor is in the video. + /// Hide the iPad pointer; we draw the Mac's own cursor (or it's in the video). func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? { .hidden() } @@ -410,6 +571,7 @@ struct StreamViewRepresentable: UIViewRepresentable { let session: StreamSession let welcome: Welcome? let handle: StreamViewHandle + let trackpadMode: Bool let onThreeFingerTap: () -> Void func makeUIView(context: Context) -> StreamUIView { @@ -424,18 +586,21 @@ struct StreamViewRepresentable: UIViewRepresentable { view.setNeedsLayout() } view.onThreeFingerTap = onThreeFingerTap + view.trackpadMode = trackpadMode } } struct StreamScreen: View { @EnvironmentObject private var session: StreamSession @StateObject private var handle = StreamViewHandle() + @AppStorage("trackpadMode") private var trackpadMode = true @State private var showToolbar = true @State private var showStats = false var body: some View { ZStack(alignment: .top) { - StreamViewRepresentable(session: session, welcome: session.welcome, handle: handle) { + StreamViewRepresentable(session: session, welcome: session.welcome, handle: handle, + trackpadMode: trackpadMode) { withAnimation(.easeOut(duration: 0.15)) { showToolbar.toggle() } } .ignoresSafeArea() @@ -483,6 +648,8 @@ struct StreamScreen: View { private var toolbar: some View { HStack(spacing: 12) { ToolbarCircle(symbol: "keyboard") { handle.view?.toggleSoftwareKeyboard() } + // Lit = trackpad mode (relative pointer with flick glide); off = pointer follows finger. + ToolbarCircle(symbol: "rectangle.and.hand.point.up.left", active: trackpadMode) { trackpadMode.toggle() } ToolbarCircle(symbol: "gauge.with.dots.needle.50percent", active: showStats) { showStats.toggle() } ToolbarCircle(symbol: "xmark") { session.disconnect() } ToolbarCircle(symbol: "chevron.up") { @@ -509,28 +676,3 @@ private struct ToolbarCircle: View { .buttonStyle(.plain) } } - -struct StatsView: View { - let stats: ClientStats - let welcome: Welcome? - - var body: some View { - VStack(alignment: .leading, spacing: 2) { - if let w = welcome { - Text("\(w.hostName) · \(w.width)×\(w.height) \(w.codec.name) @ \(w.fps)") - } - if let h = stats.host { - Text(String(format: "%.0f fps · %.1f Mbps (target %.0f)", h.fps, h.mbps, h.targetMbps)) - Text(String(format: "encode %.1f ms · decode %.1f ms", h.encodeMs, stats.decodeMs)) - Text(String(format: "net RTT %.1f ms · frame ack %.1f ms", stats.rttMs, h.latencyMs)) - if h.dropped > 0 { Text("skipped \(h.dropped) frames (congestion)") } - } else { - Text("waiting for stats…") - } - } - .font(.system(size: 11, design: .monospaced)) - .foregroundStyle(.white) - .padding(8) - .background(.black.opacity(0.6), in: RoundedRectangle(cornerRadius: 8)) - } -} diff --git a/ClientShared/HostBrowser.swift b/ClientShared/HostBrowser.swift new file mode 100644 index 0000000..ddd2997 --- /dev/null +++ b/ClientShared/HostBrowser.swift @@ -0,0 +1,33 @@ +import Combine +import Network + +/// Finds Macs advertising iframe-host over Bonjour. +final class HostBrowser: ObservableObject { + struct Found: Identifiable, Hashable { + let id: String + let name: String + let endpoint: NWEndpoint + } + + @Published private(set) var hosts: [Found] = [] + private var browser: NWBrowser? + + func start() { + guard browser == nil else { return } + let browser = NWBrowser(for: .bonjour(type: IFrame.serviceType, domain: nil), using: .tcp) + browser.browseResultsChangedHandler = { [weak self] results, _ in + let found = results.compactMap { result -> Found? in + guard case let .service(name, _, _, _) = result.endpoint else { return nil } + return Found(id: "\(result.endpoint)", name: name, endpoint: result.endpoint) + } + self?.hosts = found.sorted { $0.name < $1.name } + } + browser.start(queue: .main) + self.browser = browser + } + + func stop() { + browser?.cancel() + browser = nil + } +} diff --git a/ClientShared/SavedHost.swift b/ClientShared/SavedHost.swift new file mode 100644 index 0000000..a10ec1f --- /dev/null +++ b/ClientShared/SavedHost.swift @@ -0,0 +1,22 @@ +import Foundation +import Network + +/// A Mac reached by address rather than Bonjour, remembered after it first connects. +struct SavedHost: Codable, Identifiable, Equatable { + var address: String + var pin: String + var id: String { address } + + /// Parses "host" or "host:port" (default port otherwise). Returns the endpoint and the bare host. + static func endpoint(for address: String) -> (NWEndpoint, String)? { + var host = address + var port = IFrame.defaultPort + if let colon = host.lastIndex(of: ":"), !host.contains("::"), + let p = UInt16(host[host.index(after: colon)...]) { + port = p + host = String(host[.. 0 { Text("skipped \(h.dropped) frames (congestion)") } + } else { + Text("waiting for stats…") + } + } + .font(.system(size: 11, design: .monospaced)) + .foregroundStyle(.white) + .padding(8) + .background(.black.opacity(0.6), in: RoundedRectangle(cornerRadius: 8)) + } +} diff --git a/ClientShared/StreamSession.swift b/ClientShared/StreamSession.swift index 1fbd176..0f69e7b 100644 --- a/ClientShared/StreamSession.swift +++ b/ClientShared/StreamSession.swift @@ -1,9 +1,16 @@ import AVFoundation import Combine +import ImageIO import Network -import UIKit import VideoToolbox +/// The Mac's cursor shape. Hotspot and size are in Mac points; the image is 2x. +struct MacCursor { + var image: CGImage + var hotspot: CGPoint + var size: CGSize +} + struct ClientStats: Equatable { var host: HostStats? var decodeFPS = 0 @@ -12,7 +19,7 @@ struct ClientStats: Equatable { } /// Owns the connection to iframe-host: receives and decodes video straight into the display -/// layer's renderer on one high-priority queue, and sends input. +/// layer's renderer on one high-priority queue, and sends input. Shared by the iPad and Mac apps. final class StreamSession: ObservableObject { enum Phase: Equatable { case idle @@ -30,6 +37,9 @@ final class StreamSession: ObservableObject { /// The rect is normalized over the stream. var onTextFocus: ((Bool, CGRect) -> Void)? + /// Called on the main thread with the Mac's cursor shape whenever it changes. + var onCursor: ((MacCursor) -> Void)? + // Everything below is confined to `queue`. private let queue = DispatchQueue(label: "iframe.client", qos: .userInteractive) private var channel: MessageChannel? @@ -55,18 +65,13 @@ final class StreamSession: ObservableObject { // MARK: Connection - /// Mac points per iPad pixel choice (see DisplayRequest). 0 = use the Mac's own display. + /// Mac points per client pixel choice (see DisplayRequest). 0 = use the Mac's own display. private var uiScale: Double = 2 - /// The current screen in pixels, in the current orientation. - static func screenPixels() -> CGSize { - let screen = UIScreen.main - return CGSize(width: screen.bounds.width * screen.scale, height: screen.bounds.height * screen.scale) - } - - func connect(to endpoint: NWEndpoint, pin: String, label: String, uiScale: Double) { + /// `pixels` is the client's screen (or window) size in pixels. + func connect(to endpoint: NWEndpoint, pin: String, label: String, deviceName: String, maxFPS: Int, + pixels: CGSize, uiScale: Double) { self.uiScale = uiScale - let pixels = Self.screenPixels() hostLabel = label phase = .connecting stats = ClientStats() @@ -74,10 +79,11 @@ final class StreamSession: ObservableObject { let hello = Hello( version: IFrame.protocolVersion, pin: pin.trimmingCharacters(in: .whitespaces), - name: UIDevice.current.name, + name: deviceName, supportsHEVC: VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC), - maxFPS: UIScreen.main.maximumFramesPerSecond, - display: DisplayRequest(width: Int(pixels.width), height: Int(pixels.height), uiScale: uiScale)) + maxFPS: maxFPS, + display: DisplayRequest(width: Int(pixels.width), height: Int(pixels.height), uiScale: uiScale), + localCursor: true) queue.async { [self] in teardown() @@ -107,8 +113,10 @@ final class StreamSession: ObservableObject { } } - /// Asks the host to reshape its virtual display, e.g. after the iPad rotates. - func requestDisplay(pixels: CGSize) { + /// Asks the host to reshape its virtual display, e.g. after the iPad rotates or the Mac window resizes. + /// Pass `uiScale` to change density too. + func requestDisplay(pixels: CGSize, uiScale newScale: Double? = nil) { + if let newScale { uiScale = newScale } guard uiScale > 0, let data = try? JSONEncoder().encode( DisplayRequest(width: Int(pixels.width), height: Int(pixels.height), uiScale: uiScale)) else { return } send(.display, data) @@ -167,6 +175,14 @@ final class StreamSession: ObservableObject { guard let editable = r.u8(), let x = r.f32(), let y = r.f32(), let w = r.f32(), let h = r.f32() else { return } let rect = CGRect(x: CGFloat(x), y: CGFloat(y), width: CGFloat(w), height: CGFloat(h)) DispatchQueue.main.async { self.onTextFocus?(editable != 0, rect) } + case .cursor: + var r = ByteReader(data) + guard let hx = r.u16(), let hy = r.u16(), let w = r.u16(), let h = r.u16(), + let source = CGImageSourceCreateWithData(r.rest() as CFData, nil), + let image = CGImageSourceCreateImageAtIndex(source, 0, nil) else { return } + let cursor = MacCursor(image: image, hotspot: CGPoint(x: Int(hx), y: Int(hy)), + size: CGSize(width: Int(w), height: Int(h))) + DispatchQueue.main.async { self.onCursor?(cursor) } case .stats: hostStats = try? JSONDecoder().decode(HostStats.self, from: data) case .pong: @@ -274,6 +290,10 @@ final class StreamSession: ObservableObject { send(.key, w.data) } + func requestKeyframe() { + send(.requestKeyframe) + } + func tapKey(_ code: UInt16) { key(code, action: .down, mods: []) key(code, action: .up, mods: []) diff --git a/ClientShared/Theme.swift b/ClientShared/Theme.swift new file mode 100644 index 0000000..37d3f19 --- /dev/null +++ b/ClientShared/Theme.swift @@ -0,0 +1,37 @@ +import SwiftUI + +extension Color { + static let iframeTeal = Color(red: 0x2E / 255, green: 0xE6 / 255, blue: 0xD6 / 255) // #2EE6D6 + /// Text on teal: the accent is light, so dark text reads better than white. + static let iframeInk = Color(red: 0x04 / 255, green: 0x16 / 255, blue: 0x1A / 255) +} + +/// The cursor mark streaking up and to the right, with a short motion trail +/// (iFrame's take on Pinry's falling-pin loader). +struct IFrameLoadingView: View { + let size: CGFloat + @State private var progress: CGFloat = -1 + + var body: some View { + ZStack { + ForEach(0..<3, id: \.self) { index in + Image("IFrameMark") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: size * 0.7, height: size * 0.7) + .opacity(1.0 - CGFloat(index) * 0.3) + .offset(x: (progress - CGFloat(index) * 0.18) * size, + y: -(progress - CGFloat(index) * 0.18) * size) + } + } + .frame(width: size, height: size) + .clipped() + .drawingGroup() + .onAppear { + progress = -1 + withAnimation(.easeInOut(duration: 0.6).repeatForever(autoreverses: false)) { + progress = 1 + } + } + } +} diff --git a/MacClient/Assets.xcassets/AccentColor.colorset/Contents.json b/MacClient/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..c1b71b9 --- /dev/null +++ b/MacClient/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,3 @@ +{ "colors" : [ { "idiom" : "universal", "color" : { "color-space" : "srgb", + "components" : { "red" : "0x2E", "green" : "0xE6", "blue" : "0xD6", "alpha" : "1.000" } } } ], + "info" : { "author" : "xcode", "version" : 1 } } diff --git a/MacClient/Assets.xcassets/AppIcon.appiconset/Contents.json b/MacClient/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..396aee9 --- /dev/null +++ b/MacClient/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images": [ + { + "filename": "icon_16x16.png", + "idiom": "mac", + "scale": "1x", + "size": "16x16" + }, + { + "filename": "icon_16x16@2x.png", + "idiom": "mac", + "scale": "2x", + "size": "16x16" + }, + { + "filename": "icon_32x32.png", + "idiom": "mac", + "scale": "1x", + "size": "32x32" + }, + { + "filename": "icon_32x32@2x.png", + "idiom": "mac", + "scale": "2x", + "size": "32x32" + }, + { + "filename": "icon_128x128.png", + "idiom": "mac", + "scale": "1x", + "size": "128x128" + }, + { + "filename": "icon_128x128@2x.png", + "idiom": "mac", + "scale": "2x", + "size": "128x128" + }, + { + "filename": "icon_256x256.png", + "idiom": "mac", + "scale": "1x", + "size": "256x256" + }, + { + "filename": "icon_256x256@2x.png", + "idiom": "mac", + "scale": "2x", + "size": "256x256" + }, + { + "filename": "icon_512x512.png", + "idiom": "mac", + "scale": "1x", + "size": "512x512" + }, + { + "filename": "icon_512x512@2x.png", + "idiom": "mac", + "scale": "2x", + "size": "512x512" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/MacClient/Assets.xcassets/AppIcon.appiconset/icon_128x128.png b/MacClient/Assets.xcassets/AppIcon.appiconset/icon_128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..7d3224539adc0cdd2b3086932a6fbd22f4047c1f GIT binary patch literal 9095 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 NtgQ}zRH23l`yZ$!JWT)q literal 0 HcmV?d00001 diff --git a/MacClient/Assets.xcassets/AppIcon.appiconset/icon_16x16.png b/MacClient/Assets.xcassets/AppIcon.appiconset/icon_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..f7dc361f7532df9490d83f3463ce74f4173dc25d GIT binary patch literal 772 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|6p}rHd>I(3 z)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N$@*HNrE^*Ox&H$mU>RY|mt1 z0kRl@SPF<47?>9@GB5*aMv&M7Cb+D?0%imoq|hX^@Dl?Alar^5V~B;|(#gBCMFK^R z?f*UF=FGbZH7&79i*f`OYjD|VX*&jW{BV!RTG*PAt$O5~Q}jkLZpUpM8(LjwX(S0X z843knN{L+Trv7;s|M|HykC?vva_H{OyT$wN+kUrBZ}=aR)4s2EQLD}5MB6)pk9LM# zT>kUw!2@Cn8L~3L^ZrlZd@s{7!H2`4z~EufooyS~xT4M%%y@mWeP=5BEfx;TTkj0k zn>i+(+M2qgsliYvx`6!&lbQGOO-mO~Z&<#I#pJfG=1rx^|2BBbS=F;SyB|~FP_bO@ zBw^F^HU4~3ZX&0_xAy6;3(I9+o@zL!YO~+ss#fo>mB|u&D@E((C@oNOW?y^Vu>8N^ zug&jQ3Nb$Jbc$w@X<1d)ebwg6e4FMQyQP)G^_+r5rS7;+c)sd{q#x|e|+GHY{gl@73=SBD0(gT>wLV@YW52(OqDFY3`bZOerl+j(ZIlU zc)GrVMGeQvH{v*th?MQk5~k zT-yfEuB+_*nJZY%GM;T&6qCyBzMakU&8_Q=U(|Fe9c!26T{%#5{nl9#t2sS7yOv&^ z@~U>BW7D#Y+S9f++Mnf>UM~F2;AFATvXqEZ8HZmaMK4zV_0Q|`8>vMZXEoVBsB-qE taW}5pbMu=Mr_svmzh9@W3i~6@vd$@?2>>lmHJbnc literal 0 HcmV?d00001 diff --git a/MacClient/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png b/MacClient/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3ae3456f6fe4aab01ea99bf7d2cc10fc46239395 GIT binary patch literal 1575 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 NtgQ}zRH23l`yZ$!JWT)q literal 0 HcmV?d00001 diff --git a/MacClient/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png b/MacClient/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1f64797506a0aa8aade8b2f63f4eb200479f52b1 GIT binary patch literal 58477 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�ZKgm=6R2P%<} zyPI!vP5{;q>xhFl2$*SM?#4IAsGo3Ozz$M^yW##wS|WeU27sG=1stpbJp@wRA{%&c zC$X9zekyr?K`e7W+(ASbKm~pg>1Y%J&I?QCIt1p#YM zq@j)36Q&{r8CheDyd>T*2EE{$Cj74U^X83V)yxvOYcoAiimxt|1UyjW9_v%yo+I3C z1AEUeeSJn@Tx21+@G`DAx#Z#FXzk{PeM66!~IEJCH#z=qHZ-K!IT3_2o&d^Bo& zg5S642+8EZP1W>0JXkEOCmc5*lABKKH;XAW<{1*UztS{H4;OiF3OyKdd;)C-H2 z2Y)t=XcuYxWsE;PtQFdQ1a74HHG~{?38)rsl(g>W>Quh(R)MdGB|kf!^rxX2tH!D@ zZ~9hxEIb~T0M`F8dyv-&7(djPX)$BZ&SCtf0%t?}73r@Z*{W)5vTOJ=vU3XE-F{?3 zD<2ThHnw_xauU7!#=lGN*Pi23U<-1pKFn#mY^kbzNMn)nZlTS;uEE2ay$?|c!Y8`` zM$aLug|d*av_H+%arD>8$z~d77KzX*LsIE4GLJIAMT%a`sM~xnsG8Zcnqdi4#m3u$ zuZcq;BZM&kui0(gVL-Ux_2R)-%q-mar2Xt_zHA1oS5&LGa_(&DJ}P3&Wsk{we9fCD zZs-UXfjvSmIbq5Ae6EMo$`BWs+QEjJjD53r<$FG(3FBOYjc%r>7T+9e6tpqx-PsB6{$*ME{%?h57t4k^ z6%JI2=zmxpwDxKSe10j`=1l;1U6-9ZKyr}r!C^Z?h@urCbo%J&qh(Wvw`Vle=b-H!ZpcthYLn}f?aBTh z6^-AOYha<^4|qfH6G0wm6?LQJSOkvT8pJcUSd%!~ShtZ87J?`1b0>XD7rRzjx+0|( zmVl91r(XvG!IzO`y$vzajzw8JZHjQ$hB;BYrsKgUc7b;T!cPP`7EJTu?}vPH6r>3CX~2G`f@zRRF)Ef!J*c5j{{)V1b8H7S5||Iy zsD>?1hj&|u_I1o!fnWPO-&*LirL4gcvs5=2q(sQP`Y#t8%GjT?$IP7k2#4j=U3g6` zdC>47=jl$uL_UJkP@}(3T*DD1J@-?yDTyZW7NN> zC!^kZ=H_IMHB#xN5buFkhLSSo>0-+Z6rj6_>VNXZ z=cuTR)t&7FRpJgI05)^zXwAzAd2Ul6Rkcf3{igC~7ctls{}{WqOj( zfrW&Dr$tCx-e*n%Cy=K({x(4x_BI@Sc2ic3)O7RD4hy!`|NT~=b!|(A9q&A(ZrFPI zsw2?-fGs>Tq?$3ovQJ64k$C-VNB)bdU*6oQ+a6fH9;QE-OR@!%JA0wW$!#xMrRJK+ zZ{}8d(R6kL-ezd(*wHrCK39`U!qd~5!PZLJ#z0ZzN0<{h@W~*hiCjF6CS4v4B@Z{$ z@jr0EA$B*4YG&@wQSiFNn{E90Y$Tbe!^yVKlw_qoHwyxK6dgMbJ(+X(C%1Cy-1n z#_cA@@*T!oVSj69n!Ah9l!ajfBr;KGw!WxS&b_$LL0-w!`!P19ZQ=>i_@% literal 0 HcmV?d00001 diff --git a/MacClient/Assets.xcassets/AppIcon.appiconset/icon_32x32.png b/MacClient/Assets.xcassets/AppIcon.appiconset/icon_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..3ae3456f6fe4aab01ea99bf7d2cc10fc46239395 GIT binary patch literal 1575 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�ZKgm=6R2P%<} zyPI!vP5{;q>xhFl2$*SM?#4IAsGo3Ozz$M^yW##wS|WeU27sG=1stpbJp@wRA{%&c zC$X9zekyr?K`e7W+(ASbKm~pg>1Y%J&I?QCIt1p#YM zq@j)36Q&{r8CheDyd>T*2EE{$Cj74U^X83V)yxvOYcoAiimxt|1UyjW9_v%yo+I3C z1AEUeeSJn@Tx21+@G`DAx#Z#FXzk{PeM66!~IEJCH#z=qHZ-K!IT3_2o&d^Bo& zg5S642+8EZP1W>0JXkEOCmc5*lABKKH;XAW<{1*UztS{H4;OiF3OyKdd;)C-H2 z2Y)t=XcuYxWsE;PtQFdQ1a74HHG~{?38)rsl(g>W>Quh(R)MdGB|kf!^rxX2tH!D@ zZ~9hxEIb~T0M`F8dyv-&7(djPX)$BZ&SCtf0%t?}73r@Z*{W)5vTOJ=vU3XE-F{?3 zD<2ThHnw_xauU7!#=lGN*Pi23U<-1pKFn#mY^kbzNMn)nZlTS;uEE2ay$?|c!Y8`` zM$aLug|d*av_H+%arD>8$z~d77KzX*LsIE4GLJIAMT%a`sM~xnsG8Zcnqdi4#m3u$ zuZcq;BZM&kui0(gVL-Ux_2R)-%q-mar2Xt_zHA1oS5&LGa_(&DJ}P3&Wsk{we9fCD zZs-UXfjvSmIbq5Ae6EMo$`BWs+QEjJjD53r<$FG(3FBOYjc%r>7T+9e6tpqx-PsB6{$*ME{%?h57t4k^ z6%JI2=zmxpwDxKSe10j`=1l;1U6-9ZKyr}r!C^Z?h@urCbo%J&qh(Wvw`Vle=b-H!ZpcthYLn}f?aBTh z6^-AOYha<^4|qfH6G0wm6?LQJSOkvT8pJcUSd%!~ShtZ87J?`1b0>XD7rRzjx+0|( zmVl91r(XvG!IzO`y$vzajzw8JZHjQ$hB;BYrsKgUc7b;T!cPP`7EJTu?}vPH6r>3CX~2G`f@zRRF)Ef!J*c5j{{)V1b8H7S5||Iy zsD>?1hj&|u_I1o!fnWPO-&*LirL4gcvs5=2q(sQP`Y#t8%GjT?$IP7k2#4j=U3g6` zdC>47=jl$uL_UJkP@}(3T*DD1J@-?yDTyZW7NN> zC!^kZ=H_IMHB#xN5buFkhLSSo>0-+Z6rj6_>VNXZ z=cuTR)t&7FRpJgI05)^zXwAzAd2Ul6Rkcf3{igC~7ctls{}{WqOj( zfrW&Dr$tCx-e*n%Cy=K({x(4x_BI@Sc2ic3)O7RD4hy!`|NT~=b!|(A9q&A(ZrFPI zsw2?-fGs>Tq?$3ovQJ64k$C-VNB)bdU*6oQ+a6fH9;QE-OR@!%JA0wW$!#xMrRJK+ zZ{}8d(R6kL-ezd(*wHrCK39`U!qd~5!PZLJ#z0ZzN0<{h@W~*hiCjF6CS4v4B@Z{$ z@jr0EA$B*4YG&@wQSiFNn{E90Y$Tbe!^yVKlw_qoHwyxK6dgMbJ(+X(C%1Cy-1n z#_cA@@*T!oVSj69n!Ah9l!ajfBr;KGw!WxS&b_$LL0-w!`!P19ZQ=>i_@% literal 0 HcmV?d00001 diff --git a/MacClient/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png b/MacClient/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1cbea3efb4e55fcc128ca3bbf2f808862139bd4a GIT binary patch literal 71690 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

  • 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�r|+)X<|~w$gI)%B#9?m=T7AO7BRl`R4@_o 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 Bool { true } +} + +/// How dense the Mac's virtual display is, relative to this Mac's own screen. +/// 1 = the same size things are here (sharpest); lower = more desktop space; 0 = mirror the Mac's display. +enum MacDensity: Double, CaseIterable, Identifiable { + case match = 1.0 + case more = 0.8 + case most = 0.667 + case mirror = 0 + + var id: Double { rawValue } + static let virtualChoices: [MacDensity] = [.match, .more, .most] + + var label: String { + switch self { + case .match: return "Match this Mac · sharpest" + case .more: return "More space" + case .most: return "Most space" + case .mirror: return "Use the Mac's own display" + } + } + + /// The host's uiScale (pixels per Mac point) for a screen with this backing scale. + func uiScale(backing: CGFloat) -> Double { + rawValue <= 0 ? 0 : max(1, Double(backing) * rawValue) + } +} + +/// The app's single window: sized small for the connect form and large for the stream. +final class MainWindow: ObservableObject { + static let connectSize = CGSize(width: 520, height: 720) + + weak var window: NSWindow? + /// Content size (points) the stream will use, decided when connecting. + private var streamSize: CGSize? + private var connectFrame: NSRect? + + var backingScale: CGFloat { window?.backingScaleFactor ?? NSScreen.main?.backingScaleFactor ?? 2 } + var maxFPS: Int { (window?.screen ?? NSScreen.main)?.maximumFramesPerSecond ?? 60 } + + /// Picks the window size for streaming and returns it in pixels for the host's virtual display. + func prepareStream() -> CGSize { + let size: CGSize + if let window, window.styleMask.contains(.fullScreen) { + size = window.contentLayoutRect.size + } else { + let visible = (window?.screen ?? NSScreen.main)?.visibleFrame.size ?? CGSize(width: 1440, height: 900) + let titleBar = chrome.height + let saved = CGSize(width: UserDefaults.standard.double(forKey: "streamWidth"), + height: UserDefaults.standard.double(forKey: "streamHeight")) + let wanted = saved.width >= 640 && saved.height >= 480 + ? saved + : CGSize(width: visible.width * 0.85, height: visible.height * 0.85 - titleBar) + size = CGSize(width: min(wanted.width, visible.width).rounded(), + height: min(wanted.height, visible.height - titleBar).rounded()) + } + streamSize = size + return CGSize(width: size.width * backingScale, height: size.height * backingScale) + } + + func phaseChanged(from old: StreamSession.Phase, to phase: StreamSession.Phase, session: StreamSession) { + guard let window else { return } + let fullScreen = window.styleMask.contains(.fullScreen) + if phase == .streaming, old != .streaming { + window.contentMinSize = CGSize(width: 640, height: 480) + window.title = session.welcome?.hostName ?? session.hostLabel + guard !fullScreen, let size = streamSize else { return } + connectFrame = window.frame + setContentSize(size, centered: true) + } else if old == .streaming, phase != .streaming { + window.title = "iFrame" + window.contentMinSize = CGSize(width: 420, height: 520) + guard !fullScreen else { return } + let content = window.contentLayoutRect.size + UserDefaults.standard.set(content.width, forKey: "streamWidth") + UserDefaults.standard.set(content.height, forKey: "streamHeight") + if let connectFrame { + window.setFrame(connectFrame, display: true, animate: true) + } else { + setContentSize(Self.connectSize, centered: true) + } + } + } + + /// Mirroring the Mac's own display: shape the window like it. + func welcomeChanged(_ welcome: Welcome) { + guard let window else { return } + window.title = welcome.hostName + guard !welcome.isVirtual, welcome.width > 0, welcome.height > 0 else { + return + } + let aspect = CGSize(width: welcome.width, height: welcome.height) + guard !window.styleMask.contains(.fullScreen) else { return } + let bounds = window.contentLayoutRect.size + let scale = min(bounds.width / aspect.width, bounds.height / aspect.height) + setContentSize(CGSize(width: (aspect.width * scale).rounded(), height: (aspect.height * scale).rounded()), + centered: false) + } + + /// Window frame minus the area SwiftUI lays out in. SwiftUI windows use a full-size content + /// view, so this is the title bar (plus toolbar), not what frameRect(forContentRect:) reports. + private var chrome: CGSize { + guard let window else { return CGSize(width: 0, height: 28) } + return CGSize(width: window.frame.width - window.contentLayoutRect.width, + height: window.frame.height - window.contentLayoutRect.height) + } + + /// Sizes the window so SwiftUI's layout area (below the title bar) is exactly `size`. + private func setContentSize(_ size: CGSize, centered: Bool) { + guard let window else { return } + var frame = NSRect(origin: .zero, size: CGSize(width: size.width + chrome.width, height: size.height + chrome.height)) + let visible = (window.screen ?? NSScreen.main)?.visibleFrame ?? .zero + if centered { + frame.origin = CGPoint(x: visible.midX - frame.width / 2, y: visible.midY - frame.height / 2) + } else { + // Keep the top-left corner where it is. + frame.origin = CGPoint(x: window.frame.minX, y: window.frame.maxY - frame.height) + } + frame.origin.x = max(visible.minX, min(frame.origin.x, visible.maxX - frame.width)) + frame.origin.y = max(visible.minY, min(frame.origin.y, visible.maxY - frame.height)) + window.setFrame(frame, display: true, animate: true) + } +} + +/// Hands the hosting NSWindow to MainWindow. +private struct WindowReader: NSViewRepresentable { + let window: MainWindow + + func makeNSView(context: Context) -> NSView { + let view = NSView() + DispatchQueue.main.async { [weak view] in + window.window = view?.window + view?.window?.tabbingMode = .disallowed + } + return view + } + + func updateNSView(_ view: NSView, context: Context) { + if window.window == nil, let w = view.window { window.window = w } + } +} + +extension StreamSession { + func connect(to endpoint: NWEndpoint, pin: String, label: String, window: MainWindow, density: MacDensity) { + let pixels = window.prepareStream() + connect(to: endpoint, pin: pin, label: label, + deviceName: Host.current().localizedName ?? "Mac", + maxFPS: window.maxFPS, pixels: pixels, + uiScale: density.uiScale(backing: window.backingScale)) + } +} diff --git a/MacClient/Info.plist b/MacClient/Info.plist new file mode 100644 index 0000000..4989e59 --- /dev/null +++ b/MacClient/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + iFrame + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSApplicationCategoryType + public.app-category.utilities + NSBonjourServices + + _iframe._tcp + + NSHumanReadableCopyright + MIT License + NSLocalNetworkUsageDescription + iFrame finds and connects to Macs running iframe-host on your network. + + diff --git a/MacClient/MacConnectView.swift b/MacClient/MacConnectView.swift new file mode 100644 index 0000000..9c60beb --- /dev/null +++ b/MacClient/MacConnectView.swift @@ -0,0 +1,325 @@ +import Network +import SwiftUI + +// The iPad connect screen, adapted for a Mac window: one centered column, the mark up top, +// labeled rounded fields, and a single pill-shaped action. + +struct MacConnectView: View { + @EnvironmentObject private var session: StreamSession + @EnvironmentObject private var window: MainWindow + @StateObject private var browser = HostBrowser() + @AppStorage("pin") private var pin = "" + @AppStorage("manualHost") private var manualHost = "" + @AppStorage("macDensity") private var density = MacDensity.match.rawValue + @AppStorage("lastHostName") private var lastHostName = "" + @AppStorage("savedHosts") private var savedHostsJSON = "[]" + @State private var pendingSave: SavedHost? + @State private var selectedHostID: String? + @State private var showingError = false + @State private var errorMessage = "" + + private var selectedHost: HostBrowser.Found? { + browser.hosts.first { $0.id == selectedHostID } + } + + private var trimmedAddress: String { manualHost.trimmingCharacters(in: .whitespaces) } + + private var savedHosts: [SavedHost] { + get { (try? JSONDecoder().decode([SavedHost].self, from: Data(savedHostsJSON.utf8))) ?? [] } + nonmutating set { + savedHostsJSON = (try? String(decoding: JSONEncoder().encode(newValue), as: UTF8.self)) ?? "[]" + } + } + + private var targetName: String? { + if !trimmedAddress.isEmpty { return trimmedAddress } + return selectedHost?.name + } + + private var canConnect: Bool { + !pin.isEmpty && targetName != nil && session.phase != .connecting + } + + var body: some View { + ZStack { + Color(nsColor: .windowBackgroundColor).ignoresSafeArea() + + ScrollView { + VStack(alignment: .leading, spacing: 0) { + header + fields + connectButton + footer + } + .frame(maxWidth: 440) + .padding(.horizontal, 32) + .frame(maxWidth: .infinity) + } + + if session.phase == .connecting { + connectingCard + } + } + .frame(minWidth: 420, minHeight: 520) + .onAppear { + browser.start() + // `open iFrame.app --args -manualHost -pin -autoConnect YES` connects at launch + // (launch arguments override the saved settings for that run). + if UserDefaults.standard.bool(forKey: "autoConnect"), session.phase == .idle { + UserDefaults.standard.removeObject(forKey: "autoConnect") + DispatchQueue.main.async(execute: connect) + } + } + .onDisappear { browser.stop() } + .onChange(of: browser.hosts) { _, hosts in + // Preselect the Mac used last time, or the only one around. + guard selectedHostID == nil || !hosts.contains(where: { $0.id == selectedHostID }) else { return } + selectedHostID = (hosts.first { $0.name == lastHostName } ?? (hosts.count == 1 ? hosts.first : nil))?.id + } + .onChange(of: session.phase) { _, phase in + // Only bookmark addresses that actually worked, most recent first. + if phase == .streaming, let saved = pendingSave { + savedHosts = [saved] + savedHosts.filter { $0.address != saved.address } + } + if phase != .connecting { pendingSave = nil } + if case .failed(let message) = phase { + errorMessage = message + showingError = true + } + } + .alert("Couldn't connect", isPresented: $showingError) { + Button("OK") {} + } message: { + Text(errorMessage) + } + } + + // MARK: Sections + + private var header: some View { + VStack(spacing: 12) { + Image("IFrameMark") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 112) + Text("iFrame") + .font(.system(size: 30, weight: .bold)) + Text("Another Mac, in a window. Run iframe-host on it to get started.") + .font(.system(size: 11)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + .multilineTextAlignment(.center) + } + .frame(maxWidth: .infinity) + .padding(.top, 36) + .padding(.bottom, 28) + } + + private var fields: some View { + VStack(alignment: .leading, spacing: 20) { + VStack(alignment: .leading, spacing: 8) { + FieldLabel("Mac:") + if browser.hosts.isEmpty && savedHosts.isEmpty { + HStack(spacing: 10) { + ProgressView().controlSize(.small) + Text("Looking for Macs running iframe-host…") + .foregroundColor(Color(nsColor: .secondaryLabelColor)) + } + .font(.system(size: 13)) + .frame(maxWidth: .infinity, alignment: .leading) + .frameFieldBackground() + } else { + VStack(spacing: 6) { + ForEach(browser.hosts) { host in + HostRow(name: host.name, + selected: host.id == selectedHostID && trimmedAddress.isEmpty) { + selectedHostID = host.id + manualHost = "" + } + } + ForEach(savedHosts) { saved in + HostRow(name: saved.address, symbol: "bookmark", + selected: saved.address == trimmedAddress) { + manualHost = saved.address + pin = saved.pin + } + .contextMenu { + Button("Forget \(saved.address)", systemImage: "trash", role: .destructive) { + savedHosts = savedHosts.filter { $0.address != saved.address } + if trimmedAddress == saved.address { manualHost = "" } + } + } + } + } + } + } + + FrameInputField(label: "PIN:", placeholder: "Shown by iframe-host", text: $pin, monospaced: true) + .onSubmit(connect) + + VStack(alignment: .leading, spacing: 8) { + FieldLabel("Mac display:") + Menu { + Picker("Mac display", selection: $density) { + ForEach(MacDensity.allCases) { choice in + Text(choice.label).tag(choice.rawValue) + } + } + .pickerStyle(.inline) + } label: { + Text(MacDensity(rawValue: density)?.label ?? MacDensity.match.label) + .font(.system(size: 13)) + } + .menuStyle(.borderlessButton) + .frameFieldBackground() + Text(density > 0 + ? "The Mac gets a virtual display shaped like this window. Resize or go full screen and it follows." + : "Shows the Mac's own screen, scaled to fit this window.") + .font(.system(size: 11)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + } + + FrameInputField(label: "Or connect by address (optional):", + placeholder: "192.168.1.20, mac-mini.local, Tailscale name", + text: $manualHost) + .onSubmit(connect) + } + } + + private var connectButton: some View { + Button(action: connect) { + Text(targetName.map { "Connect to \($0)" } ?? "Connect") + .lineLimit(1) + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(canConnect ? .iframeInk : Color(nsColor: .secondaryLabelColor)) + .frame(maxWidth: .infinity) + .frame(height: 44) + .background(canConnect ? Color.iframeTeal : Color.gray.opacity(0.3)) + .cornerRadius(22) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .keyboardShortcut(.defaultAction) + .disabled(!canConnect) + .padding(.top, 28) + } + + private var footer: some View { + VStack(spacing: 0) { + Divider().padding(.top, 24) + Text("Default port \(String(IFrame.defaultPort)). Use host:port to override. While streaming, ⌃⌥⌘D disconnects and ⌃⌥⌘F toggles full screen; every other shortcut goes to the remote Mac.") + .font(.system(size: 11)) + .foregroundColor(Color(nsColor: .tertiaryLabelColor)) + .multilineTextAlignment(.center) + .padding(.top, 12) + .padding(.bottom, 20) + } + .frame(maxWidth: .infinity) + } + + private var connectingCard: some View { + ZStack { + Color.black.opacity(0.4).ignoresSafeArea() + VStack(spacing: 20) { + IFrameLoadingView(size: 64) + Text("Connecting to \(session.hostLabel)…") + .font(.system(size: 15, weight: .semibold)) + Button("Cancel") { session.disconnect() } + .keyboardShortcut(.cancelAction) + } + .padding(32) + .background(Color(nsColor: .windowBackgroundColor)) + .cornerRadius(16) + .shadow(radius: 20) + } + .transition(.opacity) + } + + // MARK: Actions + + private func connect() { + guard canConnect else { return } + let choice = MacDensity(rawValue: density) ?? .match + if !trimmedAddress.isEmpty { + guard let (endpoint, host) = SavedHost.endpoint(for: trimmedAddress) else { return } + pendingSave = SavedHost(address: trimmedAddress, pin: pin) + session.connect(to: endpoint, pin: pin, label: host, window: window, density: choice) + } else if let host = selectedHost { + lastHostName = host.name + session.connect(to: host.endpoint, pin: pin, label: host.name, window: window, density: choice) + } + } +} + +// MARK: - Components + +private struct FieldLabel: View { + let text: String + init(_ text: String) { self.text = text } + + var body: some View { + Text(text) + .font(.system(size: 13)) + .foregroundColor(Color(nsColor: .secondaryLabelColor)) + } +} + +private struct FrameInputField: View { + let label: String + let placeholder: String + @Binding var text: String + var monospaced = false + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + FieldLabel(label) + TextField(placeholder, text: $text) + .textFieldStyle(.plain) + .autocorrectionDisabled() + .font(monospaced ? .system(size: 13).monospacedDigit() : .system(size: 13)) + .frameFieldBackground() + } + } +} + +private struct HostRow: View { + let name: String + var symbol = "macmini" + let selected: Bool + let action: () -> Void + + var body: some View { + Button(action: action) { + HStack(spacing: 10) { + Image(systemName: symbol) + .font(.system(size: 15)) + .foregroundColor(selected ? .iframeTeal : Color(nsColor: .secondaryLabelColor)) + Text(name) + .font(.system(size: 13)) + .foregroundColor(.primary) + Spacer() + Image(systemName: selected ? "checkmark.circle.fill" : "circle") + .font(.system(size: 16)) + .foregroundColor(selected ? .iframeTeal : Color(nsColor: .tertiaryLabelColor)) + } + .frameFieldBackground(highlighted: selected) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + } +} + +private extension View { + /// The rounded, hairline-bordered field box used throughout the iPad app. + func frameFieldBackground(highlighted: Bool = false) -> some View { + padding(.horizontal, 12) + .padding(.vertical, 10) + .frame(maxWidth: .infinity, alignment: .leading) + .background(Color(nsColor: .controlBackgroundColor)) + .cornerRadius(10) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(highlighted ? Color.iframeTeal : Color(nsColor: .separatorColor), + lineWidth: highlighted ? 2 : 1) + ) + } +} diff --git a/MacClient/MacStreamView.swift b/MacClient/MacStreamView.swift new file mode 100644 index 0000000..05a8478 --- /dev/null +++ b/MacClient/MacStreamView.swift @@ -0,0 +1,358 @@ +import AppKit +import AVFoundation +import SwiftUI + +/// The video surface plus all input handling. +/// +/// Mouse, trackpad and keyboard map 1:1 to the remote Mac. NSEvent key codes are already macOS +/// virtual key codes, so keys pass straight through. While this view has focus, every key combo +/// (⌘Q, ⌘W, ⌘Space…) goes to the remote Mac except ⌃⌥⌘ ones, which drive this app's Stream menu. +/// The system still keeps ⌘Tab and other shortcuts it handles before apps see them. +/// +/// The Mac's pointer shape arrives as a picture and becomes this window's NSCursor, so the pointer +/// is the real hardware cursor and moves with zero lag. Capture leaves it out of the video. +final class StreamNSView: NSView { + let session: StreamSession + private let displayLayer = AVSampleBufferDisplayLayer() + + var welcome: Welcome? { + didSet { + guard welcome != oldValue else { return } + updateCursor() + scheduleDisplayRequest() + } + } + + /// See MacDensity. Changing it reshapes the virtual display. + var density: Double = 1 { + didSet { if density != oldValue { scheduleDisplayRequest(force: true) } } + } + + private var keyMonitor: Any? + private var heldKeys = Set() + private var heldModifiers = Set() + private var heldButtons = Set() + private var lastPoint = CGPoint(x: 0.5, y: 0.5) + private var macCursor: MacCursor? + private var nsCursor: NSCursor = .arrow + + init(session: StreamSession) { + self.session = session + super.init(frame: .zero) + wantsLayer = true + layerContentsRedrawPolicy = .never + displayLayer.videoGravity = .resizeAspect + displayLayer.backgroundColor = NSColor.black.cgColor + session.attach(renderer: displayLayer.sampleBufferRenderer) + session.onCursor = { [weak self] cursor in + self?.macCursor = cursor + self?.updateCursor() + } + } + + required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") } + + override func makeBackingLayer() -> CALayer { displayLayer } + + override var acceptsFirstResponder: Bool { true } + override func acceptsFirstMouse(for event: NSEvent?) -> Bool { true } + + override func viewDidMoveToWindow() { + super.viewDidMoveToWindow() + let center = NotificationCenter.default + center.removeObserver(self) + if let window { + DispatchQueue.main.async { window.makeFirstResponder(self) } + installKeyMonitor() + center.addObserver(self, selector: #selector(windowResignedKey), + name: NSWindow.didResignKeyNotification, object: window) + } else { + releaseAll() + removeKeyMonitor() + pendingRequest?.cancel() + } + } + + override func resignFirstResponder() -> Bool { + releaseAll() + return super.resignFirstResponder() + } + + @objc private func windowResignedKey() { + releaseAll() + } + + // MARK: Geometry + + private var videoRect: CGRect { + guard let welcome, welcome.width > 0, welcome.height > 0 else { return bounds } + return AVMakeRect(aspectRatio: CGSize(width: welcome.width, height: welcome.height), insideRect: bounds) + } + + /// View point -> 0...1 over the video, top-left origin like the Mac's screen. + private func normalized(_ event: NSEvent) -> CGPoint { + let p = convert(event.locationInWindow, from: nil) + let rect = videoRect + guard rect.width > 0, rect.height > 0 else { return lastPoint } + lastPoint = CGPoint(x: min(max((p.x - rect.minX) / rect.width, 0), 1), + y: min(max((rect.maxY - p.y) / rect.height, 0), 1)) + return lastPoint + } + + /// Mac points per view point, so trackpad scrolling moves content 1:1 under your fingers. + private var scrollScale: CGFloat { + guard let welcome, videoRect.width > 0 else { return 1 } + return CGFloat(welcome.pointWidth) / videoRect.width + } + + override func setFrameSize(_ newSize: NSSize) { + super.setFrameSize(newSize) + updateCursor() + scheduleDisplayRequest() + } + + override func viewDidEndLiveResize() { + super.viewDidEndLiveResize() + scheduleDisplayRequest() + } + + override func viewDidChangeBackingProperties() { + super.viewDidChangeBackingProperties() + scheduleDisplayRequest(force: true) + } + + // MARK: Virtual display follows the window + + private var pendingRequest: DispatchWorkItem? + private var requestedPixels: CGSize? + private var requestedScale: Double? + + /// The host's virtual display is reshaped in place, so resizing the window (or going full screen) + /// gives the Mac a desktop exactly this size instead of letterboxing a fixed one. + private func scheduleDisplayRequest(force: Bool = false) { + guard let welcome, welcome.isVirtual else { return } + if requestedPixels == nil { + // The stream we connected with already matches; only react to changes from here on. + requestedPixels = targetPixels + requestedScale = targetScale + if !force { return } + } + pendingRequest?.cancel() + let work = DispatchWorkItem { [weak self] in self?.sendDisplayRequest() } + pendingRequest = work + DispatchQueue.main.asyncAfter(deadline: .now() + 0.4, execute: work) + } + + private var backing: CGFloat { window?.backingScaleFactor ?? 2 } + + private var targetPixels: CGSize { + // The host needs at least 640x480 for a virtual display. + CGSize(width: max(640, (bounds.width * backing).rounded()), height: max(480, (bounds.height * backing).rounded())) + } + + private var targetScale: Double { + MacDensity(rawValue: density)?.uiScale(backing: backing) ?? Double(backing) + } + + private func sendDisplayRequest() { + guard !inLiveResize, window != nil, bounds.width > 0, welcome?.isVirtual == true else { return } + let pixels = targetPixels + let scale = targetScale + if let current = requestedPixels, abs(current.width - pixels.width) <= 2, + abs(current.height - pixels.height) <= 2, requestedScale == scale { return } + requestedPixels = pixels + requestedScale = scale + session.requestDisplay(pixels: pixels, uiScale: scale) + } + + // MARK: Cursor + + /// Sizes the Mac's pointer like it is on the Mac: Mac points scaled to the video on screen. + private func updateCursor() { + if let macCursor, let welcome, welcome.pointWidth > 0 { + let scale = videoRect.width / CGFloat(welcome.pointWidth) // view points per Mac point + let size = NSSize(width: max(1, macCursor.size.width * scale), height: max(1, macCursor.size.height * scale)) + nsCursor = NSCursor(image: NSImage(cgImage: macCursor.image, size: size), + hotSpot: NSPoint(x: macCursor.hotspot.x * scale, y: macCursor.hotspot.y * scale)) + } else { + nsCursor = .arrow + } + window?.invalidateCursorRects(for: self) + // Cursor rects only apply on the next mouse move; show a new shape right away. + if let window, window.isKeyWindow, videoRect.contains(convert(window.mouseLocationOutsideOfEventStream, from: nil)) { + nsCursor.set() + } + } + + override func resetCursorRects() { + addCursorRect(videoRect, cursor: nsCursor) + } + + // MARK: Mouse + + private var trackingArea: NSTrackingArea? + + override func updateTrackingAreas() { + super.updateTrackingAreas() + if let trackingArea { removeTrackingArea(trackingArea) } + let area = NSTrackingArea(rect: .zero, options: [.mouseMoved, .activeInKeyWindow, .inVisibleRect], + owner: self, userInfo: nil) + addTrackingArea(area) + trackingArea = area + } + + override func mouseMoved(with event: NSEvent) { session.mouseMove(normalized(event)) } + override func mouseDragged(with event: NSEvent) { session.mouseMove(normalized(event)) } + override func rightMouseDragged(with event: NSEvent) { session.mouseMove(normalized(event)) } + override func otherMouseDragged(with event: NSEvent) { session.mouseMove(normalized(event)) } + + override func mouseDown(with event: NSEvent) { button(0, down: true, event) } + override func mouseUp(with event: NSEvent) { button(0, down: false, event) } + override func rightMouseDown(with event: NSEvent) { button(1, down: true, event) } + override func rightMouseUp(with event: NSEvent) { button(1, down: false, event) } + override func otherMouseDown(with event: NSEvent) { button(UInt8(clamping: event.buttonNumber), down: true, event) } + override func otherMouseUp(with event: NSEvent) { button(UInt8(clamping: event.buttonNumber), down: false, event) } + + private func button(_ button: UInt8, down: Bool, _ event: NSEvent) { + if down { + window?.makeFirstResponder(self) + heldButtons.insert(button) + } else { + heldButtons.remove(button) + } + let p = normalized(event) + session.mouseMove(p) + session.mouseButton(button, down: down, at: p) + } + + override func scrollWheel(with event: NSEvent) { + var dx = event.scrollingDeltaX + var dy = event.scrollingDeltaY + if event.hasPreciseScrollingDeltas { + dx *= scrollScale + dy *= scrollScale + } else { + // Wheel mice report lines. + dx *= 16 + dy *= 16 + } + guard dx != 0 || dy != 0 else { return } + session.scroll(dx: dx, dy: dy) + } + + // MARK: Keyboard + + /// Device-dependent modifier bits (NX_DEVICE*KEYMASK), to tell a left from a right modifier. + private static let modifierBits: [UInt16: UInt] = [ + 0x3B: 0x0001, 0x3E: 0x2000, // left, right control + 0x38: 0x0002, 0x3C: 0x0004, // left, right shift + 0x37: 0x0008, 0x36: 0x0010, // left, right command + 0x3A: 0x0020, 0x3D: 0x0040, // left, right option + ] + private static let capsLock: UInt16 = 0x39 + private static let appShortcut: NSEvent.ModifierFlags = [.control, .option, .command] + + /// A local monitor sees every key event before menus do, including key-ups with ⌘ held + /// (which never reach keyUp(with:)), so shortcuts can go to the remote Mac. + private func installKeyMonitor() { + guard keyMonitor == nil else { return } + keyMonitor = NSEvent.addLocalMonitorForEvents(matching: [.keyDown, .keyUp, .flagsChanged]) { [weak self] event in + guard let self, let window = self.window, event.window === window, window.isKeyWindow, + window.firstResponder === self else { return event } + if event.type == .keyDown, + event.modifierFlags.intersection(.deviceIndependentFlagsMask).isSuperset(of: Self.appShortcut) { + return event // the Stream menu + } + self.forward(event) + return nil + } + } + + private func removeKeyMonitor() { + if let keyMonitor { NSEvent.removeMonitor(keyMonitor) } + keyMonitor = nil + } + + private static func mods(_ flags: NSEvent.ModifierFlags) -> KeyMods { + var mods = KeyMods() + if flags.contains(.shift) { mods.insert(.shift) } + if flags.contains(.control) { mods.insert(.control) } + if flags.contains(.option) { mods.insert(.option) } + if flags.contains(.command) { mods.insert(.command) } + if flags.contains(.capsLock) { mods.insert(.capsLock) } + return mods + } + + private func forward(_ event: NSEvent) { + let code = event.keyCode + let mods = Self.mods(event.modifierFlags) + switch event.type { + case .keyDown: + heldKeys.insert(code) + session.key(code, action: event.isARepeat ? .repeatDown : .down, mods: mods) + case .keyUp: + heldKeys.remove(code) + session.key(code, action: .up, mods: mods) + case .flagsChanged: + if code == Self.capsLock { + // Each press toggles caps lock; there's no separate release event. + session.key(code, action: .down, mods: mods) + session.key(code, action: .up, mods: mods) + } else if let bit = Self.modifierBits[code] { + let down = event.modifierFlags.rawValue & bit != 0 + if down { heldModifiers.insert(code) } else { heldModifiers.remove(code) } + session.key(code, action: down ? .down : .up, mods: mods) + } + default: + break + } + } + + /// Lets go of everything on the remote Mac when focus leaves, so nothing stays stuck down. + func releaseAll() { + for code in heldKeys.union(heldModifiers) { session.key(code, action: .up, mods: []) } + heldKeys.removeAll() + heldModifiers.removeAll() + for button in heldButtons { session.mouseButton(button, down: false, at: lastPoint) } + heldButtons.removeAll() + } +} + +private struct StreamViewRepresentable: NSViewRepresentable { + let session: StreamSession + let welcome: Welcome? + let density: Double + + func makeNSView(context: Context) -> StreamNSView { + StreamNSView(session: session) + } + + func updateNSView(_ view: StreamNSView, context: Context) { + view.welcome = welcome + view.density = density + } + + static func dismantleNSView(_ view: StreamNSView, coordinator: ()) { + view.releaseAll() + } +} + +struct MacStreamScreen: View { + @EnvironmentObject private var session: StreamSession + @AppStorage("showStats") private var showStats = false + @AppStorage("macDensity") private var density = MacDensity.match.rawValue + + var body: some View { + ZStack(alignment: .topLeading) { + StreamViewRepresentable(session: session, welcome: session.welcome, + density: density > 0 ? density : MacDensity.match.rawValue) + if showStats { + StatsView(stats: session.stats, welcome: session.welcome) + .padding(10) + .allowsHitTesting(false) + } + } + .background(Color.black) + .frame(minWidth: 640, minHeight: 480) + } +} diff --git a/README.md b/README.md index 6ea2408..9d80430 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # iFrame -Low-latency Mac → iPad or Windows remote desktop for Apple silicon. It sends a video stream instead of pixel tiles. +Low-latency Mac → iPad, Mac, Windows or Linux remote desktop for Apple silicon. It sends a video stream instead of pixel tiles. **Host requirements:** a Mac with Apple silicon running macOS 14 or later (developed on an M4 Mac mini, macOS 15) and Xcode command-line tools. -**Clients:** an iPad on iPadOS 17 or later, or a Windows 10/11 x64 PC. Building the iPad app requires Xcode and [XcodeGen](https://github.com/yonaskolb/XcodeGen), plus `rsvg-convert` and ImageMagick if you regenerate icons (`brew install xcodegen librsvg imagemagick`). The [Windows client](Windows/README.md) uses Node.js to build and includes its runtime when packaged. +**Clients:** an iPad on iPadOS 17 or later, a Mac on macOS 14 or later (Apple silicon or Intel), or a Windows 10/11 x64 PC. Building the iPad app requires Xcode and [XcodeGen](https://github.com/yonaskolb/XcodeGen), plus `rsvg-convert` and ImageMagick if you regenerate icons (`brew install xcodegen librsvg imagemagick`). The [Windows client](Windows/README.md) uses Node.js to build and includes its runtime when packaged. ``` Mac (iframe-host) iPad (iFrame app) @@ -18,6 +18,7 @@ CGEvent input injection ``` What makes it fast: +- **Local cursor.** The Mac sends its cursor shape (arrow, I-beam, resize…) and leaves the pointer out of the video. Every client draws it at the local pointer position, so it moves with no network delay. - **Zero-copy pipeline.** Capture hands IOSurfaces straight to the media engine. No color conversion and no CPU copies. - **Low-latency rate control.** One frame in, one frame out, no reordering. Keyframes only on connect or after an error, so there are no periodic spikes. - **Ack-based flow control.** At most 3 frames can be unacknowledged. When Wi-Fi backs up, frames are skipped *before* encoding instead of piling up in socket buffers, and the newest screen goes out as soon as the link frees up. Lag never builds up. @@ -56,6 +57,30 @@ Building it yourself? Change `DEVELOPMENT_TEAM` and the `com.toddfaulls` bundle Your Mac appears under **Mac** on the connect screen. Enter the PIN, pick a display density, and tap **Connect**. +## Client (Mac) + +A native Universal app (Apple silicon + Intel, macOS 14+), target `iFrameMac` in the same Xcode project. + +```sh +scripts/build-mac-client.sh # → dist/iFrame.app and dist/iFrame--macOS-Universal.zip +``` + +It signs with your Apple Development / Developer ID identity if you have one (`security unlock-keychain` +first over SSH), otherwise ad hoc; `IFRAME_SIGN_IDENTITY=-` forces ad hoc. The app isn't notarized, so on +another Mac open it once with right-click → **Open** (or System Settings → Privacy & Security → **Open Anyway**). + +- **Display:** the host makes a virtual display shaped like the window. Resize the window or go full + screen and the remote desktop reshapes to fit, instead of being letterboxed. *More space* / *Most space* + trade sharpness for room; *Use the Mac's own display* mirrors the host's screen instead. +- **Input:** mouse (all buttons), trackpad scrolling 1:1, and the keyboard pass straight through. Key codes are + already Mac key codes, so every layout and shortcut works. While the stream has focus, **every** shortcut + goes to the remote Mac (⌘Q, ⌘W, ⌘Space…) except ⌃⌥⌘ ones: ⌃⌥⌘F full screen, ⌃⌥⌘S stats, + ⌃⌥⌘R refresh, ⌃⌥⌘D disconnect. macOS still keeps ⌘Tab and Mission Control for itself. +- **Cursor:** the remote Mac's pointer shape becomes this window's real cursor, so it moves with no lag. +- Don't run it on the host Mac itself against its own virtual display. The window would sit on the display + it reshapes. +- Testing: `open dist/iFrame.app --args -manualHost -pin -autoConnect YES` connects at launch. + ## Client (Linux) `Linux/` has a C client (SDL2 + FFmpeg, NVDEC/VAAPI hardware decoding) that speaks the same @@ -116,7 +141,6 @@ the host PIN. The packaged app includes its runtime. See [Windows controls, test ## Known limits / next steps - **Video isn't encrypted; there's only a PIN.** Use it on a trusted LAN or over Tailscale. -- **The cursor is part of the video.** A local cursor overlay would make pointing feel instant. - **Transport is TCP.** That's fine on a LAN. UDP with FEC would handle lossy networks better. - **No audio and no clipboard sync yet.** diff --git a/project.yml b/project.yml index 843f8b3..274a54b 100644 --- a/project.yml +++ b/project.yml @@ -3,6 +3,7 @@ options: bundleIdPrefix: com.toddfaulls deploymentTarget: iOS: "17.0" + macOS: "14.0" createIntermediateGroups: true settings: base: @@ -13,6 +14,7 @@ targets: platform: iOS sources: - path: Client + - path: ClientShared - path: Shared settings: base: @@ -48,3 +50,37 @@ targets: - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight + iFrameMac: + type: application + platform: macOS + sources: + - path: MacClient + - path: ClientShared + - path: Shared + settings: + base: + PRODUCT_NAME: iFrame + PRODUCT_BUNDLE_IDENTIFIER: com.toddfaulls.iframe.mac + CODE_SIGN_STYLE: Automatic + DEVELOPMENT_TEAM: 6PB44SHJS5 + ENABLE_HARDENED_RUNTIME: YES + ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon + MARKETING_VERSION: "0.1" + CURRENT_PROJECT_VERSION: "1" + # Universal: Apple silicon and Intel in one app. + ARCHS: "$(ARCHS_STANDARD)" + ONLY_ACTIVE_ARCH: NO + configs: + Release: + SWIFT_OPTIMIZATION_LEVEL: "-O" + info: + path: MacClient/Info.plist + properties: + CFBundleDisplayName: iFrame + CFBundleShortVersionString: $(MARKETING_VERSION) + CFBundleVersion: $(CURRENT_PROJECT_VERSION) + LSApplicationCategoryType: public.app-category.utilities + NSHumanReadableCopyright: MIT License + NSLocalNetworkUsageDescription: iFrame finds and connects to Macs running iframe-host on your network. + NSBonjourServices: + - _iframe._tcp diff --git a/scripts/build-icons.sh b/scripts/build-icons.sh index e116846..02f8979 100755 --- a/scripts/build-icons.sh +++ b/scripts/build-icons.sh @@ -1,6 +1,6 @@ #!/bin/bash # Regenerates every icon from scripts/make-icon.py: iPad app icon set (light/dark/tinted) -# and the macOS iFrame.icns for iFrame Host. Needs rsvg-convert and ImageMagick (brew). +# the macOS iFrame.icns for iFrame Host, and the Mac client's icon set. Needs rsvg-convert and ImageMagick (brew). set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" @@ -44,4 +44,19 @@ for sz in 16 32 128 256 512; do magick Design/icon-mac-1024.png -resize $((sz*2))x$((sz*2)) "$TMP/iFrame.iconset/icon_${sz}x${sz}@2x.png" done iconutil -c icns "$TMP/iFrame.iconset" -o Design/iFrame.icns + +# Mac client: same tile as an asset catalog icon set, plus the logo and accent color. +MACSET=MacClient/Assets.xcassets/AppIcon.appiconset; mkdir -p "$MACSET" +cp "$TMP"/iFrame.iconset/*.png "$MACSET/" +python3 - "$MACSET" <<'PY' +import json, sys +images = [] +for sz in (16, 32, 128, 256, 512): + for scale in (1, 2): + name = f"icon_{sz}x{sz}{'@2x' if scale == 2 else ''}.png" + images.append({"filename": name, "idiom": "mac", "scale": f"{scale}x", "size": f"{sz}x{sz}"}) +json.dump({"images": images, "info": {"author": "xcode", "version": 1}}, open(sys.argv[1] + "/Contents.json", "w"), indent=2) +PY +rm -rf MacClient/Assets.xcassets/IFrameMark.imageset MacClient/Assets.xcassets/AccentColor.colorset +cp -R "$MARK" "$COLOR" MacClient/Assets.xcassets/ echo "icons rebuilt" diff --git a/scripts/build-mac-client.sh b/scripts/build-mac-client.sh new file mode 100755 index 0000000..b601d95 --- /dev/null +++ b/scripts/build-mac-client.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# Builds the iFrame Mac client as one Universal app (Apple silicon + Intel) and zips it for +# copying to other Macs: dist/iFrame.app and dist/iFrame--macOS-Universal.zip. +# +# Signs with your Apple Development / Developer ID identity when there is one, otherwise ad hoc. +# Set IFRAME_SIGN_IDENTITY=- to force ad hoc. Over SSH, run `security unlock-keychain` first. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" +DERIVED="$ROOT/.build/mac-client" +DIST="$ROOT/dist" + +IDENTITY="${IFRAME_SIGN_IDENTITY:-$(security find-identity -v -p codesigning | awk -F'"' '/Developer ID Application|Apple Development/ {print $2; exit}')}" +IDENTITY="${IDENTITY:--}" +SIGNING=() +if [ "$IDENTITY" = "-" ]; then + SIGNING=(CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY=- DEVELOPMENT_TEAM=) +fi + +echo "==> generating project" +xcodegen generate --quiet + +echo "==> building (arm64 + x86_64, signing: $IDENTITY)" +# The generic destination is what makes xcodebuild build every architecture, not just this Mac's. +APP="$DERIVED/Build/Products/Release/iFrame.app" +rm -rf "$APP" +set +e +xcodebuild -project iFrame.xcodeproj -scheme iFrameMac -configuration Release \ + -destination 'generic/platform=macOS' -derivedDataPath "$DERIVED" ${SIGNING[@]+"${SIGNING[@]}"} build \ + | grep -E "error:|warning: .*\.swift|BUILD (SUCCEEDED|FAILED)" +BUILD_STATUS=${PIPESTATUS[0]} +set -e +if [ "$BUILD_STATUS" -ne 0 ] || [ ! -d "$APP" ] || ! codesign --verify --strict "$APP" 2>/dev/null; then + echo "build or signing failed (over SSH? run: security unlock-keychain)" + exit 1 +fi + +VERSION="$(/usr/libexec/PlistBuddy -c 'Print CFBundleShortVersionString' "$APP/Contents/Info.plist")" +ZIP="$DIST/iFrame-$VERSION-macOS-Universal.zip" +mkdir -p "$DIST" +rm -rf "$DIST/iFrame.app" "$ZIP" +ditto "$APP" "$DIST/iFrame.app" +ditto -c -k --keepParent "$DIST/iFrame.app" "$ZIP" + +SIGNER="$(codesign -dv "$DIST/iFrame.app" 2>&1 | awk -F= '/^Authority/ {print $2; exit}')" +echo "==> $(lipo -archs "$DIST/iFrame.app/Contents/MacOS/iFrame") · signed ${SIGNER:-ad hoc}" +echo " $DIST/iFrame.app" +echo " $ZIP"