Mavericks read current desktop background filename?

Is there a way to read the currently displayed background image? I want to use geek tool to display the filename as a caption overlaying, so I know which of the 2000 images it is.

Hi nc12fth,

Look in the System Events dictionary:

tell application "System Events"
	set posix_path to (picture of current desktop)
	set file_ref to (POSIX file posix_path) as alias
	set file_name to displayed name of file_ref
end tell

gl,
kel

So I understand you and you understand me :slight_smile:

I have it set to change random desktop backgrounds every 30 mins, this pulls from a folder of 2000+ backgrounds, saved form InterfaceLift.

I want to know which file is being displayed with a caption, in case I want to know more about the picture.

Will this do that?

Hi ncl2fth,

Not sure if I understand you, but as is, I don’t think the system allows you to display a desktop picture with a caption. You would need to create a new file with a caption. Then display that image. Not sure how to do that.

gl,
kel

Hi.

I’m not sure that there is a scriptable way now to get the current desktop picture when you’ve got a random choice set in System Preferences. At least, I haven’t been able to find it. The only thing recorded in the preference files is the folder from which the random choices are made.

Hi Nigel,

I didn’t know that. Thanks for pointing that out.

Edited: checked it out and it’s not the random order but the changing picture setting.

Goodday,
kel

What I’m thinking is that you might be able to see which file of the pictures folder is in use somehow.

This looks like the list of pictures in the current folder when you have the change picture preference set:

2077808447 doesn’t exist if change picture is not set.

Edited: note that these pictures have indexed names. That’s their real names.

Edited: operator error. Those aren’t the real names of the files. They must be aliai. Need to check if you can get the original item of an alias.

You can get the folder easily enough, and you should be able to get the most recently accessed file from that. You could use ls with the -u option, and run it in some sort of repeat loop. FWIW, here’s a proof-of-concept snippet of ASObjC code that does the same thing:

use framework "Foundation"
use framework "AppKit"

on doIt()
	repeat 5 times
		set theScreen to current application's NSScreen's mainScreen() -- change to suit
		set fm to current application's NSFileManager's defaultManager()
		set theKey to current application's NSURLContentAccessDateKey -- what we want
		set theDescriptor to current application's NSSortDescriptor's sortDescriptorWithKey:theKey ascending:false -- so we can find the most recently accessed
		set theInfo to current application's NSMutableArray's array() -- array to store new values in
		set theURL to (current application's NSWorkspace's sharedWorkspace()'s desktopImageURLForScreen:theScreen) -- get URL of folder being used
		set theFiles to (fm's contentsOfDirectoryAtURL:theURL includingPropertiesForKeys:{theKey, current application's NSURLNameKey} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)) as list -- get contents of folder
		repeat with aFile in theFiles -- loop through getting names and last access dates
			(theInfo's addObject:(aFile's resourceValuesForKeys:{theKey, current application's NSURLNameKey} |error|:(missing value)))
		end repeat
		theInfo's sortUsingDescriptors:{theDescriptor} -- do the sort
		log ((theInfo's objectAtIndex:0)'s valueForKey:(current application's NSURLNameKey)) as text -- log the file's name
		current application's NSThread's sleepForTimeInterval:5.0 -- sleep for 5 seconds
	end repeat
end doIt

And my log:

0000.016  (*Death Valley.jpg*)
0005.029  (*Panther Aqua Blue.jpg*)
0010.042  (*Grass Blades.jpg*)
0015.055  (*Underwater.jpg*)
0020.069  (*Aqua Blue.jpg*)

:cool:

I figured I could have figured it out by using lsof, but I also figured out, by the time I had used forensics on lsof’s outpuut, the picture would have been gone from the background.

IMHO ASOC as it best, and it sleeps too!

Yes. That works, provided no other items in the folder are accessed between the picture change and the script being run.

tell application "System Events" to set pFldr to pictures folder of current desktop
set picturePath to pFldr & "/" & paragraph 1 of (do shell script "ls -tu " & quoted form of pFldr)

That’s probably a safe bet. Call it from an idle handler and Bob’s your uncle.

Good stuff. BTW, scripts might error if the user has the changing of the desktop off. Just nit picking. Nice scripts.

Have a good day,
kel

This works nicely in osascript (this is how I plan to get this into geek tool and overlay over the desktop).

Thank you. The objective-C stuff was way over my head.

Is there a way to format output to strip the path?

Thanks!

Thanks for such a simple script! This is very much like what I was looking for, as the shell script I used in Lion doesn’t work in Mavericks.

The only thing missing is a way to produce the output for all work spaces. Is there a simple way to do more than “current desktop” in Mavericks?

Just wanted to share a technique related to the current topic that might be helpful. It returns the current desktop’s picture placement setting, which is not available through System Events. The three-step procedure consists of:

(1) relaunching the Dock to make current the com.apple.spaces plist file
(2) getting the uuid of the current desktop from the com.apple.spaces plist file
(3) getting the current picture placement setting of the current desktop from the com.apple.desktop plist file (located at “[current desktop’s uuid]” > default > Placement in the plist file)

do shell script "" & ¬
	"killall Dock ;" & ¬
	"sleep 1 ;" & ¬
	"u=$(defaults read com.apple.spaces | sed -En '/Current Space/,/uuid =/s/^.+uuid = ([\"][^\"]*[\"]).+$/\\1/p') ;" & ¬
	"defaults read com.apple.desktop | sed -En '/'\"$u\"'/,${/default =/,/Placement =/s/^.+Placement = ([a-zA-Z]+);$/\\1/p;}' | egrep -m 1 .+"

This will return one of the following signifying the current desktop’s picture placement setting:

“Crop” (= preference pane’s “Fill Screen”)
“SizeToFit” (= preference pane’s “Fit To Screen”)
“FillScreen” (= preference pane’s “Stretch To Fill Screen”)
“Centered” (= preference pane’s “Center”)
“Tiled” (= preference pane’s “Tile”)

(I suspect the sed/egrep scripts can be tidied up a bit.)

Regarding the original question of this post, further investigation revealed the following:

The current desktop image is saved in com.apple.desktop > Background > spaces > [current desktop uuid] > default. The tricky part was discovering that the image is saved in different properties depending on whether the desktop is static or dynamic:

Static desktop (i.e., set to never rotate):
The image path is saved in the ImageFilePath property.

Dynamic desktop (i.e., set to rotate at a time interval, at sleep, or at login):
The image container path is saved in the ChangePath property.
The image name is saved in the LastName property.

In the latter case, the image path can be reconstructed as: image path = image container path + “/” + image name

The following shell script adjusts for the current desktop rotation status and returns the current desktop image path. As described in my prior post, it also returns the current desktop picture placement setting (Crop, SizeToFit, FillScreen, Centered, or Tiled), which is also not accessible through System Events.

set {imagePath, imagePlacement} to paragraphs of (do shell script "" & ¬
	"killall Dock ; " & ¬
	"sleep 1 ; " & ¬
	"currUuid=$(defaults read com.apple.spaces | sed -En '/Current Space/,/uuid *=/s/^.+uuid *= *([\"][^\"]*[\"]).+$/\\1/p') ; " & ¬
	"currDesktopSettings=$(defaults read com.apple.desktop | sed -En '/'\"$currUuid\"' *= *[{]/,/\"[^\"]*\" *= *[{]/{/default *=/,/[}][;]/p;}') ; " & ¬
	"rotationType=$(echo \"$currDesktopSettings\" | sed -En 's/^ +Change *= *([a-zA-Z]+)[;]$/\\1/p') ; " & ¬
	"[ \"$rotationType\" = Never ] " & ¬
	"&& " & ¬
	"imagePath=$(echo \"$currDesktopSettings\" | sed -En 's/^ +ImageFilePath *= *[\"]([^\"]+)[\"][;]$/\\1/p') " & ¬
	"|| " & ¬
	"imagePath=$(echo \"$currDesktopSettings\" | sed -En 's/^ +( ChangePath|LastName) *= *[\"]([^\"]+)[\"][;]$/\\2/p' | tr '\\n' '/' | sed -E 's/[/]+$//') ; " & ¬
	"imagePlacement=$(echo \"$currDesktopSettings\" | sed -En 's/^ +Placement *= *([a-zA-Z]+)[;]$/\\1/p') ; " & ¬
	"echo \"$imagePath\\n$imagePlacement\"")

Killing the Dock is necessary at least in some circumstances to assure that the plist file remains up-to-date. A side-effect of killing the Dock is a rotation to the next image if the desktop is dynamic, although the script does return the proper image path of the newly displayed image. If there were a way to keep the plist file up-to-date without having to kill the Dock, this unfortunate side-effect would be avoided, but I am unaware of how to do this.

How does killing the Dock keep a plist file up-to-date? And why would it matter when you’re not reading a plist file?

The shell script approach I presented retrieves the current desktop image from the com.apple.desktop.plist file. The Dock application does appear to be a (the?) principle application controlling Mission Control and its desktops. I learned from a post from Nigel Garvey on another thread that rebooting the Dock (by killing it; it reboots immediately) does update the plist file, and my experience corroborates that.

I have been working on an ASObjC app that manages the desktop image. Something happens in the app that keeps the com.apple.desktop.plist file up to date without the need to reboot the Dock, but I haven’t isolated what that something is yet. If and when I do, I’ll certainly post it.

Really, it doesn’t. There’s a common misconception that applications write their preferences to a plist file in the Preferences folder, and that the shell command defaults reads these files. That’s not actually what happens.

The plist files are read/written by a separate process. Applications and the defaults command actually communicate with that process, and not the files on disk (which, if the app is sandboxed or uses iCloud, can be stored elsewhere anyway).

This separate process caches the values and decides when to write to file, and as of Mavericks, it appears to be in no hurry after a change is made. And there’s no great need to, other than the fact that a complete system crash might mean something never gets written, because nothing else reads those files.

But the state of that file really only matters if you are trying to read it directly – it doesn’t matter if you are using defaults, which should give you the latest values.