Contrôle du Joystick sous Windows

gRRosminet

- Titre : Contrôle du Joystick sous Windows

- Auteur : gRRosminet

- Parution : 6 novembre 2002

- Licence : Copyright ® 2002 gRRosminet. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction ♪

- II. Fonctionnement

- III. Obtenir les caractéristiques du joystick

- III-A. Liste des périphériques connectés

- III-B. Caractéristiques d'un périphérique

- IV. La capture du Joystick

- IV-A. La capture

- IV-B. Les messages associés à la capture

- V. L'interrogation du Joystick

I - Introduction ♪

Aujourd'hui, de plus en plus de monde a son joystick posé sur le coin du bureau. Grce aux efforts des designers, on peut dire que si on n’en a pas trop l'usage (tout le monde n'est pas passionné par Flight Simulator et ses congénères), ça fait presque « œuvre d'art ». Et dans un monde où les PC sont taillés à la hache, c'est déjà pas mal ! Cependant, pour faire plaisir aux gestionnaires, on aimerait bien leur trouver une utilité supplémentaire (surtout pour que Mademoiselle notre copine arrête de nous dire « mais t'as encore acheté un truc inutile ?!? Ça va te servir à quoi ? T'en avais pas assez comme ça pour encombrer le bureau ?!"). Hé bien que diriez-vous si on en faisait une télécommande ? Certes elle est à fil, mais c'est tout de même mieux que de se lever pour aller monter le volume du film quand c'est pas assez fort et inversement.

Je me propose donc d'étudier avec vous les possibilités que nous offre Windows pour la gestion des joysticks à travers la réalisation d'une télécommande.

II - Fonctionnement

Afin de mieux utiliser les fonctions associées au joystick, voyons tout d'abord comment il fonctionne. Tout d'abord, le joystick est un périphérique passif, c'est-à-dire que contrairement au clavier, il ne va pas déclencher d'évènements dans le système (interruption ou autre). C'est donc le système d'exploitation qui est chargé, via le pilote associé, d'aller interroger le joystick sur son état. Bien sûr, pour éviter de surcharger le système, cette interrogation est faite en temps réel, à la demande des programmes clients. Afin de différencier chacun des périphériques connectés à la machine, Windows leur attribue un numéro identifiant. Cet identifiant est celui affiché dans le panneau de contrôle des périphériques de jeu auquel il faut retrancher 1 (pourquoi faire simple quand on peut faire compliqué ?). Le système peut supporter jusqu'à 16 périphériques et fourni pour répondre aux besoins les plus courants deux constantes : JOYSTICKID1 et JOYSTICKID2 qui correspondent aux deux premiers périphériques (héritage de Windows 3.x et précédents).

Avant toute chose, on va devoir commencer par apprendre le joystick qu'on veut manipuler. En effet, comment gérer un joystick si on ne connaît pas ses caractéristiques ? Nombre de boutons ? Les types d'axes ? Y a-t-il un point de vue ? Nous allons donc commencer par nous renseigner sur les capacités du joystick, puis voir le fonctionnement de la capture avant de nous enfoncer dans les méandres de l'interrogation du joystick.

III - Obtenir les caractéristiques du joystick

III-A - Liste des périphériques connectés

Avant de rechercher les caractéristiques d'un joystick, il faut nous assurer qu'il est connecté. Pour cela nous allons utiliser la fonction joyGetPos() qui renvoie deux constantes : JOYERR_NOERROR si le joystick est connecté, ou JOYERR_UNPLUGGED si le joystick n'est pas connecté. Cependant, ce n'est pas parce que le joystick d'ID 0 n'est pas connecté qu'aucun ne l'est : il faut tester tous les ID. Voyons un exemple de code qui affiche les ID des joysticks connectés :

#include <windows.h>
#include <stdio.h>

...

void AfficherJoysConnectes()
{
int i;
JOYINFO structtmp;

printf("Voici la liste des joysticks connectés :\n");
for (i = JOYSTICKID1 ; i < (JOYSTICKID1 + 16) ; i++) //seuls 16 IDs sont possibles
{
if (joyGetPos(i,&structtmp) == JOYERR_NOERROR)
printf("%d\n",i);
}
}

III-B - Caractéristiques d'un périphérique

Maintenant qu'on sait comment détecter la présence d'un joystick, nous allons pouvoir nous pencher sur le problème de la récupération des caractéristiques. L'API Microsoft fournit à cet effet la fonction joyGetDevCaps() qui prend trois paramètres et renvoie JOYERR_NOERROR si tout se passe bien. Les trois paramètres sont l'ID du joystick à interroger, l'adresse vers une structure de type JOYCAPS où seront stockées les informations et la taille de cette structure afin que Windows puisse détecter la version utilisée par le programme. Voyons le contenu de la structure de caractéristiques :

WORD wMid

ID du manufacteur

WORD wPid

ID du produit

CHAR szPname[MAXPNAMELEN]

nom du produit

UINT wXmin

valeur minimale de l'axe X

UINT wXmax

valeur maximale de l'axe X

UINT wYmin

valeur minimale de l'axe Y

UINT wYmax

valeur maximale de l'axe Y

UINT wZmin

valeur minimale de l'axe Z (Gaz)

UINT wZmax

valeur maximale de l'axe Z (Gaz)

UINT wNumButtons

nombre de boutons du périphérique

UINT wPeriodMin

Période minimale à utiliser lors de la capture

UINT wPeriodMax

Période maximale à utiliser lors de la capture

UINT wRmin

valeur minimale de l'axe R (palloniers)

UINT wRmax

valeur maximale de l'axe R (palonniers)

UINT wUmin

valeur minimale de l'axe U

UINT wUmax

valeur maximale de l'axe U

UINT wVmin

valeur minimale de l'axe V

UINT wVmax

y'a pas idée d'avoir autant d'axes ! ;-)

UINT wCaps

Zone de drapeaux indiquant les capacités du périphérique

UINT wMaxAxes

Nombre d'axes que le joystick peut gérer

UINT wNumAxes

Nombre d'axes actuellement utilisés

UINT wMaxButtons

Nombre de boutons que le joystick peut gérer

CHAR szRegKey[MAXPNAMELEN]

Clé du registre associée au joystick

CHAR szOEMVxD[MAX_JOYSTICKOEMVXDNAME]

Nom du driver du joystick

wCaps utilise les drapeaux suivants :

- JOYCAPS_HASZ : l'axe Z existe.

- JOYCAPS_HASR : l'axe R existe.

- JOYCAPS_HASU : l'axe U existe.

- JOYCAPS_HASV : l'axe V existe.

- JOYCAPS_HASPOV : le joystick peut fournir des informations de point de vue.

- JOYCAPS_POV4DIR : le joystick ne reconnaît que les valeurs d'orientation discrète (haute, basse, gauche et droite).

- JOYCAPS_POVCTS : le joystick reconnaît les valeurs d'orientation continues en degrés.

Voyons un exemple de programme qui affiche les capacités d'un périphérique :

#include <windows.h>
#include <stdio.h>

...

void AfficherCaracs(UINT JoyID)
{
JOYCAPS InfosCaps;
if (joyGetDevCaps(JoyID, &InfosCaps, sizeof(JOYCAPS)) == JOYERR_NOERROR)
{
printf("\nNom du périphérique : %s", InfoCaps.szPname);
printf("Nombre d'axes possibles : %d, gérables : %d\n",
InfoCaps.wNumAxes, InfoCaps.wMaxAxes);
printf("Axe |  Min  |  Max\n");
printf("----+-------+-------\n");
printf(" X  | ] | ]\n", InfoCaps.wXMin, InfoCaps.wXMax);
printf(" Y  | ] | ]\n", InfoCaps.wYMin, InfoCaps.wYMax);
if (InfoCaps.wCaps & JOYCAPS_HASZ)
printf(" Z  | ] | ]\n", InfoCaps.wZMin, InfoCaps.wZMax);
else
printf(" Z  |    Absent\n");
if (InfoCaps.wCaps & JOYCAPS_HASR)
printf(" R  | ] | ]\n", InfoCaps.wRMin, InfoCaps.wRMax);
else
printf(" R  |    Absent\n");
if (InfoCaps.wCaps & JOYCAPS_HASU)
printf(" U  | ] | ]\n", InfoCaps.wUMin, InfoCaps.wUMax);
else
printf(" U  |    Absent\n");
if (InfoCaps.wCaps & JOYCAPS_HASV)
printf(" V  | ] | ]\n", InfoCaps.wVMin, InfoCaps.wVMax);
else
printf(" V  |    Absent\n");
printf("\nPoint de vue : ");
if (InfoCaps.wCaps & JOYCAPS_HASPOV)
{
if (InfoCaps.wCaps & JOYCAPS_POV4DIR)
printf("Quadridirectionnel\n");
else
printf("Continu\n");
}
else
printf("Absent\n");
printf("Nombre de boutons possibles : %d, gérables : %d\n",
InfoCaps.wNumButtons, InfoCaps.wMaxButtons);
}
}

IV - La capture du Joystick

Afin d'éviter aux programmeurs d'avoir à réaliser un thread séparé s'occupant de la gestion du joystick afin de ne pas perturber l'application principale, Microsoft a mis à la disposition des programmeurs une fonctionnalité spécifique : la capture du joystick. La capture du joystick permet de se décharger de la gestion du joystick et de recevoir des messages associés au joystick. Comme tout, cela a également des désavantages : seule une application à la fois peut capturer un joystick, un maximum de deux joysticks peuvent être capturés au même moment, et les messages sont assez limités et ne permettent pas de contrôler complètement les joysticks actuels. Par ailleurs, il se peut qu'un message n'arrive pas jusqu'à l'application qui a capturé le joystick si une autre fait appel à joyGetPos() à peu près au même moment que le message est envoyé.

IV-A - La capture

Il y a deux types de captures possibles : la première envoie des messages à intervalles réguliers (entre wPeriodMin et wPeriodMax) et la seconde envoie des messages quand il y a un changement d'état. La capture est mise en place par la fonction joySetCapture() qui prend quatre paramètres et renvoie JOYERR_NOERROR si tout se passe bien. Les quatre paramètres sont : le handle de la fenêtre à laquelle Windows doit envoyer les messages, l'ID du joystick à capturer, la période de temps entre deux tests d'état et le type de capture (FALSE pour la première et TRUE pour la seconde). Dans le cas du second type de capture, l'application devrait également préciser l'amplitude minimale de mouvement sur un axe pour laquelle on considère qu'il y a changement d'état en faisant un appel à la fonction joySetThreshold(). Cette dernière prend deux paramètres : l'ID du joystick et la quantité de mouvement. Pour connaître la valeur actuelle, on peut utiliser la fonction joyGetThreshold() qui prend en paramètres l'ID du joystick et l'adresse d'un entier non signé de type UINT. Les deux fonctions renvoient JOYERR_NOERROR quand tout se passe bien. Bien entendu, lorsque l'application n'a plus besoin du joystick, elle peut relcher la capture en faisant un appel à la fonction joyReleaseCapture() qui prend en paramètre l'ID du joystick à relcher et renvoie toujours la même constante si tout se passe bien. Voyons un exemple de capture du joystick basée sur le changement d'état (cet exemple utilise l'API pour créer une fenêtre, voyez ici pour un tutoriel de Bob sur l'utilisation de l'API).

fichier entete : capture.h

#ifndef Unit1H
#define Unit1H

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

#endif
fichier principal (WinMain) : capture.c
//---------------------------------------------------------------------------
#include <windows.h>

#include "Unit1.h"
//---------------------------------------------------------------------------
HINSTANCE hInst; // instance de l'application
LPCTSTR lpszAppName = "CAPTURE";
LPCTSTR lpszTitle = "Capture du joystick";
HWND ZoneTexte;

WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
HWND hWnd;
WNDCLASSEX wc;
MMRESULT ret;

// Enregistrement des caractéristiques du class name de la fenêtre
wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = 0;
wc.hIcon = LoadIcon(hInstance, lpszAppName);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
wc.lpszMenuName = lpszAppName;
wc.lpszClassName = lpszAppName;
wc.cbSize = sizeof(WNDCLASSEX);
wc.hIconSm = NULL;

if(!RegisterClassEx(&wc))
return FALSE;

hInst = hInstance;
// Création de la fenêtre
hWnd = CreateWindow(lpszAppName, lpszTitle, WS_OVERLAPPEDWINDOW,
0, 0, 300, 300, NULL, NULL, hInstance, NULL);

if (!hWnd)
return FALSE;

// Affichage de la fenêtre
ShowWindow(hWnd,nCmdShow);

// Création d'une zone de texte dans la fenêtre
ZoneTexte = CreateWindow("EDIT", "", WS_VISIBLE | WS_VSCROLL | WS_CHILD
| ES_AUTOVSCROLL | ES_MULTILINE , 1, 2, 290, 270,
hWnd, NULL, hInstance, NULL);
UpdateWindow(hWnd);

// Capture du joystick
ret = joySetCapture(hWnd, // les messages seront envoyés à la fenêtre
JOYSTICKID1, // capture du premier joystick
50, // période de 50 ms. Si c'est hors bornes, Windows ajuste
TRUE); // Basée sur les changements d'état

if (ret == JOYERR_NOERROR)
{
// ajuster les valeurs en fonction des caractéristiques du joystick
// ici, c'est environ 3% pour le mien
joySetThreshold(JOYSTICKID1, 2000);
}
else
PostQuitMessage(0);

// lancement de la boucle de traitement des messages
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return (msg.wParam);
}

IV-B - Les messages associés à la capture

Voyons maintenant quels sont les messages que Windows envoie à l'application qui a capturé un joystick. Tout d'abord, afin de différencier les joysticks Windows préfixe le nom des constantes de messages par MM_JOY1 ou MM_JOY2. Il y a quatre types de messages :

MM_JOY1BUTTONDOWN

Un bouton a été pressé

MM_JOY1BUTTONUP

Un bouton a été relâché

MM_JOY1MOVE

La position de l'axe X ou Y a changé

MM_JOY1ZMOVE

La position de l'axe Z a changé

Pour les deux premiers messages, wParam contient des drapeaux pour indiquer les changements d'état et les boutons enfoncés à l'aide des constantes JOY_BUTTONxCHG et JOY_BUTTONx (x compris entre 1 et 4). Pour les deux autres, wParam ne contient que les boutons enfoncés et seules les constantes JOY_BUTTONx sont utilisées.

Pour les trois premiers messages, LOWORD(lParam) contient la coordonnée X et HIWORD(lParam) la coordonnée Y par rapport au coin supérieur gauche de la fenêtre. Le dernier message ne complète que LOWORD(lParam) avec la coordonnée Z.

Voyons un exemple de code qui va modifier le contenu de la zone de texte créée précédemment en fonction du message reçu (suite du fichier principal : capture.c):

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
UINT wID;
HWND hwndCtrl;
char Texte[1000];
char b1,b2,b3,b4;

switch (uMsg)
{
case MM_JOY1BUTTONDOWN :
b1 = (wParam & JOY_BUTTON1) ? 'O' : 'N';
b2 = (wParam & JOY_BUTTON2) ? 'O' : 'N';
b3 = (wParam & JOY_BUTTON3) ? 'O' : 'N';
b4 = (wParam & JOY_BUTTON4) ? 'O' : 'N';
sprintf(Texte,"Boutons enfoncés :\r\n1 : %c\r\n2 : %c\r\n3 : %c\r\n4 : %c",
b1, b2, b3, b4);
break;
case MM_JOY1BUTTONUP :
b1 = (wParam & JOY_BUTTON1) ? 'O' : 'N';
b2 = (wParam & JOY_BUTTON2) ? 'O' : 'N';
b3 = (wParam & JOY_BUTTON3) ? 'O' : 'N';
b4 = (wParam & JOY_BUTTON4) ? 'O' : 'N';
sprintf(Texte,"Boutons enfoncés :\r\n1 : %c\r\n2 : %c\r\n3 : %c\r\n4 : %c",
b1, b2, b3, b4);
break;
case MM_JOY1MOVE :
sprintf(Texte,"Axe X : %d\r\nAxe Y %d",LOWORD(lParam), HIWORD(lParam));
break;
case MM_JOY1ZMOVE :
sprintf(Texte,"Axe Z : %d",LOWORD(lParam);
break;
case WM_DESTROY :
PostQuitMessage(0);
break;
default :
return (DefWindowProc(hWnd, uMsg, wParam, lParam));
}
SetWindowText(ZoneTexte,Texte);
return 0L;
}

V - L'interrogation du Joystick

Voyons maintenant comment faire lorsque la capture ne permet pas de résoudre nos problèmes ou qu'il est préférable de contrôler le déroulement des opérations par nous même. En effet, la capture ne permet que de gérer quatre boutons et trois axes ce qui est limité si on veut transformer notre merveilleux petit outil en télécommande.

Windows fournit une fonction avancée pour interroger le joystick de manière plus complète que ce que permet la capture. Pour l'interrogation, il y a les fonctions joyGetPos() et joyGetPosEx() ; nous ne nous occuperons pas de la première puisqu'elle n'apporte rien par rapport à la capture, nous ne verrons donc que la seconde. Cette fonction prend en paramètres l'ID du joystick à interroger ainsi que l'adresse d'une structure JOYINFOEX et renvoie JOYERR_NOERROR quand tout se passe bien. Voyons en détail le contenu de la structure JOYINFOEX :

DWORD dwSize

Taille de la structure (nécessaire pour détecter la version utilisée)

DWORD dwFlags

Drapeaux indiquant les données qu'on souhaite obtenir

DWORD dwXpos

Positions des différents axes du joystick

DWORD dwYpos

DWORD dwZpos

DWORD dwRpos

DWORD dwUpos

DWORD dwVpos

DWORD dwButtons

Etat des boutons (jusqu'à 32 boutons)

DWORD dwButtonNumber

Nombre de boutons pressés

DWORD dwPOV

Position du point de vue

DWORD dwReserved1

Réservé (pourquoi faire, je ne sais pas €¦ ), ne pas utiliser

DWORD dwReserved2

À la lumière de ces informations, on peut déjà se réjouir en imaginant ce qu'on va pouvoir faire faire à notre manette de jeu. Que dis-je notre baguette magique spéciale PC ;-). Voyons maintenant un peu plus en détail les quelques spécificités de cette structure. Tout d'abord dwButtons : pour connaître l'état d'un bouton, il suffit de lire l'état du bit correspondant : au bouton 1 correspond le bit de poids le plus faible et ainsi de suite jusqu'au 32e bouton auquel correspond le bit de poids le plus fort. Ensuite, dwPOV : cette variable contient l'orientation du point de vue en centièmes de degrés (90° <=> 9000). Windows fournit cinq constantes correspondant aux positions primaires : JOY_POVCENTERED (-1 : centre), JOY_POVBACKWARD (18000 : arrière), JOY_POVFORWARD (0 : avant), JOY_POVLEFT (27000 : gauche) et JOYPOVRIGHT (9000 : droite). Finalement, il ne nous reste plus qu'à indiquer à Windows quelles sont les informations dont on a besoin, et c'est le rôle de dwFlags. Voici un tableau récapitulatif des drapeaux utilisables.

JOY_RETURNALL

Équivalent à mettre tous les bits JOY_RETURN à l'exception de JOY_RETURNRAWDATA.

JOY_RETURNBUTTONS

dwButtons contient des informations valides à propos de chaque bouton.

JOY_RETURNCENTERED

Centre la position neutre du joystick au milieu de chaque axe.

JOY_RETURNPOV

dwPOV contient des informations valides à propos du point de vue exprimé à l'aide des constantes énoncées ci-dessus (utile pour les applications qui ne gèrent que les positions discrètes quand le joystick fournit des informations continues).

JOY_RETURNPOVCTS

dwPOV contient des informations valides a propos du point de vue exprimées en valeurs discrètes.

JOY_RETURNR

dwRpos, dwUpos, dwVpos, dwXpos, dwYpos et/ou dwZpos contiennent des informations valides sur les positions des axes

JOY_RETURNU

JOY_RETURNV

JOY_RETURNX

JOY_RETURNY

JOY_RETURNZ

JOY_USEDEADZONE

Etend la zone morte du joystick. Le driver renvoie une valeur constante tant qu'il est dans la zone morte.

JOY_RETURNRAWDATA

Les données contenues dans la structure sont des données brutes (non calibrées)

Les drapeaux suivants fournissent des informations pour calibrer le joystick. Je ne les ai jamais utilisées, alors n'hésitez pas à me faire part de vos expériences afin de compléter ce tutoriel.

JOY_CAL_READ3

Lit les données brutes X, Y et Z et les fournit par l'intermédiaire de dwXpos et ses consœurs.

JOY_CAL_READ4

Lit les données brutes X, Y, Z et R et les fournit par l'intermédiaire de dwXpos et ses consœurs.

JOY_CAL_READ5

Lit les données brutes X, Y, Z, R et U et les fournit par l'intermédiaire de dwXpos et ses consœurs.

JOY_CAL_READ6

Lit les données brutes X, Y, Z, R, U et V et les fournit par l'intermédiaire de dwXpos et ses consœurs.

JOY_CAL_READALWAYS

Lit les données du joystick même si le driver indique qu'il n'est pas présent.

JOY_CAL_READRONLY

Lit uniquement les données brutes associées à(aux) l'axe(s) indiqués.

JOY_CAL_READXONLY

JOY_CAL_READXYONLY

JOY_CAL_READYONLY

JOY_CAL_READZONLY

JOY_CAL_READUONLY

JOY_CAL_READVONLY

Nous allons maintenant mettre ces informations en œuvre pour utiliser le joystick à la place de la souris. Pour plus d'informations, je vous oriente vers mon tutoriel sur le contrôle du clavier et de la souris. Dans cet exemple, je ne ferais aucune vérification quant aux caractéristiques du joystick : je considère que le joystick à contrôler est connu et qu'il n'y aura aucun problème. Si votre périphérique ne prend pas en charge l'une de ces caractéristiques, vous n'aurez qu'à la mettre en commentaire.

#include <windows.h>

#define BOUTON1 1
#define BOUTON2 2
#define BOUTON3 4
#define BOUTON4 8

int CalculerDeplacement(UINT Max, UINT Min, UINT Val);

WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
JOYINFOEX ActualPos; // État actuel et dernier état du joystick
DWORD dwBoutons; // Masque pour récupérer l'état des boutons
UINT uMax[2], uMin[2]; // Maximums et minimums de chacun des axes
UINT uPoolPeriod; // Temps entre deux interrogations du joystick
JOYCAPS InfosCaps; // Infos sur les capacités du joystick
BOOL bStop; // Test d'arrêt de la boucle (bouton 4)
POINT Curseur; // Curseur de la souris

// initialisation du temps d'attente avec un minimum de 20 millisecondes
uPoolPeriod = 20;

if (joyGetDevCaps(JOYSTICKID1, &InfosCaps, sizeof(JOYCAPS)) != JOYERR_NOERROR)
return 0;

// Initialisation de la structure informationnelle avec sa taille
ActualPos.dwSize = sizeof(JOYINFOEX);

// Initialisation des autres membres que la taille de la structure à 0
// (Plus rapide que d'affecter 0 à chaque élément. En effet, il est
// obligatoire que tous les membres soient nuls sous peine d'échec)
memset(&(ActualPos.dwFlags),0,sizeof(JOYINFOEX) - sizeof(DWORD));
// Initialisation des variables d'état actuel avant de lancer la boucle
// de gestion du joystick
ActualPos.dwFlags = JOY_RETURNALL;
joyGetPosEx(JOYSTICKID1,&ActualPos);
dwBoutons = ActualPos.dwButtons;

bStop = FALSE;

// Lancement de la gestion du joystick
while (!bStop)
{
// on réinitialise ActualPos avant d'appeler l'API
// pour savoir la position actuelle du joystick
memset(&(ActualPos.dwFlags),0,sizeof(JOYINFOEX) - sizeof(DWORD));
ActualPos.dwFlags = JOY_RETURNALL;
joyGetPosEx(JOYSTICKID1,&ActualPos);

// Position du curseur
GetCursorPos(&Curseur);

// Gestion de l'axe X
Curseur.x += CalculerDeplacement(InfosCaps.wXmin, InfosCaps.wXmax, ActualPos.dwXpos);

// Gestion de l'axe Y
Curseur.y += CalculerDeplacement(InfosCaps.wYmin, InfosCaps.wYmax, ActualPos.dwYpos);

// Déplacement du curseur
SetCursorPos(Curseur.x, Curseur.y);

// Gestion de la molette avec le point de vue
if (ActualPos.dwPOV == JOY_POVFORWARD)
mouse_event(MOUSEEVENTF_WHEEL,Curseur.x,Curseur.y, 120, 0);
else if (ActualPos.dwPOV == JOY_POVBACKWARD)
mouse_event(MOUSEEVENTF_WHEEL,Curseur.x,Curseur.y, -120, 0);

// Test de l'état des boutons
// on teste l'état du bouton et on agit uniquement
// si il y a eu un changement d'état
if ((ActualPos.dwButtons & BOUTON1) && !(dwBoutons & BOUTON1))
mouse_event(MOUSEEVENTF_LEFTDOWN,Curseur.x,Curseur.y, 0, 0);
else if (!(ActualPos.dwButtons & BOUTON1) && (dwBoutons & BOUTON1))
mouse_event(MOUSEEVENTF_LEFTUP,Curseur.x,Curseur.y, 0, 0);

if ((ActualPos.dwButtons & BOUTON2) && !(dwBoutons & BOUTON2))
mouse_event(MOUSEEVENTF_MIDDLEDOWN,Curseur.x,Curseur.y, 0, 0);
else if (!(ActualPos.dwButtons & BOUTON2) && (dwBoutons & BOUTON2))
mouse_event(MOUSEEVENTF_MIDDLEUP,Curseur.x,Curseur.y, 0, 0);

if ((ActualPos.dwButtons & BOUTON3) && !(dwBoutons & BOUTON3))
mouse_event(MOUSEEVENTF_RIGHTDOWN,Curseur.x,Curseur.y, 0, 0);
else if (!(ActualPos.dwButtons & BOUTON3) && (dwBoutons & BOUTON3))
mouse_event(MOUSEEVENTF_RIGHTUP,Curseur.x,Curseur.y, 0, 0);

if (ActualPos.dwButtons & BOUTON4)
bStop = TRUE;

dwBoutons = ActualPos.dwButtons;

// On patiente avant de retester la position du joystick
Sleep(uPoolPeriod);
}
return 0;
}
//---------------------------------------------------------------------------

int CalculerDeplacement(UINT Max, UINT Min, UINT Val)
{
// On calcule la position actuelle en pourcentage et on en déduit le mouvement
float PCent = (float)(Val) / (float)(Min - Max) * 100.0;
PCent -= 50.0;
PCent /= 10.0;
if ((PCent < 1) && (PCent > -1))
PCent = 0.0;

return (int)(PCent);
}