5

I was trying to find all monitors and their coordinates (width w, height h, x origin/top-left-most x, and y origin/top-left-most y) and was using this code, it works good on some systems. But on other systems I get false and duplicate entries. Would I be able to avoid these dupilcate/false monitor entries if I tested if monitor is mirror? How to test if its mirror?

So this is my code:

// start - get all monitor resolutions
var screen = XRRGetScreenResources(getXOpenDisplay(), getDefaultRootWindow(getXOpenDisplay()));

var noutputs = screen.noutput;

for (var i=noutputs-1; i>=0; i--) {
    var info = XRRGetOutputInfo(getXOpenDisplay(), screen, screen.outputs[i]);
    if (info.connection == RR_Connected) {
        var ncrtcs = info.ncrtc;
        for (var j=ncrtcs-1; j>=0; j--) {
            var crtc_info = XRRGetCrtcInfo(getXOpenDisplay(), screen, infoCrtcs[j]);
            console.info('screen #' + i + ' mon#' + j + ' details:', crtc_info.x, crtc_info.y, crtc_info.width, crtc_info.height);

            collMonInfos.push({
                x: crtc_info.x,
                y: crtc_info.y,
                w: crtc_info.width,
                h: crtc_info.height
            });

            XRRFreeCrtcInfo(crtc_info);
        }
    }
    XRRFreeOutputInfo(info);
}
XRRFreeScreenResources(screen);
console.info('JSON:', JSON.stringify(collMonInfos));
// end - get all monitor resolutions

And this outputs this to log:

"screen #4 mon#0 details:" 0 0 0 0
"screen #3 mon#1 details:" 0 0 1920 1200
"screen #3 mon#0 details:" 1920 469 1366 768
"screen #2 mon#1 details:" 0 0 1920 1200
"screen #2 mon#0 details:" 1920 469 1366 768
"screen #1 mon#1 details:" 0 0 1920 1200
"screen #1 mon#0 details:" 1920 469 1366 768
"screen #0 mon#1 details:" 0 0 1920 1200
"screen #0 mon#0 details:" 1920 469 1366 768

This is it in JSON format:

[{
    "x": 0,
    "y": 0,
    "w": 0,
    "h": 0
}, {
    "x": 0,
    "y": 0,
    "w": 1920,
    "h": 1200
}, {
    "x": 1920,
    "y": 469,
    "w": 1366,
    "h": 768
}, {
    "x": 0,
    "y": 0,
    "w": 1920,
    "h": 1200
}, {
    "x": 1920,
    "y": 469,
    "w": 1366,
    "h": 768
}, {
    "x": 0,
    "y": 0,
    "w": 1920,
    "h": 1200
}, {
    "x": 1920,
    "y": 469,
    "w": 1366,
    "h": 768
}, {
    "x": 0,
    "y": 0,
    "w": 1920,
    "h": 1200
}, {
    "x": 1920,
    "y": 469,
    "w": 1366,
    "h": 768
}]

I really only have 2 monitors, the 1920x1200 one and the 1366x768 one. How come all the other entries and how to test to avoid (rather then filter out in retrospect based on duplicates or 0 h/w)?

yatg
  • 1,121
  • 1
  • 9
  • 15
  • 2
    Why you tagged it 'c'? – nsilent22 Jul 20 '15 at 18:32
  • May be virtual desktops? – fassl Jul 20 '15 at 18:46
  • Thanks @fassl I'm thinking the similar and am wondering how can make diffrentiate it out, other then at the very end going through them all and removing duplicates or anything with w/h of 0. – yatg Jul 20 '15 at 20:29
  • @nsilent22 this is ctypes but the ctypes people from python or js will not be able to help out, its more of a question for the xrandrd/x11 people so i could go without the c tag, but due it being a copy of c code just syntax tweaked i went with c. – yatg Jul 20 '15 at 20:31
  • 1
    @yatg: What says "normal" xrandr command? – nsilent22 Jul 20 '15 at 20:33
  • @nsilent22 thanks much im not sure can you please tell me how to do this (what to type in terminal), i just converted code from one langauge to another, im not too familiar with xrandr – yatg Jul 20 '15 at 20:37
  • 1
    Just install xrandr (in x11-xserver-utils package on Ubuntu), and type `xrandr`. – nsilent22 Jul 20 '15 at 20:49
  • Thanks very much @nsilent22 this is what it gives me: http://i.imgur.com/9tT20HE.png – yatg Jul 20 '15 at 20:57
  • 1
    Where `screenOutputs` come from? I don't see its initialization in the code. – nsilent22 Jul 20 '15 at 21:21
  • Oh thanks @nsilent22 that is actually a typo i was changing it from ctypes to easier to read/fromatted code, that is actually `screen.outputs[i]` – yatg Jul 20 '15 at 22:02
  • 1
    Not sure about that, but another reason might be an X server being configured to send (cloned) video to all possible outputs by default—whether anything is connected or not. I've seen systems configured this way to ensure whichever output is connected, the user will get video. – liori Jul 20 '15 at 22:11
  • Thanks @liori that's an interesting note! – yatg Jul 20 '15 at 22:19

2 Answers2

4

You are unnecessarily iterating over each output and then over each monitor. So you receive a duplicate entries. You do not have to call XRRGetOutputInfo for each output, since all data you need (number of monitors) can be found in structure returned by XRRGetScreenResources. Here is the C code that works (at least for me):

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>

int main(void) {
    Display *d = XOpenDisplay(getenv("DISPLAY"));
    Window   w = DefaultRootWindow(d);
    XRRScreenResources *xrrr = XRRGetScreenResources(d, w);
    XRRCrtcInfo *xrrci;
    int i;
    int ncrtc = xrrr->ncrtc;
    for (i = 0; i < ncrtc; ++i) {
        xrrci = XRRGetCrtcInfo(d, xrrr, xrrr->crtcs[i]);
        printf("%dx%d+%d+%d\n", xrrci->width, xrrci->height, xrrci->x, xrrci->y);
        XRRFreeCrtcInfo(xrrci);
    }
    XRRFreeScreenResources(xrrr);
    return 0;
}
nsilent22
  • 2,763
  • 10
  • 14
  • Thank you very much I didn't test yet but it makes sense, this way will also have that width 0 and height 0 monitor no? – yatg Jul 20 '15 at 22:19
  • This way does not give you those width 0 and height 0 monitors. Awesome solution! – Noitidart Sep 22 '15 at 06:04
3

The accepted answer did not work for me. The correct way I found of doing this was this:

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>

int main(void) {
    Display *display = XOpenDisplay(NULL);

    if (NULL == display) {
        perror("No DISPLAY in environment!");
        exit(EXIT_FAILURE);
    }

    Window window = DefaultRootWindow(display);
    XRRScreenResources *screenr = XRRGetScreenResources(display, window);

    // This is the key right here. Use XRRScreenResources::noutput
    int output = screenr->noutput;

    for (int i = 0; i < output; ++i) {
        XRROutputInfo* out_info = XRRGetOutputInfo(display, screenr, screenr->outputs[i]);

        if (NULL != out_info && out_info->connection == RR_Connected) {
            XRRCrtcInfo* crt_info = XRRGetCrtcInfo(display, screenr, out_info->crtc);
            printf("%s\t%dx%d+%d+%d\n", out_info->name, 
                                        crt_info->width, 
                                        crt_info->height,
                                        crt_info->x,
                                        crt_info->y);
            XRRFreeCrtcInfo(crt_info);
        }

        XRRFreeOutputInfo(out_info);
    }

    XRRFreeScreenResources(screenr);
    XCloseDisplay(display);

    return 0;
}

As I said in the code comment, the trick is using XRRScreenResources::noutput instead of XRRScreenResources::ncrtc

smac89
  • 39,374
  • 15
  • 132
  • 179
  • This is a valid approach and the results are consistent with the output from xrandr tool. However, note that `out_info->crtc` may be null (0) in some cases (e.g. when the laptop lid is closed) even if `out_info->connection == RR_Connected`. Robust solution must handle this case or an X server error is generated. – marski Jun 13 '21 at 21:01