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
ButtonNMotionMask
PointerMotionMask
PointerMotionHintMask

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.

Image non trouvée !Ces informations seront envoyées à notre fenêtre que celle-ci est ou non le focus.

Image non trouvée !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.

Image non trouvée !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.

Image non trouvée !

 

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.

Image non trouvée !

 

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.

Image non trouvée !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.

Image non trouvée !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 !!!

Image non trouvée !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

Image non trouvée !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;

Image non trouvée !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.

Image non trouvée !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.

 

Image non trouvée !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

Image non trouvée !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...