10

La communication entre clients et avec l'utilisateur

 

10.1. La communication entre clients

                  Plusieurs mécanismes de communication entre clients ont été prévus. Tous ces mécanismes suivent les conventions définies dans l'Inter-Client Communications Conventions Manual [ROS 88]. On en a déjà vu indirectement un exemple avec les tables de couleurs standards présentées dans le chapitre précédent, où les fonctions introduites permettent aux différents clients de partager une table de couleurs.

 

                  Il est important de noter que tous les mécanismes de communication passent par l’intermédiaire du serveur. Un client ne peut en effet pas supposer qu’un autre client puisse avoir accès aux mêmes fichiers, ni même qu'il puisse communiquer à travers les mêmes canaux de transmission. Les autres clients peuvent en effet tourner sur des machines provenant de réseaux différents (par exemple DECNET et TCP/IP). On ne doit donc pas — sauf accord préalable — communiquer avec les autres applications en faisant références à des ports, des fichiers, etc. Voici une brève énumération des possibilités de communication actuelles :

            • Les mémoires tampons ou cut buffers. Un mécanisme de communication de chaînes de caractères basé sur l'utilisation de tampons appelés cut buffers, permet d'échanger des chaînes de caractères en faisant appel à des requêtes particulières. Ce mécanisme permet d'implanter facilement un mécanisme de couper/coller entre différentes fenêtres utilisant du texte. Plus généralement, on pourra aussi s'en servir pour échanger des octets ASCII quelconques, dont l'interprétation sera faite par les clients.

            • Les sélections. Le mécanisme des sélections permet de transférer des informations de type arbitraire, chaînes, images, pixmaps, etc. entre diverses applications. On s’en servira pour implanter un mécanisme de couper/coller entre applications sur des objets de format complexe.

            • Les propriétés. Les propriétés sont des structures que l’on peut adresser dans le serveur pour y lire ou écrire n’importe quelle information. Cela permet de mettre à disposition des différents clients des informations de format quelconque. Si une propriété est modifiée par un client, le serveur envoie des événements de type PropertyNotify à tous les clients concernés. Ce mécanisme permet donc aux clients de communiquer entre eux. Le window manager utilise un grand nombre de propriétés et les mécanismes de communication par sélections et mémoires tampons sont en fait basés sur des propriétés particulières attachées à la racine.

                  Les mémoires de couper/coller mises à part, tous les mécanismes de communication entre clients utilisent également des événements. Par exemple, le mécanisme des sélections utilise les événements de type SelectionClear, SelectionNotify et SelectionRequest. De manière générale, les mécanismes de communication basés sur les propriétés utilisent les événements de type PropertyNotify.

                  En outre la fonction XSendEvent permet à une application d'envoyer un événement (quelconque) à une fenêtre donnée indépendamment des mécanismes d'appropriation. On pourra donc soi-même définir un mécanisme de communication en utilisant les propriétés et/ou les événements de type ClientMessage.

 

XSendEvent (dpy, win, propagate, event_mask, &event_send)

Bool propagate ;

long event_mask ;

XEvent            event_send ;

 

                  On peut spécifier la fenêtre win par un identificateur de fenêtre ou par une constante décrivant sa situation relativement au système (PointerWindow ou InputFocus).  La fonction XSendEvent est par exemple utilisée dans la communication entre clients utilisant le mécanisme des sélections.

 

                 

10.2. Les mémoires de couper/coller

                  Ces mémoires, au nombre de 8, sont prédéfinies et numérotées de 0 à 7. Elles sont implantées comme propriétés de la fenêtre racine, mais la librairie Xlib fournit des fonctions simples permettant de les manipuler directement :

XStoreBytes (dpy, tab, ntab)

char * XFetchBytes (dpy, &ntab)

                  La procédure XStoreBytes permet de copier un tableau de caractères tab de longueur ntab dans la mémoire de rang zéro et la fonction XFetchBytes permet de récupérer le contenu de cette mémoire dans un tableau alloué par le serveur. L’entier ntab dont l’adresse est passée en argument contient en retour le nombre de caractères placés dans le tableau retourné. Le client doit ensuite libérer le tableau avec la fonction XFree. Les caractères échangés par ces procédures doivent nécessairement être des caractères ASCII et finir par un caractère nul.

 

                  Pour accéder aux autres mémoires (numérotées de 1 à 7), on utilise de manière similaire les fonctions suivantes, dont l’argument supplémentaire num permet d’identifier le numéro du tampon :

 

XStoreBuffer (dpy, tab, ntab, num_buf)

char * XFetchBuffer (dpy, &ntab, num_buf)

 

                  En outre, la fonction XRotateBuffer (dpy, n) permet d’effectuer une permutation circulaire des contenus des 8 mémoires — le contenu du tampon 0 se trouvant alors placé dans le tampon de rang n (mod 8), celui du tampon 1 dans celui de rang n+1 modulo 8, etc.

 

10.3. Les propriétés et les atomes

                  Les propriétés fournissent le moyen d’associer des données aux fenêtres pour communiquer entre clients. Chaque propriété a un identificateur unique appelé atome (un entier de type Atom servant de nom pour le système) et une propriété est individualisée de manière unique par un atome et une fenêtre. Pour la communication entre clients, cette fenêtre sera souvent la fenêtre racine.

                  Toutes les constantes entières définissant des atomes commencent conventionnellement par le préfixe XA_. Par exemple, il existe une propriété correspondant à l’atome XA_WM_NAME qui se trouve associée à chaque fenêtre et permet de spécifier le nom de l'application. Le window manager pourra utiliser ce nom pour donner un titre aux fenêtres et pour rechercher des valeurs de défaut dans une base de ressources. De même, les mémoires tampons sont en réalité implantées au moyen de propriétés prédéfinies et les atomes qui leur correspondent s’appellent XA_CUT_BUFFER0, XA_CUT_BUFFER1, etc. jusqu’à XA_CUT_BUFFER7.

 

                  Les propriétés possèdent de nombreux attributs :  un nom (chaîne ASCII), un type (qui est lui-même une propriété), un format, et un certain nombre de données associées dans ce format. A partir de la Release 2, il y a 68 propriétés prédéfinies pour la communication avec le window manager, les mécanismes de sélections, les tables de couleurs standards et les fontes. Les atomes des propriétés prédéfinies se trouvent dans le fichier <X11/Xatom.h>.

.h;

                  Les atomes sont de véritables noms propres pour les propriétés et les noms des propriétés prédéfinies ne figurent jamais dans le code. Par contre, pour les propriétés définies par les applications, on utilisera leur nom (type char *) pour définir un atome (entier de type Atom). Ce nom permettra ensuite aux différents clients de récupérer (ou de créer) l’unique atome associé grâce à la fonction :

Atom XInternAtom (dpy, nom, only_if_exists)

const char *   nom ;

Bool only_if_exists ;

 

                  L’argument only_if_exists permet de préciser si la fonction doit ou non créer l’atome dans le cas où il n’existerait pas. S'il est positionné à True, la fonction retourne la valeur None quand l’atome n’existe pas ; positionné à False, elle créera dans ce cas un nouvel atome.

 

                  La fonction qui permet à l’inverse de connaître le nom d’une propriété associée à un atome donné est

 

char * XGetAtomName (dpy, atom)

 

On libérera la mémoire allouée par le serveur par cette fonction avec la fonction XFree.

 

 

10.4. Accès aux propriétés

                  Une liste de propriétés est attachée à chaque fenêtre. Chaque propriété a un nom, un type et une valeur fournie dans un certain format. La valeur est, selon le format utilisé, un tableau de 8, 16 ou 32 bits, et son interprétation est laissée au client.

                  Le type d’une propriété est lui-même une propriété. Les types de propriétés prédéfinis sont indiqués figure 10.1.

 

                  On peut affecter les différents champs d’une propriété avec la fonction XChangeProperty :

 

 

                               ARC                                                POINT
                               ATOM                                             RGB_COLOR_MAP
                               BITMAP                                         RECTANGLE
                               CARDINAL                                   STRING

                               COLORMAP                                 VISUALID

                               CURSOR                                        WINDOW

                               DRAWABLE                                WM_HINTS

                               FONT                                              WM_SIZE_HINTS

                               INTEGER

                               PIXMAP;                                        

 

fig. 10.1. Types de propriétés prédéfinis

.h;

 

XChangeProperty (dpy, w, property, type, format, mode, data, nelem)

Atom                                  property, type ;

int                                        format, mode ;

const unsigned char*   data ;

int                                        nelem ;

 

                  Chaque fois que XChangeProperty est appelée, un événement PropertyNotify est généré. Le format est un entier qui peut valoir 8, 16 ou 32 ; nelem spécifie le nombre d’éléments (au format donné) indiqués dans data. On doit explicitement convertir le tableau de données data  par un cast[1] de(char *). L’argument type n’est pas interprété par le serveur, mais il peut être récupérer ensuite avec XGetWindowProperty. L’argument mode spécifie le mode de l’opération et peut prendre les valeurs PropModeReplace, PropModePrepend, ou PropModeAppend.

 

                  Avec PropModeReplace, la fonction détruit les valeurs précédentes et enregistre les nouvelles. Avec PropModePrepend ou PropModeAppend, XChangeProperty place les nouvelles données respectivement en tête ou en queue des anciennes. Le type et le format doivent coïncider avec les précédents type et format ou une erreur de type BadMatch sera déclenchée.

                  Les propriétés demeurent présentes dans le serveur[2] jusqu’à un appel explicite à XDeleteProperty (dpy, w, property). La destruction d’une propriété déclenche l’envoi d’événement de type PropertyNotify.

                  On récupère les valeurs associées à une propriété avec la fonction XGetWindowProperty :

 

int XGetWindowProperty (dpy, w, property, offset, length, delete,

                                                              request_type, &type_return, &format_return,

                                                              &nitems_return, &bytes_after_return, &prop_return)

Atom                                  property ;

long                   offset, length ;

Bool                  delete ;

Atom                                  request_type, type_return ;

int                                        format_return ;

unsigned long                 nitems_return, bytes_after_return ;

unsigned char*               prop_return;

 

                   La fonction XGetWindowProperty retourne Success en cas de succès. L’argument offset spécifie (en quantité de 32 bits) où les données doivent être récupérées ; length spécifie la quantité de données de 32 bits à récupérer. Le booléen delete spécifie si la propriété doit être détruite après lecture. Le mode "destruction après lecture" permet d'utiliser une propriété comme une sorte de boîte de communication : un premier client y dépose une donnée pouvant être lue par un autre client mais cette donnée n'est ensuite plus disponible si un autre client la prise. L'atome request_type indique le type requis ou bien AnyPropertyType. Les autres arguments sont des valeurs de retour : format_return indique le format effectif, type_return, le type effectif, nitems_return, le nombre d’éléments retournés dans prop_return, et bytes_after_return le nombre d’éléments restant à lire dans la propriété si l’on a procédé à une lecture partielle. On utilisera XFree pour libérer les données ainsi retournées.

 

                  Pour clore cette section sur les propriétés, signalons encore deux fonctions :

 

Atom * XListProperties (dpy, win, &num_prop_return)

 

XRotateWindowProperties (dpy, win, properties, num_prop, npositions)

Atom *             properties ;

int                      num_prop, npositions ;

      XListProperties retourne un pointeur sur un tableau d’atomes qui sont définis pour une fenêtre win donnée. XRotateWindowProperties permet de faire une permutation circulaire sur les propriétés d’une fenêtre.

 

 

10.5. Les sélections

                  Des propriétés (XA_PRIMARY et XA_SECONDARY) ont permis d'implanter un mécanisme de communication de données appelé sélection.  Le mécanisme des sélections est un mécanisme général de communication permettant de partager des données désignées par l’utilisateur.

                  Ce mécanisme utilise des fonctions et des événements particuliers :  SelectionNotify, SelectionRequest et SelectionClear. Son principe est le suivant :  une seule fenêtre est propriétaire à un instant donné de la sélection. XGetSelectionOwner retourne ce propriétaire. On peut se rendre propriétaire de la sélection en faisant appel à XSetSelectionOwner. Pour vérifier qu’on est bien le propriétaire d’une sélection, on appellera ensuite la fonction XGetSelectionOwner.

XSetSelectionOwner (dpy, selection, owner, time)

Window XGetSelectionOwner (dpy, selection)

Atom                selection;

Window          owner ;

Time                 time ;

 

                  Le serveur envoie alors un événement de type SelectionClear à l'ancien propriétaire s’il existe. SelectionClear rapporte à l’ancien propriétaire qu'un nouveau propriétaire est défini. Cet événement n'est pas sélectionné par masque, mais toujours envoyé à l'ancien propriétaire d'une sélection lorsqu'un autre client s’est rendu maître de la même sélection par un appel à XSetSelectionOwner. Cela permet (entre autres) à l'ancien propriétaire de désélectionner à l'écran l'ancienne sélection.

 

                  D’autre part, les autres clients peuvent à tout moment faire appel à la fonction XConvertSelection pour récupérer le contenu de la sélection. Le serveur envoie alors un événement de type SelectionRequest au propriétaire courant qui est tenu d'envoyer en réponse (par XSendEvent) un événement de type SelectionNotify à la fenêtre intéressée.

XConvertSelection (dpy, selection, target, property, requestor, time)

Atom                selection, target, property ;

Window          requestor ;

Time                 time ;

 

                  Les arguments provenant d’un appel à XConvertSelection sont transmis inchangés aux événements. En particulier, les événements SelectionRequest envoyés au propriétaire (s’il existe) auront exactement les mêmes champs. Cependant les événements SelectionNotify envoyés directement par le serveur quand la sélection n’a pas de propriétaire auront un champ property à None.

 

                  Les clients peuvent ainsi échanger des données de type arbitraire et négocier le type des données échangées. Une sélection peut en quelque sorte être vue comme une propriété de type dynamique. C’est-à-dire qu’au lieu d’être mémorisée dans le serveur, la propriété est maintenue par un client (le propriétaire) et est en quelque sorte globale à toutes les fenêtres et tous les clients. Son type pourra être négocié grâce à l’indication d’un type cible (target type). Quand un client demande par XConvertSelection le contenu d’une sélection, il indique en effet un type cible. Le principe est que le propriétaire doit pouvoir interpréter ce type cible et réaliser la conversion en renvoyant un événement de type SelectionNotify ayant le type demandé par le client. S’il ne le peut pas, l’événement SelectionNotify envoyé en retour au demandeur aura un champ property à None. Libre alors au demandeur de reformuler sa question ou d’abandonner la récupération de la sélection.

                 Conventionnellement, la propriété PRIMARY est utilisée chaque fois qu’un argument est suffisant pour une commande. La propriété nommée XA_SECONDARY sera utilisée pour les commandes qui requièrent deux arguments (par exemple un échange de données) ou quand l'utilisateur veut conserver la première sélection. Une sélection nommée CLIPBOARD a également été rajoutée aux conventions de la Release 4 dans le but de conserver la sélection détruite[3]. Les types prédéfinis pouvant être appliqués aux sélections sont les types XA_BITMAP, XA_CARDINAL, XA_INTEGER, XA_PIXMAP, XA_POINT, XA_RECTANGLE, et XA_STRING.

                  Cependant, d'autres types peuvent être utilisés par les clients. Le type cible peut être utilisé pour contrôler la transmission du contenu. Par exemple, si la sélection décrit une image, le type cible pourrait être une indication du format de l’image (en format XY ou en format Z). Le type cible peut aussi être utilisé pour contrôler la nature des informations transmises. Le protocole n’en contraint pas la sémantique.

 

 

                  Pour une meilleure compréhension du mécanisme des sélections, le lecteur consultera l’index alphabétique sur les événements. Les champs des événements de type SelectionClear, SelectionRequest et SelectionNotify y sont détaillés.

 

10.6. Communiquer avec le window manager

                  Des propriétés permettent d'indiquer des préférences au window manager en ce qui concerne la taille des fenêtres, les icônes à utiliser ou le titre à placer dans la bannière de décoration. Les propriétés standards permettant de communiquer avec le window manager sont énumérées ci-après :

 

 

Nom (et type)

Format

Description

 

 

 

 

 

WM_CLASS

(STRING)

8

Affectée par les applications pour permettre au window manager d'obtenir les valeurs de ressources de l'application à partir de la base de ressources.

 

 

 

WM_CLIENT_MACHINE

(TEXT)

 

La chaîne du nom de la machine sur laquelle est exécutée l'application.

 

 

 

WM_COLORMAP_WINDOWS

(WINDOW)

32

La liste des fenêtres qui ont une table de couleurs différente de celle de la fenêtre principale de l'application.

 

 

 

WM_COMMAND

(TEXT)

 

La commande et les arguments utilisés pour invoquer l'application.

 

 

 

WM_HINTS

(WM_HINTS)

32

Des indications essentiellement géométriques pouvant être utilisées par le window manager.

 

WM_ICON_NAME

(TEXT)

 

Le nom à utiliser pour l'icône.

 

 

 

WM_ICON_SIZE

(WM_ICON_SIZE)

32

Le window manager peut affecter cette propriété sur la racine pour indiquer les tailles d'icônes qu'il supporte.

 

 

 

WM_NAME

(TEXT)

 

Le nom de l'application. En particulier, le nom a utiliser pour rechercher des valeurs dans une base de ressources.

 

 

 

WM_NORMAL_HINTS

(WM_SIZE_HINTS)

32

Indications de taille pour une fenêtre dans son état normal.

 

 

 

WM_PROTOCOLS

(ATOM)

32

La liste des atomes qui identifient les protocoles auxquels le client veut participer avec le window manager.

 

 

 

WM_STATE

(WM_STATE)

32

Pour la communication entre window manager et session manager.

 

 

 

WM_TRANSIENT_FOR

(WINDOW)

32

Utilisé par les programmes pour indiquer qu'une fenêtre fille de la racine (comme par exemple une boîte de dialogue) a le type transient, c'est-à-dire souhaite être connue du window manager pour des déplacements éventuels, mais ne souhaite pas avoir de décorations.

 

                  La bibliothèque fournit des fonctions permettant de manipuler les fenêtres filles de la racine[4] par l'intermédiaire de propriétés et de requêtes transmises au window manager. On peut ainsi modifier la géométrie des fenêtres avec :

 

Status XIconifyWindow (dpy, win, screen_number)

Status XWithDrawWindow (dpy, win, screen_number)

Status XReconfigureWMWindow (dpy, win, screen_number, value_mask,

                                                                                &values)

XWindowChanges values ;

 

                  La première fonction est une demande d'iconification. La seconde demande à ce que la fenêtre et son icône soient supprimées. La dernière permet de formuler une requête de reconfiguration. Toutes ces fonctions retournent zéro en cas d'échec.

 

                 

Accès aux propriétés standards

 

                  Les propriétés de type TEXT peuvent être codées de nombreuses manières et dans des formats très différents. Une structure de type XTextProperty est utilisée pour en décrire le codage (cf. figure 10.2.).

 

typedef struct {

              unsigned char *value ;                          /* donnée de la propriété */

              Atom encoding ;                  /* type de la propriété */

              int format ;                                                                              /* 8, 16 ou 32 */

              unsigned long nitems ;                /* nombre d'items dans  value */

} XTextProperty ;

 

fig. 10.2. La structure XTextProperty

 

                  La librairie fournit de nombreuses fonctions de conversions pour permettre la traduction selon la localité. Nous ne détaillerons pas toutes ces fonctions ici[5]. Notons qu'il existe des fonctions permettant la conversion dans le format 8 (STRING), comme XDefaultString, XStringListToTextProperty, etc.

 

Status XStringListToTextProperty (list, count, &text_prop_return)

char **                              list;

int                                        count ;

XTerProperty                 text_prop_return ;

 

Status XTextPropertyToStringList (&text_prop, &list_return,

                                                                                                   &count_return)

XTextProperty text_prop ;

char **                              list_return;

int                                        count_return ;

 

                  Pour libérer l'espace mémoire alloué pour la liste de chaînes, on utilisera la fonction

XFreeStringList(list)

char **                              list ;

 

                  Pour affecter une propriété de type TEXT, on pourra utiliser la fonction de base XSetTextProperty, et pour récupérer une propriété textuelle, la fonction XGetTextProperty.

 

XSetTextProperty (dpy, win, &text_prop, property)

XTextProperty               text_prop ;

Atom                                  property ;

 

XGetTextProperty (dpy,win, &text_prop_return, property)

XTextProperty               text_prop_return ;

Atom                                  property ;

 

 

                  Mais il existe des fonctions spécifiques permettant de récupérer ou d'affecter chacune des propriétés standards, qu'elles soient textuelles ou non. (Pour les propriétés de type TEXT, selon la fonction utilisée, on aura un codage dans l'environnement local ou non.) Ces fonctions spécifiques sont les suivantes :

 

Propriétés                                                   Fonctions d'accès

 

WM_CLASS                                               XSetClassHint     XGetClassHint

                                                                      

WM_CLIENT_MACHINE                      XSetWMClientMachine

                        XGetWMClientMachine

                                                                      

WM_COLORMAP_WINDOWS           XSetWMColormapWindows

                        XGetWMColormapWindows

                                                                      

WM_COMMAND                                     XSetCommand

                        XGetCommand

                                                                      

WM_HINTS                                                XSetWMHints

                        XGetWMHints

                                                                      

WM_SIZE_HINTS                                    XSetWMSizeHints

                        XGetWMSizeHints

 

WM_ICON_NAME                                  XSetWMIconName

                        XGetWMIconName

                        XSetIconName

                        XGetIconName

 

WM_ICON_SIZE                                      XSetIconSizes

                        XGetIconSizes

                                                                      

WM_NAME                                                XSetWMName

                        XGetWMName

                        XStoreName

                        XFetchName

                                                                      

WM_NORMAL_HINTS                          XSetWMNormalHints

                        XGetWMNormalHints

                                                                      

WM_PROTOCOLS                                   XSetWMProtocols

                        XGetWMProtocols

                                                                      

WM_TRANSIENT_FOR;                         XSetTransientForHint

                        XGetTransientForHint

 

                  On notera également l'existence des fonctions XSetWMProperties et XmbSetWMProperties (pour un codage dans la localité) qui permettent d'instancier dans la Release 5 les propriétés standards de communication avec le window manager en un seul appel à la librairie :

 

XSetWMProperties (dpy, win, &window_name, &icon_name, argv, argc,

                                                               &normal_hints, &wm_hints, &class_hints)

Display*                           dpy ;

Window                            win ;

XTextProperty               window_name,

                                             icon_name ;           /* terminent par zéro */

char **                              argv ;                        /* liste des arguments de l'application */

int                                        argc ;

XSizeHints                      normal_hints ;      /* suggestions faites au window manager */

XWMHints                      wm_hints ;            

XClassHint                      class_hints ;

         

                  Pour chaque argument non nul, cette fonction appellera la procédure correspondante du tableau précédent.

10.7. Les fichiers de ressources

                  Les fichiers de ressources de l'utilisateur permettent de spécifier des valeurs pour les champs de différents objets manipulés par les programmes et habituellement introduits par des toolkits.

                  Les bases de ressources ont été prévues à l'origine[6] pour permettre de stocker des valeurs de ressources utilisées comme attributs de widgets. Une application intégrant ces fichiers utilisateur satisfera la demande de l'utilisateur et augmentera la cohérence générale des différentes applications présentes à l'écran. Les widgets sont des objets structurés composés à partir de fenêtres qui peuvent être utilisés par différents programmes comme une bibliothèque de fonctions. Ainsi, un bouton, une barre de défilement, un menu ou une boîte de dialogue peuvent être implantés comme widgets.  Un ensemble de widgets constitue une boîte à outils ou toolkit. Une boîte à outils propose un ensemble de widgets et divers moyens de les combiner par l'intermédiaire d'une bibliothèque de fonctions.

                  Ces objets réagissent de manière standard à diverses actions de l'utilisateur. Par exemple, un bouton peut déclencher une procédure et s'afficher en inverse vidéo quand il est activé par l'utilisateur.

 

                  Pour ce qui nous importe ici, il suffit de savoir que les widgets ont des noms dans les programmes, qu'ils appartiennent tous à des classes (définies par la toolkit) et qu'ils sont hiérarchisés (eux et leurs classes) en arbre. Les noms des widgets utilisés par une application ne sont pas nécessairement connus des utilisateurs mais les noms des classes d'une toolkit donnée sont connus. Un programme a également un nom et peut également faire partie d'une classe de programme. Les noms des classes commencent toujours conventionnellement par une majuscule et les noms de widgets ou d'attributs de widgets par une minuscule. Un programme a également un nom et peut faire partie d'une classe de programme.

 

                  Ainsi par exemple, le programme xmh qui manipule du courrier pourrait faire partie d'une classe Mail de programmes. Le programme xmh utilise une fenêtre principale de nom toc qui est un widget de la classe Paned (panneau). Cette fenêtre contient une boîte de boutons appelée buttons qui fait partie de la classe Box et dans laquelle se trouvent des boutons de commandes. L'un d'entre eux s'appelle incorporate car il permet d'intégrer de nouveaux messages à la boîte de courrier de l'utilisateur. La classe de ce bouton de commande est la classe Command. La fenêtre incorporate correspondant à ce bouton de commande aura pour nom complet la liste des noms des différents objets qui la contiennent dans le programme :

xmh.toc.buttons.incorporate

 

                  Les points indiquent ici les liens de filiation entre les différents objets. De même, le nom complet de la classe correspondant à cette fenêtre sera formé de la concaténation des noms de classe des différents composants :

 

Mail.Paned.Box.Command

 

                  Mais la base de ressources a été construite pour permettre à l'utilisateur d'indiquer des valeurs d'attributs aux objets sans avoir à spécifier le nom complet des objets. Dans une ligne spécifiant des valeurs, on va préciser les noms des objets de manière plus ou moins complète, en ne mentionnant parfois que les classes des objets. On peut alterner indifféremment dans la liste des noms d'objets ou des noms de classes. En outre, on peut remplacer n'importe quel composant intermédiaire par le symbole ? et n'importe quelle suite de composants par le symbole *. Cependant, on doit toujours spécifier le dernier terme, qui décrit un champ terminal de l'objet par son nom ou sa classe. Ainsi la ligne de spécification

 

xmh*Paned*Command.foreground :   blue

indique que la couleur d'avant-plan (attribut foreground) de tous les objets de classe Command qui sont inclus dans un objet de classe Paned à l'intérieur du programme xmh, est bleue. Les attributs ayant également un nom et une classe, on pourra en fait spécifier le dernier champ comme classe d'attributs. Par exemple, la couleur d'un bouton de commande lorsqu'il est actif est stockée dans l'attribut activeForeground, et quand il est inactif dans l'attribut foreground. Ces deux attributs ont la même classe, la classe Foreground. On peut ainsi indiquer que tous les objets du programme xmh seront écrits en bleu, mais que la couleur utilisée par les boutons de commandes lorsqu'ils sont actifs sera verte :

 

xmh*Foreground:                       blue 

*Command*activeForeground:       green

                  Si l'on veut distinguer le bouton de commande spécifique à la commande incorporate, on pourra rajouter la ligne

 

xmh*incorporate.activeForeground:     red

                  Quand plusieurs lignes peuvent être appariées avec la même ressource (comme c'est le cas ici), un algorithme précis est appliqué pour déterminer la valeur correspondant à la classe et à la ressource pour laquelle la valeur est recherchée. Les différentes lignes sont alors comparées, niveau par niveau, en parcourant le nom complet de la ressource de gauche à droite, et les diverses valeurs possibles sont éliminées au fur et à mesure en appliquant les règles suivantes (données ici par ordre de précédence) :

                  • Règle 1 : Priorité de l'explicite sur l'implicite. Les termes contenant des composants de noms ou de classes explicites sur un niveau donné ont priorité par rapport aux termes où le composant de ce niveau est éludé.

                  • Règle 2 : Priorité des objets sur les classes. Un nom a toujours priorité par rapport à une classe ou par rapport au symbole "?".

                  • Règle 3 : Priorité des liens serrés sur les liens lâches. Une entrée précédée d'un lien direct (symbole ".") a toujours priorité par rapport à une entrée précédée d'un lien lâche (symbole "*").

                  Suivons par exemple  l'algorithme de recherche pour la ressource de nom et de classe

 

xmh.toc.messagefunctions.incorporate.activeForeground                   (nom)

Xmh.Paned.Box.Command.Foreground                                                     (classe)

 

effectué sur les cinq lignes de ressources suivantes

 

xmh*Paned*activeForeground:                                   red                             (ligne 1)

*incorporate.Foreground:                                               blue                           (ligne 2)

xmh.toc*Command*activeForeground:  green                        (ligne 3)

xmh.toc?.Foreground:                                    white                        (ligne 4)

xmh.toc*Command.activeForeground:                    black                        (ligne 5)

 

 

                  La première règle élimine la deuxième ligne au profit des quatre autres car xmh est plus explicite que l'étoile. Au niveau suivant, la seconde règle élimine la ligne 1 (toc est un nom plus précis que la classe Paned). Au troisième niveau (messagefunctions et Box) aucune ligne n'est éliminée. Au quatrième niveau, la deuxième règle élimine la ligne 4 et au cinquième niveau, la troisième règle élimine la ligne 3. C'est donc finalement la dernière ligne qui fournit la valeur cherchée.

 

                  On retiendra de cet exemple qu'il est préférable d'éviter ce type de fichiers de ressources. Signalons à ce propos qu'il existe (à partir de la Release 5) un logiciel d'édition de ressources standard, editres, qui permet de visualiser l'arbre des widgets et d'obtenir la liste complète des ressources de chaque widget en pointant directement les objets à l'écran. Cette commande permet aussi de modifier interactivement les valeurs des ressources et de les sauvegarder automatiquement dans des fichiers de ressources.

10.8. Le chargement d'options des applications simples

                  Si l'application n'utilise pas de toolkit, elle peut néanmoins se servir de XGetDefault pour récupérer des valeurs d'options dans une base de ressources. Les options seront alors semblables à des attributs du programme et elles pourront figurer dans un fichier de ressources sous la forme de lignes

 

programme.option:    valeur

 

                  Ainsi les deux lignes suivantes peuvent figurer dans un fichier de ressources :

 

xterm.foreground:    blue

prog.option:         6

 

                  La fonction XGetDefault peut alors être utilisée pour récupérer une valeur de défaut de la forme programme.option :

 

char *XGetDefault (dpy, programme, option)

char *               programme,

                            *option ;

 

                  Cette fonction rapporte la valeur des entrées qui contiennent à la fois le nom du programme et le nom de l'option. La chaîne retournée par XGetDefault appartient à la Xlib et doit être libérée par XFree. La classe utilisée dans cette recherche est la classe de nom Prog.Name. La base de ressources utilisée par cette fonction est créée à partir de plusieurs fichiers de ressources. En particulier, l'algorithme utilise parfois le fichier $HOME/.Xdefaults. De ce fait, certains utilisateurs non avertis croient que c'est dans ce fichier que doivent figurer les valeurs d'options de leurs applications. En réalité, il est préférable que ces options soient placées dans un fichier chargé par la commande xrdb car elles seront plus sûrement intégrées dans ce cas à la base de ressources.

 

 

                  XGetDefault procède en effet de la façon suivante : si une base de ressources a été associée dans la localité au Display (grâce à XrmSetDatabase), XGetDefault interroge cette base de ressources. Sinon, XGetDefault crée une base de ressources et l'affecte au terminal. La base est créée à partir des données stockées dans la propriété RESOURCE_MANAGER si elle est définie. Sinon un fichier de l'utilisateur est recherché dans son répertoire principal. Sur les systèmes POSIX, c'est le fichier $HOME/.Xdefaults. Après avoir chargé ses valeurs dans la base, XGetDefault rajoute les valeurs spécifiées dans le fichier pointé par la variable d'environnement XENVIRONMENT. Si cette variable n'est pas affectée, XGetDefault cherche à fusionner la base avec les valeurs du fichier $HOME/.Xdefaults‑hostname hostname est le nom de la machine sur la­quelle tourne l'application.

 

                  On retiendra donc que la meilleure manière d'instancier des défauts dans une base de ressources est d'affecter la propriété RESSOURCE_MANAGER de la fenêtre racine. Ainsi, tous les utilisateurs et toutes les applications qui seront lancées sur l'écran partageront les mêmes valeurs de défaut : celles qui sont attachées à cette propriété de la racine. La commande xrdb permet de modifier cette propriété et l'on pourra lancer cette commande au démarrage du serveur, dans le fichier .xinitrc, en testant au besoin des variables d'environnement.

 

 

10.9. Accéder aux ressources

                  Les valeurs des attributs et les noms des composants sont donnés sous forme de chaînes de caractères car les entrées sont fournies dans des fichiers. Mais les accès aux chaînes dans une base de données étant inefficaces, les différents composants et valeurs sont en réalité convertis par le serveur en quarks (type XrmQuark). Différentes fonctions permettent de manipuler et créer des quarks. Les requêtes de lecture et d'écriture de la base de ressources existent en deux formules, une formule où les arguments sont exprimés en chaînes de caractères et une formule où les arguments sont exprimés en quarks. Nous ne donnerons ici que les expressions en chaîne de caractères. A partir de la Release 5, ces chaînes de caractères sont codées dans la localité courante.

                  Pour utiliser les requêtes sur les ressources, il faut inclure le fichier <X11/Xresource.h> dans lequel sont déclarées les fonctions concernant les ressources et les constantes qu'elles utilisent. Pour initialiser le gestionnaire de ressources, on utilise la fonction

 void XrmInitialize()

                  Pour récupérer la base partagée associée au Display (à l'ouverture de la connexion) on utilise la fonction

 

XrmDatabase XrmGetDataBase(dpy)

 

                  Si elle n'était pas affectée, on pourra créer une nouvelle base de données à partir de propriétés (RESOURCE_MANAGER ou SCREEN_RESOURCES), de fichiers ($HOME/.Xdefaults etc.), ou des deux[7] ; cette base de ressources pourra alors être affectée au dispositif d'affichage, afin d'être partagée avec les autres clients, grâce à la fonction

 

void XrmSetDatabase(dpy, database)

 

                  On pourra ensuite récupérer des données dans la base partagée avec

Bool XrmGetResource (db, str_name, str_class, &str_type_return,

                                                               &value_return)

XrmDatabase                  db ;

char *                                str_name, str_class, str_type_return ;

XrmValue                        value_return ;

 

                  Cette fonction est moins rapide que son homologue XrmQGetResource qui prend des quarks en argument, mais elle évite d'avoir à utiliser des fonctions de conversions. Les chaînes str_name et str_class décrivant le nom et la classe de la ressource recherchée sont ici des noms complets. La valeur recherchée est rendue sous la forme d'une structure XrmValue qui indique la taille de la valeur retournée et fournit son adresse sous la forme d'un XPointer. La spécification qui aura permis l'appariement avec les spécifications fournies sera retournée dans str_type_return.

                  Pour créer une base de données en récupérant les valeurs d'un fichier de ressources on utilisera la fonction

 

XrmDatabase XrmGetFileDatabase (filename)

char *               filename ;

 

et pour récupérer la propriété RESOURCE_MANAGER attachée à la racine ou la propriété de l'écran SCREEN_RESOURCES dans une chaîne on utilisera respectivement

char *XResourceManagerString (dpy)

char *XScreenResourceString(screen)

Screen *          screen ;

 

                  La propriété RESOURCE_MANAGER contient les valeurs de défauts qui sont indépendantes de l'écran (screen 0) alors que la propriété SCREEN_RESOURCES contient celles qui sont liées à l'écran qui est indiqué en argument. On peut ensuite créer des bases de ressources à partir des données récupérées dans ces propriétés grâce à la requête

 

XrmDatabase XrmGetStringDatabase (data)

char *               data;

 

                  La chaîne sera parcourue en tenant compte du codage dans la localité courante si elle est affectée.

 

                  Il existe également des fonctions permettant de combiner un fichier et une base de données ou de fusionner deux bases de données (XrmCombineFileDatabase et XrmMergeDatabases). Ces fonctions seront utilisées pour créer ou compléter une base de données existante. L'ordre dans lesquelles ces fusions sont opérées détermine quelles bases sont prioritaires. Conventionnellement, la base de ressources doit être formée à partir des ressources suivantes, intégrées dans l'ordre suivant :

                  • le fichier du nom de la classe de l'application (Classname) trouvé dans le répertoire app‑defaults.

                  • le fichier Classname  spécifié par les variables d'environnement XUSERFILESEARCHPATH ou XAPPLRESDIR.

                  • la propriété affectée par xrdb, accessible avec la macro XResourceManagerString, ou si elle est vide, le fichier .Xdefaults.

                  • le fichier pointé par la variable XENVIRONMENT ou si elle n'est pas affectée, le fichier .Xdefaults-hostname.

                  • et enfin, la ligne de commande.

 

                  L'intégration des valeurs d'options de la ligne de commande s'effectuera facilement à l'aide de la fonction XrmParseCommand que nous allons détailler maintenant.

 

 

10.10. Analyser la ligne de commande

                  Il existe plusieurs fonctions utiles pour analyser la ligne de commande. Le grand intérêt de XrmParseCommand est de permettre une analyse de la ligne de commande qui modifie simultanément la base de données conformément aux souhaits de l'utilisateur. Ainsi, les programmes de la toolkit qui gère les widgets et qui consulte également cette base seront en accord avec les défauts fournis par l'utilisateur. La fonction XrmParseCommand admet la syntaxe suivante

void XrmParseCommand (&db, table, table_count, name, &argc, argv)

XrmDatabase                  db ;                           /* la base de ressources */

XrmOptionDescList    table ;                       /* une table d'options */

int                                        table_count ;         /* la taille de la table */

const char*                      name ;                      /* le nom de l'application */

int                                        argc  ;                      /* les arguments du programme */

char**                                argv ;

                  Les arguments argc et argv sont les traditionnels arguments du programme. Ils sont ici donnés par leurs adresses car ils sont modifiés par la requête. L'argument argc contient en entrée le nombre des arguments du programme, et en sortie le nombre des arguments qui n'ont pas été reconnus relativement à la table d'options. De même, argv spécifie en entrée les arguments du programme et fournit en sortie la table de ceux qui n'auront pas été intégrés après analyse.

                  Les arguments sont analysés relativement à une table de description d'options. Quand des options sont reconnues, les valeurs correspondantes sont chargées dans la base de données (avec le nom du programme argv[0] en tête de la description fournie par la table d'option), et les arguments correspondants sont supprimés de la table argv. Si la base passée en argument est NULL, une nouvelle base est créée (dans la localité courante) et son adresse est retournée.

                  L'originalité de cette commande est de prévoir des formats d'options pour l'intégration des valeurs grâce à l'argument table. La table d'options est un tableau de structures de type XrmOptionDescRec. Ces structures contiennent la description d'une option avec quatre champs : une chaîne caractérisant l'option dans la ligne de commande, une chaîne semblable à celle que l'on trouve dans les fichiers de ressources (pour spécifier les liens du nom de la ressource aux autres objets du programme, mais sans le nom du programme en tête), un style d'option, et une valeur, qui sera consultée pour les styles XrmoptionNoArg et XrmoptionSkipNArgs (cf. l'exemple donné figure 10.3. page 265). Les différents styles d'options indiquent où trouver la valeur associée à une option. Ces styles sont les suivants :

 

XrmoptionNoArg              indique que la valeur se trouve dans le dernier champ de description de l'option (OptionDescRec.value). Par exemple  prog -rv, indique que la valeur de "*reverseVideo" doit être "on" conformé­ment à la valeur trouvée dans le champ value de la description de l'option.

 

XrmoptionIsArg              indique que la valeur n'a pas besoin d'être fournie et est indiquée par la présence de la chaîne d'option elle-même. Le programme pourra détecter que cette option a été lue et se comportera en conséquence.

 

XrmoptionStickyArg indique que la valeur est constituée des caractères immédiatement collés à l'option, sans séparation. Ainsi le signe = lorsqu'il introduit l'option géométrie dans prog =100x240+20+10 est une option de style XrmoptionStickyArg.

 

XrmoptionSepArg           indique que la valeur est l'argument qui suit dans la chaîne argv. (C'est le cas courant des options introduites par un tiret comme -fg et -fn dans xterm ‑fg blue ‑fn 9x15.)

 

XrmoptionResArg           Ce type d'option caractérise l'introduction de valeur de ressources. Elle indique que la valeur et la ressource sont à lire dans l'argument suivant.Ainsi par exemple prog ‑res prog*background:white.

 

XrmoptionSkipArg        permet d'ignorer cette option et l'argument qui la suit dans argv.

 

XrmoptionSkipLine     permet d'ignorer cette option et tous les arguments qui suivent dans argv.

 

XrmoptionSkipNArgs permet d'ignorer cette option et les n arguments qui la suivent dans argv, n étant fourni en valeur dans le dernier champ de la description de cette option. Ainsi pour une valeur de 1 cette option est équivalente à XrmoptionSkipArg. Pour une valeur nulle, elle permet d'ignorer l'option elle-même.

static XrmOptionDescRec                 TableOptions [] = {

                  {"-background",  "*background", XrmoptionSepArg, (Xpointer) NULL},

                  {"-bg",   "*background", XrmoptionSepArg, (Xpointer) NULL},

                  {"-fg",    "*foreground", XrmoptionSepArg, (Xpointer) NULL},

                  {"-fn",    "*font", XrmoptionSepArg, (Xpointer) NULL},

                  {"-font", "*font", XrmoptionSepArg, (Xpointer) NULL},

                  {"-foreground", "*foreground", XrmoptionSepArg, (Xpointer) NULL},

                  {"-iconic", ".TopLevelShell.iconic", XrmoptionNoArg, (Xpointer) "on"},

                  {"-name", ".name", XrmoptionSepArg, (Xpointer) NULL},

                  {"-rv",    "*reverseVideo", XrmoptionNoArg, (Xpointer) "on"},

                  {"-synchronous", "*synchronous", XrmoptionNoArg, (Xpointer) "on"},

                  {"-title", ".TopLevelShell.title", XrmoptionSepArg, (Xpointer) "on"},

                  {"-xrm", NULL, XrmoptionResArg, (Xpointer) NULL}

             } ;

 

fig. 10.3. Une table d'options pour analyser la ligne de commande

 

                  Autre fonction utile pour analyser la ligne de commande, XParseGeometry permet d'analyser facilement une chaîne de format géométrique :

int XParseGeometry (parse_string, &x, &y, &width, &height)

const char*                      parse_string ;

int                                        x, y ;

unsigned int                    width, height ;     

                  XParseGeometry prend en argument une chaîne de description d'option géométrique (introduite par -geometry). Pour positionner une fenêtre en (x, y) avec une hauteur height et une largeur width, l'utilisateur pourra lancer un programme avec une option géométrique vérifiant la syntaxe suivante :

 

[<largeur>x<hauteur>] [ {+-}<abscisse>{+-}<ordonnée> ]

 

      XParseGeometry permet d'analyser une telle chaîne et retourne une combinaison de masques permettant de déterminer les valeurs qui étaient présentes dans la description[8]. Les masques de réponses possibles sont définis dans <X11/Xutil.h> et sont les suivants : NoValue, XValue, YValue, WidthValue, HeightValue, AllValues, XNegative , YNegative.   

 

10.11. Quelques conseils d’ergonomie

                  Bien qu'il soit impossible de traiter rapidement des problèmes d'ergonomie,  nous faisons ici, en guise de conclusion à ce manuel, quelques remarques qui permettront peut-être aux débutants d'éviter des erreurs classiques.

 

                  Un des mots clés de l'ergonomie est le mot cohérence. En premier lieu, il faut veiller à la cohérence de la terminologie. Cette cohérence doit se traduire par le fait qu'un même terme doit toujours décrire le même objet ou la même action. Les termes doivent figurer en clair partout où il est question des notions qu'ils recouvrent : dans les commandes, les menus, les boîtes de dialogues, la documentation, le code, etc. En particulier, il ne faudra jamais utiliser de synonymes. Ainsi par exemple, si le mot effacer a été choisi pour décrire une commande permettant d'effacer un mot, ce nom et lui seul doit alors apparaître quand il est question d'enclencher la commande. Les synonymes comme supprimer, enlever,  etc. sont à bannir.

                  Autre cohérence à maintenir, celle des fontes utilisées. Tâchez d'utiliser peu de fontes, une seule idéalement et éventuellement quelques variantes (tailles, italiques, grasses). Ici encore chercher la cohérence dans l'usage des fontes. Ne changez pas de taille à l'intérieur d'un même cadre. Par exemple, toutes les commandes doivent figurer dans la même fonte. Soyez également attentif à donner de la cohérence aux emplois des couleurs. Vous pouvez laisser l'utilisateur choisir une couleur pour l'annonce du danger et une couleur pour l'annonce de confirmation ; maintenez cependant vous-même la cohérence de l'usage de ces deux couleurs. En outre vérifiez (pour les utilisateurs qui ne distinguent pas les couleurs) que les niveaux d'intensités des couleurs proposées soient toujours assez contrastés.

 

                  Respectez les conventions introduites par X. En particulier, respectez les règles de communication entre clients et les règles de communication avec les window manager. Votre application n'en sera que mieux adaptée. En outre, si votre application permet d'entrer du texte ou des dessins, utilisez les conventions de couper/coller pour communiquer avec les autres applications.

 

                  Autre gestion nécessitant de la cohérence, celle de la souris.  Respectez les conventions introduites par les toolkits pour l'utilisation des boutons. Choisissez avec quelle toolkit votre application doit travailler ou prévoyez à tout le moins une compatibilité possible en consultant les documentations. Par exemple, certaines toolkits ont adopté la convention SAM pour les souris à 3 boutons :

 

                  (S)    le premier bouton est réservé à la sélection des objets

                  (A)    le deuxième bouton permet d'ajouter ou d'ajuster des éléments

                  (M)   le troisième bouton permet de modifier les objets et de faire apparaître des menus.

                  Autre autre mot clé de l'ergonomie, le mot utilisateur. Vous devez vous adapter aux goûts des utilisateurs en leur proposant d'affecter les ressources utilisées par votre application sur la ligne de commande ou dans des fichiers de ressources. Concernant le coeur de votre application, vous devez penser à plusieurs types d'utilisateurs : le débutant et l'expert.

 

                  Penser au débutant, c'est prévoir des commandes claires, faciles à déclencher par des menus et des boîtes de dialogues. Penser à l'expert, c'est doubler systématiquement les accès aux commandes souris par un raccourci clavier. C'est aussi prévoir des moyens de faire apparaître des pop-up menus extensibles (menus courts ou menus long) ou reconfigurables. Pour le novice, il faut minimiser les connaissances qu'il doit avoir sur l'application pour s'en servir. Pour l'expert, il faut minimiser le coût des actions, en temps et moyen qu'il doit mettre en oeuvre pour les déclencher. Dans tous les cas cependant il faudra ici encore veiller à la cohérence des commandes. Qu'une commande soit déclenchée par un menu, une combinaison de touches ou certaines actions de la souris sur des icônes, la commande déclenchée doit avoir exactement le même effet.

 

                  Enfin, pour que les utilisateurs puissent suivre le déroulement des commandes, chaque action de l'utilisateur doit se répercuter visuellement ou auditivement (avoir un feed‑back). De même, les transformations internes opérées sur les objets visibles à l'écran doivent être perceptibles pour que l'utilisateur puisse suivre les transformations. De nombreuses techniques existent parmi lesquelles on retiendra les changements de couleurs (l'inverse vidéo noir et blanc ou couleurs), les clignotements, l’entraînement des icônes, les rectangles élastiques qui suivent la souris, les commandes grisées dans les menus (pour marquer l'inaccessibilité), les bips sonores, etc.


 

 

       Les fonctions importantes

 

   XStoreBytes (dpy, tab, ntab)

          char * XFetchBytes (dpy, &ntab)

 

          Atom XInternAtom (dpy, nom, only_if_exists)

   XChangeProperty (dpy, w, property, type, format, mode, data, nelem)

          int XGetWindowProperty (dpy, w, property, offset, length, delete,

                                                               request_type, &type_return, &format_return,

                                                              &nitems_return, &bytes_after_return, &prop_return)

 

   XSendEvent (dpy, win, propagate, event_mask, &event_send)

 

   XSetSelectionOwner (dpy, selection, owner, time)

   XConvertSelection (dpy, selection, target, property, requestor, time)

 

          Status XIconifyWindow (dpy, win, screen_number)

          Status XReconfigureWMWindow (dpy, win, screen_number, value_mask,

                                                                                                  &values)

   XSetIconName (dpy, win, icon_name)

   XStoreName (dpy, win, win_name)

 

          char *XGetDefault (dpy, programme, option)

 

          void XrmInitialize()

          XrmDatabase XrmGetDataBase(dpy)

          char *XResourceManagerString (dpy)

          XrmDatabase XrmGetStringDatabase (data)

 

          void XrmParseCommand (&db, table, table_count, name, &argc, argv)

 

          int XParseGeometry (parse_string, &x, &y, &width, &height)

 

 



[1] En C, on peut forcer la conversion de type en préfixant des variables (de type pointeur) d'une déclaration de type entre parenthèses.

[2] Même si le client qui les a créées meurt.

[3] Il existe d'ailleurs une commande standard xclipboard permettant de visualiser (et de récupérer) cette sélection.

[4]  Les autres fenêtres étant inconnues du window manager, on utilisera les fonctions de base mentionnées chapitre 4 pour les modifier.

[5] Il s'agit de XmbTextListToTextProperty et XwcTextListToTextProperty et de leurs converses  XmbTextPropertyToTextList et XwcTextPropertyToTextList.

[6] On verra dans la section suivante que l'on peut également utiliser les fichiers de ressources pour entrer des valeurs d'options par défaut pour des applications n'utilisant pas de toolkit.

[7] Voir plus loin les fonctions de création et combinaison de bases de ressources.

[8] On ne pourrait pas sinon déterminer si les valeurs de retour ont été affectées ou non.