List connected displays with details and ASCII layout diagram.
Like lsusb, lspci, lscpu, lsblk, lsmem — but for displays.
A useful CLI tool for Linux users and admins. Zero-dependency — just Python 3.7+ and /sys/class/drm. (Config-path handling follows the XDG spec more closely when the optional pyxdg module — Debian python3-xdg — is installed.)
Companion tool: lsgpu — list the GPUs that drive those displays (with NVIDIA/AMD stats).
I built lsdisplay and its companion lsgpu in parallel, both for the same reason: setting up my sysadmin workstation — six monitors driven by three GPUs (two NVIDIA cards plus the Arrow Lake iGPU), one of them a 65" overview TV — and finding that no single Linux command could tell me which physical screen was driven by which output, on which card, doing what.
Try answering this concretely: which GPU is driving the 32" Samsung sitting in the bottom-right corner of my desk right now, and what is that card actually doing?
lsdisplay answers the screen-and-cable side. It identifies each physical panel and shows where it sits:
CONNECTED DISPLAYS
==================
HDMI-A-2 1440x2560+1441+0 27" 75Hz Iiyama PL2792Q HDMI S/N:1152032422031 rot=left [PRIMARY]
HDMI-A-3 1440x2560+2881+0 27" 75Hz Iiyama PL2792Q HDMI S/N:1152032422030 rot=left
HDMI-A-5 5376x3024+0+2561 65" 60Hz Samsung TQ65QN800DTXXC HDMI S/N:94:e6:ba:dd:9a:7a
DP-4 1440x2560+0+0 27" 75Hz Iiyama PL2792Q DisplayPort S/N:1152031921274 rot=left
HDMI-A-4 1440x2560+4322+0 27" 75Hz Iiyama PL2793Q HDMI S/N:12464540C1808 rot=left
HDMI-A-1 1920x1080+5376+2561 32" 60Hz Samsung QE32Q50A HDMI S/N:bc:45:5b:e4:e8:13
Total: 6 displays connected
LAYOUT
======
+-------------+-------------+------------+-------------+
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| DP-4 | HDMI-A-2* | HDMI-A-3 | HDMI-A-4 |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
+-------------+-------------+------------+----------+-----------------+
| | |
| | HDMI-A-1 |
| | |
| | |
| +-----------------+
| |
| HDMI-A-5 |
| |
| |
| |
| |
| |
| |
| |
+---------------------------------------------------+
→ the 32" Samsung in the bottom-right corner is HDMI-A-1.
lsgpu answers the silicon side. It identifies each card and shows what it is currently running:
GRAPHICS CARDS
==============
card0: NVIDIA Corporation GA107 [GeForce RTX 3050 6GB] (rev a1)
Driver: nvidia | GPU:26% MEM:4422/6144MB 48°C 27.3W
├─ DP-4: connected ← Iiyama PL2792Q 27"
├─ HDMI-A-2: connected ← Iiyama PL2792Q 27"
├─ HDMI-A-3: connected ← Iiyama PL2792Q 27"
card1: NVIDIA Corporation AD106 [GeForce RTX 4060 Ti] (rev a1)
Driver: nvidia | GPU:0% MEM:12794/16380MB 48°C 15.1W
├─ HDMI-A-1: connected ← Samsung QE32Q50A 32"
Processes:
PID 9728 ollama 12744MB
card2: Intel Corporation Arrow Lake-S [Intel Graphics] (rev 06)
Driver: i915
├─ HDMI-A-4: connected ← Iiyama PL2793Q 27"
├─ HDMI-A-5: connected ← Samsung TQ65QN800DTXXC 65"
Total: 3 GPUs, 6 outputs connected
→ HDMI-A-1 lives on card1, the RTX 4060 Ti, currently pinned by Ollama with 12.7 GB of VRAM. The three Iiyama 27" on the top row are all driven by card0 (RTX 3050 6 GB); the fourth 27" and the 65" overview TV come straight out of the Intel Arrow Lake iGPU (card2).
Together they tell the whole story: the screen in front of me, the cable behind it, the card driving it, and what that card is doing right now. That's the workflow lsdisplay exists for — and zero-dependency Python is what makes it work on the locked-down sysadmin boxes where I actually need it.
- EDID parsing from
/sys/class/drm/*/edid: manufacturer, model, serial number - Resolution, position, rotation via xrandr (fallback: kscreen-doctor, wlr-randr)
- ASCII art layout diagram with correct proportions
- JSON output for scripting
- Works on X11 and Wayland (KDE, Sway, etc.)
- No external dependencies, Python 3.7+
Download the .deb from the Releases page, then:
sudo dpkg -i lsdisplay_0.2.1-1_all.debThe package installs /usr/bin/lsdisplay, the man page lsdisplay(1), and documentation.
Available in the AUR thanks to @seraf1:
yay -S lsdisplay-gitPackage page: https://aur.archlinux.org/packages/lsdisplay-git
git clone https://github.com/AGuyMarc/lsdisplay
cd lsdisplay
# System-wide
sudo cp lsdisplay.py /usr/local/bin/lsdisplay
sudo chmod +x /usr/local/bin/lsdisplay
# Or user-local
cp lsdisplay.py ~/.local/bin/lsdisplay
chmod +x ~/.local/bin/lsdisplaylsdisplay # Full output with layout diagram
lsdisplay --short # Compact one-line-per-display
lsdisplay --json # JSON output for scripting
lsdisplay --no-layout # Skip the ASCII art diagram
lsdisplay --connected-only # Hide disconnected outputs
lsdisplay --list-priority # Display priority order with GPU mapping
lsdisplay --no-color # Disable coloured output
lsdisplay --version # Show versionlsdisplay --scan # Auto-detect the local subnet
lsdisplay --scan 192.168.1.0/24 # Scan a specific subnetlsdisplay --override-list # List current overrides
lsdisplay --override-add # Interactive wizard for a detected display
lsdisplay --override-set SAM7513 --override-model QN65QN900B --override-diagonal 65 --override-note Salon
lsdisplay --override-remove SAM7513lsdisplay --write-config # Save overrides to your user config
lsdisplay --write-config=system # Install overrides machine-wide (needs sudo)
lsdisplay --restore-config=system # Copy machine config into your user configlsdisplay --json | jq '.[].manufacturer'Display overrides live in overrides.json, searched in XDG order (first match wins per key):
| Scope | Path |
|---|---|
| User | $XDG_CONFIG_HOME/lsdisplay/ (default ~/.config/lsdisplay/) |
| System | $XDG_CONFIG_DIRS entries, then /etc/xdg/lsdisplay/ — canonical system path |
| Legacy | /etc/lsdisplay/ — deprecated, still read as a last-resort fallback |
Full XDG semantics need the optional pyxdg module; without it the fallback paths
above are used. If /etc/lsdisplay/overrides.json is found, a one-line deprecation
note is printed to stderr (stdout and --json stay clean). lsdisplay never moves
or writes it for you.
lsdisplay --write-config # save current overrides to your user config
lsdisplay --write-config=system # install them machine-wide (needs sudo)
sudo lsdisplay --write-config=system
lsdisplay --restore-config=system # pull the machine config into your user config
lsdisplay --restore-config=~/backup.json # import an overrides.json (e.g. a *.bak)--write-config pushes your active overrides to a scope; --restore-config
pulls a config into your user scope. Both back up any existing target to
*.bak first. --write-config=system replaces the old silent /etc auto-copy
with an explicit, visible action.
Compact one-line-per-display view (lsdisplay --short) of a six-screen
workstation — * marks the primary. The full table with serials plus the ASCII
layout diagram is shown in Why this exists above.
HDMI-A-1 1920x1080@60Hz 32" Samsung QE32Q50A [HDMI]
* HDMI-A-2 1440x2560@75Hz 27" Iiyama PL2792Q [HDMI]
HDMI-A-3 1440x2560@75Hz 27" Iiyama PL2792Q [HDMI]
HDMI-A-5 5376x3024@60Hz 65" Samsung TQ65QN800DTXXC [HDMI]
DP-4 1440x2560@75Hz 27" Iiyama PL2792Q [DisplayPort]
HDMI-A-4 1440x2560@75Hz 27" Iiyama PL2793Q [HDMI]
Required:
- Python 3.7+
- Linux with
/sys/class/drm(any modern kernel)
Optional (lsdisplay still lists every display with EDID details without them):
xrandr(X11),kscreen-doctor(KDE Wayland) orwlr-randr(wlroots Wayland) — for live resolution, position and rotationpyxdg(Debianpython3-xdg) — full$XDG_CONFIG_HOME/$XDG_CONFIG_DIRSconfig-path semantics
- Scans
/sys/class/drm/card*/edidfor raw EDID data - Parses EDID to extract PNP manufacturer ID, monitor name, serial number
- Maps PNP IDs to human-readable names (Samsung, Dell, Iiyama, etc.)
- Uses
xrandroutput for resolution, position, rotation - Draws ASCII art layout proportional to actual pixel dimensions
Hardware enumeration ls* family on Linux:
lsgpu— GPUs (NVIDIA/AMD/Intel) with stats and output mapping (companion to this tool)lscpu— CPU architecture infolspci— PCI deviceslsusb— USB deviceslsblk— block devices (disks, partitions)lsmem— memory rangeslsmod— kernel moduleslsipc— IPC facilitieslsns— namespaces
GPL-2.0. See LICENSE for the full text.