使用xcb和randr获取屏幕信息

3
我曾尝试使用Xlib、XF86VidMode和OpenGL编写简单的图形,但遇到了两个问题:
  1. Xlib似乎没有类似于WM_TIMER的东西,所以我编写了一个SIGALRM处理程序来发送消息以解除消息循环的阻塞状态。但是,由于这种用法完全不安全,所以程序会在一段时间后挂起。因此,我尝试了在xcb中重新编码。
  2. 使用XF86VidMode非常麻烦,而且结果不理想,所以我切换到了RandR。
经过以上操作,发现xcb也会出现同样的挂起情况,因此我无法编写阻塞的消息循环。相反,我每隔一段时间进行轮询,程序就不会挂起,但会出现跳帧的情况,令人恼火。
虽然我可以使用RandR切换视频模式,但我想使用xcb版本,但它没有生效。我抄袭了@datenwolf的xcb示例,但某种程度上xcb_randr_get_screen_info_reply没有生效。它应该返回一个指向结构体的指针,该结构体后面跟着一个屏幕维度数组(毫米维度不正确),然后是刷新率数据。刷新率数据是垃圾,大多数是零。我做错了什么?
/*
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个回答

3

好的,在放一天Linux之后,我又花了一天时间撞在这个特定的障碍上,我解决了大部分问题。xcb_randr_get_screen_info_reply没有完全工作的原因是,在调用xcb_randr_get_screen_info_unchecked之前,您必须首先使用major_version和minor_version设置至少为1来调用xcb_randr_query_version,否则xcb认为您的版本低于1.1或其他某些版本,您将无法正确设置刷新率数据结构。

然后当我走到那一步时,我发现我的安装中的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的值会减少一个,而不是data->nRates,因此这个函数的调用使rem无效。这只是xcb最初规范中存在的错误吗?还是有其他原因呢?因此,即使第一个问题得到解决,依靠rem计数到零也不会起作用。
我甚至发现为什么RandR会得到我的屏幕错误的物理尺寸:物理尺寸以厘米为单位在EDID字节21:22中可用,并且对于我的电视是正确的。但是,在EDID字节66:68和字节84:86中的描述符1和描述符2中也给出了以毫米为单位的尺寸,这些数字对应于电视上的84!当然,三星应该通过向我发送与其EDID数据匹配的电视来解决这个问题 :)
此外,EDID中还存在另一个问题,即首选视频模式为3840x2150 @ 60 Hz,即使我的计算机和电视都只有HDMI 1.4,因此指定的像素时钟速率过快。我想这就是Windows 8.1在这台电视上启动困难的原因,以及Linux和OSX都跳过本地模式并引导到4096x2160 @ 24 Hz,因此当Unity shell未激活时,每侧会被裁剪128个像素。然而,Windows 10与电视没有问题。
我希望自己回答问题是正确的做法;我以前从未在这个论坛上尝试过。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接