Lekce 10 - SDL - Události klávesnice
V předchozí lekci, SDL - Práce s časovačem, jsme pracovali s časovačem a dále pak na optimalizaci FPS, tak aby byla aplikace co nejplynulejší.
V dnešním díle se podíváme na události, které může vyvolat klávesnice. Nejprve zjistíme, jak se s klávesnicí pracuje ve frontě událostí, a následně jak můžeme kdekoliv v programu zjistit aktuální stav klávesnice.
V úvodu bych měl zmínit, že všechny události jsou v
SDL_Event
uloženy pod svými atributy. Například k
SDL_KeyboardEvent
se dostaneme skrze event.key
.
Podobně k události upravující text (SDL_TextInputEvent
) se
dostaneme skrz event.text
. Stejná pravidla platí pro každou
událost, atribut je vždy uveden v dokumentaci a nebudu jej u každé události
uvádět.
Scancode, Keycode a Keymod
SDL disponujeme dvěma enumerátory, které identifikují klávesu. Prvním
je SDL_Scancode
a druhý SDL_Keycode
. Jejich přehled
můžete vidět zde.
Jsou téměř totožné, ale neplatí to vždy. SDL_Scancode
je
namapován na fyzické rozložení klávesnice, zatímco
SDL_Keycode
na aktuální rozvržení klávesnice určené
operačním systémem. Rozdíl můžeme poznat, když budeme mít dvě
rozvržení klávesnice, jedno QWERTZ a druhé QWERTY. SDL_Scancode
bude mít pro klávesu Y v obou případech stejnou hodnotu, zatímco u
SDL_Keycode
se bude měnit podle toho, které rozvržení bude
aktuálně aktivní (Y/Z). Podle toho bychom také měli zachytávat události.
Například WSAD budeme chtít mít vždy na stejném místě, proto použijeme
SDL_Scancode
. Jestliže má klávesa „I“ otevřít inventář,
měli bychom použít SDL_Keycode
.
S enumerátorem SDL_Keymod
je situace odlišná. Obsahuje hodnoty jako bitové flagy a můžeme hodnoty
binárně ORovat nebo ANDovat. Flagy jsou určeny pro speciální klávesy, jako
je Ctrl, Alt, Shift, ale také NumLock, Tab a další. Pro klávesy, které jsou
duplicitní (například Ctrl je vpravo i vlevo), obsahuje jednotlivé flagy pro
každou z těchto kláves (KMOD_LCTRL
, KMOD_RCTRL
),
ale také ORovanou hodnotu pro obě klávesy (KMOD_CTRL
=
KMOD_LCTRL|KMOD_RCTRL
). Vše je popsáno v dokumentaci.
SDL_KeyboardEvent
SDL_KeyboardEvent je základní struktura, ze které budeme vycházet.
typedef struct SDL_KeyboardEvent { Uint32 type; Uint32 timestamp; Uint32 windowID; Uint8 state; Uint8 repeat; SDL_Keysym keysym; } SDL_KeyboardEvent;
type
může býtSDL_KEYDOWN
neboSDL_KEYUP
pro stisk nebo uvolnění klávesy.timestamp
je čas, ve kterém byla událost vyvolána.windowsID
je ID okna, které událost vyvolalo. Takové okno můžeme získat funkcí SDL_GetWindowFromID.state
nabývá hodnotSDL_PRESSED
aSDL_RELEASED
. Jedná se vlastně o duplicitní informaci, kterou získáme i ztype
.- z
keysym
se dozvíme, o kterou klávesu se jednalo.
Úmyslně jsem vynechal repeat
, protože tento atribut je
komplikovanější. Operační systém Windows při stisku klávesy vygeneruje
událost KEYDOWN
. Jestliže klávesu držíme déle, začne po
určité době opakovaně generovat KEYDOWN
událost, která už
bude mít nastavený atribut repeat
na jinou hodnotu než je 0.
Tím poznáme, že klávesa nebyla stisknuta, ale je držena. Protože je SDL
multiplatformní, vývojáři se rozhodli, že i na jiných systémech než
Windows se bude SDL chovat stejným způsobem a generovat opakovaně
KEYDOWN
událost. Je možné, že se to v dalších verzích SDL
změní, proto se vyplatí kontrolovat dokumentaci.
Strukturu SDL_Keysym jsem už prakticky
popsal. Obsahuje pouze SDL_Keycode
, SDL_Scancode
a
SDL_Keymod
, neměl by s ní být tedy problém.
Aktuální stav klávesnice
Někdy potřebujeme přímo v programu zjistit, která klávesa je stisknuta. Událost nám řekne pouze změnu klávesy (tedy jestli byla právě stisknuta nebo uvolněna). Někdy tedy můžeme vidět v programu konstrukce podobné této.
bool PPressed = false; bool End = false; while (!End) { while (SDL_PollEvent(event)) { if (event->type == SDL_QUIT) End = true; if (event->type == SDL_KEYDOWN && event->key.keysym.scancode == SDL_SCANCODE_P) PPressed = true; if (event->type == SDL_KEYUP && event->key.keysym.scancode == SDL_SCANCODE_P) PPressed = false; } //vykreslení }
Při stisku klávesy nějakým způsobem zaregistrujeme, že byla klávesa stisknuta, a obdobně se zachováme při uvolnění. Podobným konstrukcím se můžeme vyhnout použitím funkce SDL_GetKeyboardState, která nám vrátí pole aktuálního stavu. Indexy pole jsou scancode kláves. Jestliže budeme chtít zjistit, zda je aktuálně stisknuta šipka nahoru, použijeme tuto část programu.
const Uint8 *state = SDL_GetKeyboardState(NULL); if (state[SDL_SCANCODE_UP]) { //šipka nahoru je stisknutá }
Do funkce můžeme podle potřeby vložit ukazatel na int
, ve
kterém nám SDL vrátí délku pole.
Výše zmíněná funkce nebere v potaz klávesy jako Shift, Ctrl a podobné. K tomu slouží funkce SDL_GetModState, která ale na rozdíl od předchozí funkce nevrací pole, ale ORované hodnoty jednotlivých kláves. Pro vykonání kódu při stisknutém Ctrl použijeme tento kód.
SDL_Keymod mod=SDL_GetModState(); if (mod & KMOD_CTRL) { //jedno z Ctrl je stisknuté }
Můžeme i ručně nastavit, které z těchto kláves se budou tvářit jako
stisknuté. Použijeme k tomu funkci SDL_SetModState.
Funkce přijímá jako parametr binárně ORované hodnoty z
SDL_Keysym
. Klávesy, které nastavíme, budou při příštím
volání SDL_GetModState
zahrnuty v návratové hodnotě.
Poslední funkcí, kterou můžeme něco zjistit o aktuální situaci, je
funkce SDL_GetKeyboardFocus.
Vrací SDL_Window
, které je aktuálně aktivní, a přijímá
události vyvolané klávesnicí.
Text
SDL poskytuje i rozhraní pro zachytávání textu. Proč potřebujeme separátní rozhraní pro text, když víme, která klávesa byla stisknuta? Pro text je nepřeberné množství kombinací a klávesnice samotná může mít několik rozvržení. Pokud bychom chtěli každý znak vytvářet ručně, byla by to spousta práce (v současnosti existuje přibližně 110 000 znaků). Toto rozhraní nás od konkrétní implementace odstíní a můžeme pracovat pouze se samotným textem.
Základní funkce jsou SDL_StartTextInput
a SDL_StopTextInput.
Po zavolání první funkce SDL začne generovat události typu
SDL_TEXTINPUT
, popřípadě SDL_TEXTEDITING
. SDL_TextInputEvent
má kromě základních atributů pouze jeden vlastní, a tím je
text
. Ten je ukazatel typu char
a je v něm uložený
řetězec formátu UTF-8 ukončený nulovým znakem. SDL_TextEditingEvent
je o něco rozsáhlejší, proto si jej ukážeme.
typedef struct SDL_TextEditingEvent { Uint32 type; Uint32 timestamp; Uint32 windowID; char text[SDL_TEXTEDITINGEVENT_TEXT_SIZE]; Sint32 start; Sint32 length; } SDL_TextEditingEvent;
Kromě základních atributů obsahuje atribut text
, který
obsahuje nový text. Dále atribut start
(kde editace začíná) a
length
(délka editovaného textu). Taková je alespoň představa.
Z pokusů na Windows jsem zjistil, že SDL_TextEditingEvent
se
generuje z neznámého důvodu pouze při změně okna, ale nepředává
žádné informace. text
, start
i length
jsou 0. Vůbec nebere v potaz backspace, o tom, že bych se chtěl v textu
posouvat pomocí šipek ani nemluvě. Nevím jaká je situace na Linuxu a
popřípadě na dalších systémech, pokud někdo bude zkoušet program spustit
i pod jiným operačním systémem, prosím dejte vědět, jak program
reaguje.
Klávesnice na obrazovce
Protože je SDL multiplatformní, a to včetně platformy Android a iOS,
může nastat situace, kdy nebudeme mít připojenou fyzickou klávesnici. U
tabletů a mobilních telefonů si musíme vystačit se softwarovou
klávesnicí, kterou nám poskytuje operační systém. Funkcí SDL_HasScreenKeyboardSuppor
zjistíme, jestli aktuální platforma podporuje klávesnici na obrazovce.
Druhou funkcí, která se může hodit, je SDL_IsScreenKeyboardShown,
která vrátí SDL_bool
, pokud je klávesnice zobrazena.
Klávesnice by se měla automaticky zobrazit při volání
SDL_StartTextInput
a při SDL_StopTextInput
by se
měla opět skrýt.
Na Windows 8.1 mi funkcionalita opět nefungovala. I když jsem zapnul klávesnici na obrazovce, klávesnice se ne a ne zobrazit. Pravděpodobně se opět jedná o záležitost konkrétní platformy.
Příklad
V dnešním příkladu jsem připravil ukázku, jak zachytávat právě textový vstup. Při stisku klávesy nahoru se vstup aktivuje, při stisku klávesy dolů se text přestane zapisovat.
V příští lekci, SDL - Události myši, se blíže podíváme na myš. Ukážeme si zachytávání událostí jednotlivých tlačítek a pohybu.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkamiStaženo 735x (9.51 MB)