Как получить и синхронизировать полный список всех окон X11?
Я хочу отслеживать все открытые окна под X11. В настоящее время я делаю это следующим образом:
- Изначально проецирование всего дерева путем рекурсивного вызова XQueryTree из корневого окна
- Прослушивание изменений подструктуры на всем рабочем столе:
XSelectInput( display, root_window, SubstructureNotifyMask | PropertyChangeMask )
- Обработка всех событий MapNotify, UnmapNotify и DestroyNotify, обновление моего собственного списка окон в процессе
В основном я беспокоюсь о точке 1. Во время рекурсии XQueryTree будет вызываться несколько раз. Есть ли способ гарантировать, что дерево не изменится тем временем? Другими словами, чтобы получить "снимок" всего дерева в один момент времени?
Кроме того, я заметил, что в некоторых системах X11 не все события поступают правильно. Например, при открытии нового окна на рабочем столе MapNotify для этого окна может никогда не появиться в моем приложении мониторинга. Как это может быть? Возможно ли, что он выброшен до прибытия?
Update:
Я написал небольшую программу, которая будет отслеживать события X в корневом окне (см. ниже). Теперь, когда я запускаю эту программу и запускаю и завершаю xcalc, я получаю следующий вывод:
Reparented: 0x4a0005b to 0x1001e40
Mapped : 0x1001e40
Destroyed : 0x1001e40
Что это. Я никогда не уведомляю об уничтожении реального окна (0x4a0005b). Даже при этом не отображается! Может ли кто-нибудь сказать мне, почему бы и нет? Подструктура SubStructureNotifyMask вызывает только сообщения подкадров direct вместо всего поддерева?
Кстати, это, похоже, не происходит, когда работает Compiz. Затем никакого повторного убора не производится:
Mapped : 0x4a0005b
Mapped : 0x4e00233
Destroyed : 0x4a0005b
Destroyed : 0x4e00233
Источник программы мониторинга:
#include <X11/Xlib.h>
#include <cstdio>
int main()
{
Display *display;
Window rootwin;
display = XOpenDisplay( NULL );
rootwin = DefaultRootWindow( display );
XSelectInput( display, rootwin, SubstructureNotifyMask );
XEvent event;
while ( 1 ) {
XNextEvent( display, &event );
if ( event.type == MapNotify ) {
XMapEvent *mapevent = (XMapEvent *)&event;
printf( "Mapped : 0x%x\n", (unsigned int)(mapevent->window) );
}
if ( event.type == DestroyNotify ) {
XDestroyWindowEvent *destroywindowevent = (XDestroyWindowEvent *)&event;
printf( "Destroyed : 0x%x\n", (unsigned int)(destroywindowevent->window) );
}
if ( event.type == ReparentNotify ) {
XReparentEvent *reparentevent = (XReparentEvent *)&event;
printf( "Reparented: 0x%x to 0x%x\n", (unsigned int)(reparentevent->window), (unsigned int)(reparentevent->parent) );
}
}
return 0;
}
Ответы
Ответ 1
Посмотрите xwininfo
.
Вам также могут понравиться xprop
и xspy
для получения дополнительной информации.
Обновление: Да. Попробуйте использовать xwininfo
и -root
с помощью -tree
или -children
, чтобы задействовать все окна.
И изменения можно отслеживать с помощью xprop -spy
.
Ответ 2
Я считаю, что захват X-сервера (XGrabServer (3)) предотвратит изменения в иерархии окон. Это немного тяжело, хотя, возможно, вы должны делать это только в том случае, если вам это действительно нужно.
Для примера кода, который просматривает иерархию окон, создает дерево, использует события окна, чтобы поддерживать его в актуальном состоянии, и игнорирует ошибки протокола X, которые неизбежны из-за рас, см. файл src/VBox/Additions/x11/VBoxClient/seamless-x11.cpp в исходном коде для VirtualBox.
Ответ 3
X11 - это удаленный протокол. Это означает, что когда вы запрашиваете X-сервер для получения какой-либо информации, вы всегда получаете свою собственную копию. Ваша копия никогда не изменяется, когда X-сервер обновляет свои внутренние структуры данных.
Это означает, что дерево не будет внезапно изменяться, пока вы проходите его, но когда вы используете информацию в нем (например, осмотр окна), эта информация может быть устаревшей (кто-то мог закрыть окно). Вот почему вам необходимо выполнить правильную обработку ошибок.