feat(desktop): add support for mouse side buttons on macOS w/Logi Options+#130
Open
michael131468 wants to merge 1 commit intosipeed:mainfrom
Open
feat(desktop): add support for mouse side buttons on macOS w/Logi Options+#130michael131468 wants to merge 1 commit intosipeed:mainfrom
michael131468 wants to merge 1 commit intosipeed:mainfrom
Conversation
…ions+
The use case for this commit is to enable the MacOS version of the app to
collect mouse side button events and forward them on through the usb-kvm.
By default, it's configured to collect mouse side button events that are
configured as forward/backward navigation events (configured by Logi Options+).
But it can be reconfigured to forward select mouse events by modifying a json
configuration file.
This change is quite complex because it was made to handle a case where Logi
Options+ intercepts the raw HID side-button presses and re-emits them as macOS
NSEventTypeGesture events (CGEventType 29), which Chromium does not surface to
the renderer as mouse events. This requires significant workarounds.
It is architected into two parts:
(1) The Hook (desktop/native/):
Electron doesn't seem to expose macOS NSEventTypeOtherMouseDown (the AppKit
event type that carries button 3/4 back/forward) to the main process JavaScript
layer. And if configured via Logi Options+ to be backward/forward events,
Chromium consumes those events and doesn't forward them onto the app. Therefore
we need a side process to tap into the events and forward them on to the app.
That is what this hook is.
- A C++ N-API addon opens a CGEventTap on kCGSessionEventTap from a
dedicated thread running a CFRunLoop.
- The hook itself is generic: it has no hardcoded knowledge of CGEventTypes.
startHook(rules, callback) accepts a rules array supplied by the Electron
app; each rule specifies a CGEventType, a list of field matchers (field id +
any-of values), and which button ('side' or 'extra') to emit on match. The tap
callback iterates the rules and, on the first match, emits {kind: 'button',
button: 'side' | 'extra'} back to Node via a ThreadSafeFunction.
- The default rule set (loaded from the JSON config in the Electron
app) matches CGEventType 29 with phase=ended (field 132) and reads
the direction (field 115) to distinguish side vs. extra, which is
what Logi Options+ emits for back/forward. The rule config can be
edited without rebuilding the native addon.
Note that the build instructions for MacOS get more complicated as we need
to build this hook application too as a dependency for the main app. See
desktop/README.md.
(2) The Forwarder (desktop/src/main/):
This modifies the electron app to load the hook as an addon and receive events
to forward on as HID side-button press+release events.
- index.ts loads the addon (different path in dev vs packaged) and
translates each side-button event into an HID press+release.
- The forwarder owns the rule configuration: on startup it reads
side-button-rules.json from the user data directory if present,
otherwise falls back to the bundled side-button-rules.default.json,
and passes the parsed rules array into startHook(). This is what
lets users reconfigure which CGEvents map to side/extra without
rebuilding the native addon.
- Side buttons are only declared on the device's absolute HID
interface (report prefix 0x02); the relative interface (0x01)
declares only 3 buttons, so bits 0x08/0x10 sent there are silently
dropped by the Linux host. Button presses are always routed through
the absolute interface, regardless of which mode the user is in.
- Device.sendMouseData now sniffs outgoing absolute reports and
remembers the last x/y, exposed via getLastAbsPosition(), so the
injected press doesn't teleport the cursor.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Author
|
Hi, I'm not really expecting this to get merged as it's quite the added complexity for a very small use case. But I wanted to share it in a pull request to increase visibility in case there are others out there who want such a thing and need such a workaround. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The use case for this commit is to enable the MacOS version of the app to collect mouse side button events and forward them on through the usb-kvm.
By default, it's configured to collect mouse side button events that are configured as forward/backward navigation events (configured by Logi Options+). But it can be reconfigured to forward select mouse events by modifying a json configuration file.
This change is quite complex because it was made to handle a case where Logi Options+ intercepts the raw HID side-button presses and re-emits them as macOS NSEventTypeGesture events (CGEventType 29), which Chromium does not surface to the renderer as mouse events. This requires significant workarounds.
It is architected into two parts:
(1) The Hook (desktop/native/):
Electron doesn't seem to expose macOS NSEventTypeOtherMouseDown (the AppKit event type that carries button 3/4 back/forward) to the main process JavaScript layer. And if configured via Logi Options+ to be backward/forward events, Chromium consumes those events and doesn't forward them onto the app. Therefore we need a side process to tap into the events and forward them on to the app.
That is what this hook is.
Note that the build instructions for MacOS get more complicated as we need to build this hook application too as a dependency for the main app. See desktop/README.md.
(2) The Forwarder (desktop/src/main/):
This modifies the electron app to load the hook as an addon and receive events to forward on as HID side-button press+release events.