Curseur de la souris

 

Le curseur de la souris est (comme souvent dans les interfaces graphiques) l'image représentant la position de la souris à l'écran.

Le curseur sera géré par le serveur, il se débrouillera aussi pour restaurer le fond de l'écran lorsque le curseur se déplace sur l'écran.

Sous X Window, vous devrez tout d'abord créer/instancier un objet curseur. Puis vous aurez à affecter ce curseur à votre fenêtre. Le fait d'avoir instancié votre curseur vous obligera évidemment à supprimer celui-ci lorsque vous n'en n'aurez plus besoin afin de libérer les ressources que cela génère...

 

Créer une instance de curseur "standard"

Il existe bien évidemment (comme dans toutes les interfaces graphiques) des curseurs pré-definis.

Pour les utiliser, il faudra ajouter l'include suivant: <X11/cursorfont.h>. Ce fichier contient les noms des différentes formes pré-définies. Il s'agit en fait de constantes où la valeur représente une forme. Voir exemple de valeurs de cursorfont.h

Pour ce créer une instance de curseur standard, vous utiliserez la fonction suivante:

Cursor XCreateFontCursor (Display* dpy, unsigned int forme)

En entrée:

dpy toujours le même...

forme représentant la valeur de la forme que l'on veut utiliser.

En sortie:

L'identifiant du curseur.

La fonction peut indiquer une erreur:

BadAlloc: Le serveur n'arrive pas à allouer de la mémoire pour la ressource
BadValue: Une valeur dans les paramètres est incorrecte

(cf. Intercepter les erreurs non fatales)

 

Associer notre curseur à une fenêtre

La fonction suivante va permettre d'associer notre curseur de souris à notre fenêtre. Pour cela nous utiliserons la fonction suivante:

int XDefineCursor (Display* dpy, Window w, Cursor cursor);

La fonction peut produire les erreurs suivantes:

BadCursor : L'identifiant du curseur est incorrecte.
BadWindow : L'identifiant de la fenêtre est incorrecte.

(cf. Intercepter les erreurs non fatales)

 

Dissocier notre curseur d'une fenêtre

int XUndefineCursor(Display *display, Window w);

Dans le cas d'une fenêtre standard, le curseur sera remis à celle de sa fenêtre parent.

Pour la fenêtre root, le curseur redeviendra au curseur par défaut du système.

BadWindow est retournée si la fenêtre indiquée n'est pas valide.

(cf. Intercepter les erreurs non fatales)

Libérer les ressources

Pour libérer les ressources utilisées par un curseur, on appellera la fonction XFreeCursor (Display* dpy, Cursor cursor).

Image non trouvée !N'oubliez pas de faire XUndefineCursor avant de libérer la ressource !

BadCursor si identifiant du curseur n'est pas valide !

(cf. Intercepter les erreurs non fatales)

Initialiser le curseur de la souris lors de la création d'une fenêtre "complexe"

Il sera possible de définir le curseur de la souris via la fonction XCreateWindow() en indiquant l'identifiant du curseur dans la structure XSetWindowAttributes.

J'y reviendrai plus tard...

 

Pour résumer

Pas d'exemple, c'est vraiment trop simple. Alors pour résumer:

- Création d'une instance de curseur (soit en utilisant un curseur standard, soit personnalisé)

- Associer ce curseur à une fenêtre

- Lorsque vous n'avez plus besoin du curseur dans la fenêtre vous pouvez le dissocier

- Lorsque vous n'avez plus besoin du curseur dans votre application, vous devez libérer les ressources associées.

 

Créer une instance de curseur "personnalisé"

Il est possible de se créer sa propre image de curseur. Le problème, c'est que pour expliquer cette fonction, il va me falloir anticiper un peu sur les opérations graphiques sous X Window. Je n'irai pas trop dans le détail ici...

X Window n'échappe pas à la règle, comme pour tous les systèmes fenêtrées que je connais, il faut définir deux images.

Une représentant le dessin du curseur, l'autre représentant le masque du curseur. Sachant que le masque doit recouvrir au moins l'image du curseur, et même déborder afin de permettre de faire resortir ce curseur dans le cas où celui-ci serait de la même couleur que le fond sur lequel il se déplace.

La fonction permettant d'informer à X Window le nouveau dessin du curseur est:

Cursor XCreatePixmapCursor(Display *dpy, Pixmap source, Pixmap mask, XColor *fg, XColor *bg, unsigned int x_hot, y_hot);

En entrée:

dpy: connu, l'identifiant sur le serveur X

Pixmap source, mask : les images du curseur: Image et masque

XColor fg, bg : Les couleurs pour le curseur (fg foreground, avant plan) et le masque (background, arrière plan). Evidemment, ces couleurs devront être différentes !

unsigned int x_hot, y_hot : Il s'agit du point d'ancrage dans l'image du curseur. C'est à dire celui qui est utilisé comme référence par le système lorsque vous cliquer par exemple pour savoir si le clique se produit sur telle ou telle fenêtre, ...

BadAlloc : Problème lors de l'allocation mémoire depuis le serveur
BadPixmap : Une valeur de pixmap ne correspond pas à un pixmap

(cf. Intercepter les erreurs non fatales)

 

Pixmap

Un pixmap est souvant utilisé pour créer une icone ou un curseur. Il s'agit d'une resource drawable (au même titre qu'une fenêtre), en quelque sorte, un buffer mémoire logique. Ce pixmap recevra une image (encore appelé bitmap). Cette image est monochrome (voir fichier xbm). Il y a donc une fonction qui va permettre d'associer une image à un pixmap. Sans entrer trop dans le détail (ce n'est pas le but ici), voici une méthode pour se créer un pixmap:

Pixmap XCreatePixmapFromBitmapData(Display *display, Drawable d, data, unsigned int width, unsigned int height, unsigned long fg, unsigned long bg, unsigned int depth)

En entrée:

Display : Toujours notre identifiant au serveur
Drawable d : Zone dessinable (fenêtre root pour nous)
char *data : L'image (Attention, tout est inversé dedans le poid fort et poid faible ainsi que le bit de poid fort et de poid faible)
unsigned int width, height : Largeur et hauteur de l'image (16x16 pour un curseur)
unsigned long fg, bg: La valeur représentant l'image (en principe 1) et le fond (en principe 0)
unsigned int depth: La valeur représentant le nombre de plans pour définir l'image (ici 1 puisque monochrome)

En sortie:

Un identifiant sur la ressource PIXMAP.

Il est aussi possible de charger une image bitmap (xbm) par la fonction XReadBitmapFile(). Dans ce cas, vous utiliserez par exemple gimp pour créer votre image.

BadAlloc : Problème lors de l'allocation mémoire depuis le serveur
BadDrawable : Une valeur drawable ne représente pas une Window Pixmap.
BadGC : Problème sur GContext.
BadMatch : Une fenêtre de type InputOnly est utilisée comme Drawable, ce qui n'est pas possible !

(cf. Intercepter les erreurs non fatales)

Les couleurs

Là encore, ce sujet sera traité plus loin...

Comme il s'agit d'une image monochrome, vous devez indiquer les couleurs pour le curseur et le masque. Pour cela, il faudra définir ces couleurs:

Sans appronfondir, voici un exemple de code pour charger ces couleurs:

XColor rouge, bleu;
Colormap colormap;

colormap = XDefaultColormap(dpy, screen);

// Définition des couleurs manuellement ou en laissant le système nous les donner

rouge.red = 65535;

rouge.green = 0;

rouge.blue = 0;

ou
XParseColor (dpy,colormap, "blue",&bleu);

Maintenant que nous avons nos pixmaps pour le curseur souris et son masque ainsi que les couleurs correspondantes, nous pouvons utiliser notre curseur:

cursor = XCreatePixmapCursor (dpy, pixsouris, pixmasque, &rouge, &bleu, 8, 5);
XDefineCursor (dpy, fen, cursor);

Les couleurs d'un curseur pourront être modifiées grâce à la requête

XRecolorCursor(Display *display, Cursor cursor, XColor *foreground_color, XColor *background_color)

En entrée:

display pour le serveur X...

cursor : identifiant du curseur

XColor *foreground_color, *background_color : les nouvelles couleurs

 

Cette fonction pouvant générer une erreur BadCursor

Taille optimale du curseur

Chaque serveur définit une taille pour représenter les curseurs de la souris. Cette taille dépendant du système, il est possible de la récupérer via deux fonctions:

XQueryBestSize ou XQueryBestCursor qui est le même que XQueryBestSize avec un paramètre en moins qui indique que l'on veut la meilleur taille pour un curseur.

Status XQueryBestSize(Display *display, int class, Drawable which_screen, unsigned int width, unsigned int height, unsigned int *width_return, unsigned int *height_return)

En entrée:
display: Connexion au serveur
class: Classe pour laquelle on veut connaître la taille. Ici se sera CursorShape (les autres valeurs possible étant TileShape ou StippleShape).
which_screen: Un objet drawable de l'écran (PIXMAP ou window)
width: Largeur
height: Hauteur
width_return: Largeur retournée par le système
height_return: Hauteur retournée par le système

XQueryBestCursor(Display *display, Drawable d, unsigned int width, unsigned int height, unsigned int *width_return, unsigned int *height_return)
Les paramètres étant les mêmes (ou presque, puisque un en moins) que la fonction précédente.

Un caractère comme curseur

Au passage, il est possible d'utiliser un caractère comme curseur. Là encore, des notions qui seront vues plus loin, puisqu'il s'agit de fonte...

Cursor XCreateGlyphCursor(Display *display, Font source_font, Font mask_font, unsigned int source_char, unsigned int mask_char, XColor *
foreground_color, XColor *background_color)

Bon, je n'en dirais pas plus, jamais utilisé... Je n'en vois pas comme cela l'utilité. Mais il y en a sûrement une !

Libérer les ressources

Dans le cas de création de curseur personnel, il sera nécessaire de libérer les ressources. Pour libérer les ressources utilisées par un curseur, on appellera là encore la fonction

XFreeCursor (Display *display, Cursor cursor).

Image non trouvée !N'oubliez pas de faire XUndefineCursor avant de libérer la ressource !

 

Mais, cela ne sera pas tout, nous avons aussi créé des pixmaps qui occupent de la place, donc à libérer:

XFreePixmap(Display *display, Pixmap pixmap)

 

Cacher le curseur de la souris

Il n'y a pas de curseur "caché" parmis les définitions standards de la souris, de même qu'il n'y a pas de fonction pour désactiver la gestion de l'affichage d'un curseur. Pour cacher un curseur, il faut créer une instance de curseur "personnalisé" "vide", c'est à dire que le dessin du masque et du curseur doivent utiliser un bitmap avec tous les octets à 0.

Pour "remontrer" un curseur, soit faire undefine du curseur "vide", soit utiliser un des curseurs dans la liste des curseurs standards ou, enfin un que vous vous êtes défini.

 

 

Exemple de code

Ce code reprend l'exemple vu précédemment pour lire les positions de la souris.

Le masque est en bleu, le curseur est en rouge.

Image non trouvée !

Vous verrez aussi à la fin de la procédure tous les appels pour libérer les différentes ressources utilisées. Mais cela ne servira pas puisque pour le moment, nous n'avons pas encore vu comment quitter proprement notre programme !