Skip to content

Fix Dock Swipe simulation on macOS 27 (Spaces & Mission Control) - #1916

Open
thiagotfroes wants to merge 1 commit into
noah-nuebling:masterfrom
thiagotfroes:fix/dockswipe-macos27
Open

Fix Dock Swipe simulation on macOS 27 (Spaces & Mission Control)#1916
thiagotfroes wants to merge 1 commit into
noah-nuebling:masterfrom
thiagotfroes:fix/dockswipe-macos27

Conversation

@thiagotfroes

Copy link
Copy Markdown

Problem

On macOS 27 (Golden Gate), the Spaces & Mission Control action stopped working when triggered via Click & Drag (switching Spaces, opening Mission Control / App Windows). The simulated dock swipe events were no longer registered by the system.

Root cause

The macOS 27 code path in TouchSimulator attaches an IOHIDEvent to a CGEvent via CGEventSetHIDEventCGEventSetIOHIDEvent. That function is a hand-rolled reimplementation that writes the IOHIDEvent pointer into the CGEvent using hardcoded offsets (0x18 / 0xd0) into the private CGEvent/CGSEventRecord layout.

macOS 27 changed that layout, so the pointer was written to the wrong address and the resulting dock swipe carried no valid HID event.

Fix

Prefer Apple's own private SLEventSetIOHIDEvent (SkyLight), resolved at runtime via dlsym, and fall back to the offset-based implementation only if the symbol can't be found. This matches the working approach explored in Tests/FixDockSwipes.m.

  • Backwards compatible: SLEventSetIOHIDEvent exists on older macOS too, so it's used there as well; the hand-rolled method stays as a graceful fallback. No macOS-version gate needed.
  • Why dlsym instead of linking SkyLight.framework: linking a private framework at build time could be an issue for the Mac App Store variant, whereas dlsym creates no link-time dependency and degrades gracefully if the symbol ever disappears.

Testing

  • Built and tested on macOS 27 Golden Gate Beta 3 (Xcode 27 beta 3), Apple Silicon (M2).
  • Middle-button Click & Drag → Spaces & Mission Control works again: switching Spaces (left/right), Mission Control (up), and App Windows (down).
  • Single file changed: Shared/IOKit/CGEventHIDEventBridge.m.

macOS 27 (Golden Gate) broke the "Spaces & Mission Control" action when
triggered via Click & Drag (switching Spaces, opening Mission Control,
App Windows). The dock swipe events were no longer being registered by
the system.

Root cause:
The macOS 27 code path in TouchSimulator attaches an IOHIDEvent to a
CGEvent via CGEventSetHIDEvent -> CGEventSetIOHIDEvent. That function is
a hand-rolled reimplementation that writes the IOHIDEvent pointer into
the CGEvent using hardcoded offsets (0x18 / 0xd0) into the private
CGEvent/CGSEventRecord layout. macOS 27 changed that layout, so the
pointer was written to the wrong address and the resulting dock swipe
carried no valid HID event.

Fix:
Prefer Apple's own private SLEventSetIOHIDEvent (SkyLight), resolved at
runtime via dlsym, and fall back to the offset-based implementation only
if the symbol can't be found. This matches the working approach explored
in Tests/FixDockSwipes.m.

Notes:
- Backwards compatible: SLEventSetIOHIDEvent is available on older macOS
  too, so it is used there as well; the hand-rolled method remains as a
  graceful fallback. No macOS-version gate is needed.
- Resolved via dlsym rather than linking SkyLight.framework on purpose:
  linking a private framework at build time could be a problem for the
  Mac App Store variant, whereas dlsym creates no link-time dependency
  and degrades gracefully if the symbol ever disappears.
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant