18

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?

1
  • I still wonder why Xlib's ScreenCount returns the "invalid" actual screen count. Commented Aug 11, 2023 at 20:16

5 Answers 5

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
Sign up to request clarification or add additional context in comments.

3 Comments

Any ideas how to get the connected output names from xilb/xrandr?
@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
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?
6

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())

5 Comments

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?
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 Did you find something elsewhere in the meantime?
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, or here, or here... a nightmare, to be honest.
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. Search for getAllScreens() function.
2

Your code is getting information about the X11 screen. You are looking for real+virtual (X11) monitors. So try this instead:

import Xlib.display

# grab default X11 display (from $DISPLAY environment var)    
display = Xlib.display.Display()

# get first and almost always the only X11 screen of this X11 display
screen = display.screen()

# the root window of this screen provides the info we need
root = screen.root

# iterate over the X11 monitors; often equal real monitors
# will include virtual X11 monitors
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
    • X11 screen concept, separate from the X11 display, did not prove very useful
  • thus today’s X11 screen has to encompass all monitors
    • usually a big rectangle enclosing them all
  • an X11 monitor is usually a real monitor
    • apps should consider these monitors to be real and nothing else
    • sometimes you really want to influence what apps think of as monitors
      • this is why you can create virtual X11 monitors
      • ...and can remove real ones from getting listed here
        • usually for those real monitors which form part of a virtual one
      • examples:
        • huge hardware monitors composed of border-less monitor tiles
        • two border-less monitors on a desktop where the user really wants:
          • only one “desktop” spanning both
          • all other apps should take it like one super wide monitor
          • possibly for testing code on very wide or tall monitors with extreme aspect ratios
  • 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

Comments

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.

1 Comment

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.
-1

To get a display count in windows you can use

import win32api
print(len(win32api.EnumDisplayMonitors()))

3 Comments

Not really an answer to the question on linux, but handy for bad google searching.
@Martlark indeed! That's why I wrote this 'answer'. For those who arrive here looking for a windows solution.
I arrived here from Review Posts and recommended deletion for not answering the question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.