Learn the basics of programming your own Mac OS X screensaver
The ?Wirbel? Screensaver
Apple?s default screensavers for Mac OS X are nice, but they get boring after a while. Any true nerd would take matters into their own hands when that happens!
If you want to make your own custom screensaver for Mac OS X, this article is for you. We?ll walk through a tutorial for making a screensaver which emulates the game Pong. It?s going to be super simple ? this is just to get you started with how to make a screensaver. By the end, it will look something like this:
If you want inspiration to make something else, more complex screensavers are available here.
Step 1: Project Setup
Screensavers for Mac OS X are developed with Xcode. The first thing you?ll need to do is create a new Xcode project with the category ?Screen Saver.?
I named my project Pong, but you can name yours anything you?d like. As of Xcode 10.2.1, Xcode will generate some files: PongView.h, PongView.m, and Info.plist.
Since Objective-C isn?t the prettiest language and Swift is becoming more mainstream, we?re going to delete these auto-generated Objective-C files and create a new file, PongView.swift.
The structure of this Swift file is quite simple. You can copy-paste the template from below:
We will use the function draw() to render content for each frame of the screensaver animation.
We will use the function animateOneFrame() to update the ?state? of the screensaver every time the animation timer fires (this timer is created and handled automatically by the OS). It?s important to call setNeedsDisplay(bounds) at the end of this function so that the OS knows to re-draw the screen.
Important: Since we got rid of the Objective-C stuff and are using Swift, the project?s Info.plist file needs to be updated. Prefix the value for the key ?Principal class? with the name of the Xcode project. For example, my Xcode project is called Pong, so I changed the value from PongView to Pong.PongView.
Step 2: Implement Logic
The state of our pong screensaver is going to be tracked with just a few simple variables:
In the init() function, we?re going to configure the ball?s initial position and velocity. We set the initial position to the center of the screen and set the initial velocity to a random vector with magnitude 10.
Now we need some helper functions to determine when the ball makes contact with the edges of the screen and/or with the paddle. The following two functions will do the trick:
The function ballHitPaddle() is not 100% robust for verifying contact between the ball and paddle, I know. But for our purposes, it?s good enough.
Now, we?re going to use these helper functions to update the ?state? of our screensaver in animateOneFrame().
The paddle is guaranteed to track the x-position of the ball. Therefore, it will always make contact with the ball before it bounces on the bottom of the screen (unless you set the ball?s velocity really high, in which case it could ?jump through? the paddle).
Step 3: Draw Animations
The only thing left to do is to draw the ball and paddle in the draw() function. Note that we re-draw the background first before drawing anything else, otherwise the ball would leave a ?trail? as it bounces around the screen.
We also define more helper functions to compartmentalize the drawing logic:
Step 4: Install and Debug
At this point, the screensaver is ready to go. You can find all of the code here.
To install the screensaver, press the ?Run? button on Xcode, open the build product Pong.saver in Finder, and double-click it. The Settings application should open up, and you?ll be prompted to install the screensaver.
If you make changes to the screensaver and want to re-install it, it?s important to close the Settings application before trying to re-install your screensaver.
It?s not ideal to test your screensaver using the tedious process of opening your build product in Finder, closing the Settings application, re-installing the screensaver, and etc.
Moreover, this method does not allow you to set breakpoints, interact with lldb, or view console output. But there?s a way!
Your screensaver class inherits from ScreenSaverView, which itself inherits from NSView. This means that it can be added to a subview of a regular old Mac OS X application.
Just create a new Xcode project of type ?Cocoa App.? Then, add your screensaver as a subview of the main NSViewController’s view generated by Xcode. We need to artificially create a timer for the animation loop, but it?s a pretty simple fix.
When you run this Cocoa App from Xcode, all of the normal tools such as breakpoints and console logs will be available for your debugging pleasure, and developing your screensaver will be a lot easier!
This was just an introduction to developing a Mac OS X screensaver. Your possibilities are only constrained by Apple?s API, and there?s a whole lot more that can be done.
- Additional examples are available at https://trevphil.com/screensavers.
- Complete code for PongView.swift is available here.