Index   Table   Précédent   Suivant

9. Compléments

Dans ce chapitre on présente les données attachées en ClientData, les DestroyProc, les arguments de la ligne de commande et les resources X11, les signaux Unix, et des compléments sur les widgets.

Index   Table   Précédent   Suivant

9.1. Une donnée ClientData

Parmi les propriétés communes des widget, il y a une propriété « fourre-tout » qui permet de stocker sur un 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
    /* 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.

Index   Table   Précédent   Suivant

9.2. Destruction d'un widget

On a vu dans la section « 2.4. Principe général » que pour détruire 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)
    {
    }
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
    /* 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);
    }
Index   Table   Précédent   Suivant

9.3. Arguments de la ligne de commande

Helium propose des fonctions qui décodent la ligne de commande. Par défaut, Helium reconnait déjà un certain nombre d'options, telles que "-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
    /* 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.

Index   Table   Précédent   Suivant

9.4. Timeout

On peut demander à Helium d'appeler périodiquement une callback TimeoutProc tous les n millisecondes. Il suffit d'appeler
    HeAddTimeout (interval, timeout_proc, data);
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
    /* 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

Index   Table   Précédent   Suivant

9.5. Bulles et petits conseils

Une bulle est une petite fenêtre sans décoration, dans laquelle est affiché un petit texte d'une ou plusieures lignes. La bulle se met automatiquement à la taille du texte ; elle disparait lorsque l'utilisateur clique dessus.

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
    /* 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
    /* 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
    /* 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);
    }

Index   Table   Précédent   Suivant

9.6. Interception des signaux Unix

On peut utiliser les fonctions Unix 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);
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)
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
    /* 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);
    }

Index   Table   Précédent   Suivant

9.7. Entrées-sorties

En général, l'écriture ou la lecture sur un descripteur de fichier est un appel bloquant, qui peut avoir pour effet de « geler » l'interface graphique de votre programme : rien à lire (tampon de lecture vide), ou impossible d'écrire (tampon d'écriture plein), etc. À éviter absolument !

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);
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
    /* 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
    /* 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).

Index   Table   Précédent   Suivant

9.8. A propos des widgets

Helium est conçu sur un modèle objet très simple, qui est suffisant pour ce que l'on veut en faire, et ne demande pas de mécanismes complexes pour gérer par exemple l'héritage.

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++)
Index   Table   Précédent   Suivant

9.9. Dont je n'ai pas encore parlé

De nombreuses fonctions ne sont pas encore documentées dans le tutorial. En attendant, on peut consulter les sources de libHelium, abondament commentés, situés dans le répertoire gui/.


Index   Table   Début   Suivant