програмування для «іксів» зсередини
Автор: David Chisnall, Sep 18,
2009
Оригінал:
Автор перекладу: somebyte,
Tue Nov 3 2009
При роботі над перекладом
використовувався OpenOffice.org.
Останні
кілька років, XC Binding (XCB) Бібліотека
поступово визрівав, як зміна для
поважного Xlib. У даній статті Девід
Чіснол (David Chisnall) розглядає новий
API і те як він покращує життя розробників.
Нещодавно,
я писав про кілька нові можливості
в Х11. Одна з них ігнорується, але
як і раніше, важлива, це XCB (the XC Binding
library). Для розуміння
важливості цієї нової бібліотеки, ми
потребуємо, для початку, в розумінні
деяких внутрішніх особливостей
розробки для X11.
За своєю суттю X11 це
протокол. Він визначає серію з двійкових
повідомлень, яка може бути передана
по мережі (через сокет) або через деяку
поділювану пам'ять, між клієнтом
(додатком) і серверів (програмою
керуючої дисплеєм). Якщо ви хочете
написати програму, яка буде
працювати через Х-сервер, то для цього
вам потрібно буде можливість генерувати
ці повідомлення.
Кожен Х-сервер, з
1980 р. по даний час, здатний зрозуміти
ці повідомлення (хоча попередні могли
розуміти лише підмножина). Звідси
Х черпають взаємодія і зворотний
сумісність. Я можу запустити додаток
на SPARC64 з ОС Solaris і демонструвати його
на Power PC c OS X і «Яблучним» X11, тому що
взаємодія між двома компонентами
добре визначено.
Xlib
У більшості випадків
люди, які пишуть графічні додатки,
не хочуть думати про Xlib. Деталі протоколу
це речі, які будь-яка людина пише
мережний додаток намагається абстрагувати
від іншої частини коду та написання для
кожного GUI своєї власної реалізації
клієнтської частини протоколу, було б
катастрофою.
Традиційно,
програмування c «іксами», довго
час, проводилося Xlib'ом. Ця бібліотека
визначає набір інтерфейсів загальному
призначення, такі як створення вікон і
малювання ліній. Вони приблизно відбивали
протокол, але надавали високий
рівень абстракції.
На жаль, через
роки, виявилося, що Xlib надає
як раз неправильний рівень абстракції.
Далеко не всі люди хочуть програмувати
чимось низького рівня, як Xlib, вважаючи за краще
більш загальні засоби такі як GTK, Qt або
GNUStep. У той час, коли Xlib був молодий, люди
воліли речі подібні Motif.
Єдині люди
використовують Xlib, ті то пишуть високорівневі
інструментарії (тулкіти). На жаль,
в більшості випадків Xlib пропонує
надто високий рівень абстракції для
них. Зокрема, Xlib намагається надати
синхронний API де тільки це можливо.
Це означає, що при виконанні функції Xlib
буде надіслано повідомлення на сервер, а
потім очікуватися відповідь. Це робить
програмування простим, але затримка
швидко зростає. Найчастіше, код виробляє
виклик не потребує негайного відповіді,
навіть якщо те що йому необхідно відбувається
досить скоро. При віддаленому з'єднанні
це швидко вбиває продуктивність.
Невелика мережа,
наприклад домашня LAN, має час
прийому / передачі близько 1 мс. Уявіть
собі, на секундочку, що ви запустили
віддалене додаток, написаний при
допомоги Xlib. Час прийому / передачі означає,
що ви можете, найбільше, зробити
1000 синхронних дзвінків за секунду, навіть
якщо обидва комп'ютери нескінченно швидкі.
Якщо ви зробите асинхронний виклики,
то навіть виконання невеликого обсягу
роботи між передачею вихідного
повідомлення і блокуванням в очікуванні
відповіді може дати помітний приріст
продуктивності.
Один з типових
прикладів вищесказаного це повідомлення
у вигляді дерева запитів (the Query Tree). Це
перерахування всіх нащадків даного
вікна. Зазвичай це затребувано віконними
менеджерами для знаходження всіх нащадків
кореневого вікна і призначення їм обробки.
З Xlib, асинхронний API означає, що виклик
буде блокований до завершення циклу
прийому / передачі з сервером, хоч і не
існує ніяких вимог від
більшості віконних менеджерів до
синхронного завершення; вони то якраз
могли б робити це асинхронно через
зворотний виклик і передати результат
коли він буде готовий.
Xlib розробляється
у напрямку як для розробки
додатки, так і розробки інструменту
(toolkit), що робить його
надзвичайно роздутим. Вона містить
величезна кількість зручних функцій
для будь-якого варіанту використання
протоколу, що проектувальники
змогли б придумати. На жаль,
фактично більшість додатків
використовують невелика підмножина
функцій, що означає що деякі з
перспективно корисних на сьогодні
містять помилки і ледь відтестовані.
Існує ряд
інших проблем з Xlib, таких як відсутність
ряду форм перевірки часу компіляції
і труднощі використання в багатопотоковому
коді.
Представлення
XCB
Проект стартував в
2002 року: кілька Х-розробників
почали свою роботу над легковагої
клієнтської бібліотекою для Х11. X C Binding
(XCB) – це як
саме те, що має на увазі
назва: зв'язок протоколу X11 з мовою
«C». Будь-який формат повідомлення протоколу
має відповідний «С»-тип, а будь-яке
взаємодія відповідну функцію.
Весь допоміжний код, наданий
Xlib'ом, був прибраний.
Для того, щоб
забезпечити зворотну сумісність,
новітня версія Xlib використовує XCB в основі
для забезпечення інкапсуляції повідомлення
і транспорту. Це означає, що існує
можливість для використання як Xlib,
так і XCB API в одній програмі, поступово
мігруючи тільки до використання XCB.
Винесені
покажчики і помилки
Коли ви створюєте
об'єкт або структуру на локальній
машиною, то зазвичай отримуєте покажчик
на нього. Це покажчик – унікальний
ідентифікатор, що надає адреса
в пам'яті на об'єкт. Коли ви створюєте
об'єкт в деякому адресному просторі,
вам необхідно його абстрактне
подання.
Кожен об'єкт
Х-сервера ідентифікується XID'ом. Це
всього лише довільне число і X-сервер
усередині відображає його в щось корисне.
Xlib дозволяє вам взаємодіяти з
XID'амі, як якби вони були покажчиками
на непрозорі типи. Ви отримуєте їх,
як що повертають значення з функцій.
Це одна з ключових
областей, де XCB API має семантику
відмінну від Xlib. Існує два види XID:
ті, що визначені на сервері і ті, що
визначені на клієнти. Якщо ресурс
створений у відповідь на подію сервера, сервер
присвоїть йому ID. Якщо ви створює ресурс,
то вам слід зробити два кроки:
створити ID на себе і пов'язати його з ресурсом.
Розгляньте, як ви створюєте вікно в
обох API:
/ / XCB xcb_window_t winid = xcb_generate_id (connection); xcb_create_window (connection, depth, wid, parent, x, y, width, height, border_width, class, visual, value_mask, value_list); / / Xlib Window xlibwinid = XCreateWindow (display, parent, x, y, width, height, border_width, depth, class, visual, valuemask, attributes);
Обидві
версії завершаться негайно. У разі
помилки, Xlib версія здійснить один з двох
зворотних викликів для помилок. XCB версія
управляє помилками трохи по іншому.
Повертає значення функції
xcb_create_window ()
є спеціальним складовою
об'єкт для віддаленого взаємодії,
створений сервером, якщо той готовий
взаємодіяти (далі «випічка» або
cookie). Всі асинхронне поведінка в XCB
реалізовано через ф'ючерс.
Отримати
дійсне значення повертається
будь-який XCB-функцією, ви можете двома
способами. Найпростіший, передати
«Випічку» іншої функції, яка
отримує це повертається дійсне
значення. «Випічка» типу xcb_void_cookie_t,
означає «випічку», яка представляє
собою порожню сутність для використання
в програмі пізніше. Ви можете передати
її в xcb_request_check (), яка поверне помилку,
якщо та мала місце або NULL якщо запит
завершився без помилки. Цей виклик буде
блокуватися до тих пір поки XCB отримує
відповідь на запит, від X-сервера.
Інший
альтернативою є опитування та отримання
відгуків. Це дозволяє вам зареєструвати
функції зворотного виклику і обробляти
повертають значення повністю
асинхронно. Ці два підходи дають більшу
гнучкість. Іноді, у деякої частини
коду, потрібно отримати повертається
значення швидко, а іноді це потрібно в
довільний момент часу багато
пізніше.
Цикли
подій
Основою
більшості графічних програм
є цикл подій. В UNIX-програмі
існує єдине джерело
подій: файлові дескриптори. Все,
включаючи мережеві з'єднання та керуючий
термінал, управляється файлу-подібним
інтерфейсом. Десь близько верхнього
рівня графічна програма під час
має цикл з умовою, що використовує
одну з двох функцій: BSD'шний select () або
SysV'шний poll () (або часом kevent () в новітніх
BSD-системах), щоб чекати поки дані
будуть доступні на одному з набору
файлових дескрипторів. Коли один з
них готовий, цикл викликає асоційовану
функцію зворотного виклику для обробки
нових даних.
Як
Xlib так і XCB дозволяють отримати, дескриптор,
який вони використовують для зв'язку з
сервером і інтеграції його із зовнішнім
циклом подій. Графічні програми
звичайно програмуються в подієво-керованому
стилі, де події або якась форма
введення / виводу, або виникають від таймерів.
У центрі програми звичайно існує
цикл з умовою, що виробляє
виклик функції select () (або еквівалентної)
зі значенням тайм-ауту до наступного
події таймера. Потім їм викликається
обробник події для файлового
дескриптора з доступними даними або
функцією зворотного виклику для таймера.
В
більшості випадків, розробникам не
потрібно турбується про деталі цього
рівня, вони сховані в інструментарії
(toolkit). Це особливо вірно щодо
подій Х-сервера. Розробляє
програма бажає знати щось на зразок:
«Користувач клацнув в цьому місці»
або «користувач надрукував ось тут»,
замість того, щоб розглядати деталі
повідомлень нижнього рівня.
Події
і відгуки
Як
я згадував раніше, існує два види
повідомлень, які ви можете отримати
від сервера: події та відгуки. Обидва
повідомлення приходять через один і той же
сокет, таким чином, якщо ви чекаєте даних
на сокеті за допомогою функції poll () або
функції select (), то ви будете повідомлені,
коли одне з двох готове.
Для
того, щоб отримати така подія,
ви зазвичай викликаєте xcb_poll_for_event () в циклі
подібному до цього:
xcb_generic_event_t * event; while (NULL! = (event = xcb_poll_for_event (connection )))
Якщо
немає подій в очікуванні, то буде повернуто
NULL тому ви повернетеся до блокування
або обробці введення інших файлових
дескрипторів. Якщо існують події в
очікуванні, то вам потрібно обробити
подія. Поле response_type в структурі події
говорить вам про вид цієї події і потім
ви преобразуете покажчик до дійсної
структурі події до його обробки.
Зауважу що, то ж повертається значення,
так само добре використовується у випадку
помилок. Структура xcb_generic_error_t має такі
ж поля, як структура події та відзначена
типом.
Коли
ви преобразуете покажчик ви отримуєте
структуру точно такий як тип Паломництва.
XCB
буде
обробляти для вас речі подібні
вирівнювання структури і відмінності в
порядку байт, але нічого іншого.
Відгук
можуть бути оброблені таким самим
шляхом. Ми вже розглядали як обробити
конкретні відгуки синхронно, але
більшість інструментарію (тулкітов)
захоче підставити асинхронний API в
додаток розробників, в якому
вони можуть реєструвати обробники
для відгуків, що можливо займе час,
але допоможе уникнути блокування.
Функція xcb_poll_for_reply () дозволяє вам
реалізувати подібний вид API.
Використання
цієї функції на нижньому рівні API звичайне
справа. Ця функція приймає чотири
аргументу і дозволяє вам побачити
конкретний відгук, що був отриманий:
xcb_poll_for_reply (connection, sequenceNumber,% 26amp; reply,% 26amp; error)
Другий
аргумент це порядковий номер запиту,
який буде повернений «випічкою»,
коли запит завершено. Інші два
аргументу це покажчики на покажчики,
що будуть використані для повернення
або відгуку, які помилки, що залежить
від успішності запиту.
Обидві
позначені функції видалять дані з
сокета і збережуть їх у чергах прихованих
в бібліотеці. Це означає вам потрібно
переконатися, що як чергу відгуків,
так і подій порожні, до блокування
та очікування великої кількості даних
на сокеті, а інакше ви втратите деякі
повідомлення. Найбільше упущення XCB
API відсутність єдиного виклику для одержання
наступного відгуку чи події.
Шаблони
XCB
Значна
частина XCB API автоматично генерується
з XML опису протоколу і ви побачите
багато хто з повторюваних шаблонів. Це
робить навчання простим, як тільки ви
зрозумієте основні концепції.
Один
з найбільш поширених шаблонів
це ітерація по колекціях. Документація
протоколу X11 часто описує списки
різних типів в пакетних структура.
XCB пропонує структуру для кожної з
них, що виглядають як щось подібне
слующему:
typedef
struct (
xcb_name_t * data; int rem; int index; ) Xcb_name_iterator_t;
У
цій структурі ім'я буде замінено на
ім'я списку для ітерації по ньому. Наприклад
ви можете пройтися по всіх підключеним
екранів, щоб встановити з'єднання,
як тут:
for ( xcb_screen_iterator_t iter = xcb_setup_roots_iterator (xcb_get_setup (connection)); iter.rem; xcb_screen_next (% 26amp; iter)) ( / / Do something )
Поле
rem ітератора отримує число що залишилися
записів і досягає нуля до кінця списку.
Поле data завжди вказує на наступний
елемент даних, у цьому випадку елемент
типу xcb_screen_t.
Розширення
Одна
з сильних сторін Х11 – підтримка
розширень. У 1990-х це було його слабкістю,
тому що кожен продавець UNIX пропонував
свій власний X сервер з різними
розширеннями. Зараз, X.org використовується
майже скрізь, і таким чином один і той
самий набір розширень може бути використаний
скрізь.
Підтримувати
розширення з Xlib було дуже важко. Код
був плутано і потребував модифікації
для кожного розширення. З XCB все стало
набагато простіше. Всім розробникам
розширень потрібно надати XML опис
протоколу. Потім воно автоматично
перетвориться в набір функцій. Усім
користувачам потрібно задовольнити
залежно від згенерованої з
опису бібліотеки і включити
відповідні файли-заголовки.
Будь-яке
розширення Х11 визначає набір запитів
і подій. Будь-X сервер здатний
підтримувати самостійний набір
Розширення та не завжди з усіма з них
використовуються однакові клієнти. Тому
числа використовуються для ідентифікації
подій присвоюються динамічно.
Якщо
ви хочете використовувати розширення в
XCB-програмі, що ви майже напевно
хочете, то спочатку з'ясуйте його наявність
за допомогою xcb_query_extension (). Як всі інші
XCB функції ця виконує запит, вона
повертає «випічку», яка потім
передається в xcb_query_extension_reply () для того
щоб отримати відповідь пізніше. Відображення
запитів обробляється всередині XCB, API
буде автоматично розраховувати номер
запиту, але обробляєте події ви
самостійно.
Поле
response_type структури події містить
номер однозначно визначає тип
події. Для основного протоколу ці
номера – константи. Для розширень, вони
знаходяться через додаткове поле
first_event відгуку розширення з номером
зазначеним у протоколі.
Коли
ви подивіться заголовки
розширення, ви знайдете константи для
будь-якої події, що розширення
здатний генерувати. Вони повинні
додаватися до першого номера події
для розширення, щоб отримати реальний
номер події.
Висновок
Документація
по XCB обмежена і це може сильно
розчарувати програмістів бажаючих
її використовувати. Багато підручники, що
ви знайдете засновані на старій версії
API і більше не працюють. На щастя API, сам
по собі дуже простий. Багато складності
у навчанні того, як використовувати XCB
связаны с необходимостью понимать
протокол X11, который документирован
дуже добре
(ftp://ftp.x.org/pub/X11R7.0/doc/PDF/proto.pdf).
Если
вы пишете новый инструментарий или
обновляете старый, то похоже что XCB лучше
подходит на долгосрочную перспективу
чем Xlib. XCB раскрывает чисто асинхронный
API, делает удалённую реакцию по протоколу
X11 при правильном использовании и
получает поддержку новых расширений
очень быстро.



