La gestion des évènements |
|
|
|
Sans interactivité vos applications deviendraient vraiment morne et n'attireraient certainements pas beaucoup de publics ! Alors plongeons nous dans cette partie si interressant mais aussi assez complexe que sont les évènements. |
|
Principes généraux |
|
|
|
Plus haut j'ai dit que la gestion des évènements était souvent complexe, je voudrais indiquer que ce n'est pas SDL qui possède une mauvaise gestion des évènements, en fait SDL rend la choses très simple. Mais comme dans tout programme, il y a les fonctions préécrites et ce que vous en faites. Je m'explique, SDL vous fournira un ensemble des structures et de fonctions très pratiques mais il vous appartient dans faire quelque chose dans VOTRE application et ça ce n'est pas toujours facile ! SDL traite donc le clavier, la souris, les joysticks (à partir des versions 1.1.x),les appels systèmes et la gestion de la fenêtre. La librairie gère les évènements à la manière des Window Manager actuels, ie par file d'attente. Lorsqu'un évènement quelconque est généré il est placé dans la file d'attente de gestion des évènements de l'application. C'est ensuite à l'application de le sortir de cette file et de le traiter comme il se doit. La thread dédiée à la gestion des évènements est créée en lorsque vous initialisée le drapeau SDL_INIT_VIDEO (OK il existe aussi le drapeau SDL_INIT_EVENT_THREAD permettant une gesion complètement threadée des évènements... mais pour l'instant je n'en parlerai pas). L'union de base pour gérée les différents types d'évènements est la suivante : 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_QuitEvent quit; SDL_UserEvent user; SDL_SywWMEvent syswm; } SDL_Event; Comme vous pouvez donc le constater SDL gère beaucoup d'évènements. Le champs type permet d'identifier le type d'évènement, il recevra l'une des valeurs suivantes :
OUF !!! Ca en fait ! Mais c'est somme toute logique, non ? Je vous ai mentionné l'histoire de la file d'attente plus haut. Mais comment la manipule-t-on ? Il existe plusieurs fonctions pour cela les voici :
Cette fonction laisse le soin à SDL de remplir cette file avec ceux en attente sur le système.
Cette fonction tente d'enlever un évènement de la file d'attente d'une manière non bloquante. C'est à dire qu'elle n'essaiera qu'une fois et redonnera la main à la suite du programme. Si la fonction renvoie 0 alors c'est qu'il n'y avait aucun évènement sur la file d'attente, sinon elle retourne 1. Dans ce dernier cas la fonction stock l'évènement dans la variable pointée par *event et NULL sinon.
Cette fonction réalise la même opération que SDL_PollEvent() mais elle est bloquante, aussi elle ne s'arrête que lorsqu'elle réussie à enlever un évènement, ou si une se produit.
Cette fonction réalsie différentes opérations suivant le paramètre action :
Le paramètre mask de cette fonction est une combinaison binaire vous indiquant ainsi quels types d'évènements sont à traiter. Elle retourne le nombre d'éléments traiter ou -1 en cas d'erreurs.
Cette fonction vous permet de placer vous même un évènement dans la file d'attente.
Cette fonction vous permet de spécifier le traitement à adpter suivant les évènements et change donc leur comportement par défaut. Si state prend l'une des valeurs suivantes :
Cette fonction est très puissante et souple d'utilisation. Elle vous octroie le loisir d'examiner chaque évènement avant son insertion dans la file. Vous pouvez ainsi décider de son avenir avant son entrée dans la file. Le type SDL_EventFilter est en fait un pointeur sur une fonction. Celle-le même que le programmeur utilisera pour traiter les évènements. Son prototype est le suivant : typedef int (*SDL_EventFilter)(const SDL_Event *event); La fonction filtre devra renvoyer 1 si l'évènement doit être ajouté à la file d'attente ou 0 sinon.
Voilà vous connaisssez maintenant les fonctions de traitement des évènements, je vais donc pouvoir détailler chaque type d'évènement. |
|
|
|
Le clavier |
|
|
|
Le type SDL_Event vous indique quel est l'évènement que vous traitez, comme il s'agit d'une union, vous ne traitez qu'un et un seul type à la fois. Donc si type vaut SDL_KEYDOWN ou SDL_KEYUP, vous êtes sur de traiter un évènement du clavier ! La structure liée à de tels évènements est la suivante : typedef sruct { Uint8 type; Uint8 state; SDL_keysym keysym; }SDL_KeyboardEvent; type est évidemment le type d'évènement SDL_KEYUP ou SDL_KEYDOWN state signifie SDL_PRESSED ou SDL_RELEASED keysym vous permet de connaître la touche qui a envoyé cet évènement La structure SDL_keysym : typedef struct { Uint8 scancode; SDLKey sym; SDLMod mod; Uint16 unicode; }SDL_keysym; scancode représente le code de la touche et géré par le système. sym représente le symbole lié à ce code, vous les trouverer ici. mod fournit un état des modificateurs au moment du changement d'état de la touche, il s'agit de Shift, Alt, Control... unicode si la gestion par SDL de l'unicode est active c'est le caractère unicode correspondant Ainsi grâce à ces deux stuctures, vous pouvez très précisément dire si une touche a été modifiée et de quelle touche il s'agit. Un petit bout de code vous éclairera : while(SDL_WaitEvent(&event)){ switch(event.type){ case SDL_KEYDOWN: if(event.key.keysym.sym == SDLK_LEFT) bouge_vers_la_gauche(); break; } } Vous voyez c'est très simple, il suffit de récupérer un évènement, puis de l'analyser et enfin de le traiter. Bien entendu on peut utiliser d'autres fonctions que SDL_WaitEvent() pour ça. Le paramètre scancode est dépendant du système et donc peu portable, aussi travaillez de préférence sur la valeur de sym. D'autre part la gestion unicode est pratique dans certains cas afin de laisser le soin au système de gérer les claviers non QWERTY. On active la gestion unicode par un appel à int SDL_EnableUNICODE(int enable); Vous pouvez aussi connaître le nom et l'état d'une touche par des appels à : char *SDL_GetKeyName(SDLKey key); qui vous retourne le nom de la touche passée en paramètre. Uint8 *SDL_GetKeyState(int *numkeys); vous retourne quant à elle l'état du clavier en le gradant en mémoire au moment de l'appel dans un tableau d'entier. Ce tableau est indexé dans l'ordre des valeurs du type SDL_Key. Si la valeur est 1la touche est enfoncée, sinon 0. Enfin je traiterais des modificateurs, les touches Shift, Ctrl, Alt ou Meta. Vous pouvez connaître à tout moment l'état de ces touches par un appel à SDLMod SDL_GetModState(void); Cette fonction retourne une combinaison de valeurs parmi les suivantes : typedef enum { KMOD_NONE = 0x0000, KMOD_LSHIFT= 0x0001, KMOD_RSHIFT= 0x0002, KMOD_LCTRL = 0x0040, KMOD_RCTRL = 0x0080, KMOD_LALT = 0x0100, KMOD_RALT = 0x0200, KMOD_LMETA = 0x0400, KMOD_RMETA = 0x0800, KMOD_NUM = 0x1000, KMOD_CAPS = 0x2000, KMOD_MODE = 0x4000, } SDLMod; SDL définie aussi les quelques macros suivantes pour vous faciliter un peu la tâche : #define KMOD_CTRL (KMOD_LCTRL|KMOD_RCTRL) #define KMOD_SHIFT (KMOD_LSHIFT|KMOD_RSHIFT) #define KMOD_ALT (KMOD_LALT|KMOD_RALT) #define KMOD_META (KMOD_LMETA|KMOD_RMETA) | |
|
|
La souris |
|
|
|
La souris connaît elle aussi deux structures, en effet une souris à deux types d'évènements possibles, soit elle bouge soit on modifie l'état d'un de ses boutons (non non on ne démonte pas sa souris ;-)). Commençons par son déplacement et la structure qui lui est dédié. typedef struct{ Uint8 type; Uint8 state; Uint16 x, y; Sint16 xrel, yrel; }SDL_MouseMotionEvent; type forcément SDL_MOUSEMOTION state l'état des boutons x et y représentent les coordonnées absolue de la souris xrel et yrel représente la différence avec les anciennes coordonnées, autrement dit les coordonnées liées au déplacement La structure liée à l'état des boutons est quant à elle la suivante : typedef struct{ Uint8 type; Uint8 button; Uint8 state; Uint16 x, y; }SDL_MouseButtonEvent; type vous indique si c'est un évènement SDL_MOUSEBUTTONDOWN ou SDL_MOUSEBUTTONUP state indiquant si c'est SDL_PRESSED ou SDL_RELEASED (pressé ou relaché pour les nuls en anglais) button est le numéro du bouton visé (1 pour le bouton de gauche...) x et y correspondent aux coordonnées du click La fonction : Uint8 SDL_GetMouseState(int *x, int *y); vous indiquant l'état de la souris à tout moment. Evidemment x et y représente les coordonnées du curseur lors de l'appel, mais au vu de la définition vous pouvez passer NULL aux deux si vous ne désirez pas ce renseignement. Par exemple vous pouvez faire ceci (ex tirez de la doc officielle) : if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(1)) printf("Mouse Button 1(left) is pressed.\n"); SDL_BUTTON(x) est une macro prenant le numéro du boutton que vous désirez tester.
| |
|
|
Le joystick |
|
Passer le drapeau SDL_INIT_JOYSTICK lors de l'initialisation de SDL. Puis vous devez créer un accès vers le périphérique par un appel à : SDL_Joystick *SDL_JoystickOpen(int index); Cette fonction renverra alors les données concernant le périphérique ou NULL en cas d'échec, le paramètre index correspond au numéro du joystick sur le système que vous désirez utiliser. Pour vous éclairer, encore un petit exemple tiré de la doc : SDL_Joystick *joy; //Cherche des joysticks if(SDL_NumJoysticks()>0){ // Ouvre un accès au joystick 0 si possible joy=SDL_JoystickOpen(0); if(joy){ printf("Joystick 0 ouvert\n"); printf("Nom: %s\n", SDL_JoystickName(0)); printf("Nombre d'axes: %s\n", SDL_JoystickNumAxes(joy)); printf("Nombre de boutons: %s\n", SDL_JoystickNumButtons(joy)); printf("Nombre de trackballs: %s\n", SDL_JoystickNumBalls(joy)); }else printf("Couldn't open Joystick 0\n"); } //Relache du périphérique if(SDL_JoystickOpened(0)) SDL_JoystickClose(joy); } Ce petit exmple vous permet aussi de vous familiariser avec les différentes fonctions d'accès aux données d'un joystick. Son nom, son nombre de boutons, son nombre d'axes, son nombre de trackballs. L'exemple vous présente aussi quelques fonctions qu'il est utile de connaître : int SDL_NumJoysticks(void); qui vous rend le nombre de joystick(s) attaché(s) au système. int SDL_JoystickIndex(SDL_Joystick *joystick); vous donne l'index correspondant à un joystick donné. SDL conseille fortement de traiter les évènements liés au joystick dans la boucle des évènements, mais vous pouvez décider de faire autrement et de traiter ce type d'évènement manuellement et à part. Ainsi il vous appartient de réactualiser les évènements liés au joystick par la fonction : void SDL_JoystickUpdate(void); Cette fonction réactualisant les données attachées à tous les joysticks ouvert. En revanche si vous désirez vous occupez de ces évènements au sein de la boucle principale vous devrez d'abord appeler cette fonction qui vous en donnera le droit : int SDL_JoystickEventState(int state); Cette fonction prend pour paramètre soit SDL_ENABLE, soit SDL_DISABLE ou SDL_QUERY. Dès lors la réactualisation des paramètres liés aux joystick sera automatique dans la boucle d'évènements. Vous possédez aussi diverses fonctions d'accès aux paramètres d'un joystick vous permettant par exmple de savoir quel axe a bougé sur un joystick et de combien. Lisez la doc pour connaître ces fonctions, et les structures... :) | |
|
|
Divers |
|
|
|
Je vous ai détailler les principaux domaines susceptible de générer des évènements. Mais ils en rest quelques uns plus sommaire mais néanmoins très utiles. Chaque type d'évènement ci-dessous possède une structure qui lui est rattachée et propre.
Cet évènement est généré lorsque l'utilisateur a tenté de fermer l'application d'une manière non prévu par le programme (Ctrl-C, fermeture de la fenêtre par l'icone correspondante...) Ainsi si un tel évènement est reçu par SDL, l'application se terminera proprement. Cet évènement passera outre l'éventuelle fonction de filtre que vous auriez pu créer.
Ce genre d'évènement vous renseigne sur l'état de l'application dans son environnement. Vous pouvez ainsi savoir si l'application a gagné ou perdu le focus de la souris, du clavier...
Vous l'aurez deviné, cet évènement vous permet de gerer des évènements non traités par SDL mais générer par le WM.
Ce type d'évènement vous permet de connaître les nouvelles dimensions d'une fenêtre après son redimensionnement.
Vous pouvez aussi créer des évènements qui ne sont pas gérés par SDL en utilsant ce type. Il est vrai que je ne détaille pas beaucoup ces derniers types d'évènements tout simplement car une simple lecture de la documentation officielle vous éclairera. Elle le fera d'ailleurs sur pleins d'autres points ;-) |
|
|
|
|