2016-12-26 17 views
1

Я пишу приложение в Linux с помощью Xlib для управления окном и Каира нарисовать какой-нибудь текст в нем. Текстовое содержимое окна изменяется во время выполнения, поэтому я хочу адаптировать размер окна, чтобы он соответствовал размеру текста. Если размер текста не изменяется, окно всегда корректно обновляется новым текстом.После XResizeWindow, новое содержимое окна обращается не отображается

Но когда размер текста изменяется, и поэтому окно изменяется соответственно, окно очищается, но новый текст никогда не отображается. Только если нет вызова XResizeWindow, текст фактически отображается. Код я использую

if (/* Text extent is changed */) 
{ 
    XResizeWindow (display, window, new_width, new_height); 
    cairo_xlib_surface_set_size (surface, new_width, new_height); 
} 

XClearWindow (display, window); 

/* ... Cairo code to draw the text ... */ 

// cairo_surface_flush (surface); 
// XFlush (display); 

Я также попытался добавить после кода Cairo, который черпает методы cairo_surface_flush и XFlush (прокомментировал в пример), но ничего не изменяет текст.

EDIT: Я решил проблему с помощью две темы: первый поток с обычной петлей для прослушивания разоблачительных событий плюс код для перерисовки содержимого и второго потока, который выдает изменения размера окна и отправляет событие Expose для пробуждения первого потока.

В этом примере окно изменяется каждые 500 мс до случайной ширины и высоты, и при каждом изменении размера отображается прогрессивный счетчик. Я использую C++ 11, компилировать с:

g++ -std=c++11 -o test test.cpp -lX11 -lcairo -lpthread 

Код:

#include <random> 
#include <chrono> 
#include <thread> 
#include <string> 
#include <X11/Xlib.h> 
#include <cairo/cairo-xlib.h> 

Display * d; 
Window w; 
cairo_surface_t * surface; 
int width = 300, height = 300; 
unsigned char counter = 0; 
std::random_device rd; 
std::knuth_b gen (rd()); 
std::uniform_int_distribution < > dist (150, 300); 

void logic() 
{ 
    XEvent send_event; 
    send_event.type = Expose; 
    send_event.xexpose.window = w; 

    while (true) 
    { 
     std::this_thread::sleep_for (std::chrono::milliseconds (500)); 
     ++ counter; 
     width = dist (gen); 
     height = dist (gen); 
     cairo_xlib_surface_set_size (surface, width, height); 
     XResizeWindow (d, w, width, height); 
     XSendEvent (d, w, False, ExposureMask, & send_event); 
     XFlush (d); 
    } 
} 

int main () 
{ 
    XInitThreads(); 

    d = XOpenDisplay (NULL); 
    w = XCreateSimpleWindow (d, RootWindow (d, 0), 0, 0, width, height, 0, 0, 0x000000); 
    XMapWindow (d, w); 
    XSelectInput (d, w, ExposureMask | KeyPressMask); 

    surface = cairo_xlib_surface_create (d, w, DefaultVisual (d, 0), width, height); 
    cairo_t * cairo = cairo_create (surface); 
    cairo_select_font_face (cairo, "FreeSans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); 
    cairo_set_font_size (cairo, 40); 
    cairo_set_source_rgb (cairo, 0.8, 0.8, 0.8); 
    cairo_move_to (cairo, 40.0, 60.0); 
    cairo_show_text (cairo, std::to_string (counter).c_str()); 
    XFlush (d); 

    std::thread T (logic); 

    XEvent event; 
    while (true) 
    { 
     XNextEvent (d, & event); 
     if (event.type == Expose) 
     { 
      XClearWindow (d, w); 
      cairo_move_to (cairo, 40.0, 60.0); 
      cairo_show_text (cairo, std::to_string (counter).c_str()); 
     } 
     else if (event.type == KeyPress) 
     { 
      XCloseDisplay (d); 
      return 0; 
     } 
    } 
} 

Но вопрос остается: можно ли получить тот же результат, используя только один нить?

+0

У вас есть самодостаточный пример, который можно было бы проверить? Как выглядит ваш цикл событий, т. Е. Что вы делаете в событиях Expose? –

+1

Хорошо, я дам компактный самодостаточный пример кода, который воспроизводит поведение. – disquisitiones

ответ

1

Вот однопоточная версия вашего кода. Это не приятно, но, похоже, это работает. Жесткая часть ожидает событий с сервера X11 и таймаута одновременно. Я делаю это с select() в следующем коде. Обратите внимание, что я также обрабатываю события ConfigureNotify вместо того, чтобы предполагать, что XResizeWindow всегда делает именно то, что мы хотим.

#include <random> 
#include <chrono> 
#include <thread> 
#include <string> 
#include <X11/Xlib.h> 
#include <cairo/cairo-xlib.h> 
#include <sys/time.h> 

Display * d; 
Window w; 
cairo_surface_t * surface; 
int width = 300, height = 300; 
unsigned char counter = 0; 
std::random_device rd; 
std::knuth_b gen (rd()); 
std::uniform_int_distribution < > dist (150, 300); 

void do_update() 
{ 
     ++ counter; 
     width = dist (gen); 
     height = dist (gen); 
     XResizeWindow (d, w, width, height); 
     // Force a redraw 
     XClearArea(d, w, 0, 0, 0, 0, True); 
} 

int main () 
{ 
    XInitThreads(); 

    d = XOpenDisplay (NULL); 
    w = XCreateSimpleWindow (d, RootWindow (d, 0), 0, 0, width, height, 0, 0, 0x000000); 
    XMapWindow (d, w); 
    XSelectInput (d, w, ExposureMask | KeyPressMask); 

    surface = cairo_xlib_surface_create (d, w, DefaultVisual (d, 0), width, height); 
    cairo_t * cairo = cairo_create (surface); 
    cairo_select_font_face (cairo, "FreeSans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); 
    cairo_set_font_size (cairo, 40); 
    cairo_set_source_rgb (cairo, 0.8, 0.8, 0.8); 
    cairo_move_to (cairo, 40.0, 60.0); 
    cairo_show_text (cairo, std::to_string (counter).c_str()); 
    XFlush (d); 

    struct timeval next_update; 
    struct timeval now; 
    struct timeval interval = { 0, 500000 }; 
    gettimeofday(&now, NULL); 
    timeradd(&now, &interval, &next_update); 

    while (true) 
    { 
     XEvent event; 

     gettimeofday(&now, NULL); 
     if (timercmp(&now, &next_update, >)) { 
      // Store time of next update 
      timeradd(&now, &interval, &next_update); 
      puts("update"); 
      do_update(); 
     } 

     if (!XPending(d)) { 
      struct timeval remaining; 
      fd_set fds; 
      int fd = ConnectionNumber(d); 
      FD_ZERO(&fds); 
      FD_SET(fd, &fds); 
      timersub(&next_update, &now, &remaining); 
      select(fd + 1, &fds, NULL, NULL, &remaining); 
     } else { 
      XNextEvent (d, & event); 
      if (event.type == Expose) 
      { 
       XClearWindow (d, w); 
       cairo_move_to (cairo, 40.0, 60.0); 
       cairo_show_text (cairo, std::to_string (counter).c_str()); 
      } 
      if (event.type == ConfigureNotify) 
      { 
       cairo_xlib_surface_set_size (surface, event.xconfigure.width, event.xconfigure.height); 
      } 
      else if (event.type == KeyPress) 
      { 
       XCloseDisplay (d); 
       return 0; 
      } 
     } 
    } 
} 
+0

Код довольно интересный, спасибо. Пара вопросов: 1) Используется ли функция ** select ** для ожидания и реализации таймера или есть что-то, что связано с ожиданием неблокирующих чтений в дескрипторе файла дисплея? 2) Используется ли функция ClearArea, используемая в update(), только для того, чтобы инициировать отправку события Expose с сервера? – disquisitiones

+0

1) Ну ​​... select() используется для «ждать до истечения времени, но обрабатывать все события с сервера немедленно, если они есть». 2) Yup. 3) Обратите внимание, что я никоим образом не оптимизировал этот код. Он должен вызвать cairo_surface_flush() после рисования. Он должен обрабатывать только события экспонирования, чья запись счетчика равна нулю и т. Д. Вероятно, есть много других проблем. –

+0

Хорошо, получилось ... Но все же остается сомнение в том, что я работаю на X-сервере, и это на самом деле является причиной моего вопроса. Я не понимаю, почему, даже после того, как клиент вызывает XFlush или XSync, сервер не выполняет фактический чертеж в окне.Кажется, что только если клиент читает в обычном цикле события Expose с XNextEvent, сервер будет по-настоящему соблюдать запросы на рисование клиента. Почему это? Проверяется ли сервером, прочитал ли клиент событие Expose, а затем выполнить фактический чертеж? – disquisitiones