Open xmddmx opened 2 years ago
Hey @xmddmx
I can confirm I also see this when I close Aerial's sheet.
As far as I understand the new System Preferences has been rewritten as a bunch of .appex
, and it's the ScreenSaver.appex
for System Preferences that crashes (which itself launches legacyScreenSaver.appex
which then launches our plugins). I have the crash with Aerial too here but it's minimal, the panel goes blank + the crash log in console.
I think you did right filling the feedback, as I don't think there's anything we can do on our end.
Historically closing the sheet has been a mess as there are few solutions that work accross multiple macOS versions.
This is what I came up with a long time ago and has been working fine, it's possible that this triggers something :
window?.sheetParent?.endSheet(window!)
(taken from here, Aerial does the same but in a more convoluted way) : https://github.com/glouel/ScreenSaverMinimal/blob/master/ScreenSaverMinimal/ConfigureSheetController.swift
It's possible that using a simpler dismissal could make it not crash though I haven't tried yet. Feel free to reference this in the feedback if needed.
Edit : I also checked just in case but there's still no Swift screen saver template in Xcode 14 ! Maybe someday if we get the non legacy API 😅
I notice that some of the sample code calls .loadWindow() on the Preferences window controller.
Example:
https://github.com/soffes/Clock.saver/blob/main/Clock/Classes/MainView.swift#L24
Apple says
You should never directly invoke this method. Instead, access the window property so the windowDidLoad() and windowWillLoad() methods are invoked
See https://developer.apple.com/documentation/appkit/nswindowcontroller/1535137-loadwindow
I've played with not calling loadWindow() but it didn't seem to help.
For what it's worth, I don't use loadWindow
on either Aerial nor my ScreenSaverMinimal sample.
Historically closing the sheet has been a mess as there are few solutions that work accross multiple macOS versions
Agreed. In earlier OS's you could do this:
override var hasConfigureSheet: Bool { return true }
override var configureSheet: NSWIndow? {
// launch a stand-alone app which hosts the configure sheet
configProcess = Process()
configProcess?.arguments = arguments
configProcess?.launchPath = "/path/to/helper/app"
configProcess?.terminationHandler = configTerminatedHandler
configProcess?.launch()
return nil
}
However, in my testsing, it's very fickle: macOS 10.11 through 10.13 : configureSheet=nil works OK macOS 10.14, 10.15, 11.x, and 12.x : configureSheet=nil crashes, so need to provide a dummy configureSheet macOS 13.0 : configureSheet crashes when not nil. Maybe try using nil again?
Since Aerial already has the helper app, I wonder if you should consider doing something similar - hosting the Preferences in a stand-alone app?
Hmm, I had no idea this was possible !
Is your helper somewhere in an app in /Applications
? Despite the sandboxing you can launch stuff easily and I do that a bit for various stuff, including launching some app bundled with companion (although that's not fully baked in right now) but it's mostly command line stuff.
I do it in reverse right now, Aerial Companion loads Aerial.saver
and puts it in a window or triggers the configuresheet manually. My biggest issue is I want to sync settings between both and the sandbox makes it hard.
In your situation though, I'm unclear about how settings work ? Your app panel conigureSheet should have it's settings outside the sandbox ? Yet the screensaver proper will read from the legacyScreenSaver sandbox ? How do you work that around, or am I missing something ?
In any case thanks for the info a lot of food for thought !
Interesting.
Here's what I'm doing:
~/Library/Containers/com.apple.ScreenSaver.Engine.legacyScreenSaver/Data/Library/Preferences/com.example.plist
(or)
~/Library/Containers/com.apple.ScreenSaver.Engine.legacyScreenSaver.x86-64/Data/Library/Preferences/com.example.plist
when running under Rosetta, since this is an Intel screensaver.Clearly, the best outcome would be if Apple:
But given their track record, I feel like exploring these workarounds is prudent in case these issues go un-fixed in Ventura...
Clearly, the best outcome would be if Apple:
- fixed the bug
I'll keep an eye on this but considering it's beta 1 and a whole new System Preferences, I think the odds are higher than usual they'll fix this. If needed I'll also file another feedback, that does help sometimes. I haven't checked yet if Obj-C screensavers that are run through legacyScreenSaver
also have the same issue. It could very well be a Swift thing (there was a lot of that in Catalina).
- gave us documentation / sample code showing how to do a Swift screensaver correctly
- published the Appex screensaver API
I keep hoping every year ! Surely, some day, they would. But even their latest screensavers (like the Hello one for iMac) are legacy plugins...
Regarding the other stuff:
ScreenSaverDefault
wrapper thing, I may need to override that in some way, though I'm not sure yet how. I hope to investigate this soon.
- Does WKWebView2 require some specific entitlement ? I think I tried using it at some point though that wasn't on Aerial I think, and had issues.
Have not had any issues using it within the actual .saver, however we are only using it to read HTTP data via a localhost server on the same mac. I wouldn't be surprised if the sandbox might prevent other uses?
- Sharing prefs : Theoretically yes, the unsandboxed version of Aerial could definitely read/write the sandboxed file. It's unclear to me however how to approach this, since we use in screen savers the the
ScreenSaverDefault
wrapper thing, I may need to override that in some way, though I'm not sure yet how. I hope to investigate this soon.
I believe that ScreenSaverDefault stores data in ~/Library/Preferences/ByHost/com.example.[GUID String].plist (or the equivalent sandboxed location). I've never liked or understood the ByHost folder - I think it's something that was supposed to enable one person to be logged into the same account using multiple machines? (Wasn't that an idea of the NextStep era?) but I've always just found it confusing and clumsy. My screensaver just saves preferences using
CFPreferencesSetValue(key as CFString, val as CFString, self.bundleID as CFString, kCFPreferencesCurrentUser, kCFPreferencesAnyHost)
[...]
CFPreferencesAppSynchronize(self.bundleID as CFString)
which puts data in
~/Library/Preferences/[bundleID].plist
which I find a lot easier to use.
@xmddmx
First sorry for the late reply, missed your message the other day and had a busy week.
Have not had any issues using it within the actual .saver, however we are only using it to read HTTP data via a localhost server on the same mac. I wouldn't be surprised if the sandbox might prevent other uses?
This may be exactly that, I think one issue I had was regarding https being mandatory.
I believe that ScreenSaverDefault stores data in ~/Library/Preferences/ByHost/com.example.[GUID String].plist (or the equivalent sandboxed location). I've never liked or understood the ByHost folder - I think it's something that was supposed to enable one person to be logged into the same account using multiple machines? (Wasn't that an idea of the NextStep era?)
If I remember correctly, the ByHost folder is like a "host temporary" preferences, as in those only apply to this machine and could be recreated easily (things that must be tied to the machine like they have some unique MAC address or something). Which is not really great for a screensaver, but that's where it puts them sadly. Those settings are not always migrated in some scenarios (like machine upgrade), and if I remember correctly settings were lost when legacyScreenSaver.appex was introduced, and also in some other macOS transition (can't remember which). Just a pain.
but I've always just found it confusing and clumsy. My screensaver just saves preferences using
CFPreferencesSetValue(key as CFString, val as CFString, self.bundleID as CFString, kCFPreferencesCurrentUser, kCFPreferencesAnyHost) [...] CFPreferencesAppSynchronize(self.bundleID as CFString)
which puts data in
~/Library/Preferences/[bundleID].plist
which I find a lot easier to use.
That's... super super useful, thanks a whole lot. I'll have a look at implementing this as this would solve my biggest issue right now with the desktop/full screen mode of Companion. I had an idea it was probably possible but I was completely unfamiliar with those APIs ! Thanks again 👍
Now, regarding your original issue, I had a look with my ScreenSaverMinimal
project and tried a few "other" things to dismiss the panel but none worked.
I also tried two other screensavers to see if I could reproduce just to be sure :
Both have the exact same issue, so if I had to guess, there's a callback from legacyScreenSaver.appex
that seems to crash the new Screen Saver.appex
. I'll file a radar with more details just in case.
Couple of other bugs I saw :
I put a list so I can track stuff in the wiki here : https://github.com/glouel/ScreenSaverMinimal/wiki/Issues-with-macOS-Ventura-betas
Hopefully things will get better in a couple of betas, I'll keep you posted if I find other stuff.
@xmddmx You probably noticed this but the original issue (crashing after closing) has been fixed in Ventura beta3.
Also, double clicking a .saver to install works again.
Last thing, I tried your methods to write and read preferences but I'm running into an issue when running in sandbox, the CFPreferences api will read and write inside the container. I'm wondering if you have a trick to read the specific unsandboxed file. Thanks !
Edit : I think it's stupid and I'm probably getting tired about this but I'm this close to calling defaults read/write
in terminal and call it a day 😅
I agree, those 2 issues (crash after close the settings panel, double-click .saver file) seem fixed in Beta 3.
Regarding CFPreferences : I think I'm doing the opposite of what you are. In my setup, the screensaver itself reads/writes settings from within the sandbox container. I have an external app, which is not sandboxed, which can also read/write those same settings in the container.
It sounds like you are trying to go the opposite direction, where the .saver is trying to break out of the sandbox?
Thanks for taking the time to answer @xmddmx.
This is where I'm not getting it. If I call CFPreferences from outside the sandbox, I will read and write the one outside the sandbox. I don't see how to specify the sandbox path ?
CFPreferencesAppSynchronize("com.glouel.synctest" as CFString) <- should I pass the path here ???
Edit: I tried calling this in the unsandboxed app :
CFPreferencesAppSynchronize("~/Library/Containers/com.apple.ScreenSaver.Engine.legacyScreenSaver/Data/Library/Preferences/com.glouel.synctest" as CFString)
frustratingly, it still writes in ~/Library/Preferences
Perhaps using a relative path with "~" doesn't work? I had to go back and untangle my code, but here's what I'm doing to read from inside the Sandbox from an app Outside the sandbox:
bundleID = "/Users/username/Library/Containers/com.apple.ScreenSaver.Engine.legacyScreenSaver/Data/Library/Preferences/com.mycompany.myscreensaver"
key = "foobar"
value = CFPreferencesCopyValue(key, bundleID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost)
This works for me. Writing a value is similar, just uses the CFPreferencesSetValue() method.
Where I learned that the "applicationID" can include the path too? Don't remember!
Also, if your App and .Saver are Intel, you would need to replace legacyScreenSaver with legacyScreenSaver.x86-64 when running on a M1 system.
Absolute path it is...
With an absolute path, it works, and I can succesfully read write from outside the container to it 🎉
Seriously I can't thank you enough for helping me on this, I've been stuck on this for about 6 months on and off, and this should finally solve it.
Thanks for your patience !
Edit: I do dual compile and my understanding is that it always hit the non .x86-64, even on Intel in that situation, and the x86-64 is a non factor. But I may have this wrong ! (I'm running a M1 based with dual compiled, but 99% sure this was the same with my previous x86 iMac dual compiled). To be clear, .x86-64 only matters if your app kicks in rosetta is my understanding.
FYI, I did further testing and the absolute path actually also work with the "modern" UserDefault API :
// Test 2
let bundleID2 = "/Users/guillaume/Library/Containers/com.apple.ScreenSaver.Engine.legacyScreenSaver/Data/Library/Preferences/com.glouel.synctest2"
let userDefaults = UserDefaults(suiteName: bundleID2)
userDefaults?.setValue(time, forKey: "lastRun")
userDefaults?.synchronize()
This actually works ! I'm working on cleaning it all up as I have a mix of stuff currently in preferences but this is looking very very promising. Again many thanks for sharing all that info !
Glad to be of help!
I'd be interested in pursuing some other quality-of-life issues with screensavers on Ventura. I've pretty much given up on the ability to capture Keyboard events on Catalina or higher, but I did have a pretty robust way of capturing mouse events, which allows the screensaver to provide on-screen controls / Heads-Up-Display (HUD). This seems broken in Ventura.
Are you interested/willing to collaborate? I don't have any github repos, so would you mind if I posted a new Issue here? Or would it perhaps be better posted on ScreenSaverMinimal ? https://github.com/AerialScreensaver/ScreenSaverMinimal/issues
Sure thing, if we can find ways to improve things, I'm all for it.
Either repo is fine to me, this one may have a tiny bit more visibility although I'm not sure it matters much. I'll pin the issue in any case.
(Code wise, it's definitely easier to test stuff on the other repo though !)
Edit : Aside on the preferences, onterestingly, you can't do anything in the ByHost directory. Even if you try to create a file in a ByHost directory, it's created one level down. So I'll take the opportunity to migrate everything from ByHost to regular prefs ;)
M1 mac mini, macOS Ventura Beta 1 22a5226r two monitors (17" HDMI and 15" VGA)
I believe this is a macOS bug, as it's being seen with various screensavers such as https://github.com/soffes/Clock.saver and https://iscreensaver.com
Reported to Apple Feedback as FB10103112.
Seen both with ARM native and Intel x64 Rosetta emulated screensavers.
I'm trying to find a workaround using my own screensaver software, if I stumble across anything I will report back.
Example crash log: