La souris et les fonctions associées
La souris était un accessoire optionnel au tout début du PC. Très peu d'applications s'en servaient en fait, et comme Windows n'existait pas encore, tout était en mode texte, y compris le curseur de la souris.
Bref pas beau du tout...
Lorsque Windows est apparue, et comme la souris n'était forcément présente, des fonctions permettaient de détecter la présence de la souris et des raccourcies au clavier permettaient d'effectuer la ou les options normalement accéssibles par la souris.
Bien que de nos jours, on peut difficilement concevoir un PC sans souris, la possibilitée d'accéder à des options plutôt orientées souris via le clavier existe toujours, mais ceci plutôt pour un gain de vitesse.
Présence et informations sur la souris (physiquement parlant):
On trouve donc encore des fonctions sous Windows permettant de vérifier la présence de la bête. (Au cas où vous auriez encore un PC sans souris...)
Il suffit d'utiliser la fonction GetSystemMetrics(SM_MOUSEPRESENT);
La fonction renvoie TRUE si une souris est connectée...
Déjà plus intéressant (et encore), la possibilité de connaître le nombre d'oreilles du petit mulot (j'entends par là le nombre de boutons):
GetSystemMetrics(SM_CMOUSEBOUTONS);
0 : pas de souris, sinon la valeur correspondante est le nombre de boutons.
Le curseur de la souris:
Le curseur de la souris sous Windows fonctionne de la même manière que dans les autres systèmes:
Une image et un masque forme le dessin représentant laposition de souris à l'écran. Dans ce dessin, il y a un point particulier appelé point d'ancrage ou hotspot. Ce point est celui qui va être utilisé pour savoir sur quoi vous cliquez. Evidemment, les positions de la souris que vous pourrez récupérées seront basées par rapport à ce point.
Enfin, il n'y a normalement que la fenêtre qui a le focus qui pourra recevoir les informations en provenance de la souris. Les messages WM_SETFOCUS ou WM_KILLFOCUS peuvent vous permettre de savoir si la fenêtre à ou non le contrôle de la souris
Les fenêtres
filles d'une fenêtre classique ne recevront normalement pas ces messages.
Modifier le curseur de la souris:
Le curseur de la souris est l'image du pointeur à l'écran. Chaque fenêtre peut avoir sa propre représentation du curseur. Comme vous l'avez déjà vu, il suffit de définir cette image lors de la définition de la classe en utilisant la fonction LoadCursor : Par exemple wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
Maintenant, plus compliqué, la modification de la souris en dynamique:
Imaginons que vous ayez sélectionné une option et vous aimeriez que le curseur de la souris change pour justement rappeler l'utilisateur qu'il est dans cette option, comment faire ?
La fonction SetCursor() va permettre de modifier le curseur de la souris:
HCURSOR SetCursor(
HCURSOR hCursor
);
Avec hCursor le handle d'un curseur intialisé par LoadCursor() ou CreateCursor().
La fonction retourne alors le handle du curseur précédent ou 0 s'il n'y en avais pas avant...
Il faut impérativement
que le curseur soit à NULL lors de la définition de la classe
de la fenêtre. Sinon, le nouveau curseur sera immédiatement remodifié
par celui défini dans la classe de
la fenêtre dès que vous déplacerez votre souris.
Bidouille, pas forcemment conseillée:
Si vous ajoutez ceci dans la procédure de gestion de la fenêtre:
case WM_SETCURSOR:
GetCursorPos(&point); // cursor position
SetCursorPos(point.x,point.y);
return 0;
Alors vous pourrez modifier la forme du curseur bien qu'elle soit déjà déclarée dans la classe de la fenêtre. (Cela marche sur NT4, mais pas vérifié sur les autres systèmes).
Curseur animé:
En se créant une horloge, rien ne vous empêchera d'animer votre curseur...(Voir un chapitre un peu plus sur horloges ou timers).
Des messages provenant de la souris dans la zone client:
Si la fenêtre a le focus, alors elle recevra des informations en provenance de la souris sous forme de messages.
Ces messages spécialisés pour la souris fonctionnent tous sur le même principe: lParam contient la position de la souris en x et y. Vous pourrez récupérer ces informations de la manière suivante:
x = LOWORD(lParam);
y = HIWORD(lParam);
ou encore:
x = GET_X_LPARAM(lParam);
y = GET_Y_LPARAM(lParam);
Remarque : Ces coordonnées sont indiquées par rapport à la zone client. Si vous sortez de cette zone, les valeurs ne seront plus valables (En fait, elles ne changeront plus et ne donneront alors que la dernière position valide de la souris pour la fenêtre).
wParam contient l'état des boutons
Valeur | Désignation |
MK_LBUTTON | bouton gauche appuyé |
MK_MBUTTON | bouton du milieu appuyé |
MK_RBUTTON | bouton droit appuyé |
MK_SHIFT | touche MAJ appuyé |
MK_CONTROL | touche CTRL appuyé |
Pour tester le bouton shift par exemple, il suffit de faire if (wParam && MK_shift)
Les messages suivants sont utilisés pour traiter les événements provenant de la souris :
WM_MOUSEMOVE est envoyé à chaque déplacement de la souris au dessus de la zone client (et uniquement à ce moment là !).
Les messages sur le bouton gauche:
WM_LBUTTONDOWN est envoyé si le bouton gauche de la souris est pressé.
WM_LBUTTONUP est envoyé si le bouton gauche de la souris est relevé.
WM_LBUTTONDBLCLK est envoyé lors d'un double clic sur le bouton gauche de la souris. Ce message n'est envoyé que si le style CS_DBLCLKS est indiqué dans la classe de notre fenêtre.
De même, pour le bouton du milieu:
WM_MBUTTONDOWN est envoyé si le bouton du milieu de la souris est pressé.
WM_MBUTTONUP est envoyé si le bouton du milieu de la souris est relevé.
WM_MBUTTONDBLCLK est envoyé lors d'un double clic sur le bouton du milieu de la souris. Ce message n'est envoyé que si le style CS_DBLCLKS est indiqué dans la classe de notre fenêtre.
Et enfin le bouton droit:
WM_RBUTTONDOWN est envoyé si le bouton droit de la souris est pressé.
WM_RBUTTONUP est envoyé si le bouton droit de la souris est relevé.
WM_RBUTTONDBLCLK est envoyé lors d'un double clic sur le bouton droit de la souris. Ce message n'est envoyé que si le style CS_DBLCLKS est indiqué dans la classe de notre fenêtre
Remarque : Il est tout à fait possible de recevoir un WM_MBUTTONUP sans WM_MBUTTONDOWN ou inversement. Cela se produit lorsque l'utilisateur change de fenêtre entre les deux évènements.
Pour ceux qui auraient une souris à 5 boutons (jamais vu...souris mutante ou irradiée), il faut utiliser les messages:
WM_XBUTTONDOWN
WM_XBUTTONUP
WM_XBUTTONDBLCLK
Dans ce cas, wparam et lparam contiennent des informations supplémentaires:
wParam:
La partie basse de wParam indiquera le bouton pressé:
Valeur | Désignation |
MK_CONTROL | Etat touche ctrl correspondant (up, down) |
MK_LBUTTON | Bouton gauche |
MK_MBUTTON | Bouton milieu |
MK_RBUTTON | Bouton droit |
MK_SHIFT | Shift |
MK_XBUTTON1 | Bouton X1 |
MK_XBUTTON2 | Bouton X2 |
Remarque Microsoft (je n'ai pas ce genre de bête), on reçoit beaucoup de messages si double clique sur les X boutons: down, up dblclk et enfin up ...
La partie haute indique le type de clique sur les boutons X:
Si XBUTTON1, alors le bouton X1 double cliqué.
Si XBUTTON2, alors le bouton X2 double cliqué.
lParam:
La partie basse contient les coordonnées en X dans la zone client de la souris.
La partie haute contient les coordonnées en Y dans la zone client de la souris.
Comme indiqué chez Microsoft, il existe des macros pour lire les parties
haute et basse:
Pour wParam:
fwKeys = GET_KEYSTATE_WPARAM (wParam);
fwButton = GET_XBUTTON_WPARAM (wParam);
Pour lParam:
xPos = GET_X_LPARAM(lParam);
yPos = GET_Y_LPARAM(lParam);
Le message permettant de gérer la molette de la souris est: WM_MOUSEWHEEL
avec: wParam
La partie haute de ce mot indiquant la distance parcourue par la molette (multiple
de WHEEL_DELTA positionné à 120 par défaut). Une valeur
positive indiquant un déplacement vers l'avant, négative pour
l'arrière.
La partie basse l'état des différents boutons de la souris:
Valeur | Désignation |
MK_CONTROL | Touche contrôle pressée |
MK_LBUTTON | Bouton gauche de la souris pressée |
MK_MBUTTON | Bouton du milieu de la souris pressée |
MK_RBUTTON | Bouton droit de la souris pressée |
MK_SHIFT | Shift pressée |
MK_XBUTTON1 | (à partir de 2000/XP), premier bouton X |
MK_XBUTTON2 | (à partir de 2000/XP), second bouton X |
lParam
La partie basse contient la position de la souris en X, la partie haute, Y
Si vous traitez ce message, ne pas oublier de retourner 0 !
Pour lire les valeurs dans wParam et lParam, vous pourrez utiliser les speudos fonctions suivantes:
fwKeys = GET_KEYSTATE_WPARAM(wParam);
zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
xPos = GET_X_LPARAM(lParam);
yPos = GET_Y_LPARAM(lParam);
Vous pourrez utiliser MAKEPOINTS pour convertir lParam dans une structure POINTS.
Pour Windows 95 ou NT 3.51, vous utiliserez le message MSH_MOUSEWHEEL de zmouse.h
Si vous avez
un problème lors de la compilation du genre WM_MOUSEWHEEL non définie,
il faut alors se créer une des deux variables prépros: _WIN32_WINNT
(ou _WIN32_WINDOWS) avec une valeur > 0x0400 (Voir le test qui est fait dans
Winuser.h).
La souris est hors zone !
Il est tout aussi possible d'avoir des messages de la souris lorsqu'elle quitte la zone client.
Ces messages ne sont pas plus compliqués, ceux sont les mêmes que ceux que vous venez de voir préfixer de NC: Par exemple WM_NCMOUSEMOVE.
Mais attention, les positions correspondront alors à des positions par rapport à l'écran et non plus la zone client. Mais vous verrez dans quelques lignes que l'on peut les convertir en zone client.
De même, wParam va indiquer la zone de la fenêtre où se trouve la souris.
Petit exemple d'utilisation, on va afficher la position de la souris sur WM_MOUSEMOVE et WM_NCMOUSEMOVE:
En fait, tous ces
messages sont générés par la procédure
par défaut de la fenêtre. A l'origine, votre fenêtre
va recevoir un message WM_NCHITTEST
qui sera traité par cette procédure par défaut, qui à
son tour produira l'un des messages ci-dessus, mais aussi les messages comme
WM_CLOSE, ...Cette procédure retournera une valeur indiquant la
zone de la fenêtre où se trouve la souris.
wParam n'est pas renseigné, mais lParam va contenir les coordonnées X et Y de la souris par rapport à l'écran.
Alors imaginez un peu si vous codiez ceci:
case WM_NCHITTEST:
return (long) HTNOWHERE;
Il n'y aura alors plus beaucoup (et même aucun) des nombreux messages générés à partir de celui-ci qui pourront être traités par votre fenêtre...
La souris est hors zone client:
Pour obtenir à tout moment la position courante de la souris, il y a aussi la fonction GetCursorPos(). Cependant, la position récupérée est une position par rapport à l'écran et non pas par rapport à la zone client de la fenêtre.
BOOL GetCursorPos(
LPPOINT lpPoint
);
Paramètres:
lpPoint pointeur sur une structure de type POINT
qui recevra les coordonnées de la souris.
La fonction retourne 0 si erreur.
Vous pourrez ensuite reconvertir cette position en position dans la zone client en utilisant la fonction ScreenToClient()
BOOL ScreenToClient(
HWND hWnd, // Handle de la fenêtre pour laquelle on veut obtenir les coordonnées
corespondantes
LPPOINT lpPoint // Les coordonnées dans l'écran
);
En sortie:
Si la fonction échoue, elle retourne 0, sinon une valeur si pas de pb. et lpPoint est maj: conversion des coordonnées écran en coordonnées zone client.
Les valeurs peuvent être négatives ou bien plus grandes que la taille réelle de la zone écran si le curseur est hors zone client. (Normal, on ne fait que changer les origines du repère).
Il y a bien entendu la fonction réciproque les coordonées zone client en coordonées écran:ClientToScreen()
BOOL ClientToScreen(
HWND hWnd, // handle to window
LPPOINT lpPoint // screen coordinates
);
A ma connaissance, on ne peut pas faire grand chose pour connaître les déplacements de la souris si elle sort de la fenêtre.
Soit nous capturons la souris par la fonction SetCapture (hwnd) où hwnd est la fenêtre qui recevra les messages de la souris. Les messages WM_MOUSEMOVE ou WM_xBUTTONxxx donneront alors la position de la souris sur l'écran par rapport à la zone client. Il faudra ensuite faire un ReleaseCapture() pour libérer la souris lorsque vous n'aurez plus besoin de traiter les informations de la souris. Remarque, on ne peut pas capturer indéfiniment la souris. Il faudra donc répéter la fonction aussi longtemps que l'on en a besoin. Ce cas sera vu au chapitre Capturer la souris. Elle est souvent utilisée lorsque vous voulez vous créer un objet graphique qui doit (ré)agir lorsque vous quittez la zone cliente de votre objet qui n'est ni plus ni moins qu'une fenêtre...
Soit une solution beaucoup moins orthodoxe, qui consiste à lire la position de la souris avec GetCursorPos(), mais pas dans procédure de la fenêtre car il n'y aura pas de messages indiquant un changement de position de la souris comme c'est le cas dans l'exemple de programme précédent qui affiche les coordonnées de la souris dans la fenêtre. Une idée pourrait être de créer un thread qui va boucler sur la lecture de la position de la bête, mais n'oubliez de le faire dormir un peu par sleep(). Voir le multithread.
Repositionner la souris:
Ce qui peut alors être utile avec la fonction permettant de positionner le curseur de la souris sur l'écran (et donc avec un repère dont les origines sont en haut à gauche de l'écran):SetCursorPos( )
BOOL SetCursorPos(
int X,
int Y
);
Activer ou cacher le curseur de la souris:
La fonction qui montre ou cache la souris est:ShowCursor()
int ShowCursor(
BOOL bShow
);
Les paramètres:
bShow Incrémenter ou décrémenter le compteur d'affichage de la souris.
La valeur retournée donne la valeur de ce compteur permettant de savoir si la souris est cachée ou non.
Vous venez de perdre le focus...
Un autre message (autre que WM_KILLFOCUS) va vous permettre de savoir que vous venez de perdre le contrôle de la souris:
WM_CAPTURECHANGED avec lParam contenant le handle de la window qui récupère le contrôle de la souris.
Des informations logiques sur la souris:
La structure CURSORINFO contient des informations sur la souris:
typedef struct {
DWORD cbSize;
DWORD flags;
HCURSOR hCursor;
POINT ptScreenPos;
} CURSORINFO, *PCURSORINFO, *LPCURSORINFO;
Avec:
cbSize Taille en octet de la structure. Utilisez sizeof(CURSORINFO).
flags Etat d'affichage du curseur: 0 pour caché ou CURSOR_SHOWING pour
visible.
hCursor Pour le handle du curseur
ptScreenPos Structure de type POINT pour les coordonnées de la souris
par rapport à l'écran.
Pour récupérer ces informations, vous utiliserez
BOOL GetCursorInfo(
PCURSORINFO pci
);
Capturer la souris !
Le problème, c'est que si l'utilisateur sort le pointeur de la souris de la fenêtre, vous ne serez plus ce qui se passe. Ceci pouvant être génant dans certain cas (Un exemple sera traité dans le chapitre complément sur les fenêtres lors de la création d'un objet bouton complétement personnalisé). Bien sûr, il ne peut avoir qu'une seule fenêtre à la fois qui capture la souris et cette fenêtre est celle qui se trouve en premier plan.
Il existe pour cela les fonctions:
- SetCapture()
HWND SetCapture(
HWND hWnd
);En entrée, le handle de la fenêtre demandant la capture de la souris.
En sortie, le handle de la fenêtre qui avait la souris, ou NULL si la souris n'était pas affectée à une fenêtre.
La fenêtre qui va perdre la souris va recevoir le message WM_CAPTURECHANGED.
- Enfin, après avoir capturé la souris, il faut la relâcher
lorsque nous n'en avons plus besoin via ReleaseCapture()!
BOOL ReleaseCapture(VOID)
En sortie, la fonction retourne 0 si nous avons une erreur.