How do I get the resolution of RandR outputs through the xcb RandR extension?
Asked Answered
R

1

8

I'm working on a project that is already using xcb and need to get the resolution of individual outputs rather than the resolution of the combined screen. Can I do this with the RandR extension for xcb? If so, how can I use my xcb_connection_t object to do this.

Ruhnke answered 1/3, 2014 at 1:16 Comment(0)
D
12

I was looking for the answer as well. I don't know if you have succeeded or not, but since you haven't answered this question I guess you have not.

For those who are looking how to do this, you have to understand how XCB generally works. It's especially important for RandR XCB extension. As you probably know to get some information from the X server you need to query it first with a request. In case of XCB after sending a request you are getting a cookie which stores ID of a sent request. Why do you need such cookie? Reason is simple and quite obvious - to get a reply from the X server we need to ask for it with ID of a request we have sent (so the server knows which request you want the answer for).

To make it even clearer here is a simple example from daily life - imagine a bartender with extremely short memory and so it goes like this:

client   : "I'd like to order a beer."
bartender: "OK, I'll get you a beer."
*bartender handles a ticket with an ID of the beer order to the client*
*client waits and waits and then he approaches bartender*
client   : "Dude, give me my beer finally, here's your damn ticket"
*client handles the ticket to bartender, who looks for the order from the 
 ticket in his order book and when he finds it he replies*
bartender: "Here's your beer, enjoy."

OK, so why wouldn't XCB simply send a request and get an answer in one go? Well, that's exactly how Xlib works and it turns out that XCB has been observed to be up to 117 times faster in some conditions. Please read the Basic XCB Notions from XCB tutorial if you want to know more.

Back to the question - how to get the resolution of the outputs (or rather CRTCs)? Here's a minimized version of how I do it:

#include <cstdio>
#include <xcb/xcb.h>
#include <xcb/randr.h>

int main()
{
    //Open connection to X server
    xcb_connection_t* XConnection = xcb_connect(0, 0);

    //Get the first X screen
    xcb_screen_t* XFirstScreen = xcb_setup_roots_iterator(
                               xcb_get_setup(XConnection)).data;
    //Generate ID for the X window
    xcb_window_t XWindowDummy = xcb_generate_id(XConnection);

    //Create dummy X window
    xcb_create_window(XConnection, 0, XWindowDummy, XFirstScreen->root,
                      0, 0, 1, 1, 0, 0, 0, 0, 0);

    //Flush pending requests to the X server
    xcb_flush(XConnection);

    //Send a request for screen resources to the X server
    xcb_randr_get_screen_resources_cookie_t screenResCookie = {};
    screenResCookie = xcb_randr_get_screen_resources(XConnection, 
                                                     XWindowDummy);

    //Receive reply from X server
    xcb_randr_get_screen_resources_reply_t* screenResReply = {};
    screenResReply = xcb_randr_get_screen_resources_reply(XConnection,
                     screenResCookie, 0);

    int crtcs_num = 0;
    xcb_randr_crtc_t* firstCRTC;

    //Get a pointer to the first CRTC and number of CRTCs
    //It is crucial to notice that you are in fact getting
    //an array with firstCRTC being the first element of
    //this array and crtcs_length - length of this array
    if(screenResReply)
    {
        crtcs_num = xcb_randr_get_screen_resources_crtcs_length(screenResReply);

        firstCRTC = xcb_randr_get_screen_resources_crtcs(screenResReply);
    }
    else
        return -1;

    //Array of requests to the X server for CRTC info
    xcb_randr_get_crtc_info_cookie_t* crtcResCookie = new   
               xcb_randr_get_crtc_info_cookie_t[crtcs_num];
    for(int i = 0; i < crtcs_num; i++)
        crtcResCookie[i] = xcb_randr_get_crtc_info(XConnection, 
                                            *(firstCRTC+i), 0);
    //Array of pointers to replies from X server
    xcb_randr_get_crtc_info_reply_t** crtcResReply = new 
               xcb_randr_get_crtc_info_reply_t*[crtcs_num];
    for(int i = 0; i < crtcs_num; i++)
        crtcResReply[i] = xcb_randr_get_crtc_info_reply(XConnection,
                                             crtcResCookie[i], 0);
    //Self-explanatory
    for(int i = 0; i < crtcs_num; i++)
    {
        if(crtcResReply[i])
        {
            printf("CRTC[%i] INFO:\n", i);
            printf("x-off\t: %i\n", crtcResReply[i]->x);
            printf("y-off\t: %i\n", crtcResReply[i]->y);
            printf("width\t: %i\n", crtcResReply[i]->width);
            printf("height\t: %i\n\n", crtcResReply[i]->height);
        }
    }

    xcb_disconnect(XConnection);

    return 0;
}

Example output:

CRTC[0] INFO:
x-off   : 1920
y-off   : 0
width   : 1920
height  : 1200

CRTC[1] INFO:
x-off   : 0
y-off   : 0
width   : 1920
height  : 1200

CRTC[2] INFO:
x-off   : 0
y-off   : 0
width   : 0
height  : 0

CRTC[3] INFO:
x-off   : 0
y-off   : 0
width   : 0
height  : 0

If you want you can improve the readability of the code with vector and whatnot instead of using dynamic allocation with pointers, but I don't think it's that difficult to comprehend.

I'm still new to XCB programming and there PROBABLY is a better way, but this method doesn't seem bad at all. I've spent huge amount of time trying to understand this, so I do hope it will help somebody.

Useful links:

  • XCB Tutorial (recomended to find your way around)
  • XCB API (EXTREMELY useful, in this case especially RandR API)
Displume answered 26/11, 2014 at 4:40 Comment(4)
What a beautiful answerJanettejaneva
Can I assume xrandr to exist in most of modern linux desktops? Or should I also write something similar in xierama to get resolutions if randr is not present?Dominic
There is no need to create a dummy-window. XFirstScreen->root should do the job as well. Anyways ... thank you for this wonderful answer!Carrycarryall
I know you need to free replies. Do you need to free the array of xcb_randr_crtc_t too?Labial

© 2022 - 2024 — McMap. All rights reserved.