Compare commits
4 Commits
a39a938d08
...
6158bdb832
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 6158bdb832 | |
Shadowfacts | 82bdcec9e1 | |
Shadowfacts | be7e1439bc | |
Shadowfacts | 0400e1d611 |
16
lib/util.ts
|
@ -75,12 +75,18 @@ export function video(metadata: Metadata, name: string, attributes: object): str
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const domain = process.env.DOMAIN;
|
||||||
|
|
||||||
export function render(template: string, data: any, filename?: string): string {
|
export function render(template: string, data: any, filename?: string): string {
|
||||||
data.require = require;
|
const newData = {
|
||||||
data.fancyLink = fancyLink;
|
require,
|
||||||
data.formatDate = formatDate;
|
fancyLink,
|
||||||
data.video = video;
|
formatDate,
|
||||||
return ejs.render(template, data, {
|
video,
|
||||||
|
domain,
|
||||||
|
...data,
|
||||||
|
};
|
||||||
|
return ejs.render(template, newData, {
|
||||||
filename
|
filename
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
|
|
||||||
<title><%= metadata.title %></title>
|
<title><%= metadata.title %></title>
|
||||||
|
|
||||||
<link rel="canonical" href="https://shadowfacts.net<%= metadata.permalink %>">
|
<link rel="canonical" href="https://<%= domain %><%= metadata.permalink %>">
|
||||||
<link rel="alternate" type="application/rss+xml" title="Shadowfacts" href="https://shadowfacts.net/feed.xml">
|
<link rel="alternate" type="application/rss+xml" title="Shadowfacts" href="https://<%= domain %>/feed.xml">
|
||||||
|
|
||||||
<link rel="icon" href="/favicon.ico">
|
<link rel="icon" href="/favicon.ico">
|
||||||
<link rel="apple-touch-icon-precomposed" href="/favicon-152.png">
|
<link rel="apple-touch-icon-precomposed" href="/favicon-152.png">
|
||||||
|
@ -25,9 +25,14 @@
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:description" content="Just my various ramblings." />
|
<meta property="og:description" content="Just my various ramblings." />
|
||||||
<% } %>
|
<% } %>
|
||||||
<meta property="og:image" content="https://shadowfacts.net/shadowfacts.png" />
|
<meta property="og:url" content="https://<%= domain %><%= metadata.permalink %>" />
|
||||||
<meta property="og:url" content="https://shadowfacts.net<%= metadata.permalink %>" />
|
|
||||||
<meta property="og:site_name" content="Shadowfacts" />
|
<meta property="og:site_name" content="Shadowfacts" />
|
||||||
|
<% if (metadata.cardImagePath) { %>
|
||||||
|
<meta property="twitter:image" content="https://<%= domain %><%= metadata.cardImagePath %>">
|
||||||
|
<meta property="og:image" content="https://<%= domain %><%= metadata.cardImagePath %>">
|
||||||
|
<% } else { %>
|
||||||
|
<meta property="og:image" content="https://<%= domain %>/shadowfacts.png" />
|
||||||
|
<% } %>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(() => {
|
(() => {
|
||||||
|
|
|
@ -289,7 +289,7 @@ Next up: Java versions. Every single request was made with Java 8, which is unsu
|
||||||
<p class="chart-caption container">The number of requests made with each Java version, along with the percentage of the total requests that version accounted for.</p>
|
<p class="chart-caption container">The number of requests made with each Java version, along with the percentage of the total requests that version accounted for.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Far and away the most popular version was Java 8 update 51. I'm not certain, but I believe this may have been the version of Java that shipped with the Minecraft launcher. This chart is limited to only versions that account for 1% or more of the total requests, so it's not visible, but 6,056 of the requests (0.063%) were made with versions of Java that identiy themselves as being OpenJDK, instead of the regular Oracle JDK/JRE. Additionally, a whole 37 requests (0.00039%) were made with versions of Java that included RedHat in the version string.
|
Far and away the most popular version was Java 8 update 51. I'm not certain, but I believe this may have been the version of Java that shipped with the Minecraft launcher. This chart is limited to only versions that account for 1% or more of the total requests, so it's not visible, but 6,056 of the requests (0.063%) were made with versions of Java that identify themselves as being OpenJDK, instead of the regular Oracle JDK/JRE. Additionally, a whole 37 requests (0.00039%) were made with versions of Java that included RedHat in the version string.
|
||||||
|
|
||||||
Next, broken down by country. This isn't perfectly accurate, since IP addresses aren't terribly reliable for determining location. But at only country granularity, it's acceptable.
|
Next, broken down by country. This isn't perfectly accurate, since IP addresses aren't terribly reliable for determining location. But at only country granularity, it's acceptable.
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ Rise of the Tomb Raider and Shadow of the Tomb Raider both run under Rosetta and
|
||||||
|
|
||||||
Minecraft runs well, once you’ve got a JVM installed that’s built for ARM and the LWJGL natives swapped out with ones compiled for ARM, since the set Minecraft ships isn’t. ([These](https://gist.github.com/nikhiljha/7313ac5553aafb1c8596b1fca0f4cdff) instructions explain how to replace the native libs when using MultiMC[^4]. Though I use the Temurin JDK, not whatever's in Homebrew.)
|
Minecraft runs well, once you’ve got a JVM installed that’s built for ARM and the LWJGL natives swapped out with ones compiled for ARM, since the set Minecraft ships isn’t. ([These](https://gist.github.com/nikhiljha/7313ac5553aafb1c8596b1fca0f4cdff) instructions explain how to replace the native libs when using MultiMC[^4]. Though I use the Temurin JDK, not whatever's in Homebrew.)
|
||||||
|
|
||||||
[^4]: Those only apply for Minecraft versions recent enough to use LWJGL 3. Getting earlier versions running natively is possible, but a fair bit more involved. Perhaps a subject for another time.
|
[^4]: Those only apply for Minecraft versions recent enough to use LWJGL 3. Getting earlier versions running natively is possible, but a fair bit more involved. Perhaps a subject for [another time](/2022/lwjgl-arm64/).
|
||||||
|
|
||||||
Cities: Skylines doesn’t see much of a graphical difference, but does benefit from the faster CPU. My old laptop can’t simulate the most recent city I built at 3x speed without things like vehicle movement appearing noticeably jerky whereas the Apple Silicon one can handle it. That said, I haven’t spent enough time playing C:S on it to know if the ceiling (that is to say, how much farther I could grow my city before I encountered performance issues) is substantially higher.
|
Cities: Skylines doesn’t see much of a graphical difference, but does benefit from the faster CPU. My old laptop can’t simulate the most recent city I built at 3x speed without things like vehicle movement appearing noticeably jerky whereas the Apple Silicon one can handle it. That said, I haven’t spent enough time playing C:S on it to know if the ceiling (that is to say, how much farther I could grow my city before I encountered performance issues) is substantially higher.
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ As my luck would have it, just a few weeks after I published my [last post](/202
|
||||||
|
|
||||||
Some time ago, a bug was filed against WebKit because setting `scrollIndicatorStyle` on a web view's scroll view was broken on iOS 15. The fix for this bug landed in iOS 15.4 and it subtly changed the behavior of WKScrollView when it comes to the indicator style.
|
Some time ago, a bug was filed against WebKit because setting `scrollIndicatorStyle` on a web view's scroll view was broken on iOS 15. The fix for this bug landed in iOS 15.4 and it subtly changed the behavior of WKScrollView when it comes to the indicator style.
|
||||||
|
|
||||||
The bug was fixed by tracking whether the web view client has overriden the scroll indicator style and, if so, blocking the web view from resetting it internally. Unfortunately, it [does this](https://github.com/WebKit/WebKit/blob/1dbd34cf01d8b5aedcb8820b13cb6553ed60e8ed/Source/WebKit/UIProcess/ios/WKScrollView.mm#L247) by checking if the new indicator style is not `.default`. So, even if you set it to `.default` to make it automatically switch based on system appearance, the scroll view will interpret that to mean it the indicator style hasn't been overriden and continue erroneously setting it based on background color (or, in my case, the non-opaqueness of the web view).
|
The bug was fixed by tracking whether the web view client has overriden the scroll indicator style and, if so, blocking the web view from resetting it internally. Unfortunately, it [does this](https://github.com/WebKit/WebKit/blob/1dbd34cf01d8b5aedcb8820b13cb6553ed60e8ed/Source/WebKit/UIProcess/ios/WKScrollView.mm#L247) by checking if the new indicator style is not `.default`. So, even if you set it to `.default` to make it automatically switch based on system appearance, the scroll view will interpret that to mean the indicator style hasn't been overriden and continue erroneously setting it based on background color (or, in my case, the non-opaqueness of the web view).
|
||||||
|
|
||||||
The solution is simple, if annoying. You need to check the current user interface style and select the appropriate scroll indicator style yourself.
|
The solution is simple, if annoying. You need to check the current user interface style and select the appropriate scroll indicator style yourself.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
```
|
||||||
|
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"
|
||||||
|
```
|
||||||
|
|
||||||
|
<figure>
|
||||||
|
<div style="display: flex; flex-direction: row; align-items: center; background-color: white;">
|
||||||
|
<img src="<%= metadata.permalink %>/clarus-kare.png" alt="Susan Kare's pixel art dogcow icon" style="width: 50%; image-rendering: pixelated;">
|
||||||
|
<img src="<%= metadata.permalink %>/clarus-smooth.png" alt="The high resolution dogcow icon that ships with macOS Ventura" style="width: 50%;">
|
||||||
|
</div>
|
||||||
|
<figcaption>How it started / How it's going</figcaption>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
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 becuase 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.
|
||||||
|
|
||||||
|
<!-- excerpt-end -->
|
||||||
|
|
||||||
|
<img src="<%= metadata.permalink %>/page-setup.png" alt="The Page Setup dialog. The page preview on the left shows the high-resolution Clarus icon.">
|
||||||
|
|
||||||
|
I don't have a Monterey machine to test it at the moment (I, er, [accidentally](https://social.shadowfacts.net/notice/AKGSrBOxnVDVO0ueem) updated my laptop to the beta), but I _believe_ this is a new change with Ventura.
|
||||||
|
|
||||||
|
**Updated:** 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`](https://developer.apple.com/documentation/appkit/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](/2021/scrollswitcher/) 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](https://github.com/keith/dyld-shared-cache-extractor) 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](https://threedots.ovh/blog/2022/06/a-quick-look-at-macos-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.
|
||||||
|
|
||||||
|
<div class="article-content-wide">
|
||||||
|
<img src="<%= metadata.permalink %>/appkit.png" alt="Hopper window with AppKit showing the runModalWithPrintInfo: method">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class="inline">
|
||||||
|
|
||||||
|
Curious about what those `_objc_msgSend$...` calls are? I would be to, if I haddn't watched the fascinating [Improve app size and runtime performance](https://developer.apple.com/wwdc22/110363) session from WWDC this year.
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<div class="article-content-wide">
|
||||||
|
<img src="<%= metadata.permalink %>/printingprivate.png" alt="PrintingPrivate in Hopper showing the _PMPrepareAppKitPageSetupDialogWithPrintInfoPrivate function">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
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?
|
||||||
|
|
||||||
|
<div class="article-content-wide">
|
||||||
|
<img src="<%= metadata.permalink %>/printingui.png" alt="The PrintingUI binary in Hopper with the search panel showing a bunch of methods with 'clarus' in the name">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ 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](https://github.com/bartoszj/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](https://twitter.com/illian/status/1534014772848365568).
|
||||||
|
|
||||||
|
<aside>
|
||||||
|
|
||||||
|
The `assetutil` manpage does contain a reference to a dump option (`-d`/`--dump`) which is curiously not present in the manpage nor is it recognized by the program. Perhaps an Apple-internal feature that is excluded from compilation in public builds?
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
And with that, I finally gazed upon the 512 × 512px beauty that is Smooth Clarus:
|
||||||
|
|
||||||
|
<figure>
|
||||||
|
<img src="<%= metadata.permalink %>/clarus-smooth.png" alt="Smooth Clarus">
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
The version shown here I've added a white background to, so it's not invisible in dark mode. The <a href="<%= metadata.permalink %>/Clarus256x256@2x.png" data-link="<%= metadata.permalink %>Clarus256x256@2x.png">original image</a> has a transparent background.
|
||||||
|
|
||||||
|
<aside class="inline">
|
||||||
|
|
||||||
|
The keen-eyed among you may notice that although, it had a `.pdf` extension in `assetutil` info, I've given it here as a PNG. I was confused by this too, but upon closer inspection I believe the PNG is what ships with the OS. Although the `.car` format is not documented, you can still open it up in a hex viewer and learn a bit about what it contains. Looking through it, there appears to be some metadata for each file, followed by the image data itself.
|
||||||
|
|
||||||
|
As `assetutil` showed, there are multiple entries for `ClarusSmooth2.pdf`—and one of them is followed by data that starts with the PDF file format header (`%PDF`). But, unfortunately, extracting that data into a separate file seems to result in a blank PDF. And I don't know enough about the format to figure out whether there is any vector data in it, or if it truly is empty.
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
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.
|
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 258 KiB |
After Width: | Height: | Size: 188 B |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 213 KiB |
After Width: | Height: | Size: 200 KiB |