3

I was trying to write some simple graphics with Xlib, XF86VidMode, and OpenGL. I had two issues:

  1. Xlib doesn't seem to have the equivalent of WM_TIMER, so I wrote a SIGALRM handler that sent messages to unblock the message loop, but since such usage is totally thread-unsafe, the program would hang after a little while. Thus I tried recoding in xcb.
  2. XF86VidMode was awkward to use and I didn't like the results so I switched to RandR.

Having done the above, it turned out that xcb had the same hang, so I couldn't code a blocking message loop. Instead I polled every once in a while and the program didn't hang, but there were skipped frames that were annoying.

While I could switch the video mode with RandR, I wanted to use the xcb version, which didn't work. I plagiarized an example of @datenwolf for the xcb, but somehow xcb_randr_get_screen_info_reply didn't work. It's supposed to return a pointer to a structure that's followed by an array of screen dimensions (the dimensions in mm are wrong) and then the refresh rate data. The refresh rate data is garbage and mostly zeros. What am I doing wrong?

/*
gcc rrxcb.c -lxcb-randr -lxcb -lX11 -lX11-xcb -lGL -orrxcb
*/

#include <xcb/randr.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xlib-xcb.h>
#include <xcb/xcb.h>
#include <GL/glx.h>
#include <unistd.h>

void screen_from_Xlib_Display(
    Display * const display,
    xcb_connection_t *connection,
    int * const out_screen_num,
    xcb_screen_t ** const out_screen)
{
    xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
    int screen_num = DefaultScreen(display);
    while( screen_iter.rem && screen_num > 0 ) {
        xcb_screen_next(&screen_iter);
        --screen_num;
    }
    *out_screen_num = screen_num;
    *out_screen = screen_iter.data;
}

int main()
{
   Display *display;
   xcb_connection_t *connection;
   xcb_window_t win;
   const int GLX_TRUE = True;
   int attrib_list[] = {GLX_X_RENDERABLE,GLX_TRUE,
      GLX_DRAWABLE_TYPE,GLX_WINDOW_BIT,
      GLX_RENDER_TYPE,GLX_RGBA_BIT,
      GLX_CONFIG_CAVEAT,GLX_NONE,
      GLX_DOUBLEBUFFER,GLX_TRUE,
      GLX_BUFFER_SIZE,32,
      GLX_DEPTH_SIZE,24,
      GLX_STENCIL_SIZE,8,
      0};
   GLXFBConfig *FBConfigs;
   int nelements;
   GLXFBConfig fb_config;
   XVisualInfo *visual;
   int visualID;
   GLXContext context;
   xcb_colormap_t colormap;
   xcb_void_cookie_t create_color;
   xcb_void_cookie_t create_win;
   const uint32_t eventmask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS;
   const uint32_t valuemask = XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
   uint32_t valuelist[] = {eventmask,colormap};
   xcb_randr_get_screen_info_cookie_t screen_info;
   xcb_randr_get_screen_info_reply_t *reply;
   int screen_num;
   xcb_screen_t *screen;
   xcb_generic_error_t *error;
   xcb_randr_screen_size_t *sizes;
   int sizes_length;
   xcb_randr_refresh_rates_iterator_t rates_iter;
   uint16_t *rates;
   int rates_length;
   int i;

   /* Open Xlib Display */
   display = XOpenDisplay(NULL);
   printf("display = %p\n",display);
   connection = XGetXCBConnection(display);
   printf("connection = %p\n",connection);
   XSetEventQueueOwner(display,XCBOwnsEventQueue);
   win = xcb_generate_id(connection);
   printf("win = %d\n",win);
   screen_from_Xlib_Display(display,connection,&screen_num,&screen);
   printf("screen_num = %d\n",screen_num);
   printf("screen->root = %d\n",screen->root);
   FBConfigs = glXChooseFBConfig(display,screen_num,attrib_list,
      &nelements);
   printf("FBConfig = %p\n",FBConfigs);
   printf("nelements = %d\n",nelements);
   fb_config = FBConfigs[0];
   visual = glXGetVisualFromFBConfig(display,fb_config);
   printf("visual = %p\n",visual);
   visualID = visual->visualid;
   printf("visualID = %d\n",visualID);
   context = glXCreateNewContext(display,fb_config,GLX_RGBA_TYPE,
      0,True);
   printf("context = %p\n",context);
   colormap = xcb_generate_id(connection);
   printf("colormap = %d\n",colormap);
   create_color = xcb_create_colormap_checked(connection,
     XCB_COLORMAP_ALLOC_NONE,colormap,screen->root,visualID);
   printf("create_color.sequence = %d\n",create_color.sequence);
   error = xcb_request_check(connection,create_color);
   printf("error = %p\n",error);
   create_win = xcb_create_window_checked(connection,
      XCB_COPY_FROM_PARENT,win, screen->root,0,0,640,480,2,
      XCB_WINDOW_CLASS_INPUT_OUTPUT,visualID,valuemask,valuelist);
   printf("create_win.sequence = %d\n",create_win.sequence);
   error = xcb_request_check(connection,create_win);
   printf("error = %p\n",error);
   screen_info = xcb_randr_get_screen_info_unchecked(connection, screen->root);
   printf("screen_info.sequence = %d\n",screen_info.sequence);
   reply = xcb_randr_get_screen_info_reply(connection,screen_info,
      NULL);
   printf("reply = %p\n",reply);
   printf("reply->response_type = %d\n",reply->response_type);
   printf("reply->rotations = %d\n",reply->rotations);
   printf("reply->sequence = %d\n",reply->sequence);
   printf("reply->length = %d\n",reply->length);
   printf("reply->nSizes = %d\n",reply->nSizes);
   printf("reply->sizeID = %d\n",reply->sizeID);
   printf("reply->rotation = %d\n",reply->rotation);
   printf("reply->rate = %d\n",reply->rate);
   printf("reply->nInfo = %d\n",reply->nInfo);
   printf("reply+1 = %p\n",reply+1);
   sizes = xcb_randr_get_screen_info_sizes(reply);
   printf("sizes = %p\n",sizes);
   sizes_length = xcb_randr_get_screen_info_sizes_length(reply);
   printf("sizes_length = %d\n",sizes_length);
   rates_iter = xcb_randr_get_screen_info_rates_iterator(reply);
   printf("rates_iter.data = %p\n",rates_iter.data);
   printf("rates_iter.rem = %d\n",rates_iter.rem);
   printf("rates_iter.index = %d\n",rates_iter.index);
   for( ; rates_iter.rem; xcb_randr_refresh_rates_next(&rates_iter))
   {
      rates = xcb_randr_refresh_rates_rates(rates_iter.data);
      printf("rates = %p\n",rates);
      rates_length =
         xcb_randr_refresh_rates_rates_length(rates_iter.data);
      printf("rates_length = %d\n",rates_length);
      printf("rates[0] = %d\n",rates[0]);
/*
      for(i = 0; i < rates_length; i++)
      {
         printf("%d%c",rates[i],(i==rates_length-1)?'\n':' ');
      }
*/
   }
   for(i = 0; i < sizes_length; i++)
   {
      printf("%d %d %d %d %d\n",i,sizes[i].width,sizes[i].height,sizes[i].mwidth,sizes[i].mheight);
   }
   return 0;
}

So my output is

display = 0x563687942010
connection = 0x563687943410
win = 54525954
screen_num = 0
screen->root = 241
FBConfig = 0x563687951b20
nelements = 8
visual = 0x563687951d30
visualID = 33
context = 0x563687951680
colormap = 54525956
create_color.sequence = 26
error = (nil)
create_win.sequence = 28
error = (nil)
screen_info.sequence = 31
reply = 0x563687abde30
reply->response_type = 1
reply->rotations = 63
reply->sequence = 31
reply->length = 36
reply->nSizes = 18
reply->sizeID = 1
reply->rotation = 1
reply->rate = 30
reply->nInfo = 53
reply+1 = 0x563687abde50
sizes = 0x563687abde50
sizes_length = 18
rates_iter.data = 0x563687abdee0
rates_iter.rem = 35
rates_iter.index = 176
rates = 0x563687abdee2
rates_length = 0
rates[0] = 0
rates = 0x563687abdee4
rates_length = 0
rates[0] = 0
rates = 0x563687abdee6
rates_length = 0
...
rates = 0x563687add7a8
rates_length = 0
rates[0] = 0
0 4096 2160 1872 1053
1 3840 2160 1872 1053
2 1920 1080 1872 1053
3 1680 1050 1872 1053
4 1600 900 1872 1053
5 1280 1024 1872 1053
6 1440 900 1872 1053
7 1366 768 1872 1053
8 1280 800 1872 1053
9 1152 864 1872 1053
10 1280 720 1872 1053
11 1024 768 1872 1053
12 832 624 1872 1053
13 800 600 1872 1053
14 720 576 1872 1053
15 720 480 1872 1053
16 640 480 1872 1053
17 720 400 1872 1053

Can anyone spot what I am doing wrong?

user5713492
  • 954
  • 5
  • 11

1 Answers1

3

OK, after a day off from Linux and then a day banging my head against this particular brick wall, I have answered most of my question. The reason xcb_randr_get_screen_info_reply didn't fully work was that before you call xcb_randr_get_screen_info_unchecked, you have to first invoke xcb_randr_query_version with both major_version and minor_version set to at least 1 or xcb thinks you have a lower version than 1.1 or something and you don't get the refresh rates data structures set up correctly.

Then when I got that far, I found something horrible in my installation's xcb/randr.h:

/**
 * Get the next element of the iterator
 * @param i Pointer to a xcb_randr_refresh_rates_iterator_t
 *
 * Get the next element in the iterator. The member rem is
 * decreased by one. The member data points to the next
 * element. The member index is increased by     sizeof(xcb_randr_refresh_rates_t)
 */
void
xcb_randr_refresh_rates_next (xcb_randr_refresh_rates_iterator_t *i  /**< */);

See that? Member rem gets decreased by one, not by data->nRates, so rem is invalidated by invocation of this function. Is that just a bug that was there in the first specification of xcb and is now frozen in, or is there some reason for this? Thus even when the first problem is fixed, depending on rem to count down to zero was not going to work.

I even discovered why RandR gets the wrong physical dimensions for my screen: the physical dimensions are available in cm in EDID bytes 21:22, and are correct for my TV. However they are also given in mm in descriptor 1 and descriptor 2 in EDID bytes 66:68 and bytes 84:86 and those numbers correspond to an 84 in TV! Of course Samsung should fix this problem by sending me a TV that matches their EDID data :)

There is another issue in that the EDID says the preferred video mode is 3840x2150 @ 60 Hz, even though both by computer and TV only have HDMI 1.4, so the pixel clock rate specified is impossibly fast. I guess that's why Windows 8.1 has a hard time booting up with this TV and why Linux and OSX both skip the native mode and boot to 4096x2160 @ 24 Hz, so when the Unity shell isn't active 128 pixels get chopped off of either side. Windows 10 has no problems with the TV, however.

I hope that answering my own question was the right thing to do; I've never tried that before in this forum.

user5713492
  • 954
  • 5
  • 11