mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 03:00:18 +02:00
Keyboard and trackpad for the Frame, through its own KDE Connect
Home → Keyboard and trackpad, in every version of Frame Control (Mac, Windows, Linux, iPhone, iPad) with nothing to install on the device in your hand. On a phone: a trackpad (drag, tap, two-finger scroll and right-click) and a field that types on the Frame. On a computer: click the pad to pass the mouse and keyboard through; Esc to stop. First-party route: ui/frame_input_agent.py runs on the Frame and talks KDE Connect's LAN protocol (v7) to kdeconnectd as if it were a phone. KDE Connect isn't on the image, but Valve's package repository has it; the agent fetches it and four libraries into ~ (no root, survives updates), starts it, pairs by itself (accepting over D-Bus), and stops it again when the last device disconnects. Each device has its own identity; a stuck KDE Connect is restarted once. Verified against the real Frame (SteamOS 0.4.1): first-time install, pairing, pointer moves from the Mac's server and the iPhone app (Simulator), Mac and iPhone at once, two installs at once, a frozen daemon replaced, and the daemon stopping when the app quits. Reviewed by GPT-6 Astra (xhigh) over seven rounds; all findings fixed except per-event delivery acknowledgement (documented known limit). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
0016d9200c
commit
b9e31178f3
12 files changed
+1118
-11
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import Citadel
|
||||
import CryptoKit
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
@@ -33,6 +34,11 @@ final class AppModel: ObservableObject {
|
||||
}
|
||||
|
||||
var deviceName: String { UIDevice.current.userInterfaceIdiom == .pad ? "iPad" : "iPhone" }
|
||||
/// Stable per install: a hash of this device's SSH key, which is made once and kept in the Keychain.
|
||||
var clientID: String {
|
||||
let digest = SHA256.hash(data: Data(authorizedKeysLine.utf8))
|
||||
return deviceName.lowercased() + "-" + digest.prefix(6).map { String(format: "%02x", $0) }.joined()
|
||||
}
|
||||
private var hostKey: String? { UserDefaults.standard.string(forKey: Self.hostKeyKey) }
|
||||
|
||||
// MARK: pairing
|
||||
@@ -167,7 +173,7 @@ final class AppModel: ObservableObject {
|
||||
guard current() else { throw CancellationError() }
|
||||
step("Starting Frame Control on the headset")
|
||||
let key = Self.randomKey()
|
||||
let server = try await HeadsetServer.start(in: dir, over: l, key: key, device: deviceName)
|
||||
let server = try await HeadsetServer.start(in: dir, over: l, key: key, device: deviceName, client: clientID)
|
||||
guard current() else { throw CancellationError() }
|
||||
let f = try await PortForwarder.start(over: l, to: server.port)
|
||||
forwarder = f
|
||||
|
||||
Reference in new issue
Block a user