Получение информации об экране с помощью xcb и randr

Я пытался написать простую графику с помощью 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

Может ли кто-нибудь заметить, что я делаю неправильно?


person user5713492    schedule 06.03.2017    source источник


Ответы (1)


Хорошо, после дня перерыва в 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 или что-то в этом роде, и вы не получаете Структуры данных частоты обновления настроены правильно.

Затем, когда я зашел так далеко, я обнаружил что-то ужасное в файле 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 и верны для моего телевизора. Однако они также даны в миллиметрах в дескрипторе 1 и дескрипторе 2 в байтах EDID 66:68 и байтах 84:86, и эти числа соответствуют 84 в TV! Конечно, Samsung должен решить эту проблему, отправив мне телевизор, который соответствует их данным EDID :)

Есть еще одна проблема: в EDID указано, что предпочтительным видеорежимом является 3840x2150 при 60 Гц, хотя и на компьютере, и на телевизоре есть только HDMI 1.4, поэтому указанная тактовая частота пикселей невероятно высока. Я думаю, именно поэтому Windows 8.1 с трудом загружается с этим телевизором и почему Linux и OSX пропускают собственный режим и загружаются с разрешением 4096x2160 при 24 Гц, поэтому, когда оболочка Unity не активна, 128 пикселей обрезаются с обеих сторон. . Однако Windows 10 не имеет проблем с телевизором.

Я надеюсь, что ответ на мой собственный вопрос был правильным; Я никогда не пробовал это раньше на этом форуме.

person user5713492    schedule 08.03.2017