Skip to content

Commit

Permalink
refactor: observe mouse events from main thread: simpler/faster
Browse files Browse the repository at this point in the history
  • Loading branch information
lwouis committed Nov 27, 2024
1 parent f56c65d commit 3e423a1
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 16 deletions.
5 changes: 1 addition & 4 deletions src/logic/BackgroundWork.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ class BackgroundWork {
static var axCallsQueue: DispatchQueue!
static var crashReportsQueue: DispatchQueue!
static var accessibilityEventsThread: BackgroundThreadWithRunLoop!
static var mouseEventsThread: BackgroundThreadWithRunLoop!
static var keyboardEventsThread: BackgroundThreadWithRunLoop!
static var systemPermissionsThread: BackgroundThreadWithRunLoop!
static var repeatingKeyThread: BackgroundThreadWithRunLoop!
Expand All @@ -26,9 +25,7 @@ class BackgroundWork {
axCallsQueue = DispatchQueue.globalConcurrent("axCallsQueue", .userInteractive)
// we observe app and windows notifications. They arrive on this thread, and are handled off the main thread initially
accessibilityEventsThread = BackgroundThreadWithRunLoop("accessibilityEventsThread", .userInteractive)
// we observe mouse clicks when thumbnailsPanel is open. They arrive on this thread, and are handled off the main thread initially
mouseEventsThread = BackgroundThreadWithRunLoop("mouseEventsThread", .userInteractive)
// some instances of events can be handled off the main thread; maybe not worth moving to a background thread
// we listen to as any keyboard events as possible on a background thread, as it's more available/reliable than the main thread
keyboardEventsThread = BackgroundThreadWithRunLoop("keyboardEventsThread", .userInteractive)
// not 100% sure this shouldn't be on the main-thread; it doesn't do anything except dispatch to main.async
repeatingKeyThread = BackgroundThreadWithRunLoop("repeatingKeyThread", .userInteractive)
Expand Down
19 changes: 7 additions & 12 deletions src/logic/events/MouseEvents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Carbon.HIToolbox.Events

fileprivate var eventTap: CFMachPort!
fileprivate var shouldBeEnabled: Bool!
fileprivate var isPointerInsideUi: Bool!

class MouseEvents {
static func observe() {
Expand All @@ -20,7 +19,7 @@ class MouseEvents {

private func observe_() {
let eventMask = [CGEventType.leftMouseDown, CGEventType.leftMouseUp].reduce(CGEventMask(0), { $0 | (1 << $1.rawValue) })
// CGEvent.tapCreate returns null if ensureAccessibilityCheckboxIsChecked() didn't pass
// CGEvent.tapCreate returns nil if ensureAccessibilityCheckboxIsChecked() didn't pass
eventTap = CGEvent.tapCreate(
tap: .cgSessionEventTap,
place: .headInsertEventTap,
Expand All @@ -31,22 +30,20 @@ private func observe_() {
if let eventTap = eventTap {
MouseEvents.toggle(false)
let runLoopSource = CFMachPortCreateRunLoopSource(nil, eventTap, 0)
CFRunLoopAddSource(BackgroundWork.mouseEventsThread.runLoop, runLoopSource, .commonModes)
CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, .commonModes)
} else {
App.app.restart()
}
}

private let handleEvent: CGEventTapCallBack = { _, type, cgEvent, _ in
if type == .leftMouseDown {
isPointerInsideUi_()
if !isPointerInsideUi {
if !isPointerInsideUi() {
return nil // focused app won't receive the event
}
} else if type == .leftMouseUp && cgEvent.getIntegerValueField(.mouseEventClickState) >= 1 {
isPointerInsideUi_()
if !isPointerInsideUi {
DispatchQueue.main.async { App.app.hideUi() }
if !isPointerInsideUi() {
App.app.hideUi()
return nil // focused app won't receive the event
}
} else if (type == .tapDisabledByUserInput || type == .tapDisabledByTimeout) && shouldBeEnabled {
Expand All @@ -55,8 +52,6 @@ private let handleEvent: CGEventTapCallBack = { _, type, cgEvent, _ in
return Unmanaged.passUnretained(cgEvent) // focused app will receive the event
}

private func isPointerInsideUi_() {
DispatchQueue.main.sync {
isPointerInsideUi = App.app.thumbnailsPanel.contentLayoutRect.contains(App.app.thumbnailsPanel.mouseLocationOutsideOfEventStream)
}
private func isPointerInsideUi() -> Bool {
return App.app.thumbnailsPanel.contentLayoutRect.contains(App.app.thumbnailsPanel.mouseLocationOutsideOfEventStream)
}

0 comments on commit 3e423a1

Please sign in to comment.