How to Reset Hididletime on MACos 10.14

How to reset HIDIdleTime on macOS 10.14

The thing is that the registry property isn't a normal property, but is generated on the fly every time properties are queried (see _idleTimeSerializerCallback in the source).

Long story short, you need to force lastUndimEvent to be reset, which you can do with external method 6 of an IOHIDParamUserClient.

I don't speak Swift, but here is some C code that does precisely that:

// clang -o t t.c -Wall -O3 -framework CoreFoundation -framework IOKit
#include <stdio.h>
#include <stdint.h>
#include <mach/mach.h>
#include <CoreFoundation/CoreFoundation.h>

extern const mach_port_t kIOMasterPortDefault;

typedef mach_port_t io_object_t;
typedef io_object_t io_service_t;
typedef io_object_t io_connect_t;

kern_return_t IOObjectRelease(io_object_t object);
CFMutableDictionaryRef IOServiceMatching(const char *name) CF_RETURNS_RETAINED;
io_service_t IOServiceGetMatchingService(mach_port_t master, CFDictionaryRef matching CF_RELEASES_ARGUMENT);
kern_return_t IOServiceOpen(io_service_t service, task_t task, uint32_t type, io_connect_t *client);
kern_return_t IOServiceClose(io_connect_t client);
kern_return_t IOConnectCallScalarMethod(io_connect_t client, uint32_t selector, const uint64_t *in, uint32_t inCnt, uint64_t *out, uint32_t *outCnt);

const uint32_t kIOHIDParamConnectType = 1;
const uint32_t kIOHIDActivityUserIdle = 3;
const uint32_t kIOHIDActivityReport = 0;
const uint32_t kIOHIDParam_extSetStateForSelector = 6;

#define LOG(str, args...) do { fprintf(stderr, str "\n", ##args); } while(0)

int hid_reset(void)
{
int retval = -1;
kern_return_t ret = 0;
io_service_t service = MACH_PORT_NULL;
io_connect_t client = MACH_PORT_NULL;

service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOHIDSystem"));
LOG("service: %x", service);
if(!MACH_PORT_VALID(service)) goto out;

ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &client);
LOG("client: %x, %s", client, mach_error_string(ret));
if(ret != KERN_SUCCESS || !MACH_PORT_VALID(client)) goto out;

uint64_t in[] = { kIOHIDActivityUserIdle, kIOHIDActivityReport };
ret = IOConnectCallScalarMethod(client, kIOHIDParam_extSetStateForSelector, in, 2, NULL, NULL);
LOG("extSetStateForSelector: %s", mach_error_string(ret));
if(ret != KERN_SUCCESS) goto out;

retval = 0;

out:;
if(MACH_PORT_VALID(client)) IOServiceClose(client);
if(MACH_PORT_VALID(service)) IOObjectRelease(service);
return retval;
}

int main(void)
{
return hid_reset();
}

It works for me on High Sierra as non-root, haven't tested it elsewhere. I do run a non-standard system configuration though, so if you get an error saying (iokit/common) not permitted on the external method, it's likely you're hitting mac_iokit_check_hid_control and might need additional entitlements, accessibility clearance, or something like that.

Question about macOS's caffeinate utility

The -i flag uses the PreventUserIdleSystemSleep assertion status which prevents the entire system from idle sleeping, which is the default mode when no assertion flags are specified.

The -s flag uses the PreventSystemSleep assertion status, which is only valid when your device is connected to an AC power.

The core difference is that with -i flag, it prevents the kind of sleep that is activated when the user sits idle for a certain period of time, but does not prevent the system from sleeping when you (or something else) command it to. The -s flag, on the other hand, prevents the entire system from sleeping regardless, even when it's instructed to do so.

P.S. You can see the assertion type created by caffeinate using pmset -g assertions | grep 'caffeinate'.

Applying Non-Standard Power Assertions & Creating Virtual HIDs

Would Apple notarize an app using this?

I haven't seen any issues with notarising code that uses private APIs. Currently, Apple only seems to use notarisation for scanning for inclusion of known malware.

Would someone be willing to take a look at the foohid project and help me understand whether it is theoretically possible to include this functionality in an App Store compatible app?

Taking a quick glance at the code of that project, it's clear it implements a kernel extension (kext). Those are not allowed on the App Store.

However, since macOS 10.15 Catalina, there's a new way to write HID drivers, using DriverKit. The idea is that the APIs are very similar to the kernel APIs, although I suspect it'll be a rewrite of the kext as a DriverKit driver, rather than a simple port.

  • DriverKit drivers are permitted to be included in App Store apps.
  • I don't know if a DriverKit based HID driver will solve your specific power management issue.
  • If you go with a DriverKit solution, this will only work on 10.15+.

I suspect that AntiSleep may also be creating a virtual keyboard and mouse.

I haven't looked at AntiSleep, but I do know that in addition to writing an outright HID driver, it's possible to generate HID events using user space APIs such as IOHIDPostEvent(). I don't know if those are allowed on the App Store, but as far as I'm aware, IOKitLib is generally fine.

It's possible you might be able to implement your virtual input device using those.

How to detect if user is away in OS X?

Here's a command that might work for you. This will tell you how long it's been since the mouse moved or a keystroke was pressed.

set idleTime to do shell script "ioreg -c IOHIDSystem | awk '/HIDIdleTime/ {print $NF/1000000000; exit}'"

So you might assume that if no key was pressed or the mouse was moved for a period of time then the user is not using the computer. With some clever tracking of the idleTime you can tell when the user left the computer and also when he returned. Something like this.

Save this as an applescript application and check the "stay open after run handler" checkbox. You can quit it any time by right-clicking on the dock icon and choosing quit.

global timeBeforeComputerIsNotInUse, computerIsInUse, previousIdleTime

on run
set timeBeforeComputerIsNotInUse to 300 -- 5 minutes
set computerIsInUse to true
set previousIdleTime to 0
end run

on idle
set idleTime to (do shell script "ioreg -c IOHIDSystem | awk '/HIDIdleTime/ {print $NF/1000000000; exit}'") as number

if not computerIsInUse then
if idleTime is less than previousIdleTime then
set computerIsInUse to true
say "User is using the computer again."
end if
else if idleTime is greater than or equal to timeBeforeComputerIsNotInUse then
set computerIsInUse to false
say "User has left the computer."
end if

set previousIdleTime to idleTime
return 1
end idle

Question about macOS's caffeinate utility

The -i flag uses the PreventUserIdleSystemSleep assertion status which prevents the entire system from idle sleeping, which is the default mode when no assertion flags are specified.

The -s flag uses the PreventSystemSleep assertion status, which is only valid when your device is connected to an AC power.

The core difference is that with -i flag, it prevents the kind of sleep that is activated when the user sits idle for a certain period of time, but does not prevent the system from sleeping when you (or something else) command it to. The -s flag, on the other hand, prevents the entire system from sleeping regardless, even when it's instructed to do so.

P.S. You can see the assertion type created by caffeinate using pmset -g assertions | grep 'caffeinate'.

Convert Objective-C code to C++ for detecting user idle time on OS X

You just need to add IOKit.framework in the list of linked frameworks. Think of a framework as a bundle of shared libraries and associated resources. IOKit.framework is at

 /System/Library/Frameworks/IOKit.framework

I don't know how to do that in a Qt project; the project should have a list of extra frameworks which you want to link against.
If it's a standard XCode project, there's a menu entry called add a framework to the project or something like that.



Related Topics



Leave a reply



Submit