Is there a simple way to set up dynamic wallpaper switching in Sway for dual displays?

So I have recently started using Sway, and I like it. In i3, I have a simple little script using the feh utility that randomly changes my wallpaper every ten minutes. I wanted to set up something similar in Sway, but it was not as easy to do. I did try to get feh to work in Sway, but I was unsuccessful (You know, noobish…). I did find a script by a Mr. Antonin Dach that worked, but was rather complicated for such a simple task and only worked for a single display. Anyway, I wrote my own little script that is less complex and will do the job for two or more displays. I shall share it here for anyone else who wants to set this up and is having trouble. Anyway, it may not be the best solution, but I offer it up here as an option. Feel free to use it, change it, or laugh at it (I am sort of noobish, so what do I know?!). It does work. I will post it in the reply below as a solution to the problem.

2 Likes

Here is the script:

#!/bin/bash

#This script will change your background every ten minutes in Sway
 
while true
do
     #Create an array consisting of all your wallpaper files.  Use your own path to your wallpaper files
      
      Display=(~/Pictures/Wallpaper/*) 
 
      #Create a variable for each display using a random number
      #Random will automatically be equal to the number of files in your wallpaper folder
       
      x=$((0 + RANDOM % ${#Display[@]}))  
      y=$((0 + RANDOM % ${#Display[@]})) 
       
      #Optional conditional test to prevent the same wallpaper displaying on both displays
      #If you don't care about this, or only have a single display, skip these lines
 
            if [ "$x" -eq "$y" ]
            then
                  continue
            fi
 
      #Parse array for files to be displayed on each display and display them
      #Use your system's display names here, these are mine
 
      swaymsg output "HDMI-A-1" bg "${Display[$x]}" fill
      swaymsg output "HDMI-A-2" bg "${Display[$y]}" fill
 
      #Sleep for 10 minutes (or whatever interval you choose)
       
      sleep 10m

done

That is it! It works well enough. I am sure it could be improved, so feel free to improve it and adapt it to your needs. It is very simple, and that is what I was aiming for.

  • The ‘if’ statement is optional. It prevents the same image being displayed on both monitors at the same time in a dual display configuration. If x = y, the loop is restarted so you will always have a different image on each monitor. If you are not bothered by the potential image redundancy, or you only have a single monitor, you can eliminate this code.
  • if you only have a single monitor, eliminate one of the variable declarations and one of the ‘swaymsg’ statements as well.
  • The * glob at the end of the path eliminates the need to type out all the files in your directory. The * tells the script to parse all the files in the directory and works as shorthand for a much bigger array.
  • If you add (or remove) files to your wallpaper directory, you do not need to do anything to the script, it will automatically know the number of files in your directory.

Hopefully someone will find this useful…

EDITS: Lots of edits for typos, grammar, and getting the post formatted to meet the requirements of the Forum.

FINAL EDIT (I hope…): I made a change to the code in the above script. The random number will now automatically be the number of files in your array (representing your wallpaper folder). Now if you add or remove files from your wallpaper folder, you do NOT have to edit the script at all. It will automatically parse the correct number of files for you. You will have to restart Sway for any changes to take effect.

1 Like

Done, Fabby! I hope it is an improvement.

1 Like
  1. In the future, when providing code/output, please copy-paste that output in-between 3 backticks ``` at the beginning and end of the code/text so that the output looks like this:

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
    

    instead of like this:

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.`

    (as that makes reading output so much easier for everyone trying to copy your script) :innocent:

  2. No need to do anything right now as I’m a moderator here on this site. Please review my RAW edits by pushing the orange pencil in the upper right corner of your post. :wink:
    :clap:

1 Like

Thank you Fabby, for your guidance and edits! Post looks much better now. :sunglasses:

1 Like

Interesting problem. I only have one display, but found an elegant way to select a file randomly from a directory (thanks to Antonín Dach)

#Get a random file from $DIRNAME.
PIC=$(find "$DIRNAME" -type f | shuf -n 1 --random-source=/dev/random)

This only picks one file at random from $DIRNAME, so for two displays, you need “shuf” to return 2 lines with “shuf -n 2”. But then you need an array. If there are no spaces in your filename, bash can work its magic with

# PIC is now a two element array (if your files have no spaces)
PIC=($(find ~/Pictures/Wallpaper -type f | shuf -n 2 --random-source=/dev/random))

You can use “mapfile” if your files contain spaces, but I admit I don’t really get bash syntax sometimes (what is “< <” ??)

# PIC is now a two element array, even if your filenames contain spaces
mapfile -t PIC < <(find "$DIRNAME" -type f | shuf -n 2 --random-source=/dev/random)

Bash certainly has very elegant ways of doing things, if you can crib enough tips and tricks from the gurus on the internet :slight_smile:

2 Likes

Thanks @DrCrashALot!! Applied the Dr’s code prescription and tested it on two displays. This simple elegant script does the job perfectly!!

#!/bin/bash

while true
do

      PIC=($(find ~/Pictures/Wallpaper -type f | shuf -n 2 --random-source=/dev/random))

      swaymsg output "HDMI-A-1" bg "${PIC[0]}" fill > /dev/null
      swaymsg output "HDMI-A-2" bg "${PIC[1]}" fill > /dev/null

      sleep 10m

done

This works great with two displays (I tested it). Use your own path to your wallpaper directory, or put it into a variable like the Dr. Use your own display names as well. Clever code!! I doff my hat…

BTW, this script eliminates the need for the if then conditional test in my original script. You will always get two different images on each display!!

Therefore, I’ve marked this answer as the solution to your question as it is by far the best answer you’ll get.

Thanks Fabby for your help.

The actual solution to my problem is the script in the post #9. DrCrashALot’s code was a big part of that solution, but not quite the end of the story.

I do apologize if this thread is annoying. The good news is I rarely post here because I know almost nothing!! Also, this horse is good and dead now so I shall no longer kick it!!

I will respect your decision to call the Dr’s post the solution. A clever person is @DrCrashALot!! :face_with_monocle:

1 Like

@DrCrashALot Could you:

  1. incorporate the last bit of @BashCrash into your answer
    OR
  2. Let me know if I should give @BashCrash the solution.

@BashCrash If @DrCrashALot hasn’t responded in the next 24h, you can move the solution yourself.

For the future, if someone else does 90% of the hard work and points you in the right direction, give them a chance to incorporate the small specific additional change into their answer before awarding them the solution. If they don’t respond within 24h/48h, please edit your post to include the complete solution and then award yourself said solution.

:bowing_man:

P.S. You do both seem to have a propendency for crashing, so please read this too

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.