Programování pro PalmOS počítače krok za krokem (18)

Dnes si povíme víc o tom, jak naše aplikace přijímá zprávy a jak na ně reaguje. Uvedeme příklady standardních funkcí, které by měly být součástí aplikace.

2.32. Smyčka zpracování událostí

Minule jsme psali o tom, jak komunikuje operační systém PalmOS s naší aplikací - metodou zpracování událostí. O každé akci uživatele nebo systému (které říkáme událost) je vytvořena zpráva a tato zpráva je zařazena do fronty zpráv. Ve smyčce zpracování událostí naší aplikace je tato zpráva vyzvednuta. Některé události obsluhuje sám operační systém (například vypnutí Palm Pilota), ostatní události mohou být zpracovány naší aplikací.

V této kapitole budu občas používat termíny událost, zpráva a zpráva o události jako synonyma. Není to přesné: při reakci na jednu událost může operační systém uložit do fronty zpráv několik zpráv. Podobně budu volně zaměňovat termíny fronta zpráv a fronta událostí. Oba tyto termíny mají své opodstatnění a zatím se nedovedu rozhodnout, který je vhodnější.

Zprávy, které zpracovává naše aplikace, je možné rozdělit do dvou skupin. První z nich zahrnuje zprávy určené obecně pro naši aplikaci: příkladem je žádost o změnu zobrazeného formuláře. Druhá skupina zpráv, která je mnohem rozsáhlejší, se týká právě zobrazeného (aktivního) formuláře. Zprávy o akcích uživatele jsou zasílány funkci formuláře a tato funkce v sobě obsahuje naše příkazy pro zpracování těchto zpráv (například po doteku pera na displeji (= událost) nakreslíme značku). Na obrázku z minulé kapitoly, který zobrazuje předávání zprávy operačním systémem, si tento mechanismus osvěžíme:

Na začátku tohoto oddílu si uvedeme příklad funkce, ve které je implementována nejjednodušší smyčka zpracování událostí (která ještě neumí obsluhovat menu) a pak si rozebereme jednotlivé její části. Tato smyčka odpovídá obrázku, který je nahoře. Tato funkce se bude vyskytovat v každé naší aplikaci.

static void ZpracovaniUdalosti()
{
  EventType udalost;

  do
  {
    EvtGetEvent(&udalost, evtWaitForever);

    if(SysHandleEvent(&udalost)) continue;

    if(UdalostZpracovanaAplikaci(&udalost)) continue;

    FrmDispatchEvent(&udalost);
	
  }
  while(udalost.eType != appStopEvent);
}

Základ funkce tvoří smyčka, která se opakuje do té doby, než je aplikaci zaslána událost appStopEvent (žádost systému o to, aby naše aplikace skončila). Ve smyčce nejprve událost vyzvedneme z fronty událostí, nebo - pokud je fronta událostí prázdná - na událost počkáme (funkce EvtGetEvent()). Pak zprávu předáme ke zpracování systému. Pokud funkce SysHandleEvent() vrátí nenulovou hodnotu, systém událost zpracoval a není třeba ji předávat dál.

Pokud systém událost nezpracoval, tak se pokusíme předat podobně událost ke zpracování naší funkci UdalostZpracovanaAplikaci(). Tato funkce bude zpracovávat události (zprávy), které se týkají naší aplikace jako celku. Nejvýznamnější z těchto událostí bude požadavek na inicializaci a zobrazení formuláře. Pokud ani funkce UdalostZpracovanaAplikaci() zprávu nezpracovala, tak událost pomocí funkce FrmDispatchEvent() automaticky předáme funkci aktivního formuláře.

Čekání na událost

Pro vyzvednutí události z fronty slouží funkce EvtGetEvent(). Tato funkce vyzvedne z fronty událostí další událost. Pokud je fronta událostí prázdná (uživatel nic nedělá), tato funkce čeká, dokud uživatel něco neudělá nebo dokud neuplyne nastavený časový interval.

void EvtGetEvent(EventType *event, Int32 timeout)

Čeká na zprávu o události a vyzvedne zprávu události z fronty.

Vstupní parametry
  • timeout - nejdelší časový interval (v počtu tiknutí systémového časovače), po který aplikace vydrží čekat na zprávu. Pokud tento časový interval vyprší, pošle systém aplikaci událost nilEvent. To je vhodné například pro aplikace, které zobrazují na displeji animované obrázky.

Předáme-li jako parametr timeout konstantu evtWaitForever, tato funkce pozastaví činnost aplikace, dokud uživatel něco neudělá nebo nedojde k systémové události.

Výstupní parametry
  • event - ukazatel na strukturu, do které bude po návratu z funkce vyzvednuta z fronty událostí zpráva o události.

V naší nejjednodušší funkci obsahující smyčku zpracování událostí předáváme jako parametr timeout hodnotu evtWaitForever, která způsobí, že naše aplikace vždy počká na nějakou akci uživatele nebo operačního systému (událost) a po převzetí zprávy o této události teprve něco sama udělá. Až zpracuje tuto zprávu, bude znovu čekat atd., dokud jí nebude zaslána událost appStopEvent, která způsobí ukončení programu.

Událost appStopEvent zašle naší aplikaci operační systém PalmOS, jakmile uživatel stiskne hardwarové tlačítko spouštějící jinou aplikaci.

Zpracování událostí operačním systémem

Po vyzvednutí události z fronty událostí ji musíme nejprve předat ke zpracování operačnímu systému (v některých případech můžeme reagovat na události ještě před jejich zpracováním operačním systémem, ale až v některé z následujících kapitol si povíme, proč to dělat). Systém sám se postará o události, které slouží k přepínání mezi aplikacemi, globálnímu vyhledávání, vypnutí Palm Pilota apod.

K nabídnutí události operačnímu systému ke zpracování slouží funkce SysHandleEvent(). Tato funkce zkontroluje, zda je událost zajímavá pro operační systém, a pokud ano, tak ji zpracuje a vrátí nenulovou hodnotu (true).

Funkce SysHandleEvent() také může při zpracovávání události vložit do fronty událostí několik dalších zpráv, které si naše aplikace vyzvedne. Například pokud stiskneme hardwarové tlačítko spouštějící aplikaci MemoPad, systém vytvoří zprávu o stisknutí tohoto tlačítka. Funkce SysHandleEvent() tuto zprávu při zpracování vymaže a místo ní vloží do fronty událostí zprávu požadující ukončení aplikace.

Pokud funkce SysHandleEvent() vrátí nulovou hodnotu (false), znamená to, že systém tato událost nezajímá a mmůžeme ji nabídnout ke zpracování naší aplikaci:

Boolean SysHandleEvent (EventType *event)

Zpracování události operačním systémem.

Vstupní hodnota
  • event - ukazatel na strukturu, do které byla funkcí EvtGetEvent() uložena zpráva o události.
Funkce vrací
  • true - pokud systém událost zpracoval a naše aplikace ji může ignorovat
  • false - pokud systém událost nezpracoval

Zpracování událostí vaší aplikací

V aplikaci je třeba zprávu o události zpracovat na dvou různých místech. Některé zprávy (zejména frmLoadEvent pro výměnu aktivního formuláře) je třeba zpracovat na jednom místě vaší aplikace. K tomu slouží námi definovaná funkce UdalostZpracovanaAplikaci(), která vrací nenulovou hodnotu (true), pokud zprávu o události zpracovala. O způsobu zpracování zpráv v této funkci a o tom, jak se vyměňují formuláře, si řekneme v dalším dílu.

Druhé místo, kde je třeba zpracovat událost, která byla předána vaší aplikaci, jsou funkce jednotlivých formulářů. Pro zaslání události funkci aktivního formuláře slouží funkce FrmDispatchEvent().

Boolean FrmDispatchEvent(EventType *event)

Předání události ke zpracování aktuálnímu formuláři.

Vstupní hodnota
  • event - ukazatel na strukturu, do které byla funkcí EvtGetEvent() uložena zpráva o události.
Funkce vrací
  • true - pokud aktivní formulář událost zpracoval
  • false - pokud aktivní formulář událost nezpracoval

Tato funkce předá zprávu o události funkci aktivního formuláře (která bude popsána v následujícím oddíle) a podle toho, zda zprávu aktivní formulář zpracoval, vrátí true (nenulová hodnota) nebo false (nula).

Další funkce pro zpracování událostí

Pokud si přejeme zařadit do fronty zpráv vlastní zprávu, můžeme to zařídit voláním funkce EvtAddEventToQueue(). Například můžeme takto jednoduše vynutit ukončení aplikace: stačí do fronty zpráv zařadit zprávu appStopEvent.

void EvtAddEventToQueue(const EventType *event)

Přidá do fronty zpráv námi definovanou zprávu.

Vstupní parametry
  • event - ukazatel na strukturu, do které jsme uložili data zprávy

V následující ukázce si ukážeme funkci KonecAplikace(), která uloží do fronty zpráv zprávu appStopEvent a tím způsobí ukončení naší aplikace. K tomuto ukončení dojde při dalším průchodu smyčkou zpracování událostí. O funkci MemSet() jsme ještě nemluvili - slouží k vyplnění bloku paměti jedním byte. Popis jednotlivých součástí struktury EventType bude později - pokud chcete, podívejte se do souboru Core\UI\Event.h.

Další funkce, kterou zde uvedeme - funkce EvtCopyEvent(), slouží ke zkopírování zprávy o události z jedné proměnné typu EventType do druhé.

void EvtCopyEvent(const EventType *source, EventType *dest) 

Zkopíruje zprávu z jedné proměnné do druhé.

Vstupní parametry
  • source - ukazatel na proměnnou, ze které budeme zprávu kopírovat
Výstupní parametry
  • dest - ukazatel na proměnnou, do které budeme zprávu kopírovat

Pozor na nekonečné smyčky

Na celém mechanismu zpracování událostí je nejdůležitější si uvědomit, že aplikace spolu musí spolupracovat a žádná z nich nesmí obsahovat smyčku, ve které by nebyla testována fronta zpráv. Pokud by totiž naše aplikace takovou smyčku obsahovala, nebo pokud bychom netestovali zprávu appStopEvent, tak se nám nepodaří naši aplikaci ukončit jinak než tlačítkem Reset.


Základním prvkem uživatelského rozhraní Palm Pilota jsou formuláře. Příště si uvedeme seznam funkcí, které s formuláři pracují, a několik poznámek k jejich využití.


Témata: MEMS