« Programmation GTK/Ajouter le support XInput » : différence entre les versions

Contenu supprimé Contenu ajouté
DCimarosa (discussion | contributions)
&nbsp -> & nbsp; + essai d'encadrement du code par <code>
DCimarosa (discussion | contributions)
insertion source lang="C"
Ligne 7 :
 
Les signaux GTK que nous avons déjà vus concernent les actions de haut niveau, comme la sélection d'un choix d'un menu. Cependant, il est quelques fois utile de connaître les cas de bas niveau, comme le déplacement de la souris, ou la pression d'une touche. Il existe aussi des signaux GTK correspondant à ces événements bas niveau. Les gestionnaires de ces signaux ont un paramètre supplémentaire qui est un pointeur vers une structure contenant des informations sur l'événement. Par exemple, les gestionnaires des événements de déplacement recoivent un paramètre vers une structure GdkEventMotion qui ressemble (en partie) à ceci&nbsp;:
<source lang="C">
<code>
struct _GdkEventMotion
{
Ligne 19 :
...
};
</codesource>
type sera initialisé avec le type de l'événement, ici GDK_MOTION_NOTIFY, window est la fenêtre dans laquelle l'événement est survenu. x et y donnent les coordonnées de l'événement et state spécifie l'état du modificateur lorsque l'événement s'est produit (c'est-à-dire quelles sont les touches de modification et les boutons souris qui ont été pressés). Il s'agit d'un OU bit à bit de l'une des valeurs suivantes&nbsp;:
<source lang="C">
<code>
GDK_SHIFT_MASK
GDK_LOCK_MASK
Ligne 35 :
GDK_BUTTON4_MASK
GDK_BUTTON5_MASK
</codesource>
Comme pour les autres signaux, on appelle gtk_signal_connect() pour déterminer ce qui se passe lorsqu'un événement survient. Mais nous devons aussi faire en sorte que GTK sache de quels événements nous voulons être avertis. Pour ce faire, on appelle la fonction&nbsp;:
<source lang="C">
<code>
void gtk_widget_set_events (GtkWidget *widget,
gint events);
</codesource>
Le deuxième champ spécifie les événements qui nous intéressent. Il s'agit d'un OU bit à bit de constantes qui indiquent différent types d'événements. Pour référence ultérieure, les types d'événements sont&nbsp;:
<source lang="C">
<code>
GDK_EXPOSURE_MASK
GDK_POINTER_MOTION_MASK
Ligne 61 :
GDK_PROXIMITY_IN_MASK
GDK_PROXIMITY_OUT_MASK
</codesource>
Il y a quelques points subtils qui doivent être observés lorsqu'on appelle gtk_widget_set_events(). D'abord, elle doit être appelée avant que la fenêtre X d'un widget GTK soit créée. En pratique, cela signifie que l'on doit l'appeler immédiatement après avoir créé le widget. Ensuite, le widget doit avoir une fenêtre X associée. Pour des raisons d'efficacité, de nombreux types de widgets n'ont pas de fenêtre propre, mais se dessinent dans la fenêtre de leur parent. Ces widgets sont&nbsp;:
<source lang="C">
<code>
GtkAlignment
GtkArrow
Ligne 85 :
GtkVSeparator
GtkHSeparator
</codesource>
Pour capturer les événements pour ces widgets, on doit utiliser un widget EventBox. Voir la section sur Le widget EventBox pour plus de détails.
 
Ligne 93 :
 
Avec GDK_POINTER_MOTION_HINT_MASK, le serveur nous envoit un événement de déplacement la première fois que la pointeur se déplace après être entré dans la fenêtre, ou après un événement d'appui ou de relâchement d'un bouton. Les événements de déplacement suivants seront supprimés jusqu'à ce que l'on demande explicitement la position du pointeur en utilisant la fonction&nbsp;:
<source lang="C">
<code>
GdkWindow* gdk_window_get_pointer (GdkWindow *window,
gint *x,
gint *y,
GdkModifierType *mask);
</codesource>
(Il existe une autre fonction, gtk_widget_get_pointer() qui possède une interface simple, mais n'est pas très utile car elle ne fait que récupérer la position de la souris et ne se préoccupe pas de savoir si les boutons sont pressés).
 
Le code pour configurer les événements pour notre fenêtre ressemble alors à&nbsp;:
<source lang="C">
<code>
gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
(GtkSignalFunc) expose_event, NULL);
Ligne 117 :
| GDK_POINTER_MOTION_MASK
| GDK_POINTER_MOTION_HINT_MASK);
</codesource>
Nous garderons les gestionnaires de "expose_event" et "configure_event" pour plus tard. Les gestionnaires de "motion_notify_event" et "button_press_event" sont très simples&nbsp;:
<source lang="C">
<code>
static gint
button_press_event (GtkWidget *widget, GdkEventButton *event)
Ligne 149 :
return TRUE;
}
</codesource>
20.3 Le widget DrawingArea et le dessin
 
Ligne 155 :
 
Une zone de dessin est créée avec l'appel&nbsp;:
<source lang="C">
<code>
GtkWidget* gtk_drawing_area_new (void);
</codesource>
Une taille par défaut peut être donnée au widget par l'appel&nbsp;:
<source lang="C">
<code>
void gtk_drawing_area_size (GtkDrawingArea *darea,
gint width,
gint height);
</codesource>
Cette taille par défaut peu être surchargée en appelant gtk_widget_set_usize() et celle-ci, à son tour, peut être surchargée si l'utilisateur modifie manuellement la taille de la fenêtre contenant la zone de dessin.
 
Ligne 171 :
 
Pour créer un pixmap mémoire, on appelle la fonction&nbsp;:
<source lang="C">
<code>
GdkPixmap* gdk_pixmap_new (GdkWindow *window,
gint width,
gint height,
gint depth);
</codesource>
Le paramètre windows indique une fenêtre GTK de laquelle ce pixmap tire certaines de ses propriétés. width et height précisent la taille du pixmap. depth précise la profondeur de couleur, c'est-à-dire le nombre de bits par pixel, de la nouvelle fenêtre. Si cette profondeur vaut -1, elle correspondra à celle de window.
 
Nous créons le pixmap dans notre gestionnaire "configure_event". Cet événement est généré à chaque fois que la fenêtre change de taille, y compris lorsqu'elle initialement créée.
<source lang="C">
<code>
/* Pixmap d'arrière-plan pour la zone de dessin */
static GdkPixmap *pixmap = NULL;
Ligne 205 :
return TRUE;
}
</codesource>
L'appel à gdk_draw_rectangle() remet le pixmap à blanc. Nous en dirons un peu plus dans un moment.
 
Notre gestionnaire d'événement d'exposition copie alors simplement la partie concernées du pixmap sur l'écran (on détermine la zone qu'il faut redessiner en utilisant le champ event->area de l'événement d'exposition)&nbsp:
<source lang="C">
<code>
/* Remplit l'écran à partir du pixmap d'arrière-plan */
static gint
Ligne 223 :
return FALSE;
}
</codesource>
Nous avons vu comment garder l'écran à jour avec notre pixmap, mais comment dessine-t-on réellement ce que l'on veut dans le pixmap ? Il existe un grand nombre d'appels dans la bibliothèque GDK de GTK pour dessiner sur des dessinables. Un dessinable est simplement quelque chose sur lequel on peut dessiner. Cela peut être une fenêtre, un pixmap, ou un bitmap (une image en noir et blanc). Nous avons déjà vu plus haut deux de ces appels, gdk_draw_rectangle() et gdk_draw_pixmap(). La liste complète est&nbsp;:
<source lang="C">
<code>
gdk_draw_line ()
gdk_draw_rectangle ()
Ligne 237 :
gdk_draw_points ()
gdk_draw_segments ()
</codesource>
Consultez la documentation de référence ou le fichier en-tête <gdkgdk.h>/ pour plus de détails sur ces fonctions. Celles-ci partagent toutes les mêmes deux paramètres. Le premier est le dessinable sur lequel dessiner, le second est un contexte graphique (GC).
 
Un contexte graphique encapsule l'information sur des choses comme les couleurs de premier et d'arrière plan et la largeur de ligne. GDK possède un ensemble complet de fonctions pour créer et manipuler les contextes graphiques, mais, pour simplifier, nous n'utiliserons que les contextes graphiques prédéfinis. Chaque widget a un style associé (qui peut être modifié dans un fichier gtkrc, voir la section sur les fichiers rc de GTK). Celui-ci, entre autres choses, stocke plusieurs contextes graphiques. Quelques exemples d'accès à ces contextes sont&nbsp;:
<source lang="C">
<code>
widget->style->white_gc
widget->style->black_gc
widget->style->fg_gc[GTK_STATE_NORMAL]
widget->style->bg_gc[GTK_WIDGET_STATE(widget)]
</codesource>
Les champs fg_gc, bg_gc, dark_gc et light_gc sont indexés par un paramètre de type GtkStateType qui peut prendre les valeurs&nbsp;:
<source lang="C">
<code>
GTK_STATE_NORMAL,
GTK_STATE_ACTIVE,
Ligne 254 :
GTK_STATE_SELECTED,
GTK_STATE_INSENSITIVE
</codesource>
Par exemple, pour GTK_STATE_SELECTED, la couleur de premier plan par défaut est blanc et la couleur d'arrière plan par défaut est bleu foncé.
 
Notre fonction draw_brush(), qui réalise le dessin à l'écran est alors&nbsp;:
<source lang="C">
<code>
/* Dessine un rectangle à l'écran */
static void
Ligne 276 :
gtk_widget_draw (widget, &update_rect);
}
</codesource>
Après avoir dessiné le rectangle représentant la brosse sur le pixmap, nous appelons la fonction&nbsp;:
<source lang="C">
<code>
void gtk_widget_draw (GtkWidget *widget,
GdkRectangle *area);
</codesource>
qui indique à X que la zone donnée par le paramètre area a besoin d'être mise à jour. X génèrera éventuellement un événement d'exposition (en combinant peut-être les zones passés dans plusieurs appels à gtk_widget_draw()) ce qui forcera notre gestionnaire d'événement d'exposition à copier les parties adéquates à l'écran.
 
Ligne 297 :
 
Si l'on examine la définition complète de, par exemple, la structure GdkEventMotion, on voit qu'elle possède des champs pour supporter des informations étendues sur les périphériques.
<source lang="C">
<code>
struct _GdkEventMotion
{
Ligne 313 :
guint32 deviceid;
};
</codesource>
pressure indique la pression comme un nombre réel compris entre 0 et 1. xtilt et ytilt peuvent prendre des valeurs entre -1 et 1, correspondant au degré d'inclinaison dans chaque direction, source et deviceid précisent de deux façons différentes le périphérique pour lequel l'événement est survenus. source donne une simple information sur le type du périphérique. Il peut prendre l'une des valeurs suivantes&nbsp;:
<source lang="C">
<code>
GDK_SOURCE_MOUSE
GDK_SOURCE_PEN
GDK_SOURCE_ERASER
GDK_SOURCE_CURSOR
</codesource>
deviceid précise un ID numérique unique pour le périphérique. Il peut être utilisé pour trouver des informations supplémentaires sur le périphérique en utilisant l'appel gdk_input_list_devices() (voir ci-dessous). La valeur spéciale GDK_CORE_POINTER sert à désigner le périphérique de pointage principal (habituellement la souris).
Valider l'information supplémentaire sur un périphérique
Ligne 331 :
 
Toutefois, nous ne sommes pas complètement à la fin de l'histoire. Par défaut, aucun périphérique d'extension n'est autorisé. Nous avons besoin d'un mécanisme pour que les utilisateurs puissent autoriser et configurer leur extensions. GTK dispose du widget InputDialog pour automatiser cette tâche. La procédure suivante gère un widget InputDialog. Elle crée le dialogue s'il n'est pas présent et le place au premier plan sinon.
<source lang="C">
<code>
void
input_dialog_destroy (GtkWidget *w, gpointer data)
Ligne 365 :
}
}
</codesource>
(vous pouvez remarquer la façon dont nous gérons ce dialogue. En le connectant au signal "destroy", nous nous assurons que nous ne garderons pas un pointeur sur le dialogue après l'avoir détruit -- cela pourrait provoquer une erreur de segmentation).
 
Ligne 374 :
 
La seule modification consiste à appeler gdk_input_window_get_pointer() au lieu de gdk_window_get_pointer. Cela est nécessaire car gdk_window_get_pointer ne retourne pas l'information supplémentaire.
<source lang="C">
<code>
void gdk_input_window_get_pointer (GdkWindow *window,
guint32 deviceid,
Ligne 383 :
gdouble *ytilt,
GdkModifierType *mask);
</codesource>
Lorsque l'on appelle cette fonction, on doit préciser l'ID du périphérique ainsi que la fenêtre. Habituellement, on aura obtenu cet ID par le champ deviceid d'une structure d'événement. Cette fonction retournera une valeur cohérente lorsque les événements ne sont pas autorisés (dans ce cas, event->deviceid aura la valeur GDK_CORE_POINTER).
 
La structure de base des gestionnaires d'événements de déplacement et de bouton pressé ne change donc pas trop -- il faut juste ajouter le code permettant de traiter l'information supplémentaire.
<source lang="C">
<code>
static gint
button_press_event (GtkWidget *widget, GdkEventButton *event)
Ligne 422 :
return TRUE;
}
</codesource>
On doit aussi faire quelquechose de cette nouvelle information. Notre nouvelle fonction draw_brush() dessine avec une couleur différente pour chaque event->source et change la taille du pinceau selon la pression.
<source lang="C">
<code>
/* Dessine un rectangle à l'écran, la taille dépend de la pression,
et la couleur dépend du type de périphérique */
Ligne 458 :
gtk_widget_draw (widget, &update_rect);
}
</codesource>
En savoir plus sur un périphérique
 
Notre programme affichera le nom du périphérique qui a généré chaque appui de bouton. Pour trouver le nom d'un périphérique, nous appelons la fonction&nbsp;:
<source lang="C">
<code>
GList *gdk_input_list_devices (void);
</codesource>
qui retourne une GList (un type de liste chaînée de la bibliothèque glib) de structures GdkDeviceInfo. La structure GdkDeviceInfo est définie de la façon suivante&nbsp:
<source lang="C">
<code>
struct _GdkDeviceInfo
{
Ligne 479 :
GdkDeviceKey *keys;
};
</codesource>
La plupart de ces champs sont des informations de configuration que l'on peut ignorer sauf si l'on implante la sauvegarde de la configuration XInput. Celui qui nous intéresse est name qui est, tout simplement, le nom que X donne au périphérique. L'autre champ, qui n'est pas une information de configuration, est has_cursor. Si has_cursor est faux, on doit dessiner notre propre curseur, mais puisque nous avons précisé GDK_EXTENSION_EVENTS_CURSOR, nous n'avons pas à nous en préoccuper.
 
Notre fonction print_button_press() ne fait parcourir la liste retournée jusqu'à trouver une correspondance, puis affiche le nom du périphérique.
<source lang="C">
<code>
static void
print_button_press (guint32 deviceid)
Ligne 506 :
}
}
</codesource>
Ceci termine les modifications de notre programme « XInputize ». Comme pour la première version, le code complet est disponible à l'endroit où vous avez obtenu ce didacticiel.
Sophistications supplémentaires
Ligne 513 :
 
Pour restaurer un état au prochain démarrage du programme, GDK dispose de fonctions pour changer la configuration des périphériques&nbsp;:
<source lang="C">
<code>
gdk_input_set_extension_events()
gdk_input_set_source()
Ligne 519 :
gdk_input_set_axes()
gdk_input_set_key()
</codesource>
(La liste retournée par gdk_input_list_devices() ne doit pas être modifiée directement). Un exemple est donné dans le programme de dessin gsumi (disponible à l'adresse http://www.msc.cornell.edu/~otaylor/gsumi/). De plus, ce serait pratique d'avoir une méthode standard pour faire cela pour toutes les applications. Ceci appartient probablement à un niveau légèrement supérieur à GTK, peut-être dans la bibliothèque GNOME.