1. 程式人生 > >OSX下監聽休眠(sleep)和喚醒(wake)通知

OSX下監聽休眠(sleep)和喚醒(wake)通知

前言

很多時候我們的客戶端軟體在電腦休眠之前都需要做一些事情,比如儲存,清理工作等事件。這個時候我們就希望在電腦休眠前能夠接收到通知,然後有足夠的時間去處理這些事情,讓我們的軟體更好的執行,帶來更好的使用者體驗。本文來源於蘋果的官方文件,原文地址。個人翻譯過來,希望可以幫助有需要的同學,讓mac的中文資料更多一點。

正文

Cocoa 和 I/O Kit都可以用來接收休眠和喚醒通知,除了正常的接收通知以外,I/O Kit還可以阻止和延遲閒置休眠。注意,即使是I/O Kit,也無法阻止強制休眠,只能延遲強制休眠。

提示:Mac OS X 有兩種休眠模式- 強制休眠和閒置休眠.

  • 當用戶執行一些操作導致的電腦休眠是強制休眠。 盒上筆記本的蓋子,從右上角的蘋果選單中選擇休眠都會導致強制休眠. 在一些特定的情況下,系統也會觸發強制休眠, 比如, 溫度過高或電量過低.

  • 在系統設定中節能下設定的時間長度下,電腦都沒有被使用,則會進入閒置休眠。

清單1:在Cocoa下注冊休眠和喚醒通知

- (void) receiveSleepNote: (NSNotification*) note
{
    NSLog(@"receiveSleepNote: %@", [note name]);
}
 
- (void) receiveWakeNote: (NSNotification*) note
{
    NSLog(@"receiveWakeNote: %@", [note name]);
}
 
- (void) fileNotifications
{
    //These notifications are filed on NSWorkspace's notification center, not the default
// notification center. You will not receive sleep/wake notifications if you file //with the default notification center. [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self selector: @selector(receiveSleepNote:) name: NSWorkspaceWillSleepNotification
object: NULL]; [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self selector: @selector(receiveWakeNote:) name: NSWorkspaceDidWakeNotification object: NULL]; }

提示:IOPMAssertionCreateWithName 是一個在Mac OS X 10.6雪豹系統下新增的API。IOPMAssertionCreateWithName 允許應用彈出一個帶有簡要說明的提示框,告訴使用者為什麼要阻止休眠。如果你想要支援早期的Mac OS X 版本,你可能會使用到基於清單3和4的API,或者已經被啟動的API——IOPMAssertionCreate 。

清單2:在Mac OS X 10.6中使用I/O Kit阻止休眠

...
 
#import <IOKit/pwr_mgt/IOPMLib.h>
 
...
// kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
// kIOPMAssertionTypeNoIdleSleep prevents idle sleep
 
//reasonForActivity is a descriptive string used by the system whenever it needs
//  to tell the user why the system is not sleeping. For example,
//  "Mail Compacting Mailboxes" would be a useful string.
 
//  NOTE: IOPMAssertionCreateWithName limits the string to 128 characters.
CFStringRef* reasonForActivity= CFSTR("Describe Activity Type");
 
IOPMAssertionID assertionID;
IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
                                    kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
if (success == kIOReturnSuccess)
{
 
    //Add the work you need to do without
    //  the system sleeping here.
 
    success = IOPMAssertionRelease(assertionID);
    //The system will be able to sleep again.
}
...

清單3:在 I/O Kit下注冊休眠和喚醒通知

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
 
#include <mach/mach_port.h>
#include <mach/mach_interface.h>
#include <mach/mach_init.h>
 
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/IOMessage.h>
 
io_connect_t  root_port; // a reference to the Root Power Domain IOService
 
void
MySleepCallBack( void * refCon, io_service_t service, natural_t messageType, void * messageArgument )
{
    printf( "messageType %08lx, arg %08lx\n",
        (long unsigned int)messageType,
        (long unsigned int)messageArgument );
 
    switch ( messageType )
    {
 
        case kIOMessageCanSystemSleep:
            /* Idle sleep is about to kick in. This message will not be sent for forced sleep.
                Applications have a chance to prevent sleep by calling IOCancelPowerChange.
                Most applications should not prevent idle sleep.
 
                Power Management waits up to 30 seconds for you to either allow or deny idle
                sleep. If you don't acknowledge this power change by calling either
                IOAllowPowerChange or IOCancelPowerChange, the system will wait 30
                seconds then go to sleep.
            */
 
            //Uncomment to cancel idle sleep
            //IOCancelPowerChange( root_port, (long)messageArgument );
            // we will allow idle sleep
            IOAllowPowerChange( root_port, (long)messageArgument );
            break;
 
        case kIOMessageSystemWillSleep:
            /* The system WILL go to sleep. If you do not call IOAllowPowerChange or
                IOCancelPowerChange to acknowledge this message, sleep will be
                delayed by 30 seconds.
 
                NOTE: If you call IOCancelPowerChange to deny sleep it returns
                kIOReturnSuccess, however the system WILL still go to sleep.
            */
 
            IOAllowPowerChange( root_port, (long)messageArgument );
            break;
 
        case kIOMessageSystemWillPowerOn:
            //System has started the wake up process...
            break;
 
        case kIOMessageSystemHasPoweredOn:
            //System has finished waking up...
        break;
 
        default:
            break;
 
    }
}
 
 
int main( int argc, char **argv )
{
    // notification port allocated by IORegisterForSystemPower
    IONotificationPortRef  notifyPortRef;
 
    // notifier object, used to deregister later
    io_object_t            notifierObject;
   // this parameter is passed to the callback
    void*                  refCon;
 
    // register to receive system sleep notifications
 
    root_port = IORegisterForSystemPower( refCon, &notifyPortRef, MySleepCallBack, &notifierObject );
    if ( root_port == 0 )
    {
        printf("IORegisterForSystemPower failed\n");
        return 1;
    }
 
    // add the notification port to the application runloop
    CFRunLoopAddSource( CFRunLoopGetCurrent(),
            IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes );
 
    /* Start the run loop to receive sleep notifications. Don't call CFRunLoopRun if this code
        is running on the main thread of a Cocoa or Carbon application. Cocoa and Carbon
        manage the main thread's run loop for you as part of their event handling
        mechanisms.
    */
    CFRunLoopRun();
 
    //Not reached, CFRunLoopRun doesn't return in this case.
    return (0);
    
}

停止接收 I/O Kit 的休眠通知, 你需要從應用程式的runloop中刪除事件源,並做一些清理工作。

清單4:移除 I/O Kit休眠/喚醒通知

...
    // we no longer want sleep notifications:
 
    // remove the sleep notification port from the application runloop
    CFRunLoopRemoveSource( CFRunLoopGetCurrent(),
                           IONotificationPortGetRunLoopSource(notifyPortRef),
                           kCFRunLoopCommonModes );
 
    // deregister for system sleep notifications
    IODeregisterForSystemPower( &notifierObject );
 
    // IORegisterForSystemPower implicitly opens the Root Power Domain IOService
    // so we close it here
    IOServiceClose( root_port );
 
    // destroy the notification port allocated by IORegisterForSystemPower
    IONotificationPortDestroy( notifyPortRef );
...

結尾

翻譯有不足的地方,希望大家諒解,也歡迎發現問題指正出來,我會及時處理更改。有什麼問題也可以留言一起討論。