Programmer en C sous Linux
Cet article n'est pas un cours sur le langage C mais récapitule l'ensemble des paquets à installer sous Linux Ubuntu pour bénéficier d'un environnement de développement performant en langage C, avec compilateur en ligne de commande, accès aux bibliothèques et aux services du système, ainsi que l'utilisation de certaines API particulières (Gtk+, Gnome, OpenGL, etc.).
L'objectif de cette page est d'apporter des solutions et des explications aux différents problèmes rencontrés lors de la compilation d'un code source en C sous Linux. Si vous rencontrez un problème de compilation pas encore mentioné sur cette page vous pouvez m'écrire à l'adresse jc.michel@gecif.net.
Sommaire de cette page |
Editons un nouveau fichier texte dans Gedit nommé toto.c :
gedit toto.c
Voici le code source en C de ce premier programme qui se contente d'afficher "Bonjour" sur la sortie standard :
#include <stdio.h>
int main()
{
printf("Bonjour\n");
}
Compilons le programme source toto.c en utilisant le copilateur gcc en ligne de commande :
gcc toto.c
Remarques :
Exécutons le programme a.out :
./a.out
Voyons comment utiliser les API GDK, GTK+, Gnome et OpenGL. Chacune des ces API est composée d'un ensemble de fichiers de librairies dynamiques (fichiers .so disponibles dans le répertoire /urs/lib) et d'un ensemble de fichiers d'en-tête (fichiers .h disponibles dans le répertoire /usr/include).
Le programme pkg-config exécutable en ligne de commande sous Linux permet de connaître la configuration de chacune des librairies installées dans le système (version, chemin d'accès au fichiers .so et aux fichier .h, etc.). La commande pkg-config --list-all donne l'ensemble des librairies pouvant être utiliser dans un programme.
|
||||
Procédure à suivre pour utiliser une API particulière :
gcc toto.c $(pkg-config --cflags --libs nom_librairie)
La documentation des différentes librairies est installable grâce aux paquets .deb xxxxxxx-doc et est disponible dans les répertoires /usr/share et /usr/share/doc.
Le tableau suivant indique le nom du paquet .DEB à installer dans Ubuntu afin de pouvoir utiliser une bibliothèque particuilière dans votre environnement de développement en langage C :
|
||
|
||
|
|
|
Le programme apt-file permet de savoir à quel paquet .DEB appartient un fichier donné. Le tableau précédent a été réalisé grâce à une série de commandes sur le modèle suivant :
apt-file search nom_du_fichier_d_en_tete_a_rechercher
Le programme apt-file permet également de connaître l'ensemble des fichiers installé grâce à un paquet donné. Par exemple pour obtenir la liste de tous les fichiers d'en-tête .h installé par la librairies standard libc6-dev la commande est la suivante :
apt-file list libc6-dev | grep \\.h
Et le résultat est une liste de 386 fichiers d'en-tête .h :
|
libc6-dev: /usr/include/netinet/ip.h libc6-dev: /usr/include/netinet/ip6.h libc6-dev: /usr/include/netinet/ip_icmp.h libc6-dev: /usr/include/netinet/tcp.h libc6-dev: /usr/include/netinet/udp.h libc6-dev: /usr/include/netipx/ipx.h libc6-dev: /usr/include/netiucv/iucv.h libc6-dev: /usr/include/netpacket/packet.h libc6-dev: /usr/include/netrom/netrom.h libc6-dev: /usr/include/netrose/rose.h libc6-dev: /usr/include/nfs/nfs.h libc6-dev: /usr/include/nl_types.h libc6-dev: /usr/include/nss.h libc6-dev: /usr/include/obstack.h libc6-dev: /usr/include/paths.h libc6-dev: /usr/include/poll.h libc6-dev: /usr/include/printf.h libc6-dev: /usr/include/protocols/routed.h libc6-dev: /usr/include/protocols/rwhod.h libc6-dev: /usr/include/protocols/talkd.h libc6-dev: /usr/include/protocols/timed.h libc6-dev: /usr/include/pthread.h libc6-dev: /usr/include/pty.h libc6-dev: /usr/include/pwd.h libc6-dev: /usr/include/re_comp.h libc6-dev: /usr/include/regex.h libc6-dev: /usr/include/regexp.h libc6-dev: /usr/include/resolv.h libc6-dev: /usr/include/rpc/auth.h libc6-dev: /usr/include/rpc/auth_des.h libc6-dev: /usr/include/rpc/auth_unix.h libc6-dev: /usr/include/rpc/clnt.h libc6-dev: /usr/include/rpc/des_crypt.h libc6-dev: /usr/include/rpc/key_prot.h libc6-dev: /usr/include/rpc/netdb.h libc6-dev: /usr/include/rpc/pmap_clnt.h libc6-dev: /usr/include/rpc/pmap_prot.h libc6-dev: /usr/include/rpc/pmap_rmt.h libc6-dev: /usr/include/rpc/rpc.h libc6-dev: /usr/include/rpc/rpc_des.h libc6-dev: /usr/include/rpc/rpc_msg.h libc6-dev: /usr/include/rpc/svc.h libc6-dev: /usr/include/rpc/svc_auth.h libc6-dev: /usr/include/rpc/types.h libc6-dev: /usr/include/rpc/xdr.h libc6-dev: /usr/include/rpcsvc/bootparam.h libc6-dev: /usr/include/rpcsvc/bootparam_prot.h libc6-dev: /usr/include/rpcsvc/key_prot.h libc6-dev: /usr/include/rpcsvc/klm_prot.h libc6-dev: /usr/include/rpcsvc/mount.h libc6-dev: /usr/include/rpcsvc/nfs_prot.h libc6-dev: /usr/include/rpcsvc/nis.h libc6-dev: /usr/include/rpcsvc/nis_callback.h libc6-dev: /usr/include/rpcsvc/nis_tags.h libc6-dev: /usr/include/rpcsvc/nislib.h libc6-dev: /usr/include/rpcsvc/nlm_prot.h libc6-dev: /usr/include/rpcsvc/rex.h libc6-dev: /usr/include/rpcsvc/rquota.h libc6-dev: /usr/include/rpcsvc/rstat.h libc6-dev: /usr/include/rpcsvc/rusers.h libc6-dev: /usr/include/rpcsvc/sm_inter.h libc6-dev: /usr/include/rpcsvc/spray.h libc6-dev: /usr/include/rpcsvc/yp.h libc6-dev: /usr/include/rpcsvc/yp_prot.h libc6-dev: /usr/include/rpcsvc/ypclnt.h libc6-dev: /usr/include/rpcsvc/yppasswd.h libc6-dev: /usr/include/rpcsvc/ypupd.h libc6-dev: /usr/include/sched.h libc6-dev: /usr/include/scsi/scsi.h libc6-dev: /usr/include/scsi/scsi_ioctl.h libc6-dev: /usr/include/scsi/sg.h libc6-dev: /usr/include/search.h libc6-dev: /usr/include/semaphore.h libc6-dev: /usr/include/setjmp.h libc6-dev: /usr/include/sgtty.h libc6-dev: /usr/include/shadow.h libc6-dev: /usr/include/signal.h libc6-dev: /usr/include/spawn.h libc6-dev: /usr/include/stab.h libc6-dev: /usr/include/stdint.h libc6-dev: /usr/include/stdio.h libc6-dev: /usr/include/stdio_ext.h libc6-dev: /usr/include/stdlib.h libc6-dev: /usr/include/string.h libc6-dev: /usr/include/strings.h libc6-dev: /usr/include/stropts.h libc6-dev: /usr/include/sys/acct.h libc6-dev: /usr/include/sys/bitypes.h libc6-dev: /usr/include/sys/cdefs.h libc6-dev: /usr/include/sys/debugreg.h libc6-dev: /usr/include/sys/dir.h libc6-dev: /usr/include/sys/elf.h libc6-dev: /usr/include/sys/epoll.h libc6-dev: /usr/include/sys/errno.h libc6-dev: /usr/include/sys/eventfd.h libc6-dev: /usr/include/sys/fcntl.h libc6-dev: /usr/include/sys/file.h libc6-dev: /usr/include/sys/fsuid.h libc6-dev: /usr/include/sys/gmon.h libc6-dev: /usr/include/sys/gmon_out.h libc6-dev: /usr/include/sys/inotify.h libc6-dev: /usr/include/sys/io.h libc6-dev: /usr/include/sys/ioctl.h libc6-dev: /usr/include/sys/ipc.h libc6-dev: /usr/include/sys/kd.h libc6-dev: /usr/include/sys/kdaemon.h libc6-dev: /usr/include/sys/klog.h libc6-dev: /usr/include/sys/mman.h libc6-dev: /usr/include/sys/mount.h libc6-dev: /usr/include/sys/msg.h libc6-dev: /usr/include/sys/mtio.h libc6-dev: /usr/include/sys/param.h libc6-dev: /usr/include/sys/pci.h libc6-dev: /usr/include/sys/perm.h libc6-dev: /usr/include/sys/personality.h libc6-dev: /usr/include/sys/poll.h libc6-dev: /usr/include/sys/prctl.h libc6-dev: /usr/include/sys/procfs.h libc6-dev: /usr/include/sys/profil.h libc6-dev: /usr/include/sys/ptrace.h libc6-dev: /usr/include/sys/queue.h libc6-dev: /usr/include/sys/quota.h libc6-dev: /usr/include/sys/raw.h libc6-dev: /usr/include/sys/reboot.h libc6-dev: /usr/include/sys/reg.h libc6-dev: /usr/include/sys/resource.h libc6-dev: /usr/include/sys/select.h libc6-dev: /usr/include/sys/sem.h libc6-dev: /usr/include/sys/sendfile.h libc6-dev: /usr/include/sys/shm.h libc6-dev: /usr/include/sys/signal.h libc6-dev: /usr/include/sys/signalfd.h libc6-dev: /usr/include/sys/socket.h libc6-dev: /usr/include/sys/socketvar.h libc6-dev: /usr/include/sys/soundcard.h libc6-dev: /usr/include/sys/stat.h libc6-dev: /usr/include/sys/statfs.h libc6-dev: /usr/include/sys/statvfs.h libc6-dev: /usr/include/sys/stropts.h libc6-dev: /usr/include/sys/swap.h libc6-dev: /usr/include/sys/syscall.h libc6-dev: /usr/include/sys/sysctl.h libc6-dev: /usr/include/sys/sysinfo.h libc6-dev: /usr/include/sys/syslog.h libc6-dev: /usr/include/sys/sysmacros.h libc6-dev: /usr/include/sys/termios.h libc6-dev: /usr/include/sys/time.h libc6-dev: /usr/include/sys/timeb.h libc6-dev: /usr/include/sys/timerfd.h libc6-dev: /usr/include/sys/times.h libc6-dev: /usr/include/sys/timex.h libc6-dev: /usr/include/sys/ttychars.h libc6-dev: /usr/include/sys/ttydefaults.h libc6-dev: /usr/include/sys/types.h libc6-dev: /usr/include/sys/ucontext.h libc6-dev: /usr/include/sys/uio.h libc6-dev: /usr/include/sys/ultrasound.h libc6-dev: /usr/include/sys/un.h libc6-dev: /usr/include/sys/unistd.h libc6-dev: /usr/include/sys/user.h libc6-dev: /usr/include/sys/ustat.h libc6-dev: /usr/include/sys/utsname.h libc6-dev: /usr/include/sys/vfs.h libc6-dev: /usr/include/sys/vlimit.h libc6-dev: /usr/include/sys/vm86.h libc6-dev: /usr/include/sys/vt.h libc6-dev: /usr/include/sys/vtimes.h libc6-dev: /usr/include/sys/wait.h libc6-dev: /usr/include/sys/xattr.h libc6-dev: /usr/include/syscall.h libc6-dev: /usr/include/sysexits.h libc6-dev: /usr/include/syslog.h libc6-dev: /usr/include/tar.h libc6-dev: /usr/include/termio.h libc6-dev: /usr/include/termios.h libc6-dev: /usr/include/tgmath.h libc6-dev: /usr/include/thread_db.h libc6-dev: /usr/include/time.h libc6-dev: /usr/include/ttyent.h libc6-dev: /usr/include/ucontext.h libc6-dev: /usr/include/ulimit.h libc6-dev: /usr/include/unistd.h libc6-dev: /usr/include/ustat.h libc6-dev: /usr/include/utime.h libc6-dev: /usr/include/utmp.h libc6-dev: /usr/include/utmpx.h libc6-dev: /usr/include/values.h libc6-dev: /usr/include/wait.h libc6-dev: /usr/include/wchar.h libc6-dev: /usr/include/wctype.h libc6-dev: /usr/include/wordexp.h libc6-dev: /usr/include/xlocale.h libc6-dev-amd64: /usr/include/gnu/stubs-64.h |
Exemple de programme utilisant la librairie regex.h comme support d'expressions régulières en C :
#include <stdio.h>
#include <sys/types.h>
#include <regex.h>int match(const char *str, const char *pattern)
{
int status;
regex_t re; if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) return 0; status = regexec(&re, str, (size_t) 0, NULL, 0);
regfree(&re);
if (status != 0) return 0; return 1;
}int main()
{
int i;
const char *pattern = "([0-9]{1,3}\\.){3}[0-9]{1,3}";
const char *str1="une IP 335.0.0.1 uhu";
const char *str2="Pas d'IP 125.0.0. .."; i = match(str1, pattern);
printf("Dans [%s] IP %strouvée.\n", str1, (i==1)?"":"non "); i = match(str2, pattern);
printf("Dans [%s] IP %strouvée.\n", str2, (i==1)?"":"non ");return 0;
}
Exemple de programme utilisant la librairie math.h . Ce programme calcule puis affiche les 10000 premiers nombres premiers et sera compilé avec gcc premier.c -lm :
#include <stdio.h>
#include <math.h>
#define MAX_NB 10000int main (int argc, char *argv[])
{
// le tableau des nombres
int tab[MAX_NB] = { 2, 3, 5, 7 };
int nb = 11, index = 4, racine, index2, x;// remplissage du tableau :
retour:
index2 = 0;
nb += 2;
racine = (int) sqrt (nb) + 1;
while (racine >= tab[index2])
{
if (!(nb % tab[index2]))
goto retour;
index2++;
}
if (index < MAX_NB)
{
tab[index] = nb;
index++;
goto retour;
}// affichage du tableau :
for (x = 0; x < MAX_NB; x++)
{
if (!(x % 11))
printf("%d\n",tab[x]);
else
printf("%d\t",tab[x]);
}return 0;
}
Notre programme en C utilisant les fonctions de la librairie GDK commence par la ligne suivante :
#include <gdk/gdk.h>
Il faut donc vérifier si le fichier d'en-tête gdk.h est bien présent dans le répertoire /usr/include. Une recherche récurcive grâce à MC nous informe que le fichier gdk.h se trouve précisément dans le répertoire /usr/include/gdk-2.0/gdk/gdk.h.
La commande locate peut aussi permettre de rechercher un fichier dans l'ensemble du système de fichier :
locate gdk.h
Le programme locate nous répond que l'emplacement du fichier gdk.h est /usr/include/gtk-2.0/gdk/gdk.h
Le programme apt-file permet de faire des recherches dans les paquets .DEB, par exemple pour savoir quel paquet .DEB contient un fichier d'en-tête .h particulier. Avant la première utilisation de apt-file il faut mettre à jour la liste des paquets :
apt-file update
Interrogeons le programme apt-file pour savoir quel paquet .DEB a installé le fichier gdk.h :
apt-file search /usr/include/gtk-2.0/gdk/gdk.h
Le programme apt-file nous répond que gdk.h a été installé par le paquet libgtk2.0-dev
En cas de besoin la commande apt-file list libgtk-2.0 nous donne la liste des fichiers contenus dans le paquet libgtk2.0-dev.deb.
Interrogeons le programme pkg-config pour savoir quel est le nom exact de la librairie gdk :
pkg-config --list-all | grep gdk
Nous trouvons dans la liste plusieurs librairies contenant gdk dans leur nom (sous Ubuntu 10.04) dont gdk-x11-2.0 et gdk-2.0. Nous utiliserons la librairie gdk-2.0.
Demandons à pkg-config quelle est le numéro exact de version de gdk actuellement installé dans le système :
pkg-config --modversion gdk-2.0
Pkg-config nous répond 2.20.1
Demandons à pkg-config quel est l'ensemble des librairies dynamiques utilisées par l'éditeur de liens lors de la compilation d'un programme source en C utilisant gdk-2.0 :
pkg-config --libs gdk-2.0
Pkg-config nous donne une liste de 11 librairies.
Demandons à pkg-config quelle est la liste des répertoires dans lesquels il faudra rechercher les fichiers d'en-tête .h lors de la compilation d'un programme source en C utilisant gdk-2.0 :
pkg-config --cflags gdk-2.0
Pkg-config nous donne une liste de 11 répertoires (qui sont soit des sous-répertoires de /usr/include soit des sous-répertoires de /usr/lib).
Voici un premier programme de base qui crée une fenêtre puis qui y dessine un rectangle et un rond grâce aux fonctions de dessin de GDK. Pour utiliser ce code source :
/********************************************************************************************************/ #include <gdk/gdk.h>
|
Voici maintenant le jeu Othello programmé en C en utilisant GDK. Cette première version d'Othello se joue à deux joueurs : à tour de rôle chaque joueur clique dans la grille, et un piont de couleur alternativement noir et blanc se dessine dans la case visée. Pour utiliser ce code source :
/********************************************************************************************************/ #include <gdk/gdk.h> int tab_grille[9][9]; /* les indices du tableau vont de 0 à 8, mais seuls les indices 1 à 8 sont utilisés */ /********************************************************************************************************************/ gc=gdk_gc_new(window); if (couleur_piont==1) { switch (case_x) switch (case_y) /* dessine le piont */ /* met à jour le tableau */ /* change la couleur du prochain joueur seulement si la case vient d'etre jouee */ /********************************************************************************************************************/ void TracerGrille(GdkEventExpose *ev, gboolean force) /* efface la fenêtre */ /* trace le rectangle vert sur le fond de la fenetre */ /* trace les lignes verticales */ /* trace les 4 pionts de départ */ /* initialise le premier joueur */ gdk_gc_destroy(gc); /********************************************************************************************************************/ /* teste dans quelle case le bouton a été enfoncé */ if (ev->y < 50 ) { y=1; } if (tab_grille[x][y]==0) /* teste si la case est vide avant de dessiner le piont */ } /********************************************************************************************************************/ int main(int argc, char *argv[])
/* Boucle d'attente des événements */
|
Voici enfin la version complète du jeu Othello que j'ai programmé en C en utilisant GDK. Cette version d'Othello permet à un joueur de jouer contre l'ordinateur. Par défaut le joueur a les points noirs et commence. Pour utiliser ce code source :
/********************************************************************************************************/ #include <gdk/gdk.h> int tab_grille[9][9]; /* les indices du tableau vont de 0 à 8, mais seuls les indices 1 à 8 sont utilisés */ GdkWindow *fenetre; /* fenetre principale */ void RedessinerJeu(void); /********************************************************************************************************************/ gc=gdk_gc_new(fenetre); if (couleur_piont==1) { switch (case_x) switch (case_y) /* dessine le piont */ if (allumer_piont==1) { } /* met à jour le tableau */ } /********************************************************************************************************************/ void TracerGrille(gboolean raz) /* déclaration des messages à afficher */ /* efface la fenêtre */ /* trace le rectangle vert sur le fond de la fenetre */ /* trace les lignes verticales */ gc=gdk_gc_new(fenetre); /* prépare le contexte graphique */ gdk_font_unref(police); /* si raz alors on remet à zéro le tableau, en plus de dessiner la grille (début d'une nouvelle partie) */ /* prépare une nouvelle partie : */ /* initialise le tableau à 2 dimentions, image de la grille */ /* trace les 4 pionts de départ */ /* initialise le premier joueur */ gdk_gc_destroy(gc); /********************************************************************************************************************/ void TesterJoueur(void) int x,y,i,j,adversaire,case_correcte=0,chemin_correct,case_x,case_y; /* détermine la couleur de l'adversaire */ /* recherche et teste les cases vides */ /* teste les 64 cases une par une */ if ((tab_grille[x][y] == prochain_joueur) && ((abs(case_x-x) > 1) || (abs(case_y-y) > 1))) } } /* cas 4 */ } } /* cas 6 */ } } /* cas 8 */ } else /* la case est considérée jouable a partir du moment ou au moins 1 chemin correct a été trouvé */ if (jeu_fini==1) /* la partie est finie, on compte le nombre de pionts BLANCS et de pionts NOIRS */ } /* si case_correcte==1 alors il y a au moins une case jouable pour le prochain joueur */ else if (case_correcte==0) { /********************************************************************************************************************/ void TesterCase(int case_x, int case_y) int x,y,i,j,adversaire,case_correcte=0,chemin_correct; /* si la case n'est pas vide, on sort immédiatement de TesterCase */ /* détermine la couleur de l'adversaire */ /* teste les 64 cases une par une */ if ((tab_grille[x][y] == prochain_joueur) && ((abs(case_x-x) > 1) || (abs(case_y-y) > 1))) if (chemin_correct==1) { if (chemin_correct==1) { if (chemin_correct==1) { /* cas 4 */ if (chemin_correct==1) { if (chemin_correct) { /* cas 6 */ if (chemin_correct) { if (chemin_correct) { /* cas 8 */ if (chemin_correct) { else /* la case est considérée jouable a partir du moment ou au moins 1 chemin correct a été trouvé */ if (case_correcte==1) { /* on teste maintenant si le prochain joueur peut jouer ou s'il doit passer son tour */ } } /* fin de la fonction TESTER_CASE() */
/********************************************************************************************************************/ void RedessinerJeu(void) /* redessine la grille et le fond vert sant toucher au tableau */ /* redessine les piont sur les 64 cases une par une */ } /********************************************************************************************************************/ /* teste dans quelle case le bouton a été enfoncé */ if (ev->y < 50 ) { y=1; } if ((x!=-1) && (y!=-1) && (tab_grille[x][y]==0)) } /********************************************************************************************************************/ void OrdinateurJoue(int niveau, int joueur) if (joueur==1) { adversaire=2; } switch (niveau) /* -------------------------- NIVEAU 0 ------------------------------------------------------------------*/ case 0: /* L'ordinateur joue au hasard */ /* -------------------------- NIVEAU 1 ------------------------------------------------------------------*/ case 1: /* Tant que (prochain_joueur==joueur) l'ordinateur n'a pas encore joué : on teste alors une nouvelle case */ /* teste les 4 coins : */ /* teste les bords non adjacents aux coins : */ /* teste les cases centrales : */ /* teste les cases lignes 2 et 7 et colonnes 2 et 7 : */ /* en dernier recourt, teste les cases adjacentes aux coins */ if (prochain_joueur==joueur) TesterCase(2,2); break; /* -------------------------- NIVEAU 2 ------------------------------------------------------------------*/ case 2: /* Tant que (prochain_joueur==joueur) l'ordinateur n'a pas encore joué : on teste alors une nouvelle case */ /* teste les 4 coins : */ /************** Tests de cases conditionnels **********************/ /* si un coin est déjà pris par l'ordinateur, il teste les 3 cases adjacentes à ce coin */ /* teste les bords non adjacents aux coins si l'ordinateur possède déjà un coin : */ for (i=3;i<=6;i++) for (i=3;i<=6;i++) for (i=3;i<=6;i++) /* teste les bords non adjacents aux coins si l'adversaire ne possède pas un coin de ce bord : */ for (i=3;i<=6;i++) for (i=3;i<=6;i++) for (i=3;i<=6;i++) /* teste les cases lignes 2 et 7 et colonnes 2 et 7 si l'ordinateur possède déjà un coin : */ /************** Tests de cases inconditionnels (comme pour le niveau 1) **********************/ /* teste les cases centrales : */ /* teste les bords non adjacents aux coins : */ /* teste les cases lignes 2 et 7 et colonnes 2 et 7 : */ /* en dernier recourt, teste les cases adjacentes aux coins */ if (prochain_joueur==joueur) TesterCase(2,2); break; /* -------------------------- NIVEAU 3 ------------------------------------------------------------------*/ case 3: /* Tant que (prochain_joueur==joueur) l'ordinateur n'a pas encore joué : on teste alors une nouvelle case */ /* teste les 4 coins : */ /************** Balayage de la moitiè du jeu si l'ordinateur possède un coin **********************/ /* si un coin est déjà pris par l'ordinateur, il teste les 27 cases les plus proches du coin */ if (tab_grille[1][1]==joueur) /* puis on teste les 24 autres cases "par balayage" du damier */ if (tab_grille[1][1]==joueur) /************** Tests de cases conditionnels (comme au niveau 2) **********************/ /* teste les bords non adjacents aux coins si l'adversaire ne possède pas un coin de ce bord : */ for (i=3;i<=6;i++) for (i=3;i<=6;i++) for (i=3;i<=6;i++) /************** Tests de cases inconditionnels (comme pour le niveau 1) **********************/ /* teste les cases centrales : */ /* teste les bords non adjacents aux coins : */ /* teste les cases lignes 2 et 7 et colonnes 2 et 7 : */ /* en dernier recourt, teste les cases adjacentes aux coins */ if (prochain_joueur==joueur) TesterCase(2,2); break; /* -------------------------- NIVEAU 4 ------------------------------------------------------------------*/ case 4: /* Le niveau 4 est comme le niveau 3 avec en plus : On teste la case T si les 2 cases A sont occupées par l'adersaire, et si le coin V est vide (idem pour chaque coin) : ------------------------------------------------- /* tests horizontaux dans les 4 coins : */ /* tests verticaux dans les 4 coins : */ /* Si le coin V est vide, et que la case J contient la couleur du joueur alors que la case A contient la couleur de l'adversaire, ------------------------------------------------- /* tests horizontaux dans les 4 coins : */ /* tests verticaux dans les 4 coins : */
/* On teste les cases T si les 4 cases V sont vides (idem pour chaque coin) : ------------------------------------------------- /* test des 2 cases T si les 4 cases V sont vides (pour chaque coin) */ if ((tab_grille[1][1]==0) && (tab_grille[2][2]==0) && (tab_grille[3][1]==0) && (tab_grille[1][3]==0)) if ((tab_grille[8][1]==0) && (tab_grille[7][2]==0) && (tab_grille[6][1]==0) && (tab_grille[8][3]==0)) if ((tab_grille[1][8]==0) && (tab_grille[2][7]==0) && (tab_grille[1][6]==0) && (tab_grille[3][8]==0)) if ((tab_grille[8][8]==0) && (tab_grille[7][7]==0) && (tab_grille[8][6]==0) && (tab_grille[6][8]==0))
/************** Balayage de la moitiè du jeu si l'ordinateur possède un coin (comme au niveau 3) **********************/ /* si un coin est déjà pris par l'ordinateur, il teste les 27 cases les plus proches du coin */ if (tab_grille[1][1]==joueur) /* puis on teste les 24 autres cases "par balayage" du damier */ if (tab_grille[1][1]==joueur) /* Si les cases V sont vides, et si la case J contient la couleur du joueur alors IL NE FAUT PAS JOUER DANS LA CASE 4 ------------------------------------------------- /* si l'ordinateur possède une case 2, il ne teste pas les cases de ce bord, mais teste les autres bords en priorité : */ /* teste les bords non adjacents aux coins si l'ordinateur NE POSSEDE PAS une case 2 sur ce bord : */ if (!((tab_grille[1][1]==0) && (tab_grille[1][2]==joueur) && (tab_grille[1][3]==0) && (tab_grille[1][4]==0) || if (!((tab_grille[8][1]==0) && (tab_grille[8][2]==joueur) && (tab_grille[8][3]==0) && (tab_grille[8][4]==0) || /* teste les lignes 2 et 7 si l'ordinateur NE POSSEDE PAS une case 2 sir ce bord : */ if (!((tab_grille[1][1]==0) && (tab_grille[1][2]==joueur) && (tab_grille[1][3]==0) && (tab_grille[1][4]==0) || if (!((tab_grille[8][1]==0) && (tab_grille[8][2]==joueur) && (tab_grille[8][3]==0) && (tab_grille[8][4]==0) || /************** Tests de cases conditionnels (comme au niveau 2) **********************/ /* teste les bords non adjacents aux coins si l'adversaire ne possède pas un coin de ce bord : */ for (i=3;i<=6;i++) for (i=3;i<=6;i++) for (i=3;i<=6;i++) /************** Tests de cases inconditionnels (comme pour le niveau 1) **********************/ /* teste les cases centrales : */ /* teste les bords non adjacents aux coins : */ /* teste les cases lignes 2 et 7 et colonnes 2 et 7 : */ /* en dernier recourt, teste les cases adjacentes aux coins */ if (prochain_joueur==joueur) TesterCase(2,2); break; /* -------------------------- FIN DES NIVEAUX ------------------------------------------------------------------*/ } /* switch */ } /********************************************************************************************************************/ void TraitementTouche(GdkEvent *ev) //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- } //------------------------------------------------------------------------------------------------------------------- case 65363 : /* touche flèche DROITE : on rectifie la dernière action annulée */ //------------------------------------------------------------------------------------------------------------------- case 65470 : /* touche F1 : on bascule les blancs entre le mode manuel et le mode automatique */ //------------------------------------------------------------------------------------------------------------------- case 65471 : /* touche F2 : on bascule les noirs entre le mode manuel et le mode automatique */ //------------------------------------------------------------------------------------------------------------------- case 65307 : /* touche ESC : on quitte le jeu */ //------------------------------------------------------------------------------------------------------------------- case 114 : /* touche R : on active ou désactive l'allumage de la lumière rouge sur le piont joué */ //------------------------------------------------------------------------------------------------------------------- } /* switch */ } /********************************************************************************************************************/ int main(int argc, char *argv[]) GdkWindowAttr attr; int debut_jeu=1; attr.width = 50*8+400; gdk_init(&argc, &argv); /* Création de la fenêtre */ /* Affichage de la fenetre */
/* Boucle d'attente principale des événements */ case GDK_KEY_PRESS: /* si blanc_auto == -1 alors les blancs jouent en manuel */ } |
Si vous avez testé le jeu reversi (ou othello, au choix ...) ci-dessus vous avez pu constater que l'ordinateur est très fort : il arrive toujours à s'emparer des coins sans que vous ne puissiez l'en empêcher. Voici une solution pour gagner contre l'ordinateur (en prenant notamment les 4 coins) : 60 pionts NOIRS (vous) contre 4 pionts BLANCS (l'ordinateur) en fin de partie ! Cette grille (qui n'est ni unique ni la meilleure solution) vous indique l'ordre des cases dans lequel vous devez jouer le piont NOIR :
Notre programme en C utilisant les fonctions de la librairie GDK commence par la ligne suivante :
#include <gtk/gtk.h>
Il faut donc vérifier si le fichier d'en-tête gtk.h est bien présent dans le répertoire /usr/include :
locate gtk.h
Le programme locate nous répond que l'emplacement du fichier gtk.h est /usr/include/gtk-2.0/gtk/gtk.h
Interrogeons le programme apt-file pour savoir quel paquet .DEB a installé le fichier gtk.h :
apt-file search /usr/include/gtk-2.0/gtk/gtk.h
Le programme apt-file nous répond que le fichier gtk.h a été installé par le paquet libgtk2.0-dev
Interrogeons le programme pkg-config pour savoir quel est le nom exact de la librairie gdk :
pkg-config --list-all | grep gtk
Nous trouvons dans la liste plusieurs librairies contenant gtk dans leur nom (sous Ubuntu 10.04) dont gtk+-x11-2.0 et gtk+-2.0. Nous utiliserons la librairie gtk+-2.0.
Demandons à pkg-config quelle est le numéro exact de version de gtk+-2.0 actuellement installé dans le système :
pkg-config --modversion gtk+-2.0
Pkg-config nous répond 2.20.1
Demandons à pkg-config quel est l'ensemble des librairies dynamiques utilisées par l'éditeur de liens lors de la compilation d'un programme source en C utilisant gtk+-2.0 :
pkg-config --libs gtk+-2.0
Pkg-config nous donne une liste de 17 librairies.
Demandons à pkg-config quelle est la liste des répertoires dans lesquels il faudra rechercher les fichiers d'en-tête .h lors de la compilation d'un programme source en C utilisant gtk+-2.0 :
pkg-config --cflags gtk+-2.0
Pkg-config nous donne une liste de 12 répertoires (qui sont soit des sous-répertoires de /usr/include soit des sous-répertoires de /usr/lib) dont le répertoire /usr/include/gtk-2.0/. Comme le fichier d'entête gtk.h se trouve dans le sous-répertoire gtk du répertoire /usr/include/gtk-2.0/, et que le répertoire /usr/include/gtk-2.0/gtk/ ne fait pas partie de la liste donnée par pkg-config --cflags, il faudra alors mettre la ligne #include <gtk/gtk.h> dans le programme source et non simplement #include <gtk.h>.
Voici un exemple de programme utilisant GTK+. Ce programme dessine l'ensemble de Mandelbrot dans une fenêtre GTK+. Pour utiliser ce code source :
/*****************************************************************************/ #include <gtk/gtk.h> /*****************************************************************************/ GtkWidget *Fenetre,*boite_dialog_h,*boite_dialog_v,*ZoneDessin,*bouton; struct complexe struct complexe z1, z2, z; float x_min=-2.0, x_max=2.0, y_min=-2.0, y_max=2.0, nouveau_x_min, nouveau_x_max, nouveau_y_min, /**************************************************************************/ void } /**************************************************************************/ void } /*****************************************************************************/ void CreerInterface(); /*****************************************************************************/ int main(int argc, char *argv[]) /*Création de l'interface */ /* On affiche l'interface */ /* La boucle principale */ /*****************************************************************************/
/*****************************************************************************/ void CreerInterface() /* Connexion des signaux "delete" et "destroy" */ //-------------------------------------------------------------------- //-------------------------------------------------------------------- /* Création et attachement de la zone de gtk_box_pack_start(GTK_BOX(boite_dialog_h),ZoneDessin,FALSE,FALSE,0); gtk_drawing_area_size(GTK_DRAWING_AREA(ZoneDessin), //-------------------------------------------------------------------- /* Définition de la taille initiale de la boite : */ bouton=gtk_check_button_new_with_label("Bouton 1"); bouton=gtk_check_button_new_with_label("Bouton 2"); bouton=gtk_button_new_with_label("Bouton 3"); bouton=gtk_button_new_with_label("Bouton 4"); bouton=gtk_button_new_with_label("Quitter"); } /*****************************************************************************/ gboolean Dessine(GtkWidget *ZoneDessin, GdkEventExpose *ev) /* On récupère la colormap de la zone de dessin */ /* On récupère le GC par défaut de la fenêtre */ gdk_draw_point(ZoneDessin->window, gc,x,y); |
Voyons maintenant comment concevoir rapidement une interface graphique GTK+ avec Glade et comment l'intégrer dans un programme en C.
Il faut avant tout savoir que :
Installation de Glade et de libglade sous Ubuntu : il faut installer les paquets glade et libglade2-0.
Vérification de l'installation :
Procédure pour obtenir un programma exécutable utilisant l'interface graphique créée dans Glade 3.6.7 :
/********************************************************************************/ #include <gtk/gtk.h> int main(int argc,char **argv) xml = glade_xml_new("toto.glade",NULL,NULL); |
Important : le nom du widget appelé par la fonction glade_xml_get_widget dans le code C doit être le même que le nom entré dans Glade lors de la conception de l'interface grpahique.
Pour l'instant ce tutoriel rapide ne dit pas encore comment récupérer dans le programme en C les évènements venant de l'interface utilisateur (état des boutons, des cases à cocher, ligne de saisie, affichage d'un message, etc ...). Le but principal est ici de donner la procédure de conversion Glade --> langage C et la ligne de commande de la compilation.
Notre programme en C utilisant les fonctions de la librairie GNOME commence par la ligne suivante :
#include <gnome.h>
Il faut donc vérifier si le fichier d'en-tête gnome.h est bien présent dans le répertoire /usr/include :
locate gnome.h
Le programme locate nous répond que l'emplacement du fichier gnome.h est /usr/include/libgnomeui-2.0/gnome.h
Interrogeons le programme apt-file pour savoir quel paquet .DEB a installé le fichier gnome.h :
apt-file search /usr/include/libgnomeui-2.0/gnome.h
Le programme apt-file nous répond que le fichier gnome.h a été installé par le paquet libgnomeui-dev
Interrogeons le programme pkg-config pour savoir quel est le nom exact de la librairie gnome :
pkg-config --list-all | grep gnome
Nous trouvons dans la liste plusieurs librairies contenant gnome dans leur nom (sous Ubuntu 10.04) dont libgnome-2.0 et libgnomeui-2.0. Nous utiliserons la librairie libgnomeui-2.0.
Demandons à pkg-config quelle est le numéro exact de version de libgnomeui-2.0 actuellement installé dans le système :
pkg-config --modversion libgnomeui-2.0
Pkg-config nous répond 2.24.3
Demandons à pkg-config quel est l'ensemble des librairies dynamiques utilisées par l'éditeur de liens lors de la compilation d'un programme source en C utilisant libgnomeui-2.0 :
pkg-config --libs libgnomeui-2.0
Pkg-config nous donne une liste de plus de 30 librairies.
Demandons à pkg-config quelle est la liste des répertoires dans lesquels il faudra rechercher les fichiers d'en-tête .h lors de la compilation d'un programme source en C utilisant libgnomeui-2.0 :
pkg-config --cflags libgnomeui-2.0
Pkg-config nous donne une liste de plus de 30 répertoires (qui sont soit des sous-répertoires de /usr/include soit des sous-répertoires de /usr/lib).
Voici un exemple de programme utilisant Gnome. Ce programme dessine une mire de couleur en colorisant chaque pixel de la fenêtre avec une couleur différente. Pour utiliser ce code source :
/******************************************************************************************/ #include <gnome.h> /* Interfaces des 3 fonctions définies plus bas : */ gint dessiner_les_pixels(gpointer data); #define WIDTH 655 /******************************************************************************************/ int main(int argc,char *argv[]) gnome_init("couleur","1.0",argc,argv); area = gtk_drawing_area_new(); gtk_signal_connect(GTK_OBJECT(area),"event", gtk_widget_show_all(app); /******************************************************************************************/ gint dessiner_les_pixels(gpointer data) { /* Déclaration des variables locales : */ if (colormap==NULL) { /* création d'un nouveau contexte graphique permettant de dessiner */ /* On allume tous les pixels de la fenêtre un par un : */ for(x=0; x<WIDTH; x++) { /* rend la couleur disponible : */ /* insère l'objet GdkColor (variable color) dans le contexte graphique */ /* allume le pixel de coordonnées x et y en utilisant le contexte */ return(0); /******************************************************************************************/ gint eventDelete(GtkWidget *widget, /******************************************************************************************/ gint eventDestroy(GtkWidget *widget, |
Notre programme en C utilisant les fonctions de l'interface OpenGL commence par la ligne suivante :
#include <GL/glut.h>
Il faut donc vérifier si le fichier d'en-tête glut.h est bien présent dans le répertoire /usr/include.
locate glut.h
La commande locate nous indique que le chemin exact du fichier d'en-tête est /urs/include/GL/glut.h
Interrogeons le programme apt-file pour savoir quel paquet .DEB a installé le fichier glut.h :
apt-file search /usr/include/GL/glut.h
Le programme apt-file nous répond que le fichier glut.h a été installé par le paquet freeglut3-dev
Interrogeons le programme pkg-config pour savoir quel est le nom exact de la librairie glut :
pkg-config --list-all | grep glut
Et là surprise : glut n'existe pas pour pkg-config. En effet, pkg-config ne connaît pas toutes les librairies du système mais seulement celles qui ont installé un fichier .pc dans le répertoire /usr/lib/pkgconfig lors de leur installation.
En allant voir le répertoire /usr/lib/pkgconfig on y trouve un fichier gl.pc et un fichier glu.pc (mais pas glut.pc).
En consultant ces deux fichiers .pc ou en demandant à pkg-config on constate que les seules librairies utiles lors de l'édition de liens sont GL et GLU (en majuscule).
L'utilisation de pkg-config n'est donc pas justifié pour OpenGL et les librairies utilisées seront indiquées "à la main" sur la ligne de commande de gcc lors de la compilation :
gcc toto.c -lGL -lGLU -lglut
Voici un exemple de base de programme en C qui utilise OpenGL. Pour utiliser ce code source :
/**************************************/ #include <GL/glut.h> /********************************************************************************************************/ void dessiner(void) glColor3f(0.2, 0.8, 1.0); /* définit la couleur RVB des objets */ /* glVertex3f crée un nouveau sommet dans l'espace */ glBegin(GL_POLYGON); /* prépare dans le tampon un objet tracé sommet par sommet (ici un polygone) */ glFlush(); /* vide le tampon dans la fenêtre en y ajoutant tous les objets préparés */ /*************************************************************************************************/ int main(int argc, char **argv) glutInit(&argc, argv); /* initialise GLUT (Open GL Utility Toolkit) */ /* prépare la fenêtre */ glutMainLoop(); /* affiche à l'écran toutes les fenêtres préparées */ /*************************************************************************************************/
|
Remarques :
L'exemple suivant montre comment lire l'état des boutons de la souris et modifier en conséquence l'objet tracé à l'écran. Dans cet exemple :
/********************************************/ /********************************************************************************************************/ #include <GL/glut.h> static GLfloat r,v,b,x1,y1; /* variables globales modifiées par les boutons de la souris */ /********************************************************************************************************/ void dessiner(void)
} /* dessiner */ /***************************************************************************************************/ void traiter_souris(int bouton, int etat, int x, int y)
} /* traiter_souris */ /*************************************************************************************************/ int main(int argc, char **argv)
} /* main */ /*************************************************************************************************/ |
L'exemple suivant montre comment prendre en compte les évènements clavier avec GLUT. Dans cet exemple :
/********************************************/ /***************************************************************************************/ #include <GL/glut.h> static GLfloat r,v,b,x1,y1; /* variables globales modifiées par les boutons de la souris */ /********************************************************************************************************/ void dessiner(void)
} /* dessiner */ /**********************************************************************************************/ void traiter_clavier(unsigned char touche, int x, int y)
} /* traiter_clavier */ /*************************************************************************************************/ int main(int argc, char **argv)
} /* main */ /*************************************************************************************************/ |
Le programme suivant montre comment réaliser la rotation dans l'espace d'un solide 3D toujours grâce à glut. Dans cet exemple :
/********************************************/ #include <GL/glut.h> typedef int BOOL; static GLfloat angle = 0.0; /***************************************************************************************************/ void traiter_souris(int bouton, int etat, int x, int y)
} /***************************************************************************************************/ void mouvement_souris(int x, int y)
} /********************************************************************************************************/ void dessiner(void)
} /********************************************************************************************************/ void animer (void)
} /*************************************************************************************************/ int main(int argc, char** argv)
} /*************************************************************************************************/ |
Enfin le dernier exemple suivant montre une démonstration des différents objets 3D prédéfinis dans GLUT (cube, sphère, théière, dodécaèdre, tore, cone, etc.). Dans cet exemple :
/***********************************************/ /*****************************************************/ #include <GL/glut.h> typedef int BOOL; static GLfloat angle = 0.0; /***************************************************************************************************/ void traiter_souris(int bouton, int etat, int x, int y) if (bouton == GLUT_LEFT_BUTTON) /***************************************************************************************************/ void mouvement_souris(int x, int y) if (Button1Down) /********************************************************************************************************/ void dessiner(void) switch(couleur) { switch(solide) { /********************************************************************************************************/ void animer (void) /**********************************************************************************************/ void traiter_clavier(unsigned char touche, int x, int y) switch(touche) { } /* traiter_clavier */ /*************************************************************************************************/ int main(int argc, char** argv) /*************************************************************************************************/ |
Autres exemples de code source en C utilisant OpenGL :
Petite ballade dans un monde en 3D
Création d'un menu interactif et affichage d'un message textuel
D'autres programmes source utilisant glut sont disponibles en exemple sur les sites suivants :
http://www.opengl.org/resources/code/samples/glut_examples/