Skip to main content

Where I work has two separate desktop background policies: a standard one for all people and a custom one for certain departments. 10.9 really threw some spanners in the works when Apple shifted from MCX to their sqlite3 database to control this.

However thanks to being persistently bugged by Casper warning email messages around 50+ times per day, I think I have it! I should point out there are solutions out there for this, but they're mainly for the Munki crowd and written in python and in one case, Ruby. I am not a Ruby man. Or Python.

Anyway I like the idea of as few policies to do the job as possible. Also, as few scripts as possible too. So before I release this on my github ... here's what I have.

#!/bin/bash

# Script to set desktop background for students

# Author : r.purves@arts.ac.uk
# Version 1.0 : Initial Version
# Version 1.1 : 23/04/2014 - Massive reworking to use applescript for 10.8 and below, modify the db for 10.9+
# Version 1.2 : 24/04/2014 - Removed applescript because of osascript parsing issues. replaced with mcx.

OSversion=$( sw_vers | grep ProductVersion: | cut -c 20-20 )
currentuser=$( ls -l /dev/console | awk '{print $3}' )

if [ "$4" = "custom" ];
then

if [[ "$OSversion" -ge "9" ]];
then

sqlite3 /Users/$currentuser/Library/Application Support/Dock/desktoppicture.db << EOF
UPDATE data SET value = "/Users/Shared/Background/custombg.jpeg";
.quit
EOF

killall Dock

else
defaults write com.apple.desktop Background '{default = {ImageFilePath = "/Users/Shared/Background/custombg.jpeg"; };}'
killall Dock
fi

else

if [[ "$OSversion" -ge "9" ]];
then

sqlite3 /Users/$currentuser/Library/Application Support/Dock/desktoppicture.db << EOF
UPDATE data SET value = "/Library/Desktop Pictures/default_grey2560x1600.jpeg";
.quit
EOF

killall Dock

else
defaults write com.apple.desktop Background '{default = {ImageFilePath = "/Library/Desktop Pictures/default_grey2560x1600.jpeg"; };}'
killall Dock
fi

fi
exit 0

A little explanation as I haven't finished annotating my code yet. If you put the word "custom" in parameter 4 when calling the script, you'll get the custom background file or else you get the standard one.

For 10.8 computers and under, the script setting is done through an osascript call which has been detailed on many other posts on jamfnation. Since Casper scripts run as root, i'm using "su -l" to run the command as the currently logged in user which seems to help A LOT.

For 10.9 computers and above, i'm directly manipulating the currently logged in user's desktoppictures.db file and reprogramming ALL entries in the data table. To force the change through, I'm killing the dock process. It's not the most seamless process in the world, but it does work. This has not been tested for multi monitor support but it should theoretically work.

Credits and kudos for providing the information to make this all work go to the following:
http://1klb.com/blog/desktop-background-on-os-x-109-mavericks.html for his work on the database file side of things
/url">@rtrouton][/url for his work located at [http://derflounder.wordpress.com/2013/10/26/mavericks-desktop-background-picture-settings-moved-from-librarypreferencescom-apple-desktop-plist/

Hi,
Did anybody checked the latest revision of @seanhansell on High Sierra or Mojave?
Can't seem to get it to work.


I found, can't remember where, and use this to set ours for different users by passing through the parameters from a bash script, works well on 10.12 and 10.13.
call it from bash like this python scriptname --path wallpaperpath

#!/usr/bin/python

'''Uses Cocoa classes via PyObjC to set a desktop picture on all screens.
Tested on Mountain Lion and Mavericks. Inspired by Greg Neagle's work: https://gist.github.com/gregneagle/6957826

See:
https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSWorkspace_Class/Reference/Reference.html

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/Reference/Reference.html

https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSScreen_Class/Reference/Reference.html
'''

from AppKit import NSWorkspace, NSScreen
from Foundation import NSURL
import argparse
import sys

parser = argparse.ArgumentParser(description='Sets the desktop picture on all screens')
parser.add_argument('--path', help='The path of the image')
args = vars(parser.parse_args())

if args['path']:
    picture_path = args['path']
else:
    print >> sys.stderr, 'You must supply a path for the desktop picture'
    exit(-1)

# generate a fileURL for the desktop picture
file_url = NSURL.fileURLWithPath_(picture_path)

# make image options dictionary
# we just make an empty one because the defaults are fine
options = {}

# get shared workspace
ws = NSWorkspace.sharedWorkspace()

# iterate over all screens
for screen in NSScreen.screens():
    # tell the workspace to set the desktop picture
    (result, error) = ws.setDesktopImageURL_forScreen_options_error_(
                file_url, screen, options, None)
    if error:
        print error
        exit(-1)

@marklamont thats Graham Gilbert’s script, as mentioned earlier. https://github.com/grahamgilbert/macscripts/blob/master/set_desktops/set_desktops.py


@Ninyo My script is still working for me. :)


Thanks for the Python script Graham Gilbert.

Not sure if it helps anyone else, but I made a few modifications.

I've tested on Catalina deploying from Jamf and it works great.

  • Removed the option to supply a path with --path.
  • Added a check to make sure that the file type is .png.
  • Update: Make sure to put the desktop photo in a place like /Library/Desktop Pictures and point the script to that location.
#!/usr/bin/python

"""A script to set the desktop wallpaper on macOS.
"""

###############################################################################
#
#   DESCRIPTION:
#
#
#       Uses Cocoa classes via PyObjC to set a desktop picture on all screens.
#       Tested on Mountain Lion and Mavericks. Inspired by Greg Neagle's work:
#
#           https://gist.github.com/gregneagle/6957826
#
#       Modified from @gramhamgilbert's script:
#
#       https://github.com/grahamgilbert/macscripts/blob/master/set_desktops/
#       set_desktops.py
#
#       Removed the ability to define a file path via argparse. Defines the
#       path to the image dynamically in the script. The image should live in
#       the same location where the script is being executed.
#
#       The onlything hardcoded in the script is the name of the image. In this
#       case the PICTURE_NAME is set to "stock_wallpaper.png"
#
#   SEE:
#
#       https://developer.apple.com/library/mac/documentation/cocoa/reference/
#       applicationkit/classes/NSWorkspace_Class/Reference/Reference.html
#
#       https://developer.apple.com/library/mac/documentation/Cocoa/Reference/
#       Foundation/Classes/NSURL_Class/Reference/Reference.html
#
#       https://developer.apple.com/library/mac/documentation/cocoa/reference/
#       applicationkit/classes/NSScreen_Class/Reference/Reference.html
#
###############################################################################


import sys
import os


from AppKit import NSWorkspace, NSScreen
from Foundation import NSURL

HERE = os.path.abspath(os.path.dirname(__file__))
PICTURE_NAME = "stock_wallpaper.png"

picture_path = os.path.join("/Library", "Desktop Pictures", PICTURE_NAME)


def verify_file_extension():
    """Verify that file extension is set to png"""
    if not PICTURE_NAME.endswith(".png"):
        print(
            "ERROR: Make sure that you are using a PNG file for your desktop "
            "image."
        )
        print("Picture Name: %s" % PICTURE_NAME)
        sys.exit(1)


def gen_file_url(path):
    """generate a fileURL for the desktop picture"""
    global file_url
    file_url = NSURL.fileURLWithPath_(picture_path)
    return file_url


def get_shared_workspace():
    """get shared workspace"""
    global ws
    ws = NSWorkspace.sharedWorkspace()
    return ws


def apply_desktop_wallpaper(ws, url):
    """Apply desktop wallpaper"""

    # make image options dictionary
    # we just make an empty one because the defaults are fine
    options = {}

    # iterate over all screens
    for screen in NSScreen.screens():
        # tell the workspace to set the desktop picture
        result = ws.setDesktopImageURL_forScreen_options_error_(
            file_url, screen, options, None
        )

        for item in result:

            if item is True:
                print("Wallpaper applied!")
                break

            elif item is False:
                print("Wallpaper NOT applied successfully ...")
                print(result)
                sys.exit(1)
            else:
                pass


def main():
    """The main event."""

    verify_file_extension()
    gen_file_url(path=picture_path)
    get_shared_workspace()
    apply_desktop_wallpaper(ws=ws, url=file_url)


if __name__ == "__main__":
    main()