Fix dock-swipe gestures on macOS 27 - #1918
Draft
SalmonC wants to merge 4 commits into
Draft
Conversation
On macOS 27 (Tahoe beta), the Dock server no longer acts on the public CGEvent gesture fields for *synthetic* dock-swipe events. As a result the Mission Control / Spaces / Show-Desktop gestures produced by TouchSimulator.postDockSwipeEventWithDelta: silently do nothing (noah-nuebling#1892). The Dock server now requires the raw IOKit HID event payload to be embedded in the serialized CGEvent (field 4205). This ports the workaround the InstantSpaceSwitcher / FasterSwiper community worked out (jurplel/InstantSpaceSwitcher#72): before posting, serialize the event with CGEventCreateData(), splice in a hand-built IOHID gesture payload (16.16 fixed-point), rebuild with CGEventCreateFromData(), and post that instead. Gated behind a macOS 27+ check, so behavior on older systems is unchanged. Verified on macOS 27.0 (26A5353q): with augmentation the Mission Control (vertical) swipe triggers correctly; the un-augmented path does nothing, confirming the augmentation is both necessary and sufficient. Refs: noah-nuebling#1892, jurplel/InstantSpaceSwitcher#72 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
zhaoqiman
added a commit
to zhaoqiman/mac-mouse-fix
that referenced
this pull request
Jul 13, 2026
…ed CGEvent offsets CGEventSetIOHIDEvent() attaches an IOHIDEvent to a CGEvent by poking the pointer into the opaque CGEvent/CGSEventRecord struct at hardcoded offsets (0x18 / 0xd0). Those offsets were never a stable ABI, and the struct layout shifted on macOS 27, so the pointer ends up at the wrong address and Dock never sees a real IOHIDEvent - which is why every synthetic dock-swipe gesture (Spaces, Mission Control, Show Desktop, Launchpad) silently stopped doing anything. See noah-nuebling#1871 and the pile of duplicates (noah-nuebling#1873, noah-nuebling#1878, noah-nuebling#1887, noah-nuebling#1891, noah-nuebling#1892, noah-nuebling#1919, ...). SkyLight.framework is already linked into the Mac Mouse Fix and Mac Mouse Fix Helper targets, so instead of poking memory ourselves we can just resolve Apple's own SLEventSetIOHIDEvent with dlsym(RTLD_DEFAULT, ...) and call that. Using RTLD_DEFAULT instead of dlopen()'ing the framework again since it's already loaded in-process. Falls back to the old offset writer if the symbol is ever missing (now with a DDLogWarn so it's not a silent failure this time). Tested on macOS 27 - Spaces/Mission Control/Show Desktop click-and-drag work again. Same root cause as noah-nuebling#1920, noah-nuebling#1912, noah-nuebling#1916 (also switch to SLEventSetIOHIDEvent); related to noah-nuebling#1895, noah-nuebling#1918.
zhaoqiman
added a commit
to zhaoqiman/mac-mouse-fix
that referenced
this pull request
Jul 13, 2026
…ed CGEvent offsets CGEventSetIOHIDEvent() attaches an IOHIDEvent to a CGEvent by poking the pointer into the opaque CGEvent/CGSEventRecord struct at hardcoded offsets (0x18 / 0xd0). Those offsets were never a stable ABI, and the struct layout shifted on macOS 27, so the pointer ends up at the wrong address and Dock never sees a real IOHIDEvent - which is why every synthetic dock-swipe gesture (Spaces, Mission Control, Show Desktop, Launchpad) silently stopped doing anything. See noah-nuebling#1871 and the pile of duplicates (noah-nuebling#1873, noah-nuebling#1878, noah-nuebling#1887, noah-nuebling#1891, noah-nuebling#1892, noah-nuebling#1919, ...). SkyLight.framework is already linked into the Mac Mouse Fix and Mac Mouse Fix Helper targets, so instead of poking memory ourselves we can just resolve Apple's own SLEventSetIOHIDEvent with dlsym(RTLD_DEFAULT, ...) and call that. Using RTLD_DEFAULT instead of dlopen()'ing the framework again since it's already loaded in-process. Falls back to the old offset writer if the symbol is ever missing (now with a DDLogWarn so it's not a silent failure this time). Tested on macOS 27 - Spaces/Mission Control/Show Desktop click-and-drag work again. Same root cause as noah-nuebling#1920, noah-nuebling#1912, noah-nuebling#1916 (also switch to SLEventSetIOHIDEvent); related to noah-nuebling#1895, noah-nuebling#1918.
zhaoqiman
added a commit
to zhaoqiman/mac-mouse-fix
that referenced
this pull request
Jul 14, 2026
…ed CGEvent offsets CGEventSetIOHIDEvent() attaches an IOHIDEvent to a CGEvent by poking the pointer into the opaque CGEvent/CGSEventRecord struct at hardcoded offsets (0x18 / 0xd0). Those offsets were never a stable ABI, and the struct layout shifted on macOS 27, so the pointer ends up at the wrong address and Dock never sees a real IOHIDEvent - which is why every synthetic dock-swipe gesture (Spaces, Mission Control, Show Desktop, Launchpad) silently stopped doing anything. See noah-nuebling#1871 and the pile of duplicates (noah-nuebling#1873, noah-nuebling#1878, noah-nuebling#1887, noah-nuebling#1891, noah-nuebling#1892, noah-nuebling#1919, ...). Now prefers Apple's own SLEventSetIOHIDEvent, resolved once via the project's existing MFLoadSymbol_native(kMFFrameworkSkyLight, ...) helper (Shared/Utility/PrivateFunctions) instead of a hand-rolled dlsym call. Also retains the IOHIDEventRef on this path, same as the old offset-writer did, since SLEventSetIOHIDEvent's retain contract isn't documented and an extra retain is safer than an under-retain. The offset-writer is kept as a fallback only for pre-macOS-27, where it was actually validated - on 27+ it's skipped instead of executed, since those offsets are already known to be wrong there and running it would provide no real safety net, just continued exposure to writing an unvalidated address. Tested on macOS 27 - Spaces/Mission Control/Show Desktop click-and-drag all work again after this. Same root cause as noah-nuebling#1920, noah-nuebling#1912, noah-nuebling#1916 (also switch to SLEventSetIOHIDEvent); related to noah-nuebling#1895, noah-nuebling#1918.
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.
Summary
Notes/macOS27DockSwipe.mdSource / attribution
This builds on PR #1895 by @megyland:
#1895
That PR identified the macOS 27 dock-swipe breakage and the need to embed raw HID payload data in serialized
CGEventfield4205, referencing the InstantSpaceSwitcher / FasterSwiper reverse-engineering work.Root cause
On macOS 27, Dock no longer appears to act on only the public synthetic
CGEventgesture fields for dock swipes. It expects the raw IOKit HID gesture payload inside the serialized event. For modified-drag gestures on multi-display setups, Dock also appears to tie the gesture stream to the payload position/display, so continuing one synthetic stream after the real cursor crosses displays can leave Dock's gesture state stuck.Validation
git diff --cached --checkxcodebuild -project "/tmp/mac-mouse-fix/Mouse Fix.xcodeproj" -scheme "App - Release" -configuration Release -derivedDataPath "/tmp/MacMouseFix-pr-DerivedData" CODE_SIGNING_ALLOWED=NO build