Positions de la souris et état de ses boutons
Les évènements de la souris
Il apparaît normal qu'une application ait un compte-rendu de la position et/ou de l'état de la souris lorsque son curseur (image représentant la souris à l'écran) se trouve sur une des fenêtres de l'application.
Pour cela, cette application devra en faire la demande au serveur X. En effet, il faudra utiliser les évènements pour être tenu informé.
Type d'évènement | Description | Masque d'évènement |
EnterNotify | Le pointeur entre dans la fenêtre | EnterWindowMask |
MotionNotify | Le pointeur a été déplacé dans la fenêtre | ButtonMotionMask |
LeaveNotify | Le pointeur a quitté la fenêtre | LeaveWindowMask |
ButtonPress | Un bouton de la souris a été enfoncé | ButtonPressMask |
ButtonRelease | Un bouton de la souris a été relaché | ButtonReleaseMask |
Rappel: Ne pas confondre le type d'évènement et le masque d'évènement !
Clic de la souris
Pour tester le clic de la souris, il suffit donc de demander les évènements suivant en tapant:
XSelectInput (display, window, ButtonPressMask | ButtonReleaseMask);
je passe les explications sur display et window...quant-au reste, c'est déjà indiqué dans le tableau précedent !
Lorsque l'un ou l'autre des évènements survient, X Window renseigne la structure XButtonEvent (nom du membre: xbutton) qui contient entre autre:
type et Champ | Désignation |
Window subwindow | La fenêtre d'où provient réellement l'évènement (falculté de propagation des évènements qui fait qu'une fenêtre peut recevoir l'évènement à la place de celle d'où est déclenchée l'événement). |
Time time | Moment prècis du clic (n'oublions pas que l'évènement est envoyé dans une file d'évènements et donc ne sera pas traité immédiatement). |
int x, y | Coordonnées du pointeur dans la fenêtre où c'est produit l'évènement |
int x_root, y_root | Coordonnées du pointeur dans la fenêtre root |
Bool same_screen | Indique si le pointeur est toujours dans le même écran. |
unsigned int state | Etat des boutons (y compris les touches ctrl, alt, ...) |
unsigned int button | Numéro du boutton ayant changé d'état (de 1 à 5) |
Les différentes valeurs de state sont les suivantes: ShiftMask, LockMask, ControlMask pour les touches shift, lock et control
Les touches : Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
Et bien sûr les boutons de la souris : Button1Mask, Button2Mask, Button3Mask,
Button4Mask, Button5Mask
Double-clic
Hélas, XWindow ne prend pas en charge le cas du double clic. Il nous faudra donc le développer.
Il faudra alors prendre en compte le champ time de XButtonEvent, et pourquoi pas aussi tester que la position de la souris n'a pas trop évoluée.
Un exemple à venir...
Entrée ou sortie du pointeur de la souris dans la fenêtre
Lorsque la souris entre ou quitte une fenêtre de notre application, celle-ci peut recevoir des évènements pour connaître la position du pointeur.
Ceci pourra être utilisé par exemple dans les menus afin d'indiquer l'option survolée.
Nous pourrons recevoir ces informations suivant 2 types d'évènements:
- EnterNotify pour indiquer que la souris pénètre dans la région de notre fenêtre.
- LeaveNotify pour indiquer que la souris quitte la région de notre fenêtre.
Ces informations
seront envoyées à notre fenêtre que celle-ci est ou non
le focus.
Les
informations que vous obtiendrez concerneront l'entrée ou la sortie de
la souris dans la zone cliente de votre fenêtre, c'est à dire la
zone où vous pouvez effectivement agir/dessiner. Vous n'aurez donc pas
d'informations si la souris entre ou quitte la zone de barre de titre ou sur
la bordure de votre fenêtre par exemple.
On imagine
souvent le cas de la souris qui survole une fenêtre, mais l'inverse est
aussi valable pour ces types d'évènements, càd une fenêtre
qui se retrouve sous le pointeur de la souris.
Les types d'évènements impactés sont:
EnterNotify et LeaveNotify qui remplissent la structure suivante : XCrossingEvent (nom du membre: xcrossing).
Il s'agit d'un mixe entre XButtonEvent et XFocusEvent
Il faudra donc utiliser XSelectInput (display, window, EnterNotify | LeaveNotify);
type et Champ | Désignation |
Window subwindow | La fenêtre d'où provient réellement l'évènement (falculté de propagation des évènements qui fait qu'une fenêtre peut recevoir l'évènement à la place de celle d'où est déclenchée l'événement). |
Time time | Moment prècis du clic en millisecondes (n'oublions pas que l'évènement est envoyé dans une file d'évènements et donc ne sera pas traité immédiatement). |
int x, y | Coordonnées du pointeur dans la fenêtre où c'est produit l'évènement |
int x_root, y_root | Coordonnées du pointeur dans la fenêtre root |
Bool same_screen | Indique si le pointeur est toujours dans le même écran. |
unsigned int state | Etat des boutons (y compris les touches ctrl, alt, ...) - Voir paragraphe ci-dessus 'Clic de la souris' |
unsigned int button | Numéro du boutton ayant changé d'état (de 1 à 5) |
int mode | Façon dont c'est produit l'évènement normal, début ou fin de saisie |
int detail | Décrit les relations hiérarchiques entre la fenêtre quittée et celle dans laquelle on entre. |
Bool focus | Indique si vrai que la fenêtre à le focus |
Généralement, le champ détail est analysé pour savoir si l'évènement est à traiter ou non dans notre fenêtre. En effet, ce type d'évènement peut simplement "passer virtuellement" dans notre fenêtre avant d'atteindre sa cible. X Window "passe" en effet "à travers" toutes les fenêtres qui sont comprises entre la dernière fenêtre ayant eu la souris et celle qui la reçoit.
Si une fenêtre reçoit un évènement et que le détail indique NotifyVirtual ou NotifyNonLinearVirtual, alors on peut généralement ignorer l'évènement. Le type d'évènement en question ne faisant que traverser notre fenêtre pour "arriver" à sa destination.
Si vous débutez, ou si vous ne voyez pas l'utilité d'une telle information provenant de X Window, vous pouvez passer au chapitre suivant. Sinon, pour plus de détail, voyez les informations données sur les champs detail et mode.
Exemple clic et entrée ou sortie de la souris
Dans cet exemple, le programme indique que l'on presse un bouton de la souris et si le pointeur entre ou sort de la fenêtre.
Récupérer les positions de la souris hors évènements
XQueryPointer
Bool XQueryPointer(display, win, root_return, child_return, root_x_return,
root_y_return,
win_x_return, win_y_return, mask_return)
Display *display;
Window win;
Window *root_return, *child_return;
int *root_x_return, *root_y_return;
int *win_x_return, *win_y_return;
unsigned int *mask_return;
En sortie:
display et win toujours les mêmes ...
root_return est l'identifiant de la fenêtre root où se trouve notre pointeur
child_return est l'identifiant de la fenêtre ayant le focus (None si la souris n'est pas dans notre écran ou si la fenêtre ne contient pas la souris)
root_x_return et root_y_return indiquent la position de la souris en X et Y dans la root window (0 si la souris n'est pas dans notre écran)
child_x_return et child_y_return indiquent la position de la souris en X et Y par rapport à l'origine de la fenêtre ayant le focus. La souris aura donc des positions négatives si hors de la fenêtre (à gauche et en haut de celle-ci) ou supérieure à la taille de la fenêtre (à droite ou en bas de la fenêtre)
mask_return retourne l'état des boutons de la souris et des boutons
"modifier"
La fonction retourne false si le pointeur de la souris n'est pas dans l'écran indiqué lors de l'appel à la fonction
XQueryPointer peut générer une erreur BadWindow (cf. Intercepter les erreurs non fatales) si la fenêtre est incorrecte.
XGetMotionEvents
Si vous avez utilisé PointerMotionHintMask, cette fonction permet de récupérer les positions de la souris sur un intervale de temps. En effet, vous ne recevrez alors qu'un évènement, mais vous pourrez avoir la liste détaillée des déplacements.
Cette
fonction utilise une éventuelle facultée de mémorisation
du serveur des déplacements de la souris (ce qui signifie qu'elle n'est
pas forcement présente !): motion history buffer
Vous pourrez tester l'existance de cette mémoire via un appel à int XDisplayMotionBufferSize(dpy) qui devra donner une réponse positive.
La fonction XGetMotionEvents est la suivante:
XTimeCoord *XGetMotionEvents(display, win, start, stop, nevents_return)
Display *display;
Window win;
Time start, stop;
int *nevents_return;
En entrée:
On ne présente plus display et win...
start et stop sont la période sur laquelle vous voulez connaître les déplacements de la souris. (timestamp ou CurrentTime). Si stop est dans le futur, alors cela reviend au même que de le positionner à CurrentTime.
nevents_return contient le nombre d'évènements retournés.
En sortie:
typedef struct {
Time time;
short x, y;
} XTimeCoord;
Cette fonction pourra générer une erreur badwindow (cf. Intercepter les erreurs non fatales) si handle incorrecte.
Pointeur de type XTimeCoord sur une zone mémoire contenant la liste des déplacements.
La valeur
de retour peut-être NULL:
- soit parce que la possibilité d'historisation n'existe pas,
- soit car il n'y a pas d'évènement à retourner
- ou encore car start se trouve positionnée dans le futur alors que cette fonction retourne ce qui c'est passée !
- Enfin car start est supérieure à stop !!!
Ne
pas oublier d'effacer la zone mémoire retournée dans XTimeCoord
en utilisant XFree() !
Lire les coordonnées du pointeur de la souris par évènement
Cette fois-ci, nous allons être informés des mouvements de notre souris dans la zone cliente de notre fenêtre.
Le type d'évènement à utiliser sera MotionNotify que l'on peut sélectionner grâce à plusieurs masques (que l'on pourra évidemment combiner entre eux):
- ButtonMotionMask (mouvement avec un bouton enfoncé)
- ButtonnMotionMask (mouvement avec bouton n enfoncé ou n est une valeur entre 1 et 5 pour indiquer le bouton que l'on veut traiter)
- PointerMotionMask (mouvement quelconque du pointeur avec précision maximale du compte rendu des évènements de déplacement, utile pour un programme de dessin par exemple)
- PointerMotionHintMask (mouvement quelconque du pointeur sans précision maximale du compte rendu des évènements de déplacement, allège les échanges !). Voir à la fin de cette page pour plus de détails pour ce masque un peu particulier.
Les informations seront récupérables via la structure XMotionEvent (nom du membre: xmotion).
type et Champ | Désignation |
Window root | Fenêtre racine sur laquelle l'évènement s'est produit |
Window subwindow | La fenêtre d'où provient réellement l'évènement (falculté de propagation des évènements qui fait qu'une fenêtre peut recevoir l'évènement à la place de celle d'où est déclenchée l'événement). |
Time time | Moment prècis du clic en millisecondes (n'oublions pas que l'évènement est envoyé dans une file d'évènements et donc ne sera pas traité immédiatement). |
int x, y | Coordonnées du pointeur dans la fenêtre c'est produit l'évènement (subwindow) |
int x_root, y_root | Coordonnées du pointeur dans la fenêtre root |
Bool same_screen | Indique si le pointeur est toujours dans le même écran. |
unsigned int state | Etat des boutons (y compris les touches ctrl, alt, ...) - Voir paragraphe ci-dessus 'Clic de la souris' |
unsigned int button | Numéro du boutton ayant changé d'état (de 1 à 5) |
Bool same_screen | Indique si le pointeur est sur le même écran ou non |
char is_hint | Indice indiquant que l'évènement MotionNotify a été envoyé normalement ou en réponse au masque PointerMotionHint |
Détail sur PointerMotionHintMask
Il s'agit d'un masque un peu particulier, il ne peut pas être utilisé seul, mais forcement avec ButtonMotionMask, ButtonnMotionMask ( n est une valeur entre 1 et 5 ) ou encore PointerMotionMask.
Ce masque informe le serveur que nous ne voulons pas avoir le détail des évènements de la souris. Ce qui aura pour effet de diminuer le traffic d'informations entre la station et le serveur.
Il sera cependant conseillé de synchroniser les positions de la souris en lisant les positions directement via XQueryPointer ou XGetMotionEvents. De plus, cette synchronisation aura pour effet de relancer l'autorisation de réception d'un nouvel évènement concernant les déplacements de la souris, sinon vous ne recevriez pratiquement plus rien.
Exemple de récupération de l'évènement MotionNotify
Dans
cet exemple, un clic sur un bouton de la souris passera le programme en mode
détaillé ou non (PointerMotionHintMask)
Positionner le pointeur de la souris
Il est possible de positionner le curseur de la souris:
XWarpPointer(display, src_w, dest_w, src_x, src_y, src_width, src_height, dest_x,
dest_y)
Display *display;
Window src_w, dest_w;
int src_x, src_y;
unsigned int src_width, src_height;
int dest_x, dest_y;
Le déplacement
de la souris va déclencher des évènements pour indiquer
la nouvelle position de la souris.
En entrée:
display : Déjà vu...
src_w ou dest_w fenêtre source ou fenêtre destination (Ces paramètres pouvant être positionnés à None)
src_x, src_y : Position d'un rectangle dans la fenêtre source
src_width, src_height : largeur et hauteur du rectangle par rapport au coin supérieur gauche de la fenêtre source. Ces valeurs peuvent être à null. Dans ce cas, le système va déterminer automatiquement ces valeurs. Soit src_width = largeur de la fenêtre - src_x et src_height = hauteur de la fenêtre - src_y.
dest_x et dest_y: Position de la souris dans la fenêtre destination (origine étant le coin supérieur gauche de la fenêtre).
En sortie: la fonction peut déclencher un BadWindow si la fenêtre est incorrecte.
Si
vous avez virtualisé votre système linux sous VirtualBox, le déplacement
de la souris ne se fera pas, la commande est sans effet !!!
Le déplacement de la souris est toujours relatif à quelque chose:
- Soit par rapport à sa position actuelle (déplacement relatif à sa position courante)
Dans ce cas, dest_w est positionné à None et dest_x, dest_y sont les positions relatives à la position courante du pointeur.
- Soit par rapport à la position de la fenêtre ayant actuellement le focus
Dans ce cas, il faut que la fenêtre dont l'identifiant est positionné dans src_w, possède le pointeur et que celui-ci soit dans le rectangle que vous aurez préalablement précisé via src_x, src_y et src_width, src_height. dest_x, dest_y sont les nouvelles positions de la souris par rapport au coin supérieur gauche de la fenêtre.
- Soit par rapport à une fenêtre qui va recevoir le curseur (est donc le focus)
Dans ce cas, dest_w contient le handle de la fenêtre destination et dest_x et dest_y indiquent la position relative au coin supérieur gauche de la fenêtre.
L'écran
est une fenêtre comme une autre. Elle se nomme root window. Il est donc
possible d'indiquer comme fenêtre destination la root window. Cela peut
permettre de déplacer la souris sur la totalité de la surface
de l'écran.
Pour rappel, le handle de la root window se récupère avec :
root_window = DefaultRootWindow (dpy); // Ecran par défaut
root_window = XRootWindow(dpy, n°écran); // En précisant le
n° d'écran
Il peut
être nécessaire de forcer le repositionnement de la souris via
XFlush(dpy);
Convertir des positions de la souris en positions relative à une fenêtre
Voir chapitre complément sur les fenêtres - XTranslateCoordinates()...
Il y a encore des choses à mettre dans ce chapitre...