Open xmddmx opened 9 months ago
Hey @xmddmx !
Someone I know tried to mess with it with limited results. The problem is, even if you overwrite the Index.plist, there's something that rebuilds it at startup from "somewhere" (maybe one more sqllite database).
Feel free to poke around but from what I understand, it's idleassetsd
that is responsible for overwriting this when it gets restarted.
For now I went the boring way, I use the "old" plist to detect if we are set as default (because they still update it for some reason), and if not I put a screen with instructions. It's really messy though!
One more thing, I tried looking up private frameworks and had limited success.
/System/Library/PrivateFrameworks/Wallpaper.framework/Versions/A/Wallpaper
is the one I believe that contains all we need.
I managed to extract those with resymbol
after extracting from the dylibs (what a mess). I've attached the file below
I found a few interesting references:
struct WallpaperFirstRunChooser {
let _defaultWallpaperImage: So0C
let _priorWallpaperImage: So0C
let _initiallySelectedChoice: Choice
}
Which I believe is linked to the Sonoma first run thing that kicks us out if the user picks a new wallpaper, and
enum AgentXPCMessage {
...
case updateDesktopWallpaperUserSettings: WallpaperUserSettings
case updateScreenSaverWallpaperUserSettings: WallpaperUserSettings
I'm sure someone more knowledgeable may be able to make something out of these. If I'm guessing correctly, there's a way to make a XPC call to set savers, maybe?
I think I found a solution - WallpaperAgent is the key
Does not work:
Works:
Code:
# first, go to System Preferences / Screen Saver and set a screensaver you want, then close the window
# make a backup copy of this plist:
cd ~/Library/Application\ Support/com.apple.wallpaper/Store
cp Index.plist MyPlist.plist
# go back to System Preferences / Screen Saver and set a different screensaver (such as 'Messages') then close the window
# now, replace the plist with your plist which should select your screensaver
cp MyPlist.plist Index.plist
# kill the Wallpaper agent, which seems to force re-loading of the plist datat
killall WallpaperAgent
# launch the screensaver
# result: works! your screensaver is selected.
That sounds great, but I have to ask, does it survive a reboot ? I think someone tried something fairly close and it didn't, at reboot (or maybe when wallpaperagent is launched again) it got overwritten ?
I tried exactly this and it doesn't survive a reboot unless you lock the plist file. The system will write out the values it has in memory over the values in that file. Locking the plist file caused some other issues, like the wallpaper resetting on reboot.
Actually let me correct myself there, I didn't try actually killing the Wallpaper Agent. It's possible that by doing that, you've found a way to avoid it writing out the value from memory.
Just tried it, and it survives a reboot:
# close System Settings
# edit ~/Library/Application\ Support/com.apple.wallpaper/Store/Index.plist
killall WallpaperAgent
# launched the screensaver using hot corner to test
# rebooted
# launched the screensaver using hot corner to test
Yeah, I can confirm, that this works. The drawback is that the wallpaper disappears for a second while it's happening. But if the app can kill WallpaperAgent without other consequences this may be acceptable.
Oh wow, this is seriously awesome 😭
I'll have a look at making this work tomorrow in Companion, but from here it should be straightforward ! I did have some bits of Swift code to decode the whole plist thing somewhere (it's a mess of nested plists) if you want, it might be somewhere in a repository, let me know !
Again, many thanks this is a great find!
I tried other kill signals:
killall -HUP WallpaperAgent
killall -INT WallpaperAgent
but all of them also cause the wallpaper to flash briefly, so no improvement.
i'll be surprised if the wallpaper flicker can be avoided other than with a cosmetic hack. it is reloading the wallpaper. this trick also works for changing the wallpaper.
Yeah I won't worry about this, it's absolutely great as is! I'll keep you posted on implementation tomorrow if no one else beats me to it.
Again, many thanks for sharing this.
@xmddmx This is my code for playing around with that plist (it was v2 at the time, needs changed to v1)
What we need on top of that is detecting user path (I 99,9999% think we need absolute paths), base64 encoding properly the saver path, and do it for all spaces/screens.
let defaultFile = "/Users/guillaume/Library/Application Support/com.apple.wallpaper/Store/Index_v2.plist"
let plistURL = URL(fileURLWithPath: defaultFile)
let data = try? Data(contentsOf: plistURL)
guard let plistDictionary = try? PropertyListSerialization.propertyList(from: data!, options: [], format: nil) as? [String: AnyObject] else {
return
}
let allDisplays = plistDictionary["AllSpacesAndDisplays"] as! NSDictionary
let idle = allDisplays["Idle"] as! NSDictionary
let content = idle["Content"] as! NSDictionary
let choices = content["Choices"] as! NSArray
let choicesDict = choices[0] as! NSDictionary
let encodedData = choicesDict["Configuration"] // This is our data
//print(encodedData.debugDescription)
guard let configurationPlist = try? PropertyListSerialization.propertyList(from: encodedData as! Data, options: [], format: nil) as? [String: AnyObject] else {
return
}
print(configurationPlist)
important side note, it appears the killall WallpaperAgent approach does NOT update the classic plist setting, so it will need to be set this way AND set the classic way.
This is a followup to: https://github.com/JohnCoates/Aerial/issues/1305#issuecomment-1677202879
Sonoma changes the location and format of the configuration files / plists for choosing which screen saver(s) are active on which screens.
Initial notes:
~/Library/Application\ Support/com.apple.wallpaper/Store
Here's a conmmand to fully decode it :
Which gives results like this: