Same finding here, measured rather than guessed: on macOS 26 with a 120 Hz ProMotion panel, mouse-moved NSEvents reach a Cocoa app in bursts once per display refresh, 8.3 ms apart, no matter how fast the mouse reports — a 1000 Hz mouse gets merged into one event per frame. I haven't verified whether NSEvent.isMouseCoalescingEnabled = false restores the full rate on 26.2.
What does give you every report is reading the device directly with IOHIDManager on its own thread:
import Foundation
import IOKit.hid
let manager = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone))
IOHIDManagerSetDeviceMatching(manager, [kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop,
kIOHIDDeviceUsageKey: kHIDUsage_GD_Mouse] as NSDictionary as CFDictionary)
IOHIDManagerRegisterInputValueCallback(manager, { _, _, _, value in
let element = IOHIDValueGetElement(value)
guard IOHIDElementGetUsagePage(element) == UInt32(kHIDPage_GenericDesktop),
IOHIDElementIsRelative(element) else { return }
let usage = IOHIDElementGetUsage(element) // kHIDUsage_GD_X or kHIDUsage_GD_Y
let delta = IOHIDValueGetIntegerValue(value) // raw counts, no acceleration curve
let stamp = IOHIDValueGetTimeStamp(value) // mach time of the report
// X and Y of one report share a timestamp: accumulate until it changes.
}, nil)
IOHIDManagerScheduleWithRunLoop(manager, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
IOHIDManagerOpen(manager, IOOptionBits(kIOHIDOptionsTypeNone))
Things that bit us: it needs the Input Monitoring permission (IOHIDCheckAccess / IOHIDRequestAccess(kIOHIDRequestTypeListenEvent)); the values are raw device counts, so if you want the system's pointer feel you apply the curve yourself; and some mice expose two HID interfaces that report the same motion, so dedupe by device. For a pen tablet the Digitizer usage page (absolute coordinates) is the one to look at — I haven't tried that part.
Context: we ran into this in a game-input layer, where shooters need every one of those 1000 reports; reading HID like this is what fixed it.