feat(apple): stage-2 default + pixel-perfect, decode robustness, UI/rumble polish
apple / swift (push) Successful in 1m4s
android / android (push) Successful in 4m33s
ci / rust (push) Successful in 5m4s
ci / web (push) Successful in 51s
ci / docs-site (push) Successful in 59s
deb / build-publish (push) Successful in 3m12s
decky / build-publish (push) Successful in 12s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
release / apple (push) Successful in 8m30s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 19s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
ci / bench (push) Successful in 4m45s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m48s
apple / screenshots (push) Successful in 5m43s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m24s
docker / deploy-docs (push) Successful in 19s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m46s
apple / swift (push) Successful in 1m4s
android / android (push) Successful in 4m33s
ci / rust (push) Successful in 5m4s
ci / web (push) Successful in 51s
ci / docs-site (push) Successful in 59s
deb / build-publish (push) Successful in 3m12s
decky / build-publish (push) Successful in 12s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
release / apple (push) Successful in 8m30s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 19s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
ci / bench (push) Successful in 4m45s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m48s
apple / screenshots (push) Successful in 5m43s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m24s
docker / deploy-docs (push) Successful in 19s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m46s
Stream reliability - Default to the stage-2 presenter (VTDecompressionSession + CAMetalLayer): it detects and recovers a wedged decoder, where stage-1's AVSampleBufferDisplayLayer freezes hard on a lost HEVC reference frame with no app-side recovery (confirmed Apple limitation). Stage 1 is now a DEBUG-only presenter toggle, plus the automatic no-Metal fallback. - Stage-2 pixel-perfect: render the drawable at the decoded size (shader stays 1:1 = identity) and let the layer's contentsGravity scale via the system compositor — the same path stage-1's videoGravity used — instead of scaling in-shader. - Loss recovery in both pumps is now a persistent awaitingIDR want, retried until an IDR actually lands, so a keyframe request swallowed by the throttle can't strand a frozen frame; 100 ms keyframe throttle to match the Android path. - Fix "Publishing changes from within view updates": defer the HostStore writes out of the .onChange(of: model.phase) callback. - Move AVAudioSession setActive/setCategory off the main thread (async on a shared serial queue) to stop the UI-stall warning. Controllers - Rumble: capped-exponential backoff when the gamecontrollerd.haptics XPC breaks (-4811) so a transient server interruption self-heals instead of cascading; playsHapticsOnly so a controller engine doesn't join the always-active streaming audio session. - Host cards: iPad pointer "magnet" hover effect; iPhone press scale + light haptic. UI / design - Ship Geist (SIL OFL 1.1) as the app font (bundled OTFs + registration), with the license surfaced in Acknowledgements. - Restructure iOS/iPadOS Settings into a category NavigationSplitView; resolution wheel with custom-resolution entry; 10-bit HDR toggle in Display. - Industrial host-card redesign (left-aligned, bold, brand monogram tiles). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -54,7 +54,7 @@ struct ControllerTestView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
Text("Test Controller").font(.headline)
|
||||
Text("Test Controller").font(.geist(17, .semibold, relativeTo: .headline))
|
||||
Spacer()
|
||||
Button("Done") { dismiss() }.keyboardShortcut(.cancelAction)
|
||||
}
|
||||
@@ -99,8 +99,8 @@ struct ControllerTestView: View {
|
||||
.font(.title2)
|
||||
.foregroundStyle(.secondary)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(c.name).font(.headline)
|
||||
Text(c.productCategory).font(.caption).foregroundStyle(.secondary)
|
||||
Text(c.name).font(.geist(17, .semibold, relativeTo: .headline))
|
||||
Text(c.productCategory).font(.geist(12, relativeTo: .caption)).foregroundStyle(.secondary)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
@@ -209,7 +209,7 @@ struct ControllerTestView: View {
|
||||
) -> some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Touchpad\(tp.button.isPressed ? " — click" : "")")
|
||||
.font(.caption2).foregroundStyle(.secondary)
|
||||
.font(.geist(11, relativeTo: .caption2)).foregroundStyle(.secondary)
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 8).stroke(Color.secondary.opacity(0.3))
|
||||
fingerDot(tp.primary, color: .accentColor)
|
||||
@@ -230,7 +230,7 @@ struct ControllerTestView: View {
|
||||
private func motionReadout(_ m: GCMotion) -> some View {
|
||||
let a = Self.totalAccel(m)
|
||||
return VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Motion").font(.caption2).foregroundStyle(.secondary)
|
||||
Text("Motion").font(.geist(11, relativeTo: .caption2)).foregroundStyle(.secondary)
|
||||
Text(String(format: "gyro %+.2f %+.2f %+.2f",
|
||||
m.rotationRate.x, m.rotationRate.y, m.rotationRate.z))
|
||||
.font(.caption2.monospaced())
|
||||
@@ -254,11 +254,11 @@ struct ControllerTestView: View {
|
||||
Toggle("Heavy motor (left)", isOn: $heavyOn)
|
||||
Toggle("Light motor (right)", isOn: $lightOn)
|
||||
Label("Backend: \(tester.rumbleBackend)", systemImage: "waveform")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
.font(.geist(12, relativeTo: .caption)).foregroundStyle(.secondary)
|
||||
Text("Toggle a motor to feel it. The host maps a game's low/high-frequency "
|
||||
+ "rumble onto these two. A DualSense is driven over raw HID (CoreHaptics "
|
||||
+ "can't reach its motors on macOS).")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
.font(.geist(12, relativeTo: .caption)).foregroundStyle(.secondary)
|
||||
}
|
||||
.onChange(of: heavyOn) { _, _ in applyRumble() }
|
||||
.onChange(of: lightOn) { _, _ in applyRumble() }
|
||||
@@ -289,11 +289,11 @@ struct ControllerTestView: View {
|
||||
}
|
||||
}
|
||||
Text("Pick an effect, then pull L2/R2 to feel the resistance.")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
.font(.geist(12, relativeTo: .caption)).foregroundStyle(.secondary)
|
||||
}
|
||||
} else {
|
||||
Text("Adaptive triggers need a DualSense.")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
.font(.geist(12, relativeTo: .caption)).foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,7 +348,7 @@ struct ControllerTestView: View {
|
||||
_ title: String, @ViewBuilder _ content: () -> Content
|
||||
) -> some View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
Text(title).font(.subheadline.weight(.semibold))
|
||||
Text(title).font(.geist(15, .semibold, relativeTo: .subheadline))
|
||||
content()
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Reference in New Issue
Block a user