I. Introduction

Pour lire ce tutoriel, il est nécessaire d'avoir un minimum de connaissances sur la bibliothèque SDL. Ainsi, nous ne reviendrons pas sur les bases telles que l'ouverture d'une fenêtre, l'affichage des images ou la gestion des événements. Pour cela, n'hésitez pas à regarder les autres tutoriels sur la SDL de Developpez.com, notamment le tutoriel de Loka sur la gestion des événements. Ce tutoriel ne s'intéressera qu'à la gestion des joysticks.

De base, la SDL propose un ensemble de fonctions pour la gestion des joysticks. Ce tutoriel a pour but de vous introduire à ces fonctions et de vous apprendre à les utiliser. Rassurez-vous, il n'y a rien de compliqué.

II. Les fonctions

Comme pour toutes fonctions de la SDL, il vous faut inclure le fichier SDL.h.

Afin d'utiliser les fonctionnalités liées aux joysticks, vous devez indiquer à la SDL de démarrer le module approprié. Cela peut être fait avec la fonction SDL_Init(), ou plus tard dans le programme avec SDL_InitSubSystem().Le paramètre du module des joysticks est SDL_INIT_JOYSTICK.

Dans la SDL, un joystick se présente comme une ressource (tel un fichier), qui s'ouvre et qui se ferme. Comme il est possible de gérer plusieurs joysticks à la fois, il faut tout d'abord déterminer le nombre de joysticks présents sur la machine.

  • int SDL_NumJoysticks(void)
    retourne le nombre de joysticks présents sur la machine.

Suivant le nombre retourné par SDL_NumJoysticks(), vous pouvez utiliser SDL_JoystickOpen() pour utiliser un joystick.

  • SDL_Joystick* SDL_JoystickOpen(int index)
    index : le énième joystick présent sur la machine.
    retourne un pointeur sur la structure SDL_Joystick ou NULL si l'ouverture a échouée.

Une fois le joystick initialisé, il est possible d'indiquer la façon dont vous voulez récupérer les actions de l'utilisateur avec la fonction SDL_JoystickEventState(). Vous pouvez passer soit SDL_QUERY, pour récupérer l'état actuel, SDL_ENABLE pour activer la gestion des événements à travers SDL_WaitEvent() ou SDL_PollEvent() ou SDL_DISABLE pour ne pas recevoir d'événement à chaque action de l'utilisateur.

  • int SDL_JoystickEventState(int state)
    state : si oui (SDL_ENABLE) ou non (SDL_DISABLE) vous voulez récupérer les actions de l'utilisateur à travers le système d'événements de la SDL. Il est possible d'utiliser la valeur spéciale SDL_QUERY pour connaître l'état actuel.
    retourne l'état actuellement défini si state est SDL_QUERY ou state dans les autres cas.

La documentation indique qu'il est préférable d'utiliser la boucle événementielle.

Si vous avez désactivé la gestion des joysticks à travers le système d'événements, il faudra appeler régulièrement SDL_JoystickUpdate() pour mettre à jour les états des joysticks.

La fonction SDL_JoystickUpdate() est appelée automatiquement par la boucle événementielle (lorsque SDL_JoystickEventState() a reçu la valeur SDL_ENABLE).

Si vous avez décidé d'utiliser les états pour connaître les boutons utilisés par le joueur, vous pouvez récupérer leurs valeurs avec SDL_JoystickGetButton().

  • Uint8 SDL_JoystickGetButton(SDL_Joystick* joystick, int button)
    joystick : le joystick pour lequel vous voulez connaître l'état.
    button : le bouton pour lequel vous voulez connaître l'état.
    retourne 1 si le bouton est appuyé, sinon 0.

Vous pouvez aussi récupérer l'état des joysticks, des chapeaux et des trackballs de la manette avec SDL_JoystickGetAxis(), SDL_JoystickGetHat() et SDL_JoystickGetBall().

  • Sint16 SDL_JoystickGetAxis(SDL_Joystick* joystick, int axis)
    joystick : le joystick pour lequel vous voulez des informations.
    axis : l'axe du joystick pour lequel vous voulez connaître la valeur.
    retourne un nombre entre -32768 et 32768. Au point mort, l'axe retourne 0.
  • Uint8 SDL_JoystickGetHat(SDL_Joystick* joystick, int hat)
    joystick : le joystick pour lequel vous voulez des informations.
    hat : le chapeau dont vous voulez connaître l'état.
    retourne une combinaison des valeurs suivantes : SDL_HAT_CENTERED, SDL_HAT_UP, SDL_HAT_RIGHT, SDL_HAT_DOWN, SDL_HAT_LEFT, SDL_HAT_RIGHTUP, SDL_HAT_RIGHTDOWN, SDL_HAT_LEFTUP, SDL_HAT_LEFTDOWN.
  • int SDL_JoystickGetBall(SDL_Joystick* joystick, int ball, int* dx, int* dy)
    joystick : le joystick pour lequel vous voulez des informations.
    ball : le numéro du trackball pour lequel vous voulez connaître le mouvement.
    dx : le déplacement sur l'axe des X depuis le dernier appel à la fonction SDL_JoystickGetBall().
    dy : le déplacement sur l'axe des Y depuis le dernier appel à la fonction SDL_JoystickGetBall().
    retourne 0 en cas de réussite ou -1 si une erreur de lecture a été rencontrée.

Finalement, il ne faut pas oublier de fermer les joysticks après utilisation.

En plus de ces fonctions, la bibliothèque SDL apporte d'autres fonctions pour récupérer des informations sur le joystick.

Vous pouvez connaître le nom du périphérique avec SDL_JoystickName(), si le joystick est déjà ouvert avec SDL_JoystickOpened(), le numéro du joystick avec SDL_JoystickIndex(), le nombre d'axes, de trackballs, de chapeaux et de boutons avec SDL_JoystickNumAxes(), SDL_JoystickNumBalls(), SDL_JoystickNumHats() et SDL_JoystickNumButtons(), respectivement.

  • const char* SDL_JoystickName(int index)
    index : le numéro du joystick pour le système.
    retourne le nom du joystick dans une chaîne de caractères.
  • int SDL_JoystickOpened(int index)
    index : le numéro du joystick pour le système.
    retourne 1 si le joystick est déjà ouvert, sinon 0.
  • int SDL_JoystickIndex(SDL_Joystick* joystick)
    joystick : le joystick pour lequel vous voulez l'index.
    retourne le numéro du joystick pour le système.
  • int SDL_JoystickNumAxes(SDL_Joystick* joystick)
    joystick : le joystick pour lequel vous voulez le nombre d'axes.
    retourne le nombre d'axes disponibles pour ce joystick.
  • int SDL_JoystickNumBalls(SDL_Joystick* joystick)
    joystick : le joystick pour lequel vous voulez le nombre de trackballs.
    retourne le nombre de trackballs disponibles pour ce joystick.
  • int SDL_JoystickNumHats(SDL_Joystick* joystick)
    joystick : le joystick pour lequel vous voulez le nombre de chapeaux.
    retourne le nombre de chapeaux disponibles pour ce joystick.
  • int SDL_JoystickNumButtons(SDL_Joystick* joystick)
    joystick : le joystick pour lequel vous voulez le nombre de boutons.
    retourne le nombre de boutons disponibles pour ce joystick.

Connaissant les fonctions que propose la SDL, nous pouvons maintenant écrire le code pour utiliser les joysticks.

III. Utilisation

L'utilisation des joysticks avec la SDL se décompose en trois parties, l'initialisation avec l'ouverture du ou des joysticks, la récupération des actions de l'utilisateur puis la fermeture des joysticks.

III-A. Initialisation

Pour commencer, il faut connaître le nombre de joysticks présents sur la machine. Dans l'exemple suivant, le programme vérifie si au moins un joystick est présent et, dans ce cas, ouvre le premier :

 
Sélectionnez
// Démarre SDL avec la gestion des joysticks
if ( SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1 )
{
    fprintf(stderr,"Erreur lors de l'initialisation de la SDL\n");
    return -1;
}

SDL_Joystick* pJoystick = NULL;
int numJoystick = SDL_NumJoysticks(); // Compte le nombre de joysticks
printf("Vous avez %d joysticks sur cette machine\n",numJoystick);
if ( numJoystick >= 1 )
{
    // Ouvre le premier joystick présent sur la machine
    pJoystick = SDL_JoystickOpen(0);
    if ( pJoystick == NULL )
    {
        fprintf(stderr,"Erreur pour ouvrir le premier joystick\n");
    }
}

Si vous souhaitez connaître plus d'information sur votre périphérique, vous pouvez utiliser le code suivant :

 
Sélectionnez
printf("Informations du joystick\n");
printf("Nom : %s\n",SDL_JoystickName(0));
printf("Nombre d'axes : %d\n",SDL_JoystickNumAxes(pJoystick));
printf("Nombre de chapeaux : %d\n",SDL_JoystickNumHats(pJoystick));
printf("Nombre de trackballs : %d\n",SDL_JoystickNumBalls(pJoystick));
printf("Nombre de boutons : %d\n",SDL_JoystickNumButtons(pJoystick));

qui, pour une manette XBOX 360, vous affichera :

Informations du joystick

Nom : XBOX 360 For Windows (Controller)

Nombre d'axes : 5

Nombre de chapeaux : 1

Nombre de trackballs : 0

Nombre de boutons : 10

III-B. Récupération des entrées

Avec la SDL, il existe deux techniques pour récupérer les informations sur les actions des joueurs : soit en utilisant le système d'événements, soit le système d'états. Pour sélectionner la méthode que vous voulez utiliser, vous devez appeler la fonction SDL_JoystickEventState() avec SDL_ENABLE ou SDL_DISABLE.

III-B-1. Avec le système d'événements

Pour récupérer les événements, vous devez utiliser la fonction SDL_WaitEvent() ou SDL_PollEvent(). Celles-ci remplissent une structure SDL_Event selon le type de l'événement. La sous-structure possède les valeurs liées à l'événement telles que la nouvelle position du chapeau, le déplacement de la trackball, la nouvelle position du joystick… Voici un tableau récapitulatif des types d'événements liés aux joysticks et de la structure de ces événements :

Type Champ Valeur
SDL_JOYAXISMOTION
Désigne un mouvement d'un joystick de la manette (plus précisément, un axe).
jaxis
  • Uint8 type : SDL_JOYAXISMOTION.
  • Uint8 which : numéro du joystick.
  • Uint8 axis : numéro de l'axe.
  • Sint16 value : valeur de l'axe (de -32768 à 32768).
SDL_JOYBUTTONDOWN
et
SDL_JOYBUTTONUP
Lorsqu'un bouton est appuyé ou relâché.
jbutton
  • Uint8 type : SDL_JOYBUTTONDOWN ou SDL_JOYBUTTONUP.
  • Uint8 which : numéro du joystick.
  • Uint8 button : numéro du bouton.
  • Uint8 state : SDL_PRESSED (pour appuyé) ou SDL_RELEASED (pour relâché).
SDL_JOYHATMOTION
Désigne un mouvement d'un chapeau de la manette.
jhat
  • Uint8 type : SDL_JOYHATMOTION.
  • Uint8 which : numéro du joystick.
  • Uint8 hat : numéro du chapeau.
  • Uint8 value : une valeur parmi SDL_HAT_CENTERED, SDL_HAT_UP, SDL_HAT_DOWN, SDL_HAT_RIGHT, SDL_HAT_LEFT représentant respectivement lorsque le chapeau est centré, sur le haut, sur le bas, sur la droite et sur la gauche. De plus, il existe les valeurs SDL_HAT_RIGHTUP, SDL_HAT_RIGHTDOWN, SDL_HAT_LEFTUP et SDL_HAT_LEFTDOWN pour les combinaisons droite et haut, droite et bas, gauche et haut puis gauche et bas.
SDL_JOYBALLMOTION
Désigne un mouvement d'une trackball de la manette.
jball
  • Uint8 type : SDL_JOYBALLMOTION.
  • Uint8 which : numéro du joystick.
  • Uint8 ball : numéro de la trackball.
  • Sint16 xrel : déplacement sur l'axe des X.
  • Sint16 yrel : déplacement sur l'axe des Y.

En conclusion, si vous recevez un événement de type SDL_JOYBUTTONDOWN, vous pouvez récupérer son numéro dans un code similaire à celui-ci :

 
Sélectionnez
SDL_Event event;
SDL_WaitEvent(&event);
if ( event.type == SDL_JOYBUTTONDOWN )
{
    printf("Bouton : %d\n",event.jbutton.button);
}

Afin d'activer la gestion des joysticks par le système d'événements il faut appeler la fonction SDL_JoystickEventState() avec la paramètre SDL_ENABLE.

Voici un code qui affiche sur la console tous les événements reçus :

 
Sélectionnez
// On indique que l'on souhaite utiliser le système d'événements
SDL_JoystickEventState(SDL_ENABLE);

if ( event.type == SDL_JOYAXISMOTION )
{
    // Mouvement d'un axe
    // Nous devons donc utiliser le champ jaxis
    printf("Mouvement d'un axe\n");
    printf("%d est la nouvelle valeur de l'axe %d pour le joystick %d\n",
           event.jaxis.value,
           event.jaxis.axis,
           event.jaxis.which);
}
else if ( event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP )
{
    // Bouton appuyé ou relâché
    // Nous devons donc utiliser le champ jbutton
    if ( event.jbutton.state == SDL_PRESSED )
    {
        printf("Appui sur le bouton %d du joystick %d\n",
                event.jbutton.button,
                event.jbutton.which);
    }
    else if ( event.jbutton.state == SDL_RELEASED )
    {
        printf("Relâchement sur le bouton %d du joystick %d\n",
                event.jbutton.button,
                event.jbutton.which);
    }
}
else if ( event.type == SDL_JOYBALLMOTION )
{
    // Mouvement de trackball
    // Nous devons donc utiliser le champ jball
    printf("Mouvement du trackball\n");
    printf("La balle %d du joystick %d a bougé de %d;%d\n",
           event.jball.ball,
           event.jball.which,
           event.jball.xrel,
           event.jball.yrel);
}
else if ( event.type == SDL_JOYHATMOTION )
{
    // Mouvement d'un chapeau
    // Nous devons donc utiliser le champ jhat
    printf("Mouvement du chapeau %d du joystick %d\n",event.jhat.hat,event.jhat.which);
    if ( event.jhat.value == SDL_HAT_CENTERED )
    {
        printf("Retour position neutre\n");
    }
    if ( event.jhat.value == SDL_HAT_DOWN )
    {
        printf("Position bas\n");
    }
    if ( event.jhat.value == SDL_HAT_LEFT )
    {
        printf("Position gauche\n");
    }
    if ( event.jhat.value == SDL_HAT_RIGHT )
    {
        printf("Position droite\n");
    }
    if ( event.jhat.value == SDL_HAT_UP )
    {
        printf("Position haut\n");
    }
}

III-B-2. Avec le système d'états

Le système d'états permet de connaître l'état des joysticks à n'importe quel moment du programme. Pour l'utiliser, il faut d'abord appeler SDL_JoystickEventState() avec le paramètre SDL_DISABLE.

Afin de connaître l'état des axes, boutons, chapeaux et trackballs, il faut utiliser les fonctions SDL_JoystickGetAxis(), SDL_JoystickGetButton(), SDL_JoystickGetHat() et SDL_JoystickGetBall() respectivement.

De plus, il faut appeler SDL_JoystickUpdate(), chaque fois que l'on souhaite renouveler les informations que la SDL possède sur les joysticks.

Le code suivant affiche tous les états disponibles pour un joystick :

 
Sélectionnez
// Gestion du joystick
SDL_JoystickUpdate(); // Mise à jour de l'état des joysticks
int i;
// Analyse des axes
for ( i = 0 ; i < SDL_JoystickNumAxes(pJoystick) ; i++ )
{
    printf("Axe %d à la valeur %d\n",i,SDL_JoystickGetAxis(pJoystick,i));
}

// Analyse des boutons
for ( i = 0 ; i < SDL_JoystickNumButtons(pJoystick) ; i++ )
{
    if ( SDL_JoystickGetButton(pJoystick,i) )
    {
        printf("Bouton %d appuyé\n",i);
    }
    else
    {
        printf("Bouton %d n'est pas appuyé\n",i);
    }
}

// Analyse des chapeaux
for ( i = 0 ; i < SDL_JoystickNumHats(pJoystick) ; i++ )
{
    Uint8 hatValue = SDL_JoystickGetHat(pJoystick,i);
    printf("Chapeau %d : ",i);
    if ( hatValue == SDL_HAT_CENTERED )
    {
        printf("Retour position neutre\n");
    }
    if ( hatValue == SDL_HAT_DOWN )
    {
        printf("Position bas\n");
    }
    if ( hatValue == SDL_HAT_LEFT )
    {
        printf("Position gauche\n");
    }
    if ( hatValue == SDL_HAT_RIGHT )
    {
        printf("Position droite\n");
    }
    if ( hatValue == SDL_HAT_UP )
    {
        printf("Position haut\n");
    }
}

// Analyse du déplacement des trackballs
for ( i = 0 ; i < SDL_JoystickNumBalls(pJoystick) ; i++ )
{
    int dx = 0;
    int dy = 0;
    if ( SDL_JoystickGetBall(pJoystick,i,&dx,&dy) == 0)
    {
        printf("Trackball %d a été déplacée %d;%d\n",i,dx,dy);
    }
    else
    {
        fprintf(stderr,"Erreur de lecture de la trackball %d\n",i);
    }
}

III-C. Fermeture

Une fois que vous avez fini d'utiliser le joystick, il ne faut pas oublier de le fermer. Pour cela, la SDL propose la fonction SDL_JoystickClose().

Exemple :

 
Sélectionnez
SDL_JoystickClose(pJoystick);

IV. Code source

Vous pouvez retrouver le code source de ce tutoriel, dans deux projets distincts pour Code::Blocks sous Windows. Le but de ces programmes est d'afficher sur la console les informations du premier joystick. SDL_Joy_Event est un court programme d'exemple utilisant le système d'événements. SDL_Joy_State est un programme similaire mais utilisant le système d'états.

V. Liens

VI. Remerciements

Je tiens à remercier Winjerome pour ses divers retours sur l'article ainsi que f-leb pour ses corrections.