Gestion des terminaux sous UNIX Alain Riffart, ariffart@april.org v1.0, 13 janvier 2001 Ce document tente d'apporter quelques éclaircissements sur la gestion des terminaux sous Linux, FreeBSD, ... en présentant les fonctions C qui permettent d'accéder aux fichiers terminfo. Ces fichiers décrivent les capacités d'un terminal. ______________________________________________________________________ Table des matières 1. Introduction 1.1 La bibliothèque ncurses 1.2 Les codes d'échappement 2. Le paquetage Terminfo 2.1 Emplacement des fichiers terminfo 2.2 Visualisation du contenu d'un fichier terminfo 2.3 Première interprétation du contenu d'un fichier terminfo 3. Première approche des capacités d'un terminal 3.1 Définir le terminal actif : la fonction setupterm 3.2 Le manuel d'aide 3.3 Fonctionnement de setupterm 3.4 Mise en oeuvre de la fonction setupterm 4. Identifier et comprendre les principaux noms de capacités de terminfo 4.1 Gestion du curseur 4.1.1 4.1.1 Visibilité 4.1.2 4.1.2 Positionnement du curseur 4.2 Gestion des couleurs 4.2.1 4.2.1 Capacités générales de traitement de la couleur 4.2.2 4.2.2 Couleur de fond et d'écriture 4.2.3 4.2.3 Réinitialisation des couleurs par défaut 4.3 Effacement de l'écran 4.4 Défilement du texte 5. Accès aux valeurs définies par les capacités d'une base de données terminfo 5.1 Prototype des fonctions d'accès aux capacités 5.2 Cahier des charges des fonctions d'accès 5.3 Exemple d'emploi de tigetflag 5.4 Exemple d'emploi de tigetnum 5.5 Exemple d'emploi de tigetstr 6. Ecriture des valeurs des capacités dans un terminal 6.1 Lecture des chaînes de caractères associées aux capacités clear et cup 6.1.1 6.1.1 Analyse de la valeur affectée à la capacité clear 6.1.2 6.1.2 Composition de séquences d'échappement 6.1.3 6.1.3 Analyse d'une valeur de capacité plus complexe : cup 6.2 Chaîne de caractères à envoyer au terminal pour la capacité cup 6.2.1 6.2.1 la fonction tparm 6.2.2 6.2.2 Exemple d'utilisation de tparm avec la capacité cup 6.3 Fonction d'affichage printf ou putp ? 7. Pallier les insuffisances d'une base de données 7.1 Réécrire un fichier source 7.2 Pourquoi ne pas utiliser les fonctions de la bibliothèque ncurses ? 7.3 Se créer sa propre bibliothèque de fonctions 8. Réalisation d'une bibliothèque de fonctions gérant les capacités d'un terminal 8.1 La fonction tparm est bien un interpréteur 8.2 Ebauche d'une ébauche de bibliothèque de fonctions 8.2.1 8.2.1 Le fichier d'en tête xinuconio.h 8.2.2 8.2.2 Implémentation de quelques fonctions dans le fichier xinuconio.c 8.2.3 8.2.3 Quelques remarques sur les couleurs et les codes 9. Conclusion 10. Copyright ______________________________________________________________________ 1. Introduction Sous les systèmes Unix vous avez accès, en mode console comme en mode graphique,à un ou des terminaux ou émulateurs de terminal. En langage C, Les fonctions de sortie, de la famille printf, sur ces terminaux ne permettent pas de réaliser un affichage satisfaisant. En effet, il est impossible avec ces fonctions de placer un texte dans l'écran, d'effacer l'écran ou une ligne,de mettre certains caractères en surbrillance ou en couleur, etc... Pour pallier cette limitation, il existe deux solutions : · une bibliothèque dédiée ncurses · l'utilisation des codes d'échappement ANSI. 1.1. La bibliothèque ncurses Il s'agit d'une bibliothèque complète. Elle en est à la version 5.2 actuellement. Elle offre bien plus que de simples possibilités de mise en page d'un texte sur un terminal. Elle permet de gérer diverses fenêtres en mode caractères. J'ai un peu travaillé sur cette question et je me propose, si le groupe documentation est d'accord, de rédiger une présentation des différentes fonctionnalités de ncurses dans un prochain article. 1.2. Les codes d'échappement Une présentation de ces codes d'échappement est proposée dans la documentation présente sur le site d'April. Cet article écrit par Cédric Benharous, définit de manière très claire ce que sont les séquences de codes d'échappement. Mon objectif est différent et complémentaire de celui de Cédric. En effet, Cédric livre deux séquences de codes, l'une pour placer le curseur dans l'écran, l'autre pour gérer les couleurs. Ces séquences sont envoyées soit directement sous le shell au terminal, soit en utilisant la fonction printf. Je me fixe un autre objectif. En réalité, il existe un paquetage, nommé Terminfo qui contient un certain nombre de fichiers. Chaque fichier compilé définit des séquences de codes propres à un type de terminal qui lui permettent de gérer différentes fonctionnalités d'affichage. C'est ce paquetage Terminfo que je veux présenter ainsi que les fonctions C qui permettent de l'utiliser d'une manière à peu prés sécurisée. Bien entendu, je vous invite à consulter sur ce sujet le manuel terminfo. Je reviendrai, dans le présent document, de manière succincte sur la notion de séquences de codes d'échappement. 2. Le paquetage Terminfo Le paquetage terminfo est un ensemble de fichiers compilés à l'aide de l'utilitaire tic. Chaque fichier contient une série de commandes capables de réaliser, sur le terminal référencé par ce fichier, toutes les opérations de gestion d'un écran : modification de la visibilité du curseur, déplacement du curseur, défilement du texte, modification des couleurs du texte et/ou du fond, etc...Puisque les fichiers sont compilés cela signifie qu'il existe des fichiers sources, modifiables et directement lisibles. En général un jeu de fichiers sources accompagnent la bibliothèque ncurses. 2.1. Emplacement des fichiers terminfo Les fichiers terminfo sont habituellement situés dans les chemins suivants : · /usr/lib/terminfo · /usr/share/terminfo · /etc/terminfo La dernière localisation est plus rare et ne se rencontre que sur des systèmes plus anciens. Pour faciliter la recherche, chaque fichier compilé décrivant les capacités de traitement d'un terminal est placé dans un sous- répertoire du répertoire terminfo. Le nom de ce sous répertoire est constitué de la première lettre du terminal géré. Le fichier lui-même porte le nom du terminal suivi de différentes lettres qui indiquent les multiples déclinaisons du terminal en question.Ainsi si vous tapez la commande : locate xterm Vous verrez défiler à l'écran toutes les variantes du terminal xterm qui est le terminal habituellemnt émulé sous X Window. Tous ces fichiers sont regroupés dans le répertoire : /usr/share/terminfo/x En ce qui concerne le fichier source, il peut très bien manquer à votre distribution. En tout état de cause vous pourrez le télécharger aux adresses : · http://www.tuxedo.org/terminfo · http://download.sourceforge.net/mirrors/openBSD/share/termtype/termtypes.master 2.2. Visualisation du contenu d'un fichier terminfo Il existe un utilitaire qui permet de visualiser le contenu d'un fichier terminfo décrivant les capacités d'un terminal déterminé. Cet utilitaire est infocmp. Ainsi, si sous le shell vous validez la commande suivante : infocmp xterm Vous obtiendrez l'affichage suivant qui, évidemment, éclairera immédiatement, pour vous, les capacités de traitement d'un terminal xterm! [pierre@localhost pierre]$ infocmp xterm # Reconstructed via infocmp from file: /usr/share/terminfo/x/xterm xterm|xterm terminal emulator (X Window System), am, km, mir, msgr, xenl, cols#80, it#8, lines#24, acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, bel=^G, bold=\E[1m, clear=\E[H\E[2J, cr=^M, csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, cud=\E[%p1%dB, cud1=^J, cuf=\E[%p1%dC, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A, dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM, dl1=\E[M, ed=\E[J, el=\E[K, enacs=\E)0, home=\E[H, ht=^I, il=\E[%p1%dL, il1=\E[L, ind=^J, is2=\E7\E[r\E[m\E[?7h\E[?1;3;4;6l\E[4l\E8\E>, kbs=^H, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, kdch1=\E[3~, kf1=\EOP, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\E[25~, kf14=\E[26~, kf15=\E[28~, kf16=\E[29~, kf17=\E[31~, kf18=\E[32~, kf19=\E[33~, kf2=\EOQ, kf20=\E[34~, kf3=\EOR, kf4=\EOS, kf5=\E[15~, kf6=\E[17~, kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, kfnd=\E[1~, kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~, kslt=\E[4~, rc=\E8, rev=\E[7m, ri=\EM, rmacs=^O, rmcup=\E[2J\E[?47l\E8, rmir=\E[4l, rmkx=\E[?1l\E>, rmso=\E[m, rmul=\E[m, rs2=\E7\E[r\E[m\E[?7h\E[?1;3;4;6l\E[4l\E8\E>, sc=\E7, sgr0=\E[m, smacs=^N, smcup=\E7\E[?47h, smir=\E[4h, smkx=\E[?1h\E=, smso=\E[7m, smul=\E[4m, tbc=\E[3g, u6=\E[%i%d;%dR, u7=\E[6n, u8=\E[?1;2c, u9=\E[c, 2.3. Première interprétation du contenu d'un fichier terminfo Nous comprenons tout de même que passée la première ligne qui rappelle les différents noms du terminal nous trouvons une série d'identificateurs. Chaque identificateur désigne une capacité de traitement du terminal ; pour cette raison ils sont souvent appelés capname. Nous constatons que chaque identificateur de capacité du terminal est séparé du suivant par une virgule. Il est clair également que nous pouvons distinguer trois types différents d'identificateur : 1. Les premiers sont immédiatement suivis d'une virgule et sont de type booléen. 2. Les deuxièmes sont suivis d'un dièse, lui-même suivi d'une valeur numérique. Ils sont de type numérique. 3. Les troisièmes sont suivis du signe =, lui-même suivi d'un ensemble de caractères. Ils sont de type chaîne de caractères. Cette première approche nous a permis de débroussailler le terrain. Nous allons tenter d'utiliser les outils mis à la disposition du programmeur pour accéder proprement aux différentes capacités d'un terminal. 3. Première approche des capacités d'un terminal Peut-être est-il bon de rappeler que vous pouvez connaître le terminal utilisé par votre système en affichant la valeur de la variable d'environnement TERM. Pour cela, sous le shell validez la commande : echo $TERM Sous un système Linux en mode console vous obtiendrez sans doute la réponse linux, en mode graphique sous X Window, la réponse xterm. 3.1. Définir le terminal actif : la fonction setupterm La première idée qui vient à l'esprit quand on veut se familiariser avec la gestion des terminaux, c'est d'avoir la possibilité de décider du terminal actif, et de savoir s'il existe une base de données terminfo décrivant les capacités de tel terminal. Pour cela il existe une fonction dont le prototype est le suivant : int setupterm(const char * term, int fildes, int errret); 3.2. Le manuel d'aide Fin connaisseur de votre système, vous vous précipitez sur les pages du manuel d'aide, pour connaître le cahier des charges de cette fonction en tapant : man setupterm ... et vous obtenez la réponse décevante suivante : No manual entry for setupterm En effet, cette fonction est incluse dans la bibliothèque ncurses et vous devrez procéder en deux temps pour accéder au manuel d'aide de la fonction setupterm. D'abord invoquer l'aide général de ncurses : man ncurses Ensuite, repérer dans cette aide la fonction setupterm et le sous- ensemble dans lequel elle est située. Il s'agit de curs_terminfo. Donc en utilisant la commande man curs_terminfo, vous obtiendrez les renseignements voulus sur la fonction setupterm. 3.3. Fonctionnement de setupterm Je me contenterai de fournir une traduction du passage de la page du manuel qui décrit la fonction setupterm. int setupterm(const char * term, int fildes, int errret); La fonction setupterm lit dans la base de données terminfo, les structures terminfo sont ainsi initialisées...Le type de terminal est la chaîne de caractères term ; si term vaut NULL, la variable d'environnement TERM est utilisée.Toutes les sorties sont envoyées sur le descripteur de fichier fildes qui doit être initialisé en écriture. Si errret ne vaut pas NULL, la fonction setupterm renvoie OK ou ERR et charge la valeur du statut d'erreur dans l'entier pointé par errret. Les valeurs possibles de errret sont : · 1 : tout est normal, · 0 : le terminal appelé n'a pas été trouvé, · -1 : la base de données terminfo n'a pas été trouvée. Si errret vaut NULL, setupterm affiche un message d'erreur correspondant à l'erreur décelée et interrompt le programme.Ainsi l'appel le plus simple est : setupterm((char *)0,1,(int *)0); qui utilise toutes les valeurs par défaut et envoie les sorties sur stdout. 3.4. Mise en oeuvre de la fonction setupterm Vous pourriez tester et mettre en oeuvre un programme qui ressemblerait à celui-ci : #include #include #include int main(int argc, char * argv[]) { int r; int erreur; if (argc>2) r=setupterm(NULL, fileno(stdout),&erreur); else r=setupterm(argv[1], fileno(stdout),&erreur); if (r==OK) printf("Traitement correct\n"); else switch(erreur){ case 0 : printf("Le terminal %s est inconnu.\n",argv[1]); break; case -1 : printf("Aucune base de données pour le terminal %s.\n",argv[1]); break; } return 0; } J'espère ne vexer personne par l'édition d'un programme aussi trivial. Il n'est là que pour rappeler : · Les fichiers d'en-tête à inclure : 1. stdio.h pour les fonctions d'entrée-sortie, dont la fonction fileno, 2. term.h pour les fonctions de gestion de terminfo, 3. ncurses.h pour les constantes OK et ERR définis dans cette librairie. · La ligne de commande à utiliser pour compiler ce programme : gcc prog.c -o prog -lncurses Cette dernière ligne n'a pas d'autres ambitions que de permettre à l'éditeur de liens de retrouver la bibliothèque ncurses dont il a besoin pour le code de certaines fonctions : d'où -lncurses. 4. Identifier et comprendre les principaux noms de capacités de ter­ minfo Je vous propose de chercher à identifier les noms des capacités reconnus par les bases de données terminfo en les classant par catégories et, éventuellement sous-catégories, les plus souvent nécessaires et utilisées dans une gestion d'affichage des sorties écrans. Ces catégories sont les suivantes : 1. Le curseur, · visibilité, · emplacement, 2. les couleurs, 3. l'effacement de l'écran, 4. le défilement des lignes d'écriture. Bien entendu, cette liste est loin d'être exhaustive et si d'éventuels lecteurs cherchent à utiliser d'autres capacités, je les invite à me contacter pour compléter cette modeste étude. La référence en la matière est le manuel d'aide de terminfo disponible sur tous les systèmes. Je me contente de rappeler et de traduire les informations auxquelles vous pouvez accéder en tapant : man terminfo 4.1. Gestion du curseur Vous trouverez, dans ce paragraphe et dans les suivants, des tableaux dont la compréhension est assez évidente. La première colonne, sous le titre Signification contient le nom significatif de la capacité, la deuxième colonne, sous le titre Nom de capacité contient le nom de la capacité telle qu'elle apparaît dans une base de données terminfo, enfin, la troisième colonne, sous le titre Utilisation contient une brève explication de la fonctionnalité de la capacité. 4.1.1. 4.1.1 Visibilité Il arrive très souvent que l'on veuille rendre invisible le curseur lors d'un affichage. Pour cela terminfo définit trois capacités : Signification Nom de capacité Utilisation cursor_invisible civis Curseur rendu invisible cursor_normal cnorm Curseur apparence normale(Annulation de civis/cvvis) cursor_visible cvvis Curseur en surbrillance 4.1.2. 4.1.2 Positionnement du curseur L'une des fonctionnalités essentielles que l'on attend des capacités de gestion d'un terminal est la possibilité d'afficher un texte à un emplacement déterminé.Cette fois nous avons retenu sept capacités : Signification Nom de capacité Utilisation cursor_address cup Curseur en ligne, colonne cursor_down cud1 Curseur une ligne plus bas cursor_home home Curseur en 0,0 cursor_left cub1 Curseur à gauche début ligne cursor_right cuf1 Curseur à droite fin ligne cursor_to_ll ll Dernière ligne,première colonne cursor_up cuu1 Curseur une ligne plus haut 4.2. Gestion des couleurs 4.2.1. 4.2.1 Capacités générales de traitement de la couleur Signification Nom de capacité Utilisation max_colors colors Nombre maximum de couleurs supportées max_pairs pairs Nombre maximum de paires de couleurs supportées 4.2.2. 4.2.2 Couleur de fond et d'écriture Signification Nom de capacité Utilisation set_a_background setab Active la couleur de fond en utilisant les codes d'échappement ANSI set_a_foreground setaf Active la couleur d'écriture en utilisant les codes d'échappement ANSI 4.2.3. 4.2.3 Réinitialisation des couleurs par défaut Signification Nom de capacité Utilisation orig-pair op Rétablit les couleurs fond/écriture dans leur valeur par défaut 4.3. Effacement de l'écran Encore une fonctionnalité primordiale pour la gestion des sorties sur un terminal, les capacités qui gèrent l'effacement de l'écran : en voici quatre... Signification Nom de capacité Utilisation clear_screen clear Efface l'écran et curseur en 0,0 clr_bol el1 Efface jusqu'au début de la ligne clr_eol el Efface jusqu'à la fin de la ligne clr_eos ed Efface jusqu'à la fin de l'écran 4.4. Défilement du texte Signification Nom de capacité Utilisation scroll-forward ind Déplace le texte vers le haut scroll-reverse ri Déplace le texte vers le bas 5. Accès aux valeurs définies par les capacités d'une base de données terminfo 5.1. Prototype des fonctions d'accès aux capacités Il existe trois fonctions qui permettent d'accéder aux valeurs des capacités définies dans une base de données d'un terminal déterminé : · int tigetflag(const char * capname); qui renvoie la valeur des capacités de type booléen. · int tigetnum(const char * capname); qui renvoie la valeur des capacités de type numérique. · char *tigetstr(const char * capname); qui renvoie la valeur des capacités de type chaîne de caractères. 5.2. Cahier des charges des fonctions d'accès Les fonctions tigetflag, tigetnum et tigetstr renvoient la valeur de la capacité correspondant dans terminfo à l'argument capname, qui leur a été transmis. La fonction tigetflag renvoie la valeur -1 si capname n'est pas une capacité de type booléen, ou 0 si cette capacité est, soit effacée, soit absente dans la base de données du terminal. La fonction tigetnum renvoie la valeur -2 si capname n'est pas une capacité de type numérique, ou -1 si cette capacité est, soit effacée, soit absente dans la base de données du terminal. La fonction tigetstr renvoie la valeur (char *)-1 si capname n'est pas une capacité de type chaîne de caractères, ou 0 si cette capacité est, soit effacée, soit absente dans la base de données du terminal. Notez : les quatre phrases ci-dessus ne sont que la traduction du manuel d'aide que vous pouvez consulter en tapant la commande : man curs_terminfo 5.3. Exemple d'emploi de tigetflag Vous pourriez tester le programme suivant... #include #include #include int main(int argc, char * argv[]) { int r,rb; int erreur; if (argc<2){ printf("Entrez les noms d'un terminal et d'une capacité séparés par un espace\n"); exit(1); } else r=setupterm(argv[1], fileno(stdout),&erreur);//Terminal de la ligne de commande if (r==OK){ printf("Traitement correct\n"); rb=tigetflag(argv[2]); switch(rb){ case -1 : printf("%s n'est pas une capacité booléenne\n",argv[2]); break; case 0 : printf("La capacité %s n'est pas supportée par le terminal %s\n",argv[2],argv[1]); break; default : printf("La capacité %s est supportée par le terminal %s\n",argv[2],argv[1]); break; } } else switch(erreur){ case 0 : printf("Le terminal %s est inconnu.\n",argv[1]); break; case -1 : printf("Aucune base de données pour le terminal %s.\n",argv[1]); break; } return 0; } Voici un exemple d'appels et de réponses successifs au programme ci dessus : [pierre@localhost terminfo]$ ./testcapb xterm km Traitement correct La capacité km est supportée par le terminal xterm [pierre@localhost terminfo]$ ./testcapb linux km Traitement correct La capacité km n'est pas supportée par le terminal linux [pierre@localhost terminfo]$ ./testcapb linux bce Traitement correct La capacité bce est supportée par le terminal linux [pierre@localhost terminfo]$ ./testcapb xterm bce Traitement correct La capacité bce n'est pas supportée par le terminal xterm 5.4. Exemple d'emploi de tigetnum Je vous laisse le soin d'imaginer les modifications à apporter au programme précédent pour tester, cette fois, des capacités de type numérique à l'aide de la fonction tigetnum. Je me contenterai de reproduire quelques exemples de tests de ces capacités de type numérique sur mon système.Si nous nous intéressons, par exemple, aux capacités de traitement des couleurs, nous pouvons interroger trois bases de données, d'un terminal, respectivement linux, xterm, nxterm. L'interrogation porte sur le support de la capacité colors. Nous obtenons : [pierre@localhost terminfo]$ ./testcapn linux colors Traitement correct La capacité colors est supportée par le terminal linux Valeur de colors : 8 La capacité colors est gérée par le terminal linux. Huit couleurs sont reconnues. [pierre@localhost terminfo]$ ./testcapn xterm colors Traitement correct La capacité colors n'est pas supportée par le terminal xterm Petite surprise tout de même les couleurs ne semblent pas être reconnues par la base de données du terminal xterm. Vous pouvez le vérifier en faisant afficher cette base de données par l'utilitaire infocmp ; or ce terminal est celui qui est habituellement émulé sous X Window. [pierre@localhost terminfo]$ ./testcapn nxterm colors Traitement correct La capacité colors est supportée par le terminal nxterm Valeur de colors : 8 Autre surprise, le terminal nxterm, cousin issu de germain du terminal xterm, lui supporte la capacité colors. 5.5. Exemple d'emploi de tigetstr Nous pourrons appliquer des modifications semblables aux précédentes à notre programme pour tester, cette fois, la fonction tigetstr.Parmi les capacités de type chaîne de caractères, une d'entre elles nous a semblé importante, celle qui permet de rendre le curseur invisible : civis. Testons donc cette capacité sur nos terminaux habituels : [pierre@localhost terminfo]$ ./testcapc linux civis Traitement correct La capacité civis est supportée par le terminal linux Valeur de civis : \E[?25l\E[?1c [pierre@localhost terminfo]$ ./testcapc xterm civis Traitement correct La capacité civis n'est pas supportée par le terminal xterm Le lecteur voudra bien me pardonner ce fastidieux affichage, il n'est là que pour souligner la diversité du contenu des fichiers terminfo. Ainsi, une fois de plus, et sur une capacité aussi usuelle et utile que l'effacement du curseur, force est de constater que pour un terminal linux, la capacité est supportée et qu'elle ne l'est pas sur un terminal de type xterm.Nous examinerons les conséquences de tout cela dans le chapitre 7 intitulé Pallier les insuffisances d'une base de données. 6. Ecriture des valeurs des capacités dans un terminal Nous venons d'étudier les fonctions qui permettent de lire les informations contenues dans une base de données, mais pour agir sur l'apparence du terminal nous nous doutons bien qu'il ne suffit pas de recevoir des données mais aussi d'en transmettre.En effet, s'il s'agit d'une action simple que doit réaliser le terminal, nous pouvons penser qu'il suffit de lui transmettre un code qui déclenche l'action, si l'action est plus complexe il faudra certainement que nous puissions transmettre, en plus des paramètres. Nous allons étudier les deux cas à travers l'examen détaillé de deux nouvelles capacités, clear qui efface l'écran et replace le curseur en haut et à gauche de l'écran, cup qui place le curseur à un emplacement déterminé de l'écran. Enregistrons tout d'abord les chaînes de caractères associées à ces deux capacités. 6.1. Lecture des chaînes de caractères associées aux capacités clear et cup L'utilisation de la fonction tigetstr avec la capacité clear donne les messages suivants : [pierre@localhost terminfo]$ ./testcapc xterm clear Traitement correct La capacité clear est supportée par le terminal xterm Valeur de clear : \E[H\E[2J [pierre@localhost terminfo]$ ./testcapc linux clear Traitement correct La capacité clear est supportée par le terminal linux Valeur de clear : \E[H\E[J 6.1.1. 6.1.1 Analyse de la valeur affectée à la capacité clear Un petit commentaire s'impose sur la signification de cette chaîne de caractères. J'ai opté pour un affichage semblable à celui que l'on trouve dans les fichiers sources de terminfo et dans l'affichage produit par l'utilitaire infocmp. Le groupe de caractères \E correspond effectivement au démarrage d'une séquence d'échappement.Si vous faites afficher le code ASCII, vous obtiendrez la valeur 27, c'est à dire ESCAPE. Le second caractère '[' a pour code ASCII 91. Ce caractère indique l'attente de codes de commandes de contrôle du terminal (CSI). Le caractère 'H' qui suit est le code de la commande qui déplace le curseur à une position déterminée. Par défaut, c'est à dire en l'absence de paramètres définissant la position en Ligne,Colonne, le curseur est placé dans le coin supérieur gauche de l'écran du terminal (position 0,0).Vous pouvez aisément tester cela en utilisant la fonction printf comme le conseille Cédric dans sa documentation sur ce sujet. Nous comprenons dès lors que la deuxième chaîne \E[ ouvre une seconde séquence et cette fois le code 'J' est le code qui efface l'écran. Vous noterez que le terminal linux et xterm n'ont pas le même attribut : le terminal linux est sans attribut, xterm utilise la paramètre 2. Rien d'étonnant puisque cela revient au même ! En effet selon la norme ECMA-48/ISO dont le gestionnaire de terminaux Linux, implémente un sous-ensemble important, par défaut cette séquence de codes efface tout l'écran, lorsque l'on place 1 comme attribut l'écran est effacé de son origine jusqu'à la position du curseur, lorqu'il vaut 2, tout l'écran est effacé. 6.1.2. 6.1.2 Composition de séquences d'échappement Ainsi, la capacité clear de la base de données terminfo se présente comme une composition de séquence de codes d'échappement : une première qui envoie une commande de positionnement du curseur, \E[H ; une seconde qui efface l'écran, \EJ ou \E2J. Vous pouvez vérifier cela en envoyant séparément chacune des commandes au Terminal à l'aide de la fonction printf, de cette manière : printf("\033[H"); car=getchar(); printf("\033[J"); 6.1.3. 6.1.3 Analyse d'une valeur de capacité plus complexe : cup La lecture de la valeur associée à la capacité cup dans la base de données terminfo des terminaux linux et xterm, nous donne ce résultat : [pierre@localhost terminfo]$ ./testcapc linux cup Traitement correct La capacité cup est supportée par le terminal linux Valeur de cup : \E[%i%p1%d;%p2%dH [pierre@localhost terminfo]$ ./testcapc xterm cup Traitement correct La capacité cup est supportée par le terminal xterm Valeur de cup : \E[%i%p1%d;%p2%dH Cette fois, les bases de données des deux terminaux contiennent la même chaîne de caractères.Si nous isolons le début et la fin de la chaîne de caractères, nous retrouvons bien la séquence \E[...H que nous avions analysé dans le paragraphe précédent et qui correspond à une commande de positionnement du curseur.Entre les deux nous trouvons une succession de caractères séparés par un point-virgule (;), ce point virgule sépare les deux attributs attendus par le terminal, la position dans la ligne, la position en colonne. Voici donc la signification de chacun de ces signes : %i indique que les arguments transmis doivent être incrémentés de 1. Cette incrémentation est rendue nécessaire parce que l'origine de l'écran est 0,0. Mais cerains terminaux ont une origine 1,1. Donc pour les terminaux à origine 0,0, on incrémente de 1 et on applique la même procédure. %p1 et %p2 placent respectivement le premier argument et les second argument sur la pile. %d Affiche le sommet de la pile sous forme décimal. Nous constatons donc que cette chaîne de caractères ne correspond pas à la chaîne de caractères à envoyer au terminal. Puisqu'elle décrit un processus à mettre en oeuvre. Comment allons-nous créer cette chaîne de caractères correspondant à la séquence d'échappement à envoyer au terminal ? 6.2. Chaîne de caractères à envoyer au terminal pour la capacité cup Deux arguments sont attendus pour cette capacité : · le premier repésente la ligne où doit être affichée le curseur, · le deuxième représente la colonne. 6.2.1. 6.2.1 la fonction tparm Le prototype de cette fonction est le suivant : char * tparm(const char * str,...); Cette fonction reçoit comme premier paramètre une chaîne de caractères qui est le résultat de la fonction de lecture d'une capacité tigetstr, les arguments suivants sont de type numérique et doivent être au nom­ bre et dans l'ordre des arguments attendus par la capacité utilisée. 6.2.2. 6.2.2 Exemple d'utilisation de tparm avec la capacité cup Une fois de plus, je me permets de reproduire un programme bien trivial. #include #include #include void affiche(char * m) { int i; for(i=0; i #include #include void affiche(char * m) { int i; for(i=0; i #include #include const char * chEffaceEcran="\033[H\033[J"; const char * chCacheCurseur="\033[?25l\033[?1c"; const char * chMontreCurseur="\033[?25h\033[?0c"; const char * chPlaceCurseur="\033[%i%p1%d;%p2%dH"; const char * chEffaceFinLigne="\033[K"; const char * chEffaceDebLigne="\033[1K"; const char * chCouleurFond="\033[4%p1%dm"; const char * chCouleurForme="\033[3%p1%dm"; const char * chCoulDef="\033[39;49m"; void clrscr(); // fonction qui efface l'écran void clreol(); // fonction qui efface jusqu'à la fin de la ligne void clrbof(); // fonction qui efface du curseur au début de la ligne void gotoxy(unsigned short x, unsigned short y); // Placement curseur en x,y void cursorOff(); // Curseur invisible void cursorOn(); // Curseur normal void colorBkgd(unsigned short c); // Chargement couleur de fond c void colorFrgd(unsigned short c); // Chargement couleur écriture c void defaultColor(); // Rétablissement couleurs par défaut 8.2.2. 8.2.2 Implémentation de quelques fonctions dans le fichier xinuconio.c #include // fonction qui efface l'écran void clrscr() { putp(chEffaceEcran); } // fonction qui efface jusqu'à la fin de la ligne void clreol() { putp(chEffaceFinLigne); } //............etc ......... // Placement curseur en x,y void gotoxy(unsigned short x, unsigned short y) { putp(tparm(chPlaceCurseur,y,x)); } // Chargement couleur de fond c void colorBkgd(unsigned short c) { putp(tparm(chCouleurFond,c)); } // ...... 8.2.3. 8.2.3 Quelques remarques sur les couleurs et les codes Le début d'une séquence d'échappement commence par "\033", c'est à dire le caractère de code ASCII 27 soit 033 en octal qui était affiché \E dans l'affichage conventionnel des fichiers terminfo et dans les fichiers sources. Vous aurez constaté les valeurs des capacités setab et setaf qui permettent de modifier respectivement la couleur de fond et la couleur d'écriture : \E[4%p1%dm et \E[3%p1%dm. Vous remarquerez que la commande commence pour l'un par 4, pour l'autre par 3 ce qui correspond à la différenciation entre couleur fond et forme, ce qui fait que la valeur à transmettre est une valeur numérique comprise entre 0 et le nombre maximum de couleurs supportées par le terminal. Le paramètre à transmettre pour ces deux fonctions est donc un nombre compris entre 0 et max_colors-1(le plus souvent max_colors est égal à 8 ; il est donné par la valeur de la capacité colors). Ainsi si vous appliquez à ces capacités le traitement qui permet de produire la séquence d'échappement à l'aide de la fonction tparm, vous obtiendrez les résultats suivants : [pierre@localhost terminfo]$ ./tesparm Valeur de la capacité : \E[4%p1%dm Chaîne à envoyer : \E[45m au terminal et [pierre@localhost terminfo]$ ./tesparm Valeur de la capacité : \E[3%p1%dm Chaîne à envoyer : \E[36m au terminal pour deux appels avec les valeurs de couleur 5 et 6.Vous reconnaîtrez sans peine les couleurs définies par Cédric dans son document . Donc en utilisant les capacités de terminfo le code c à transmettre aux fonctions que nous avons créées colorBkgd(unsigned short c) est une valeur comprise dans l'intervalle [0,7]. Pour faciliter les choses vous pouvez utiliser les constantes symboliques définies dans ncurses. En voici les valeurs : COLOR_BLACK 0 COLOR_RED 1 COLOR_GREEN 2 COLOR_YELLOW 3 COLOR_BLUE 4 COLOR_MAGENTA 5 COLOR_CYAN 6 COLOR_WHITE 7 9. Conclusion Bien entendu, cette documentation n'a pas la prétention de faire le tour de la question. Elle ne peut en aucun cas remplacer l'aide fournie sur terminfo. J'ai essayé de proposer les outils et les liens qui, je l'espère, permettent de comprendre l'organisation des bases de données terminfo. Je vous laisse le soin de faire le reste du chemin. Les pas supplémentaires pourraient aller dans deux directions : 1. Soit vers un plus bas niveau de programmation pour découvrir la gestion des périphériques d'entrée/sortie, 2. Soit vers le niveau supérieur pour utiliser pleinement les ressources de la bibliothèque ncurses. Je ne peux terminer cette bien ridicule contribution sans affirmer ma gratitude et mon attachement à tous les acteurs des logiciels libres ; mon adhésion à la philosophie mise en oeuvre dans leur pratique, et exprimée dans leurs écrits. Merci à eux et merci à vous qui êtes arrivés jusqu'à ce dernier mot. 10. Copyright Benjamin Drieu me demande de préciser le copyright du présent document. Si je ne l'ai pas fait dans un premier temps c'est que pour moi, il ne peut qu'être libre de tous droits. Mais bien entendu, je laisse alors la porte ouverte à tous les abus signalés sur le site GNU à propos du copyleft. Donc par principe, et non parce que je crois que ce document puisse être d'un intérêt inestimable pour l'humanité, je rappelle : ******************************************************************************* * Copyright (C) janvier 2001 Documentation sur la gestion des terminaux sous * * les systèmes Unix * * * * Auteur : Alain Riffart, ariffart@club-internet.fr * * * * Cette documentation est libre, vous pouvez la redistribuer et/ou la modifier* * selon les termes de la Licence Publique Générale GNU publiée par la Free * * Software Foundation (version 2 ou bien toute autre version ultérieure * * choisie par vous). * * * * * * Cette documentation est distribuée car potentiellement utile, mais SANS * * AUCUNE GARANTIE, ni explicite ni implicite, y compris les garanties de * * commercialisation ou d'adaptation dans un but spécifique. Reportez-vous à * * la Licence Publique Générale GNU pour plus de détails. * * * * Vous pouvez consulter la Licence Publique Générale GNU sur le site d'APRIL * * aux adresses reproduites ci-dessous. Si vous désirez obtenir une copie de * * cette licence écrivez à la Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, États-Unis. * ******************************************************************************* Consultez la documentation sur la philosophie GNU sur le site d'APRIL. Ou prenez connaissance de la GPL sur le site d'APRIL.