17

I'm running Ubuntu and I want to get the number of attached monitors, their current resolution and, if possible, their position in relation to each other. Because I don't like parsing Console output of the xrandr command line tool—at least not if I don't have to—I would like to do that with Python-XLib or a similar Pythonic approach.

This is the xrandr output for my display config:

$ xrandr
Screen 0: minimum 320 x 200, current 2960 x 1050, maximum 8192 x 8192
DVI-0 connected 1680x1050+0+0 (normal left inverted right x axis y axis) 473mm x 296mm
   1680x1050      60.0*+
   [some lines cut]
VGA-0 connected 1280x1024+1680+26 (normal left inverted right x axis y axis) 376mm x 301mm
   1280x1024      60.0 +   75.0*
   [some more lines cut]

I want to get these values with Python, in a way like this:

monitors = get_monitors()
print monitors[0].width # out: 1680
print monitors[1].width # out: 1280
print monitors[0].x_position # out: 0
print monitors[1].x_position # out: 1680

When trying to get informations via Python-XLib (or other libs like pyGTK and pygame), it seems that all monitors are always handled as one single display. For example this is what I got with XLib so far:

import Xlib
import Xlib.display

display = Xlib.display.Display(':0')

print display.screen_count()        # output: 1
root = display.screen().root
print root.get_geometry().width     # output: 2960 -> no way to get width of single monitor?
print root.get_geometry().height    # output: 1050

But as I said I would prefer a cleaner approach without having to parse Console output. Is there really no way to get (detailed) Display informations with Python without having to parse xrandr output?

Robert Siemer
  • 32,405
  • 11
  • 84
  • 94
Wolkenarchitekt
  • 20,170
  • 29
  • 111
  • 174

5 Answers5

13

xrandr is just a client to access the "RandR" X11 extension from the command line. You can access the functionality directly from Python-Xlib. Here's an example (from Python-Xlib's own code!).

Just in case the URL changes again, here's a minimal piece of code that gets us the display modes. We need to create window (it doesn't matter the size, etc):

from __future__ import print_function
from Xlib import X, display
from Xlib.ext import randr

d = display.Display()
s = d.screen()
window = s.root.create_window(0, 0, 1, 1, 1, s.root_depth)

Then we can query the screen resources using it. Eg, following OP's example:

res = randr.get_screen_resources(window)
for mode in res.modes:
    w, h = mode.width, mode.height
    print("Width: {}, height: {}".format(w, h))

In my computer I get:

$ python minimal.py 
Xlib.protocol.request.QueryExtension
Width: 1600, height: 900
Width: 1440, height: 900
Width: 1360, height: 768
Width: 1360, height: 768
Width: 1152, height: 864
Width: 1024, height: 768
Width: 800, height: 600
Width: 800, height: 600
Width: 640, height: 480
Mikel
  • 24,855
  • 8
  • 65
  • 66
Ricardo Cárdenes
  • 9,004
  • 1
  • 21
  • 34
  • Any ideas how to get the connected output names from xilb/xrandr? – Ben Davis Sep 22 '14 at 23:20
  • 3
    @BenDavis you could get the possible outputs querying `randr.get_screen_resources(window).outputs` (returns a list of integers), and then get info about the output using `randr.get_output_info(window, ID, timestamp)` (I've used `timestamp=0`). From the "output info" you should be able to figure out if an output is connected – Ricardo Cárdenes Sep 23 '14 at 02:08
  • Answer posted below yours may never be updated, I wonder if you would consider incorporating it along with my comment below his answer into an all-inclusive answer? – WinEunuuchs2Unix Jul 01 '21 at 15:35
5

Latest snippet. It extracts all modes with current resolution from all connected monitors.

from Xlib import display
from Xlib.ext import randr

def find_mode(id, modes):
   for mode in modes:
       if id == mode.id:
           return "{}x{}".format(mode.width, mode.height)

def get_display_info():
    d = display.Display(':0')
    screen_count = d.screen_count()
    default_screen = d.get_default_screen()
    result = []
    screen = 0
    info = d.screen(screen)
    window = info.root

    res = randr.get_screen_resources(window)
    for output in res.outputs:
        params = d.xrandr_get_output_info(output, res.config_timestamp)
        if not params.crtc:
           continue
        crtc = d.xrandr_get_crtc_info(params.crtc, res.config_timestamp)
        modes = set()
        for mode in params.modes:
            modes.add(find_mode(mode, res.modes))
        result.append({
            'name': params.name,
            'resolution': "{}x{}".format(crtc.width, crtc.height),
            'available_resolutions': list(modes)
        })

    return result

print(get_display_info())
Max Ivanov
  • 141
  • 2
  • 3
  • Just having a quick glance at the code and it seems like `mode.width, mode.height` should be prefaced by `mode.x, mode.y` for the monitors' coordinates on extended desktop? – WinEunuuchs2Unix Jul 01 '21 at 15:32
  • HI @Max Ivanov! This was really useful! I'm having troubles with res.config_timestamp in xrandr_get_crtc_info() on LXDE (though surprisingly NOT in xrandr_get_output_info() and NOT in other Desktop environments). Can you please point where to find info about these "xrandr_*" methods so I can investigate? – Kalma Apr 07 '22 at 18:00
  • @Kalma Did you find something elsewhere in the meantime? – Robert Siemer Dec 07 '22 at 14:08
  • Hi! @RobertSiemer. If you mean finding some documentation on those xrandr_* functions, the answer is no, unfortunately. I had to pick from here and there like [here](https://stackoverflow.com/questions/49136692/python-xlib-how-to-deterministically-tell-whether-display-output-is-in-extendi), or [here](https://github.com/python-xlib/python-xlib/blob/master/examples/xrandr.py), or [here](https://tronche.com/gui/x/xlib/display/display-macros.html)... a nightmare, to be honest. – Kalma Dec 07 '22 at 16:17
  • Continuation to previous answer to @RobertSiemer. I discovered you can use those functions by importing Xlib.ext.randr or as (undocumented) methods of the display (Xlib.display.Display()) class. You can see an example of extracting some useful info [here](https://github.com/Kalmat/PyWinCtl/blob/master/src/pywinctl/_pywinctl_linux.py). Search for getAllScreens() function. – Kalma Dec 07 '22 at 16:18
1
import Xlib.display

display = Xlib.display.Display()
root = display.screen().root
for m in root.xrandr_get_monitors().monitors:
  connector = display.get_atom_name(m.name)
  print(f'{connector}, {m.width_in_pixels}x{m.height_in_pixels}, '\
        f'x={m.x}, y={m.y}')

returns for me:

HDMI-0, 1920x1080, x=0, y=1050
DVI-0, 1680x1050, x=1920, y=1050
DVI-1, 1680x1050, x=0, y=0

Note that “display” and “screen” are X11 technical terms which do not correlate with real world usage. “Monitor” is often for the real thing—in X11, though, you can join multiple monitors into virtual monitors with Xrandr...

  • X11 display refers to entire X-server
    • different X11 display on the same host → probably a different user
    • all keyboards and mice a user has (usually one of each) belong to one X11 display
  • one X11 screen per X11 display these days
    • multiple X11 screens per X11 display actually refers to a concept which died out
    • imagine a mouse and keyboard shared by otherwise totally different output devices
    • can not share or move windows between X11 screens
    • could run different window managers per X11 screen
      • or at least has to run totally independent instances of a window manager
    • did not prove very useful
  • today’s X11 screen has to encompass all monitors
    • usually a big rectangle enclosing them all
  • there is a desktop term from the (X11) EWMH spec
    • it is up to the window manager if it considers the X11 screen one desktop
    • ...or if it thinks of each monitor as a desktop
    • an X11 application (“client”) wanting to move its window(s) in between (X11 EWMH) desktops may need to do it differently for different window managers
    • but the apps mostly leave it to the window manager & user to full-screen or organize them over the (X11 EWMH) desktops or monitors
Robert Siemer
  • 32,405
  • 11
  • 84
  • 94
0

You just need to pip3 install Xlib and use this code to get the info:

from Xlib import display
d = display.Display()
screen_count = d.screen_count()
default_screen = d.get_default_screen()
for screen in range(0,screen_count):
    info = d.screen(screen)
    print("Screen: %s. Default: %s" % (screen, screen==default_screen))
    print("Width: %s, height: %s" % (info.width_in_pixels,info.height_in_pixels))

The list of properties you get from there can be found here: http://python-xlib.sourceforge.net/doc/html/python-xlib_16.html#SEC15

Use the python Xlib library instead of calling the command line. They both do the same.

Emilio
  • 2,526
  • 22
  • 26
  • 1
    This seems to be the entire logical screen, i.e. all monitors added together into one screen. I have 3 monitors, but this just prints 1 screen that's 2640x2520. – Mikel Jan 23 '21 at 00:42
-2

To get a display count in windows you can use

import win32api
print(len(win32api.EnumDisplayMonitors()))
delica
  • 1,647
  • 13
  • 17
  • 5
    Not really an answer to the question on linux, but handy for bad google searching. – Martlark Sep 26 '19 at 22:50
  • 2
    @Martlark indeed! That's why I wrote this 'answer'. For those who arrive here looking for a windows solution. – delica Sep 27 '19 at 06:46
  • I arrived here from Review Posts and recommended deletion for not answering the question. – Rob Jan 23 '21 at 00:55