I recently made the switch from Windows 11 using WSL to Arch using Hyprland as my desktop environment, and I couldn't be happier with the change. Windows 11 came pre-installed on my laptop, and I didn't initially get around to changing the OS. WSL is impressive in a sense, it's very good at what it does, but Windows in general is a pain and small issues kept cropping up, so I made the switch.
This was also my first time using either Wayland or Pipewire/Wireplumber, so I was a bit concerned about using Wayland with my GTX 3060, but so far so it hasn't caused many issues. The only issue I haven't been able to resolve was that Emacs clients will crash when I switch desktops from time to time, but using the Emacs daemon isn't actually a part of my workflow, so I can happily overlook that for now until I figure it out or it gets fixed by an update.
I was initially turned off by the simplicity of hyprpaper, but after closer inspection I realized that with a bit of effort and a shell script, it provided me with the tools I needed to configure it to work pretty much however I wanted. I set to work on making this script to control how my wallpapers are loaded. I thought I would share how I did it in the hopes that it could be helpful to someone else doing a similar setup or perhaps someone could recommend some changes to improve it.
# Setup wallaper directory and monitor names. dir="/home/naokotani/Pictures/wallpapers/" monitor1="eDP-1" monitor2="DP-1" cd $dir # Get a list of the .png files in the directory. papers=("${dir}"*.png) cd /home/naokotani/.config/hypr/ # Get the length of the list of files. length=${#papers[@]}
This section sets everything up. I set the values for the wallpaper directory and my monitor names. This makes it easy to change later because I just need to change it once at the start of the file. I then cd into the directory and get the list of .png files. hyprpaper only works on .png, so I want to filter out any .jpg or other files that might be left in there so it doesn't break. I also remove my old hyprpaper.conf because I am going to be generating a new one from scratch. Finally, I get the length of the list of files that I will be using to choose a random wallpaper later.
# Get the first random index. first=$((1 + $RANDOM % $length - 1)) function dif_random { second=$((1 + $RANDOM % $length - 1)) # Check to see if the second random index is equal # to the first, if so, find a new one. if [ $1 -eq $second ] then dif_random "$1" else return $second fi } # Make sure that there is more than one file. # This avoids an infite loop. if [ $length -gt 1 ] then dif_random "$first" second=$? else second=$first fi
This little section is responsible for selecting a random paper by taking the length and deducting one to select a random index. Then the function dif_random()
will get a second random number and check to see if it is the same as the first, if it is, it will run the function again with the first random number as the argument. Once it finds a valid number it returns it. There is no doubt a better way to do this, but this works well enough. Finally I check to make sure that the length is greater than 1 before I actually call the function. If it's not, it doesn't call dif_random()
because that would end up causing a stack overflow or infinite loop, either way it would break the script.
# Use the random indexes to get the two filenames paper_one=${papers[$first]} paper_two=${papers[$second]} # Check to see if hyprpaper is running if pgrep -x "hyprpaper" > /dev/null then # If hyprpaper is running, select two new preloaded wallpapers hyprctl hyprpaper wallpaper "${monitor1},${paper_one}" hyprctl hyprpaper wallpaper "${monitor2},${paper_two}" else # Remove the old conf file. rm -f hyprpaper.conf # If it's not running, generate a new hyprpaper.conf for p in "${papers[@]}"; do echo "preload = ${p}" >> hyprpaper.conf done echo "wallpaper = ${monitor1},${paper_one}" >> hyprpaper.conf echo "wallpaper = ${monitor2},${paper_two}" >> hyprpaper.conf # Start hyprpaper hyprpaper & disown fi
Finally, I use my newly created numbers to index into the papers list and grab two random file names. I then check to see if hyprpaper is running, if it is I load the new wallpapers, if it is not I generate a new hyprpaper.conf
and start hyprpaper.
I place this script in two places in my hypland.conf
file. First I put it as an exec-once command. This way whenever hyprland starts, it will run the script, find that hyprpaper is inevitably not running, generate the hyprpaper.conf
, select two random wallpapers and start hyprpaper. If at any point I want to change my wallpaper list, I just need to change the directory in the script, or put new wallpapers in the wallpapers directory, pkill -9 hyprpaper
and run the script. This will regenerate a new configuration from the directory and reload hyprpaper. I also bind the script to a keybind so that, whenever I feel the need, I can pick two new random wallpapers.
Finally, you can use cron to have the script run on a schedule once an hour, once a day or whatever you like to periodically change your wallpaper. This shouldn't have cause any significant performance issues if it comes up in the middle of doing something else because the wallpapers are already pre-loaded into memory.
I initially ran this script on about a dozen very large image files. As is pointed out in the documentation for hyprpaper, this both took a long time to load (I believe it was around 8 seconds), and took a decent chunk of memory. I was able to mitigate this problem a little bit by getting rid of a few of my least favorite wallpapers and then using ImageMagick to reduce the size of the images down to my actual screen resolution by running magick image.png --resize 2560x1440 image.png
you can also use this to switch from a different file type to a png, for example: magick image.jpg --resize 2560x1440 image.png
reducing the size of the images greatly reduced the time it took to load going down to perhaps a second or less (the initial images were something in the order of 8000x6000, which was quite excessive anyway).
Another small issue with the script is that it will always choose two different images, but one or both of them might be the same as the one that is already loaded. I thought about using the suggested command from the wiki, hyprctl hyprpaper listactive
to find the active paper, but for some reason it just wasn't working. I finally determined that this wasn't such a big issue. The wallpapers almost instantly, so its not a big deal to simple press the keybind twice if I get a result I don't like.
(Credit to trev for these observations)
My method for getting a random image involved using a recursive function that keeps getting called until two different random numbers are selected. It's also possible to use a while loop to accomplish the same thing, but the effective difference between the two is probably pretty trivial. Both would have the limitation of creating an infinite loop if fewer than two filenames are supplied, which requires the check to make sure $length
is greater than 1. This solution requires quite a few lines of code and is a bit complex. It also depends on $RANDOM
, which is a bash internal. A similar solution could be based on using the shuf coreutil, which would be less complicated, require fewer lines of code and be more readable. If you wanted the script to be fully posix compliant, you could generate a random number yourself in the script instead of relying on $RANDOM
.
Messing around with computers and coding since I was 8. Now getting paid to do what I love.