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
@@ -1,7 +1,28 @@
// InputCapture's static keymap tables: HID usage Windows VK (the GCKeyboard path on all
// platforms) and, on macOS, NSEvent.keyCode Windows VK (the NSEvent key path).
import PunktfunkShared
extension InputCapture {
/// Remap one modifier VK for the active location-based [`ModifierLayout`] just before it goes on
/// the wire. `.mac` (and every non-modifier VK) is the identity. `.windows` swaps the Alt vs
/// Super/Windows ROLE between the Option and Command keys while KEEPING the side, so a Windows
/// user's ` = Alt, = Win` muscle memory lands correctly:
/// L Command 0x5B L Alt 0xA4 · R Command 0x5C R Alt 0xA5.
/// It's applied ONLY at the send boundary (`InputCapture.emitKey`) all press/release
/// bookkeeping stays on the physical VK, so modifier direction tracking and the client-local
/// -based shortcuts are untouched. The swap is its own inverse: a key that went down remapped
/// goes up remapped, so the host never sees a stuck modifier.
static func applyModifierLayout(_ vk: UInt32, _ layout: ModifierLayout) -> UInt32 {
guard layout == .windows else { return vk }
switch vk {
case 0x5B: return 0xA4 // L Command L Alt (VK_LWIN VK_LMENU)
case 0x5C: return 0xA5 // R Command R Alt (VK_RWIN VK_RMENU)
case 0xA4: return 0x5B // L Option L Win (VK_LMENU VK_LWIN)
case 0xA5: return 0x5C // R Option R Win (VK_RMENU VK_RWIN)
default: return vk
}
}
/// HID usage (GCKeyCode raw) Windows VK (the host maps VK evdev; every VK emitted
/// here exists in punktfunk-host/src/inject.rs::vk_to_evdev extend the two together).
static let hidToVK: [Int: UInt32] = {