10 KiB
metadata.title = "Clarus Returns Home"
metadata.tags = ["misc"]
metadata.date = "2022-06-14 10:11:42 -0400"
metadata.shortDesc = "Did you know Clarus the Dogcow is hiding in the macOS Ventura beta?"
metadata.slug = "clarus"
metadata.cardImagePath = "/2022/clarus/clarus-smooth.png"
Did you know that with macOS Ventura, Clarus the Dogcow has at long last returned home? Recently, while doing something else, I accidentally hit Cmd+Shift+P which opened the Page Setup dialog. I was greeted, surprisingly, with a new high-resolution version of the classic Clarus icon that I'd never seen before. I looked at it briefly, and then closed the dialog and went back to whatever I was doing before. I had assumed that because I'd been in a 3rd-party app at the time, that the Clarus icon was just some easter egg the developer had left. But a little while later, I got to thinking. What were the chances that someone went to the trouble of customizing the Page Setup dialog, of all things, just for an easter egg? Zero, it turns out. That dialog shows Clarus on the page preview in every app.
I don't have a Monterey machine to test it at the moment (I, er, accidentally updated my laptop to the beta), but I believe this is a new change with Ventura.
Update: I installed Monterey in a virtual machine to check, and, indeed, the Page Setup dialog there bears no sign of Clarus.
The next step, then—having been thoroughly nerd-sniped by this—was to figure out where the icon was coming from and if I could pull it out of whatever nook it was hidden in.
The first stop was NSPageLayout
, the panel object that is responsible for displaying the panel. It was unlikely that the class would actually contain the implementation of the panel, but it was at least a starting point.
In order to actually look at the disassembled implementation of this AppKit class, I needed the actual AppKit framework binary. Since macOS Big Sur, all system framework binaries are stored merged in the dyld
shared cache, rather than in separate files. But, I need them as separate files in order to actually inspect them.
Since the last time I wrote about this, a couple things have changed. Before, I built the Apple dyld_shared_cache_util
from one of the periodic dyld
source dumps. This is annoying because you have to make a bunch of changes to the source code to get it to compile outside of an Apple-internal environment. It also may break whenever there's an OS update. So, I've switched to using this utility which uses the dyld_extractor.bundle
that ships with Xcode. The other difference since before is a minor one: the dyld shared cache has moved. Wheras before it was in /System/Library/dyld/
, in the Ventura beta it's moved to /System/Cryptexes/OS/System/Library/dyld/
(the Cryptex seems to be part of the Rapid Security Response feature Apple announced).
With the shared cache extracted, I could load the AppKit binary into Hopper (I had to disable the Objective-C analysis, otherwise the app crashed when trying to load the binary) and start poking around. I searched for the NSPageLayout
class that I'm interested in, and looked at the runModalWithPrintInfo:
method, since that sounded like a good candidate for something that would lead to the bulk of the implementation. And, indeed, it was. The method appears to be a fairly simple wrapper around the PMPrepare...
function that sounds like it lives in a separate private framework.
The next step was figuring out where that prepare function is actually implemented. Running otool -L
on the AppKit binary doesn't reveal anything obviously useful, but in the PrivateFrameworks directory extracted from the dyld shared cache, there's something called PrintingPrivate.framework
, which sounds promising. Opening it up in Hopper, I saw that this is indeed the framework I was looking for.
Looking at the implementation of the prepare function, what immediately jumps out is the call to _LoadAndGetPrintingUIBundle
. This seems to be yet another layer of indirection with the actual thing implemented in a different bundle. There's also a call in the else branch to the similarly-named _LoadAndGetPrintCocoaUIBundle
, but let's start with the first one in hopes that it's more common.
The implementation of that function goes through another helper function and it ends up loading a PrintingUI.bundle
plugin from inside the PrintingPrivate framework bundle. This one isn't part of the dyld shared cache, so I can just open it right up in Hopper without any fuss.
If you look for the function PrintingPrivate calls, it turns out it winds up in a method on PMPageSetupController
. This sounds promising, let's see what else that class can do.
What's this? A method called updateClarus
? Could it be? Have we finally reached it?
Yes! Clarus, I'm coming! One method that sounds particularly encouraging is -[PMPageSetupController setClarusImageView:]
. If I can find out what's setting the image view, maybe that'll lead to where it's being configured with the image.
Unfortunately, the setter for that property isn't referenced anywhere in the PrintingUI binary. Nor is the getter. I was stuck here for a while, until I realized that the setter not being called anywhere was probably a sign that the UI was defined in a Nib and that an outlet was added from Interface Builder, even though it was never used.
Sure enough, in the plugin bundle's resources, there is a PMPageSetup.nib
. And if the page setup UI is defined in a Nib, and Clarus is being shown in a image view, the image itself is probably located in the asset catalog.
Using the system assetutil
program, one can list all of the files in a compiled asset catalog. And sure enough, there she is:
$ assetutil --info /System/Library/PrivateFrameworks/PrintingPrivate.framework/Versions/A/Plugins/PrintingUI.bundle/Contents/Resources/Assets.car | grep -i clarus
"Name" : "Clarus",
"RenditionName" : "ClarusSmooth2.pdf",
"Name" : "Clarus",
"RenditionName" : "ClarusSmooth2.pdf",
"Name" : "Clarus",
"RenditionName" : "ClarusSmooth2.pdf",
To actually extract the image from the asset catalog, I needed to use a third-party tool. acextract worked perfectly on the first try, though it did need couple of additional @import
s to compile on Ventura since Foundation no-longer re-exports CoreGraphics.
And with that, I finally gazed upon the 512 × 512px beauty that is Smooth Clarus:
The version shown here I've added a white background to, so it's not invisible in dark mode. The original image has a transparent background.
Lastly, if you're writing a Mac app and would like to hide Clarus somewhere, you can load the bundle yourself and then pull the image out like so:
let bundle = Bundle(path: "/System/Library/PrivateFrameworks/PrintingPrivate.framework/Versions/A/Plugins/PrintingUI.bundle")!
try! bundle.loadAndReturnError()
let image = bundle.image(forResource: "Clarus")
I'm not sure if the Mac App Store would consider that using private SPI, so use it at your own risk.
It would be very cool to see Clarus return as an SF Symbol some day. If hundreds of icons for various Apple products can go in, so too can everyone's favorite dogcow.