void*
une adresse
quelconque (string, adresse d'un struct, d'un tableau, etc, ou un int
en le castant) dont votre programme a besoin. C'est la propriété
ClientData.
Pour attacher une donnée client_data
à un widget hn
:
HeSetClientData (hn, client_data);
On peut retrouver cette donnée plus tard, par exemple dans une
callback, avec
client_data = HeGetClientData (hn);
On rappelle qu'en C ANSI, le type void*
est compatible avec tous les
types pointeurs, et qu'il faut éviter chaque fois que possible de faire
un cast pour bénéficier pleinement des vérifications du compilateur.
Dans l'exemple clientdata.c
, on crée deux boutons "Asterix" et
"Obelix". On attache en ClientData une légende (un string) à chaque
bouton. Lorsque l'un des boutons est pressé, la callback affi_legende
retrouve la légende dans le ClientData du bouton, puis l'affiche.
/* examples/misc/clientdata.c */
#include <helium.h>
He_node *princ, *panel, *butt1, *butt2;
void affi_legende (He_node *hn)
{
char *nom = HeGetButtonLabel(hn),
*legende = HeGetClientData (hn);
printf ("%s : %s\n", nom, legende);
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Client Data");
panel = HeCreatePanel (princ);
butt1 = HeCreateButton (panel);
HeSetButtonLabel(butt1, "Asterix");
HeSetClientData (butt1, "petit guerrier malin et rusé");
HeSetButtonNotifyProc (butt1, affi_legende);
butt2 = HeCreateButton (panel);
HeSetButtonLabel(butt2, "Obelix");
HeSetClientData (butt2, "gros livreur de menhirs");
HeSetButtonNotifyProc (butt2, affi_legende);
return HeMainLoop (princ);
}
On a vu dans la section « 2.6. Raccourci pour le bouton » la fonction HeCreateButtonP ; le quatrième paramètre de la fonction, que l'on avait laissé à NULL, sert en fait pour attacher un ClientData. Ainsi, le code
butt1 = HeCreateButton (panel);
HeSetButtonLabel(butt1, "Asterix");
HeSetClientData (butt1, "petit guerrier malin et rusé");
HeSetButtonNotifyProc (butt1, affi_legende);
est équivalent à
butt1 = HeCreateButtonP (panel, "Asterix", affi_legende,
"petit guerrier malin et rusé");
Dans la section suivante, on voit comment libérer automatiquement
un ClientData lors de la destruction d'un widget. hn
il suffit d'appeler HeDestroy(hn)
.
En fait cet appel détruit récursivement hn
et tous ses fils. De même, un appel à HeQuit(0) provoque la destruction de tous les widgets du programme.
On peut bien sûr appeler HeDestroy
ou HeQuit
depuis n'importe quelle
callback, par exemple depuis la callback d'un bouton ; on peut aussi
détruire un widget depuis sa propre callback sans problème.
On a la possibilité d'attacher à un widget une callback qui sera
appelée à la destruction de ce widget : c'est la callback DestroyProc.
Pour attacher une DestroyProc à un widget hn
on fait :
HeSetDestroyProc (hn, destroy_proc);
Le prototype de la callback est
void destroy_proc (He_node *hn)
{
}
où hn
est le widget allant être détruit (il existe encore au moment
de l'appel de la callback). Dans l'exemple suivant, on crée quelques widgets auxquels on attache à chacun une DestroyProc, chacune affichant un message lors de son appel. Dans la callback de l'un des boutons on détruit le bouton lui-même, dans une autre on détruit le Panel.
/* examples/misc/destroy.c */
#include <helium.h>
He_node *princ, *panel, *butt1, *butt2, *butt3;
void butt_destroy_proc (He_node *hn)
{
printf ("butt_destroy_proc %s\n", HeGetButtonLabel(hn));
}
void panel_destroy_proc (He_node *hn)
{
printf ("panel_destroy_proc\n");
}
void princ_destroy_proc (He_node *hn)
{
printf ("princ_destroy_proc\n");
}
void butt1_proc (He_node *hn)
{
HeDestroy (hn);
}
void butt2_proc (He_node *hn)
{
HeDestroy (panel);
}
void butt3_proc (He_node *hn)
{
HeQuit(0);
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "DestroyProc");
HeSetDestroyProc (princ, princ_destroy_proc);
panel = HeCreatePanel (princ);
HeSetDestroyProc (panel, panel_destroy_proc);
butt1 = HeCreateButtonP (panel, "Suicide", butt1_proc, NULL);
HeSetDestroyProc (butt1, butt_destroy_proc);
butt2 = HeCreateButtonP (panel, "Détruit Panel", butt2_proc, NULL);
HeSetDestroyProc (butt2, butt_destroy_proc);
butt3 = HeCreateButtonP (panel, "Quit", butt3_proc, NULL);
HeSetDestroyProc (butt3, butt_destroy_proc);
return HeMainLoop (princ);
}
Helium garantit que l'appel de la DestroyProc est unique. Un usage
classique de la DestroyProc est le suivant : lorsqu'une donnée est
allouée avec un malloc
, et que son adresse n'est
mémorisée que dans un ClientData, on attache une DestroyProc pour
libérer automatiquement la mémoire du ClientData à la destruction du
widget.
Voici un exemple de DestroyProc qui fait un free
sur le ClientData :
void destroy_proc (He_node *hn)
{
void *client_data = HeGetClientData (hn);
free (client_data);
}
-help
", "-res
", "-display
", etc. Pour connaître la liste des
options disponibles, il suffit de lancer votre programme avec l'option
"-help
". Le décodage des options de la ligne de commande se fait au moment du
HeInit (&argc, &argv);
Helium parcourt la ligne de commande, et supprime de argc
et argv
toutes les options reconnues. Autrement dit, après l'appel de HeInit,
il ne reste dans argc
et argv
que les arguments qui n'ont pas été
reconnus par Helium (remarque : les variables globales he_argc
et he_argv
conservent la ligne de commande originale ).
Vous pouvez alors décoder les arguments restants avec vos propres
fonctions. Vous pouvez également profiter des fonctions de Helium pour
décoder ces arguments. L'avantage est que vos nouvelles options seront
affichées avec les options d'Helium lorsque vous lancez votre
programme avec l'option "-help
".
On procède en deux temps : avant l'appel de HeInit on déclare les nouvelles options ; après l'appel de HeInit on teste la présence des options dans la ligne de commande.
Pour déclarer une nouvelle option "-pomme
" on fait
HeDeclArg ("-pomme", NULL, NULL, 0, NULL);
Pour qu'il y ait une petite légende affichée lors du "-help
" on fait
HeDeclArg ("-pomme", NULL, NULL, 0, "petite légende");
Pour tester la présence de l'option on fait
if (HeGetOption("-pomme", NULL)) { ... }
0n peut aussi déclarer une option avec un ou plusieurs paramètres, ainsi qu'une petite légende sur l'option et sur le ou les paramètres ; par exemple
HeDeclArg ("-poire", NULL, "<variété>", 1, "petite légende");
HeDeclArg ("-scoubidou", NULL, "<couleur longueur>", 2, "légende");
Lorsque l'option figure dans la ligne de commande, la présence de son
ou de ses paramètres est obligatoire (sinon Helium affiche un message
d'erreur). Pour récupérer le paramètre 1 de l'option on fait
char *p = HeGetOption("-poire", NULL);
Pour récupérer le paramètre numéro i
de l'option on fait
char *p = HeGetOptionN("-scoubidou", i, NULL);
Le deuxième paramètre de HeGetOption (le troisième de HeGetOptionN)
est la valeur renvoyée à p
si l'option n'est pas présente
dans la ligne de commande. Donc ici p
est NULL si l'option
est absente. Dans l'exemple suivant
char *p = HeGetOption("-poire", "williams");
p
vaut "williams
" si l'option est absente, sinon p
vaut le string de
l'option. Voici un exemple récapitulatif :
/* examples/misc/args.c */
#include <helium.h>
He_node *princ;
int main (int argc, char *argv[])
{
int i;
char *tmp;
/* Declarations d'options supplementaires a Helium */
HeDeclArg ("-zero", NULL, NULL, 0, "sans param");
HeDeclArg ("-un", NULL, "<param1>", 1, "un param");
HeDeclArg ("-deux", NULL, "<param1 param2>", 2, "deux params");
/* Affichage des arguments recus avant analyse */
printf ("Args AVANT HeInit() :\n");
for (i = 0; i < argc; i++)
printf (" %2d \"%s\"\n", i, argv[i]);
/* Initialisation d'Helium, ouverture du display et analyse des
arguments : toutes les options reconnues sont enlevees de argv
avec leurs parametres */
HeInit (&argc, &argv);
/* Affichage des arguments restant apres analyse */
printf ("Args APRES HeInit() :\n");
for (i = 0; i < argc; i++)
printf (" %2d \"%s\"\n", i, argv[i]);
/* Affichages des options supplementaires reconnues */
printf ("Options reconnues :\n");
tmp = HeGetOption ("-zero", NULL);
if (tmp) printf ("-zero\n");
tmp = HeGetOption ("-un", NULL);
if (tmp) printf ("-un \"%s\"\n", tmp);
tmp = HeGetOption ("-deux", NULL);
if (tmp) printf ("-deux \"%s\" \"%s\"\n",
tmp, HeGetOptionN("-deux", 2, ""));
/* Creation d'une fenetre */
princ = HeCreateFrame();
HeSetFrameLabel (princ, "Args");
/* Boucle d'evenements */
return HeMainLoop (princ);
}
Les ressources de X11 sont des sortes d'options permanentes, très
souvent stockées dans le fichier texte $HOME/.Xdefaults
. Pour
connaître les ressources reconnues par Helium, il suffit de lancer
votre programme avec l'option "-res
".
Pour rajouter une ressource dans Helium il suffit de la donner en second paramètre à HeDeclArg (on avait jusqu'à présent laissé ce paramètre à NULL). Une ressource peut avoir au maximum un paramètre.
On peut déclarer une ressource qui correspond à une option de la ligne de commande, ou une ressource qui ne correspond à aucune option de la ligne de commande. Dans le premier cas, la présence de l'option supplante la valeur de la ressource.
Pour connaître le paramètre d'une option/ressource (ou leur présence), on utilise HeGetOption ; pour connaître le paramètre d'une ressource seule, on utilise HeGetRessource.
Si on modifie le fichier $HOME/.Xdefaults
, il ne faut pas oublier de
taper xrdb -load $HOME/.Xdefaults
pour mettre à jour le serveur X11
(sinon les changements ne sont pas pris en compte).
On peut lire les nombreux commentaires dans gui/args.c
pour
en savoir plus sur les fonctions gérant les options et les ressources
dans Helium.
HeAddTimeout (interval, timeout_proc, data);
où interval
est la durée de la période en millisecondes,
timeout_proc
est la callback et data
est un void*
.
La fonction HeAddTimeout
renvoie -1 en cas d'erreur, sinon renvoie
un entier handle
> 0 qui sert à indentifier le Timeout.
Le prototype de la callback TimeoutProc est
int timeout_proc (int handle, void *data)
Lorsqu'elle est appelée, la callback reçoit le numéro handle
du
Timeout et la donnée data
passée en argument à HeAddTimeout
.
La callback doit renvoyer TRUE ou FALSE, pour indiquer si le Timeout doit
être supprimé (FALSE) ou réarmé (TRUE). Attention, le décompte du temps
commence au retour de la callback (ce n'est pas du temps réel).Pour supprimer un Timeout en attente, on fait
HeRemoveTimeout (handle);
La fonction renvoie 0 en cas de succès, -1 en cas d'erreur (mauvais
handle). Chaque handle de Timeout est unique ; on peut donc demander de
supprimer un Timeout même s'il n'existe déjà plus, sans risquer de
supprimer un autre Timeout par mégarde.
Dans l'exemple suivant, on enclenche un Timeout à 1000 ms, qui
affiche l'heure en se servant de la fonction système time()
:
/* examples/misc/horloge.c */
#include <helium.h>
He_node *princ, *panel, *mess;
int han1;
int proc1 (int h, void *data)
{
time_t pt;
time(&pt);
HeSetMessageLabel (mess, ctime(&pt));
return TRUE ;
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Horloge");
panel = HeCreatePanel (princ);
mess = HeCreateMessageP (panel, "Quelle heure est-il ?", FALSE);
han1 = HeAddTimeout (1000, proc1, NULL);
return HeMainLoop (princ);
}
Complément : la fonction HeGetTime()
renvoie le temps absolu (obtenu
en interne avec gettimeofday()
) sur un double, en secondes virgule
microsecondes. Ceci peut permettre de mesurer un taux de transfert ou la
durée d'exécution d'une fonction ; voir par exemple :
demo/hsv.c
Le code suivant crée une bulle avec un texte sur deux lignes :
He_node *bubble = HeCreateBubble ();
HeSetBubbleLabel (bubble, "Ceci est un texte\nsur deux lignes");
La chaîne passée à HeSetBubbleLabel
peut contenir des '\n'
,
qui sont interprétés comme des sauts de lignes. On peut récupérer le texte
de la bulle avec
char *label = HeGetBubbleLabel (bubble);
Pour afficher la bulle, on peut se servir des fonctions HeSetX
,
HeSetY
et HeSetShow
, sachant que les coordonnées de la bulle
doivent être données en coordonnées absolues pour l'écran. La fonction
suivante simplifie l'affichage et le positionnement de la bulle :
HeShowBubbleXYF (bubble, ref, x, y, flag);
lorsque ref
est non NULL et flag
est FALSE, affiche la bulle
bubble
aux coordonnées x,y
relatives au widget ref
. Si
ref
est NULL, affiche la bulle en coordonnées x,y
absolues par
rapport à l'écran. Si flag
est TRUE, ignore y
et place la bulle
juste en dessous de ref
. La fonction recadre également la bulle pour
qu'elle apparaisse entièrement à l'écran. Dans ce premier exemple, on crée une bulle et un bouton, et on affiche la bulle en dessous du bouton lorsque le bouton est pressé :
/* examples/misc/bulle1.c */
#include <helium.h>
He_node *princ, *panel, *bulle;
void affi_bulle (He_node *hn)
{
HeShowBubbleXYF (bulle, hn, 50, 0, TRUE);
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Bulle 1");
panel = HeCreatePanel (princ);
HeCreateButtonP (panel, "Affiche bulle", affi_bulle, NULL);
bulle = HeCreateBubble ();
HeSetBubbleLabel (bulle, "Cliquez dans cette bulle\n"
"pour la faire disparaître");
return HeMainLoop (princ);
}
Dans ce deuxième exemple, on crée une bulle et un canvas ; la bulle est utilisée pour afficher les coordonnées de la souris lorsqu'elle est tirée dans le canvas :
/* examples/misc/bulle2.c */
#include <helium.h>
He_node *princ, *canvas, *bulle;
void canvas_event (He_node *hn, He_event *hev)
{
switch (hev->type) {
case ButtonPress :
case MotionNotify :
if (hev->sb == 1) {
char bla[80];
sprintf (bla, "Bulle \nen %d,%d", hev->sx, hev->sy);
HeSetBubbleLabel (bulle, bla);
HeShowBubbleXYF (bulle, hn, hev->sx, hev->sy, FALSE);
}
break;
case ButtonRelease :
if (hev->sb == 1)
HeSetShow (bulle, FALSE);
break;
}
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Bulle 2");
canvas = HeCreateCanvas (princ);
HeExpand (canvas, NULL, HE_BOTTOM_RIGHT);
HeSetCanvasEventProc (canvas, canvas_event);
bulle = HeCreateBubble ();
return HeMainLoop (princ);
}
Les petits conseils (tips), encore appelés bulles d'aides, sont des petits messages qui apparaissent dans des bulles lorsque la souris survole certains widgets. Plus exactement, le conseil est affiché 500 millisecondes après l'entrée de la souris dans un widget ; il est effacé lorsque la souris sort du widget, si un bouton ou une touche est pressé, ou si la souris passe sur le conseil lui-même.
Pour attacher un petit conseil à un widget hn
il suffit de faire
HeSetTip (hn, "Ceci est un petit conseil");
On peut retrouver le conseil attaché à hn
avec
char *label = HeGetTip (hn);
Pour supprimer le conseil attaché à hn
, on fait simplement
HeSetTip (hn, NULL);
L'exemple suivant montre quels types de widgets supportent ce mécanisme :
/* examples/misc/tips.c */
#include <helium.h>
He_node *princ, *panel, *canvas, *tmp;
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Petits conseils (tips)");
panel = HeCreatePanel (princ);
tmp = HeCreateButtonP (panel, "Bouton", NULL, NULL);
HeSetTip (tmp, "Ceci est un bouton");
tmp = HeCreateToggleLedP (panel, "Led", NULL, TRUE);
HeSetTip (tmp, "Ceci est un led");
tmp = HeCreateMessageP (panel, "Message", TRUE);
HeSetTip (tmp, "Ceci est un message");
HeSetPanelLayout (panel, HE_LINE_FEED);
tmp = HeCreateText (panel);
HeSetTextValue (tmp, "Champ de saisie");
HeSetTip (tmp, "Ceci est un champ text");
HeFit (panel);
canvas = HeCreateCanvas (princ);
HeJustify (canvas, panel, HE_TOP);
HeExpand (canvas, NULL, HE_BOTTOM_RIGHT);
HeSetTip (canvas, "Les tips fonctionnent aussi\n"
"pour les Canvas et les GLArea");
return HeMainLoop (princ);
}
signal
ou sigaction
pour capter
un signal Unix et lui assigner un "handler de signal", c'est-à-dire
une callback appelée par le système Unix chaque fois que le signal
est reçu. L'inconvénient est que ce handler peut être appelé à un "mauvais moment" pour le toolkit Helium, pendant lequel un appel aux fonctions de Helium ou de Xlib aurait des effets imprévisibles.
Nous conseillons donc d'utiliser les fonctions de Helium : pour capter un signal sig et attacher une callback SignalProc au signal, on fait
HeAddSignal (sig, sig_proc, data);
où sig_proc
est la callback et data est un void*
. La fonction
HeAddSignal renvoie 0 si l'attachement a réussi, -1 en cas d'erreur
(par exemple si le signal n'existe pas). Le prototype de la callback
est
void sig_proc (int sig, void *data)
où sig
est le signal capté et data
est la donnée attachée avec
HeAddSignal. Helium garantit que la callback sera appelée dans
un moment "sans risque", comme toute autre callback de Helium. À noter,
si un même signal est capté plusieurs fois de suite dans un très court
labs de temps, Helium n'appelle qu'une seule fois la callback
(comme le fait le noyau Unix).
Pour supprimer le mécanisme de capture sur un signal sig
(et rétablir
le handler Unix par défaut) on appelle la fonction suivante, qui renvoie 0
en cas de succès et -1 en cas d'erreur :
HeRemoveSignal (sig);
Helium ne se réserve aucun signal ; en particulier, le mécanisme des Timeout
est gêré par un select()
et non avec le signal SIGALRM, ce qui vous
laisse libre d'utiliser ce signal (mais le mécanisme des Timeout est beaucoup
plus puissant).
Dans l'exemple suivant, on capte les signaux SIGHUP, SIGTERM et SIGINT et SIGUSR1.
Lancer le programme et essayer kill -HUP
ou kill -TERM
ou kill -INT
ou kill -USR1
sur le pid du programme, ou encore ^C :
/* examples/misc/signal.c */
#include <helium.h>
He_node *princ, *panel;
void dialog_proc (char *name, void *client_data)
{
if (!strcmp (name, "Oui")) HeQuit(0);
}
void ask_quit (He_node *hn)
{
HeSimpleDialog (
HE_DIALOG_BELL,
HE_DIALOG_TITLE, "Attention",
HE_DIALOG_MESSAGE, "Etes-vous certain",
HE_DIALOG_MESSAGE, "de vouloir quitter ?",
HE_DIALOG_BUTTON, "Oui",
HE_DIALOG_BUTTOK, "Annuler",
HE_DIALOG_PROC, dialog_proc,
0);
}
void quit_proc (He_node *hn)
{
ask_quit (NULL);
}
void sig1_proc (int sig, void *data)
{
ask_quit (NULL);
}
void sig2_proc (int sig, void *data)
{
printf ("sig2_proc: sig = %d, data = \"%s\"\n", sig, (char*)data);
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Test signaux");
HeSetFrameCloseProc (princ, ask_quit);
panel = HeCreatePanel (princ);
HeCreateButtonP (panel, "Quit", quit_proc, NULL);
HeFit(panel);
HeFit(princ);
printf ("pid: %d\n", getpid());
HeAddSignal (SIGHUP, sig1_proc, NULL);
HeAddSignal (SIGTERM, sig1_proc, NULL);
HeAddSignal (SIGINT, sig1_proc, NULL);
HeAddSignal (SIGUSR1, sig2_proc, "string passé en data");
return HeMainLoop (princ);
}
Helium fournit un mécanisme qui permet d'être averti lorsqu'on peut réaliser une lecture, une écriture ou une lecture urgente sans être bloqué. L'appel
HeAddIO (fd, cond, io_proc, data);
demande à Helium de scruter le descripteur de fichier fd
et d'appeler la callback io_proc
chaque fois que la condition
cond
est réalisée.
fd
peut être un descripteur de fichier
de toute nature (entrée ou sortie standard, tube, socket, etc).
cond
est l'une des constantes HE_IO_READ
(le fd
est prêt
en lecture), HE_IO_WRITE
(le fd
est prêt en écriture),
HE_IO_EXCEPT
(le fd
reçoit les données urgentes, par exemple
avec tcp), ou une combinaison bit-à-bit, par exemple HE_IO_READ |
HE_IO_WRITE
.
Le prototype de la callback IoProc est
void io_proc (int fd, Ulong rcond, void *data);
où data
est la donnée passée en paramètre à HeAddIO
, et
rcond
contient les conditions qui sont satisfaites (combinaison
bit-à-bit). Pour savoir par exemple si la condition HE_IO_WRITE
est
satisfaite, on teste (rcond & HE_IO_WRITE)
.La callback est appelée chaque fois qu'il y a au moins une condition satisfaite ; on peut alors réaliser pendant l'appel de la callback l'opération de lecture ou d'écriture sans risquer d'être bloqué.
La fonction HeAddIO
renvoie -1 en cas d'erreur, 0 sinon.
Chaque appel de HeAddIO
sur un fd
écrase le précédent.
Pour supprimer les scrutations sur un fd
, on appelle
HeRemoveIO (fd);
qui renvoie également -1 en cas d'erreur, 0 sinon. Dans l'exemple suivant, on scrute l'entrée standard et on affiche dans un champ de saisie Text chaque ligne tapée dans la console :
/* examples/misc/conso.c */
#include <helium.h>
He_node *princ, *panel, *text;
void lecture_proc (int fd, Ulong rcond, void *data)
{
char buf[256];
read (fd, buf, 256);
HeSetTextValue (text, buf);
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Lecture console");
panel = HeCreatePanel (princ);
text = HeCreateText (panel);
HeSetTextValue (text, "Tapez du texte dans la console");
/* fd=0 correspond à stdin */
HeAddIO (0, HE_IO_READ, lecture_proc, NULL);
return HeMainLoop (princ);
}
Dans cet exemple on ouvre un tube, on fait un fork()
pour dupliquer
le processus, et le père envoie des octets au fils. On mesure les taux de
transfert à l'aide d'un Timeout tous les 200 ms.
/* examples/misc/tube.c */
#include <helium.h>
He_node *princ, *panel, *mess1, *mess2, *mess3;
int fork_val, timeout1, nb_oper = 0;
double nb_octets = 0, temps0;
/* Le taux de transfert dépend de BUFMAX. Si BUFMAX
est trop grand, la lecture se fait en plusieurs fois */
#define BUFMAX 4096
char buf[BUFMAX];
void tube_write (int fd, Ulong rcond, void *data)
{
int n = write (fd, buf, BUFMAX);
nb_octets += n;
nb_oper++;
}
void tube_read (int fd, Ulong rcond, void *data)
{
int n = read (fd, buf, BUFMAX);
nb_octets += n;
nb_oper++;
}
int timeout1_proc (int h, void *data)
{
char bla[80];
sprintf (bla, "%.0f", nb_octets / 1024);
HeSetMessageLabel (mess1, bla);
sprintf (bla, "%.0f", (nb_octets / 1024) / (HeGetTime()-temps0));
HeSetMessageLabel (mess2, bla);
sprintf (bla, "%.0f", (double) nb_oper / (HeGetTime()-temps0));
HeSetMessageLabel (mess3, bla);
return TRUE ;
}
void butt_quit (He_node *hn)
{
HeQuit(0);
}
int main (int argc, char *argv[])
{
int tube[2];
/* Création tube */
if (pipe(tube) < 0) { perror("pipe"); exit(1); }
/* fils = 0, père != 0 */
fork_val = fork();
/* Le processus propriétaire d'un connection X doit être unique ;
il faut donc impérativement que le fork() ait lieu AVANT HeInit,
car sinon le père et le fils auraient la même connection X. */
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, fork_val == 0 ?
"Tube réception (fils)" : "Tube émission (père)");
panel = HeCreatePanel (princ);
HeCreateButtonP (panel, "Quit", butt_quit, NULL);
HeSetPanelLayout (panel, HE_LINE_FEED);
HeCreateMessageP (panel, fork_val == 0 ?
"K-octets reçus :" : "K-octets envoyés :", TRUE);
mess1 = HeCreateMessage(panel);
HeSetPanelLayout (panel, HE_LINE_FEED);
HeCreateMessageP (panel, "K-octets/seconde :", TRUE);
mess2 = HeCreateMessage(panel);
HeSetPanelLayout (panel, HE_LINE_FEED);
HeCreateMessageP (panel, "opérations/seconde :", TRUE);
mess3 = HeCreateMessage(panel);
if (fork_val == 0)
HeAddIO (tube[0], HE_IO_READ, tube_read, NULL);
else HeAddIO (tube[1], HE_IO_WRITE, tube_write, NULL);
timeout1 = HeAddTimeout (200, timeout1_proc, NULL);
temps0 = HeGetTime();
return HeMainLoop (princ);
}
Compléments : le programme demo/tcp.c
est un exemple de client/serveur TCP/IP. La
partie client permet de dialoguer avec tout serveur TCP/IP (un alter ego,
démons httpd, sendmail, in.pop3d, etc) ; la partie serveur répond à tout
client TCP/IP (un alter ego, telnet, etc).
Les widgets sont ainsi des objets sur deux niveaux ; le premier niveau contient les propriétés communes à tous les widgets, telles que taille et position, visible/caché, actif/inactif, ClientData, etc ; le second niveau contient les propriétés et les méthodes spécifiques à une classe donnée de widgets, tel que le nom d'un bouton, Layout d'un panel, callback d'un champ de saisie, etc.
Dans cette logique, toutes les propriétés Prop du premier niveau sont accessible par
HeSetProp(hn, prop);
prop = HeGetProp (hn);
tandis que toutes les propriétés de second niveau Prop spécifiques à
une classe Class sont accessibles par
HeSetClassProp(hn, prop);
prop = HeGetClassProp (hn);
Dans les sources d'Helium, tous les prototypes de fonctions sont
déclarés dans les fichiers .h du répertoire gui/ .
La plupart d'entre elles ont été vues auparavant dans le tutorial ;
voici quelques fonctions complémentaires :
void HeSetBorder (He_node *hn, int border);
int HeGetBorder (He_node *hn);
utile essentiellement pour les classes Panel et Canvas : ajoute un bord
noir autour du widget (c'est en fait le bord du Window X11 associé).
Window HeGetWindow (He_node *hn);
très utile, renvoie le Window X11 dans lequel est dessiné le widget ;
par exemple, dans un Canvas (zone de dessin Xlib), c'est le Window
dans lequel on va dessiner. Pour un bouton, le Window que l'on obtient
est en fait le Window du Panel propriétaire du bouton, car un bouton
n'est pas un Window X11.
void HeBoundingBox (He_node *hn, int *xmax, int *ymax);
calcule la boîte englobante des fils du widget hn
(sur un seul niveau);
utilisé dans HeFit
.
He_node *HeGetOwner (He_node *hn);
He_node *HeGetNext (He_node *hn);
He_node *HeGetSub (He_node *hn);
renvoient le widget du père (le propriétaire), d'un frère ou d'un fils
du widget hn
; peu utilisé, sauf en interne.
Ulong HeGetNat (He_node *hn);
renvoie un identificateur entier de la classe du widget hn
(voir
include/attr.h
).
Lorsqu'on mémorise un widget, il se peut que l'on ne sache pas s'il
existera encore lorsqu'on voudra l'utiliser. Dans ce cas on demande à
Helium de fournir un identificateur entier unique id
du widget hn
int id = HeGetId (He_node *hn);
et on stocke l'entier id
. Lorsqu'on voudra utiliser le widget stocké,
on commencera par demander à Helium de retrouver le hn
correspondant :
He_node *hn = HeGetNodeFromId (int id);
Si le résultat est NULL, c'est que le widget n'existe plus.
Pour balayer tous les widgets en mémoire on utilise HeNodeScan
:
for (i = 0; hn = HeNodeScan(i) ; i++)
gui/
.