Hlavní navigace

SDL: Hry nejen pro Linux (10)

Michal Turek 26. 4. 2005

Seriál se přehoupl do druhé desítky, příště už na počítání přestanou stačit prsty ;-). Ale ještě než se tak stane, probereme si komunikaci aplikace se správcem oken, což v sobě zahrnuje změnu titulku okna, minimalizaci, přepínání do/z fullscreenu a několik dalších věcí. Ke konci bude také přidán lehký úvod do zpracování událostí.

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_SetCap­tion(), 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_Iconify­Window(). 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_Toggle­FullScreen() 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_GrabIn­put()). Pokud je stisknuta jiná klávesa, vypíše se její číslo a jméno. (Zdrojový kód se zvýrazněním syntaxe.)

SDL 10

Download

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.

Našli jste v článku chybu?

26. 4. 2005 16:18

uživatel si přál zůstat v anonymitě
Tak jsem nahodou zabrousil na tento clanek a vyzkousel jsem uvedeny priklad s oknem, ktere jen ceka na klavesy. Prekvapilo me, ze to vytoci procesor na plne obratky. To by slusne vychovany program delat nemel, zvlaste kdyz nedela nic moc nez ceka na klavesu.

Zmenil jsem tedy funkci SDL_PollEvent za SDL_WaitEvent a pak uz jsem byl spokojeny. Podotykam, ze o SDL nic nevim a serial jsem necetl od zacatku. Pouze si myslim, ze bychom nemeli ucit uzivatele delat programy, ktere uzurpuji veskery vykon…

17. 11. 2010 16:55

:-) (neregistrovaný)

Přestanou stačit prsty? Vždyť to bude teprve 11. díl, napočítat můžeme až do 1023. dílu..

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Vitalia.cz: Jak koupit Mikuláše a nenaletět

Jak koupit Mikuláše a nenaletět

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

Vitalia.cz: Vláknina: Rozpustná, nebo nerozpustná?

Vláknina: Rozpustná, nebo nerozpustná?

Podnikatel.cz: Dárky v podnikání. Jak je uplatnit v daních?

Dárky v podnikání. Jak je uplatnit v daních?

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Podnikatel.cz: Víme první výsledky doby odezvy #EET

Víme první výsledky doby odezvy #EET

Lupa.cz: Avast po spojení s AVG propustí 700 lidí

Avast po spojení s AVG propustí 700 lidí

Vitalia.cz: Tesco: Chudá rodina si koupí levné polské kuře

Tesco: Chudá rodina si koupí levné polské kuře

Root.cz: Vypadl Google a rozbilo se toho hodně

Vypadl Google a rozbilo se toho hodně

Vitalia.cz: To není kašel! Správná diagnóza zachrání život

To není kašel! Správná diagnóza zachrání život

Vitalia.cz: Baletky propagují zdravotní superpostel

Baletky propagují zdravotní superpostel

Podnikatel.cz: EET: Totálně nezvládli metodologii projektu

EET: Totálně nezvládli metodologii projektu

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

Podnikatel.cz: Prodává přes internet. Kdy platí zdravotko?

Prodává přes internet. Kdy platí zdravotko?

Vitalia.cz: 9 největších mýtů o mase

9 největších mýtů o mase

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

Podnikatel.cz: Na poslední chvíli šokuje vyjímkami v EET

Na poslední chvíli šokuje vyjímkami v EET

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?