使用xcb和randr获取屏幕信息

时间:2017-03-06 07:46:38

标签: opengl xcb xrandr

我试图用Xlib,XF86VidMode和OpenGL编写一些简单的图形。我有两个问题:

  1. Xlib似乎没有WM_TIMER的等价物,所以我编写了一个SIGALRM处理程序,它发送消息来解锁消息循环,但由于这种用法完全是线程不安全的,程序会在一段时间后挂起。因此我尝试在xcb中重新编码。
  2. XF86VidMode使用起来很尴尬,我不喜欢结果,所以我切换到了RandR。
  3. 完成上述操作后,结果发现xcb具有相同的挂起,因此我无法对阻塞消息循环进行编码。相反,我偶尔会轮询一次,程序没有挂起,但是跳过的框架很烦人。

    虽然我可以使用RandR切换视频模式,但我想使用xcb版本,但是没有用。我为xcb抄袭了@datenwolf的一个例子,但不知怎的,xcb_randr_get_screen_info_reply没有用。它应该返回一个指向结构的指针,该结构后面跟着一个屏幕尺寸数组(以mm为单位的尺寸是错误的),然后是刷新率数据。刷新率数据是垃圾,大多数为零。我做错了什么?

    /*
    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;
    }
    

    所以我的输出是

    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
    

    有人能发现我做错了吗?

1 个答案:

答案 0 :(得分:0)

好的,在离开Linux一天后,再过一天撞到这个特殊的砖墙,我回答了大部分问题。 xcb_randr_get_screen_info_reply没有完全发挥作用的原因是在你调用xcb_randr_get_screen_info_unchecked之前,你必须首先调用xcb_randr_query_version,同时将major_version和minor_version设置为至少1或者xcb认为你的版本低于1.1或者其他什么而且你没有&#39 ; t正确设置刷新率数据结构。

然后,当我走到那么远的时候,我在我的装置中发现了一些可怕的东西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  /**< */);

看到了吗?成员rem减少了一个,而不是数据 - > nRates,因此通过调用此函数使rem无效。这只是xcb的第一个规范中存在的错误,现在被冻结了,或者有什么理由吗?因此,即使第一个问题得到解决,依赖于重新计数到零也不会起作用。

我甚至发现为什么RandR为我的屏幕获取了错误的物理尺寸:物理尺寸在EDID字节21:22中以厘米为单位,并且对于我的电视来说是正确的。然而,它们在描述符1中以毫米为单位,在EDID字节66:68和字节84:86中以描述符2给出,这些数字对应于电视中的84!当然,三星应该通过向我发送符合其EDID数据的电视来解决这个问题:)

另一个问题是,EDID表示首选视频模式为3840x2150 @ 60 Hz,即使计算机和电视都只有HDMI 1.4,因此指定的像素时钟速率非常快。我想这就是为什么Windows 8.1很难用这台电视启动的原因以及为什么Linux和OSX都跳过本机模式并启动到4096x2160 @ 24 Hz,所以当Unity shell没有活动128像素时从任何一方切断。但是,Windows 10对电视没有任何问题。

我希望回答我自己的问题是正确的做法;我之前从未尝试过这个论坛。