Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 35 additions & 15 deletions Loop/Private APIs/SkyLightToolBelt.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,34 @@ enum SkyLightToolBelt {
}

///
/// Byte layout for the synthetic `CGSEventRecord` posted by `makeKeyWindow`.
/// Offsets match CGSInternal's CGSEvent.h / yabai / AltTab.
private enum MakeKeyWindowEvent {
/// Allocated buffer size. The record's declared length stays `recordLength`;
/// we allocate a little more because newer macOS WindowServer encoding can
/// read past the record (see AltTab / paneru#123).
static let bufferSize = 0x100
static let lengthOffset = 0x04
static let recordLength: UInt8 = 0xF8
static let eventTypeOffset = 0x08
static let leftMouseDown: UInt8 = 0x01
static let leftMouseUp: UInt8 = 0x02
/// Window-relative click point. Just outside the frame so the window becomes
/// key without hitting content. Must be finite — `0xFF` fill decodes as NaN
/// and can terminate Chromium PWA app-shim Mojo connections (#1131).
static let windowLocationOffset = 0x20
static let offContentPoint = CGPoint(x: -1, y: -1)
static let unknownFlagOffset = 0x3A
static let unknownFlagValue: UInt8 = 0x10
static let windowIdOffset = 0x3C
}

/// Focuses a window. This will attempt to bring the window to the front and make it the active window.
/// Note that this first sets the process as frontmost, *then* sends a left click event to the window itself.
///
/// This method uses a private API to focus the window.
/// The code for this method is derived from the Amethyst source code. Details of its implementation can be found [here](https://github.com/Hammerspoon/hammerspoon/issues/370#issuecomment-545545468)
/// Uses a private API. Derived from Hammerspoon / yabai / AltTab
/// (https://github.com/Hammerspoon/hammerspoon/issues/370#issuecomment-545545468,
/// https://github.com/lwouis/alt-tab-macos/commit/782f1fe2e7272f185526e3e69eadd08c241fe050).
///
/// - Parameters:
/// - windowID: The `CGWindowID` of the window to focus.
Expand All @@ -117,19 +140,16 @@ enum SkyLightToolBelt {
return false
}

// `0x01` is left click down, `0x02` is left click up (see `CGEventType`)
for byte in [0x01, 0x02] {
// Create raw `SLSEvent` data.
// Future consideration: instead of manually creating the bytes here, investigate:
// - Creating a `SLSEvent` (likely analogous to `CGEvent`)
// - Apply an identifier to the event to help Loop differentiate events that originate from itself
// - Converting the `SLSEvent` to data using `SLEventCreateData` in SkyLight
var bytes = [UInt8](repeating: 0, count: 0xF8)
bytes[0x04] = 0xF8
bytes[0x08] = UInt8(byte)
bytes[0x3A] = 0x10
memcpy(&bytes[0x3C], &wid, MemoryLayout<UInt32>.size)
memset(&bytes[0x20], 0xFF, 0x10)
var offContentPoint = MakeKeyWindowEvent.offContentPoint

for eventType in [MakeKeyWindowEvent.leftMouseDown, MakeKeyWindowEvent.leftMouseUp] {
var bytes = [UInt8](repeating: 0, count: MakeKeyWindowEvent.bufferSize)
bytes[MakeKeyWindowEvent.lengthOffset] = MakeKeyWindowEvent.recordLength
bytes[MakeKeyWindowEvent.eventTypeOffset] = eventType
bytes[MakeKeyWindowEvent.unknownFlagOffset] = MakeKeyWindowEvent.unknownFlagValue
memcpy(&bytes[MakeKeyWindowEvent.windowIdOffset], &wid, MemoryLayout<UInt32>.size)
memcpy(&bytes[MakeKeyWindowEvent.windowLocationOffset], &offContentPoint, MemoryLayout<CGPoint>.size)

let cgStatus = bytes.withUnsafeMutableBufferPointer { pointer in
SLPSPostEventRecordTo(&psn, &pointer.baseAddress!.pointee)
}
Expand Down
Loading