如何在macOS 10.14上重置HIDIdleTime [英] How to reset HIDIdleTime on macOS 10.14

查看:149
本文介绍了如何在macOS 10.14上重置HIDIdleTime的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在过去的几天里,我一直在尝试编写一个可重置IORegistry> IOHIDSystem> HIDIdleTime条目的应用程序.最终目标将是防止读取此值的其他应用程序将用户标记为空闲(这不仅与电源管理有关,还与防止睡眠有关).假设已禁用沙箱,并且该应用程序具有所有必需的权限(如辅助功能访问权限).

这是我尝试执行的操作(到目前为止未成功):

尝试1-移动鼠标光标以模拟活动:

变体1:

let mouseCursorPosition = CGPoint(x: Int.random(in: 0...500), y: Int.random(in: 0...500))
CGWarpMouseCursorPosition(mouseCursorPosition)

变体2:

CGDisplayMoveCursorToPoint(CGMainDisplayID(), mouseCursorPosition)

变体3(单独使用CGEvent或与上述2个变体之一一起使用):

let moveEvent = CGEvent(mouseEventSource: nil, mouseType: 
CGEventType.mouseMoved, mouseCursorPosition: mouseCursorPosition, 
mouseButton: CGMouseButton.left)
moveEvent?.post(tap: CGEventTapLocation.cghidEventTap)

变体4(使用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)
    }
}

注意::我后来了解到,从macOS 10.12开始,IOHIDPostEvent不会重置HIDIdleTime(来源:

虽然这似乎可行(上面的函数返回true),但是该值随后被系统覆盖,从而跟踪内存中的实际空闲时间. Apple从IOHIDSystem 此脚本来轻松监控系统空闲时间并测试解决方案.

任何建议都将不胜感激.如果可能的话,我试图避免编写自己的虚拟驱动程序(尽管我愿意挂接到现有的驱动程序上,并尽可能模拟事件).

解决方案

问题是注册表属性不是普通属性,但是每次查询属性时都会动态生成(请参见_idleTimeSerializerCallback href ="https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-1090.260.23/IOHIDSystem/IOHIDSystem.cpp.auto.html" rel ="nofollow noreferrer">源).
长话短说,您需要强制重置lastUndimEvent,这可以通过IOHIDParamUserClient的外部方法6进行.

我不会讲Swift,但是下面的一些C代码正是这样做的:

 // 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();
}
 

它以非根用户身份在High Sierra上对我有效,尚未在其他地方进行过测试.我确实运行了非标准的系统配置,因此,如果在外部方法上出现错误消息(iokit/common) not permitted,则很可能是您击中了mac_iokit_check_hid_control,并且可能需要其他权利,可访问性清除之类的东西./p>

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).

解决方案

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.

这篇关于如何在macOS 10.14上重置HIDIdleTime的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆