První aplikace
Naším cílem bude vytvoření jednoduchého okna vyplněného černou barvou s titulkem „Ahoj, svete!“ a s logem knihovny uprostřed. Okno se zavře po stisknutí klávesy escape.
Aplikace se bude skládat z jednoho hlavičkového souboru Ahoj.h a jednoho zdrojového souboru Ahoj.cpp.
Hlavičkový soubor bude vypadat následovně:
#ifndef AHOJ_H #define AHOJ_H // hlavickove souhory ClanLibu #include <ClanLib/core.h> #include <ClanLib/application.h> #include <ClanLib/gl.h> #include <ClanLib/display.h> /************************************************ * T_AhojSveteApp * ************************************************/ // Potomek CL_ClanApplication nutny pro fungovani // knihovny ClanLib. Bude nutne vytvorit jednu // globalni instanci teto tridy!!! Nikde v programu // nebudeme deklarovat klasickou funkci main() // nebo WinMain() jak jsme byli dosud zvykli!!! class T_AhojSveteApp : public CL_ClanApplication { // verejne metody: public: // Zastupuje klasickou funkci main. Prebira // parametry z prikazoveho radku. virtual int main(int argc, char** argv); }; // T_AhojSveteApp **************************** #endif
V hlavičkovém souboru nejprve includujeme jednotlivé části knihovny, které budeme potřebovat pro náš program (core.h, application.h, gl.h a display.h). Dále následuje hlavička třídy T_AhojSveteApp, odvozené od CL_ClanApplication, s jedinou virtuální metodou main().
Jelikož existuje nejednotnost v pojmenování první spuštěné funkce (např. Main(), WinMain()) a jelikož ClanLib je multiplatformová knihovna, rozhodli se autoři řešit tento problém tím, že první funkci zapouzdří přímo do knihovny.
Pro nás to znamená prostě ten příjemný fakt, že naše virtuální metoda main() se bude na všech platformách chovat jako první funkce programu. Proto bude nutné vytvořit právě jednu globální instanci naší třídy T_AhojSveteApp a nikde nepsat klasickou funkci main() (příp. WinMain() a podobně).
Podívejme se tedy na vlastní implementaci v souboru Ahoj.cpp:
#include "Ahoj.h"
#include <iostream>
#include <string>
// nezbytna globalni instance teto tridy
T_AhojSveteApp AhojSveteApp;
//-----------------------------------------------
// main()
//-----------------------------------------------
int T_AhojSveteApp::main(int argc, char** argv) {
using namespace std;
// vytvorime okno konzole kvuli textovemu vystupu
// jako jsou zpravy o chybach
CL_ConsoleWindow Console("Konzole");
// presmerujeme na konsoli standardni vstup a vystup
Console.redirect_stdio();
try {
// nutna inicializace pouzivanych casti knihovny
CL_SetupCore::init();
CL_SetupDisplay::init();
CL_SetupGL::init();
// vytvorime okno 640 x 480 pixelu s titulkem Ahoj, Svete!
const string Titulek = "Ahoj, Svete!";
const int SirkaOkna = 640;
const int VyskaOkna = 480;
const bool FullScreen = false;
CL_DisplayWindow NaseOkno(Titulek, SirkaOkna, VyskaOkna, FullScreen);
// vytvorime obrazek (logo knihovny ClanLib)
const string CestaKObrazku = "clanlib.png";
CL_Surface Logo(CestaKObrazku);
// souradnice Loga
const int LogoX = 170;
const int LogoY = 180;
// pockame si na stisteni klavesy escape
while (! CL_Keyboard::get_keycode(CL_KEY_ESCAPE)) {
// invariant: Dosud nebyl stisten escape pri get_keycode()
// vyplnime okno cernou barvou
CL_Display::clear(CL_Color::black);
// vykreslime logo, souradnice udavaji polohu
// leveho horniho rohu
Logo.draw(LogoX, LogoY);
// zobrazime nakreslene zmeny (prehozeni
// predniho a zadniho bufferu)
CL_Display::flip();
// uspime aplikaci na deset milisekund,
// abychom zbytecne neblokovali procesor
const int DobaObnoveni = 10;
CL_System::sleep(DobaObnoveni);
// probudime aplikaci resp. knihovnu aby
// byla aktualni
CL_System::keep_alive();
} // while
// deinicializujeme drive inicializovane casti knihovny
CL_SetupGL::deinit();
CL_SetupDisplay::deinit();
CL_SetupCore::deinit();
} // try
catch (CL_Error Chyba) {
cout << "Zachycena chyba: " << Chyba.message.c_str() << endl;
// zavreme okno konzole a pockame na stisk klavesy
Console.display_close_message();
// ukoncime aplikaci, jedna signalizuje neuspech
return 1;
} // catch
// ukoncime nasi aplikaci, nula signalizuje uspech
return 0;
} // main() -------------------------------------
V první řadě vytvoříme z výše popsaných důvodů jedinou instanci třídy T_AhojSveteApp.
Následuje implementace metody main(). Hned na začátku vidíme jeden z typických a velice užitečných obratů při programování s ClanLibem. Vytvoříme totiž nejprve textovou konzoli a přesměrujeme na ni standardní vstup a výstup. Využijeme ji k zobrazování informací o chybách a případných ladících výpisů.
ClanLib definuje třídu výjimek CL_Error, které vyvolává v případě chybových stavů. Je tedy velice vhodné zbytek kódu umístit do chráněného bloku try{}, v bloku catch{} odchytávat tyto ClanLibovské výjimky a vypisovat jejich hlášení na konzoli, případně se rozhodovat, co dál. V našem příkladu vypíšeme na konzoli ještě ukončovací zprávu a program ukončíme.
Podívejme se tedy dovnitř chráněného bloku. Nejprve inicializujeme použité části knihovny. Syntaxe inicializace a deinicializace je jednotná:
CL_SetupXXX::init(); CL_SetupXXX::deinit();
Zde XXX zastupuje jméno inicializované (deinicializované) části knihovny. Deinicializaci provádíme na konci chráněného bloku tj. na konci programu.
Po inicializaci vytvoříme hlavní okno s příslušnými parametry v konstruktoru:
CL_DisplayWindow NaseOkno(Titulek, SirkaOkna, VyskaOkna, FullScreen);
Nyní vytvoříme obrázek loga:
CL_Surface Logo(CestaKObrazku);
Poznamenáme si také souřadnice tohoto obrázku. K podporovaným formátům patří .tga, .png, .pcx, .jpg.
Následuje while smyčka, která končí v momentě, kdy stiskneme klávesu escape. Přesně řečeno příkaz
CL_Keyboard::get_keycode(CL_KEY_ESCAPE)
vrací true, pokud je v momentě jeho provedení stisknut escape. Zanedbáváme tedy jisté riziko, že se někomu podaří escape stisknout na dobu kratsí než deset milisekund a program se neukončí. S tím se však myslím dokážeme nyní smířit :-).
Uvnitř while cyklu nejprve vyplníme okno černou barvou. Ekvivalentně bychom mohli použít příkaz:
// vyplnime okno barvou rgba CL_Display::clear(CL_Color(0, 0, 0, 0));
Dále vykreslíme obrázek Loga:
Logo.draw(LogoX, LogoY);
Přitom LogoX, LogoY budou souřadnice levého horního rohu.
Jelikož ClanLib používá double buffering, projeví se naše kreslení až po příkazu:
CL_Display::flip();
Double buffering je zjednodušeně řečeno metoda, při níž kreslíme do zadního bufferu, zatímco přední buffer se vykresluje na obrazovku. Tím je umožněna lepší synchronizace s monitorem a rychlejší vykreslování.
Nakonec naši aplikaci na chvilinku, tj. asi na 10 milisekund, uspíme, abychom neblokovali ostatní programy, a poté ji opět probudíme příkazy:
CL_System::sleep(DobaObnoveni); CL_System::keep_alive();
Nastavení KDevelop
V KDevelop 2.1.3 můžeme postupovat následovně. Nejprve vytvoříme nový Terminal C++ projekt (Project → New → Terminal C++ ->… zadáme jméno projektu a další údaje, pokud chceme … vypneme automatické generování generate sources and headers" → Create → Exit).
Nyní v menu Project Options (Project → Options) v záložce Configure-Settings zatrhneme use exception handling. Knihovna používá výjimky a nám se budou hodit.
Ve stejnem menu v záložce Compiler Options ve Flags and Warnings napíšeme do textového pole C Preprocessor flags (CPPFLAGS) následující řetězec:
`pkg-config --cflags clanCore-0.7 clanDisplay-0.7 clanApp-0.7 clanGL-0.7`
Podobně v Linker Flags napíšeme do textového pole:
`pkg-config --libs clanCore-0.7 clanDisplay-0.7 clanApp-0.7 clanGL-0.7`
Zde máme tedy jeden ze slibovaných příkladů využití Pkg-configu popsaného v minulém dílu.
Nyní vytvoříme nový hlavičkový soubor Ahoj.h (File → New ->… OK). Podobně vytvoříme nový zdrojový soubor Ahoj.cpp. Tyto soubory budou obsahovat výše uvedený kód.
Pokud jsme již soubory Ahoj.h a Ahoj.cpp vytvořili dříve, můžeme je rovnou vložit do projektu (Project → Add existing file(s) → … nalistujeme vytvořené soubory a potvrdíme – je možné vybrat několik souborů najednou). Tento postup se nám může hodit například při portování rozsáhlejší aplikace z Windows.
Nyní už nic nebrání tomu, abychom náš program zkompilovali a spustili. V menu Build provedeme postupně Autoconf and Automake,Configure…, Compile File a Execute.
Nastavení Visual Studia 6.0
Nejprve vytvoříme nový projekt (New->Projects->Win32 Application) pojmenovaný třeba „Ahoj“.
V menu Project Settings (Project → Settings) v záložce C/C++ vybereme kategorii (Category:) Code Generation a ujistíme se, že pod Use run-time library je zadáno Debug Multithreaded v případě debug verze, neboMultithreaded v případě release verze.
Pokud máme dobře nastavené cesty k hlavičkovým souborům a knihovnám ClanLibu, jak bylo popsáno v minulém dílu (Tools → Options → Directories), stačí již jen vytvořit příslušný hlavičkový a zdrojový soubor s obsahem popsaným výše a program zkompilovat.
Ručně psaný makefile
Nakonec se ještě podíváme, jak by mohl vypadat ručně napsaný makefile našeho programu:
PACKAGES = clanCore-0.7 clanDisplay-0.7 clanApp-0.7 clanGL-0.7
CPPFLAGS = `pkg-config --cflags $(PACKAGES)`
LIBS = `pkg-config --libs $(PACKAGES)`
OBJS = Ahoj.o
GCC = g++
all: $(OBJS)
$(GCC) $(CPPFLAGS) -o Ahoj $(OBJS) $(LIBS)
ahoj.o: Ahoj.cpp Ahoj.h
$(GCC) $(CPPFLAGS) -c Ahoj.cpp $(LIBS)
clean:
-rm -rf *.o
-rm Ahoj
Závěrem
Na konci tohoto dílu bych ještě rád podotkl, že je na světě ClanLib verze 0.7.8, a také, že vývojáři ClanLibu začali intenzivně pracovat na podpoře MacOS.
Zdrojové kódy zahrnující i KDevelopí project file dnes vytvořeného programu si můžete stáhnout zde. Logo knihovny použité v programu je zde.
V příštím dílu se podíváme na to, jak se v ClanLibu pracuje se soubory pro definování zdrojů, což je můj překlad resource definition files. Ty nám umožní velmi pohodlně psát programy konfigurovatelné zvenčí, tj. bez nutnosti kompilovat program kvůli změně nějakého konfiguračního údaje.