UTILISATION DE LA LIBRAIRIE SVGALIB en C

Introduction :

Comment faire du Graphique simplement, sous Linux ? C'est une question que je me suis souvent posée, et qu'on me pose souvent. Il y a de nombreuses solutions (dont certaines décrites dans mon site), celle-ci se distingue par sa simplicité, et le fait qu'elle soit accessible sur tout PC avec une carte graphique VGA, même si X Window n'y est pas installé.

Ce document résulte du projet réalisé début 2002 par Emmanuel DUCHAINE et Jorn VAN DER STAP, étudiants en DESS TSI-MA à l'IPST - ULP. Vous pouvez le consulter ici, mais il y a quelques erreurs, et il est peut-être un peu "fouillis".

J'ai quant à moi essayé de créer un document plus simple, présentant progressivement les fonctions de base de la librairie SVGALIB. Il est décomposé en 4 phases :

Je n'y montre que lFFe minimum, vous trouverez toutes les options dans le man, en particulier man svgalib. Mais pour les informations de première main, dirigez-vous vers www.svgalib.org

SVGALIB est une bibliothèque très simple, qui accède à votre carte graphique de manière directe, en utilisant l'intégralité de l'écran. Il est donc impossible de l'utiliser dans une fenêtre d'un environnement X, ou sur un écran distant (par telnet). Vous pouvez néanmoins conserver d'autres écrans en mémoire, sur un environnement Linux classique les 6 consoles texte (CTRL ALT F1 à F6), X (CTRL ALT F7), la fenêtre SVGALIB étant affiché par CTRL ALT F8.

Parlons tout de suite de ses inconvénients. C'est vraiment une bibliothèque "brute de fonderie" (pour beaucoup c'est aussi un avantage), ne permettant que des fonctionnalités de base. Tout le reste est à écrire. Déjà, les tracés se font en "coordonnées écran", c'est à dire que si vous voulez pouvoir utiliser des écrans de différentes résolutions c'est à vous de programmer les mise à l'échelle. Second inconvénient majeur : pour accéder aussi brutalement à la carte graphique du PC, il faut des accès root. Aïe ! Tout le monde sait bien que quand on développe sous root, on a toutes les chances de faire des bêtises catastrophiques. Je vous propose la méthode que j'utilise :

Ah, j'ai oublié de précisé une condition nécessaire : il faut installer SVGALIB, sur mes distributions même anciennes je l'ai toujours trouvé facilement. Vous pouvez peaufiner sa configuration dans /etc/vga/libvga.config mais moi j'ai tout laissé tel quel.

les tracés de base

Pour voir le petit programme exemple dans une seconde fenêtre, cliquez ici. Pour télécharger le source, cliquez là (avec le bouton droit ?).

Un programme utilisant SVGALIB doit avant tout inclure <vga.h>. Il faut initialiser le graphique avant tout tracé (mais souvent après avoir posé les questions, car printf ne fonctionne plus en mode graphique). On réalise cela par vga_init();, puis en appelant vga_setmode(m); avec m désignant le mode graphique désiré (et compatible avec votre carte). m est un entier, dans l'exemple que j'ai écrit la fonction choix_mode en propose les plus courants en interactif. Mais vous pouvez aussi utiliser pour m les constantes prédéfinies telles que G640x480x16 (la liste complète est évidement visible dans vga.h, souvent sous /usr/include).
Le must : exportez SVGALIB_DEFAULT_MODE=votremode (pour que ce soit le même pour tout le monde, le mettre dans /etc/profile.local), vga_getdefaultmode() vous rendra cette valeur. C'est bien si vous travaillez sur deux ordinateurs différents, à la maison et au boulot.

Pour allumer un point utilisez vga_drawpixel(x,y), pour un segment vga_drawline(x0 ,y0 ,x1 ,y1 ). Les coordonnées sont des entiers, (0,0) correspond au coin supérieur gauche de l'écran, les X augmentent vers la droite, les Y vers le bas (vga_getxdim() et vga_getydim() donnent le nombre de pixels de l'écran, à vous d'empêcher tout dépassement, si vous dépassez à droite de l'écran vous y rentrez par la gauche). Le dernier vga_setcolor(c) définit la couleur des tracés suivants, avec c entier entre 0 (noir) et inférieur à vga_getcolors(), vga_white() donnant le blanc.

Pour les couleurs VGA standard, regardez l'exemple.

Pour attendre l'appui d'une touche au clavier : vga_getch() qui retourne le code ASCII de la touche. Quant à lui, vga_getkey() n'attend pas l'appui (retourne 0 si rien d'appuyé).

Pour retourner au mode texte, il ne faut pas oublier d'appeler vga_setmode(TEXT);

lecture de la souris

Pour voir le petit programme exemple dans une seconde fenêtre, cliquez ici. Pour télécharger le source, cliquez là (avec le bouton droit ?).

Il faut inclure <vgamouse.h> à votre programme. Après avoir initialisé le mode graphique VGA, il faut initialiser la souris, par ces 5 instructions :

On peut ensuite lire l'état de la souris. Pour cela, il faut appeler mouse_update(); assez souvent, en tous cas peu avant l'appel des fonctions ci-après. Il met à jour les différentes variables, et retourne immédiatement même sans aucun mouvement de la souris. mouse_getbutton() rend le numéro de bouton appuyé (0 pas de bouton, 4 gauche, 1 droite, le bouton du milieu devrait valoir 2. Les appuis simultanés en donnent la somme). mouse_getx() et mouse_gety() rendent la position de la souris.

Dans mon exemple, je trace tout le temps sous le déplacement de la souris, en changeant la couleur suivant les boutons appuyés. Mais si l'on choisissait de ne rien tracer quand on n'appuie aucun bouton, on serait bien embêté car ne disposant pas de curseur.

gestion d'un curseur de souris

Pour voir le petit programme exemple dans une seconde fenêtre, cliquez ici. Pour télécharger le source, cliquez là (avec le bouton droit ?).

Rien n'est prévu dans SVGALIB pour gérer le curseur de la souris (ni même dans vgagl). A vous de le gérer. Comme c'est mon jour de bonté, je vous en propose une version simple (mais efficace).

Ici j'affiche le curseur de la souris (une petite croix). Ce n'a pas été pas facile : à chaque fois qu'on dessine le curseur (parce que la souris a bougé, en général), il faut sauver le fond avant de dessiner. Puis quand on veut l'effacer, il faut en fait redessiner ce qu'on avait sauvegardé. Evidement, entre les deux le fond (c.a.d le dessin, sauf le curseur) ne doit pas avoir été modifié sinon on redessine un vieux fond non modifié.

J'ai donc créé deux fonctions : cachecurs() et montrecurs(). Avant tout dessin on doit cacher le curseur, on dessine, puis on remontre le curseur (ça marche même si la souris a bougé pendant le dessin). A tout moment (ou du moins pendant les phases où l'on attend un mouvement de souris), il faut appeler montrecurs() (je l'ai fait vérifier si la souris a bougé, et dans ce cas il cache l'ancien puis montre le nouveau).

La position du curseur et le dessin qui était en dessous sont sauvés dans des variables globales statiques. J'ai choisi d'économiser la mémoire et surtout le temps de traitement en ne testant que les points sous le curseur, qui lui-même n'est qu'une petite croix. Pour un curseur plus complexe, il vaudrait mieux sauver une zone rectangulaire, d'autant qu'habituellement l'accès à une zone est bien plus rapide que la somme des accès individuels aux pixels. Dans le projet des étudiants de DESS vous trouverez un exemple de curseur plus complexe.

écrire du texte

Pour voir le petit programme exemple dans une seconde fenêtre, cliquez ici. Pour télécharger le source, cliquez là (avec le bouton droit ?).

Après inclusion de <vglgl.h> (en plus de <vga.h>), on dispose d'un ensemble de fonctions de tracé un peu plus évoluées (cercles, rectangles pleins, triangles pleins avec couleurs constantes ou dégradées...). Nous n'allons ici utiliser vgagl que pour écrire du texte dans le graphique. Le reste sera toujours uniquement en svgalib simple.

Il faut initialiser la bibliothèque par l'appel de gl_setcontextvga(m); m étant obligatoirement le même mode que celui donné à vga_setmode(m); (ce dernier ayant été appelé auparavant).

Puis il faut initialiser la police que nous allons utiliser. Ceci consiste à charger en mémoire 256 matrices de pixels. Je n'ai d'ailleurs pas fait ainsi. Par défaut, la police gl_font8x8 est incluse dans vgagl. Chaque caractère fait 8x8 pixels. On initialise la police par les trois instructions suivantes :
   gl_setfont(8, 8, gl_font8x8); //largeur, hauteur, pointeur sur la police (c'est la seule en standard)
   gl_setwritemode(FONT_COMPRESSED); //la police gl_font8x8 est comprimée (on ne l'a pas "expandée")
   gl_setfontcolors(0, vga_white()); //couleur du fond, couleur du texte
J'ai l'impression que la police gl_font8x8 ne fonctionne qu'en 256 couleurs (au moins), mais je me demande bien pourquoi.

Pour écrire, gl_write(x,y,texte); qui ne fonctionne que pour un texte (mais on peut utiliser sprintf avant), ou gl_printf(x,y,format,liste d'arguments); qui fonctionne comme printf mais d'après la doc seulement pour des bibliothèques ELF.

Pour compiler, il faut lier vgagl avant vga : gcc src.c -O3 -o exec -lvgagl -l vga (et n'oubliez pas de lancer sous root ou chmod s+u).


Patrick TRAU, ipst-ulp, Strasbourg Mai 2002