Objective C: Get notifications about a user's

2019-01-14 14:01发布

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?

4条回答
The star\"
2楼-- · 2019-01-14 14:50

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

查看更多
▲ chillily
3楼-- · 2019-01-14 14:52

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

查看更多
我想做一个坏孩纸
4楼-- · 2019-01-14 15:04

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

查看更多
做个烂人
5楼-- · 2019-01-14 15:04

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.

查看更多
登录 后发表回答