Как правильно обрабатывать не событийные задачи в Xlib? - PullRequest
0 голосов
/ 13 апреля 2020

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

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

Решение 1a:

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

Решение 1b:

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

Решение 1 c:

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

Решение 2:

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

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

1 Ответ

0 голосов
/ 14 апреля 2020

Пользователь: mosvy

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

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

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

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

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

// 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 () возвращается, я могу проверить, истекло ли оно или событие было вызвано. Затем я могу действовать соответствующим образом.

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

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

...