Мониторинг событий заставки в OSX

Я хочу отслеживать события заставки и блокировки экрана в окне OSX. Как первый проход, я в порядке с ними, просто печатая на консоли.

Следуя советам другого вопроса, я написал несколько Objective C для прослушивания Cocoa Уведомления для com.apple.screensaver.didstart, com.apple.screensaver.didstop, com.apple.screenIsLocked и com.apple.screenIsUnlocked.

// ScreenSaverMonitor.h
#import <Foundation/NSObject.h>
#import <Foundation/NSNotification.h>

@interface ScreenSaverMonitor: NSObject {}
-(id) init;
-(void) receive: (NSNotification*) notification;
@end

// ScreenSaverMonitor.m
#import "ScreenSaverMonitor.h"
#import <Foundation/NSString.h>
#import <Foundation/NSDistributedNotificationCenter.h>
#import <Foundation/NSRunLoop.h>
#import <stdio.h>

@implementation ScreenSaverMonitor
-(id) init {
  NSDistributedNotificationCenter * center 
    = [NSDistributedNotificationCenter defaultCenter];

  [center addObserver: self
          selector:    @selector(receive:)
          name:        @"com.apple.screensaver.didstart"
          object:      nil
  ];
  [center addObserver: self
          selector:    @selector(receive:)
          name:        @"com.apple.screensaver.didstop"
          object:      nil
  ];
  [center addObserver: self
          selector:    @selector(receive:)
          name:        @"com.apple.screenIsLocked"
          object:      nil
  ];
  [center addObserver: self
          selector:    @selector(receive:)
          name:        @"com.apple.screenIsUnlocked"
          object:      nil
  ];
  printf("running loop... (^C to quit)");
  [[NSRunLoop currentRunLoop] run];
  printf("...ending loop");
  return self;
}
-(void) receive: (NSNotification*) notification {
  printf("%s\n", [[notification name] UTF8String] );
}
@end

// ScreenSaverMonitorMain.m
#import "ScreenSaverMonitor.h"

int main( int argc, char ** argv) {
  [[ScreenSaverMonitor alloc] init];
  return 0;
}

Он компилируется отлично, но когда я его запускаю, я, кажется, не наблюдаю за событиями экранной заставки (несмотря на то, что заставка заходит многократно):

% gcc -Wall ScreenSaverMonitor.m ScreenSaverMonitorMain.m -o ScreenSaverMonitor -lobjc -framework Cocoa
% ./ScreenSaverMonitor
running loop (^C to quit)...
^C
%

Знания My Objective C и Cocoa очень ржавые, поэтому я не уверен, что я неправильно использую фреймворк, или если я зарегистрировал неправильные события (и где искать, чтобы выяснить, являются правильными событиями или нет).

Так что же я делаю неправильно?

Ответ 1

Вы уже прокомментировали свою проблему.

while(1); // busy wait bad, I know, but easy to implement

Вышеуказанное почти ВСЕГДА плохая идея.

NSDistributedNotificationCenter на самом деле требует работы основного NSRunLoop для работы.

http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Notifications/Articles/NotificationCenters.html#//apple_ref/doc/uid/20000216-BAJGDAFC

Создание и прятка цикла запуска из main() приложения командной строки в OS X - довольно простая задача. Существует множество примеров, доступных для быстрого поиска.

Ответ 2

Изменить: дальнейшее тестирование показало, что com.apple.screensaver.didlaunch также доступен, код здесь проверен на 10.6.8 и 10.8.4

Ваш код будет работать, если вы удалите [[NSRunLoop currentRunLoop] run]; и  инициализировать ScreenSaverMonitor из метода Cocoa application applicationDidFinishLaunching:. (Просто создайте новый проект приложения Cocoa в XCode и добавьте свой код там, где это необходимо).

ScreenSaverMonitor.h

#import <Foundation/Foundation.h>

@interface ScreenSaverMonitor : NSObject
-(id) init;
-(void) receive: (NSNotification*) notification;

@end

ScreenSaverMonitor.m

#import "ScreenSaverMonitor.h"
#import <Foundation/NSString.h>
#import <Foundation/NSDistributedNotificationCenter.h>
#import <Foundation/NSRunLoop.h>
#import <stdio.h>

@implementation ScreenSaverMonitor
-(id) init {
    NSDistributedNotificationCenter * center
    = [NSDistributedNotificationCenter defaultCenter];

    [center addObserver: self
               selector:    @selector(receive:)
                   name:        @"com.apple.screensaver.didlaunch"
                 object:      nil
     ];

    [center addObserver: self
               selector:    @selector(receive:)
                   name:        @"com.apple.screensaver.didstart"
                 object:      nil
     ];
    [center addObserver: self
               selector:    @selector(receive:)
                   name:        @"com.apple.screensaver.didstop"
                 object:      nil
     ];
    [center addObserver: self
               selector:    @selector(receive:)
                   name:        @"com.apple.screenIsLocked"
                 object:      nil
     ];
    [center addObserver: self
               selector:    @selector(receive:)
                   name:        @"com.apple.screenIsUnlocked"
                 object:      nil
     ];
    return self;
}
-(void) receive: (NSNotification*) notification {
    printf("%s\n", [[notification name] UTF8String] );
}

@end

AppDelegate.h

#import <Cocoa/Cocoa.h>
#import "ScreenSaverMonitor.h"

@interface AppDelegate : NSObject <NSApplicationDelegate>

@property (assign) IBOutlet NSWindow *window;
@property (retain) ScreenSaverMonitor *monitor;
@end

AppDelegate.m

#import "AppDelegate.h"
#import "ScreenSaverMonitor.h"

@implementation AppDelegate
@synthesize monitor;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
    self.monitor = [[ScreenSaverMonitor alloc] init];

}

@end

main.m

#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[])
{
    return NSApplicationMain(argc, (const char **)argv);
}

Ответ 3

Похоже, что стратегия, которую вы пытаетесь использовать для этого, не будет работать, потому что уведомления com.apple.screensaver. * больше не поддерживаются. В ответах на этот эквивалентный вопрос упоминается, что: "для Snow Leopard недоступны идентификаторы screenIsLocked и screenIsUnlocked".
Вы можете зарегистрироваться на экранные спящие, что, конечно, не одно и то же, но может быть приемлемой альтернативой для вас, прослушивая уведомление NSWorkspaceScreensDidSleepNotification или на компьютер, отправляющийся спать, прослушивая NSWorkspaceWillSleepNotification. Образец кода можно найти на этом форуме.

Sidenote: если вы используете nil для имени, вы получите много событий:

[center addObserver:self
           selector:@selector(receive:)
               name:nil
             object:nil
];

Если вы это сделаете, вы увидите, что делаете все в основном правильно, потому что вы получите все виды событий, но не заставки.