13. díl - SDL - Vlastní události a filtrování

C++ SDL SDL - Vlastní události a filtrování

V aplikaci často potřebujeme vlastní události. Může se jednat o základní věci, jako vznik kolize, spuštění určitého zvuku a dále. Všechny tyto věci lze (ale není nutné) řešit přes události. Proto si dnes ukážeme, jak můžeme vytvořit vlastní událost a vložit ji do fronty.

Vytvoření vlastních událostí

K vytvoření vlastní události budeme potřebovat vlastní typ. type je Uint32, to znamená, že může nabývat celkem 0xFFFF = 65535 hodnot. Konstanta SDL_USEREVENT, od které by měly typy vlastních událostí začínat, má hodnotu 0x8000 = 32768. Sami tedy můžeme vytvořit celkem 32767 událostí – máme dostatek prostoru pro vlastní události. Tím bych naši matematickou vložku ukončil a podíváme se na praktickou část.

Nejjednodušší je si pro každou událost vytvořit konstantu v hlavičkovém souboru. Později nebudeme muset řešit kolize, až se počet událostí zvětší nad únosnou míru. Druhou možností je typy událostí generovat za běhu programu pomocí funkce SDL_RegisterE­vents. Jestliže už nebudou k dispozici další volné události, program vrátí (Uint32)-1. Osobně se přikláním ke konstantám v hlavičkových souborech. Bohužel nejsou oba postupy navzájem kompatibilní.

Pro uživatelské události máme k dispozici SDL_UserEvent, ke kterému přistupuje skrz user atribut. Struktura má dva ukazatele typu void, to znamená, že můžeme přenést prakticky libovolná data, respektive ukazatele na ně. Nakonec musíme událost vložit do fronty funkcí SDL_PushEvent nebo SDL_PeepEvents.

Filtrování událostí

Všechny filtrovací funkce pracují s interní frontou - probíhá během volání SDL_PumpEvents. Jak už bylo poznamenáno na začátku, v aplikaci se nedozvíme o tom, že uživatel právě teď něco provádí. Vždy se dozvíme jen to, že něco provedl. Okamžitá zpětná vazba není možná. To je důvod, proč filtrování probíhá až během přenášení událostí z externí fronty do interní.

První možnost je zablokovat všechny události určitého typu. Použijeme k tomu funkci SDL_EventState. V prvním parametru předáme typ události, kterou nechceme v aplikaci získávat. Druhým parametrem je jedna z hodnot SDL_QUERY pro dotázání na aktuální stav, SDL_IGNORE pro ignorování události a SDL_ENABLE pro opětovné získávání události.

Další v řadě jsou funkce SDL_SetEventFil­ter a SDL_GetEventFil­ter. Níže uvádím definice každé funkce.

int YourEventFilter(void* userdata, SDL_Event* event);
void SDL_SetEventFilter(SDL_EventFilter filter, void* userdata);
SDL_bool SDL_GetEventFilter(SDL_EventFilter* filter, void**  userdata);

Při nastavení filtru můžeme druhým parametrem předat libovolný objekt, který dostane filtrovací funkce při jejím zavolání. Jestliže filtrovací funkce vrátí hodnotu 0, událost nebude do interní fronty přidána. Naopak při hodnotě 1 přidána bude. Funkce by měla být skutečně použita pro filtrování. Pokud nás bude zajímat pouze vnitřní stav události, použijeme Event Watch.

Event Watch přidáme funkcí SDL_AddEventWatch a smažeme funkcí SDL_DelEventWat­ch.

int YourEventFilter(void* userdata, SDL_Event* event);
void SDL_AddEventWatch(SDL_EventFilter filter, void* userdata);
void SDL_DelEventWatch(SDL_EventFilter filter, void* userdata);

Vidíme, že callback je stejný jako pro filtr. Na rozdíl od něj, návratová hodnota callbacku je ignorována a jak je podle pojmenování funkcí patrné, callbacků může být několik. Můžeme mít samostatný callback pro každý typ události, ale bude potřeba na začátku každého callbacku ověřit, zda jde o správný typ. Není možné nikde nastavit, pro který typ události je callback určen - je volán pro všechny typy.

Použití filtru a Event Watch není tak snadné, jak by se na první pohled mohlo zdát. Oba callbacky mohou běžet v samostatném vlákně. Problematika vícevláknových aplikací je mimo rozsah tohoto tutoriálu. Zde platí pouze jednoduché pravidlo – data předaná při vytváření callbacku by se neměla v žádné části aplikace měnit. Poté bude vše fungovat, jak má. Callbacky se volají i pro události vložené do fronty funkcí SDL_PushEvent, ale už ne pro funkci SDL_PeepEvents. Při návrhu programu je s tím potřeba počítat.

Poslední funkcí je SDL_FilterEvents. V tomto případě proběhne předaný callback pro každou událost v interní frontě. Jedná se o jednorázovou záležitost na aktuální interní frontě.

Funkce jsou seřazeny tak, jak probíhají za sebou. Jestliže funkce zmíněná výše odfiltruje událost, funkce nebo callback zmíněný pod ní již neproběhne. Nastavíme-li SDL_KEYDOWN state na SDL_IGNORE, Event Watch se nezavolá. Pro přehlednost uvádím graf (první proběhne funkce vlevo).

SDL_EventState -> SDL_ SetEventFilter -> SDL_AddEventWatch -> SDL_FilterEvents

Příklad

Přiložený program obsahuje i prvky z minulé lekce a demonstruje vytvoření vlastní události, filtrování a smazání události po SDL_WarpMouseInWindow. V příštím díle již opustíme události a podíváme se na zbytek věci, které nám ještě chybí. Bude to například abstrakce souborů nebo vícevláknové programování.


 

Stáhnout

Staženo 109x (11.77 MB)

 

  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 - Obecná práce s událostmi
Miniatura
Všechny články v sekci
SDL
Miniatura
Následující článek
SDL - RWops, vlákna a další

 

 

Komentáře

Avatar
MrWarlockX
Člen
Avatar
MrWarlockX:

Mám jen jeden malý dotaz... Jsi si jistý tím číslem 0xFFFF? Já bych řekl, že to bude 0xFFFFFFFF (32-bit integer) :-) ... 0xFFFF je u Uint16 :-)

 
Odpovědět  +1 18.9.2015 8:53
Avatar
patrik.valkovic
Šéfredaktor
Avatar
Odpovídá na MrWarlockX
patrik.valkovic:

Ve skutečnosti je Type typu Uint32, ale prakticky SDL definuje konstantu SDL_LASTEVENT=0xFFFF, se kterou interně pracuje. Například SDL_PeepEvents má parametr minType a maxType. Kdyby měl typ události vyšší číslo, tak jej SDL nestáhne do fronty.
Samozřejmě to jde vyřešit, máme tam zbylé 2 bajty, ale musel bys zasahovat do zdrojových kódů SDL.

Odpovědět  +1 18.9.2015 9:09
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
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.

Zobrazeno 2 zpráv z 2.