Tutoriel ncurses

buchs

Vous désirez utiliser les librairies Ncurses mais ne savez pas comment les utiliser? Ce tutoriel va vous permettre de faire vos premiers pas et vous montrer la multitude de choses que l'on peut réaliser et ce, pour le plus grand confort de l'utilisateur.

- Titre : Tutoriel ncurses

- Auteur : buchs

- Parution : 12 octobre 2007

- Licence : Copyright ® 2007 buchs. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- I-A. Ncurses c'est quoi?

- I-B. Pré-requis

- I-C. Remerciements

- II. Premier pas

- II-A. Ce qu'il faut savoir

- II-B. Première fenêtre : "Hello world"

- II-C. Se positionner dans la fenêtre

- II-D. Écrire en utilisant les mises en forme

- II-E. Lire une chaîne de caractères à partir du clavier

- II-F. Créer plusieurs fenêtres

- II-G. Créer une bordure autour de la fenêtre

- II-H. Centrer le texte sur le terminal

- III. Ajout de couleurs

- III-A. Ce qu'il faut savoir

- III-B. Créer des couleurs personnalisées

- III-C. Écrire en couleur

- III-D. Mettre une couleur en fond

- III-E. Récupérer la couleur d'une fenêtre

- IV. Aller plus loin

- IV-A. Créer des fenêtres imbriquées

- IV-B. Faire une barre de progression

- IV-C. Créer un menu

- IV-D. Interception des signaux

- V. Liens

I - Introduction

I-A - Ncurses c'est quoi?

Ncurses est une bibliothèque qui vous permet de disposer d'une interface couleur sur un terminal texte. Ncurses permet d'utiliser la totalité de l'écran, de créer et gérer des fenêtres, d'utiliser nativement 8 couleurs différentes. Mais aussi d'utiliser la souris ainsi que les touches de fonctions du clavier.

N'importe quel système conforme à la norme ANSI/POSIX supporte Ncurses. Ainsi, elle est capable de détecter toutes les propriétés du terminal utilisé.

I-B - Pré-requis

Pour pouvoir utiliser les librairies Ncurses il faut tout d'abord que ces dernières soient installer sur votre Linux, pour cela vous pouvez, soit passer par votre gestionnaire de paquets habituels (si vous savez quelles librairies installer), soit vous rendre sur le FTP de ncurses et télécharger et installer manuellement la dernière version disponible.

I-C - Remerciements

Je tiens tout particulièrement à remercier X et Y pour leurs conseils avisés ainsi que Z pour la relecture orthographique.

II - Premier pas

II-A - Ce qu'il faut savoir

Lors de la compilation avec gcc, il faut spécifier que vous utilisez la bibliothèque Ncurses et pour cela il vous faut rajouter l'option lncurses.

Exemple de compilation

gcc -lncurses -o HelloWorld HelloWorld.c

Il se peut que dans certains cas (et surtout au début) que vous ne quittiez pas proprement votre interface Ncurses et lors de votre retour en mode console vous ne verrez plus ce que vous tapez. De même lorsque vous essayerez de retourner à la ligne, cela décalera les lignes et ce ne sera plus du tout gérable. Pour cela je vous propose une petite astuce. Créer un fichier nommé sos, lequel vous restaurera les paramètres d'origine de la console. Il suffit d'y mettre :

sos

#!/bin/sh

stty -raw # Réutilise le mode RAW pour l'interception des signaux et autres
stty echo # Remise en place de l'echo
stty onlcr # Remise en place du retour chariot

Suffit de rendre ce script exécutable via :

chmod +x sos

Ainsi vous n'aurez plus qu'a taper sos dans votre console pour effacer les effets dûs à de mauvaises manipulations. Il est nettement plus simple de taper sos à l'aveuglette que toute la commande en une seule fois. Cela évite aussi d'avoir à ouvrir une nouvelle console pour restaurer les paramètres par défaut.

II-B - Première fenêtre : "Hello world"

HelloWorld.c

#include <ncurses.h>

int main(void)
{
initscr();              // Initialise la structure WINDOW et autres paramètres
printw("Hello World");  // Écrit Hello World à l'endroit où le curseur logique est positionné
refresh();              // Rafraîchit la fenêtre courante afin de voir le message apparaître
getch();                // On attend que l'utilisateur appui sur une touche pour quitter
endwin();               // Restaure les paramètres par défaut du terminal

return 0;
}

En incluant la bibliothèque ncurses.h, nous n'avons plus besoin d'inclure la bibliothèque stdio.h car celle ci est inclue implicitement dans ncurses.h

II-C - Se positionner dans la fenêtre

Pour se positionner dans la fenêtre courante, rien de plus simple, il suffit d'utiliser la fonction move pour déplacer le curseur à l'endroit voulu et ainsi lire un texte saisi au clavier ou bien écrire tout simplement. Ainsi pour déplacer le curseur au centre de la fenêtre, on aura un code du style :

#include <ncurses.h>

int main(void) {
initscr();

move(LINES - 1, COLS - 1);  // Déplace le curseur tout en bas à droite de l'écran
addch('.');                 // Écrit le caractère . au nouvel emplacement

/** est équivalent à : **/
//mvaddch(LINES - 1, COLS - 1, '.');

/** ou encore à : **/
//mvprintw(LINES - 1, COLS - 1, ".");

getch();
endwin();

return 0;
}

Les variables LINES et COLS sont des variables représentant le nombre de lignes et colonnes du terminal actuel. Après redimensionnement du terminal, ces deux variables sont mises à jour. Faites en donc bon usage.

Il existe la fonction mvFonction pour plusieurs fonctions, comme par exemple, mvaddstr, etc. Reportez vous aux pages man correspondantes.

II-D - Écrire en utilisant les mises en forme

Un des avantages de Ncurses et que vous pouvez utiliser les mises en formes pour mettre en valeur certaines parties de votre code. Et il est très simple de mettre tout cela en oeuvre.

Voici donc la liste des mises en forme utilisable :

- Normal (A_NORMAL)

- Meilleur surlignement (A_STANDOUT)

- Inversion vidéo (A_REVERSE)

- À moitié brillant (A_DIM)

- Très brillant ou en gras (A_BOLD)

- Souligné (A_UNDERLINE)

- Mode invisible ou blanc (A_INVIS)

Et d'autres encore dont je ne parlerais pas ici. La fonction attron permet d'appliquer les mises en forme passées en paramètre et attroff permet d'annuler la mise en forme passée en paramètre. Le texte que l'on veut mettre en forme doit donc se situer après la balise attron. Petite précision, l'attribut A_NORMAL permet d'annuler toutes les mises en forme appliquées. Voici un bout de code suffisamment explicite pour vous montrer le résultat des différentes mises en forme possible (liste non exhaustive bien sûr) :

#include <ncurses.h>

int main(void)
{
initscr();

attron(A_NORMAL);
printw("Texte sans mise en forme\n");

attron(A_STANDOUT);
printw("Texte en meilleur surlignement\n");
attroff(A_STANDOUT);

attron(A_REVERSE);
printw("Texte en inversion video\n");
attroff(A_REVERSE);

attron(A_DIM);
printw("Texte a moitie brillant\n");
attroff(A_DIM);

attron(A_BOLD);
printw("Texte en gras\n");
attroff(A_BOLD);

attron(A_UNDERLINE);
printw("Texte en souligne\n");
attroff(A_UNDERLINE);

attron(A_INVIS);
printw("Texte invisible\n");
attroff(A_INVIS);

attron(A_UNDERLINE | A_BOLD); // Pour appliquer plusieurs type de mises en forme, on utilise l'opérateur unaire |
printw("Texte en gras souligne\n");

attron(A_NORMAL);
refresh();
getch();
endwin();

return 0;
}

Si vous désirez utiliser une mise en forme commune pour chaque fenêtre et sous-fenêtre que vous créer (par exemple écrire en gras), plutôt que d'utiliser attron à chaque fois que vous voulez écrire, vous pouvez utiliser la fonction attrset avec votre liste d'attribut. Ainsi, n'importe qu'elle fenêtre que vous créerez héritera de cet attribut et donc chaque fois que vous écrirez dedans, ça sera automatiquement mis en gras.

II-E - Lire une chaîne de caractères à partir du clavier

Pour rendre vos applications plus interactives et donc non statique, il peut être intéressant de demander à l'utilisateur d'entrer une chaîne de caractères (lui demander son âge ou que sais-je encore). Bien évidemment, il existe des fonctions vous permettant de lire des chaînes de caractères d'une seule traite sans avoir à passer par la méthode getch utilisée jusqu'à présent.

Cette méthode se nomme getstr et permet donc de lire une chaîne entière de caractères. Voyons maintenant comment s'en servir.

II-F - Créer plusieurs fenêtres

Dans les programmes un peu plus évolués, il sera sans doute utile voir indispensable de diviser la fenêtre courante en plusieurs sous fenêtre afin de mieux pouvoir les gérer et mieux organiser son prgramme. Cela allègera votre code et le rendra plus lisible, donc plus maintenable.
Vous pouvez faire une sous-fenêtre pour un menu par exemple, une autre que vous utiliserez pour affichez du texte et une dernière que vous utiliserez pour permettre à l'utilisateur de saisir une chaîne au clavier. Bien sûr vous pourrez rediviser ces dernières en autant de sous-fenêtres que vous voudrez.

La fonction subwin vous permet de faire cela de manière aisée. En effet, il suffit juste de dire quelle est la fenêtre à divisée ainsi que les dimensions voulues. Une fois fait, une nouvelle sous-fenêtre vous sera donc retournée (de type WINDOW *).

Un petit exemple simpliste divisant la fenêtre de base en deux sous-fenêtres :

#include <ncurses.h>

int main(void) {
WINDOW *haut, *bas;

initscr();
haut= subwin(stdscr, LINES / 2, COLS, 0, 0);        // Créé une fenêtre de 'LINES / 2' lignes et de COLS colonnes en 0, 0
bas= subwin(stdscr, LINES / 2, COLS, LINES / 2, 0); // Créé la même fenêtre que ci-dessus sauf que les coordonnées changent

box(haut, ACS_VLINE, ACS_HLINE);
box(bas, ACS_VLINE, ACS_HLINE);

mvwprintw(haut, 1, 1, "Ceci est la fenetre du haut");
mvwprintw(bas, 1, 1, "Ceci est la fenetre du bas");

wrefresh(haut);
wrefresh(bas);

getch();
endwin();

free(haut);
free(bas);

return 0;
}

Ne vous attardez pas sur la fonction box, elle sera détaillée juste après. Je l'ai juste utilisée pour vous montrer la taille des fenêtres que j'ai créées.

II-G - Créer une bordure autour de la fenêtre

Pour créer une jolie bordure autour d'une zone définie, vous pouvez utiliser la fonction box sur une fenêtre (de type WINDOW *), afin d'encadrer et embellir votre interface. Pour cela deux méthodes sont possibles :

La première consiste à créer une "boîte" avec les bordures par défaut (que l'on peut choisir parmi plusieurs types de bordures prédéfinies).

La seconde consiste à définir soit même quel caractère sera sur la ligne du haut, du bas, dans les coins, etc.

Un exemple de code pour les deux méthodes :

Méthode box

#include <ncurses.h>

int main(void) {
WINDOW *boite;

initscr();
boite= subwin(stdscr, 10, 10, LINES / 2, COLS /2);

box(boite, ACS_VLINE, ACS_HLINE); // ACS_VLINE et ACS_HLINE sont des constantes qui génèrent des bordures par défaut
refresh();

getch();
endwin();

free(boite);

return 0;
}

Reportez vous aux pages man pour voir les différentes bordures disponibles (comme ACS_VLINE et ACS_HLINE par exemple).

Méthode wborder

#include <ncurses.h>

int main(void) {
WINDOW *boite;

initscr();
boite= subwin(stdscr, 10, 10, LINES / 2, COLS /2);
wborder(boite, '|', '|', '-', '-', '+', '+', '+', '+');
wrefresh(boite);

getch();
endwin();

free(boite);

return 0;
}

Dans la méthode wborder, les deux premiers arguments après le pointeur de WINDOW que l'on passe en paramètre représentent respectivement les côtés gauche et droite, les deux suivants les côtés haut et bas et enfin les quatre derniers représentent les quatre coins de la bordure (haut gauche, haut droit, bas gauche, bas droit).

II-H - Centrer le texte sur le terminal

Pour centrer un texte sur le terminal ou une fenêtre donnée, nous allons nous servir des variables LINES et COLS générées par Ncurses. Ainsi, même si nous redimensionnons une fenêtre, le texte sera toujours centré. Attention cependant, lorsque vous redimensionner le terminal, c'est comme si une touche du clavier était appuyé.
Exemple :

#include <ncurses.h>

int main(void) {
WINDOW *boite;

initscr();
printw("Le terminal actuel comporte %d lignes et %d colonnes\n", LINES, COLS);
refresh();
getch();
endwin();

free(boite);

return 0;
}

Lorsque vous exécutez ce code et que vous tentez de redimensionner votre terminal, l'application s'arrête instantanément. Pour pallier à ce problème, nous utiliserons une boucle. Le code devient donc :

#include <ncurses.h>

int main(void) {
WINDOW *boite;

initscr();
while(1) {
printw("Le terminal actuel comporte %d lignes et %d colonnes\n", LINES, COLS);
refresh();  // Rafraîchit la fenêtre par défaut (stdscr) afin d'afficher le message
if(getch() != 410)  // 410 est le code de la touche générée lorsqu'on redimensionne le terminal
break;
}

endwin();

free(boite);

return 0;
}

Vous pouvez donc constater que lors du redimensionnement de la fenêtre, les variables LINES et COLS sont bien mises à jour lors de chaque évènement.

Néanmoins, je vous vois déjà vous demander pourquoi je vous dis ça. En réalité, selon l'utilisateur, la taille de la console ne sera pas là même, donc plutôt que de donner des nombres fixes pour le positionnement, vous pouvez donner des positions relatives à vos composants par rapport au terminal et donc, quelque soit la taille du terminal, le rendu final sera le même.

Maintenant venons au fait, pour centrer un texte, il suffit de partir du centre de la fenêtre, auquel on enlève la moitié de la longueur de notre texte.
Ce qui donne :

#include <ncurses.h>
#include <string.h>

int main(void) {
WINDOW *boite;
char *msg= "Texte au centre";
int taille= strlen(msg);

initscr();
while(1) {
clear();    // Efface la fenêtre (donc l'ancien message)
mvprintw(LINES/2, (COLS / 2) - (taille / 2), msg);
refresh();
if(getch() != 410)
break;
}

endwin();

free(boite);

return 0;
}

Voici donc comment centrer un texte et qui le restera toujours même après un redimensionnement du terminal. Bien évidemment, il en va de même pour centrer une fenêtre ou tout autre composant.

III - Ajout de couleurs

III-A - Ce qu'il faut savoir

III-B - Créer des couleurs personnalisées

III-C - Écrire en couleur

III-D - Mettre une couleur en fond

III-E - Récupérer la couleur d'une fenêtre

IV - Aller plus loin

IV-A - Créer des fenêtres imbriquées

IV-B - Faire une barre de progression

IV-C - Créer un menu

IV-D - Interception des signaux

V - Liens

Site officiel de ncurses