12. díl - SDL - Obecná práce s událostmi

C++ SDL SDL - Obecná práce s událostmi

Tentokrát se podíváme na události ze širšího úhlu. Řekneme si něco o filozofii událostí, dále se podíváme na frontu událostí a práci s ní. V příštím díle budeme pokračovat a povíme si o tom, jak si můžeme vytvořit vlastní události a jak události filtrovat.

Filozofie událostí v SDL

Filozofie událostí v SDL je trochu rozdílná od událostí, na které jsme zvyklí z ostatních technologií. Pro představu uvedu Windows Forms (WF), Windows Presentation Foundation (WPF) nebo také webový vývoj. Ve zmíněných technologiích fungují události na principu callbacku, který se volá ihned, jakmile se událost registruje. Často se tyto událostí volají odděleně od hlavního proudu programu a můžou běžet i v samostatném vlákně. Hlavním aspektem je zde tedy callback, který se spustí ihned po tom, co se událost zaregistruje.

Z předchozích dílů je patrné, že SDL funguje jinak. SDL pracuje s frontou událostí, která je rozdílová oproti poslednímu volání. Aby to nebylo tak jednoduché, SDL používá fronty dvě. První je systémová a ukládá do ní operační systém. Po zavolání funkce SDL_PumpEvents se události přesunou z první fronty do druhé, která už přísluší SDL. S touto frontou pracujeme v programu. Také je potřeba počítat s tím, že do interní fronty se přidá od každého typu události pouze jedna událost. Bude to rozebráno v dalších odstavcích. Dosud jsme žádné SDL_PumpEvents nepoužili. Jak jsme tedy události z fronty získávali? Funkce SDL_PollEvent, kterou jsme používali do teď, v sobě interně SDL_PumpEvents volá, proto nebylo potřeba volat funkci explicitně.

Než se blíže podíváme na práci s interní frontou, bude potřeba probrat ještě pár věcí. Zmínil jsem rozdílové události. To znamená, že dostaneme informace o tom, co se změnilo od posledního volání SDL_PumpEvents. Uděláme simulaci složitého výpočtu a vlákno na sekundu uspíme (SDL_Delay). I když to vypadá, že okno neodpovídá (což je vlastně pravda), operační systém stále zachytává události, které aplikaci později předá. Když budeme myší pohybovat přes celé okno, událost nás bude informovat o přesunu, ale na aktuální místo. Pohneme-li třeba myší na stranu a poté se vrátíme do stejného místa, bude vyvolána SDL_MOUSEMOTION událost, ale xrel i yrel se budou rovnat nule (jako by se myš nepohnula). Proto se po zavolání SDL_WarpMouseInWindow můžeme téměř s jistotou spolehnout na promazání další události typu SDL_MOUSEMOTION, protože veškerý pohyb myši byl již v předešlé události (tak jak jsme to dělali v minulém díle). Jestliže používáme SDL_PeepEvents, máme jistotu stoprocentní, ale o tom si povíme až v dalších odstavcích.

Zajímavá vlastnost, kdy se po zavolání SDL_PumpEvents vrátí od každého typu události pouze jedna instance, má i své nevýhody. Hlavně to pocítíme u SDL_KEYDOWN události. Jestliže během doby, kdy je vlákno uspané, zmáčkneme 4 různé klávesy. V každém cyklu dostaneme pouze jednu událost typu SDL_KEYDOWN. Pro získání událostí nad všemi čtyřmi klávesami potřebuje program projít hlavní smyčku 4x. Osobně si myslím, že je to chyba a SDL_PumpEvents by mělo vrátit všechny 4 události hned v prvním průchodu. Musíme se smířit s tím, že ne vždy funguje podle našich představ (pokud nechceme SDL sami přepsat a zkompilovat). Opět se to týká pouze SDL_PeepEvents. Při použití SDL_PollEvent se s touto chybou nesetkáme.

Práce s frontou událostí

Protože funkce SDL_PumpEvents je interně volána v SDL_PollEvent i v SDL_WaitEvent, je patrné, že ji budeme volat ve spojení s jinou funkcí. Tato funkce je SDL_PeepEvents. Definice je následující:

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

Jako první parametr předáváme buffer, do kterého se budou události ukládat, popřípadě odkud se budou události brát – SDL_PeepEvents můžeme použít i k přidání událostí do fronty. Druhým parametrem je maximální počet událostí, které chceme z fronty vzít, popřípadě přidat. Parametr action může nabývat tří různých konstant – SDL_ADDEVENT pro přidání událostí nebo SDL_PEEKEVENT pro získání událostí z fronty. Při použití SDL_PEEKEVENT se události z fronty nesmažou, ale zůstávají v ní. Pokud bychom chtěli událost z fronty i odstranit, použijeme konstantu SDL_GETEVENT. minType a maxType je rozpětí událostí, které chceme získat. Pro všechny události můžeme použít konstanty SDL_FIRSTEVENT a SDL_LASTEVENT. Jako u většiny funkcí v SDL, návratová hodnota je při erroru negativní. V obvyklých případech vrací počet událostí, které funkce zpracovala.

S nově nabitými znalostmi se můžeme podívat na část kódu, která bezpečně vymaže SDL_MOUSEMOTION událost po SDL_WarpMouseInWindow. events je v tomhle případě pole typu SDL_Event o velikosti 255 prvků.

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);
      }
}

Nejdříve přesuneme všechny události (pokud ještě nějaké čekají) do interní fronty. Poté myš přesuneme. Výsledek bude ten, že událost pro přesun myši bude skutečně první v externí frontě. Všechny události, které proběhly do přesunu, si uložíme v bufferu. Nyní může následovat nějaká dlouhá operace. Poté z externí fronty získáme opět všechny události, mezi nimiž bude i událost vyvolaná přesunem myši. Protože víme, že je událost uložena jako první, vrátíme kromě ní všechny události zpět do fronty.

Klasické funkce pro práci s kontejnerem

Funkce pro získání událostí jsme již probrali, nyní se zbývá podívat na zbytek funkcí, které jsou typické pro práci s jakýmkoliv kontejnerem. Jeden způsob přidání událostí do fronty jsme již viděli (SDL_PeepEvents). Jestliže máme pouze jednu událost, bude jednodušší použít SDL_PushEvent. Tato funkce přidá jednu událost do fronty a vrací zápornou hodnotu při neúspěchu, nulu jestliže byla událost vyfiltrována a 1 při úspěchu.

Také se někdy hodí frontu vyčistit. Použijeme funkci SDL_FlushEvent pro jeden typ události (nebo ORované hodnoty), popřípadě SDL_FlushEvents pro rozpětí událostí. Stejně fungují i funkce SDL_HasEvent a SDL_HasEvents, které ověřují, zda se nějaká událost právě vyskytuje ve frontě. Zmíněné metody pracují pouze s interní frontou, nemají tedy efekt na frontu externí.

Někdy musíme na událost v programu čekat. K tomu slouží funkce SDL_WaitEvent a SDL_WaitEventTi­meout. První zmíněná čeká na událost neomezeně dlouho (aplikace zamrzne), druhá přijímá jako druhý parametr čas, který má čekat (v milisekundách). Obě funkce fungují podobně jako SDL_PollEvent, ale uživateli se bude zdát, že se program zasekl, proto použití těchto funkcí nedoporučuji.

Zajímavostí je funkce SDL_QuitReques­ted. Nedělá nic jiného, než že se podívá, zda je ve frontě událost typu SDL_QUIT. Můžeme zjednodušit hlavní smyčku a místo proměnné použijeme tuto funkci.

V příštím díle si vytvoříme vlastní událost a vložíme si ji do fronty událostí. Také se blíže podíváme na filtrování událostí. Ukázkový program k této lekci je (z důvodů návaznosti) v další lekci.


 

  Aktivity (1)

Článek pro vás napsal patrik.valkovic
Avatar
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu.

Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!


 


Miniatura
Předchozí článek
SDL - Události myši
Miniatura
Všechny články v sekci
SDL
Miniatura
Následující článek
SDL - Vlastní události a filtrování

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!