IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

12. diel - SDĽ - Všeobecná práca s udalosťami

Tentoraz sa pozrieme na udalosti zo širšieho uhla. Povieme si niečo o filozofiu udalostí, ďalej sa pozrieme na front udalostí a prácu s ňou. V budúcom dieli budeme pokračovať a povieme si o tom, ako si môžeme vytvoriť vlastné udalosti a ako udalosti filtrovať.

Filozofia udalostí v SDL

Filozofia udalostí v SDL je trochu rozdielna od udalostí, na ktoré sme zvyknutí z ostatných technológií. Pre predstavu uvediem Windows Forms (WF), Windows Presentation Foundation (WPF) alebo tiež webový vývoj. Vo spomínaných technológiách fungujú udalosti na princípe callbacku, ktorý sa volá ihneď, ako sa udalosť registruje. Často sa tieto udalostí volajú oddelene od hlavného prúdu programu a môžu bežať aj v samostatnom vlákne. Hlavným aspektom je tu teda callback, ktorý sa spustí ihneď po tom, čo sa udalosť zaregistruje.

Z predchádzajúcich dielov je zrejmé, že SDL funguje inak. SDL pracuje s frontom udalosťou, ktorá je rozdielová oproti poslednému volanie. Aby to nebolo také jednoduché, SDL používa fronty dve. Prvá je systémová a ukladá do nej operačný systém. Po zavolaní funkcie SDL_PumpEvents sa udalosti presunú z prvej fronty do druhej, ktorá už prislúcha SDL. S touto frontom pracujeme v programe. Tiež je potrebné počítať s tým, že do internej fronty sa pridá od každého typu udalosti iba jedna udalosť. Bude to rozobrané v ďalších odsekoch. Doteraz sme žiadne SDL_PumpEvents nepoužili. Ako sme teda udalosti z frontu získavali? Funkcia SDL_PollEvent, ktorú sme používali do teraz, v sebe interne SDL_PumpEvents volá, preto nebolo potrebné volať funkciu explicitne.

Než sa bližšie pozrieme na prácu s internou frontom, bude potrebné prebrať ešte pár vecí. Spomenul som rozdielové udalosti. To znamená, že dostaneme informácie o tom, čo sa zmenilo od posledného volania SDL_PumpEvents. Urobíme simuláciu zložitého výpočtu a vlákno na sekundu uspí (SDL_Delay). Aj keď to vyzerá, že okno neodpovedá (čo je vlastne pravda), operačný systém stále zachytáva udalosti, ktoré aplikáciu neskôr odovzdá. Keď budeme myšou pohybovať cez celé okno, udalosť nás bude informovať o presune, ale na aktuálnu miesto. Pohneme Ak treba myšou na stranu a potom sa vrátime do rovnakého miesta, bude vyvolaná SDL_MOUSEMOTION udalosť, ale xrel aj yrel sa budú rovnať nule (ako by sa myš nepohla). Preto sa po zavolaní SDL_WarpMouseInWindow môžeme takmer s istotou spoľahnúť na premazanie ďalšie udalosti typu SDL_MOUSEMOTION, pretože všetok pohyb myši bol už v predchádzajúcej udalosti (tak ako sme to robili v minulom diele). Ak používame SDL_PeepEvents, máme istotu stopercentné, ale o tom si povieme až v ďalších odsekoch.

Zaujímavá vlastnosť, kedy sa po zavolaní SDL_PumpEvents vráti z každého typu udalosti len jedna inštancia, má aj svoje nevýhody. Hlavne to pocítime u SDL_KEYDOWN udalosti. Ak počas doby, keď je vlákno uspané, stlačíme 4 rôzne klávesy. V každom cykle dostaneme iba jednu udalosť typu SDL_KEYDOWN. Pre získanie udalostí nad všetkými štyrmi klávesmi potrebuje program prejsť hlavné slučku 4x. Osobne si myslím, že je to chyba a SDL_PumpEvents by malo vrátiť všetky 4 udalosti hneď v prvom prechode. Musíme sa zmieriť s tým, že nie vždy funguje podľa našich predstáv (ak nechceme SDL sami prepísať a skompilovať). Opäť sa to týka iba SDL_PeepEvents. Pri použití SDL_PollEvent sa s touto chybou nestretneme.

Práca s frontom udalosťou

Pretože funkcia SDL_PumpEvents je interne volaná v SDL_PollEvent aj v SDL_WaitEvent, je zrejmé, že ju budeme volať v spojení s inou funkciou. Táto funkcia je SDL_PeepEvents. Definícia je nasledujúci:

int SDL_PeepEvents(SDL_Event* events, int numevents, SDL_eventaction action, Uint32 minType, Uint32 maxType);

Ako prvý parameter odovzdávame buffer, do ktorého sa budú udalosti ukladať, prípadne odkiaľ sa budú udalosti brať - SDL_PeepEvents môžeme použiť ik pridanie udalostí do fronty. Druhým parametrom je maximálny počet udalostí, ktoré chceme z frontu vziať, prípadne pridať. Parameter action môže nadobúdať troch rôznych konštánt - SDL_ADDEVENT pre pridanie udalostí alebo SDL_PEEKEVENT na získanie udalostí z frontu. Pri použití SDL_PEEKEVENT sa udalosti z frontu neodstránia, ale zostávajú v nej. Ak by sme chceli udalosť z frontu aj odstrániť, použijeme konštantu SDL_GETEVENT. minType a maxType je rozpätie udalostí, ktoré chceme získať. Pre všetky udalosti môžeme použiť konštanty SDL_FIRSTEVENT a SDL_LASTEVENT. Ako u väčšiny funkcií v SDL, vrátená hodnota je pri error negatívny. V obvyklých prípadoch vracia počet udalostí, ktoré funkcie spracovala.

S novo nabitými znalosťami sa môžeme pozrieť na časť kódu, ktorá bezpečne vymaže SDL_MOUSEMOTION udalosť po SDL_WarpMouseInWindow. events je v tomto prípade poľa typu SDL_Event o veľkosti 255 prvkov.

while (SDL_PollEvent(events))
{
   if (events[0].type == SDL_KEYDOWN && events[0].key.keysym.scancode == SDL_SCANCODE_X)
   {
      SDL_PumpEvents();
      //Přesun myši
      SDL_WarpMouseInWindow(MainWindow,400,300);
      //Získá zbývající události
      int NumOfOld = SDL_PeepEvents(events, 255, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION);
      SDL_Delay(1000); //simulace dlouho trvající úlohy
      //získání nových událostí
      SDL_PumpEvents();
      int NumOfNew = SDL_PeepEvents(events+NumOfOld,255-NumOfOld,SDL_GETEVENT,SDL_MOUSEMOTION, SDL_MOUSEMOTION);
      //vrácení událostí zpět do fronty
      SDL_PeepEvents(events, NumOfOld, SDL_ADDEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION);
      if (NumOfNew > 1)
         SDL_PeepEvents(events+NumOfOld+1, NumOfNew-1, SDL_ADDEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION);
      }
}

Najskôr presunieme všetky udalosti (ak ešte nejaké čakajú) do internej fronty. Potom myš presunieme. Výsledok bude ten, že udalosť pre presun myši bude skutočne prvý v externej fronte. Všetky udalosti, ktoré prebehli do presunu, si uložíme v bufferu. Teraz môže nasledovať nejaká dlhá operácia. Potom z externej frontu získame opäť všetky udalosti, medzi ktorými bude aj udalosť vyvolaná presunom myši. Pretože vieme, že je udalosť uložená ako prvá, vrátime okrem nej všetky udalosti späť do fronty.

Klasické funkcie pre prácu s kontajnerom

Funkcie pre získanie udalostí sme už prebrali, teraz sa zostáva pozrieť na zvyšok funkcií, ktoré sú typické pre prácu s akýmkoľvek kontajnerom. Jeden spôsob pridanie udalostí do fronty sme už videli (SDL_PeepEvents). Ak máme len jednu udalosť, bude jednoduchšie použiť SDL_PushEvent. Táto funkcia pridá jednu udalosť do fronty a vracia zápornú hodnotu pri neúspechu, nulu ak bola udalosť vyfiltrované a 1 pri úspechu.

Tiež sa niekedy hodia front vyčistiť. Použijeme funkciu SDL_FlushEvent pre jeden typ udalosti (alebo ORované hodnoty), prípadne SDL_FlushEvents pre rozpätie udalostí. Rovnako fungujú aj funkcia SDL_HasEvent a SDL_HasEvents, ktoré overujú, či sa nejaká udalosť práve vyskytuje vo fronte. Spomínané metódy pracujú iba s internou frontom, nemajú teda efekt na front externé.

Niekedy musíme na udalosť v programe čakať. K tomu slúži funkcia SDL_WaitEvent a SDL_WaitEventTi­meout. Prvá spomínaná čaká na udalosť neobmedzene dlho (aplikácia zamrzne), druhá prijíma ako druhý parameter čas, ktorý má čakať (v milisekundách). Obe funkcie fungujú podobne ako SDL_PollEvent, ale užívateľovi sa bude zdať, že sa program zasekol, preto použitie týchto funkcií neodporúčam.

Zaujímavosťou je funkcia SDL_QuitReques­ted. Nerobí nič iné, než že sa pozrie, či je vo fronte udalosť typu SDL_QUIT. Môžeme zjednodušiť hlavné slučku a miesto premenné použijeme túto funkciu.

V budúcom diele si vytvoríme vlastný udalosť a vložíme si ju do fronty udalostí. Tiež sa bližšie pozrieme na filtrovanie udalostí. Ukážkový program k tejto lekcii je (z dôvodov nadväznosti) v ďalšej lekcii.


 

Predchádzajúci článok
SDĽ - Udalosti myši
Všetky články v sekcii
SDĽ
Preskočiť článok
(neodporúčame)
SDĽ - Vlastné udalosti a filtrovanie
Článok pre vás napísal Patrik Valkovič
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity