For the past couple of days I've been trying to write an application that would reset the IORegistry
> IOHIDSystem
> HIDIdleTime
entry. The end goal would be to prevent other applications that read this value from marking the user as idle (it's not only about power management or preventing sleep). Assume that sandboxing is disabled and the application has all necessary permissions (such as accessibility access).
Here are my attempts at doing this (unsuccessful so far):
Attempt 1 - move the mouse cursor to simulate activity:
Variant 1:
let mouseCursorPosition = CGPoint(x: Int.random(in: 0...500), y: Int.random(in: 0...500))
CGWarpMouseCursorPosition(mouseCursorPosition)
Variant 2:
CGDisplayMoveCursorToPoint(CGMainDisplayID(), mouseCursorPosition)
Variant 3 (using CGEvent
by itself or together with one of the 2 variants above):
let moveEvent = CGEvent(mouseEventSource: nil, mouseType:
CGEventType.mouseMoved, mouseCursorPosition: mouseCursorPosition,
mouseButton: CGMouseButton.left)
moveEvent?.post(tap: CGEventTapLocation.cghidEventTap)
Variant 4 (using IOHIDSetMouseLocation
/ IOHIDPostEvent
):
func moveCursor() {
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOHIDSystem"))
if (service == 0) { return }
var connect:io_connect_t = 0
let result = IOServiceOpen(service, mach_task_self_, UInt32(kIOHIDParamConnectType), &connect)
IOObjectRelease(service)
if (result == kIOReturnSuccess) {
let cursorX = Int16.random(in: 0...100)
let cursorY = Int16.random(in: 0...100)
IOHIDSetMouseLocation(connect, Int32(cursorX), Int32(cursorY))
let cursorLocation:IOGPoint = IOGPoint(x: cursorX, y: cursorY)
var event:NXEventData = NXEventData()
IOHIDPostEvent(connect, UInt32(NX_MOUSEMOVED), cursorLocation, &event, 0, 0, 0)
}
}
NOTE: I've later learned that starting with macOS 10.12, IOHIDPostEvent
doesn't reset HIDIdleTime
(source: https://github.com/tekezo/Karabiner-Elements/issues/385). Also tried simulating keypresses without success.
Attempt 2 - overwrite the value directly in the IORegistry
func overwriteValue() -> Bool {
var iterator: io_iterator_t = 0
defer { IOObjectRelease(iterator) }
guard IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOHIDSystem"), &iterator) == kIOReturnSuccess else { return false }
let entry: io_registry_entry_t = IOIteratorNext(iterator)
defer { IOObjectRelease(entry) }
guard entry != 0 else { return false }
var value:NSInteger = 0;
var convertedValue:CFNumber = CFNumberCreate(kCFAllocatorDefault, CFNumberType.nsIntegerType, &value);
let result = IORegistryEntrySetCFProperty(entry, "HIDIdleTime" as CFString, convertedValue)
if (result != kIOReturnSuccess) { return false }
return true
}
While this seems to work (the function above returns true
), the value is then overwritten by the system, which keeps track of the actual idle time in memory. Got a bit of insight into this from the source code release by Apple for IOHIDSystem
here. Currently using this script to easily monitor system idle time and test solutions.
Any suggestions are greatly appreciated. If at all possible, I'm trying to avoid writing my own virtual driver (although I'm open to hooking into an existing one and simulating events if at all possible).