X Window – восполняя пробелы. Часть 2 – Xlib

Комментарии: 10

Короткий обзор X Window в предыдущей части статьи, где не были отражены многие нюансы и даже ряд очень специфических понятий X Window, пока свидетельствует лишь о том, что система действительно сложна и функциональна. И чтобы использовать эту функциональность, необходимы удачный программный интерфейс и реализация этого интерфейса. К счастью, они существуют, называются, соответственно, Xlib.h и Xlib и обязательно входят в комплект поставки дистрибутива той или иной реализации X Window. В файле Xlib.h собраны описания основных типов данных и операций над ними, реализованных в рамках облегчающей разработку клиентских приложений библиотеки Xlib. С точки зрения C-программиста, может оказаться весьма важным своевременное замечание о том, что к большинству элементов сложных структур данных X Window нельзя обращаться "напрямую" — для этого есть специальные функции, а многие типы данных определены в файле Xlib.h как "непрозрачные" (opaque, для объектов таких типов известны только их типизованные адреса, а сами реализации скрыты).

Дисплеи и экраны

Теперь совершим короткий экскурс в функциональное богатство Xlib. Начнем, естественно, с базовых понятий или, в терминах объектно-ориентированного проекта, — с объектов. Самый главный в их обширном перечне — "дисплей". Программа-клиент должна вступить в "общение" с каким-нибудь X-сервером — иначе она прекратит выполнение (неспособность "сохранения состояния"). Естественно, что в процессе подключения программа-клиент должна как можно больше узнать о дисплее, с которым ей предстоит работать, — в разношерстном мире систем, на которых работает X Window, любые допущения недопустимы. Инициация процедуры подключения программы-клиента к X-серверу предельно проста — надо только вызвать функцию XOpenDisplay, передав ей в качестве параметра строку-имя X-сервера. XOpenDisplay возвращает указатель на opaque структуру данных, содержащую всю информацию об X-сервере: от версии ПО до диапазона кода клавиш. Для "вытягивания" этой информации в клиентской программе существует целый ряд операций: например, XDefaultDepth возвращает текущее значение количества битов на пиксел для данного дисплея, а XListDepth — перечень возможных значений количества битов, используемых для представления пиксела, поддерживаемый дисплеем. XDefayltGC возвращает предустановленный по умолчанию графический контекст, XDefaultScreenOfDisplay — номер экрана, на котором начнется последующий графический вывод, и т. д. Для оповещения X-сервера об окончании работы программа-клиент должна воспользоваться вызовом XCloseDisplay — с крайне простым синтаксисом, но с очень сложной семантикой. И опять стоит предупредить читателя — тонкие вопросы сложной семантики Xlib в статье рассматриваться не будут и из-за ограниченного объема, и по причине наличия хорошей документации на каждый вызов Xlib в базовой поставке системы. Мы же сконцентрируемся на концептуальных особенностях Xlib, без знания которых изучение системы станет неоправданно сложным.


Окна

Следующая обзорная часть — операции с окнами. Здесь надо четко разделять три концептуальных представления объекта "окно" в X Window. Как уже говорилось в первой части статьи, "окно" является так называемым "ресурсом" (в терминах X Window). Но, кроме того, "окно" — и сущность, обладающая рядом специфических графических атрибутов и характеристик, и отображаемая сущность, характеризующаяся, например, различными свойствами видимости/невидимости в разные моменты времени (если, конечно, это предусмотрел программист). Поэтому и предоставляемые Xlib функции для работы с объектами типа "окно" можно разбить на три условные категории: "уровня главных ресурсов", "уровня атрибутных ресурсов" и "визуальной". Основные особенности функций этих категорий заключаются в наблюдаемости результатов их выполнения — вызовы функций "ресурсных" категорий незаметны на экране до вызова "визуальной" функции.

Первая категория — уровня главных ресурсов — очень компактна. Она включает в себя инициализацию (создание) объекта-ресурса "окно" и его уничтожение. Для создания окна используются два вызова Xlib — XCreateWindow и XCreateSimpleWindow. Оба они возвращают идентификатор ресурса "окно", для обоих должен быть обязательно указан главный параметр — идентификатор "родительского" окна, а отличия между ними минимальны. Впрочем, касаются они весьма специфического нюанса X Window, оставить без внимания который никак нельзя. Дело в том, что объекты типа "окно" могут принадлежать двум классам, существенно различающимся по внешнему представлению. Класс InputOutput — это "обычные" окна, обладающие отображаемым содержимым (точнее, в терминах X Window, это свойство называется способностью окна принимать и обрабатывать графические запросы). Более непривычны представители класса InputOnly — эти окна являются прозрачными невидимыми областями, способными только генерировать события, инициируемые устройствами ввода — позиционирующим (например, мышью) и клавиатурой. "Обычность" окон класса InputOutput и вызвала потребность в создании дополнительного (но необязательного) вызова инициализации ресурса объекта "окно". XCreateSimpleWindow, который всегда создает окно класса InputOutput. Соответственно, функция XCreateWindow более универсальна и позволяет инициировать ресурсы типа "окно" любого класса. Раз объект "окно" можно инициировать (иначе — создать ресурс или кэшировать X-сервером), значит, его можно и уничтожить. Xlib для выполнения такого действия предлагает две группы операций: во-первых, программист может принудительно уничтожить строго определенное окно и все окна, для которого оно является "родителем", во-вторых, программист может… этого не делать вообще — в большинстве случаев ему достаточно сообщить X-серверу о желании завершить работу вызовом XcloseDisplay, остальное — забота самого X-сервера. Имя команды принудительного удаления окна и его "детей" звучит вполне очевидно — XdestroyWindow, и ей, естественно, надо указать идентификатор конкретного ресурса-"окна". Собственно, этим коротким перечнем и ограничивается первая, "ресурсная", категория функций Xlib, отвечающих за работу с окнами.

Вторая категория группирует функции, отвечающие за управление атрибутами окон. Понятие "атрибуты окна" в X Window весьма обширно, оно включает в себя и цвета, и фоновые изображения, и ряд исключительно специфичных для X-технологии моментов, но соблюдение правила изоляции сложности привело к тому, что перечень вызовов Xlib этой группы действительно минимален. В него входят упомянутые функции создания окон (перед их вызовом можно полностью определить атрибуты будущего окна) и один вызов изменения атрибутов уже созданного окна — XChangeWindowAttributes.

Итак, не вдаваясь в детали, после вызова функции создания окна и необязательного пост-изменения его атрибутов X-сервер формирует в собственной памяти объект "окно" определенного класса (InputOutput/InputOnly). На экране же, обслуживаемом X-сервером, визуально ничего не изменилось — ведь были использованы только так называемые "ресурсные" вызовы. Естественно, что эти замечательные возможности совсем не соответствуют предназначению графической подсистемы и пора пускать в ход "визуальные" вызовы Xlib. Их также немного — всего два: XMapWindow и XMapRaised. Слово "map" (помечать, отмечать) в именах этих функций использовано не случайно — вызывая их для определенного идентификатора окна, мы "отмечаем" X-серверу, что хотели бы увидеть изображение на экране. X-сервер проверяет соблюдение ряда условий и, наконец, отображает желаемую "картинку". Разница между двумя вызовами невелика и заключается в положении отображаемого окна "по глубине" экрана. XMapWindow указывает X-серверу, что окно надо "нарисовать" именно на той "глубине", где оно было создано, ведь мы могли построить достаточно сложную иерархию из "окон"-ресурсов задолго до первого вызова "визуальной" функции. XMapRaised, вызванная для идентификатора некоторого окна, напротив, отмечает наше пожелание "нарисовать" это окно поверх всех остальных. А как быть, если мы хотим на время убрать "картинку" с экрана, но не уничтожать отвечающие за ее формирование ресурсы из памяти X-сервера, чтобы иметь возможность впоследствии быстро ее восстановить? Для этого предназначен вызов с очевидным названием XUnmapWindow, также попадающий в категорию "визуальных".

На этом этапе уже можно и подвести краткие итоги, и сделать некоторые успокаивающие уточнения. Итак, концептуальная последовательность операций на уровне Xlib, которая должна присутствовать в любой программе для X Window, выглядит приблизительно так:

Установка соединения с X-сервером:


XOpenDisplay

Создание оконной иерархии будущего приложения:


ряд вызовов XCreateWindow/XCreateSimpleWindow

Необязательная установка атрибутов некоторых окон:


ряд вызовов XChangeWindowAttributes

Указание X-серверу отобразить построенную оконную иерархию:


вызовы XMapWindow/XMapRaised для всех созданных окон

Необязательное принудительное закрытие окон:


вызовы XDestroyWindow для всех созданных окон

Отсоединение от X-сервера:

XCloseDisplay

Естественно, что даже в такой концептуальной картине есть нечто настораживающее. А именно, скрытое требование к внимательности программиста—разработчика ПО для X Window. Ведь идентификаторы окон всей иерархии надо не просто хранить, но и, например, гарантированно выполнять для всех них "визуальные" вызовы, чтобы на экране отобразилась "правильная картинка". Вот и настало время успокаивающего уточнения — Xlib располагает дополнительными функциями, играющими роль итераторов. Для большинства упомянутых функций, работающих с одним из множества объектов, есть их итеративные аналоги. Так, например, вызов XMapWindow для одного окна дублирован вызовом-итератором XMapSubwindows, который "отмечает" указанием "отобразить" все окна, чьим "родителем" является переданное в вызове. По аналогичному принципу работают вызовы XUnmapSubwindows и XDestroySubwindows — укажите им идентификатор некоторого окна и переложите на X Window заботу и об "обходе" всех окон, родителем которых оно является, и о выполнении для каждого соответствующей операции.

События

Первая концептуальная модель нашей "программы" для X Window обладает одним принципиальным свойством — она статична, т. е. обеспечивает отображение некоторой "картинки" (пусть даже сложной), но не более. Динамика же поддерживается именно механизмом событий — основой большинства систем, отвечающих за взаимодействие человека и машины.

В терминах X Window под событиями понимается информация о чем-то "стороннем" по отношению к программе-клиенту, но о чем она должна быть проинформирована. События разделены на две основные категории — "непосредственные", инициируемые человеком-пользователем, и "косвенные", инициируемые X-сервером или другой программой-клиентом. Очевидно, что человек-пользователь может воздействовать на X-сервер только с помощью двух доступных средств — клавиатуры и позиционирующего устройства (ПУ). И ничего неожиданного и сложного нет в том, что в категорию "непосредственных" событий входят ButtonPress (нажатие клавиши ПУ), KeyPress (нажатие клавиши клавиатуры), MotionNotify (перемещение ПУ) и т. п. "Косвенные" события интересней и намного сложней — они формируются X-сервером на основе кэшированной информации об оконных иерархиях и отражают изменения в последних. Впрочем, весьма условные вопросы классификации всего тридцати событий — это далеко не самое главное и сложное в механизме событий X Window. Самое главное (с точки зрения программиста) — концептуальная модель самого этого механизма, выраженная в уже известных и новых терминах Xlib. А она одновременно сложна и проста, и именно ей мы посвятим этот раздел статьи.

В основе механизма сообщений лежит понятие "очереди сообщений" (events queue). "Очередь" — абстрактный тип данных, обеспечивающий две базовые асинхронные операции: запись_в_очередь/чтение_из_очереди и поддерживающий политику "первым вошел — первым вышел", т. е. данные из очереди читаются в порядке их записи в очередь.

X Window иногда называют "системой N+1 очередей" — в этом названии достаточно точно отражена основная идея ее системы событий: X-сервер поддерживает главную очередь событий, а Xlib реализует дополнительные очереди — для каждого клиентского приложения. Собственно, в этом и заключается вся "архитектурная сложность", а настоящие сложности начинаются в нюансах. Первый, естественно, связан с ограничениями, диктуемыми реальностью, а именно, с разнообразием событий и множеством окон в каждом приложении. Тридцать событий и несколько сотен окон (или даже несколько тысяч) образуют слишком большое количество комбинаций, а если учесть, что реакции на одинаковые события в разных окнах могут понадобиться разные… И здесь разработчики X Window нашли весьма успешное решение проблемы — реализовав так называемый "механизм интересов окна" (название это ни в коей мере не официальное). Суть его заключается в том, что в перечне атрибутов каждого окна присутствует перечисление событий, информация о которых нужна данному окну. Раз это атрибут окна, значит, он устанавливается при создании окна посредством тех же функций XCreateWindow/XCreateSimpleWindow, а впоследствии его можно изменить с помощью опять же уже известного нам вызова XChangeWindowAttributes или специального — XSelectInput. Перечисление событий, попадающих в "круг интересов окна", реализуется тривиально — битово-масочным способом, при котором в битовом массиве достаточного размера каждый бит является аналогом тумблера "Вкл/Выкл" для соответствующего события. Количество событий X Window (30) обеспечивает эффективную упаковку всего перечисления в одно машинное слово современных 32-битовых машин и оставляет возможности для расширения функциональности системы событий в будущем. Но вернемся к общей картине: "механизм интересов окна" — это то, что, с одной стороны, позволяет Xlib по-умному распоряжаться ресурсами, избегая ненужного копирования всех событий от одного X-сервера в очередях всех клиентских приложений, с другой — освобождает программиста от необходимости мучительного отбора только интересующих его событий. Естественно, что вся реализация самих очередей событий и механизма отбора нужных событий в очереди клиентских программ никаких действий от программиста не требует — после конфигурирования "механизма интересов окна" он должен только все время "просматривать" очередь событий своей задачи и активировать необходимые действия при нахождении соответствующих событий. Термин "все время" в данном случае означает все время исполнения клиентской программы, а для просмотра очереди сообщений в Xlib предусмотрен богатый набор функций, таких, как XNextEvent, XMaskEvent, XCheckMaskEvent, XWindowEvent, XCheckWindowEvent, XIfEvent, XCheckIfEvent и т. д. Сразу можно обратить внимание на "неожиданное" изобилие операций с очередью сообщений по сравнению с предыдущим минимализмом — мы столкнулись с ключевыми функциями X Window. Но и они совсем не сложны. Так, например, XNextEvent просто читает из очереди событий клиентского приложения информацию о любом событии, а если очередь пуста — ожидает появления такой информации.

Теперь мы можем уточнить нашу концептуальную модель программы для X Window, добавив ей способность реагировать на события.

Установка соединения с X-сервером:


XOpenDisplay

Создание оконной иерархии будущего приложения:

ряд вызовов XCreateWindow/XCreateSimpleWindow

Необязательная установка атрибутов некоторых окон:


ряд вызовов XChangeWindowAttributes

Конфигурирование "механизма интересов окна":


ряд вызовов XSelectInput

Указание X-серверу отобразить построенную оконную иерархию:


вызовы XMapWindow/XMapRaised для всех созданных окон
Выполнять всегда:

прочитать событие из очереди сообщений:


XNextEvent
определить тип прочитанного события
выполнить действия, ассоциированные с событием данного типа.

В реальных программах "Выполнять всегда" традиционно реализуется с помощью бесконечного цикла, реализация механизмов выполнения ассоциированных с событиями действий полностью возложена на программиста (как он того хочет, так и будет), а определение типа прочитанного сообщения — действие не более сложное, чем чтение содержимого переменной.

И это все?

Конечно же, нет. За пределами статьи осталась тьма нюансов, возможностей, "подводных камней" и расширений Xlib. Впрочем, автор и не ставил перед собой заведомо невыполнимой задачи — "обучить X Window за 10 минут". Зато все оказалось совсем не таким страшным, и заинтересовавшиеся читатели могут продолжить самостоятельное изучение X Window с посещения персонального сайта признанного специалиста Кентона Ли, где собрана каталогизированная подборка ссылок на множество посвященных X Window-тематике ресурсов.

  • Guest

    Мда..Иксы наверное самое больное место у *NIX систем…ведь наверное только благодоря X Window Microsoft все еще «у руля» со своими «Not X» Windows. Комментарий правда не по содержанию статьи, но наверное по теме…
    А.Зубинский, как было замечено в форуме, создается такое впечаетление что вы сами чуть ли не принимаете участвие во всех проектах, о которых пишете…так вот что хотелось бы узнать(читай как «прочитать в вашей статье»)…есть ли графическое будущее у юникс-систем с настоящей графической системой (X Window — ОТЛИЧНАЯ ИДЕЯ..и всего то)

    • Moderator

      Комментарии к статье [url="http://itc.ua/9003"]X Window — восполняя пробелы. Часть 2 — Xlib[/url]

    • Guest

      > юникс-систем с настоящей графической системой (X
      > Window — ОТЛИЧНАЯ ИДЕЯ..и всего то)
      Разве X Window — бутафорская система? :-)
      Не скажу «за всю Одессу» и А.Зубинского, но лично мне пока реальной альтернативы Х-м пока не видно, да и особой нужды в ней тоже как-то не вижу. Зато клиент-серверная раскладка таки дает кое-какие преимущества для «корпоративного» пользования.

    • Guest

      Увы, это мнение — «X Window — больное место», уже стало расхожим. Причем — незаслуженно расхожим. И по вполне очевидной причине — если я два слова о ней скажу, будет и ответ на ваш вопрос «есть ли будущее?».

      Причина такова — X-сервер должен (!) выполняться отдельным вычислителем. Это очевидно — архитектура X Window для того и создавалась.

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

      Опять же, изменяя точку зрения на пользователя ПК, превращённого в рабочую станцию, это же звучит так — вместо видеокарты (ND-акселератор bla-bla-bla…) за $150-$500 должна быть карточка с разъёмом PCI/AGP/…/?, на которой установлен процессор, флеш-память, чип графического акселератора и, наконец, ОЗУ (непременно расширяемое). И на этой же карточке должен быть USB/RS232/PS/2… для соединения клавиатур/мышей… Именно на такой карточке и должен выполняться X-сервер.

      Если бы такое устройство было, то:

      1. Хост был бы существенно (ой как существенно) разгружен

      2. Самые неприятные проблемы с безопасностою удалось бы устранить.

      3. Не понадобилась бы поддержка кучи драйверов (модулей) для X-серверов

      4. Выросла бы суммарная производительность всей системы в целом

      5. Все прелести сетецентрической идеи X Window сохранились бы…

      Но таких карточек/устройств не было (когда их было выгодно проектировать/выпускать их стоимость была бы нереальной), нет (сегодня рынок Unix-рабочих станций слишком мал, поэтому их невыгодно проектировать/выпускать) и, вероятнее всего, не будет. А жаль — тем паче, что такие устройства абсолютно реальны.

      И если вы хотели моего мнения о будущем — вот оно:

      дешёвый процессор класса MIPS Orion (R44xx) с тактовой частотой порядка 200 MHz + 2-4 MB Flash-памяти + видеоакселератор класса Matrox G400 (попроще чтобы) или вообще основанный на UMA акселератор + слоты под стандартные DIMM (до 1 GB) + контроллер периферии (USB/RS232… много чего можно и разумно реализовать) + контроллер PCI-шины. Всё это на плате в формате почти стандартной видеокарты помещается (реально чипов немного — а если использовать интегрированные MIPS-процессоры (контроллер памяти + PCI + ввод/вывод), то по сложности получается намного проще любого сегодняшнего 3D-акселератора. Всё это может работать, например, под управлением специально «заточенной» NetBSD — точнее, крохотным ядром NetBSD. X-сервер для такого удовольствия размером 600-1200 KB давно создан (он коммерческий, естественно) компанией MetroLink ([url="http://www.metrolink.com"]www.metrolink.com[/url]) и поддерживает все мыслимые и немыслимые удовольствия, включая расширения прямого доступа к видеопамяти (DGA из проекта XFree86) и растеризатор TrueType-шрифтов. По предварительной оценке в 4 MB Flash всё это помещается свободно (скорее всего, 4MB даже много).

      Вот, собственно, и всё, что касается будущего — вместо видеокарты в слот PCI ставится X-сервер с необходимым количеством памяти (да хоть 256 MB), к нему подключаются клавиатура-мышь-монитор и всё.

      Опять же повторюсь — это, увы, мечты. Подобных устройств и подобной полноценной архитектуры рабочей станции никто не производил (за исключением NeXT — но там не было X Window, а был DPS — аппаратный растеризатор дисплейного PostScript).

      А альтернативные X Window системы… Есть проект Berlin — графическая подсистема на основе CORBA-компонентов. Странный проект — как по мне, дикое несоответствие уровней используемой технологии и решаемых задач. Варится в собственном соку без каких-либо существенных перспектив. Славные ребята из hungry programmers в своё время начали хороший проект Y Window, но его можно считать усопшим. там была идея «строить не ломая» — расширить X-протокол (а он, естественно, расширяемый) высокоуровневыми сообщениями. Собственно и всё (если не считать Rio из Plan9 — но эта подсистема зависима от платформы).

    • cless

      Статья по моему не той сложности …
      Очень удивляет отсутствие иллюстраций.
      Хорошая иллюстрация с использованием UML могла бы сократить объем статьи в 2 раза и повысить понятность еще как минимум во столько же …

    • Pavlosh

      Oh, do you speak UML? :)))

      > повысить понятность
      … для какого процента читателей?

    • DitchCowSky

      Поясніть, будь-ласка, для чого X-Window серверу так багато пам»яті ? Програми будуть виконуватися локально, чи на клієнті ?
      Можливо, я неуважно читав.

      PS. А статті чудово все розклали по поличкам. Дякую.
      PPS2. [url="http://motif.hut.ru"]http://motif.hut.ru/url — на жаль прикрили. Там був переклад документації «Программирование в X Window и OSF/Motif». Можу вислати бажаючим архів — 130 кб.

    • Guest

      Ян, а кеширование ресурсов, на которе был сделан такой нажим в первой статье? И тот факт, что к ресурсам относятся и пиксельные картинки. А копия фреймбуфера (обязательно), и т.д. и т.п. — всё это много памяти. К тому же, динамически выделяемой. И ведь может понадобиться своп (чур-чур) — вот чтобы и не понадобился, нужно много.

      Короче, если пользовать GTK с пиксмапными темами + E DR17 с навороченной темой — самое то для «очень много памяти». А ежели поскромнее — то можно и не так много, но многие ли сегодня «поскромнее» стараются :-)

    • Guest

      Вопрос к А. Зубинскому:
      извините, но не понял вашего ответа: вы считаете, что у Х нет будущего? Т.е. если не будет такой аппаратной поддержки, которую вы описали, то система бесперспективна?

      P.S. Спасибо за статью, было очень интересно прочитать и многое разложилось по полочкам:).

    • Guest

      А где есть форумы в инете, где водятся специалисты по X-Window? Хотелось бы пообщаться. Статья неплохая, но остаются вопросы, не освещенные здесь.

Новости партнеров