6

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.
  • If the server is listening on TCP DISPLAY = portnumber - 6000 But ... I doubt cron is smart enough to get this to check for a free port. So you probably need a script to set it. – Rinzwind Mar 11 '16 at 15:27
  • UNIX/Linux is an inherently multi-user, multi-session environment: one of the Ubuntu systems I'm currently logged in to has X servers running on displays :0, :2, and :5 (and often has more). So first you need to think about what you mean by "the X server" – steeldriver Mar 11 '16 at 15:28
  • 1
    This seems a good starting point: unix.stackexchange.com/questions/17255/… – Rinzwind Mar 11 '16 at 15:29
  • By the X server I mean the WM and/or the DM instance. I need to change the wallpaper for the desktop. I guess I can use a loop and run the commands on all DISPLAY from 0 through 10? – Ashhar Hasan Mar 11 '16 at 15:33
  • Thanks @Rinzwind. I found the answer and will be adding an answer here after proper testing. – Ashhar Hasan Mar 11 '16 at 15:36
6

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.

  • 1
    Well, I guess cron is too low level stuff to be handling wallpapers then. Thanks for the great answer. – Ashhar Hasan Mar 11 '16 at 18:00
  • 2
    @AshharHasan I think so as well. Perhaps you should demonize a script and let it do the task, it looks both easier and more reliable. – kos Mar 11 '16 at 18:18
1

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
    

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.