VI. Événements▲
VI-A. Introduction▲
Ce que l'on appelle « événement » dans un programme est en général une action sur quelque chose produite par l'utilisateur du programme. Par exemple bouger la souris, cliquer sur un bouton de la souris, enfoncer une touche du clavier, laisser remonter une touche appuyée, manœuvrer un joystick, redimensionner une fenêtre, etc.
Le programme répond à certaines de ces actions, pas nécessairement à toutes. Par exemple il peut répondre au clavier sans se préoccuper de la souris ou l'inverse, et nous n'avons jamais utilisé le joystick jusqu'à maintenant. C'est pourquoi au départ il faut indiquer au programme les événements qu'il contrôle. Ensuite comment le programme capture ces événements et pour finir associer à chacun d'eux une réponse, à savoir l'action correspondante. Par exemple, si la touche [Echap] est appuyée le programme quitte, ou si on appuie sur une touche flèche le personnage avance, ou encore s'il y a clic dans tel bouton l'action prévue est lancée, etc.
VI-B. Mettre en place une boucle d'événements▲
Les événements sont stockés dans le programme au fur et à mesure qu'ils arrivent dans une file d'événements. Sous Allegro 5, cette file peut contenir des événements de différentes natures concernant le clavier, la souris, un ou plusieurs joysticks, un ou plusieurs minuteurs, une ou plusieurs fenêtres. Il est possible également d'obtenir des événements créés par l'utilisateur. Chaque nouvel événement détecté s'ajoute dans la file et Allegro propose différentes modalités pour l'attente et la capture des événements :
- Attente bloquante d'un événement : le programme reste en attente d'un événement et rien ne se passe tant qu'il n'y a pas d'événement.
- Attente non bloquante chronométrée : si aucun événement n'arrive durant un laps de temps déterminé, l'attente est levée et la suite des instructions est exécutée.
- Pas d'attente, le programme prend l'événement en tête de file s'il y en a un et s'il n'y en a pas, il passe à la suite des instructions.
Une file d'événements est un peu plus longue à mettre en place qu'une simple capture du clavier et de la souris comme nous l'avons vu précédemment. Mais c'est plus puissant et offre davantage de possibilités. C'est en général la solution retenue pour la création d'un jeu.
En résumé le premier point est de créer une file d'événements. Le second point consiste à indiquer quels sont les événements que la file doit traiter (souris, clavier joystick ?). Viennent ensuite l'attente et la capture des événements souhaités, et pour finir l'analyse de ces événements avec les déclenchements des actions correspondantes.
VI-B-1. Création d'une file d'événements▲
Sous Allegro 5, la récupération par l'application des événements déclenchés par l'utilisateur se fait avec une file d'attente du type :
ALLEGRO_EVENT_QUEUE*
queue;
Une file d'attente, ou queue, c'est comme dans un supermarché à la caisse. Le premier arrivé est le premier servi : « First in first out », d'où aussi le surnom de FIFO. Cette file doit être initialisée avec la fonction :
ALLEGRO_EVENT_QUEUE *
al_create_event_queue
(
void
)
qui retourne une file dûment allouée, ce qui donne le code :
ALLEGRO_EVENT_QUEUE*
queue;
queue =
al_create_event_queue
(
) ;
VI-B-2. Sélection des types d'événements à prendre en compte▲
Une fois la file initialisée, il faut spécifier les types d'événements que l'application va utiliser. C'est le rôle de la fonction :
void
al_register_event_source
(
ALLEGRO_EVENT_QUEUE *
queue, ALLEGRO_EVENT_SOURCE *
source)
Elle prend en paramètre la file concernée, queue , préalablement allouée et un type d'événement à récupérer source. Chaque source d'événements s'obtient avec une fonction spécialisée :
- Pour les événements relatifs à la fenêtre (par exemple la boîte de contrôle dans le coin en haut à droite permettant fermeture, redimensionnement, mise dans la barre des tâches) c'est la fonction :
ALLEGRO_EVENT_SOURCE *
al_get_display_event_source
(
ALLEGRO_DISPLAY *
display)
- Pour la récupération des événements clavier, c'est la fonction :
ALLEGRO_EVENT_SOURCE *
al_get_keyboard_event_source
(
void
)
- Pour la récupération des événements souris, c'est la fonction :
ALLEGRO_EVENT_SOURCE *
al_get_mouse_event_source
(
void
)
- Pour la récupération des événements minuteur, c'est la fonction :
ALLEGRO_EVENT_SOURCE *
al_get_timer_event_source
(
ALLEGRO_TIMER *
timer)
- Pour la récupération des événements joystick, c'est la fonction :
ALLEGRO_EVENT_SOURCE *
al_get_joystick_event_source
(
void
)
Pour notre premier test nous n'avons pris en compte qu'un seul type d'événement, les événements relatifs à la fenêtre, ce qui donne l'enregistrement suivant :
al_register_event_source
(
queue, //la file
al_get_display_event_source
(
display)); //le type d'événements
Le programme ci-dessous récapitule les étapes de la mise en place d'une boucle d'événements avec un seul type d'événement pris en compte, les événements fenêtre (display) :
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
// remplacer un appel de fonction par un mot
#define COLORALEA al_map_rgb(rand()%256,rand()%256,rand()%256)
void
erreur
(
const
char
*
txt)
{
ALLEGRO_DISPLAY*
d;
d =
al_is_system_installed
(
) ? al_get_current_display
(
) : NULL
;
al_show_native_message_box
(
d, "
ERREUR
"
, txt, NULL
, NULL
, 0
);
exit
(
EXIT_FAILURE);
}
/**
***********************************************************
************************************************************
*/
int
main
(
)
{
ALLEGRO_DISPLAY*
display;
ALLEGRO_EVENT_QUEUE*
queue; //file d'attente des événements
if
(!
al_init
(
))
erreur
(
"
al_init()
"
);
display =
al_create_display
(
800
, 600
);
if
(!
display)
erreur
(
"
al_create_display()
"
);
// 1) création de la file
queue =
al_create_event_queue
(
);
if
(!
queue)
erreur
(
"
al_create_event_queue()
"
);
//2) sélection des types d'événements à prendre en charge
// ici l'événement choisi concerne la barre des tâches de
// la fenêtre(carré rouge avec croix pour fermer...)
al_register_event_source
(
queue, // la file
al_get_display_event_source
(
display));// l'événement
// mise en noir de la fenêtre
al_clear_to_color
(
al_map_rgb
(
0
, 0
, 0
));
al_flip_display
(
);
// La suite plus bas
VI-B-3. Capture des événements▲
Une fois les initialisations posées, la file mise en place avec ses types d'événements précisés, il reste à établir la boucle de récupération de ces événements. Chaque événement avec ses informations est stocké dans une union du type ALLEGRO_EVENT.
ALLEGRO_EVENT event;
L'union permet en effet de combiner sous une seule forme les structures des différents types d'événements (voir la section Comprendre l'implémentation des événementsComprendre l'implémentation des événements).
Pour la récupération ensuite il y a plusieurs possibilités d'attente des événements (voir la section Autres possibilités de capture d'événementsAutres possibilités de capture d'événements) et dans l'exemple ci-dessous nous avons choisi une attente non bloquante chronométrée avec la fonction :
bool al_wait_for_event_timed
(
ALLEGRO_EVENT_QUEUE *
queue, ALLEGRO_EVENT *
ret_event, float
secs)
Cette fonction prend en paramètre la file d'événements concernée queue , la structure instanciée dans l'union ALLEGRO_EVENT est passée par référence (modifiable en sortie) au paramètre 2 ret_event . Le paramètre 3 secs prend pour valeur la durée souhaitée entre chaque attente. La fonction retourne true si un événement est trouvé dans la file. L'événement est alors retiré de la file, ses informations sont copiées dans la structure passée par référence et accessible en sortie. À l'issue de l'attente, si rien n'arrive, la fonction retourne false et son exécution est terminée.
Le code précédent se poursuit de la façon suivante :
// boucle d'événements
while
(
1
){
ALLEGRO_EVENT event =
{
0
}
;// mise à 0
// récupération NON bloquante et topée des événements
al_wait_for_event_timed
(
queue, // la file
&
event, // l'event par référence
1
.0
/
10
); // topage en seconde
// (...)
VI-B-4. Identification de l'événement▲
Vient ensuite l'identification de l'événement récupéré, s'il y a un événement. Différents événements peuvent se présenter : fenêtre, clavier, souris, joystick, etc. Chaque type d'événement est identifié avec une constante dans le fichier event.h de la bibliothèque Allegro, voici tous les identificateurs :
ALLEGRO_EVENT_JOYSTICK_AXIS Un axe du joystick a changé de valeur.
ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN Un bouton du joystick a été pressé.
ALLEGRO_EVENT_JOYSTICK_BUTTON_UP Un bouton du joystick a été relevé.
ALLEGRO_EVENT_JOYSTICK_CONFIGURATION Indique qu'
un joystick a été branché ou débranché.
ALLEGRO_EVENT_KEY_DOWN Une touche est enfoncée.
ALLEGRO_EVENT_KEY_UP Une touche est relevée.
ALLEGRO_EVENT_KEY_CHAR Un caractère est appuyé sur le clavier ou il y a une répétition, la touche restant enfoncée.
ALLEGRO_EVENT_MOUSE_AXES Un changement de position est survenu sur l
'
un des axes de la souris.
ALLEGRO_EVENT_MOUSE_BUTTON_DOWN Un bouton de la souris est cliqué.
ALLEGRO_EVENT_MOUSE_BUTTON_UP Un bouton de la souris est relevé.
ALLEGRO_EVENT_MOUSE_WARPED La fonction al_set_mouse_xy a été appelée pour bouger la souris.
ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY Le curseur de la souris entre sur une fenêtre ouverte par l'
application.
ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY Le curseur de la souris quitte une fenêtre de l
'
application.
ALLEGRO_EVENT_TIMER Un top du minuteur a eu lieu.
ALLEGRO_EVENT_DISPLAY_EXPOSE La fenêtre ou une partie de celle-
ci est devenue visible.
ALLEGRO_EVENT_DISPLAY_RESIZE La fenêtre a été redimensionnée.
ALLEGRO_EVENT_DISPLAY_CLOSE Événement produit lorsque le bouton de
fermeture de la fenêtre est cliqué.
ALLEGRO_EVENT_DISPLAY_LOST Lors de l'
utilisation de Direct3D, écran ou fenêtre peuvent être « perdus »
ou sans réponse. Dans cet état, les opérations de dessin sont ignorées
et des données des pixels de bitmap peuvent être inaccessibles et indéfinies.
ALLEGRO_EVENT_DISPLAY_FOUND Cet événement est généré lorsqu
'
une fenêtre précédemment « perdue »
est rétablie dans son fonctionnement normal.
ALLEGRO_EVENT_DISPLAY_SWITCH_OUT Événement produit lorsque la fenêtre devient inactive si par
exemple l'
utilisateur clique dans une autre fenêtre
ou renvoie la fenêtre dans la barre des tâches.
ALLEGRO_EVENT_DISPLAY_SWITCH_IN Événement produit lorsqu
'
une fenêtre préalablement désactivée se trouve réactivée.
ALLEGRO_EVENT_DISPLAY_ORIENTATION Cet événement est généré lorsque l'
orientation de la fenêtre ou
de l
'
écran change sur des appareils capables de la détecter.
Dans la structure ALLEGRO_EVENT retournée par la fonction de récupération des événements, le champ type contient une de ces valeurs. Pour identifier l'événement il suffit de la comparer aux possibles identificateurs :
if
(
event.type ==
ALLEGRO_EVENT_<
type>
){
// faire les instructions correspondant à l'événement <type>
}
Ensuite, dans le bloc correspondant, il reste à accomplir les instructions spécifiques à l'événement.
Voici complétée la boucle d'événements du programme. Elle récupère uniquement un événement de type ALLEGRO_EVENT_DISPLAY_CLOSE . Il est déclenché par un clic dans la case à cocher rouge à droite dans la barre de navigation d'une fenêtre. En principe il sert à fermer le programme mais nous pourrions très bien lui attribuer d'autres actions :
// boucle d'événements
while
(
1
){
ALLEGRO_EVENT event =
{
0
}
;// mise à 0
// récupération NON bloquante et topée des événements
al_wait_for_event_timed
(
queue, // la file
&
event, // l'event par référence
1
.0
/
10
); // topage en seconde
// analyse de l'événement et action en conséquence
if
(
event.type ==
ALLEGRO_EVENT_DISPLAY_CLOSE)
break
;
// si non changer la couleur de la fenêtre
al_clear_to_color
(
COLORALEA);
al_flip_display
(
);
}
//libérer les ressources utilisées
al_destroy_event_queue
(
queue);
al_destroy_display
(
display);
return
0
;
}
/**
***********************************************************
************************************************************
*/
Le changement de couleur de la fenêtre est là juste pour vérifier que sans événement la boucle n'est pas bloquée.
Il est conseillé d'initialiser à 0 la structure event. En effet, la fonction de capture al_wait_for_event_timed peut n'avoir capturé aucun événement pendant le laps de temps spécifié en paramètre. Dans ce cas, la structure dans l'union ALLEGRO_EVENT si elle n'est pas initialisée contient ce qui traîne en mémoire et risque de provoquer un comportement inattendu. Pour éviter cela, il faut initialiser cette structure. A priori la valeur 0 n'est jamais utilisée pour identifier un type d'événement. C'est pourquoi la mise à 0 de la structure ALLEGRO_EVENT garantit que le champ type ne correspondra pas par erreur à un type d'événement si la fonction de capture ne retourne rien.
VI-B-5. Autres possibilités de capture d'événements▲
Pour l'attente et la capture des événements, nous disposons de plusieurs fonctions.
La fonction al_wait_for_event :
void
al_wait_for_event
(
ALLEGRO_EVENT_QUEUE *
queue, ALLEGRO_EVENT *
ret_event)
Version bloquante de la capture d'événements, la fonction attend un événement pour la file queue . Si événement, il est copié dans le paramètre par référence ret_event . Exemple d'appel dans la boucle d'événement :
al_wait_for_event
(
queue,&
event);
La fonction al_wait_for_event_timed :
bool al_wait_for_event_timed
(
ALLEGRO_EVENT_QUEUE *
queue, ALLEGRO_EVENT *
ret_event, float
secs)
À la différence de la fonction al_wait_for_event() elle retourne true si elle trouve un événement mais s'il n'y a aucun événement dans le temps spécifié en seconde au paramètre secs , elle retourne false et termine son exécution (voir le programme précédent).
La fonction al_wait_for_event_until :
bool al_wait_for_event_until
(
ALLEGRO_EVENT_QUEUE *
queue, ALLEGRO_EVENT *
ret_event, ALLEGRO_TIMEOUT *
timeout)
Similaire à la fonction al_wait_for_event_timed() mais le temps est déterminé par un minuteur externe à la fonction. Ce minuteur peut être réinitialisé à chaque tour de boucle :
ALLEGRO_TIMEOUT timeout;
(
...)
al_init_timeout
(&
timeout,(
rand
(
)%
5
)/
10
.0
);
al_wait_for_event_until
(
queue,&
event,&
timeout);
Dans cet exemple, le temps d'attente varie à chaque tour avec une valeur aléatoire. C'est équivalent à :
al_wait_for_event_timed
(
queue,&
event,(
rand
(
)%
5
)/
10
.0
);
La fonction al_is_event_queue_empty :
bool al_is_event_queue_empty
(
ALLEGRO_EVENT_QUEUE *
queue)
retourne true si la file passée en paramètre est vide et false sinon.
La fonction al_get_next_event :
bool al_get_next_event
(
ALLEGRO_EVENT_QUEUE *
queue, ALLEGRO_EVENT *
ret_event)
S'il y a un événement dans la file, il est défilé et retourné au paramètre ret_event puis la fonction retourne true , sinon la fonction retourne false . Il n'y a pas d'attente.
La fonction al_peek_next_event :
bool al_peek_next_event
(
ALLEGRO_EVENT_QUEUE *
queue, ALLEGRO_EVENT *
ret_event)
Si un ou plusieurs événements sont dans la file, la fonction retourne true et copie le premier en ret_event sans le retirer de la file. Elle retourne false s'il n'y a aucun élément.
La fonction al_drop_next_event :
bool al_drop_next_event
(
ALLEGRO_EVENT_QUEUE *
queue)
La fonction retire l'élément en tête de la file sans faire de copie. Elle retourne true si elle a trouvé un élément et false sinon.
La fonction al_flush_event_queue :
void
al_flush_event_queue
(
ALLEGRO_EVENT_QUEUE *
queue)
La fonction vide la file de tous ses éléments. Elle retourne true si elle a trouvé au moins un élément et false si la file était vide.
VI-C. Récupérer le clavier et la souris dans une boucle d'événements▲
Pour disposer du clavier, comme nous l'avons déjà vu, le clavier doit être installé avec un appel à la fonction :
bool al_install_keyboard
(
void
)
Elle retourne true en cas de succès et false sinon.
Pour disposer de la souris, celle-ci doit de même être précédemment installée avec un appel à la fonction :
bool al_install_mouse
(
void
)
Elle retourne true en cas de succès et false sinon.
Nous avons également besoin d'une file d'événements :
ALLEGRO_EVENT_QUEUE*
f;
f =
al_create_event_queue
(
);
if
(!
f)
erreur
(
"
al_create_event_queue()
"
);
Et pour la file nous devons sélectionner les événements qui seront utilisés :
al_register_event_source
(
f,al_get_display_event_source
(
display)) ;
al_register_event_source
(
f, al_get_keyboard_event_source
(
)) ;
al_register_event_source
(
f, al_get_mouse_event_source
(
)) ;
Dans le programme ci-dessous, nous allons utiliser une ALLEGRO_TEXTLOG pour sortir les résultats. Nous l'avons présentée au chapitre Premiers pas avec Allegro 5Premier pas. Il s'agit d'une console native à Allegro, pratique en cas de portage du programme sous différents environnements. Cette fenêtre console de sortie de messages texte s'obtient de la façon suivante :
ALLEGRO_TEXTLOG*
textlog=
NULL
;
textlog=
al_open_native_text_log
(
"
Evénements souris, clavier
"
,
ALLEGRO_TEXTLOG_MONOSPACE);
Après tous ces préparatifs, dans la boucle d'événements la structure ALLEGRO_EVENT est réinitialisée à chaque nouvel événement avec l'appel :
ALLEGRO_EVENT event={
0
}
;
al_wait_for_event
(
queue,&
event);
L'analyse de l'événement se fait avec un switch sur le type, chaque type d'événement correspond à un case, voici ceux utilisés dans l'expérimentation :
switch
(
event.type){
// EVENEMENTS SOURIS
case
ALLEGRO_EVENT_MOUSE_BUTTON_DOWN : ...break
;
case
ALLEGRO_EVENT_MOUSE_BUTTON_UP : ...break
;
case
ALLEGRO_EVENT_MOUSE_AXES : ...break
;
// EVENEMENTS CLAVIER
case
ALLEGRO_EVENT_KEY_DOWN : ...break
;
case
ALLEGRO_EVENT_KEY_UP : ...break
;
case
ALLEGRO_EVENT_KEY_CHAR : ...break
;
// EVENEMENTS FENETRE
case
ALLEGRO_EVENT_DISPLAY_CLOSE :
...break
;
(
...)
Les principales informations de la souris sont obtenues de la façon suivante :
event.button contient le numéro du bouton appuyé ou relâché.
event.mouse.x et event.mouse.y donnent la position de la souris dans la fenêtre active concernée.
event.mouse.z correspond à la position de la molette verticale.
Les principales informations du clavier sont obtenues de la façon suivante :
event.keyboard.keycode contient la valeur qui identifie une touche du clavier.
event.keyboard.repeat indique par true ou false s'il y a ou pas une répétition automatique de touche.
La totalité des informations accessibles pour les différents types d'événements est détaillée dans la section Comprendre l'implémentationComprendre l'implémentation des événementsdes événementsComprendre l'implémentation des événements.
Voici le programme d'expérimentation complet :
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
void
erreur
(
const
char
*
txt)
{
ALLEGRO_DISPLAY*
d;
d =
al_is_system_installed
(
) ? al_get_current_display
(
) : NULL
;
al_show_native_message_box
(
d, "
ERREUR
"
, txt, NULL
, NULL
, 0
);
exit
(
EXIT_FAILURE);
}
/**
***********************************************************
************************************************************
*/
int
main
(
)
{
if
(!
al_init
(
))
erreur
(
"
al_init()
"
);
// FENETRE
ALLEGRO_DISPLAY*
display;
display =
al_create_display
(
800
, 600
);
if
(!
display)
erreur
(
"
al_create_display()
"
);
// AVOIR LE CLAVIER
if
(!
al_install_keyboard
(
))
erreur
(
"
al_install_keyboard()
"
);
// AVOIR LA SOURIS
if
(!
al_install_mouse
(
))
erreur
(
"
al_install_mouse()
"
);
// FILE D'EVENEMENTS
ALLEGRO_EVENT_QUEUE*
queue;
queue =
al_create_event_queue
(
);
if
(!
queue)
erreur
(
"
al_create_event_queue()
"
);
// SELECTION DES EVENEMENTS à prendre dans la file
al_register_event_source
(
queue,
al_get_display_event_source
(
display));
al_register_event_source
(
queue,
al_get_keyboard_event_source
(
));
al_register_event_source
(
queue, al_get_mouse_event_source
(
));
// CONSOLE NATIVE ALLEGRO pour affichage des tests
ALLEGRO_TEXTLOG*
textlog =
NULL
;
textlog =
al_open_native_text_log
(
"
Evénements souris, clavier
"
,
ALLEGRO_TEXTLOG_MONOSPACE);
// BOUCLE EVENEMENTS
int
fin =
0
;
while
(!
fin){
// RECUPERATION DES EVENEMENTS
ALLEGRO_EVENT event =
{
0
}
;
al_wait_for_event
(
queue, &
event);
// SELON EVENEMENT TROUVE
switch
(
event.type){
// EVENEMENTS SOURIS
case
ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
al_append_native_text_log
(
textlog,"
bouton %d presse
\n
"
, event.mouse.button);
break
;
case
ALLEGRO_EVENT_MOUSE_BUTTON_UP:
al_append_native_text_log
(
textlog,"
bouton %d relache
\n
"
, event.mouse.button);
break
;
// si mouvement
case
ALLEGRO_EVENT_MOUSE_AXES:
al_append_native_text_log
(
textlog,"
x:%4d y:%4d dx:%4d dy%4d z:%3d w%3d
\n
"
,
event.mouse.x, event.mouse.y, //position verticale, horizontale
event.mouse.dx, event.mouse.dy,// mouvement
event.mouse.z,// position molette verticale
event.mouse.w);// position molette horizontale
break
;
// EVENEMENTS CLAVIER
case
ALLEGRO_EVENT_KEY_DOWN:
{
const
char
*
nomkey =
al_keycode_to_name
(
event.keyboard.keycode);
al_append_native_text_log
(
textlog, "
%8s : %s
\n
"
,"
DOWN
"
, nomkey);
}
break
;
case
ALLEGRO_EVENT_KEY_UP:
{
const
char
*
nomkey =
al_keycode_to_name
(
event.keyboard.keycode);
al_append_native_text_log
(
textlog, "
%8s : %s
\n
"
,"
UP
"
, nomkey);
}
break
;
// caractères et répétitions
case
ALLEGRO_EVENT_KEY_CHAR:
{
char
*
label =
event.keyboard.repeat ?"
repeat
"
: "
KEY_CHAR
"
;
const
char
*
nomkey =
al_keycode_to_name
(
event.keyboard.keycode);
al_append_native_text_log
(
textlog, "
%8s : %s
\n
"
,label, nomkey);
}
break
;
// EVENEMENTS FENETRE
case
ALLEGRO_EVENT_DISPLAY_CLOSE:
fin =
TRUE;
break
;
}
}
//LIBERER LES RESSOURCES UTILISEES
al_destroy_display
(
display);
al_destroy_event_queue
(
queue);
return
0
;
}
/**
***********************************************************
************************************************************
*/
Appuyer sur une touche clavier déclenche deux événements :
ALLEGRO_EVENT_KEY_DOWN : une touche est appuyée, elle est identifiable avec le champ event.keyboard.keycode . La répétition automatique sur une pression continue n'est pas prise en compte.
ALLEGRO_EVENT_KEY_CHAR : une touche est appuyée, elle est également identifiable avec le champ event.keyboard.keycode . Cet événement prend en compte la répétition automatique si le doigt reste appuyé sur la touche. Chaque répétition est considérée comme un événement. Pour le vérifier voici une modification à apporter au programme :
// Mettre en commentaire
/*case ALLEGRO_EVENT_KEY_CHAR :
{
char*label = event.keyboard.repeat ? "repeat" : "KEY_CHAR";
const char*nomkey = al_keycode_to_name(event.keyboard.keycode);
al_append_native_text_log(textlog,"%8s : %s\n", label,nomkey);
}
break;*/
et juste à la sortie du switch faire changer la fenêtre de couleur, ajouter :
switch
(
event.type){
(
...)
}
al_clear_to_color
(
al_map_rgb
(
rand
(
)%
256
,rand
(
)%
256
,rand
(
)%
256
));
al_flip_display
(
);
Nous constatons qu'à chaque événement la fenêtre change de couleur, notamment en cas de répétition si le doigt reste appuyé sur une touche. L'événement existe même s'il n'est pas traité dans le switch.
VI-D. Piloter un rectangle avec les flèches du clavier▲
Maintenant que nous savons mettre en place une boucle d'événements, nous allons entrer dans la création de jeux avec le déplacement d'un rectangle au clavier.
Pour ce faire, nous avons besoin de fonctions de dessin. Les fonctions de dessin sont dans un greffon nommé « primitives addon ». Nous l'avons présenté au chapitre Texte, polices, couleur, dessinTexte, police, couleur, dessin. Pour rappel, ce module nécessite un include :
#include <allegro5/allegro_primitives.h>
puis dans le main l'appel d'une fonction d'initialisation :
al_init_primitives_addon
(
)
Notre programme tout d'abord installe Allegro, crée une fenêtre, crée une file d'événements, sélectionne les types d'événements de fenêtre ( display ) et clavier ( keyboard ). Ensuite l'algorithme pour une animation consiste
simplement à :
- Effacer le rectangle à sa position courante.
- Bouger le rectangle en modifiant sa position.
- Réafficher le rectangle à sa nouvelle position.
- Recommencer.
Plus précisément, nous procédons de la façon suivante :
- effacer tout le double buffer invisible à l'écran,
- modifier la position du rectangle,
- afficher le rectangle à sa nouvelle position dans le double buffer,
- copier le double buffer à l'écran,
- recommencer.
Le double buffer, comme nous l'avons déjà vu, est une image en mémoire de la taille de l'écran, mais invisible à l'écran, qui sert uniquement à préparer l'affichage à l'écran. Tous les dessins sont faits dans le double buffer invisible d'abord et ensuite le double buffer est copié à l'écran. Cette technique pour des raisons de hardware permet d'éviter les effets désastreux de scintillements et de morcellement de l'affichage. C'est ce qui permet d'avoir des animations fluides.
Dans la boucle d'événement ci-dessous, nous avons placé l'affichage au départ afin de voir tout de suite le rectangle. Le mouvement du rectangle vient après, ce qui modifie un peu l'ordre des opérations de l'algorithme :
- effacer tout le double buffer invisible à l'écran,
- afficher le rectangle à sa nouvelle position dans le double buffer,
- copier le double buffer à l'écran,
- modifier la position du rectangle,
- recommencer.
La fonction pour afficher un rectangle est la suivante :
void
al_draw_filled_rectangle
(
float
x1, float
y1, float
x2, float
y2, ALLEGRO_COLOR color)
Les paramètres x1 , y1 correspondent au coin haut gauche du rectangle et x2 , y2 au coin bas droite. Si les valeurs sont inversées alors (x1,y1) et (x2,y2) sont permutées. Le paramètre color est une couleur. filled dans le nom précise qu'il s'agit d'un rectangle plein.
Voici le programme :
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
#include <allegro5/allegro_primitives.h>
#define NOIR al_map_rgb(0,0,0)
#define BLEU al_map_rgb(128,0,255)
void
erreur
(
const
char
*
txt)
{
ALLEGRO_DISPLAY*
d;
d =
al_is_system_installed
(
) ? al_get_current_display
(
) : NULL
;
al_show_native_message_box
(
d, "
ERREUR
"
, txt, NULL
, NULL
, 0
);
exit
(
EXIT_FAILURE);
}
/**
***************************************************************
****************************************************************
*/
int
main
(
)
{
ALLEGRO_DISPLAY*
display;
ALLEGRO_EVENT_QUEUE*
queue;
const
int
screenx =
800
; // dimension fenêtre
const
int
screeny =
600
;
int
fin =
0
;
int
x =
screenx /
2
; // position du rectangle
int
y =
screeny /
2
;
if
(!
al_init
(
))
erreur
(
"
al_init()
"
);
// initialisation opérations de dessin
if
(!
al_init_primitives_addon
(
))
erreur
(
"
al_init_primitives_addon()
"
);
if
(!
al_install_keyboard
(
))
erreur
(
"
install_keyboard()
"
);
display =
al_create_display
(
screenx, screeny);
if
(!
display)
erreur
(
"
al_create_display()
"
);
queue =
al_create_event_queue
(
);
if
(!
queue)
erreur
(
"
al_create_event_queue()
"
);
al_register_event_source
(
queue,
al_get_display_event_source
(
display));
al_register_event_source
(
queue,
al_get_keyboard_event_source
(
));
while
(!
fin){
// affichage au début pour être visible dés le départ
// 1 effacer le double buffer
al_clear_to_color
(
NOIR);
// 2 le rectangle à sa position x,y dans le double buffer
// (invisible, en mémoire)
al_draw_filled_rectangle
(
x, y, x +
20
, y +
20
, BLEU);
// 3 passer le double buffer à l'écran
al_flip_display
(
);
// Récupération des événements
ALLEGRO_EVENT event;
al_wait_for_event
(
queue, &
event);
// de quel type d'événement s'agit-il ?
if
(
event.type ==
ALLEGRO_EVENT_KEY_DOWN){
// si clavier selon touche appuyée,
switch
(
event.keyboard.keycode){
case
ALLEGRO_KEY_UP: y -=
10
; break
;
case
ALLEGRO_KEY_RIGHT: x +=
10
; break
;
case
ALLEGRO_KEY_DOWN: y +=
10
; break
;
case
ALLEGRO_KEY_LEFT: x -=
10
; break
;
case
ALLEGRO_KEY_ESCAPE: fin =
1
; break
;
}
}
else
if
(
event.type ==
ALLEGRO_EVENT_DISPLAY_CLOSE)
fin =
1
;
}
al_destroy_event_queue
(
queue);
al_destroy_display
(
display);
return
0
;
}
/**
***************************************************************
****************************************************************
*/
Lors de l'utilisation, nous constatons qu'appuyer sur une flèche ne produit qu'un seul événement même en laissant le doigt enfoncé. À chaque fois le rectangle fait un seul pas. Il faut relever le doigt et réappuyer pour qu'il en fasse un nouveau. Nous verrons un peu plus loin comment contourner ce problème sans utiliser les événements « repeat » récupérables en ALLEGRO_EVENT_KEY_CHAR vus précédemment. En effet ces événements nécessitent un petit temps d'attente juste avant de se déclencher ce qui n'est pas adapté pour un personnage dans un jeu.
Par ailleurs nous constatons également que, arrivé sur un bord, le rectangle disparaît. La bonne nouvelle est que le programme n'en est pas déstabilisé comme c'était le cas en console avec un gotoxy() en dehors de la fenêtre. Néanmoins il faudra contrôler les bords afin de ne pas perdre le contrôle du rectangle.
VI-E. Contrôler l'exécution d'un programme avec un minuteur▲
Un minuteur (timer en anglais) envoie un événement de type ALLEGRO_EVENT_TIMER régulièrement selon une durée définie au départ. Il peut servir de montre ou de chronomètre et il peut intervenir afin de contrôler le déroulement temporel du programme. C'est très utile notamment graphiquement afin que, quelle que soit la puissance de l'ordinateur, l'application se déroule toujours à la même vitesse.
Pour avoir un minuteur dans le programme, il faut déclarer un ALLEGRO_TIMER :
ALLEGRO_TIMER*
timer;
et ensuite l'initialiser avec la fonction :
ALLEGRO_TIMER *
al_create_timer
(
double
speed_secs)
Elle retourne un pointeur sur une structure ALLEGRO_TIMER initialisée avec la durée entre chaque tic mentionnée au paramètre speed_secs . Cette durée est exprimée en seconde. Voici un exemple d'appel :
timer =
al_create_timer
(
1
.5
);
Chaque tic d'horloge est un événement à récupérer dans une file d'événements. Il faut donc spécifier à la file qu'elle doit prendre en compte ce type d'événement. Cette catégorie d'événements est récupérée avec la fonction al_get_timer_event_source() qui prend en paramètre le minuteur souhaité. Voici un exemple d'appel, soit une file queue et le minuteur timer :
al_register_event_source
(
queue,al_get_timer_event_source
(
timer));
Le minuteur, comme un chronomètre doit être activé au départ sinon rien ne se passe. C'est la fonction :
void
al_start_timer
(
ALLEGRO_TIMER *
timer)
et pour le minuteur alloué l'appel :
al_start_timer
(
timer);
Ensuite les événements du minuteur sont identifiés par la valeur constante ALLEGRO_EVENT_TIMER , par exemple :
if
(
event.type==
ALLEGRO_EVENT_TIMER){
// instructions topées
}
Pour détruire un minuteur préalablement alloué c'est la fonction :
void
al_destroy_timer
(
ALLEGRO_TIMER *
timer)
Voici un exemple de mise en place :
#include <allegro5/allegro5.h>
#include <allegro5/allegro_native_dialog.h>
#define COLORALEA al_map_rgb(rand()%256,rand()%256,rand()%256)
void
erreur
(
const
char
*
txt)
{
ALLEGRO_DISPLAY*
d;
d =
al_is_system_installed
(
) ? al_get_current_display
(
) : NULL
;
al_show_native_message_box
(
d, "
ERREUR
"
, txt, NULL
, NULL
, 0
);
exit
(
EXIT_FAILURE);
}
/**
***************************************************************
****************************************************************
*/
int
main
(
)
{
if
(!
al_init
(
))
erreur
(
"
al_init()
"
);
ALLEGRO_DISPLAY*
display;
display =
al_create_display
(
800
, 600
);
if
(!
display)
erreur
(
"
al_create_display()
"
);
// création du timer
ALLEGRO_TIMER*
timer;
timer =
al_create_timer
(
1
.5
); // en secondes
// gestion de la file d'événements
ALLEGRO_EVENT_QUEUE*
queue;
queue =
al_create_event_queue
(
);
al_register_event_source
(
queue,
al_get_display_event_source
(
display));
// enregistrement du timer comme source d'événements
al_register_event_source
(
queue,
al_get_timer_event_source
(
timer));
// ATTENTION, ne pas oublier : démarre le timer
al_start_timer
(
timer);
int
fin =
0
;
while
(!
fin){
ALLEGRO_EVENT event;
al_wait_for_event
(
queue, &
event);
// si évènement timer changer la couleur
if
(
event.type ==
ALLEGRO_EVENT_TIMER){
al_clear_to_color
(
COLORALEA);
al_flip_display
(
);
}
// fermeture fenêtre
else
if
(
event.type ==
ALLEGRO_EVENT_DISPLAY_CLOSE)
fin =
1
;
}
al_destroy_display
(
display);
al_destroy_event_queue
(
queue);
al_destroy_timer
(
timer);
return
0
;
}
/**
***************************************************************
****************************************************************
*/
Notons qu'il est possible d'installer plusieurs minuteurs qui fonctionnent simultanément dans un même programme. Pour ce faire, il suffit d'initialiser plusieurs minuteurs, d'associer les événements de chacun à la file d'événements. Pour la récupération des événements dans la boucle, chaque minuteur est différencié de la façon suivante, soit m1, m2, m3 trois minuteurs correctement initialisés :
if
(
event.type==
ALLEGRO_EVENT_TIMER){
if
(
event.timer.source ==
m1)
// action pour m1
if
(
event.timer.source ==
m2)
// action pour m2
if
(
event.timer.source ==
m3)
// action pour m3
}
VI-F. Donner de la fluidité aux mouvements du rectangle▲
Nous allons modifier le programme du rectangle piloté avec les flèches pour lui donner réactivité et fluidité grâce à un minuteur et l'ajout d'une modification algorithmique dans la gestion des touches du clavier. Le programme proposé est inspiré d'un tutoriel donné sur le wiki d'Allegro.
Dans le programme précédent du rectangle, il fallait pour qu'il avance toujours réappuyer sur les touches flèches, c'est-à-dire relever le doigt et réenfoncer la touche. Maintenant nous souhaitons que le rectangle se déplace tant que la touche est appuyée et qu'il ne s'arrête que si aucune touche n'est pressée. Nativement Allegro ne nous permet pas d'obtenir ce résultat. Nous devons modifier la manière de prendre en compte les événements d'enfoncement et de relâchement des touches concernées.
Pour ce faire, nous allons doubler chaque touche flèche d'un booléen. Il est mis à true si la touche correspondante est enfoncée (événement ALLEGRO_EVENT_KEY_DOWN ). Il est mis à false si la touche correspondante est relevée (événement ALLEGRO_EVENT_KEY_UP ). Ensuite tant que ce booléen est à true , le rectangle avance dans la direction indiquée. Il ne s'arrête que si le booléen est à false . Comme il y a quatre touches ([Flèche en haut][Flèche à droite][Flèche en bas][Flèche à gauche]), il faut quatre booléens. Ils sont réunis dans un tableau et chaque indice est identifié avec une constante dans un enum, ce qui donne la structure de données :
enum
{
KEY_UP,KEY_RIGHT,KEY_DOWN,KEY_LEFT,KEY_MAX}
;
bool key[KEY_MAX]={
0
}
;
KEY_MAX (ici d'une valeur de 4) donne la taille du tableau. On peut facilement rajouter des touches le cas échéant. Au départ le tableau est initialisé à 0 ( false ).
Le programme commence comme habituellement par les initialisations obligées : bibliothèque Allegro, primitives, clavier, fenêtre ( display ), file d'événements, minuteur ( timer ), types d'événements à récupérer. Ensuite la boucle d'événements filtre les événements voulus qui sont de type :
ALLEGRO_EVENT_KEY_DOWN, ALLEGRO_EVENT_KEY_UP, ALLEGRO_EVENT_TIMER
et aussi pour quitter le programme :
ALLEGRO_EVENT_DISPLAY_CLOSE
Sur événement ALLEGRO_EVENT_KEY_DOWN si la touche concernée est une flèche, le booléen correspondant est mis à true :
if
(
event.type==
ALLEGRO_EVENT_KEY_DOWN){
switch
(
event.keyboard.keycode){
case
ALLEGRO_KEY_UP:
key[KEY_UP]=
true
;
break
;
case
ALLEGRO_KEY_RIGHT: key[KEY_RIGHT]=
true
; break
;
case
ALLEGRO_KEY_DOWN: key[KEY_DOWN]=
true
; break
;
case
ALLEGRO_KEY_LEFT: key[KEY_LEFT]=
true
; break
;
}
}
Sur événement ALLEGRO_EVENT_KEY_UP si la touche concernée est une flèche, son booléen est mis à false :
else
if
(
event.type==
ALLEGRO_EVENT_KEY_UP){
switch
(
event.keyboard.keycode){
case
ALLEGRO_KEY_UP:
key[KEY_UP]=
false
;
break
;
case
ALLEGRO_KEY_RIGHT: key[KEY_RIGHT]=
false
;break
;
case
ALLEGRO_KEY_DOWN: key[KEY_DOWN]=
false
; break
;
case
ALLEGRO_KEY_LEFT: key[KEY_LEFT]=
false
; break
;
}
}
Sur événement ALLEGRO_EVENT_TIMER , la position du rectangle est modifiée ou pas selon qu'une touche flèche est appuyée ou non :
else
if
(
event.type==
ALLEGRO_EVENT_TIMER){
y-=
key[KEY_UP]*
10
;
x+=
key[KEY_RIGHT]*
10
;
y+=
key[KEY_DOWN]*
10
;
x-=
key[KEY_LEFT]*
10
;
dessine=
true
;
}
Si la touche n'est pas appuyée, key[la_flèche] vaut 0 et si elle est appuyée, key[la_flèche] vaut 1. Le booléen dessine indique qu'un rafraîchissement de l'affichage est demandé. L'affichage a lieu si dessine est à true mais également si la file d'événements est vide. C'est-à-dire si aucune touche n'a été appuyée entre temps, de façon à ce que le tableau key soit parfaitement synchronisé avec le jeu de l'utilisateur. C'est ce qui motive le test :
if
(
dessine==
true
&&
al_is_event_queue_empty
(
queue)){
}
L'affichage en lui-même, repose toujours sur le même principe :
- Effacer le double buffer.
- Afficher dans le double buffer le rectangle à sa position courante.
- Passer le double buffer à l'écran .
Ce qui donne :
if
(
dessine==
true
&&
al_is_event_queue_empty
(
queue)){
al_clear_to_color
(
NOIR);
al_draw_filled_rectangle
(
x,y,x+
20
,y+
20
,BLEU);
al_flip_display
(
);
dessine=
false
;
}
Le booléen dessine est mis à false pour indiquer que le rafraîchissement a été effectué.
Voici le programme complet :
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
#include <allegro5/allegro_primitives.h>
#define NOIR al_map_rgb(0,0,0)
#define BLEU al_map_rgb(128,0,255)
void
erreur
(
const
char
*
txt)
{
ALLEGRO_DISPLAY*
d;
d =
al_is_system_installed
(
) ? al_get_current_display
(
) : NULL
;
al_show_native_message_box
(
d, "
ERREUR
"
, txt, NULL
, NULL
, 0
);
exit
(
EXIT_FAILURE);
}
/**
***************************************************************
****************************************************************
*/
int
main
(
)
{
ALLEGRO_DISPLAY*
display;
ALLEGRO_EVENT_QUEUE*
queue;
ALLEGRO_TIMER*
timer;
int
screenx =
800
;// dimension fenêtre
int
screeny =
600
;
int
x =
screenx /
2
;// position rectangle
int
y =
screeny /
2
;
int
fin =
false
;
bool dessine =
true
;// pour contrôler les opérations d'affichage
/*Pour mieux contrôler le clavier nous allons conserver en
permanence l'état des touches qui nous intéressent dans un
tableau de booléens à part. L'indice de chaque touche est
identifiée dans le tableau par une constante et toutes les
constantes sont réunies dans un enum
*/
enum
{
KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_LEFT, KEY_MAX }
;
bool key[KEY_MAX] =
{
0
}
;
// les initialisations
if
(!
al_init
(
))
erreur
(
"
al_init()
"
);
if
(!
al_init_primitives_addon
(
))
erreur
(
"
al_init_primitives_addon()
"
);
if
(!
al_install_keyboard
(
))
erreur
(
"
al_install_keyboard()
"
);
display =
al_create_display
(
screenx, screeny);
if
(!
display)
erreur
(
"
al_create_display()
"
);
queue =
al_create_event_queue
(
);
if
(!
queue)
erreur
(
"
al_create_event_queue()
"
);
timer =
al_create_timer
(
1
.0
/
30
);
if
(!
timer)
erreur
(
"
al_create_timer()
"
);
// enregistrement événements
al_register_event_source
(
queue,
al_get_display_event_source
(
display));
al_register_event_source
(
queue,
al_get_keyboard_event_source
(
));
al_register_event_source
(
queue,
al_get_timer_event_source
(
timer));
// démarrer le timer
al_start_timer
(
timer);
while
(!
fin){
ALLEGRO_EVENT event;
al_wait_for_event
(
queue, &
event);
if
(
event.type ==
ALLEGRO_EVENT_DISPLAY_CLOSE)
fin =
true
;
else
if
(
event.type ==
ALLEGRO_EVENT_KEY_DOWN){
switch
(
event.keyboard.keycode){
case
ALLEGRO_KEY_UP: key[KEY_UP] =
true
; break
;
case
ALLEGRO_KEY_RIGHT: key[KEY_RIGHT] =
true
; break
;
case
ALLEGRO_KEY_DOWN: key[KEY_DOWN] =
true
; break
;
case
ALLEGRO_KEY_LEFT: key[KEY_LEFT] =
true
; break
;
}
}
else
if
(
event.type ==
ALLEGRO_EVENT_KEY_UP){
switch
(
event.keyboard.keycode){
case
ALLEGRO_KEY_UP: key[KEY_UP] =
false
; break
;
case
ALLEGRO_KEY_RIGHT:key[KEY_RIGHT] =
false
; break
;
case
ALLEGRO_KEY_DOWN: key[KEY_DOWN] =
false
; break
;
case
ALLEGRO_KEY_LEFT: key[KEY_LEFT] =
false
; break
;
case
ALLEGRO_KEY_ESCAPE: fin =
true
; break
;
}
}
else
if
(
event.type ==
ALLEGRO_EVENT_TIMER){
y -=
key[KEY_UP] *
10
; //(true vaut 1 et false 0)
x +=
key[KEY_RIGHT] *
10
;
y +=
key[KEY_DOWN] *
10
;
x -=
key[KEY_LEFT] *
10
;
dessine =
true
;
}
// sur événement timer ET dernière touche appuyée prise
// en compte
if
(
dessine ==
true
&&
al_is_event_queue_empty
(
queue)){
// effacer le double buffer
al_clear_to_color
(
NOIR);
// afficher le rectangle à sa position courantes
// dans le double buffer
al_draw_filled_rectangle
(
x, y, x +
20
, y +
20
, BLEU);
// passer le double buffer à l'écran
al_flip_display
(
);
dessine =
false
;
}
}
al_destroy_timer
(
timer);
al_destroy_display
(
display);
al_destroy_event_queue
(
queue);
return
0
;
}
/**
***************************************************************
****************************************************************
*/
Attention, il n'y a toujours pas de contrôle des bords et le rectangle peut sortir de la fenêtre.
VI-G. Constituer un modèle de projet (template)▲
Nous avons présenté comment réaliser un modèle de projet dans un chapitre précédent. C'est peut-être le moment d'en faire un second qui intègre la gestion des principaux événements.
Voici une nouvelle proposition de départ plus complète dans une perspective de jeu qui installe primitives de dessin, clavier, timer, file d'événements, boucle d'événements avec filtrage des événements fenêtre, clavier et timer. Elle prévoit également le rafraîchissement des affichages d'une animation. À la sortie de la boucle d'événements, la libération mémoire des composants est également assurée.
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
#include <allegro5/allegro_primitives.h>
void
erreur
(
const
char
*
txt)
{
ALLEGRO_DISPLAY*
d;
d =
al_is_system_installed
(
) ? al_get_current_display
(
) : NULL
;
al_show_native_message_box
(
d, "
ERREUR
"
, txt, NULL
, NULL
, 0
);
exit
(
EXIT_FAILURE);
}
/**
***************************************************************
****************************************************************
*/
int
main
(
)
{
ALLEGRO_DISPLAY*
display;
ALLEGRO_EVENT_QUEUE*
queue;
ALLEGRO_TIMER*
timer;
int
screenx =
800
;
int
screeny =
600
;
bool fin =
0
;
bool dessine =
true
;
if
(!
al_init
(
))
erreur
(
"
al_init()
"
);
if
(!
al_init_primitives_addon
(
))
erreur
(
"
al_init_primitives_addon()
"
);
if
(!
al_install_keyboard
(
))
erreur
(
"
al_install_keyboard()
"
);
display =
al_create_display
(
screenx, screeny);
if
(!
display)
erreur
(
"
al_create_display()
"
);
queue =
al_create_event_queue
(
);
if
(!
queue)
erreur
(
"
al_create_event_queue()
"
);
timer =
al_create_timer
(
1
.0
/
30
);
if
(!
timer)
erreur
(
"
al_create_timer()
"
);
// enregistrement événements
al_register_event_source
(
queue,
al_get_display_event_source
(
display));
al_register_event_source
(
queue,
al_get_keyboard_event_source
(
));
al_register_event_source
(
queue,
al_get_timer_event_source
(
timer));
// démarrer le timer
al_start_timer
(
timer);
while
(!
fin){
ALLEGRO_EVENT event;
al_wait_for_event
(
queue, &
event);
if
(
event.type ==
ALLEGRO_EVENT_DISPLAY_CLOSE)
fin =
true
;
else
if
(
event.type ==
ALLEGRO_EVENT_KEY_DOWN){
switch
(
event.keyboard.keycode){
case
ALLEGRO_KEY_ESCAPE:
fin =
true
;
break
;
}
}
else
if
(
event.type ==
ALLEGRO_EVENT_TIMER){
// mouvement
// redessiner
dessine =
true
;
}
if
(
dessine ==
true
&&
al_is_event_queue_empty
(
queue)){
// opérations d'affichage
// 1 effacer le double buffer
al_clear_to_color
(
al_map_rgb
(
0
, 0
, 0
));
// 2 afficher dessins et images dans le double buffer
// 3 passer le double buffer à l'écran
al_flip_display
(
);
dessine =
false
;
}
}
al_destroy_display
(
display);
al_destroy_timer
(
timer);
al_destroy_event_queue
(
queue);
return
0
;
}
Avant de passer à la sauvegarde du projet en tant que modèle, compiler le projet en mode debug et en mode release et vérifiez qu'il n'y pas de bogue.
Une fois ces opérations terminées, sauvegarder le projet sous forme de modèle.
VI-H. Comprendre l'implémentation des événements▲
Il nous paraît intéressant du point de vue de la programmation d'explorer un peu comment sont implémentés les événements sous Allegro 5 et notamment l'utilisation de l'union ALLEGRO_EVENT . Cette exploration montre le potentiel de la bibliothèque qui continue de faire l'objet de nombreux développements. Pour l'heure nous n'avons pas encore testé toutes les possibilités révélées ici.
VI-H-1. Les identificateurs d'événements▲
Nous avons déjà mentionné que chaque type d'événement est identifié par une valeur constante dans le fichier event.h de la bibliothèque Allegro. Plus précisément ces valeurs sont réunies dans l'énumération que voici :
enum
{
ALLEGRO_EVENT_JOYSTICK_AXIS =
1
,
ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN =
2
,
ALLEGRO_EVENT_JOYSTICK_BUTTON_UP =
3
,
ALLEGRO_EVENT_JOYSTICK_CONFIGURATION =
4
,
ALLEGRO_EVENT_KEY_DOWN =
10
,
ALLEGRO_EVENT_KEY_CHAR =
11
,
ALLEGRO_EVENT_KEY_UP =
12
,
ALLEGRO_EVENT_MOUSE_AXES =
20
,
ALLEGRO_EVENT_MOUSE_BUTTON_DOWN =
21
,
ALLEGRO_EVENT_MOUSE_BUTTON_UP =
22
,
ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY =
23
,
ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY =
24
,
ALLEGRO_EVENT_MOUSE_WARPED =
25
,
ALLEGRO_EVENT_TIMER =
30
,
ALLEGRO_EVENT_DISPLAY_EXPOSE =
40
,
ALLEGRO_EVENT_DISPLAY_RESIZE =
41
,
ALLEGRO_EVENT_DISPLAY_CLOSE =
42
,
ALLEGRO_EVENT_DISPLAY_LOST =
43
,
ALLEGRO_EVENT_DISPLAY_FOUND =
44
,
ALLEGRO_EVENT_DISPLAY_SWITCH_IN =
45
,
ALLEGRO_EVENT_DISPLAY_SWITCH_OUT =
46
,
ALLEGRO_EVENT_DISPLAY_ORIENTATION =
47
}
;
VI-H-2. Les structures d'événements▲
Chaque catégorie d'événements est implémentée par une structure. Allegro fournit ainsi les structures suivantes :
ALLEGRO_DISPLAY_EVENT
ALLEGRO_JOYSTICK_EVENT
ALLEGRO_KEYBOARD_EVENT
ALLEGRO_MOUSE_EVENT
ALLEGRO_TIMER_EVENT
ALLEGRO_USER_EVENT
Ces structures ont toutes les trois les mêmes premiers champs qui sont dans l'ordre :
type , source , et timestamp .
Le champ type sert à spécifier l'événement avec un des identificateurs de type d'événement. C'est une valeur ALLEGRO_EVENT_TYPE qui est une redéfinition de unsigned int dans event.h de la façon suivante :
typedef
unsigned
int
ALLEGRO_EVENT_TYPE;
Le champ source est un pointeur qui pointe sur la source de l'événement, fenêtre, souris, clavier, etc. et permet de l'identifier.
Le champ timestamp indique quand l'événement a été généré.
Ces trois champs communs à toutes les structures d'événements sont regroupés grâce à un #define qui les réunit de la façon suivante :
#define _AL_EVENT_HEADER(srctype)
ALLEGRO_EVENT_TYPE type; \
srctype *
source; \
double
timestamp;
Rappelons qu'un #define fournit un texte, ici _AL_EVENT_HEADER , qui se substitue à la séquence de code correspondante dans le programme. Ce texte est remplacé par le code réel lors de la première étape de compilation. Si plusieurs lignes de code sont associées au texte de remplacement du #define , chaque ligne suivie d'une autre ligne doit se terminer par un antislash « \ »
Ce #define _AL_EVENT_HEADERest intégré ensuite comme premier champ dans chacune des structures d'événements.
VI-H-2-a. Structure ALLEGRO_DISPLAY_EVENT▲
VI-H-2-a-i. Définition de la structure ▲
Cette structure est définie comme suit :
typedef
struct
ALLEGRO_DISPLAY_EVENT
{
_AL_EVENT_HEADER
(
struct
ALLEGRO_DISPLAY)
int
x, y;
int
width, height;
int
orientation;
}
ALLEGRO_DISPLAY_EVENT;
Dans la structure, _AL_EVENT_HEADER(struct ALLEGRO_DISPLAY) est remplacé à la compilation par :
ALLEGRO_EVENT_TYPE type;
struct
ALLEGRO_DISPLAY *
source;
double
timestamp;
type : donne le type de l'événement, un événement ALLEGRO_EVENT_DISPLAY_<type>.
source : indique la fenêtre concernée par l'événement.
timestamp : date le moment où l'événement a été généré.
x et y : correspondent à la position du coin haut-gauche de la fenêtre considérée.
width et height spécifient la largeur et la hauteur de la fenêtre Allegro active.
orientation : restitue l'orientation physique de l'écran sur des appareils équipés pour la détecter comme la plupart des téléphones mobiles actuels. Les valeurs possibles sont définies par les macros constantes suivantes :
ALLEGRO_DISPLAY_ORIENTATION_0_DEGREES
ALLEGRO_DISPLAY_ORIENTATION_90_DEGREES
ALLEGRO_DISPLAY_ORIENTATION_180_DEGREES
ALLEGRO_DISPLAY_ORIENTATION_270_DEGREES
ALLEGRO_DISPLAY_ORIENTATION_FACE_UP
ALLEGRO_DISPLAY_ORIENTATION_FACE_DOWN
VI-H-2-a-ii. Événements correspondants ▲
Les événements concernés par une fenêtre ou un écran ALLEGRO_DISPLAY sont les suivants :
ALLEGRO_EVENT_DISPLAY_EXPOSE : la fenêtre ou une partie de celle-ci est devenue visible. Pour rendre possible ce type d'événement dans le programme, la fenêtre doit être créée avec l'option ALLEGRO_GENERATE_EXPOSE_EVENTS activée par la fonction al_set_new_display_flags , par exemple :
al_set_new_display_flags
(
ALLEGRO_GENERATE_EXPOSE_EVENTS) ;
display=
al_create_display
(
800
,600
);
ALLEGRO_EVENT_DISPLAY_RESIZE : la fenêtre a été redimensionnée. Pour rendre possible cet événement, la fenêtre doit être créée avec l'option ALLEGRO_RESIZABLE activée par la fonction al_set_new_display_flags , par exemple :
al_set_new_display_flags
(
ALLEGRO_RESIZABLE) ;
display=
al_create_display
(
800
,600
);
ALLEGRO_EVENT_DISPLAY_CLOSE : événement produit lorsque le bouton de fermeture de la fenêtre est cliqué.
ALLEGRO_EVENT_DISPLAY_LOST : lors de l'utilisation de Direct3D, écran ou fenêtre peuvent être « perdus » ou sans réponse. Dans cet état, les opérations de dessin sont ignorées et des données des pixels de bitmap peuvent être inaccessibles et indéfinies. Le contenu de la fenêtre peut être altéré et il convient de le restaurer ensuite lorsqu'un événement ALLEGRO_EVENT_DISPLAY_FOUNDsurvient.
ALLEGRO_EVENT_DISPLAY_FOUND : cet événement est généré lorsqu'une fenêtre précédemment « perdue » est rétablie dans son fonctionnement normal.
ALLEGRO_EVENT_DISPLAY_SWITCH_OUT : événement produit lorsque la fenêtre devient inactive si par exemple l'utilisateur clique dans une autre fenêtre ou renvoie la fenêtre dans la barre des tâches.
ALLEGRO_EVENT_DISPLAY_SWITCH_IN : événement produit lorsqu'une fenêtre préalablement désactivée se trouve réactivée.
ALLEGRO_EVENT_DISPLAY_ORIENTATION : cet événement est généré lorsque l'orientation de la fenêtre ou de l'écran change sur des appareils capables de la détecter. Cette information est stockée dans le champ orientation de la structure ALLEGRO_DISPLAY_EVENT .
VI-H-2-b. Structure ALLEGRO_JOYSTICK_EVENT▲
VI-H-2-b-i. Définition de la structure ▲
Voici la structure utilisée pour l'utilisation d'un ou plusieurs joysticks :
typedef
struct
ALLEGRO_JOYSTICK_EVENT
{
_AL_EVENT_HEADER
(
struct
ALLEGRO_JOYSTICK)
struct
ALLEGRO_JOYSTICK *
id;
int
stick;
int
axis;
float
pos;
int
button;
}
ALLEGRO_JOYSTICK_EVENT;
En tout premier, nous avons _AL_EVENT_HEADER(struct ALLEGRO_JOYSTICK) remplacé à la compilation par :
ALLEGRO_EVENT_TYPE type;
struct
ALLEGRO_JOYSTICK *
source;
double
timestamp;
type : donne le type de l'événement, un événement ALLEGRO_EVENT_JOYSTICK_<type> .
source : indique le joystick concerné par l'événement.
timestamp : date le moment où l'événement a été généré.
id : contient l'adresse du joystick qui génère l'événement. Ce n'est pas nécessairement la même que celle stockée dans le champ source.
stick : contient le numéro de la manette « stick » utilisée pour un événement qui lui est associé. Le compte des manettes se fait à partir de 0 compris. Un joystick peut regrouper plusieurs manettes chacune disposant de plusieurs axes (horizontal, vertical).
axis : contient le numéro de l'axe concerné par une manette du joystick lors d'un événement associé à cette manette. Pour chaque manette le compte des axes se fait à partir de 0 compris.
pos : contient la position de l'axe entre -1.0 et +1.0
button : indique le numéro du bouton pressé sur le joystick. Ils sont numérotés depuis 0 compris.
VI-H-2-b-ii. Événements correspondants ▲
ALLEGRO_EVENT_JOYSTICK_AXIS : un axe du joystick a changé de valeur. Cet axe est identifié en utilisant les champs id , stick et axis . La position est donnée dans le champ pos.
ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN : un bouton du joystick a été pressé. Le joystick est identifié avec le champ id et le numéro du bouton avec le champ button .
ALLEGRO_EVENT_JOYSTICK_BUTTON_UP : un bouton du joystick a été relevé. Le joystick est identifié avec le champ id et le numéro du bouton avec le champ button .
ALLEGRO_EVENT_JOYSTICK_CONFIGURATION : indique qu'un joystick a été branché ou débranché.
VI-H-2-c. Structure ALLEGRO_KEYBOARD_EVENT▲
VI-H-2-c-i. Définition de la structure▲
Les informations concernant les événements du clavier sont stockées dans une structure du type :
typedef
struct
ALLEGRO_KEYBOARD_EVENT
{
_AL_EVENT_HEADER
(
struct
ALLEGRO_KEYBOARD)
struct
ALLEGRO_DISPLAY *
display;
int
keycode;
int
unichar;
unsigned
int
modifiers;
bool repeat;
}
ALLEGRO_KEYBOARD_EVENT;
Pour toutes les structures d'événement, nous avons en tout premier _AL_EVENT_HEADER(struct ALLEGRO_KEYBOARD) remplacé à la compilation par :
ALLEGRO_EVENT_TYPE type;
struct
ALLEGRO_KEYBOARD *
source;
double
timestamp;
type : donne le type de l'événement, un événement ALLEGRO_EVENT_KEYBOARD_<type> .
source : indique le joystick concerné par l'événement.
timestamp : date le moment où l'événement a été généré.
display : donne la fenêtre sélectionnée qui reçoit les événements du clavier.
keycode : contient le code de la touche qui est pressée. Il est identifié par une macro constante dont le nom commence toujours par ALLEGRO_KEY_ puis est ajouté NOMDELATOUCHE , pour rappel (cf. chapitre Premiers pas avec Allegro 5 - Obtenir le clavierObtenir le clavier).
ALLEGRO_KEY_A ... ALLEGRO_KEY_Z
ALLEGRO_KEY_0 ... ALLEGRO_KEY_9
ALLEGRO_KEY_PAD_0 ... ALLEGRO_KEY_PAD_9
ALLEGRO_KEY_F1 ... ALLEGRO_KEY_F12
ALLEGRO_KEY_ESCAPE
etc.
unichar : contient la valeur unicode d'une touche en tant que caractère. Si la touche ne correspond à aucun caractère et qu'il existe une valeur ASCII c'est cette valeur qui est prise. Ainsi [Tab] vaut 9, [Retour arrière] vaut 13 et [Echap] vaut 27. En revanche s'il n'y a pas d'équivalent ASCII la valeur est soit négative soit 0.
Par ailleurs une combinaison de touche de contrôle avec une touche lettre prend une valeur entre 1 à 26, avec 1 pour A jusque 26 pour Z. Les valeurs des touches [Retour arrière] et [Suppr] peuvent varier selon les plates-formes en prenant respectivement 8 ou 127 et 127 ou 0. Mais ceci n'affecte aucunement le champ keycode .
modifiers : c'est un tableau de bits dans lequel sont consignés les états des touches spéciales comme [Ctrl], [Shift], [Alt], etc. utilisées en combinaison de touches.
Les identifiants de ces touches sont :
ALLEGRO_KEYMOD_SHIFT
ALLEGRO_KEYMOD_CTRL
ALLEGRO_KEYMOD_ALT
ALLEGRO_KEYMOD_LWIN
ALLEGRO_KEYMOD_RWIN
ALLEGRO_KEYMOD_MENU
ALLEGRO_KEYMOD_ALTGR
ALLEGRO_KEYMOD_COMMAND
ALLEGRO_KEYMOD_SCROLLLOCK
ALLEGRO_KEYMOD_NUMLOCK
ALLEGRO_KEYMOD_CAPSLOCK
ALLEGRO_KEYMOD_INALTSEQ
ALLEGRO_KEYMOD_ACCENT1
ALLEGRO_KEYMOD_ACCENT2
ALLEGRO_KEYMOD_ACCENT3
ALLEGRO_KEYMOD_ACCENT4
L'opérateur & permet de savoir si une de ces touches est active, par exemple :
if
(
event.keyboard.modifier &
ALLEGRO_KEYMOD_SHIFT)
printf
(
"
shift pressé
\n
"
) ;
Un exemple complet de combinaison de touches est donné plus bas.
repeat : c'est un booléen. Avec la valeur true il indique la répétition automatique d'un caractère lors d'une pression prolongée sur la touche correspondante.
VI-H-2-c-ii. Événements correspondants ▲
Les événements correspondant au clavier sont les suivants :
ALLEGRO_EVENT_KEY_DOWN : une touche est enfoncée. Il s'agit de la touche physique identifiée par le champ keycode qui contient la valeur associée à cette touche. Le champ display indique quelle est la fenêtre active concernée.
ALLEGRO_EVENT_KEY_UP : une touche est relevée. Il s'agit de la touche physique et non d'un caractère. Elle est identifiée par le champ keycode qui contient la valeur associée à la touche. Le champ display indique quelle est la fenêtre active concernée.
ALLEGRO_EVENT_KEY_CHAR : un caractère est appuyé sur le clavier ou il y a une répétition la touche restant enfoncée. Le code de la touche est accessible dans le champ keycode . Le caractère correspondant à la touche est donné dans le champ unichar . En cas de combinaison de touches, les touches additionnelles sont identifiables dans le tableau de bits modifiers . S'il y a répétition de touche, le champ repeat est à true , à false sinon. Le champ display indique quelle est la fenêtre sélectionnée à l'origine de l'événement.
Les combinaisons de touches ne sont pas forcément évidentes au début. Dans le programme ci-dessous, si l'utilisateur appuie simultanément sur [Alt] ou [Shift] et la touche C, alors une boîte de dialogue s'ouvre et à sa fermeture la fenêtre change de couleur.
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_image.h>
#define SCREENX 800
#define SCREENY 600
#define COLORALEA al_map_rgb(rand()%256,rand()%128,rand()%64)
ALLEGRO_DISPLAY*
allegro_init (
ALLEGRO_EVENT_QUEUE**
queue,
ALLEGRO_TIMER**
timer);
void
erreur (
const
char
*
msg);
/**
***************************************************************
****************************************************************
*/
int
main
(
)
{
ALLEGRO_DISPLAY*
display;
ALLEGRO_EVENT_QUEUE*
queue;
ALLEGRO_TIMER*
timer;
bool fin =
0
;
bool dessine =
true
;
display =
allegro_init
(&
queue, &
timer);
while
(!
fin){
ALLEGRO_EVENT event;
al_wait_for_event
(
queue, &
event);
if
(
event.type ==
ALLEGRO_EVENT_DISPLAY_CLOSE)
fin =
true
;
else
if
(
event.type ==
ALLEGRO_EVENT_KEY_CHAR){
if
(
event.keyboard.keycode ==
ALLEGRO_KEY_ESCAPE)
fin =
true
;
else
if
(
event.keyboard.keycode ==
ALLEGRO_KEY_C &&
(
event.keyboard.modifiers &
(
ALLEGRO_KEYMOD_ALT |
ALLEGRO_KEYMOD_SHIFT))){
al_show_native_message_box
(
display, NULL
, NULL
, "
hello
"
, "
BO
"
, 0
);
al_clear_to_color
(
COLORALEA);
}
}
else
if
(
event.type ==
ALLEGRO_EVENT_TIMER){
}
al_flip_display
(
);
}
al_destroy_display
(
display);
al_destroy_timer
(
timer);
al_destroy_event_queue
(
queue);
return
0
;
}
/**
***************************************************************
****************************************************************
*/
ALLEGRO_DISPLAY*
allegro_init
(
ALLEGRO_EVENT_QUEUE**
queue,
ALLEGRO_TIMER**
timer)
{
ALLEGRO_DISPLAY*
display;
if
(!
al_init
(
))
erreur
(
"
al_init()
"
);
if
(!
al_install_keyboard
(
))
erreur
(
"
al_install_keyboard()
"
);
if
(!
al_init_primitives_addon
(
))
erreur
(
"
al_init_primitives_addon()
"
);
if
(!
al_init_image_addon
(
))
erreur
(
"
al_init_image_addon()
"
);
display =
al_create_display
(
SCREENX, SCREENY);
if
(!
display)
erreur
(
"
al_create_display()
"
);
*
queue =
al_create_event_queue
(
);
if
(!*
queue)
erreur
(
"
al_create_event_queue()
"
);
*
timer =
al_create_timer
(
1
.0
/
30
);
if
(!*
timer)
erreur
(
"
al_create_timer()
"
);
// enregistrement événements
al_register_event_source
(*
queue,
al_get_display_event_source
(
display));
al_register_event_source
(*
queue,
al_get_keyboard_event_source
(
));
al_register_event_source
(*
queue,
al_get_timer_event_source
(*
timer));
// démarrer le timer
al_start_timer
(*
timer);
return
display;
}
/**
***************************************************************
****************************************************************
*/
void
erreur
(
const
char
*
txt)
{
ALLEGRO_DISPLAY*
d;
d =
al_is_system_installed
(
) ? al_get_current_display
(
) : NULL
;
al_show_native_message_box
(
d, "
ERREUR
"
, txt, NULL
, NULL
, 0
);
exit
(
EXIT_FAILURE);
}
/**
***************************************************************
****************************************************************
*/
VI-H-2-d. Structure ALLEGRO_MOUSE_EVENT▲
VI-H-2-d-i. Définition de la structure ▲
Toutes les informations de la souris sont restituées avec une structure du type :
typedef
struct
ALLEGRO_MOUSE_EVENT
{
_AL_EVENT_HEADER
(
struct
ALLEGRO_MOUSE)
struct
ALLEGRO_DISPLAY *
display;
int
x,
y,
z, w;
int
dx, dy, dz, dw;
unsigned
int
button;
float
pressure;
}
ALLEGRO_MOUSE_EVENT;
En tout premier nous avons _AL_EVENT_HEADER(struct ALLEGRO_MOUSE) remplacé à la compilation par :
ALLEGRO_EVENT_TYPE type;
struct
ALLEGRO_MOUSE *
source;
double
timestamp;
type : donne le type de l'événement, un événement ALLEGRO_EVENT_MOUSE_<type> .
source : indique la souris concernée par l'événement.
timestamp : date le moment où l'événement a été généré.
display : contient l'adresse de la fenêtre active concernée par l'événement souris.
x , y sont les coordonnées horizontale et verticale de la souris dans la fenêtre.
z correspond à la position de la molette de défilement verticale de la souris.
w correspond à la position de la molette horizontale de la souris.
dx , dy donnent en pixels le déplacement de la souris depuis sa position précédente.
dz donne une modification de la molette verticale depuis sa dernière position.
dw donne une modification de la molette horizontale depuis sa dernière position.
button correspond au numéro du bouton concerné par un clic ou un relâchement. Ils sont numérotés à partir de 1.
pressure concerne l'utilisation d'un stylet avec une tablette graphique. Peu probablement être également utilisé sur un écran tactile.
VI-H-2-d-ii. Événements correspondants ▲
ALLEGRO_EVENT_MOUSE_AXES : un changement de position est survenu sur l'un des axes de la souris. Les champs concernés sont x , y , dx , dy , z , w , dz et dw .
ALLEGRO_EVENT_MOUSE_BUTTON_DOWN : un bouton de la souris est cliqué. Le champ button contient le numéro du bouton concerné.
ALLEGRO_EVENT_MOUSE_BUTTON_UP : un bouton est relevé. Le champ button contient le numéro du bouton concerné.
ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY : le curseur de la souris entre sur une fenêtre ouverte par l'application. Le champ display donne l'adresse de la fenêtre sélectionnée et les champs x , y , z , w les positions des différents axes de la souris pour cette fenêtre.
ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY : le curseur de la souris quitte une fenêtre de l'application. Le champ display contient l'adresse de la fenêtre sélectionnée et les champs x , y , z , w les positions des différents axes de la souris.
ALLEGRO_EVENT_MOUSE_WARPED : la fonction al_set_mouse_xy a été appelée pour bouger la souris. C'est la fonction suivante :
bool al_set_mouse_xy
(
ALLEGRO_DISPLAY *
display, int
x, int
y)
Le paramètre display contient l'adresse de la fenêtre concernée. Les paramètres x et y donnent la position voulue. Sa particularité est de déclencher un événement ALLEGRO_EVENT_MOUSE_WARPED .
VI-H-2-e. Structure ALLEGRO_TIMER_EVENT▲
VI-H-2-e-i. Définition de la structure ▲
Les événements d'un minuteur (timer) sont gérés avec une structure du type :
typedef
struct
ALLEGRO_TIMER_EVENT
{
_AL_EVENT_HEADER
(
struct
ALLEGRO_TIMER)
int64_t count;
double
error;
}
ALLEGRO_TIMER_EVENT;
Comme pour toutes les structures d'événement nous avons d'abord _AL_EVENT_HEADER(struct ALLEGRO_TIMER) remplacé à la compilation par :
ALLEGRO_EVENT_TYPE type;
struct
ALLEGRO_TIMER *
source;
double
timestamp;
type : donne le type de l'événement, un événement ALLEGRO_EVENT_TIMER .
source : indique le minuteur qui génère l'événement.
timestamp : date le moment où l'événement a été généré.
count : contient la valeur de comptage de la minuterie. Cette valeur peut être utilisée directement. Mais également pour obtenir le temps écoulé la bibliothèque fournit les deux fonctions suivantes :
La fonction al_get_time :
double
al_get_time
(
void
)
Retourne le temps écoulé en seconde depuis l'initialisation d'Allegro dans le programme. Le résultat est indéterminé si la bibliothèque n'est pas initialisée. La résolution dépend de l'implémentation et en général c'est quelques microsecondes (tics d'horloge).
La fonction al_current_time :
double
al_current_time
(
void
)
Exactement la même fonction que al_get_time mais avec un autre nom.
error : usage interne à la bibliothèque.
VI-H-2-e-ii. Un seul événement correspondant ▲
ALLEGRO_EVENT_TIMER : un top du minuteur a eu lieu. Le champ count indique du combientième il s'agit. Le champ source pointe sur le minuteur concerné.
VI-H-2-f. Structure ALLEGRO_USER_EVENT▲
Allegro permet la mise en œuvre d'événements créés par le programmeur. Ces événements sont un peu plus complexes à implémenter. Nous allons nous contenter ici d'en présenter la structure de données avec les bases d'une implémentation.
VI-H-2-f-i. Définition de la structure ▲
Un tel événement utilise une structure du type :
typedef
struct
ALLEGRO_USER_EVENT ALLEGRO_USER_EVENT;
struct
ALLEGRO_USER_EVENT
{
_AL_EVENT_HEADER
(
struct
ALLEGRO_EVENT_SOURCE)
struct
ALLEGRO_USER_EVENT_DESCRIPTOR *
__internal__descr;
intptr_t data1;
intptr_t data2;
intptr_t data3;
intptr_t data4;
}
;
Comme pour tous les événements les trois premiers champs sont déterminés avec _AL_EVENT_HEADER (struct ALLEGRO_TIMER) remplacé à la compilation par :
ALLEGRO_EVENT_TYPE type;
struct
ALLEGRO_TIMER *
source;
double
timestamp;
Le champ type contient le type de l'événement. Il doit être créé par le développeur en utilisant la macro ALLEGRO_GET_EVENT_TYPE . Elle retourne une valeur entière codée sur 32 bits.
Le champ source donne la source de l'événement et le champ timestamp le moment du déclenchement.
Le champ suivant est un pointeur à usage interne qui sert au fonctionnement mais ne doit pas être modifié. C'est en quelque sorte une donnée privée.
Un événement user dispose de quatre champs que le développeur doit définir lui-même. Ce sont des valeurs entières int, unsigned int en 32 ou 64 bits qu'il est suggéré d'utiliser éventuellement aussi comme pointeurs génériques en respectant les conversions nécessaires. À cet effet le type intptr_t est défini dans le fichier astdint.h de la bibliothèque Allegro 5. Cette définition dépend de l'implémentation (Windows ou autre). Elle est un peu longue et il n'est pas utile de la présenter ici.
Les données d'un événement personnalisé correspondent aux champs data1 , data2 et data3 et data4 .
VI-H-2-f-ii. Aperçu de la mise en œuvre ▲
La structure de données pour un événement personnalisé a besoin d'une source, d'un événement et de valeurs (les données de l'événement). Dans le code, nous aurons :
ALLEGRO_EVENT_SOURCE ma_source_event;
ALLEGRO_EVENT mon_event;
float
une_donnee;
Ensuite il y a quatre opérations à prévoir :
1) l'initialisation d'une source personnalisée d'événements :
al_init_user_event_source
(&
ma_source_event);
2) la définition d'un type d'événement :
mon_event.user.type =
ALLEGRO_GET_EVENT_TYPE
(
'
M
'
, '
I
'
, '
N
'
, '
E
'
);
3) l'initialisation des données de l'événement :
mon_event.user.data1 =
1
;
mon_event.user.data2 =
(
intptr_t)&
une_donnee;
4) le déclenchement d'un événement :
al_emit_user_event
(&
ma_source_event, &
mon_event, NULL
);
VI-H-3. L'union ALLEGRO_EVENT▲
Voici le fameux union que nous utilisons dans chaque boucle d'événements.
VI-H-3-a. Rappel sur le type union▲
Pour rappel un union est une variable unique mais avec un choix de types. L'espace mémoire de la variable correspond à celui nécessaire pour le type le plus grand. Par exemple :
typedef
union
{
char
c;
int
i;
float
f;
double
d;
}
utest;
En C++ le typedef est automatique et l'on peut écrire :
union
utest{
char
c;
int
i;
float
f;
double
d;
}
;
Cet union utest définit le type d'une variable qui peut être soit un char , soit un int soit un float soit un double . Il y a une seule et unique variable sur un seul espace mémoire qui prend la taille mémoire nécessaire pour coder le plus grand type. Le choix du type pour la variable s'effectue en utilisant l'opérateur point comme pour une structure. Par exemple :
int
main
(
)
{
utest u;
printf
(
"
%d
\n
"
, sizeof
(
u));
u.c =
'
A
'
;
// imprime 8, taille du double
// u pris comme char
printf
(
"
%c, %d octets
\n
"
, u.c, sizeof
(
u));
u.i =
50
;
// u pris comme int
printf
(
"
%d, %d octets
\n
"
, u.i, sizeof
(
u));
u.f =
1
.5f
; // u pris comme float
printf
(
"
%f, %d octets
\n
"
, u.f, sizeof
(
u));
u.d =
2
.5
;
// u pris comme double
printf
(
"
%lf, %d octets
\n
"
, u.d, sizeof
(
u));
// selon le type choisi l'utilisation de la variable doit être
// cohérente :
u.c =
-
129
;
printf
(
"
%d
\n
"
, u.c); // provoque une alerte : troncature
// imprime 127
u.i =
1
.5f
;
printf
(
"
%d
\n
"
, u.f); // provoque une alerte : troncature
// imprime 1
system
(
"
PAUSE
"
);
return
0
;
}
VI-H-3-b. Détail de l'union ALLEGRO_EVENT▲
L'union ALLEGRO_EVENT présente en une forme unique n'importe laquelle des structures d'événement. Il est implémenté de la façon suivante :
typedef
union
ALLEGRO_EVENT ALLEGRO_EVENT;
union
ALLEGRO_EVENT
{
ALLEGRO_EVENT_TYPE type;
ALLEGRO_ANY_EVENT any;
ALLEGRO_DISPLAY_EVENT display;
ALLEGRO_JOYSTICK_EVENT joystick;
ALLEGRO_KEYBOARD_EVENT keyboard;
ALLEGRO_MOUSE_EVENT mouse;
ALLEGRO_TIMER_EVENT timer;
ALLEGRO_USER_EVENT user;
}
;
Dans tous les cas, le champ type doit permettre de lire le type de l'événement quelle que soit la structure de l'événement. C'est pourquoi il doit absolument être commun à toutes les structures et venir en premier dans chaque structure d'événement via le #define _AL_EVENT_HEADER .
Le champ suivant any correspond à une structure définie comme suit :
typedef
struct
ALLEGRO_ANY_EVENT
{
_AL_EVENT_HEADER
(
ALLEGRO_EVENT_SOURCE)
}
ALLEGRO_ANY_EVENT;
Cette structure reprend les éléments d'informations communs à toutes les structures dans le même ordre au début, type , source et timestamp sans être obligé de passer par un type particulier d'événement.
S'il s'agit d'un événement fenêtre, c'est le champ display qui est concerné et l'union contient une structure ALLEGRO_DISPLAY_EVENT initialisée en conséquence. Pour un événement joystick, l'union contient une structure ALLEGRO_JOYSTICK_EVENT accessible via le champ joystick.
Pour un événement clavier, l'union contient une structure ALLEGRO_KEYBOARD_EVENT accessible avec le champ keyboard .
Pour un événement souris, c'est une structure ALLEGRO_MOUSE_EVENT accessible par le champ mouse .
Pour un événement minuteur, l'union contient une structure ALLEGRO_TIMER_EVENT accessible par le champ timer .
Pour un événement utilisateur, c'est une structure ALLEGRO_USER_EVENT accessible avec le champ user .
La fenêtre intellisense du Visual Studio permet de visualiser ces informations. Par exemple en tapant :
ALLEGRO_EVENT event ;
event. <
ici s'
ouvre la fenêtre qui permet de visualiser les informations de l
'
union
>
Obtenir ce livre▲
Ce document est une retranscription autorisée du livre Allegro 5 - Programmation de jeux en C et C++ écrit par Frédéric DROUILLON. Initialement publié aux éditions ENI, vous pouvez commander le livre sur le site de l'éditeur. |