VNCConnection.Settings.inputMode looks like it configures keyboard-shortcut
forwarding on macOS, but in RoyalVNCKit's enqueue path it's a master gate:
// VNCConnection+Queue.swift
guard settings.inputMode != .none else { return } // every path
So every PointerEvent and KeyEvent we enqueued was discarded before hitting
the wire. The Mac received zero input even though the framebuffer was live.
Frames streamed because that queue is server→client, not gated by inputMode.
Fix: pass .forwardKeyboardShortcutsEvenIfInUseLocally. On iOS we have no
local keyboard shortcuts to steal from, so the most permissive value is
safe and it unblocks the input queue.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The earlier UIKeyInput conformance was declared in a separate extension. ObjC
protocol conformance via Swift extension is fragile when the protocol
inherits another @objc protocol (UIKeyInput inherits UITextInputTraits) — the
runtime didn't always pick up insertText:, so the on-screen keyboard came
up but characters never reached controller.type(_:).
Fix: declare UIKeyInput conformance directly on FramebufferUIView's class
declaration, with insertText / deleteBackward / hasText as native members.
Also caught and fixed by the new UI test:
- The toolbar's keyboard-icon button had a 20×13 hit region (SF Symbol size)
even though the visual frame was 34×34 — XCUI taps couldn't land on it
reliably. .contentShape(Rectangle()) widens the hit area to the frame.
- accessibilityValue is reserved by iOS for UIKeyInput-classed views (treats
them as TextView), so a separate hidden "fb-diag" accessibility probe
records keyboard plumbing events for the test to verify.
Tests:
- KeyboardInputTests (5): pure mapping from String → X11 keysym down/up pairs
- ScreensUITests.testSoftwareKeyboardSendsCharactersToFramebuffer:
opens a session, taps the keyboard toggle, types "hi" via the system
keyboard, and asserts the framebuffer's diagnostic probe records
[ins:h] and [ins:i] — proving the chars reach controller.type(_:)
- A SwiftUI state probe (sessionview-state) verifies the binding flips,
which guards against future tap-routing regressions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
RoyalVNCKit prioritizes .diffieHellman (ARD) over .vnc during handshake when
both are offered. My delegate adapter was passing an empty username to
VNCUsernamePasswordCredential, so any Mac with user-account screen sharing
enabled rejected the credential before .vnc fallback could happen.
Fix: persist a username on SavedConnection and pipe it through to the
credential callback. Leave blank to use the VNC-only password path.
AddConnection footer now explains the two Mac paths:
• User account (ARD) — macOS short name + full account password
• VNC-only password — blank username + ≤8 char password
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>