NeverSleep Again
Recently I’ve been continuing my adventures in the wonderful land of Cocoa development.
Tonights adventure:
NeverSleep
The other night I was sitting on The Couch (it’s such a big piece of furniture that it has to be a proper noun), trying to decide whether I should watch Conan or read some of this awesome book. But wait! There was a horrible catch! I was also waiting for someone near and dear to IM me. But if I set my laptop on the table to watch the hilarious stylings of the adorable red headed giant the display would go to sleep and I would never see those important messages.
Now I could do what a normal person would do and go into System Preferences, tell it not to sleep the display, and then just remember to turn display sleeping back lest I forget.
But I am a Nerd so of course the easy course was out. I was going to take the “easy” course by solving this general problem. So off goes Conan, down goes my book and I start on NeverSleep.
The Goal
Here were the goals I set out for the app:
- A small, unobtrusive status bar icon
- A simple menu to toggle Display Sleeping
- Not letting the display sleeping be disabled if we’re running on battery power
- “Remember” what state we should be in
The Code
The code is currently living here on github : https://github.com/ecoffey/NeverSleep.
Cocoa makes this pretty easy actually.
We use the IO Power Management API to set Assertions on the display. We set two. One to control Display Sleep, and another to control Idle Sleep.
We then use the IO Power Sources API to tie into power changes. I liked this code because it’s all event driven:
CFRunLoopSourceRef loopSource = IOPSNotificationCreateRunLoopSource(updatePowerStatus, delegate);
if (loopSource)
{
CFRunLoopAddSource(CFRunLoopGetCurrent(), loopSource, kCFRunLoopDefaultMode);
}That’s all the code you need to register power status change notifications. The updatePowerStatus function is a little wonky since it mainly deals with Core Foundation classes, which includes some Refs and Releases.
The code layout is pretty simple. MenuletController handles the awakeFromNib: event, and coordinates the other two objects PowerMonitor and DisplayControl. Guessing what they do is left as an exercise for the reader.
DisplayControl handles our “remember” magic. Basically we track if we should disable display sleep, and if we can disable display sleep.
The first one is controlled by the status bar menu. The second variable is controlled by PowerMonitor, proxied through MenuletController.
Future features
For some reason disable the menu items still lets you click and interact with them. Would like to figure that out, so when you can’t toggle the display sleeping, the UI won’t just let you uselessly click on things.
Also would be really nice to have some way to force display sleeping off while on battery. And some growl notification to let you know when the state switches through no direct NeverSleep UI interaction.
Also I’d like the status bar icon to change depending on what state we’re in, but I have fail worthy graphic skills. Although the github repo does contain the pings I’d like to combine to represent the two different states. Hint hint.
Ok so maybe I’ll sleep again
All in all, this was a quick little app to get a first draft of. The rest is polish. Cocoa makes it pretty easy to put these kind of things together.
If you want to actually use this, check it out from github and run it from there. I’ll post an actual app bundle at some point.