Wallpaper Swapping with Hammerspoon

This is an old post!

This post is over 2 years old. Solutions referenced in this article may no longer be valid. Please consider this when utilizing any information referenced here.

Hammerspoon is a pretty nifty tool. It’s kind of difficult to explain what it does, but the best I can do is that it allows you to use Lua to script actions on your Mac and, crucially, respond to events.

For instance, I use Hammerspoon to lauch all my applications when I get to work and lay them out on the screen in the order that I like. I can do this because I was able to attach a location listener to work’s location, and execute Lua code on arrival. The amount of things that you can do with this tool is pretty stunning. It’s become an indespensible part of my macOS experience.

Today, I want to talk about swapping your wallpapers around. It serves as a useful visual reference for where you are if you have a dictinct wallpaper that you have at work vs. home.

Since changing the wallpaper is not natively supported in Hammerspoon, I first started looking around for a command line tool that would allow me to change the macOS wallpaper. It turns out you can do this with AppleScript using the osascript command. Many solutions I found were based around this approach, but it has some problems.

  1. There are some security hoops you have to jump through to give your script permission through universal access.

  2. If you have more than one desktop, the script devolves into forcing keystrokes to change to each desktop and then change the wallpaper, which sucks.

In general, it “works” but it’s a pretty poor solution.

But then I stumbled on this Go program that was the eureka moment. It turns out that macOS stores the data about wallpapers in a SQLite database! And, you can literally update this database and change the wallpapers.

Try it out yourself!

$ sqlite3  ~/Library/Application\ Support/Dock/desktoppicture.db
sqlite> select * from data;
~/Pictures/Backgrounds/Background.jpg
~/Pictures/Backgrounds/Background.jpg
~/Pictures/Backgrounds/Background.jpg
~/Pictures/Backgrounds/Background.jpg
~/Pictures/Backgrounds/Background.jpg
~/Pictures/Backgrounds/Background.jpg
~/Pictures/Backgrounds/Background.jpg
~/Pictures/Backgrounds/Background.jpg

There it is! A list of every background for every desktop and screen. Exploring around inside this database is interesting but a subject for another time. Right now, we want to change our background. Which you can now totally do with SQLite.

WARNING: the command below will set all your wallpapers to the default.

sqlite> update data set value = "";

You might have noticed that nothing changed. Unfortunately, the database isn’t read in realtime, and the way you can force it to read … is by killing Dock. Which makes total sense and is certainly graceful.

sqlite> .exit
$ killall Dock

And now your wallpaper is the default macOS wallpaper.

So, now that we know how to change the wallpaper using SQLite, back to Hammerspoon. Hammerspoon, of course, has SQLite support exposed through the Lua language, which you can access using hs.sqlite3. Using this, it’s pretty easy to construct a couple of functions that can get and set the wallpapers.

function setWallpaper(file)
    local connection = hs.sqlite3.open(os.getenv("HOME") .. "/Library/Application Support/Dock/desktoppicture.db")
    local dock = hs.appfinder.appFromName("Dock")
    connection:exec("update data set value = '" .. file .. "'")
    connection:close()
    dock:kill()
    hs.alert.show("Wallpaper Changed")
end

function getWallpaper()
    local connection = hs.sqlite3.open(os.getenv("HOME") .. "/Library/Application Support/Dock/desktoppicture.db")
    for a in connection:rows("select value from data limit 1") do
        connection:close()
        return a[1];
    end
end

Now, you can do fun things like this:

local wallpaper = ""
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
    wallpaper = getWallpaper()
    setWallpaper("")
end)

hs.hotkey.bind({"cmd", "alt", "ctrl"}, "H", function()
    setWallpaper(wallpaper)
end)

So now when you press command + control + option

  • W, you wallpaper changes to the default. And pressing command + control + option + H sets it back again.

With these two functions, we can now get and set the wallpapers. Note, that I’m just blindly setting all the screens to the same wallpaper. You could make these functions smarter by storing the individual wallpaper per screen and restoring that. But this is good enough for my purposes.

With this knowledge, it would also be pretty easy to write a small script to update it from the command line. The possibilities are endless.

Comments (0)

Interested in why you can't leave comments on my blog? Read the article about why comments are uniquely terrible and need to die. If you are still interested in commenting on this article, feel free to reach out to me directly and/or share it on social media.

Contact Me
Share It
What I Use
Since it’s been a good six years since I did one of these, here’s what I am using in the year 2022 as far as tech and tech-adjacent things.
Read More
What I Use
Since it’s been awhile since I wrote a post about what I use in regards to software, hardware, etc. Perhaps it’s time that I did that again. So here’s a list of what I’m using in 2016:
Read More
Mac
So there’s this program out there called Calibre which, despite it’s pretty terrible UI, is pretty much the gold standard for managing eBooks. Seriously, it’s such a great program whose only fault is its terrible engineer UI. One of the nice things that Calibre includes is a built-in web server that can serve books via OPDS. If you have an OPDS-compatible reader (I use Marvin), you can browse and download from your library directly on your device, basically creating your own private eBook cloud. But, this presents a little bit of an issue. Namely, I don’t want all of my books to be publicly available, while still providing a subset of my library for visitors to browse and use. But I still want to be able to access them myself from my “private reserve collection.” Fortunately, with a little bit of work, you can do that under Calibre.
Read More