To start the screensaver on OS X Lion, I use a system-wide keyboard shortcut that immediately starts the screensaver, using a simple applescript:
tell application "ScreenSaverEngine" to launch
Although this works perfectly in most cases, I have a strange issue when the mouse is moved exactly at the time the script is invoked. Then, the ScreenSaverEngine.app is loaded and the desktop is locked, but the ScreenSaverEngine UI does not show up. Instead, the desktop is still visible, but it is not possible to set focus on other applications or UI controls; the desktop session is basically locked.
The only way to 'unlock' the desktop is to force setting focus on the Activity Monitor app (by Ctrl-clicking its dock icon and selecting Show All Windows) and then killing the ScreenSaverEngine process.
The same behaviour can be easily reproduced by launching ScreenSaverEngine from the Terminal:
/System/Library/Frameworks/ScreenSaver.framework/Resources/ScreenSaverEngine.app/Contents/MacOS/ScreenSaverEngine
and actively moving the mouse cursor while pressing the Enter key.
Does anyone know a method that can be used to launch the screensaver programmatically, and does not suffer from this issue?
Note: I know that there are other options to lock the screen (Fast user switching, the Keychain lock and Hot Corners), but I'm not interested in those solutions, I need a way to programmatically start the screensaver.
I found the answer myself in the deep depths of Google. Using the undocumented ScreenSaverController class from the private ScreenSaver.framework to start the screensaver works as expected.
I use the following header:
#import <Foundation/Foundation.h>
@interface ScreenSaverController : NSObject
+ controller;
@end
@protocol ScreenSaverControl
- (BOOL) screenSaverIsRunning;
- (BOOL) screenSaverCanRun;
- (void) setScreenSaverCanRun:(BOOL)fp8;
- (void) screenSaverStartNow;
- (void) screenSaverStopNow;
- (void) restartForUser:(id)fp8;
- (double) screenSaverTimeRemaining;
- (void) screenSaverDidFade;
- (BOOL) screenSaverIsRunningInBackground;
- (void) screenSaverDidFadeInBackground:(BOOL)fp8
psnHi:(unsigned int)fp12
psnLow:(unsigned int)fp16;
@end
and link the ScreenSaver.framework to my project. Starting the screensaver is then as simple as:
[[ScreenSaverController controller] screenSaverStartNow];
The behaviour I described in my question cannot be reproduced when the screensaver is started this way. If the mouse is moved actively when invoking this method, the screen quickly flashes as the screensaver returns immediately (without locking the desktop).
Works on 10.6.8 and 10.7.4.
Disclaimer: use undocumented classes from Apple frameworks with care, as they are unsupported and functionality may break in future versions of OS X (consider weak linking). Also, it will disqualify your app from the App Store.