Správce oken
Knihovna SDL poskytuje několik příkazů, které zajišťují komunikaci mezi aplikací a správcem oken (Window Manager). Samozřejmě není možné komunikovat, neexistuje-li druhá strana – většinou se jedná o běh v textovém režimu, když není spuštěný X server. SDL toho po pravdě nepodporuje mnoho, v podstatě pouze změnu názvu a ikony v titulkovém pruhu, programovou minimalizaci okna a přepnutí do/z fullscreenu. Názvy funkce, které zajišťují tyto činnosti, začínají na předponu SDL_WM_.
Titulkový pruh
Začneme jednoduše, řetězec v titulku okna se změní funkcí SDL_WM_SetCaption(), ostatně tato funkce byla použita snad ve všech ukázkových příkladech, takže by se nemělo jednat o nic nového.
void SDL_WM_SetCaption(const char *title, const char *icon);
void SDL_WM_GetCaption(char **title, char **icon);
První parametr je jasný, specifikuje se jím řetězec v titulku. Existenci druhého jsem však nikdy nepochopil. SDL dokumentace ho popisuje jako „jméno ikony“ a ani hlavičkový soubor ani zdrojové kódy více informací bohužel neposkytují. Co si pod ním představit tedy opravdu netuším. Možná se jedná o „textovou ikonu“ pro správce oken, které grafické neumožňují, ale toto je pouze má neověřená spekulace. Každopádně pokud se předá NULL, nic se nezkazí.
Ikona aplikace se nastavuje funkcí SDL_WM_SetIcon(), která by měla být volána před SDL_SetVideoMode(). Co se týká rozměrů, jsou doporučovány klasické 32×32 pixelů velké ikony, neměly by s nimi být žádné problémy.
void SDL_WM_SetIcon(SDL_Surface *icon, Uint8 *mask);
První parametr představuje surface s ikonou a druhý je bitovou maskou pro průhledné části. Je-li předáno NULL, použije se klíčová barva surface, a pokud ani ta není specifikována, bude ikona neprůhledná.
Bity masky nastavené do jedničky specifikují zobrazované a nuly naopak průhledné pixely, řádky jdou odshora dolů a každý z nich se skládá z (šířka/8) bytů, zaokrouhleno nahoru. Nejvýznamnější bit každého bytu reprezentuje nejlevější pixel.
Typický příklad nastavení ikony okna, která nepoužívá průhlednost, může vypadat například takto:
// Před SDL_SetVideoMode()
SDL_Surface *icon = SDL_LoadBMP("./icon.bmp");
if(icon != NULL)
{
SDL_WM_SetIcon(icon, NULL);
SDL_FreeSurface(icon);
}
Minimalizace okna
Okno se dá programem minimalizovat voláním funkce SDL_WM_IconifyWindow(). Vrácená nula značí, že minimalizace buď není podporována, nebo ji správce oken odmítl provést. V případě, že se vše uskutečnilo v pořádku, obdrží aplikace zprávu SDL_APPACTIVE s parametrem označujícím ztrátu fokusu.
int SDL_WM_IconifyWindow(void);
Přepnutí do/z fullscreenu
Pro přepnutí mezi oknem a fullscreenem stačí jediný řádek kódu. Tedy, abychom byli přesní, stačil by, pokud by nebyla funkce SDL_WM_ToggleFullScreen() podporována pouze v X11, v BeOSu je zatím pouze experimentálně.
int SDL_WM_ToggleFullScreen(SDL_Surface *surface);
Funkce vrací při úspěchu jedničku, jinak nulu, po jejím zavolání by se obsah okna neměl změnit. Pokud surface okna nevyžaduje zamykání při přístupu k pixelům, bude ukazatel obsahovat stejnou adresu paměti jako před voláním.
Jak už bylo zmíněno, pokud program neběží pod X11, ale například v MS Windows, přepnutí mezi oknem a celoobrazovkovým režimem nelze provést. Nicméně…, jak ukazuje demonstrační příklad níže, není problém okno zrušit a následně ho znovu vytvořit s negovaným parametrem režimu.
#define WIN_WIDTH 640
#define WIN_HEIGHT 480
#define WIN_BPP 0
// Globální proměnné
SDL_Surface *g_screen;
Uint32 g_win_flags = SDL_RESIZABLE|SDL_FULLSCREEN;
// Přepíná mezi režimy okno/fullscreen
bool ToggleFullscreen()
{
if(g_win_flags & SDL_FULLSCREEN)// Z fullscreenu do okna
g_win_flags &= ~SDL_FULLSCREEN;
else// Z okna do fullscreenu
g_win_flags |= SDL_FULLSCREEN;
// Pokus o přepnutí, podporováno pouze v x11
if(SDL_WM_ToggleFullScreen(g_screen) == 0)
{
fprintf(stderr, "Unable to toggle fullscreen,"
"trying to recreate window\n");
SDL_FreeSurface(g_screen);
g_screen = SDL_SetVideoMode(WIN_WIDTH, WIN_HEIGHT,
WIN_BPP, g_win_flags);
if(g_screen == NULL)
{
fprintf(stderr, "Unable to recreate window: %s\n",
SDL_GetError());
return false;// Ukončí program
}
#ifdef OPENGL_APLIKACE
// Reinicializace OpenGL (parametry, textury...),
// starý kontext už není dostupný
if(!InitGL())
{
fprintf(stderr, "Unable to reinitialize OpenGL\n");
return false;// Ukončí program
}
ResizeGLWindow();// Nastaví perspektivu
#endif
Draw();// Překreslí scénu
}
return true;// OK
}
Tato a jí podobné funkce se většinou volají v reakci na stisk nějaké klávesy, a protože se v tomto článku začínáme zabývat událostmi, příklad volání lze nalézt níže…
Způsob grabování vstupů
Následující funkce umožňuje nastavit způsob grabování vstupů klávesnice a myši.
SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode);
V případě, že je nastaveno SDL_GRAB_OFF (implicitní nastavení), budou se zprávy předávat oknu pouze tehdy, pokud je aktivní (má fokus). Naopak je-li ve stavu SDL_GRAB_ON, myš nemůže opustit klientskou oblast okna a všechny vstupy klávesnice jsou předávány přímo oknu, čili nejsou interpretovány okenním manažerem. Poslední možný parametr SDL_GRAB_QUERY slouží k dotazu na aktuální stav.
Správa událostí
Komunikace mezi operačním systémem a SDL aplikací je vystavěna na tzv. událostním modelu. Vždy, když v systému nastane nějaká událost, například uživatel stiskne klávesu nebo pohne myší, generuje operační systém objekt dané události, nastaví jeho parametry (stisknutá klávesa, nová pozice myši) a předá ho aplikaci. Někdy se také říká, že operační systém poslal aplikaci zprávu o události. Ta na ni může zareagovat naprosto libovolným způsobem, včetně její ignorace.
Povídání o událostech začneme praktickým příkladem jejich zpracování. V tuto chvíli nemusíte pochopit naprosto všechny detaily, pokud však vstřebáte základní principy, máte z 95 procent vyhráno, dále už se bude jednat jen o nabalování speciálních znalostí. No a pokud následující příklad nepochopíte, zkuste to ještě jednou ;), od této chvíle se bez těchto věcí neobejdete.
Bývá dobrým zvykem vložit veškerou práci s událostmi do specializované funkce. Definujeme, že vrácené false z ProcessEvent() říká hlavní smyčce programu, že je z nějakého důvodu nutné ukončit aplikaci. V tomto případě chce uživatel buď ukončit program, nebo stisknul klávesu Escape, nebo se nezdařilo přepnutí mezi oknem a fullscreenem.
Uvnitř funkce deklarujeme proměnnou typu SDL_Event, kterou budeme v cyklu naplňovat událostmi čekajícími ve frontě. Pokud je fronta prázdná, cyklus, a tedy i celá funkce se ukončí a řízení je předáno hlavní smyčce programu.
bool ProcessEvent()
{
SDL_Event event;// Objekt události
while(SDL_PollEvent(&event))
{
Rozvětvíme kód podle typu události, a pokud se jedná o klávesnici, zanoříme se, v závislosti na typu klávesy, ještě více do hloubky. Ošetřen je pouze Escape ukončující aplikaci a klásesa F1, která způsobí přepnutí do/z fullscreenu.
Mimochodem, názvy kláves lze najít v SDL dokumentaci téměř dole pod nadpisem „SDL Keysym definitions“ nebo v hlavičkovém souboru SDL_keysym.h.
switch(event.type)
{
// Klávesnice
case SDL_KEYDOWN:
switch(event.key.keysym.sym)
{
case SDLK_ESCAPE:
return false;
break;
case SDLK_F1:
if(!ToggleFullscreen())
return false;
break;
default:
break;
}
break;
V každé aplikaci by měl být ošetřen SDL_QUIT, tato událost nastane, když má být program ukončen. Uživatel například klikl na křížek v pravém horním rohu okna, stiskl ALT+F4, klikl pravým tlačítkem myši v hlavním panelu na aplikaci a zvolil Zavřít atd. Zareagujeme, jak se očekává, skončíme.
// Požadavek na ukončení
case SDL_QUIT:
return false;
break;
Pro zachování jednoduchosti tento ukázkový kód ostatní události ignoruje.
// Ostatní se ignorují
default:
break;
}
}
return true;
}
Ukázkové programy
Úvod do událostí
Program tentokrát nic nevykresluje, na začátku je nastavena ikona a titulek okna a poté se hlídají události klávesnice. Pokud je stisknut ESC, aplikace se ukončí, v reakci na F1 se okno přepne do celoobrazovkého režimu nebo zpět, M okno minimalizuje a G změní způsob grabování vstupů (SDL_WM_GrabInput()). Pokud je stisknuta jiná klávesa, vypíše se její číslo a jméno. (Zdrojový kód se zvýrazněním syntaxe.)
Download
- Příklad: Úvod do událostí
- Offline verze článku včetně všech příloh
- Zdrojový kód se zvýrazněním syntaxe
Pokračování
Příště se budeme především věnovat operacím s frontou událostí, naučíme se, jak z ní grabovat zprávy, a naopak, jak je do ní vkládat.