SDL: Die erste SDL-Anwendung

Quelltext der ersten SDL-Anwendung

Bearbeiten
#include <SDL2/SDL.h>

int main(){
	SDL_Event event;
	SDL_Window* window = NULL;
	SDL_Surface* surface = NULL;
	int done = 0;
	atexit(SDL_Quit);
	
	if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) { 
		printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() ); 
		return 1;
	}
	
	window = SDL_CreateWindow(
                                  "SDL Tutorial", 
                                  SDL_WINDOWPOS_UNDEFINED, 
                                  SDL_WINDOWPOS_UNDEFINED, 
                                  640, 480, 
                                  SDL_WINDOW_SHOWN
                 );
	if (window == NULL){
		printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
		return 1;
	}
	
	surface = SDL_GetWindowSurface(window);
	if (surface == NULL){
		printf( "Surface could not be created! SDL_Error: %s\n", SDL_GetError() );
		return 1;
	}
	
	while (done == 0)
	{
		while (SDL_PollEvent(&event))
		{
			switch (event.type)
			{
			case SDL_QUIT:
				done = 1;
				break;
			}
		}
	}

	SDL_DestroyWindow(window);
	return 0;
}

Erläuterungen

Bearbeiten

In den ersten beiden Zeilen werden die benötigten Header eingebunden, SDL.h für die SDL-Funktionen und die stdlib.h für die Funktionen exit und atexit. Eingeleitet wird das Programm durch die main-Funktion. Danach kommt der erste für uns neue Funktionsaufruf, SDL_Init, welcher zur Initialisierung der SDL dient. Als Parameter werden Flags übergeben, um zu bestimmen, welche Subsysteme von SDL zu initialisieren sind. Durch Verbindung mit dem logischen oder |-Operator können mehrere Flags gleichzeitig angegeben werden.

Mögliche Flags sind:

 SDL_INIT_TIMER 	// Initialisiert Timer-Subsystem
 SDL_INIT_AUDIO 	// Initialisiert Audio-Subsystem
 SDL_INIT_VIDEO 	// Initialisiert Video-Subsystem
 SDL_INIT_CDROM 	// Initialisiert CD-Subsystem
 SDL_INIT_JOYSTICK 	// Initialisiert Joystick-Subsystem
 SDL_INIT_EVERYTHING 	// Initialisiert alles oben genannte
 SDL_INIT_NOPARACHUTE 	// Verhindert, dass die SDL falsche Signale auffängt
 SDL_INIT_EVENTTHREAD 	// Lässt den Event-Manager in einem einzelnen Thread laufen

Beispiel:

SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);

Im obenstehenden Code werden wir nur das Video-Subsystem initialisieren, da wir die anderen noch nicht benötigen. Für die ganz pfiffigen unter euch liefert die SDL auch noch die geniale Funktion SDL_InitSubSystem, die z.b dann zum Einsatz kommt, wenn ein Subsystem erst später in der Anwendung benötigt wird oder in der Laufzeit optional initialisiert werden soll. Ein perfektes Beispiel dafür ist das Subsystem Joystick, denn ob man auf diesen zugreifen will, weiß man meist erst während der Laufzeit, z.B. wenn es im Menü ausgewählt wird (zum vorzeitigen Beenden einzelner Subsysteme gibt es auch die SDL_QuitSubSystem Funktion).

Nun kann es aber mal passieren, dass die Initialisierung fehlschlägt und uns die SDL_Init-Funktion das durch -1 als Rückgabewert mitteilt. Wenn dies der Fall ist, lassen wir aus Debug-Gründen eine ausführliche Fehlermeldung in eine Textdatei schreiben. Danach beenden wir das Ganze mit Fehlercode 1.

Wenn jedoch kein Fehler auftrat, sagen wir dem Computer durch die atexit-Funktion, dass er im Falle einer Beendigung des Programmes die Funktion SDL_Quit aufruft, da ja jetzt die SDL initialisiert wurde und somit alles noch von der SDL im Speicher befindliche löscht.

In der nächsten Zeile kommen dann auch schon die ersten unbekannten Datentypen: SDL_Surface und SDL_Window. Der eine steht für eine Oberfläche, der andere für das (Parent)Fenster wobei man sich jetzt schon merken sollte, dass aus Geschwindigkeitsgründen Zeiger verwendet werden (die meisten Funktionen der SDL geben Pointer zurück).

Nun erzeugen wir ein neues Fenster mit der Funktion SDL_CreateWindow(). Diese empfängt als Parameter zuerst den Titel, also der Text der Frame oben angezeigt wird. Anschließend die x und y Position, sowie die Weite/Breite und die Höhe. Außerdem auch noch ein Flag(in diesem Fall das Flag, das anzeigt, dass das Fenster angezeigt werden soll). Wie jeder gute Programmierer sollten wir auch prüfen, ob überhaupt ein Fenster erzeugt wurde: Das geschieht mit einer einfachen If-Abfrage. Im Falle eines Fehlers (das Fenster ist dann 'NULL' - existiert nicht), wird ein Fehler ausgegeben(printf("Window could not be created! SDL_Error: %s\n", SDL_GetError() );). Als nächstes kümmern wir uns um die Kompatibilität zu SDL 1.x.x.: Aus der ersten Version sind viele Funktionen erhalten die ein Surface erwarten, wir jedoch arbeiten mit dem Typen SDL_Window. Mit der einfachen Funktion SDL_GetWindowSurface(), die ein Window-Objekt erwartet und ein SDL_Surface Objekt zurück gibt, erhalten wir die zum Window zugehörige Surface, welche wir dann mit [|SDL:_Bilder_und_Ereignisse|Bildern füllen können(blitten)]]. Auch hier prüfen wir wieder, ob das Erstellen geklappt hat, wenn nicht bekommen wieder eine Fehlerausgabe. Wenn wir das Programm jetzt starten, werden wir nicht viel sehen. Je nach Konfiguration ploppt, bei dem ein oder anderen, das Fenster und die Console auf. Bei den meisten sollte es jedoch nur das Fenster an sich erscheinen (Keine Sorge, wenn bei dir auch die CMD aufgeht, das liegt nur daran, dass du als Subsystem die Konsole gewählt hast. Ist eher sogar ein Vorteil, jetzt kannst du dir auch die Fehlerausgabe in der CMD ansehen :D). Deswegen bauen wir noch eine Schleife, die solange arbeitet, bis wir das Programm/Fenster schließen. Deswegen wurde auch oben das SDL_Event event deklariert. Auch brauchen wir eine Zustandsvariable, welche die äußere Schleife am Leben erhält. Die innere Schleife empfängt das gerade "gemachte" Event (Maus-Klick, Minimieren, Resizen oder eben schließen) und speichert es durch die SDL_PollEvent() Funktion in der via Referenz übergebenen Event Variable. In der nachfolgenden Switch-Case Struktur wird die Art des Events ausgewertet, im Falle des Schließens(SDL_Quit), wird die Zustandsvariable auf einen anderen Wert als '0' gesetzt(1) und die innere Schleife beendet. Da 'done' nun nicht mehr '0' ist, beendet sich auch die äußere Schleife. Anschließend sollte noch mit SDL_DestroyWindow() das Fenster (wenn noch andere Elemente, wie Rendere1, Texture... übrig sind) mit entsprechenden SDL_Destroy Anweisungen zerstört werden.

Wenn alles gutgegangen ist, haben wir jetzt SDL initialisiert und einen Bildschirmmodus gesetzt. Beim Ausführen sollte sich ein Fenster zeigen, welches nicht sofort verschwindet weil wir noch die Funktion SDL_Delay aufrufen. In unserem Fall wird die Ausführung der Anwendung auf genau 3000 Millisekunden verzögert.

Anmerkung: Wer unter Windows Probleme beim Kompilieren hat, sollte Folgendes vor der main-Funktion benutzen:

 #ifdef _WIN32
 #undef main
 #endif

Wer immer noch Fehlermeldungen bekommt, sollte es hiermit versuchen:

 #ifdef _WIN32
 #pragma comment(lib, "SDL.lib")
 #pragma comment(lib, "SDLmain.lib")
 #endif

Seit der Version 2 ist die Memberfunktion SDL_SetVideoMode() nicht mehr vorhanden. Es muss die Funktion SDL_CreateWindow() verwendet werden. Diese empfängt jedoch kein Surface, sondern ein SDL_Window Objekt. Dies kann zu Problem mit SDL_BlitSurface() führen. Jedoch kann mit SDL_GetWindowSurface(), welches ein Window empfängt, wieder ein Surface Objekt gewonnen werden das in die SDL_BlitSurface() passt.

Man kompiliert die Datei unter Linux im Terminal wie folgt:

gcc datei.c -o datei `sdl-config --cflags --libs`

Danach muss das Kompilat noch gestartet werden:

./datei