Как правильно обрабатывать задачи, не управляемые событиями, с помощью Xlib?

Я пишу графическую программу для Linux на C, используя Xlib. Проблема проста. У меня есть событие, происходящее вне цикла событий Xlib, которое необходимо обработать и которое вызовет некоторые изменения в том, что отображается на экране. В моем случае я пытаюсь заставить курсор мигать внутри текстового поля. Мой цикл событий выглядит так:

XEvent event;
while(window.loop_running == 1) {
    XNextEvent(window.dis, &event);
    event_handler(&window, &event); // This is a custom function to handle events
}

Решение 1а.

Моей первой мыслью было создать второй цикл с помощью pthreads. Этот второй цикл может обрабатывать асинхронное событие и рисовать на экране по мере необходимости. Однако Xlib плохо работает с несколькими потоками. Даже если я использовал мьютекс для блокировки функции event_handler(), во время асинхронной отрисовки во 2-м цикле у меня все равно периодически возникали сбои из X. Кроме того, если цикл событий не зациклен, примерно после 10 вызовов из цикла pthread, программа зависает.

Решение 1б.

Это можно решить, вызвав XInitThreads() в начале моей программы. Однако это приводит к тому, что valgrind сообщает об утечке памяти. Кажется, что есть некоторая выделенная память, которая не освобождается при выходе. И вызовы XCloseDisplay() просто зависают, если я вызываю XInitThreads(). Я до сих пор не понял, как уничтожать и очищать окна в моей программе, но это лучше оставить для отдельного вопроса. Кроме того, вызов XInitThreads() в начале моей программы предотвращает зависание программы после 10 вызовов из цикла pthread без зацикливания цикла событий. Однако вызовы X начинают блокироваться примерно после 10 вызовов из цикла pthread. Вещи ненадолго возобновляются после циклического цикла событий, например, при наведении указателя мыши на окно. Однако вызовы быстро снова начинают блокироваться, когда активность событий в цикле прекращается. Интересно, что я заметил, что могу воспроизвести эту проблему в некоторых других программах, таких как Bluefish. Если я открою Bluefish, запущу курсор в основное текстовое поле, а затем уберу мышь, примерно через 10 секунд курсор перестанет мигать. Очевидно, что это не всегда проблема, поскольку такие вещи, как дисплей видеоплеера, зависают после некоторого периода отсутствия запуска событий X.

Решение 1c:

Я могу остановить зависание окна, используя XSendEvent() для циклического цикла событий после завершения рисования из цикла pthread. Тем не менее, это кажется действительно Hacky. И я не могу гарантировать, что это сработает, поскольку я точно не знаю, в какой момент X перестанет слушать. Я не смог определить основную причину этой проблемы. Как я уже сказал, кажется, что это происходит примерно через 10 секунд, но это зависит от того, как я изменяю скорость цикла мигающего курсора. У меня возникает соблазн предположить, что это функция реальных вызовов X. Приблизительно 2 на пиксель при перерисовке. Он должен 1) установить цвет переднего плана и 2) нарисовать пиксель из растрового буфера на экран. В настоящее время мое окно поддерживает только разрешение 640x480. Конечно, я просто предполагаю, что это можно использовать для определения точки отказа, поскольку я действительно не знаю причины.

Решение 2.

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

у кого-нибудь есть решение? Это такая простая и фундаментальная проблема, но я видел только примеры приложений, которые используют XNextEvent в цикле обработки событий. Я не нашел примеров того, как обрабатывать события вне цикла событий. Спасибо за помощь. Я новый участник Stack Overflow. Это мой первый пост. Так что извиняюсь, если ошибся.


person Echelon X-Ray    schedule 13.04.2020    source источник
comment
Вы должны использовать poll() с fd, полученным с помощью ConnectionNumber(), и fds, на которые приходят ваши другие события. Когда X11 fd готов, вы обрабатываете события с помощью while(XPending()){ XNextEvent(); ... }. Даже в этом случае функции X11, имеющие форму запроса/ответа (например, XQueryTree), могут остановить ваш цикл обработки событий. Решение состоит в том, чтобы переключиться на xcb (где вы можете разделить их на части запроса/ответа). ИМХО, xcb такой же уродливый и не намного лучше, чем Xlib, но это единственное, что доступно.   -  person mosvy    schedule 14.04.2020


Ответы (1)


Пользователь: мосви

Написал этот комментарий:

Вы должны использовать poll() с fd, полученным с помощью ConnectionNumber(), и fds, на которые приходят ваши другие события. Когда X11 fd "готов", вы обрабатываете события с помощью while(XPending()){ XNextEvent(); ... }. Даже в этом случае функции X11, имеющие форму запроса/ответа (например, XQueryTree), могут остановить ваш цикл обработки событий. Решение состоит в том, чтобы переключиться на xcb (где вы можете разделить их на части запроса/ответа). ИМХО, xcb такой же уродливый и не намного лучше, чем Xlib, но это единственное, что доступно.

Это отлично работает! Спасибо, что указали мне правильное направление. Я бы хотел, чтобы вы написали это в качестве ответа. Я бы принял это для тебя.

РЕДАКТИРОВАТЬ: Удалено мое предыдущее редактирование, потому что позже я обнаружил, что проблема была ошибкой в ​​другом месте моей полной программы. На самом деле редактирование было неправильным, и код неправильно читался в событиях.

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

// window is a custom struct defined and setup elsewhere in my program
// event_handler() is a custom function elsewhere in my program
window.fd = XConnectionNumber(window.dis);
struct pollfd fds;
fds.fd = window.fd;
fds.events = POLLIN;
int poll_ret;
XEvent event;
while(window.loop_running == 1) {
    poll_ret = poll(&fds, 1, 10);
    if (poll_ret < 0) {
        window.loop_running = 0;
    } else if(poll_ret > 0) {
        while (XPending(window.dis) > 0) {
            XNextEvent(window.dis, &event);
            event_handler(&window, &event);
        }
    }
}

Я могу использовать poll() для прослушивания всех событий, используя файловые дескрипторы ядра, включая события X. Дескриптор файла для событий X получается с помощью ConnectionNumber(). Мне также нужно было настроить его на прослушивание событий файлового дескриптора типа «POLLIN». Poll принимает массив файловых дескрипторов. Дополнительную информацию о схеме см. в документации. Когда poll() возвращается, я могу проверить, истекло ли время ожидания или было ли событие закодировано. Затем я могу действовать соответственно.

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

Это решение означает, что мне не нужно вызывать XInitThreads(), так как я никогда не делаю одновременных вызовов X.

person Echelon X-Ray    schedule 14.04.2020