Programování pro X Window System

31. 3. 2004
Doba čtení: 11 minut

Sdílet

X Window System (zkráceně X) je standardní technologie používaná v unixových systémech pro tvorbu grafického uživatelského rozhraní (GUI - graphical user interface). Uživatelům Linuxu je dobře známá implementace XFree86. V tomto seriálu článků se budeme zabývat programováním aplikací pro X, proto předpokládám, že čtenář má uživatelské znalosti prostředí X a zná programovací jazyky C a C++.

Probíraná tématika vychází z mých stejnojmenných přednášek na MFF UK. Na webových stránkách k přednášce www.ms.mff.cu­ni.cz/~beran/vy­uka/X/ lze najít další doplňující informace a sbírku krátkých ukázkových programů.

Tento úvodní článek obsahuje seznámení s architekturou X a připomenutí obecných principů používaných při programování grafických rozhraní. Dalších několik dílů seriálu bude věnováno úvodu do toolkitu GTK+ (http://www.gtk­.org/). Pak navážeme popisem konkurenčního toolkitu Qt (http://www.tro­lltech.com/pro­ducts/qt/index­.html). Závěr bude patřit low-level knihovně Xlib. Možná by byl logičtější obrácený postup od Xlib k toolkitům, nicméně takto se čtenář dříve dozví informace potřebné pro praktické programování aplikací pomocí toolkitů. Xlib je zajímavá z důvodu pochopení, jak X Window System funguje uvnitř, nicméně používat přímo tuto knihovnu není moc obvyklé. Až si ukážeme třistařádkový program „hello-world“, uvidíme proč.

X Window System se začal vyvíjet jako součást projektu Athena v Laboratory for Computer Science na MIT v roce 1983. Dalším známým produktem projektu Athena je autentizační systém Kerberos. V rychlém sledu následovaly verze X1 (1984) až X11 (1987). Verze X10 Release 4 se stala základem pro první komerční implementace dodávané výrobci unixových pracovních stanic. Zatím poslední verze je X11 Release 6.6 z roku 2001. Referenční implementaci X spravuje The Open Group – konsorcium pro otevřené systémy, které spravuje např. i standard pro OS Unix.

Architektura X

Architektura X Window System je založena na modelu klient-server. X server, v terminologii X display, je počítač, ke kterému je připojena grafická obrazovka, klávesnice a myš. Dnes obvykle X server běží na PC, dříve se používaly spíše unixové pracovní stanice nebo specializované X terminály, tj. jednoúčelové počítače, na nichž je instalován pouze X server. Aplikace se spouští na jiných počítačích a X terminál s nimi komunikuje po síti. X klient je program, který používá služby X serveru: zobrazování grafiky a zpracování vstupu od uživatele. Server a klient spolu komunikují pomocí socketů, mohou tedy každý běžet na jiném počítači v síti. Systém X Window je distribuovaný, uživatel může mít na jedné obrazovce okna programů běžících na různých počítačích a naopak aplikace z jednoho počítače mohou komunikovat s uživateli prostřednictvím mnoha X serverů. Další důležitou vlastností je otevřenost. X se skládá z mnoha částí komunikujících mezi sebou přes standardizovaná rozhraní. Proto mohou bez problémů spolupracovat implementace X od různých výrobců. Některé části skládačky (například toolkit nebo window manager) je možné vyměňovat a tím přizpůsobit vzhled a chování systému potřebám a zvyklostem konkrétního uživatele.

Klient a server si povídají X protokolem. Komunikace probíhá přes síťové (typicky TCP) sockety. Jestliže klient i server běží na stejném počítači, používají se lokální sockety (AF_UNIX) a pro přenosy objemnějších dat i sdílená paměť. Verzi protokolu vyjadřuje číslo za písmenem X v názvu verze. Protokol již dlouho zůstává neměnný – od verze X11 Release 1 v roce 1987. Samozřejmě bylo od té doby potřeba do X přidat nové funkce a u jiných změnit jejich chování. Takové úpravy se provádějí pomocí rozšíření (extensions), která definují nová volání Xlib a nové typy zpráv X protokolu. Pro rozšíření existuje definovaný systém registrace. Klient a server se v rámci standardního X protokolu domluví, která rozšíření podporují.

Architektura X
Obr. 1: Architektura X

Obrázek 1 zachycuje vztahy mezi hlavními částmi celého systému X. Na jedné straně sítě je X server, jehož architekturou se zde nebudeme dále zabývat. Naším cílem je programování v X, proto nás více zajímá, jak vypadá klient. Teoreticky by aplikace (klient) mohla sama otevřít síťový socket a X protokolem komunikovat se serverem. To ale nikdo nedělá. Místo toho se používá knihovna Xlib, která poskytuje rozhraní ve formě funkcí v jazyce C a interně zajišťuje komunikaci se serverem pomocí X protokolu. Xlib umožňuje provádět jen poměrně primitivní operace: vytvoření okna, kreslení grafiky do okna a příjem událostí, vzniklých např. na základě akcí uživatele.

Pokud se používají přímo volání Xlib, musí se aplikace starat o spoustu technických detailů. Nelze např. v kódu napsat něco jako: „Vytvoř menu s těmito položkami, a když uživatel vybere položku, generuj událost s identifikátorem položky.“ Místo toho je třeba nakreslit rámeček menu, do něj napsat jednotlivé položky, detekovat kliknutí myši (pomocí zpracování události), překreslit vybranou položku zvýrazněným stylem a teprve pak je možné provést akci iniciovanou výběrem položky. Proto kód aplikace obvykle nevolá Xlib, ale používá toolkit – knihovnu, která poskytuje jednotlivé prvky uživatelského rozhraní (menu, tlačítka, řádky pro vkládání textu, apod.). Toolkit navenek poskytuje funkce na úrovni abstrakce odpovídající našemu příkladu s menu. Programátor se už nemusí starat o detaily vykreslování a zpracování uživatelského vstupu a může se zaměřit na logiku aplikace. Uvnitř toolkit v implementaci svých funkcí používá Xlib.

V prostředí X je žádoucí provozovat i shell a další unixové programy, které fungují v textovém režimu. K tomu se používají emulátory terminálů (např. xterm), fungující na jednu stranu jako X-ový program. Na druhé straně emulátor používá pseudoterminál a k němu připojí program běžící v textovém režimu.

Posledním dílkem mozaiky je window manager. Ten spravuje hlavní (top-level) okna aplikací. Obvykle kolem každého top-level okna zobrazí rámeček a umožňuje uživateli pomocí myši přesouvat okna, měnit jejich velikost a zavírat je. Window manager také poskytuje menu pro spouštění aplikací nebo je zapojen do integrovaného desktopu (GNOME, KDE). Rozdělení na low-level podporu pro tvorbu GUI (Xlib), vlastní definici prvků GUI (toolkit) a správu oken (window manager) může sice mírně snižovat efektivitu, ale na druhou stranu poskytuje velkou flexibilitu. Teoreticky může každá aplikace na obrazovce používat jiný toolkit. V rámci jednoho X serveru smí sice běžet jen jeden window manager, ale uživatel si může zvolit takový, jaký mu vyhovuje.

Spuštění prostředí X

Než může uživatel začít pracovat v prostředí X Window System, je třeba nastartovat X server, window manager a případně počáteční sadu klientů, např. emulátor terminálu nebo skupinu aplikací tvořící desktopové pracovní prostředí. Existují tři varianty, jak server a klienty spustit.

První možností je samostatné ruční spuštění serveru ze shellu příkazem

$ X

a následně klientů. Na jednom počítači je možné spustit několik serverů. Každý z nich běží na samostatné virtuální konzoli. Klienti se po startu spojí s X serverem specifikovaným v environmentové proměnné DISPLAY ve tvaru [host]:displa­y[.screen]. Část před dvojtečkou je jméno počítače s X serverem (klienti se připojují přes TCP socket). Když jméno chybí, znamená to lokální X server (klienti se připojují přes unixový socket). Za dvojtečkou je číslo displeje, tj. X serveru. První X server má číslo 0, při spuštění dalších je nutné číslo zadat:

$ X :1

Pokud X server používá několik monitorů, následuje za číslem displeje tečka a číslo obrazovky, na které se mají objevit okna klienta.

Další variantou nastartování prostředí X je příkaz

$ startx [ [ client ] options ... ] [ -- [ server ] options ... ] 

Program startx je skript. Administrátor ho může upravit podle lokálních požadavků. Standardně vezme inicializační skript pro nastartování klientů (uživatelský $HOME/.xinitrc nebo systémový /usr/X11R6/lib/­X11/xinit/xini­trc) a serveru (uživatelský $HOME/.xserverrc nebo systémový/usr/X11R6/lib/­X11/xinit/xser­verrc). Jména skriptů předá programu xinit, který nastartuje server a následně klienty. Po skončení posledního klienta xinit ukončí server a skončí. Činnost startx je schematicky znázorněna na obr. 2.

startx
Obr. 2: Spuštění prostředí X pomocí startx

Poslední způsob inicializace prostředí X poskytuje uživatelům grafický login do systému. Ze systémových inicializačních skriptů je spuštěn display manager – program xdm – s rootovskými právy. Display manager nastartuje X server, inicializuje ho pomocí Xsetup a spustí klienta xlogin. Ten přečte jméno a heslo, ověří je a spustí Xstartup. Následně změní uživatelskou identitu na nově nalogovaného uživatele. Skript Xsession pak nastartuje klienty. Po skončení posledního klienta končí Xsession, display manager provede úklid pomocí Xreset (již opět s rootovskými právy), resetuje nebo restartuje X server a celý cyklus se opakuje. Fungováníxdm je zobrazeno na obr. 3.

xdm
Obr. 3: Grafický login do prostředí X pomocí xdm

Klient má na serveru neomezená práva. Může manipulovat i s okny jiných klientů a přijímat z nich události. Proto je potřeba nežádoucí klienty zablokovat. V X existují dvě metody řízení přístupu: xhost a xauth. Příkaz xhost nastavuje v serveru seznam adres počítačů, ze kterých se mohou připojovat klienti. Problém této metody je, že z povoleného stroje mohou server používat všechny klientské programy, i když patří jinému uživateli, než který spustil X server. Řízení přístupu pomocí metody xhost se používá, jestliže není zapnuta metoda xauth. Na počátku jsou povoleny jen lokální klientské aplikace. Kompletně vypnout kontrolu přístupu lze příkazem

$ xhost +

nebo nastartováním serveru pomocí

$ X -ac

Metoda xauth funguje tak, že server povolí připojení klienta, jestliže se klient autentizuje klíčem. Používáno je několik variant, nejčastější je MIT-MAGIC-COOKIE-1. Při startu serveru program startx nebo xdm vygeneruje náhodný klíč o délce 128 bitů a předá ho serveru. Zároveň klíč uloží do souboru .Xauthorityv do­movském adresáři uživatele. Soubor je přístupný jen pro vlastníka. Klient si před připojením k serveru přečte klíč z .Xauthority a pošle ho serveru. Jestliže klíč souhlasí, server klienta akceptuje. V souboru .Xauthority je možné pomocí příkazu xauth udržovat klíče několika serverů. Autentizace funguje zcela transparentně z pohledu uživatele i programátora, správný klíč automaticky vybere a pošle knihovna Xlib. Autentizační data i veškerá ostatní komunikace v rámci X protokolu jsou po síti posílány bez jakékoliv ochrany a lze je odposlouchávat. Proto je v nedůvěryhodné síti vhodné X-ovou komunikaci chránit např. pomocí SSH. Příkaz ssh umí vytvořit šifrovaný tunel a X protokol do něj automaticky přesměrovat.

Události a objekty

Unixový program běžící v textovém režimu (na terminálu) obvykle funguje tak, že přečte kus dat ze vstupu, zpracuje je a pošle výsledek na výstup. Pak přečte další část vstupu a tak dále až do konce vstupního souboru. Z logiky programu plyne, kdy se bude číst vstup a kdy generovat výstup. Takto fungují i interaktivní programy, ve kterých vstup pochází z klávesnice a výstup je vypisován na obrazovku.

Programy, které mají grafické uživatelské rozhraní, typicky používají jiný model – řízení událostmi. Akce uživatele, jako stisk klávesy nebo pohyb myší, jsou překládány na události. Událost je datová struktura obsahující typ (např. stisk tlačítka myši) a další parametry (poloha myši a identifikátor okna, v němž je kurzor). Program přijímá události a volá pro ně obslužné funkce – handlery. Jádrem událostmi řízeného kódu je cyklus zpracování událostí. Detaily jsou v různých grafických prostředích odlišné, ale princip je vždy stejný:

while(event = wait_for_event())
    process_event(event);

Zdrojem událostí není jen uživatel. Události si mohou posílat i aplikace navzájem. Některé události generuje X server. Například když je potřeba nakreslit nově vytvořené okno nebo překreslit část okna, která byla předtím zakrytá, server pošle klientovi událost. V ní je specifikována oblast, která se má vykreslit.

Aby program promptně reagoval na požadavky uživatele, je nutné dodržovat několik zásad. Každý handler by měl běžet jen krátkou dobu a co nejdřív se vrátit zpět do cyklu zpracování událostí. Handler by neměl volat systémové funkce, které se mohou zablokovat, např. čekání na data ze sítě. Pro takové situace je k dispozici obdoba volání select. Proces si u správce událostí zaregistruje deskriptor souboru. Když je možné číst z deskriptoru nebo na něj zapisovat, je vygenerována událost. V handleru události se pak volá neblokující operace čtení nebo zápisu. Existují i další možnosti, jak řešit situace, kdy je potřeba vykonat delší nebo potenciálně blokující funkci. Výpočet se rozdělí na kratší úseky a v rámci jednoho volání handleru se provede jen jeden úsek. Handler se zaregistruje, aby se volal buď periodicky po uplynutí určitého časového intervalu, nebo vždy, když nejsou k dispozici žádné události pro zpracování. Dlouhotrvající činnosti se také dají přesunout do samostatného procesu nebo vlákna.

Při programování GUI se s výhodou používá objektově orientované programování. Každý prvek rozhraní (widget) je reprezentován jedním objektem. Mezi třídami widgetů je přirozená hierarchie dědičnosti daná podobností mezi widgety. Kořenem hierarchie je obecný widget, který má definovanou např. velikost nebo handler zajišťující nakreslení. Jedním z jeho potomků je tlačítko. Z něj je dále odvozený check box, lišící se vzhledem a tím, že se přepíná mezi dvěma stavy. Radio button je skoro totéž jako check box, ale vypadá trochu jinak a obvykle je prvkem skupiny obsahující vždy jen jedno zaškrtnuté tlačítko. Dalším příkladem jsou widgety pro editaci textu a pro zobrazení seznamu položek. Pro ně je společná schopnost rolování, pokud obsah přesahuje viditelnou oblast widgetu. Sdílejí předka, který implementuje spolupráci se scrollbary. Objektově orientovaný toolkit nevyžaduje programovací jazyk s podporou objektů. Např. GTK+ je celé napsáno v C. Nicméně uvidíme, jak implementace objektů v C prodlužuje a zesložiťuje výsledný kód. Rozdíl bude patrný při srovnání GTK+ a toolkitu Qt, který je napsán v jazyce C++. Objektově orientovaný programovací jazyk tedy není nutností, ale je značnou výhodou.

bitcoin_skoleni

Řízení událostmi je v X používáno univerzálně. Na událostech je založené GTK+, Qt, ostatní toolkity i knihovna Xlib. Na druhou stranu objektová orientace je užitečná, ale ne nutná. Toolkity jsou obvykle založeny na objektech, ale Xlib objekty nepoužívá. Pokud se pracuje s objekty, je oddělená logická struktura uživatelského rozhraní dána objekty (widgets) uvnitř programu a reprezentace GUI viditelná uživatelem je reprezentována zdroji (resources) X serveru – okna, fonty, grafické kontexty atd.

Literatura

  1. The Definitive Guides to the X Window System. O'Reilly & Associates – klasická série knih o X, popisuje X protokol, Xlib, X Toolkit, toolkit Motif a použití prostředí X z pohledu uživatele a správce
  2. Havoc Pennington: GTK+/GNOME Application Development. New Riders Publishing, developer.gno­me.org/doc/GGAD/– úvodní výklad programování v prostředí GNOME a vybraná témata o programování GTK+ (např. vytváření nových tříd widgetů)
  3. Web toolkitu GTK+: www.gtk.org/
  4. Web desktopu GNOME: www.gnome.org/
  5. Web firmy Trolltech: www.trolltech­.org/
  6. Web desktopu KDE: www.kde.org/
  7. Martin Beran: Programování pro X Window System. Přednáška na MFF UK Praha, www.ms.mff.cu­ni.cz/~beran/vy­uka/X/

Stránky GTK+, GNOME, Trolltech a KDE obsahují zdrojové texty, uživatelskou a programátorskou dokumentaci, tutoriály i referenční příručky.

Autor článku