Tous les exemples du tutorial sont sous forme de sources et
d'exécutables dans le repertoire examples/
.
Pour télécharger un fichier source depuis votre navigateur il suffit
de maintenir enfoncé le bouton [Shift] et de cliquer sur le lien.
/* examples/hello/hello.c */
#include <helium.h>
He_node *princ;
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Hello World");
return HeMainLoop (princ);
}
La ligne #include <helium.h>
inclut tous les fichiers .h
standards (stdio.h
,
stdlib.h
, etc, mais pas math.h
) et les fichiers .h
de
X11, puis définit tous les types et prototypes de fonctions de Helium.
Un « widget » est un objet tel qu'une fenêtre, un bouton, un champ de
saisie, etc. Tous les widgets d'Helium ont le type He_node*
.
Ici on déclare le widget princ
, qui sera la fenêtre principale.
La fonction main
reçoit les arguments de la ligne de commande du
système, et renvoie le code de sortie entier (prototype ANSI).
On commence par appeler HeInit
, qui initialise Helium, ouvre le display
X11 (ou échoue). HeInit
reçoit les arguments de la ligne de commande,
les analyse et retire de argv
toutes les options reconnues (telles que
-display
, -geom
, -color
, etc). C'est pourquoi on passe
argc
et argv
avec des &
.
On crée ensuite la fenêtre principale, le nom de la classe fenêtre
étant « Frame » dans Helium. La fonction HeCreateFrame()
renvoie
l'adresse du widget créé. On ne manipule jamais directement les champs
d'un He_node
, qui est un type interne à Helium, mais on utilise les
nombreuses fonctions fournies par Helium. Par exemple ici, on donne un
titre à la fenêtre avec HeSetFrameLabel()
.
Une fois que tous les widgets on été créés, on appelle HeMainLoop
en
lui donnant la fenêtre principale. HeMainLoop
fait les choses
suivantes : elle affiche la fenêtre principale, applique
éventuellement l'option -geom
sur la taille et la position de la
fenêtre, puis lance la boucle d'évènements infinie, qui attend puis
répartit chaque évènement (souris, clavier, etc).
La fonction HeMainLoop
sort lorsqu'on ferme la fenêtre par le Window
Manager, et donne un code de sortie que l'on renvoie à main()
; c'est
le code de sortie du programme.
Remarque : il ne doit y avoir qu'un seul appel à HeInit
et à HeMainLoop
dans tout le programme.
Pour compiler l'exemple précédent, il suffit de taper :
gcc hello.c -o hello `/chemin-helium/helium-cfg --cflags --libs`
la commande entre backquotes ` `
permet de retrouver les options
dépendant de l'installation d'Helium ; il suffit de remplacer
/chemin-helium
par le bon chemin absolu. Lancer le programme avec les options suivantes et observer :
hello &
hello -help
hello -res
hello -geom 600x500+200-100
Pour faciliter la compilation des exemples de ce tutorial, voici un
petit script en sh
(remplacer /chemin-helium
par le chemin absolu) :
#! /bin/sh
p=/chemin-helium
f=`basename $1 .c`
gcc $f.c -o $f `$p/helium-cfg --cflags --libs`
Appeler ce script "hcomp
", sauver, taper "chmod +x hcomp
". Pour compiler
un fichier "ex.c
", on tape "./hcomp ex.c
" ou "./hcomp ex
".
Pour compiler un exemple et lancer automatiquement l'exécution si la
compilation a réussie, on tape "./hcomp ex.c && ex
".
Voici un Makefile simple ; on inclut le fichier de configuration de
Helium /chemin-helium/.config
, où les variables nécessaires sont
déclarées, en particulier $(CC)
, $(HE_CFLAGS)
et $(HE_LIBS)
.
Attention, vérifiez que les lignes décalées commencent bien par un [TAB].
include /chemin-helium/.config
.c.o :
$(CC) -c $(HE_CFLAGS) $*.c
bouton : bouton.o
$(CC) -o $@ $@.o $(HE_LIBS)
Enfin, voici un Makefile sophistiqué, qui affiche un Message d'aide,
permet de compiler plusieurs programmes et de nettoyer le répertoire.
include /chemin-helium/.config
.c.o :
$(CC) -c $(HE_CFLAGS) $*.c
# Rajoutez ici le nom de votre_prog
EXECS = bouton bouton2 bouton3
help ::
@echo "Options du make : help all clean distclean $(EXECS)"
# Rajoutez ici votre_prog : votre_prog.o
bouton : bouton.o
bouton2 : bouton2.o
bouton3 : bouton3.o
all :: $(EXECS)
$(EXECS) :
$(CC) -o $@ $@.o $(HE_LIBS)
clean ::
\rm -f *.o core
distclean :: clean
\rm -f $(EXECS)
/* examples/button/pressme.c */
#include <helium.h>
He_node *princ, *panel, *butt;
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Premier bouton");
panel = HeCreatePanel (princ);
butt = HeCreateButton (panel);
HeSetButtonLabel (butt, "Press me");
return HeMainLoop (princ);
}
On ne peut pas attacher directement un bouton à une fenêtre ; il faut créer un objet intermédiaire, le widget Panel : c'est un conteneur de boutons, chargé de leur transmettre les évènements X11.
On crée donc la fenêtre princ
; ensuite on crée le Panel avec
HeCreatePanel(
propriétaire)
, le propriétaire étant princ
.
Enfin on crée le bouton butt
, dont le propriétaire est le Panel.
hn = HeCreateClass(owner);
(remplacer Class par Frame, Panel, Button, Text, etc).
Un Frame n'a pas de propriétaire owner
; le propriétaire d'un Panel est
en général un Frame ; le propriétaire d'un bouton, d'un message, d'un
champ de saisie, etc est toujours un Panel. Tous les widgets ont des propriétés communes, telles que taille, position, visible, actif, etc ; on peut changer une propriété Prop par
HeSetProp (hn, value);
(remplacer Prop par X, Y, Width, Height, Show, Active, etc). On peut
récupérer la valeur de cette propriété par
value = HeGetProp (hn);
Les widgets d'une classe Class ont aussi des propriétés spécifiques à
leur classe, par exemple le nom d'un bouton, la valeur d'un champ de
saisie, etc ; on peut changer une telle propriété Prop par
HeSetClassProp (hn, value);
(remplacer ClassProp par ButtonLabel, TextValue, MessageBold, etc).
On peut récupérer la valeur de cette propriété par
value = HeGetClassProp (hn);
Pour détruire un widget, on appelle
HeDestroy (hn);
Cet appel détruit le widget et tous ses fils. Tout widget détruit est
masqué, c'est-à-dire effacé de l'écran. L'affichage est entièrement automatique ; lorsqu'on crée un widget, modifie ses propriété, ou détruit le widget, Helium reporte les changements nécessaires à l'écran (en une seule fois).
pressme.c
ne déclenche aucune action lorsqu'on clique
dessus. On va modifier le programme pour qu'il affiche le nom du
bouton.
/* examples/button/callback.c */
#include <helium.h>
He_node *princ, *panel, *butt;
void butt_proc (He_node *hn)
{
char *nom = HeGetButtonLabel (hn);
printf ("butt_proc: %s\n", nom);
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Action liée au bouton");
panel = HeCreatePanel (princ);
butt = HeCreateButton (panel);
HeSetButtonLabel (butt, "Press me");
HeSetButtonNotifyProc (butt, butt_proc);
return HeMainLoop (princ);
}
On appelle « callback » une fonction de votre programme, qui est appelée automatiquement par Helium lorsqu'un certain évènement se produit.
Il faut dire à Helium quelle callback doit être appelée dans quelle circonstance. Cela s'appelle « attacher » une callback.
Par exemple ici, on attache la callback butt_proc
au bouton butt
.
Cette fonction sera appelée automatiquement par Helium chaque fois que
le bouton sera pressé.
Une callback a toujours un prototype précis, imposé par Helium (puisque c'est Helium qui appelle votre fonction).
Ici, la fonction reçoit un seul paramètre, qui est le bouton pressé. Ainsi, plusieurs boutons peuvent avoir la même callback NotifyProc et déclencher une action dépendant par exemple du nom du bouton.
He_node *butt;
butt = HeCreateButton (panel);
HeSetButtonLabel (butt, "Press me");
HeSetButtonNotifyProc (butt, butt_proc);
par
He_node *butt;
butt = HeCreateButtonP (panel, "Press me", butt_proc, NULL);
Si on n'a pas besoin de mémoriser butt, on peut écrire
HeCreateButtonP (panel, "Press me", butt_proc, NULL);
L'exemple callback.c
devient donc
/* examples/button/raccourci.c */
#include <helium.h>
He_node *princ, *panel;
void butt_proc (He_node *hn)
{
char *nom = HeGetButtonLabel (hn);
printf ("butt_proc: %s\n", nom);
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Raccourci du bouton");
panel = HeCreatePanel (princ);
HeCreateButtonP (panel, "Press me", butt_proc, NULL);
return HeMainLoop (princ);
}
Le quatrième paramètre de HeCreateButtonP permet d'attacher un ClientData au bouton (voir la section « 9.1. Une donnée ClientData »).
/* examples/button/quit.c */
#include <helium.h>
He_node *princ, *panel;
void butt_proc (He_node *hn)
{
char *nom = HeGetButtonLabel (hn);
printf ("butt_proc: %s\n", nom);
}
void quit_proc (He_node *hn)
{
HeQuit (0);
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Quitter un programme");
panel = HeCreatePanel (princ);
HeCreateButtonP (panel, "Press me", butt_proc, NULL);
HeCreateButtonP (panel, "Quit", quit_proc, NULL);
return HeMainLoop (princ);
}
Pour quitter le programme, l'utilisateur presse le bouton "Quit".
À ce moment, Helium appelle la callback du bouton, qui est quit_proc
.
Dans quit_proc
, on appelle HeQuit(0)
, ce qui demande à Helium de
quitter proprement le programme.
Le paramètre de HeQuit est le code de sortie de programme ; typiquement 0 signifie pas d'erreur, et 1 signifie erreur.
On peut appeler HeQuit dans n'importe quelle partie du programme, que ce soit avant, pendant ou après HeInit ou HeMainLoop : HeQuit réagit différemment en s'adaptant à la situation.
Lorsque HeQuit est appelé dans une callback, c'est à dire pendant la durée de vie de HeMainLoop, Hequit détruit récursivement tous les widgets puis ferme le display X11.