master ohmu/fastfetch-openbsd / src / detection / wallpaper / wallpaper_apple.m
ohmu  ·  2026-10-15
  1#include "wallpaper.h"
  2#include "common/settings.h"
  3
  4#import <Foundation/Foundation.h>
  5#import <AppKit/AppKit.h>
  6
  7const char* detectFromPlist(FFstrbuf* result) {
  8    // For Sonoma and later (macOS 14.0+)
  9    // https://github.com/JohnCoates/Aerial/issues/1332
 10    // Initialised because the check below reads it: the method is documented to assign it, but an
 11    // uninitialised read decides between two sources of the wallpaper, so it is not worth relying on.
 12    NSError* error = nil;
 13    NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Application Support/com.apple.wallpaper/Store/Index.plist", instance.state.platform.homeDir.chars];
 14    NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:fileName]
 15                                                             error:&error];
 16
 17    if (error) {
 18        return "Failed to read wallpaper plist file";
 19    }
 20
 21    NSArray* choices = [dict valueForKeyPath:@"SystemDefault.Desktop.Content.Choices"];
 22    if (choices.count > 0) {
 23        NSDictionary* choice = choices[0];
 24
 25        NSArray* files = choice[@"Files"];
 26        if ([files isKindOfClass:NSArray.class] && files.count > 0) {
 27            NSDictionary* fileEntry = files[0];
 28            // `relative` is read through a dictionary, which raises an unrecognized selector rather
 29            // than returning nil when the entry is not one.
 30            if ([fileEntry isKindOfClass:NSDictionary.class]) {
 31                NSString* relative = fileEntry[@"relative"];
 32                if ([relative isKindOfClass:NSString.class]) {
 33                    ffStrbufSetS(result, [NSURL URLWithString:relative].path.UTF8String);
 34                }
 35            }
 36        }
 37
 38        if (result->length == 0) {
 39            NSData* configData = choice[@"Configuration"];
 40            if (configData && configData.length > 0) {
 41                NSError* plistError = nil;
 42                NSDictionary* configPlist = [NSPropertyListSerialization propertyListWithData:configData
 43                                                                                      options:NSPropertyListImmutable
 44                                                                                       format:NULL
 45                                                                                        error:&plistError];
 46
 47                if (!plistError && [configPlist isKindOfClass:NSDictionary.class]) {
 48                    NSDictionary* urlDict = configPlist[@"url"];
 49                    if ([urlDict isKindOfClass:NSDictionary.class]) {
 50                        NSString* relativeUrlString = urlDict[@"relative"];
 51                        if ([relativeUrlString isKindOfClass:[NSString class]]) {
 52                            NSURL* fileUrl = [NSURL URLWithString:relativeUrlString];
 53                            if (fileUrl.fileURL) {
 54                                ffStrbufSetS(result, fileUrl.path.UTF8String);
 55                            }
 56                        }
 57                    }
 58                }
 59            }
 60        }
 61
 62        if (result->length == 0) {
 63            NSString* provider = choice[@"Provider"];
 64            if ([provider isKindOfClass:NSString.class]) {
 65                NSString* builtinPrefix = @"com.apple.wallpaper.choice.";
 66                if ([provider hasPrefix:builtinPrefix]) {
 67                    provider = [provider substringFromIndex:builtinPrefix.length];
 68                }
 69
 70                // macOS internal wallpapers
 71                if ([provider isEqualToString:@"aerials"]) { // Most builtin aerial wallpapers are private
 72                    ffStrbufSetStatic(result, "Built-in aerial photography");
 73                } else if ([provider isEqualToString:@"default"]) {
 74                    ffStrbufSetStatic(result, "macOS Default Wallpaper");
 75                } else {
 76                    ffStrbufSetF(result, "Built-in %s wallpaper", provider.UTF8String);
 77                }
 78            }
 79        }
 80    }
 81    if (result->length == 0) {
 82        return "Failed to detect wallpaper from plist";
 83    }
 84    return nullptr;
 85}
 86
 87#ifdef FF_HAVE_SQLITE3
 88const char* detectFromSQLite(FFstrbuf* result) {
 89    // For Ventura
 90    // https://stackoverflow.com/questions/301215/getting-desktop-background-on-mac
 91    FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateCopy(&instance.state.platform.homeDir);
 92    ffStrbufAppendS(&path, "Library/Application Support/Dock/desktoppicture.db");
 93    if (ffSettingsGetSQLite3String(path.chars,
 94            "SELECT value\n"
 95            "FROM preferences\n"
 96            "JOIN data ON preferences.data_id=data.ROWID\n"
 97            "JOIN pictures ON preferences.picture_id=pictures.ROWID\n"
 98            "JOIN displays ON pictures.display_id=displays.ROWID\n"
 99            "JOIN spaces ON pictures.space_id=spaces.ROWID\n"
100            "WHERE display_id=1 AND space_id=1 AND key=1",
101            result)) {
102        return nullptr;
103    }
104    return "Failed to detect wallpaper from SQLite database";
105}
106#endif
107
108const char* detectFromNSWorkspace(FFstrbuf* result) {
109    // Reliable for user-picked static images.
110    NSScreen* mainScreen = NSScreen.mainScreen;
111    if (!mainScreen) {
112        return "Failed to detect wallpaper from NSWorkspace: No main screen found";
113    }
114
115    NSURL* url = [NSWorkspace.sharedWorkspace desktopImageURLForScreen:mainScreen];
116    if (url.fileURL) {
117        ffStrbufSetS(result, url.path.UTF8String);
118        return nullptr;
119    }
120
121    return "Failed to detect wallpaper from NSWorkspace";
122}
123
124const char* ffDetectWallpaper(FFstrbuf* result) {
125    const char* error;
126
127    if (@available(macOS 14.0, *)) {
128        error = detectFromPlist(result);
129        if (error) {
130            // The plist is the authoritative source on Sonoma and later, but when it can not be read
131            // at all, NSWorkspace can still resolve a user-picked static image.
132            error = detectFromNSWorkspace(result);
133        }
134    } else {
135#ifdef FF_HAVE_SQLITE3
136        error = detectFromSQLite(result);
137        if (error) {
138            // The database only holds the user-picked picture, so it goes missing on a machine that
139            // never changed its wallpaper. NSWorkspace answers for the system default there.
140            error = detectFromNSWorkspace(result);
141        }
142#else
143        error = detectFromNSWorkspace(result);
144#endif
145    }
146    return error;
147}