23

I was wondering if there was any decent way, other than NSLog-ing just about everything - to properly debug a Screensaver app bundle in OS X?

The "Screensaver" is a project type in Xcode, but there's obviously no Build and Go debugging. Further, I've found that in fact my bundle is getting loaded in to the

/System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app 

application as some sort of plugin.

So is there a decent way to debug your code? Looking at crash reports and NSLog-ing to the console helps, but it's far from perfect.

Keith Smiley
  • 54,143
  • 12
  • 89
  • 105
Dave Martorana
  • 1,311
  • 13
  • 19

9 Answers9

16

There is an old MacTech article that describes the Screen Saver development cycle. There is also a Part 2 to the article. Look in the "Debugging Tips" section.

I find this method a pain so I wrote an application, the basic application was one window and a controller that initialized a ScreenSaverView with my new screensaver bundle. Once that was working all I had to do to test a change was hit Command-R in Xcode.

Mark
  • 5,952
  • 3
  • 33
  • 49
8

Because of OS X 10.11 El Capitan's System Integrity Protection feature, the debugger can't attach to anything running from /System/. Also, the other info here applies to old versions of Xcode.

Here's how I got it working on El Capitan with Xcode 7.2:

  1. Copy /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app/ to /tmp/. (Since the .xcscheme references the fully-qualified path, copying it to someplace common is best for collaboration, instead of to somewhere in a particular user's home directory.)
  2. Edit the project's .xcscheme:
    • Set the Executable for its Run action to the copied app, and add the arguments: -debug -background -module "<product-name>" (where <product-name> is the bundle name without the .saver extension).
    • Add a Pre-action script (source below), with its shell set to /bin/bash and its build settings to come from the scheme. It creates a symbolic link to the built .saver bundle in ~/Library/Screen Savers/

Source:

SCREEN_SAVER_PATH="${HOME}/Library/Screen Savers/${FULL_PRODUCT_NAME}"
if [[ -d "${SCREEN_SAVER_PATH}" || -f "${SCREEN_SAVER_PATH}" || -L "${SCREEN_SAVER_PATH}" ]]; then
    rm -Rf "${SCREEN_SAVER_PATH}"
fi
ln -s "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}" "${SCREEN_SAVER_PATH}"

Now, when you hit Xcode's Run button, the screen saver will run in wallpaper mode on your Desktop, and you can use the debugger.

6

You can debug plugins by executing the application that will load the plugin.

So to debug a screensaver, open your plugin project, choose New Custom Executable from the Project menu and set the application to be the screensaver engine.

For debugging a screensaver, you might also want to use a second Mac and use remote debugging so your user interface actions don't interfere with the screensaver.

Peter N Lewis
  • 17,346
  • 2
  • 38
  • 55
  • On Lion with XCode4, I found this was done by changing the executable of the scheme to /System/Frameworks/ScreenSaver.framework/Resources/ScreenSaverEngine.app – iforce2d Jan 06 '12 at 14:06
  • I also found you still need to install the screensaver into the system preferences as usual each time you change it, but that's still a darn sight better than waiting 3 minutes every time. – iforce2d Jan 06 '12 at 14:14
  • 1
    Instead of remote debugging you can pass "-background" as a command line argument to the ScreenSaverEngine to have it run as your desktop… – geowar May 16 '12 at 06:15
3

You could also make the screensaver engine ('/System/Library/Frameworks/ScreenSaver.framework/Resources/ScreenSaverEngine.app') the target executable and pass it the -background flag (so it runs behind everything instead of in front of everything).

geowar
  • 4,226
  • 1
  • 25
  • 23
  • 2
    Unfortunately, this does not work on 10.11 El Capitan because System Integrity Protection prevents running ScreenSaverEngine within the debugger. – Jay Lieske Dec 09 '15 at 06:18
  • 1
    It's working for me here… perhaps because I've disabled SIP? – geowar Dec 09 '15 at 22:25
  • 2
    Right, disabling SIP lets you debug /System apps like ScreenSaverEngine. I did figure out a workaround, however: make a duplicate of ScreenSaverEngine.app by copying it out of the /System folder hierarchy, and then set that duplicate as the target executable. The duplicate copy of the app is no longer subject to SIP, so it can be debugged by Xcode. – Jay Lieske Dec 10 '15 at 07:21
3

There's a few Mac OS X apps that will run screen savers: SaverLab, Screenalicious, etc. Just find one of them on the web and download it and then set it as the target executable (as Peter N Lewis said).

To avoid copying the build product to '~/Library/Screen Savers/' after each build you can add a custom build script (note: I'm using '/bin/tcsh -x' for the shell):

#remove the old screen saver or link
rm -Rf "${SCRIPT_OUTPUT_FILE_0}"

#if this is a debug build…
if ("${CONFIGURATION}" == "Debug" ) then

# create a symbolic link from our screen saver to this users screen saver directory
ln -sfv "${SCRIPT_INPUT_FILE_0}" "${SCRIPT_OUTPUT_FILE_0}"

#if this is a release build…
else if ("${CONFIGURATION}" == "Release" ) then

# copy our screen saver to this users CMM directory
cp -Rfv "${SCRIPT_INPUT_FILE_0}" "${SCRIPT_OUTPUT_FILE_0}"

endif

Then set its input file to "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}" and its output file to "${HOME}/Library/Screen Savers/${FULL_PRODUCT_NAME}".

Now when you build / run your project it will auto-magicly link to your debug build or copy your release build.

geowar
  • 4,226
  • 1
  • 25
  • 23
2

As Peter says, you can debug the plugin by executing an application that will load the plugin.

However, rather than using the screensaver engine you could also use system preferences. When the preferences appear navigate to your screensaver under "Desktop & Screen Saver" to load your plugin.

It's not perfect as your view won't be full size, but it can be easier than setting up remote debugging.

  • I've been using this method. Works great for my purposes. – Rob Segal Sep 04 '12 at 02:36
  • This is what I use (I also use logging to files, from standalone test apps, to get some data intensive core code right). On more recent versions of Mac OS X and Xcode (ex., 10.10 and Xc 7), note that you simply enable your breakpoints, "attach" to System Preferences (Xc debug menu), click on your screensaver in the list, and voila, you can step through your source (assuming you installed a debug build). You cannot easily use the preview feature to debug larger instances of your screen saver, given that it covers the entire screen. – jarFlooby Apr 18 '16 at 06:36
1

not necessarily the best way, but you could ssh in from another machine and launch ScreenSaverEngine from gdb (untested)

edit:

also, you could try adding a new application target and add your ScreenSaverView to a window in IB, you may have to manually configure stuff like settings, but it could help some and should probably work OK as ScreenSaverView is a subclass of NSView

cobbal
  • 66,343
  • 18
  • 135
  • 154
0

If you make a copy of the ScreenSaverEngine app, and sign it with your Developer ID, it will fix the situation where System Integrity Protection prevents attaching the debugger. Just make sure to set the executable to your own-signed copy.

Trygve
  • 1,123
  • 8
  • 22
0

I would like to note that @Karl's solution worked the best for me. However, if you are like me and you reboot your computer every night you may want to think about putting:

cp -Rn /System/Library/CoreServices/ScreenSaverEngine.app /tmp

at the beginning of the pre-Build shell script mentioned in his answer. This will automatically do the copying step for you.

(though I believe this really belongs in a comment, my xp is not yet high enough)

Samuel-IH
  • 423
  • 2
  • 5