Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Linux-UNIX-Programmierung von Jürgen Wolf
Das umfassende Handbuch – 2., aktualisierte und erweiterte Auflage 2006
Buch: Linux-UNIX-Programmierung

Linux-UNIX-Programmierung
1216 S., mit CD, 49,90 Euro
Rheinwerk Computing
ISBN 3-89842-749-8
gp Kapitel 16 SDL
  gp 16.1 Was ist und kann SDL?
  gp 16.2 SDL installieren
  gp 16.3 SDL-Anwendungen erstellen
  gp 16.4 SDL initialisieren und Videomodus einstellen
    gp 16.4.1 Programmbeispiel – SDL initialisieren und Videomodus einstellen
  gp 16.5 Direkt auf den Bildschirm zeichnen
    gp 16.5.1 Programmbeispiel – direkt auf den Bildschirm zeichnen
  gp 16.6 Bitmap laden und anzeigen
    gp 16.6.1 Programmbeispiel – Bitmap laden und anzeigen
  gp 16.7 Ein anderes Grafikformat laden und anzeigen
  gp 16.8 Der rechteckige Bildbereich von SDL
  gp 16.9 Farbenschlüssel, Transparenz und Alpha-Blending
    gp 16.9.1 Alpha-Blending
  gp 16.10 Animation
    gp 16.10.1 Programmbeispiel – Animation
  gp 16.11 Eingabe- und Ereignisverarbeitung
    gp 16.11.1 SDL-Event-Struktur
    gp 16.11.2 Maus-Events
    gp 16.11.3 Programmbeispiel – Maus-Event
    gp 16.11.4 Tastatur-Events
    gp 16.11.5 Programmbeispiel – Tastatur-Events
    gp 16.11.6 Joystick-Events
    gp 16.11.7 Programmbeispiel – Joystick-Events
    gp 16.11.8 Weitere Events
  gp 16.12 Audio
    gp 16.12.1 Programmbeispiel – Audio
  gp 16.13 Ausblick


Rheinwerk Computing

16.11 Eingabe- und Ereignisverarbeitung  downtop

Wenn Sie vorhaben sollten, Spiele oder sonstige interaktive Anwendungen zu erstellen, dann benötigen Sie auch eine Verarbeitung der Eingabe wie die Maus, die Tastatur oder den Joystick. Da Sie ja bereits die Möglichkeit hatten oder noch haben, sich in diesem Buch mit der Ereignisverarbeitung unter X oder mit der GTK+-Bibliothek näher vertraut zu machen, dürfte Ihnen das Kapitel relativ einfach fallen. Im Prinzip ist dies wieder nur eine Wiederholung des Themas mit neuen Namen und neuer Syntax. Aber auch ohne diese Vorkenntnisse reißt der Abschnitt keinen mehr vom Hocker.


Rheinwerk Computing

16.11.1 SDL-Event-Struktur  downtop

Am besten arbeiten Sie sich von ganz oben nach unten durch. Ganz oben steht das Event, das bei SDL in einer Struktur namens SDL_Event verpackt wurde. Diese Struktur ist der Anfang der Ereignisverarbeitung bei einem Eintreffen eines Ereignisses, woran alle vorbei müssen, sei dies nun das Neuzeichnen eines Bildschirms, ein Tastendruck, ein Mausereignis oder der Knopfdruck eines Joysticks. Hier die Mutter aller SDL-Events im Überblick:

typedef union {
  Uint8 type;
  SDL_ActiveEvent active;
  SDL_KeyboardEvent key;
  SDL_MouseMotionEvent motion;
  SDL_MouseButtonEvent button;
  SDL_JoyAxisEvent jaxis;
  SDL_JoyBallEvent jball;
  SDL_JoyHatEvent jhat;
  SDL_JoyButtonEvent jbutton;
  SDL_ResizeEvent resize;
  SDL_ExposeEvent expose;
  SDL_QuitEvent quit;
  SDL_UserEvent user;
  SDL_SysWMEvent syswm;
} SDL_Event;

Anhand der Namen lässt sich schon so einiges herauslesen, um was für Events es sich handelt. Denn letztendlich sind hier alle Strukturvariablen, die mit SDL_ beginnen, auch wieder Strukturen, die exakte Daten eines entsprechenden Events beinhalten.

Intern wird dabei für jedes auftretendes Event ein neues SDL_Event erzeugt und in einen FIFO-Puffer (FIFO = First in First out) abgelegt. Dieser Puffer ist nötig, da sonst bei sehr vielen auf einmal auftretenden Events einige verloren gehen könnten – was bei einem Spiel unangenehme Folgen haben könnte.

Bevor Sie Funktionen kennen lernen, die von diesem FIFO Gebrauch machen, um die Events zu lesen, müssen Sie erst einmal wissen, was für ein Event überhaupt aufgetreten ist. Anhand der Struktur SDL_Event können Sie erkennen, dass es davon eine Menge gibt. Was für ein Event letztendlich aufgetreten ist, findet sich in der Strukturvariablen type von SDL_Event. Dabei müssen Sie nur type auswerten und können anschließend eine der entsprechenden Strukturen auslesen. Welchen Wert (symbolische Konstante) type dabei besitzen kann und auf welche Struktur sich dieser Wert dann bezieht, können Sie der folgenden Tabelle entnehmen:


Tabelle 16.3    Event-type und die entsprechenden Strukturen

Event-type Event-Struktur
SDL_ACTIVEEVENT SDL_ActiveEvent
SDL_KEYDOWN SDL_KEYUP SDL_KeyboardEvent
SDL_MOUSEMOTION SDL_MouseMotionEvent
SDL_MOUSEBUTTONDOWN SDL_MOUSEBUTTONUP SDL_MouseButtonEvent
SDL_JOYAXISMOTION SDL_JoyAxisEvent
SDL_JOYBALLMOTION SDL_JoyBallEvent
SDL_JOYHATMOTION SDL_JoyHatEvent
SDL_JOYBUTTONDOWN SDL_JOYBUTTONUP SDL_JoyButtonEvent
SDL_QUIT SDL_QuitEvent
SDL_SYSWMEVENT SDL_SysWMEvent
SDL_VIDEORESIZE SDL_ResizeEvent
SDL_VIDEOEXPOSE SDL_ExposeEvent
SDL_USEREVENT SDL_UserEvent

Bevor Sie jetzt den Typ des Events abfragen können, müssen Sie dieses erst noch vom FIFO abholen. Hierfür gibt es zwei gängige Funktionen, die sich von der Funktionalität erheblich unterscheiden.

int SDL_WaitEvent(SDL_Event *event);
int SDL_PollEvent(SDL_Event *event);

Mit SDL_WaitEvent() wird gewartet, bis sich ein Event im FIFO befindet. Befindet sich ein Event im FIFO, wird es von SDL_WaitEvent() abgeholt und aus dem FIFO entfernt. SDL_WaitEvent() gibt bei Erfolg 1 zurück oder 0, wenn beim Warten auf ein Event ein Fehler aufgetreten ist.

Bei der Funktion SDL_PollEvent() hingegen werden die Events abgefragt. Befindet sich ein Event im FIFO, wird es auch hiermit entfernt, und die Funktion SDL_PollEvent() gibt 1 zurück. Befindet sich kein Event im FIFO, wird 0 zurückgegeben und mit der Programmausführung fortgefahren.


Hinweis   Da die beiden eben erwähnten Funktionen immer das nächste Event aus dem FIFO entfernen, können Sie, falls nötig, auch die Funktion SDL_PeepEvents() und das Flag SDL_PEEKEVENT verwenden, um nur einen Blick auf die Events im FIFO zu werfen, ohne diese zu entfernen. Des Weiteren ist es auch möglich, wie es für einen FIFO typisch ist, eigene Events dem FIFO hinzuzufügen.

Dies können Sie mit der Funktion SDL_PushEvent() erledigen. Beides wird allerdings in diesem Buch nicht benötigt – daher sei hierfür die Dokumentation von SDL zurate gezogen.

Hinweis   Neben den hier erwähnten Funktionen gibt es noch eine ganze Palette anderer, die sich auf die Events beziehen.



Rheinwerk Computing

16.11.2 Maus-Events  downtop

Angefangen wird mit den Events der Maus. Um jetzt ein Event mit der Funktion SDL_PollEvent() oder SDL_WaitEvent()aus dem FIFO zu holen und auszulesen, müssen Sie erst eine leere Event-Struktur erzeugen. Dies geht recht einfach mit einer simplen Deklaration von:

SDL_Event event;

Als Beispiel wird zum Entnehmen eines Events aus dem FIFO die Funktion SDL_PollEvents() in einer Schleife verwendet:

while(SDL_PollEvent(&event)) {
   ...
}

Tritt jetzt ein Event auf, füllt die Funktion SDL_PollEvent() (oder auch die Funktion SDL_WaitEvent()) die SDL_event-Struktur mit Informationen. Welche Art von Informationen dies sind, erfahren Sie mit der Strukturvariablen type von SDL_Event (wurde eben besprochen). Hierzu verwenden Sie am besten (ist so üblich, aber nicht die Regel) eine switch()-Fallunterscheidung:

switch(event.type) {
   case ... : 
   case ... :
   ...
}

Welche Typen (type) von Events hierbei auftreten können, haben Sie bereits in der Tabelle 15.3 sehen können. Da uns in diesem Abschnitt vorerst einmal die Mausereignisse interessieren, wären dies die Event-Typen SDL_MOUSEMOTION, SDL_MOUSEBUTTONDOWN und SDL_MOUSEBUTTONUP. SDL_MOUSEMOTION wird z. B. ausgelöst, wenn eine Bewegung der Maus auf dem Fenster registriert wurde, und die Events SDL_MOUSEBUTTONDOWN oder SDL_MOUSEBUTTONUP werden ausgelöst, wenn ein Maus-Button betätigt bzw. losgelassen wurde.

switch(event.type) {
   case SDL_MOUSEMOTON :  /* Die Maus hat sich bewegt ;-) */
}

Ein Blick auf die Tabelle der type von Events zeigt Ihnen, dass sich SDL_MOUSEMOTION (ganz wichtiges Event für Ego-Shooter) auf die Struktur SDL_MouseMotionEvent bezieht. Wenn Sie sich jetzt die Struktur dazu ansehen, dann wissen Sie, welche Informationen Sie hieraus ermitteln können:

typedef struct {
  Uint8 type;
  Uint8 state;
  Uint16 x, y;
  Sint16 xrel, yrel;
} SDL_MouseMotionEvent;

Die ersten beiden Strukturvariablen haben im Prinzip dieselbe Bedeutung und enthalten den Typen des Ereignisses – in type befindet sich SDL_MOUSEMOTION und in state der Status des Buttons, wenn dieser gerade gedrückt wurde. Die aktuellen Koordinaten finden Sie in x, y, und die relativen Koordinaten zu x, y finden Sie in xrel und yrel.

Natürlich wollen Sie jetzt auch noch wissen, wie Sie an diese Informationen herankommen. Hierzu nochmals ein Ausschnitt der Struktur SDL_Event (schließlich ist die Struktur SDL_MouseMotionEvent ehrenvolles Mitglied):

typedef union {
  Uint8 type;
  SDL_ActiveEvent active;
  SDL_KeyboardEvent key;
  SDL_MouseMotionEvent motion;  /* Danach suchen wir ... */
  SDL_MouseButtonEvent button;
   ...
   ...
  SDL_SywWMEvent syswm;
} SDL_Event;

Mithilfe der Strukturvariablen motion können Sie jetzt auf die einzelnen Mitglieder der Struktur SDL_MouseMotionEvent zugreifen:

switch(event.type) {
      case SDL_MOUSEMOTION:
      printf ("Mouse-Motion: ");
      printf ("Neue Position (x:%d,y:%d). ",
         event.motion.x, event.motion.y);
      printf ("Veränderung zur alten Pos.:(%i,%i)\n",
         event.motion.xrel, event.motion.yrel);
      break;
}

Und ähnlich einfach können Sie die Informationen des type SDL_MOUSEBUTTONDOWN und SDL_MOUSEBUTTONUP abfragen.

switch(event.type) {
   case SDL_MOUSEBUTTONDOWN :  /* Die Maus wurde geklickt */
 }

Das Mitglied der Struktur SDL_Event hierfür ist die Struktur SDL_MouseButtonEvent und folgendermaßen definiert:

typedef struct {
  Uint8 type;
  Uint8 button;
  Uint8 state;
  Uint16 x, y;
} SDL_MouseButtonEvent;

In type finden Sie die Information des Events, ob es sich hierbei um SDL_MOUSEBUTTONDOWN oder SDL_MOUSEBUTTONUP handelt. button enthält den Button, der gedrückt wurde (linker, mittlerer, rechter). Hierfür gibt es auch die symbolischen Konstanten SDL_BUTTON_LEFT, SDL_BUTTON_MIDDLE, SDL_BUTTON_RIGHT. Mit state erfahren Sie, ob die Maus gedrückt oder losgelassen wurde (SDL_PRESSED oder SDL_RELEASED) – was im Endeffekt type entspricht. Und in x und y stehen die x-, y-Koordinaten, wo der Button (vom linken oberen Fenstereck aus) gedrückt wurde. Der Mitgliedsname von SDL_MouseButtonEvent in der Struktur SDL_Event lautet button. Somit können Sie die Informationen zum Klicken eines Buttons folgendermaßen ermitteln:

switch(event.type) {
   case SDL_MOUSEBUTTONDOWN :  /* Die Maus wurde geklickt */
           printf ("[Button (%d) an Position (x:%d,y:%d)]\n",
           event.button.button, event.button.x, event.button.y);
           break;
}

Rheinwerk Computing

16.11.3 Programmbeispiel – Maus-Event  downtop

Genug der Theorie. Im folgenden Beispiel finden Sie einen leeren Weltraum, durch den Sie sich durch Anklicken der Pfeiltasten scrollen können. Anbei werden die Informationen der Maus auch auf der Konsole ausgegeben. Lassen Sie sich von dem vielen Drumherum nicht stören, und befassen Sie sich ausschließlich mit dem Abfangen der Maus-Events in der Funktion eventloop().


Hinweis   Auch wenn Ihnen der Code recht komplex erscheinen mag, in Sachen SDL finden Sie dabei nichts Neues – abgesehen davon, dass der Code ein wenig modularer aufgebaut wurde.


/* sdl7.c */
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* Wie viele Universen sind auf dem Bildschirm? */
#define UNIVERSES 50
struct universum {
  int x, y; /* Koordinaten des Universums */
  int type; /* Welches Bild wird für die Universen verwendet? */
};
struct universum univ[UNIVERSES];
/* Verschiedene Surfaces */
static SDL_Surface *screen;
static SDL_Surface *left, *up, *right, *down, *background;
/* Farbwert Schwarz */
static Uint32 black;
static int y_scrolling = 0, x_scrolling = 0;
static void RandBackground () {
  int i;
  int num_types;
  num_types = background->w / 16;
  for (i = 0; i < UNIVERSES; i++) {
    univ[i].x = rand () & 1023;
    univ[i].y = rand () & 511;
    univ[i].type = rand () % num_types;
  }
}
static void DrawBackground (int xn, int yn) {
  int i;
  for (i = 0; i < UNIVERSES; i++) {
    SDL_Rect src;
    SDL_Rect dest;
    src.x = univ[i].type * 16;
    src.y = 0;
    src.w = 16;
    src.h = 16;
    dest.x = (univ[i].x - xn) & 1023 - 16;
    dest.y = (univ[i].y - yn) & 511 - 16;
    SDL_BlitSurface (background, &src, screen, &dest);
  }
}
static void RS_Blit (SDL_Surface * bitmap, Sint16 x, Sint16 y) {
  SDL_Rect dest, src;
  dest.x = x;
  dest.y = y;
  dest.w = bitmap->w;
  dest.h = bitmap->h;
  src.x = 0;
  src.y = 0;
  src.w = bitmap->w;
  src.h = bitmap->h;
  SDL_BlitSurface (bitmap, &src, screen, &dest);
  SDL_UpdateRects (screen, 1, &dest);
}
static SDL_Surface *load_image (char *file) {
  static SDL_Surface *image, *temp;
  temp = IMG_Load (file);
  image = SDL_DisplayFormat (temp);
  if (image == NULL) {
    printf ("Konnte Grafik nicht laden: %s\n",SDL_GetError ());
    return NULL;
  }
  SDL_FreeSurface (temp);
  return (image);
}
static int eventloop (void) {
  SDL_Event event;
  while (SDL_PollEvent (&event) != 0) {
    switch (event.type) {
    case SDL_MOUSEBUTTONDOWN:
      if ((event.button.x > up->w)
            && (event.button.x < up->w * 2)
            && (event.button.y > 0)
            && (event.button.y < up->w)) {
        y_scrolling -= 12;
        printf ("Pfeil nach oben angeklickt ");
        printf ("[Button (%d) an Position (x:%d,y:%d)]\n",
           event.button.button, event.button.x, event.button.y);
      } 
      else if ((event.button.x > 0)
            && (event.button.x < left->w)
            && (event.button.y > left->h)
            && (event.button.y < left->h * 2)) {
        x_scrolling -= 12;
        printf ("Pfeil nach links angeklickt\n");
        printf ("[Button (%d) an Position (x:%d,y:%d)]\n",
           event.button.button, event.button.x, event.button.y);
      } 
      else if ((event.button.x > down->w)
            && (event.button.x < down->w * 2)
            && (event.button.y > down->h * 2)
            && (event.button.y < down->h * 3)) {
        y_scrolling += 12;
        printf ("Pfeil nach unten angeklickt\n");
        printf ("[Button (%d) an Position (x:%d,y:%d)]\n",
           event.button.button, event.button.x, event.button.y);
      } 
      else if ((event.button.x > right->w * 2)
            && (event.button.x < right->w * 3)
            && (event.button.y > right->h)
            && (event.button.y < right->h * 2)) {
        x_scrolling += 12;
        printf ("Pfeil nach rechts angeklickt\n");
        printf ("[Button (%d) an Position (x:%d,y:%d)]\n",
           event.button.button, event.button.x, event.button.y);
      } 
      else {
        printf ("Maus-Button (%d) gedrückt an Position "
            " (x:%d,y:%d)]\n",
            event.button.button, event.button.x, event.button.y);
      }
      break;
    case SDL_MOUSEMOTION:
      printf ("Mouse-Motion: ");
      printf ("Neue Position (x:%d,y:%d). ",
         event.motion.x, event.motion.y);
      printf ("Veränderung zur alten Pos.:(%i,%i)\n",
         event.motion.xrel, event.motion.yrel);
      break;
    case SDL_QUIT:
      printf ("Quit event. Bye.\n");
      exit (EXIT_SUCCESS);
    }
  }
  return 1;
}
int main (void) {
  int done = 1;
  if (SDL_Init (SDL_INIT_VIDEO) != 0) {
    printf ("Konnte SDL nicht initialisieren: %s\n",
       SDL_GetError ());
    return EXIT_FAILURE;
  }
  atexit (SDL_Quit);
  screen = SDL_SetVideoMode (640, 480, 16,
                             SDL_DOUBLEBUF | SDL_HWSURFACE);
  if (screen == NULL) {
    printf ("Kann Videomodus nicht setzen: %s\n",
       SDL_GetError ());
    return EXIT_FAILURE;
  }
  srand (time (0));
  up = load_image ("up.png");
  right = load_image ("right.png");
  down = load_image ("down.png");
  left = load_image ("left.png");
  background = load_image ("background1.png");
  RandBackground ();
  black = SDL_MapRGB (screen->format, 0, 0, 0);
  SDL_FillRect (screen, 0, black);
  DrawBackground (0, 0);
  RS_Blit (up, up->h, 0);
  RS_Blit (left, 0, left->w);
  RS_Blit (down, down->h, down->w * 2);
  RS_Blit (right, down->h * 2, left->w);
  SDL_UpdateRect (screen, 0, 0, 0, 0);
  while (done) {
    done = eventloop ();
    SDL_FillRect (screen, 0, black);
    DrawBackground (x_scrolling, y_scrolling);
    RS_Blit (up, up->h, 0);
    RS_Blit (left, 0, left->w);
    RS_Blit (down, down->h, down->w * 2);
    RS_Blit (right, down->h * 2, left->w);
    SDL_Flip (screen);
  }
  /* Ressourcen wieder freigeben */
  SDL_FreeSurface (up);
  SDL_FreeSurface (down);
  SDL_FreeSurface (right);
  SDL_FreeSurface (left);
  SDL_FreeSurface (background);
  SDL_FreeSurface (screen);
  return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc `sdl-config --libs` `sdl-config --cflags` -o \
  sdl7 sdl7.c -lSDL_image
$ ./sdl7
...
Mouse-Motion: Neue Position (x:20,y:391). Veränderung zur alten Pos.:(-1,0)
Mouse-Motion: Neue Position (x:19,y:391). Veränderung zur alten Pos.:(-1,0)
Maus-Button (1) gedrückt an Position (x:19,y:391)]
Maus-Button (3) gedrückt an Position (x:19,y:391)]
Maus-Button (1) gedrückt an Position (x:19,y:391)]
Mouse-Motion: Neue Position (x:17,y:391). Veränderung zur alten Pos.:(-2,0)
Mouse-Motion: Neue Position (x:16,y:391). Veränderung zur alten Pos.:(-1,0)
Mouse-Motion: Neue Position (x:15,y:391). Veränderung zur alten Pos.:(-1,0)
Maus-Button (3) gedrückt an Position (x:15,y:391)]
Maus-Button (3) gedrückt an Position (x:15,y:391)]
Mouse-Motion: Neue Position (x:16,y:392). Veränderung zur alten Pos.:(1,1)
...

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 16.5    Mit Mausklick auf den Pfeilen durchs Universum scrollen



Rheinwerk Computing

16.11.4 Tastatur-Events  downtop

Ähnlich wie bei den Maus-Events können Sie auch bei den Events von der Tastatur vorgehen. Ein solches Event ist aufgetreten, wenn in der Strukturvariablen type mit SDL_KEYDOWN oder SDL_KEYUP gesetzt ist. SDL_KEYDOWN steht für das Drücken einer Taste und SDL_KEYUP für das Loslassen.

switch(event.type) {
   case SDL_KEYDOWN :  /* Eine Taste wurde gedrückt */
}

Ist also z. B. das Event SDL_KEYDOWN aufgetreten, finden Sie in der Struktur SDL_Event eine Strukturvariable namens SDL_KeyboardEvent, die folgende Mitglieder beinhaltet:

typedef struct {
  Uint8 type;
  Uint8 state;
  SDL_keysym keysym;
} SDL_KeyboardEvent;

In type befindet sich der Event des Tastaturereignisses (SDL_KEYDOWN oder SDL_KEYUP). state entspricht hierbei auch wieder demselben wie type, worin sich im Grunde dieselben Informationen (SDL_PRESSED oder SDL_RELEASED) befinden. Anders sieht es schon mit der Strukturvariablen keysym vom Typ SDL_Keysym aus – was letztendlich eine weitere Struktur darstellt.

Somit müssen Sie bei den Tastatur-Events noch eine Etage tiefer forschen. In der Struktur SDL_Keysym befinden sich wiederum folgende Mitglieder:

typedef struct {
  Uint8 scancode;
  SDLKey sym;
  SDLMod mod;
  Uint16 unicode;
} SDL_keysym;

Der erste Wert ist der Scancode – was ein Wert von der Hardware ist, den Sie aber im Grunde nicht benötigen. In sym werden die ASCII-Werte des Tastendrucks gespeichert. Wurde zusätzlich eine Modifier-Taste ((Ş), (Alt), (ş_) ...) gedrückt, finden Sie diesen Wert in mod wieder. Mehrere Tastenwerte eines Modifiers können natürlich mit einem bitweisen ODER verknüpft werden. Für einen internationalen Zeichensatz steht Ihnen noch die Variable unicode zur Verfügung.

Die Werte für SDLKey und SDLMod sind in der Headerdatei SDL_Keysym.h definiert. Werte von SDLKey beginnen alle mit dem Präfix SDLK_ und Werte von SDLMod mit dem Präfix KMOD_. Einige Beispiele für den ASCII-Wert eines Tastendrucks finden Sie in der folgenden Tabelle (mehr entnehmen Sie bitte der Headerdatei SDL_Keysym.h):


Tabelle 16.4    Einige Werte für SDLKey aus der Headerdatei SDL_Keysym.h

SDLKey ASCII-Wert Bezeichnung
SDLK_BACKSPACE '\b' backspace
SDLK_TAB '\t' tabulator
SDLK_CLEAR   clear
SDLK_RETURN '\r' return
SDLK_PAUSE   pause
SDLK_ESCAPE '^[' escape
SDLK_SPACE ' ' space
SDLK_EXCLAIM '!' exclaim
SDLK_QUOTEDBL '"' quotedbl
SDLK_0 '0' 0
SDLK_1 '1' 1
SDLK_2 '2' 2
SDLK_a 'a' a
SDLK_b 'b' b
SDLK_c 'c' c
SDLK_UP   up arrow
SDLK_DOWN   down arrow
SDLK_RIGHT   right arrow
SDLK_LEFT   left arrow

Natürlich können Sie z. B. auch den ASCII-Wert der linken (Ctrl)-Taste mit SDLK_LCTRL ermitteln. Um aber bestimmte Modifier-Abfragen (SDLMod) zu machen, können Sie folgende symbolische Konstanten verwenden:


Tabelle 16.5    SDL Modifier und ihre Bedeutung

SDL Modifier Bedeutung
KMOD_NONE keine Modifiers ausgeführt
KMOD_NUM Numlock gedrückt
KMOD_CAPS Capslock gedrückt
KMOD_LCTRL linke Control-Taste gedrückt
KMOD_RCTRL rechte Control-Taste gedrückt
KMOD_RSHIFT rechte Shift-Taste gedrückt
KMOD_LSHIFT linke Shift-Taste gedrückt
KMOD_RALT rechte Alt-Taste gedrückt
KMOD_LALT linke Alt-Taste gedrückt
KMOD_CTRL eine Control-Taste gedrückt
KMOD_SHIFT eine Shift-Taste gedrückt
KMOD_ALT eine Alt-Taste gedrückt

Wollen Sie jetzt wissen, ob die Pfeil-nach-links-Taste gedrückt wurde, können Sie folgendermaßen vorgehen:

SDL_keysym keysym;
switch (event.type) {
case SDL_KEYDOWN:
   keysym = event.key.keysym;
   switch (keysym.sym) {
   case SDLK_LEFT: /* ASCII-Wert für Pfeil-nach-links gelesen */
                      ...
    }
...

Natürlich müssen Sie den Typ SDL_keysym nicht extra deklarieren. Sie können auch über – angefangen von der Struktur SDL_Event, worin der Name von SDL_KeyboardEvent key lautet, worin sich wiederum die Strukturvariable SDL_keysym namens keysym befindet – SDL_keysym auf den ASCII-Wert von SDL_key namens sym zugreifen (ziemlich tief greifend, aber dennoch übersichtlich) – was in der Praxis so aussieht:

switch(event.type) {
    case SDL_KEYDOWN:
        if(event.key.keysym.sym == SDLK_LEFT)
         /* ASCII-Wert für Pfeil-nach-links gelesen */

Bei den Modifiers können Sie ganz ähnlich vorgehen. Wollen Sie z. B. wissen, ob die linke (Ş)-Taste gedrückt wurde, während gleichzeitig eine andere ASCII-Taste gedrückt wurde (bitte nicht verwechseln – bei den Modifiers gilt nicht nur das Drücken einer Modifier-Taste!), gehen Sie folgendermaßen vor:

if(event.key.keysym.mod & KMOD_LSHIFT)
/* die linke Shift-Taste wurde in Kombination mit einer */
/* anderen ASCII-Taste gedrückt                    */

Benötigen Sie den Namen einer Taste, die gedrückt wurde, können Sie die Funktion

char *SDL_GetKeyName(SDLKey key);

verwenden. Diese gibt Ihnen bei Erfolg den SDL-Namen des Tastendrucks wieder.


Rheinwerk Computing

16.11.5 Programmbeispiel – Tastatur-Events  downtop

Zu den Tastatur-Events soll jetzt das Anwendungsbeispiel, das schon bei den Maus-Events verwendet wurde, erweitert werden. Verändert wurde in diesem Beispiel eigentlich nur die Funktion eventloop() – weshalb auch nur diese hier abgedruckt wird. Erweitern Sie einfach diese Funktion vom Beispiel zuvor um die Tastatur-Events (oder verwenden Sie einfach den Code von der Buch-CD, wenn Sie keine Lust zum Tippen haben). Fügen Sie außerdem in der main()-Funktion noch vor der Event-Schleife Folgendes ein:

...
   SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY,
                        SDL_DEFAULT_REPEAT_INTERVAL);
   while (done) {
      done = eventloop ();
      SDL_FillRect (screen, 0, black);
      DrawBackground (x_scrolling, y_scrolling);
      RS_Blit (up, up->h, 0);
      RS_Blit (left, 0, left->w);
      RS_Blit (down, down->h, down->w * 2);
      RS_Blit (right, down->h * 2, left->w);
      SDL_Flip (screen);
   }
...

Damit schalten Sie die Wiederholungsrate der Tastatur ein. Mit dem ersten Parameter geben Sie an, wie lange eine Taste gedrückt werden muss, bevor die Wiederholung des Tastendrucks beginnt. Mit dem zweiten Parameter geben Sie das Intervall an, womit die Wiederholung ausgeführt wird. Beide Angaben erfolgen in Millisekunden. Im Beispiel werden allerdings jeweils Standardwerte verwendet. Mit dieser Funktion sorgen Sie beim Scrollen in einer Richtung dafür, dass beim dauerhaften Drücken der Taste auch weiterhin gescrollt wird und Sie nicht wie wild auf Ihre Pfeiltasten hämmern müssen. Die Tastendrücke, bei denen keine Pfeiltaste gedrückt wurde, werden wieder auf der Konsole ausgegeben.

static int eventloop (void) {
   SDL_Event event;
   while (SDL_PollEvent (&event) != 0) {
      SDL_keysym keysym;
      switch (event.type) {
      case SDL_KEYDOWN:
         keysym = event.key.keysym;
         switch (keysym.sym) {
         case SDLK_LEFT:
            x_scrolling -= 12;
            break;
         case SDLK_RIGHT:
            x_scrolling += 12;
            break;
         case SDLK_UP:
            y_scrolling -= 12;
            break;
         case SDLK_DOWN:
            y_scrolling += 12;
            break;
         default:
            printf ("Taste gedrückt. ");
            printf ("SDL keysym ist %i. ", keysym.sym);
            printf ("(%s) ", SDL_GetKeyName (keysym.sym));
            printf ("linke Umschalttaste %s gedrückt\n",
            (event.key.keysym. mod & KMOD_LSHIFT) ?
            "ist" : "nicht");
            if (keysym.sym == SDLK_ESCAPE) {
               printf ("ESCAPE für Ende gedrückt .\n");
               exit (EXIT_FAILURE);
            }
         }
         break;
      case SDL_MOUSEBUTTONDOWN:
         if ((event.button.x > up->w)
               && (event.button.x < up->w * 2)
               && (event.button.y > 0)
               && (event.button.y < up->w)) {
            y_scrolling -= 12;
            printf ("Pfeil nach oben angeklickt ");
            printf ("[Button (%d) an Position (x:%d,y:%d)]\n",
            event.button.button, event.button.x, event.button.y);
         }
         else if ((event.button.x > 0)
               && (event.button.x < left->w)
               && (event.button.y > left->h)
               && (event.button.y < left->h * 2)) {
            x_scrolling -= 12;
            printf ("Pfeil nach links angeklickt\n");
            printf ("[Button (%d) an Position (x:%d,y:%d)]\n",
            event.button.button, event.button.x, event.button.y);
         }
         else if ((event.button.x > down->w)
               && (event.button.x < down->w * 2)
               && (event.button.y > down->h * 2)
               && (event.button.y < down->h * 3)) {
            y_scrolling += 12;
            printf ("Pfeil nach unten angeklickt\n");
            printf ("[Button (%d) an Position (x:%d,y:%d)]\n",
            event.button.button, event.button.x, event.button.y);
         }
         else if ((event.button.x > right->w * 2)
               && (event.button.x < right->w * 3)
               && (event.button.y > right->h)
               && (event.button.y < right->h * 2)) {
            x_scrolling += 12;
            printf ("Pfeil nach rechts angeklickt\n");
            printf ("[Button (%d) an Position (x:%d,y:%d)]\n",
            event.button.button, event.button.x, event.button.y);
         }
         else {
            printf ("Maus-Button (%d) gedrückt an Position " 
                    " (x:%d,y:%d)]\n",
            event.button.button, event.button.x, event.button.y);
         }
         break;
      case SDL_MOUSEMOTION:
         printf ("Mouse-Motion: ");
         printf ("Neue Position (x:%d,y:%d). ",
            event.motion.x, event.motion.y);
         printf ("Veränderung zur alten Pos.:(%i,%i)\n",
            event.motion.xrel, event.motion.yrel);
         break;
      case SDL_QUIT:
         printf ("Quit event. Bye.\n");
         exit (EXIT_FAILURE);
      }
   }
   return 1;
}

Einen Screenshot kann ich mir hierzu ersparen, da das Bild dasselbe ist wie schon bei der Event-Behandlung der Maus, nur dass Sie sich hier jetzt auch mit der Tastatur durch den Weltraum scrollen können.


Rheinwerk Computing

16.11.6 Joystick-Events  downtop

Joysticks sind bei vielen Spielen nicht wegzudenken. Daher unterstützt SDL auch diese Eingabegeräte – mitsamt den Achsen POV Hats und Trackballs. Um allerdings Joysticks zu verwenden, müssen Sie beim Initialisieren mit SDL_Init() das Flag SDL_INIT_JOYSTICK mit angeben (und natürlich auch entsprechende Kernel-Module laden!).

Joystick-Informationen

Um einen Joystick überhaupt verwenden zu können, müssen Sie diesen erst mit der Funktion SDL_JoystickOpen() öffnen.

SDL_Joystick *SDL_JoystickOpen(int index);

Mit dem index geben Sie die Nummer des Joysticks (beginnend bei 0) an – da es ja hier auch mehrere Joysticks auf dem System geben kann. Bei Erfolg erhalten Sie einen Zeiger auf die SDL_Joystick-Struktur, bei einem Fehler erhalten Sie NULL zurück.

Jetzt können Sie sich einige Informationen zum Joystick auslesen lassen.

const char *SDL_JoystickName(int index);
int SDL_JoystickNumAxes(SDL_Joystick *joystick);
int SDL_JoystickNumBalls(SDL_Joystick *joystick);
int SDL_JoystickNumHats(SDL_Joystick *joystick);
int SDL_JoystickNumButtons(SDL_Joystick *joystick);

Anhand der Namen lässt sich schon erkennen, um was für Informationen es sich hierbei handelt. Einen Zeiger auf den Namen des Joysticks gibt Ihnen die Funktion SDL_JoystickName() unter Angabe des entsprechenden Joystick-Index (erster Joystick = 0; ...) zurück. Die Anzahl der Achsen bekommen Sie mit SDL_JoystickAxes(), die Anzahl der Trackballs mit SDL_JoystickBalls(), die Anzahl der Hats mit SDLJoystickHats() und die Anzahl der Buttons mit SDL_JoystickButtons() zurück. Wobei all diese Funktionen als Parameter einen Zeiger auf die Struktur von SDL_Joystick benötigen, den Sie zuvor mit SDL_JoystickOpen() erhalten haben.

Wenn Sie mit dem Arbeiten (besser dem Spielen) des Joysticks fertig sind, dann sollten Sie diesen mit SDL_JoystickClose() wieder schließen.

void SDL_JoystickClose(SDL_Joystick *joystick);

Hinweis   Bei einem Blick in die Dokumentation dürften Ihnen weitere Joystick-Funktionen auffallen. Mit diesen Funktionen besteht die Möglichkeit, den Joystick auch ohne eine Event-Schleife zu behandeln. Allerdings müssen Sie dabei immer selbst die Funktion SDL_JoystickUpdate() aufrufen, was Ihnen bei einer Event-Behandlung abgenommen wird. In der Praxis wird die Joystick-Behandlung via Events empfohlen.


Joystick-Events

Am besten kann man mit den Eingabedaten eines Joysticks arbeiten, indem man Events dazu verwendet. Bei einem Blick auf die SDL_Event-Struktur finden Sie hierfür gleich mehrere Einträge:

typedef union {
  Uint8 type;
  ... 
  SDL_JoyAxisEvent jaxis;
  SDL_JoyBallEvent jball;
  SDL_JoyHatEvent jhat;
  SDL_JoyButtonEvent jbutton;
  ...
} SDL_Event;

Je nach type der Events, die auftreten können, können Sie das Ereignis des Joysticks auslesen. Da diese Tabelle schon ein paar Seiten zurückliegt, hier nochmals die Auflistung der Joystick-Events:


Tabelle 16.6    Events, die in Verbindung mit einem Joystick stehen

Event-type Event-Struktur Bedeutung
SDL_JOYAXISMOTION SDL_JoyAxisEvent Achse des Joysticks wurde bewegt.
SDL_JOYBALLMOTION SDL_JoyBallEvent Trackball wurde bewegt.
SDL_JOYHATMOTION SDL_JoyHatEvent Hat des Joysticks wurde betätigt.
SDL_JOYBUTTONDOWN SDL_JOYBUTTONUP SDL_JoyButtonEvent Ein Button des Joysticks wurde betätigt bzw. losgelassen.


Hinweis   Um auch Events vom Joystick verarbeiten zu können, muss dieser zuvor mit SDL_JoystickOpen() geöffnet werden!


Wurden z. B. die Achsen (oder auch das Steuerkreuz) des Joysticks betätigt, so tritt das Event SDL_JOYAXISMOTION auf.

switch (event.type) {
     case SDL_JOYAXISMOTION: /* Achse bewegt */
   }

Die jetzt benötigten Informationen finden Sie in der Struktur SDL_JoyAxisEvent, die wie folgt aussieht:

typedef struct {
  Uint8 type;
  Uint8 which;
  Uint8 axis;
  Sint16 value;
} SDL_JoyAxisEvent;

In type finden Sie wie immer das Event, das aufgetreten ist (SDL_JOYAXISMOTION). Auf welchen Joystick sich das Event bezieht, finden Sie in which. Die Nummer des Index der Joystick-Achse befindet sich in axis und der Wert (variiert zwischen –32768 und 32767) in value. Somit können Sie die Informationen aus der Achse folgendermaßen auslesen:

while (SDL_PollEvent (&event)) {
   switch (event.type) {
     case SDL_JOYAXISMOTION:
         printf ("Joystick %d, Achse  %d bewegt nach  %d\n",
         event.jaxis.which, event.jaxis.axis, event.jaxis.value);
   }
}

Ähnlich verläuft dies bei Betätigung eines Buttons des Joysticks (feuert aus allen Rohren), was den Event-Typ SDL_JOYBUTTONUP oder SDL_JOYBUTTONDOWN hervorruft. Dieses Event speichert seine Informationen in der Struktur SDL_JoyButtonEvent.

typedef struct {
  Uint8 type;
  Uint8 which;
  Uint8 button;
  Uint8 state;
} SDL_JoyButtonEvent;

type und state entsprechend wieder im Grunde demselben. Darin befindet sich die Information, was für ein Event aufgetreten ist (gedrückt oder nicht gedrückt). Von welchem Joystick der Button-Druck kam, befindet sich in which, und welcher Button gedrückt wurde, steht in button.

Auf die andern beiden Events SDL_JOYBALLMOTION und SDL_JOYHATMOTION wird nicht mehr eingegangen, da sich der Vorgang immer recht ähnlich abspielt.


Rheinwerk Computing

16.11.7 Programmbeispiel – Joystick-Events  downtop

Das folgende Programmbeispiel zeigt, wie Sie auf einfache Joystick-Events wie die Achse und die Buttons reagieren können. Im Beispiel habe ich ein einfaches Joypad an den USB-Port gestöpselt.

/* sdl9.c */
#include <SDL/SDL.h>
#include <stdlib.h>
#include <stdio.h>
static SDL_Joystick *js;
static void Joystick_Info (void) {
  int num_js, i;
  num_js = SDL_NumJoysticks ();
  printf ("Es wurde(n) %d Joystick(s) auf dem System gefunden\n",
     num_js);
  if (num_js == 0) {
    printf ("Es wurde kein Joystick gefunden\n");
    return;
  }
  /* Informationen zum Joystick */
  for (i = 0; i < num_js; i++) {
    js = SDL_JoystickOpen (i);
    if (js == NULL) {
      printf ("Kann Joystick %d nicht öffnen\n", i);
    } 
    else {
      printf ("Joystick %d\n", i);
      printf ("\tName:       %s\n", SDL_JoystickName(i));
      printf ("\tAxen:       %i\n", SDL_JoystickNumAxes(js));
      printf ("\tTrackballs: %i\n", SDL_JoystickNumBalls(js));
      printf ("\tButtons:   %i\n",SDL_JoystickNumButtons(js));
      SDL_JoystickClose (js);
    }
  }
}
static int eventloop_joystick (void) {
  SDL_Event event;
  while (SDL_PollEvent (&event)) {
    switch (event.type) {
    case SDL_KEYDOWN:
      if (event.key.keysym.sym == SDLK_ESCAPE) {
        printf ("ESCAPE für Ende betätigt\n");
        return 0;
      }
      break;
    case SDL_JOYAXISMOTION:
      printf ("Joystick %d, Achse %d bewegt nach %d\n",
         event.jaxis.which, event.jaxis.axis, event.jaxis.value);
      break;
    case SDL_JOYBUTTONUP:
    case SDL_JOYBUTTONDOWN:
      printf ("Joystick %i Button %i: %i\n",
         event.jbutton.which, event.jbutton.button,
         event.jbutton.state);
      break;
    case SDL_QUIT:
      return 0;
    }
  }
  return 1;
}
int main (void) {
  int done = 1;
  if (SDL_Init (SDL_INIT_JOYSTICK | SDL_INIT_VIDEO) != 0) {
    printf ("Fehler: %s\n", SDL_GetError ());
    return 1;
  }
  atexit (SDL_Quit);
  if (SDL_SetVideoMode (256, 256, 16, 0) == NULL) {
    printf ("Fehler: %s\n", SDL_GetError ());
    return EXIT_FAILURE;
  }
  Joystick_Info ();
  js = SDL_JoystickOpen (0);
  if (js == NULL) {
    printf("Konnte Joystick nicht öffnen:%s\n",SDL_GetError());
  }
  while( done ) {
    done = eventloop_joystick ();
    SDL_Delay(10);
  }
  SDL_JoystickClose (js);
  return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc `sdl-config --libs` `sdl-config --cflags` -o sdl9 sdl9.c 
$ ./sdl9
Es wurde(n) 1 Joystick(s) auf dem System gefunden
Joystick 0
        Name:       0603:6875
        Axen:       2
        Trackballs: 0
        Buttons:    8
Joystick 0 Button 0: 0
Joystick 0 Button 1: 0
Joystick 0 Button 2: 0
Joystick 0 Button 3: 0
Joystick 0 Button 4: 0
Joystick 0 Button 5: 0
Joystick 0 Button 6: 0
Joystick 0 Button 7: 0
Joystick 0, Achse 0 bewegt nach -32767
Joystick 0, Achse 1 bewegt nach -32767
Joystick 0 Button 0: 1
Joystick 0 Button 3: 1
Joystick 0, Achse 1 bewegt nach 0
Joystick 0 Button 0: 0
Joystick 0 Button 3: 0
Joystick 0, Achse 1 bewegt nach 32767
Joystick 0, Achse 1 bewegt nach 0
Joystick 0, Achse 0 bewegt nach 0
Joystick 0 Button 2: 1
Joystick 0, Achse 1 bewegt nach -32767
Joystick 0, Achse 0 bewegt nach 32767
Joystick 0, Achse 0 bewegt nach -32767
Joystick 0, Achse 0 bewegt nach 32767
Joystick 0, Achse 1 bewegt nach 32767
Joystick 0, Achse 0 bewegt nach -32767
...

Rheinwerk Computing

16.11.8 Weitere Events  toptop

Es gibt noch einige weitere Events, worauf hier allerdings nicht mehr eingegangen wird. Wie Sie an entsprechende Informationen herankommen können, haben Sie jetzt bereits einige Male gesehen. Fangen Sie immer mit der Struktur SDL_Event an, und graben Sie sich anschließend durch. In den Beispielen wurde recht häufig der Event-Typ SDL_QUIT verwendet, der, wie ich meine, für sich selbst spricht.

 << zurück
  
  Zum Rheinwerk-Shop
Neuauflage: Linux-UNIX-Programmierung
Neuauflage:
Linux-UNIX-
Programmierung

bestellen
 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: Linux-Server






 Linux-Server


Zum Rheinwerk-Shop: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Rheinwerk-Shop: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Rheinwerk-Shop: Shell-Programmierung






 Shell-
 Programmierung


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
Info





Copyright © Rheinwerk Verlag GmbH 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern