Почему это важно, когда вы рисуете в XWindows? - PullRequest
3 голосов
/ 05 июня 2019

Я получил этот XWindows "привет, мир" из сети. У меня есть поведение, которое я не понимаю в более сложной программе, но простая программа здесь также отображает его:

#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
   Display *d;
   Window w;
   XEvent e;
   const char *msg = "Hello, World!";
   int s;
   int x;

   d = XOpenDisplay(NULL);
   if (d == NULL) {
      fprintf(stderr, "Cannot open display\n");
      exit(1);
   }

   s = DefaultScreen(d);
   w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
                           BlackPixel(d, s), WhitePixel(d, s));
   XSelectInput(d, w, ExposureMask | KeyPressMask);
   XMapWindow(d, w);

   XDrawString(d, w, DefaultGC(d, s), 10, 50, msg, strlen(msg));

   //XFlush(d);

   while (1) {
      XNextEvent(d, &e);
      if (e.type == Expose) {
//         XDrawString(d, w, DefaultGC(d, s), 10, 50, msg, strlen(msg));
      }
      if (e.type == KeyPress) break;
   }

   XCloseDisplay(d);
   return 0;
}

Таким образом, первый вызов XDrawString () на самом деле не записывает в окно, а раскомментирует тот, что в событии expose. Я нахожу это поведение запутанным. Во время первого XDrawString () дисплей, экран и окно все настроены. Тем не менее, он не рисует, а тот, который появляется в обработчике событий. Я также пробовал XFlush () в очереди, это не имеет значения.

Этот эффект имел отношение к (гораздо) более сложному коду, над которым я работаю, а именно к размещению символов на экране. Я помещаю их в нужное место в растровом изображении, которое поддерживает экран, и затем делаю то же самое рисование на реальном экране, чтобы синхронизировать его. Обработчик экспозиции скопирует растровое изображение на экран, который обновляет его, но событие выставления не произойдет до позднего времени, и, кроме того, копирование всего растрового изображения обратно на экран обходится дороже, чем размещение одного символа.

Я подозреваю, что мне нужно проследить отрисовку в буфер с помощью операции, помечающей прямоугольник под символом как недопустимый (это было бы способом MS Windows) и позволяющего обработчику событий позаботиться об этом, но я хотел бы понять, что продолжается внутри X11, чтобы это произошло.

Ответы [ 4 ]

5 голосов
/ 05 июня 2019
Серверы

X не обязаны самостоятельно обеспечивать резервное хранилище, поэтому события Expose отправляются, когда необходимо нарисовать окно.Это та же функциональность, что и в других средах.Вначале вы можете рисовать, но X-сервер, возможно, еще не настроил окно должным образом и позже сообщит вам, когда будет готов к выводу содержимого на экран.Рисование происходит непосредственно на экране, а не в резервном буфере, поэтому его следует выполнять каждый раз, когда окно выводится вперед.

Что касается запроса на перерисовку самостоятельно, вы можете использовать XSendEvent, чтобы самостоятельно отправить событие Expose, а затем X передаст его в ваш цикл обработки событий.Так что это практически точно так же, как Windows.

2 голосов
/ 05 июня 2019

Помните, что вы находитесь в среде клиент / сервер.Означает, что есть задержки между вашими запросами кода и их реализацией.Возврат из вызова на XMapWindow не означает, что ваше окно действительно готово принимать чертежи только потому, что XServer должен это сделать, поэтому вам нужно , чтобы дождаться первого события Expose для рисования, что означаетчто XServer сделал эту работу.Это обязательно, потому что Expose - это событие, представляющее тот факт, что окно (или его часть) отображается на экране.

Всегда помните: пользовательские интерфейсы управляются событиями.

1 голос
/ 05 июня 2019

Хорошо, я подумал об этом вчера вечером и понял, что старая классическая программа, называемая «линии», иллюстрирует проблему, о которой я говорю:

#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int randr(int upper)

{

   return rand() % (upper+1);

}   

int main(void) {

    Display *d;
    Window w;
    XEvent e;
    const char *msg = "Hello, World!";
    int s;
    XEvent se;

    d = XOpenDisplay(NULL);
    if (d == NULL) {
       fprintf(stderr, "Cannot open display\n");
       exit(1);
    }

    s = DefaultScreen(d);
    w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 400, 400, 1,
                            BlackPixel(d, s), WhitePixel(d, s));
    XSelectInput(d, w, ExposureMask | KeyPressMask);
    XMapWindow(d, w);

    se.type = Expose;
    se.xexpose.type = Expose;
    se.xexpose.serial = 0;
    se.xexpose.send_event = 1;
    se.xexpose.window = w;
    se.xexpose.x = 0;
    se.xexpose.y = 0;
    se.xexpose.width = 400;
    se.xexpose.height = 400;
    se.xexpose.count = 0;

    while (1) {
       XNextEvent(d, &e);
       if (e.type == Expose) {
          XSetForeground(d, DefaultGC(d, s), randr(255)<<16 | randr(255)<<8 | randr(255));
          XDrawLine(d, w, DefaultGC(d, s), randr(400), randr(400), randr(400), randr(400));
          XSendEvent(d, w, 0, ExposureMask, &se);
       }
       if (e.type == KeyPress) break;
    }

    XCloseDisplay(d);

    return 0;
}

Lines - старая программа, восходящая к временам Cromemco Dazzler конца 1970-х годов. Он просто рисует случайные линии, он делает это довольно быстро. Он не нуждается во вводе, он просто рисует и рисует, а также может служить тестом производительности.

Как вы видите здесь, я обманул X11 в непрерывной обработке события expose, разместив еще одно в самом событии expose. В (например) окнах MS это не было бы необходимо. Вы можете рисовать в окне в любое время, и не читать вашу очередь событий не приятно, но программа все еще работает.

Эта программа работает в X11, и (на моей машине) работает так быстро, что практически полностью заполняет окно.

Теперь, если бы я хотел, чтобы эта демонстрация работала как можно быстрее, мне нужно было бы учитывать основные временные потери в программе, которые, насколько я могу судить, заключаются в необходимости постоянно публиковать новые события выставки.

1 голос
/ 05 июня 2019

Просто добавлю немного к хорошему ответу Сами.

Если серьезно задуматься, то, как работает X11, является единственно правильным, когда используются несколько окон, которые могут перекрывать друг друга (не считая многих других вещей). Вот почему этот механизм (сделать недействительным и выставить | draw | paint) используется многими фреймворками (X11, windows, gtk, qt ...).

Все дело в оптимизации чертежа. Вы не должны рисовать вслепую, потому что ваш рисунок может быть отброшен; возможно, ваше окно невидимо, возможно, пользователь перемещает окно, не перерисовывая его содержимое, или что-то подобное.

Вместо этого X11 (или другой инструментарий / инфраструктура) очень хорошо знает, когда необходимо отображать данные, и может отображать . В этой ситуации X11 говорит вам перерисовать (через событие expose). Когда X11 попросит перекрасить, ваши операции рисования не будут отменены.

С другой стороны, хорошая программа X11 должна быть готова к перекрашиванию при необходимости: это часто случается, если пользователь перетаскивает окна или изменяет виртуальные рабочие столы, обозначает приложение и т. Д.

Объедините два аспекта: программа должна нарисовать, когда ее попросят, и она обязательно будет задана сразу после создания ее первого окна. Так что лучше всего рисовать только в выставленном событии.

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

...