11

I'm using Arch Linux and things are not so easy when you try to discover the current Xorg DISPLAY (not through the env variable).

Here is a working example:

$ cat /sys/class/tty/tty0/active
tty2

who | awk -v term="tty2" '/\(:/ $0 ~ term { gsub(/\(|\)/, "", $0); print $5 }'
:0

That works in some cases, in others not:

$ cat /sys/class/tty/tty0/active
tty3

who | awk -v term="`cat /sys/class/tty/tty0/active`" '/\(:/ $0 ~ term { gsub(/\(|\)/, "", $0); print $5 }'
NOTHING

Cause:

$ who
uzumaki  tty2         2015-05-16 10:50 (:0)
guest    pts/2        2015-05-16 11:47 (:1)
uzumaki  tty4         2015-05-16 11:07

The guest user (tty3) had his session lauched using pts (pseudo terminal).

I'm using gdm and it launches xorg servers without a specific command (not clarifying the display number).

$ ps au | grep -e Xorg -e vt
uzumaki    788  1.4  0.9 740464 150672 tty2    Sl+  10:50   5:38 /usr/lib/xorg-server/Xorg vt2 -displayfd 3 -auth /run/user/1000/gdm/Xauthority -nolisten tcp -background none -noreset -keeptty -verbose 3
guest     1062  0.0  0.1 465748 32344 tty3     Sl+  10:51   0:19 /usr/lib/xorg-server/Xorg vt3 -displayfd 3 -auth /run/user/1002/gdm/Xauthority -nolisten tcp -background none -noreset -keeptty -verbose 3

So the million question is: How to find out the current (in use) XServer DISPLAY number? Current because, as above, we can have multiples servers (multiple users).

Geyslan Gregório
  • 267
  • 1
  • 2
  • 9

5 Answers5

9

You may try to use loginctl list-sessions to get the list of sessions, and then use loginctl show-session -p Display -p Active <session ID> on each of these to get the X11 display number associated with the currently active session.

Something like this (in bash):

TARGET_DISPLAY=()
while read id uid user seat; do
    while IFS='=' read property value; do
        case "$property" in
        Active)
            if [[ "$value" != "yes" ]]; then continue; fi
            ;;
        Display)
            if [[ "$value" ]]; then
                TARGET_DISPLAY+=( "$value" )
            fi
            # else the session isn't graphical
            ;;
        esac
    done < <(loginctl show-session -p Display -p Active "$id")
done < <(loginctl list-sessions --no-legend)

Here the $TARGET_DISPLAY is an array, because hypothetically there may be more than one active session (on a multi-seat system). If this is not possible in your configuration, you may make it a simple variable and add a break 2 statement after the assignment to exit both loops.

HOWEVER...

...this does not anymore work with GDM, because in this case the X server is started inside the session (with user privileges) and /usr/bin/gdm-x-session does not communicate this information back to logind (due to lack of an API for that).

In other cases (when the X server is started by the DM before the session is created) this will most probably work. I'm not aware of any other methods, sorry.

intelfx
  • 5,121
  • 26
  • 39
  • Hi @intelfx, here it didn't work: `loginctl list-sessions` returns **c1, c2 and c3** as SESSIONS. So `loginctl show-session -p Display c1` or `c2` or even `c3` returns always **Display=** – Geyslan Gregório May 21 '15 at 19:22
  • @geyslan: Sorry, I've misremembered information which is returned by `loginctl list-sessions` -- there is no "ACTIVE" column... And, actually, situation turns out to be worse than I thought: starting with 3.16, GDM stopped passing the X11 display number to `logind` due to a structural rework. Though this may work with other compliant DMs. – intelfx May 21 '15 at 19:48
  • No problem @intelfx. Thank you for your help anyway. – Geyslan Gregório May 22 '15 at 21:11
  • @intelfx: very nice approach. In 1-2 major releases I may come to like this *d stuff. Just to throw in another idea: I once had to match XDisplays to usernames. I build a little something that looks at all incoming TCP connections to the various Xservers and does a majority vote on the users the connecting processes belong to. Works fairly well with modern DEs. – Bananguin May 25 '15 at 19:17
5

Well, as current I mean active Display of a XOrg Server. Remember that we can have more than one XOrg Server running and they can be multi-head, what make the things harder to manipulate.

The other important note is that the DISPLAY variable in some cases cannot be accessed (eg. systemd).

After some discussion in commandlinefu, I got this result:

for p in $(pgrep -t $(cat /sys/class/tty/tty0/active));
do 
    d=$(awk -v RS='\0' -F= '$1=="DISPLAY" {print $2}' /proc/$p/environ 2>/dev/null); 
    [[ -n $d ]] && break; 
done;
echo $d

This snippet iterates over a list of all processes (actually their IDs) where the controlling terminal is set and matches the terminal of this process. It checks each process' environment for the DISPLAY variable and is satisfied as soon as it finds one. Having found a DISPLAY variable it stops iterating the list and echos the result.

That will print the active display which run the script.

Geyslan Gregório
  • 267
  • 1
  • 2
  • 9
  • This is a very greedy approach and needn't return the correct `DISPLAY`. You should evaluate _all_ environments and assume the value of `DISPLAY` which you read most frequently. – Bananguin May 25 '15 at 19:10
  • @bananguin Given some tty could more than one x server be running in it? – Geyslan Gregório May 26 '15 at 19:57
  • you are not looking at Xservers, only at environmental variables. We always need to be very careful about what we assume about environmental variables. – Bananguin May 26 '15 at 22:22
  • 1
    I see your caution. Do you have some code to suggest (bash)? – Geyslan Gregório May 27 '15 at 23:07
  • The question is ill-defined. I can easily have a dozen of "active" X-connections (some local, some remote), and I can have all of them in the same shell, on the same controlling tty. I can change `DISPLAY` at will, several times. You can look for `DISPLAY` in the environment of processes you are interested in (you didn't say which ones), but that's all you can do. – dirkt Oct 17 '16 at 14:55
3

Here's an example with two servers running on the same host. I used startx to launch both of them, as you can see from the output of pstree -pl below. The first child process with $DISPLAY set is .xinitrc:

|-login(698)---zsh(1077)---startx(1359)---xinit(1381)-+-.xinitrc(1386)...

This is the command; if you have a different setup, you could change .xinitrc to something else here:

$ pgrep .xinitrc | while read i; do \
    d=$(cat /proc/$i/environ | tr '\0' '\n' | grep DISPLAY); \
    echo -n $d" "; eval $d xprintidle; \
  done | sort -n -k 2
DISPLAY=:1 93
DISPLAY=:0 603301

The numbers show the idle time of each display, in milliseconds. The top line shows the most recently active (least idle) display.

For what it's worth, none of the other answers worked for me.

Metamorphic
  • 1,109
  • 1
  • 10
  • 26
2

This is my solution based on other answers:

#!/bin/bash

DISPLAY=""

# Guess the active DISPLAY
while read session; do
    # Explode to needed variables
    set -- $session; tty=$2; display=$3
    # If there is an X session in thet TTY
    if [ "$display" != "-" ]; then
        # 1st non root display is used if TTY is not matched with the active
        [ "$DISPLAY" == "" ] && DISPLAY="$display"
        # If it is the active TTY we can stop, this is the active X!
        [ "$tty" == "$(cat /sys/class/tty/tty0/active)" ] && DISPLAY="$display";
    fi
done <<< "$(w -hs)"

echo "$DISPLAY"

This checks all login sessions which have X sessions as well. If the tty is also match we can be sure it is the active X session, else we use the 1st one.

This script can be extended to match current user as well if you want.

wyrm
  • 543
  • 2
  • 8
Adam Wallner
  • 121
  • 2
1

Next script lists user name and display number for logged in users when the corresponding terminal is active.

w -hs | \
awk -v tty="$(cat /sys/class/tty/tty0/active)" \
    '$2 == tty && $3 != "-" {print $1 FS $3}'

Result:

myuser :0