Programování pro X Window System (5)

29. 4. 2004
Doba čtení: 8 minut

Sdílet

V pátém dílu seriálu se podíváme na funkce knihovny GLib. V další části článku se budeme zabývat podporou GTK+ pro periodicky volané funkce, vstupy/výstupy a konfiguraci grafických stylů widgetů. Nakonec probereme možnosti komunikace mezi programy pomocí výběrů a drag&drop.

Knihovna GLib

Knihovna GLib obsahuje různé užitečné funkce a datové struktury. Není svázána s toolkitem GTK+, dá se používat i samostatně. GLib poskytuje náhradu pro některé standardní typy jazyka C, definuje např. gchar, gint, gpointer, guint16… Součástí GLib jsou i redefinice standardních knihovních funkcí, jako jsou funkce pro manipulaci s nulou ukončenými řetězci ve stylu jazyka C (g_snprintf, g_strcasecmp, g_strdup, atd.). Důvodem existence této části GLib je snaha o přenositelnost. V moderních unixových systémech už obvykle knihovny obsahují vše, co by podle standardů obsahovat měly, a definice toho samého pod jiným jménem se může zdát nadbytečná. Nicméně v době, kdy GLib a GTK+ začaly vznikat, nebyly systémové knihovny na různých verzích Unixu dostatečně kompatibilní. Knihovna GLib navíc funguje i na jiných platformách, jako MS Windows.

Druhá část GLib obsahuje pomocné funkce a makra. Jsou zde makra pro přetypování mezi čísly a ukazateli (GINT_TO_POINTER, GPOINTER_TO_INT). Ladění programů usnadňují makra g_return_if_fail (pro funkce s návratovým typem void) a g_return_val_if_fa­il (pro funkce vracející hodnotu). Jejich typické použití je kontrola parametrů při vstupu do funkce. Pokud není splněna podmínka zadaná jako parametr makra, vypíše se chybové hlášení a provede se návrat z aktuální funkce. Další ladicí makra jsou g_assert a g_assert_not_re­ached. GLib má vlastní funkce pro alokace paměti, které ukončí běh programu, pokud nelze alokaci provést: g_malloc je obdoba standardní funkcemalloc a g_malloc0 navíc naalokovanou paměť vynuluje (jako calloc). Pro dealokaci slouží funkce g_free.

Ve třetí části knihovny GLib jsou definovány datové struktury podobné STL kontejnerům v C++. K dispozici jsou řetězce GString, které automaticky zvětšují datový buffer při prodlužování řetězce, jednosměrné (GSList) a obousměrné (GList) spojové seznamy, vyvážené binární stromy (GTree), n-ární stromy (GNode), hašovací tabulky (GHashTable), automaticky se zvětšující pole (GArray) aj.

Poslední skupina typů a funkcí v GLib poskytuje prostředky, nad kterými je postavena infrastruktura toolkitu GTK+. Struktura GHookList reprezentuje seznam callback funkcí. V GTK+ se používá v mechanismu registrace a volání handlerů signálů. Z generického cyklu pro zpracování událostí je odvozena funkce gtk_main. Lexikální analyzátor se používá při zpracování resource souborů definujících styly widgetů.

Timeouty, I/O

Zatím jsme uvažovali grafické aplikace, které komunikují pouze s uživatelem. Program čeká v gtk_main na událost od X serveru, zpracuje ji a vrátí se zpět do gtk_main. Když není žádná událost k dispozici, program nic nedělá. Takový model ale není vždy postačující. Někdy je potřeba provádět určité akce periodicky. Např. při zobrazování animace se v pravidelných intervalech překresluje obsah widgetu. Pomocí gtk_timeout_add lze zaregistrovat funkci, která bude volána po uplynutí zadaného počtu milisekund. Když funkce vrátíTRUE, bude se volat znovu za stejnou dobu. Návratová hodnota FALSE nebo volání gtk_timeout_remove funkci odregistruje, takže se už nebude provádět. Někdy potřebujeme určitou funkci volat tak často, jak je to možné, např. pokud každé zavolání provede část nějakého dlouhotrvajícího výpočtu. K tomuto účelu se dají využít funkce gtk_idle_add a gtk_idle_remove, které fungují podobně jako timeouty, ale registrovaná funkce se bude volat vždy, když ve frontě nečeká na zpracování žádná událost.

Pokud program potřebuje číst nebo zapisovat do souboru, zařízení nebo soketu, je potřeba zajistit správné kódování dat a nezablokovat uživatelské rozhraní při čekání na data. Oba problémy v GTK+, resp. GLib, řeší I/O kanály (typ GIOChannel). Když máme otevřený deskriptor, voláním funkce g_io_channel_u­nix_new k němu vytvoříme kanál. Pomocí g_io_channel_set_en­coding se dá nastavit kódování dat v kanálu. Implicitně se používá UTF-8. Jestliže nechceme, aby kanál do dat jakkoliv zasahoval, nastavíme kódování na NULL. I/O kanály používají počítání referencí. Kanál se zruší, když g_io_channel_unref sníží počet referencí na nulu. Voláníg_io_ad­d_watch zaregistruje pro zadaný kanál handler, který se bude volat, když na kanálu nastane požadovaná událost: je k dispozici alespoň jeden bajt dat ke čtení (G_IO_IN) nebo lze alespoň jeden bajt zapsat (G_IO_OUT). Čekání na kanálech je součástí gtk_main, takže neblokuje fungování uživatelského rozhraní.

Konfigurace vzhledu widgetů

Styl, jakým se zobrazují widgety, lze konfigurovat pomocí textových resource souborů. V rámci stylu je definován font používaný widgetem, barvy pozadí a popředí, případně pixmapa na pozadí. V resource souboru se dají také přiřazovat signály ke klávesám, a tím nastavit, že stisk určité klávesy vyvolá nějakou funkci v programu. Funkce gtk_init načítá soubory SYSCONFDIR/gtk-2.0/gtkrc(kde SYSCONFDIR je adresář nastavený při instalaci toolkitu) a $HOME/.gtkrc-2.0. Navíc čte ještě soubor specifický pro aktuální locale, např. $HOME/.gtkrc-2.0.cs_CZ. Další resource soubory je možné načíst voláním funkce gtk_rc_parse.

V resource souboru jsou definice stylů a mapování kláves odděleny od jejich přiřazení jednotlivým widgetům. Definice stylu může vypadat takto:

style "my-menu"
{
  font="-*-arial-medium-r-*-*-*-120-*-*-p-*-iso8859-2"
  bg[PRELIGHT] = { 0.0, 0.0, 0.6 }
  fg[PRELIGHT] = { 1.0, 1.0, 1.0 }
}

Každý styl má své jméno. Font se nastavuje buď pomocí font a fontset (X-ová jména fontů), nebo pomocí font_name (jména fontů ve tvaru pro knihovnu Pango). Tato poslední varianta má nejvyšší prioritu. Barvy se definují samostatně pro různé stavy widgetu:

  • NORMAL … normální stav widgetu
  • ACTIVE … aktivovaný widget, např. kliknutím myši
  • PRELIGHT … tlačítko nebo položka menu, na které je kurzor myši
  • SELECTED … vybrané položky v seznamu, označený text v editačním řádku apod.
  • INSENSITIVE … widgety, které mají vypnutou interakci s uživatelem

Pokud chceme umožnit nastavovat styl pro konkrétní widget, měli bychom widget pojmenovat pomocí gtk_widget_set_na­me. Následně mu můžeme v resource souboru přiřadit styl takto:

widget "mywindow.*.GtkMenuItem" style "my-menu"

Jméno widgetu v resource souboru zadáváme ve formě posloupnosti jmen widgetů oddělených tečkou, která začíná v top-level okně a končí ve widgetu, jehož styl nastavujeme. Pokud nemá některý widget v posloupnosti nastavené jméno, používá se jméno jeho třídy (v našem příkladu je to případ GtkMenuItem). V cestě od top-level okna k widgetu můžeme použít wildcardy„*“ (zastupuje libovolnou posloupnost znaků) a „?“ (zastupuje jeden znak).

Místo nastavení konkrétního widgetu lze určit styl pro všechny widgety z nějaké třídy:

widget_class "GtkWindow.*.GtkMenuItem style "my-menu"

Opět se zadává posloupnost jmen začínající top-level oknem, ale tentokrát to jsou jména tříd. Poslední variantou je nastavení stylu pro úplně všechny widgety v aplikaci, které patří do určité třídy nebo do některé z ní odvozené třídy:

class "GtkMenuItem" style "my-menu"

Položky ze všech načtených resource souborů, jež odpovídají určitému widgetu, se skládají dohromady tak, že nejvyšší prioritu mají položky widget, následuje widget_class a nejmenší prioritu má class. Mezi položkami stejného typu mají přednost ty, které byly načteny později.

Komunikace mezi programy

Základním mechanismem komunikace mezi programy v GTK+ jsou výběry (selections). Z pohledu uživatele funguje výběr velice jednoduše. Uživatel myší označí např. blok textu a kliknutím prostředním tlačítkem ho vloží do jiného widgetu. Výběry v GTK+ jsou implementovány pomocí funkcí pro výběry v Xlib. Použitím standardního mechanismu výběrů X je zajištěno, že aplikace GTK+ mohou komunikovat i s X-ovými programy používajícími jiné toolkity nebo jen samotnou knihovnu Xlib. Přenos dat probíhá prostřednictvím X serveru, komunikující aplikace mezi sebou nemusí mít přímé spojení. Některé widgety, jako GtkEntry nebo GtkTextView, umí pracovat s výběry, aniž by se o to musel programátor starat.

Obr. 1 ukazuje fungování selections na straně zdroje i cíle přenosu dat. Pokud má být widget schopen poskytovat data pro výběr, musí každý typ (v terminologii X cíl – target), do něhož je schopen data konvertovat, registrovat funkcí gtk_selection_ad­d_target. Tuto funkci je nutné volat pro každou dvojici výběr, cíl. Obvykle se používá primární výběr GDK_PRIMARY_SE­LECTION. Widgety umí automaticky poskytovat cíl „TARGETS“, což nejsou data, ale seznam všech cílů, které má widget registrovány. Když uživatel označí vybraná data, widget si přivlastní výběr voláním gtk_selection_ow­ner_set. Předchozí vlastník výběru dostane signál „selection_cle­ar_event“. Obvykle na něj reaguje zrušením grafického zvýraznění vybraných dat. Vložení vybraných dat do cílového widgetu je aktivováno akcí uživatele. Proces přenosu dat zahájí funkce gtk_selection_con­vert. Zadává se požadovaný výběr a cíl (typ dat). Vlastník widgetu je o žádosti o data informován signálem „selection_get“. Zareaguje na něj uložením dat v požadovaném formátu pomocí funkcegtk_selec­tion_data_set. Následně cílový widget dostane signál „selection_re­ceived“. Přijatá data dostane jako parametr handleru tohoto signálu. Komunikace je tedy asynchronní. Cílový widget požádá o obsah výběru, ale nečeká na dokončení přenosu dat. Místo toho pokračuje normálně dál ve zpracování událostí a časem dostane data prostřednictvím signálu.

selections
Obr. 1: Komunikace pomocí výběru (selection)

GTK+ poskytuje ještě alternativní metodu manipulace s výběry – clipboard. Příslušné funkce mají prefix gtk_clipboard_. Typy dat (cíle) a jména výběrů jsou řetězce, např. „TARGETS“,„STRING“, „INTEGER“. Aby se nepřenášely potenciálně dlouhé řetězce, používají se místo nich číselné identifikátory – atomy. Program, který chce používat atom, si jej nejprve zaregistruje voláním gdk_atom_intern. Jako parametr zadá jméno atomu – řetězec. Zpět dostane atom – číslo. Atomy jsou platné v rámci jednoho spuštění X serveru. Každé volání gdk_atom_intern se stejným jménem vrátí stejný atom. Pro různá jména dostaneme různé atomy.

Další způsob komunikace mezi programy je drag&drop. Opět používá standardní mechanismy X a funguje i s programy, které nepoužívají toolkit GTK+. Schéma jeho fungování je na obr. 2.

drag drop
Obr. 2: Drag &drop

bitcoin školení listopad 24

Widget, který chce být potenciálním zdrojem nebo cílem operace drag&drop, se musí registrovat pomocí funkcí

gtk_drag_source_set, resp. gtk_drag_dest_set. Jestliže uživatel přetáhne myší ikonu reprezentující nějaká data z jednoho widgetu do jiného, dostane zdrojový widget signál „drag_data_get“. Zareaguje na něj uložením dat pomocí volání gtk_selection_da­ta_set. Cílový widget dostane signál „drag_data_re­ceived“, který má jako jeden z parametrů doručená data. Jestliže byla operace drag&drop typu „move“, dostane zdroj signál „drag_data_delete“ informující o tom, že data dorazila do cíle a na zdrojové straně mohou být smazána.

Autor článku