Get the current wallpaper in Cocoa
Asked Answered
H

2

11

I'm using this code to get the current wallpaper:

NSURL *imageURL = [[NSWorkspace sharedWorkspace] desktopImageURLForScreen:[NSScreen mainScreen]];

This works fine, but when i set a folder of pictures to be the wallpaper(As shown in the picture), the imageURL is a directory, so how can i get the current wallpaper's USURL in this situation?

enter image description here

Hangout answered 31/12, 2012 at 8:33 Comment(1)
I've opened openradar.appspot.com/radar?id=5782854294306816 since it would be better if the API did what it promises.Allergy
D
4

I've been trying to do the exact same thing. You can get the current desktop picture URL by:

  1. Getting the current space UUID from the com.apple.spaces property list,
  2. Searching the com.apple.desktop property list for the matching space,
  3. Extracting the URL from the LastName property

I am still working on this, but the following code obtains the correct URL... sometimes. The main problem is that the property lists aren't updated frequently enough, and I haven't been able to force them to refresh (short of killing the dock). Let me know if you figure something out!

NSDictionary *spacesPLIST = (__bridge NSDictionary *)(CFPreferencesCopyAppValue(CFSTR("SpacesConfiguration"), CFSTR("com.apple.spaces")));
NSDictionary *desktopPLIST = (__bridge NSDictionary *)(CFPreferencesCopyAppValue(CFSTR("Background"), CFSTR("com.apple.desktop")));

NSArray *monitors = [spacesPLIST valueForKeyPath:@"Management Data.Monitors"];
NSInteger monitorIndex = 0;
if ([monitors count] > 1) {
    //search for main (or ask user to select)
}
NSDictionary *monitor = [monitors objectAtIndex:monitorIndex];
NSDictionary *spaces = [desktopPLIST valueForKey:@"spaces"];
NSString *currentSpaceUUID = [monitor valueForKeyPath:@"Current Space.uuid"];
NSDictionary *currentSpace = [spaces valueForKey:currentSpaceUUID];
NSURL *desktopPicturesDirectory = [NSURL fileURLWithPath:[currentSpace valueForKeyPath:@"default.ChangePath"] isDirectory:true];
NSString *desktopPictureName = [currentSpace valueForKeyPath:@"default.LastName"];
NSURL *desktopPictureURL = [NSURL URLWithString:desktopPictureName relativeToURL:desktopPicturesDirectory];
[[NSWorkspace sharedWorkspace] selectFile:[desktopPictureURL path] inFileViewerRootedAtPath:@""];
Disputation answered 3/3, 2013 at 2:17 Comment(2)
You want __bridge_transfer in those casts, since CFPreferencesCopyAppValue returns an owned reference. Also, NSWorkspace has activateFileViewerSelectingURLs:, relieving the need to extract a path from the URL, and NSURL has URLByAppendingPathComponent:.Eyelid
Has anyone figured out a way to refresh the property lists without having to kill the Dock?Medovich
K
6

There is another way to get the image by taking a screenshot of the current wallpaper.

extension NSImage {

    static func desktopPicture() -> NSImage {

        let windows = CGWindowListCopyWindowInfo(
            CGWindowListOption.OptionOnScreenOnly,
            CGWindowID(0))! as NSArray

        var index = 0
        for var i = 0; i < windows.count; i++  {
            let window = windows[i]

            // we need windows owned by Dock
            let owner = window["kCGWindowOwnerName"] as! String
            if owner != "Dock" {
                continue
            }

            // we need windows named like "Desktop Picture %"
            let name = window["kCGWindowName"] as! String
            if !name.hasPrefix("Desktop Picture") {
                continue
            }

            // wee need the one which belongs to the current screen
            let bounds = window["kCGWindowBounds"] as! NSDictionary
            let x = bounds["X"] as! CGFloat
            if x == NSScreen.mainScreen()!.frame.origin.x {
                index = window["kCGWindowNumber"] as! Int
                break
            }
        }

        let cgImage = CGWindowListCreateImage(
            CGRectZero,
            CGWindowListOption(arrayLiteral: CGWindowListOption.OptionIncludingWindow),
            CGWindowID(index),
            CGWindowImageOption.Default)!

        let image = NSImage(CGImage: cgImage, size: NSScreen.mainScreen()!.frame.size)
        return image
    }        
}

This approach looks much simpler IMHO, if you need the picture, not the url.

Note that wallpaper is no longer defined in the com.apple.dektop plist: starting from Mavericks, the setting is moved to ~/Library/Application Support/Dock/desktoppicture.db. This is SQLite file, and the "data" table contains the url.

Klaraklarika answered 3/10, 2015 at 1:38 Comment(1)
It seems that name = window["kCGWindowName"] of wallpaper has been renamed from name.hasPrefix("Desktop Picture") to name.hasPrefix("Wallpaper") in macOS SonomaDoubly
D
4

I've been trying to do the exact same thing. You can get the current desktop picture URL by:

  1. Getting the current space UUID from the com.apple.spaces property list,
  2. Searching the com.apple.desktop property list for the matching space,
  3. Extracting the URL from the LastName property

I am still working on this, but the following code obtains the correct URL... sometimes. The main problem is that the property lists aren't updated frequently enough, and I haven't been able to force them to refresh (short of killing the dock). Let me know if you figure something out!

NSDictionary *spacesPLIST = (__bridge NSDictionary *)(CFPreferencesCopyAppValue(CFSTR("SpacesConfiguration"), CFSTR("com.apple.spaces")));
NSDictionary *desktopPLIST = (__bridge NSDictionary *)(CFPreferencesCopyAppValue(CFSTR("Background"), CFSTR("com.apple.desktop")));

NSArray *monitors = [spacesPLIST valueForKeyPath:@"Management Data.Monitors"];
NSInteger monitorIndex = 0;
if ([monitors count] > 1) {
    //search for main (or ask user to select)
}
NSDictionary *monitor = [monitors objectAtIndex:monitorIndex];
NSDictionary *spaces = [desktopPLIST valueForKey:@"spaces"];
NSString *currentSpaceUUID = [monitor valueForKeyPath:@"Current Space.uuid"];
NSDictionary *currentSpace = [spaces valueForKey:currentSpaceUUID];
NSURL *desktopPicturesDirectory = [NSURL fileURLWithPath:[currentSpace valueForKeyPath:@"default.ChangePath"] isDirectory:true];
NSString *desktopPictureName = [currentSpace valueForKeyPath:@"default.LastName"];
NSURL *desktopPictureURL = [NSURL URLWithString:desktopPictureName relativeToURL:desktopPicturesDirectory];
[[NSWorkspace sharedWorkspace] selectFile:[desktopPictureURL path] inFileViewerRootedAtPath:@""];
Disputation answered 3/3, 2013 at 2:17 Comment(2)
You want __bridge_transfer in those casts, since CFPreferencesCopyAppValue returns an owned reference. Also, NSWorkspace has activateFileViewerSelectingURLs:, relieving the need to extract a path from the URL, and NSURL has URLByAppendingPathComponent:.Eyelid
Has anyone figured out a way to refresh the property lists without having to kill the Dock?Medovich

© 2022 - 2024 — McMap. All rights reserved.