Gestion des terminaux sous <tt>UNIX</tt> 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. Introduction 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.

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 :

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,

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 : Les premiers sont immédiatement suivis d'une virgule et sont de type booléen. Les deuxièmes sont suivis d'un dièse, lui-même suivi d'une valeur numérique. Ils sont de type numérique. 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.

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.

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);

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.

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 .

Mise en oeuvre de la fonction setupterm

Vous pourriez tester et mettre en oeuvre un programme qui ressemblerait à celui-ci : #include <stdio.h> #include <term.h> #include <ncurses.h> 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 : stdio.h pour les fonctions d'entrée-sortie, dont la fonction fileno, term.h pour les fonctions de gestion de terminfo, 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.

Identifier et comprendre les principaux noms de capacités de terminfo

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 : Le curseur, visibilité, emplacement, les couleurs, l'effacement de l'écran, 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

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 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 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

Gestion des couleurs 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 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 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

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

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

Accès aux valeurs définies par les capacités d'une base de données terminfo 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.

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

Exemple d'emploi de tigetflag

Vous pourriez tester le programme suivant... /#include <stdio.h> #include <term.h> #include <ncurses.h> 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

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.

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.

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.

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 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 '&lsqb' 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

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 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 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 ?

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 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 nombre et dans l'ordre des arguments attendus par la capacité utilisée.

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 <stdio.h> #include <term.h> #include <ncurses.h> void affiche(char * m) { int i; for(i=0; i<strlen(m);i++) if (m[i]==27) printf("\\E"); else printf("%c",m[i]); } int main(int argc, char * argv[]) { int r,rb; char * resu; char * esc_seq; int erreur; char capa[]="cup"; if (argc<2){ printf("Entrez le nom d'un terminal\n"); exit(1); } else r=setupterm(argv[1], fileno(stdout),&erreur);//Terminal de la ligne de commande if (r==OK){ printf("Traitement correct\n"); resu=tigetstr(capa); rb=(int)resu; switch(rb){ case -1 : printf("%s n'est pas une capacité de type chaîne\n",capa); break; case 0 : printf("La capacité %s n'est pas supportée par le terminal %s\n",capa,argv[1]); break; /*Si tout s'est bien passé on applique le traitement par défaut suivant. La chaîne resu contient alors la valeur dans terminfo de la capacité cup pour le terminal transmis par la ligne de commande. On fait afficher cette chaîne et on construit à l'aide de la fonction tparm la séquence d'échappement à envoyer au terminal enregistrée dans esc_seq*/ default : printf("Valeur de la capacité cup lue par tigetstr : "); affiche(resu); printf("\n"); esc_seq=tparm(resu,15,30); printf("Chaîne à envoyer : "); affiche(esc_seq); printf(" au terminal %s\n",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 le résultat affiché par ce programme : [pierre@localhost terminfo]$ ./testcha linux Traitement correct Valeur de la capacité cup lue par tigetstr : \E[%i%p1%d;%p2%dH Chaîne à envoyer : \E[16;31H au terminal linux Trois constatations s'imposent à la lecture de cet affichage : La fonction tigetstr a bien ramené la chaîne de caractères contenue dans la base de données terminfo pour le terminal linux : \E[%i%p1%d;%p2%dH La fonction tparm a bien construit une séquence de codes d'échappement : \E[16;31H La fonction tparm est un interpréteur des capacités définies dansterminfo. On remarque en particulier que la fonction tparm a été invoquée avec comme arguments 15,30. Comme prévu par la valeur %i de la capacité cup ces deux nombres ont bien été incrémentés de 1.

Fonction d'affichage printf ou putp ?

Une fois obtenue la séquence de codes d'échappement à l'aide de la fonction tparm, reste à expédier le tout au terminal. Vous pouvez bien entendu envoyer la séquence de codes à l'aide de la fonction printf. Nous pouvons cependant préférer l'utilisation de la fonction putp qui appartient à la bibliothèque que nous avons utilisée. Le prototype de cette fonction est le suivant : int putp(const char * str); De sorte que dans l'exemple précédent nous aurions pu invoquer directement la fonction putp ainsi : putp(tparm(resu,15,30)); pour obtenir le déplacement du curseur en ligne 15, colonne 30.

Pallier les insuffisances d'une base de données

Mais, les choses ne sont pas aussi simples lorsque l'on veut mettre en oeuvre les bases données existant. Il n'est pas rare que certaines capacités ne soient pas définies dans la base de données du terminal que vous utilisez ou que votre système émule.

Réécrire un fichier source

La première solution qui vient à l'esprit est tout simplement de réécrire un fichier source et de le faire compiler par le programme tic. Pour cela, partez d'un fichier existant pour la famille du terminal que vous utilisez et ajoutez-y les capacités manquantes qui vous semblent importantes, en les retrouvant dans d'autres bases de données dans lesquelles elles sont présentes.

Ainsi vous avez pu constater, dans les chapitres précédents que les capacités de gestion de la visibilté du curseur, étaient absentes de la bases de données du terminal xterm. Reprenez le fichier source terminfo, recopiez la base de données de xterm, renommez la monxterm, ajoutez les capacités civis,cnorm,cvvis, telles que vous pouvez les trouver dans la base de données du terminal linux. Recompilez le fichier source à l'aide du programme tic.

Vous trouverez dans le manuel d'aide de votre distribution tous les renseignements nécessaires pour mettre en oeuvre cette compilation à l'aide du programme tic. Entrez la commande : man tic

Pourquoi ne pas utiliser les fonctions de la bibliothèque ncurses ?

Certains penseront qu'il serait plus simple d'utiliser les fonctions disponibles dans la bibliothèque ncurses. Il existe certes une fonction de la bibliothèque ncurses. Il existe bien entendu une fonction, curs_set qui gère la visibilité du curseur. Mais cette fonction teste les capacités du terminal utilisé et ne fonctionne pas si la capacité civis n'est pas définies pour le terminal utilisé. Donc,... retour à la case départ.

Se créer sa propre bibliothèque de fonctions

Encore un qui réinvente la roue ! penseront certains. Peut-être. Mais c'est une question qui revient souvent chez certains jeunes programmeurs qui ont commencé avec des outils sous DOS, dont le portage des outils GNU. On y trouve des fonctions rassemblées dans le fichier d'en-tête conio.h qui efface l'écran ou rendent le curseur invisible etc...

On pourra ainsi, sans problème récupérer un ensemble de valeurs de capacités à l'aide des fonctions de lecture que nous avons vues précédemment. L'on pourra alors définir une liste de valeurs de capacités. Nous pourrons créer,par exemple les constantes suivantes : const char * sansCurseur = "\033[?25l\E[?1c"; const char * deplace Curseur = "\033[%i%p1%d;%p2%dH"; const char * effaceEcran="\033[H\E[2J"; Il suffit ensuite de faire produire la séquence de code d'échappement par la fonction tparm

Réalisation d'une bibliothèque de fonctions gérant les capacités d'un terminal

Tout est prêt pour que vous réalisiez une bibliothèque des fonctions les plus souvent utilisées pour la gestion d'un écran. Bien entendu, vous savez qu'un terminal est un ensemble entrée/sortie et vous trouverez dans les bases de données terminfo des capacités de gestion du clavier. Ceci vous permettra d'étendre les possibilités de cette bibliothèque que l'on pourrait appeler xinuconio

La fonction tparm est bien un interpréteur

Il me paraît utile de lever une ambigüité qui pourrait subsister dans l'esprit de certains lecteurs.Les exemples d'utilisation que nous avons proposés jusqu'à présent pourraient laisser penser que la fonction tparm est associée à l'activation d'un terminal qui contiendrait dans sa base de données terminfo, la valeur de capacité traitée par tparm.

Il n'en est rien la fonction tparm interprète bien la chaîne de caractères transmise en argument suivant la grammaire, la syntaxe, le lexique des chaînes de caractères définies pour les fichiers terminfo.

Ainsi, si vous mettez en oeuvre le petit programme suivant quel que soit le terminal dans lequel vous exécuterez ce programme, vous obtiendrez le même résultat, semblable à celui que nous avions obtenu précédemment, alors que nous avions utilisé la fonction setupterm. #include <stdio.h> #include <term.h> #include <ncurses.h> void affiche(char * m) { int i; for(i=0; i<strlen(m);i++) if (m[i]==27) printf("\\E"); else printf("%c",m[i]); } int main(int argc, char * argv[]) { char * deplaceCurseur = "\033[%i%p1%d;%p2%dH"; char * esc_seq; printf("Valeur de la capacité : "); affiche(deplaceCurseur); printf("\n"); esc_seq=tparm(deplaceCurseur,0,0); printf("Chaîne à envoyer : "); affiche(esc_seq); printf(" au terminal\n"); return 0; } L'exécution de ce programme donnera le résultat suivant : [pierre@localhost terminfo]$ ./tesparm Valeur de la capacité : \E[%i%p1%d;%p2%dH Chaîne à envoyer : \E[1;1H au terminal conforme à ce que nous attendions.

Ebauche d'une ébauche de bibliothèque de fonctions

Une structure minimale définirait dans le fichier d'en tête les valeurs des capacités J'en donne neuf exemples et ceux qui sont intéressés par la question pourront en rechercher et imaginer d'autres.

8.2.1 Le fichier d'en tête xinuconio.h

#include <stdio.h> #include <term.h> #include <ncurses.h> 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 Implémentation de quelques fonctions dans le fichier xinuconio.c

#include <xinuconio.h> // 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 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 .

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

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 : Soit vers un plus bas niveau de programmation pour découvrir la gestion des périphériques d'entrée/sortie, 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.

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

Ou prenez connaissance