MirageKit

repository·main·Indexed 20 days ago

https://github.com/ethanlipnik/miragekit

A framework for high-performance window and desktop streaming between Apple platforms (macOS, iOS, visionOS). It provides tools for discovery, transport, encoding, and input forwarding, allowing a Mac to be controlled remotely. The library includes MirageKitHost for capturing and serving streams, MirageKitClient for rendering and input, and SwiftUI views like MirageStreamContentView for stream integration.

Tokens
2K
Snippets
7
Records
8
Agent score
20%

What's inside MirageKit

  1. Configure streaming modes and encoder settings

    main

    MirageKit supports three primary streaming modes:

    • Window: Captures a single window using ScreenCaptureKit.
    • App: Groups windows by bundle identifier and follows new windows as they spawn.
    • Desktop: Mirrors a virtual display sized to the client for 1:1 pixel mapping.

    For fine-grained control, use MirageEncoderConfiguration to adjust codec, frame rate, encoder quality, and bit depth. Clients can also request per-stream tweaks using MirageEncoderOverrides.

  2. Connect from a client using MirageKitClient

    main

    To connect to a host, use MirageClientService. Implement MirageClientDelegate to handle client-side events. Use connect(to:) with a LoomPeer to establish the connection, and requestWindowList() to discover available windows.

    import MirageKitClient
    
    @MainActor
    final class ClientController: MirageClientDelegate {
        let client = MirageClientService()
    
        init() { client.delegate = self }
    
        func connect(to host: LoomPeer) async throws {
            try await client.connect(to: host)
            try await client.requestWindowList()
        }
    }
  3. Render a stream in SwiftUI

    main

    There are two ways to render the stream in SwiftUI:

    1. Low-level: MirageStreamViewRepresentable

    Use this for manual control over input events and drawable metrics.

    2. High-level: MirageStreamContentView

    This is the recommended approach for most apps. When paired with a MirageClientSessionStore, it automatically handles input, focus, and resizing plumbing.

    // High-level approach
    let sessionStore = MirageClientSessionStore()
    let client = MirageClientService(sessionStore: sessionStore)
    
    MirageStreamContentView(
        session: session,
        sessionStore: sessionStore,
        clientService: client,
        isDesktopStream: false
    )
  4. Install MirageKit via Swift Package Manager

    main

    Add MirageKit to your Swift project using the following package dependency. You can choose specific products based on your role (Host or Client):

    • MirageKitClient: For connecting to a host, rendering the stream, and sending input.
    • MirageKitHost: For capturing, encoding, and serving a Mac to clients.
    • MirageKit: Shared types and protocols.
    • MirageHostBootstrapRuntime: For pre-login and remote unlock support.
    .package(url: "https://github.com/EthanLipnik/MirageKit.git", from: "1.0.5"),
  5. Host a Mac using MirageKitHost

    main

    To turn a Mac into a stream provider, use MirageHostService. Implement MirageHostDelegate to handle host-side events and call start() to begin serving the Mac.

    import MirageKitHost
    
    @MainActor
    final class HostController: MirageHostDelegate {
        private let host = MirageHostService()
    
        init() { host.delegate = self }
    
        func start() async throws {
            try await host.start()
        }
    }
  6. Configure Permissions and Info.plist for MirageKit

    main

    MirageKit requires specific permissions to function:

    macOS Host Requirements

    • Screen Recording: Required for ScreenCaptureKit window/desktop capture.
    • Accessibility: Required for input forwarding and window activation.

    Network Discovery (Both Host and Client)

    You must add Bonjour service support to your Info.plist to allow device discovery via local network:

    <key>NSBonjourServices</key>
    <array>
        <string>_miragekit._tcp</string>
    </array>
    
    <key>NSLocalNetworkUsageDescription</key>
    <string>Discover and connect to nearby Mirage devices.</string>

    Note: For App Store distribution, you must include the com.apple.developer.networking.multicast entitlement.

  7. Fix UI stutter and high CPU usage in ColorSync

    main

    If your system experiences stuttering (scrolling, window dragging, or animations) and you observe high CPU usage from colorsyncd or colorsync.displayservices in top, it may be due to accumulated Mirage display profiles.

    To resolve this, run the following cleanup script in Terminal. This script performs four actions:

    1. Backs up system-level display profiles: Moves files matching Mirage Shared Display* from /Library/ColorSync/Profiles/Displays to a timestamped backup folder (requires sudo).
    2. Backs up user-level profiles: Moves files matching Mirage* from ~/Library/ColorSync/Profiles to a timestamped backup folder.
    3. Resets ColorSync device cache: Moves the device cache and profiles from /Library/Caches/ColorSync/ to a backup folder (requires sudo).
    4. Restarts ColorSync services: Kills colorsyncd and colorsync.displayservices to force a reload (requires sudo).
    ts=$(date +%Y%m%d-%H%M%S)
    
    # System-level display profiles (requires sudo)
    SYS_SRC="/Library/ColorSync/Profiles/Displays"
    SYS_DST="/Library/ColorSync/Profiles/MirageBackup-$ts"
    sudo mkdir -p "$SYS_DST"
    sudo find "$SYS_SRC" -maxdepth 1 -type f -name 'Mirage Shared Display*' -exec mv -n {} "$SYS_DST/" \;
    
    # User-level profiles (no sudo)
    USER_SRC="$HOME/Library/ColorSync/Profiles"
    USER_DST="$HOME/Library/ColorSync/Profiles/MirageBackup-$ts"
    mkdir -p "$USER_DST"
    find "$USER_SRC" -maxdepth 1 -type f -name 'Mirage*' -exec mv -n {} "$USER_DST/" \;
    
    # ColorSync device cache reset (requires sudo)
    CACHE_DST="/Library/Caches/ColorSync/Backup-$ts"
    sudo mkdir -p "$CACHE_DST"
    sudo mv /Library/Caches/ColorSync/com.apple.colorsync.devices "$CACHE_DST/" 2>/dev/null || true
    sudo mv /Library/Caches/ColorSync/Profiles "$CACHE_DST/" 2>/dev/null || true
    
    # Restart ColorSync services
    sudo killall colorsyncd colorsync.displayservices
  8. Verify ColorSync service status and profile count

    main

    After running the cleanup script, use these commands to verify that the ColorSync services have settled and that the number of Mirage profiles has been reduced.

    1. Check CPU usage: Use top to monitor colorsyncd and colorsync.displayservices.
    2. Check profile count: Count the remaining Mirage Shared Display files in the system directory.
    top -l 2 -o cpu -n 10 -stats pid,command,cpu,mem,time
    ls /Library/ColorSync/Profiles/Displays | grep -c 'Mirage Shared Display'