2

I would like to enumerate the X Server display connection points, e.g. DisplayPort-1-7 or HDMI-A-1-1, while simultaneously identifying the physical monitor (if any) attached to each.

Motivation: I'm using Xubuntu 18.04 (Bionic), and xfce does a poor job auto-configuring my displays when docking/undocking. I'm on a Dell laptop, and the GPU changes the identifiers of the display connections when docked, undocked, and re-docked, so I can't just use a fixed identifier to set up the monitors, which makes the scripts created by arandr fail.

My objective is to write a simple Bash/Python script that can look at the connected monitors and run xrandr to configure the displays the way I want them to be set up.

Potential tools:

  • xrandr gives me a list of display connection points, which are specific to X Server, but doesn't give me monitor IDs.
  • hwinfo --monitor can list the monitors and decode the EDID data to determine their model names and serial IDs, but doesn't know the X Windows names.

I've yet to find a way to determine which physical monitor is connected to which X Server port.

Edit to clarify about a comment below: My goal is to identify the monitor from its name, model number, or serial number. Then I can use the monitor's identity to set its resolution and position using xrandr in a script.

  • Do you want something to list the serial number, or do you want something to pop up on the screen and say "this is HDMI-0 on DISPLAY:3"? Is this more a "how do I make my displays have constant names" question? – icarus Nov 5 '19 at 23:32
  • Does this question help? There may be other stuff in the /sys/.../HDMI-A-1-1/ etc. directories that may already be enough to identify the monitor if you don't need the full EDID, but right now I can't look. – dirkt Nov 6 '19 at 10:43
  • @dirkt Actually that looks like the solution. This answer, specifically, includes a description of navigating the /sys filesystem tree to find each card's display connection points, plus the current monitor's EDID data to go with it. That's the solution I was looking for. Thanks! – Ethan T Nov 6 '19 at 16:07
  • 1
    @dirkt Correcting my previous comment. The answer I linked is unfortunately not a solution, as the names used in /sys do not match the names used by x11 to describe the connection points. On my system, /sys has a path named DP-4, whereas the X Server name for the port is DisplayPort-1-3. So I can't use these names to configure X using xrandr. The search continues. – Ethan T Nov 6 '19 at 16:28
2

Just the good part: Here's my Python script for doing this, which requires python-xlib be installed.

I have found one source, so far, to get the list of X Server display connections and simultaneously get the monitors connected to each, and that is X itself. There are multiple ways to get this information from X, depending on your tolerance to parsing vs. getting the data directly from X.

Method 1: Parsing xrandr --verbose

The xrandr --verbose command has all the information needed to match display connections with monitors. Its output for a single display connection looks like this:

Screen 0: minimum 320 x 200, current 6400 x 1600, maximum 8192 x 8192
DP-1 disconnected (normal left inverted right x axis y axis)
    [extra content removed]
DisplayPort-1-7 connected 1920x1080+0+464 (0x4a) normal (normal left inverted right x axis y axis) 510mm x 287mm
    Identifier: 0x224
    Timestamp:  6668772
    Subpixel:   unknown
    Gamma:      1.0:1.0:1.0
    Brightness: 1.0
    Clones:    
    CRTC:       4
    CRTCs:      0 4 5 6 7
    Transform:  1.000000 0.000000 0.000000
                0.000000 1.000000 0.000000
                0.000000 0.000000 1.000000
               filter: 
    EDID: 
        00ffffffffffff0010ac73404c424241
        ------[more hex redacted]-------
        001155223811000a202020202020007e
    GAMMA_LUT_SIZE: 4096 
        range: (0, -1)
    DEGAMMA_LUT_SIZE: 4096 
        range: (0, -1)
    GAMMA_LUT: 0 
        range: (0, 65535)
    CTM: 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 
        0 1 
    DEGAMMA_LUT: 0 
        range: (0, 65535)
    TearFree: auto 
        supported: off, on, auto
    vrr_capable: 0 
        range: (0, 1)
    max bpc: 8 
        range: (8, 16)
    underscan vborder: 0 
        range: (0, 128)
    underscan hborder: 0 
        range: (0, 128)
    underscan: off 
        supported: off, on, auto
    scaling mode: None 
        supported: None, Full, Center, Full aspect
    link-status: Good 
        supported: Good, Bad
    CONNECTOR_ID: 76 
        supported: 76
    non-desktop: 0 
        range: (0, 1)
  1920x1080 (0x4a) 148.500MHz +HSync +VSync *current +preferred
        h: width  1920 start 2008 end 2052 total 2200 skew    0 clock  67.50KHz
        v: height 1080 start 1084 end 1089 total 1125           clock  60.00Hz
  1680x1050 (0x9b) 146.250MHz -HSync +VSync
        h: width  1680 start 1784 end 1960 total 2240 skew    0 clock  65.29KHz
        v: height 1050 start 1053 end 1059 total 1089           clock  59.95Hz
[extra content removed]

Parsing this output isn't great: you'll need to identify the lines that represent a display connection vs. the screen, with variations based on whether it's connected/disconnected, etc. But if you do, you can extract the EDID, convert it to raw bytes, and pass it to a tool like parse-edid.

Method 2: Xlib client and EDID parser

There is a Python client for Xlib that has the tools needed to fetch the display connections and the relevant EDID bytes from each connected monitor. Similarly, there is a Python library for parsing EDID data, though it is not quite ideal (explained below). However, I've made an example script that puts the two of these items together to yield the answer I want:

#!/usr/bin/env python3

import pyedid.edid, pyedid.helpers.registry
import Xlib.display

def get_x_displays():
    # Used by PyEDID to fetch manufacturers given their EDID code
    registry = pyedid.helpers.registry.Registry.from_web()

    # Xlib resources
    d = Xlib.display.Display()
    root = d.screen().root
    resources = root.xrandr_get_screen_resources()._data

    outputs = {}
    for output in resources['outputs']:
        output_info = d.xrandr_get_output_info(output, resources['config_timestamp'])._data
        output_name = output_info['name']
        props = d.xrandr_list_output_properties(output)
        edid_data = None

        # Look through the atoms (properties) of each output to see if there's one named 'EDID'
        for atom in props._data['atoms']:
            atom_name = d.get_atom_name(atom)
            if atom_name == 'EDID':
                edid_raw = d.xrandr_get_output_property(output, atom, 0, 0, 1000)._data['value']
                edid_data = pyedid.edid.Edid(bytes(edid_raw)[:128], registry)
                break

        outputs[output_name] = edid_data

    return outputs

if __name__ == '__main__':
    displays = get_x_displays()
    for connection, monitor in sorted(displays.items(), key=lambda kv: kv[0]):
        print(connection)
        print('    ' + ("No connection or empty EDID" if monitor is None else
                        "{} ({})".format(monitor.name, monitor.serial)))

On my machine, this produces the following output (serial numbers mangled):

DP-1
    No connection or empty EDID
DP-2
    No connection or empty EDID
DP-3
    No connection or empty EDID
DisplayPort-1-3
    No connection or empty EDID
DisplayPort-1-4
    No connection or empty EDID
DisplayPort-1-5
    No connection or empty EDID
DisplayPort-1-6
    DELL UP3017 (DDSB553SBDFL)
DisplayPort-1-7
    DELL U2312HM (V092DMLS657D)
DisplayPort-1-8
    No connection or empty EDID
HDMI-1
    No connection or empty EDID
HDMI-A-1-1
    No connection or empty EDID
eDP-1
    None (0)
eDP-1-1
    No connection or empty EDID

The PyEDID library requires a "registry" of manufacturers so that it can populate the manufacturer field of the EDID. The from_web() call to create the registry from an online source is the slowest part of this script. I have created an alternative version of the script that eliminates that requirement by skipping the manufacturer lookup altogether, instead passing out the raw value.

0

Have you tried`

    $ xrandr --listactivemonitors
    Monitors: 2
    0: +*DVI-0 1920/521x1080/293+0+0  DVI-0
    1:  +DVI-1 1920/521x1080/293+0+0  DVI-1

It breaks it down into a table format like this:

[Screen/Display] number: [Connector + Mode] [Connector]

  • Unfortunately that doesn't identify the monitors themselves. The closest thing I've found so far is xrandr --verbose, which includes the EDID data of each connected monitor. The EDID data can then be parsed to read out the model number and serial number (if supported) from the display. – Ethan T Nov 6 '19 at 16:03

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.