fix(apple): recover dropped macOS client features lost in the W7/W8 refactor
apple / swift (push) Successful in 1m20s
android / android (push) Has been cancelled
apple / screenshots (push) Has been cancelled
arch / build-publish (push) Has been cancelled
ci / web (push) Successful in 52s
ci / docs-site (push) Successful in 1m17s
ci / rust (push) Has been cancelled
ci / bench (push) Has been cancelled
deb / build-publish (push) Has been cancelled
decky / build-publish (push) Successful in 24s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 12s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 17s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 18s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 18s
docker / deploy-docs (push) Has been cancelled
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Has been cancelled
release / apple (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled

The W7/W8 client refactor (extracting FullscreenController/ApprovalRequest out of
ContentView, splitting Settings) landed the DefaultsKeys + scaffolding but dropped
the feature WIRING, which was uncommitted WIP shelved in a stash during the
16:28 clipboard/v4 reconcile. Recovered from that stash (+ its untracked-files
parent for the whole new ModifierLayout.swift) and re-ported onto the refactored
tree:

- invertScroll: applied in InputCapture.sendScroll (the one scroll sink) + Settings toggle
- modifierLayout (Cmd/Option switch): restored ModifierLayout.swift enum + KeyMaps
  .applyModifierLayout + InputCapture.emitKey wire-boundary + Settings picker
- fullscreen shortcut (Ctrl+Cmd+F): InputCapture ⌃⌘F interception + onToggleFullscreen
  + StreamView wiring + Stream-menu item (the .punktfunkToggleFullscreen sink +
  FullscreenController observer already survived on main)
- render scale: Settings picker + MatchWindowFollower renderScale/maxDimension
  application + StreamView/StreamViewIOS plumbing (RenderScale.swift already on main)

StreamCommands re-ported by hand (the stash hunk deleted the since-landed clipboard
item — kept it, added the fullscreen item alongside). Recovered ModifierLayout +
RenderScale tests. swift build green; 15 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 22:20:33 +02:00
parent 3584b47fb8
commit 205e5c5a59
11 changed files with 348 additions and 10 deletions
@@ -0,0 +1,82 @@
import XCTest
import PunktfunkShared
/// Pins the render-scale geometry (`RenderScale`) the multiply aspect-preserve even-floor
/// codec-clamp that turns the `renderScale` setting into a host-valid `Mode`, plus the label/clamp
/// helpers the UI and the connect share.
final class RenderScaleTests: XCTestCase {
func testSanitizeClampsToRangeAndDefaultsMissing() {
XCTAssertEqual(RenderScale.sanitize(0), 1.0) // absent / zero Native
XCTAssertEqual(RenderScale.sanitize(-2), 1.0) // nonsense Native
XCTAssertEqual(RenderScale.sanitize(0.1), 0.5) // below the floor
XCTAssertEqual(RenderScale.sanitize(9), 4.0) // above the ceiling
XCTAssertEqual(RenderScale.sanitize(1.5), 1.5) // in range, untouched
}
func testMaxDimensionIsCodecAware() {
XCTAssertEqual(RenderScale.maxDimension(codec: "h264"), 4096)
XCTAssertEqual(RenderScale.maxDimension(codec: "hevc"), 8192)
XCTAssertEqual(RenderScale.maxDimension(codec: "av1"), 8192)
XCTAssertEqual(RenderScale.maxDimension(codec: "auto"), 8192)
}
func testNativeScaleIsIdentity() {
let m = RenderScale.apply(baseWidth: 1920, baseHeight: 1080, scale: 1.0, maxDimension: 8192)
XCTAssertEqual(m.width, 1920)
XCTAssertEqual(m.height, 1080)
}
func testSupersampleDoubles() {
let m = RenderScale.apply(baseWidth: 1920, baseHeight: 1080, scale: 2.0, maxDimension: 8192)
XCTAssertEqual(m.width, 3840)
XCTAssertEqual(m.height, 2160)
}
func testUnderRenderHalves() {
let m = RenderScale.apply(baseWidth: 1920, baseHeight: 1080, scale: 0.5, maxDimension: 8192)
XCTAssertEqual(m.width, 960)
XCTAssertEqual(m.height, 540)
}
func testResultsAreAlwaysEven() {
// 1280×720 × 1.5 = 1920×1080 (already even); 1366×768 × 1.5 = 2049×1152 2048×1152.
let m = RenderScale.apply(baseWidth: 1366, baseHeight: 768, scale: 1.5, maxDimension: 8192)
XCTAssertEqual(m.width % 2, 0)
XCTAssertEqual(m.height % 2, 0)
XCTAssertEqual(m.width, 2048)
XCTAssertEqual(m.height, 1152)
}
func testOverCeilingClampsUniformlyPreservingAspect() {
// 4K × 4 would be 15360×8640; both axes exceed 8192, so the LARGER (width) lands on the cap
// and the ratio is kept (16:9 8192×4608, even).
let m = RenderScale.apply(baseWidth: 3840, baseHeight: 2160, scale: 4.0, maxDimension: 8192)
XCTAssertLessThanOrEqual(m.width, 8192)
XCTAssertLessThanOrEqual(m.height, 8192)
XCTAssertEqual(m.width, 8192)
XCTAssertEqual(m.height, 4608)
// 16:9 preserved to within the even-floor rounding.
XCTAssertEqual(Double(m.width) / Double(m.height), 16.0 / 9.0, accuracy: 0.01)
}
func testH264CeilingIsTighter() {
// 1080p × 4 = 7680×4320; under H.264's 4096 wall the width clamps to 4096, aspect kept.
let m = RenderScale.apply(baseWidth: 1920, baseHeight: 1080, scale: 4.0, maxDimension: 4096)
XCTAssertEqual(m.width, 4096)
XCTAssertEqual(m.height, 2304)
}
func testMinimumFloorHonoured() {
// A tiny base under a small scale can't fall below 320×200.
let m = RenderScale.apply(baseWidth: 400, baseHeight: 300, scale: 0.5, maxDimension: 8192)
XCTAssertGreaterThanOrEqual(m.width, 320)
XCTAssertGreaterThanOrEqual(m.height, 200)
}
func testLabels() {
XCTAssertEqual(RenderScale.label(1.0), "Native (1×)")
XCTAssertEqual(RenderScale.label(2.0), "2× · supersample")
XCTAssertEqual(RenderScale.label(0.5), "0.5×")
}
}