Wallpaper Swapping with Hammerspoon

By · Published · hammerspoon, lua, mac, apple, osx

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 )

Did something I wrote help you out?

That's great! I don't earn any money from this site - I run no ads, sell no products and participate in no affiliate programs. I do this solely because it's fun; I enjoy writing and sharing what I learn.

All the same, if you found this article helpful and want to show your appreciation, here's my Amazon.com wishlist.


Related Posts

Securing static resources with cookies, nginx, and Lua

What I use: 2016


comments powered by Disqus