fix(apple): drive macOS keyboard from NSEvent (GCKeyboard unreliable)
ci / rust (push) Has been cancelled
ci / rust (push) Has been cancelled
macOS GCKeyboard delivery is flaky — the same GameController quirk that
killed GCMouse motion (e414ec0). Keyboard input intermittently failed to
reach the host (e.g. typing in a gamescope game). Switch the macOS key
source to NSEvent, mirroring the mouse fix:
- StreamLayerView.keyDown/keyUp map NSEvent.keyCode (Carbon virtual
keycode) → Windows VK via the new InputCapture.keyCodeToVK table and
forward through InputCapture.sendKey, then consume the event (no beep).
- flagsChanged drives InputCapture.handleFlagsChanged, which diffs the raw
modifier flags to recover each L/R modifier down/up (modifiers never fire
keyDown/keyUp on macOS) and emits the same L/R VKs hidToVK already does.
- The macOS GCKeyboard keyChangedHandler is disabled (#if !os(macOS)) so it
can't double-send; iOS keeps the GCKeyboard path unchanged.
sendKey honors the ⌘⎋ capture-toggle suppressedVK latch and tracks into
pressedVKs so releaseAll()/blur flushes anything still held. The emitted
VKs are identical to the existing HID path, so the host (vk_to_evdev)
needs no change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -228,19 +228,44 @@ public final class StreamLayerView: NSView {
|
||||
dy: Float(event.scrollingDeltaY) * scale)
|
||||
}
|
||||
|
||||
// While captured, the view is first responder and consumes key events — GC delivers
|
||||
// them to the host independently, and consuming here stops the responder chain's
|
||||
// "unhandled keyDown" beep without touching the event stream GC may rely on.
|
||||
// ⌘-combos arrive via performKeyEquivalent instead and stay fully functional (⌘D).
|
||||
// While captured, the view is first responder and SENDS key events to the host straight
|
||||
// from NSEvent — GCKeyboard delivery proved unreliable on macOS (the same GameController
|
||||
// quirk that killed GCMouse motion, fixed in e414ec0), so the macOS GCKeyboard send path
|
||||
// is disabled and NSEvent is the single source. We map NSEvent.keyCode (a Carbon virtual
|
||||
// keycode) → Windows VK and forward via InputCapture.sendKey, then CONSUME (return without
|
||||
// super) to stop the responder chain's "unhandled keyDown" beep. Keys with no VK mapping
|
||||
// are still consumed while captured so they don't beep either. The ⌘⎋ toggle's Esc is
|
||||
// swallowed upstream by InputCapture's keyDown monitor (suppressedVK), so it never gets
|
||||
// here as a send; ⌘-combos still arrive via performKeyEquivalent and stay functional (⌘D).
|
||||
// Modifier keys never fire keyDown/keyUp — they come through flagsChanged below.
|
||||
public override var acceptsFirstResponder: Bool { true }
|
||||
public override func keyDown(with event: NSEvent) {
|
||||
if captured { return }
|
||||
if captured {
|
||||
if let ic = inputCapture, let vk = InputCapture.keyCodeToVK[event.keyCode] {
|
||||
ic.sendKey(vk, down: true) // autorepeat (event.isARepeat) passes through — fine for VK
|
||||
}
|
||||
return // consume even unmapped keys while captured (no beep)
|
||||
}
|
||||
super.keyDown(with: event)
|
||||
}
|
||||
public override func keyUp(with event: NSEvent) {
|
||||
if captured { return }
|
||||
if captured {
|
||||
if let ic = inputCapture, let vk = InputCapture.keyCodeToVK[event.keyCode] {
|
||||
ic.sendKey(vk, down: false)
|
||||
}
|
||||
return
|
||||
}
|
||||
super.keyUp(with: event)
|
||||
}
|
||||
/// Modifier keys (shift/control/option/command) arrive ONLY as flagsChanged on macOS,
|
||||
/// never keyDown/keyUp — InputCapture diffs the raw flags to recover each L/R down/up.
|
||||
public override func flagsChanged(with event: NSEvent) {
|
||||
if captured, let inputCapture {
|
||||
inputCapture.handleFlagsChanged(UInt(event.modifierFlags.rawValue))
|
||||
return
|
||||
}
|
||||
super.flagsChanged(with: event)
|
||||
}
|
||||
|
||||
private func requestAutoCapture() {
|
||||
pendingAutoCapture = true
|
||||
|
||||
Reference in New Issue
Block a user