Objective C: Get notifications about a user's

2019-01-14 14:24发布

问题:

My cocoa app runs background tasks, which I would like to stop when the user becomes idle (no keyboard/mouse input) and then resume when the user becomes active again. Is there a way to register for idle-state notifications?

回答1:

In case you can't link to Carbon (ie. you want to compile x86_64 bit binary) you can wrap this function (which returns current idle time in seconds resolution as double - CFTimeInterval) in a timer:

#include <IOKit/IOKitLib.h>

CFTimeInterval CFDateGetIdleTimeInterval() {
    mach_port_t port;
    io_iterator_t iter;
    CFTypeRef value = kCFNull;
    uint64_t idle = 0;
    CFMutableDictionaryRef properties = NULL;
    io_registry_entry_t entry;

    IOMasterPort(MACH_PORT_NULL, &port);
    IOServiceGetMatchingServices(port, IOServiceMatching("IOHIDSystem"), &iter);
    if (iter) {
        if ((entry = IOIteratorNext(iter))) {
            if (IORegistryEntryCreateCFProperties(entry, &properties, kCFAllocatorDefault, 0) == KERN_SUCCESS && properties) {
                if (CFDictionaryGetValueIfPresent(properties, CFSTR("HIDIdleTime"), &value)) {
                    if (CFGetTypeID(value) == CFDataGetTypeID()) {
                        CFDataGetBytes(value, CFRangeMake(0, sizeof(idle)), (UInt8 *) &idle);
                    } else if (CFGetTypeID(value) == CFNumberGetTypeID()) {
                        CFNumberGetValue(value, kCFNumberSInt64Type, &idle);
                    }
                }
                CFRelease(properties);
            }
            IOObjectRelease(entry);
        }
        IOObjectRelease(iter);
    }

    return idle / 1000000000.0;
}

You'll need to link your code to IOKit.framework



回答2:

There's a Carbon API that will send a notification when there hasn't been a user event after a certain duration called EventLoopIdleTimer. Uli Kusterer has written a Cocoa wrapper for here (look for UKIdleTimer).

If you want something lower level, you may be able to implement the behavior you want with a combination of timers and the CoreGraphics function CGEventSourceSecondsSinceLastEventType (available in <CoreGraphics/CGEventSource.h>).



回答3:

Apple's Technical Q&A QA1340 Registering and unregistering for sleep and wake notifications may be what you are looking for.

If you need more control than NSWorkspaceWillSleepNotification (Listing 1), use I/O Kit and register to receive power notifications (Listing 3).



回答4:

I used a different approach. Subclassing UIApplication I override the sendEvent method filtering touches (actually you can filter any kind of event, acceleration, touches, etc.). Using a shared variable and a background timer I managed the "idle". Every time the user touch the screen the variable is set with current timeInterval (current time). The timer fire method checks for the elapsed time since last touch, if greater than the threshold (in my case was around 90seconds) you can POST your own notification.

I used this simple approach to create a custom set of apps that after some idle time automatically call the "screensaver" app.

Nothing clever, it just do the job.

Hope that helps.