From c6ca69dab02ba462e87c34c6c33cd8c174c494a2 Mon Sep 17 00:00:00 2001 From: paravozz Date: Sat, 18 Apr 2026 23:45:50 +0100 Subject: [PATCH] Implement IPlugView::onKeyDown with Editor::on_key_down trait hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously the VST3 wrapper's `IPlugView::onKeyDown` returned `kNotImplemented` unconditionally. In REAPER on macOS this causes the host to intercept keys it has bound as accelerators — most visibly the space bar toggles transport instead of inserting a space in focused text inputs, even when the plugin editor has keyboard focus. Add a new `Editor::on_key_down(Option) -> bool` trait method (default impl returns `false`, backwards compatible) and call it from the VST3 wrapper for keys with a non-zero VST3 virtual key code. Keys without a virtual key code (letters, digits, symbols) continue to flow through AppKit's normal `keyDown:` path so NSTextInputContext handles them — consuming them here would break letter input. This matches the mechanism VSTGUI already implements (and that Serum relies on), and the fix shape documented on the JUCE forum for the equivalent JUCE bug. --- src/editor.rs | 14 ++++++++++++++ src/wrapper/vst3/view.rs | 21 ++++++++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index c6bd190e0..227ca1350 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -69,6 +69,20 @@ pub trait Editor: Send { /// loaded. fn param_values_changed(&self); + /// Called when the host sends a key-down event to the plugin view (VST3 + /// `IPlugView::onKeyDown`). Return `true` if the editor consumed the key + /// (the wrapper will return `kResultTrue` to the host so the host skips its + /// own accelerator handling); return `false` to let the host process the + /// key normally. + /// + /// This is primarily for text-input routing in hosts like REAPER that + /// intercept certain keys (space, Cmd-shortcuts) before they reach the + /// plugin's native view. The editor should only return `true` if a text + /// input in the editor currently has focus and can consume the character. + fn on_key_down(&self, _character: Option) -> bool { + false + } + // TODO: Reconsider adding a tick function here for the Linux `IRunLoop`. To keep this platform // and API agnostic, add a way to ask the GuiContext if the wrapper already provides a // tick function. If it does not, then the Editor implementation must handle this by diff --git a/src/wrapper/vst3/view.rs b/src/wrapper/vst3/view.rs index fd0fd255a..170e9c710 100644 --- a/src/wrapper/vst3/view.rs +++ b/src/wrapper/vst3/view.rs @@ -338,11 +338,26 @@ impl IPlugView for WrapperView

{ unsafe fn on_key_down( &self, - _key: vst3_sys::base::char16, - _key_code: i16, + key: vst3_sys::base::char16, + key_code: i16, _modifiers: i16, ) -> tresult { - kNotImplemented + // Only forward keys that carry a VST3 virtual key code. Those are + // keys the host (notably REAPER) may intercept as accelerators + // before they reach our native view — space, tab, return, arrows, + // etc. Plain printable characters arrive with `key_code = 0` and + // flow through the normal AppKit `keyDown:` path, which already + // handles text input via NSTextInputContext. Consuming them here + // would break letter input. + if key_code == 0 { + return kResultFalse; + } + let character = char::from_u32(key as u32); + if self.editor.lock().on_key_down(character) { + kResultOk + } else { + kResultFalse + } } unsafe fn on_key_up(