Using Applescript to realize custom dynamic wallpaper

Using Applescript to realize custom dynamic wallpaper

1. Dynamic Wallpaper

When it comes to the whole dynamic wallpaper on apple, we have to talk about his own scheme first. Apple first introduced this concept in macOS Mojave, which changes the desktop wallpaper at different times of the day. The technology used is to set the wallpaper with a Heic format picture with time information, which contains multiple tiff pictures and time information. When it's time, switch the pictures in the application. This technology is very good, does not occupy resources and is practical. For some time, you can even make such pictures to realize such functions, but now, it's impossible on Big Sur. Because the wallpaper directory is here in / System/Library/Desktop\ Pictures. The whole system directory is read-only, so you can't add it yourself.

At this point, the "official channel" that can realize this function has been "blocked". We have to find another way.

Before looking up the information, I thought about whether it was possible to use scripts to do this. After all, Mac is Unix. Is it possible to use Bash script with some event hooks to realize dynamic wallpaper.

After searching, it can be, but it is a little different from what you think.

You can use Applescript to do this, and use Launchctl to implement timed calls. Theoretically, cronab + bash should also be used to implement the call, but Cron seems to have little effect on the latest system. Even if you give access to the entire file. It is temporarily estimated that it is due to Apple's security design. In addition, using Lauchctl has advantages that Cron does not have. Let's talk about it in detail. If you are a Linux machine, you can use cronab + bash to achieve this effect. But I didn't try 🤔

2. Applescript

So what is Applescript?

AppleScript is a scripting language created by Apple. It allows users to directly control scriptable Macintosh applications, as well as parts of macOS itself. You can create scripts—sets of written instructions—to automate repetitive tasks, combine features from multiple scriptable applications, and create complex workflows.

The above is an introduction to Apple's official website. Basically, it can be understood as a script language specially tailored for apple under the apple system.

On the basis of writing other languages, the learning cost of Applescript is not high. And it is very similar to the natural language of English. So on the whole, it's good to learn. It's just that there's no IDE 🌚 The system has an Applescript editor. It can be said that there is no Notepad under Windows. When I learned basic grammar, there was a Bug in the highlight card of the script. I can't write when I write. However, because Applescript cannot be opened with an editor such as Vim, there is no choice.

We need to have the wallpaper ready before we start. My idea is to change every 24 hours. A day consists of 24 different wallpapers. You can take a group of pictures at a fixed time in a place, or look for other people's works on the Internet.

Here I put the file in the picture under the home directory:

# Display X refers to the X-th display.
# The picture name is the specific number of hours.
~/Pictures/Wallpapers/Display1/01.jpg

like this:

The script we want to use is as follows:

(*

Script By:
Puls Garney 08.11.2021

The Idea Came From:
https://github.com/pipwerks/OS-X-Wallpaper-Changer

Picture Locations:
~/Pictures/Wallpapers/Display1/01.jpg
~/Pictures/Wallpapers/Display2/03.jpg
~/Pictures/Wallpapers/Display3/24.jpg

*)

set useSameWallpaper to false

set wallpaperPath to "~/Pictures/Wallpapers/Display"

set currentPhoto to hours of (current date) as string

if ((the length of currentPhoto) < 2) then
    set currentPhoto to "0" & currentPhoto
end if

tell application "System Events"
    
    if (useSameWallpaper) then
        
        tell every desktop
            
            set wallpaper to wallpaperPath & "1/" & currentPhoto & ".jpg"
            
            set picture to POSIX file wallpaper
            
        end tell
        
    else
        
        set displays to a reference to every desktop
        
        repeat with counter from 1 to (count displays)
            
            try
                
                set wallpaper to (wallpaperPath & counter as string) & "/" & currentPhoto & ".jpg"
                
                set picture of item counter of displays to POSIX file wallpaper
                
            end try
            
        end repeat
        
    end if
    
end tell

Let me talk about the following functions paragraph by paragraph:

set useSameWallpaper to false

This sentence is a variable. If you want to use the same set of pictures on all monitors, you can change it to true. I want each to be different, so I used false

set currentPhoto to hours of (current date) as string

if ((the length of currentPhoto) < 2) then
    set currentPhoto to "0" & currentPhoto
end if

Then we get the hours of the current system time. If the total length of the hours is less than 2 digits, it will be filled into 2 digits. For example, it will be "02" instead of "2" at two o'clock.

Next, we use the system event method to broadcast different events of setting pictures.

tell every desktop

    set wallpaper to wallpaperPath & "1/" & currentPhoto & ".jpg"

    set picture to POSIX file wallpaper

end tell

If you want all monitors to display the same set of pictures. The first set of pictures will be used here.

set displays to a reference to every desktop

repeat with counter from 1 to (count displays)
    
    try
        
        set wallpaper to (wallpaperPath & counter as string) & "/" & currentPhoto & ".jpg"
        
        set picture of item counter of displays to POSIX file wallpaper
        
    end try
    
end repeat

Here we get all the display instances. And set different pictures for each different display according to the previously prepared picture path.

By and large, the script is ready. If you have picture resources, you can try to run it manually. Just run it directly in the Applescript editor. The built-in editor is called Script Editor. You can find it with Spotlight.

Or if you have saved the script, you can run it directly from the command line.

osascript ~/Pictures/Wallpapers/Wallpaper.scpt

If there is a problem with the operation, you can add a log xxx to print the log debugging.

Now we have pictures and scripts. The next thing is to make him move. We can't manually open an editor and run it every once in a while 🤣🤣

3. Launchctl

So what is Lauchctl?

Launchctl is a unified service management framework introduced after Mac OS X 10.4. It can start, stop and manage daemons, applications, processes and scripts. It specifies the execution cycle and tasks through the plist configuration file.

The main places to place plist are:

~/Library/LaunchAgents           # Current user-defined task
 /Library/LaunchAgents           # System administrator defined tasks
 /Library/LaunchDaemons          # Administrator defined system daemon tasks
 /System/Library/LaunchAgents    # Apple defined tasks
 /System/Library/LaunchDaemons   # Apple defined system daemon tasks

If you are familiar with Crontab, they are similar in general. There are many settable contents in plist file, which uses xml language. If you are interested, you can learn more by yourself. Next, I will only talk about the settings we need in this example.

Let's create a new file under ~ / Library/LaunchAgents:

<!-- ~/Library/LaunchAgents/me.dynamic.wallpaper.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- Label Unique identification -->
    <key>Label</key>
    <string>me.dynamic.wallpaper.plist</string>

    <!-- Specify the script to run -->
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/osascript</string>
        <string>/Users/pulsgarney/Pictures/Wallpapers/Wallpaper.scpt</string>
    </array>

    <!-- Specify when to run -->
    <key>StartCalendarInterval</key>
    <dict>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
</dict>
</plist>

The above file defines that we use osascript to run the script we wrote earlier. Run once at the beginning of the first minute of each hour, that is, 24 times a day. If your script has problems and can't run normally, you can add the following two sentences to make a log for debugging.

    <!-- Standard output file -->
    <key>StandardOutPath</key>
    <string>/Users/pulsgarney/Pictures/Wallpapers/log/output.log</string>

    <!-- Standard error output file, error log -->
    <key>StandardErrorPath</key>
    <string>/Users/pulsgarney/Pictures/Wallpapers/log/error.log</string>

If you want it to refresh more frequently, you can add this sentence to refresh it regularly. The unit is seconds:

    <!-- time interval -->
    <key>StartInterval</key>
    <integer>300</integer>

Next, we can load the task from the command line.

# Load task
launchctl load me.dynamic.wallpaper.plist
# Uninstall task
launchctl unload me.dynamic.wallpaper.plist

# Run the task once, regardless of the time setting of the file
launchctl start me.dynamic.wallpaper.plist

If everything is normal, the wallpaper will be automatically changed every hour after load ing. Moreover, the advantage of using launchctl over cronab is that when your computer does not have the task triggered when it is working, it will be integrated into a task to run once after you re run the computer.

This application scenario is mainly for example, when I use up the computer and close the screen, the next time I open it, whenever I open the screen, it will update my wallpaper, which can avoid the trouble that you open the computer in the middle of the night and the morning. As a result, your wallpaper shows that the wallpaper in the sun at two or three o'clock in the afternoon will send people away as soon as the screen is on.

That's all for today. Break it~

Tags: macOS MacBook

Posted on Wed, 17 Nov 2021 06:05:24 -0500 by darksniperx