From 8b060947d7fe8538d52ab25671ed7c7c39751df5 Mon Sep 17 00:00:00 2001 From: taliesin-ai <281494191+taliesin-ai@users.noreply.github.com> Date: Wed, 26 Aug 2026 21:56:52 +1000 Subject: [PATCH] fix(v3): offer the Fn/Globe key to the input method on macOS The Fn/Globe key arrives as a modifier change rather than a key press, and WebKit drops keyCode 63 in WebViewImpl::flagsChanged() before it reaches interpretKeyEvent(). The current input method therefore never sees it, and an input method that binds a shortcut to holding Fn - push-to-talk voice input is the common one, and hardware remappers produce the same event - does nothing at all inside a Wails window while working in native and Chromium applications. Nothing in Wails handled flagsChanged, so there was no code path to fix; this adds one. A local event monitor, alongside the two this file already installs for mouse events, offers a keyCode 63 modifier change to the first responder's input context. The event is returned unchanged either way, so AppKit's own handling is untouched and no DOM keyboard event is synthesised - the page still does not see Fn, which is both the existing behaviour and the right one. The monitor ignores every other key code, and events belonging to windows that are not ours. Reported in #6033, which also proposed this approach and cited the NSTextInputContext.handleEvent documentation for it. Verified here: the build and vet are clean, the test suite passes, and sending real Fn presses to a running application changes nothing else - key bindings fire before and after, and the application does not crash. What could not be verified here is the symptom itself, which needs an input method with a hold-Fn voice shortcut bound. Synthesising the event proves delivery, not that the input method acts on it, so the reporter should confirm before this is trusted. Claude-Session: https://claude.ai/code/session_01WP7MWnMpQDT2WB7CPLWbqr --- v3/pkg/application/application_darwin.go | 39 ++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/v3/pkg/application/application_darwin.go b/v3/pkg/application/application_darwin.go index cb05aeae731..8c90b0c955a 100644 --- a/v3/pkg/application/application_darwin.go +++ b/v3/pkg/application/application_darwin.go @@ -19,11 +19,50 @@ extern void registerListener(unsigned int event); static AppDelegate *appDelegate = nil; +// kVK_Function from Carbon's Events.h, spelled out so that header is not +// needed for one number. +static const unsigned short kWailsFunctionKeyCode = 63; + + static void init(void) { [NSApplication sharedApplication]; appDelegate = [[AppDelegate alloc] init]; [NSApp setDelegate:appDelegate]; + // The Fn/Globe key arrives as a modifier change rather than a key press, + // and WebKit drops keyCode 63 in WebViewImpl::flagsChanged() before it + // reaches interpretKeyEvent(), so the current input method never sees it. + // Input methods that bind a shortcut to holding Fn - push-to-talk voice + // input is the common one - therefore do nothing at all inside a Wails + // window, while working in native and Chromium applications. + // + // Offer the event to the first responder's input context here instead. The + // event is returned unchanged either way, so AppKit's own handling is + // untouched and no DOM keyboard event is synthesised: the page still does + // not see Fn, which is the existing behaviour and the correct one. + [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskFlagsChanged handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) { + if ([event keyCode] != kWailsFunctionKeyCode) { + return event; + } + NSWindow* eventWindow = [event window]; + if (eventWindow == nil) { + return event; + } + if (![[eventWindow delegate] isKindOfClass:[WebviewWindowDelegate class]]) { + return event; + } + // inputContext is declared on NSView, not on NSResponder, and the + // first responder is not necessarily a view. + NSResponder* responder = [eventWindow firstResponder]; + if ([responder isKindOfClass:[NSView class]]) { + NSTextInputContext* inputContext = [(NSView*)responder inputContext]; + if (inputContext != nil) { + [inputContext handleEvent:event]; + } + } + return event; + }]; + [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) { NSWindow* eventWindow = [event window]; if (eventWindow == nil ) {