How to programmatically find the current value of DISPLAY when DISPLAY is unset? (for use in crontab)

I have a script that runs via cron to change the desktop wallpaper. I can get it to work by exporting the DISPLAY variable.

But the issue I am concerned about it that the script should work on all systems. How to find the value of DISPLAY set by the X server without using $DISPLAY?

So how can I find the correct value for DISPLAY programmatically. I can get it to work only when DISPLAY=:1. Setting it to ":0" makes the script exit with

No protocol specified
Cannot open display.

You can't for sure. You have to make assumptions.

Pretend you're cron and you're facing the worst case scenario for a second: there are multiple users logged in, and each user is running multiple X sessions. You'll have to guess the user (easy enough, we're executing their crontab) and one of that user's X sessions.

If we want to assume the user is running one and only one X session from a tty, and get that session's $DISPLAY value we can use w:

USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
user     tty1                      16:32    7:15   0.21s  0.19s -zsh
user     tty2     :1               15:52   48:13   2:17   0.08s /sbin/upstart
user     pts/3    :1               16:19    0.00s  0.66s  0.00s w

For example here I'm logged in on tty1, on tty2 (where I'm running an X session) and on pts/3 (the terminal from which I'm running the command).

With a bit of parsing:

% w $(id -un) | awk 'NF > 7 && $2 ~ /tty[0-9]+/ {print $3; exit}'
:1

So, assuming all the above:

0 0 * * * DISPLAY=$(w $(id -un) | awk 'NF > 7 && $2 ~ /tty[0-9]+/ {print $3; exit}') command

Will make cron execute command with $DISPLAY set to the first X session running in a tty's $DISPLAY value found for the user.


For a more detailed discussion, refer to https://unix.stackexchange.com/questions/17255/is-there-a-command-to-list-all-open-displays-on-a-machine

I will simply list the relevant information from that answer here:

There seems to be two simple ways to find the X server instances running on your system.

  • w:
    The w command lists all the open displays. You can then use awk to filter out the information you need. The values under FROM are the values corresponding to DISPLAY.

    ashhar@xenon:[/tmp/.X11-unix]🍷🍸 
    $ w
    21:18:24 up  3:39,  4 users,  load average: 0.31, 0.27, 0.30
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
    ashhar   tty2     :1               17:40    3:39m  6:11   0.08s /usr/bin/dunst
    ashhar   pts/0    :1               17:41    3:36m  0.13s  0.05s vim .i3/config
    ashhar   pts/1    :1               18:07    0.00s  1.44s  0.00s w
    ashhar   pts/2    :1               18:15    9:59   0.79s  0.79s bash
    
  • Local displays correspond to a socket in /tmp/.X11-unix, so we can simply do:

    cd /tmp/.X11-unix && for x in X*; do echo ":${x#X}"; done